]> Git Repo - linux.git/blob - net/devlink/resource.c
Linux 6.14-rc3
[linux.git] / net / devlink / resource.c
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 <[email protected]>
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);
This page took 0.090951 seconds and 4 git commands to generate.