mirror of
https://github.com/torvalds/linux
synced 2024-09-06 18:01:05 +00:00
![Jiri Pirko](/assets/img/avatar_default.png)
All remaining doit and dumpit netlink callback functions are going to be used by generated split ops. They expect certain name format. Rename the callback to be aligned with generated names. Signed-off-by: Jiri Pirko <jiri@nvidia.com> Reviewed-by: Jacob Keller <jacob.e.keller@intel.com> Link: https://lore.kernel.org/r/20231021112711.660606-8-jiri@resnulli.us Signed-off-by: Jakub Kicinski <kuba@kernel.org>
580 lines
15 KiB
C
580 lines
15 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
|
|
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
|
|
*/
|
|
|
|
#include "devl_internal.h"
|
|
|
|
/**
|
|
* struct devlink_resource - devlink resource
|
|
* @name: name of the resource
|
|
* @id: id, per devlink instance
|
|
* @size: size of the resource
|
|
* @size_new: updated size of the resource, reload is needed
|
|
* @size_valid: valid in case the total size of the resource is valid
|
|
* including its children
|
|
* @parent: parent resource
|
|
* @size_params: size parameters
|
|
* @list: parent list
|
|
* @resource_list: list of child resources
|
|
* @occ_get: occupancy getter callback
|
|
* @occ_get_priv: occupancy getter callback priv
|
|
*/
|
|
struct devlink_resource {
|
|
const char *name;
|
|
u64 id;
|
|
u64 size;
|
|
u64 size_new;
|
|
bool size_valid;
|
|
struct devlink_resource *parent;
|
|
struct devlink_resource_size_params size_params;
|
|
struct list_head list;
|
|
struct list_head resource_list;
|
|
devlink_resource_occ_get_t *occ_get;
|
|
void *occ_get_priv;
|
|
};
|
|
|
|
static struct devlink_resource *
|
|
devlink_resource_find(struct devlink *devlink,
|
|
struct devlink_resource *resource, u64 resource_id)
|
|
{
|
|
struct list_head *resource_list;
|
|
|
|
if (resource)
|
|
resource_list = &resource->resource_list;
|
|
else
|
|
resource_list = &devlink->resource_list;
|
|
|
|
list_for_each_entry(resource, resource_list, list) {
|
|
struct devlink_resource *child_resource;
|
|
|
|
if (resource->id == resource_id)
|
|
return resource;
|
|
|
|
child_resource = devlink_resource_find(devlink, resource,
|
|
resource_id);
|
|
if (child_resource)
|
|
return child_resource;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
devlink_resource_validate_children(struct devlink_resource *resource)
|
|
{
|
|
struct devlink_resource *child_resource;
|
|
bool size_valid = true;
|
|
u64 parts_size = 0;
|
|
|
|
if (list_empty(&resource->resource_list))
|
|
goto out;
|
|
|
|
list_for_each_entry(child_resource, &resource->resource_list, list)
|
|
parts_size += child_resource->size_new;
|
|
|
|
if (parts_size > resource->size_new)
|
|
size_valid = false;
|
|
out:
|
|
resource->size_valid = size_valid;
|
|
}
|
|
|
|
static int
|
|
devlink_resource_validate_size(struct devlink_resource *resource, u64 size,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
u64 reminder;
|
|
int err = 0;
|
|
|
|
if (size > resource->size_params.size_max) {
|
|
NL_SET_ERR_MSG(extack, "Size larger than maximum");
|
|
err = -EINVAL;
|
|
}
|
|
|
|
if (size < resource->size_params.size_min) {
|
|
NL_SET_ERR_MSG(extack, "Size smaller than minimum");
|
|
err = -EINVAL;
|
|
}
|
|
|
|
div64_u64_rem(size, resource->size_params.size_granularity, &reminder);
|
|
if (reminder) {
|
|
NL_SET_ERR_MSG(extack, "Wrong granularity");
|
|
err = -EINVAL;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int devlink_nl_resource_set_doit(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
struct devlink_resource *resource;
|
|
u64 resource_id;
|
|
u64 size;
|
|
int err;
|
|
|
|
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_ID) ||
|
|
GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_SIZE))
|
|
return -EINVAL;
|
|
resource_id = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_ID]);
|
|
|
|
resource = devlink_resource_find(devlink, NULL, resource_id);
|
|
if (!resource)
|
|
return -EINVAL;
|
|
|
|
size = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_SIZE]);
|
|
err = devlink_resource_validate_size(resource, size, info->extack);
|
|
if (err)
|
|
return err;
|
|
|
|
resource->size_new = size;
|
|
devlink_resource_validate_children(resource);
|
|
if (resource->parent)
|
|
devlink_resource_validate_children(resource->parent);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
devlink_resource_size_params_put(struct devlink_resource *resource,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct devlink_resource_size_params *size_params;
|
|
|
|
size_params = &resource->size_params;
|
|
if (nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_GRAN,
|
|
size_params->size_granularity, DEVLINK_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_MAX,
|
|
size_params->size_max, DEVLINK_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_MIN,
|
|
size_params->size_min, DEVLINK_ATTR_PAD) ||
|
|
nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_UNIT, size_params->unit))
|
|
return -EMSGSIZE;
|
|
return 0;
|
|
}
|
|
|
|
static int devlink_resource_occ_put(struct devlink_resource *resource,
|
|
struct sk_buff *skb)
|
|
{
|
|
if (!resource->occ_get)
|
|
return 0;
|
|
return nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_OCC,
|
|
resource->occ_get(resource->occ_get_priv),
|
|
DEVLINK_ATTR_PAD);
|
|
}
|
|
|
|
static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb,
|
|
struct devlink_resource *resource)
|
|
{
|
|
struct devlink_resource *child_resource;
|
|
struct nlattr *child_resource_attr;
|
|
struct nlattr *resource_attr;
|
|
|
|
resource_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE);
|
|
if (!resource_attr)
|
|
return -EMSGSIZE;
|
|
|
|
if (nla_put_string(skb, DEVLINK_ATTR_RESOURCE_NAME, resource->name) ||
|
|
nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE, resource->size,
|
|
DEVLINK_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_ID, resource->id,
|
|
DEVLINK_ATTR_PAD))
|
|
goto nla_put_failure;
|
|
if (resource->size != resource->size_new &&
|
|
nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_NEW,
|
|
resource->size_new, DEVLINK_ATTR_PAD))
|
|
goto nla_put_failure;
|
|
if (devlink_resource_occ_put(resource, skb))
|
|
goto nla_put_failure;
|
|
if (devlink_resource_size_params_put(resource, skb))
|
|
goto nla_put_failure;
|
|
if (list_empty(&resource->resource_list))
|
|
goto out;
|
|
|
|
if (nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_SIZE_VALID,
|
|
resource->size_valid))
|
|
goto nla_put_failure;
|
|
|
|
child_resource_attr = nla_nest_start_noflag(skb,
|
|
DEVLINK_ATTR_RESOURCE_LIST);
|
|
if (!child_resource_attr)
|
|
goto nla_put_failure;
|
|
|
|
list_for_each_entry(child_resource, &resource->resource_list, list) {
|
|
if (devlink_resource_put(devlink, skb, child_resource))
|
|
goto resource_put_failure;
|
|
}
|
|
|
|
nla_nest_end(skb, child_resource_attr);
|
|
out:
|
|
nla_nest_end(skb, resource_attr);
|
|
return 0;
|
|
|
|
resource_put_failure:
|
|
nla_nest_cancel(skb, child_resource_attr);
|
|
nla_put_failure:
|
|
nla_nest_cancel(skb, resource_attr);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
static int devlink_resource_fill(struct genl_info *info,
|
|
enum devlink_command cmd, int flags)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
struct devlink_resource *resource;
|
|
struct nlattr *resources_attr;
|
|
struct sk_buff *skb = NULL;
|
|
struct nlmsghdr *nlh;
|
|
bool incomplete;
|
|
void *hdr;
|
|
int i;
|
|
int err;
|
|
|
|
resource = list_first_entry(&devlink->resource_list,
|
|
struct devlink_resource, list);
|
|
start_again:
|
|
err = devlink_nl_msg_reply_and_new(&skb, info);
|
|
if (err)
|
|
return err;
|
|
|
|
hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
|
|
&devlink_nl_family, NLM_F_MULTI, cmd);
|
|
if (!hdr) {
|
|
nlmsg_free(skb);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
if (devlink_nl_put_handle(skb, devlink))
|
|
goto nla_put_failure;
|
|
|
|
resources_attr = nla_nest_start_noflag(skb,
|
|
DEVLINK_ATTR_RESOURCE_LIST);
|
|
if (!resources_attr)
|
|
goto nla_put_failure;
|
|
|
|
incomplete = false;
|
|
i = 0;
|
|
list_for_each_entry_from(resource, &devlink->resource_list, list) {
|
|
err = devlink_resource_put(devlink, skb, resource);
|
|
if (err) {
|
|
if (!i)
|
|
goto err_resource_put;
|
|
incomplete = true;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
nla_nest_end(skb, resources_attr);
|
|
genlmsg_end(skb, hdr);
|
|
if (incomplete)
|
|
goto start_again;
|
|
send_done:
|
|
nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
|
|
NLMSG_DONE, 0, flags | NLM_F_MULTI);
|
|
if (!nlh) {
|
|
err = devlink_nl_msg_reply_and_new(&skb, info);
|
|
if (err)
|
|
return err;
|
|
goto send_done;
|
|
}
|
|
return genlmsg_reply(skb, info);
|
|
|
|
nla_put_failure:
|
|
err = -EMSGSIZE;
|
|
err_resource_put:
|
|
nlmsg_free(skb);
|
|
return err;
|
|
}
|
|
|
|
int devlink_nl_resource_dump_doit(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
|
|
if (list_empty(&devlink->resource_list))
|
|
return -EOPNOTSUPP;
|
|
|
|
return devlink_resource_fill(info, DEVLINK_CMD_RESOURCE_DUMP, 0);
|
|
}
|
|
|
|
int devlink_resources_validate(struct devlink *devlink,
|
|
struct devlink_resource *resource,
|
|
struct genl_info *info)
|
|
{
|
|
struct list_head *resource_list;
|
|
int err = 0;
|
|
|
|
if (resource)
|
|
resource_list = &resource->resource_list;
|
|
else
|
|
resource_list = &devlink->resource_list;
|
|
|
|
list_for_each_entry(resource, resource_list, list) {
|
|
if (!resource->size_valid)
|
|
return -EINVAL;
|
|
err = devlink_resources_validate(devlink, resource, info);
|
|
if (err)
|
|
return err;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* devl_resource_register - devlink resource register
|
|
*
|
|
* @devlink: devlink
|
|
* @resource_name: resource's name
|
|
* @resource_size: resource's size
|
|
* @resource_id: resource's id
|
|
* @parent_resource_id: resource's parent id
|
|
* @size_params: size parameters
|
|
*
|
|
* Generic resources should reuse the same names across drivers.
|
|
* Please see the generic resources list at:
|
|
* Documentation/networking/devlink/devlink-resource.rst
|
|
*/
|
|
int devl_resource_register(struct devlink *devlink,
|
|
const char *resource_name,
|
|
u64 resource_size,
|
|
u64 resource_id,
|
|
u64 parent_resource_id,
|
|
const struct devlink_resource_size_params *size_params)
|
|
{
|
|
struct devlink_resource *resource;
|
|
struct list_head *resource_list;
|
|
bool top_hierarchy;
|
|
|
|
lockdep_assert_held(&devlink->lock);
|
|
|
|
top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
|
|
|
|
resource = devlink_resource_find(devlink, NULL, resource_id);
|
|
if (resource)
|
|
return -EINVAL;
|
|
|
|
resource = kzalloc(sizeof(*resource), GFP_KERNEL);
|
|
if (!resource)
|
|
return -ENOMEM;
|
|
|
|
if (top_hierarchy) {
|
|
resource_list = &devlink->resource_list;
|
|
} else {
|
|
struct devlink_resource *parent_resource;
|
|
|
|
parent_resource = devlink_resource_find(devlink, NULL,
|
|
parent_resource_id);
|
|
if (parent_resource) {
|
|
resource_list = &parent_resource->resource_list;
|
|
resource->parent = parent_resource;
|
|
} else {
|
|
kfree(resource);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
resource->name = resource_name;
|
|
resource->size = resource_size;
|
|
resource->size_new = resource_size;
|
|
resource->id = resource_id;
|
|
resource->size_valid = true;
|
|
memcpy(&resource->size_params, size_params,
|
|
sizeof(resource->size_params));
|
|
INIT_LIST_HEAD(&resource->resource_list);
|
|
list_add_tail(&resource->list, resource_list);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devl_resource_register);
|
|
|
|
/**
|
|
* devlink_resource_register - devlink resource register
|
|
*
|
|
* @devlink: devlink
|
|
* @resource_name: resource's name
|
|
* @resource_size: resource's size
|
|
* @resource_id: resource's id
|
|
* @parent_resource_id: resource's parent id
|
|
* @size_params: size parameters
|
|
*
|
|
* Generic resources should reuse the same names across drivers.
|
|
* Please see the generic resources list at:
|
|
* Documentation/networking/devlink/devlink-resource.rst
|
|
*
|
|
* Context: Takes and release devlink->lock <mutex>.
|
|
*/
|
|
int devlink_resource_register(struct devlink *devlink,
|
|
const char *resource_name,
|
|
u64 resource_size,
|
|
u64 resource_id,
|
|
u64 parent_resource_id,
|
|
const struct devlink_resource_size_params *size_params)
|
|
{
|
|
int err;
|
|
|
|
devl_lock(devlink);
|
|
err = devl_resource_register(devlink, resource_name, resource_size,
|
|
resource_id, parent_resource_id, size_params);
|
|
devl_unlock(devlink);
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_resource_register);
|
|
|
|
static void devlink_resource_unregister(struct devlink *devlink,
|
|
struct devlink_resource *resource)
|
|
{
|
|
struct devlink_resource *tmp, *child_resource;
|
|
|
|
list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
|
|
list) {
|
|
devlink_resource_unregister(devlink, child_resource);
|
|
list_del(&child_resource->list);
|
|
kfree(child_resource);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* devl_resources_unregister - free all resources
|
|
*
|
|
* @devlink: devlink
|
|
*/
|
|
void devl_resources_unregister(struct devlink *devlink)
|
|
{
|
|
struct devlink_resource *tmp, *child_resource;
|
|
|
|
lockdep_assert_held(&devlink->lock);
|
|
|
|
list_for_each_entry_safe(child_resource, tmp, &devlink->resource_list,
|
|
list) {
|
|
devlink_resource_unregister(devlink, child_resource);
|
|
list_del(&child_resource->list);
|
|
kfree(child_resource);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(devl_resources_unregister);
|
|
|
|
/**
|
|
* devlink_resources_unregister - free all resources
|
|
*
|
|
* @devlink: devlink
|
|
*
|
|
* Context: Takes and release devlink->lock <mutex>.
|
|
*/
|
|
void devlink_resources_unregister(struct devlink *devlink)
|
|
{
|
|
devl_lock(devlink);
|
|
devl_resources_unregister(devlink);
|
|
devl_unlock(devlink);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_resources_unregister);
|
|
|
|
/**
|
|
* devl_resource_size_get - get and update size
|
|
*
|
|
* @devlink: devlink
|
|
* @resource_id: the requested resource id
|
|
* @p_resource_size: ptr to update
|
|
*/
|
|
int devl_resource_size_get(struct devlink *devlink,
|
|
u64 resource_id,
|
|
u64 *p_resource_size)
|
|
{
|
|
struct devlink_resource *resource;
|
|
|
|
lockdep_assert_held(&devlink->lock);
|
|
|
|
resource = devlink_resource_find(devlink, NULL, resource_id);
|
|
if (!resource)
|
|
return -EINVAL;
|
|
*p_resource_size = resource->size_new;
|
|
resource->size = resource->size_new;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devl_resource_size_get);
|
|
|
|
/**
|
|
* devl_resource_occ_get_register - register occupancy getter
|
|
*
|
|
* @devlink: devlink
|
|
* @resource_id: resource id
|
|
* @occ_get: occupancy getter callback
|
|
* @occ_get_priv: occupancy getter callback priv
|
|
*/
|
|
void devl_resource_occ_get_register(struct devlink *devlink,
|
|
u64 resource_id,
|
|
devlink_resource_occ_get_t *occ_get,
|
|
void *occ_get_priv)
|
|
{
|
|
struct devlink_resource *resource;
|
|
|
|
lockdep_assert_held(&devlink->lock);
|
|
|
|
resource = devlink_resource_find(devlink, NULL, resource_id);
|
|
if (WARN_ON(!resource))
|
|
return;
|
|
WARN_ON(resource->occ_get);
|
|
|
|
resource->occ_get = occ_get;
|
|
resource->occ_get_priv = occ_get_priv;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devl_resource_occ_get_register);
|
|
|
|
/**
|
|
* devlink_resource_occ_get_register - register occupancy getter
|
|
*
|
|
* @devlink: devlink
|
|
* @resource_id: resource id
|
|
* @occ_get: occupancy getter callback
|
|
* @occ_get_priv: occupancy getter callback priv
|
|
*
|
|
* Context: Takes and release devlink->lock <mutex>.
|
|
*/
|
|
void devlink_resource_occ_get_register(struct devlink *devlink,
|
|
u64 resource_id,
|
|
devlink_resource_occ_get_t *occ_get,
|
|
void *occ_get_priv)
|
|
{
|
|
devl_lock(devlink);
|
|
devl_resource_occ_get_register(devlink, resource_id,
|
|
occ_get, occ_get_priv);
|
|
devl_unlock(devlink);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_resource_occ_get_register);
|
|
|
|
/**
|
|
* devl_resource_occ_get_unregister - unregister occupancy getter
|
|
*
|
|
* @devlink: devlink
|
|
* @resource_id: resource id
|
|
*/
|
|
void devl_resource_occ_get_unregister(struct devlink *devlink,
|
|
u64 resource_id)
|
|
{
|
|
struct devlink_resource *resource;
|
|
|
|
lockdep_assert_held(&devlink->lock);
|
|
|
|
resource = devlink_resource_find(devlink, NULL, resource_id);
|
|
if (WARN_ON(!resource))
|
|
return;
|
|
WARN_ON(!resource->occ_get);
|
|
|
|
resource->occ_get = NULL;
|
|
resource->occ_get_priv = NULL;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);
|
|
|
|
/**
|
|
* devlink_resource_occ_get_unregister - unregister occupancy getter
|
|
*
|
|
* @devlink: devlink
|
|
* @resource_id: resource id
|
|
*
|
|
* Context: Takes and release devlink->lock <mutex>.
|
|
*/
|
|
void devlink_resource_occ_get_unregister(struct devlink *devlink,
|
|
u64 resource_id)
|
|
{
|
|
devl_lock(devlink);
|
|
devl_resource_occ_get_unregister(devlink, resource_id);
|
|
devl_unlock(devlink);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_resource_occ_get_unregister);
|