|
|
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 |
|