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(head: &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(dividend: size, divisor: resource->size_params.size_granularity, remainder: &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(nla: 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(nla: info->attrs[DEVLINK_ATTR_RESOURCE_SIZE]);
126 err = devlink_resource_validate_size(resource, size, extack: 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: 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, attrtype: DEVLINK_ATTR_RESOURCE_SIZE_GRAN,
145 value: size_params->size_granularity, padattr: DEVLINK_ATTR_PAD) ||
146 nla_put_u64_64bit(skb, attrtype: DEVLINK_ATTR_RESOURCE_SIZE_MAX,
147 value: size_params->size_max, padattr: DEVLINK_ATTR_PAD) ||
148 nla_put_u64_64bit(skb, attrtype: DEVLINK_ATTR_RESOURCE_SIZE_MIN,
149 value: size_params->size_min, padattr: DEVLINK_ATTR_PAD) ||
150 nla_put_u8(skb, attrtype: DEVLINK_ATTR_RESOURCE_UNIT, value: 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, attrtype: DEVLINK_ATTR_RESOURCE_OCC,
161 value: resource->occ_get(resource->occ_get_priv),
162 padattr: 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, attrtype: DEVLINK_ATTR_RESOURCE);
173 if (!resource_attr)
174 return -EMSGSIZE;
175
176 if (nla_put_string(skb, attrtype: DEVLINK_ATTR_RESOURCE_NAME, str: resource->name) ||
177 nla_put_u64_64bit(skb, attrtype: DEVLINK_ATTR_RESOURCE_SIZE, value: resource->size,
178 padattr: DEVLINK_ATTR_PAD) ||
179 nla_put_u64_64bit(skb, attrtype: DEVLINK_ATTR_RESOURCE_ID, value: resource->id,
180 padattr: DEVLINK_ATTR_PAD))
181 goto nla_put_failure;
182 if (resource->size != resource->size_new &&
183 nla_put_u64_64bit(skb, attrtype: DEVLINK_ATTR_RESOURCE_SIZE_NEW,
184 value: resource->size_new, padattr: 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(head: &resource->resource_list))
191 goto out;
192
193 if (nla_put_u8(skb, attrtype: DEVLINK_ATTR_RESOURCE_SIZE_VALID,
194 value: resource->size_valid))
195 goto nla_put_failure;
196
197 child_resource_attr = nla_nest_start_noflag(skb,
198 attrtype: 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, resource: child_resource))
204 goto resource_put_failure;
205 }
206
207 nla_nest_end(skb, start: child_resource_attr);
208out:
209 nla_nest_end(skb, start: resource_attr);
210 return 0;
211
212resource_put_failure:
213 nla_nest_cancel(skb, start: child_resource_attr);
214nla_put_failure:
215 nla_nest_cancel(skb, start: 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(msg: &skb, info);
236 if (err)
237 return err;
238
239 hdr = genlmsg_put(skb, portid: info->snd_portid, seq: info->snd_seq,
240 family: &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(msg: skb, devlink))
247 goto nla_put_failure;
248
249 resources_attr = nla_nest_start_noflag(skb,
250 attrtype: 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, start: resources_attr);
267 genlmsg_end(skb, hdr);
268 if (incomplete)
269 goto start_again;
270send_done:
271 nlh = nlmsg_put(skb, portid: info->snd_portid, seq: info->snd_seq,
272 NLMSG_DONE, payload: 0, flags: flags | NLM_F_MULTI);
273 if (!nlh) {
274 err = devlink_nl_msg_reply_and_new(msg: &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(head: &devlink->resource_list))
293 return -EOPNOTSUPP;
294
295 return devlink_resource_fill(info, cmd: DEVLINK_CMD_RESOURCE_DUMP, flags: 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(size: 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 resource_id: parent_resource_id);
364 if (parent_resource) {
365 resource_list = &parent_resource->resource_list;
366 resource->parent = parent_resource;
367 } else {
368 kfree(objp: 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(list: &resource->resource_list);
381 list_add_tail(new: &resource->list, head: 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, resource: child_resource);
428 list_del(entry: &child_resource->list);
429 kfree(objp: 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, resource: child_resource);
447 list_del(entry: &child_resource->list);
448 kfree(objp: 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

source code of linux/net/devlink/resource.c