naccyde / rpms / iproute

Forked from rpms/iproute 7 months ago
Clone

Blame SOURCES/0056-devlink-Add-support-for-devlink-resource-abstraction.patch

36cfb7
From 5190aa430d198420679e53163604f7b6860bcd0f Mon Sep 17 00:00:00 2001
36cfb7
From: Andrea Claudi <aclaudi@redhat.com>
36cfb7
Date: Mon, 25 Mar 2019 11:40:10 +0100
36cfb7
Subject: [PATCH] devlink: Add support for devlink resource abstraction
36cfb7
36cfb7
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1644731
36cfb7
Upstream Status: iproute2.git commit 8cd644095842a
36cfb7
Conflicts: adjusted help printout due to missing commit
36cfb7
           3e897912cbff9 ("devlink: add batch command support")
36cfb7
36cfb7
commit 8cd644095842af3107320e86eeb01be6af6c77bb
36cfb7
Author: Arkadi Sharshevsky <arkadis@mellanox.com>
36cfb7
Date:   Wed Feb 14 10:55:18 2018 +0200
36cfb7
36cfb7
    devlink: Add support for devlink resource abstraction
36cfb7
36cfb7
    Add support for devlink resource abstraction. The resources are
36cfb7
    represented by a tree based structure and are identified by a name and
36cfb7
    a size. Some resources can present their real time occupancy.
36cfb7
36cfb7
    First the resources exposed by the driver can be observed, for example:
36cfb7
36cfb7
    $devlink resource show pci/0000:03:00.0
36cfb7
    pci/0000:03:00.0:
36cfb7
      name kvd size 245760 unit entry
36cfb7
        resources:
36cfb7
          name linear size 98304 occ 0 unit entry size_min 0 size_max 147456 size_gran 128
36cfb7
          name hash_double size 60416 unit entry size_min 32768 size_max 180224 size_gran 128
36cfb7
          name hash_single size 87040 unit entry size_min 65536 size_max 212992 size_gran 128
36cfb7
36cfb7
    Some resource's size can be changed. Examples:
36cfb7
36cfb7
    $devlink resource set pci/0000:03:00.0 path /kvd/hash_single size 73088
36cfb7
    $devlink resource set pci/0000:03:00.0 path /kvd/hash_double size 74368
36cfb7
36cfb7
    The changes do not apply immediately, this can be validate by the 'size_new'
36cfb7
    attribute, which represents the pending changed size. For example
36cfb7
36cfb7
    $devlink resource show pci/0000:03:00.0
36cfb7
    pci/0000:03:00.0:
36cfb7
      name kvd size 245760 unit entry size_valid false
36cfb7
      resources:
36cfb7
        name linear size 98304 size_new 147456 occ 0 unit entry size_min 0 size_max 147456 size_gran 128
36cfb7
        name hash_double size 60416 unit entry size_min 32768 size_max 180224 size_gran 128
36cfb7
        name hash_single size 87040 unit entry size_min 65536 size_max 212992 size_gran 128
36cfb7
36cfb7
    In case of a pending change the nested resources present an indication
36cfb7
    for a valid configuration of its children (sum of its children sizes
36cfb7
    doesn't exceed the parent's size).
36cfb7
36cfb7
    In order for the changes to take place hot reload is needed. The hot
36cfb7
    reload through devlink will be introduced in the following patch.
36cfb7
36cfb7
    Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
36cfb7
    Acked-by: Jiri Pirko <jiri@mellanox.com>
36cfb7
    Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
36cfb7
---
36cfb7
 devlink/devlink.c | 490 +++++++++++++++++++++++++++++++++++++++++++++-
36cfb7
 include/list.h    |   5 +
36cfb7
 2 files changed, 494 insertions(+), 1 deletion(-)
36cfb7
36cfb7
diff --git a/devlink/devlink.c b/devlink/devlink.c
36cfb7
index f9bc16c350c40..7f47b79450094 100644
36cfb7
--- a/devlink/devlink.c
36cfb7
+++ b/devlink/devlink.c
36cfb7
@@ -177,6 +177,8 @@ static void ifname_map_free(struct ifname_map *ifname_map)
36cfb7
 #define DL_OPT_DPIPE_TABLE_NAME	BIT(13)
36cfb7
 #define DL_OPT_DPIPE_TABLE_COUNTERS	BIT(14)
36cfb7
 #define DL_OPT_ESWITCH_ENCAP_MODE	BIT(15)
36cfb7
+#define DL_OPT_RESOURCE_PATH	BIT(16)
36cfb7
+#define DL_OPT_RESOURCE_SIZE	BIT(17)
36cfb7
 
36cfb7
 struct dl_opts {
36cfb7
 	uint32_t present; /* flags of present items */
36cfb7
@@ -197,6 +199,10 @@ struct dl_opts {
36cfb7
 	const char *dpipe_table_name;
36cfb7
 	bool dpipe_counters_enable;
36cfb7
 	bool eswitch_encap_mode;
36cfb7
+	const char *resource_path;
36cfb7
+	uint32_t resource_size;
36cfb7
+	uint32_t resource_id;
36cfb7
+	bool resource_id_valid;
36cfb7
 };
36cfb7
 
36cfb7
 struct dl {
36cfb7
@@ -937,6 +943,20 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required,
36cfb7
 			if (err)
36cfb7
 				return err;
36cfb7
 			o_found |= DL_OPT_ESWITCH_ENCAP_MODE;
36cfb7
+		} else if (dl_argv_match(dl, "path") &&
36cfb7
+			   (o_all & DL_OPT_RESOURCE_PATH)) {
36cfb7
+			dl_arg_inc(dl);
36cfb7
+			err = dl_argv_str(dl, &opts->resource_path);
36cfb7
+			if (err)
36cfb7
+				return err;
36cfb7
+			o_found |= DL_OPT_RESOURCE_PATH;
36cfb7
+		} else if (dl_argv_match(dl, "size") &&
36cfb7
+			   (o_all & DL_OPT_RESOURCE_SIZE)) {
36cfb7
+			dl_arg_inc(dl);
36cfb7
+			err = dl_argv_uint32_t(dl, &opts->resource_size);
36cfb7
+			if (err)
36cfb7
+				return err;
36cfb7
+			o_found |= DL_OPT_RESOURCE_SIZE;
36cfb7
 		} else {
36cfb7
 			pr_err("Unknown option \"%s\"\n", dl_argv(dl));
36cfb7
 			return -EINVAL;
36cfb7
@@ -1079,6 +1099,12 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
36cfb7
 	if (opts->present & DL_OPT_ESWITCH_ENCAP_MODE)
36cfb7
 		mnl_attr_put_u8(nlh, DEVLINK_ATTR_ESWITCH_ENCAP_MODE,
36cfb7
 				opts->eswitch_encap_mode);
36cfb7
+	if ((opts->present & DL_OPT_RESOURCE_PATH) && opts->resource_id_valid)
36cfb7
+		mnl_attr_put_u64(nlh, DEVLINK_ATTR_RESOURCE_ID,
36cfb7
+				 opts->resource_id);
36cfb7
+	if (opts->present & DL_OPT_RESOURCE_SIZE)
36cfb7
+		mnl_attr_put_u64(nlh, DEVLINK_ATTR_RESOURCE_SIZE,
36cfb7
+				 opts->resource_size);
36cfb7
 }
36cfb7
 
36cfb7
 static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl,
36cfb7
@@ -2666,6 +2692,91 @@ struct dpipe_header {
36cfb7
 	unsigned int fields_count;
36cfb7
 };
36cfb7
 
36cfb7
+struct resource {
36cfb7
+	char *name;
36cfb7
+	uint64_t size;
36cfb7
+	uint64_t size_new;
36cfb7
+	uint64_t size_min;
36cfb7
+	uint64_t size_max;
36cfb7
+	uint64_t size_gran;
36cfb7
+	enum devlink_resource_unit unit;
36cfb7
+	bool size_valid;
36cfb7
+	uint64_t size_occ;
36cfb7
+	bool occ_valid;
36cfb7
+	uint64_t id;
36cfb7
+	struct list_head list;
36cfb7
+	struct list_head resource_list;
36cfb7
+	struct resource *parent;
36cfb7
+};
36cfb7
+
36cfb7
+struct resources {
36cfb7
+	struct list_head resource_list;
36cfb7
+};
36cfb7
+
36cfb7
+struct resource_ctx {
36cfb7
+	struct dl *dl;
36cfb7
+	int err;
36cfb7
+	struct resources *resources;
36cfb7
+	bool print_resources;
36cfb7
+	bool pending_change;
36cfb7
+};
36cfb7
+
36cfb7
+static struct resource *resource_alloc(void)
36cfb7
+{
36cfb7
+	struct resource *resource;
36cfb7
+
36cfb7
+	resource = calloc(1, sizeof(struct resource));
36cfb7
+	if (!resource)
36cfb7
+		return NULL;
36cfb7
+	INIT_LIST_HEAD(&resource->resource_list);
36cfb7
+	return resource;
36cfb7
+}
36cfb7
+
36cfb7
+static void resource_free(struct resource *resource)
36cfb7
+{
36cfb7
+	struct resource *child_resource, *tmp;
36cfb7
+
36cfb7
+	list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
36cfb7
+				 list) {
36cfb7
+		free(child_resource->name);
36cfb7
+		resource_free(child_resource);
36cfb7
+	}
36cfb7
+	free(resource);
36cfb7
+}
36cfb7
+
36cfb7
+static struct resources *resources_alloc(void)
36cfb7
+{
36cfb7
+	struct resources *resources;
36cfb7
+
36cfb7
+	resources = calloc(1, sizeof(struct resources));
36cfb7
+	if (!resources)
36cfb7
+		return NULL;
36cfb7
+	INIT_LIST_HEAD(&resources->resource_list);
36cfb7
+	return resources;
36cfb7
+}
36cfb7
+
36cfb7
+static void resources_free(struct resources *resources)
36cfb7
+{
36cfb7
+	struct resource *resource, *tmp;
36cfb7
+
36cfb7
+	list_for_each_entry_safe(resource, tmp, &resources->resource_list, list)
36cfb7
+		resource_free(resource);
36cfb7
+}
36cfb7
+
36cfb7
+static int resource_ctx_init(struct resource_ctx *ctx, struct dl *dl)
36cfb7
+{
36cfb7
+	ctx->resources = resources_alloc();
36cfb7
+	if (!ctx->resources)
36cfb7
+		return -ENOMEM;
36cfb7
+	ctx->dl = dl;
36cfb7
+	return 0;
36cfb7
+}
36cfb7
+
36cfb7
+static void resource_ctx_fini(struct resource_ctx *ctx)
36cfb7
+{
36cfb7
+	resources_free(ctx->resources);
36cfb7
+}
36cfb7
+
36cfb7
 struct dpipe_ctx {
36cfb7
 	struct dl *dl;
36cfb7
 	int err;
36cfb7
@@ -3203,6 +3314,66 @@ err_match_show:
36cfb7
 	return -EINVAL;
36cfb7
 }
36cfb7
 
36cfb7
+static struct resource *
36cfb7
+resource_find(struct resources *resources, struct resource *resource,
36cfb7
+	      uint64_t resource_id)
36cfb7
+{
36cfb7
+	struct list_head *list_head;
36cfb7
+
36cfb7
+	if (!resource)
36cfb7
+		list_head = &resources->resource_list;
36cfb7
+	else
36cfb7
+		list_head = &resource->resource_list;
36cfb7
+
36cfb7
+	list_for_each_entry(resource, list_head, list) {
36cfb7
+		struct resource *child_resource;
36cfb7
+
36cfb7
+		if (resource->id == resource_id)
36cfb7
+			return resource;
36cfb7
+
36cfb7
+		child_resource = resource_find(resources, resource,
36cfb7
+					       resource_id);
36cfb7
+		if (child_resource)
36cfb7
+			return child_resource;
36cfb7
+	}
36cfb7
+	return NULL;
36cfb7
+}
36cfb7
+
36cfb7
+static void
36cfb7
+resource_path_print(struct dl *dl, struct resources *resources,
36cfb7
+		    uint64_t resource_id)
36cfb7
+{
36cfb7
+	struct resource *resource, *parent_resource;
36cfb7
+	const char del[] = "/";
36cfb7
+	int path_len = 0;
36cfb7
+	char *path;
36cfb7
+
36cfb7
+	resource = resource_find(resources, NULL, resource_id);
36cfb7
+	if (!resource)
36cfb7
+		return;
36cfb7
+
36cfb7
+	for (parent_resource = resource; parent_resource;
36cfb7
+	     parent_resource = parent_resource->parent)
36cfb7
+		path_len += strlen(parent_resource->name) + 1;
36cfb7
+
36cfb7
+	path_len++;
36cfb7
+	path = calloc(1, path_len);
36cfb7
+	if (!path)
36cfb7
+		return;
36cfb7
+
36cfb7
+	path += path_len - 1;
36cfb7
+	for (parent_resource = resource; parent_resource;
36cfb7
+		parent_resource = parent_resource->parent) {
36cfb7
+		path -= strlen(parent_resource->name);
36cfb7
+		memcpy(path, parent_resource->name,
36cfb7
+		       strlen(parent_resource->name));
36cfb7
+		path -= strlen(del);
36cfb7
+		memcpy(path, del, strlen(del));
36cfb7
+	}
36cfb7
+	pr_out_str(dl, "resource_path", path);
36cfb7
+	free(path);
36cfb7
+}
36cfb7
+
36cfb7
 static int dpipe_table_show(struct dpipe_ctx *ctx, struct nlattr *nl)
36cfb7
 {
36cfb7
 	struct nlattr *nla_table[DEVLINK_ATTR_MAX + 1] = {};
36cfb7
@@ -3617,10 +3788,324 @@ static int cmd_dpipe(struct dl *dl)
36cfb7
 	return -ENOENT;
36cfb7
 }
36cfb7
 
36cfb7
+static int
36cfb7
+resource_parse(struct resource_ctx *ctx, struct resource *resource,
36cfb7
+	       struct nlattr **nla_resource)
36cfb7
+{
36cfb7
+	if (!nla_resource[DEVLINK_ATTR_RESOURCE_NAME] ||
36cfb7
+	    !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE] ||
36cfb7
+	    !nla_resource[DEVLINK_ATTR_RESOURCE_ID] ||
36cfb7
+	    !nla_resource[DEVLINK_ATTR_RESOURCE_UNIT] ||
36cfb7
+	    !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MIN] ||
36cfb7
+	    !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MAX] ||
36cfb7
+	    !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_GRAN]) {
36cfb7
+		return -EINVAL;
36cfb7
+	}
36cfb7
+
36cfb7
+	resource->name = strdup(mnl_attr_get_str(nla_resource[DEVLINK_ATTR_RESOURCE_NAME]));
36cfb7
+	resource->size = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE]);
36cfb7
+	resource->id = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_ID]);
36cfb7
+	resource->unit = mnl_attr_get_u8(nla_resource[DEVLINK_ATTR_RESOURCE_UNIT]);
36cfb7
+	resource->size_min = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MIN]);
36cfb7
+	resource->size_max = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MAX]);
36cfb7
+	resource->size_gran = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_GRAN]);
36cfb7
+
36cfb7
+	if (nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_NEW])
36cfb7
+		resource->size_new = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_NEW]);
36cfb7
+	else
36cfb7
+		resource->size_new = resource->size;
36cfb7
+
36cfb7
+	if (nla_resource[DEVLINK_ATTR_RESOURCE_OCC]) {
36cfb7
+		resource->size_occ = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_OCC]);
36cfb7
+		resource->occ_valid = true;
36cfb7
+	}
36cfb7
+
36cfb7
+	if (resource->size_new != resource->size)
36cfb7
+		ctx->pending_change = true;
36cfb7
+
36cfb7
+	return 0;
36cfb7
+}
36cfb7
+
36cfb7
+static int
36cfb7
+resource_get(struct resource_ctx *ctx, struct resource *resource,
36cfb7
+	     struct resource *parent_resource, struct nlattr *nl)
36cfb7
+{
36cfb7
+	struct nlattr *nla_resource[DEVLINK_ATTR_MAX + 1] = {};
36cfb7
+	struct nlattr *nla_child_resource;
36cfb7
+	struct nlattr *nla_resources;
36cfb7
+	bool top = false;
36cfb7
+	int err;
36cfb7
+
36cfb7
+	if (!resource) {
36cfb7
+		nla_resources = nl;
36cfb7
+		top = true;
36cfb7
+		goto out;
36cfb7
+	}
36cfb7
+
36cfb7
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_resource);
36cfb7
+	if (err != MNL_CB_OK)
36cfb7
+		return -EINVAL;
36cfb7
+
36cfb7
+	err = resource_parse(ctx, resource, nla_resource);
36cfb7
+	if (err)
36cfb7
+		return err;
36cfb7
+
36cfb7
+	resource->parent = parent_resource;
36cfb7
+	if (!nla_resource[DEVLINK_ATTR_RESOURCE_LIST])
36cfb7
+		return 0;
36cfb7
+
36cfb7
+	resource->size_valid = !!mnl_attr_get_u8(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_VALID]);
36cfb7
+	nla_resources = nla_resource[DEVLINK_ATTR_RESOURCE_LIST];
36cfb7
+out:
36cfb7
+	mnl_attr_for_each_nested(nla_child_resource, nla_resources) {
36cfb7
+		struct resource *child_resource;
36cfb7
+		struct list_head *list;
36cfb7
+
36cfb7
+		child_resource = resource_alloc();
36cfb7
+		if (!child_resource)
36cfb7
+			return -ENOMEM;
36cfb7
+
36cfb7
+		if (top)
36cfb7
+			list = &ctx->resources->resource_list;
36cfb7
+		else
36cfb7
+			list = &resource->resource_list;
36cfb7
+
36cfb7
+		list_add_tail(&child_resource->list, list);
36cfb7
+		err = resource_get(ctx, child_resource, resource,
36cfb7
+				   nla_child_resource);
36cfb7
+		if (err)
36cfb7
+			return err;
36cfb7
+	}
36cfb7
+
36cfb7
+	return 0;
36cfb7
+}
36cfb7
+
36cfb7
+static const char *resource_unit_str_get(enum devlink_resource_unit unit)
36cfb7
+{
36cfb7
+	switch (unit) {
36cfb7
+	case DEVLINK_RESOURCE_UNIT_ENTRY: return "entry";
36cfb7
+	default: return "<unknown unit>";
36cfb7
+	}
36cfb7
+}
36cfb7
+
36cfb7
+static void resource_show(struct resource *resource,
36cfb7
+			  struct resource_ctx *ctx)
36cfb7
+{
36cfb7
+	struct resource *child_resource;
36cfb7
+	struct dl *dl = ctx->dl;
36cfb7
+
36cfb7
+	pr_out_str(dl, "name", resource->name);
36cfb7
+	if (dl->verbose)
36cfb7
+		resource_path_print(dl, ctx->resources, resource->id);
36cfb7
+	pr_out_uint(dl, "size", resource->size);
36cfb7
+	if (resource->size != resource->size_new)
36cfb7
+		pr_out_uint(dl, "size_new", resource->size_new);
36cfb7
+	if (resource->occ_valid)
36cfb7
+		pr_out_uint(dl, "occ", resource->size_occ);
36cfb7
+	pr_out_str(dl, "unit", resource_unit_str_get(resource->unit));
36cfb7
+
36cfb7
+	if (resource->size_min != resource->size_max) {
36cfb7
+		pr_out_uint(dl, "size_min", resource->size_min);
36cfb7
+		pr_out_uint(dl, "size_max", resource->size_max);
36cfb7
+		pr_out_uint(dl, "size_gran", resource->size_gran);
36cfb7
+	}
36cfb7
+
36cfb7
+	if (list_empty(&resource->resource_list))
36cfb7
+		return;
36cfb7
+
36cfb7
+	if (ctx->pending_change)
36cfb7
+		pr_out_str(dl, "size_valid", resource->size_valid ?
36cfb7
+			   "true" : "false");
36cfb7
+	pr_out_array_start(dl, "resources");
36cfb7
+	list_for_each_entry(child_resource, &resource->resource_list, list) {
36cfb7
+		pr_out_entry_start(dl);
36cfb7
+		resource_show(child_resource, ctx);
36cfb7
+		pr_out_entry_end(dl);
36cfb7
+	}
36cfb7
+	pr_out_array_end(dl);
36cfb7
+}
36cfb7
+
36cfb7
+static void
36cfb7
+resources_show(struct resource_ctx *ctx, struct nlattr **tb)
36cfb7
+{
36cfb7
+	struct resources *resources = ctx->resources;
36cfb7
+	struct resource *resource;
36cfb7
+
36cfb7
+	list_for_each_entry(resource, &resources->resource_list, list) {
36cfb7
+		pr_out_handle_start_arr(ctx->dl, tb);
36cfb7
+		resource_show(resource, ctx);
36cfb7
+		pr_out_handle_end(ctx->dl);
36cfb7
+	}
36cfb7
+}
36cfb7
+
36cfb7
+static int resources_get(struct resource_ctx *ctx, struct nlattr **tb)
36cfb7
+{
36cfb7
+	return resource_get(ctx, NULL, NULL, tb[DEVLINK_ATTR_RESOURCE_LIST]);
36cfb7
+}
36cfb7
+
36cfb7
+static int cmd_resource_dump_cb(const struct nlmsghdr *nlh, void *data)
36cfb7
+{
36cfb7
+	struct resource_ctx *ctx = data;
36cfb7
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
36cfb7
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
36cfb7
+	int err;
36cfb7
+
36cfb7
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
36cfb7
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
36cfb7
+	    !tb[DEVLINK_ATTR_RESOURCE_LIST])
36cfb7
+		return MNL_CB_ERROR;
36cfb7
+
36cfb7
+	err = resources_get(ctx, tb);
36cfb7
+	if (err) {
36cfb7
+		ctx->err = err;
36cfb7
+		return MNL_CB_ERROR;
36cfb7
+	}
36cfb7
+
36cfb7
+	if (ctx->print_resources)
36cfb7
+		resources_show(ctx, tb);
36cfb7
+
36cfb7
+	return MNL_CB_OK;
36cfb7
+}
36cfb7
+
36cfb7
+static int cmd_resource_show(struct dl *dl)
36cfb7
+{
36cfb7
+	struct nlmsghdr *nlh;
36cfb7
+	struct resource_ctx ctx = {};
36cfb7
+	int err;
36cfb7
+
36cfb7
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP,
36cfb7
+			       NLM_F_REQUEST | NLM_F_ACK);
36cfb7
+
36cfb7
+	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0);
36cfb7
+	if (err)
36cfb7
+		return err;
36cfb7
+
36cfb7
+	err = resource_ctx_init(&ctx, dl);
36cfb7
+	if (err)
36cfb7
+		return err;
36cfb7
+
36cfb7
+	ctx.print_resources = true;
36cfb7
+	pr_out_section_start(dl, "resources");
36cfb7
+	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb, &ctx;;
36cfb7
+	pr_out_section_end(dl);
36cfb7
+	resource_ctx_fini(&ctx;;
36cfb7
+	return err;
36cfb7
+}
36cfb7
+
36cfb7
+static void cmd_resource_help(void)
36cfb7
+{
36cfb7
+	pr_err("Usage: devlink resource show DEV\n"
36cfb7
+	       "       devlink resource set DEV path PATH size SIZE\n");
36cfb7
+}
36cfb7
+
36cfb7
+static struct resource *
36cfb7
+resource_find_by_name(struct list_head *list, char *name)
36cfb7
+{
36cfb7
+	struct resource *resource;
36cfb7
+
36cfb7
+	list_for_each_entry(resource, list, list) {
36cfb7
+		if (!strcmp(resource->name, name))
36cfb7
+			return resource;
36cfb7
+	}
36cfb7
+	return NULL;
36cfb7
+}
36cfb7
+
36cfb7
+static int
36cfb7
+resource_path_parse(struct resource_ctx *ctx, const char *resource_path,
36cfb7
+		    uint32_t *p_resource_id, bool *p_resource_valid)
36cfb7
+{
36cfb7
+	struct resource *resource;
36cfb7
+	uint32_t resource_id = 0;
36cfb7
+	char *resource_path_dup;
36cfb7
+	struct list_head *list;
36cfb7
+	const char del[] = "/";
36cfb7
+	char *resource_name;
36cfb7
+
36cfb7
+	resource_path_dup = strdup(resource_path);
36cfb7
+	list = &ctx->resources->resource_list;
36cfb7
+	resource_name = strtok(resource_path_dup, del);
36cfb7
+	while (resource_name != NULL) {
36cfb7
+		resource = resource_find_by_name(list, resource_name);
36cfb7
+		if (!resource)
36cfb7
+			goto err_resource_lookup;
36cfb7
+
36cfb7
+		list = &resource->resource_list;
36cfb7
+		resource_name = strtok(NULL, del);
36cfb7
+		resource_id = resource->id;
36cfb7
+	}
36cfb7
+	free(resource_path_dup);
36cfb7
+	*p_resource_valid = true;
36cfb7
+	*p_resource_id = resource_id;
36cfb7
+	return 0;
36cfb7
+
36cfb7
+err_resource_lookup:
36cfb7
+	free(resource_path_dup);
36cfb7
+	return -EINVAL;
36cfb7
+}
36cfb7
+
36cfb7
+static int cmd_resource_set(struct dl *dl)
36cfb7
+{
36cfb7
+	struct nlmsghdr *nlh;
36cfb7
+	struct resource_ctx ctx = {};
36cfb7
+	int err;
36cfb7
+
36cfb7
+	err = resource_ctx_init(&ctx, dl);
36cfb7
+	if (err)
36cfb7
+		return err;
36cfb7
+
36cfb7
+	ctx.print_resources = false;
36cfb7
+	err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_RESOURCE_PATH |
36cfb7
+			    DL_OPT_RESOURCE_SIZE, 0);
36cfb7
+	if (err)
36cfb7
+		goto out;
36cfb7
+
36cfb7
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP,
36cfb7
+			       NLM_F_REQUEST);
36cfb7
+	dl_opts_put(nlh, dl);
36cfb7
+	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb, &ctx;;
36cfb7
+	if (err) {
36cfb7
+		pr_err("error getting resources %s\n", strerror(ctx.err));
36cfb7
+		goto out;
36cfb7
+	}
36cfb7
+
36cfb7
+	err = resource_path_parse(&ctx, dl->opts.resource_path,
36cfb7
+				  &dl->opts.resource_id,
36cfb7
+				  &dl->opts.resource_id_valid);
36cfb7
+	if (err) {
36cfb7
+		pr_err("error parsing resource path %s\n", strerror(err));
36cfb7
+		goto out;
36cfb7
+	}
36cfb7
+
36cfb7
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_SET,
36cfb7
+			       NLM_F_REQUEST | NLM_F_ACK);
36cfb7
+
36cfb7
+	dl_opts_put(nlh, dl);
36cfb7
+	err = _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
36cfb7
+out:
36cfb7
+	resource_ctx_fini(&ctx;;
36cfb7
+	return err;
36cfb7
+}
36cfb7
+
36cfb7
+static int cmd_resource(struct dl *dl)
36cfb7
+{
36cfb7
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
36cfb7
+		cmd_resource_help();
36cfb7
+		return 0;
36cfb7
+	} else if (dl_argv_match(dl, "show")) {
36cfb7
+		dl_arg_inc(dl);
36cfb7
+		return cmd_resource_show(dl);
36cfb7
+	} else if (dl_argv_match(dl, "set")) {
36cfb7
+		dl_arg_inc(dl);
36cfb7
+		return cmd_resource_set(dl);
36cfb7
+	}
36cfb7
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
36cfb7
+	return -ENOENT;
36cfb7
+}
36cfb7
+
36cfb7
 static void help(void)
36cfb7
 {
36cfb7
 	pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n"
36cfb7
-	       "where  OBJECT := { dev | port | sb | monitor | dpipe }\n"
36cfb7
+	       "where  OBJECT := { dev | port | sb | monitor | dpipe | resource }\n"
36cfb7
 	       "       OPTIONS := { -V[ersion] | -n[no-nice-names] | -j[json] | -p[pretty] | -v[verbose] }\n");
36cfb7
 }
36cfb7
 
36cfb7
@@ -3644,6 +4129,9 @@ static int dl_cmd(struct dl *dl)
36cfb7
 	} else if (dl_argv_match(dl, "dpipe")) {
36cfb7
 		dl_arg_inc(dl);
36cfb7
 		return cmd_dpipe(dl);
36cfb7
+	} else if (dl_argv_match(dl, "resource")) {
36cfb7
+		dl_arg_inc(dl);
36cfb7
+		return cmd_resource(dl);
36cfb7
 	}
36cfb7
 	pr_err("Object \"%s\" not found\n", dl_argv(dl));
36cfb7
 	return -ENOENT;
36cfb7
diff --git a/include/list.h b/include/list.h
36cfb7
index 5b529dc6e5211..b2adf55578449 100644
36cfb7
--- a/include/list.h
36cfb7
+++ b/include/list.h
36cfb7
@@ -107,6 +107,11 @@ static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
36cfb7
 	n->pprev = &h->first;
36cfb7
 }
36cfb7
 
36cfb7
+static inline int list_empty(const struct list_head *head)
36cfb7
+{
36cfb7
+	return head->next == head;
36cfb7
+}
36cfb7
+
36cfb7
 #define hlist_for_each(pos, head) \
36cfb7
 	for (pos = (head)->first; pos ; pos = pos->next)
36cfb7
 
36cfb7
-- 
e138d9
2.21.0
36cfb7