Skip to content

Commit

Permalink
dev: device and port specific args
Browse files Browse the repository at this point in the history
Type: improvement
Change-Id: I26124a50d8e05d6f01a2e6dbc4bc8183fb5a09c4
Signed-off-by: Damjan Marion <damarion@cisco.com>
  • Loading branch information
dmarion authored and ayourtch committed Nov 16, 2023
1 parent f3be34e commit 69768d9
Show file tree
Hide file tree
Showing 12 changed files with 406 additions and 7 deletions.
2 changes: 2 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
1 change: 1 addition & 0 deletions src/vnet/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions src/vnet/dev/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 "
Expand All @@ -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)
{
Expand Down
237 changes: 237 additions & 0 deletions src/vnet/dev/args.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright (c) 2023 Cisco Systems, Inc.
*/

#include "vppinfra/pool.h"
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
#include <vnet/dev/counters.h>
#include <vnet/dev/log.h>
#include <vnet/dev/types.h>
#include <vppinfra/format_table.h>

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, "<not set>");

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;
}
74 changes: 74 additions & 0 deletions src/vnet/dev/args.h
Original file line number Diff line number Diff line change
@@ -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 <vppinfra/clib.h>
#include <vnet/dev/errors.h>

#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_ */
Loading

0 comments on commit 69768d9

Please sign in to comment.