b9b3e68d9043095eebbed128328b047d44b81b29
[platform/kernel/linux-rpi.git] / net / devlink / health.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 <net/genetlink.h>
8 #include <net/sock.h>
9 #include <trace/events/devlink.h>
10 #include "devl_internal.h"
11
12 struct devlink_fmsg_item {
13         struct list_head list;
14         int attrtype;
15         u8 nla_type;
16         u16 len;
17         int value[];
18 };
19
20 struct devlink_fmsg {
21         struct list_head item_list;
22         bool putting_binary; /* This flag forces enclosing of binary data
23                               * in an array brackets. It forces using
24                               * of designated API:
25                               * devlink_fmsg_binary_pair_nest_start()
26                               * devlink_fmsg_binary_pair_nest_end()
27                               */
28 };
29
30 static struct devlink_fmsg *devlink_fmsg_alloc(void)
31 {
32         struct devlink_fmsg *fmsg;
33
34         fmsg = kzalloc(sizeof(*fmsg), GFP_KERNEL);
35         if (!fmsg)
36                 return NULL;
37
38         INIT_LIST_HEAD(&fmsg->item_list);
39
40         return fmsg;
41 }
42
43 static void devlink_fmsg_free(struct devlink_fmsg *fmsg)
44 {
45         struct devlink_fmsg_item *item, *tmp;
46
47         list_for_each_entry_safe(item, tmp, &fmsg->item_list, list) {
48                 list_del(&item->list);
49                 kfree(item);
50         }
51         kfree(fmsg);
52 }
53
54 struct devlink_health_reporter {
55         struct list_head list;
56         void *priv;
57         const struct devlink_health_reporter_ops *ops;
58         struct devlink *devlink;
59         struct devlink_port *devlink_port;
60         struct devlink_fmsg *dump_fmsg;
61         struct mutex dump_lock; /* lock parallel read/write from dump buffers */
62         u64 graceful_period;
63         bool auto_recover;
64         bool auto_dump;
65         u8 health_state;
66         u64 dump_ts;
67         u64 dump_real_ts;
68         u64 error_count;
69         u64 recovery_count;
70         u64 last_recovery_ts;
71 };
72
73 void *
74 devlink_health_reporter_priv(struct devlink_health_reporter *reporter)
75 {
76         return reporter->priv;
77 }
78 EXPORT_SYMBOL_GPL(devlink_health_reporter_priv);
79
80 static struct devlink_health_reporter *
81 __devlink_health_reporter_find_by_name(struct list_head *reporter_list,
82                                        const char *reporter_name)
83 {
84         struct devlink_health_reporter *reporter;
85
86         list_for_each_entry(reporter, reporter_list, list)
87                 if (!strcmp(reporter->ops->name, reporter_name))
88                         return reporter;
89         return NULL;
90 }
91
92 static struct devlink_health_reporter *
93 devlink_health_reporter_find_by_name(struct devlink *devlink,
94                                      const char *reporter_name)
95 {
96         return __devlink_health_reporter_find_by_name(&devlink->reporter_list,
97                                                       reporter_name);
98 }
99
100 static struct devlink_health_reporter *
101 devlink_port_health_reporter_find_by_name(struct devlink_port *devlink_port,
102                                           const char *reporter_name)
103 {
104         return __devlink_health_reporter_find_by_name(&devlink_port->reporter_list,
105                                                       reporter_name);
106 }
107
108 static struct devlink_health_reporter *
109 __devlink_health_reporter_create(struct devlink *devlink,
110                                  const struct devlink_health_reporter_ops *ops,
111                                  u64 graceful_period, void *priv)
112 {
113         struct devlink_health_reporter *reporter;
114
115         if (WARN_ON(graceful_period && !ops->recover))
116                 return ERR_PTR(-EINVAL);
117
118         reporter = kzalloc(sizeof(*reporter), GFP_KERNEL);
119         if (!reporter)
120                 return ERR_PTR(-ENOMEM);
121
122         reporter->priv = priv;
123         reporter->ops = ops;
124         reporter->devlink = devlink;
125         reporter->graceful_period = graceful_period;
126         reporter->auto_recover = !!ops->recover;
127         reporter->auto_dump = !!ops->dump;
128         mutex_init(&reporter->dump_lock);
129         return reporter;
130 }
131
132 /**
133  * devl_port_health_reporter_create() - create devlink health reporter for
134  *                                      specified port instance
135  *
136  * @port: devlink_port to which health reports will relate
137  * @ops: devlink health reporter ops
138  * @graceful_period: min time (in msec) between recovery attempts
139  * @priv: driver priv pointer
140  */
141 struct devlink_health_reporter *
142 devl_port_health_reporter_create(struct devlink_port *port,
143                                  const struct devlink_health_reporter_ops *ops,
144                                  u64 graceful_period, void *priv)
145 {
146         struct devlink_health_reporter *reporter;
147
148         devl_assert_locked(port->devlink);
149
150         if (__devlink_health_reporter_find_by_name(&port->reporter_list,
151                                                    ops->name))
152                 return ERR_PTR(-EEXIST);
153
154         reporter = __devlink_health_reporter_create(port->devlink, ops,
155                                                     graceful_period, priv);
156         if (IS_ERR(reporter))
157                 return reporter;
158
159         reporter->devlink_port = port;
160         list_add_tail(&reporter->list, &port->reporter_list);
161         return reporter;
162 }
163 EXPORT_SYMBOL_GPL(devl_port_health_reporter_create);
164
165 struct devlink_health_reporter *
166 devlink_port_health_reporter_create(struct devlink_port *port,
167                                     const struct devlink_health_reporter_ops *ops,
168                                     u64 graceful_period, void *priv)
169 {
170         struct devlink_health_reporter *reporter;
171         struct devlink *devlink = port->devlink;
172
173         devl_lock(devlink);
174         reporter = devl_port_health_reporter_create(port, ops,
175                                                     graceful_period, priv);
176         devl_unlock(devlink);
177         return reporter;
178 }
179 EXPORT_SYMBOL_GPL(devlink_port_health_reporter_create);
180
181 /**
182  * devl_health_reporter_create - create devlink health reporter
183  *
184  * @devlink: devlink instance which the health reports will relate
185  * @ops: devlink health reporter ops
186  * @graceful_period: min time (in msec) between recovery attempts
187  * @priv: driver priv pointer
188  */
189 struct devlink_health_reporter *
190 devl_health_reporter_create(struct devlink *devlink,
191                             const struct devlink_health_reporter_ops *ops,
192                             u64 graceful_period, void *priv)
193 {
194         struct devlink_health_reporter *reporter;
195
196         devl_assert_locked(devlink);
197
198         if (devlink_health_reporter_find_by_name(devlink, ops->name))
199                 return ERR_PTR(-EEXIST);
200
201         reporter = __devlink_health_reporter_create(devlink, ops,
202                                                     graceful_period, priv);
203         if (IS_ERR(reporter))
204                 return reporter;
205
206         list_add_tail(&reporter->list, &devlink->reporter_list);
207         return reporter;
208 }
209 EXPORT_SYMBOL_GPL(devl_health_reporter_create);
210
211 struct devlink_health_reporter *
212 devlink_health_reporter_create(struct devlink *devlink,
213                                const struct devlink_health_reporter_ops *ops,
214                                u64 graceful_period, void *priv)
215 {
216         struct devlink_health_reporter *reporter;
217
218         devl_lock(devlink);
219         reporter = devl_health_reporter_create(devlink, ops,
220                                                graceful_period, priv);
221         devl_unlock(devlink);
222         return reporter;
223 }
224 EXPORT_SYMBOL_GPL(devlink_health_reporter_create);
225
226 static void
227 devlink_health_reporter_free(struct devlink_health_reporter *reporter)
228 {
229         mutex_destroy(&reporter->dump_lock);
230         if (reporter->dump_fmsg)
231                 devlink_fmsg_free(reporter->dump_fmsg);
232         kfree(reporter);
233 }
234
235 /**
236  * devl_health_reporter_destroy() - destroy devlink health reporter
237  *
238  * @reporter: devlink health reporter to destroy
239  */
240 void
241 devl_health_reporter_destroy(struct devlink_health_reporter *reporter)
242 {
243         devl_assert_locked(reporter->devlink);
244
245         list_del(&reporter->list);
246         devlink_health_reporter_free(reporter);
247 }
248 EXPORT_SYMBOL_GPL(devl_health_reporter_destroy);
249
250 void
251 devlink_health_reporter_destroy(struct devlink_health_reporter *reporter)
252 {
253         struct devlink *devlink = reporter->devlink;
254
255         devl_lock(devlink);
256         devl_health_reporter_destroy(reporter);
257         devl_unlock(devlink);
258 }
259 EXPORT_SYMBOL_GPL(devlink_health_reporter_destroy);
260
261 static int
262 devlink_nl_health_reporter_fill(struct sk_buff *msg,
263                                 struct devlink_health_reporter *reporter,
264                                 enum devlink_command cmd, u32 portid,
265                                 u32 seq, int flags)
266 {
267         struct devlink *devlink = reporter->devlink;
268         struct nlattr *reporter_attr;
269         void *hdr;
270
271         hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
272         if (!hdr)
273                 return -EMSGSIZE;
274
275         if (devlink_nl_put_handle(msg, devlink))
276                 goto genlmsg_cancel;
277
278         if (reporter->devlink_port) {
279                 if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, reporter->devlink_port->index))
280                         goto genlmsg_cancel;
281         }
282         reporter_attr = nla_nest_start_noflag(msg,
283                                               DEVLINK_ATTR_HEALTH_REPORTER);
284         if (!reporter_attr)
285                 goto genlmsg_cancel;
286         if (nla_put_string(msg, DEVLINK_ATTR_HEALTH_REPORTER_NAME,
287                            reporter->ops->name))
288                 goto reporter_nest_cancel;
289         if (nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_STATE,
290                        reporter->health_state))
291                 goto reporter_nest_cancel;
292         if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT,
293                               reporter->error_count, DEVLINK_ATTR_PAD))
294                 goto reporter_nest_cancel;
295         if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT,
296                               reporter->recovery_count, DEVLINK_ATTR_PAD))
297                 goto reporter_nest_cancel;
298         if (reporter->ops->recover &&
299             nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD,
300                               reporter->graceful_period,
301                               DEVLINK_ATTR_PAD))
302                 goto reporter_nest_cancel;
303         if (reporter->ops->recover &&
304             nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
305                        reporter->auto_recover))
306                 goto reporter_nest_cancel;
307         if (reporter->dump_fmsg &&
308             nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS,
309                               jiffies_to_msecs(reporter->dump_ts),
310                               DEVLINK_ATTR_PAD))
311                 goto reporter_nest_cancel;
312         if (reporter->dump_fmsg &&
313             nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS,
314                               reporter->dump_real_ts, DEVLINK_ATTR_PAD))
315                 goto reporter_nest_cancel;
316         if (reporter->ops->dump &&
317             nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP,
318                        reporter->auto_dump))
319                 goto reporter_nest_cancel;
320
321         nla_nest_end(msg, reporter_attr);
322         genlmsg_end(msg, hdr);
323         return 0;
324
325 reporter_nest_cancel:
326         nla_nest_cancel(msg, reporter_attr);
327 genlmsg_cancel:
328         genlmsg_cancel(msg, hdr);
329         return -EMSGSIZE;
330 }
331
332 static struct devlink_health_reporter *
333 devlink_health_reporter_get_from_attrs(struct devlink *devlink,
334                                        struct nlattr **attrs)
335 {
336         struct devlink_port *devlink_port;
337         char *reporter_name;
338
339         if (!attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME])
340                 return NULL;
341
342         reporter_name = nla_data(attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME]);
343         devlink_port = devlink_port_get_from_attrs(devlink, attrs);
344         if (IS_ERR(devlink_port))
345                 return devlink_health_reporter_find_by_name(devlink,
346                                                             reporter_name);
347         else
348                 return devlink_port_health_reporter_find_by_name(devlink_port,
349                                                                  reporter_name);
350 }
351
352 static struct devlink_health_reporter *
353 devlink_health_reporter_get_from_info(struct devlink *devlink,
354                                       struct genl_info *info)
355 {
356         return devlink_health_reporter_get_from_attrs(devlink, info->attrs);
357 }
358
359 int devlink_nl_health_reporter_get_doit(struct sk_buff *skb,
360                                         struct genl_info *info)
361 {
362         struct devlink *devlink = info->user_ptr[0];
363         struct devlink_health_reporter *reporter;
364         struct sk_buff *msg;
365         int err;
366
367         reporter = devlink_health_reporter_get_from_info(devlink, info);
368         if (!reporter)
369                 return -EINVAL;
370
371         msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
372         if (!msg)
373                 return -ENOMEM;
374
375         err = devlink_nl_health_reporter_fill(msg, reporter,
376                                               DEVLINK_CMD_HEALTH_REPORTER_GET,
377                                               info->snd_portid, info->snd_seq,
378                                               0);
379         if (err) {
380                 nlmsg_free(msg);
381                 return err;
382         }
383
384         return genlmsg_reply(msg, info);
385 }
386
387 static int devlink_nl_health_reporter_get_dump_one(struct sk_buff *msg,
388                                                    struct devlink *devlink,
389                                                    struct netlink_callback *cb,
390                                                    int flags)
391 {
392         struct devlink_nl_dump_state *state = devlink_dump_state(cb);
393         struct devlink_health_reporter *reporter;
394         struct devlink_port *port;
395         unsigned long port_index;
396         int idx = 0;
397         int err;
398
399         list_for_each_entry(reporter, &devlink->reporter_list, list) {
400                 if (idx < state->idx) {
401                         idx++;
402                         continue;
403                 }
404                 err = devlink_nl_health_reporter_fill(msg, reporter,
405                                                       DEVLINK_CMD_HEALTH_REPORTER_GET,
406                                                       NETLINK_CB(cb->skb).portid,
407                                                       cb->nlh->nlmsg_seq,
408                                                       flags);
409                 if (err) {
410                         state->idx = idx;
411                         return err;
412                 }
413                 idx++;
414         }
415         xa_for_each(&devlink->ports, port_index, port) {
416                 list_for_each_entry(reporter, &port->reporter_list, list) {
417                         if (idx < state->idx) {
418                                 idx++;
419                                 continue;
420                         }
421                         err = devlink_nl_health_reporter_fill(msg, reporter,
422                                                               DEVLINK_CMD_HEALTH_REPORTER_GET,
423                                                               NETLINK_CB(cb->skb).portid,
424                                                               cb->nlh->nlmsg_seq,
425                                                               flags);
426                         if (err) {
427                                 state->idx = idx;
428                                 return err;
429                         }
430                         idx++;
431                 }
432         }
433
434         return 0;
435 }
436
437 int devlink_nl_health_reporter_get_dumpit(struct sk_buff *skb,
438                                           struct netlink_callback *cb)
439 {
440         return devlink_nl_dumpit(skb, cb,
441                                  devlink_nl_health_reporter_get_dump_one);
442 }
443
444 int devlink_nl_cmd_health_reporter_set_doit(struct sk_buff *skb,
445                                             struct genl_info *info)
446 {
447         struct devlink *devlink = info->user_ptr[0];
448         struct devlink_health_reporter *reporter;
449
450         reporter = devlink_health_reporter_get_from_info(devlink, info);
451         if (!reporter)
452                 return -EINVAL;
453
454         if (!reporter->ops->recover &&
455             (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] ||
456              info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]))
457                 return -EOPNOTSUPP;
458
459         if (!reporter->ops->dump &&
460             info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
461                 return -EOPNOTSUPP;
462
463         if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD])
464                 reporter->graceful_period =
465                         nla_get_u64(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD]);
466
467         if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])
468                 reporter->auto_recover =
469                         nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]);
470
471         if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
472                 reporter->auto_dump =
473                 nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP]);
474
475         return 0;
476 }
477
478 static void devlink_recover_notify(struct devlink_health_reporter *reporter,
479                                    enum devlink_command cmd)
480 {
481         struct devlink *devlink = reporter->devlink;
482         struct sk_buff *msg;
483         int err;
484
485         WARN_ON(cmd != DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
486         ASSERT_DEVLINK_REGISTERED(devlink);
487
488         msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
489         if (!msg)
490                 return;
491
492         err = devlink_nl_health_reporter_fill(msg, reporter, cmd, 0, 0, 0);
493         if (err) {
494                 nlmsg_free(msg);
495                 return;
496         }
497
498         genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), msg,
499                                 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
500 }
501
502 void
503 devlink_health_reporter_recovery_done(struct devlink_health_reporter *reporter)
504 {
505         reporter->recovery_count++;
506         reporter->last_recovery_ts = jiffies;
507 }
508 EXPORT_SYMBOL_GPL(devlink_health_reporter_recovery_done);
509
510 static int
511 devlink_health_reporter_recover(struct devlink_health_reporter *reporter,
512                                 void *priv_ctx, struct netlink_ext_ack *extack)
513 {
514         int err;
515
516         if (reporter->health_state == DEVLINK_HEALTH_REPORTER_STATE_HEALTHY)
517                 return 0;
518
519         if (!reporter->ops->recover)
520                 return -EOPNOTSUPP;
521
522         err = reporter->ops->recover(reporter, priv_ctx, extack);
523         if (err)
524                 return err;
525
526         devlink_health_reporter_recovery_done(reporter);
527         reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_HEALTHY;
528         devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
529
530         return 0;
531 }
532
533 static void
534 devlink_health_dump_clear(struct devlink_health_reporter *reporter)
535 {
536         if (!reporter->dump_fmsg)
537                 return;
538         devlink_fmsg_free(reporter->dump_fmsg);
539         reporter->dump_fmsg = NULL;
540 }
541
542 static int devlink_health_do_dump(struct devlink_health_reporter *reporter,
543                                   void *priv_ctx,
544                                   struct netlink_ext_ack *extack)
545 {
546         int err;
547
548         if (!reporter->ops->dump)
549                 return 0;
550
551         if (reporter->dump_fmsg)
552                 return 0;
553
554         reporter->dump_fmsg = devlink_fmsg_alloc();
555         if (!reporter->dump_fmsg) {
556                 err = -ENOMEM;
557                 return err;
558         }
559
560         err = devlink_fmsg_obj_nest_start(reporter->dump_fmsg);
561         if (err)
562                 goto dump_err;
563
564         err = reporter->ops->dump(reporter, reporter->dump_fmsg,
565                                   priv_ctx, extack);
566         if (err)
567                 goto dump_err;
568
569         err = devlink_fmsg_obj_nest_end(reporter->dump_fmsg);
570         if (err)
571                 goto dump_err;
572
573         reporter->dump_ts = jiffies;
574         reporter->dump_real_ts = ktime_get_real_ns();
575
576         return 0;
577
578 dump_err:
579         devlink_health_dump_clear(reporter);
580         return err;
581 }
582
583 int devlink_health_report(struct devlink_health_reporter *reporter,
584                           const char *msg, void *priv_ctx)
585 {
586         enum devlink_health_reporter_state prev_health_state;
587         struct devlink *devlink = reporter->devlink;
588         unsigned long recover_ts_threshold;
589         int ret;
590
591         /* write a log message of the current error */
592         WARN_ON(!msg);
593         trace_devlink_health_report(devlink, reporter->ops->name, msg);
594         reporter->error_count++;
595         prev_health_state = reporter->health_state;
596         reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_ERROR;
597         devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
598
599         /* abort if the previous error wasn't recovered */
600         recover_ts_threshold = reporter->last_recovery_ts +
601                                msecs_to_jiffies(reporter->graceful_period);
602         if (reporter->auto_recover &&
603             (prev_health_state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY ||
604              (reporter->last_recovery_ts && reporter->recovery_count &&
605               time_is_after_jiffies(recover_ts_threshold)))) {
606                 trace_devlink_health_recover_aborted(devlink,
607                                                      reporter->ops->name,
608                                                      reporter->health_state,
609                                                      jiffies -
610                                                      reporter->last_recovery_ts);
611                 return -ECANCELED;
612         }
613
614         if (reporter->auto_dump) {
615                 mutex_lock(&reporter->dump_lock);
616                 /* store current dump of current error, for later analysis */
617                 devlink_health_do_dump(reporter, priv_ctx, NULL);
618                 mutex_unlock(&reporter->dump_lock);
619         }
620
621         if (!reporter->auto_recover)
622                 return 0;
623
624         devl_lock(devlink);
625         ret = devlink_health_reporter_recover(reporter, priv_ctx, NULL);
626         devl_unlock(devlink);
627
628         return ret;
629 }
630 EXPORT_SYMBOL_GPL(devlink_health_report);
631
632 void
633 devlink_health_reporter_state_update(struct devlink_health_reporter *reporter,
634                                      enum devlink_health_reporter_state state)
635 {
636         if (WARN_ON(state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY &&
637                     state != DEVLINK_HEALTH_REPORTER_STATE_ERROR))
638                 return;
639
640         if (reporter->health_state == state)
641                 return;
642
643         reporter->health_state = state;
644         trace_devlink_health_reporter_state_update(reporter->devlink,
645                                                    reporter->ops->name, state);
646         devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
647 }
648 EXPORT_SYMBOL_GPL(devlink_health_reporter_state_update);
649
650 int devlink_nl_cmd_health_reporter_recover_doit(struct sk_buff *skb,
651                                                 struct genl_info *info)
652 {
653         struct devlink *devlink = info->user_ptr[0];
654         struct devlink_health_reporter *reporter;
655
656         reporter = devlink_health_reporter_get_from_info(devlink, info);
657         if (!reporter)
658                 return -EINVAL;
659
660         return devlink_health_reporter_recover(reporter, NULL, info->extack);
661 }
662
663 static int devlink_fmsg_nest_common(struct devlink_fmsg *fmsg,
664                                     int attrtype)
665 {
666         struct devlink_fmsg_item *item;
667
668         item = kzalloc(sizeof(*item), GFP_KERNEL);
669         if (!item)
670                 return -ENOMEM;
671
672         item->attrtype = attrtype;
673         list_add_tail(&item->list, &fmsg->item_list);
674
675         return 0;
676 }
677
678 int devlink_fmsg_obj_nest_start(struct devlink_fmsg *fmsg)
679 {
680         if (fmsg->putting_binary)
681                 return -EINVAL;
682
683         return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_OBJ_NEST_START);
684 }
685 EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_start);
686
687 static int devlink_fmsg_nest_end(struct devlink_fmsg *fmsg)
688 {
689         if (fmsg->putting_binary)
690                 return -EINVAL;
691
692         return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_NEST_END);
693 }
694
695 int devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg)
696 {
697         if (fmsg->putting_binary)
698                 return -EINVAL;
699
700         return devlink_fmsg_nest_end(fmsg);
701 }
702 EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_end);
703
704 #define DEVLINK_FMSG_MAX_SIZE (GENLMSG_DEFAULT_SIZE - GENL_HDRLEN - NLA_HDRLEN)
705
706 static int devlink_fmsg_put_name(struct devlink_fmsg *fmsg, const char *name)
707 {
708         struct devlink_fmsg_item *item;
709
710         if (fmsg->putting_binary)
711                 return -EINVAL;
712
713         if (strlen(name) + 1 > DEVLINK_FMSG_MAX_SIZE)
714                 return -EMSGSIZE;
715
716         item = kzalloc(sizeof(*item) + strlen(name) + 1, GFP_KERNEL);
717         if (!item)
718                 return -ENOMEM;
719
720         item->nla_type = NLA_NUL_STRING;
721         item->len = strlen(name) + 1;
722         item->attrtype = DEVLINK_ATTR_FMSG_OBJ_NAME;
723         memcpy(&item->value, name, item->len);
724         list_add_tail(&item->list, &fmsg->item_list);
725
726         return 0;
727 }
728
729 int devlink_fmsg_pair_nest_start(struct devlink_fmsg *fmsg, const char *name)
730 {
731         int err;
732
733         if (fmsg->putting_binary)
734                 return -EINVAL;
735
736         err = devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_PAIR_NEST_START);
737         if (err)
738                 return err;
739
740         err = devlink_fmsg_put_name(fmsg, name);
741         if (err)
742                 return err;
743
744         return 0;
745 }
746 EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_start);
747
748 int devlink_fmsg_pair_nest_end(struct devlink_fmsg *fmsg)
749 {
750         if (fmsg->putting_binary)
751                 return -EINVAL;
752
753         return devlink_fmsg_nest_end(fmsg);
754 }
755 EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_end);
756
757 int devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg *fmsg,
758                                      const char *name)
759 {
760         int err;
761
762         if (fmsg->putting_binary)
763                 return -EINVAL;
764
765         err = devlink_fmsg_pair_nest_start(fmsg, name);
766         if (err)
767                 return err;
768
769         err = devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_ARR_NEST_START);
770         if (err)
771                 return err;
772
773         return 0;
774 }
775 EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_start);
776
777 int devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg *fmsg)
778 {
779         int err;
780
781         if (fmsg->putting_binary)
782                 return -EINVAL;
783
784         err = devlink_fmsg_nest_end(fmsg);
785         if (err)
786                 return err;
787
788         err = devlink_fmsg_nest_end(fmsg);
789         if (err)
790                 return err;
791
792         return 0;
793 }
794 EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_end);
795
796 int devlink_fmsg_binary_pair_nest_start(struct devlink_fmsg *fmsg,
797                                         const char *name)
798 {
799         int err;
800
801         err = devlink_fmsg_arr_pair_nest_start(fmsg, name);
802         if (err)
803                 return err;
804
805         fmsg->putting_binary = true;
806         return err;
807 }
808 EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_start);
809
810 int devlink_fmsg_binary_pair_nest_end(struct devlink_fmsg *fmsg)
811 {
812         if (!fmsg->putting_binary)
813                 return -EINVAL;
814
815         fmsg->putting_binary = false;
816         return devlink_fmsg_arr_pair_nest_end(fmsg);
817 }
818 EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_end);
819
820 static int devlink_fmsg_put_value(struct devlink_fmsg *fmsg,
821                                   const void *value, u16 value_len,
822                                   u8 value_nla_type)
823 {
824         struct devlink_fmsg_item *item;
825
826         if (value_len > DEVLINK_FMSG_MAX_SIZE)
827                 return -EMSGSIZE;
828
829         item = kzalloc(sizeof(*item) + value_len, GFP_KERNEL);
830         if (!item)
831                 return -ENOMEM;
832
833         item->nla_type = value_nla_type;
834         item->len = value_len;
835         item->attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
836         memcpy(&item->value, value, item->len);
837         list_add_tail(&item->list, &fmsg->item_list);
838
839         return 0;
840 }
841
842 static int devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value)
843 {
844         if (fmsg->putting_binary)
845                 return -EINVAL;
846
847         return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_FLAG);
848 }
849
850 static int devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value)
851 {
852         if (fmsg->putting_binary)
853                 return -EINVAL;
854
855         return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U8);
856 }
857
858 int devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value)
859 {
860         if (fmsg->putting_binary)
861                 return -EINVAL;
862
863         return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U32);
864 }
865 EXPORT_SYMBOL_GPL(devlink_fmsg_u32_put);
866
867 static int devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value)
868 {
869         if (fmsg->putting_binary)
870                 return -EINVAL;
871
872         return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U64);
873 }
874
875 int devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value)
876 {
877         if (fmsg->putting_binary)
878                 return -EINVAL;
879
880         return devlink_fmsg_put_value(fmsg, value, strlen(value) + 1,
881                                       NLA_NUL_STRING);
882 }
883 EXPORT_SYMBOL_GPL(devlink_fmsg_string_put);
884
885 int devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value,
886                             u16 value_len)
887 {
888         if (!fmsg->putting_binary)
889                 return -EINVAL;
890
891         return devlink_fmsg_put_value(fmsg, value, value_len, NLA_BINARY);
892 }
893 EXPORT_SYMBOL_GPL(devlink_fmsg_binary_put);
894
895 int devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name,
896                                bool value)
897 {
898         int err;
899
900         err = devlink_fmsg_pair_nest_start(fmsg, name);
901         if (err)
902                 return err;
903
904         err = devlink_fmsg_bool_put(fmsg, value);
905         if (err)
906                 return err;
907
908         err = devlink_fmsg_pair_nest_end(fmsg);
909         if (err)
910                 return err;
911
912         return 0;
913 }
914 EXPORT_SYMBOL_GPL(devlink_fmsg_bool_pair_put);
915
916 int devlink_fmsg_u8_pair_put(struct devlink_fmsg *fmsg, const char *name,
917                              u8 value)
918 {
919         int err;
920
921         err = devlink_fmsg_pair_nest_start(fmsg, name);
922         if (err)
923                 return err;
924
925         err = devlink_fmsg_u8_put(fmsg, value);
926         if (err)
927                 return err;
928
929         err = devlink_fmsg_pair_nest_end(fmsg);
930         if (err)
931                 return err;
932
933         return 0;
934 }
935 EXPORT_SYMBOL_GPL(devlink_fmsg_u8_pair_put);
936
937 int devlink_fmsg_u32_pair_put(struct devlink_fmsg *fmsg, const char *name,
938                               u32 value)
939 {
940         int err;
941
942         err = devlink_fmsg_pair_nest_start(fmsg, name);
943         if (err)
944                 return err;
945
946         err = devlink_fmsg_u32_put(fmsg, value);
947         if (err)
948                 return err;
949
950         err = devlink_fmsg_pair_nest_end(fmsg);
951         if (err)
952                 return err;
953
954         return 0;
955 }
956 EXPORT_SYMBOL_GPL(devlink_fmsg_u32_pair_put);
957
958 int devlink_fmsg_u64_pair_put(struct devlink_fmsg *fmsg, const char *name,
959                               u64 value)
960 {
961         int err;
962
963         err = devlink_fmsg_pair_nest_start(fmsg, name);
964         if (err)
965                 return err;
966
967         err = devlink_fmsg_u64_put(fmsg, value);
968         if (err)
969                 return err;
970
971         err = devlink_fmsg_pair_nest_end(fmsg);
972         if (err)
973                 return err;
974
975         return 0;
976 }
977 EXPORT_SYMBOL_GPL(devlink_fmsg_u64_pair_put);
978
979 int devlink_fmsg_string_pair_put(struct devlink_fmsg *fmsg, const char *name,
980                                  const char *value)
981 {
982         int err;
983
984         err = devlink_fmsg_pair_nest_start(fmsg, name);
985         if (err)
986                 return err;
987
988         err = devlink_fmsg_string_put(fmsg, value);
989         if (err)
990                 return err;
991
992         err = devlink_fmsg_pair_nest_end(fmsg);
993         if (err)
994                 return err;
995
996         return 0;
997 }
998 EXPORT_SYMBOL_GPL(devlink_fmsg_string_pair_put);
999
1000 int devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name,
1001                                  const void *value, u32 value_len)
1002 {
1003         u32 data_size;
1004         int end_err;
1005         u32 offset;
1006         int err;
1007
1008         err = devlink_fmsg_binary_pair_nest_start(fmsg, name);
1009         if (err)
1010                 return err;
1011
1012         for (offset = 0; offset < value_len; offset += data_size) {
1013                 data_size = value_len - offset;
1014                 if (data_size > DEVLINK_FMSG_MAX_SIZE)
1015                         data_size = DEVLINK_FMSG_MAX_SIZE;
1016                 err = devlink_fmsg_binary_put(fmsg, value + offset, data_size);
1017                 if (err)
1018                         break;
1019                 /* Exit from loop with a break (instead of
1020                  * return) to make sure putting_binary is turned off in
1021                  * devlink_fmsg_binary_pair_nest_end
1022                  */
1023         }
1024
1025         end_err = devlink_fmsg_binary_pair_nest_end(fmsg);
1026         if (end_err)
1027                 err = end_err;
1028
1029         return err;
1030 }
1031 EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_put);
1032
1033 static int
1034 devlink_fmsg_item_fill_type(struct devlink_fmsg_item *msg, struct sk_buff *skb)
1035 {
1036         switch (msg->nla_type) {
1037         case NLA_FLAG:
1038         case NLA_U8:
1039         case NLA_U32:
1040         case NLA_U64:
1041         case NLA_NUL_STRING:
1042         case NLA_BINARY:
1043                 return nla_put_u8(skb, DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE,
1044                                   msg->nla_type);
1045         default:
1046                 return -EINVAL;
1047         }
1048 }
1049
1050 static int
1051 devlink_fmsg_item_fill_data(struct devlink_fmsg_item *msg, struct sk_buff *skb)
1052 {
1053         int attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
1054         u8 tmp;
1055
1056         switch (msg->nla_type) {
1057         case NLA_FLAG:
1058                 /* Always provide flag data, regardless of its value */
1059                 tmp = *(bool *)msg->value;
1060
1061                 return nla_put_u8(skb, attrtype, tmp);
1062         case NLA_U8:
1063                 return nla_put_u8(skb, attrtype, *(u8 *)msg->value);
1064         case NLA_U32:
1065                 return nla_put_u32(skb, attrtype, *(u32 *)msg->value);
1066         case NLA_U64:
1067                 return nla_put_u64_64bit(skb, attrtype, *(u64 *)msg->value,
1068                                          DEVLINK_ATTR_PAD);
1069         case NLA_NUL_STRING:
1070                 return nla_put_string(skb, attrtype, (char *)&msg->value);
1071         case NLA_BINARY:
1072                 return nla_put(skb, attrtype, msg->len, (void *)&msg->value);
1073         default:
1074                 return -EINVAL;
1075         }
1076 }
1077
1078 static int
1079 devlink_fmsg_prepare_skb(struct devlink_fmsg *fmsg, struct sk_buff *skb,
1080                          int *start)
1081 {
1082         struct devlink_fmsg_item *item;
1083         struct nlattr *fmsg_nlattr;
1084         int err = 0;
1085         int i = 0;
1086
1087         fmsg_nlattr = nla_nest_start_noflag(skb, DEVLINK_ATTR_FMSG);
1088         if (!fmsg_nlattr)
1089                 return -EMSGSIZE;
1090
1091         list_for_each_entry(item, &fmsg->item_list, list) {
1092                 if (i < *start) {
1093                         i++;
1094                         continue;
1095                 }
1096
1097                 switch (item->attrtype) {
1098                 case DEVLINK_ATTR_FMSG_OBJ_NEST_START:
1099                 case DEVLINK_ATTR_FMSG_PAIR_NEST_START:
1100                 case DEVLINK_ATTR_FMSG_ARR_NEST_START:
1101                 case DEVLINK_ATTR_FMSG_NEST_END:
1102                         err = nla_put_flag(skb, item->attrtype);
1103                         break;
1104                 case DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA:
1105                         err = devlink_fmsg_item_fill_type(item, skb);
1106                         if (err)
1107                                 break;
1108                         err = devlink_fmsg_item_fill_data(item, skb);
1109                         break;
1110                 case DEVLINK_ATTR_FMSG_OBJ_NAME:
1111                         err = nla_put_string(skb, item->attrtype,
1112                                              (char *)&item->value);
1113                         break;
1114                 default:
1115                         err = -EINVAL;
1116                         break;
1117                 }
1118                 if (!err)
1119                         *start = ++i;
1120                 else
1121                         break;
1122         }
1123
1124         nla_nest_end(skb, fmsg_nlattr);
1125         return err;
1126 }
1127
1128 static int devlink_fmsg_snd(struct devlink_fmsg *fmsg,
1129                             struct genl_info *info,
1130                             enum devlink_command cmd, int flags)
1131 {
1132         struct nlmsghdr *nlh;
1133         struct sk_buff *skb;
1134         bool last = false;
1135         int index = 0;
1136         void *hdr;
1137         int err;
1138
1139         while (!last) {
1140                 int tmp_index = index;
1141
1142                 skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1143                 if (!skb)
1144                         return -ENOMEM;
1145
1146                 hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
1147                                   &devlink_nl_family, flags | NLM_F_MULTI, cmd);
1148                 if (!hdr) {
1149                         err = -EMSGSIZE;
1150                         goto nla_put_failure;
1151                 }
1152
1153                 err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1154                 if (!err)
1155                         last = true;
1156                 else if (err != -EMSGSIZE || tmp_index == index)
1157                         goto nla_put_failure;
1158
1159                 genlmsg_end(skb, hdr);
1160                 err = genlmsg_reply(skb, info);
1161                 if (err)
1162                         return err;
1163         }
1164
1165         skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1166         if (!skb)
1167                 return -ENOMEM;
1168         nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
1169                         NLMSG_DONE, 0, flags | NLM_F_MULTI);
1170         if (!nlh) {
1171                 err = -EMSGSIZE;
1172                 goto nla_put_failure;
1173         }
1174
1175         return genlmsg_reply(skb, info);
1176
1177 nla_put_failure:
1178         nlmsg_free(skb);
1179         return err;
1180 }
1181
1182 static int devlink_fmsg_dumpit(struct devlink_fmsg *fmsg, struct sk_buff *skb,
1183                                struct netlink_callback *cb,
1184                                enum devlink_command cmd)
1185 {
1186         struct devlink_nl_dump_state *state = devlink_dump_state(cb);
1187         int index = state->idx;
1188         int tmp_index = index;
1189         void *hdr;
1190         int err;
1191
1192         hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
1193                           &devlink_nl_family, NLM_F_ACK | NLM_F_MULTI, cmd);
1194         if (!hdr) {
1195                 err = -EMSGSIZE;
1196                 goto nla_put_failure;
1197         }
1198
1199         err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1200         if ((err && err != -EMSGSIZE) || tmp_index == index)
1201                 goto nla_put_failure;
1202
1203         state->idx = index;
1204         genlmsg_end(skb, hdr);
1205         return skb->len;
1206
1207 nla_put_failure:
1208         genlmsg_cancel(skb, hdr);
1209         return err;
1210 }
1211
1212 int devlink_nl_cmd_health_reporter_diagnose_doit(struct sk_buff *skb,
1213                                                  struct genl_info *info)
1214 {
1215         struct devlink *devlink = info->user_ptr[0];
1216         struct devlink_health_reporter *reporter;
1217         struct devlink_fmsg *fmsg;
1218         int err;
1219
1220         reporter = devlink_health_reporter_get_from_info(devlink, info);
1221         if (!reporter)
1222                 return -EINVAL;
1223
1224         if (!reporter->ops->diagnose)
1225                 return -EOPNOTSUPP;
1226
1227         fmsg = devlink_fmsg_alloc();
1228         if (!fmsg)
1229                 return -ENOMEM;
1230
1231         err = devlink_fmsg_obj_nest_start(fmsg);
1232         if (err)
1233                 goto out;
1234
1235         err = reporter->ops->diagnose(reporter, fmsg, info->extack);
1236         if (err)
1237                 goto out;
1238
1239         err = devlink_fmsg_obj_nest_end(fmsg);
1240         if (err)
1241                 goto out;
1242
1243         err = devlink_fmsg_snd(fmsg, info,
1244                                DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, 0);
1245
1246 out:
1247         devlink_fmsg_free(fmsg);
1248         return err;
1249 }
1250
1251 static struct devlink_health_reporter *
1252 devlink_health_reporter_get_from_cb(struct netlink_callback *cb)
1253 {
1254         const struct genl_dumpit_info *info = genl_dumpit_info(cb);
1255         struct devlink_health_reporter *reporter;
1256         struct nlattr **attrs = info->attrs;
1257         struct devlink *devlink;
1258
1259         devlink = devlink_get_from_attrs_lock(sock_net(cb->skb->sk), attrs);
1260         if (IS_ERR(devlink))
1261                 return NULL;
1262         devl_unlock(devlink);
1263
1264         reporter = devlink_health_reporter_get_from_attrs(devlink, attrs);
1265         devlink_put(devlink);
1266         return reporter;
1267 }
1268
1269 int devlink_nl_cmd_health_reporter_dump_get_dumpit(struct sk_buff *skb,
1270                                                    struct netlink_callback *cb)
1271 {
1272         struct devlink_nl_dump_state *state = devlink_dump_state(cb);
1273         struct devlink_health_reporter *reporter;
1274         int err;
1275
1276         reporter = devlink_health_reporter_get_from_cb(cb);
1277         if (!reporter)
1278                 return -EINVAL;
1279
1280         if (!reporter->ops->dump)
1281                 return -EOPNOTSUPP;
1282
1283         mutex_lock(&reporter->dump_lock);
1284         if (!state->idx) {
1285                 err = devlink_health_do_dump(reporter, NULL, cb->extack);
1286                 if (err)
1287                         goto unlock;
1288                 state->dump_ts = reporter->dump_ts;
1289         }
1290         if (!reporter->dump_fmsg || state->dump_ts != reporter->dump_ts) {
1291                 NL_SET_ERR_MSG(cb->extack, "Dump trampled, please retry");
1292                 err = -EAGAIN;
1293                 goto unlock;
1294         }
1295
1296         err = devlink_fmsg_dumpit(reporter->dump_fmsg, skb, cb,
1297                                   DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET);
1298 unlock:
1299         mutex_unlock(&reporter->dump_lock);
1300         return err;
1301 }
1302
1303 int devlink_nl_cmd_health_reporter_dump_clear_doit(struct sk_buff *skb,
1304                                                    struct genl_info *info)
1305 {
1306         struct devlink *devlink = info->user_ptr[0];
1307         struct devlink_health_reporter *reporter;
1308
1309         reporter = devlink_health_reporter_get_from_info(devlink, info);
1310         if (!reporter)
1311                 return -EINVAL;
1312
1313         if (!reporter->ops->dump)
1314                 return -EOPNOTSUPP;
1315
1316         mutex_lock(&reporter->dump_lock);
1317         devlink_health_dump_clear(reporter);
1318         mutex_unlock(&reporter->dump_lock);
1319         return 0;
1320 }
1321
1322 int devlink_nl_cmd_health_reporter_test_doit(struct sk_buff *skb,
1323                                              struct genl_info *info)
1324 {
1325         struct devlink *devlink = info->user_ptr[0];
1326         struct devlink_health_reporter *reporter;
1327
1328         reporter = devlink_health_reporter_get_from_info(devlink, info);
1329         if (!reporter)
1330                 return -EINVAL;
1331
1332         if (!reporter->ops->test)
1333                 return -EOPNOTSUPP;
1334
1335         return reporter->ops->test(reporter, info->extack);
1336 }