blob: 3c1f0b8d2d8cd2e93183b933892419fb7e6c3cb1 [file] [log] [blame]
Damjan Marion38c61912023-10-17 16:06:26 +00001/* 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
10VLIB_REGISTER_LOG_CLASS (dev_log, static) = {
11 .class_name = "dev",
12 .subclass_name = "process",
13};
14
15typedef 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
30typedef 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
72static vnet_dev_rv_t
73vnet_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
130static uword
131vnet_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
215vnet_dev_rv_t
216vnet_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
261static void
262vnet_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
270static vnet_dev_rv_t
271vnet_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
300void
301vnet_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
307static 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
325static 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
342void
343vnet_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
357void
358vnet_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
365void
366vnet_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
381void
382vnet_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
390vnet_dev_rv_t
391vnet_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
405vnet_dev_rv_t
406vnet_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
416vnet_dev_rv_t
417vnet_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
428void
429vnet_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
440vnet_dev_rv_t
441vnet_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
452vnet_dev_rv_t
453vnet_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
464void
465vnet_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}