mirror of
https://git.proxmox.com/git/mirror_iproute2
synced 2025-10-27 22:51:49 +00:00
Merge branch 'devlink-port-mac-addr' into next
Parav Pandit says: ==================== Currently ip link set dev <pfndev> vf <vf_num> <param> <value> has few below limitations. 1. Command is limited to set VF parameters only. It cannot set the default MAC address for the PCI PF. 2. It can be set only on system where PCI SR-IOV is supported. In smartnic based system, eswitch of a NIC resides on a different embedded cpu which has the VF and PF representors for the SR-IOV support on a host system in which this smartnic is plugged-in. 3. It cannot setup the function attributes of sub-function described in detail in comprehensive RFC [1] and [2]. This series covers the first small part to let user query and set MAC address (hardware address) of a PCI PF/VF which is represented by devlink port. [1] https://lore.kernel.org/netdev/20200519092258.GF4655@nanopsycho/ [2] https://marc.info/?l=linux-netdev&m=158555928517777&w=2 ==================== Signed-off-by: David Ahern <dsahern@kernel.org>
This commit is contained in:
commit
482e463d6c
@ -26,6 +26,7 @@
|
||||
#include <libmnl/libmnl.h>
|
||||
#include <netinet/ether.h>
|
||||
#include <sys/types.h>
|
||||
#include <rt_names.h>
|
||||
|
||||
#include "SNAPSHOT.h"
|
||||
#include "list.h"
|
||||
@ -293,6 +294,7 @@ static void ifname_map_free(struct ifname_map *ifname_map)
|
||||
#define DL_OPT_TRAP_POLICER_RATE BIT(35)
|
||||
#define DL_OPT_TRAP_POLICER_BURST BIT(36)
|
||||
#define DL_OPT_HEALTH_REPORTER_AUTO_DUMP BIT(37)
|
||||
#define DL_OPT_PORT_FUNCTION_HW_ADDR BIT(38)
|
||||
|
||||
struct dl_opts {
|
||||
uint64_t present; /* flags of present items */
|
||||
@ -338,6 +340,8 @@ struct dl_opts {
|
||||
uint32_t trap_policer_id;
|
||||
uint64_t trap_policer_rate;
|
||||
uint64_t trap_policer_burst;
|
||||
char port_function_hw_addr[MAX_ADDR_LEN];
|
||||
uint32_t port_function_hw_addr_len;
|
||||
};
|
||||
|
||||
struct dl {
|
||||
@ -429,6 +433,127 @@ static void __pr_out_indent_newline(struct dl *dl)
|
||||
pr_out(" ");
|
||||
}
|
||||
|
||||
static bool is_binary_eol(int i)
|
||||
{
|
||||
return !(i%16);
|
||||
}
|
||||
|
||||
static void pr_out_binary_value(struct dl *dl, uint8_t *data, uint32_t len)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (i < len) {
|
||||
if (dl->json_output)
|
||||
print_int(PRINT_JSON, NULL, NULL, data[i]);
|
||||
else
|
||||
pr_out("%02x ", data[i]);
|
||||
i++;
|
||||
if (!dl->json_output && is_binary_eol(i))
|
||||
__pr_out_newline();
|
||||
}
|
||||
if (!dl->json_output && !is_binary_eol(i))
|
||||
__pr_out_newline();
|
||||
}
|
||||
|
||||
static void pr_out_name(struct dl *dl, const char *name)
|
||||
{
|
||||
__pr_out_indent_newline(dl);
|
||||
if (dl->json_output)
|
||||
print_string(PRINT_JSON, name, NULL, NULL);
|
||||
else
|
||||
pr_out("%s:", name);
|
||||
}
|
||||
|
||||
static void pr_out_u64(struct dl *dl, const char *name, uint64_t val)
|
||||
{
|
||||
__pr_out_indent_newline(dl);
|
||||
if (val == (uint64_t) -1)
|
||||
return print_string_name_value(name, "unlimited");
|
||||
|
||||
if (dl->json_output)
|
||||
print_u64(PRINT_JSON, name, NULL, val);
|
||||
else
|
||||
pr_out("%s %"PRIu64, name, val);
|
||||
}
|
||||
|
||||
static void pr_out_section_start(struct dl *dl, const char *name)
|
||||
{
|
||||
if (dl->json_output) {
|
||||
open_json_object(NULL);
|
||||
open_json_object(name);
|
||||
}
|
||||
}
|
||||
|
||||
static void pr_out_section_end(struct dl *dl)
|
||||
{
|
||||
if (dl->json_output) {
|
||||
if (dl->arr_last.present)
|
||||
close_json_array(PRINT_JSON, NULL);
|
||||
close_json_object();
|
||||
close_json_object();
|
||||
}
|
||||
}
|
||||
|
||||
static void pr_out_array_start(struct dl *dl, const char *name)
|
||||
{
|
||||
if (dl->json_output) {
|
||||
open_json_array(PRINT_JSON, name);
|
||||
} else {
|
||||
__pr_out_indent_inc();
|
||||
__pr_out_newline();
|
||||
pr_out("%s:", name);
|
||||
__pr_out_indent_inc();
|
||||
__pr_out_newline();
|
||||
}
|
||||
}
|
||||
|
||||
static void pr_out_array_end(struct dl *dl)
|
||||
{
|
||||
if (dl->json_output) {
|
||||
close_json_array(PRINT_JSON, NULL);
|
||||
} else {
|
||||
__pr_out_indent_dec();
|
||||
__pr_out_indent_dec();
|
||||
}
|
||||
}
|
||||
|
||||
static void pr_out_object_start(struct dl *dl, const char *name)
|
||||
{
|
||||
if (dl->json_output) {
|
||||
open_json_object(name);
|
||||
} else {
|
||||
__pr_out_indent_inc();
|
||||
__pr_out_newline();
|
||||
pr_out("%s:", name);
|
||||
__pr_out_indent_inc();
|
||||
__pr_out_newline();
|
||||
}
|
||||
}
|
||||
|
||||
static void pr_out_object_end(struct dl *dl)
|
||||
{
|
||||
if (dl->json_output) {
|
||||
close_json_object();
|
||||
} else {
|
||||
__pr_out_indent_dec();
|
||||
__pr_out_indent_dec();
|
||||
}
|
||||
}
|
||||
|
||||
static void pr_out_entry_start(struct dl *dl)
|
||||
{
|
||||
if (dl->json_output)
|
||||
open_json_object(NULL);
|
||||
}
|
||||
|
||||
static void pr_out_entry_end(struct dl *dl)
|
||||
{
|
||||
if (dl->json_output)
|
||||
close_json_object();
|
||||
else
|
||||
__pr_out_newline();
|
||||
}
|
||||
|
||||
static void check_indent_newline(struct dl *dl)
|
||||
{
|
||||
__pr_out_indent_newline(dl);
|
||||
@ -587,6 +712,30 @@ static int attr_stats_cb(const struct nlattr *attr, void *data)
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
static int function_attr_cb(const struct nlattr *attr, void *data)
|
||||
{
|
||||
const struct nlattr **tb = data;
|
||||
int type;
|
||||
|
||||
/* Allow the tool to work on top of newer kernels that might contain
|
||||
* more attributes.
|
||||
*/
|
||||
if (mnl_attr_type_valid(attr, DEVLINK_PORT_FUNCTION_ATTR_MAX) < 0)
|
||||
return MNL_CB_OK;
|
||||
|
||||
type = mnl_attr_get_type(attr);
|
||||
if (mnl_attr_validate(attr, devlink_function_policy[type]) < 0)
|
||||
return MNL_CB_ERROR;
|
||||
|
||||
tb[type] = attr;
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int ifname_map_cb(const struct nlmsghdr *nlh, void *data)
|
||||
{
|
||||
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
|
||||
@ -1156,6 +1305,17 @@ static int trap_action_get(const char *actionstr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hw_addr_parse(const char *addrstr, char *hw_addr, uint32_t *len)
|
||||
{
|
||||
int alen;
|
||||
|
||||
alen = ll_addr_a2n(hw_addr, MAX_ADDR_LEN, addrstr);
|
||||
if (alen < 0)
|
||||
return -EINVAL;
|
||||
*len = alen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dl_args_metadata {
|
||||
uint64_t o_flag;
|
||||
char err_msg[DL_ARGS_REQUIRED_MAX_ERR_LEN];
|
||||
@ -1186,6 +1346,7 @@ static const struct dl_args_metadata dl_args_required[] = {
|
||||
{DL_OPT_HEALTH_REPORTER_NAME, "Reporter's name is expected."},
|
||||
{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."},
|
||||
};
|
||||
|
||||
static int dl_args_finding_required_validate(uint64_t o_required,
|
||||
@ -1552,6 +1713,20 @@ static int dl_argv_parse(struct dl *dl, uint64_t o_required,
|
||||
if (err)
|
||||
return err;
|
||||
o_found |= DL_OPT_TRAP_POLICER_BURST;
|
||||
} else if (dl_argv_match(dl, "hw_addr") &&
|
||||
(o_all & DL_OPT_PORT_FUNCTION_HW_ADDR)) {
|
||||
const char *addrstr;
|
||||
|
||||
dl_arg_inc(dl);
|
||||
err = dl_argv_str(dl, &addrstr);
|
||||
if (err)
|
||||
return err;
|
||||
err = hw_addr_parse(addrstr, opts->port_function_hw_addr,
|
||||
&opts->port_function_hw_addr_len);
|
||||
if (err)
|
||||
return err;
|
||||
o_found |= DL_OPT_PORT_FUNCTION_HW_ADDR;
|
||||
|
||||
} else {
|
||||
pr_err("Unknown option \"%s\"\n", dl_argv(dl));
|
||||
return -EINVAL;
|
||||
@ -1568,6 +1743,18 @@ static int dl_argv_parse(struct dl *dl, uint64_t o_required,
|
||||
return dl_args_finding_required_validate(o_required, o_found);
|
||||
}
|
||||
|
||||
static void
|
||||
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);
|
||||
mnl_attr_put(nlh, DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR,
|
||||
opts->port_function_hw_addr_len,
|
||||
opts->port_function_hw_addr);
|
||||
mnl_attr_nest_end(nlh, nest);
|
||||
}
|
||||
|
||||
static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
|
||||
{
|
||||
struct dl_opts *opts = &dl->opts;
|
||||
@ -1691,6 +1878,8 @@ 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)
|
||||
dl_function_attr_put(nlh, opts);
|
||||
}
|
||||
|
||||
static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl,
|
||||
@ -1950,49 +2139,6 @@ static void pr_out_port_handle_end(struct dl *dl)
|
||||
pr_out("\n");
|
||||
}
|
||||
|
||||
static void pr_out_u64(struct dl *dl, const char *name, uint64_t val)
|
||||
{
|
||||
__pr_out_indent_newline(dl);
|
||||
if (val == (uint64_t) -1)
|
||||
return print_string_name_value(name, "unlimited");
|
||||
|
||||
if (dl->json_output)
|
||||
print_u64(PRINT_JSON, name, NULL, val);
|
||||
else
|
||||
pr_out("%s %"PRIu64, name, val);
|
||||
}
|
||||
|
||||
static bool is_binary_eol(int i)
|
||||
{
|
||||
return !(i%16);
|
||||
}
|
||||
|
||||
static void pr_out_binary_value(struct dl *dl, uint8_t *data, uint32_t len)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (i < len) {
|
||||
if (dl->json_output)
|
||||
print_int(PRINT_JSON, NULL, NULL, data[i]);
|
||||
else
|
||||
pr_out("%02x ", data[i]);
|
||||
i++;
|
||||
if (!dl->json_output && is_binary_eol(i))
|
||||
__pr_out_newline();
|
||||
}
|
||||
if (!dl->json_output && !is_binary_eol(i))
|
||||
__pr_out_newline();
|
||||
}
|
||||
|
||||
static void pr_out_name(struct dl *dl, const char *name)
|
||||
{
|
||||
__pr_out_indent_newline(dl);
|
||||
if (dl->json_output)
|
||||
print_string(PRINT_JSON, name, NULL, NULL);
|
||||
else
|
||||
pr_out("%s:", name);
|
||||
}
|
||||
|
||||
static void pr_out_region_chunk_start(struct dl *dl, uint64_t addr)
|
||||
{
|
||||
if (dl->json_output) {
|
||||
@ -2034,84 +2180,6 @@ static void pr_out_region_chunk(struct dl *dl, uint8_t *data, uint32_t len,
|
||||
pr_out_region_chunk_end(dl);
|
||||
}
|
||||
|
||||
static void pr_out_section_start(struct dl *dl, const char *name)
|
||||
{
|
||||
if (dl->json_output) {
|
||||
open_json_object(NULL);
|
||||
open_json_object(name);
|
||||
}
|
||||
}
|
||||
|
||||
static void pr_out_section_end(struct dl *dl)
|
||||
{
|
||||
if (dl->json_output) {
|
||||
if (dl->arr_last.present)
|
||||
close_json_array(PRINT_JSON, NULL);
|
||||
close_json_object();
|
||||
close_json_object();
|
||||
}
|
||||
}
|
||||
|
||||
static void pr_out_array_start(struct dl *dl, const char *name)
|
||||
{
|
||||
if (dl->json_output) {
|
||||
open_json_array(PRINT_JSON, name);
|
||||
} else {
|
||||
__pr_out_indent_inc();
|
||||
__pr_out_newline();
|
||||
pr_out("%s:", name);
|
||||
__pr_out_indent_inc();
|
||||
__pr_out_newline();
|
||||
}
|
||||
}
|
||||
|
||||
static void pr_out_array_end(struct dl *dl)
|
||||
{
|
||||
if (dl->json_output) {
|
||||
close_json_array(PRINT_JSON, NULL);
|
||||
} else {
|
||||
__pr_out_indent_dec();
|
||||
__pr_out_indent_dec();
|
||||
}
|
||||
}
|
||||
|
||||
static void pr_out_object_start(struct dl *dl, const char *name)
|
||||
{
|
||||
if (dl->json_output) {
|
||||
open_json_object(name);
|
||||
} else {
|
||||
__pr_out_indent_inc();
|
||||
__pr_out_newline();
|
||||
pr_out("%s:", name);
|
||||
__pr_out_indent_inc();
|
||||
__pr_out_newline();
|
||||
}
|
||||
}
|
||||
|
||||
static void pr_out_object_end(struct dl *dl)
|
||||
{
|
||||
if (dl->json_output) {
|
||||
close_json_object();
|
||||
} else {
|
||||
__pr_out_indent_dec();
|
||||
__pr_out_indent_dec();
|
||||
}
|
||||
}
|
||||
|
||||
static void pr_out_entry_start(struct dl *dl)
|
||||
{
|
||||
if (dl->json_output)
|
||||
open_json_object(NULL);
|
||||
}
|
||||
|
||||
static void pr_out_entry_end(struct dl *dl)
|
||||
{
|
||||
if (dl->json_output)
|
||||
close_json_object();
|
||||
else
|
||||
__pr_out_newline();
|
||||
}
|
||||
|
||||
static void pr_out_stats(struct dl *dl, struct nlattr *nla_stats)
|
||||
{
|
||||
struct nlattr *tb[DEVLINK_ATTR_STATS_MAX + 1] = {};
|
||||
@ -3196,6 +3264,7 @@ 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");
|
||||
}
|
||||
|
||||
static const char *port_type_name(uint32_t type)
|
||||
@ -3243,6 +3312,37 @@ static void pr_out_port_pfvf_num(struct dl *dl, struct nlattr **tb)
|
||||
}
|
||||
}
|
||||
|
||||
static void pr_out_port_function(struct dl *dl, struct nlattr **tb_port)
|
||||
{
|
||||
struct nlattr *tb[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1] = {};
|
||||
unsigned char *data;
|
||||
SPRINT_BUF(hw_addr);
|
||||
uint32_t len;
|
||||
int err;
|
||||
|
||||
if (!tb_port[DEVLINK_ATTR_PORT_FUNCTION])
|
||||
return;
|
||||
|
||||
err = mnl_attr_parse_nested(tb_port[DEVLINK_ATTR_PORT_FUNCTION],
|
||||
function_attr_cb, tb);
|
||||
if (err != MNL_CB_OK)
|
||||
return;
|
||||
|
||||
if (!tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR])
|
||||
return;
|
||||
|
||||
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 (!dl->json_output)
|
||||
__pr_out_indent_dec();
|
||||
pr_out_object_end(dl);
|
||||
}
|
||||
|
||||
static void pr_out_port(struct dl *dl, struct nlattr **tb)
|
||||
{
|
||||
struct nlattr *pt_attr = tb[DEVLINK_ATTR_PORT_TYPE];
|
||||
@ -3296,6 +3396,7 @@ static void pr_out_port(struct dl *dl, struct nlattr **tb)
|
||||
if (tb[DEVLINK_ATTR_PORT_SPLIT_GROUP])
|
||||
print_uint(PRINT_ANY, "split_group", " split_group %u",
|
||||
mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_SPLIT_GROUP]));
|
||||
pr_out_port_function(dl, tb);
|
||||
pr_out_port_handle_end(dl);
|
||||
}
|
||||
|
||||
@ -3381,6 +3482,38 @@ static int cmd_port_unsplit(struct dl *dl)
|
||||
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
|
||||
}
|
||||
|
||||
static void cmd_port_function_help(void)
|
||||
{
|
||||
pr_err("Usage: devlink port function set DEV/PORT_INDEX [ hw_addr ADDR ]\n");
|
||||
}
|
||||
|
||||
static int cmd_port_function_set(struct dl *dl)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
int err;
|
||||
|
||||
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);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
|
||||
}
|
||||
|
||||
static int cmd_port_function(struct dl *dl)
|
||||
{
|
||||
if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
|
||||
cmd_port_function_help();
|
||||
return 0;
|
||||
} else if (dl_argv_match(dl, "set")) {
|
||||
dl_arg_inc(dl);
|
||||
return cmd_port_function_set(dl);
|
||||
}
|
||||
pr_err("Command \"%s\" not found\n", dl_argv(dl));
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int cmd_port(struct dl *dl)
|
||||
{
|
||||
if (dl_argv_match(dl, "help")) {
|
||||
@ -3399,6 +3532,9 @@ static int cmd_port(struct dl *dl)
|
||||
} else if (dl_argv_match(dl, "unsplit")) {
|
||||
dl_arg_inc(dl);
|
||||
return cmd_port_unsplit(dl);
|
||||
} else if (dl_argv_match(dl, "function")) {
|
||||
dl_arg_inc(dl);
|
||||
return cmd_port_function(dl);
|
||||
}
|
||||
pr_err("Command \"%s\" not found\n", dl_argv(dl));
|
||||
return -ENOENT;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user