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 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 65 static struct devlink_resource * 66 devlink_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 73 static void 74 devlink_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; 88 out: 89 resource->size_valid = size_valid; 90 } 91 92 static int 93 devlink_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 118 int devlink_nl_resource_set_doit(struct sk_buff *skb, struct genl_info *info) 119 { 120 struct devlink *devlink = devlink_nl_ctx(info)->devlink; 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 147 static int 148 devlink_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 165 static 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 174 static 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); 215 out: 216 nla_nest_end(skb, resource_attr); 217 return 0; 218 219 resource_put_failure: 220 nla_nest_cancel(skb, child_resource_attr); 221 nla_put_failure: 222 nla_nest_cancel(skb, resource_attr); 223 return -EMSGSIZE; 224 } 225 226 static 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 251 static int devlink_resource_fill(struct genl_info *info, 252 enum devlink_command cmd, int flags) 253 { 254 struct devlink_nl_ctx *ctx = devlink_nl_ctx(info); 255 struct devlink *devlink = ctx->devlink; 256 struct devlink_port *devlink_port; 257 struct devlink_resource *resource; 258 struct list_head *resource_list; 259 struct nlattr *resources_attr; 260 struct sk_buff *skb = NULL; 261 struct nlmsghdr *nlh; 262 bool incomplete; 263 void *hdr; 264 int i; 265 int err; 266 267 devlink_port = ctx->devlink_port; 268 resource_list = devlink_port ? 269 &devlink_port->resource_list : &devlink->resource_list; 270 resource = list_first_entry(resource_list, 271 struct devlink_resource, list); 272 start_again: 273 err = devlink_nl_msg_reply_and_new(&skb, info); 274 if (err) 275 return err; 276 277 hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq, 278 &devlink_nl_family, NLM_F_MULTI, cmd); 279 if (!hdr) { 280 nlmsg_free(skb); 281 return -EMSGSIZE; 282 } 283 284 if (devlink_nl_put_handle(skb, devlink)) 285 goto nla_put_failure; 286 if (devlink_port && 287 nla_put_u32(skb, DEVLINK_ATTR_PORT_INDEX, devlink_port->index)) 288 goto nla_put_failure; 289 290 resources_attr = nla_nest_start_noflag(skb, 291 DEVLINK_ATTR_RESOURCE_LIST); 292 if (!resources_attr) 293 goto nla_put_failure; 294 295 incomplete = false; 296 i = 0; 297 list_for_each_entry_from(resource, resource_list, list) { 298 err = devlink_resource_put(devlink, skb, resource); 299 if (err) { 300 if (!i) 301 goto err_resource_put; 302 incomplete = true; 303 break; 304 } 305 i++; 306 } 307 nla_nest_end(skb, resources_attr); 308 genlmsg_end(skb, hdr); 309 if (incomplete) 310 goto start_again; 311 send_done: 312 nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq, 313 NLMSG_DONE, 0, flags | NLM_F_MULTI); 314 if (!nlh) { 315 err = devlink_nl_msg_reply_and_new(&skb, info); 316 if (err) 317 return err; 318 goto send_done; 319 } 320 return genlmsg_reply(skb, info); 321 322 nla_put_failure: 323 err = -EMSGSIZE; 324 err_resource_put: 325 nlmsg_free(skb); 326 return err; 327 } 328 329 int devlink_nl_resource_dump_doit(struct sk_buff *skb, struct genl_info *info) 330 { 331 struct devlink_nl_ctx *ctx = devlink_nl_ctx(info); 332 struct devlink *devlink = ctx->devlink; 333 struct devlink_port *devlink_port; 334 struct list_head *resource_list; 335 336 devlink_port = ctx->devlink_port; 337 if (info->attrs[DEVLINK_ATTR_PORT_INDEX] && !devlink_port) 338 return -ENODEV; 339 340 resource_list = devlink_port ? 341 &devlink_port->resource_list : &devlink->resource_list; 342 if (list_empty(resource_list)) 343 return -EOPNOTSUPP; 344 345 return devlink_resource_fill(info, DEVLINK_CMD_RESOURCE_DUMP, 0); 346 } 347 348 static int 349 devlink_resource_dump_fill_one(struct sk_buff *skb, struct devlink *devlink, 350 struct devlink_port *devlink_port, 351 struct netlink_callback *cb, int flags, int *idx) 352 { 353 struct list_head *resource_list; 354 struct nlattr *resources_attr; 355 int start_idx = *idx; 356 void *hdr; 357 int err; 358 359 resource_list = devlink_port ? 360 &devlink_port->resource_list : &devlink->resource_list; 361 362 if (list_empty(resource_list)) 363 return 0; 364 365 err = -EMSGSIZE; 366 hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 367 &devlink_nl_family, flags, DEVLINK_CMD_RESOURCE_DUMP); 368 if (!hdr) 369 return err; 370 371 if (devlink_nl_put_handle(skb, devlink)) 372 goto nla_put_failure; 373 if (devlink_port && 374 nla_put_u32(skb, DEVLINK_ATTR_PORT_INDEX, devlink_port->index)) 375 goto nla_put_failure; 376 377 resources_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE_LIST); 378 if (!resources_attr) 379 goto nla_put_failure; 380 381 err = devlink_resource_list_fill(skb, devlink, resource_list, idx); 382 if (err) { 383 if (*idx == start_idx) 384 goto resource_list_cancel; 385 nla_nest_end(skb, resources_attr); 386 genlmsg_end(skb, hdr); 387 return err; 388 } 389 nla_nest_end(skb, resources_attr); 390 genlmsg_end(skb, hdr); 391 return 0; 392 393 resource_list_cancel: 394 nla_nest_cancel(skb, resources_attr); 395 nla_put_failure: 396 genlmsg_cancel(skb, hdr); 397 return err; 398 } 399 400 static int 401 devlink_nl_resource_dump_one(struct sk_buff *skb, struct devlink *devlink, 402 struct netlink_callback *cb, int flags) 403 { 404 struct devlink_nl_dump_state *state = devlink_dump_state(cb); 405 const struct genl_info *info = genl_info_dump(cb); 406 struct devlink_port *devlink_port; 407 struct nlattr *scope_attr = NULL; 408 unsigned long port_idx; 409 u32 scope = 0; 410 int err; 411 412 if (info->attrs && info->attrs[DEVLINK_ATTR_RESOURCE_SCOPE_MASK]) { 413 scope_attr = info->attrs[DEVLINK_ATTR_RESOURCE_SCOPE_MASK]; 414 scope = nla_get_u32(scope_attr); 415 if (!scope) { 416 NL_SET_ERR_MSG_ATTR(info->extack, scope_attr, 417 "empty resource scope selection"); 418 return -EINVAL; 419 } 420 } 421 422 if (!state->port_ctx.index_valid && 423 (!scope || (scope & DEVLINK_RESOURCE_SCOPE_DEV))) { 424 err = devlink_resource_dump_fill_one(skb, devlink, NULL, 425 cb, flags, &state->idx); 426 if (err) 427 return err; 428 state->idx = 0; 429 } 430 431 if (scope && !(scope & DEVLINK_RESOURCE_SCOPE_PORT)) 432 goto out; 433 /* Check in case port was removed between dump callbacks. */ 434 if (state->port_ctx.index_valid && 435 !xa_load(&devlink->ports, state->port_ctx.index)) 436 state->idx = 0; 437 state->port_ctx.index_valid = true; 438 xa_for_each_start(&devlink->ports, port_idx, devlink_port, 439 state->port_ctx.index) { 440 err = devlink_resource_dump_fill_one(skb, devlink, devlink_port, 441 cb, flags, &state->idx); 442 if (err) { 443 state->port_ctx.index = port_idx; 444 return err; 445 } 446 state->idx = 0; 447 } 448 out: 449 state->port_ctx.index_valid = false; 450 state->port_ctx.index = 0; 451 return 0; 452 } 453 454 int devlink_nl_resource_dump_dumpit(struct sk_buff *skb, 455 struct netlink_callback *cb) 456 { 457 return devlink_nl_dumpit(skb, cb, devlink_nl_resource_dump_one); 458 } 459 460 int devlink_resources_validate(struct devlink *devlink, 461 struct devlink_resource *resource, 462 struct genl_info *info) 463 { 464 struct list_head *resource_list; 465 int err = 0; 466 467 if (resource) 468 resource_list = &resource->resource_list; 469 else 470 resource_list = &devlink->resource_list; 471 472 list_for_each_entry(resource, resource_list, list) { 473 if (!resource->size_valid) 474 return -EINVAL; 475 err = devlink_resources_validate(devlink, resource, info); 476 if (err) 477 return err; 478 } 479 return err; 480 } 481 482 static int 483 __devl_resource_register(struct devlink *devlink, 484 struct list_head *resource_list_head, 485 const char *resource_name, u64 resource_size, 486 u64 resource_id, u64 parent_resource_id, 487 const struct devlink_resource_size_params *params) 488 { 489 struct devlink_resource *resource; 490 struct list_head *resource_list; 491 bool top_hierarchy; 492 493 WARN_ON(resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP); 494 495 lockdep_assert_held(&devlink->lock); 496 497 top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP; 498 499 resource = __devlink_resource_find(resource_list_head, NULL, 500 resource_id); 501 if (resource) 502 return -EEXIST; 503 504 resource = kzalloc_obj(*resource); 505 if (!resource) 506 return -ENOMEM; 507 508 if (top_hierarchy) { 509 resource_list = resource_list_head; 510 } else { 511 struct devlink_resource *parent_resource; 512 513 parent_resource = __devlink_resource_find(resource_list_head, 514 NULL, 515 parent_resource_id); 516 if (parent_resource) { 517 resource_list = &parent_resource->resource_list; 518 resource->parent = parent_resource; 519 } else { 520 kfree(resource); 521 return -EINVAL; 522 } 523 } 524 525 resource->name = resource_name; 526 resource->size = resource_size; 527 resource->size_new = resource_size; 528 resource->id = resource_id; 529 resource->size_valid = true; 530 memcpy(&resource->size_params, params, sizeof(resource->size_params)); 531 INIT_LIST_HEAD(&resource->resource_list); 532 list_add_tail(&resource->list, resource_list); 533 534 return 0; 535 } 536 537 /** 538 * devl_resource_register - devlink resource register 539 * 540 * @devlink: devlink 541 * @resource_name: resource's name 542 * @resource_size: resource's size 543 * @resource_id: resource's id 544 * @parent_resource_id: resource's parent id 545 * @params: size parameters 546 * 547 * Generic resources should reuse the same names across drivers. 548 * Please see the generic resources list at: 549 * Documentation/networking/devlink/devlink-resource.rst 550 * 551 * Return: 0 on success, negative error code otherwise. 552 */ 553 int devl_resource_register(struct devlink *devlink, const char *resource_name, 554 u64 resource_size, u64 resource_id, 555 u64 parent_resource_id, 556 const struct devlink_resource_size_params *params) 557 { 558 return __devl_resource_register(devlink, &devlink->resource_list, 559 resource_name, resource_size, 560 resource_id, parent_resource_id, 561 params); 562 } 563 EXPORT_SYMBOL_GPL(devl_resource_register); 564 565 static void devlink_resource_unregister(struct devlink_resource *resource) 566 { 567 struct devlink_resource *tmp, *child_resource; 568 569 list_for_each_entry_safe(child_resource, tmp, &resource->resource_list, 570 list) { 571 devlink_resource_unregister(child_resource); 572 list_del(&child_resource->list); 573 kfree(child_resource); 574 } 575 } 576 577 static void 578 __devl_resources_unregister(struct devlink *devlink, 579 struct list_head *resource_list_head) 580 { 581 struct devlink_resource *tmp, *child_resource; 582 583 lockdep_assert_held(&devlink->lock); 584 585 list_for_each_entry_safe(child_resource, tmp, resource_list_head, 586 list) { 587 devlink_resource_unregister(child_resource); 588 list_del(&child_resource->list); 589 kfree(child_resource); 590 } 591 } 592 593 /** 594 * devl_resources_unregister - free all resources 595 * 596 * @devlink: devlink 597 */ 598 void devl_resources_unregister(struct devlink *devlink) 599 { 600 __devl_resources_unregister(devlink, &devlink->resource_list); 601 } 602 EXPORT_SYMBOL_GPL(devl_resources_unregister); 603 604 /** 605 * devlink_resources_unregister - free all resources 606 * 607 * @devlink: devlink 608 * 609 * Context: Takes and release devlink->lock <mutex>. 610 */ 611 void devlink_resources_unregister(struct devlink *devlink) 612 { 613 devl_lock(devlink); 614 devl_resources_unregister(devlink); 615 devl_unlock(devlink); 616 } 617 EXPORT_SYMBOL_GPL(devlink_resources_unregister); 618 619 /** 620 * devl_resource_size_get - get and update size 621 * 622 * @devlink: devlink 623 * @resource_id: the requested resource id 624 * @p_resource_size: ptr to update 625 */ 626 int devl_resource_size_get(struct devlink *devlink, 627 u64 resource_id, 628 u64 *p_resource_size) 629 { 630 struct devlink_resource *resource; 631 632 lockdep_assert_held(&devlink->lock); 633 634 resource = devlink_resource_find(devlink, NULL, resource_id); 635 if (!resource) 636 return -EINVAL; 637 *p_resource_size = resource->size_new; 638 resource->size = resource->size_new; 639 return 0; 640 } 641 EXPORT_SYMBOL_GPL(devl_resource_size_get); 642 643 /** 644 * devl_resource_occ_get_register - register occupancy getter 645 * 646 * @devlink: devlink 647 * @resource_id: resource id 648 * @occ_get: occupancy getter callback 649 * @occ_get_priv: occupancy getter callback priv 650 */ 651 void devl_resource_occ_get_register(struct devlink *devlink, 652 u64 resource_id, 653 devlink_resource_occ_get_t *occ_get, 654 void *occ_get_priv) 655 { 656 struct devlink_resource *resource; 657 658 lockdep_assert_held(&devlink->lock); 659 660 resource = devlink_resource_find(devlink, NULL, resource_id); 661 if (WARN_ON(!resource)) 662 return; 663 WARN_ON(resource->occ_get); 664 665 resource->occ_get = occ_get; 666 resource->occ_get_priv = occ_get_priv; 667 } 668 EXPORT_SYMBOL_GPL(devl_resource_occ_get_register); 669 670 /** 671 * devl_resource_occ_get_unregister - unregister occupancy getter 672 * 673 * @devlink: devlink 674 * @resource_id: resource id 675 */ 676 void devl_resource_occ_get_unregister(struct devlink *devlink, 677 u64 resource_id) 678 { 679 struct devlink_resource *resource; 680 681 lockdep_assert_held(&devlink->lock); 682 683 resource = devlink_resource_find(devlink, NULL, resource_id); 684 if (WARN_ON(!resource)) 685 return; 686 WARN_ON(!resource->occ_get); 687 688 resource->occ_get = NULL; 689 resource->occ_get_priv = NULL; 690 } 691 EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister); 692 693 /** 694 * devl_port_resource_register - devlink port resource register 695 * 696 * @devlink_port: devlink port 697 * @resource_name: resource's name 698 * @resource_size: resource's size 699 * @resource_id: resource's id 700 * @parent_resource_id: resource's parent id 701 * @params: size parameters 702 * 703 * Generic resources should reuse the same names across drivers. 704 * Please see the generic resources list at: 705 * Documentation/networking/devlink/devlink-resource.rst 706 * 707 * Return: 0 on success, negative error code otherwise. 708 */ 709 int 710 devl_port_resource_register(struct devlink_port *devlink_port, 711 const char *resource_name, 712 u64 resource_size, u64 resource_id, 713 u64 parent_resource_id, 714 const struct devlink_resource_size_params *params) 715 { 716 return __devl_resource_register(devlink_port->devlink, 717 &devlink_port->resource_list, 718 resource_name, resource_size, 719 resource_id, parent_resource_id, 720 params); 721 } 722 EXPORT_SYMBOL_GPL(devl_port_resource_register); 723 724 /** 725 * devl_port_resources_unregister - unregister all devlink port resources 726 * 727 * @devlink_port: devlink port 728 */ 729 void devl_port_resources_unregister(struct devlink_port *devlink_port) 730 { 731 __devl_resources_unregister(devlink_port->devlink, 732 &devlink_port->resource_list); 733 } 734 EXPORT_SYMBOL_GPL(devl_port_resources_unregister); 735