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_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 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 (devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_GRAN, 145 size_params->size_granularity) || 146 devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_MAX, 147 size_params->size_max) || 148 devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_MIN, 149 size_params->size_min) || 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 devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_OCC, 161 resource->occ_get(resource->occ_get_priv)); 162 } 163 164 static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb, 165 struct devlink_resource *resource) 166 { 167 struct devlink_resource *child_resource; 168 struct nlattr *child_resource_attr; 169 struct nlattr *resource_attr; 170 171 resource_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE); 172 if (!resource_attr) 173 return -EMSGSIZE; 174 175 if (nla_put_string(skb, DEVLINK_ATTR_RESOURCE_NAME, resource->name) || 176 devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE, resource->size) || 177 devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_ID, resource->id)) 178 goto nla_put_failure; 179 if (resource->size != resource->size_new && 180 devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_NEW, 181 resource->size_new)) 182 goto nla_put_failure; 183 if (devlink_resource_occ_put(resource, skb)) 184 goto nla_put_failure; 185 if (devlink_resource_size_params_put(resource, skb)) 186 goto nla_put_failure; 187 if (list_empty(&resource->resource_list)) 188 goto out; 189 190 if (nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_SIZE_VALID, 191 resource->size_valid)) 192 goto nla_put_failure; 193 194 child_resource_attr = nla_nest_start_noflag(skb, 195 DEVLINK_ATTR_RESOURCE_LIST); 196 if (!child_resource_attr) 197 goto nla_put_failure; 198 199 list_for_each_entry(child_resource, &resource->resource_list, list) { 200 if (devlink_resource_put(devlink, skb, child_resource)) 201 goto resource_put_failure; 202 } 203 204 nla_nest_end(skb, child_resource_attr); 205 out: 206 nla_nest_end(skb, resource_attr); 207 return 0; 208 209 resource_put_failure: 210 nla_nest_cancel(skb, child_resource_attr); 211 nla_put_failure: 212 nla_nest_cancel(skb, resource_attr); 213 return -EMSGSIZE; 214 } 215 216 static int devlink_resource_fill(struct genl_info *info, 217 enum devlink_command cmd, int flags) 218 { 219 struct devlink *devlink = info->user_ptr[0]; 220 struct devlink_resource *resource; 221 struct nlattr *resources_attr; 222 struct sk_buff *skb = NULL; 223 struct nlmsghdr *nlh; 224 bool incomplete; 225 void *hdr; 226 int i; 227 int err; 228 229 resource = list_first_entry(&devlink->resource_list, 230 struct devlink_resource, list); 231 start_again: 232 err = devlink_nl_msg_reply_and_new(&skb, info); 233 if (err) 234 return err; 235 236 hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq, 237 &devlink_nl_family, NLM_F_MULTI, cmd); 238 if (!hdr) { 239 nlmsg_free(skb); 240 return -EMSGSIZE; 241 } 242 243 if (devlink_nl_put_handle(skb, devlink)) 244 goto nla_put_failure; 245 246 resources_attr = nla_nest_start_noflag(skb, 247 DEVLINK_ATTR_RESOURCE_LIST); 248 if (!resources_attr) 249 goto nla_put_failure; 250 251 incomplete = false; 252 i = 0; 253 list_for_each_entry_from(resource, &devlink->resource_list, list) { 254 err = devlink_resource_put(devlink, skb, resource); 255 if (err) { 256 if (!i) 257 goto err_resource_put; 258 incomplete = true; 259 break; 260 } 261 i++; 262 } 263 nla_nest_end(skb, resources_attr); 264 genlmsg_end(skb, hdr); 265 if (incomplete) 266 goto start_again; 267 send_done: 268 nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq, 269 NLMSG_DONE, 0, flags | NLM_F_MULTI); 270 if (!nlh) { 271 err = devlink_nl_msg_reply_and_new(&skb, info); 272 if (err) 273 return err; 274 goto send_done; 275 } 276 return genlmsg_reply(skb, info); 277 278 nla_put_failure: 279 err = -EMSGSIZE; 280 err_resource_put: 281 nlmsg_free(skb); 282 return err; 283 } 284 285 int devlink_nl_resource_dump_doit(struct sk_buff *skb, struct genl_info *info) 286 { 287 struct devlink *devlink = info->user_ptr[0]; 288 289 if (list_empty(&devlink->resource_list)) 290 return -EOPNOTSUPP; 291 292 return devlink_resource_fill(info, DEVLINK_CMD_RESOURCE_DUMP, 0); 293 } 294 295 int devlink_resources_validate(struct devlink *devlink, 296 struct devlink_resource *resource, 297 struct genl_info *info) 298 { 299 struct list_head *resource_list; 300 int err = 0; 301 302 if (resource) 303 resource_list = &resource->resource_list; 304 else 305 resource_list = &devlink->resource_list; 306 307 list_for_each_entry(resource, resource_list, list) { 308 if (!resource->size_valid) 309 return -EINVAL; 310 err = devlink_resources_validate(devlink, resource, info); 311 if (err) 312 return err; 313 } 314 return err; 315 } 316 317 /** 318 * devl_resource_register - devlink resource register 319 * 320 * @devlink: devlink 321 * @resource_name: resource's name 322 * @resource_size: resource's size 323 * @resource_id: resource's id 324 * @parent_resource_id: resource's parent id 325 * @size_params: size parameters 326 * 327 * Generic resources should reuse the same names across drivers. 328 * Please see the generic resources list at: 329 * Documentation/networking/devlink/devlink-resource.rst 330 */ 331 int devl_resource_register(struct devlink *devlink, 332 const char *resource_name, 333 u64 resource_size, 334 u64 resource_id, 335 u64 parent_resource_id, 336 const struct devlink_resource_size_params *size_params) 337 { 338 struct devlink_resource *resource; 339 struct list_head *resource_list; 340 bool top_hierarchy; 341 342 lockdep_assert_held(&devlink->lock); 343 344 top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP; 345 346 resource = devlink_resource_find(devlink, NULL, resource_id); 347 if (resource) 348 return -EEXIST; 349 350 resource = kzalloc(sizeof(*resource), GFP_KERNEL); 351 if (!resource) 352 return -ENOMEM; 353 354 if (top_hierarchy) { 355 resource_list = &devlink->resource_list; 356 } else { 357 struct devlink_resource *parent_resource; 358 359 parent_resource = devlink_resource_find(devlink, NULL, 360 parent_resource_id); 361 if (parent_resource) { 362 resource_list = &parent_resource->resource_list; 363 resource->parent = parent_resource; 364 } else { 365 kfree(resource); 366 return -EINVAL; 367 } 368 } 369 370 resource->name = resource_name; 371 resource->size = resource_size; 372 resource->size_new = resource_size; 373 resource->id = resource_id; 374 resource->size_valid = true; 375 memcpy(&resource->size_params, size_params, 376 sizeof(resource->size_params)); 377 INIT_LIST_HEAD(&resource->resource_list); 378 list_add_tail(&resource->list, resource_list); 379 380 return 0; 381 } 382 EXPORT_SYMBOL_GPL(devl_resource_register); 383 384 static void devlink_resource_unregister(struct devlink *devlink, 385 struct devlink_resource *resource) 386 { 387 struct devlink_resource *tmp, *child_resource; 388 389 list_for_each_entry_safe(child_resource, tmp, &resource->resource_list, 390 list) { 391 devlink_resource_unregister(devlink, child_resource); 392 list_del(&child_resource->list); 393 kfree(child_resource); 394 } 395 } 396 397 /** 398 * devl_resources_unregister - free all resources 399 * 400 * @devlink: devlink 401 */ 402 void devl_resources_unregister(struct devlink *devlink) 403 { 404 struct devlink_resource *tmp, *child_resource; 405 406 lockdep_assert_held(&devlink->lock); 407 408 list_for_each_entry_safe(child_resource, tmp, &devlink->resource_list, 409 list) { 410 devlink_resource_unregister(devlink, child_resource); 411 list_del(&child_resource->list); 412 kfree(child_resource); 413 } 414 } 415 EXPORT_SYMBOL_GPL(devl_resources_unregister); 416 417 /** 418 * devlink_resources_unregister - free all resources 419 * 420 * @devlink: devlink 421 * 422 * Context: Takes and release devlink->lock <mutex>. 423 */ 424 void devlink_resources_unregister(struct devlink *devlink) 425 { 426 devl_lock(devlink); 427 devl_resources_unregister(devlink); 428 devl_unlock(devlink); 429 } 430 EXPORT_SYMBOL_GPL(devlink_resources_unregister); 431 432 /** 433 * devl_resource_size_get - get and update size 434 * 435 * @devlink: devlink 436 * @resource_id: the requested resource id 437 * @p_resource_size: ptr to update 438 */ 439 int devl_resource_size_get(struct devlink *devlink, 440 u64 resource_id, 441 u64 *p_resource_size) 442 { 443 struct devlink_resource *resource; 444 445 lockdep_assert_held(&devlink->lock); 446 447 resource = devlink_resource_find(devlink, NULL, resource_id); 448 if (!resource) 449 return -EINVAL; 450 *p_resource_size = resource->size_new; 451 resource->size = resource->size_new; 452 return 0; 453 } 454 EXPORT_SYMBOL_GPL(devl_resource_size_get); 455 456 /** 457 * devl_resource_occ_get_register - register occupancy getter 458 * 459 * @devlink: devlink 460 * @resource_id: resource id 461 * @occ_get: occupancy getter callback 462 * @occ_get_priv: occupancy getter callback priv 463 */ 464 void devl_resource_occ_get_register(struct devlink *devlink, 465 u64 resource_id, 466 devlink_resource_occ_get_t *occ_get, 467 void *occ_get_priv) 468 { 469 struct devlink_resource *resource; 470 471 lockdep_assert_held(&devlink->lock); 472 473 resource = devlink_resource_find(devlink, NULL, resource_id); 474 if (WARN_ON(!resource)) 475 return; 476 WARN_ON(resource->occ_get); 477 478 resource->occ_get = occ_get; 479 resource->occ_get_priv = occ_get_priv; 480 } 481 EXPORT_SYMBOL_GPL(devl_resource_occ_get_register); 482 483 /** 484 * devl_resource_occ_get_unregister - unregister occupancy getter 485 * 486 * @devlink: devlink 487 * @resource_id: resource id 488 */ 489 void devl_resource_occ_get_unregister(struct devlink *devlink, 490 u64 resource_id) 491 { 492 struct devlink_resource *resource; 493 494 lockdep_assert_held(&devlink->lock); 495 496 resource = devlink_resource_find(devlink, NULL, resource_id); 497 if (WARN_ON(!resource)) 498 return; 499 WARN_ON(!resource->occ_get); 500 501 resource->occ_get = NULL; 502 resource->occ_get_priv = NULL; 503 } 504 EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister); 505