From: Or Har-Toov <[email protected]>

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 <[email protected]>
Reviewed-by: Shay Drori <[email protected]>
Signed-off-by: Tariq Toukan <[email protected]>
---
 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


Reply via email to