blob: f9d6c010b970fa7d4cd3788c2e343c1e16f175d8 [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 <vnet/vnet.h>
6#include <vnet/ethernet/ethernet.h>
7#include <vnet/dev/dev.h>
8#include <vnet/dev/counters.h>
9#include <vnet/dev/log.h>
10
11VLIB_REGISTER_LOG_CLASS (dev_log, static) = {
12 .class_name = "dev",
13 .subclass_name = "port",
14};
15
16static uword
17dummy_input_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
18 vlib_frame_t *frame)
19{
20 ASSERT (0);
21 return 0;
22}
23
24VLIB_REGISTER_NODE (port_rx_eth_node) = {
25 .function = dummy_input_fn,
26 .name = "port-rx-eth",
27 .runtime_data_bytes = sizeof (vnet_dev_rx_node_runtime_t),
28 .type = VLIB_NODE_TYPE_INPUT,
29 .state = VLIB_NODE_STATE_DISABLED,
30 .n_next_nodes = VNET_DEV_ETH_RX_PORT_N_NEXTS,
31 .next_nodes = {
32#define _(n, s) [VNET_DEV_ETH_RX_PORT_NEXT_##n] = s,
33 foreach_vnet_dev_port_rx_next
34#undef _
35 },
36};
37
38u16 vnet_dev_default_next_index_by_port_type[] = {
39 [VNET_DEV_PORT_TYPE_ETHERNET] = VNET_DEV_ETH_RX_PORT_NEXT_ETH_INPUT,
40};
41
42VNET_FEATURE_ARC_INIT (eth_port_rx, static) = {
43 .arc_name = "port-rx-eth",
44 .start_nodes = VNET_FEATURES ("port-rx-eth"),
45 .last_in_arc = "ethernet-input",
46 .arc_index_ptr = &vnet_dev_main.eth_port_rx_feature_arc_index,
47};
48
49VNET_FEATURE_INIT (l2_patch, static) = {
50 .arc_name = "port-rx-eth",
51 .node_name = "l2-patch",
52 .runs_before = VNET_FEATURES ("ethernet-input"),
53};
54
55VNET_FEATURE_INIT (worker_handoff, static) = {
56 .arc_name = "port-rx-eth",
57 .node_name = "worker-handoff",
58 .runs_before = VNET_FEATURES ("ethernet-input"),
59};
60
61VNET_FEATURE_INIT (span_input, static) = {
62 .arc_name = "port-rx-eth",
63 .node_name = "span-input",
64 .runs_before = VNET_FEATURES ("ethernet-input"),
65};
66
67VNET_FEATURE_INIT (p2p_ethernet_node, static) = {
68 .arc_name = "port-rx-eth",
69 .node_name = "p2p-ethernet-input",
70 .runs_before = VNET_FEATURES ("ethernet-input"),
71};
72
73VNET_FEATURE_INIT (ethernet_input, static) = {
74 .arc_name = "port-rx-eth",
75 .node_name = "ethernet-input",
76 .runs_before = 0, /* not before any other features */
77};
78
79void
80vnet_dev_port_free (vlib_main_t *vm, vnet_dev_port_t *port)
81{
82 vnet_dev_t *dev = port->dev;
83
84 vnet_dev_port_validate (vm, port);
85
86 ASSERT (port->started == 0);
87
88 log_debug (dev, "port %u", port->port_id);
89
90 if (port->port_ops.free)
91 port->port_ops.free (vm, port);
92
93 pool_free (port->secondary_hw_addr);
94 pool_free (port->rx_queues);
95 pool_free (port->tx_queues);
96 pool_put_index (dev->ports, port->index);
97 clib_mem_free (port);
98}
99
100void
101vnet_dev_port_update_tx_node_runtime (vlib_main_t *vm, vnet_dev_port_t *port)
102{
103 vnet_dev_port_validate (vm, port);
104
105 foreach_vnet_dev_port_tx_queue (q, port)
106 {
107 u32 ti;
108 clib_bitmap_foreach (ti, q->assigned_threads)
109 {
110 vlib_main_t *tvm = vlib_get_main_by_index (ti);
111 vlib_node_runtime_t *nr =
112 vlib_node_get_runtime (tvm, port->intf.tx_node_index);
113 vnet_dev_tx_node_runtime_t *tnr = vnet_dev_get_tx_node_runtime (nr);
114 tnr->hw_if_index = port->intf.hw_if_index;
115 tnr->tx_queue = q;
116 }
117 }
118}
119
120void
121vnet_dev_port_stop (vlib_main_t *vm, vnet_dev_port_t *port)
122{
123 vnet_dev_t *dev = port->dev;
124 vnet_dev_rt_op_t *ops = 0;
125
126 log_debug (dev, "stopping port %u", port->port_id);
127
128 foreach_vnet_dev_port_rx_queue (q, port)
129 if (q->started)
130 {
131 vnet_dev_rt_op_t op = {
132 .type = VNET_DEV_RT_OP_TYPE_RX_QUEUE,
133 .action = VNET_DEV_RT_OP_ACTION_STOP,
134 .thread_index = q->rx_thread_index,
135 .rx_queue = q,
136 };
137 vec_add1 (ops, op);
138 }
139
140 vnet_dev_rt_exec_ops (vm, dev, ops, vec_len (ops));
141 vec_free (ops);
142
143 port->port_ops.stop (vm, port);
144
145 foreach_vnet_dev_port_rx_queue (q, port)
146 {
147 q->started = 0;
148 log_debug (dev, "port %u rx queue %u stopped", port->port_id,
149 q->queue_id);
150 }
151
152 foreach_vnet_dev_port_tx_queue (q, port)
153 {
154 q->started = 0;
155 log_debug (dev, "port %u tx queue %u stopped", port->port_id,
156 q->queue_id);
157 }
158
159 log_debug (dev, "port %u stopped", port->port_id);
160 port->started = 0;
161}
162
163vnet_dev_rv_t
164vnet_dev_port_start_all_rx_queues (vlib_main_t *vm, vnet_dev_port_t *port)
165{
166 vnet_dev_rv_t rv = VNET_DEV_OK;
167
168 vnet_dev_port_validate (vm, port);
169
170 foreach_vnet_dev_port_rx_queue (q, port)
171 {
172 rv = vnet_dev_rx_queue_start (vm, q);
173 if (rv != VNET_DEV_OK)
174 return rv;
175 }
176 return rv;
177}
178
179vnet_dev_rv_t
180vnet_dev_port_start_all_tx_queues (vlib_main_t *vm, vnet_dev_port_t *port)
181{
182 vnet_dev_rv_t rv = VNET_DEV_OK;
183
184 vnet_dev_port_validate (vm, port);
185
186 foreach_vnet_dev_port_tx_queue (q, port)
187 {
188 rv = vnet_dev_tx_queue_start (vm, q);
189 if (rv != VNET_DEV_OK)
190 return rv;
191 }
192 return rv;
193}
194
195vnet_dev_rv_t
196vnet_dev_port_start (vlib_main_t *vm, vnet_dev_port_t *port)
197{
198 vnet_dev_t *dev = port->dev;
199 vnet_dev_rt_op_t *ops = 0;
200 vnet_dev_rv_t rv;
201
202 vnet_dev_port_validate (vm, port);
203
204 log_debug (dev, "starting port %u", port->port_id);
205
206 vnet_dev_port_update_tx_node_runtime (vm, port);
207
208 if ((rv = port->port_ops.start (vm, port)) != VNET_DEV_OK)
209 {
210 vnet_dev_port_stop (vm, port);
211 return rv;
212 }
213
214 foreach_vnet_dev_port_rx_queue (q, port)
215 if (q->enabled)
216 {
217 vnet_dev_rt_op_t op = {
218 .type = VNET_DEV_RT_OP_TYPE_RX_QUEUE,
219 .action = VNET_DEV_RT_OP_ACTION_START,
220 .thread_index = q->rx_thread_index,
221 .rx_queue = q,
222 };
223 vec_add1 (ops, op);
224 }
225
226 vnet_dev_rt_exec_ops (vm, dev, ops, vec_len (ops));
227 vec_free (ops);
228
229 foreach_vnet_dev_port_rx_queue (q, port)
230 if (q->enabled)
231 {
232 log_debug (dev, "port %u rx queue %u started", port->port_id,
233 q->queue_id);
234 q->started = 1;
235 }
236
237 foreach_vnet_dev_port_tx_queue (q, port)
238 if (q->enabled)
239 {
240 log_debug (dev, "port %u tx queue %u started", port->port_id,
241 q->queue_id);
242 q->started = 1;
243 }
244
245 port->started = 1;
246 log_debug (dev, "port %u started", port->port_id);
247
248 return VNET_DEV_OK;
249}
250
251vnet_dev_rv_t
252vnet_dev_port_add (vlib_main_t *vm, vnet_dev_t *dev, vnet_dev_port_id_t id,
253 vnet_dev_port_add_args_t *args)
254{
255 vnet_dev_port_t **pp, *port;
256 vnet_dev_rv_t rv = VNET_DEV_OK;
257
258 ASSERT (args->port.attr.type != VNET_DEV_PORT_TYPE_UNKNOWN);
259 ASSERT (args->port.attr.max_supported_frame_size);
260
261 port =
262 vnet_dev_alloc_with_data (sizeof (vnet_dev_port_t), args->port.data_size);
263 pool_get (dev->ports, pp);
264 pp[0] = port;
265 clib_memcpy (vnet_dev_get_port_data (port), args->port.initial_data,
266 args->port.data_size);
267 port->port_id = id;
268 port->index = pp - dev->ports;
269 port->dev = dev;
270 port->attr = args->port.attr;
271 port->rx_queue_config = args->rx_queue.config;
272 port->tx_queue_config = args->tx_queue.config;
273 port->rx_queue_ops = args->rx_queue.ops;
274 port->tx_queue_ops = args->tx_queue.ops;
275 port->port_ops = args->port.ops;
276 port->rx_node = *args->rx_node;
277 port->tx_node = *args->tx_node;
278
279 /* defaults out of port attributes */
280 port->max_frame_size = args->port.attr.max_supported_frame_size;
281 port->primary_hw_addr = args->port.attr.hw_addr;
282
283 if (port->port_ops.alloc)
284 rv = port->port_ops.alloc (vm, port);
285
286 if (rv == VNET_DEV_OK)
287 port->initialized = 1;
288
289 return rv;
290}
291
292vnet_dev_rv_t
293vnet_dev_port_cfg_change_req_validate (vlib_main_t *vm, vnet_dev_port_t *port,
294 vnet_dev_port_cfg_change_req_t *req)
295{
296 vnet_dev_rv_t rv;
297 vnet_dev_hw_addr_t *addr;
298 int found;
299
300 if (req->validated)
301 return VNET_DEV_OK;
302
303 switch (req->type)
304 {
305 case VNET_DEV_PORT_CFG_MAX_FRAME_SIZE:
306 if (req->max_frame_size > port->attr.max_supported_frame_size)
307 return VNET_DEV_ERR_INVALID_VALUE;
308 if (req->max_frame_size == port->max_frame_size)
309 return VNET_DEV_ERR_NO_CHANGE;
310 break;
311
312 case VNET_DEV_PORT_CFG_PROMISC_MODE:
313 if (req->promisc == port->promisc)
314 return VNET_DEV_ERR_NO_CHANGE;
315 break;
316
317 case VNET_DEV_PORT_CFG_CHANGE_PRIMARY_HW_ADDR:
318 if (clib_memcmp (&req->addr, &port->primary_hw_addr,
319 sizeof (vnet_dev_hw_addr_t)) == 0)
320 return VNET_DEV_ERR_NO_CHANGE;
321 break;
322
323 case VNET_DEV_PORT_CFG_ADD_SECONDARY_HW_ADDR:
324 pool_foreach (addr, port->secondary_hw_addr)
325 if (clib_memcmp (addr, &req->addr, sizeof (*addr)) == 0)
326 return VNET_DEV_ERR_ALREADY_EXISTS;
327 break;
328
329 case VNET_DEV_PORT_CFG_REMOVE_SECONDARY_HW_ADDR:
330 found = 0;
331 pool_foreach (addr, port->secondary_hw_addr)
332 if (clib_memcmp (addr, &req->addr, sizeof (*addr)) == 0)
333 found = 1;
334 if (!found)
335 return VNET_DEV_ERR_NO_SUCH_ENTRY;
336 break;
337
338 default:
339 break;
340 }
341
342 if (port->port_ops.config_change_validate)
343 {
344 rv = port->port_ops.config_change_validate (vm, port, req);
345 if (rv != VNET_DEV_OK)
346 return rv;
347 }
348
349 req->validated = 1;
350 return VNET_DEV_OK;
351}
352
353vnet_dev_rv_t
354vnet_dev_port_cfg_change (vlib_main_t *vm, vnet_dev_port_t *port,
355 vnet_dev_port_cfg_change_req_t *req)
356{
357 vnet_dev_rv_t rv = VNET_DEV_OK;
358 vnet_dev_hw_addr_t *a;
359
360 vnet_dev_port_validate (vm, port);
361
362 vnet_dev_port_cfg_change_req_validate (vm, port, req);
363
364 if (port->port_ops.config_change)
365 rv = port->port_ops.config_change (vm, port, req);
366
367 if (rv != VNET_DEV_OK)
368 return rv;
369
370 switch (req->type)
371 {
372 case VNET_DEV_PORT_CFG_MAX_FRAME_SIZE:
373 port->max_frame_size = req->max_frame_size;
374 break;
375
376 case VNET_DEV_PORT_CFG_PROMISC_MODE:
377 port->promisc = req->promisc;
378 break;
379
380 case VNET_DEV_PORT_CFG_CHANGE_PRIMARY_HW_ADDR:
381 clib_memcpy (&port->primary_hw_addr, &req->addr,
382 sizeof (vnet_dev_hw_addr_t));
383 break;
384
385 case VNET_DEV_PORT_CFG_ADD_SECONDARY_HW_ADDR:
386 pool_get (port->secondary_hw_addr, a);
387 clib_memcpy (a, &req->addr, sizeof (vnet_dev_hw_addr_t));
388 break;
389
390 case VNET_DEV_PORT_CFG_REMOVE_SECONDARY_HW_ADDR:
391 pool_foreach (a, port->secondary_hw_addr)
392 if (clib_memcmp (a, &req->addr, sizeof (vnet_dev_hw_addr_t)) == 0)
393 {
394 pool_put (port->secondary_hw_addr, a);
395 break;
396 }
397 break;
398
399 default:
400 break;
401 }
402
403 return VNET_DEV_OK;
404}
405
406void
407vnet_dev_port_state_change (vlib_main_t *vm, vnet_dev_port_t *port,
408 vnet_dev_port_state_changes_t changes)
409{
410 vnet_main_t *vnm = vnet_get_main ();
411
412 vnet_dev_port_validate (vm, port);
413
414 if (changes.change.link_speed)
415 {
416 port->speed = changes.link_speed;
417 if (port->interface_created)
418 vnet_hw_interface_set_link_speed (vnm, port->intf.hw_if_index,
419 changes.link_speed);
420 log_debug (port->dev, "port speed changed to %u", changes.link_speed);
421 }
422
423 if (changes.change.link_state)
424 {
425 port->link_up = changes.link_state;
426 if (port->interface_created)
427 vnet_hw_interface_set_flags (
428 vnm, port->intf.hw_if_index,
429 changes.link_state ? VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
430 log_debug (port->dev, "port link state changed to %s",
431 changes.link_state ? "up" : "down");
432 }
433}
434
435void
436vnet_dev_port_add_counters (vlib_main_t *vm, vnet_dev_port_t *port,
437 vnet_dev_counter_t *counters, u16 n_counters)
438{
439 vnet_dev_port_validate (vm, port);
440
441 port->counter_main =
442 vnet_dev_counters_alloc (vm, counters, n_counters, "%s port %u counters",
443 port->dev->device_id, port->port_id);
444}
445
446void
447vnet_dev_port_free_counters (vlib_main_t *vm, vnet_dev_port_t *port)
448{
449 vnet_dev_port_validate (vm, port);
450
451 if (port->counter_main)
452 vnet_dev_counters_free (vm, port->counter_main);
453}
454
455vnet_dev_rv_t
456vnet_dev_port_if_create (vlib_main_t *vm, vnet_dev_port_t *port)
457{
458 vnet_main_t *vnm = vnet_get_main ();
459 u16 n_threads = vlib_get_n_threads ();
460 vnet_dev_main_t *dm = &vnet_dev_main;
461 vnet_dev_t *dev = port->dev;
462 vnet_dev_port_t **pp;
463 vnet_dev_rv_t rv;
464 u16 ti = 0;
465
466 if (port->intf.name[0] == 0)
467 {
468 u8 *s;
469 s = format (0, "%s%u/%u",
470 dm->drivers[port->dev->driver_index].registration->name,
471 port->dev->index, port->index);
472 u32 n = vec_len (s);
473
474 if (n >= sizeof (port->intf.name))
475 {
476 vec_free (s);
477 return VNET_DEV_ERR_BUG;
478 }
479 clib_memcpy (port->intf.name, s, n);
480 port->intf.name[n] = 0;
481 vec_free (s);
482 }
483
484 log_debug (
485 dev, "allocating %u rx queues with size %u and %u tx queues with size %u",
486 port->intf.num_rx_queues, port->intf.rxq_sz, port->intf.num_tx_queues,
487 port->intf.txq_sz);
488
489 for (int i = 0; i < port->intf.num_rx_queues; i++)
490 if ((rv = vnet_dev_rx_queue_alloc (vm, port, port->intf.rxq_sz)) !=
491 VNET_DEV_OK)
492 goto error;
493
494 for (u32 i = 0; i < port->intf.num_tx_queues; i++)
495 if ((rv = vnet_dev_tx_queue_alloc (vm, port, port->intf.txq_sz)) !=
496 VNET_DEV_OK)
497 goto error;
498
499 foreach_vnet_dev_port_tx_queue (q, port)
500 {
501 q->assigned_threads = clib_bitmap_set (q->assigned_threads, ti, 1);
502 log_debug (dev, "port %u tx queue %u assigned to thread %u",
503 port->port_id, q->queue_id, ti);
504 if (++ti >= n_threads)
505 break;
506 }
507
508 /* pool of port pointers helps us to assign unique dev_instance */
509 pool_get (dm->ports_by_dev_instance, pp);
510 port->intf.dev_instance = pp - dm->ports_by_dev_instance;
511 pp[0] = port;
512
513 if (port->attr.type == VNET_DEV_PORT_TYPE_ETHERNET)
514 {
515 vnet_device_class_t *dev_class;
516 vnet_dev_driver_t *driver;
517 vnet_sw_interface_t *sw;
518 vnet_hw_interface_t *hw;
519 u32 rx_node_index;
520
521 driver = pool_elt_at_index (dm->drivers, dev->driver_index);
522
523 /* hack to provide per-port tx node function */
524 dev_class = vnet_get_device_class (vnm, driver->dev_class_index);
525 dev_class->tx_fn_registrations = port->tx_node.registrations;
526 dev_class->format_tx_trace = port->tx_node.format_trace;
527 dev_class->tx_function_error_counters = port->tx_node.error_counters;
528 dev_class->tx_function_n_errors = port->tx_node.n_error_counters;
529
530 /* create new interface including tx and output nodes */
531 port->intf.hw_if_index = vnet_eth_register_interface (
532 vnm, &(vnet_eth_interface_registration_t){
533 .address = port->primary_hw_addr.eth_mac,
534 .max_frame_size = port->max_frame_size,
535 .dev_class_index = driver->dev_class_index,
536 .dev_instance = port->intf.dev_instance,
537 .cb.set_max_frame_size = vnet_dev_port_set_max_frame_size,
538 .cb.flag_change = vnet_dev_port_eth_flag_change,
539 });
540
541 sw = vnet_get_hw_sw_interface (vnm, port->intf.hw_if_index);
542 hw = vnet_get_hw_interface (vnm, port->intf.hw_if_index);
543 port->intf.sw_if_index = sw->sw_if_index;
544 vnet_hw_interface_set_flags (
545 vnm, port->intf.hw_if_index,
546 port->link_up ? VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
547 if (port->speed)
548 vnet_hw_interface_set_link_speed (vnm, port->intf.hw_if_index,
549 port->speed);
550
551 port->intf.tx_node_index = hw->tx_node_index;
552
553 /* create / reuse rx node */
554 if (vec_len (dm->free_rx_node_indices))
555 {
556 vlib_node_t *n;
557 rx_node_index = vec_pop (dm->free_rx_node_indices);
558 vlib_node_rename (vm, rx_node_index, "%s-rx", port->intf.name);
559 n = vlib_get_node (vm, rx_node_index);
560 n->function = vlib_node_get_preferred_node_fn_variant (
561 vm, port->rx_node.registrations);
562 n->format_trace = port->rx_node.format_trace;
563 vlib_register_errors (vm, rx_node_index,
564 port->rx_node.n_error_counters, 0,
565 port->rx_node.error_counters);
566 }
567 else
568 {
569 dev_class->format_tx_trace = port->tx_node.format_trace;
570 dev_class->tx_function_error_counters = port->tx_node.error_counters;
571 dev_class->tx_function_n_errors = port->tx_node.n_error_counters;
572 vlib_node_registration_t rx_node_reg = {
573 .sibling_of = "port-rx-eth",
574 .type = VLIB_NODE_TYPE_INPUT,
575 .state = VLIB_NODE_STATE_DISABLED,
576 .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED,
577 .node_fn_registrations = port->rx_node.registrations,
578 .format_trace = port->rx_node.format_trace,
579 .error_counters = port->rx_node.error_counters,
580 .n_errors = port->rx_node.n_error_counters,
581 };
582 rx_node_index =
583 vlib_register_node (vm, &rx_node_reg, "%s-rx", port->intf.name);
584 }
585 port->rx_node_assigned = 1;
586 port->intf.rx_node_index = rx_node_index;
587 port->intf.rx_next_index =
588 vnet_dev_default_next_index_by_port_type[port->attr.type];
589
590 vlib_worker_thread_node_runtime_update ();
591 log_debug (dev,
592 "ethernet interface created, hw_if_index %u sw_if_index %u "
593 "rx_node_index %u tx_node_index %u",
594 port->intf.hw_if_index, port->intf.sw_if_index,
595 port->intf.rx_node_index, port->intf.tx_node_index);
596 }
597
598 port->interface_created = 1;
599 foreach_vnet_dev_port_rx_queue (q, port)
600 {
601 vnet_buffer (&q->buffer_template)->sw_if_index[VLIB_RX] =
602 port->intf.sw_if_index;
603 /* poison to catch node not calling runtime update function */
604 q->next_index = ~0;
605 vnet_dev_rx_queue_rt_request (
606 vm, q, (vnet_dev_rx_queue_rt_req_t){ .update_next_index = 1 });
607 }
608
609 vnet_dev_port_update_tx_node_runtime (vm, port);
610
611 if (port->port_ops.init)
612 rv = port->port_ops.init (vm, port);
613
614error:
615 if (rv != VNET_DEV_OK)
616 vnet_dev_port_if_remove (vm, port);
617 return rv;
618}
619
620vnet_dev_rv_t
621vnet_dev_port_if_remove (vlib_main_t *vm, vnet_dev_port_t *port)
622{
623 vnet_dev_main_t *dm = &vnet_dev_main;
624 vnet_main_t *vnm = vnet_get_main ();
625
626 vnet_dev_port_validate (vm, port);
627
628 if (port->started)
629 vnet_dev_port_stop (vm, port);
630
631 if (port->rx_node_assigned)
632 {
633 vlib_node_rename (vm, port->intf.rx_node_index, "deleted-%u",
634 port->intf.rx_node_index);
635 vec_add1 (dm->free_rx_node_indices, port->intf.rx_node_index);
636 port->rx_node_assigned = 0;
637 }
638
639 if (port->interface_created)
640 {
641 vlib_worker_thread_barrier_sync (vm);
642 vnet_delete_hw_interface (vnm, port->intf.hw_if_index);
643 vlib_worker_thread_barrier_release (vm);
644 pool_put_index (dm->ports_by_dev_instance, port->intf.dev_instance);
645 port->interface_created = 0;
646 }
647
648 port->intf = (typeof (port->intf)){};
649
650 if (port->port_ops.deinit)
651 port->port_ops.deinit (vm, port);
652
653 foreach_vnet_dev_port_tx_queue (q, port)
654 vnet_dev_tx_queue_free (vm, q);
655
656 foreach_vnet_dev_port_rx_queue (q, port)
657 vnet_dev_rx_queue_free (vm, q);
658
659 vnet_dev_port_free_counters (vm, port);
660
661 return VNET_DEV_OK;
662}
663void
664vnet_dev_port_clear_counters (vlib_main_t *vm, vnet_dev_port_t *port)
665{
666 if (port->counter_main)
667 vnet_dev_counters_clear (vm, port->counter_main);
668
669 foreach_vnet_dev_port_rx_queue (q, port)
670 if (q->counter_main)
671 vnet_dev_counters_clear (vm, q->counter_main);
672
673 foreach_vnet_dev_port_tx_queue (q, port)
674 if (q->counter_main)
675 vnet_dev_counters_clear (vm, q->counter_main);
676
677 log_notice (port->dev, "counters cleared on port %u", port->port_id);
678}