From 69768d99eed0f44f6955370cb1ad83b8b73e5368 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Mon, 13 Nov 2023 17:33:32 +0000 Subject: dev: device and port specific args Type: improvement Change-Id: I26124a50d8e05d6f01a2e6dbc4bc8183fb5a09c4 Signed-off-by: Damjan Marion --- .clang-format | 2 + src/vnet/CMakeLists.txt | 1 + src/vnet/dev/api.c | 19 ++++ src/vnet/dev/args.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++ src/vnet/dev/args.h | 74 +++++++++++++++ src/vnet/dev/config.c | 8 +- src/vnet/dev/dev.c | 1 + src/vnet/dev/dev.h | 14 +++ src/vnet/dev/dev_funcs.h | 30 ++++++ src/vnet/dev/errors.h | 1 + src/vnet/dev/format.c | 19 ++-- src/vnet/dev/port.c | 7 ++ 12 files changed, 406 insertions(+), 7 deletions(-) create mode 100644 src/vnet/dev/args.c create mode 100644 src/vnet/dev/args.h diff --git a/.clang-format b/.clang-format index 62791fad06c..617c5db417f 100644 --- a/.clang-format +++ b/.clang-format @@ -32,6 +32,8 @@ ForEachMacros: - 'foreach_vnet_dev_port_rx_queue' - 'foreach_vnet_dev_port_tx_queue' - 'foreach_vnet_dev_port' + - 'foreach_vnet_dev_args' + - 'foreach_vnet_dev_port_args' StatementMacros: - 'CLIB_MULTIARCH_FN' diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt index 5236d7e67b5..1e4bb155336 100644 --- a/src/vnet/CMakeLists.txt +++ b/src/vnet/CMakeLists.txt @@ -27,6 +27,7 @@ list(APPEND VNET_SOURCES devices/devices.c devices/netlink.c dev/api.c + dev/args.c dev/cli.c dev/config.c dev/counters.c diff --git a/src/vnet/dev/api.c b/src/vnet/dev/api.c index 73d315db519..260bc7b2e22 100644 --- a/src/vnet/dev/api.c +++ b/src/vnet/dev/api.c @@ -90,6 +90,17 @@ vnet_dev_api_attach (vlib_main_t *vm, vnet_dev_api_attach_args_t *args) } dev->description = dev_desc; + for (vnet_dev_arg_t *a = driver->registration->args; + a->type != VNET_DEV_ARG_END; a++) + vec_add1 (dev->args, *a); + + if (args->args) + { + if ((rv = vnet_dev_arg_parse (vm, dev, dev->args, args->args)) != + VNET_DEV_OK) + goto done; + } + if ((args->flags.e & VNET_DEV_F_NO_STATS) == 0) dev->poll_stats = 1; @@ -144,6 +155,7 @@ vnet_dev_api_create_port_if (vlib_main_t *vm, vnet_dev_port_t *port = 0; u16 n_threads = vlib_get_n_threads (); int default_is_intr_mode; + vnet_dev_rv_t rv; log_debug (dev, "create_port_if: device '%s' port %u intf_name '%s' num_rx_q %u " @@ -169,6 +181,13 @@ vnet_dev_api_create_port_if (vlib_main_t *vm, if (port->interface_created) return VNET_DEV_ERR_ALREADY_EXISTS; + if (args->args) + { + rv = vnet_dev_arg_parse (vm, dev, port->args, args->args); + if (rv != VNET_DEV_OK) + return rv; + } + default_is_intr_mode = (args->flags.e & VNET_DEV_PORT_F_INTERRUPT_MODE) != 0; if (default_is_intr_mode && port->attr.caps.interrupt_mode == 0) { diff --git a/src/vnet/dev/args.c b/src/vnet/dev/args.c new file mode 100644 index 00000000000..e302517cc61 --- /dev/null +++ b/src/vnet/dev/args.c @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#include "vppinfra/pool.h" +#include +#include +#include +#include +#include +#include + +VLIB_REGISTER_LOG_CLASS (dev_log, static) = { + .class_name = "dev", + .subclass_name = "args", +}; + +void +vnet_dev_arg_clear_value (vnet_dev_arg_t *a) +{ + if (a->type == VNET_DEV_ARG_TYPE_STRING) + vec_free (a->val.string); + a->val = (typeof (a->val)){}; + a->val_set = 0; +} + +void +vnet_dev_arg_free (vnet_dev_arg_t **vp) +{ + vnet_dev_arg_t *v; + vec_foreach (v, *vp) + vnet_dev_arg_clear_value (v); + vec_free (*vp); +} + +vnet_dev_rv_t +vnet_dev_arg_parse (vlib_main_t *vm, vnet_dev_t *dev, vnet_dev_arg_t *args, + u8 *str) +{ + vnet_dev_rv_t rv = VNET_DEV_OK; + unformat_input_t in; + u8 *name = 0; + u8 *err = 0; + + log_debug (dev, "input '%v'", str); + if (args == 0) + return rv; + + unformat_init_string (&in, (char *) str, vec_len (str)); + + while (unformat (&in, "%U=", unformat_token, "a-zA-Z0-9_", &name)) + { + vnet_dev_arg_t *a = args; + vec_add1 (name, 0); + while (a < vec_end (args)) + if (strcmp (a->name, (char *) name) == 0) + break; + else + a++; + + if (a->type == VNET_DEV_ARG_TYPE_BOOL) + { + + if (unformat (&in, "true") || unformat (&in, "1") || + unformat (&in, "on") || unformat (&in, "yes")) + a->val.boolean = 1; + else if (unformat (&in, "false") || unformat (&in, "0") || + unformat (&in, "off") || unformat (&in, "no")) + a->val.boolean = 0; + else + { + log_err (dev, "unable to parse args: %U", format_unformat_error, + &in); + err = format ( + 0, + "boolean value expected ('yes', 'no', '0', '1', 'on', " + "'off', 'true' or 'false') for argument '%s', found '%U'", + a->name, format_unformat_error, &in); + goto done; + } + } + else if (a->type == VNET_DEV_ARG_TYPE_UINT32) + { + u32 val, min = 0, max = CLIB_U32_MAX; + if (!unformat (&in, "%u", &val)) + { + err = format (0, + "unsigned integer in range %u - %u expected for " + "argument '%s', found '%U'", + min, max, a->name, format_unformat_error, &in); + goto done; + } + + if (a->min || a->max) + { + min = a->min; + max = a->max; + } + + if (val < min || val > max) + { + err = format (0, + "unsigned integer in range %u - %u expected for " + "argument '%s', found '%u'", + min, max, a->name, val); + goto done; + } + a->val.uint32 = val; + } + else if (a->type == VNET_DEV_ARG_TYPE_STRING) + { + if (!unformat (&in, "%U", unformat_double_quoted_string, + &a->val.string)) + { + err = format ( + 0, + "double quoted string expected for argument '%s', found '%U'", + a->name, format_unformat_error, &in); + goto done; + } + + if (a->min && vec_len (a->val.string) < a->min) + { + err = + format (0, "string '%v' too short, must be at least %u chars", + a->val.string, a->min); + goto done; + } + if (a->max && vec_len (a->val.string) > a->max) + { + err = format ( + 0, "string '%v' too long, must be no longer than %u chars", + a->val.string, a->max); + goto done; + } + } + else + { + err = format (0, "unknown argument '%s'", name); + goto done; + } + + a->val_set = 1; + log_debug (dev, "name '%s' type %U value %U", name, + format_vnet_dev_arg_type, a->type, format_vnet_dev_arg_value, + a->type, &a->val); + vec_free (name); + unformat (&in, ","); + } + + if (unformat_check_input (&in) != UNFORMAT_END_OF_INPUT) + err = format (0, "unable to parse argument name '%U'", + format_unformat_error, &in); + +done: + if (err) + { + vnet_dev_arg_t *a = 0; + log_err (dev, "%v", err); + vec_free (err); + vec_foreach (a, args) + vnet_dev_arg_clear_value (a); + rv = VNET_DEV_ERR_INVALID_ARG; + } + + vec_free (name); + unformat_free (&in); + return rv; +} + +u8 * +format_vnet_dev_arg_type (u8 *s, va_list *args) +{ + vnet_dev_arg_type_t t = va_arg (*args, u32); + switch (t) + { +#define _(n, f, val) \ + case VNET_DEV_ARG_TYPE_##n: \ + return format (s, #n); + foreach_vnet_dev_arg_type +#undef _ + default : ASSERT (0); + break; + } + return s; +} + +u8 * +format_vnet_dev_arg_value (u8 *s, va_list *args) +{ + vnet_dev_arg_type_t t = va_arg (*args, u32); + vnet_dev_arg_value_t *v = va_arg (*args, vnet_dev_arg_value_t *); + + switch (t) + { +#define _(n, f, value) \ + case VNET_DEV_ARG_TYPE_##n: \ + s = format (s, f, v->value); \ + break; + foreach_vnet_dev_arg_type +#undef _ + default : break; + } + return s; +} + +u8 * +format_vnet_dev_args (u8 *s, va_list *va) +{ + vnet_dev_arg_t *a, *args = va_arg (*va, vnet_dev_arg_t *); + table_t t = { .no_ansi = 1 }; + + table_add_header_col (&t, 4, "Name", "Value", "Default", "Description"); + table_set_cell_align (&t, -1, 0, TTAA_LEFT); + table_set_cell_align (&t, -1, 3, TTAA_LEFT); + vec_foreach (a, args) + { + int r = a - args; + table_format_cell (&t, r, 0, "%s", a->name); + if (a->val_set) + table_format_cell (&t, r, 1, "%U", format_vnet_dev_arg_value, a->type, + &a->val); + else + table_format_cell (&t, r, 1, ""); + + table_format_cell (&t, r, 2, "%U", format_vnet_dev_arg_value, a->type, + &a->default_val); + table_format_cell (&t, r, 3, "%s", a->desc); + table_set_cell_align (&t, r, 0, TTAA_LEFT); + table_set_cell_align (&t, r, 3, TTAA_LEFT); + } + + s = format (s, "%U", format_table, &t); + + table_free (&t); + return s; +} diff --git a/src/vnet/dev/args.h b/src/vnet/dev/args.h new file mode 100644 index 00000000000..0c49d1fcfe2 --- /dev/null +++ b/src/vnet/dev/args.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#ifndef _VNET_DEV_ARGS_H_ +#define _VNET_DEV_ARGS_H_ + +#include +#include + +#define foreach_vnet_dev_arg_type \ + _ (BOOL, "%u", boolean) \ + _ (UINT32, "%u", uint32) \ + _ (STRING, "\'%v\'", string) + +typedef enum +{ + VNET_DEV_ARG_END, +#define _(n, f, v) VNET_DEV_ARG_TYPE_##n, + foreach_vnet_dev_arg_type +#undef _ +} __clib_packed vnet_dev_arg_type_t; + +typedef union +{ + u8 boolean; + u8 uint32; + u8 *string; +} vnet_dev_arg_value_t; + +typedef struct +{ + char *name; + char *desc; + vnet_dev_arg_type_t type; + u8 val_set; + u32 min; + u32 max; + u64 id; + vnet_dev_arg_value_t val; + vnet_dev_arg_value_t default_val; +} vnet_dev_arg_t; + +#define VNET_DEV_ARG_BOOL(ud, n, d, ...) \ + { \ + .type = VNET_DEV_ARG_TYPE_BOOL, .id = ud, .name = n, .desc = d, \ + __VA_ARGS__ \ + } +#define VNET_DEV_ARG_UINT32(ud, n, d, ...) \ + { \ + .type = VNET_DEV_ARG_TYPE_UINT32, .id = ud, .name = n, .desc = d, \ + __VA_ARGS__ \ + } +#define VNET_DEV_ARG_STRING(ud, n, d, ...) \ + { \ + .type = VNET_DEV_ARG_TYPE_STRING, .id = ud, .name = n, .desc = d, \ + __VA_ARGS__ \ + } +#define VNET_DEV_ARG_END() \ + { \ + .type = VNET_DEV_ARG_END \ + } + +#define VNET_DEV_ARGS(...) \ + (vnet_dev_arg_t[]) { __VA_ARGS__, VNET_DEV_ARG_END () } + +#define foreach_vnet_dev_args(a, d) \ + for (typeof ((d)->args[0]) *(a) = (d)->args; (a) < vec_end ((d)->args); \ + (a)++) +#define foreach_vnet_dev_port_args(a, p) \ + for (typeof ((p)->args[0]) *(a) = (p)->args; (a) < vec_end ((p)->args); \ + (a)++) + +#endif /* _VNET_DEV_ARGS_H_ */ diff --git a/src/vnet/dev/config.c b/src/vnet/dev/config.c index 091d83ecfec..3026eaadca7 100644 --- a/src/vnet/dev/config.c +++ b/src/vnet/dev/config.c @@ -41,6 +41,9 @@ vnet_dev_config_one_interface (vlib_main_t *vm, unformat_input_t *input, else if (unformat (input, "flags %U", unformat_vnet_dev_port_flags, &args->flags)) ; + else if (unformat (input, "args %U", unformat_single_quoted_string, + &args->args)) + ; else { err = clib_error_return (0, "unknown input '%U'", @@ -50,7 +53,6 @@ vnet_dev_config_one_interface (vlib_main_t *vm, unformat_input_t *input, } return err; } - static clib_error_t * vnet_dev_config_one_device (vlib_main_t *vm, unformat_input_t *input, char *device_id) @@ -71,6 +73,9 @@ vnet_dev_config_one_device (vlib_main_t *vm, unformat_input_t *input, else if (unformat (input, "flags %U", unformat_vnet_dev_flags, &args.flags)) ; + else if (unformat (input, "args %U", unformat_single_quoted_string, + &args.args)) + ; else if (unformat (input, "port %u %U", &n, unformat_vlib_cli_sub_input, &sub_input)) { @@ -96,6 +101,7 @@ vnet_dev_config_one_device (vlib_main_t *vm, unformat_input_t *input, clib_memcpy (args.device_id, device_id, sizeof (args.device_id)); rv = vnet_dev_api_attach (vm, &args); + vec_free (args.args); if (rv == VNET_DEV_OK) { diff --git a/src/vnet/dev/dev.c b/src/vnet/dev/dev.c index 9b99fe432a4..0e04e9ab64b 100644 --- a/src/vnet/dev/dev.c +++ b/src/vnet/dev/dev.c @@ -160,6 +160,7 @@ vnet_dev_free (vlib_main_t *vm, vnet_dev_t *dev) pool_free (dev->ports); pool_free (dev->periodic_ops); hash_unset (dm->device_index_by_id, dev->device_id); + vnet_dev_arg_free (&dev->args); pool_put_index (dm->devices, dev->index); } diff --git a/src/vnet/dev/dev.h b/src/vnet/dev/dev.h index e3421b91978..c18d29a8d5b 100644 --- a/src/vnet/dev/dev.h +++ b/src/vnet/dev/dev.h @@ -10,6 +10,7 @@ #include #include #include +#include #define VNET_DEV_DEVICE_ID_PREFIX_DELIMITER "/" @@ -287,6 +288,7 @@ typedef struct vnet_dev_port vnet_dev_rx_queue_t **rx_queues; vnet_dev_tx_queue_t **tx_queues; vnet_dev_port_ops_t port_ops; + vnet_dev_arg_t *args; vnet_dev_rx_queue_ops_t rx_queue_ops; vnet_dev_tx_queue_ops_t tx_queue_ops; vnet_dev_node_t rx_node; @@ -338,6 +340,7 @@ typedef struct vnet_dev vnet_dev_port_t **ports; vnet_dev_periodic_op_t *periodic_ops; u8 *description; + vnet_dev_arg_t *args; u8 __clib_aligned (16) data[]; } vnet_dev_t; @@ -386,6 +389,7 @@ struct vnet_dev_driver_registration vnet_dev_match_t *match; int priority; vnet_dev_ops_t ops; + vnet_dev_arg_t *args; }; typedef struct @@ -432,6 +436,7 @@ typedef struct { vnet_dev_port_attr_t attr; vnet_dev_port_ops_t ops; + vnet_dev_arg_t *args; u16 data_size; void *initial_data; } port; @@ -469,6 +474,15 @@ typedef struct u32 link_speed; } vnet_dev_port_state_changes_t; +/* args.c */ +vnet_dev_rv_t vnet_dev_arg_parse (vlib_main_t *, vnet_dev_t *, + vnet_dev_arg_t *, u8 *); +void vnet_dev_arg_free (vnet_dev_arg_t **); +void vnet_dev_arg_clear_value (vnet_dev_arg_t *); +format_function_t format_vnet_dev_arg_type; +format_function_t format_vnet_dev_arg_value; +format_function_t format_vnet_dev_args; + /* dev.c */ vnet_dev_t *vnet_dev_alloc (vlib_main_t *, vnet_dev_device_id_t, vnet_dev_driver_t *); diff --git a/src/vnet/dev/dev_funcs.h b/src/vnet/dev/dev_funcs.h index 33159ffa65d..a74d3399511 100644 --- a/src/vnet/dev/dev_funcs.h +++ b/src/vnet/dev/dev_funcs.h @@ -289,4 +289,34 @@ vnet_dev_set_hw_addr_eth_mac (vnet_dev_hw_addr_t *addr, const u8 *eth_mac_addr) *addr = ha; } +static_always_inline vnet_dev_arg_t * +vnet_dev_get_port_arg_by_id (vnet_dev_port_t *port, u32 id) +{ + foreach_vnet_dev_port_args (a, port) + if (a->id == id) + return a; + return 0; +} + +static_always_inline int +vnet_dev_arg_get_bool (vnet_dev_arg_t *arg) +{ + ASSERT (arg->type == VNET_DEV_ARG_TYPE_BOOL); + return arg->val_set ? arg->val.boolean : arg->default_val.boolean; +} + +static_always_inline u32 +vnet_dev_arg_get_uint32 (vnet_dev_arg_t *arg) +{ + ASSERT (arg->type == VNET_DEV_ARG_TYPE_UINT32); + return arg->val_set ? arg->val.uint32 : arg->default_val.uint32; +} + +static_always_inline u8 * +vnet_dev_arg_get_string (vnet_dev_arg_t *arg) +{ + ASSERT (arg->type == VNET_DEV_ARG_TYPE_STRING); + return arg->val_set ? arg->val.string : arg->default_val.string; +} + #endif /* _VNET_DEV_FUNCS_H_ */ diff --git a/src/vnet/dev/errors.h b/src/vnet/dev/errors.h index 2256e1e4203..1f45ce2ba97 100644 --- a/src/vnet/dev/errors.h +++ b/src/vnet/dev/errors.h @@ -14,6 +14,7 @@ _ (DEVICE_NO_REPLY, "no reply from device") \ _ (DMA_MEM_ALLOC_FAIL, "DMA memory allocation error") \ _ (DRIVER_NOT_AVAILABLE, "driver not available") \ + _ (INVALID_ARG, "invalid argument") \ _ (INVALID_BUS, "invalid bus") \ _ (INVALID_DATA, "invalid data") \ _ (INVALID_DEVICE_ID, "invalid device id") \ diff --git a/src/vnet/dev/format.c b/src/vnet/dev/format.c index 8816c0e552c..944da0698ea 100644 --- a/src/vnet/dev/format.c +++ b/src/vnet/dev/format.c @@ -2,9 +2,6 @@ * Copyright (c) 2023 Cisco Systems, Inc. */ -#include "vlib/pci/pci.h" -#include "vnet/dev/counters.h" -#include "vppinfra/error.h" #include #include #include @@ -75,9 +72,14 @@ format_vnet_dev_info (u8 *s, va_list *args) s = format (s, "\n%UAssigned process node is '%U'", format_white_space, indent, format_vlib_node_name, vm, dev->process_node_index); + if (dev->args) + s = format (s, "\n%UDevice Specific Arguments:\n%U%U", format_white_space, + indent, format_white_space, indent + 2, format_vnet_dev_args, + dev->args); if (dev->ops.format_info) - s = format (s, "\n%U%U", format_white_space, indent, dev->ops.format_info, - a, dev); + s = + format (s, "\n%UDevice Specific Info:\n%U%U", format_white_space, indent, + format_white_space, indent + 2, dev->ops.format_info, a, dev); return s; } @@ -121,8 +123,13 @@ format_vnet_dev_port_info (u8 *s, va_list *args) format_white_space, indent, port->max_rx_frame_size, port->attr.max_supported_rx_frame_size); if (port->port_ops.format_status) - s = format (s, "\n%U%U", format_white_space, indent, + s = format (s, "\n%UDevice Specific Port Status:\n%U%U", + format_white_space, indent, format_white_space, indent + 2, port->port_ops.format_status, a, port); + if (port->args) + s = format (s, "\n%UDevice Specific Port Arguments:\n%U%U", + format_white_space, indent, format_white_space, indent + 2, + format_vnet_dev_args, port->args); s = format (s, "\n%UInterface ", format_white_space, indent); if (port->interface_created) diff --git a/src/vnet/dev/port.c b/src/vnet/dev/port.c index 350c1d84c73..b3f40354c7a 100644 --- a/src/vnet/dev/port.c +++ b/src/vnet/dev/port.c @@ -93,6 +93,7 @@ vnet_dev_port_free (vlib_main_t *vm, vnet_dev_port_t *port) pool_free (port->secondary_hw_addr); pool_free (port->rx_queues); pool_free (port->tx_queues); + vnet_dev_arg_free (&port->args); pool_put_index (dev->ports, port->index); clib_mem_free (port); } @@ -266,6 +267,9 @@ vnet_dev_port_add (vlib_main_t *vm, vnet_dev_t *dev, vnet_dev_port_id_t id, port->rx_node = *args->rx_node; port->tx_node = *args->tx_node; + for (vnet_dev_arg_t *a = args->port.args; a->type != VNET_DEV_ARG_END; a++) + vec_add1 (port->args, *a); + /* defaults out of port attributes */ port->max_rx_frame_size = args->port.attr.max_supported_rx_frame_size; port->primary_hw_addr = args->port.attr.hw_addr; @@ -711,6 +715,9 @@ vnet_dev_port_if_remove (vlib_main_t *vm, vnet_dev_port_t *port) vnet_dev_port_free_counters (vm, port); + foreach_vnet_dev_port_args (v, port) + vnet_dev_arg_clear_value (v); + return VNET_DEV_OK; } void -- cgit 1.2.3-korg