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

source code of linux/net/devlink/linecard.c