[PATCH net-next V3 06/10] devlink: Add port resource netlink command
From: Tariq Toukan <tariqt@nvidia.com>
Date: 2026-02-26 22:20:31
Also in:
linux-doc, linux-kselftest, linux-rdma, lkml
Subsystem:
devlink, networking [general], the rest, yaml netlink (ynl) · Maintainers:
Jiri Pirko, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds, Donald Hunter
From: Or Har-Toov <redacted>
Add support for userspace to query resources registered on devlink
ports, allowing drivers to expose per-port resource limits and usage.
Example output:
$ devlink port resource show
pci/0000:03:00.0/196608:
name max_SFs size 20 unit entry
pci/0000:03:00.1/262144:
name max_SFs size 20 unit entry
$ devlink port resource show pci/0000:03:00.0/196608
pci/0000:03:00.0/196608:
name max_SFs size 20 unit entry
Signed-off-by: Or Har-Toov <redacted>
Reviewed-by: Shay Drori <redacted>
Reviewed-by: Moshe Shemesh <redacted>
Reviewed-by: Jiri Pirko <redacted>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
Documentation/netlink/specs/devlink.yaml | 23 ++++++
include/uapi/linux/devlink.h | 3 +
net/devlink/devl_internal.h | 4 ++
net/devlink/netlink.c | 2 +-
net/devlink/netlink_gen.c | 32 ++++++++-
net/devlink/netlink_gen.h | 6 +-
net/devlink/resource.c | 90 +++++++++++++++++++++++-
7 files changed, 155 insertions(+), 5 deletions(-)
diff --git a/Documentation/netlink/specs/devlink.yaml b/Documentation/netlink/specs/devlink.yaml
index ee679ac14261..9b813f5fc51c 100644
--- a/Documentation/netlink/specs/devlink.yaml
+++ b/Documentation/netlink/specs/devlink.yaml@@ -2340,3 +2340,26 @@ operations: - bus-name - dev-name - port-index + + - + name: port-resource-get + doc: Get port resources. + attribute-set: devlink + dont-validate: [strict] + do: + pre: devlink-nl-pre-doit-port + post: devlink-nl-post-doit + request: + value: 85 + attributes: *port-id-attrs + reply: &port-resource-get-reply + value: 85 + attributes: + - bus-name + - dev-name + - port-index + - resource-list + dump: + request: + attributes: *dev-id-attrs + reply: *port-resource-get-reply
diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
index e7d6b6d13470..1cabd1f6cba0 100644
--- a/include/uapi/linux/devlink.h
+++ b/include/uapi/linux/devlink.h@@ -141,6 +141,9 @@ enum devlink_command { DEVLINK_CMD_NOTIFY_FILTER_SET, + DEVLINK_CMD_PORT_RESOURCE_GET, /* can dump */ + DEVLINK_CMD_PORT_RESOURCE_SET, + /* add new commands above here */ __DEVLINK_CMD_MAX, DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h
index 1377864383bc..ddf855bc893f 100644
--- a/net/devlink/devl_internal.h
+++ b/net/devlink/devl_internal.h@@ -148,6 +148,10 @@ struct devlink_nl_dump_state { struct { u64 dump_ts; }; + /* DEVLINK_CMD_PORT_RESOURCE_GET - dump */ + struct { + unsigned long port_index; + }; }; };
diff --git a/net/devlink/netlink.c b/net/devlink/netlink.c
index 593605c1b1ef..c78c31779622 100644
--- a/net/devlink/netlink.c
+++ b/net/devlink/netlink.c@@ -367,7 +367,7 @@ struct genl_family devlink_nl_family __ro_after_init = { .module = THIS_MODULE, .split_ops = devlink_nl_ops, .n_split_ops = ARRAY_SIZE(devlink_nl_ops), - .resv_start_op = DEVLINK_CMD_SELFTESTS_RUN + 1, + .resv_start_op = DEVLINK_CMD_PORT_RESOURCE_GET + 1, .mcgrps = devlink_nl_mcgrps, .n_mcgrps = ARRAY_SIZE(devlink_nl_mcgrps), .sock_priv_size = sizeof(struct devlink_nl_sock_priv),
diff --git a/net/devlink/netlink_gen.c b/net/devlink/netlink_gen.c
index d6667a3f87a0..a235748d0688 100644
--- a/net/devlink/netlink_gen.c
+++ b/net/devlink/netlink_gen.c@@ -610,8 +610,21 @@ static const struct nla_policy devlink_notify_filter_set_nl_policy[DEVLINK_ATTR_ [DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32, }, }; +/* DEVLINK_CMD_PORT_RESOURCE_GET - do */ +static const struct nla_policy devlink_port_resource_get_do_nl_policy[DEVLINK_ATTR_PORT_INDEX + 1] = { + [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING, }, + [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING, }, + [DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32, }, +}; + +/* DEVLINK_CMD_PORT_RESOURCE_GET - dump */ +static const struct nla_policy devlink_port_resource_get_dump_nl_policy[DEVLINK_ATTR_DEV_NAME + 1] = { + [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING, }, + [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING, }, +}; + /* Ops table for devlink */ -const struct genl_split_ops devlink_nl_ops[75] = { +const struct genl_split_ops devlink_nl_ops[77] = { { .cmd = DEVLINK_CMD_GET, .validate = GENL_DONT_VALIDATE_STRICT,
@@ -1297,4 +1310,21 @@ const struct genl_split_ops devlink_nl_ops[75] = { .maxattr = DEVLINK_ATTR_PORT_INDEX, .flags = GENL_CMD_CAP_DO, }, + { + .cmd = DEVLINK_CMD_PORT_RESOURCE_GET, + .validate = GENL_DONT_VALIDATE_STRICT, + .pre_doit = devlink_nl_pre_doit_port, + .doit = devlink_nl_port_resource_get_doit, + .post_doit = devlink_nl_post_doit, + .policy = devlink_port_resource_get_do_nl_policy, + .maxattr = DEVLINK_ATTR_PORT_INDEX, + .flags = GENL_CMD_CAP_DO, + }, + { + .cmd = DEVLINK_CMD_PORT_RESOURCE_GET, + .dumpit = devlink_nl_port_resource_get_dumpit, + .policy = devlink_port_resource_get_dump_nl_policy, + .maxattr = DEVLINK_ATTR_DEV_NAME, + .flags = GENL_CMD_CAP_DUMP, + }, };
diff --git a/net/devlink/netlink_gen.h b/net/devlink/netlink_gen.h
index d79f6a0888f6..cab9d30e913a 100644
--- a/net/devlink/netlink_gen.h
+++ b/net/devlink/netlink_gen.h@@ -18,7 +18,7 @@ extern const struct nla_policy devlink_dl_rate_tc_bws_nl_policy[DEVLINK_RATE_TC_ extern const struct nla_policy devlink_dl_selftest_id_nl_policy[DEVLINK_ATTR_SELFTEST_ID_FLASH + 1]; /* Ops table for devlink */ -extern const struct genl_split_ops devlink_nl_ops[75]; +extern const struct genl_split_ops devlink_nl_ops[77]; int devlink_nl_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb, struct genl_info *info);
@@ -148,5 +148,9 @@ int devlink_nl_selftests_get_dumpit(struct sk_buff *skb, int devlink_nl_selftests_run_doit(struct sk_buff *skb, struct genl_info *info); int devlink_nl_notify_filter_set_doit(struct sk_buff *skb, struct genl_info *info); +int devlink_nl_port_resource_get_doit(struct sk_buff *skb, + struct genl_info *info); +int devlink_nl_port_resource_get_dumpit(struct sk_buff *skb, + struct netlink_callback *cb); #endif /* _LINUX_DEVLINK_GEN_H */
diff --git a/net/devlink/resource.c b/net/devlink/resource.c
index 71f00e580f59..0a1d1610ff67 100644
--- a/net/devlink/resource.c
+++ b/net/devlink/resource.c@@ -252,6 +252,7 @@ static int __devlink_resource_fill(struct genl_info *info, struct list_head *resource_list_head, enum devlink_command cmd, int flags) { + struct devlink_port *devlink_port = info->user_ptr[1]; struct devlink *devlink = info->user_ptr[0]; struct nlattr *resources_attr; struct sk_buff *skb = NULL;
@@ -279,9 +280,13 @@ static int __devlink_resource_fill(struct genl_info *info, if (devlink_nl_put_handle(skb, devlink)) goto nla_put_failure; + if (devlink_port) { + if (nla_put_u32(skb, DEVLINK_ATTR_PORT_INDEX, + devlink_port->index)) + goto nla_put_failure; + } - resources_attr = nla_nest_start_noflag(skb, - DEVLINK_ATTR_RESOURCE_LIST); + resources_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE_LIST); if (!resources_attr) goto nla_put_failure;
@@ -656,3 +661,84 @@ void devl_port_resources_unregister(struct devlink_port *devlink_port) &devlink_port->resource_list); } EXPORT_SYMBOL_GPL(devl_port_resources_unregister); + +int devlink_nl_port_resource_get_doit(struct sk_buff *skb, + struct genl_info *info) +{ + struct devlink_port *devlink_port = info->user_ptr[1]; + + return __devlink_resource_fill(info, &devlink_port->resource_list, + DEVLINK_CMD_PORT_RESOURCE_GET, 0); +} + +static int +devlink_nl_port_resource_get_dump_one(struct sk_buff *skb, + struct devlink *devlink, + struct netlink_callback *cb, int flags) +{ + struct devlink_nl_dump_state *state = devlink_dump_state(cb); + struct devlink_port *devlink_port; + struct list_head *resource_list; + struct nlattr *resources_attr; + int resource_idx, start_idx; + unsigned long port_idx; + void *hdr; + int err; + + xa_for_each_start(&devlink->ports, port_idx, devlink_port, + state->port_index) { + if (list_empty(&devlink_port->resource_list)) + continue; + + resource_idx = (port_idx == state->port_index) ? state->idx : 0; + start_idx = resource_idx; + err = -EMSGSIZE; + + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, &devlink_nl_family, flags, + DEVLINK_CMD_PORT_RESOURCE_GET); + if (!hdr) + return err; + + if (devlink_nl_put_handle(skb, devlink) || + nla_put_u32(skb, DEVLINK_ATTR_PORT_INDEX, + devlink_port->index)) + goto nla_put_failure; + + resources_attr = + nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE_LIST); + if (!resources_attr) + goto nla_put_failure; + + resource_list = &devlink_port->resource_list; + err = devlink_resource_list_fill(skb, devlink, resource_list, + &resource_idx); + if (err) { + state->port_index = port_idx; + state->idx = resource_idx; + if (resource_idx == start_idx) + goto nla_put_failure_unwind; + nla_nest_end(skb, resources_attr); + genlmsg_end(skb, hdr); + return err; + } + nla_nest_end(skb, resources_attr); + genlmsg_end(skb, hdr); + } + state->port_index = 0; + state->idx = 0; + return 0; + +nla_put_failure_unwind: + nla_nest_cancel(skb, resources_attr); +nla_put_failure: + genlmsg_cancel(skb, hdr); + return err; +} + +int devlink_nl_port_resource_get_dumpit(struct sk_buff *skb, + struct netlink_callback *cb) +{ + return devlink_nl_dumpit(skb, cb, + devlink_nl_port_resource_get_dump_one); +}
--
2.44.0