Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[platform/kernel/linux-starfive.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 <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_cmd_resource_set(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 (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))
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 nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_OCC,
161                                  resource->occ_get(resource->occ_get_priv),
162                                  DEVLINK_ATTR_PAD);
163 }
164
165 static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb,
166                                 struct devlink_resource *resource)
167 {
168         struct devlink_resource *child_resource;
169         struct nlattr *child_resource_attr;
170         struct nlattr *resource_attr;
171
172         resource_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE);
173         if (!resource_attr)
174                 return -EMSGSIZE;
175
176         if (nla_put_string(skb, DEVLINK_ATTR_RESOURCE_NAME, resource->name) ||
177             nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE, resource->size,
178                               DEVLINK_ATTR_PAD) ||
179             nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_ID, resource->id,
180                               DEVLINK_ATTR_PAD))
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))
191                 goto out;
192
193         if (nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_SIZE_VALID,
194                        resource->size_valid))
195                 goto nla_put_failure;
196
197         child_resource_attr = nla_nest_start_noflag(skb,
198                                                     DEVLINK_ATTR_RESOURCE_LIST);
199         if (!child_resource_attr)
200                 goto nla_put_failure;
201
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;
205         }
206
207         nla_nest_end(skb, child_resource_attr);
208 out:
209         nla_nest_end(skb, resource_attr);
210         return 0;
211
212 resource_put_failure:
213         nla_nest_cancel(skb, child_resource_attr);
214 nla_put_failure:
215         nla_nest_cancel(skb, resource_attr);
216         return -EMSGSIZE;
217 }
218
219 static int devlink_resource_fill(struct genl_info *info,
220                                  enum devlink_command cmd, int flags)
221 {
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;
227         bool incomplete;
228         void *hdr;
229         int i;
230         int err;
231
232         resource = list_first_entry(&devlink->resource_list,
233                                     struct devlink_resource, list);
234 start_again:
235         err = devlink_nl_msg_reply_and_new(&skb, info);
236         if (err)
237                 return err;
238
239         hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
240                           &devlink_nl_family, NLM_F_MULTI, cmd);
241         if (!hdr) {
242                 nlmsg_free(skb);
243                 return -EMSGSIZE;
244         }
245
246         if (devlink_nl_put_handle(skb, devlink))
247                 goto nla_put_failure;
248
249         resources_attr = nla_nest_start_noflag(skb,
250                                                DEVLINK_ATTR_RESOURCE_LIST);
251         if (!resources_attr)
252                 goto nla_put_failure;
253
254         incomplete = false;
255         i = 0;
256         list_for_each_entry_from(resource, &devlink->resource_list, list) {
257                 err = devlink_resource_put(devlink, skb, resource);
258                 if (err) {
259                         if (!i)
260                                 goto err_resource_put;
261                         incomplete = true;
262                         break;
263                 }
264                 i++;
265         }
266         nla_nest_end(skb, resources_attr);
267         genlmsg_end(skb, hdr);
268         if (incomplete)
269                 goto start_again;
270 send_done:
271         nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
272                         NLMSG_DONE, 0, flags | NLM_F_MULTI);
273         if (!nlh) {
274                 err = devlink_nl_msg_reply_and_new(&skb, info);
275                 if (err)
276                         return err;
277                 goto send_done;
278         }
279         return genlmsg_reply(skb, info);
280
281 nla_put_failure:
282         err = -EMSGSIZE;
283 err_resource_put:
284         nlmsg_free(skb);
285         return err;
286 }
287
288 int devlink_nl_cmd_resource_dump(struct sk_buff *skb, struct genl_info *info)
289 {
290         struct devlink *devlink = info->user_ptr[0];
291
292         if (list_empty(&devlink->resource_list))
293                 return -EOPNOTSUPP;
294
295         return devlink_resource_fill(info, DEVLINK_CMD_RESOURCE_DUMP, 0);
296 }
297
298 int devlink_resources_validate(struct devlink *devlink,
299                                struct devlink_resource *resource,
300                                struct genl_info *info)
301 {
302         struct list_head *resource_list;
303         int err = 0;
304
305         if (resource)
306                 resource_list = &resource->resource_list;
307         else
308                 resource_list = &devlink->resource_list;
309
310         list_for_each_entry(resource, resource_list, list) {
311                 if (!resource->size_valid)
312                         return -EINVAL;
313                 err = devlink_resources_validate(devlink, resource, info);
314                 if (err)
315                         return err;
316         }
317         return err;
318 }
319
320 /**
321  * devl_resource_register - devlink resource register
322  *
323  * @devlink: devlink
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
329  *
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
333  */
334 int devl_resource_register(struct devlink *devlink,
335                            const char *resource_name,
336                            u64 resource_size,
337                            u64 resource_id,
338                            u64 parent_resource_id,
339                            const struct devlink_resource_size_params *size_params)
340 {
341         struct devlink_resource *resource;
342         struct list_head *resource_list;
343         bool top_hierarchy;
344
345         lockdep_assert_held(&devlink->lock);
346
347         top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
348
349         resource = devlink_resource_find(devlink, NULL, resource_id);
350         if (resource)
351                 return -EINVAL;
352
353         resource = kzalloc(sizeof(*resource), GFP_KERNEL);
354         if (!resource)
355                 return -ENOMEM;
356
357         if (top_hierarchy) {
358                 resource_list = &devlink->resource_list;
359         } else {
360                 struct devlink_resource *parent_resource;
361
362                 parent_resource = devlink_resource_find(devlink, NULL,
363                                                         parent_resource_id);
364                 if (parent_resource) {
365                         resource_list = &parent_resource->resource_list;
366                         resource->parent = parent_resource;
367                 } else {
368                         kfree(resource);
369                         return -EINVAL;
370                 }
371         }
372
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);
382
383         return 0;
384 }
385 EXPORT_SYMBOL_GPL(devl_resource_register);
386
387 /**
388  *      devlink_resource_register - devlink resource register
389  *
390  *      @devlink: devlink
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
396  *
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
400  *
401  *      Context: Takes and release devlink->lock <mutex>.
402  */
403 int devlink_resource_register(struct devlink *devlink,
404                               const char *resource_name,
405                               u64 resource_size,
406                               u64 resource_id,
407                               u64 parent_resource_id,
408                               const struct devlink_resource_size_params *size_params)
409 {
410         int err;
411
412         devl_lock(devlink);
413         err = devl_resource_register(devlink, resource_name, resource_size,
414                                      resource_id, parent_resource_id, size_params);
415         devl_unlock(devlink);
416         return err;
417 }
418 EXPORT_SYMBOL_GPL(devlink_resource_register);
419
420 static void devlink_resource_unregister(struct devlink *devlink,
421                                         struct devlink_resource *resource)
422 {
423         struct devlink_resource *tmp, *child_resource;
424
425         list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
426                                  list) {
427                 devlink_resource_unregister(devlink, child_resource);
428                 list_del(&child_resource->list);
429                 kfree(child_resource);
430         }
431 }
432
433 /**
434  * devl_resources_unregister - free all resources
435  *
436  * @devlink: devlink
437  */
438 void devl_resources_unregister(struct devlink *devlink)
439 {
440         struct devlink_resource *tmp, *child_resource;
441
442         lockdep_assert_held(&devlink->lock);
443
444         list_for_each_entry_safe(child_resource, tmp, &devlink->resource_list,
445                                  list) {
446                 devlink_resource_unregister(devlink, child_resource);
447                 list_del(&child_resource->list);
448                 kfree(child_resource);
449         }
450 }
451 EXPORT_SYMBOL_GPL(devl_resources_unregister);
452
453 /**
454  *      devlink_resources_unregister - free all resources
455  *
456  *      @devlink: devlink
457  *
458  *      Context: Takes and release devlink->lock <mutex>.
459  */
460 void devlink_resources_unregister(struct devlink *devlink)
461 {
462         devl_lock(devlink);
463         devl_resources_unregister(devlink);
464         devl_unlock(devlink);
465 }
466 EXPORT_SYMBOL_GPL(devlink_resources_unregister);
467
468 /**
469  * devl_resource_size_get - get and update size
470  *
471  * @devlink: devlink
472  * @resource_id: the requested resource id
473  * @p_resource_size: ptr to update
474  */
475 int devl_resource_size_get(struct devlink *devlink,
476                            u64 resource_id,
477                            u64 *p_resource_size)
478 {
479         struct devlink_resource *resource;
480
481         lockdep_assert_held(&devlink->lock);
482
483         resource = devlink_resource_find(devlink, NULL, resource_id);
484         if (!resource)
485                 return -EINVAL;
486         *p_resource_size = resource->size_new;
487         resource->size = resource->size_new;
488         return 0;
489 }
490 EXPORT_SYMBOL_GPL(devl_resource_size_get);
491
492 /**
493  * devl_resource_occ_get_register - register occupancy getter
494  *
495  * @devlink: devlink
496  * @resource_id: resource id
497  * @occ_get: occupancy getter callback
498  * @occ_get_priv: occupancy getter callback priv
499  */
500 void devl_resource_occ_get_register(struct devlink *devlink,
501                                     u64 resource_id,
502                                     devlink_resource_occ_get_t *occ_get,
503                                     void *occ_get_priv)
504 {
505         struct devlink_resource *resource;
506
507         lockdep_assert_held(&devlink->lock);
508
509         resource = devlink_resource_find(devlink, NULL, resource_id);
510         if (WARN_ON(!resource))
511                 return;
512         WARN_ON(resource->occ_get);
513
514         resource->occ_get = occ_get;
515         resource->occ_get_priv = occ_get_priv;
516 }
517 EXPORT_SYMBOL_GPL(devl_resource_occ_get_register);
518
519 /**
520  *      devlink_resource_occ_get_register - register occupancy getter
521  *
522  *      @devlink: devlink
523  *      @resource_id: resource id
524  *      @occ_get: occupancy getter callback
525  *      @occ_get_priv: occupancy getter callback priv
526  *
527  *      Context: Takes and release devlink->lock <mutex>.
528  */
529 void devlink_resource_occ_get_register(struct devlink *devlink,
530                                        u64 resource_id,
531                                        devlink_resource_occ_get_t *occ_get,
532                                        void *occ_get_priv)
533 {
534         devl_lock(devlink);
535         devl_resource_occ_get_register(devlink, resource_id,
536                                        occ_get, occ_get_priv);
537         devl_unlock(devlink);
538 }
539 EXPORT_SYMBOL_GPL(devlink_resource_occ_get_register);
540
541 /**
542  * devl_resource_occ_get_unregister - unregister occupancy getter
543  *
544  * @devlink: devlink
545  * @resource_id: resource id
546  */
547 void devl_resource_occ_get_unregister(struct devlink *devlink,
548                                       u64 resource_id)
549 {
550         struct devlink_resource *resource;
551
552         lockdep_assert_held(&devlink->lock);
553
554         resource = devlink_resource_find(devlink, NULL, resource_id);
555         if (WARN_ON(!resource))
556                 return;
557         WARN_ON(!resource->occ_get);
558
559         resource->occ_get = NULL;
560         resource->occ_get_priv = NULL;
561 }
562 EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);
563
564 /**
565  *      devlink_resource_occ_get_unregister - unregister occupancy getter
566  *
567  *      @devlink: devlink
568  *      @resource_id: resource id
569  *
570  *      Context: Takes and release devlink->lock <mutex>.
571  */
572 void devlink_resource_occ_get_unregister(struct devlink *devlink,
573                                          u64 resource_id)
574 {
575         devl_lock(devlink);
576         devl_resource_occ_get_unregister(devlink, resource_id);
577         devl_unlock(devlink);
578 }
579 EXPORT_SYMBOL_GPL(devlink_resource_occ_get_unregister);