diff options
Diffstat (limited to 'src/vnet/dev/api.c')
-rw-r--r-- | src/vnet/dev/api.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/src/vnet/dev/api.c b/src/vnet/dev/api.c new file mode 100644 index 00000000000..4d556c72367 --- /dev/null +++ b/src/vnet/dev/api.c @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#include "vppinfra/pool.h" +#include <vnet/vnet.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/dev/dev.h> +#include <vnet/dev/counters.h> +#include <vnet/dev/log.h> +#include <vnet/dev/api.h> + +VLIB_REGISTER_LOG_CLASS (dev_log, static) = { + .class_name = "dev", + .subclass_name = "api", +}; + +static int +_vnet_dev_queue_size_validate (u32 size, vnet_dev_queue_config_t c) +{ + if (size < c.min_size) + return 0; + if (size > c.max_size) + return 0; + if (c.size_is_power_of_two && count_set_bits (size) != 1) + return 0; + if (c.multiplier && size % c.multiplier) + return 0; + + return 1; +} + +vnet_dev_rv_t +vnet_dev_api_attach (vlib_main_t *vm, vnet_dev_api_attach_args_t *args) +{ + vnet_dev_main_t *dm = &vnet_dev_main; + vnet_dev_t *dev = 0; + vnet_dev_rv_t rv = VNET_DEV_OK; + vnet_dev_bus_t *bus; + vnet_dev_driver_t *driver; + void *bus_dev_info = 0; + u8 *dev_desc = 0; + + log_debug (0, "%s driver %s flags '%U' args '%v'", args->device_id, + args->driver_name, format_vnet_dev_flags, &args->flags, + args->args); + + if (vnet_dev_by_id (args->device_id)) + return VNET_DEV_ERR_ALREADY_IN_USE; + + bus = vnet_dev_find_device_bus (vm, args->device_id); + if (!bus) + { + log_err (dev, "unknown bus"); + rv = VNET_DEV_ERR_INVALID_BUS; + goto done; + } + + bus_dev_info = vnet_dev_get_device_info (vm, args->device_id); + if (!bus_dev_info) + { + log_err (dev, "invalid or unsupported device id"); + rv = VNET_DEV_ERR_INVALID_DEVICE_ID; + goto done; + } + + vec_foreach (driver, dm->drivers) + { + if (args->driver_name[0] && + strcmp (args->driver_name, driver->registration->name)) + continue; + if (driver->ops.probe && + (dev_desc = driver->ops.probe (vm, bus->index, bus_dev_info))) + break; + } + + if (!dev_desc) + { + log_err (dev, "driver not available for %s", args->device_id); + rv = VNET_DEV_ERR_DRIVER_NOT_AVAILABLE; + goto done; + } + + dev = vnet_dev_alloc (vm, args->device_id, driver); + if (!dev) + { + log_err (dev, "dev alloc failed for %s", args->device_id); + rv = VNET_DEV_ERR_BUG; + goto done; + } + dev->description = dev_desc; + + if ((args->flags.e & VNET_DEV_F_NO_STATS) == 0) + dev->poll_stats = 1; + + log_debug (0, "found '%v'", dev->description); + + rv = vnet_dev_process_call_op (vm, dev, vnet_dev_init); + +done: + if (bus_dev_info) + bus->ops.free_device_info (vm, bus_dev_info); + + if (rv != VNET_DEV_OK && dev) + vnet_dev_process_call_op_no_rv (vm, dev, vnet_dev_free); + + return rv; +} + +vnet_dev_rv_t +vnet_dev_api_detach (vlib_main_t *vm, vnet_dev_api_detach_args_t *args) +{ + vnet_dev_t *dev = vnet_dev_by_id (args->device_id); + + log_debug (dev, "detach"); + + if (dev) + return vnet_dev_process_call_op_no_rv (vm, dev, vnet_dev_detach); + + return VNET_DEV_ERR_NOT_FOUND; +} + +vnet_dev_rv_t +vnet_dev_api_reset (vlib_main_t *vm, vnet_dev_api_reset_args_t *args) +{ + vnet_dev_t *dev = vnet_dev_by_id (args->device_id); + + log_debug (dev, "detach"); + + if (!dev) + return VNET_DEV_ERR_NOT_FOUND; + + if (dev->ops.reset) + return VNET_DEV_ERR_NOT_SUPPORTED; + + return vnet_dev_process_call_op (vm, dev, vnet_dev_reset); +} + +vnet_dev_rv_t +vnet_dev_api_create_port_if (vlib_main_t *vm, + vnet_dev_api_create_port_if_args_t *args) +{ + vnet_dev_t *dev = vnet_dev_by_id (args->device_id); + vnet_dev_port_t *port = 0; + u16 n_threads = vlib_get_n_threads (); + + log_debug (dev, + "create_port_if: device '%s' port %u intf_name '%s' num_rx_q %u " + "num_tx_q %u rx_q_sz %u tx_q_sz %u, flags '%U' args '%v'", + args->device_id, args->port_id, args->intf_name, + args->num_rx_queues, args->num_tx_queues, args->rx_queue_size, + args->tx_queue_size, format_vnet_dev_port_flags, &args->flags, + args->args); + + if (dev == 0) + return VNET_DEV_ERR_NOT_FOUND; + + foreach_vnet_dev_port (p, dev) + if (p->port_id == args->port_id) + { + port = p; + break; + } + + if (!port) + return VNET_DEV_ERR_INVALID_DEVICE_ID; + + if (port->interface_created) + return VNET_DEV_ERR_ALREADY_EXISTS; + + if (args->num_rx_queues) + { + if (args->num_rx_queues > port->attr.max_rx_queues) + return VNET_DEV_ERR_INVALID_NUM_RX_QUEUES; + port->intf.num_rx_queues = args->num_rx_queues; + } + else + port->intf.num_rx_queues = clib_min (port->attr.max_tx_queues, 1); + + if (args->num_tx_queues) + { + if (args->num_tx_queues > port->attr.max_tx_queues) + return VNET_DEV_ERR_INVALID_NUM_TX_QUEUES; + port->intf.num_tx_queues = args->num_tx_queues; + } + else + port->intf.num_tx_queues = clib_min (port->attr.max_tx_queues, n_threads); + + if (args->rx_queue_size) + { + if (!_vnet_dev_queue_size_validate (args->rx_queue_size, + port->rx_queue_config)) + return VNET_DEV_ERR_INVALID_RX_QUEUE_SIZE; + port->intf.rxq_sz = args->rx_queue_size; + } + else + port->intf.rxq_sz = port->rx_queue_config.default_size; + + if (args->tx_queue_size) + { + if (!_vnet_dev_queue_size_validate (args->tx_queue_size, + port->tx_queue_config)) + return VNET_DEV_ERR_INVALID_TX_QUEUE_SIZE; + port->intf.txq_sz = args->tx_queue_size; + } + else + port->intf.txq_sz = port->tx_queue_config.default_size; + + clib_memcpy (port->intf.name, args->intf_name, sizeof (port->intf.name)); + + return vnet_dev_process_call_port_op (vm, port, vnet_dev_port_if_create); +} + +vnet_dev_rv_t +vnet_dev_api_remove_port_if (vlib_main_t *vm, + vnet_dev_api_remove_port_if_args_t *args) +{ + vnet_dev_main_t *dm = &vnet_dev_main; + vnet_main_t *vnm = vnet_get_main (); + vnet_sw_interface_t *si; + vnet_hw_interface_t *hi; + vnet_dev_port_t *port; + + si = vnet_get_sw_interface_or_null (vnm, args->sw_if_index); + if (!si) + return VNET_DEV_ERR_UNKNOWN_INTERFACE; + + hi = vnet_get_hw_interface_or_null (vnm, si->hw_if_index); + if (!hi) + return VNET_DEV_ERR_UNKNOWN_INTERFACE; + + if (pool_is_free_index (dm->ports_by_dev_instance, hi->dev_instance)) + return VNET_DEV_ERR_UNKNOWN_INTERFACE; + + port = vnet_dev_get_port_from_dev_instance (hi->dev_instance); + + if (port->intf.hw_if_index != si->hw_if_index) + return VNET_DEV_ERR_UNKNOWN_INTERFACE; + + return vnet_dev_process_call_port_op (vm, port, vnet_dev_port_if_remove); +} |