1 /* SPDX-License-Identifier: Apache-2.0
2 * Copyright (c) 2023 Cisco Systems, Inc.
5 #include "vppinfra/error.h"
7 #include <vnet/dev/dev.h>
8 #include <vnet/dev/log.h>
10 VLIB_REGISTER_LOG_CLASS (dev_log, static) = {
12 .subclass_name = "process",
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;
32 vnet_dev_event_t event;
34 u32 calling_process_index;
39 vnet_dev_port_t *port;
40 vnet_dev_port_cfg_change_req_t *change_req;
48 vnet_dev_op_no_rv_t *op;
52 vnet_dev_op_no_rv_t *op;
56 vnet_dev_port_op_t *op;
57 vnet_dev_port_t *port;
61 vnet_dev_port_op_no_rv_t *op;
62 vnet_dev_port_t *port;
66 vnet_dev_port_op_no_rv_t *op;
67 vnet_dev_port_t *port;
68 } call_port_op_no_wait;
70 } vnet_dev_event_data_t;
73 vnet_dev_process_one_event (vlib_main_t *vm, vnet_dev_t *dev,
74 vnet_dev_event_data_t *ed)
77 vnet_dev_rv_t rv = VNET_DEV_OK;
81 case VNET_DEV_EVENT_CLOCK:
83 case VNET_DEV_EVENT_PROCESS_QUIT:
84 log_debug (dev, "quit requested");
85 dev->process_node_quit = 1;
87 case VNET_DEV_EVENT_PERIODIC_START:
88 log_debug (dev, "periodic start");
89 dev->process_node_periodic = 1;
91 case VNET_DEV_EVENT_PERIODIC_STOP:
92 log_debug (dev, "periodic stop");
93 dev->process_node_periodic = 0;
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);
100 case VNET_DEV_EVENT_CALL_OP:
101 log_debug (dev, "call op");
102 rv = ed->call_op.op (vm, dev);
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);
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);
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);
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);
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);
131 vnet_dev_process (vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
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;
139 *((vnet_dev_t **) vlib_node_get_runtime_data (vm, rt->node_index));
141 log_debug (dev, "process '%U' started", format_vlib_node_name, vm,
144 while (dev->process_node_quit == 0)
147 f64 now = vlib_time_now (vm);
149 if (dev->process_node_periodic)
150 vlib_process_wait_for_event_or_clock (vm, next > now ? next - now : 0);
152 vlib_process_wait_for_event (vm);
154 new_event_data = vlib_process_get_event_data (vm, &event_type);
158 vec_append (event_data, new_event_data);
159 vlib_process_put_event_data (vm, new_event_data);
161 ASSERT (event_type == 0);
163 vec_foreach (ed, event_data)
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,
171 vec_reset_length (event_data);
175 pool_foreach (pop, dev->periodic_ops)
177 if (pop->last_run + pop->interval < now)
179 vec_add1 (pops, *pop);
182 if (pop->last_run + pop->interval < next)
183 next = pop->last_run + pop->interval;
186 vec_foreach (pop, pops)
190 case VNET_DEV_PERIODIC_OP_TYPE_DEV:
191 pop->dev_op (vm, pop->dev);
193 case VNET_DEV_PERIODIC_OP_TYPE_PORT:
194 pop->port_op (vm, pop->port);
200 vec_reset_length (pops);
203 log_debug (dev, "process '%U' quit", format_vlib_node_name, vm,
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);
208 /* add node index to the freelist */
209 vec_add1 (dm->free_process_node_indices, rt->node_index);
211 vec_free (event_data);
216 vnet_dev_process_create (vlib_main_t *vm, vnet_dev_t *dev)
218 vnet_dev_main_t *dm = &vnet_dev_main;
222 l = vec_len (dm->free_process_node_indices);
225 n = vlib_get_node (vm, dm->free_process_node_indices[l - 1]);
226 if (n->function != vnet_dev_process)
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;
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);
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 *),
247 vlib_register_node (vm, &r, "%s-process", dev->device_id);
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);
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);
262 vnet_dev_process_event_send (vlib_main_t *vm, vnet_dev_t *dev,
263 vnet_dev_event_data_t ed)
265 vnet_dev_event_data_t *edp = vlib_process_signal_event_data (
266 vm, dev->process_node_index, 0, 1, sizeof (ed));
271 vnet_dev_process_event_send_and_wait (vlib_main_t *vm, vnet_dev_t *dev,
272 vnet_dev_event_data_t ed)
274 uword event, *event_data = 0;
277 ed.calling_process_index = vlib_get_current_process_node_index (vm);
279 if (ed.calling_process_index == dev->process_node_index)
280 return vnet_dev_process_one_event (vm, dev, &ed);
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)
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;
296 vec_free (event_data);
301 vnet_dev_process_quit (vlib_main_t *vm, vnet_dev_t *dev)
303 vnet_dev_event_data_t ed = { .event = VNET_DEV_EVENT_PROCESS_QUIT };
304 vnet_dev_process_event_send_and_wait (vm, dev, ed);
308 _vnet_dev_poll_add (vlib_main_t *vm, vnet_dev_t *dev,
309 vnet_dev_periodic_op_t pop)
311 const vnet_dev_event_data_t ed = { .event = VNET_DEV_EVENT_PERIODIC_START };
312 vnet_dev_periodic_op_t *p;
314 pool_foreach (p, dev->periodic_ops)
315 if (p->op == pop.op && p->arg == pop.arg)
318 pool_get_zero (dev->periodic_ops, p);
320 if (pool_elts (dev->periodic_ops) == 1)
321 vnet_dev_process_event_send (vm, dev, ed);
326 _vnet_dev_poll_remove (vlib_main_t *vm, vnet_dev_t *dev, void *op, void *arg)
328 const vnet_dev_event_data_t ed = { .event = VNET_DEV_EVENT_PERIODIC_STOP };
329 vnet_dev_periodic_op_t *pop;
331 pool_foreach (pop, dev->periodic_ops)
332 if (pop->op == op && pop->arg == arg)
334 pool_put (dev->periodic_ops, pop);
335 if (pool_elts (dev->periodic_ops) == 0)
336 vnet_dev_process_event_send (vm, dev, ed);
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)
346 vnet_dev_periodic_op_t pop = {
347 .interval = interval,
348 .type = VNET_DEV_PERIODIC_OP_TYPE_DEV,
353 if (_vnet_dev_poll_add (vm, dev, pop) == 0)
354 log_warn (dev, "poll_dev_add: op already exists, not added");
358 vnet_dev_poll_dev_remove (vlib_main_t *vm, vnet_dev_t *dev,
359 vnet_dev_op_no_rv_t *dev_op)
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");
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)
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,
377 if (_vnet_dev_poll_add (vm, dev, pop) == 0)
378 log_warn (dev, "poll_port_add: op already exists, not added");
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)
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");
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)
394 const vnet_dev_event_data_t ed = {
395 .event = VNET_DEV_EVENT_PORT_CONFIG_CHANGE_REQ,
402 return vnet_dev_process_event_send_and_wait (vm, port->dev, ed);
406 vnet_dev_process_call_op (vlib_main_t *vm, vnet_dev_t *dev, vnet_dev_op_t *op)
408 const vnet_dev_event_data_t ed = {
409 .event = VNET_DEV_EVENT_CALL_OP,
413 return vnet_dev_process_event_send_and_wait (vm, dev, ed);
417 vnet_dev_process_call_op_no_rv (vlib_main_t *vm, vnet_dev_t *dev,
418 vnet_dev_op_no_rv_t *op)
420 const vnet_dev_event_data_t ed = {
421 .event = VNET_DEV_EVENT_CALL_OP_NO_RV,
422 .call_op_no_rv.op = op,
425 return vnet_dev_process_event_send_and_wait (vm, dev, ed);
429 vnet_dev_process_call_op_no_wait (vlib_main_t *vm, vnet_dev_t *dev,
430 vnet_dev_op_no_rv_t *op)
432 const vnet_dev_event_data_t ed = {
433 .event = VNET_DEV_EVENT_CALL_OP_NO_WAIT,
434 .call_op_no_rv.op = op,
437 vnet_dev_process_event_send (vm, dev, ed);
441 vnet_dev_process_call_port_op (vlib_main_t *vm, vnet_dev_port_t *port,
442 vnet_dev_port_op_t *op)
444 const vnet_dev_event_data_t ed = {
445 .event = VNET_DEV_EVENT_CALL_PORT_OP,
446 .call_port_op = { .op = op, .port = port },
449 return vnet_dev_process_event_send_and_wait (vm, port->dev, ed);
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)
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 },
461 return vnet_dev_process_event_send_and_wait (vm, port->dev, ed);
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)
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 },
473 vnet_dev_process_event_send (vm, port->dev, ed);