From: Or Har-Toov 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 Reviewed-by: Shay Drori Signed-off-by: Tariq Toukan --- Documentation/netlink/specs/devlink.yaml | 23 ++++++ include/uapi/linux/devlink.h | 3 + net/devlink/netlink.c | 2 +- net/devlink/netlink_gen.c | 32 ++++++- net/devlink/netlink_gen.h | 6 +- net/devlink/resource.c | 101 +++++++++++++++++++++++ 6 files changed, 164 insertions(+), 3 deletions(-) diff --git a/Documentation/netlink/specs/devlink.yaml b/Documentation/netlink/specs/devlink.yaml index 837112da6738..0290db1b8393 100644 --- a/Documentation/netlink/specs/devlink.yaml +++ b/Documentation/netlink/specs/devlink.yaml @@ -2336,3 +2336,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/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 f4c61c2b4f22..692d7862183a 100644 --- a/net/devlink/netlink_gen.c +++ b/net/devlink/netlink_gen.c @@ -604,8 +604,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[74] = { +const struct genl_split_ops devlink_nl_ops[76] = { { .cmd = DEVLINK_CMD_GET, .validate = GENL_DONT_VALIDATE_STRICT, @@ -1284,4 +1297,21 @@ const struct genl_split_ops devlink_nl_ops[74] = { .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 2817d53a0eba..204a665d2fd2 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[74]; +extern const struct genl_split_ops devlink_nl_ops[76]; int devlink_nl_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb, struct genl_info *info); @@ -146,5 +146,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 1b06a1f408fa..42ad7c96a740 100644 --- a/net/devlink/resource.c +++ b/net/devlink/resource.c @@ -227,6 +227,7 @@ static int devlink_resource_fill(struct genl_info *info, struct list_head *resource_list, enum devlink_command cmd, int flags) { + struct devlink_port *devlink_port = info->user_ptr[1]; struct devlink *devlink = info->user_ptr[0]; struct devlink_resource *resource; struct nlattr *resources_attr; @@ -257,6 +258,10 @@ static int devlink_resource_fill(struct genl_info *info, if (devlink_nl_put_handle(skb, devlink)) goto nla_put_failure; + if (devlink_port && 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) @@ -576,3 +581,99 @@ void devl_port_resources_unregister(struct devlink_port *devlink_port) &devlink_port->resource_list); } EXPORT_SYMBOL_GPL(devl_port_resources_unregister); + +static int devlink_nl_port_resource_fill(struct sk_buff *msg, + struct devlink_port *devlink_port, + enum devlink_command cmd, + u32 portid, u32 seq, int flags) +{ + struct devlink *devlink = devlink_port->devlink; + struct devlink_resource *resource; + struct nlattr *resources_attr; + void *hdr; + + if (list_empty(&devlink_port->resource_list)) + return 0; + + hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd); + if (!hdr) + return -EMSGSIZE; + + if (devlink_nl_put_handle(msg, devlink)) + goto nla_put_failure; + if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, devlink_port->index)) + goto nla_put_failure; + + resources_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_RESOURCE_LIST); + if (!resources_attr) + goto nla_put_failure; + + list_for_each_entry(resource, &devlink_port->resource_list, list) { + if (devlink_resource_put(devlink, msg, resource)) { + nla_nest_cancel(msg, resources_attr); + goto nla_put_failure; + } + } + nla_nest_end(msg, resources_attr); + genlmsg_end(msg, hdr); + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +int devlink_nl_port_resource_get_doit(struct sk_buff *skb, + struct genl_info *info) +{ + struct devlink_port *devlink_port = info->user_ptr[1]; + struct sk_buff *msg; + int err; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + err = devlink_nl_port_resource_fill(msg, devlink_port, + DEVLINK_CMD_PORT_RESOURCE_GET, + info->snd_portid, info->snd_seq, 0); + if (err) { + nlmsg_free(msg); + return err; + } + + return genlmsg_reply(msg, info); +} + +static int +devlink_nl_port_resource_get_dump_one(struct sk_buff *msg, + struct devlink *devlink, + struct netlink_callback *cb, int flags) +{ + enum devlink_command cmd = DEVLINK_CMD_PORT_RESOURCE_GET; + struct devlink_nl_dump_state *state = devlink_dump_state(cb); + struct devlink_port *devlink_port; + unsigned long port_index; + int err; + + xa_for_each_start(&devlink->ports, port_index, devlink_port, + state->idx) { + err = devlink_nl_port_resource_fill(msg, devlink_port, + cmd, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, flags); + if (err) { + state->idx = port_index; + return err; + } + } + + return 0; +} + +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