Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/devlink/linecard.c
26285 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
struct devlink_linecard {
10
struct list_head list;
11
struct devlink *devlink;
12
unsigned int index;
13
const struct devlink_linecard_ops *ops;
14
void *priv;
15
enum devlink_linecard_state state;
16
struct mutex state_lock; /* Protects state */
17
const char *type;
18
struct devlink_linecard_type *types;
19
unsigned int types_count;
20
u32 rel_index;
21
};
22
23
unsigned int devlink_linecard_index(struct devlink_linecard *linecard)
24
{
25
return linecard->index;
26
}
27
28
static struct devlink_linecard *
29
devlink_linecard_get_by_index(struct devlink *devlink,
30
unsigned int linecard_index)
31
{
32
struct devlink_linecard *devlink_linecard;
33
34
list_for_each_entry(devlink_linecard, &devlink->linecard_list, list) {
35
if (devlink_linecard->index == linecard_index)
36
return devlink_linecard;
37
}
38
return NULL;
39
}
40
41
static bool devlink_linecard_index_exists(struct devlink *devlink,
42
unsigned int linecard_index)
43
{
44
return devlink_linecard_get_by_index(devlink, linecard_index);
45
}
46
47
static struct devlink_linecard *
48
devlink_linecard_get_from_attrs(struct devlink *devlink, struct nlattr **attrs)
49
{
50
if (attrs[DEVLINK_ATTR_LINECARD_INDEX]) {
51
u32 linecard_index = nla_get_u32(attrs[DEVLINK_ATTR_LINECARD_INDEX]);
52
struct devlink_linecard *linecard;
53
54
linecard = devlink_linecard_get_by_index(devlink, linecard_index);
55
if (!linecard)
56
return ERR_PTR(-ENODEV);
57
return linecard;
58
}
59
return ERR_PTR(-EINVAL);
60
}
61
62
static struct devlink_linecard *
63
devlink_linecard_get_from_info(struct devlink *devlink, struct genl_info *info)
64
{
65
return devlink_linecard_get_from_attrs(devlink, info->attrs);
66
}
67
68
struct devlink_linecard_type {
69
const char *type;
70
const void *priv;
71
};
72
73
static int devlink_nl_linecard_fill(struct sk_buff *msg,
74
struct devlink *devlink,
75
struct devlink_linecard *linecard,
76
enum devlink_command cmd, u32 portid,
77
u32 seq, int flags,
78
struct netlink_ext_ack *extack)
79
{
80
struct devlink_linecard_type *linecard_type;
81
struct nlattr *attr;
82
void *hdr;
83
int i;
84
85
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
86
if (!hdr)
87
return -EMSGSIZE;
88
89
if (devlink_nl_put_handle(msg, devlink))
90
goto nla_put_failure;
91
if (nla_put_u32(msg, DEVLINK_ATTR_LINECARD_INDEX, linecard->index))
92
goto nla_put_failure;
93
if (nla_put_u8(msg, DEVLINK_ATTR_LINECARD_STATE, linecard->state))
94
goto nla_put_failure;
95
if (linecard->type &&
96
nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE, linecard->type))
97
goto nla_put_failure;
98
99
if (linecard->types_count) {
100
attr = nla_nest_start(msg,
101
DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES);
102
if (!attr)
103
goto nla_put_failure;
104
for (i = 0; i < linecard->types_count; i++) {
105
linecard_type = &linecard->types[i];
106
if (nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE,
107
linecard_type->type)) {
108
nla_nest_cancel(msg, attr);
109
goto nla_put_failure;
110
}
111
}
112
nla_nest_end(msg, attr);
113
}
114
115
if (devlink_rel_devlink_handle_put(msg, devlink,
116
linecard->rel_index,
117
DEVLINK_ATTR_NESTED_DEVLINK,
118
NULL))
119
goto nla_put_failure;
120
121
genlmsg_end(msg, hdr);
122
return 0;
123
124
nla_put_failure:
125
genlmsg_cancel(msg, hdr);
126
return -EMSGSIZE;
127
}
128
129
static void devlink_linecard_notify(struct devlink_linecard *linecard,
130
enum devlink_command cmd)
131
{
132
struct devlink *devlink = linecard->devlink;
133
struct sk_buff *msg;
134
int err;
135
136
WARN_ON(cmd != DEVLINK_CMD_LINECARD_NEW &&
137
cmd != DEVLINK_CMD_LINECARD_DEL);
138
139
if (!__devl_is_registered(devlink) || !devlink_nl_notify_need(devlink))
140
return;
141
142
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
143
if (!msg)
144
return;
145
146
err = devlink_nl_linecard_fill(msg, devlink, linecard, cmd, 0, 0, 0,
147
NULL);
148
if (err) {
149
nlmsg_free(msg);
150
return;
151
}
152
153
devlink_nl_notify_send(devlink, msg);
154
}
155
156
void devlink_linecards_notify_register(struct devlink *devlink)
157
{
158
struct devlink_linecard *linecard;
159
160
list_for_each_entry(linecard, &devlink->linecard_list, list)
161
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
162
}
163
164
void devlink_linecards_notify_unregister(struct devlink *devlink)
165
{
166
struct devlink_linecard *linecard;
167
168
list_for_each_entry_reverse(linecard, &devlink->linecard_list, list)
169
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);
170
}
171
172
int devlink_nl_linecard_get_doit(struct sk_buff *skb, struct genl_info *info)
173
{
174
struct devlink *devlink = info->user_ptr[0];
175
struct devlink_linecard *linecard;
176
struct sk_buff *msg;
177
int err;
178
179
linecard = devlink_linecard_get_from_info(devlink, info);
180
if (IS_ERR(linecard))
181
return PTR_ERR(linecard);
182
183
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
184
if (!msg)
185
return -ENOMEM;
186
187
mutex_lock(&linecard->state_lock);
188
err = devlink_nl_linecard_fill(msg, devlink, linecard,
189
DEVLINK_CMD_LINECARD_NEW,
190
info->snd_portid, info->snd_seq, 0,
191
info->extack);
192
mutex_unlock(&linecard->state_lock);
193
if (err) {
194
nlmsg_free(msg);
195
return err;
196
}
197
198
return genlmsg_reply(msg, info);
199
}
200
201
static int devlink_nl_linecard_get_dump_one(struct sk_buff *msg,
202
struct devlink *devlink,
203
struct netlink_callback *cb,
204
int flags)
205
{
206
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
207
struct devlink_linecard *linecard;
208
int idx = 0;
209
int err = 0;
210
211
list_for_each_entry(linecard, &devlink->linecard_list, list) {
212
if (idx < state->idx) {
213
idx++;
214
continue;
215
}
216
mutex_lock(&linecard->state_lock);
217
err = devlink_nl_linecard_fill(msg, devlink, linecard,
218
DEVLINK_CMD_LINECARD_NEW,
219
NETLINK_CB(cb->skb).portid,
220
cb->nlh->nlmsg_seq, flags,
221
cb->extack);
222
mutex_unlock(&linecard->state_lock);
223
if (err) {
224
state->idx = idx;
225
break;
226
}
227
idx++;
228
}
229
230
return err;
231
}
232
233
int devlink_nl_linecard_get_dumpit(struct sk_buff *skb,
234
struct netlink_callback *cb)
235
{
236
return devlink_nl_dumpit(skb, cb, devlink_nl_linecard_get_dump_one);
237
}
238
239
static struct devlink_linecard_type *
240
devlink_linecard_type_lookup(struct devlink_linecard *linecard,
241
const char *type)
242
{
243
struct devlink_linecard_type *linecard_type;
244
int i;
245
246
for (i = 0; i < linecard->types_count; i++) {
247
linecard_type = &linecard->types[i];
248
if (!strcmp(type, linecard_type->type))
249
return linecard_type;
250
}
251
return NULL;
252
}
253
254
static int devlink_linecard_type_set(struct devlink_linecard *linecard,
255
const char *type,
256
struct netlink_ext_ack *extack)
257
{
258
const struct devlink_linecard_ops *ops = linecard->ops;
259
struct devlink_linecard_type *linecard_type;
260
int err;
261
262
mutex_lock(&linecard->state_lock);
263
if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {
264
NL_SET_ERR_MSG(extack, "Line card is currently being provisioned");
265
err = -EBUSY;
266
goto out;
267
}
268
if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {
269
NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned");
270
err = -EBUSY;
271
goto out;
272
}
273
274
linecard_type = devlink_linecard_type_lookup(linecard, type);
275
if (!linecard_type) {
276
NL_SET_ERR_MSG(extack, "Unsupported line card type provided");
277
err = -EINVAL;
278
goto out;
279
}
280
281
if (linecard->state != DEVLINK_LINECARD_STATE_UNPROVISIONED &&
282
linecard->state != DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {
283
NL_SET_ERR_MSG(extack, "Line card already provisioned");
284
err = -EBUSY;
285
/* Check if the line card is provisioned in the same
286
* way the user asks. In case it is, make the operation
287
* to return success.
288
*/
289
if (ops->same_provision &&
290
ops->same_provision(linecard, linecard->priv,
291
linecard_type->type,
292
linecard_type->priv))
293
err = 0;
294
goto out;
295
}
296
297
linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING;
298
linecard->type = linecard_type->type;
299
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
300
mutex_unlock(&linecard->state_lock);
301
err = ops->provision(linecard, linecard->priv, linecard_type->type,
302
linecard_type->priv, extack);
303
if (err) {
304
/* Provisioning failed. Assume the linecard is unprovisioned
305
* for future operations.
306
*/
307
mutex_lock(&linecard->state_lock);
308
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
309
linecard->type = NULL;
310
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
311
mutex_unlock(&linecard->state_lock);
312
}
313
return err;
314
315
out:
316
mutex_unlock(&linecard->state_lock);
317
return err;
318
}
319
320
static int devlink_linecard_type_unset(struct devlink_linecard *linecard,
321
struct netlink_ext_ack *extack)
322
{
323
int err;
324
325
mutex_lock(&linecard->state_lock);
326
if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {
327
NL_SET_ERR_MSG(extack, "Line card is currently being provisioned");
328
err = -EBUSY;
329
goto out;
330
}
331
if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {
332
NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned");
333
err = -EBUSY;
334
goto out;
335
}
336
if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {
337
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
338
linecard->type = NULL;
339
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
340
err = 0;
341
goto out;
342
}
343
344
if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONED) {
345
NL_SET_ERR_MSG(extack, "Line card is not provisioned");
346
err = 0;
347
goto out;
348
}
349
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONING;
350
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
351
mutex_unlock(&linecard->state_lock);
352
err = linecard->ops->unprovision(linecard, linecard->priv,
353
extack);
354
if (err) {
355
/* Unprovisioning failed. Assume the linecard is unprovisioned
356
* for future operations.
357
*/
358
mutex_lock(&linecard->state_lock);
359
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
360
linecard->type = NULL;
361
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
362
mutex_unlock(&linecard->state_lock);
363
}
364
return err;
365
366
out:
367
mutex_unlock(&linecard->state_lock);
368
return err;
369
}
370
371
int devlink_nl_linecard_set_doit(struct sk_buff *skb, struct genl_info *info)
372
{
373
struct netlink_ext_ack *extack = info->extack;
374
struct devlink *devlink = info->user_ptr[0];
375
struct devlink_linecard *linecard;
376
int err;
377
378
linecard = devlink_linecard_get_from_info(devlink, info);
379
if (IS_ERR(linecard))
380
return PTR_ERR(linecard);
381
382
if (info->attrs[DEVLINK_ATTR_LINECARD_TYPE]) {
383
const char *type;
384
385
type = nla_data(info->attrs[DEVLINK_ATTR_LINECARD_TYPE]);
386
if (strcmp(type, "")) {
387
err = devlink_linecard_type_set(linecard, type, extack);
388
if (err)
389
return err;
390
} else {
391
err = devlink_linecard_type_unset(linecard, extack);
392
if (err)
393
return err;
394
}
395
}
396
397
return 0;
398
}
399
400
static int devlink_linecard_types_init(struct devlink_linecard *linecard)
401
{
402
struct devlink_linecard_type *linecard_type;
403
unsigned int count;
404
int i;
405
406
count = linecard->ops->types_count(linecard, linecard->priv);
407
linecard->types = kmalloc_array(count, sizeof(*linecard_type),
408
GFP_KERNEL);
409
if (!linecard->types)
410
return -ENOMEM;
411
linecard->types_count = count;
412
413
for (i = 0; i < count; i++) {
414
linecard_type = &linecard->types[i];
415
linecard->ops->types_get(linecard, linecard->priv, i,
416
&linecard_type->type,
417
&linecard_type->priv);
418
}
419
return 0;
420
}
421
422
static void devlink_linecard_types_fini(struct devlink_linecard *linecard)
423
{
424
kfree(linecard->types);
425
}
426
427
/**
428
* devl_linecard_create - Create devlink linecard
429
*
430
* @devlink: devlink
431
* @linecard_index: driver-specific numerical identifier of the linecard
432
* @ops: linecards ops
433
* @priv: user priv pointer
434
*
435
* Create devlink linecard instance with provided linecard index.
436
* Caller can use any indexing, even hw-related one.
437
*
438
* Return: Line card structure or an ERR_PTR() encoded error code.
439
*/
440
struct devlink_linecard *
441
devl_linecard_create(struct devlink *devlink, unsigned int linecard_index,
442
const struct devlink_linecard_ops *ops, void *priv)
443
{
444
struct devlink_linecard *linecard;
445
int err;
446
447
if (WARN_ON(!ops || !ops->provision || !ops->unprovision ||
448
!ops->types_count || !ops->types_get))
449
return ERR_PTR(-EINVAL);
450
451
if (devlink_linecard_index_exists(devlink, linecard_index))
452
return ERR_PTR(-EEXIST);
453
454
linecard = kzalloc(sizeof(*linecard), GFP_KERNEL);
455
if (!linecard)
456
return ERR_PTR(-ENOMEM);
457
458
linecard->devlink = devlink;
459
linecard->index = linecard_index;
460
linecard->ops = ops;
461
linecard->priv = priv;
462
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
463
mutex_init(&linecard->state_lock);
464
465
err = devlink_linecard_types_init(linecard);
466
if (err) {
467
mutex_destroy(&linecard->state_lock);
468
kfree(linecard);
469
return ERR_PTR(err);
470
}
471
472
list_add_tail(&linecard->list, &devlink->linecard_list);
473
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
474
return linecard;
475
}
476
EXPORT_SYMBOL_GPL(devl_linecard_create);
477
478
/**
479
* devl_linecard_destroy - Destroy devlink linecard
480
*
481
* @linecard: devlink linecard
482
*/
483
void devl_linecard_destroy(struct devlink_linecard *linecard)
484
{
485
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);
486
list_del(&linecard->list);
487
devlink_linecard_types_fini(linecard);
488
mutex_destroy(&linecard->state_lock);
489
kfree(linecard);
490
}
491
EXPORT_SYMBOL_GPL(devl_linecard_destroy);
492
493
/**
494
* devlink_linecard_provision_set - Set provisioning on linecard
495
*
496
* @linecard: devlink linecard
497
* @type: linecard type
498
*
499
* This is either called directly from the provision() op call or
500
* as a result of the provision() op call asynchronously.
501
*/
502
void devlink_linecard_provision_set(struct devlink_linecard *linecard,
503
const char *type)
504
{
505
mutex_lock(&linecard->state_lock);
506
WARN_ON(linecard->type && strcmp(linecard->type, type));
507
linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
508
linecard->type = type;
509
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
510
mutex_unlock(&linecard->state_lock);
511
}
512
EXPORT_SYMBOL_GPL(devlink_linecard_provision_set);
513
514
/**
515
* devlink_linecard_provision_clear - Clear provisioning on linecard
516
*
517
* @linecard: devlink linecard
518
*
519
* This is either called directly from the unprovision() op call or
520
* as a result of the unprovision() op call asynchronously.
521
*/
522
void devlink_linecard_provision_clear(struct devlink_linecard *linecard)
523
{
524
mutex_lock(&linecard->state_lock);
525
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
526
linecard->type = NULL;
527
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
528
mutex_unlock(&linecard->state_lock);
529
}
530
EXPORT_SYMBOL_GPL(devlink_linecard_provision_clear);
531
532
/**
533
* devlink_linecard_provision_fail - Fail provisioning on linecard
534
*
535
* @linecard: devlink linecard
536
*
537
* This is either called directly from the provision() op call or
538
* as a result of the provision() op call asynchronously.
539
*/
540
void devlink_linecard_provision_fail(struct devlink_linecard *linecard)
541
{
542
mutex_lock(&linecard->state_lock);
543
linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED;
544
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
545
mutex_unlock(&linecard->state_lock);
546
}
547
EXPORT_SYMBOL_GPL(devlink_linecard_provision_fail);
548
549
/**
550
* devlink_linecard_activate - Set linecard active
551
*
552
* @linecard: devlink linecard
553
*/
554
void devlink_linecard_activate(struct devlink_linecard *linecard)
555
{
556
mutex_lock(&linecard->state_lock);
557
WARN_ON(linecard->state != DEVLINK_LINECARD_STATE_PROVISIONED);
558
linecard->state = DEVLINK_LINECARD_STATE_ACTIVE;
559
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
560
mutex_unlock(&linecard->state_lock);
561
}
562
EXPORT_SYMBOL_GPL(devlink_linecard_activate);
563
564
/**
565
* devlink_linecard_deactivate - Set linecard inactive
566
*
567
* @linecard: devlink linecard
568
*/
569
void devlink_linecard_deactivate(struct devlink_linecard *linecard)
570
{
571
mutex_lock(&linecard->state_lock);
572
switch (linecard->state) {
573
case DEVLINK_LINECARD_STATE_ACTIVE:
574
linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
575
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
576
break;
577
case DEVLINK_LINECARD_STATE_UNPROVISIONING:
578
/* Line card is being deactivated as part
579
* of unprovisioning flow.
580
*/
581
break;
582
default:
583
WARN_ON(1);
584
break;
585
}
586
mutex_unlock(&linecard->state_lock);
587
}
588
EXPORT_SYMBOL_GPL(devlink_linecard_deactivate);
589
590
static void devlink_linecard_rel_notify_cb(struct devlink *devlink,
591
u32 linecard_index)
592
{
593
struct devlink_linecard *linecard;
594
595
linecard = devlink_linecard_get_by_index(devlink, linecard_index);
596
if (!linecard)
597
return;
598
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
599
}
600
601
static void devlink_linecard_rel_cleanup_cb(struct devlink *devlink,
602
u32 linecard_index, u32 rel_index)
603
{
604
struct devlink_linecard *linecard;
605
606
linecard = devlink_linecard_get_by_index(devlink, linecard_index);
607
if (linecard && linecard->rel_index == rel_index)
608
linecard->rel_index = 0;
609
}
610
611
/**
612
* devlink_linecard_nested_dl_set - Attach/detach nested devlink
613
* instance to linecard.
614
*
615
* @linecard: devlink linecard
616
* @nested_devlink: devlink instance to attach or NULL to detach
617
*/
618
int devlink_linecard_nested_dl_set(struct devlink_linecard *linecard,
619
struct devlink *nested_devlink)
620
{
621
return devlink_rel_nested_in_add(&linecard->rel_index,
622
linecard->devlink->index,
623
linecard->index,
624
devlink_linecard_rel_notify_cb,
625
devlink_linecard_rel_cleanup_cb,
626
nested_devlink);
627
}
628
EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set);
629
630