diff mbox series

[net-next,RFC,v2,02/11] devlink: Add support for resource abstraction

Message ID 20171114161852.6633-3-jiri@resnulli.us
State RFC, archived
Delegated to: David Miller
Headers show
Series Add support for resource abstraction | expand

Commit Message

Jiri Pirko Nov. 14, 2017, 4:18 p.m. UTC
From: Arkadi Sharshevsky <arkadis@mellanox.com>

Add support for hardware resource abstraction over devlink. Each resource
is identified via id, furthermore it contains information regarding its
size and its related sub resources. Each resource can also provide its
current occupancy.

In some cases the sizes of some resources can be changed, yet for those
changes to take place a hot driver reload may be needed. The reload
capability will be introduced in the next patch.

Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 include/net/devlink.h        |  80 +++++++++++
 include/uapi/linux/devlink.h |  10 ++
 net/core/devlink.c           | 330 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 420 insertions(+)

Comments

Jakub Kicinski Nov. 15, 2017, 7:59 a.m. UTC | #1
On Tue, 14 Nov 2017 17:18:43 +0100, Jiri Pirko wrote:
> From: Arkadi Sharshevsky <arkadis@mellanox.com>
> 
> Add support for hardware resource abstraction over devlink. Each resource
> is identified via id, furthermore it contains information regarding its
> size and its related sub resources. Each resource can also provide its
> current occupancy.
> 
> In some cases the sizes of some resources can be changed, yet for those
> changes to take place a hot driver reload may be needed. The reload
> capability will be introduced in the next patch.
> 
> Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
> Signed-off-by: Jiri Pirko <jiri@mellanox.com>
> ---
>  include/net/devlink.h        |  80 +++++++++++
>  include/uapi/linux/devlink.h |  10 ++
>  net/core/devlink.c           | 330 +++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 420 insertions(+)
> 
> diff --git a/include/net/devlink.h b/include/net/devlink.h
> index 4d2c6fc..960e80a 100644
> --- a/include/net/devlink.h
> +++ b/include/net/devlink.h
> @@ -224,6 +225,45 @@ struct devlink_dpipe_headers {
>  	unsigned int headers_count;
>  };
>  
> +/**
> + * struct devlink_resource_ops - resource ops
> + * @occ_get - get the occupied size
> + * @size_validate - validate the size of the resource before update, reload

nit:
@member: is more common and used throughout this file, rather than
@member - 

> + *                  is needed for changes to take place
> + */
> +struct devlink_resource_ops {
> +	u64 (*occ_get)(struct devlink *devlink);
> +	int (*size_validate)(struct devlink *devlink, u64 size,
> +			     struct list_head *resource_list,
> +			     struct netlink_ext_ack *extack);
> +};
Arkadi Sharshevsky Nov. 15, 2017, 11:27 a.m. UTC | #2
On 11/15/2017 09:59 AM, Jakub Kicinski wrote:
> On Tue, 14 Nov 2017 17:18:43 +0100, Jiri Pirko wrote:
>> From: Arkadi Sharshevsky <arkadis@mellanox.com>
>>
>> Add support for hardware resource abstraction over devlink. Each resource
>> is identified via id, furthermore it contains information regarding its
>> size and its related sub resources. Each resource can also provide its
>> current occupancy.
>>
>> In some cases the sizes of some resources can be changed, yet for those
>> changes to take place a hot driver reload may be needed. The reload
>> capability will be introduced in the next patch.
>>
>> Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
>> Signed-off-by: Jiri Pirko <jiri@mellanox.com>
>> ---
>>  include/net/devlink.h        |  80 +++++++++++
>>  include/uapi/linux/devlink.h |  10 ++
>>  net/core/devlink.c           | 330 +++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 420 insertions(+)
>>
>> diff --git a/include/net/devlink.h b/include/net/devlink.h
>> index 4d2c6fc..960e80a 100644
>> --- a/include/net/devlink.h
>> +++ b/include/net/devlink.h
>> @@ -224,6 +225,45 @@ struct devlink_dpipe_headers {
>>  	unsigned int headers_count;
>>  };
>>  
>> +/**
>> + * struct devlink_resource_ops - resource ops
>> + * @occ_get - get the occupied size
>> + * @size_validate - validate the size of the resource before update, reload
> 
> nit:
> @member: is more common and used throughout this file, rather than
> @member - 
> 

Will fix, thanks for the review.

>> + *                  is needed for changes to take place
>> + */
>> +struct devlink_resource_ops {
>> +	u64 (*occ_get)(struct devlink *devlink);
>> +	int (*size_validate)(struct devlink *devlink, u64 size,
>> +			     struct list_head *resource_list,
>> +			     struct netlink_ext_ack *extack);
>> +};
David Ahern Nov. 18, 2017, 6:34 p.m. UTC | #3
On 11/14/17 9:18 AM, Jiri Pirko wrote:
> diff --git a/include/net/devlink.h b/include/net/devlink.h
> index 4d2c6fc..960e80a 100644
> --- a/include/net/devlink.h
> +++ b/include/net/devlink.h
...

> @@ -469,6 +523,32 @@ devlink_dpipe_match_put(struct sk_buff *skb,
>  	return 0;
>  }
>  
> +static inline int
> +devlink_resource_register(struct devlink *devlink,
> +			  const char *resource_name,
> +			  bool top_hierarchy,
> +			  bool reload_required,
> +			  u64 resource_size,
> +			  u64 resource_id,
> +			  u64 parent_resource_id,
> +			  struct devlink_resource_ops *resource_ops)
> +{
> +	return 0;
> +}
> +
> +static inline void
> +devlink_resources_unregister(struct devlink *devlink,
> +			     struct devlink_resource *resource)
> +{
> +}
> +
> +static inline int
> +devlink_resource_size_get(struct devlink *devlink, u64 resource_id,
> +			  u64 *p_resource_size)
> +{
> +	return -EINVAL;

It's compiled out so -EOPNOTSUPP seems more appropriate.



> diff --git a/net/core/devlink.c b/net/core/devlink.c
> index 0114dfc..6ae644f 100644
> --- a/net/core/devlink.c
> +++ b/net/core/devlink.c
> +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 (!info->attrs[DEVLINK_ATTR_RESOURCE_ID] ||
> +	    !info->attrs[DEVLINK_ATTR_RESOURCE_SIZE])
> +		return -EINVAL;

several of the of the DEVLINK_ATTR_RESOURCE attributes are kernel to
user only (e.g., DEVLINK_ATTR_RESOURCE_SIZE_NEW and
DEVLINK_ATTR_RESOURCE_RELOAD_REQUIRED), so if they are given by the user
that should be an error too right?


> +	resource_id = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_ID]);

I don't see where these attributes are validated for proper size.

> +
> +	resource = devlink_resource_find(devlink, NULL, resource_id);
> +	if (!resource)
> +		return -EINVAL;
> +
> +	if (!resource->resource_ops->size_validate)
> +		return -EINVAL;

genl_info has extack; please add user messages for the above failures.


> +
> +	size = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_SIZE]);
> +	err = resource->resource_ops->size_validate(devlink, size,
> +						    &resource->resource_list,
> +						    info->extack);
> +	if (err)
> +		return err;
> +
> +	resource->size_new = size;
> +	return 0;
> +}
> +
Arkadi Sharshevsky Nov. 19, 2017, 8:17 a.m. UTC | #4
On 11/18/2017 08:34 PM, David Ahern wrote:
> On 11/14/17 9:18 AM, Jiri Pirko wrote:
>> diff --git a/include/net/devlink.h b/include/net/devlink.h
>> index 4d2c6fc..960e80a 100644
>> --- a/include/net/devlink.h
>> +++ b/include/net/devlink.h
> ...
> 
>> @@ -469,6 +523,32 @@ devlink_dpipe_match_put(struct sk_buff *skb,
>>  	return 0;
>>  }
>>  
>> +static inline int
>> +devlink_resource_register(struct devlink *devlink,
>> +			  const char *resource_name,
>> +			  bool top_hierarchy,
>> +			  bool reload_required,
>> +			  u64 resource_size,
>> +			  u64 resource_id,
>> +			  u64 parent_resource_id,
>> +			  struct devlink_resource_ops *resource_ops)
>> +{
>> +	return 0;
>> +}
>> +
>> +static inline void
>> +devlink_resources_unregister(struct devlink *devlink,
>> +			     struct devlink_resource *resource)
>> +{
>> +}
>> +
>> +static inline int
>> +devlink_resource_size_get(struct devlink *devlink, u64 resource_id,
>> +			  u64 *p_resource_size)
>> +{
>> +	return -EINVAL;
> 
> It's compiled out so -EOPNOTSUPP seems more appropriate.
> 

will fix

> 
> 
>> diff --git a/net/core/devlink.c b/net/core/devlink.c
>> index 0114dfc..6ae644f 100644
>> --- a/net/core/devlink.c
>> +++ b/net/core/devlink.c
>> +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 (!info->attrs[DEVLINK_ATTR_RESOURCE_ID] ||
>> +	    !info->attrs[DEVLINK_ATTR_RESOURCE_SIZE])
>> +		return -EINVAL;
> 
> several of the of the DEVLINK_ATTR_RESOURCE attributes are kernel to
> user only (e.g., DEVLINK_ATTR_RESOURCE_SIZE_NEW and
> DEVLINK_ATTR_RESOURCE_RELOAD_REQUIRED), so if they are given by the user
> that should be an error too right?
> 

Not sure I understood. As you see I only check for the mandatory
attributes, if the user provides not relevant data its ignored.

We use one single nla_policy for all the commands (devlink_nl_policy)

> 
>> +	resource_id = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_ID]);
> 
> I don't see where these attributes are validated for proper size.
> 

right, forgot to update the policy.

>> +
>> +	resource = devlink_resource_find(devlink, NULL, resource_id);
>> +	if (!resource)
>> +		return -EINVAL;
>> +
>> +	if (!resource->resource_ops->size_validate)
>> +		return -EINVAL;
> 
> genl_info has extack; please add user messages for the above failures.
> 

Isn't EOPNOTSUPP enough ?

> 
>> +
>> +	size = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_SIZE]);
>> +	err = resource->resource_ops->size_validate(devlink, size,
>> +						    &resource->resource_list,
>> +						    info->extack);
>> +	if (err)
>> +		return err;
>> +
>> +	resource->size_new = size;
>> +	return 0;
>> +}
>> +
>
David Ahern Nov. 19, 2017, 3:47 p.m. UTC | #5
On 11/19/17 1:17 AM, Arkadi Sharshevsky wrote:
> 
> 
> On 11/18/2017 08:34 PM, David Ahern wrote:
>> On 11/14/17 9:18 AM, Jiri Pirko wrote:
>>> diff --git a/include/net/devlink.h b/include/net/devlink.h
>>> index 4d2c6fc..960e80a 100644
>>> --- a/include/net/devlink.h
>>> +++ b/include/net/devlink.h
>> ...
>>
>>> @@ -469,6 +523,32 @@ devlink_dpipe_match_put(struct sk_buff *skb,
>>>  	return 0;
>>>  }
>>>  
>>> +static inline int
>>> +devlink_resource_register(struct devlink *devlink,
>>> +			  const char *resource_name,
>>> +			  bool top_hierarchy,
>>> +			  bool reload_required,
>>> +			  u64 resource_size,
>>> +			  u64 resource_id,
>>> +			  u64 parent_resource_id,
>>> +			  struct devlink_resource_ops *resource_ops)
>>> +{
>>> +	return 0;
>>> +}
>>> +
>>> +static inline void
>>> +devlink_resources_unregister(struct devlink *devlink,
>>> +			     struct devlink_resource *resource)
>>> +{
>>> +}
>>> +
>>> +static inline int
>>> +devlink_resource_size_get(struct devlink *devlink, u64 resource_id,
>>> +			  u64 *p_resource_size)
>>> +{
>>> +	return -EINVAL;
>>
>> It's compiled out so -EOPNOTSUPP seems more appropriate.
>>
> 
> will fix
> 
>>
>>
>>> diff --git a/net/core/devlink.c b/net/core/devlink.c
>>> index 0114dfc..6ae644f 100644
>>> --- a/net/core/devlink.c
>>> +++ b/net/core/devlink.c
>>> +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 (!info->attrs[DEVLINK_ATTR_RESOURCE_ID] ||
>>> +	    !info->attrs[DEVLINK_ATTR_RESOURCE_SIZE])
>>> +		return -EINVAL;
>>
>> several of the of the DEVLINK_ATTR_RESOURCE attributes are kernel to
>> user only (e.g., DEVLINK_ATTR_RESOURCE_SIZE_NEW and
>> DEVLINK_ATTR_RESOURCE_RELOAD_REQUIRED), so if they are given by the user
>> that should be an error too right?
>>
> 
> Not sure I understood. As you see I only check for the mandatory
> attributes, if the user provides not relevant data its ignored.
> 
> We use one single nla_policy for all the commands (devlink_nl_policy)
> 
>>
>>> +	resource_id = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_ID]);
>>
>> I don't see where these attributes are validated for proper size.
>>
> 
> right, forgot to update the policy.
> 
>>> +
>>> +	resource = devlink_resource_find(devlink, NULL, resource_id);
>>> +	if (!resource)
>>> +		return -EINVAL;
>>> +
>>> +	if (!resource->resource_ops->size_validate)
>>> +		return -EINVAL;
>>
>> genl_info has extack; please add user messages for the above failures.
>>
> 
> Isn't EOPNOTSUPP enough ?

No, I mean every failure above returns EINVAL. Add an extack message
telling the user what is wrong. e.g,

	resource = devlink_resource_find(devlink, NULL, resource_id);
	if (!resource) {
		NL_SET_ERR_MSG(extack, "Invalid resource id");
		return -EINVAL;
	}

similarly for the rest.
Arkadi Sharshevsky Nov. 23, 2017, 12:25 p.m. UTC | #6
[...]
>>>> +
>>>> +	resource = devlink_resource_find(devlink, NULL, resource_id);
>>>> +	if (!resource)
>>>> +		return -EINVAL;
>>>> +
>>>> +	if (!resource->resource_ops->size_validate)
>>>> +		return -EINVAL;
>>>
>>> genl_info has extack; please add user messages for the above failures.
>>>
>>
>> Isn't EOPNOTSUPP enough ?
> 
> No, I mean every failure above returns EINVAL. Add an extack message
> telling the user what is wrong. e.g,
> 
> 	resource = devlink_resource_find(devlink, NULL, resource_id);
> 	if (!resource) {
> 		NL_SET_ERR_MSG(extack, "Invalid resource id");
> 		return -EINVAL;
> 	}
> 
> similarly for the rest.
> 

I don't understand why actually, extended ack should be used when
typical errno is not enough, for example something driver specific
like "KVD overlapping is not supported".

But here if the user provided id for setting the resource, I think
EINVAL is enough if devlink cannot find it.
David Ahern Nov. 27, 2017, 4:08 p.m. UTC | #7
On 11/23/17 5:25 AM, Arkadi Sharshevsky wrote:
> [...]
>>>>> +
>>>>> +	resource = devlink_resource_find(devlink, NULL, resource_id);
>>>>> +	if (!resource)
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	if (!resource->resource_ops->size_validate)
>>>>> +		return -EINVAL;
>>>>
>>>> genl_info has extack; please add user messages for the above failures.
>>>>
>>>
>>> Isn't EOPNOTSUPP enough ?
>>
>> No, I mean every failure above returns EINVAL. Add an extack message
>> telling the user what is wrong. e.g,
>>
>> 	resource = devlink_resource_find(devlink, NULL, resource_id);
>> 	if (!resource) {
>> 		NL_SET_ERR_MSG(extack, "Invalid resource id");
>> 		return -EINVAL;
>> 	}
>>
>> similarly for the rest.
>>
> 
> I don't understand why actually, extended ack should be used when
> typical errno is not enough, for example something driver specific
> like "KVD overlapping is not supported".
> 
> But here if the user provided id for setting the resource, I think
> EINVAL is enough if devlink cannot find it.
> 

EINVAL is returned in multiple places, so an EINVAL response does not
uniquely tell you the problem.

Further, we have infrastructure to give users explicit text on why the
request fails. Let's use it and make Linux more user friendly.
diff mbox series

Patch

diff --git a/include/net/devlink.h b/include/net/devlink.h
index 4d2c6fc..960e80a 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -26,6 +26,7 @@  struct devlink {
 	struct list_head port_list;
 	struct list_head sb_list;
 	struct list_head dpipe_table_list;
+	struct list_head resource_list;
 	struct devlink_dpipe_headers *dpipe_headers;
 	const struct devlink_ops *ops;
 	struct device *dev;
@@ -224,6 +225,45 @@  struct devlink_dpipe_headers {
 	unsigned int headers_count;
 };
 
+/**
+ * struct devlink_resource_ops - resource ops
+ * @occ_get - get the occupied size
+ * @size_validate - validate the size of the resource before update, reload
+ *                  is needed for changes to take place
+ */
+struct devlink_resource_ops {
+	u64 (*occ_get)(struct devlink *devlink);
+	int (*size_validate)(struct devlink *devlink, u64 size,
+			     struct list_head *resource_list,
+			     struct netlink_ext_ack *extack);
+};
+
+/**
+ * struct devlink_resource - devlink resource
+ * @name - name of the resource
+ * @size - size of the resource
+ * @size_new - updated size of the resource, reload is needed
+ * @id - id, per devlink instance
+ * @reload_required - reload is required for new configuration to apply
+ * @parent - parent resource
+ * @list - parent list
+ * @resource_list - list of child resources
+ * @resource_ops - resource ops
+ */
+struct devlink_resource {
+	const char *name;
+	u64 size;
+	u64 size_new;
+	u64 id;
+	bool reload_required;
+	struct devlink_resource *parent;
+	struct list_head list;
+	struct list_head resource_list;
+	struct devlink_resource_ops *resource_ops;
+};
+
+#define DEVLINK_RESOURCE_ID_PARENT_TOP 0
+
 struct devlink_ops {
 	int (*port_type_set)(struct devlink_port *devlink_port,
 			     enum devlink_port_type port_type);
@@ -333,6 +373,20 @@  extern struct devlink_dpipe_header devlink_dpipe_header_ethernet;
 extern struct devlink_dpipe_header devlink_dpipe_header_ipv4;
 extern struct devlink_dpipe_header devlink_dpipe_header_ipv6;
 
+int devlink_resource_register(struct devlink *devlink,
+			      const char *resource_name,
+			      bool top_hierarchy,
+			      bool reload_required,
+			      u64 resource_size,
+			      u64 resource_id,
+			      u64 parent_resource_id,
+			      struct devlink_resource_ops *resource_ops);
+void devlink_resources_unregister(struct devlink *devlink,
+				  struct devlink_resource *resource);
+int devlink_resource_size_get(struct devlink *devlink,
+			      u64 resource_id,
+			      u64 *p_resource_size);
+
 #else
 
 static inline struct devlink *devlink_alloc(const struct devlink_ops *ops,
@@ -469,6 +523,32 @@  devlink_dpipe_match_put(struct sk_buff *skb,
 	return 0;
 }
 
+static inline int
+devlink_resource_register(struct devlink *devlink,
+			  const char *resource_name,
+			  bool top_hierarchy,
+			  bool reload_required,
+			  u64 resource_size,
+			  u64 resource_id,
+			  u64 parent_resource_id,
+			  struct devlink_resource_ops *resource_ops)
+{
+	return 0;
+}
+
+static inline void
+devlink_resources_unregister(struct devlink *devlink,
+			     struct devlink_resource *resource)
+{
+}
+
+static inline int
+devlink_resource_size_get(struct devlink *devlink, u64 resource_id,
+			  u64 *p_resource_size)
+{
+	return -EINVAL;
+}
+
 #endif
 
 #endif /* _NET_DEVLINK_H_ */
diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
index 6665df6..1ee1c72 100644
--- a/include/uapi/linux/devlink.h
+++ b/include/uapi/linux/devlink.h
@@ -70,6 +70,8 @@  enum devlink_command {
 	DEVLINK_CMD_DPIPE_ENTRIES_GET,
 	DEVLINK_CMD_DPIPE_HEADERS_GET,
 	DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET,
+	DEVLINK_CMD_RESOURCE_SET,
+	DEVLINK_CMD_RESOURCE_DUMP,
 
 	/* add new commands above here */
 	__DEVLINK_CMD_MAX,
@@ -202,6 +204,14 @@  enum devlink_attr {
 	DEVLINK_ATTR_PAD,
 
 	DEVLINK_ATTR_ESWITCH_ENCAP_MODE,	/* u8 */
+	DEVLINK_ATTR_RESOURCE_LIST,		/* nested */
+	DEVLINK_ATTR_RESOURCE,			/* nested */
+	DEVLINK_ATTR_RESOURCE_NAME,		/* string */
+	DEVLINK_ATTR_RESOURCE_SIZE,		/* u64 */
+	DEVLINK_ATTR_RESOURCE_SIZE_NEW,		/* u64 */
+	DEVLINK_ATTR_RESOURCE_OCC,		/* u64 */
+	DEVLINK_ATTR_RESOURCE_ID,		/* u64 */
+	DEVLINK_ATTR_RESOURCE_RELOAD_REQUIRED,  /* u8  */
 
 	/* add new attributes above here, update the policy in devlink.c */
 
diff --git a/net/core/devlink.c b/net/core/devlink.c
index 0114dfc..6ae644f 100644
--- a/net/core/devlink.c
+++ b/net/core/devlink.c
@@ -2280,6 +2280,194 @@  static int devlink_nl_cmd_dpipe_table_counters_set(struct sk_buff *skb,
 						counters_enable);
 }
 
+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 *__resource;
+
+		if (resource->id == resource_id)
+			return resource;
+
+		__resource = devlink_resource_find(devlink, resource,
+						   resource_id);
+		if (__resource)
+			return __resource;
+	}
+	return NULL;
+}
+
+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 (!info->attrs[DEVLINK_ATTR_RESOURCE_ID] ||
+	    !info->attrs[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;
+
+	if (!resource->resource_ops->size_validate)
+		return -EINVAL;
+
+	size = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_SIZE]);
+	err = resource->resource_ops->size_validate(devlink, size,
+						    &resource->resource_list,
+						    info->extack);
+	if (err)
+		return err;
+
+	resource->size_new = size;
+	return 0;
+}
+
+static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb,
+				struct devlink_resource *resource)
+{
+	struct devlink_resource *__resource;
+	struct nlattr *__resource_attr;
+	struct nlattr *resource_attr;
+
+	resource_attr = nla_nest_start(skb, DEVLINK_ATTR_RESOURCE);
+	if (!resource_attr)
+		return -EMSGSIZE;
+
+	if (nla_put_string(skb, DEVLINK_ATTR_RESOURCE_NAME, resource->name) ||
+	    nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_RELOAD_REQUIRED,
+		       resource->reload_required) ||
+	    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);
+	if (resource->resource_ops && resource->resource_ops->occ_get)
+		nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_OCC,
+				  resource->resource_ops->occ_get(devlink),
+				  DEVLINK_ATTR_PAD);
+	if (list_empty(&resource->resource_list))
+		goto out;
+
+	__resource_attr = nla_nest_start(skb, DEVLINK_ATTR_RESOURCE_LIST);
+	if (!__resource_attr)
+		goto nla_put_failure;
+
+	list_for_each_entry(__resource, &resource->resource_list, list) {
+		if (devlink_resource_put(devlink, skb, __resource))
+			goto resource_put_failure;
+	}
+
+	nla_nest_end(skb, __resource_attr);
+out:
+	nla_nest_end(skb, resource_attr);
+	return 0;
+
+resource_put_failure:
+	nla_nest_cancel(skb, __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_dpipe_send_and_alloc_skb(&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(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_dpipe_send_and_alloc_skb(&skb, info);
+		if (err)
+			goto err_skb_send_alloc;
+		goto send_done;
+	}
+	return genlmsg_reply(skb, info);
+
+nla_put_failure:
+	err = -EMSGSIZE;
+err_resource_put:
+err_skb_send_alloc:
+	genlmsg_cancel(skb, hdr);
+	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);
+}
+
 static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
 	[DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING },
 	[DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING },
@@ -2456,6 +2644,20 @@  static const struct genl_ops devlink_nl_ops[] = {
 		.flags = GENL_ADMIN_PERM,
 		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
 	},
+	{
+		.cmd = DEVLINK_CMD_RESOURCE_SET,
+		.doit = devlink_nl_cmd_resource_set,
+		.policy = devlink_nl_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+	},
+	{
+		.cmd = DEVLINK_CMD_RESOURCE_DUMP,
+		.doit = devlink_nl_cmd_resource_dump,
+		.policy = devlink_nl_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+	},
 };
 
 static struct genl_family devlink_nl_family __ro_after_init = {
@@ -2493,6 +2695,7 @@  struct devlink *devlink_alloc(const struct devlink_ops *ops, size_t priv_size)
 	INIT_LIST_HEAD(&devlink->port_list);
 	INIT_LIST_HEAD(&devlink->sb_list);
 	INIT_LIST_HEAD_RCU(&devlink->dpipe_table_list);
+	INIT_LIST_HEAD(&devlink->resource_list);
 	mutex_init(&devlink->lock);
 	return devlink;
 }
@@ -2823,6 +3026,133 @@  void devlink_dpipe_table_unregister(struct devlink *devlink,
 }
 EXPORT_SYMBOL_GPL(devlink_dpipe_table_unregister);
 
+/**
+ *	devlink_resource_register - devlink resource register
+ *
+ *	@devlink: devlink
+ *	@resource_name: resource's name
+ *	@top_hierarchy: top hierarchy
+ *	@reload_required: reload is required for new configuration to
+ *			  apply
+ *	@resource_size: resource's size
+ *	@resource_id: resource's id
+ *	@parent_reosurce_id: resource's parent id
+ *	@resource_ops: resource ops
+ */
+int devlink_resource_register(struct devlink *devlink,
+			      const char *resource_name,
+			      bool top_hierarchy,
+			      bool reload_required,
+			      u64 resource_size,
+			      u64 resource_id,
+			      u64 parent_resource_id,
+			      struct devlink_resource_ops *resource_ops)
+{
+	struct devlink_resource *resource;
+	struct list_head *resource_list;
+	int err = 0;
+
+	mutex_lock(&devlink->lock);
+	resource = devlink_resource_find(devlink, NULL, resource_id);
+	if (resource) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	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;
+		} else {
+			err = -EINVAL;
+			goto out;
+		}
+	}
+
+	resource = kzalloc(sizeof(*resource), GFP_KERNEL);
+	if (!resource) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	resource->name = resource_name;
+	resource->size = resource_size;
+	resource->size_new = resource_size;
+	resource->id = resource_id;
+	resource->reload_required = reload_required;
+	resource->resource_ops = resource_ops;
+	INIT_LIST_HEAD(&resource->resource_list);
+	list_add_tail(&resource->list, resource_list);
+out:
+	mutex_unlock(&devlink->lock);
+	return err;
+}
+EXPORT_SYMBOL_GPL(devlink_resource_register);
+
+/**
+ *	devlink_resources_unregister - free all resources
+ *
+ *	@devlink: devlink
+ *	@resource: resource
+ */
+void devlink_resources_unregister(struct devlink *devlink,
+				  struct devlink_resource *resource)
+{
+	struct devlink_resource *tmp, *__resource;
+	struct list_head *resource_list;
+
+	if (resource)
+		resource_list = &resource->resource_list;
+	else
+		resource_list = &devlink->resource_list;
+
+	if (!resource)
+		mutex_lock(&devlink->lock);
+
+	list_for_each_entry_safe(__resource, tmp, resource_list, list) {
+		devlink_resources_unregister(devlink, __resource);
+		list_del(&__resource->list);
+		kfree(__resource);
+	}
+
+	if (!resource)
+		mutex_unlock(&devlink->lock);
+}
+EXPORT_SYMBOL_GPL(devlink_resources_unregister);
+
+/**
+ *	devlink_resource_size_get - get and update size
+ *
+ *	@devlink: devlink
+ *	@resource_id: the requested resource id
+ *	@p_resource_size: ptr to update
+ */
+int devlink_resource_size_get(struct devlink *devlink,
+			      u64 resource_id,
+			      u64 *p_resource_size)
+{
+	struct devlink_resource *resource;
+	int err = 0;
+
+	mutex_lock(&devlink->lock);
+	resource = devlink_resource_find(devlink, NULL, resource_id);
+	if (!resource) {
+		err = -EINVAL;
+		goto out;
+	}
+	*p_resource_size = resource->size_new;
+	resource->size = resource->size_new;
+out:
+	mutex_unlock(&devlink->lock);
+	return err;
+}
+EXPORT_SYMBOL_GPL(devlink_resource_size_get);
+
 static int __init devlink_module_init(void)
 {
 	return genl_register_family(&devlink_nl_family);