xref: /openbmc/linux/net/devlink/resource.c (revision a9f96007)
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  */
24 struct 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 
38 static struct devlink_resource *
devlink_resource_find(struct devlink * devlink,struct devlink_resource * resource,u64 resource_id)39 devlink_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 
63 static void
devlink_resource_validate_children(struct devlink_resource * resource)64 devlink_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;
78 out:
79 	resource->size_valid = size_valid;
80 }
81 
82 static int
devlink_resource_validate_size(struct devlink_resource * resource,u64 size,struct netlink_ext_ack * extack)83 devlink_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 
devlink_nl_cmd_resource_set(struct sk_buff * skb,struct genl_info * info)108 int devlink_nl_cmd_resource_set(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 
137 static int
devlink_resource_size_params_put(struct devlink_resource * resource,struct sk_buff * skb)138 devlink_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 
devlink_resource_occ_put(struct devlink_resource * resource,struct sk_buff * skb)155 static 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 
devlink_resource_put(struct devlink * devlink,struct sk_buff * skb,struct devlink_resource * resource)165 static 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);
208 out:
209 	nla_nest_end(skb, resource_attr);
210 	return 0;
211 
212 resource_put_failure:
213 	nla_nest_cancel(skb, child_resource_attr);
214 nla_put_failure:
215 	nla_nest_cancel(skb, resource_attr);
216 	return -EMSGSIZE;
217 }
218 
devlink_resource_fill(struct genl_info * info,enum devlink_command cmd,int flags)219 static 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);
234 start_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;
270 send_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 
281 nla_put_failure:
282 	err = -EMSGSIZE;
283 err_resource_put:
284 	nlmsg_free(skb);
285 	return err;
286 }
287 
devlink_nl_cmd_resource_dump(struct sk_buff * skb,struct genl_info * info)288 int devlink_nl_cmd_resource_dump(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 
devlink_resources_validate(struct devlink * devlink,struct devlink_resource * resource,struct genl_info * info)298 int 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  */
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)334 int 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 }
385 EXPORT_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  */
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)403 int 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 }
418 EXPORT_SYMBOL_GPL(devlink_resource_register);
419 
devlink_resource_unregister(struct devlink * devlink,struct devlink_resource * resource)420 static 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  */
devl_resources_unregister(struct devlink * devlink)438 void 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 }
451 EXPORT_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  */
devlink_resources_unregister(struct devlink * devlink)460 void devlink_resources_unregister(struct devlink *devlink)
461 {
462 	devl_lock(devlink);
463 	devl_resources_unregister(devlink);
464 	devl_unlock(devlink);
465 }
466 EXPORT_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  */
devl_resource_size_get(struct devlink * devlink,u64 resource_id,u64 * p_resource_size)475 int 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 }
490 EXPORT_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  */
devl_resource_occ_get_register(struct devlink * devlink,u64 resource_id,devlink_resource_occ_get_t * occ_get,void * occ_get_priv)500 void 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 }
517 EXPORT_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  */
devlink_resource_occ_get_register(struct devlink * devlink,u64 resource_id,devlink_resource_occ_get_t * occ_get,void * occ_get_priv)529 void 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 }
539 EXPORT_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  */
devl_resource_occ_get_unregister(struct devlink * devlink,u64 resource_id)547 void 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 }
562 EXPORT_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  */
devlink_resource_occ_get_unregister(struct devlink * devlink,u64 resource_id)572 void 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 }
579 EXPORT_SYMBOL_GPL(devlink_resource_occ_get_unregister);
580