Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.10.11.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
  4 * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
  5 */
  6
  7#include "devl_internal.h"
  8
  9/**
 10 * struct devlink_resource - devlink resource
 11 * @name: name of the resource
 12 * @id: id, per devlink instance
 13 * @size: size of the resource
 14 * @size_new: updated size of the resource, reload is needed
 15 * @size_valid: valid in case the total size of the resource is valid
 16 *              including its children
 17 * @parent: parent resource
 18 * @size_params: size parameters
 19 * @list: parent list
 20 * @resource_list: list of child resources
 21 * @occ_get: occupancy getter callback
 22 * @occ_get_priv: occupancy getter callback priv
 23 */
 24struct devlink_resource {
 25	const char *name;
 26	u64 id;
 27	u64 size;
 28	u64 size_new;
 29	bool size_valid;
 30	struct devlink_resource *parent;
 31	struct devlink_resource_size_params size_params;
 32	struct list_head list;
 33	struct list_head resource_list;
 34	devlink_resource_occ_get_t *occ_get;
 35	void *occ_get_priv;
 36};
 37
 38static struct devlink_resource *
 39devlink_resource_find(struct devlink *devlink,
 40		      struct devlink_resource *resource, u64 resource_id)
 41{
 42	struct list_head *resource_list;
 43
 44	if (resource)
 45		resource_list = &resource->resource_list;
 46	else
 47		resource_list = &devlink->resource_list;
 48
 49	list_for_each_entry(resource, resource_list, list) {
 50		struct devlink_resource *child_resource;
 51
 52		if (resource->id == resource_id)
 53			return resource;
 54
 55		child_resource = devlink_resource_find(devlink, resource,
 56						       resource_id);
 57		if (child_resource)
 58			return child_resource;
 59	}
 60	return NULL;
 61}
 62
 63static void
 64devlink_resource_validate_children(struct devlink_resource *resource)
 65{
 66	struct devlink_resource *child_resource;
 67	bool size_valid = true;
 68	u64 parts_size = 0;
 69
 70	if (list_empty(&resource->resource_list))
 71		goto out;
 72
 73	list_for_each_entry(child_resource, &resource->resource_list, list)
 74		parts_size += child_resource->size_new;
 75
 76	if (parts_size > resource->size_new)
 77		size_valid = false;
 78out:
 79	resource->size_valid = size_valid;
 80}
 81
 82static int
 83devlink_resource_validate_size(struct devlink_resource *resource, u64 size,
 84			       struct netlink_ext_ack *extack)
 85{
 86	u64 reminder;
 87	int err = 0;
 88
 89	if (size > resource->size_params.size_max) {
 90		NL_SET_ERR_MSG(extack, "Size larger than maximum");
 91		err = -EINVAL;
 92	}
 93
 94	if (size < resource->size_params.size_min) {
 95		NL_SET_ERR_MSG(extack, "Size smaller than minimum");
 96		err = -EINVAL;
 97	}
 98
 99	div64_u64_rem(size, resource->size_params.size_granularity, &reminder);
100	if (reminder) {
101		NL_SET_ERR_MSG(extack, "Wrong granularity");
102		err = -EINVAL;
103	}
104
105	return err;
106}
107
108int devlink_nl_resource_set_doit(struct sk_buff *skb, struct genl_info *info)
109{
110	struct devlink *devlink = info->user_ptr[0];
111	struct devlink_resource *resource;
112	u64 resource_id;
113	u64 size;
114	int err;
115
116	if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_ID) ||
117	    GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_SIZE))
118		return -EINVAL;
119	resource_id = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_ID]);
120
121	resource = devlink_resource_find(devlink, NULL, resource_id);
122	if (!resource)
123		return -EINVAL;
124
125	size = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_SIZE]);
126	err = devlink_resource_validate_size(resource, size, info->extack);
127	if (err)
128		return err;
129
130	resource->size_new = size;
131	devlink_resource_validate_children(resource);
132	if (resource->parent)
133		devlink_resource_validate_children(resource->parent);
134	return 0;
135}
136
137static int
138devlink_resource_size_params_put(struct devlink_resource *resource,
139				 struct sk_buff *skb)
140{
141	struct devlink_resource_size_params *size_params;
142
143	size_params = &resource->size_params;
144	if (devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_GRAN,
145			       size_params->size_granularity) ||
146	    devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_MAX,
147			       size_params->size_max) ||
148	    devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_MIN,
149			       size_params->size_min) ||
150	    nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_UNIT, size_params->unit))
151		return -EMSGSIZE;
152	return 0;
153}
154
155static int devlink_resource_occ_put(struct devlink_resource *resource,
156				    struct sk_buff *skb)
157{
158	if (!resource->occ_get)
159		return 0;
160	return devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_OCC,
161				  resource->occ_get(resource->occ_get_priv));
162}
163
164static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb,
165				struct devlink_resource *resource)
166{
167	struct devlink_resource *child_resource;
168	struct nlattr *child_resource_attr;
169	struct nlattr *resource_attr;
170
171	resource_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE);
172	if (!resource_attr)
173		return -EMSGSIZE;
174
175	if (nla_put_string(skb, DEVLINK_ATTR_RESOURCE_NAME, resource->name) ||
176	    devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE, resource->size) ||
177	    devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_ID, resource->id))
178		goto nla_put_failure;
179	if (resource->size != resource->size_new &&
180	    devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_NEW,
181			       resource->size_new))
182		goto nla_put_failure;
183	if (devlink_resource_occ_put(resource, skb))
184		goto nla_put_failure;
185	if (devlink_resource_size_params_put(resource, skb))
186		goto nla_put_failure;
187	if (list_empty(&resource->resource_list))
188		goto out;
189
190	if (nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_SIZE_VALID,
191		       resource->size_valid))
192		goto nla_put_failure;
193
194	child_resource_attr = nla_nest_start_noflag(skb,
195						    DEVLINK_ATTR_RESOURCE_LIST);
196	if (!child_resource_attr)
197		goto nla_put_failure;
198
199	list_for_each_entry(child_resource, &resource->resource_list, list) {
200		if (devlink_resource_put(devlink, skb, child_resource))
201			goto resource_put_failure;
202	}
203
204	nla_nest_end(skb, child_resource_attr);
205out:
206	nla_nest_end(skb, resource_attr);
207	return 0;
208
209resource_put_failure:
210	nla_nest_cancel(skb, child_resource_attr);
211nla_put_failure:
212	nla_nest_cancel(skb, resource_attr);
213	return -EMSGSIZE;
214}
215
216static int devlink_resource_fill(struct genl_info *info,
217				 enum devlink_command cmd, int flags)
218{
219	struct devlink *devlink = info->user_ptr[0];
220	struct devlink_resource *resource;
221	struct nlattr *resources_attr;
222	struct sk_buff *skb = NULL;
223	struct nlmsghdr *nlh;
224	bool incomplete;
225	void *hdr;
226	int i;
227	int err;
228
229	resource = list_first_entry(&devlink->resource_list,
230				    struct devlink_resource, list);
231start_again:
232	err = devlink_nl_msg_reply_and_new(&skb, info);
233	if (err)
234		return err;
235
236	hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
237			  &devlink_nl_family, NLM_F_MULTI, cmd);
238	if (!hdr) {
239		nlmsg_free(skb);
240		return -EMSGSIZE;
241	}
242
243	if (devlink_nl_put_handle(skb, devlink))
244		goto nla_put_failure;
245
246	resources_attr = nla_nest_start_noflag(skb,
247					       DEVLINK_ATTR_RESOURCE_LIST);
248	if (!resources_attr)
249		goto nla_put_failure;
250
251	incomplete = false;
252	i = 0;
253	list_for_each_entry_from(resource, &devlink->resource_list, list) {
254		err = devlink_resource_put(devlink, skb, resource);
255		if (err) {
256			if (!i)
257				goto err_resource_put;
258			incomplete = true;
259			break;
260		}
261		i++;
262	}
263	nla_nest_end(skb, resources_attr);
264	genlmsg_end(skb, hdr);
265	if (incomplete)
266		goto start_again;
267send_done:
268	nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
269			NLMSG_DONE, 0, flags | NLM_F_MULTI);
270	if (!nlh) {
271		err = devlink_nl_msg_reply_and_new(&skb, info);
272		if (err)
273			return err;
274		goto send_done;
275	}
276	return genlmsg_reply(skb, info);
277
278nla_put_failure:
279	err = -EMSGSIZE;
280err_resource_put:
281	nlmsg_free(skb);
282	return err;
283}
284
285int devlink_nl_resource_dump_doit(struct sk_buff *skb, struct genl_info *info)
286{
287	struct devlink *devlink = info->user_ptr[0];
288
289	if (list_empty(&devlink->resource_list))
290		return -EOPNOTSUPP;
291
292	return devlink_resource_fill(info, DEVLINK_CMD_RESOURCE_DUMP, 0);
293}
294
295int devlink_resources_validate(struct devlink *devlink,
296			       struct devlink_resource *resource,
297			       struct genl_info *info)
298{
299	struct list_head *resource_list;
300	int err = 0;
301
302	if (resource)
303		resource_list = &resource->resource_list;
304	else
305		resource_list = &devlink->resource_list;
306
307	list_for_each_entry(resource, resource_list, list) {
308		if (!resource->size_valid)
309			return -EINVAL;
310		err = devlink_resources_validate(devlink, resource, info);
311		if (err)
312			return err;
313	}
314	return err;
315}
316
317/**
318 * devl_resource_register - devlink resource register
319 *
320 * @devlink: devlink
321 * @resource_name: resource's name
322 * @resource_size: resource's size
323 * @resource_id: resource's id
324 * @parent_resource_id: resource's parent id
325 * @size_params: size parameters
326 *
327 * Generic resources should reuse the same names across drivers.
328 * Please see the generic resources list at:
329 * Documentation/networking/devlink/devlink-resource.rst
330 */
331int devl_resource_register(struct devlink *devlink,
332			   const char *resource_name,
333			   u64 resource_size,
334			   u64 resource_id,
335			   u64 parent_resource_id,
336			   const struct devlink_resource_size_params *size_params)
337{
338	struct devlink_resource *resource;
339	struct list_head *resource_list;
340	bool top_hierarchy;
341
342	lockdep_assert_held(&devlink->lock);
343
344	top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
345
346	resource = devlink_resource_find(devlink, NULL, resource_id);
347	if (resource)
348		return -EEXIST;
349
350	resource = kzalloc(sizeof(*resource), GFP_KERNEL);
351	if (!resource)
352		return -ENOMEM;
353
354	if (top_hierarchy) {
355		resource_list = &devlink->resource_list;
356	} else {
357		struct devlink_resource *parent_resource;
358
359		parent_resource = devlink_resource_find(devlink, NULL,
360							parent_resource_id);
361		if (parent_resource) {
362			resource_list = &parent_resource->resource_list;
363			resource->parent = parent_resource;
364		} else {
365			kfree(resource);
366			return -EINVAL;
367		}
368	}
369
370	resource->name = resource_name;
371	resource->size = resource_size;
372	resource->size_new = resource_size;
373	resource->id = resource_id;
374	resource->size_valid = true;
375	memcpy(&resource->size_params, size_params,
376	       sizeof(resource->size_params));
377	INIT_LIST_HEAD(&resource->resource_list);
378	list_add_tail(&resource->list, resource_list);
379
380	return 0;
381}
382EXPORT_SYMBOL_GPL(devl_resource_register);
383
384static void devlink_resource_unregister(struct devlink *devlink,
385					struct devlink_resource *resource)
386{
387	struct devlink_resource *tmp, *child_resource;
388
389	list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
390				 list) {
391		devlink_resource_unregister(devlink, child_resource);
392		list_del(&child_resource->list);
393		kfree(child_resource);
394	}
395}
396
397/**
398 * devl_resources_unregister - free all resources
399 *
400 * @devlink: devlink
401 */
402void devl_resources_unregister(struct devlink *devlink)
403{
404	struct devlink_resource *tmp, *child_resource;
405
406	lockdep_assert_held(&devlink->lock);
407
408	list_for_each_entry_safe(child_resource, tmp, &devlink->resource_list,
409				 list) {
410		devlink_resource_unregister(devlink, child_resource);
411		list_del(&child_resource->list);
412		kfree(child_resource);
413	}
414}
415EXPORT_SYMBOL_GPL(devl_resources_unregister);
416
417/**
418 *	devlink_resources_unregister - free all resources
419 *
420 *	@devlink: devlink
421 *
422 *	Context: Takes and release devlink->lock <mutex>.
423 */
424void devlink_resources_unregister(struct devlink *devlink)
425{
426	devl_lock(devlink);
427	devl_resources_unregister(devlink);
428	devl_unlock(devlink);
429}
430EXPORT_SYMBOL_GPL(devlink_resources_unregister);
431
432/**
433 * devl_resource_size_get - get and update size
434 *
435 * @devlink: devlink
436 * @resource_id: the requested resource id
437 * @p_resource_size: ptr to update
438 */
439int devl_resource_size_get(struct devlink *devlink,
440			   u64 resource_id,
441			   u64 *p_resource_size)
442{
443	struct devlink_resource *resource;
444
445	lockdep_assert_held(&devlink->lock);
446
447	resource = devlink_resource_find(devlink, NULL, resource_id);
448	if (!resource)
449		return -EINVAL;
450	*p_resource_size = resource->size_new;
451	resource->size = resource->size_new;
452	return 0;
453}
454EXPORT_SYMBOL_GPL(devl_resource_size_get);
455
456/**
457 * devl_resource_occ_get_register - register occupancy getter
458 *
459 * @devlink: devlink
460 * @resource_id: resource id
461 * @occ_get: occupancy getter callback
462 * @occ_get_priv: occupancy getter callback priv
463 */
464void devl_resource_occ_get_register(struct devlink *devlink,
465				    u64 resource_id,
466				    devlink_resource_occ_get_t *occ_get,
467				    void *occ_get_priv)
468{
469	struct devlink_resource *resource;
470
471	lockdep_assert_held(&devlink->lock);
472
473	resource = devlink_resource_find(devlink, NULL, resource_id);
474	if (WARN_ON(!resource))
475		return;
476	WARN_ON(resource->occ_get);
477
478	resource->occ_get = occ_get;
479	resource->occ_get_priv = occ_get_priv;
480}
481EXPORT_SYMBOL_GPL(devl_resource_occ_get_register);
482
483/**
484 * devl_resource_occ_get_unregister - unregister occupancy getter
485 *
486 * @devlink: devlink
487 * @resource_id: resource id
488 */
489void devl_resource_occ_get_unregister(struct devlink *devlink,
490				      u64 resource_id)
491{
492	struct devlink_resource *resource;
493
494	lockdep_assert_held(&devlink->lock);
495
496	resource = devlink_resource_find(devlink, NULL, resource_id);
497	if (WARN_ON(!resource))
498		return;
499	WARN_ON(!resource->occ_get);
500
501	resource->occ_get = NULL;
502	resource->occ_get_priv = NULL;
503}
504EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);