projects/linux·blob v7.2 · refs

730 lines · 18.8 KiB

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 <[email protected]>
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 WARN_ON(resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP);
490
491 lockdep_assert_held(&devlink->lock);
492
493 top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
494
495 resource = __devlink_resource_find(resource_list_head, NULL,
496 resource_id);
497 if (resource)
498 return -EEXIST;
499
500 resource = kzalloc_obj(*resource);
501 if (!resource)
502 return -ENOMEM;
503
504 if (top_hierarchy) {
505 resource_list = resource_list_head;
506 } else {
507 struct devlink_resource *parent_resource;
508
509 parent_resource = __devlink_resource_find(resource_list_head,
510 NULL,
511 parent_resource_id);
512 if (parent_resource) {
513 resource_list = &parent_resource->resource_list;
514 resource->parent = parent_resource;
515 } else {
516 kfree(resource);
517 return -EINVAL;
518 }
519 }
520
521 resource->name = resource_name;
522 resource->size = resource_size;
523 resource->size_new = resource_size;
524 resource->id = resource_id;
525 resource->size_valid = true;
526 memcpy(&resource->size_params, params, sizeof(resource->size_params));
527 INIT_LIST_HEAD(&resource->resource_list);
528 list_add_tail(&resource->list, resource_list);
529
530 return 0;
531}
532
533/**
534 * devl_resource_register - devlink resource register
535 *
536 * @devlink: devlink
537 * @resource_name: resource's name
538 * @resource_size: resource's size
539 * @resource_id: resource's id
540 * @parent_resource_id: resource's parent id
541 * @params: size parameters
542 *
543 * Generic resources should reuse the same names across drivers.
544 * Please see the generic resources list at:
545 * Documentation/networking/devlink/devlink-resource.rst
546 *
547 * Return: 0 on success, negative error code otherwise.
548 */
549int devl_resource_register(struct devlink *devlink, const char *resource_name,
550 u64 resource_size, u64 resource_id,
551 u64 parent_resource_id,
552 const struct devlink_resource_size_params *params)
553{
554 return __devl_resource_register(devlink, &devlink->resource_list,
555 resource_name, resource_size,
556 resource_id, parent_resource_id,
557 params);
558}
559EXPORT_SYMBOL_GPL(devl_resource_register);
560
561static void devlink_resource_unregister(struct devlink_resource *resource)
562{
563 struct devlink_resource *tmp, *child_resource;
564
565 list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
566 list) {
567 devlink_resource_unregister(child_resource);
568 list_del(&child_resource->list);
569 kfree(child_resource);
570 }
571}
572
573static void
574__devl_resources_unregister(struct devlink *devlink,
575 struct list_head *resource_list_head)
576{
577 struct devlink_resource *tmp, *child_resource;
578
579 lockdep_assert_held(&devlink->lock);
580
581 list_for_each_entry_safe(child_resource, tmp, resource_list_head,
582 list) {
583 devlink_resource_unregister(child_resource);
584 list_del(&child_resource->list);
585 kfree(child_resource);
586 }
587}
588
589/**
590 * devl_resources_unregister - free all resources
591 *
592 * @devlink: devlink
593 */
594void devl_resources_unregister(struct devlink *devlink)
595{
596 __devl_resources_unregister(devlink, &devlink->resource_list);
597}
598EXPORT_SYMBOL_GPL(devl_resources_unregister);
599
600/**
601 * devlink_resources_unregister - free all resources
602 *
603 * @devlink: devlink
604 *
605 * Context: Takes and release devlink->lock <mutex>.
606 */
607void devlink_resources_unregister(struct devlink *devlink)
608{
609 devl_lock(devlink);
610 devl_resources_unregister(devlink);
611 devl_unlock(devlink);
612}
613EXPORT_SYMBOL_GPL(devlink_resources_unregister);
614
615/**
616 * devl_resource_size_get - get and update size
617 *
618 * @devlink: devlink
619 * @resource_id: the requested resource id
620 * @p_resource_size: ptr to update
621 */
622int devl_resource_size_get(struct devlink *devlink,
623 u64 resource_id,
624 u64 *p_resource_size)
625{
626 struct devlink_resource *resource;
627
628 lockdep_assert_held(&devlink->lock);
629
630 resource = devlink_resource_find(devlink, NULL, resource_id);
631 if (!resource)
632 return -EINVAL;
633 *p_resource_size = resource->size_new;
634 resource->size = resource->size_new;
635 return 0;
636}
637EXPORT_SYMBOL_GPL(devl_resource_size_get);
638
639/**
640 * devl_resource_occ_get_register - register occupancy getter
641 *
642 * @devlink: devlink
643 * @resource_id: resource id
644 * @occ_get: occupancy getter callback
645 * @occ_get_priv: occupancy getter callback priv
646 */
647void devl_resource_occ_get_register(struct devlink *devlink,
648 u64 resource_id,
649 devlink_resource_occ_get_t *occ_get,
650 void *occ_get_priv)
651{
652 struct devlink_resource *resource;
653
654 lockdep_assert_held(&devlink->lock);
655
656 resource = devlink_resource_find(devlink, NULL, resource_id);
657 if (WARN_ON(!resource))
658 return;
659 WARN_ON(resource->occ_get);
660
661 resource->occ_get = occ_get;
662 resource->occ_get_priv = occ_get_priv;
663}
664EXPORT_SYMBOL_GPL(devl_resource_occ_get_register);
665
666/**
667 * devl_resource_occ_get_unregister - unregister occupancy getter
668 *
669 * @devlink: devlink
670 * @resource_id: resource id
671 */
672void devl_resource_occ_get_unregister(struct devlink *devlink,
673 u64 resource_id)
674{
675 struct devlink_resource *resource;
676
677 lockdep_assert_held(&devlink->lock);
678
679 resource = devlink_resource_find(devlink, NULL, resource_id);
680 if (WARN_ON(!resource))
681 return;
682 WARN_ON(!resource->occ_get);
683
684 resource->occ_get = NULL;
685 resource->occ_get_priv = NULL;
686}
687EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);
688
689/**
690 * devl_port_resource_register - devlink port resource register
691 *
692 * @devlink_port: devlink port
693 * @resource_name: resource's name
694 * @resource_size: resource's size
695 * @resource_id: resource's id
696 * @parent_resource_id: resource's parent id
697 * @params: size parameters
698 *
699 * Generic resources should reuse the same names across drivers.
700 * Please see the generic resources list at:
701 * Documentation/networking/devlink/devlink-resource.rst
702 *
703 * Return: 0 on success, negative error code otherwise.
704 */
705int
706devl_port_resource_register(struct devlink_port *devlink_port,
707 const char *resource_name,
708 u64 resource_size, u64 resource_id,
709 u64 parent_resource_id,
710 const struct devlink_resource_size_params *params)
711{
712 return __devl_resource_register(devlink_port->devlink,
713 &devlink_port->resource_list,
714 resource_name, resource_size,
715 resource_id, parent_resource_id,
716 params);
717}
718EXPORT_SYMBOL_GPL(devl_port_resource_register);
719
720/**
721 * devl_port_resources_unregister - unregister all devlink port resources
722 *
723 * @devlink_port: devlink port
724 */
725void devl_port_resources_unregister(struct devlink_port *devlink_port)
726{
727 __devl_resources_unregister(devlink_port->devlink,
728 &devlink_port->resource_list);
729}
730EXPORT_SYMBOL_GPL(devl_port_resources_unregister);