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