Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[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_cmd_linecard_set_doit(struct sk_buff *skb,
373                                      struct genl_info *info)
374 {
375         struct netlink_ext_ack *extack = info->extack;
376         struct devlink *devlink = info->user_ptr[0];
377         struct devlink_linecard *linecard;
378         int err;
379
380         linecard = devlink_linecard_get_from_info(devlink, info);
381         if (IS_ERR(linecard))
382                 return PTR_ERR(linecard);
383
384         if (info->attrs[DEVLINK_ATTR_LINECARD_TYPE]) {
385                 const char *type;
386
387                 type = nla_data(info->attrs[DEVLINK_ATTR_LINECARD_TYPE]);
388                 if (strcmp(type, "")) {
389                         err = devlink_linecard_type_set(linecard, type, extack);
390                         if (err)
391                                 return err;
392                 } else {
393                         err = devlink_linecard_type_unset(linecard, extack);
394                         if (err)
395                                 return err;
396                 }
397         }
398
399         return 0;
400 }
401
402 static int devlink_linecard_types_init(struct devlink_linecard *linecard)
403 {
404         struct devlink_linecard_type *linecard_type;
405         unsigned int count;
406         int i;
407
408         count = linecard->ops->types_count(linecard, linecard->priv);
409         linecard->types = kmalloc_array(count, sizeof(*linecard_type),
410                                         GFP_KERNEL);
411         if (!linecard->types)
412                 return -ENOMEM;
413         linecard->types_count = count;
414
415         for (i = 0; i < count; i++) {
416                 linecard_type = &linecard->types[i];
417                 linecard->ops->types_get(linecard, linecard->priv, i,
418                                          &linecard_type->type,
419                                          &linecard_type->priv);
420         }
421         return 0;
422 }
423
424 static void devlink_linecard_types_fini(struct devlink_linecard *linecard)
425 {
426         kfree(linecard->types);
427 }
428
429 /**
430  *      devl_linecard_create - Create devlink linecard
431  *
432  *      @devlink: devlink
433  *      @linecard_index: driver-specific numerical identifier of the linecard
434  *      @ops: linecards ops
435  *      @priv: user priv pointer
436  *
437  *      Create devlink linecard instance with provided linecard index.
438  *      Caller can use any indexing, even hw-related one.
439  *
440  *      Return: Line card structure or an ERR_PTR() encoded error code.
441  */
442 struct devlink_linecard *
443 devl_linecard_create(struct devlink *devlink, unsigned int linecard_index,
444                      const struct devlink_linecard_ops *ops, void *priv)
445 {
446         struct devlink_linecard *linecard;
447         int err;
448
449         if (WARN_ON(!ops || !ops->provision || !ops->unprovision ||
450                     !ops->types_count || !ops->types_get))
451                 return ERR_PTR(-EINVAL);
452
453         if (devlink_linecard_index_exists(devlink, linecard_index))
454                 return ERR_PTR(-EEXIST);
455
456         linecard = kzalloc(sizeof(*linecard), GFP_KERNEL);
457         if (!linecard)
458                 return ERR_PTR(-ENOMEM);
459
460         linecard->devlink = devlink;
461         linecard->index = linecard_index;
462         linecard->ops = ops;
463         linecard->priv = priv;
464         linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
465         mutex_init(&linecard->state_lock);
466
467         err = devlink_linecard_types_init(linecard);
468         if (err) {
469                 mutex_destroy(&linecard->state_lock);
470                 kfree(linecard);
471                 return ERR_PTR(err);
472         }
473
474         list_add_tail(&linecard->list, &devlink->linecard_list);
475         devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
476         return linecard;
477 }
478 EXPORT_SYMBOL_GPL(devl_linecard_create);
479
480 /**
481  *      devl_linecard_destroy - Destroy devlink linecard
482  *
483  *      @linecard: devlink linecard
484  */
485 void devl_linecard_destroy(struct devlink_linecard *linecard)
486 {
487         devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);
488         list_del(&linecard->list);
489         devlink_linecard_types_fini(linecard);
490         mutex_destroy(&linecard->state_lock);
491         kfree(linecard);
492 }
493 EXPORT_SYMBOL_GPL(devl_linecard_destroy);
494
495 /**
496  *      devlink_linecard_provision_set - Set provisioning on linecard
497  *
498  *      @linecard: devlink linecard
499  *      @type: linecard type
500  *
501  *      This is either called directly from the provision() op call or
502  *      as a result of the provision() op call asynchronously.
503  */
504 void devlink_linecard_provision_set(struct devlink_linecard *linecard,
505                                     const char *type)
506 {
507         mutex_lock(&linecard->state_lock);
508         WARN_ON(linecard->type && strcmp(linecard->type, type));
509         linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
510         linecard->type = type;
511         devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
512         mutex_unlock(&linecard->state_lock);
513 }
514 EXPORT_SYMBOL_GPL(devlink_linecard_provision_set);
515
516 /**
517  *      devlink_linecard_provision_clear - Clear provisioning on linecard
518  *
519  *      @linecard: devlink linecard
520  *
521  *      This is either called directly from the unprovision() op call or
522  *      as a result of the unprovision() op call asynchronously.
523  */
524 void devlink_linecard_provision_clear(struct devlink_linecard *linecard)
525 {
526         mutex_lock(&linecard->state_lock);
527         linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
528         linecard->type = NULL;
529         devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
530         mutex_unlock(&linecard->state_lock);
531 }
532 EXPORT_SYMBOL_GPL(devlink_linecard_provision_clear);
533
534 /**
535  *      devlink_linecard_provision_fail - Fail provisioning on linecard
536  *
537  *      @linecard: devlink linecard
538  *
539  *      This is either called directly from the provision() op call or
540  *      as a result of the provision() op call asynchronously.
541  */
542 void devlink_linecard_provision_fail(struct devlink_linecard *linecard)
543 {
544         mutex_lock(&linecard->state_lock);
545         linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED;
546         devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
547         mutex_unlock(&linecard->state_lock);
548 }
549 EXPORT_SYMBOL_GPL(devlink_linecard_provision_fail);
550
551 /**
552  *      devlink_linecard_activate - Set linecard active
553  *
554  *      @linecard: devlink linecard
555  */
556 void devlink_linecard_activate(struct devlink_linecard *linecard)
557 {
558         mutex_lock(&linecard->state_lock);
559         WARN_ON(linecard->state != DEVLINK_LINECARD_STATE_PROVISIONED);
560         linecard->state = DEVLINK_LINECARD_STATE_ACTIVE;
561         devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
562         mutex_unlock(&linecard->state_lock);
563 }
564 EXPORT_SYMBOL_GPL(devlink_linecard_activate);
565
566 /**
567  *      devlink_linecard_deactivate - Set linecard inactive
568  *
569  *      @linecard: devlink linecard
570  */
571 void devlink_linecard_deactivate(struct devlink_linecard *linecard)
572 {
573         mutex_lock(&linecard->state_lock);
574         switch (linecard->state) {
575         case DEVLINK_LINECARD_STATE_ACTIVE:
576                 linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
577                 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
578                 break;
579         case DEVLINK_LINECARD_STATE_UNPROVISIONING:
580                 /* Line card is being deactivated as part
581                  * of unprovisioning flow.
582                  */
583                 break;
584         default:
585                 WARN_ON(1);
586                 break;
587         }
588         mutex_unlock(&linecard->state_lock);
589 }
590 EXPORT_SYMBOL_GPL(devlink_linecard_deactivate);
591
592 static void devlink_linecard_rel_notify_cb(struct devlink *devlink,
593                                            u32 linecard_index)
594 {
595         struct devlink_linecard *linecard;
596
597         linecard = devlink_linecard_get_by_index(devlink, linecard_index);
598         if (!linecard)
599                 return;
600         devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
601 }
602
603 static void devlink_linecard_rel_cleanup_cb(struct devlink *devlink,
604                                             u32 linecard_index, u32 rel_index)
605 {
606         struct devlink_linecard *linecard;
607
608         linecard = devlink_linecard_get_by_index(devlink, linecard_index);
609         if (linecard && linecard->rel_index == rel_index)
610                 linecard->rel_index = 0;
611 }
612
613 /**
614  *      devlink_linecard_nested_dl_set - Attach/detach nested devlink
615  *                                       instance to linecard.
616  *
617  *      @linecard: devlink linecard
618  *      @nested_devlink: devlink instance to attach or NULL to detach
619  */
620 int devlink_linecard_nested_dl_set(struct devlink_linecard *linecard,
621                                    struct devlink *nested_devlink)
622 {
623         return devlink_rel_nested_in_add(&linecard->rel_index,
624                                          linecard->devlink->index,
625                                          linecard->index,
626                                          devlink_linecard_rel_notify_cb,
627                                          devlink_linecard_rel_cleanup_cb,
628                                          nested_devlink);
629 }
630 EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set);