Merge tag 'for-6.6-rc5-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[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 static struct devlink_linecard *
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
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 *
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 *
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
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
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
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
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
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
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
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
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 *
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
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
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
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
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
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 *
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  */
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  */
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  */
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  */
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  */
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 /**
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  */
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);