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 = 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 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_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); 270 start_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; 309 send_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 320 nla_put_failure: 321 err = -EMSGSIZE; 322 err_resource_put: 323 nlmsg_free(skb); 324 return err; 325 } 326 327 int 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 344 static int 345 devlink_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 389 resource_list_cancel: 390 nla_nest_cancel(skb, resources_attr); 391 nla_put_failure: 392 genlmsg_cancel(skb, hdr); 393 return err; 394 } 395 396 static int 397 devlink_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 } 444 out: 445 state->port_ctx.index_valid = false; 446 state->port_ctx.index = 0; 447 return 0; 448 } 449 450 int 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 456 int 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 478 static 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 */ 547 int 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 } 557 EXPORT_SYMBOL_GPL(devl_resource_register); 558 559 static 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 571 static 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 */ 592 void devl_resources_unregister(struct devlink *devlink) 593 { 594 __devl_resources_unregister(devlink, &devlink->resource_list); 595 } 596 EXPORT_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 */ 605 void devlink_resources_unregister(struct devlink *devlink) 606 { 607 devl_lock(devlink); 608 devl_resources_unregister(devlink); 609 devl_unlock(devlink); 610 } 611 EXPORT_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 */ 620 int 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 } 635 EXPORT_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 */ 645 void 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 } 662 EXPORT_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 */ 670 void 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 } 685 EXPORT_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 */ 703 int 704 devl_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 } 716 EXPORT_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 */ 723 void devl_port_resources_unregister(struct devlink_port *devlink_port) 724 { 725 __devl_resources_unregister(devlink_port->devlink, 726 &devlink_port->resource_list); 727 } 728 EXPORT_SYMBOL_GPL(devl_port_resources_unregister); 729