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