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

The current devlink resource infrastructure only supports device-level
resources. Some hardware resources are associated with specific ports
rather than the entire device, and today we have no way to show resource
per-port.

Add support for registering and querying resources at the port level,
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]>
Reviewed-by: Moshe Shemesh <[email protected]>
Signed-off-by: Tariq Toukan <[email protected]>
---
 Documentation/netlink/specs/devlink.yaml |  23 ++
 include/net/devlink.h                    |   8 +
 include/uapi/linux/devlink.h             |   2 +
 net/devlink/netlink.c                    |   2 +-
 net/devlink/netlink_gen.c                |  32 ++-
 net/devlink/netlink_gen.h                |   6 +-
 net/devlink/port.c                       |   3 +
 net/devlink/resource.c                   | 282 ++++++++++++++++++-----
 8 files changed, 301 insertions(+), 57 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/net/devlink.h b/include/net/devlink.h
index cb839e0435a1..40335ecc3343 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -129,6 +129,7 @@ struct devlink_rate {
 struct devlink_port {
        struct list_head list;
        struct list_head region_list;
+       struct list_head resource_list;
        struct devlink *devlink;
        const struct devlink_port_ops *ops;
        unsigned int index;
@@ -1881,6 +1882,13 @@ void devlink_resources_unregister(struct devlink 
*devlink);
 int devl_resource_size_get(struct devlink *devlink,
                           u64 resource_id,
                           u64 *p_resource_size);
+int
+devl_port_resource_register(struct devlink_port *devlink_port,
+                           const char *resource_name,
+                           u64 resource_size, u64 resource_id,
+                           u64 parent_resource_id,
+                           const struct devlink_resource_size_params *params);
+void devl_port_resources_unregister(struct devlink_port *devlink_port);
 int devl_dpipe_table_resource_set(struct devlink *devlink,
                                  const char *table_name, u64 resource_id,
                                  u64 resource_units);
diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
index e7d6b6d13470..74a541013af6 100644
--- a/include/uapi/linux/devlink.h
+++ b/include/uapi/linux/devlink.h
@@ -141,6 +141,8 @@ enum devlink_command {
 
        DEVLINK_CMD_NOTIFY_FILTER_SET,
 
+       DEVLINK_CMD_PORT_RESOURCE_GET,  /* can dump */
+
        /* 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/port.c b/net/devlink/port.c
index 93d8a25bb920..10d0d88894a3 100644
--- a/net/devlink/port.c
+++ b/net/devlink/port.c
@@ -1024,6 +1024,7 @@ void devlink_port_init(struct devlink *devlink,
                return;
        devlink_port->devlink = devlink;
        INIT_LIST_HEAD(&devlink_port->region_list);
+       INIT_LIST_HEAD(&devlink_port->resource_list);
        devlink_port->initialized = true;
 }
 EXPORT_SYMBOL_GPL(devlink_port_init);
@@ -1041,6 +1042,7 @@ EXPORT_SYMBOL_GPL(devlink_port_init);
 void devlink_port_fini(struct devlink_port *devlink_port)
 {
        WARN_ON(!list_empty(&devlink_port->region_list));
+       WARN_ON(!list_empty(&devlink_port->resource_list));
 }
 EXPORT_SYMBOL_GPL(devlink_port_fini);
 
@@ -1135,6 +1137,7 @@ void devl_port_unregister(struct devlink_port 
*devlink_port)
        devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL);
        xa_erase(&devlink_port->devlink->ports, devlink_port->index);
        WARN_ON(!list_empty(&devlink_port->reporter_list));
+       devlink_port_fini(devlink_port);
        devlink_port->registered = false;
 }
 EXPORT_SYMBOL_GPL(devl_port_unregister);
diff --git a/net/devlink/resource.c b/net/devlink/resource.c
index 2d6324f3d91f..1c563c76ddb6 100644
--- a/net/devlink/resource.c
+++ b/net/devlink/resource.c
@@ -36,15 +36,16 @@ struct devlink_resource {
 };
 
 static struct devlink_resource *
-devlink_resource_find(struct devlink *devlink,
-                     struct devlink_resource *resource, u64 resource_id)
+devlink_resource_find_by_list(struct list_head *res_list_head,
+                             struct devlink_resource *resource,
+                             u64 resource_id)
 {
        struct list_head *resource_list;
 
        if (resource)
                resource_list = &resource->resource_list;
        else
-               resource_list = &devlink->resource_list;
+               resource_list = res_list_head;
 
        list_for_each_entry(resource, resource_list, list) {
                struct devlink_resource *child_resource;
@@ -52,14 +53,23 @@ devlink_resource_find(struct devlink *devlink,
                if (resource->id == resource_id)
                        return resource;
 
-               child_resource = devlink_resource_find(devlink, resource,
-                                                      resource_id);
+               child_resource = devlink_resource_find_by_list(res_list_head,
+                                                              resource,
+                                                              resource_id);
                if (child_resource)
                        return child_resource;
        }
        return NULL;
 }
 
+static struct devlink_resource *
+devlink_resource_find(struct devlink *devlink,
+                     struct devlink_resource *resource, u64 resource_id)
+{
+       return devlink_resource_find_by_list(&devlink->resource_list,
+                                            resource, resource_id);
+}
+
 static void
 devlink_resource_validate_children(struct devlink_resource *resource)
 {
@@ -214,8 +224,10 @@ static int devlink_resource_put(struct devlink *devlink, 
struct sk_buff *skb,
 }
 
 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;
@@ -226,8 +238,11 @@ static int devlink_resource_fill(struct genl_info *info,
        int i;
        int err;
 
-       resource = list_first_entry(&devlink->resource_list,
-                                   struct devlink_resource, list);
+       if (list_empty(resource_list))
+               return -EOPNOTSUPP;
+
+       resource = list_first_entry(resource_list, struct devlink_resource,
+                                   list);
 start_again:
        err = devlink_nl_msg_reply_and_new(&skb, info);
        if (err)
@@ -243,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)
@@ -250,7 +269,7 @@ static int devlink_resource_fill(struct genl_info *info,
 
        incomplete = false;
        i = 0;
-       list_for_each_entry_from(resource, &devlink->resource_list, list) {
+       list_for_each_entry_from(resource, resource_list, list) {
                err = devlink_resource_put(devlink, skb, resource);
                if (err) {
                        if (!i)
@@ -286,10 +305,8 @@ int devlink_nl_resource_dump_doit(struct sk_buff *skb, 
struct genl_info *info)
 {
        struct devlink *devlink = info->user_ptr[0];
 
-       if (list_empty(&devlink->resource_list))
-               return -EOPNOTSUPP;
-
-       return devlink_resource_fill(info, DEVLINK_CMD_RESOURCE_DUMP, 0);
+       return devlink_resource_fill(info, &devlink->resource_list,
+                                    DEVLINK_CMD_RESOURCE_DUMP, 0);
 }
 
 int devlink_resources_validate(struct devlink *devlink,
@@ -314,26 +331,12 @@ int devlink_resources_validate(struct devlink *devlink,
        return err;
 }
 
-/**
- * devl_resource_register - devlink resource register
- *
- * @devlink: devlink
- * @resource_name: resource's name
- * @resource_size: resource's size
- * @resource_id: resource's id
- * @parent_resource_id: resource's parent id
- * @size_params: size parameters
- *
- * Generic resources should reuse the same names across drivers.
- * Please see the generic resources list at:
- * Documentation/networking/devlink/devlink-resource.rst
- */
-int devl_resource_register(struct devlink *devlink,
-                          const char *resource_name,
-                          u64 resource_size,
-                          u64 resource_id,
-                          u64 parent_resource_id,
-                          const struct devlink_resource_size_params 
*size_params)
+static int
+devl_resource_reg_by_list(struct devlink *devlink,
+                         struct list_head *res_list_head,
+                         const char *resource_name, u64 resource_size,
+                         u64 resource_id, u64 parent_res_id,
+                         const struct devlink_resource_size_params *params)
 {
        struct devlink_resource *resource;
        struct list_head *resource_list;
@@ -341,9 +344,10 @@ int devl_resource_register(struct devlink *devlink,
 
        lockdep_assert_held(&devlink->lock);
 
-       top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
+       top_hierarchy = parent_res_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
 
-       resource = devlink_resource_find(devlink, NULL, resource_id);
+       resource = devlink_resource_find_by_list(res_list_head, NULL,
+                                                resource_id);
        if (resource)
                return -EEXIST;
 
@@ -352,15 +356,15 @@ int devl_resource_register(struct devlink *devlink,
                return -ENOMEM;
 
        if (top_hierarchy) {
-               resource_list = &devlink->resource_list;
+               resource_list = res_list_head;
        } else {
-               struct devlink_resource *parent_resource;
+               struct devlink_resource *parent_res;
 
-               parent_resource = devlink_resource_find(devlink, NULL,
-                                                       parent_resource_id);
-               if (parent_resource) {
-                       resource_list = &parent_resource->resource_list;
-                       resource->parent = parent_resource;
+               parent_res = devlink_resource_find_by_list(res_list_head, NULL,
+                                                          parent_res_id);
+               if (parent_res) {
+                       resource_list = &parent_res->resource_list;
+                       resource->parent = parent_res;
                } else {
                        kfree(resource);
                        return -EINVAL;
@@ -372,46 +376,78 @@ int devl_resource_register(struct devlink *devlink,
        resource->size_new = resource_size;
        resource->id = resource_id;
        resource->size_valid = true;
-       memcpy(&resource->size_params, size_params,
-              sizeof(resource->size_params));
+       memcpy(&resource->size_params, params, sizeof(resource->size_params));
        INIT_LIST_HEAD(&resource->resource_list);
        list_add_tail(&resource->list, resource_list);
 
        return 0;
 }
+
+/**
+ * devl_resource_register - devlink resource register
+ *
+ * @devlink: devlink
+ * @resource_name: resource's name
+ * @resource_size: resource's size
+ * @resource_id: resource's id
+ * @parent_resource_id: resource's parent id
+ * @params: size parameters
+ *
+ * Generic resources should reuse the same names across drivers.
+ * Please see the generic resources list at:
+ * Documentation/networking/devlink/devlink-resource.rst
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+int devl_resource_register(struct devlink *devlink, const char *resource_name,
+                          u64 resource_size, u64 resource_id,
+                          u64 parent_resource_id,
+                          const struct devlink_resource_size_params *params)
+{
+       return devl_resource_reg_by_list(devlink, &devlink->resource_list,
+                                             resource_name, resource_size,
+                                             resource_id, parent_resource_id,
+                                             params);
+}
 EXPORT_SYMBOL_GPL(devl_resource_register);
 
-static void devlink_resource_unregister(struct devlink *devlink,
-                                       struct devlink_resource *resource)
+static void devlink_resource_unregister(struct devlink_resource *resource)
 {
        struct devlink_resource *tmp, *child_resource;
 
        list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
                                 list) {
-               devlink_resource_unregister(devlink, child_resource);
+               devlink_resource_unregister(child_resource);
                list_del(&child_resource->list);
                kfree(child_resource);
        }
 }
 
-/**
- * devl_resources_unregister - free all resources
- *
- * @devlink: devlink
- */
-void devl_resources_unregister(struct devlink *devlink)
+static void
+devl_resources_unregister_by_list(struct devlink *devlink,
+                                 struct list_head *res_list_head)
 {
        struct devlink_resource *tmp, *child_resource;
 
        lockdep_assert_held(&devlink->lock);
 
-       list_for_each_entry_safe(child_resource, tmp, &devlink->resource_list,
+       list_for_each_entry_safe(child_resource, tmp, res_list_head,
                                 list) {
-               devlink_resource_unregister(devlink, child_resource);
+               devlink_resource_unregister(child_resource);
                list_del(&child_resource->list);
                kfree(child_resource);
        }
 }
+
+/**
+ * devl_resources_unregister - free all resources
+ *
+ * @devlink: devlink
+ */
+void devl_resources_unregister(struct devlink *devlink)
+{
+       devl_resources_unregister_by_list(devlink, &devlink->resource_list);
+}
 EXPORT_SYMBOL_GPL(devl_resources_unregister);
 
 /**
@@ -502,3 +538,141 @@ void devl_resource_occ_get_unregister(struct devlink 
*devlink,
        resource->occ_get_priv = NULL;
 }
 EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);
+
+/**
+ * devl_port_resource_register - devlink port resource register
+ *
+ * @devlink_port: devlink port
+ * @resource_name: resource's name
+ * @resource_size: resource's size
+ * @resource_id: resource's id
+ * @parent_resource_id: resource's parent id
+ * @params: size parameters for the resource
+ *
+ * Generic resources should reuse the same names across drivers.
+ * Please see the generic resources list at:
+ * Documentation/networking/devlink/devlink-resource.rst
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+int
+devl_port_resource_register(struct devlink_port *devlink_port,
+                           const char *resource_name,
+                           u64 resource_size, u64 resource_id,
+                           u64 parent_resource_id,
+                           const struct devlink_resource_size_params *params)
+{
+       return devl_resource_reg_by_list(devlink_port->devlink,
+                                        &devlink_port->resource_list,
+                                        resource_name, resource_size,
+                                        resource_id, parent_resource_id,
+                                        params);
+}
+EXPORT_SYMBOL_GPL(devl_port_resource_register);
+
+/**
+ * devl_port_resources_unregister - unregister all devlink port resources
+ *
+ * @devlink_port: devlink port
+ */
+void devl_port_resources_unregister(struct devlink_port *devlink_port)
+{
+       devl_resources_unregister_by_list(devlink_port->devlink,
+                                         &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)
+{
+       struct devlink_nl_dump_state *state = devlink_dump_state(cb);
+       u32 cmd = DEVLINK_CMD_PORT_RESOURCE_GET;
+       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.40.1


Reply via email to