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