blob: 114b63d6662db97a045f6607bc481540721e1815 [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/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
13VLIB_REGISTER_LOG_CLASS (dev_log, static) = {
14 .class_name = "dev",
15 .subclass_name = "api",
16};
17
18static 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
33vnet_dev_rv_t
34vnet_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
Damjan Marion07a62cc2023-11-16 19:14:12 +000093 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);
Damjan Marion69768d92023-11-13 17:33:32 +000097
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
Damjan Marion38c61912023-10-17 16:06:26 +0000105 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
112done:
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);
Damjan Marionddf6cec2023-11-22 16:25:55 +0000118 else if (dev)
119 args->dev_index = dev->index;
Damjan Marion38c61912023-10-17 16:06:26 +0000120
121 return rv;
122}
123
124vnet_dev_rv_t
125vnet_dev_api_detach (vlib_main_t *vm, vnet_dev_api_detach_args_t *args)
126{
Damjan Marionddf6cec2023-11-22 16:25:55 +0000127 vnet_dev_t *dev = vnet_dev_by_index (args->dev_index);
Damjan Marion38c61912023-10-17 16:06:26 +0000128
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
137vnet_dev_rv_t
138vnet_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
153vnet_dev_rv_t
154vnet_dev_api_create_port_if (vlib_main_t *vm,
155 vnet_dev_api_create_port_if_args_t *args)
156{
Damjan Marionddf6cec2023-11-22 16:25:55 +0000157 vnet_dev_t *dev = vnet_dev_by_index (args->dev_index);
Damjan Marion38c61912023-10-17 16:06:26 +0000158 vnet_dev_port_t *port = 0;
159 u16 n_threads = vlib_get_n_threads ();
Damjan Marionb8dd9812023-11-03 13:47:05 +0000160 int default_is_intr_mode;
Damjan Marion69768d92023-11-13 17:33:32 +0000161 vnet_dev_rv_t rv;
Damjan Marion38c61912023-10-17 16:06:26 +0000162
163 log_debug (dev,
Damjan Marionddf6cec2023-11-22 16:25:55 +0000164 "create_port_if: dev_index %u port %u intf_name '%s' num_rx_q %u "
Damjan Marion38c61912023-10-17 16:06:26 +0000165 "num_tx_q %u rx_q_sz %u tx_q_sz %u, flags '%U' args '%v'",
Damjan Marionddf6cec2023-11-22 16:25:55 +0000166 args->dev_index, args->port_id, args->intf_name,
Damjan Marion38c61912023-10-17 16:06:26 +0000167 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
Damjan Marion69768d92023-11-13 17:33:32 +0000187 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
Damjan Marionb8dd9812023-11-03 13:47:05 +0000194 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
Damjan Marion38c61912023-10-17 16:06:26 +0000201 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));
Damjan Marionb8dd9812023-11-03 13:47:05 +0000240 port->intf.default_is_intr_mode = default_is_intr_mode;
Damjan Marion38c61912023-10-17 16:06:26 +0000241
Damjan Marionddf6cec2023-11-22 16:25:55 +0000242 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;
Damjan Marion38c61912023-10-17 16:06:26 +0000246}
247
248vnet_dev_rv_t
249vnet_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}