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  
devlink_fmsg_alloc(void)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  
devlink_fmsg_free(struct devlink_fmsg * fmsg)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 *
devlink_health_reporter_priv(struct devlink_health_reporter * reporter)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 *
__devlink_health_reporter_find_by_name(struct list_head * reporter_list,const char * reporter_name)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 *
devlink_health_reporter_find_by_name(struct devlink * devlink,const char * reporter_name)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 *
devlink_port_health_reporter_find_by_name(struct devlink_port * devlink_port,const char * reporter_name)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 *
__devlink_health_reporter_create(struct devlink * devlink,const struct devlink_health_reporter_ops * ops,u64 graceful_period,void * priv)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 *
devl_port_health_reporter_create(struct devlink_port * port,const struct devlink_health_reporter_ops * ops,u64 graceful_period,void * priv)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 *
devlink_port_health_reporter_create(struct devlink_port * port,const struct devlink_health_reporter_ops * ops,u64 graceful_period,void * priv)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 *
devl_health_reporter_create(struct devlink * devlink,const struct devlink_health_reporter_ops * ops,u64 graceful_period,void * priv)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 *
devlink_health_reporter_create(struct devlink * devlink,const struct devlink_health_reporter_ops * ops,u64 graceful_period,void * priv)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
devlink_health_reporter_free(struct devlink_health_reporter * reporter)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
devl_health_reporter_destroy(struct devlink_health_reporter * reporter)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
devlink_health_reporter_destroy(struct devlink_health_reporter * reporter)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
devlink_nl_health_reporter_fill(struct sk_buff * msg,struct devlink_health_reporter * reporter,enum devlink_command cmd,u32 portid,u32 seq,int flags)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 (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT,
291  			      reporter->error_count, DEVLINK_ATTR_PAD))
292  		goto reporter_nest_cancel;
293  	if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT,
294  			      reporter->recovery_count, DEVLINK_ATTR_PAD))
295  		goto reporter_nest_cancel;
296  	if (reporter->ops->recover &&
297  	    nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD,
298  			      reporter->graceful_period,
299  			      DEVLINK_ATTR_PAD))
300  		goto reporter_nest_cancel;
301  	if (reporter->ops->recover &&
302  	    nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
303  		       reporter->auto_recover))
304  		goto reporter_nest_cancel;
305  	if (reporter->dump_fmsg &&
306  	    nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS,
307  			      jiffies_to_msecs(reporter->dump_ts),
308  			      DEVLINK_ATTR_PAD))
309  		goto reporter_nest_cancel;
310  	if (reporter->dump_fmsg &&
311  	    nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS,
312  			      reporter->dump_real_ts, DEVLINK_ATTR_PAD))
313  		goto reporter_nest_cancel;
314  	if (reporter->ops->dump &&
315  	    nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP,
316  		       reporter->auto_dump))
317  		goto reporter_nest_cancel;
318  
319  	nla_nest_end(msg, reporter_attr);
320  	genlmsg_end(msg, hdr);
321  	return 0;
322  
323  reporter_nest_cancel:
324  	nla_nest_cancel(msg, reporter_attr);
325  genlmsg_cancel:
326  	genlmsg_cancel(msg, hdr);
327  	return -EMSGSIZE;
328  }
329  
330  static struct devlink_health_reporter *
devlink_health_reporter_get_from_attrs(struct devlink * devlink,struct nlattr ** attrs)331  devlink_health_reporter_get_from_attrs(struct devlink *devlink,
332  				       struct nlattr **attrs)
333  {
334  	struct devlink_port *devlink_port;
335  	char *reporter_name;
336  
337  	if (!attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME])
338  		return NULL;
339  
340  	reporter_name = nla_data(attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME]);
341  	devlink_port = devlink_port_get_from_attrs(devlink, attrs);
342  	if (IS_ERR(devlink_port))
343  		return devlink_health_reporter_find_by_name(devlink,
344  							    reporter_name);
345  	else
346  		return devlink_port_health_reporter_find_by_name(devlink_port,
347  								 reporter_name);
348  }
349  
350  static struct devlink_health_reporter *
devlink_health_reporter_get_from_info(struct devlink * devlink,struct genl_info * info)351  devlink_health_reporter_get_from_info(struct devlink *devlink,
352  				      struct genl_info *info)
353  {
354  	return devlink_health_reporter_get_from_attrs(devlink, info->attrs);
355  }
356  
devlink_nl_health_reporter_get_doit(struct sk_buff * skb,struct genl_info * info)357  int devlink_nl_health_reporter_get_doit(struct sk_buff *skb,
358  					struct genl_info *info)
359  {
360  	struct devlink *devlink = info->user_ptr[0];
361  	struct devlink_health_reporter *reporter;
362  	struct sk_buff *msg;
363  	int err;
364  
365  	reporter = devlink_health_reporter_get_from_info(devlink, info);
366  	if (!reporter)
367  		return -EINVAL;
368  
369  	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
370  	if (!msg)
371  		return -ENOMEM;
372  
373  	err = devlink_nl_health_reporter_fill(msg, reporter,
374  					      DEVLINK_CMD_HEALTH_REPORTER_GET,
375  					      info->snd_portid, info->snd_seq,
376  					      0);
377  	if (err) {
378  		nlmsg_free(msg);
379  		return err;
380  	}
381  
382  	return genlmsg_reply(msg, info);
383  }
384  
devlink_nl_health_reporter_get_dump_one(struct sk_buff * msg,struct devlink * devlink,struct netlink_callback * cb,int flags)385  static int devlink_nl_health_reporter_get_dump_one(struct sk_buff *msg,
386  						   struct devlink *devlink,
387  						   struct netlink_callback *cb,
388  						   int flags)
389  {
390  	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
391  	const struct genl_info *info = genl_info_dump(cb);
392  	struct devlink_health_reporter *reporter;
393  	unsigned long port_index_end = ULONG_MAX;
394  	struct nlattr **attrs = info->attrs;
395  	unsigned long port_index_start = 0;
396  	struct devlink_port *port;
397  	unsigned long port_index;
398  	int idx = 0;
399  	int err;
400  
401  	if (attrs && attrs[DEVLINK_ATTR_PORT_INDEX]) {
402  		port_index_start = nla_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]);
403  		port_index_end = port_index_start;
404  		flags |= NLM_F_DUMP_FILTERED;
405  		goto per_port_dump;
406  	}
407  
408  	list_for_each_entry(reporter, &devlink->reporter_list, list) {
409  		if (idx < state->idx) {
410  			idx++;
411  			continue;
412  		}
413  		err = devlink_nl_health_reporter_fill(msg, reporter,
414  						      DEVLINK_CMD_HEALTH_REPORTER_GET,
415  						      NETLINK_CB(cb->skb).portid,
416  						      cb->nlh->nlmsg_seq,
417  						      flags);
418  		if (err) {
419  			state->idx = idx;
420  			return err;
421  		}
422  		idx++;
423  	}
424  per_port_dump:
425  	xa_for_each_range(&devlink->ports, port_index, port,
426  			  port_index_start, port_index_end) {
427  		list_for_each_entry(reporter, &port->reporter_list, list) {
428  			if (idx < state->idx) {
429  				idx++;
430  				continue;
431  			}
432  			err = devlink_nl_health_reporter_fill(msg, reporter,
433  							      DEVLINK_CMD_HEALTH_REPORTER_GET,
434  							      NETLINK_CB(cb->skb).portid,
435  							      cb->nlh->nlmsg_seq,
436  							      flags);
437  			if (err) {
438  				state->idx = idx;
439  				return err;
440  			}
441  			idx++;
442  		}
443  	}
444  
445  	return 0;
446  }
447  
devlink_nl_health_reporter_get_dumpit(struct sk_buff * skb,struct netlink_callback * cb)448  int devlink_nl_health_reporter_get_dumpit(struct sk_buff *skb,
449  					  struct netlink_callback *cb)
450  {
451  	return devlink_nl_dumpit(skb, cb,
452  				 devlink_nl_health_reporter_get_dump_one);
453  }
454  
devlink_nl_health_reporter_set_doit(struct sk_buff * skb,struct genl_info * info)455  int devlink_nl_health_reporter_set_doit(struct sk_buff *skb,
456  					struct genl_info *info)
457  {
458  	struct devlink *devlink = info->user_ptr[0];
459  	struct devlink_health_reporter *reporter;
460  
461  	reporter = devlink_health_reporter_get_from_info(devlink, info);
462  	if (!reporter)
463  		return -EINVAL;
464  
465  	if (!reporter->ops->recover &&
466  	    (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] ||
467  	     info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]))
468  		return -EOPNOTSUPP;
469  
470  	if (!reporter->ops->dump &&
471  	    info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
472  		return -EOPNOTSUPP;
473  
474  	if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD])
475  		reporter->graceful_period =
476  			nla_get_u64(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD]);
477  
478  	if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])
479  		reporter->auto_recover =
480  			nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]);
481  
482  	if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
483  		reporter->auto_dump =
484  		nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP]);
485  
486  	return 0;
487  }
488  
devlink_recover_notify(struct devlink_health_reporter * reporter,enum devlink_command cmd)489  static void devlink_recover_notify(struct devlink_health_reporter *reporter,
490  				   enum devlink_command cmd)
491  {
492  	struct devlink *devlink = reporter->devlink;
493  	struct devlink_obj_desc desc;
494  	struct sk_buff *msg;
495  	int err;
496  
497  	WARN_ON(cmd != DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
498  	ASSERT_DEVLINK_REGISTERED(devlink);
499  
500  	if (!devlink_nl_notify_need(devlink))
501  		return;
502  
503  	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
504  	if (!msg)
505  		return;
506  
507  	err = devlink_nl_health_reporter_fill(msg, reporter, cmd, 0, 0, 0);
508  	if (err) {
509  		nlmsg_free(msg);
510  		return;
511  	}
512  
513  	devlink_nl_obj_desc_init(&desc, devlink);
514  	if (reporter->devlink_port)
515  		devlink_nl_obj_desc_port_set(&desc, reporter->devlink_port);
516  	devlink_nl_notify_send_desc(devlink, msg, &desc);
517  }
518  
519  void
devlink_health_reporter_recovery_done(struct devlink_health_reporter * reporter)520  devlink_health_reporter_recovery_done(struct devlink_health_reporter *reporter)
521  {
522  	reporter->recovery_count++;
523  	reporter->last_recovery_ts = jiffies;
524  }
525  EXPORT_SYMBOL_GPL(devlink_health_reporter_recovery_done);
526  
527  static int
devlink_health_reporter_recover(struct devlink_health_reporter * reporter,void * priv_ctx,struct netlink_ext_ack * extack)528  devlink_health_reporter_recover(struct devlink_health_reporter *reporter,
529  				void *priv_ctx, struct netlink_ext_ack *extack)
530  {
531  	int err;
532  
533  	if (reporter->health_state == DEVLINK_HEALTH_REPORTER_STATE_HEALTHY)
534  		return 0;
535  
536  	if (!reporter->ops->recover)
537  		return -EOPNOTSUPP;
538  
539  	err = reporter->ops->recover(reporter, priv_ctx, extack);
540  	if (err)
541  		return err;
542  
543  	devlink_health_reporter_recovery_done(reporter);
544  	reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_HEALTHY;
545  	devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
546  
547  	return 0;
548  }
549  
550  static void
devlink_health_dump_clear(struct devlink_health_reporter * reporter)551  devlink_health_dump_clear(struct devlink_health_reporter *reporter)
552  {
553  	if (!reporter->dump_fmsg)
554  		return;
555  	devlink_fmsg_free(reporter->dump_fmsg);
556  	reporter->dump_fmsg = NULL;
557  }
558  
devlink_health_do_dump(struct devlink_health_reporter * reporter,void * priv_ctx,struct netlink_ext_ack * extack)559  static int devlink_health_do_dump(struct devlink_health_reporter *reporter,
560  				  void *priv_ctx,
561  				  struct netlink_ext_ack *extack)
562  {
563  	int err;
564  
565  	if (!reporter->ops->dump)
566  		return 0;
567  
568  	if (reporter->dump_fmsg)
569  		return 0;
570  
571  	reporter->dump_fmsg = devlink_fmsg_alloc();
572  	if (!reporter->dump_fmsg)
573  		return -ENOMEM;
574  
575  	devlink_fmsg_obj_nest_start(reporter->dump_fmsg);
576  
577  	err = reporter->ops->dump(reporter, reporter->dump_fmsg,
578  				  priv_ctx, extack);
579  	if (err)
580  		goto dump_err;
581  
582  	devlink_fmsg_obj_nest_end(reporter->dump_fmsg);
583  	err = reporter->dump_fmsg->err;
584  	if (err)
585  		goto dump_err;
586  
587  	reporter->dump_ts = jiffies;
588  	reporter->dump_real_ts = ktime_get_real_ns();
589  
590  	return 0;
591  
592  dump_err:
593  	devlink_health_dump_clear(reporter);
594  	return err;
595  }
596  
devlink_health_report(struct devlink_health_reporter * reporter,const char * msg,void * priv_ctx)597  int devlink_health_report(struct devlink_health_reporter *reporter,
598  			  const char *msg, void *priv_ctx)
599  {
600  	enum devlink_health_reporter_state prev_health_state;
601  	struct devlink *devlink = reporter->devlink;
602  	unsigned long recover_ts_threshold;
603  	int ret;
604  
605  	/* write a log message of the current error */
606  	WARN_ON(!msg);
607  	trace_devlink_health_report(devlink, reporter->ops->name, msg);
608  	reporter->error_count++;
609  	prev_health_state = reporter->health_state;
610  	reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_ERROR;
611  	devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
612  
613  	/* abort if the previous error wasn't recovered */
614  	recover_ts_threshold = reporter->last_recovery_ts +
615  			       msecs_to_jiffies(reporter->graceful_period);
616  	if (reporter->auto_recover &&
617  	    (prev_health_state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY ||
618  	     (reporter->last_recovery_ts && reporter->recovery_count &&
619  	      time_is_after_jiffies(recover_ts_threshold)))) {
620  		trace_devlink_health_recover_aborted(devlink,
621  						     reporter->ops->name,
622  						     reporter->health_state,
623  						     jiffies -
624  						     reporter->last_recovery_ts);
625  		return -ECANCELED;
626  	}
627  
628  	if (reporter->auto_dump) {
629  		devl_lock(devlink);
630  		/* store current dump of current error, for later analysis */
631  		devlink_health_do_dump(reporter, priv_ctx, NULL);
632  		devl_unlock(devlink);
633  	}
634  
635  	if (!reporter->auto_recover)
636  		return 0;
637  
638  	devl_lock(devlink);
639  	ret = devlink_health_reporter_recover(reporter, priv_ctx, NULL);
640  	devl_unlock(devlink);
641  
642  	return ret;
643  }
644  EXPORT_SYMBOL_GPL(devlink_health_report);
645  
646  void
devlink_health_reporter_state_update(struct devlink_health_reporter * reporter,enum devlink_health_reporter_state state)647  devlink_health_reporter_state_update(struct devlink_health_reporter *reporter,
648  				     enum devlink_health_reporter_state state)
649  {
650  	if (WARN_ON(state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY &&
651  		    state != DEVLINK_HEALTH_REPORTER_STATE_ERROR))
652  		return;
653  
654  	if (reporter->health_state == state)
655  		return;
656  
657  	reporter->health_state = state;
658  	trace_devlink_health_reporter_state_update(reporter->devlink,
659  						   reporter->ops->name, state);
660  	devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
661  }
662  EXPORT_SYMBOL_GPL(devlink_health_reporter_state_update);
663  
devlink_nl_health_reporter_recover_doit(struct sk_buff * skb,struct genl_info * info)664  int devlink_nl_health_reporter_recover_doit(struct sk_buff *skb,
665  					    struct genl_info *info)
666  {
667  	struct devlink *devlink = info->user_ptr[0];
668  	struct devlink_health_reporter *reporter;
669  
670  	reporter = devlink_health_reporter_get_from_info(devlink, info);
671  	if (!reporter)
672  		return -EINVAL;
673  
674  	return devlink_health_reporter_recover(reporter, NULL, info->extack);
675  }
676  
devlink_fmsg_err_if_binary(struct devlink_fmsg * fmsg)677  static void devlink_fmsg_err_if_binary(struct devlink_fmsg *fmsg)
678  {
679  	if (!fmsg->err && fmsg->putting_binary)
680  		fmsg->err = -EINVAL;
681  }
682  
devlink_fmsg_nest_common(struct devlink_fmsg * fmsg,int attrtype)683  static void devlink_fmsg_nest_common(struct devlink_fmsg *fmsg, int attrtype)
684  {
685  	struct devlink_fmsg_item *item;
686  
687  	if (fmsg->err)
688  		return;
689  
690  	item = kzalloc(sizeof(*item), GFP_KERNEL);
691  	if (!item) {
692  		fmsg->err = -ENOMEM;
693  		return;
694  	}
695  
696  	item->attrtype = attrtype;
697  	list_add_tail(&item->list, &fmsg->item_list);
698  }
699  
devlink_fmsg_obj_nest_start(struct devlink_fmsg * fmsg)700  void devlink_fmsg_obj_nest_start(struct devlink_fmsg *fmsg)
701  {
702  	devlink_fmsg_err_if_binary(fmsg);
703  	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_OBJ_NEST_START);
704  }
705  EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_start);
706  
devlink_fmsg_nest_end(struct devlink_fmsg * fmsg)707  static void devlink_fmsg_nest_end(struct devlink_fmsg *fmsg)
708  {
709  	devlink_fmsg_err_if_binary(fmsg);
710  	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_NEST_END);
711  }
712  
devlink_fmsg_obj_nest_end(struct devlink_fmsg * fmsg)713  void devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg)
714  {
715  	devlink_fmsg_nest_end(fmsg);
716  }
717  EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_end);
718  
719  #define DEVLINK_FMSG_MAX_SIZE (GENLMSG_DEFAULT_SIZE - GENL_HDRLEN - NLA_HDRLEN)
720  
devlink_fmsg_put_name(struct devlink_fmsg * fmsg,const char * name)721  static void devlink_fmsg_put_name(struct devlink_fmsg *fmsg, const char *name)
722  {
723  	struct devlink_fmsg_item *item;
724  
725  	devlink_fmsg_err_if_binary(fmsg);
726  	if (fmsg->err)
727  		return;
728  
729  	if (strlen(name) + 1 > DEVLINK_FMSG_MAX_SIZE) {
730  		fmsg->err = -EMSGSIZE;
731  		return;
732  	}
733  
734  	item = kzalloc(sizeof(*item) + strlen(name) + 1, GFP_KERNEL);
735  	if (!item) {
736  		fmsg->err = -ENOMEM;
737  		return;
738  	}
739  
740  	item->nla_type = NLA_NUL_STRING;
741  	item->len = strlen(name) + 1;
742  	item->attrtype = DEVLINK_ATTR_FMSG_OBJ_NAME;
743  	memcpy(&item->value, name, item->len);
744  	list_add_tail(&item->list, &fmsg->item_list);
745  }
746  
devlink_fmsg_pair_nest_start(struct devlink_fmsg * fmsg,const char * name)747  void devlink_fmsg_pair_nest_start(struct devlink_fmsg *fmsg, const char *name)
748  {
749  	devlink_fmsg_err_if_binary(fmsg);
750  	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_PAIR_NEST_START);
751  	devlink_fmsg_put_name(fmsg, name);
752  }
753  EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_start);
754  
devlink_fmsg_pair_nest_end(struct devlink_fmsg * fmsg)755  void devlink_fmsg_pair_nest_end(struct devlink_fmsg *fmsg)
756  {
757  	devlink_fmsg_nest_end(fmsg);
758  }
759  EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_end);
760  
devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg * fmsg,const char * name)761  void devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg *fmsg,
762  				      const char *name)
763  {
764  	devlink_fmsg_pair_nest_start(fmsg, name);
765  	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_ARR_NEST_START);
766  }
767  EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_start);
768  
devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg * fmsg)769  void devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg *fmsg)
770  {
771  	devlink_fmsg_nest_end(fmsg);
772  	devlink_fmsg_nest_end(fmsg);
773  }
774  EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_end);
775  
devlink_fmsg_binary_pair_nest_start(struct devlink_fmsg * fmsg,const char * name)776  void devlink_fmsg_binary_pair_nest_start(struct devlink_fmsg *fmsg,
777  					 const char *name)
778  {
779  	devlink_fmsg_arr_pair_nest_start(fmsg, name);
780  	fmsg->putting_binary = true;
781  }
782  EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_start);
783  
devlink_fmsg_binary_pair_nest_end(struct devlink_fmsg * fmsg)784  void devlink_fmsg_binary_pair_nest_end(struct devlink_fmsg *fmsg)
785  {
786  	if (fmsg->err)
787  		return;
788  
789  	if (!fmsg->putting_binary)
790  		fmsg->err = -EINVAL;
791  
792  	fmsg->putting_binary = false;
793  	devlink_fmsg_arr_pair_nest_end(fmsg);
794  }
795  EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_end);
796  
devlink_fmsg_put_value(struct devlink_fmsg * fmsg,const void * value,u16 value_len,u8 value_nla_type)797  static void devlink_fmsg_put_value(struct devlink_fmsg *fmsg,
798  				   const void *value, u16 value_len,
799  				   u8 value_nla_type)
800  {
801  	struct devlink_fmsg_item *item;
802  
803  	if (fmsg->err)
804  		return;
805  
806  	if (value_len > DEVLINK_FMSG_MAX_SIZE) {
807  		fmsg->err = -EMSGSIZE;
808  		return;
809  	}
810  
811  	item = kzalloc(sizeof(*item) + value_len, GFP_KERNEL);
812  	if (!item) {
813  		fmsg->err = -ENOMEM;
814  		return;
815  	}
816  
817  	item->nla_type = value_nla_type;
818  	item->len = value_len;
819  	item->attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
820  	memcpy(&item->value, value, item->len);
821  	list_add_tail(&item->list, &fmsg->item_list);
822  }
823  
devlink_fmsg_bool_put(struct devlink_fmsg * fmsg,bool value)824  static void devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value)
825  {
826  	devlink_fmsg_err_if_binary(fmsg);
827  	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_FLAG);
828  }
829  
devlink_fmsg_u8_put(struct devlink_fmsg * fmsg,u8 value)830  static void devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value)
831  {
832  	devlink_fmsg_err_if_binary(fmsg);
833  	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U8);
834  }
835  
devlink_fmsg_u32_put(struct devlink_fmsg * fmsg,u32 value)836  void devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value)
837  {
838  	devlink_fmsg_err_if_binary(fmsg);
839  	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U32);
840  }
841  EXPORT_SYMBOL_GPL(devlink_fmsg_u32_put);
842  
devlink_fmsg_u64_put(struct devlink_fmsg * fmsg,u64 value)843  static void devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value)
844  {
845  	devlink_fmsg_err_if_binary(fmsg);
846  	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U64);
847  }
848  
devlink_fmsg_string_put(struct devlink_fmsg * fmsg,const char * value)849  void devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value)
850  {
851  	devlink_fmsg_err_if_binary(fmsg);
852  	devlink_fmsg_put_value(fmsg, value, strlen(value) + 1, NLA_NUL_STRING);
853  }
854  EXPORT_SYMBOL_GPL(devlink_fmsg_string_put);
855  
devlink_fmsg_binary_put(struct devlink_fmsg * fmsg,const void * value,u16 value_len)856  void devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value,
857  			     u16 value_len)
858  {
859  	if (!fmsg->err && !fmsg->putting_binary)
860  		fmsg->err = -EINVAL;
861  
862  	devlink_fmsg_put_value(fmsg, value, value_len, NLA_BINARY);
863  }
864  EXPORT_SYMBOL_GPL(devlink_fmsg_binary_put);
865  
devlink_fmsg_bool_pair_put(struct devlink_fmsg * fmsg,const char * name,bool value)866  void devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name,
867  				bool value)
868  {
869  	devlink_fmsg_pair_nest_start(fmsg, name);
870  	devlink_fmsg_bool_put(fmsg, value);
871  	devlink_fmsg_pair_nest_end(fmsg);
872  }
873  EXPORT_SYMBOL_GPL(devlink_fmsg_bool_pair_put);
874  
devlink_fmsg_u8_pair_put(struct devlink_fmsg * fmsg,const char * name,u8 value)875  void devlink_fmsg_u8_pair_put(struct devlink_fmsg *fmsg, const char *name,
876  			      u8 value)
877  {
878  	devlink_fmsg_pair_nest_start(fmsg, name);
879  	devlink_fmsg_u8_put(fmsg, value);
880  	devlink_fmsg_pair_nest_end(fmsg);
881  }
882  EXPORT_SYMBOL_GPL(devlink_fmsg_u8_pair_put);
883  
devlink_fmsg_u32_pair_put(struct devlink_fmsg * fmsg,const char * name,u32 value)884  void devlink_fmsg_u32_pair_put(struct devlink_fmsg *fmsg, const char *name,
885  			       u32 value)
886  {
887  	devlink_fmsg_pair_nest_start(fmsg, name);
888  	devlink_fmsg_u32_put(fmsg, value);
889  	devlink_fmsg_pair_nest_end(fmsg);
890  }
891  EXPORT_SYMBOL_GPL(devlink_fmsg_u32_pair_put);
892  
devlink_fmsg_u64_pair_put(struct devlink_fmsg * fmsg,const char * name,u64 value)893  void devlink_fmsg_u64_pair_put(struct devlink_fmsg *fmsg, const char *name,
894  			       u64 value)
895  {
896  	devlink_fmsg_pair_nest_start(fmsg, name);
897  	devlink_fmsg_u64_put(fmsg, value);
898  	devlink_fmsg_pair_nest_end(fmsg);
899  }
900  EXPORT_SYMBOL_GPL(devlink_fmsg_u64_pair_put);
901  
devlink_fmsg_string_pair_put(struct devlink_fmsg * fmsg,const char * name,const char * value)902  void devlink_fmsg_string_pair_put(struct devlink_fmsg *fmsg, const char *name,
903  				  const char *value)
904  {
905  	devlink_fmsg_pair_nest_start(fmsg, name);
906  	devlink_fmsg_string_put(fmsg, value);
907  	devlink_fmsg_pair_nest_end(fmsg);
908  }
909  EXPORT_SYMBOL_GPL(devlink_fmsg_string_pair_put);
910  
devlink_fmsg_binary_pair_put(struct devlink_fmsg * fmsg,const char * name,const void * value,u32 value_len)911  void devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name,
912  				  const void *value, u32 value_len)
913  {
914  	u32 data_size;
915  	u32 offset;
916  
917  	devlink_fmsg_binary_pair_nest_start(fmsg, name);
918  
919  	for (offset = 0; offset < value_len; offset += data_size) {
920  		data_size = value_len - offset;
921  		if (data_size > DEVLINK_FMSG_MAX_SIZE)
922  			data_size = DEVLINK_FMSG_MAX_SIZE;
923  
924  		devlink_fmsg_binary_put(fmsg, value + offset, data_size);
925  	}
926  
927  	devlink_fmsg_binary_pair_nest_end(fmsg);
928  	fmsg->putting_binary = false;
929  }
930  EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_put);
931  
932  static int
devlink_fmsg_item_fill_type(struct devlink_fmsg_item * msg,struct sk_buff * skb)933  devlink_fmsg_item_fill_type(struct devlink_fmsg_item *msg, struct sk_buff *skb)
934  {
935  	switch (msg->nla_type) {
936  	case NLA_FLAG:
937  	case NLA_U8:
938  	case NLA_U32:
939  	case NLA_U64:
940  	case NLA_NUL_STRING:
941  	case NLA_BINARY:
942  		return nla_put_u8(skb, DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE,
943  				  msg->nla_type);
944  	default:
945  		return -EINVAL;
946  	}
947  }
948  
949  static int
devlink_fmsg_item_fill_data(struct devlink_fmsg_item * msg,struct sk_buff * skb)950  devlink_fmsg_item_fill_data(struct devlink_fmsg_item *msg, struct sk_buff *skb)
951  {
952  	int attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
953  	u8 tmp;
954  
955  	switch (msg->nla_type) {
956  	case NLA_FLAG:
957  		/* Always provide flag data, regardless of its value */
958  		tmp = *(bool *)msg->value;
959  
960  		return nla_put_u8(skb, attrtype, tmp);
961  	case NLA_U8:
962  		return nla_put_u8(skb, attrtype, *(u8 *)msg->value);
963  	case NLA_U32:
964  		return nla_put_u32(skb, attrtype, *(u32 *)msg->value);
965  	case NLA_U64:
966  		return nla_put_u64_64bit(skb, attrtype, *(u64 *)msg->value,
967  					 DEVLINK_ATTR_PAD);
968  	case NLA_NUL_STRING:
969  		return nla_put_string(skb, attrtype, (char *)&msg->value);
970  	case NLA_BINARY:
971  		return nla_put(skb, attrtype, msg->len, (void *)&msg->value);
972  	default:
973  		return -EINVAL;
974  	}
975  }
976  
977  static int
devlink_fmsg_prepare_skb(struct devlink_fmsg * fmsg,struct sk_buff * skb,int * start)978  devlink_fmsg_prepare_skb(struct devlink_fmsg *fmsg, struct sk_buff *skb,
979  			 int *start)
980  {
981  	struct devlink_fmsg_item *item;
982  	struct nlattr *fmsg_nlattr;
983  	int err = 0;
984  	int i = 0;
985  
986  	fmsg_nlattr = nla_nest_start_noflag(skb, DEVLINK_ATTR_FMSG);
987  	if (!fmsg_nlattr)
988  		return -EMSGSIZE;
989  
990  	list_for_each_entry(item, &fmsg->item_list, list) {
991  		if (i < *start) {
992  			i++;
993  			continue;
994  		}
995  
996  		switch (item->attrtype) {
997  		case DEVLINK_ATTR_FMSG_OBJ_NEST_START:
998  		case DEVLINK_ATTR_FMSG_PAIR_NEST_START:
999  		case DEVLINK_ATTR_FMSG_ARR_NEST_START:
1000  		case DEVLINK_ATTR_FMSG_NEST_END:
1001  			err = nla_put_flag(skb, item->attrtype);
1002  			break;
1003  		case DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA:
1004  			err = devlink_fmsg_item_fill_type(item, skb);
1005  			if (err)
1006  				break;
1007  			err = devlink_fmsg_item_fill_data(item, skb);
1008  			break;
1009  		case DEVLINK_ATTR_FMSG_OBJ_NAME:
1010  			err = nla_put_string(skb, item->attrtype,
1011  					     (char *)&item->value);
1012  			break;
1013  		default:
1014  			err = -EINVAL;
1015  			break;
1016  		}
1017  		if (!err)
1018  			*start = ++i;
1019  		else
1020  			break;
1021  	}
1022  
1023  	nla_nest_end(skb, fmsg_nlattr);
1024  	return err;
1025  }
1026  
devlink_fmsg_snd(struct devlink_fmsg * fmsg,struct genl_info * info,enum devlink_command cmd,int flags)1027  static int devlink_fmsg_snd(struct devlink_fmsg *fmsg,
1028  			    struct genl_info *info,
1029  			    enum devlink_command cmd, int flags)
1030  {
1031  	struct nlmsghdr *nlh;
1032  	struct sk_buff *skb;
1033  	bool last = false;
1034  	int index = 0;
1035  	void *hdr;
1036  	int err;
1037  
1038  	if (fmsg->err)
1039  		return fmsg->err;
1040  
1041  	while (!last) {
1042  		int tmp_index = index;
1043  
1044  		skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1045  		if (!skb)
1046  			return -ENOMEM;
1047  
1048  		hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
1049  				  &devlink_nl_family, flags | NLM_F_MULTI, cmd);
1050  		if (!hdr) {
1051  			err = -EMSGSIZE;
1052  			goto nla_put_failure;
1053  		}
1054  
1055  		err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1056  		if (!err)
1057  			last = true;
1058  		else if (err != -EMSGSIZE || tmp_index == index)
1059  			goto nla_put_failure;
1060  
1061  		genlmsg_end(skb, hdr);
1062  		err = genlmsg_reply(skb, info);
1063  		if (err)
1064  			return err;
1065  	}
1066  
1067  	skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1068  	if (!skb)
1069  		return -ENOMEM;
1070  	nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
1071  			NLMSG_DONE, 0, flags | NLM_F_MULTI);
1072  	if (!nlh) {
1073  		err = -EMSGSIZE;
1074  		goto nla_put_failure;
1075  	}
1076  
1077  	return genlmsg_reply(skb, info);
1078  
1079  nla_put_failure:
1080  	nlmsg_free(skb);
1081  	return err;
1082  }
1083  
devlink_fmsg_dumpit(struct devlink_fmsg * fmsg,struct sk_buff * skb,struct netlink_callback * cb,enum devlink_command cmd)1084  static int devlink_fmsg_dumpit(struct devlink_fmsg *fmsg, struct sk_buff *skb,
1085  			       struct netlink_callback *cb,
1086  			       enum devlink_command cmd)
1087  {
1088  	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
1089  	int index = state->idx;
1090  	int tmp_index = index;
1091  	void *hdr;
1092  	int err;
1093  
1094  	if (fmsg->err)
1095  		return fmsg->err;
1096  
1097  	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
1098  			  &devlink_nl_family, NLM_F_ACK | NLM_F_MULTI, cmd);
1099  	if (!hdr) {
1100  		err = -EMSGSIZE;
1101  		goto nla_put_failure;
1102  	}
1103  
1104  	err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1105  	if ((err && err != -EMSGSIZE) || tmp_index == index)
1106  		goto nla_put_failure;
1107  
1108  	state->idx = index;
1109  	genlmsg_end(skb, hdr);
1110  	return skb->len;
1111  
1112  nla_put_failure:
1113  	genlmsg_cancel(skb, hdr);
1114  	return err;
1115  }
1116  
devlink_nl_health_reporter_diagnose_doit(struct sk_buff * skb,struct genl_info * info)1117  int devlink_nl_health_reporter_diagnose_doit(struct sk_buff *skb,
1118  					     struct genl_info *info)
1119  {
1120  	struct devlink *devlink = info->user_ptr[0];
1121  	struct devlink_health_reporter *reporter;
1122  	struct devlink_fmsg *fmsg;
1123  	int err;
1124  
1125  	reporter = devlink_health_reporter_get_from_info(devlink, info);
1126  	if (!reporter)
1127  		return -EINVAL;
1128  
1129  	if (!reporter->ops->diagnose)
1130  		return -EOPNOTSUPP;
1131  
1132  	fmsg = devlink_fmsg_alloc();
1133  	if (!fmsg)
1134  		return -ENOMEM;
1135  
1136  	devlink_fmsg_obj_nest_start(fmsg);
1137  
1138  	err = reporter->ops->diagnose(reporter, fmsg, info->extack);
1139  	if (err)
1140  		goto out;
1141  
1142  	devlink_fmsg_obj_nest_end(fmsg);
1143  
1144  	err = devlink_fmsg_snd(fmsg, info,
1145  			       DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, 0);
1146  
1147  out:
1148  	devlink_fmsg_free(fmsg);
1149  	return err;
1150  }
1151  
1152  static struct devlink_health_reporter *
devlink_health_reporter_get_from_cb_lock(struct netlink_callback * cb)1153  devlink_health_reporter_get_from_cb_lock(struct netlink_callback *cb)
1154  {
1155  	const struct genl_info *info = genl_info_dump(cb);
1156  	struct devlink_health_reporter *reporter;
1157  	struct nlattr **attrs = info->attrs;
1158  	struct devlink *devlink;
1159  
1160  	devlink = devlink_get_from_attrs_lock(sock_net(cb->skb->sk), attrs,
1161  					      false);
1162  	if (IS_ERR(devlink))
1163  		return NULL;
1164  
1165  	reporter = devlink_health_reporter_get_from_attrs(devlink, attrs);
1166  	if (!reporter) {
1167  		devl_unlock(devlink);
1168  		devlink_put(devlink);
1169  	}
1170  	return reporter;
1171  }
1172  
devlink_nl_health_reporter_dump_get_dumpit(struct sk_buff * skb,struct netlink_callback * cb)1173  int devlink_nl_health_reporter_dump_get_dumpit(struct sk_buff *skb,
1174  					       struct netlink_callback *cb)
1175  {
1176  	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
1177  	struct devlink_health_reporter *reporter;
1178  	struct devlink *devlink;
1179  	int err;
1180  
1181  	reporter = devlink_health_reporter_get_from_cb_lock(cb);
1182  	if (!reporter)
1183  		return -EINVAL;
1184  
1185  	devlink = reporter->devlink;
1186  	if (!reporter->ops->dump) {
1187  		devl_unlock(devlink);
1188  		devlink_put(devlink);
1189  		return -EOPNOTSUPP;
1190  	}
1191  
1192  	if (!state->idx) {
1193  		err = devlink_health_do_dump(reporter, NULL, cb->extack);
1194  		if (err)
1195  			goto unlock;
1196  		state->dump_ts = reporter->dump_ts;
1197  	}
1198  	if (!reporter->dump_fmsg || state->dump_ts != reporter->dump_ts) {
1199  		NL_SET_ERR_MSG(cb->extack, "Dump trampled, please retry");
1200  		err = -EAGAIN;
1201  		goto unlock;
1202  	}
1203  
1204  	err = devlink_fmsg_dumpit(reporter->dump_fmsg, skb, cb,
1205  				  DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET);
1206  unlock:
1207  	devl_unlock(devlink);
1208  	devlink_put(devlink);
1209  	return err;
1210  }
1211  
devlink_nl_health_reporter_dump_clear_doit(struct sk_buff * skb,struct genl_info * info)1212  int devlink_nl_health_reporter_dump_clear_doit(struct sk_buff *skb,
1213  					       struct genl_info *info)
1214  {
1215  	struct devlink *devlink = info->user_ptr[0];
1216  	struct devlink_health_reporter *reporter;
1217  
1218  	reporter = devlink_health_reporter_get_from_info(devlink, info);
1219  	if (!reporter)
1220  		return -EINVAL;
1221  
1222  	if (!reporter->ops->dump)
1223  		return -EOPNOTSUPP;
1224  
1225  	devlink_health_dump_clear(reporter);
1226  	return 0;
1227  }
1228  
devlink_nl_health_reporter_test_doit(struct sk_buff * skb,struct genl_info * info)1229  int devlink_nl_health_reporter_test_doit(struct sk_buff *skb,
1230  					 struct genl_info *info)
1231  {
1232  	struct devlink *devlink = info->user_ptr[0];
1233  	struct devlink_health_reporter *reporter;
1234  
1235  	reporter = devlink_health_reporter_get_from_info(devlink, info);
1236  	if (!reporter)
1237  		return -EINVAL;
1238  
1239  	if (!reporter->ops->test)
1240  		return -EOPNOTSUPP;
1241  
1242  	return reporter->ops->test(reporter, info->extack);
1243  }
1244