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 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
devlink_linecard_index(struct devlink_linecard * linecard)23 unsigned int devlink_linecard_index(struct devlink_linecard *linecard)
24 {
25 return linecard->index;
26 }
27
28 static struct devlink_linecard *
devlink_linecard_get_by_index(struct devlink * devlink,unsigned int linecard_index)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
devlink_linecard_index_exists(struct devlink * devlink,unsigned int linecard_index)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 *
devlink_linecard_get_from_attrs(struct devlink * devlink,struct nlattr ** attrs)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 *
devlink_linecard_get_from_info(struct devlink * devlink,struct genl_info * info)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
devlink_nl_linecard_fill(struct sk_buff * msg,struct devlink * devlink,struct devlink_linecard * linecard,enum devlink_command cmd,u32 portid,u32 seq,int flags,struct netlink_ext_ack * extack)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
devlink_linecard_notify(struct devlink_linecard * linecard,enum devlink_command cmd)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
devlink_linecards_notify_register(struct devlink * devlink)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
devlink_linecards_notify_unregister(struct devlink * devlink)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
devlink_nl_linecard_get_doit(struct sk_buff * skb,struct genl_info * info)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
devlink_nl_linecard_get_dump_one(struct sk_buff * msg,struct devlink * devlink,struct netlink_callback * cb,int flags)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
devlink_nl_linecard_get_dumpit(struct sk_buff * skb,struct netlink_callback * cb)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 *
devlink_linecard_type_lookup(struct devlink_linecard * linecard,const char * 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
devlink_linecard_type_set(struct devlink_linecard * linecard,const char * type,struct netlink_ext_ack * extack)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
devlink_linecard_type_unset(struct devlink_linecard * linecard,struct netlink_ext_ack * extack)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
devlink_nl_linecard_set_doit(struct sk_buff * skb,struct genl_info * info)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
devlink_linecard_types_init(struct devlink_linecard * linecard)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
devlink_linecard_types_fini(struct devlink_linecard * linecard)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 *
devl_linecard_create(struct devlink * devlink,unsigned int linecard_index,const struct devlink_linecard_ops * ops,void * priv)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 */
devl_linecard_destroy(struct devlink_linecard * linecard)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 */
devlink_linecard_provision_set(struct devlink_linecard * linecard,const char * type)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 */
devlink_linecard_provision_clear(struct devlink_linecard * linecard)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 */
devlink_linecard_provision_fail(struct devlink_linecard * linecard)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 */
devlink_linecard_activate(struct devlink_linecard * linecard)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 */
devlink_linecard_deactivate(struct devlink_linecard * linecard)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
devlink_linecard_rel_notify_cb(struct devlink * devlink,u32 linecard_index)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
devlink_linecard_rel_cleanup_cb(struct devlink * devlink,u32 linecard_index,u32 rel_index)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 */
devlink_linecard_nested_dl_set(struct devlink_linecard * linecard,struct devlink * nested_devlink)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