Merge tag 'sh-for-v6.7-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/glaubit...
[linux-2.6-microblaze.git] / net / devlink / linecard.c
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(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 (!xa_get_mark(&devlinks, 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, 0, 0, 0,
147                                        NULL);
148         if (err) {
149                 nlmsg_free(msg);
150                 return;
151         }
152
153         genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
154                                 msg, 0, 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, 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, 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(linecard))
182                 return PTR_ERR(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                                        DEVLINK_CMD_LINECARD_NEW,
191                                        info->snd_portid, info->snd_seq, 0,
192                                        info->extack);
193         mutex_unlock(&linecard->state_lock);
194         if (err) {
195                 nlmsg_free(msg);
196                 return err;
197         }
198
199         return genlmsg_reply(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                                                DEVLINK_CMD_LINECARD_NEW,
220                                                NETLINK_CB(cb->skb).portid,
221                                                cb->nlh->nlmsg_seq, flags,
222                                                cb->extack);
223                 mutex_unlock(&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(skb, cb, 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, DEVLINK_CMD_LINECARD_NEW);
301         mutex_unlock(&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, DEVLINK_CMD_LINECARD_NEW);
312                 mutex_unlock(&linecard->state_lock);
313         }
314         return err;
315
316 out:
317         mutex_unlock(&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, 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, DEVLINK_CMD_LINECARD_NEW);
352         mutex_unlock(&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, DEVLINK_CMD_LINECARD_NEW);
363                 mutex_unlock(&linecard->state_lock);
364         }
365         return err;
366
367 out:
368         mutex_unlock(&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(linecard))
381                 return PTR_ERR(linecard);
382
383         if (info->attrs[DEVLINK_ATTR_LINECARD_TYPE]) {
384                 const char *type;
385
386                 type = nla_data(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(count, 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(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(-EINVAL);
451
452         if (devlink_linecard_index_exists(devlink, linecard_index))
453                 return ERR_PTR(-EEXIST);
454
455         linecard = kzalloc(sizeof(*linecard), GFP_KERNEL);
456         if (!linecard)
457                 return ERR_PTR(-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(&linecard->state_lock);
469                 kfree(linecard);
470                 return ERR_PTR(err);
471         }
472
473         list_add_tail(&linecard->list, &devlink->linecard_list);
474         devlink_linecard_notify(linecard, 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, DEVLINK_CMD_LINECARD_DEL);
487         list_del(&linecard->list);
488         devlink_linecard_types_fini(linecard);
489         mutex_destroy(&linecard->state_lock);
490         kfree(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, DEVLINK_CMD_LINECARD_NEW);
511         mutex_unlock(&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, DEVLINK_CMD_LINECARD_NEW);
529         mutex_unlock(&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, 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  */
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  */
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 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, 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(&linecard->rel_index,
623                                          linecard->devlink->index,
624                                          linecard->index,
625                                          devlink_linecard_rel_notify_cb,
626                                          devlink_linecard_rel_cleanup_cb,
627                                          nested_devlink);
628 }
629 EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set);