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