dev: new device driver infra
[vpp.git] / src / vnet / dev / process.c
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright (c) 2023 Cisco Systems, Inc.
3  */
4
5 #include "vppinfra/error.h"
6 #include <vnet/vnet.h>
7 #include <vnet/dev/dev.h>
8 #include <vnet/dev/log.h>
9
10 VLIB_REGISTER_LOG_CLASS (dev_log, static) = {
11   .class_name = "dev",
12   .subclass_name = "process",
13 };
14
15 typedef enum
16 {
17   VNET_DEV_EVENT_PERIODIC_STOP,
18   VNET_DEV_EVENT_PERIODIC_START,
19   VNET_DEV_EVENT_PORT_CONFIG_CHANGE_REQ,
20   VNET_DEV_EVENT_PROCESS_QUIT,
21   VNET_DEV_EVENT_CALL_OP,
22   VNET_DEV_EVENT_CALL_OP_NO_RV,
23   VNET_DEV_EVENT_CALL_OP_NO_WAIT,
24   VNET_DEV_EVENT_CALL_PORT_OP,
25   VNET_DEV_EVENT_CALL_PORT_OP_NO_RV,
26   VNET_DEV_EVENT_CALL_PORT_OP_NO_WAIT,
27   VNET_DEV_EVENT_CLOCK = ~0
28 } __clib_packed vnet_dev_event_t;
29
30 typedef struct
31 {
32   vnet_dev_event_t event;
33   u8 reply_needed : 1;
34   u32 calling_process_index;
35   union
36   {
37     struct
38     {
39       vnet_dev_port_t *port;
40       vnet_dev_port_cfg_change_req_t *change_req;
41     } port_cfg_change;
42     struct
43     {
44       vnet_dev_op_t *op;
45     } call_op;
46     struct
47     {
48       vnet_dev_op_no_rv_t *op;
49     } call_op_no_rv;
50     struct
51     {
52       vnet_dev_op_no_rv_t *op;
53     } call_op_no_wait;
54     struct
55     {
56       vnet_dev_port_op_t *op;
57       vnet_dev_port_t *port;
58     } call_port_op;
59     struct
60     {
61       vnet_dev_port_op_no_rv_t *op;
62       vnet_dev_port_t *port;
63     } call_port_op_no_rv;
64     struct
65     {
66       vnet_dev_port_op_no_rv_t *op;
67       vnet_dev_port_t *port;
68     } call_port_op_no_wait;
69   };
70 } vnet_dev_event_data_t;
71
72 static vnet_dev_rv_t
73 vnet_dev_process_one_event (vlib_main_t *vm, vnet_dev_t *dev,
74                             vnet_dev_event_data_t *ed)
75 {
76   vnet_dev_port_t *p;
77   vnet_dev_rv_t rv = VNET_DEV_OK;
78
79   switch (ed->event)
80     {
81     case VNET_DEV_EVENT_CLOCK:
82       break;
83     case VNET_DEV_EVENT_PROCESS_QUIT:
84       log_debug (dev, "quit requested");
85       dev->process_node_quit = 1;
86       break;
87     case VNET_DEV_EVENT_PERIODIC_START:
88       log_debug (dev, "periodic start");
89       dev->process_node_periodic = 1;
90       break;
91     case VNET_DEV_EVENT_PERIODIC_STOP:
92       log_debug (dev, "periodic stop");
93       dev->process_node_periodic = 0;
94       break;
95     case VNET_DEV_EVENT_PORT_CONFIG_CHANGE_REQ:
96       log_debug (dev, "port config change");
97       p = ed->port_cfg_change.port;
98       rv = vnet_dev_port_cfg_change (vm, p, ed->port_cfg_change.change_req);
99       break;
100     case VNET_DEV_EVENT_CALL_OP:
101       log_debug (dev, "call op");
102       rv = ed->call_op.op (vm, dev);
103       break;
104     case VNET_DEV_EVENT_CALL_OP_NO_RV:
105       log_debug (dev, "call op no rv");
106       ed->call_op_no_rv.op (vm, dev);
107       break;
108     case VNET_DEV_EVENT_CALL_OP_NO_WAIT:
109       log_debug (dev, "call op no wait");
110       ed->call_op_no_wait.op (vm, dev);
111       break;
112     case VNET_DEV_EVENT_CALL_PORT_OP:
113       log_debug (dev, "call port op");
114       rv = ed->call_port_op.op (vm, ed->call_port_op.port);
115       break;
116     case VNET_DEV_EVENT_CALL_PORT_OP_NO_RV:
117       log_debug (dev, "call port op no rv");
118       ed->call_port_op_no_rv.op (vm, ed->call_port_op_no_rv.port);
119       break;
120     case VNET_DEV_EVENT_CALL_PORT_OP_NO_WAIT:
121       log_debug (dev, "call port op no wait");
122       ed->call_port_op_no_wait.op (vm, ed->call_port_op_no_wait.port);
123       break;
124     default:
125       ASSERT (0);
126     }
127   return rv;
128 }
129
130 static uword
131 vnet_dev_process (vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
132 {
133   vnet_dev_main_t *dm = &vnet_dev_main;
134   vnet_dev_periodic_op_t *pop, *pops = 0;
135   f64 next = CLIB_F64_MAX;
136   vnet_dev_event_data_t *event_data = 0, *new_event_data, *ed;
137
138   vnet_dev_t *dev =
139     *((vnet_dev_t **) vlib_node_get_runtime_data (vm, rt->node_index));
140
141   log_debug (dev, "process '%U' started", format_vlib_node_name, vm,
142              rt->node_index);
143
144   while (dev->process_node_quit == 0)
145     {
146       uword event_type;
147       f64 now = vlib_time_now (vm);
148
149       if (dev->process_node_periodic)
150         vlib_process_wait_for_event_or_clock (vm, next > now ? next - now : 0);
151       else
152         vlib_process_wait_for_event (vm);
153
154       new_event_data = vlib_process_get_event_data (vm, &event_type);
155
156       if (new_event_data)
157         {
158           vec_append (event_data, new_event_data);
159           vlib_process_put_event_data (vm, new_event_data);
160
161           ASSERT (event_type == 0);
162
163           vec_foreach (ed, event_data)
164             {
165               vnet_dev_rv_t rv;
166               rv = vnet_dev_process_one_event (vm, dev, ed);
167               if (ed->reply_needed)
168                 vlib_process_signal_event (vm, ed->calling_process_index,
169                                            ed->event, rv);
170             }
171           vec_reset_length (event_data);
172         }
173
174       next = CLIB_F64_MAX;
175       pool_foreach (pop, dev->periodic_ops)
176         {
177           if (pop->last_run + pop->interval < now)
178             {
179               vec_add1 (pops, *pop);
180               pop->last_run = now;
181             }
182           if (pop->last_run + pop->interval < next)
183             next = pop->last_run + pop->interval;
184         }
185
186       vec_foreach (pop, pops)
187         {
188           switch (pop->type)
189             {
190             case VNET_DEV_PERIODIC_OP_TYPE_DEV:
191               pop->dev_op (vm, pop->dev);
192               break;
193             case VNET_DEV_PERIODIC_OP_TYPE_PORT:
194               pop->port_op (vm, pop->port);
195               break;
196             default:
197               ASSERT (0);
198             }
199         }
200       vec_reset_length (pops);
201     }
202
203   log_debug (dev, "process '%U' quit", format_vlib_node_name, vm,
204              rt->node_index);
205   vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED);
206   vlib_node_rename (vm, rt->node_index, "deleted-%u", rt->node_index);
207
208   /* add node index to the freelist */
209   vec_add1 (dm->free_process_node_indices, rt->node_index);
210   vec_free (pops);
211   vec_free (event_data);
212   return 0;
213 }
214
215 vnet_dev_rv_t
216 vnet_dev_process_create (vlib_main_t *vm, vnet_dev_t *dev)
217 {
218   vnet_dev_main_t *dm = &vnet_dev_main;
219   vlib_node_t *n;
220   uword l;
221
222   l = vec_len (dm->free_process_node_indices);
223   if (l > 0)
224     {
225       n = vlib_get_node (vm, dm->free_process_node_indices[l - 1]);
226       if (n->function != vnet_dev_process)
227         {
228           vlib_node_runtime_t *rt = vlib_node_get_runtime (vm, n->index);
229           n->function = vnet_dev_process;
230           rt->function = vnet_dev_process;
231         }
232       vlib_node_rename (vm, n->index, "%s-process", dev->device_id);
233       vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
234       vec_set_len (dm->free_process_node_indices, l - 1);
235       log_debug (dev, "process node '%U' (%u) reused", format_vlib_node_name,
236                  vm, n->index, n->index);
237     }
238   else
239     {
240       vlib_node_registration_t r = {
241         .function = vnet_dev_process,
242         .type = VLIB_NODE_TYPE_PROCESS,
243         .process_log2_n_stack_bytes = 16,
244         .runtime_data_bytes = sizeof (void *),
245       };
246
247       vlib_register_node (vm, &r, "%s-process", dev->device_id);
248
249       n = vlib_get_node (vm, r.index);
250       log_debug (dev, "process node '%U' (%u) created", format_vlib_node_name,
251                  vm, r.index, r.index);
252     }
253
254   dev->process_node_index = n->index;
255   *(vnet_dev_t **) vlib_node_get_runtime_data (vm, n->index) = dev;
256   vlib_start_process (vm, n->runtime_index);
257
258   return VNET_DEV_OK;
259 }
260
261 static void
262 vnet_dev_process_event_send (vlib_main_t *vm, vnet_dev_t *dev,
263                              vnet_dev_event_data_t ed)
264 {
265   vnet_dev_event_data_t *edp = vlib_process_signal_event_data (
266     vm, dev->process_node_index, 0, 1, sizeof (ed));
267   *edp = ed;
268 }
269
270 static vnet_dev_rv_t
271 vnet_dev_process_event_send_and_wait (vlib_main_t *vm, vnet_dev_t *dev,
272                                       vnet_dev_event_data_t ed)
273 {
274   uword event, *event_data = 0;
275   vnet_dev_rv_t rv;
276
277   ed.calling_process_index = vlib_get_current_process_node_index (vm);
278
279   if (ed.calling_process_index == dev->process_node_index)
280     return vnet_dev_process_one_event (vm, dev, &ed);
281
282   ed.reply_needed = 1;
283   vnet_dev_process_event_send (vm, dev, ed);
284   vlib_process_wait_for_event_or_clock (vm, 5.0);
285   event = vlib_process_get_events (vm, &event_data);
286   if (event != ed.event)
287     {
288       log_err (dev, "%s",
289                event == VNET_DEV_EVENT_CLOCK ?
290                        "timeout waiting for process node to respond" :
291                        "unexpected event received");
292       rv = VNET_DEV_ERR_PROCESS_REPLY;
293     }
294   else
295     rv = event_data[0];
296   vec_free (event_data);
297   return rv;
298 }
299
300 void
301 vnet_dev_process_quit (vlib_main_t *vm, vnet_dev_t *dev)
302 {
303   vnet_dev_event_data_t ed = { .event = VNET_DEV_EVENT_PROCESS_QUIT };
304   vnet_dev_process_event_send_and_wait (vm, dev, ed);
305 }
306
307 static int
308 _vnet_dev_poll_add (vlib_main_t *vm, vnet_dev_t *dev,
309                     vnet_dev_periodic_op_t pop)
310 {
311   const vnet_dev_event_data_t ed = { .event = VNET_DEV_EVENT_PERIODIC_START };
312   vnet_dev_periodic_op_t *p;
313
314   pool_foreach (p, dev->periodic_ops)
315     if (p->op == pop.op && p->arg == pop.arg)
316       return 0;
317
318   pool_get_zero (dev->periodic_ops, p);
319   *p = pop;
320   if (pool_elts (dev->periodic_ops) == 1)
321     vnet_dev_process_event_send (vm, dev, ed);
322   return 1;
323 }
324
325 static int
326 _vnet_dev_poll_remove (vlib_main_t *vm, vnet_dev_t *dev, void *op, void *arg)
327 {
328   const vnet_dev_event_data_t ed = { .event = VNET_DEV_EVENT_PERIODIC_STOP };
329   vnet_dev_periodic_op_t *pop;
330
331   pool_foreach (pop, dev->periodic_ops)
332     if (pop->op == op && pop->arg == arg)
333       {
334         pool_put (dev->periodic_ops, pop);
335         if (pool_elts (dev->periodic_ops) == 0)
336           vnet_dev_process_event_send (vm, dev, ed);
337         return 1;
338       }
339   return 0;
340 }
341
342 void
343 vnet_dev_poll_dev_add (vlib_main_t *vm, vnet_dev_t *dev, f64 interval,
344                        vnet_dev_op_no_rv_t *dev_op)
345 {
346   vnet_dev_periodic_op_t pop = {
347     .interval = interval,
348     .type = VNET_DEV_PERIODIC_OP_TYPE_DEV,
349     .dev_op = dev_op,
350     .dev = dev,
351   };
352
353   if (_vnet_dev_poll_add (vm, dev, pop) == 0)
354     log_warn (dev, "poll_dev_add: op already exists, not added");
355 }
356
357 void
358 vnet_dev_poll_dev_remove (vlib_main_t *vm, vnet_dev_t *dev,
359                           vnet_dev_op_no_rv_t *dev_op)
360 {
361   if (_vnet_dev_poll_remove (vm, dev, (void *) dev_op, (void *) dev) == 0)
362     log_warn (dev, "poll_dev_remove: op not found, not removed");
363 }
364
365 void
366 vnet_dev_poll_port_add (vlib_main_t *vm, vnet_dev_port_t *port, f64 interval,
367                         vnet_dev_port_op_no_rv_t *port_op)
368 {
369   vnet_dev_t *dev = port->dev;
370   vnet_dev_periodic_op_t pop = {
371     .interval = interval,
372     .type = VNET_DEV_PERIODIC_OP_TYPE_PORT,
373     .port_op = port_op,
374     .port = port,
375   };
376
377   if (_vnet_dev_poll_add (vm, dev, pop) == 0)
378     log_warn (dev, "poll_port_add: op already exists, not added");
379 }
380
381 void
382 vnet_dev_poll_port_remove (vlib_main_t *vm, vnet_dev_port_t *port,
383                            vnet_dev_port_op_no_rv_t *port_op)
384 {
385   vnet_dev_t *dev = port->dev;
386   if (_vnet_dev_poll_remove (vm, dev, (void *) port_op, (void *) port) == 0)
387     log_warn (dev, "poll_port_remove: op not found, not removed");
388 }
389
390 vnet_dev_rv_t
391 vnet_dev_process_port_cfg_change_req (vlib_main_t *vm, vnet_dev_port_t *port,
392                                       vnet_dev_port_cfg_change_req_t *pccr)
393 {
394   const vnet_dev_event_data_t ed = {
395       .event = VNET_DEV_EVENT_PORT_CONFIG_CHANGE_REQ,
396       .port_cfg_change = {
397         .port = port,
398       .change_req = pccr,
399       },
400     };
401
402   return vnet_dev_process_event_send_and_wait (vm, port->dev, ed);
403 }
404
405 vnet_dev_rv_t
406 vnet_dev_process_call_op (vlib_main_t *vm, vnet_dev_t *dev, vnet_dev_op_t *op)
407 {
408   const vnet_dev_event_data_t ed = {
409     .event = VNET_DEV_EVENT_CALL_OP,
410     .call_op.op = op,
411   };
412
413   return vnet_dev_process_event_send_and_wait (vm, dev, ed);
414 }
415
416 vnet_dev_rv_t
417 vnet_dev_process_call_op_no_rv (vlib_main_t *vm, vnet_dev_t *dev,
418                                 vnet_dev_op_no_rv_t *op)
419 {
420   const vnet_dev_event_data_t ed = {
421     .event = VNET_DEV_EVENT_CALL_OP_NO_RV,
422     .call_op_no_rv.op = op,
423   };
424
425   return vnet_dev_process_event_send_and_wait (vm, dev, ed);
426 }
427
428 void
429 vnet_dev_process_call_op_no_wait (vlib_main_t *vm, vnet_dev_t *dev,
430                                   vnet_dev_op_no_rv_t *op)
431 {
432   const vnet_dev_event_data_t ed = {
433     .event = VNET_DEV_EVENT_CALL_OP_NO_WAIT,
434     .call_op_no_rv.op = op,
435   };
436
437   vnet_dev_process_event_send (vm, dev, ed);
438 }
439
440 vnet_dev_rv_t
441 vnet_dev_process_call_port_op (vlib_main_t *vm, vnet_dev_port_t *port,
442                                vnet_dev_port_op_t *op)
443 {
444   const vnet_dev_event_data_t ed = {
445     .event = VNET_DEV_EVENT_CALL_PORT_OP,
446     .call_port_op = { .op = op, .port = port },
447   };
448
449   return vnet_dev_process_event_send_and_wait (vm, port->dev, ed);
450 }
451
452 vnet_dev_rv_t
453 vnet_dev_process_call_port_op_no_rv (vlib_main_t *vm, vnet_dev_port_t *port,
454                                      vnet_dev_port_op_no_rv_t *op)
455 {
456   const vnet_dev_event_data_t ed = {
457     .event = VNET_DEV_EVENT_CALL_PORT_OP_NO_RV,
458     .call_port_op_no_rv = { .op = op, .port = port },
459   };
460
461   return vnet_dev_process_event_send_and_wait (vm, port->dev, ed);
462 }
463
464 void
465 vnet_dev_process_call_port_op_no_wait (vlib_main_t *vm, vnet_dev_port_t *port,
466                                        vnet_dev_port_op_no_rv_t *op)
467 {
468   const vnet_dev_event_data_t ed = {
469     .event = VNET_DEV_EVENT_CALL_PORT_OP_NO_WAIT,
470     .call_port_op_no_wait = { .op = op, .port = port },
471   };
472
473   vnet_dev_process_event_send (vm, port->dev, ed);
474 }