dev: new device driver infra
[vpp.git] / src / vnet / dev / port.c
1 /* 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
11 VLIB_REGISTER_LOG_CLASS (dev_log, static) = {
12   .class_name = "dev",
13   .subclass_name = "port",
14 };
15
16 static uword
17 dummy_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
24 VLIB_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
38 u16 vnet_dev_default_next_index_by_port_type[] = {
39   [VNET_DEV_PORT_TYPE_ETHERNET] = VNET_DEV_ETH_RX_PORT_NEXT_ETH_INPUT,
40 };
41
42 VNET_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
49 VNET_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
55 VNET_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
61 VNET_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
67 VNET_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
73 VNET_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
79 void
80 vnet_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
100 void
101 vnet_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
120 void
121 vnet_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
163 vnet_dev_rv_t
164 vnet_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
179 vnet_dev_rv_t
180 vnet_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
195 vnet_dev_rv_t
196 vnet_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
251 vnet_dev_rv_t
252 vnet_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
292 vnet_dev_rv_t
293 vnet_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
353 vnet_dev_rv_t
354 vnet_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
406 void
407 vnet_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
435 void
436 vnet_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
446 void
447 vnet_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
455 vnet_dev_rv_t
456 vnet_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
614 error:
615   if (rv != VNET_DEV_OK)
616     vnet_dev_port_if_remove (vm, port);
617   return rv;
618 }
619
620 vnet_dev_rv_t
621 vnet_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 }
663 void
664 vnet_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 }