aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Ahern <dsahern@kernel.org>2023-11-11 17:33:34 +0000
committerDavid Ahern <dsahern@kernel.org>2023-11-11 17:33:34 +0000
commitae0d34854ccfe995d28251b1ae5123970ae142a5 (patch)
treefe6eee0113c4ce73b463b5cc7d6dbcd66e9701e0
parent77138a2f947763de872edb41507a93a292139812 (diff)
parent1ac0c4450f619d93e78bb7e126b105f292925b01 (diff)
downloadiproute2-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.c140
-rw-r--r--include/namespace.h4
-rw-r--r--ip/ipnetns.c45
-rw-r--r--lib/namespace.c83
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;
+}