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 | |
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(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 | |
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, attrs: 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(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 | |
124 | nla_put_failure: |
125 | genlmsg_cancel(skb: 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 (!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 | |
157 | void 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 | |
165 | void 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 | |
173 | int 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 | |
202 | static 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 | |
234 | int 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 | |
240 | static struct devlink_linecard_type * |
241 | devlink_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 | |
255 | static 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 | |
316 | out: |
317 | mutex_unlock(lock: &linecard->state_lock); |
318 | return err; |
319 | } |
320 | |
321 | static 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 | |
367 | out: |
368 | mutex_unlock(lock: &linecard->state_lock); |
369 | return err; |
370 | } |
371 | |
372 | int 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 | |
401 | static 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 | |
423 | static 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 | */ |
441 | struct devlink_linecard * |
442 | devl_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 | } |
477 | EXPORT_SYMBOL_GPL(devl_linecard_create); |
478 | |
479 | /** |
480 | * devl_linecard_destroy - Destroy devlink linecard |
481 | * |
482 | * @linecard: devlink linecard |
483 | */ |
484 | void 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 | } |
492 | EXPORT_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 | */ |
503 | void 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 | } |
513 | EXPORT_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 | */ |
523 | void 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 | } |
531 | EXPORT_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 | */ |
541 | void 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 | } |
548 | EXPORT_SYMBOL_GPL(devlink_linecard_provision_fail); |
549 | |
550 | /** |
551 | * devlink_linecard_activate - Set linecard active |
552 | * |
553 | * @linecard: devlink linecard |
554 | */ |
555 | void 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 | } |
563 | EXPORT_SYMBOL_GPL(devlink_linecard_activate); |
564 | |
565 | /** |
566 | * devlink_linecard_deactivate - Set linecard inactive |
567 | * |
568 | * @linecard: devlink linecard |
569 | */ |
570 | void 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 | } |
589 | EXPORT_SYMBOL_GPL(devlink_linecard_deactivate); |
590 | |
591 | static 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 | |
602 | static 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 | */ |
619 | int 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 | } |
629 | EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set); |
630 | |