Merge tag 'tomoyo-pr-20230903' of git://git.osdn.net/gitroot/tomoyo/tomoyo-test1
[linux-block.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         const struct genl_info *info = genl_info_dump(cb);
394         struct devlink_health_reporter *reporter;
395         unsigned long port_index_end = ULONG_MAX;
396         struct nlattr **attrs = info->attrs;
397         unsigned long port_index_start = 0;
398         struct devlink_port *port;
399         unsigned long port_index;
400         int idx = 0;
401         int err;
402
403         if (attrs && attrs[DEVLINK_ATTR_PORT_INDEX]) {
404                 port_index_start = nla_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]);
405                 port_index_end = port_index_start;
406                 flags |= NLM_F_DUMP_FILTERED;
407                 goto per_port_dump;
408         }
409
410         list_for_each_entry(reporter, &devlink->reporter_list, list) {
411                 if (idx < state->idx) {
412                         idx++;
413                         continue;
414                 }
415                 err = devlink_nl_health_reporter_fill(msg, reporter,
416                                                       DEVLINK_CMD_HEALTH_REPORTER_GET,
417                                                       NETLINK_CB(cb->skb).portid,
418                                                       cb->nlh->nlmsg_seq,
419                                                       flags);
420                 if (err) {
421                         state->idx = idx;
422                         return err;
423                 }
424                 idx++;
425         }
426 per_port_dump:
427         xa_for_each_range(&devlink->ports, port_index, port,
428                           port_index_start, port_index_end) {
429                 list_for_each_entry(reporter, &port->reporter_list, list) {
430                         if (idx < state->idx) {
431                                 idx++;
432                                 continue;
433                         }
434                         err = devlink_nl_health_reporter_fill(msg, reporter,
435                                                               DEVLINK_CMD_HEALTH_REPORTER_GET,
436                                                               NETLINK_CB(cb->skb).portid,
437                                                               cb->nlh->nlmsg_seq,
438                                                               flags);
439                         if (err) {
440                                 state->idx = idx;
441                                 return err;
442                         }
443                         idx++;
444                 }
445         }
446
447         return 0;
448 }
449
450 int devlink_nl_health_reporter_get_dumpit(struct sk_buff *skb,
451                                           struct netlink_callback *cb)
452 {
453         return devlink_nl_dumpit(skb, cb,
454                                  devlink_nl_health_reporter_get_dump_one);
455 }
456
457 int devlink_nl_cmd_health_reporter_set_doit(struct sk_buff *skb,
458                                             struct genl_info *info)
459 {
460         struct devlink *devlink = info->user_ptr[0];
461         struct devlink_health_reporter *reporter;
462
463         reporter = devlink_health_reporter_get_from_info(devlink, info);
464         if (!reporter)
465                 return -EINVAL;
466
467         if (!reporter->ops->recover &&
468             (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] ||
469              info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]))
470                 return -EOPNOTSUPP;
471
472         if (!reporter->ops->dump &&
473             info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
474                 return -EOPNOTSUPP;
475
476         if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD])
477                 reporter->graceful_period =
478                         nla_get_u64(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD]);
479
480         if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])
481                 reporter->auto_recover =
482                         nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]);
483
484         if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
485                 reporter->auto_dump =
486                 nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP]);
487
488         return 0;
489 }
490
491 static void devlink_recover_notify(struct devlink_health_reporter *reporter,
492                                    enum devlink_command cmd)
493 {
494         struct devlink *devlink = reporter->devlink;
495         struct sk_buff *msg;
496         int err;
497
498         WARN_ON(cmd != DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
499         ASSERT_DEVLINK_REGISTERED(devlink);
500
501         msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
502         if (!msg)
503                 return;
504
505         err = devlink_nl_health_reporter_fill(msg, reporter, cmd, 0, 0, 0);
506         if (err) {
507                 nlmsg_free(msg);
508                 return;
509         }
510
511         genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), msg,
512                                 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
513 }
514
515 void
516 devlink_health_reporter_recovery_done(struct devlink_health_reporter *reporter)
517 {
518         reporter->recovery_count++;
519         reporter->last_recovery_ts = jiffies;
520 }
521 EXPORT_SYMBOL_GPL(devlink_health_reporter_recovery_done);
522
523 static int
524 devlink_health_reporter_recover(struct devlink_health_reporter *reporter,
525                                 void *priv_ctx, struct netlink_ext_ack *extack)
526 {
527         int err;
528
529         if (reporter->health_state == DEVLINK_HEALTH_REPORTER_STATE_HEALTHY)
530                 return 0;
531
532         if (!reporter->ops->recover)
533                 return -EOPNOTSUPP;
534
535         err = reporter->ops->recover(reporter, priv_ctx, extack);
536         if (err)
537                 return err;
538
539         devlink_health_reporter_recovery_done(reporter);
540         reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_HEALTHY;
541         devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
542
543         return 0;
544 }
545
546 static void
547 devlink_health_dump_clear(struct devlink_health_reporter *reporter)
548 {
549         if (!reporter->dump_fmsg)
550                 return;
551         devlink_fmsg_free(reporter->dump_fmsg);
552         reporter->dump_fmsg = NULL;
553 }
554
555 static int devlink_health_do_dump(struct devlink_health_reporter *reporter,
556                                   void *priv_ctx,
557                                   struct netlink_ext_ack *extack)
558 {
559         int err;
560
561         if (!reporter->ops->dump)
562                 return 0;
563
564         if (reporter->dump_fmsg)
565                 return 0;
566
567         reporter->dump_fmsg = devlink_fmsg_alloc();
568         if (!reporter->dump_fmsg) {
569                 err = -ENOMEM;
570                 return err;
571         }
572
573         err = devlink_fmsg_obj_nest_start(reporter->dump_fmsg);
574         if (err)
575                 goto dump_err;
576
577         err = reporter->ops->dump(reporter, reporter->dump_fmsg,
578                                   priv_ctx, extack);
579         if (err)
580                 goto dump_err;
581
582         err = devlink_fmsg_obj_nest_end(reporter->dump_fmsg);
583         if (err)
584                 goto dump_err;
585
586         reporter->dump_ts = jiffies;
587         reporter->dump_real_ts = ktime_get_real_ns();
588
589         return 0;
590
591 dump_err:
592         devlink_health_dump_clear(reporter);
593         return err;
594 }
595
596 int devlink_health_report(struct devlink_health_reporter *reporter,
597                           const char *msg, void *priv_ctx)
598 {
599         enum devlink_health_reporter_state prev_health_state;
600         struct devlink *devlink = reporter->devlink;
601         unsigned long recover_ts_threshold;
602         int ret;
603
604         /* write a log message of the current error */
605         WARN_ON(!msg);
606         trace_devlink_health_report(devlink, reporter->ops->name, msg);
607         reporter->error_count++;
608         prev_health_state = reporter->health_state;
609         reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_ERROR;
610         devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
611
612         /* abort if the previous error wasn't recovered */
613         recover_ts_threshold = reporter->last_recovery_ts +
614                                msecs_to_jiffies(reporter->graceful_period);
615         if (reporter->auto_recover &&
616             (prev_health_state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY ||
617              (reporter->last_recovery_ts && reporter->recovery_count &&
618               time_is_after_jiffies(recover_ts_threshold)))) {
619                 trace_devlink_health_recover_aborted(devlink,
620                                                      reporter->ops->name,
621                                                      reporter->health_state,
622                                                      jiffies -
623                                                      reporter->last_recovery_ts);
624                 return -ECANCELED;
625         }
626
627         if (reporter->auto_dump) {
628                 mutex_lock(&reporter->dump_lock);
629                 /* store current dump of current error, for later analysis */
630                 devlink_health_do_dump(reporter, priv_ctx, NULL);
631                 mutex_unlock(&reporter->dump_lock);
632         }
633
634         if (!reporter->auto_recover)
635                 return 0;
636
637         devl_lock(devlink);
638         ret = devlink_health_reporter_recover(reporter, priv_ctx, NULL);
639         devl_unlock(devlink);
640
641         return ret;
642 }
643 EXPORT_SYMBOL_GPL(devlink_health_report);
644
645 void
646 devlink_health_reporter_state_update(struct devlink_health_reporter *reporter,
647                                      enum devlink_health_reporter_state state)
648 {
649         if (WARN_ON(state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY &&
650                     state != DEVLINK_HEALTH_REPORTER_STATE_ERROR))
651                 return;
652
653         if (reporter->health_state == state)
654                 return;
655
656         reporter->health_state = state;
657         trace_devlink_health_reporter_state_update(reporter->devlink,
658                                                    reporter->ops->name, state);
659         devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
660 }
661 EXPORT_SYMBOL_GPL(devlink_health_reporter_state_update);
662
663 int devlink_nl_cmd_health_reporter_recover_doit(struct sk_buff *skb,
664                                                 struct genl_info *info)
665 {
666         struct devlink *devlink = info->user_ptr[0];
667         struct devlink_health_reporter *reporter;
668
669         reporter = devlink_health_reporter_get_from_info(devlink, info);
670         if (!reporter)
671                 return -EINVAL;
672
673         return devlink_health_reporter_recover(reporter, NULL, info->extack);
674 }
675
676 static int devlink_fmsg_nest_common(struct devlink_fmsg *fmsg,
677                                     int attrtype)
678 {
679         struct devlink_fmsg_item *item;
680
681         item = kzalloc(sizeof(*item), GFP_KERNEL);
682         if (!item)
683                 return -ENOMEM;
684
685         item->attrtype = attrtype;
686         list_add_tail(&item->list, &fmsg->item_list);
687
688         return 0;
689 }
690
691 int devlink_fmsg_obj_nest_start(struct devlink_fmsg *fmsg)
692 {
693         if (fmsg->putting_binary)
694                 return -EINVAL;
695
696         return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_OBJ_NEST_START);
697 }
698 EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_start);
699
700 static int devlink_fmsg_nest_end(struct devlink_fmsg *fmsg)
701 {
702         if (fmsg->putting_binary)
703                 return -EINVAL;
704
705         return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_NEST_END);
706 }
707
708 int devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg)
709 {
710         if (fmsg->putting_binary)
711                 return -EINVAL;
712
713         return devlink_fmsg_nest_end(fmsg);
714 }
715 EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_end);
716
717 #define DEVLINK_FMSG_MAX_SIZE (GENLMSG_DEFAULT_SIZE - GENL_HDRLEN - NLA_HDRLEN)
718
719 static int devlink_fmsg_put_name(struct devlink_fmsg *fmsg, const char *name)
720 {
721         struct devlink_fmsg_item *item;
722
723         if (fmsg->putting_binary)
724                 return -EINVAL;
725
726         if (strlen(name) + 1 > DEVLINK_FMSG_MAX_SIZE)
727                 return -EMSGSIZE;
728
729         item = kzalloc(sizeof(*item) + strlen(name) + 1, GFP_KERNEL);
730         if (!item)
731                 return -ENOMEM;
732
733         item->nla_type = NLA_NUL_STRING;
734         item->len = strlen(name) + 1;
735         item->attrtype = DEVLINK_ATTR_FMSG_OBJ_NAME;
736         memcpy(&item->value, name, item->len);
737         list_add_tail(&item->list, &fmsg->item_list);
738
739         return 0;
740 }
741
742 int devlink_fmsg_pair_nest_start(struct devlink_fmsg *fmsg, const char *name)
743 {
744         int err;
745
746         if (fmsg->putting_binary)
747                 return -EINVAL;
748
749         err = devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_PAIR_NEST_START);
750         if (err)
751                 return err;
752
753         err = devlink_fmsg_put_name(fmsg, name);
754         if (err)
755                 return err;
756
757         return 0;
758 }
759 EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_start);
760
761 int devlink_fmsg_pair_nest_end(struct devlink_fmsg *fmsg)
762 {
763         if (fmsg->putting_binary)
764                 return -EINVAL;
765
766         return devlink_fmsg_nest_end(fmsg);
767 }
768 EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_end);
769
770 int devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg *fmsg,
771                                      const char *name)
772 {
773         int err;
774
775         if (fmsg->putting_binary)
776                 return -EINVAL;
777
778         err = devlink_fmsg_pair_nest_start(fmsg, name);
779         if (err)
780                 return err;
781
782         err = devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_ARR_NEST_START);
783         if (err)
784                 return err;
785
786         return 0;
787 }
788 EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_start);
789
790 int devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg *fmsg)
791 {
792         int err;
793
794         if (fmsg->putting_binary)
795                 return -EINVAL;
796
797         err = devlink_fmsg_nest_end(fmsg);
798         if (err)
799                 return err;
800
801         err = devlink_fmsg_nest_end(fmsg);
802         if (err)
803                 return err;
804
805         return 0;
806 }
807 EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_end);
808
809 int devlink_fmsg_binary_pair_nest_start(struct devlink_fmsg *fmsg,
810                                         const char *name)
811 {
812         int err;
813
814         err = devlink_fmsg_arr_pair_nest_start(fmsg, name);
815         if (err)
816                 return err;
817
818         fmsg->putting_binary = true;
819         return err;
820 }
821 EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_start);
822
823 int devlink_fmsg_binary_pair_nest_end(struct devlink_fmsg *fmsg)
824 {
825         if (!fmsg->putting_binary)
826                 return -EINVAL;
827
828         fmsg->putting_binary = false;
829         return devlink_fmsg_arr_pair_nest_end(fmsg);
830 }
831 EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_end);
832
833 static int devlink_fmsg_put_value(struct devlink_fmsg *fmsg,
834                                   const void *value, u16 value_len,
835                                   u8 value_nla_type)
836 {
837         struct devlink_fmsg_item *item;
838
839         if (value_len > DEVLINK_FMSG_MAX_SIZE)
840                 return -EMSGSIZE;
841
842         item = kzalloc(sizeof(*item) + value_len, GFP_KERNEL);
843         if (!item)
844                 return -ENOMEM;
845
846         item->nla_type = value_nla_type;
847         item->len = value_len;
848         item->attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
849         memcpy(&item->value, value, item->len);
850         list_add_tail(&item->list, &fmsg->item_list);
851
852         return 0;
853 }
854
855 static int devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value)
856 {
857         if (fmsg->putting_binary)
858                 return -EINVAL;
859
860         return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_FLAG);
861 }
862
863 static int devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value)
864 {
865         if (fmsg->putting_binary)
866                 return -EINVAL;
867
868         return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U8);
869 }
870
871 int devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value)
872 {
873         if (fmsg->putting_binary)
874                 return -EINVAL;
875
876         return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U32);
877 }
878 EXPORT_SYMBOL_GPL(devlink_fmsg_u32_put);
879
880 static int devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value)
881 {
882         if (fmsg->putting_binary)
883                 return -EINVAL;
884
885         return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U64);
886 }
887
888 int devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value)
889 {
890         if (fmsg->putting_binary)
891                 return -EINVAL;
892
893         return devlink_fmsg_put_value(fmsg, value, strlen(value) + 1,
894                                       NLA_NUL_STRING);
895 }
896 EXPORT_SYMBOL_GPL(devlink_fmsg_string_put);
897
898 int devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value,
899                             u16 value_len)
900 {
901         if (!fmsg->putting_binary)
902                 return -EINVAL;
903
904         return devlink_fmsg_put_value(fmsg, value, value_len, NLA_BINARY);
905 }
906 EXPORT_SYMBOL_GPL(devlink_fmsg_binary_put);
907
908 int devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name,
909                                bool value)
910 {
911         int err;
912
913         err = devlink_fmsg_pair_nest_start(fmsg, name);
914         if (err)
915                 return err;
916
917         err = devlink_fmsg_bool_put(fmsg, value);
918         if (err)
919                 return err;
920
921         err = devlink_fmsg_pair_nest_end(fmsg);
922         if (err)
923                 return err;
924
925         return 0;
926 }
927 EXPORT_SYMBOL_GPL(devlink_fmsg_bool_pair_put);
928
929 int devlink_fmsg_u8_pair_put(struct devlink_fmsg *fmsg, const char *name,
930                              u8 value)
931 {
932         int err;
933
934         err = devlink_fmsg_pair_nest_start(fmsg, name);
935         if (err)
936                 return err;
937
938         err = devlink_fmsg_u8_put(fmsg, value);
939         if (err)
940                 return err;
941
942         err = devlink_fmsg_pair_nest_end(fmsg);
943         if (err)
944                 return err;
945
946         return 0;
947 }
948 EXPORT_SYMBOL_GPL(devlink_fmsg_u8_pair_put);
949
950 int devlink_fmsg_u32_pair_put(struct devlink_fmsg *fmsg, const char *name,
951                               u32 value)
952 {
953         int err;
954
955         err = devlink_fmsg_pair_nest_start(fmsg, name);
956         if (err)
957                 return err;
958
959         err = devlink_fmsg_u32_put(fmsg, value);
960         if (err)
961                 return err;
962
963         err = devlink_fmsg_pair_nest_end(fmsg);
964         if (err)
965                 return err;
966
967         return 0;
968 }
969 EXPORT_SYMBOL_GPL(devlink_fmsg_u32_pair_put);
970
971 int devlink_fmsg_u64_pair_put(struct devlink_fmsg *fmsg, const char *name,
972                               u64 value)
973 {
974         int err;
975
976         err = devlink_fmsg_pair_nest_start(fmsg, name);
977         if (err)
978                 return err;
979
980         err = devlink_fmsg_u64_put(fmsg, value);
981         if (err)
982                 return err;
983
984         err = devlink_fmsg_pair_nest_end(fmsg);
985         if (err)
986                 return err;
987
988         return 0;
989 }
990 EXPORT_SYMBOL_GPL(devlink_fmsg_u64_pair_put);
991
992 int devlink_fmsg_string_pair_put(struct devlink_fmsg *fmsg, const char *name,
993                                  const char *value)
994 {
995         int err;
996
997         err = devlink_fmsg_pair_nest_start(fmsg, name);
998         if (err)
999                 return err;
1000
1001         err = devlink_fmsg_string_put(fmsg, value);
1002         if (err)
1003                 return err;
1004
1005         err = devlink_fmsg_pair_nest_end(fmsg);
1006         if (err)
1007                 return err;
1008
1009         return 0;
1010 }
1011 EXPORT_SYMBOL_GPL(devlink_fmsg_string_pair_put);
1012
1013 int devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name,
1014                                  const void *value, u32 value_len)
1015 {
1016         u32 data_size;
1017         int end_err;
1018         u32 offset;
1019         int err;
1020
1021         err = devlink_fmsg_binary_pair_nest_start(fmsg, name);
1022         if (err)
1023                 return err;
1024
1025         for (offset = 0; offset < value_len; offset += data_size) {
1026                 data_size = value_len - offset;
1027                 if (data_size > DEVLINK_FMSG_MAX_SIZE)
1028                         data_size = DEVLINK_FMSG_MAX_SIZE;
1029                 err = devlink_fmsg_binary_put(fmsg, value + offset, data_size);
1030                 if (err)
1031                         break;
1032                 /* Exit from loop with a break (instead of
1033                  * return) to make sure putting_binary is turned off in
1034                  * devlink_fmsg_binary_pair_nest_end
1035                  */
1036         }
1037
1038         end_err = devlink_fmsg_binary_pair_nest_end(fmsg);
1039         if (end_err)
1040                 err = end_err;
1041
1042         return err;
1043 }
1044 EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_put);
1045
1046 static int
1047 devlink_fmsg_item_fill_type(struct devlink_fmsg_item *msg, struct sk_buff *skb)
1048 {
1049         switch (msg->nla_type) {
1050         case NLA_FLAG:
1051         case NLA_U8:
1052         case NLA_U32:
1053         case NLA_U64:
1054         case NLA_NUL_STRING:
1055         case NLA_BINARY:
1056                 return nla_put_u8(skb, DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE,
1057                                   msg->nla_type);
1058         default:
1059                 return -EINVAL;
1060         }
1061 }
1062
1063 static int
1064 devlink_fmsg_item_fill_data(struct devlink_fmsg_item *msg, struct sk_buff *skb)
1065 {
1066         int attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
1067         u8 tmp;
1068
1069         switch (msg->nla_type) {
1070         case NLA_FLAG:
1071                 /* Always provide flag data, regardless of its value */
1072                 tmp = *(bool *)msg->value;
1073
1074                 return nla_put_u8(skb, attrtype, tmp);
1075         case NLA_U8:
1076                 return nla_put_u8(skb, attrtype, *(u8 *)msg->value);
1077         case NLA_U32:
1078                 return nla_put_u32(skb, attrtype, *(u32 *)msg->value);
1079         case NLA_U64:
1080                 return nla_put_u64_64bit(skb, attrtype, *(u64 *)msg->value,
1081                                          DEVLINK_ATTR_PAD);
1082         case NLA_NUL_STRING:
1083                 return nla_put_string(skb, attrtype, (char *)&msg->value);
1084         case NLA_BINARY:
1085                 return nla_put(skb, attrtype, msg->len, (void *)&msg->value);
1086         default:
1087                 return -EINVAL;
1088         }
1089 }
1090
1091 static int
1092 devlink_fmsg_prepare_skb(struct devlink_fmsg *fmsg, struct sk_buff *skb,
1093                          int *start)
1094 {
1095         struct devlink_fmsg_item *item;
1096         struct nlattr *fmsg_nlattr;
1097         int err = 0;
1098         int i = 0;
1099
1100         fmsg_nlattr = nla_nest_start_noflag(skb, DEVLINK_ATTR_FMSG);
1101         if (!fmsg_nlattr)
1102                 return -EMSGSIZE;
1103
1104         list_for_each_entry(item, &fmsg->item_list, list) {
1105                 if (i < *start) {
1106                         i++;
1107                         continue;
1108                 }
1109
1110                 switch (item->attrtype) {
1111                 case DEVLINK_ATTR_FMSG_OBJ_NEST_START:
1112                 case DEVLINK_ATTR_FMSG_PAIR_NEST_START:
1113                 case DEVLINK_ATTR_FMSG_ARR_NEST_START:
1114                 case DEVLINK_ATTR_FMSG_NEST_END:
1115                         err = nla_put_flag(skb, item->attrtype);
1116                         break;
1117                 case DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA:
1118                         err = devlink_fmsg_item_fill_type(item, skb);
1119                         if (err)
1120                                 break;
1121                         err = devlink_fmsg_item_fill_data(item, skb);
1122                         break;
1123                 case DEVLINK_ATTR_FMSG_OBJ_NAME:
1124                         err = nla_put_string(skb, item->attrtype,
1125                                              (char *)&item->value);
1126                         break;
1127                 default:
1128                         err = -EINVAL;
1129                         break;
1130                 }
1131                 if (!err)
1132                         *start = ++i;
1133                 else
1134                         break;
1135         }
1136
1137         nla_nest_end(skb, fmsg_nlattr);
1138         return err;
1139 }
1140
1141 static int devlink_fmsg_snd(struct devlink_fmsg *fmsg,
1142                             struct genl_info *info,
1143                             enum devlink_command cmd, int flags)
1144 {
1145         struct nlmsghdr *nlh;
1146         struct sk_buff *skb;
1147         bool last = false;
1148         int index = 0;
1149         void *hdr;
1150         int err;
1151
1152         while (!last) {
1153                 int tmp_index = index;
1154
1155                 skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1156                 if (!skb)
1157                         return -ENOMEM;
1158
1159                 hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
1160                                   &devlink_nl_family, flags | NLM_F_MULTI, cmd);
1161                 if (!hdr) {
1162                         err = -EMSGSIZE;
1163                         goto nla_put_failure;
1164                 }
1165
1166                 err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1167                 if (!err)
1168                         last = true;
1169                 else if (err != -EMSGSIZE || tmp_index == index)
1170                         goto nla_put_failure;
1171
1172                 genlmsg_end(skb, hdr);
1173                 err = genlmsg_reply(skb, info);
1174                 if (err)
1175                         return err;
1176         }
1177
1178         skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1179         if (!skb)
1180                 return -ENOMEM;
1181         nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
1182                         NLMSG_DONE, 0, flags | NLM_F_MULTI);
1183         if (!nlh) {
1184                 err = -EMSGSIZE;
1185                 goto nla_put_failure;
1186         }
1187
1188         return genlmsg_reply(skb, info);
1189
1190 nla_put_failure:
1191         nlmsg_free(skb);
1192         return err;
1193 }
1194
1195 static int devlink_fmsg_dumpit(struct devlink_fmsg *fmsg, struct sk_buff *skb,
1196                                struct netlink_callback *cb,
1197                                enum devlink_command cmd)
1198 {
1199         struct devlink_nl_dump_state *state = devlink_dump_state(cb);
1200         int index = state->idx;
1201         int tmp_index = index;
1202         void *hdr;
1203         int err;
1204
1205         hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
1206                           &devlink_nl_family, NLM_F_ACK | NLM_F_MULTI, cmd);
1207         if (!hdr) {
1208                 err = -EMSGSIZE;
1209                 goto nla_put_failure;
1210         }
1211
1212         err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1213         if ((err && err != -EMSGSIZE) || tmp_index == index)
1214                 goto nla_put_failure;
1215
1216         state->idx = index;
1217         genlmsg_end(skb, hdr);
1218         return skb->len;
1219
1220 nla_put_failure:
1221         genlmsg_cancel(skb, hdr);
1222         return err;
1223 }
1224
1225 int devlink_nl_cmd_health_reporter_diagnose_doit(struct sk_buff *skb,
1226                                                  struct genl_info *info)
1227 {
1228         struct devlink *devlink = info->user_ptr[0];
1229         struct devlink_health_reporter *reporter;
1230         struct devlink_fmsg *fmsg;
1231         int err;
1232
1233         reporter = devlink_health_reporter_get_from_info(devlink, info);
1234         if (!reporter)
1235                 return -EINVAL;
1236
1237         if (!reporter->ops->diagnose)
1238                 return -EOPNOTSUPP;
1239
1240         fmsg = devlink_fmsg_alloc();
1241         if (!fmsg)
1242                 return -ENOMEM;
1243
1244         err = devlink_fmsg_obj_nest_start(fmsg);
1245         if (err)
1246                 goto out;
1247
1248         err = reporter->ops->diagnose(reporter, fmsg, info->extack);
1249         if (err)
1250                 goto out;
1251
1252         err = devlink_fmsg_obj_nest_end(fmsg);
1253         if (err)
1254                 goto out;
1255
1256         err = devlink_fmsg_snd(fmsg, info,
1257                                DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, 0);
1258
1259 out:
1260         devlink_fmsg_free(fmsg);
1261         return err;
1262 }
1263
1264 static struct devlink_health_reporter *
1265 devlink_health_reporter_get_from_cb(struct netlink_callback *cb)
1266 {
1267         const struct genl_info *info = genl_info_dump(cb);
1268         struct devlink_health_reporter *reporter;
1269         struct nlattr **attrs = info->attrs;
1270         struct devlink *devlink;
1271
1272         devlink = devlink_get_from_attrs_lock(sock_net(cb->skb->sk), attrs);
1273         if (IS_ERR(devlink))
1274                 return NULL;
1275         devl_unlock(devlink);
1276
1277         reporter = devlink_health_reporter_get_from_attrs(devlink, attrs);
1278         devlink_put(devlink);
1279         return reporter;
1280 }
1281
1282 int devlink_nl_cmd_health_reporter_dump_get_dumpit(struct sk_buff *skb,
1283                                                    struct netlink_callback *cb)
1284 {
1285         struct devlink_nl_dump_state *state = devlink_dump_state(cb);
1286         struct devlink_health_reporter *reporter;
1287         int err;
1288
1289         reporter = devlink_health_reporter_get_from_cb(cb);
1290         if (!reporter)
1291                 return -EINVAL;
1292
1293         if (!reporter->ops->dump)
1294                 return -EOPNOTSUPP;
1295
1296         mutex_lock(&reporter->dump_lock);
1297         if (!state->idx) {
1298                 err = devlink_health_do_dump(reporter, NULL, cb->extack);
1299                 if (err)
1300                         goto unlock;
1301                 state->dump_ts = reporter->dump_ts;
1302         }
1303         if (!reporter->dump_fmsg || state->dump_ts != reporter->dump_ts) {
1304                 NL_SET_ERR_MSG(cb->extack, "Dump trampled, please retry");
1305                 err = -EAGAIN;
1306                 goto unlock;
1307         }
1308
1309         err = devlink_fmsg_dumpit(reporter->dump_fmsg, skb, cb,
1310                                   DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET);
1311 unlock:
1312         mutex_unlock(&reporter->dump_lock);
1313         return err;
1314 }
1315
1316 int devlink_nl_cmd_health_reporter_dump_clear_doit(struct sk_buff *skb,
1317                                                    struct genl_info *info)
1318 {
1319         struct devlink *devlink = info->user_ptr[0];
1320         struct devlink_health_reporter *reporter;
1321
1322         reporter = devlink_health_reporter_get_from_info(devlink, info);
1323         if (!reporter)
1324                 return -EINVAL;
1325
1326         if (!reporter->ops->dump)
1327                 return -EOPNOTSUPP;
1328
1329         mutex_lock(&reporter->dump_lock);
1330         devlink_health_dump_clear(reporter);
1331         mutex_unlock(&reporter->dump_lock);
1332         return 0;
1333 }
1334
1335 int devlink_nl_cmd_health_reporter_test_doit(struct sk_buff *skb,
1336                                              struct genl_info *info)
1337 {
1338         struct devlink *devlink = info->user_ptr[0];
1339         struct devlink_health_reporter *reporter;
1340
1341         reporter = devlink_health_reporter_get_from_info(devlink, info);
1342         if (!reporter)
1343                 return -EINVAL;
1344
1345         if (!reporter->ops->test)
1346                 return -EOPNOTSUPP;
1347
1348         return reporter->ops->test(reporter, info->extack);
1349 }