Merge branch 'devlink-port-mgmt' into next

Parav Pandit  says:

====================

This patchset implements devlink port add, delete and function state
management commands.

An example sequence for a PCI SF:

Set the device in switchdev mode:
$ devlink dev eswitch set pci/0000:06:00.0 mode switchdev

View ports in switchdev mode:
$ devlink port show
pci/0000:06:00.0/65535: type eth netdev ens2f0np0 flavour physical port 0 s=
plittable false

Add a subfunction port for PCI PF 0 with sfnumber 88:
$ devlink port add pci/0000:06:00.0 flavour pcisf pfnum 0 sfnum 88
pci/0000:08:00.0/32768: type eth netdev eth6 flavour pcisf controller 0 pfn=
um 0 sfnum 88 splittable false
  function:
    hw_addr 00:00:00:00:00:00 state inactive opstate detached

Show a newly added port:
$ devlink port show pci/0000:06:00.0/32768
pci/0000:06:00.0/32768: type eth netdev ens2f0npf0sf88 flavour pcisf contro=
ller 0 pfnum 0 sfnum 88 splittable false
  function:
    hw_addr 00:00:00:00:00:00 state inactive opstate detached

Set the function state to active:
$ devlink port function set pci/0000:06:00.0/32768 hw_addr 00:00:00:00:88:8=
8 state active

Show the port in JSON format:
$ devlink port show pci/0000:06:00.0/32768 -jp
{
    "port": {
        "pci/0000:06:00.0/32768": {
            "type": "eth",
            "netdev": "ens2f0npf0sf88",
            "flavour": "pcisf",
            "controller": 0,
            "pfnum": 0,
            "sfnum": 88,
            "splittable": false,
            "function": {
                "hw_addr": "00:00:00:00:88:88",
                "state": "active",
                "opstate": "attached"
            }
        }
    }
}

Set the function state to active:
$ devlink port function set pci/0000:06:00.0/32768 state inactive

Delete the port after use:
$ devlink port del pci/0000:06:00.0/32768

====================

Signed-off-by: David Ahern <dsahern@kernel.org>
This commit is contained in:
David Ahern 2021-02-02 02:45:49 +00:00
commit d10f2a4bd8
4 changed files with 404 additions and 33 deletions

View File

@ -306,6 +306,10 @@ static void ifname_map_free(struct ifname_map *ifname_map)
#define DL_OPT_FLASH_OVERWRITE BIT(39)
#define DL_OPT_RELOAD_ACTION BIT(40)
#define DL_OPT_RELOAD_LIMIT BIT(41)
#define DL_OPT_PORT_FLAVOUR BIT(42)
#define DL_OPT_PORT_PFNUMBER BIT(43)
#define DL_OPT_PORT_SFNUMBER BIT(44)
#define DL_OPT_PORT_FUNCTION_STATE BIT(45)
struct dl_opts {
uint64_t present; /* flags of present items */
@ -356,6 +360,10 @@ struct dl_opts {
uint32_t overwrite_mask;
enum devlink_reload_action reload_action;
enum devlink_reload_limit reload_limit;
uint32_t port_sfnumber;
uint16_t port_flavour;
uint16_t port_pfnumber;
uint8_t port_fn_state;
};
struct dl {
@ -741,6 +749,7 @@ static int attr_stats_cb(const struct nlattr *attr, void *data)
static const enum mnl_attr_data_type
devlink_function_policy[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1] = {
[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR ] = MNL_TYPE_BINARY,
[DEVLINK_PORT_FN_ATTR_STATE] = MNL_TYPE_U8,
};
static int function_attr_cb(const struct nlattr *attr, void *data)
@ -1383,6 +1392,51 @@ static int reload_limit_get(struct dl *dl, const char *limitstr,
return 0;
}
static struct str_num_map port_flavour_map[] = {
{ .str = "physical", .num = DEVLINK_PORT_FLAVOUR_PHYSICAL },
{ .str = "cpu", .num = DEVLINK_PORT_FLAVOUR_CPU },
{ .str = "dsa", .num = DEVLINK_PORT_FLAVOUR_DSA },
{ .str = "pcipf", .num = DEVLINK_PORT_FLAVOUR_PCI_PF },
{ .str = "pcivf", .num = DEVLINK_PORT_FLAVOUR_PCI_VF },
{ .str = "pcisf", .num = DEVLINK_PORT_FLAVOUR_PCI_SF },
{ .str = "virtual", .num = DEVLINK_PORT_FLAVOUR_VIRTUAL},
{ .str = NULL, },
};
static struct str_num_map port_fn_state_map[] = {
{ .str = "inactive", .num = DEVLINK_PORT_FN_STATE_INACTIVE},
{ .str = "active", .num = DEVLINK_PORT_FN_STATE_ACTIVE },
{ .str = NULL, }
};
static struct str_num_map port_fn_opstate_map[] = {
{ .str = "attached", .num = DEVLINK_PORT_FN_OPSTATE_ATTACHED},
{ .str = "detached", .num = DEVLINK_PORT_FN_OPSTATE_DETACHED},
{ .str = NULL, }
};
static int port_flavour_parse(const char *flavour, uint16_t *value)
{
int num;
num = str_map_lookup_str(port_flavour_map, flavour);
if (num < 0)
return num;
*value = num;
return 0;
}
static int port_fn_state_parse(const char *statestr, uint8_t *state)
{
int num;
num = str_map_lookup_str(port_fn_state_map, statestr);
if (num < 0)
return num;
*state = num;
return 0;
}
struct dl_args_metadata {
uint64_t o_flag;
char err_msg[DL_ARGS_REQUIRED_MAX_ERR_LEN];
@ -1414,6 +1468,8 @@ static const struct dl_args_metadata dl_args_required[] = {
{DL_OPT_TRAP_NAME, "Trap's name is expected."},
{DL_OPT_TRAP_GROUP_NAME, "Trap group's name is expected."},
{DL_OPT_PORT_FUNCTION_HW_ADDR, "Port function's hardware address is expected."},
{DL_OPT_PORT_FLAVOUR, "Port flavour is expected."},
{DL_OPT_PORT_PFNUMBER, "Port PCI PF number is expected."},
};
static int dl_args_finding_required_validate(uint64_t o_required,
@ -1832,7 +1888,42 @@ static int dl_argv_parse(struct dl *dl, uint64_t o_required,
if (err)
return err;
o_found |= DL_OPT_PORT_FUNCTION_HW_ADDR;
} else if (dl_argv_match(dl, "state") &&
(o_all & DL_OPT_PORT_FUNCTION_STATE)) {
const char *statestr;
dl_arg_inc(dl);
err = dl_argv_str(dl, &statestr);
if (err)
return err;
err = port_fn_state_parse(statestr, &opts->port_fn_state);
if (err)
return err;
o_found |= DL_OPT_PORT_FUNCTION_STATE;
} else if (dl_argv_match(dl, "flavour") && (o_all & DL_OPT_PORT_FLAVOUR)) {
const char *flavourstr;
dl_arg_inc(dl);
err = dl_argv_str(dl, &flavourstr);
if (err)
return err;
err = port_flavour_parse(flavourstr, &opts->port_flavour);
if (err)
return err;
o_found |= DL_OPT_PORT_FLAVOUR;
} else if (dl_argv_match(dl, "pfnum") && (o_all & DL_OPT_PORT_PFNUMBER)) {
dl_arg_inc(dl);
err = dl_argv_uint16_t(dl, &opts->port_pfnumber);
if (err)
return err;
o_found |= DL_OPT_PORT_PFNUMBER;
} else if (dl_argv_match(dl, "sfnum") && (o_all & DL_OPT_PORT_SFNUMBER)) {
dl_arg_inc(dl);
err = dl_argv_uint32_t(dl, &opts->port_sfnumber);
if (err)
return err;
o_found |= DL_OPT_PORT_SFNUMBER;
} else {
pr_err("Unknown option \"%s\"\n", dl_argv(dl));
return -EINVAL;
@ -1855,9 +1946,14 @@ dl_function_attr_put(struct nlmsghdr *nlh, const struct dl_opts *opts)
struct nlattr *nest;
nest = mnl_attr_nest_start(nlh, DEVLINK_ATTR_PORT_FUNCTION);
if (opts->present & DL_OPT_PORT_FUNCTION_HW_ADDR)
mnl_attr_put(nlh, DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR,
opts->port_function_hw_addr_len,
opts->port_function_hw_addr);
if (opts->present & DL_OPT_PORT_FUNCTION_STATE)
mnl_attr_put_u8(nlh, DEVLINK_PORT_FN_ATTR_STATE,
opts->port_fn_state);
mnl_attr_nest_end(nlh, nest);
}
@ -2013,8 +2109,14 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
if (opts->present & DL_OPT_TRAP_POLICER_BURST)
mnl_attr_put_u64(nlh, DEVLINK_ATTR_TRAP_POLICER_BURST,
opts->trap_policer_burst);
if (opts->present & DL_OPT_PORT_FUNCTION_HW_ADDR)
if (opts->present & (DL_OPT_PORT_FUNCTION_HW_ADDR | DL_OPT_PORT_FUNCTION_STATE))
dl_function_attr_put(nlh, opts);
if (opts->present & DL_OPT_PORT_FLAVOUR)
mnl_attr_put_u16(nlh, DEVLINK_ATTR_PORT_FLAVOUR, opts->port_flavour);
if (opts->present & DL_OPT_PORT_PFNUMBER)
mnl_attr_put_u16(nlh, DEVLINK_ATTR_PORT_PCI_PF_NUMBER, opts->port_pfnumber);
if (opts->present & DL_OPT_PORT_SFNUMBER)
mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_PCI_SF_NUMBER, opts->port_sfnumber);
}
static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl,
@ -3700,8 +3802,10 @@ static void cmd_port_help(void)
pr_err(" devlink port set DEV/PORT_INDEX [ type { eth | ib | auto} ]\n");
pr_err(" devlink port split DEV/PORT_INDEX count COUNT\n");
pr_err(" devlink port unsplit DEV/PORT_INDEX\n");
pr_err(" devlink port function set DEV/PORT_INDEX [ hw_addr ADDR ]\n");
pr_err(" devlink port function set DEV/PORT_INDEX [ hw_addr ADDR ] [ state STATE ]\n");
pr_err(" devlink port health show [ DEV/PORT_INDEX reporter REPORTER_NAME ]\n");
pr_err(" devlink port add DEV/PORT_INDEX flavour FLAVOUR pfnum PFNUM [ sfnum SFNUM ]\n");
pr_err(" devlink port del DEV/PORT_INDEX\n");
}
static const char *port_type_name(uint32_t type)
@ -3717,25 +3821,13 @@ static const char *port_type_name(uint32_t type)
static const char *port_flavour_name(uint16_t flavour)
{
switch (flavour) {
case DEVLINK_PORT_FLAVOUR_PHYSICAL:
return "physical";
case DEVLINK_PORT_FLAVOUR_CPU:
return "cpu";
case DEVLINK_PORT_FLAVOUR_DSA:
return "dsa";
case DEVLINK_PORT_FLAVOUR_PCI_PF:
return "pcipf";
case DEVLINK_PORT_FLAVOUR_PCI_VF:
return "pcivf";
case DEVLINK_PORT_FLAVOUR_VIRTUAL:
return "virtual";
default:
return "<unknown flavour>";
}
const char *str;
str = str_map_lookup_u16(port_flavour_map, flavour);
return str ? str : "<unknown flavour>";
}
static void pr_out_port_pfvf_num(struct dl *dl, struct nlattr **tb)
static void pr_out_port_pfvfsf_num(struct dl *dl, struct nlattr **tb)
{
uint16_t fn_num;
@ -3750,6 +3842,10 @@ static void pr_out_port_pfvf_num(struct dl *dl, struct nlattr **tb)
fn_num = mnl_attr_get_u16(tb[DEVLINK_ATTR_PORT_PCI_VF_NUMBER]);
print_uint(PRINT_ANY, "vfnum", " vfnum %u", fn_num);
}
if (tb[DEVLINK_ATTR_PORT_PCI_SF_NUMBER]) {
fn_num = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_PCI_SF_NUMBER]);
print_uint(PRINT_ANY, "sfnum", " sfnum %u", fn_num);
}
if (tb[DEVLINK_ATTR_PORT_EXTERNAL]) {
uint8_t external;
@ -3758,6 +3854,22 @@ static void pr_out_port_pfvf_num(struct dl *dl, struct nlattr **tb)
}
}
static const char *port_fn_state(uint8_t state)
{
const char *str;
str = str_map_lookup_u8(port_fn_state_map, state);
return str ? str : "<unknown state>";
}
static const char *port_fn_opstate(uint8_t state)
{
const char *str;
str = str_map_lookup_u8(port_fn_opstate_map, state);
return str ? str : "<unknown state>";
}
static void pr_out_port_function(struct dl *dl, struct nlattr **tb_port)
{
struct nlattr *tb[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1] = {};
@ -3774,16 +3886,33 @@ static void pr_out_port_function(struct dl *dl, struct nlattr **tb_port)
if (err != MNL_CB_OK)
return;
if (!tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR])
return;
pr_out_object_start(dl, "function");
check_indent_newline(dl);
if (tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]) {
len = mnl_attr_get_payload_len(tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]);
data = mnl_attr_get_payload(tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]);
pr_out_object_start(dl, "function");
check_indent_newline(dl);
print_string(PRINT_ANY, "hw_addr", "hw_addr %s",
ll_addr_n2a(data, len, 0, hw_addr, sizeof(hw_addr)));
}
if (tb[DEVLINK_PORT_FN_ATTR_STATE]) {
uint8_t state;
state = mnl_attr_get_u8(tb[DEVLINK_PORT_FN_ATTR_STATE]);
print_string(PRINT_ANY, "state", " state %s",
port_fn_state(state));
}
if (tb[DEVLINK_PORT_FN_ATTR_OPSTATE]) {
uint8_t state;
state = mnl_attr_get_u8(tb[DEVLINK_PORT_FN_ATTR_OPSTATE]);
print_string(PRINT_ANY, "opstate", " opstate %s",
port_fn_opstate(state));
}
if (!dl->json_output)
__pr_out_indent_dec();
pr_out_object_end(dl);
@ -3827,7 +3956,8 @@ static void pr_out_port(struct dl *dl, struct nlattr **tb)
switch (port_flavour) {
case DEVLINK_PORT_FLAVOUR_PCI_PF:
case DEVLINK_PORT_FLAVOUR_PCI_VF:
pr_out_port_pfvf_num(dl, tb);
case DEVLINK_PORT_FLAVOUR_PCI_SF:
pr_out_port_pfvfsf_num(dl, tb);
break;
default:
break;
@ -3937,7 +4067,7 @@ static int cmd_port_unsplit(struct dl *dl)
static void cmd_port_function_help(void)
{
pr_err("Usage: devlink port function set DEV/PORT_INDEX [ hw_addr ADDR ]\n");
pr_err("Usage: devlink port function set DEV/PORT_INDEX [ hw_addr ADDR ] [ state STATE ]\n");
}
static int cmd_port_function_set(struct dl *dl)
@ -3945,9 +4075,14 @@ static int cmd_port_function_set(struct dl *dl)
struct nlmsghdr *nlh;
int err;
if (dl_no_arg(dl)) {
cmd_port_function_help();
return 0;
}
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_SET, NLM_F_REQUEST | NLM_F_ACK);
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_PORT_FUNCTION_HW_ADDR, 0);
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP,
DL_OPT_PORT_FUNCTION_HW_ADDR | DL_OPT_PORT_FUNCTION_STATE);
if (err)
return err;
@ -3970,6 +4105,58 @@ static int cmd_port_function(struct dl *dl)
static int cmd_health(struct dl *dl);
static int __cmd_health_show(struct dl *dl, bool show_device, bool show_port);
static void cmd_port_add_help(void)
{
pr_err(" devlink port add { DEV | DEV/PORT_INDEX } flavour FLAVOUR pfnum PFNUM [ sfnum SFNUM ]\n");
}
static int cmd_port_add(struct dl *dl)
{
struct nlmsghdr *nlh;
int err;
if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
cmd_port_add_help();
return 0;
}
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_NEW,
NLM_F_REQUEST | NLM_F_ACK);
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_HANDLEP |
DL_OPT_PORT_FLAVOUR | DL_OPT_PORT_PFNUMBER,
DL_OPT_PORT_SFNUMBER);
if (err)
return err;
return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_port_show_cb, dl);
}
static void cmd_port_del_help(void)
{
pr_err(" devlink port del DEV/PORT_INDEX\n");
}
static int cmd_port_del(struct dl *dl)
{
struct nlmsghdr *nlh;
int err;
if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
cmd_port_del_help();
return 0;
}
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_DEL,
NLM_F_REQUEST | NLM_F_ACK);
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP, 0);
if (err)
return err;
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
}
static int cmd_port(struct dl *dl)
{
if (dl_argv_match(dl, "help")) {
@ -4000,7 +4187,14 @@ static int cmd_port(struct dl *dl)
} else {
return cmd_health(dl);
}
} else if (dl_argv_match(dl, "add")) {
dl_arg_inc(dl);
return cmd_port_add(dl);
} else if (dl_argv_match(dl, "del")) {
dl_arg_inc(dl);
return cmd_port_del(dl);
}
pr_err("Command \"%s\" not found\n", dl_argv(dl));
return -ENOENT;
}

View File

@ -340,4 +340,13 @@ int parse_mapping(int *argcp, char ***argvp, bool allow_all,
int (*mapping_cb)(__u32 key, char *value, void *data),
void *mapping_cb_data);
struct str_num_map {
const char *str;
int num;
};
int str_map_lookup_str(const struct str_num_map *map, const char *needle);
const char *str_map_lookup_u16(const struct str_num_map *map, uint16_t val);
const char *str_map_lookup_u8(const struct str_num_map *map, uint8_t val);
#endif /* __UTILS_H__ */

View File

@ -1937,3 +1937,44 @@ int parse_mapping(int *argcp, char ***argvp, bool allow_all,
return parse_mapping_gen(argcp, argvp, parse_mapping_num,
mapping_cb, mapping_cb_data);
}
int str_map_lookup_str(const struct str_num_map *map, const char *needle)
{
if (!needle)
return -EINVAL;
/* Process array which is NULL terminated by the string. */
while (map && map->str) {
if (strcmp(map->str, needle) == 0)
return map->num;
map++;
}
return -EINVAL;
}
const char *str_map_lookup_u16(const struct str_num_map *map, uint16_t val)
{
int num = val;
while (map && map->str) {
if (num == map->num)
return map->str;
map++;
}
return NULL;
}
const char *str_map_lookup_u8(const struct str_num_map *map, uint8_t val)
{
int num = val;
while (map && map->str) {
if (num == map->num)
return map->str;
map++;
}
return NULL;
}

View File

@ -43,6 +43,33 @@ devlink-port \- devlink port configuration
.B devlink port health
.RI "{ " show " | " recover " | " diagnose " | " dump " | " set " }"
.ti -8
.BI "devlink port add"
.RB "["
.IR "DEV | DEV/PORT_INDEX"
.RB "] "
.RB "[ " flavour
.IR FLAVOUR " ]"
.RB "[ " pcipf
.IR PFNUMBER " ]"
.RB "{ " pcisf
.IR SFNUMBER " }"
.br
.ti -8
.B devlink port del
.IR DEV/PORT_INDEX
.ti -8
.BR "devlink port function set "
.IR DEV/PORT_INDEX
.RI "{ "
.BR "hw_addr "
.RI "ADDR }"
.RI "{ "
.BR "state"
.RI "STATE }"
.ti -8
.B devlink port help
@ -99,6 +126,65 @@ If this argument is omitted all ports are listed.
Is an alias for
.BR devlink-health (8).
.ti -8
.SS devlink port add - add a devlink port
.PP
.B "DEV"
- specifies the devlink device to operate on. or
.PP
.B "DEV/PORT_INDEX"
- specifies the devlink port index to use for the requested new port.
This is optional. When ommited, driver allocates unique port index.
.TP
.BR flavour " { " pcipf " | " pcisf " } "
set port flavour
.I pcipf
- PCI PF port
.I pcisf
- PCI SF port
.TP
.BR pfnum " { " pfnumber " } "
Specifies PCI pfnumber to use on which a SF device to create
.TP
.BR sfnum " { " sfnumber " } "
Specifies sfnumber to assign to the device of the SF.
This field is optional for those devices which supports auto assignment of the
SF number.
.ti -8
.SS devlink port function set - Set the port function attribute(s).
.PP
.B "DEV/PORT_INDEX"
- specifies the devlink port to operate on.
.TP
.BR hw_addr " ADDR"
- hardware address of the function to set. This is a Ethernet MAC address when
port type is Ethernet.
.TP
.BR state " { " active " | " inactive " } "
- new state of the function to change to.
.I active
- Once configuration of the function is done, activate the function.
.I inactive
- To inactivate the function and its device(s), set to inactive.
.ti -8
.SS devlink port del - delete a devlink port
.PP
.B "DEV/PORT_INDEX"
- specifies the devlink port to delete.
.SH "EXAMPLES"
.PP
devlink port show
@ -135,6 +221,47 @@ devlink port health show pci/0000:01:00.0/1 reporter tx
.RS 4
Shows status and configuration of tx reporter registered on pci/0000:01:00.0/1 devlink port.
.RE
.PP
devlink port add pci/0000:06:00.0 flavour pcisf pfnum 0 sfnum 88
.RS 4
Add a devlink port of flavour PCI SF on PCI PF having number 0 with SF number 88.
To make use of the function an example sequence is to add a port, configure the
function attribute and activate the function. Once function usage is completed,
inactivate the function and finally delete the port. When there is desire to
reuse the port without deletion, it can be reconfigured and activated again when
function is in inactive state and function's operational state is detached.
.RE
.PP
devlink port del pci/0000:06:00.0/1
.RS 4
Delete previously created devlink port. It is recommended to first deactivate
the function if the function supports state management.
.RE
.PP
devlink port function set pci/0000:01:00.0/1 hw_addr 00:00:00:11:22:33
.RS 4
Configure hardware address of the PCI function represented by devlink port.
If the port supports change in function state, hardware address must be configured
before activating the function.
.RE
.PP
devlink port function set pci/0000:01:00.0/1 state active
.RS 4
Activate the function. This will initiate the function enumeration and driver loading.
.RE
.PP
devlink port function set pci/0000:01:00.0/1 state inactive
.RS 4
Deactivate the function. This will initiate the function teardown which results
in driver unload and device removal.
.RE
.PP
devlink port function set pci/0000:01:00.0/1 hw_addr 00:00:00:11:22:33 state active
.RS 4
Configure hardware address and also active the function. When a function is
activated together with other configuration in a single command, all the
configuration is applied first before changing the state to active.
.RE
.SH SEE ALSO
.BR devlink (8),