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 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 63 static void 64 devlink_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(&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; 78 out: 79 resource->size_valid = size_valid; 80 } 81 82 static int 83 devlink_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(size, resource->size_params.size_granularity, &reminder); 100 if (reminder) { 101 NL_SET_ERR_MSG(extack, "Wrong granularity"); 102 err = -EINVAL; 103 } 104 105 return err; 106 } 107 108 int devlink_nl_cmd_resource_set(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(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(info->attrs[DEVLINK_ATTR_RESOURCE_SIZE]); 126 err = devlink_resource_validate_size(resource, size, 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->parent); 134 return 0; 135 } 136 137 static int 138 devlink_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, DEVLINK_ATTR_RESOURCE_SIZE_GRAN, 145 size_params->size_granularity, DEVLINK_ATTR_PAD) || 146 nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_MAX, 147 size_params->size_max, DEVLINK_ATTR_PAD) || 148 nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_MIN, 149 size_params->size_min, DEVLINK_ATTR_PAD) || 150 nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_UNIT, size_params->unit)) 151 return -EMSGSIZE; 152 return 0; 153 } 154 155 static 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, DEVLINK_ATTR_RESOURCE_OCC, 161 resource->occ_get(resource->occ_get_priv), 162 DEVLINK_ATTR_PAD); 163 } 164 165 static 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, DEVLINK_ATTR_RESOURCE); 173 if (!resource_attr) 174 return -EMSGSIZE; 175 176 if (nla_put_string(skb, DEVLINK_ATTR_RESOURCE_NAME, resource->name) || 177 nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE, resource->size, 178 DEVLINK_ATTR_PAD) || 179 nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_ID, resource->id, 180 DEVLINK_ATTR_PAD)) 181 goto nla_put_failure; 182 if (resource->size != resource->size_new && 183 nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_NEW, 184 resource->size_new, 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(&resource->resource_list)) 191 goto out; 192 193 if (nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_SIZE_VALID, 194 resource->size_valid)) 195 goto nla_put_failure; 196 197 child_resource_attr = nla_nest_start_noflag(skb, 198 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, child_resource)) 204 goto resource_put_failure; 205 } 206 207 nla_nest_end(skb, child_resource_attr); 208 out: 209 nla_nest_end(skb, resource_attr); 210 return 0; 211 212 resource_put_failure: 213 nla_nest_cancel(skb, child_resource_attr); 214 nla_put_failure: 215 nla_nest_cancel(skb, resource_attr); 216 return -EMSGSIZE; 217 } 218 219 static 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); 234 start_again: 235 err = devlink_nl_msg_reply_and_new(&skb, info); 236 if (err) 237 return err; 238 239 hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq, 240 &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(skb, devlink)) 247 goto nla_put_failure; 248 249 resources_attr = nla_nest_start_noflag(skb, 250 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, resources_attr); 267 genlmsg_end(skb, hdr); 268 if (incomplete) 269 goto start_again; 270 send_done: 271 nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq, 272 NLMSG_DONE, 0, flags | NLM_F_MULTI); 273 if (!nlh) { 274 err = devlink_nl_msg_reply_and_new(&skb, info); 275 if (err) 276 return err; 277 goto send_done; 278 } 279 return genlmsg_reply(skb, info); 280 281 nla_put_failure: 282 err = -EMSGSIZE; 283 err_resource_put: 284 nlmsg_free(skb); 285 return err; 286 } 287 288 int devlink_nl_cmd_resource_dump(struct sk_buff *skb, struct genl_info *info) 289 { 290 struct devlink *devlink = info->user_ptr[0]; 291 292 if (list_empty(&devlink->resource_list)) 293 return -EOPNOTSUPP; 294 295 return devlink_resource_fill(info, DEVLINK_CMD_RESOURCE_DUMP, 0); 296 } 297 298 int 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 */ 334 int 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(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 parent_resource_id); 364 if (parent_resource) { 365 resource_list = &parent_resource->resource_list; 366 resource->parent = parent_resource; 367 } else { 368 kfree(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(&resource->resource_list); 381 list_add_tail(&resource->list, resource_list); 382 383 return 0; 384 } 385 EXPORT_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 */ 403 int 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 } 418 EXPORT_SYMBOL_GPL(devlink_resource_register); 419 420 static 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, child_resource); 428 list_del(&child_resource->list); 429 kfree(child_resource); 430 } 431 } 432 433 /** 434 * devl_resources_unregister - free all resources 435 * 436 * @devlink: devlink 437 */ 438 void 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, child_resource); 447 list_del(&child_resource->list); 448 kfree(child_resource); 449 } 450 } 451 EXPORT_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 */ 460 void devlink_resources_unregister(struct devlink *devlink) 461 { 462 devl_lock(devlink); 463 devl_resources_unregister(devlink); 464 devl_unlock(devlink); 465 } 466 EXPORT_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 */ 475 int 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 } 490 EXPORT_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 */ 500 void 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 } 517 EXPORT_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 */ 529 void 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 } 539 EXPORT_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 */ 547 void 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 } 562 EXPORT_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 */ 572 void 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 } 579 EXPORT_SYMBOL_GPL(devlink_resource_occ_get_unregister); 580