diff options
author | David Ahern <dsahern@kernel.org> | 2023-11-11 17:33:34 +0000 |
---|---|---|
committer | David Ahern <dsahern@kernel.org> | 2023-11-11 17:33:34 +0000 |
commit | ae0d34854ccfe995d28251b1ae5123970ae142a5 (patch) | |
tree | fe6eee0113c4ce73b463b5cc7d6dbcd66e9701e0 | |
parent | 77138a2f947763de872edb41507a93a292139812 (diff) | |
parent | 1ac0c4450f619d93e78bb7e126b105f292925b01 (diff) | |
download | iproute2-ae0d34854ccfe995d28251b1ae5123970ae142a5.tar.gz |
Merge branch 'devlink-instances' into next
Jiri Pirko says:
====================
Print out recently added attributes that expose relationships between
devlink instances. This patchset extends the outputs by
"nested_devlink" attributes.
Examples:
$ devlink dev
pci/0000:08:00.0:
nested_devlink:
auxiliary/mlx5_core.eth.0
auxiliary/mlx5_core.eth.0
pci/0000:08:00.1:
nested_devlink:
auxiliary/mlx5_core.eth.1
auxiliary/mlx5_core.eth.1
$ devlink dev -j -p
{
"dev": {
"pci/0000:08:00.0": {
"nested_devlink": {
"auxiliary/mlx5_core.eth.0": {}
}
},
"auxiliary/mlx5_core.eth.0": {},
"pci/0000:08:00.1": {
"nested_devlink": {
"auxiliary/mlx5_core.eth.1": {}
}
},
"auxiliary/mlx5_core.eth.1": {}
}
}
$ devlink port add pci/0000:08:00.0 flavour pcisf pfnum 0 sfnum 106
pci/0000:08:00.0/32768: type eth netdev eth2 flavour pcisf controller 0 pfnum 0 sfnum 106 splittable false
function:
hw_addr 00:00:00:00:00:00 state inactive opstate detached roce enable
$ devlink port function set pci/0000:08:00.0/32768 state active
$ devlink port show pci/0000:08:00.0/32768
pci/0000:08:00.0/32768: type eth netdev eth2 flavour pcisf controller 0 pfnum 0 sfnum 106 splittable false
function:
hw_addr 00:00:00:00:00:00 state active opstate attached roce enable
nested_devlink:
auxiliary/mlx5_core.sf.2
$ devlink port show pci/0000:08:00.0/32768 -j -p
{
"port": {
"pci/0000:08:00.0/32768": {
"type": "eth",
"netdev": "eth2",
"flavour": "pcisf",
"controller": 0,
"pfnum": 0,
"sfnum": 106,
"splittable": false,
"function": {
"hw_addr": "00:00:00:00:00:00",
"state": "active",
"opstate": "attached",
"roce": "enable",
"nested_devlink": {
"auxiliary/mlx5_core.sf.2": {}
}
}
}
}
}
$ devlink dev reload auxiliary/mlx5_core.sf.2 netns ns1
$ devlink port show pci/0000:08:00.0/32768
pci/0000:08:00.0/32768: type eth netdev eth2 flavour pcisf controller 0 pfnum 0 sfnum 106 splittable false
function:
hw_addr 00:00:00:00:00:00 state active opstate attached roce enable
nested_devlink:
auxiliary/mlx5_core.sf.2: netns ns1
$ devlink port show pci/0000:08:00.0/32768 -j -p
{
"port": {
"pci/0000:08:00.0/32768": {
"type": "eth",
"netdev": "eth2",
"flavour": "pcisf",
"controller": 0,
"pfnum": 0,
"sfnum": 106,
"splittable": false,
"function": {
"hw_addr": "00:00:00:00:00:00",
"state": "active",
"opstate": "attached",
"roce": "enable",
"nested_devlink": {
"auxiliary/mlx5_core.sf.2": {
"netns": "ns1"
}
}
}
}
}
}
====================
Signed-off-by: David Ahern <dsahern@kernel.org>
-rw-r--r-- | devlink/devlink.c | 140 | ||||
-rw-r--r-- | include/namespace.h | 4 | ||||
-rw-r--r-- | ip/ipnetns.c | 45 | ||||
-rw-r--r-- | lib/namespace.c | 83 |
4 files changed, 197 insertions, 75 deletions
diff --git a/devlink/devlink.c b/devlink/devlink.c index 3baad3557..f999e5940 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -24,6 +24,7 @@ #include <linux/genetlink.h> #include <linux/devlink.h> #include <linux/netlink.h> +#include <linux/net_namespace.h> #include <libmnl/libmnl.h> #include <netinet/ether.h> #include <sys/select.h> @@ -722,6 +723,7 @@ static const enum mnl_attr_data_type devlink_policy[DEVLINK_ATTR_MAX + 1] = { [DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES] = MNL_TYPE_NESTED, [DEVLINK_ATTR_NESTED_DEVLINK] = MNL_TYPE_NESTED, [DEVLINK_ATTR_SELFTESTS] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_NETNS_ID] = MNL_TYPE_U32, }; static const enum mnl_attr_data_type @@ -770,6 +772,7 @@ 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, + [DEVLINK_PORT_FN_ATTR_DEVLINK] = MNL_TYPE_NESTED, }; static int function_attr_cb(const struct nlattr *attr, void *data) @@ -2747,25 +2750,6 @@ static bool should_arr_last_handle_end(struct dl *dl, const char *bus_name, !cmp_arr_last_handle(dl, bus_name, dev_name); } -static void pr_out_nested_handle(struct nlattr *nla_nested_dl) -{ - struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; - char buf[64]; - int err; - - err = mnl_attr_parse_nested(nla_nested_dl, attr_cb, tb); - if (err != MNL_CB_OK) - return; - - if (!tb[DEVLINK_ATTR_BUS_NAME] || - !tb[DEVLINK_ATTR_DEV_NAME]) - return; - - sprintf(buf, "%s/%s", mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]), - mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME])); - print_string(PRINT_ANY, "nested_devlink", " nested_devlink %s", buf); -} - static void __pr_out_handle_start(struct dl *dl, struct nlattr **tb, bool content, bool array) { @@ -2773,7 +2757,7 @@ static void __pr_out_handle_start(struct dl *dl, struct nlattr **tb, const char *dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); char buf[64]; - sprintf(buf, "%s/%s", bus_name, dev_name); + snprintf(buf, sizeof(buf), "%s/%s", bus_name, dev_name); if (dl->json_output) { if (array) { @@ -2832,7 +2816,7 @@ static void pr_out_selftests_handle_start(struct dl *dl, struct nlattr **tb) const char *dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); char buf[64]; - sprintf(buf, "%s/%s", bus_name, dev_name); + snprintf(buf, sizeof(buf), "%s/%s", bus_name, dev_name); if (dl->json_output) { if (should_arr_last_handle_end(dl, bus_name, dev_name)) @@ -2861,6 +2845,74 @@ static void pr_out_selftests_handle_end(struct dl *dl) __pr_out_newline(); } +static void __pr_out_nested_handle(struct dl *dl, struct nlattr *nla_nested_dl, + bool is_object) +{ + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + int err; + + err = mnl_attr_parse_nested(nla_nested_dl, attr_cb, tb); + if (err != MNL_CB_OK) + return; + + if (!tb[DEVLINK_ATTR_BUS_NAME] || + !tb[DEVLINK_ATTR_DEV_NAME]) + return; + + if (!is_object) { + char buf[64]; + + snprintf(buf, sizeof(buf), "%s/%s", + mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]), + mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME])); + print_string(PRINT_ANY, "nested_devlink", " nested_devlink %s", buf); + return; + } + + __pr_out_handle_start(dl, tb, tb[DEVLINK_ATTR_NETNS_ID], false); + if (tb[DEVLINK_ATTR_NETNS_ID]) { + int32_t id = mnl_attr_get_u32(tb[DEVLINK_ATTR_NETNS_ID]); + + if (id >= 0) { + char *name = netns_name_from_id(id); + + if (name) { + print_string(PRINT_ANY, "netns", + " netns %s", name); + free(name); + } else { + print_int(PRINT_ANY, "netnsid", + " netnsid %d", id); + } + } else { + print_string(PRINT_FP, NULL, " netnsid %s", "unknown"); + print_int(PRINT_JSON, "netnsid", NULL, id); + } + } + pr_out_handle_end(dl); +} + +static void pr_out_nested_handle(struct nlattr *nla_nested_dl) +{ + __pr_out_nested_handle(NULL, nla_nested_dl, false); +} + +static void pr_out_nested_handle_obj(struct dl *dl, + struct nlattr *nla_nested_dl, + bool obj_start, bool obj_end) +{ + if (obj_start) { + pr_out_object_start(dl, "nested_devlink"); + check_indent_newline(dl); + } + __pr_out_nested_handle(dl, nla_nested_dl, true); + if (obj_end) { + if (!dl->json_output) + __pr_out_indent_dec(); + pr_out_object_end(dl); + } +} + static bool cmp_arr_last_port_handle(struct dl *dl, const char *bus_name, const char *dev_name, uint32_t port_index) { @@ -2902,9 +2954,10 @@ static void __pr_out_port_handle_start(struct dl *dl, const char *bus_name, if (dl->no_nice_names || !try_nice || ifname_map_rev_lookup(dl, bus_name, dev_name, port_index, &ifname) != 0) - sprintf(buf, "%s/%s/%d", bus_name, dev_name, port_index); + snprintf(buf, sizeof(buf), "%s/%s/%d", + bus_name, dev_name, port_index); else - sprintf(buf, "%s", ifname); + snprintf(buf, sizeof(buf), "%s", ifname); if (dl->json_output) { if (array) { @@ -2974,7 +3027,7 @@ static void pr_out_port_handle_end(struct dl *dl) if (dl->json_output) close_json_object(); else - pr_out("\n"); + __pr_out_newline(); } static void pr_out_region_chunk_start(struct dl *dl, uint64_t addr) @@ -3807,13 +3860,35 @@ static void pr_out_reload_data(struct dl *dl, struct nlattr **tb) pr_out_object_end(dl); } +static void pr_out_dev_nested(struct dl *dl, const struct nlmsghdr *nlh) +{ + int i = 0, count = 0; + struct nlattr *attr; + + mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) { + if (mnl_attr_get_type(attr) == DEVLINK_ATTR_NESTED_DEVLINK) + count++; + } + if (!count) + return; + + mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) { + if (mnl_attr_get_type(attr) != DEVLINK_ATTR_NESTED_DEVLINK) + continue; + pr_out_nested_handle_obj(dl, attr, i == 0, i == count - 1); + i++; + } +} -static void pr_out_dev(struct dl *dl, struct nlattr **tb) +static void pr_out_dev(struct dl *dl, const struct nlmsghdr *nlh, + struct nlattr **tb) { if ((tb[DEVLINK_ATTR_RELOAD_FAILED] && mnl_attr_get_u8(tb[DEVLINK_ATTR_RELOAD_FAILED])) || - (tb[DEVLINK_ATTR_DEV_STATS] && dl->stats)) { + (tb[DEVLINK_ATTR_DEV_STATS] && dl->stats) || + tb[DEVLINK_ATTR_NESTED_DEVLINK]) { __pr_out_handle_start(dl, tb, true, false); pr_out_reload_data(dl, tb); + pr_out_dev_nested(dl, nlh); pr_out_handle_end(dl); } else { pr_out_handle(dl, tb); @@ -3830,7 +3905,7 @@ static int cmd_dev_show_cb(const struct nlmsghdr *nlh, void *data) if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) return MNL_CB_ERROR; - pr_out_dev(dl, tb); + pr_out_dev(dl, nlh, tb); return MNL_CB_OK; } @@ -4803,6 +4878,9 @@ static void pr_out_port_function(struct dl *dl, struct nlattr **tb_port) port_fn_caps->value & DEVLINK_PORT_FN_CAP_IPSEC_PACKET ? "enable" : "disable"); } + if (tb[DEVLINK_PORT_FN_ATTR_DEVLINK]) + pr_out_nested_handle_obj(dl, tb[DEVLINK_PORT_FN_ATTR_DEVLINK], + true, true); if (!dl->json_output) __pr_out_indent_dec(); @@ -5230,7 +5308,7 @@ pr_out_port_rate_handle_start(struct dl *dl, struct nlattr **tb, bool try_nice) bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); node_name = mnl_attr_get_str(tb[DEVLINK_ATTR_RATE_NODE_NAME]); - sprintf(buf, "%s/%s/%s", bus_name, dev_name, node_name); + snprintf(buf, sizeof(buf), "%s/%s/%s", bus_name, dev_name, node_name); if (dl->json_output) open_json_object(buf); else @@ -6305,7 +6383,7 @@ static void pr_out_json_occ_show_item_list(struct dl *dl, const char *label, open_json_object(label); list_for_each_entry(occ_item, list, list) { - sprintf(buf, "%u", occ_item->index); + snprintf(buf, sizeof(buf), "%u", occ_item->index); open_json_object(buf); if (bound_pool) print_uint(PRINT_JSON, "bound_pool", NULL, @@ -6754,7 +6832,7 @@ static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data) return MNL_CB_ERROR; pr_out_mon_header(genl->cmd); dl->stats = true; - pr_out_dev(dl, tb); + pr_out_dev(dl, nlh, tb); pr_out_mon_footer(); break; case DEVLINK_CMD_PORT_GET: /* fall through */ @@ -8674,7 +8752,7 @@ static void pr_out_region_handle_start(struct dl *dl, struct nlattr **tb) const char *region_name = mnl_attr_get_str(tb[DEVLINK_ATTR_REGION_NAME]); char buf[256]; - sprintf(buf, "%s/%s/%s", bus_name, dev_name, region_name); + snprintf(buf, sizeof(buf), "%s/%s/%s", bus_name, dev_name, region_name); if (dl->json_output) open_json_object(buf); else diff --git a/include/namespace.h b/include/namespace.h index e47f9b5d4..86000543f 100644 --- a/include/namespace.h +++ b/include/namespace.h @@ -7,6 +7,7 @@ #include <unistd.h> #include <sys/syscall.h> #include <errno.h> +#include <libnetlink.h> #ifndef NETNS_RUN_DIR #define NETNS_RUN_DIR "/var/run/netns" @@ -58,4 +59,7 @@ struct netns_func { void *arg; }; +int netns_id_from_name(struct rtnl_handle *rtnl, const char *name); +char *netns_name_from_id(int32_t id); + #endif /* __NAMESPACE_H__ */ diff --git a/ip/ipnetns.c b/ip/ipnetns.c index 9d996832a..0ae46a874 100644 --- a/ip/ipnetns.c +++ b/ip/ipnetns.c @@ -105,52 +105,9 @@ static int ipnetns_have_nsid(void) int get_netnsid_from_name(const char *name) { - struct { - struct nlmsghdr n; - struct rtgenmsg g; - char buf[1024]; - } req = { - .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)), - .n.nlmsg_flags = NLM_F_REQUEST, - .n.nlmsg_type = RTM_GETNSID, - .g.rtgen_family = AF_UNSPEC, - }; - struct nlmsghdr *answer; - struct rtattr *tb[NETNSA_MAX + 1]; - struct rtgenmsg *rthdr; - int len, fd, ret = -1; - netns_nsid_socket_init(); - fd = netns_get_fd(name); - if (fd < 0) - return fd; - - addattr32(&req.n, 1024, NETNSA_FD, fd); - if (rtnl_talk(&rtnsh, &req.n, &answer) < 0) { - close(fd); - return -2; - } - close(fd); - - /* Validate message and parse attributes */ - if (answer->nlmsg_type == NLMSG_ERROR) - goto out; - - rthdr = NLMSG_DATA(answer); - len = answer->nlmsg_len - NLMSG_SPACE(sizeof(*rthdr)); - if (len < 0) - goto out; - - parse_rtattr(tb, NETNSA_MAX, NETNS_RTA(rthdr), len); - - if (tb[NETNSA_NSID]) { - ret = rta_getattr_s32(tb[NETNSA_NSID]); - } - -out: - free(answer); - return ret; + return netns_id_from_name(&rtnsh, name); } struct nsid_cache { diff --git a/lib/namespace.c b/lib/namespace.c index 1202fa85f..d3aeb9658 100644 --- a/lib/namespace.c +++ b/lib/namespace.c @@ -7,9 +7,11 @@ #include <fcntl.h> #include <dirent.h> #include <limits.h> +#include <linux/net_namespace.h> #include "utils.h" #include "namespace.h" +#include "libnetlink.h" static void bind_etc(const char *name) { @@ -139,3 +141,84 @@ int netns_foreach(int (*func)(char *nsname, void *arg), void *arg) closedir(dir); return 0; } + +int netns_id_from_name(struct rtnl_handle *rtnl, const char *name) +{ + struct { + struct nlmsghdr n; + struct rtgenmsg g; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETNSID, + .g.rtgen_family = AF_UNSPEC, + }; + struct nlmsghdr *answer; + struct rtattr *tb[NETNSA_MAX + 1]; + struct rtgenmsg *rthdr; + int len, fd, ret = -1; + + fd = netns_get_fd(name); + if (fd < 0) + return fd; + + addattr32(&req.n, 1024, NETNSA_FD, fd); + if (rtnl_talk(rtnl, &req.n, &answer) < 0) { + close(fd); + return -2; + } + close(fd); + + /* Validate message and parse attributes */ + if (answer->nlmsg_type == NLMSG_ERROR) + goto out; + + rthdr = NLMSG_DATA(answer); + len = answer->nlmsg_len - NLMSG_SPACE(sizeof(*rthdr)); + if (len < 0) + goto out; + + parse_rtattr(tb, NETNSA_MAX, NETNS_RTA(rthdr), len); + + if (tb[NETNSA_NSID]) + ret = rta_getattr_s32(tb[NETNSA_NSID]); + +out: + free(answer); + return ret; +} + +struct netns_name_from_id_ctx { + int32_t id; + char *name; + struct rtnl_handle *rth; +}; + +static int netns_name_from_id_func(char *nsname, void *arg) +{ + struct netns_name_from_id_ctx *ctx = arg; + int32_t ret; + + ret = netns_id_from_name(ctx->rth, nsname); + if (ret < 0 || ret != ctx->id) + return 0; + ctx->name = strdup(nsname); + return 1; +} + +char *netns_name_from_id(int32_t id) +{ + struct rtnl_handle rth; + struct netns_name_from_id_ctx ctx = { + .id = id, + .rth = &rth, + }; + + if (rtnl_open(&rth, 0) < 0) + return NULL; + netns_foreach(netns_name_from_id_func, &ctx); + rtnl_close(&rth); + + return ctx.name; +} |