Loading...
Note: File does not exist in v4.10.11.
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 */
24struct 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
38static struct devlink_resource *
39devlink_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
63static void
64devlink_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;
78out:
79 resource->size_valid = size_valid;
80}
81
82static int
83devlink_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
108int 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
137static int
138devlink_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
155static 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
164static 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);
205out:
206 nla_nest_end(skb, resource_attr);
207 return 0;
208
209resource_put_failure:
210 nla_nest_cancel(skb, child_resource_attr);
211nla_put_failure:
212 nla_nest_cancel(skb, resource_attr);
213 return -EMSGSIZE;
214}
215
216static 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);
231start_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;
267send_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
278nla_put_failure:
279 err = -EMSGSIZE;
280err_resource_put:
281 nlmsg_free(skb);
282 return err;
283}
284
285int 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
295int 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 */
331int 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}
382EXPORT_SYMBOL_GPL(devl_resource_register);
383
384static 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 */
402void 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}
415EXPORT_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 */
424void devlink_resources_unregister(struct devlink *devlink)
425{
426 devl_lock(devlink);
427 devl_resources_unregister(devlink);
428 devl_unlock(devlink);
429}
430EXPORT_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 */
439int 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}
454EXPORT_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 */
464void 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}
481EXPORT_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 */
489void 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}
504EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);