devlink: push resource related code into separate file
authorJiri Pirko <jiri@nvidia.com>
Mon, 28 Aug 2023 06:16:48 +0000 (08:16 +0200)
committerJakub Kicinski <kuba@kernel.org>
Mon, 28 Aug 2023 15:02:22 +0000 (08:02 -0700)
Cut out another chunk from leftover.c and put resource related code
into a separate file.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Link: https://lore.kernel.org/r/20230828061657.300667-7-jiri@resnulli.us
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/devlink/Makefile
net/devlink/devl_internal.h
net/devlink/leftover.c
net/devlink/resource.c [new file with mode: 0644]

index 122de1d..8864cd9 100644 (file)
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-y := leftover.o core.o netlink.o netlink_gen.o dev.o port.o sb.o dpipe.o \
-        health.o
+        resource.o health.o
index 72653e1..e5c0acf 100644 (file)
@@ -238,6 +238,8 @@ int devlink_nl_cmd_dpipe_headers_get(struct sk_buff *skb,
                                     struct genl_info *info);
 int devlink_nl_cmd_dpipe_table_counters_set(struct sk_buff *skb,
                                            struct genl_info *info);
+int devlink_nl_cmd_resource_set(struct sk_buff *skb, struct genl_info *info);
+int devlink_nl_cmd_resource_dump(struct sk_buff *skb, struct genl_info *info);
 int devlink_nl_cmd_health_reporter_set_doit(struct sk_buff *skb,
                                            struct genl_info *info);
 int devlink_nl_cmd_health_reporter_recover_doit(struct sk_buff *skb,
index f71201e..1c65449 100644 (file)
 
 #include "devl_internal.h"
 
-/**
- * struct devlink_resource - devlink resource
- * @name: name of the resource
- * @id: id, per devlink instance
- * @size: size of the resource
- * @size_new: updated size of the resource, reload is needed
- * @size_valid: valid in case the total size of the resource is valid
- *              including its children
- * @parent: parent resource
- * @size_params: size parameters
- * @list: parent list
- * @resource_list: list of child resources
- * @occ_get: occupancy getter callback
- * @occ_get_priv: occupancy getter callback priv
- */
-struct devlink_resource {
-       const char *name;
-       u64 id;
-       u64 size;
-       u64 size_new;
-       bool size_valid;
-       struct devlink_resource *parent;
-       struct devlink_resource_size_params size_params;
-       struct list_head list;
-       struct list_head resource_list;
-       devlink_resource_occ_get_t *occ_get;
-       void *occ_get_priv;
-};
-
 EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg);
 EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwerr);
 EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_trap_report);
@@ -1090,290 +1061,6 @@ int devlink_rate_nodes_check(struct devlink *devlink, u16 mode,
        return 0;
 }
 
-static struct devlink_resource *
-devlink_resource_find(struct devlink *devlink,
-                     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;
-
-       list_for_each_entry(resource, resource_list, list) {
-               struct devlink_resource *child_resource;
-
-               if (resource->id == resource_id)
-                       return resource;
-
-               child_resource = devlink_resource_find(devlink, resource,
-                                                      resource_id);
-               if (child_resource)
-                       return child_resource;
-       }
-       return NULL;
-}
-
-static void
-devlink_resource_validate_children(struct devlink_resource *resource)
-{
-       struct devlink_resource *child_resource;
-       bool size_valid = true;
-       u64 parts_size = 0;
-
-       if (list_empty(&resource->resource_list))
-               goto out;
-
-       list_for_each_entry(child_resource, &resource->resource_list, list)
-               parts_size += child_resource->size_new;
-
-       if (parts_size > resource->size_new)
-               size_valid = false;
-out:
-       resource->size_valid = size_valid;
-}
-
-static int
-devlink_resource_validate_size(struct devlink_resource *resource, u64 size,
-                              struct netlink_ext_ack *extack)
-{
-       u64 reminder;
-       int err = 0;
-
-       if (size > resource->size_params.size_max) {
-               NL_SET_ERR_MSG(extack, "Size larger than maximum");
-               err = -EINVAL;
-       }
-
-       if (size < resource->size_params.size_min) {
-               NL_SET_ERR_MSG(extack, "Size smaller than minimum");
-               err = -EINVAL;
-       }
-
-       div64_u64_rem(size, resource->size_params.size_granularity, &reminder);
-       if (reminder) {
-               NL_SET_ERR_MSG(extack, "Wrong granularity");
-               err = -EINVAL;
-       }
-
-       return err;
-}
-
-static int devlink_nl_cmd_resource_set(struct sk_buff *skb,
-                                      struct genl_info *info)
-{
-       struct devlink *devlink = info->user_ptr[0];
-       struct devlink_resource *resource;
-       u64 resource_id;
-       u64 size;
-       int err;
-
-       if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_ID) ||
-           GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_SIZE))
-               return -EINVAL;
-       resource_id = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_ID]);
-
-       resource = devlink_resource_find(devlink, NULL, resource_id);
-       if (!resource)
-               return -EINVAL;
-
-       size = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_SIZE]);
-       err = devlink_resource_validate_size(resource, size, info->extack);
-       if (err)
-               return err;
-
-       resource->size_new = size;
-       devlink_resource_validate_children(resource);
-       if (resource->parent)
-               devlink_resource_validate_children(resource->parent);
-       return 0;
-}
-
-static int
-devlink_resource_size_params_put(struct devlink_resource *resource,
-                                struct sk_buff *skb)
-{
-       struct devlink_resource_size_params *size_params;
-
-       size_params = &resource->size_params;
-       if (nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_GRAN,
-                             size_params->size_granularity, DEVLINK_ATTR_PAD) ||
-           nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_MAX,
-                             size_params->size_max, DEVLINK_ATTR_PAD) ||
-           nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_MIN,
-                             size_params->size_min, DEVLINK_ATTR_PAD) ||
-           nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_UNIT, size_params->unit))
-               return -EMSGSIZE;
-       return 0;
-}
-
-static int devlink_resource_occ_put(struct devlink_resource *resource,
-                                   struct sk_buff *skb)
-{
-       if (!resource->occ_get)
-               return 0;
-       return nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_OCC,
-                                resource->occ_get(resource->occ_get_priv),
-                                DEVLINK_ATTR_PAD);
-}
-
-static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb,
-                               struct devlink_resource *resource)
-{
-       struct devlink_resource *child_resource;
-       struct nlattr *child_resource_attr;
-       struct nlattr *resource_attr;
-
-       resource_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE);
-       if (!resource_attr)
-               return -EMSGSIZE;
-
-       if (nla_put_string(skb, DEVLINK_ATTR_RESOURCE_NAME, resource->name) ||
-           nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE, resource->size,
-                             DEVLINK_ATTR_PAD) ||
-           nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_ID, resource->id,
-                             DEVLINK_ATTR_PAD))
-               goto nla_put_failure;
-       if (resource->size != resource->size_new &&
-           nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_NEW,
-                             resource->size_new, DEVLINK_ATTR_PAD))
-               goto nla_put_failure;
-       if (devlink_resource_occ_put(resource, skb))
-               goto nla_put_failure;
-       if (devlink_resource_size_params_put(resource, skb))
-               goto nla_put_failure;
-       if (list_empty(&resource->resource_list))
-               goto out;
-
-       if (nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_SIZE_VALID,
-                      resource->size_valid))
-               goto nla_put_failure;
-
-       child_resource_attr = nla_nest_start_noflag(skb,
-                                                   DEVLINK_ATTR_RESOURCE_LIST);
-       if (!child_resource_attr)
-               goto nla_put_failure;
-
-       list_for_each_entry(child_resource, &resource->resource_list, list) {
-               if (devlink_resource_put(devlink, skb, child_resource))
-                       goto resource_put_failure;
-       }
-
-       nla_nest_end(skb, child_resource_attr);
-out:
-       nla_nest_end(skb, resource_attr);
-       return 0;
-
-resource_put_failure:
-       nla_nest_cancel(skb, child_resource_attr);
-nla_put_failure:
-       nla_nest_cancel(skb, resource_attr);
-       return -EMSGSIZE;
-}
-
-static int devlink_resource_fill(struct genl_info *info,
-                                enum devlink_command cmd, int flags)
-{
-       struct devlink *devlink = info->user_ptr[0];
-       struct devlink_resource *resource;
-       struct nlattr *resources_attr;
-       struct sk_buff *skb = NULL;
-       struct nlmsghdr *nlh;
-       bool incomplete;
-       void *hdr;
-       int i;
-       int err;
-
-       resource = list_first_entry(&devlink->resource_list,
-                                   struct devlink_resource, list);
-start_again:
-       err = devlink_nl_msg_reply_and_new(&skb, info);
-       if (err)
-               return err;
-
-       hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
-                         &devlink_nl_family, NLM_F_MULTI, cmd);
-       if (!hdr) {
-               nlmsg_free(skb);
-               return -EMSGSIZE;
-       }
-
-       if (devlink_nl_put_handle(skb, devlink))
-               goto nla_put_failure;
-
-       resources_attr = nla_nest_start_noflag(skb,
-                                              DEVLINK_ATTR_RESOURCE_LIST);
-       if (!resources_attr)
-               goto nla_put_failure;
-
-       incomplete = false;
-       i = 0;
-       list_for_each_entry_from(resource, &devlink->resource_list, list) {
-               err = devlink_resource_put(devlink, skb, resource);
-               if (err) {
-                       if (!i)
-                               goto err_resource_put;
-                       incomplete = true;
-                       break;
-               }
-               i++;
-       }
-       nla_nest_end(skb, resources_attr);
-       genlmsg_end(skb, hdr);
-       if (incomplete)
-               goto start_again;
-send_done:
-       nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
-                       NLMSG_DONE, 0, flags | NLM_F_MULTI);
-       if (!nlh) {
-               err = devlink_nl_msg_reply_and_new(&skb, info);
-               if (err)
-                       return err;
-               goto send_done;
-       }
-       return genlmsg_reply(skb, info);
-
-nla_put_failure:
-       err = -EMSGSIZE;
-err_resource_put:
-       nlmsg_free(skb);
-       return err;
-}
-
-static int devlink_nl_cmd_resource_dump(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);
-}
-
-int devlink_resources_validate(struct devlink *devlink,
-                              struct devlink_resource *resource,
-                              struct genl_info *info)
-{
-       struct list_head *resource_list;
-       int err = 0;
-
-       if (resource)
-               resource_list = &resource->resource_list;
-       else
-               resource_list = &devlink->resource_list;
-
-       list_for_each_entry(resource, resource_list, list) {
-               if (!resource->size_valid)
-                       return -EINVAL;
-               err = devlink_resources_validate(devlink, resource, info);
-               if (err)
-                       return err;
-       }
-       return err;
-}
-
 static const struct devlink_param devlink_param_generic[] = {
        {
                .id = DEVLINK_PARAM_GENERIC_ID_INT_ERR_RESET,
@@ -4529,267 +4216,6 @@ void devlink_linecard_nested_dl_set(struct devlink_linecard *linecard,
 }
 EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set);
 
-/**
- * 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)
-{
-       struct devlink_resource *resource;
-       struct list_head *resource_list;
-       bool top_hierarchy;
-
-       lockdep_assert_held(&devlink->lock);
-
-       top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
-
-       resource = devlink_resource_find(devlink, NULL, resource_id);
-       if (resource)
-               return -EINVAL;
-
-       resource = kzalloc(sizeof(*resource), GFP_KERNEL);
-       if (!resource)
-               return -ENOMEM;
-
-       if (top_hierarchy) {
-               resource_list = &devlink->resource_list;
-       } else {
-               struct devlink_resource *parent_resource;
-
-               parent_resource = devlink_resource_find(devlink, NULL,
-                                                       parent_resource_id);
-               if (parent_resource) {
-                       resource_list = &parent_resource->resource_list;
-                       resource->parent = parent_resource;
-               } else {
-                       kfree(resource);
-                       return -EINVAL;
-               }
-       }
-
-       resource->name = resource_name;
-       resource->size = resource_size;
-       resource->size_new = resource_size;
-       resource->id = resource_id;
-       resource->size_valid = true;
-       memcpy(&resource->size_params, size_params,
-              sizeof(resource->size_params));
-       INIT_LIST_HEAD(&resource->resource_list);
-       list_add_tail(&resource->list, resource_list);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(devl_resource_register);
-
-/**
- *     devlink_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
- *
- *     Context: Takes and release devlink->lock <mutex>.
- */
-int devlink_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)
-{
-       int err;
-
-       devl_lock(devlink);
-       err = devl_resource_register(devlink, resource_name, resource_size,
-                                    resource_id, parent_resource_id, size_params);
-       devl_unlock(devlink);
-       return err;
-}
-EXPORT_SYMBOL_GPL(devlink_resource_register);
-
-static void devlink_resource_unregister(struct devlink *devlink,
-                                       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);
-               list_del(&child_resource->list);
-               kfree(child_resource);
-       }
-}
-
-/**
- * devl_resources_unregister - free all resources
- *
- * @devlink: devlink
- */
-void devl_resources_unregister(struct devlink *devlink)
-{
-       struct devlink_resource *tmp, *child_resource;
-
-       lockdep_assert_held(&devlink->lock);
-
-       list_for_each_entry_safe(child_resource, tmp, &devlink->resource_list,
-                                list) {
-               devlink_resource_unregister(devlink, child_resource);
-               list_del(&child_resource->list);
-               kfree(child_resource);
-       }
-}
-EXPORT_SYMBOL_GPL(devl_resources_unregister);
-
-/**
- *     devlink_resources_unregister - free all resources
- *
- *     @devlink: devlink
- *
- *     Context: Takes and release devlink->lock <mutex>.
- */
-void devlink_resources_unregister(struct devlink *devlink)
-{
-       devl_lock(devlink);
-       devl_resources_unregister(devlink);
-       devl_unlock(devlink);
-}
-EXPORT_SYMBOL_GPL(devlink_resources_unregister);
-
-/**
- * devl_resource_size_get - get and update size
- *
- * @devlink: devlink
- * @resource_id: the requested resource id
- * @p_resource_size: ptr to update
- */
-int devl_resource_size_get(struct devlink *devlink,
-                          u64 resource_id,
-                          u64 *p_resource_size)
-{
-       struct devlink_resource *resource;
-
-       lockdep_assert_held(&devlink->lock);
-
-       resource = devlink_resource_find(devlink, NULL, resource_id);
-       if (!resource)
-               return -EINVAL;
-       *p_resource_size = resource->size_new;
-       resource->size = resource->size_new;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(devl_resource_size_get);
-
-/**
- * devl_resource_occ_get_register - register occupancy getter
- *
- * @devlink: devlink
- * @resource_id: resource id
- * @occ_get: occupancy getter callback
- * @occ_get_priv: occupancy getter callback priv
- */
-void devl_resource_occ_get_register(struct devlink *devlink,
-                                   u64 resource_id,
-                                   devlink_resource_occ_get_t *occ_get,
-                                   void *occ_get_priv)
-{
-       struct devlink_resource *resource;
-
-       lockdep_assert_held(&devlink->lock);
-
-       resource = devlink_resource_find(devlink, NULL, resource_id);
-       if (WARN_ON(!resource))
-               return;
-       WARN_ON(resource->occ_get);
-
-       resource->occ_get = occ_get;
-       resource->occ_get_priv = occ_get_priv;
-}
-EXPORT_SYMBOL_GPL(devl_resource_occ_get_register);
-
-/**
- *     devlink_resource_occ_get_register - register occupancy getter
- *
- *     @devlink: devlink
- *     @resource_id: resource id
- *     @occ_get: occupancy getter callback
- *     @occ_get_priv: occupancy getter callback priv
- *
- *     Context: Takes and release devlink->lock <mutex>.
- */
-void devlink_resource_occ_get_register(struct devlink *devlink,
-                                      u64 resource_id,
-                                      devlink_resource_occ_get_t *occ_get,
-                                      void *occ_get_priv)
-{
-       devl_lock(devlink);
-       devl_resource_occ_get_register(devlink, resource_id,
-                                      occ_get, occ_get_priv);
-       devl_unlock(devlink);
-}
-EXPORT_SYMBOL_GPL(devlink_resource_occ_get_register);
-
-/**
- * devl_resource_occ_get_unregister - unregister occupancy getter
- *
- * @devlink: devlink
- * @resource_id: resource id
- */
-void devl_resource_occ_get_unregister(struct devlink *devlink,
-                                     u64 resource_id)
-{
-       struct devlink_resource *resource;
-
-       lockdep_assert_held(&devlink->lock);
-
-       resource = devlink_resource_find(devlink, NULL, resource_id);
-       if (WARN_ON(!resource))
-               return;
-       WARN_ON(!resource->occ_get);
-
-       resource->occ_get = NULL;
-       resource->occ_get_priv = NULL;
-}
-EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);
-
-/**
- *     devlink_resource_occ_get_unregister - unregister occupancy getter
- *
- *     @devlink: devlink
- *     @resource_id: resource id
- *
- *     Context: Takes and release devlink->lock <mutex>.
- */
-void devlink_resource_occ_get_unregister(struct devlink *devlink,
-                                        u64 resource_id)
-{
-       devl_lock(devlink);
-       devl_resource_occ_get_unregister(devlink, resource_id);
-       devl_unlock(devlink);
-}
-EXPORT_SYMBOL_GPL(devlink_resource_occ_get_unregister);
-
 static int devlink_param_verify(const struct devlink_param *param)
 {
        if (!param || !param->name || !param->supported_cmodes)
diff --git a/net/devlink/resource.c b/net/devlink/resource.c
new file mode 100644 (file)
index 0000000..c8b615e
--- /dev/null
@@ -0,0 +1,579 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
+ */
+
+#include "devl_internal.h"
+
+/**
+ * struct devlink_resource - devlink resource
+ * @name: name of the resource
+ * @id: id, per devlink instance
+ * @size: size of the resource
+ * @size_new: updated size of the resource, reload is needed
+ * @size_valid: valid in case the total size of the resource is valid
+ *              including its children
+ * @parent: parent resource
+ * @size_params: size parameters
+ * @list: parent list
+ * @resource_list: list of child resources
+ * @occ_get: occupancy getter callback
+ * @occ_get_priv: occupancy getter callback priv
+ */
+struct devlink_resource {
+       const char *name;
+       u64 id;
+       u64 size;
+       u64 size_new;
+       bool size_valid;
+       struct devlink_resource *parent;
+       struct devlink_resource_size_params size_params;
+       struct list_head list;
+       struct list_head resource_list;
+       devlink_resource_occ_get_t *occ_get;
+       void *occ_get_priv;
+};
+
+static struct devlink_resource *
+devlink_resource_find(struct devlink *devlink,
+                     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;
+
+       list_for_each_entry(resource, resource_list, list) {
+               struct devlink_resource *child_resource;
+
+               if (resource->id == resource_id)
+                       return resource;
+
+               child_resource = devlink_resource_find(devlink, resource,
+                                                      resource_id);
+               if (child_resource)
+                       return child_resource;
+       }
+       return NULL;
+}
+
+static void
+devlink_resource_validate_children(struct devlink_resource *resource)
+{
+       struct devlink_resource *child_resource;
+       bool size_valid = true;
+       u64 parts_size = 0;
+
+       if (list_empty(&resource->resource_list))
+               goto out;
+
+       list_for_each_entry(child_resource, &resource->resource_list, list)
+               parts_size += child_resource->size_new;
+
+       if (parts_size > resource->size_new)
+               size_valid = false;
+out:
+       resource->size_valid = size_valid;
+}
+
+static int
+devlink_resource_validate_size(struct devlink_resource *resource, u64 size,
+                              struct netlink_ext_ack *extack)
+{
+       u64 reminder;
+       int err = 0;
+
+       if (size > resource->size_params.size_max) {
+               NL_SET_ERR_MSG(extack, "Size larger than maximum");
+               err = -EINVAL;
+       }
+
+       if (size < resource->size_params.size_min) {
+               NL_SET_ERR_MSG(extack, "Size smaller than minimum");
+               err = -EINVAL;
+       }
+
+       div64_u64_rem(size, resource->size_params.size_granularity, &reminder);
+       if (reminder) {
+               NL_SET_ERR_MSG(extack, "Wrong granularity");
+               err = -EINVAL;
+       }
+
+       return err;
+}
+
+int devlink_nl_cmd_resource_set(struct sk_buff *skb, struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct devlink_resource *resource;
+       u64 resource_id;
+       u64 size;
+       int err;
+
+       if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_ID) ||
+           GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_SIZE))
+               return -EINVAL;
+       resource_id = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_ID]);
+
+       resource = devlink_resource_find(devlink, NULL, resource_id);
+       if (!resource)
+               return -EINVAL;
+
+       size = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_SIZE]);
+       err = devlink_resource_validate_size(resource, size, info->extack);
+       if (err)
+               return err;
+
+       resource->size_new = size;
+       devlink_resource_validate_children(resource);
+       if (resource->parent)
+               devlink_resource_validate_children(resource->parent);
+       return 0;
+}
+
+static int
+devlink_resource_size_params_put(struct devlink_resource *resource,
+                                struct sk_buff *skb)
+{
+       struct devlink_resource_size_params *size_params;
+
+       size_params = &resource->size_params;
+       if (nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_GRAN,
+                             size_params->size_granularity, DEVLINK_ATTR_PAD) ||
+           nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_MAX,
+                             size_params->size_max, DEVLINK_ATTR_PAD) ||
+           nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_MIN,
+                             size_params->size_min, DEVLINK_ATTR_PAD) ||
+           nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_UNIT, size_params->unit))
+               return -EMSGSIZE;
+       return 0;
+}
+
+static int devlink_resource_occ_put(struct devlink_resource *resource,
+                                   struct sk_buff *skb)
+{
+       if (!resource->occ_get)
+               return 0;
+       return nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_OCC,
+                                resource->occ_get(resource->occ_get_priv),
+                                DEVLINK_ATTR_PAD);
+}
+
+static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb,
+                               struct devlink_resource *resource)
+{
+       struct devlink_resource *child_resource;
+       struct nlattr *child_resource_attr;
+       struct nlattr *resource_attr;
+
+       resource_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE);
+       if (!resource_attr)
+               return -EMSGSIZE;
+
+       if (nla_put_string(skb, DEVLINK_ATTR_RESOURCE_NAME, resource->name) ||
+           nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE, resource->size,
+                             DEVLINK_ATTR_PAD) ||
+           nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_ID, resource->id,
+                             DEVLINK_ATTR_PAD))
+               goto nla_put_failure;
+       if (resource->size != resource->size_new &&
+           nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_NEW,
+                             resource->size_new, DEVLINK_ATTR_PAD))
+               goto nla_put_failure;
+       if (devlink_resource_occ_put(resource, skb))
+               goto nla_put_failure;
+       if (devlink_resource_size_params_put(resource, skb))
+               goto nla_put_failure;
+       if (list_empty(&resource->resource_list))
+               goto out;
+
+       if (nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_SIZE_VALID,
+                      resource->size_valid))
+               goto nla_put_failure;
+
+       child_resource_attr = nla_nest_start_noflag(skb,
+                                                   DEVLINK_ATTR_RESOURCE_LIST);
+       if (!child_resource_attr)
+               goto nla_put_failure;
+
+       list_for_each_entry(child_resource, &resource->resource_list, list) {
+               if (devlink_resource_put(devlink, skb, child_resource))
+                       goto resource_put_failure;
+       }
+
+       nla_nest_end(skb, child_resource_attr);
+out:
+       nla_nest_end(skb, resource_attr);
+       return 0;
+
+resource_put_failure:
+       nla_nest_cancel(skb, child_resource_attr);
+nla_put_failure:
+       nla_nest_cancel(skb, resource_attr);
+       return -EMSGSIZE;
+}
+
+static int devlink_resource_fill(struct genl_info *info,
+                                enum devlink_command cmd, int flags)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct devlink_resource *resource;
+       struct nlattr *resources_attr;
+       struct sk_buff *skb = NULL;
+       struct nlmsghdr *nlh;
+       bool incomplete;
+       void *hdr;
+       int i;
+       int err;
+
+       resource = list_first_entry(&devlink->resource_list,
+                                   struct devlink_resource, list);
+start_again:
+       err = devlink_nl_msg_reply_and_new(&skb, info);
+       if (err)
+               return err;
+
+       hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
+                         &devlink_nl_family, NLM_F_MULTI, cmd);
+       if (!hdr) {
+               nlmsg_free(skb);
+               return -EMSGSIZE;
+       }
+
+       if (devlink_nl_put_handle(skb, devlink))
+               goto nla_put_failure;
+
+       resources_attr = nla_nest_start_noflag(skb,
+                                              DEVLINK_ATTR_RESOURCE_LIST);
+       if (!resources_attr)
+               goto nla_put_failure;
+
+       incomplete = false;
+       i = 0;
+       list_for_each_entry_from(resource, &devlink->resource_list, list) {
+               err = devlink_resource_put(devlink, skb, resource);
+               if (err) {
+                       if (!i)
+                               goto err_resource_put;
+                       incomplete = true;
+                       break;
+               }
+               i++;
+       }
+       nla_nest_end(skb, resources_attr);
+       genlmsg_end(skb, hdr);
+       if (incomplete)
+               goto start_again;
+send_done:
+       nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
+                       NLMSG_DONE, 0, flags | NLM_F_MULTI);
+       if (!nlh) {
+               err = devlink_nl_msg_reply_and_new(&skb, info);
+               if (err)
+                       return err;
+               goto send_done;
+       }
+       return genlmsg_reply(skb, info);
+
+nla_put_failure:
+       err = -EMSGSIZE;
+err_resource_put:
+       nlmsg_free(skb);
+       return err;
+}
+
+int devlink_nl_cmd_resource_dump(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);
+}
+
+int devlink_resources_validate(struct devlink *devlink,
+                              struct devlink_resource *resource,
+                              struct genl_info *info)
+{
+       struct list_head *resource_list;
+       int err = 0;
+
+       if (resource)
+               resource_list = &resource->resource_list;
+       else
+               resource_list = &devlink->resource_list;
+
+       list_for_each_entry(resource, resource_list, list) {
+               if (!resource->size_valid)
+                       return -EINVAL;
+               err = devlink_resources_validate(devlink, resource, info);
+               if (err)
+                       return err;
+       }
+       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)
+{
+       struct devlink_resource *resource;
+       struct list_head *resource_list;
+       bool top_hierarchy;
+
+       lockdep_assert_held(&devlink->lock);
+
+       top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
+
+       resource = devlink_resource_find(devlink, NULL, resource_id);
+       if (resource)
+               return -EINVAL;
+
+       resource = kzalloc(sizeof(*resource), GFP_KERNEL);
+       if (!resource)
+               return -ENOMEM;
+
+       if (top_hierarchy) {
+               resource_list = &devlink->resource_list;
+       } else {
+               struct devlink_resource *parent_resource;
+
+               parent_resource = devlink_resource_find(devlink, NULL,
+                                                       parent_resource_id);
+               if (parent_resource) {
+                       resource_list = &parent_resource->resource_list;
+                       resource->parent = parent_resource;
+               } else {
+                       kfree(resource);
+                       return -EINVAL;
+               }
+       }
+
+       resource->name = resource_name;
+       resource->size = resource_size;
+       resource->size_new = resource_size;
+       resource->id = resource_id;
+       resource->size_valid = true;
+       memcpy(&resource->size_params, size_params,
+              sizeof(resource->size_params));
+       INIT_LIST_HEAD(&resource->resource_list);
+       list_add_tail(&resource->list, resource_list);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devl_resource_register);
+
+/**
+ *     devlink_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
+ *
+ *     Context: Takes and release devlink->lock <mutex>.
+ */
+int devlink_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)
+{
+       int err;
+
+       devl_lock(devlink);
+       err = devl_resource_register(devlink, resource_name, resource_size,
+                                    resource_id, parent_resource_id, size_params);
+       devl_unlock(devlink);
+       return err;
+}
+EXPORT_SYMBOL_GPL(devlink_resource_register);
+
+static void devlink_resource_unregister(struct devlink *devlink,
+                                       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);
+               list_del(&child_resource->list);
+               kfree(child_resource);
+       }
+}
+
+/**
+ * devl_resources_unregister - free all resources
+ *
+ * @devlink: devlink
+ */
+void devl_resources_unregister(struct devlink *devlink)
+{
+       struct devlink_resource *tmp, *child_resource;
+
+       lockdep_assert_held(&devlink->lock);
+
+       list_for_each_entry_safe(child_resource, tmp, &devlink->resource_list,
+                                list) {
+               devlink_resource_unregister(devlink, child_resource);
+               list_del(&child_resource->list);
+               kfree(child_resource);
+       }
+}
+EXPORT_SYMBOL_GPL(devl_resources_unregister);
+
+/**
+ *     devlink_resources_unregister - free all resources
+ *
+ *     @devlink: devlink
+ *
+ *     Context: Takes and release devlink->lock <mutex>.
+ */
+void devlink_resources_unregister(struct devlink *devlink)
+{
+       devl_lock(devlink);
+       devl_resources_unregister(devlink);
+       devl_unlock(devlink);
+}
+EXPORT_SYMBOL_GPL(devlink_resources_unregister);
+
+/**
+ * devl_resource_size_get - get and update size
+ *
+ * @devlink: devlink
+ * @resource_id: the requested resource id
+ * @p_resource_size: ptr to update
+ */
+int devl_resource_size_get(struct devlink *devlink,
+                          u64 resource_id,
+                          u64 *p_resource_size)
+{
+       struct devlink_resource *resource;
+
+       lockdep_assert_held(&devlink->lock);
+
+       resource = devlink_resource_find(devlink, NULL, resource_id);
+       if (!resource)
+               return -EINVAL;
+       *p_resource_size = resource->size_new;
+       resource->size = resource->size_new;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devl_resource_size_get);
+
+/**
+ * devl_resource_occ_get_register - register occupancy getter
+ *
+ * @devlink: devlink
+ * @resource_id: resource id
+ * @occ_get: occupancy getter callback
+ * @occ_get_priv: occupancy getter callback priv
+ */
+void devl_resource_occ_get_register(struct devlink *devlink,
+                                   u64 resource_id,
+                                   devlink_resource_occ_get_t *occ_get,
+                                   void *occ_get_priv)
+{
+       struct devlink_resource *resource;
+
+       lockdep_assert_held(&devlink->lock);
+
+       resource = devlink_resource_find(devlink, NULL, resource_id);
+       if (WARN_ON(!resource))
+               return;
+       WARN_ON(resource->occ_get);
+
+       resource->occ_get = occ_get;
+       resource->occ_get_priv = occ_get_priv;
+}
+EXPORT_SYMBOL_GPL(devl_resource_occ_get_register);
+
+/**
+ *     devlink_resource_occ_get_register - register occupancy getter
+ *
+ *     @devlink: devlink
+ *     @resource_id: resource id
+ *     @occ_get: occupancy getter callback
+ *     @occ_get_priv: occupancy getter callback priv
+ *
+ *     Context: Takes and release devlink->lock <mutex>.
+ */
+void devlink_resource_occ_get_register(struct devlink *devlink,
+                                      u64 resource_id,
+                                      devlink_resource_occ_get_t *occ_get,
+                                      void *occ_get_priv)
+{
+       devl_lock(devlink);
+       devl_resource_occ_get_register(devlink, resource_id,
+                                      occ_get, occ_get_priv);
+       devl_unlock(devlink);
+}
+EXPORT_SYMBOL_GPL(devlink_resource_occ_get_register);
+
+/**
+ * devl_resource_occ_get_unregister - unregister occupancy getter
+ *
+ * @devlink: devlink
+ * @resource_id: resource id
+ */
+void devl_resource_occ_get_unregister(struct devlink *devlink,
+                                     u64 resource_id)
+{
+       struct devlink_resource *resource;
+
+       lockdep_assert_held(&devlink->lock);
+
+       resource = devlink_resource_find(devlink, NULL, resource_id);
+       if (WARN_ON(!resource))
+               return;
+       WARN_ON(!resource->occ_get);
+
+       resource->occ_get = NULL;
+       resource->occ_get_priv = NULL;
+}
+EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);
+
+/**
+ *     devlink_resource_occ_get_unregister - unregister occupancy getter
+ *
+ *     @devlink: devlink
+ *     @resource_id: resource id
+ *
+ *     Context: Takes and release devlink->lock <mutex>.
+ */
+void devlink_resource_occ_get_unregister(struct devlink *devlink,
+                                        u64 resource_id)
+{
+       devl_lock(devlink);
+       devl_resource_occ_get_unregister(devlink, resource_id);
+       devl_unlock(devlink);
+}
+EXPORT_SYMBOL_GPL(devlink_resource_occ_get_unregister);