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 * 39devlink_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 63static void 64devlink_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; 78out: 79 resource->size_valid = size_valid; 80} 81 82static int 83devlink_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 108int devlink_nl_resource_set_doit(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 137static int 138devlink_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 155static 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 165static 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); 208out: 209 nla_nest_end(skb, resource_attr); 210 return 0; 211 212resource_put_failure: 213 nla_nest_cancel(skb, child_resource_attr); 214nla_put_failure: 215 nla_nest_cancel(skb, resource_attr); 216 return -EMSGSIZE; 217} 218 219static 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); 234start_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; 270send_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 281nla_put_failure: 282 err = -EMSGSIZE; 283err_resource_put: 284 nlmsg_free(skb); 285 return err; 286} 287 288int devlink_nl_resource_dump_doit(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 298int 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 */ 334int 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} 385EXPORT_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 */ 403int 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} 418EXPORT_SYMBOL_GPL(devlink_resource_register); 419 420static 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 */ 438void 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} 451EXPORT_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 */ 460void devlink_resources_unregister(struct devlink *devlink) 461{ 462 devl_lock(devlink); 463 devl_resources_unregister(devlink); 464 devl_unlock(devlink); 465} 466EXPORT_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 */ 475int 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} 490EXPORT_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 */ 500void 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} 517EXPORT_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 */ 529void 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} 539EXPORT_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 */ 547void 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} 562EXPORT_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 */ 572void 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} 579EXPORT_SYMBOL_GPL(devlink_resource_occ_get_unregister); 580