Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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 *
39__devlink_resource_find(struct list_head *resource_list_head,
40 struct devlink_resource *resource,
41 u64 resource_id)
42{
43 struct list_head *resource_list;
44
45 if (resource)
46 resource_list = &resource->resource_list;
47 else
48 resource_list = resource_list_head;
49
50 list_for_each_entry(resource, resource_list, list) {
51 struct devlink_resource *child_resource;
52
53 if (resource->id == resource_id)
54 return resource;
55
56 child_resource = __devlink_resource_find(resource_list_head,
57 resource,
58 resource_id);
59 if (child_resource)
60 return child_resource;
61 }
62 return NULL;
63}
64
65static struct devlink_resource *
66devlink_resource_find(struct devlink *devlink,
67 struct devlink_resource *resource, u64 resource_id)
68{
69 return __devlink_resource_find(&devlink->resource_list,
70 resource, resource_id);
71}
72
73static void
74devlink_resource_validate_children(struct devlink_resource *resource)
75{
76 struct devlink_resource *child_resource;
77 bool size_valid = true;
78 u64 parts_size = 0;
79
80 if (list_empty(&resource->resource_list))
81 goto out;
82
83 list_for_each_entry(child_resource, &resource->resource_list, list)
84 parts_size += child_resource->size_new;
85
86 if (parts_size > resource->size_new)
87 size_valid = false;
88out:
89 resource->size_valid = size_valid;
90}
91
92static int
93devlink_resource_validate_size(struct devlink_resource *resource, u64 size,
94 struct netlink_ext_ack *extack)
95{
96 u64 reminder;
97 int err = 0;
98
99 if (size > resource->size_params.size_max) {
100 NL_SET_ERR_MSG(extack, "Size larger than maximum");
101 err = -EINVAL;
102 }
103
104 if (size < resource->size_params.size_min) {
105 NL_SET_ERR_MSG(extack, "Size smaller than minimum");
106 err = -EINVAL;
107 }
108
109 div64_u64_rem(size, resource->size_params.size_granularity, &reminder);
110 if (reminder) {
111 NL_SET_ERR_MSG(extack, "Wrong granularity");
112 err = -EINVAL;
113 }
114
115 return err;
116}
117
118int devlink_nl_resource_set_doit(struct sk_buff *skb, struct genl_info *info)
119{
120 struct devlink *devlink = info->user_ptr[0];
121 struct devlink_resource *resource;
122 u64 resource_id;
123 u64 size;
124 int err;
125
126 if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_ID) ||
127 GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_SIZE))
128 return -EINVAL;
129 resource_id = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_ID]);
130
131 resource = devlink_resource_find(devlink, NULL, resource_id);
132 if (!resource)
133 return -EINVAL;
134
135 size = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_SIZE]);
136 err = devlink_resource_validate_size(resource, size, info->extack);
137 if (err)
138 return err;
139
140 resource->size_new = size;
141 devlink_resource_validate_children(resource);
142 if (resource->parent)
143 devlink_resource_validate_children(resource->parent);
144 return 0;
145}
146
147static int
148devlink_resource_size_params_put(struct devlink_resource *resource,
149 struct sk_buff *skb)
150{
151 struct devlink_resource_size_params *size_params;
152
153 size_params = &resource->size_params;
154 if (devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_GRAN,
155 size_params->size_granularity) ||
156 devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_MAX,
157 size_params->size_max) ||
158 devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_MIN,
159 size_params->size_min) ||
160 nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_UNIT, size_params->unit))
161 return -EMSGSIZE;
162 return 0;
163}
164
165static int devlink_resource_occ_put(struct devlink_resource *resource,
166 struct sk_buff *skb)
167{
168 if (!resource->occ_get)
169 return 0;
170 return devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_OCC,
171 resource->occ_get(resource->occ_get_priv));
172}
173
174static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb,
175 struct devlink_resource *resource)
176{
177 struct devlink_resource *child_resource;
178 struct nlattr *child_resource_attr;
179 struct nlattr *resource_attr;
180
181 resource_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE);
182 if (!resource_attr)
183 return -EMSGSIZE;
184
185 if (nla_put_string(skb, DEVLINK_ATTR_RESOURCE_NAME, resource->name) ||
186 devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE, resource->size) ||
187 devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_ID, resource->id))
188 goto nla_put_failure;
189 if (resource->size != resource->size_new &&
190 devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_NEW,
191 resource->size_new))
192 goto nla_put_failure;
193 if (devlink_resource_occ_put(resource, skb))
194 goto nla_put_failure;
195 if (devlink_resource_size_params_put(resource, skb))
196 goto nla_put_failure;
197 if (list_empty(&resource->resource_list))
198 goto out;
199
200 if (nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_SIZE_VALID,
201 resource->size_valid))
202 goto nla_put_failure;
203
204 child_resource_attr = nla_nest_start_noflag(skb,
205 DEVLINK_ATTR_RESOURCE_LIST);
206 if (!child_resource_attr)
207 goto nla_put_failure;
208
209 list_for_each_entry(child_resource, &resource->resource_list, list) {
210 if (devlink_resource_put(devlink, skb, child_resource))
211 goto resource_put_failure;
212 }
213
214 nla_nest_end(skb, child_resource_attr);
215out:
216 nla_nest_end(skb, resource_attr);
217 return 0;
218
219resource_put_failure:
220 nla_nest_cancel(skb, child_resource_attr);
221nla_put_failure:
222 nla_nest_cancel(skb, resource_attr);
223 return -EMSGSIZE;
224}
225
226static int devlink_resource_list_fill(struct sk_buff *skb,
227 struct devlink *devlink,
228 struct list_head *resource_list_head,
229 int *idx)
230{
231 struct devlink_resource *resource;
232 int i = 0;
233 int err;
234
235 list_for_each_entry(resource, resource_list_head, list) {
236 if (i < *idx) {
237 i++;
238 continue;
239 }
240 err = devlink_resource_put(devlink, skb, resource);
241 if (err) {
242 *idx = i;
243 return err;
244 }
245 i++;
246 }
247 *idx = 0;
248 return 0;
249}
250
251static int devlink_resource_fill(struct genl_info *info,
252 enum devlink_command cmd, int flags)
253{
254 struct devlink_port *devlink_port = info->user_ptr[1];
255 struct devlink *devlink = info->user_ptr[0];
256 struct devlink_resource *resource;
257 struct list_head *resource_list;
258 struct nlattr *resources_attr;
259 struct sk_buff *skb = NULL;
260 struct nlmsghdr *nlh;
261 bool incomplete;
262 void *hdr;
263 int i;
264 int err;
265
266 resource_list = devlink_port ?
267 &devlink_port->resource_list : &devlink->resource_list;
268 resource = list_first_entry(resource_list,
269 struct devlink_resource, list);
270start_again:
271 err = devlink_nl_msg_reply_and_new(&skb, info);
272 if (err)
273 return err;
274
275 hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
276 &devlink_nl_family, NLM_F_MULTI, cmd);
277 if (!hdr) {
278 nlmsg_free(skb);
279 return -EMSGSIZE;
280 }
281
282 if (devlink_nl_put_handle(skb, devlink))
283 goto nla_put_failure;
284 if (devlink_port &&
285 nla_put_u32(skb, DEVLINK_ATTR_PORT_INDEX, devlink_port->index))
286 goto nla_put_failure;
287
288 resources_attr = nla_nest_start_noflag(skb,
289 DEVLINK_ATTR_RESOURCE_LIST);
290 if (!resources_attr)
291 goto nla_put_failure;
292
293 incomplete = false;
294 i = 0;
295 list_for_each_entry_from(resource, resource_list, list) {
296 err = devlink_resource_put(devlink, skb, resource);
297 if (err) {
298 if (!i)
299 goto err_resource_put;
300 incomplete = true;
301 break;
302 }
303 i++;
304 }
305 nla_nest_end(skb, resources_attr);
306 genlmsg_end(skb, hdr);
307 if (incomplete)
308 goto start_again;
309send_done:
310 nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
311 NLMSG_DONE, 0, flags | NLM_F_MULTI);
312 if (!nlh) {
313 err = devlink_nl_msg_reply_and_new(&skb, info);
314 if (err)
315 return err;
316 goto send_done;
317 }
318 return genlmsg_reply(skb, info);
319
320nla_put_failure:
321 err = -EMSGSIZE;
322err_resource_put:
323 nlmsg_free(skb);
324 return err;
325}
326
327int devlink_nl_resource_dump_doit(struct sk_buff *skb, struct genl_info *info)
328{
329 struct devlink_port *devlink_port = info->user_ptr[1];
330 struct devlink *devlink = info->user_ptr[0];
331 struct list_head *resource_list;
332
333 if (info->attrs[DEVLINK_ATTR_PORT_INDEX] && !devlink_port)
334 return -ENODEV;
335
336 resource_list = devlink_port ?
337 &devlink_port->resource_list : &devlink->resource_list;
338 if (list_empty(resource_list))
339 return -EOPNOTSUPP;
340
341 return devlink_resource_fill(info, DEVLINK_CMD_RESOURCE_DUMP, 0);
342}
343
344static int
345devlink_resource_dump_fill_one(struct sk_buff *skb, struct devlink *devlink,
346 struct devlink_port *devlink_port,
347 struct netlink_callback *cb, int flags, int *idx)
348{
349 struct list_head *resource_list;
350 struct nlattr *resources_attr;
351 int start_idx = *idx;
352 void *hdr;
353 int err;
354
355 resource_list = devlink_port ?
356 &devlink_port->resource_list : &devlink->resource_list;
357
358 if (list_empty(resource_list))
359 return 0;
360
361 err = -EMSGSIZE;
362 hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
363 &devlink_nl_family, flags, DEVLINK_CMD_RESOURCE_DUMP);
364 if (!hdr)
365 return err;
366
367 if (devlink_nl_put_handle(skb, devlink))
368 goto nla_put_failure;
369 if (devlink_port &&
370 nla_put_u32(skb, DEVLINK_ATTR_PORT_INDEX, devlink_port->index))
371 goto nla_put_failure;
372
373 resources_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE_LIST);
374 if (!resources_attr)
375 goto nla_put_failure;
376
377 err = devlink_resource_list_fill(skb, devlink, resource_list, idx);
378 if (err) {
379 if (*idx == start_idx)
380 goto resource_list_cancel;
381 nla_nest_end(skb, resources_attr);
382 genlmsg_end(skb, hdr);
383 return err;
384 }
385 nla_nest_end(skb, resources_attr);
386 genlmsg_end(skb, hdr);
387 return 0;
388
389resource_list_cancel:
390 nla_nest_cancel(skb, resources_attr);
391nla_put_failure:
392 genlmsg_cancel(skb, hdr);
393 return err;
394}
395
396static int
397devlink_nl_resource_dump_one(struct sk_buff *skb, struct devlink *devlink,
398 struct netlink_callback *cb, int flags)
399{
400 struct devlink_nl_dump_state *state = devlink_dump_state(cb);
401 const struct genl_info *info = genl_info_dump(cb);
402 struct devlink_port *devlink_port;
403 struct nlattr *scope_attr = NULL;
404 unsigned long port_idx;
405 u32 scope = 0;
406 int err;
407
408 if (info->attrs && info->attrs[DEVLINK_ATTR_RESOURCE_SCOPE_MASK]) {
409 scope_attr = info->attrs[DEVLINK_ATTR_RESOURCE_SCOPE_MASK];
410 scope = nla_get_u32(scope_attr);
411 if (!scope) {
412 NL_SET_ERR_MSG_ATTR(info->extack, scope_attr,
413 "empty resource scope selection");
414 return -EINVAL;
415 }
416 }
417
418 if (!state->port_ctx.index_valid &&
419 (!scope || (scope & DEVLINK_RESOURCE_SCOPE_DEV))) {
420 err = devlink_resource_dump_fill_one(skb, devlink, NULL,
421 cb, flags, &state->idx);
422 if (err)
423 return err;
424 state->idx = 0;
425 }
426
427 if (scope && !(scope & DEVLINK_RESOURCE_SCOPE_PORT))
428 goto out;
429 /* Check in case port was removed between dump callbacks. */
430 if (state->port_ctx.index_valid &&
431 !xa_load(&devlink->ports, state->port_ctx.index))
432 state->idx = 0;
433 state->port_ctx.index_valid = true;
434 xa_for_each_start(&devlink->ports, port_idx, devlink_port,
435 state->port_ctx.index) {
436 err = devlink_resource_dump_fill_one(skb, devlink, devlink_port,
437 cb, flags, &state->idx);
438 if (err) {
439 state->port_ctx.index = port_idx;
440 return err;
441 }
442 state->idx = 0;
443 }
444out:
445 state->port_ctx.index_valid = false;
446 state->port_ctx.index = 0;
447 return 0;
448}
449
450int devlink_nl_resource_dump_dumpit(struct sk_buff *skb,
451 struct netlink_callback *cb)
452{
453 return devlink_nl_dumpit(skb, cb, devlink_nl_resource_dump_one);
454}
455
456int devlink_resources_validate(struct devlink *devlink,
457 struct devlink_resource *resource,
458 struct genl_info *info)
459{
460 struct list_head *resource_list;
461 int err = 0;
462
463 if (resource)
464 resource_list = &resource->resource_list;
465 else
466 resource_list = &devlink->resource_list;
467
468 list_for_each_entry(resource, resource_list, list) {
469 if (!resource->size_valid)
470 return -EINVAL;
471 err = devlink_resources_validate(devlink, resource, info);
472 if (err)
473 return err;
474 }
475 return err;
476}
477
478static int
479__devl_resource_register(struct devlink *devlink,
480 struct list_head *resource_list_head,
481 const char *resource_name, u64 resource_size,
482 u64 resource_id, u64 parent_resource_id,
483 const struct devlink_resource_size_params *params)
484{
485 struct devlink_resource *resource;
486 struct list_head *resource_list;
487 bool top_hierarchy;
488
489 lockdep_assert_held(&devlink->lock);
490
491 top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
492
493 resource = __devlink_resource_find(resource_list_head, NULL,
494 resource_id);
495 if (resource)
496 return -EEXIST;
497
498 resource = kzalloc_obj(*resource);
499 if (!resource)
500 return -ENOMEM;
501
502 if (top_hierarchy) {
503 resource_list = resource_list_head;
504 } else {
505 struct devlink_resource *parent_resource;
506
507 parent_resource = __devlink_resource_find(resource_list_head,
508 NULL,
509 parent_resource_id);
510 if (parent_resource) {
511 resource_list = &parent_resource->resource_list;
512 resource->parent = parent_resource;
513 } else {
514 kfree(resource);
515 return -EINVAL;
516 }
517 }
518
519 resource->name = resource_name;
520 resource->size = resource_size;
521 resource->size_new = resource_size;
522 resource->id = resource_id;
523 resource->size_valid = true;
524 memcpy(&resource->size_params, params, sizeof(resource->size_params));
525 INIT_LIST_HEAD(&resource->resource_list);
526 list_add_tail(&resource->list, resource_list);
527
528 return 0;
529}
530
531/**
532 * devl_resource_register - devlink resource register
533 *
534 * @devlink: devlink
535 * @resource_name: resource's name
536 * @resource_size: resource's size
537 * @resource_id: resource's id
538 * @parent_resource_id: resource's parent id
539 * @params: size parameters
540 *
541 * Generic resources should reuse the same names across drivers.
542 * Please see the generic resources list at:
543 * Documentation/networking/devlink/devlink-resource.rst
544 *
545 * Return: 0 on success, negative error code otherwise.
546 */
547int devl_resource_register(struct devlink *devlink, const char *resource_name,
548 u64 resource_size, u64 resource_id,
549 u64 parent_resource_id,
550 const struct devlink_resource_size_params *params)
551{
552 return __devl_resource_register(devlink, &devlink->resource_list,
553 resource_name, resource_size,
554 resource_id, parent_resource_id,
555 params);
556}
557EXPORT_SYMBOL_GPL(devl_resource_register);
558
559static void devlink_resource_unregister(struct devlink_resource *resource)
560{
561 struct devlink_resource *tmp, *child_resource;
562
563 list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
564 list) {
565 devlink_resource_unregister(child_resource);
566 list_del(&child_resource->list);
567 kfree(child_resource);
568 }
569}
570
571static void
572__devl_resources_unregister(struct devlink *devlink,
573 struct list_head *resource_list_head)
574{
575 struct devlink_resource *tmp, *child_resource;
576
577 lockdep_assert_held(&devlink->lock);
578
579 list_for_each_entry_safe(child_resource, tmp, resource_list_head,
580 list) {
581 devlink_resource_unregister(child_resource);
582 list_del(&child_resource->list);
583 kfree(child_resource);
584 }
585}
586
587/**
588 * devl_resources_unregister - free all resources
589 *
590 * @devlink: devlink
591 */
592void devl_resources_unregister(struct devlink *devlink)
593{
594 __devl_resources_unregister(devlink, &devlink->resource_list);
595}
596EXPORT_SYMBOL_GPL(devl_resources_unregister);
597
598/**
599 * devlink_resources_unregister - free all resources
600 *
601 * @devlink: devlink
602 *
603 * Context: Takes and release devlink->lock <mutex>.
604 */
605void devlink_resources_unregister(struct devlink *devlink)
606{
607 devl_lock(devlink);
608 devl_resources_unregister(devlink);
609 devl_unlock(devlink);
610}
611EXPORT_SYMBOL_GPL(devlink_resources_unregister);
612
613/**
614 * devl_resource_size_get - get and update size
615 *
616 * @devlink: devlink
617 * @resource_id: the requested resource id
618 * @p_resource_size: ptr to update
619 */
620int devl_resource_size_get(struct devlink *devlink,
621 u64 resource_id,
622 u64 *p_resource_size)
623{
624 struct devlink_resource *resource;
625
626 lockdep_assert_held(&devlink->lock);
627
628 resource = devlink_resource_find(devlink, NULL, resource_id);
629 if (!resource)
630 return -EINVAL;
631 *p_resource_size = resource->size_new;
632 resource->size = resource->size_new;
633 return 0;
634}
635EXPORT_SYMBOL_GPL(devl_resource_size_get);
636
637/**
638 * devl_resource_occ_get_register - register occupancy getter
639 *
640 * @devlink: devlink
641 * @resource_id: resource id
642 * @occ_get: occupancy getter callback
643 * @occ_get_priv: occupancy getter callback priv
644 */
645void devl_resource_occ_get_register(struct devlink *devlink,
646 u64 resource_id,
647 devlink_resource_occ_get_t *occ_get,
648 void *occ_get_priv)
649{
650 struct devlink_resource *resource;
651
652 lockdep_assert_held(&devlink->lock);
653
654 resource = devlink_resource_find(devlink, NULL, resource_id);
655 if (WARN_ON(!resource))
656 return;
657 WARN_ON(resource->occ_get);
658
659 resource->occ_get = occ_get;
660 resource->occ_get_priv = occ_get_priv;
661}
662EXPORT_SYMBOL_GPL(devl_resource_occ_get_register);
663
664/**
665 * devl_resource_occ_get_unregister - unregister occupancy getter
666 *
667 * @devlink: devlink
668 * @resource_id: resource id
669 */
670void devl_resource_occ_get_unregister(struct devlink *devlink,
671 u64 resource_id)
672{
673 struct devlink_resource *resource;
674
675 lockdep_assert_held(&devlink->lock);
676
677 resource = devlink_resource_find(devlink, NULL, resource_id);
678 if (WARN_ON(!resource))
679 return;
680 WARN_ON(!resource->occ_get);
681
682 resource->occ_get = NULL;
683 resource->occ_get_priv = NULL;
684}
685EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);
686
687/**
688 * devl_port_resource_register - devlink port resource register
689 *
690 * @devlink_port: devlink port
691 * @resource_name: resource's name
692 * @resource_size: resource's size
693 * @resource_id: resource's id
694 * @parent_resource_id: resource's parent id
695 * @params: size parameters
696 *
697 * Generic resources should reuse the same names across drivers.
698 * Please see the generic resources list at:
699 * Documentation/networking/devlink/devlink-resource.rst
700 *
701 * Return: 0 on success, negative error code otherwise.
702 */
703int
704devl_port_resource_register(struct devlink_port *devlink_port,
705 const char *resource_name,
706 u64 resource_size, u64 resource_id,
707 u64 parent_resource_id,
708 const struct devlink_resource_size_params *params)
709{
710 return __devl_resource_register(devlink_port->devlink,
711 &devlink_port->resource_list,
712 resource_name, resource_size,
713 resource_id, parent_resource_id,
714 params);
715}
716EXPORT_SYMBOL_GPL(devl_port_resource_register);
717
718/**
719 * devl_port_resources_unregister - unregister all devlink port resources
720 *
721 * @devlink_port: devlink port
722 */
723void devl_port_resources_unregister(struct devlink_port *devlink_port)
724{
725 __devl_resources_unregister(devlink_port->devlink,
726 &devlink_port->resource_list);
727}
728EXPORT_SYMBOL_GPL(devl_port_resources_unregister);