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 *
devlink_resource_find(struct devlink * devlink,struct devlink_resource * resource,u64 resource_id)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
devlink_resource_validate_children(struct devlink_resource * resource)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
devlink_resource_validate_size(struct devlink_resource * resource,u64 size,struct netlink_ext_ack * extack)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
devlink_nl_cmd_resource_set(struct sk_buff * skb,struct genl_info * info)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
devlink_resource_size_params_put(struct devlink_resource * resource,struct sk_buff * skb)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
devlink_resource_occ_put(struct devlink_resource * resource,struct sk_buff * skb)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
devlink_resource_put(struct devlink * devlink,struct sk_buff * skb,struct devlink_resource * resource)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
devlink_resource_fill(struct genl_info * info,enum devlink_command cmd,int flags)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
devlink_nl_cmd_resource_dump(struct sk_buff * skb,struct genl_info * info)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
devlink_resources_validate(struct devlink * devlink,struct devlink_resource * resource,struct genl_info * info)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 */
devl_resource_register(struct devlink * devlink,const char * resource_name,u64 resource_size,u64 resource_id,u64 parent_resource_id,const struct devlink_resource_size_params * size_params)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 */
devlink_resource_register(struct devlink * devlink,const char * resource_name,u64 resource_size,u64 resource_id,u64 parent_resource_id,const struct devlink_resource_size_params * size_params)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
devlink_resource_unregister(struct devlink * devlink,struct devlink_resource * resource)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 */
devl_resources_unregister(struct devlink * devlink)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 */
devlink_resources_unregister(struct devlink * devlink)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 */
devl_resource_size_get(struct devlink * devlink,u64 resource_id,u64 * p_resource_size)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 */
devl_resource_occ_get_register(struct devlink * devlink,u64 resource_id,devlink_resource_occ_get_t * occ_get,void * occ_get_priv)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 */
devlink_resource_occ_get_register(struct devlink * devlink,u64 resource_id,devlink_resource_occ_get_t * occ_get,void * occ_get_priv)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 */
devl_resource_occ_get_unregister(struct devlink * devlink,u64 resource_id)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 */
devlink_resource_occ_get_unregister(struct devlink * devlink,u64 resource_id)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);
580