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