dev: initial set of APIs
[vpp.git] / src / vnet / dev / api.c
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright (c) 2023 Cisco Systems, Inc.
3  */
4
5 #include "vppinfra/pool.h"
6 #include <vnet/vnet.h>
7 #include <vnet/ethernet/ethernet.h>
8 #include <vnet/dev/dev.h>
9 #include <vnet/dev/counters.h>
10 #include <vnet/dev/log.h>
11 #include <vnet/dev/api.h>
12
13 VLIB_REGISTER_LOG_CLASS (dev_log, static) = {
14   .class_name = "dev",
15   .subclass_name = "api",
16 };
17
18 static int
19 _vnet_dev_queue_size_validate (u32 size, vnet_dev_queue_config_t c)
20 {
21   if (size < c.min_size)
22     return 0;
23   if (size > c.max_size)
24     return 0;
25   if (c.size_is_power_of_two && count_set_bits (size) != 1)
26     return 0;
27   if (c.multiplier && size % c.multiplier)
28     return 0;
29
30   return 1;
31 }
32
33 vnet_dev_rv_t
34 vnet_dev_api_attach (vlib_main_t *vm, vnet_dev_api_attach_args_t *args)
35 {
36   vnet_dev_main_t *dm = &vnet_dev_main;
37   vnet_dev_t *dev = 0;
38   vnet_dev_rv_t rv = VNET_DEV_OK;
39   vnet_dev_bus_t *bus;
40   vnet_dev_driver_t *driver;
41   void *bus_dev_info = 0;
42   u8 *dev_desc = 0;
43
44   log_debug (0, "%s driver %s flags '%U' args '%v'", args->device_id,
45              args->driver_name, format_vnet_dev_flags, &args->flags,
46              args->args);
47
48   if (vnet_dev_by_id (args->device_id))
49     return VNET_DEV_ERR_ALREADY_IN_USE;
50
51   bus = vnet_dev_find_device_bus (vm, args->device_id);
52   if (!bus)
53     {
54       log_err (dev, "unknown bus");
55       rv = VNET_DEV_ERR_INVALID_BUS;
56       goto done;
57     }
58
59   bus_dev_info = vnet_dev_get_device_info (vm, args->device_id);
60   if (!bus_dev_info)
61     {
62       log_err (dev, "invalid or unsupported device id");
63       rv = VNET_DEV_ERR_INVALID_DEVICE_ID;
64       goto done;
65     }
66
67   vec_foreach (driver, dm->drivers)
68     {
69       if (args->driver_name[0] &&
70           strcmp (args->driver_name, driver->registration->name))
71         continue;
72       if (driver->ops.probe &&
73           (dev_desc = driver->ops.probe (vm, bus->index, bus_dev_info)))
74         break;
75     }
76
77   if (!dev_desc)
78     {
79       log_err (dev, "driver not available for %s", args->device_id);
80       rv = VNET_DEV_ERR_DRIVER_NOT_AVAILABLE;
81       goto done;
82     }
83
84   dev = vnet_dev_alloc (vm, args->device_id, driver);
85   if (!dev)
86     {
87       log_err (dev, "dev alloc failed for %s", args->device_id);
88       rv = VNET_DEV_ERR_BUG;
89       goto done;
90     }
91   dev->description = dev_desc;
92
93   if (driver->registration->args)
94     for (vnet_dev_arg_t *a = driver->registration->args;
95          a->type != VNET_DEV_ARG_END; a++)
96       vec_add1 (dev->args, *a);
97
98   if (args->args)
99     {
100       if ((rv = vnet_dev_arg_parse (vm, dev, dev->args, args->args)) !=
101           VNET_DEV_OK)
102         goto done;
103     }
104
105   if ((args->flags.e & VNET_DEV_F_NO_STATS) == 0)
106     dev->poll_stats = 1;
107
108   log_debug (0, "found '%v'", dev->description);
109
110   rv = vnet_dev_process_call_op (vm, dev, vnet_dev_init);
111
112 done:
113   if (bus_dev_info)
114     bus->ops.free_device_info (vm, bus_dev_info);
115
116   if (rv != VNET_DEV_OK && dev)
117     vnet_dev_process_call_op_no_rv (vm, dev, vnet_dev_free);
118   else if (dev)
119     args->dev_index = dev->index;
120
121   return rv;
122 }
123
124 vnet_dev_rv_t
125 vnet_dev_api_detach (vlib_main_t *vm, vnet_dev_api_detach_args_t *args)
126 {
127   vnet_dev_t *dev = vnet_dev_by_index (args->dev_index);
128
129   log_debug (dev, "detach");
130
131   if (dev)
132     return vnet_dev_process_call_op_no_rv (vm, dev, vnet_dev_detach);
133
134   return VNET_DEV_ERR_NOT_FOUND;
135 }
136
137 vnet_dev_rv_t
138 vnet_dev_api_reset (vlib_main_t *vm, vnet_dev_api_reset_args_t *args)
139 {
140   vnet_dev_t *dev = vnet_dev_by_id (args->device_id);
141
142   log_debug (dev, "detach");
143
144   if (!dev)
145     return VNET_DEV_ERR_NOT_FOUND;
146
147   if (dev->ops.reset)
148     return VNET_DEV_ERR_NOT_SUPPORTED;
149
150   return vnet_dev_process_call_op (vm, dev, vnet_dev_reset);
151 }
152
153 vnet_dev_rv_t
154 vnet_dev_api_create_port_if (vlib_main_t *vm,
155                              vnet_dev_api_create_port_if_args_t *args)
156 {
157   vnet_dev_t *dev = vnet_dev_by_index (args->dev_index);
158   vnet_dev_port_t *port = 0;
159   u16 n_threads = vlib_get_n_threads ();
160   int default_is_intr_mode;
161   vnet_dev_rv_t rv;
162
163   log_debug (dev,
164              "create_port_if: dev_index %u port %u intf_name '%s' num_rx_q %u "
165              "num_tx_q %u rx_q_sz %u tx_q_sz %u, flags '%U' args '%v'",
166              args->dev_index, args->port_id, args->intf_name,
167              args->num_rx_queues, args->num_tx_queues, args->rx_queue_size,
168              args->tx_queue_size, format_vnet_dev_port_flags, &args->flags,
169              args->args);
170
171   if (dev == 0)
172     return VNET_DEV_ERR_NOT_FOUND;
173
174   foreach_vnet_dev_port (p, dev)
175     if (p->port_id == args->port_id)
176       {
177         port = p;
178         break;
179       }
180
181   if (!port)
182     return VNET_DEV_ERR_INVALID_DEVICE_ID;
183
184   if (port->interface_created)
185     return VNET_DEV_ERR_ALREADY_EXISTS;
186
187   if (args->args)
188     {
189       rv = vnet_dev_arg_parse (vm, dev, port->args, args->args);
190       if (rv != VNET_DEV_OK)
191         return rv;
192     }
193
194   default_is_intr_mode = (args->flags.e & VNET_DEV_PORT_F_INTERRUPT_MODE) != 0;
195   if (default_is_intr_mode && port->attr.caps.interrupt_mode == 0)
196     {
197       log_err (dev, "interrupt mode requested and port doesn't support it");
198       return VNET_DEV_ERR_NOT_SUPPORTED;
199     }
200
201   if (args->num_rx_queues)
202     {
203       if (args->num_rx_queues > port->attr.max_rx_queues)
204         return VNET_DEV_ERR_INVALID_NUM_RX_QUEUES;
205       port->intf.num_rx_queues = args->num_rx_queues;
206     }
207   else
208     port->intf.num_rx_queues = clib_min (port->attr.max_tx_queues, 1);
209
210   if (args->num_tx_queues)
211     {
212       if (args->num_tx_queues > port->attr.max_tx_queues)
213         return VNET_DEV_ERR_INVALID_NUM_TX_QUEUES;
214       port->intf.num_tx_queues = args->num_tx_queues;
215     }
216   else
217     port->intf.num_tx_queues = clib_min (port->attr.max_tx_queues, n_threads);
218
219   if (args->rx_queue_size)
220     {
221       if (!_vnet_dev_queue_size_validate (args->rx_queue_size,
222                                           port->rx_queue_config))
223         return VNET_DEV_ERR_INVALID_RX_QUEUE_SIZE;
224       port->intf.rxq_sz = args->rx_queue_size;
225     }
226   else
227     port->intf.rxq_sz = port->rx_queue_config.default_size;
228
229   if (args->tx_queue_size)
230     {
231       if (!_vnet_dev_queue_size_validate (args->tx_queue_size,
232                                           port->tx_queue_config))
233         return VNET_DEV_ERR_INVALID_TX_QUEUE_SIZE;
234       port->intf.txq_sz = args->tx_queue_size;
235     }
236   else
237     port->intf.txq_sz = port->tx_queue_config.default_size;
238
239   clib_memcpy (port->intf.name, args->intf_name, sizeof (port->intf.name));
240   port->intf.default_is_intr_mode = default_is_intr_mode;
241
242   rv = vnet_dev_process_call_port_op (vm, port, vnet_dev_port_if_create);
243   args->sw_if_index = (rv == VNET_DEV_OK) ? port->intf.sw_if_index : ~0;
244
245   return rv;
246 }
247
248 vnet_dev_rv_t
249 vnet_dev_api_remove_port_if (vlib_main_t *vm,
250                              vnet_dev_api_remove_port_if_args_t *args)
251 {
252   vnet_dev_main_t *dm = &vnet_dev_main;
253   vnet_main_t *vnm = vnet_get_main ();
254   vnet_sw_interface_t *si;
255   vnet_hw_interface_t *hi;
256   vnet_dev_port_t *port;
257
258   si = vnet_get_sw_interface_or_null (vnm, args->sw_if_index);
259   if (!si)
260     return VNET_DEV_ERR_UNKNOWN_INTERFACE;
261
262   hi = vnet_get_hw_interface_or_null (vnm, si->hw_if_index);
263   if (!hi)
264     return VNET_DEV_ERR_UNKNOWN_INTERFACE;
265
266   if (pool_is_free_index (dm->ports_by_dev_instance, hi->dev_instance))
267     return VNET_DEV_ERR_UNKNOWN_INTERFACE;
268
269   port = vnet_dev_get_port_from_dev_instance (hi->dev_instance);
270
271   if (port->intf.hw_if_index != si->hw_if_index)
272     return VNET_DEV_ERR_UNKNOWN_INTERFACE;
273
274   return vnet_dev_process_call_port_op (vm, port, vnet_dev_port_if_remove);
275 }