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