xref: /openbmc/linux/net/devlink/health.c (revision 6d2779ec)
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 }
1350