|
@@ -2288,6 +2288,233 @@ 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 *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;
|
|
|
+}
|
|
|
+
|
|
|
+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)
|
|
|
+ size_valid = false;
|
|
|
+out:
|
|
|
+ resource->size_valid = size_valid;
|
|
|
+}
|
|
|
+
|
|
|
+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,
|
|
|
+ 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 void
|
|
|
+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;
|
|
|
+ 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);
|
|
|
+}
|
|
|
+
|
|
|
+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(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);
|
|
|
+ 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);
|
|
|
+ devlink_resource_size_params_put(resource, skb);
|
|
|
+ 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(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_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 },
|
|
@@ -2306,6 +2533,8 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
|
|
|
[DEVLINK_ATTR_ESWITCH_ENCAP_MODE] = { .type = NLA_U8 },
|
|
|
[DEVLINK_ATTR_DPIPE_TABLE_NAME] = { .type = NLA_NUL_STRING },
|
|
|
[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = { .type = NLA_U8 },
|
|
|
+ [DEVLINK_ATTR_RESOURCE_ID] = { .type = NLA_U64},
|
|
|
+ [DEVLINK_ATTR_RESOURCE_SIZE] = { .type = NLA_U64},
|
|
|
};
|
|
|
|
|
|
static const struct genl_ops devlink_nl_ops[] = {
|
|
@@ -2466,6 +2695,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 = {
|
|
@@ -2503,6 +2746,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;
|
|
|
}
|
|
@@ -2833,6 +3077,136 @@ unlock:
|
|
|
}
|
|
|
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
|
|
|
+ * @size params: size parameters
|
|
|
+ * @resource_ops: resource ops
|
|
|
+ */
|
|
|
+int devlink_resource_register(struct devlink *devlink,
|
|
|
+ const char *resource_name,
|
|
|
+ bool top_hierarchy,
|
|
|
+ u64 resource_size,
|
|
|
+ u64 resource_id,
|
|
|
+ u64 parent_resource_id,
|
|
|
+ struct devlink_resource_size_params *size_params,
|
|
|
+ const 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ resource = kzalloc(sizeof(*resource), GFP_KERNEL);
|
|
|
+ if (!resource) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ 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;
|
|
|
+ resource->parent = parent_resource;
|
|
|
+ } else {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ resource->name = resource_name;
|
|
|
+ resource->size = resource_size;
|
|
|
+ resource->size_new = resource_size;
|
|
|
+ resource->id = resource_id;
|
|
|
+ resource->resource_ops = resource_ops;
|
|
|
+ resource->size_valid = true;
|
|
|
+ resource->size_params = size_params;
|
|
|
+ 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, *child_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(child_resource, tmp, resource_list, list) {
|
|
|
+ devlink_resources_unregister(devlink, child_resource);
|
|
|
+ list_del(&child_resource->list);
|
|
|
+ kfree(child_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);
|