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