1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4 * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
7 #include "devl_internal.h"
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
20 * @resource_list: list of child resources
21 * @occ_get: occupancy getter callback
22 * @occ_get_priv: occupancy getter callback priv
24 struct devlink_resource {
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;
38 static struct devlink_resource *
39 devlink_resource_find(struct devlink *devlink,
40 struct devlink_resource *resource, u64 resource_id)
42 struct list_head *resource_list;
45 resource_list = &resource->resource_list;
47 resource_list = &devlink->resource_list;
49 list_for_each_entry(resource, resource_list, list) {
50 struct devlink_resource *child_resource;
52 if (resource->id == resource_id)
55 child_resource = devlink_resource_find(devlink, resource,
58 return child_resource;
64 devlink_resource_validate_children(struct devlink_resource *resource)
66 struct devlink_resource *child_resource;
67 bool size_valid = true;
70 if (list_empty(&resource->resource_list))
73 list_for_each_entry(child_resource, &resource->resource_list, list)
74 parts_size += child_resource->size_new;
76 if (parts_size > resource->size_new)
79 resource->size_valid = size_valid;
83 devlink_resource_validate_size(struct devlink_resource *resource, u64 size,
84 struct netlink_ext_ack *extack)
89 if (size > resource->size_params.size_max) {
90 NL_SET_ERR_MSG(extack, "Size larger than maximum");
94 if (size < resource->size_params.size_min) {
95 NL_SET_ERR_MSG(extack, "Size smaller than minimum");
99 div64_u64_rem(size, resource->size_params.size_granularity, &reminder);
101 NL_SET_ERR_MSG(extack, "Wrong granularity");
108 int devlink_nl_cmd_resource_set(struct sk_buff *skb, struct genl_info *info)
110 struct devlink *devlink = info->user_ptr[0];
111 struct devlink_resource *resource;
116 if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_ID) ||
117 GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_SIZE))
119 resource_id = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_ID]);
121 resource = devlink_resource_find(devlink, NULL, resource_id);
125 size = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_SIZE]);
126 err = devlink_resource_validate_size(resource, size, info->extack);
130 resource->size_new = size;
131 devlink_resource_validate_children(resource);
132 if (resource->parent)
133 devlink_resource_validate_children(resource->parent);
138 devlink_resource_size_params_put(struct devlink_resource *resource,
141 struct devlink_resource_size_params *size_params;
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))
155 static int devlink_resource_occ_put(struct devlink_resource *resource,
158 if (!resource->occ_get)
160 return nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_OCC,
161 resource->occ_get(resource->occ_get_priv),
165 static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb,
166 struct devlink_resource *resource)
168 struct devlink_resource *child_resource;
169 struct nlattr *child_resource_attr;
170 struct nlattr *resource_attr;
172 resource_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE);
176 if (nla_put_string(skb, DEVLINK_ATTR_RESOURCE_NAME, resource->name) ||
177 nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE, resource->size,
179 nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_ID, resource->id,
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))
193 if (nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_SIZE_VALID,
194 resource->size_valid))
195 goto nla_put_failure;
197 child_resource_attr = nla_nest_start_noflag(skb,
198 DEVLINK_ATTR_RESOURCE_LIST);
199 if (!child_resource_attr)
200 goto nla_put_failure;
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;
207 nla_nest_end(skb, child_resource_attr);
209 nla_nest_end(skb, resource_attr);
212 resource_put_failure:
213 nla_nest_cancel(skb, child_resource_attr);
215 nla_nest_cancel(skb, resource_attr);
219 static int devlink_resource_fill(struct genl_info *info,
220 enum devlink_command cmd, int flags)
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;
232 resource = list_first_entry(&devlink->resource_list,
233 struct devlink_resource, list);
235 err = devlink_nl_msg_reply_and_new(&skb, info);
239 hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
240 &devlink_nl_family, NLM_F_MULTI, cmd);
246 if (devlink_nl_put_handle(skb, devlink))
247 goto nla_put_failure;
249 resources_attr = nla_nest_start_noflag(skb,
250 DEVLINK_ATTR_RESOURCE_LIST);
252 goto nla_put_failure;
256 list_for_each_entry_from(resource, &devlink->resource_list, list) {
257 err = devlink_resource_put(devlink, skb, resource);
260 goto err_resource_put;
266 nla_nest_end(skb, resources_attr);
267 genlmsg_end(skb, hdr);
271 nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
272 NLMSG_DONE, 0, flags | NLM_F_MULTI);
274 err = devlink_nl_msg_reply_and_new(&skb, info);
279 return genlmsg_reply(skb, info);
288 int devlink_nl_cmd_resource_dump(struct sk_buff *skb, struct genl_info *info)
290 struct devlink *devlink = info->user_ptr[0];
292 if (list_empty(&devlink->resource_list))
295 return devlink_resource_fill(info, DEVLINK_CMD_RESOURCE_DUMP, 0);
298 int devlink_resources_validate(struct devlink *devlink,
299 struct devlink_resource *resource,
300 struct genl_info *info)
302 struct list_head *resource_list;
306 resource_list = &resource->resource_list;
308 resource_list = &devlink->resource_list;
310 list_for_each_entry(resource, resource_list, list) {
311 if (!resource->size_valid)
313 err = devlink_resources_validate(devlink, resource, info);
321 * devl_resource_register - devlink resource register
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
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
334 int devl_resource_register(struct devlink *devlink,
335 const char *resource_name,
338 u64 parent_resource_id,
339 const struct devlink_resource_size_params *size_params)
341 struct devlink_resource *resource;
342 struct list_head *resource_list;
345 lockdep_assert_held(&devlink->lock);
347 top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
349 resource = devlink_resource_find(devlink, NULL, resource_id);
353 resource = kzalloc(sizeof(*resource), GFP_KERNEL);
358 resource_list = &devlink->resource_list;
360 struct devlink_resource *parent_resource;
362 parent_resource = devlink_resource_find(devlink, NULL,
364 if (parent_resource) {
365 resource_list = &parent_resource->resource_list;
366 resource->parent = parent_resource;
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);
385 EXPORT_SYMBOL_GPL(devl_resource_register);
388 * devlink_resource_register - devlink resource register
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
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
401 * Context: Takes and release devlink->lock <mutex>.
403 int devlink_resource_register(struct devlink *devlink,
404 const char *resource_name,
407 u64 parent_resource_id,
408 const struct devlink_resource_size_params *size_params)
413 err = devl_resource_register(devlink, resource_name, resource_size,
414 resource_id, parent_resource_id, size_params);
415 devl_unlock(devlink);
418 EXPORT_SYMBOL_GPL(devlink_resource_register);
420 static void devlink_resource_unregister(struct devlink *devlink,
421 struct devlink_resource *resource)
423 struct devlink_resource *tmp, *child_resource;
425 list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
427 devlink_resource_unregister(devlink, child_resource);
428 list_del(&child_resource->list);
429 kfree(child_resource);
434 * devl_resources_unregister - free all resources
438 void devl_resources_unregister(struct devlink *devlink)
440 struct devlink_resource *tmp, *child_resource;
442 lockdep_assert_held(&devlink->lock);
444 list_for_each_entry_safe(child_resource, tmp, &devlink->resource_list,
446 devlink_resource_unregister(devlink, child_resource);
447 list_del(&child_resource->list);
448 kfree(child_resource);
451 EXPORT_SYMBOL_GPL(devl_resources_unregister);
454 * devlink_resources_unregister - free all resources
458 * Context: Takes and release devlink->lock <mutex>.
460 void devlink_resources_unregister(struct devlink *devlink)
463 devl_resources_unregister(devlink);
464 devl_unlock(devlink);
466 EXPORT_SYMBOL_GPL(devlink_resources_unregister);
469 * devl_resource_size_get - get and update size
472 * @resource_id: the requested resource id
473 * @p_resource_size: ptr to update
475 int devl_resource_size_get(struct devlink *devlink,
477 u64 *p_resource_size)
479 struct devlink_resource *resource;
481 lockdep_assert_held(&devlink->lock);
483 resource = devlink_resource_find(devlink, NULL, resource_id);
486 *p_resource_size = resource->size_new;
487 resource->size = resource->size_new;
490 EXPORT_SYMBOL_GPL(devl_resource_size_get);
493 * devl_resource_occ_get_register - register occupancy getter
496 * @resource_id: resource id
497 * @occ_get: occupancy getter callback
498 * @occ_get_priv: occupancy getter callback priv
500 void devl_resource_occ_get_register(struct devlink *devlink,
502 devlink_resource_occ_get_t *occ_get,
505 struct devlink_resource *resource;
507 lockdep_assert_held(&devlink->lock);
509 resource = devlink_resource_find(devlink, NULL, resource_id);
510 if (WARN_ON(!resource))
512 WARN_ON(resource->occ_get);
514 resource->occ_get = occ_get;
515 resource->occ_get_priv = occ_get_priv;
517 EXPORT_SYMBOL_GPL(devl_resource_occ_get_register);
520 * devlink_resource_occ_get_register - register occupancy getter
523 * @resource_id: resource id
524 * @occ_get: occupancy getter callback
525 * @occ_get_priv: occupancy getter callback priv
527 * Context: Takes and release devlink->lock <mutex>.
529 void devlink_resource_occ_get_register(struct devlink *devlink,
531 devlink_resource_occ_get_t *occ_get,
535 devl_resource_occ_get_register(devlink, resource_id,
536 occ_get, occ_get_priv);
537 devl_unlock(devlink);
539 EXPORT_SYMBOL_GPL(devlink_resource_occ_get_register);
542 * devl_resource_occ_get_unregister - unregister occupancy getter
545 * @resource_id: resource id
547 void devl_resource_occ_get_unregister(struct devlink *devlink,
550 struct devlink_resource *resource;
552 lockdep_assert_held(&devlink->lock);
554 resource = devlink_resource_find(devlink, NULL, resource_id);
555 if (WARN_ON(!resource))
557 WARN_ON(!resource->occ_get);
559 resource->occ_get = NULL;
560 resource->occ_get_priv = NULL;
562 EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);
565 * devlink_resource_occ_get_unregister - unregister occupancy getter
568 * @resource_id: resource id
570 * Context: Takes and release devlink->lock <mutex>.
572 void devlink_resource_occ_get_unregister(struct devlink *devlink,
576 devl_resource_occ_get_unregister(devlink, resource_id);
577 devl_unlock(devlink);
579 EXPORT_SYMBOL_GPL(devlink_resource_occ_get_unregister);