1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
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 (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))
155 static int devlink_resource_occ_put(struct devlink_resource *resource,
158 if (!resource->occ_get)
160 return devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_OCC,
161 resource->occ_get(resource->occ_get_priv));
164 static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb,
165 struct devlink_resource *resource)
167 struct devlink_resource *child_resource;
168 struct nlattr *child_resource_attr;
169 struct nlattr *resource_attr;
171 resource_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE);
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,
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))
190 if (nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_SIZE_VALID,
191 resource->size_valid))
192 goto nla_put_failure;
194 child_resource_attr = nla_nest_start_noflag(skb,
195 DEVLINK_ATTR_RESOURCE_LIST);
196 if (!child_resource_attr)
197 goto nla_put_failure;
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;
204 nla_nest_end(skb, child_resource_attr);
206 nla_nest_end(skb, resource_attr);
209 resource_put_failure:
210 nla_nest_cancel(skb, child_resource_attr);
212 nla_nest_cancel(skb, resource_attr);
216 static int devlink_resource_fill(struct genl_info *info,
217 enum devlink_command cmd, int flags)
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;
229 resource = list_first_entry(&devlink->resource_list,
230 struct devlink_resource, list);
232 err = devlink_nl_msg_reply_and_new(&skb, info);
236 hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
237 &devlink_nl_family, NLM_F_MULTI, cmd);
243 if (devlink_nl_put_handle(skb, devlink))
244 goto nla_put_failure;
246 resources_attr = nla_nest_start_noflag(skb,
247 DEVLINK_ATTR_RESOURCE_LIST);
249 goto nla_put_failure;
253 list_for_each_entry_from(resource, &devlink->resource_list, list) {
254 err = devlink_resource_put(devlink, skb, resource);
257 goto err_resource_put;
263 nla_nest_end(skb, resources_attr);
264 genlmsg_end(skb, hdr);
268 nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
269 NLMSG_DONE, 0, flags | NLM_F_MULTI);
271 err = devlink_nl_msg_reply_and_new(&skb, info);
276 return genlmsg_reply(skb, info);
285 int devlink_nl_resource_dump_doit(struct sk_buff *skb, struct genl_info *info)
287 struct devlink *devlink = info->user_ptr[0];
289 if (list_empty(&devlink->resource_list))
292 return devlink_resource_fill(info, DEVLINK_CMD_RESOURCE_DUMP, 0);
295 int devlink_resources_validate(struct devlink *devlink,
296 struct devlink_resource *resource,
297 struct genl_info *info)
299 struct list_head *resource_list;
303 resource_list = &resource->resource_list;
305 resource_list = &devlink->resource_list;
307 list_for_each_entry(resource, resource_list, list) {
308 if (!resource->size_valid)
310 err = devlink_resources_validate(devlink, resource, info);
318 * devl_resource_register - devlink resource register
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
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
331 int devl_resource_register(struct devlink *devlink,
332 const char *resource_name,
335 u64 parent_resource_id,
336 const struct devlink_resource_size_params *size_params)
338 struct devlink_resource *resource;
339 struct list_head *resource_list;
342 lockdep_assert_held(&devlink->lock);
344 top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
346 resource = devlink_resource_find(devlink, NULL, resource_id);
350 resource = kzalloc(sizeof(*resource), GFP_KERNEL);
355 resource_list = &devlink->resource_list;
357 struct devlink_resource *parent_resource;
359 parent_resource = devlink_resource_find(devlink, NULL,
361 if (parent_resource) {
362 resource_list = &parent_resource->resource_list;
363 resource->parent = parent_resource;
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);
382 EXPORT_SYMBOL_GPL(devl_resource_register);
384 static void devlink_resource_unregister(struct devlink *devlink,
385 struct devlink_resource *resource)
387 struct devlink_resource *tmp, *child_resource;
389 list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
391 devlink_resource_unregister(devlink, child_resource);
392 list_del(&child_resource->list);
393 kfree(child_resource);
398 * devl_resources_unregister - free all resources
402 void devl_resources_unregister(struct devlink *devlink)
404 struct devlink_resource *tmp, *child_resource;
406 lockdep_assert_held(&devlink->lock);
408 list_for_each_entry_safe(child_resource, tmp, &devlink->resource_list,
410 devlink_resource_unregister(devlink, child_resource);
411 list_del(&child_resource->list);
412 kfree(child_resource);
415 EXPORT_SYMBOL_GPL(devl_resources_unregister);
418 * devlink_resources_unregister - free all resources
422 * Context: Takes and release devlink->lock <mutex>.
424 void devlink_resources_unregister(struct devlink *devlink)
427 devl_resources_unregister(devlink);
428 devl_unlock(devlink);
430 EXPORT_SYMBOL_GPL(devlink_resources_unregister);
433 * devl_resource_size_get - get and update size
436 * @resource_id: the requested resource id
437 * @p_resource_size: ptr to update
439 int devl_resource_size_get(struct devlink *devlink,
441 u64 *p_resource_size)
443 struct devlink_resource *resource;
445 lockdep_assert_held(&devlink->lock);
447 resource = devlink_resource_find(devlink, NULL, resource_id);
450 *p_resource_size = resource->size_new;
451 resource->size = resource->size_new;
454 EXPORT_SYMBOL_GPL(devl_resource_size_get);
457 * devl_resource_occ_get_register - register occupancy getter
460 * @resource_id: resource id
461 * @occ_get: occupancy getter callback
462 * @occ_get_priv: occupancy getter callback priv
464 void devl_resource_occ_get_register(struct devlink *devlink,
466 devlink_resource_occ_get_t *occ_get,
469 struct devlink_resource *resource;
471 lockdep_assert_held(&devlink->lock);
473 resource = devlink_resource_find(devlink, NULL, resource_id);
474 if (WARN_ON(!resource))
476 WARN_ON(resource->occ_get);
478 resource->occ_get = occ_get;
479 resource->occ_get_priv = occ_get_priv;
481 EXPORT_SYMBOL_GPL(devl_resource_occ_get_register);
484 * devl_resource_occ_get_unregister - unregister occupancy getter
487 * @resource_id: resource id
489 void devl_resource_occ_get_unregister(struct devlink *devlink,
492 struct devlink_resource *resource;
494 lockdep_assert_held(&devlink->lock);
496 resource = devlink_resource_find(devlink, NULL, resource_id);
497 if (WARN_ON(!resource))
499 WARN_ON(!resource->occ_get);
501 resource->occ_get = NULL;
502 resource->occ_get_priv = NULL;
504 EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);