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 (nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_GRAN,
145			      size_params->size_granularity, DEVLINK_ATTR_PAD) ||
146	    nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_MAX,
147			      size_params->size_max, DEVLINK_ATTR_PAD) ||
148	    nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_MIN,
149			      size_params->size_min, DEVLINK_ATTR_PAD) ||
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 nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_OCC,
161				 resource->occ_get(resource->occ_get_priv),
162				 DEVLINK_ATTR_PAD);
163}
164
165static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb,
166				struct devlink_resource *resource)
167{
168	struct devlink_resource *child_resource;
169	struct nlattr *child_resource_attr;
170	struct nlattr *resource_attr;
171
172	resource_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE);
173	if (!resource_attr)
174		return -EMSGSIZE;
175
176	if (nla_put_string(skb, DEVLINK_ATTR_RESOURCE_NAME, resource->name) ||
177	    nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE, resource->size,
178			      DEVLINK_ATTR_PAD) ||
179	    nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_ID, resource->id,
180			      DEVLINK_ATTR_PAD))
181		goto nla_put_failure;
182	if (resource->size != resource->size_new &&
183	    nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_NEW,
184			      resource->size_new, DEVLINK_ATTR_PAD))
185		goto nla_put_failure;
186	if (devlink_resource_occ_put(resource, skb))
187		goto nla_put_failure;
188	if (devlink_resource_size_params_put(resource, skb))
189		goto nla_put_failure;
190	if (list_empty(&resource->resource_list))
191		goto out;
192
193	if (nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_SIZE_VALID,
194		       resource->size_valid))
195		goto nla_put_failure;
196
197	child_resource_attr = nla_nest_start_noflag(skb,
198						    DEVLINK_ATTR_RESOURCE_LIST);
199	if (!child_resource_attr)
200		goto nla_put_failure;
201
202	list_for_each_entry(child_resource, &resource->resource_list, list) {
203		if (devlink_resource_put(devlink, skb, child_resource))
204			goto resource_put_failure;
205	}
206
207	nla_nest_end(skb, child_resource_attr);
208out:
209	nla_nest_end(skb, resource_attr);
210	return 0;
211
212resource_put_failure:
213	nla_nest_cancel(skb, child_resource_attr);
214nla_put_failure:
215	nla_nest_cancel(skb, resource_attr);
216	return -EMSGSIZE;
217}
218
219static int devlink_resource_fill(struct genl_info *info,
220				 enum devlink_command cmd, int flags)
221{
222	struct devlink *devlink = info->user_ptr[0];
223	struct devlink_resource *resource;
224	struct nlattr *resources_attr;
225	struct sk_buff *skb = NULL;
226	struct nlmsghdr *nlh;
227	bool incomplete;
228	void *hdr;
229	int i;
230	int err;
231
232	resource = list_first_entry(&devlink->resource_list,
233				    struct devlink_resource, list);
234start_again:
235	err = devlink_nl_msg_reply_and_new(&skb, info);
236	if (err)
237		return err;
238
239	hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
240			  &devlink_nl_family, NLM_F_MULTI, cmd);
241	if (!hdr) {
242		nlmsg_free(skb);
243		return -EMSGSIZE;
244	}
245
246	if (devlink_nl_put_handle(skb, devlink))
247		goto nla_put_failure;
248
249	resources_attr = nla_nest_start_noflag(skb,
250					       DEVLINK_ATTR_RESOURCE_LIST);
251	if (!resources_attr)
252		goto nla_put_failure;
253
254	incomplete = false;
255	i = 0;
256	list_for_each_entry_from(resource, &devlink->resource_list, list) {
257		err = devlink_resource_put(devlink, skb, resource);
258		if (err) {
259			if (!i)
260				goto err_resource_put;
261			incomplete = true;
262			break;
263		}
264		i++;
265	}
266	nla_nest_end(skb, resources_attr);
267	genlmsg_end(skb, hdr);
268	if (incomplete)
269		goto start_again;
270send_done:
271	nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
272			NLMSG_DONE, 0, flags | NLM_F_MULTI);
273	if (!nlh) {
274		err = devlink_nl_msg_reply_and_new(&skb, info);
275		if (err)
276			return err;
277		goto send_done;
278	}
279	return genlmsg_reply(skb, info);
280
281nla_put_failure:
282	err = -EMSGSIZE;
283err_resource_put:
284	nlmsg_free(skb);
285	return err;
286}
287
288int devlink_nl_resource_dump_doit(struct sk_buff *skb, struct genl_info *info)
289{
290	struct devlink *devlink = info->user_ptr[0];
291
292	if (list_empty(&devlink->resource_list))
293		return -EOPNOTSUPP;
294
295	return devlink_resource_fill(info, DEVLINK_CMD_RESOURCE_DUMP, 0);
296}
297
298int devlink_resources_validate(struct devlink *devlink,
299			       struct devlink_resource *resource,
300			       struct genl_info *info)
301{
302	struct list_head *resource_list;
303	int err = 0;
304
305	if (resource)
306		resource_list = &resource->resource_list;
307	else
308		resource_list = &devlink->resource_list;
309
310	list_for_each_entry(resource, resource_list, list) {
311		if (!resource->size_valid)
312			return -EINVAL;
313		err = devlink_resources_validate(devlink, resource, info);
314		if (err)
315			return err;
316	}
317	return err;
318}
319
320/**
321 * devl_resource_register - devlink resource register
322 *
323 * @devlink: devlink
324 * @resource_name: resource's name
325 * @resource_size: resource's size
326 * @resource_id: resource's id
327 * @parent_resource_id: resource's parent id
328 * @size_params: size parameters
329 *
330 * Generic resources should reuse the same names across drivers.
331 * Please see the generic resources list at:
332 * Documentation/networking/devlink/devlink-resource.rst
333 */
334int devl_resource_register(struct devlink *devlink,
335			   const char *resource_name,
336			   u64 resource_size,
337			   u64 resource_id,
338			   u64 parent_resource_id,
339			   const struct devlink_resource_size_params *size_params)
340{
341	struct devlink_resource *resource;
342	struct list_head *resource_list;
343	bool top_hierarchy;
344
345	lockdep_assert_held(&devlink->lock);
346
347	top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
348
349	resource = devlink_resource_find(devlink, NULL, resource_id);
350	if (resource)
351		return -EINVAL;
352
353	resource = kzalloc(sizeof(*resource), GFP_KERNEL);
354	if (!resource)
355		return -ENOMEM;
356
357	if (top_hierarchy) {
358		resource_list = &devlink->resource_list;
359	} else {
360		struct devlink_resource *parent_resource;
361
362		parent_resource = devlink_resource_find(devlink, NULL,
363							parent_resource_id);
364		if (parent_resource) {
365			resource_list = &parent_resource->resource_list;
366			resource->parent = parent_resource;
367		} else {
368			kfree(resource);
369			return -EINVAL;
370		}
371	}
372
373	resource->name = resource_name;
374	resource->size = resource_size;
375	resource->size_new = resource_size;
376	resource->id = resource_id;
377	resource->size_valid = true;
378	memcpy(&resource->size_params, size_params,
379	       sizeof(resource->size_params));
380	INIT_LIST_HEAD(&resource->resource_list);
381	list_add_tail(&resource->list, resource_list);
382
383	return 0;
384}
385EXPORT_SYMBOL_GPL(devl_resource_register);
386
387/**
388 *	devlink_resource_register - devlink resource register
389 *
390 *	@devlink: devlink
391 *	@resource_name: resource's name
392 *	@resource_size: resource's size
393 *	@resource_id: resource's id
394 *	@parent_resource_id: resource's parent id
395 *	@size_params: size parameters
396 *
397 *	Generic resources should reuse the same names across drivers.
398 *	Please see the generic resources list at:
399 *	Documentation/networking/devlink/devlink-resource.rst
400 *
401 *	Context: Takes and release devlink->lock <mutex>.
402 */
403int devlink_resource_register(struct devlink *devlink,
404			      const char *resource_name,
405			      u64 resource_size,
406			      u64 resource_id,
407			      u64 parent_resource_id,
408			      const struct devlink_resource_size_params *size_params)
409{
410	int err;
411
412	devl_lock(devlink);
413	err = devl_resource_register(devlink, resource_name, resource_size,
414				     resource_id, parent_resource_id, size_params);
415	devl_unlock(devlink);
416	return err;
417}
418EXPORT_SYMBOL_GPL(devlink_resource_register);
419
420static void devlink_resource_unregister(struct devlink *devlink,
421					struct devlink_resource *resource)
422{
423	struct devlink_resource *tmp, *child_resource;
424
425	list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
426				 list) {
427		devlink_resource_unregister(devlink, child_resource);
428		list_del(&child_resource->list);
429		kfree(child_resource);
430	}
431}
432
433/**
434 * devl_resources_unregister - free all resources
435 *
436 * @devlink: devlink
437 */
438void devl_resources_unregister(struct devlink *devlink)
439{
440	struct devlink_resource *tmp, *child_resource;
441
442	lockdep_assert_held(&devlink->lock);
443
444	list_for_each_entry_safe(child_resource, tmp, &devlink->resource_list,
445				 list) {
446		devlink_resource_unregister(devlink, child_resource);
447		list_del(&child_resource->list);
448		kfree(child_resource);
449	}
450}
451EXPORT_SYMBOL_GPL(devl_resources_unregister);
452
453/**
454 *	devlink_resources_unregister - free all resources
455 *
456 *	@devlink: devlink
457 *
458 *	Context: Takes and release devlink->lock <mutex>.
459 */
460void devlink_resources_unregister(struct devlink *devlink)
461{
462	devl_lock(devlink);
463	devl_resources_unregister(devlink);
464	devl_unlock(devlink);
465}
466EXPORT_SYMBOL_GPL(devlink_resources_unregister);
467
468/**
469 * devl_resource_size_get - get and update size
470 *
471 * @devlink: devlink
472 * @resource_id: the requested resource id
473 * @p_resource_size: ptr to update
474 */
475int devl_resource_size_get(struct devlink *devlink,
476			   u64 resource_id,
477			   u64 *p_resource_size)
478{
479	struct devlink_resource *resource;
480
481	lockdep_assert_held(&devlink->lock);
482
483	resource = devlink_resource_find(devlink, NULL, resource_id);
484	if (!resource)
485		return -EINVAL;
486	*p_resource_size = resource->size_new;
487	resource->size = resource->size_new;
488	return 0;
489}
490EXPORT_SYMBOL_GPL(devl_resource_size_get);
491
492/**
493 * devl_resource_occ_get_register - register occupancy getter
494 *
495 * @devlink: devlink
496 * @resource_id: resource id
497 * @occ_get: occupancy getter callback
498 * @occ_get_priv: occupancy getter callback priv
499 */
500void devl_resource_occ_get_register(struct devlink *devlink,
501				    u64 resource_id,
502				    devlink_resource_occ_get_t *occ_get,
503				    void *occ_get_priv)
504{
505	struct devlink_resource *resource;
506
507	lockdep_assert_held(&devlink->lock);
508
509	resource = devlink_resource_find(devlink, NULL, resource_id);
510	if (WARN_ON(!resource))
511		return;
512	WARN_ON(resource->occ_get);
513
514	resource->occ_get = occ_get;
515	resource->occ_get_priv = occ_get_priv;
516}
517EXPORT_SYMBOL_GPL(devl_resource_occ_get_register);
518
519/**
520 *	devlink_resource_occ_get_register - register occupancy getter
521 *
522 *	@devlink: devlink
523 *	@resource_id: resource id
524 *	@occ_get: occupancy getter callback
525 *	@occ_get_priv: occupancy getter callback priv
526 *
527 *	Context: Takes and release devlink->lock <mutex>.
528 */
529void devlink_resource_occ_get_register(struct devlink *devlink,
530				       u64 resource_id,
531				       devlink_resource_occ_get_t *occ_get,
532				       void *occ_get_priv)
533{
534	devl_lock(devlink);
535	devl_resource_occ_get_register(devlink, resource_id,
536				       occ_get, occ_get_priv);
537	devl_unlock(devlink);
538}
539EXPORT_SYMBOL_GPL(devlink_resource_occ_get_register);
540
541/**
542 * devl_resource_occ_get_unregister - unregister occupancy getter
543 *
544 * @devlink: devlink
545 * @resource_id: resource id
546 */
547void devl_resource_occ_get_unregister(struct devlink *devlink,
548				      u64 resource_id)
549{
550	struct devlink_resource *resource;
551
552	lockdep_assert_held(&devlink->lock);
553
554	resource = devlink_resource_find(devlink, NULL, resource_id);
555	if (WARN_ON(!resource))
556		return;
557	WARN_ON(!resource->occ_get);
558
559	resource->occ_get = NULL;
560	resource->occ_get_priv = NULL;
561}
562EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);
563
564/**
565 *	devlink_resource_occ_get_unregister - unregister occupancy getter
566 *
567 *	@devlink: devlink
568 *	@resource_id: resource id
569 *
570 *	Context: Takes and release devlink->lock <mutex>.
571 */
572void devlink_resource_occ_get_unregister(struct devlink *devlink,
573					 u64 resource_id)
574{
575	devl_lock(devlink);
576	devl_resource_occ_get_unregister(devlink, resource_id);
577	devl_unlock(devlink);
578}
579EXPORT_SYMBOL_GPL(devlink_resource_occ_get_unregister);
580