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_resource_set_doit(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_resource_dump_doit(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
);