xref: /linux/net/devlink/resource.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
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 *
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
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
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 
108 int 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 
137 static int
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 (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 
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 devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_OCC,
161 				  resource->occ_get(resource->occ_get_priv));
162 }
163 
164 static 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);
205 out:
206 	nla_nest_end(skb, resource_attr);
207 	return 0;
208 
209 resource_put_failure:
210 	nla_nest_cancel(skb, child_resource_attr);
211 nla_put_failure:
212 	nla_nest_cancel(skb, resource_attr);
213 	return -EMSGSIZE;
214 }
215 
216 static 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);
231 start_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;
267 send_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 
278 nla_put_failure:
279 	err = -EMSGSIZE;
280 err_resource_put:
281 	nlmsg_free(skb);
282 	return err;
283 }
284 
285 int 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 
295 int 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  */
331 int 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 }
382 EXPORT_SYMBOL_GPL(devl_resource_register);
383 
384 static 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  */
402 void 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 }
415 EXPORT_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  */
424 void devlink_resources_unregister(struct devlink *devlink)
425 {
426 	devl_lock(devlink);
427 	devl_resources_unregister(devlink);
428 	devl_unlock(devlink);
429 }
430 EXPORT_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  */
439 int 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 }
454 EXPORT_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  */
464 void 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 }
481 EXPORT_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  */
489 void 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 }
504 EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);
505