Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/devlink/resource.c
26282 views
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);
505
506