xref: /linux/net/devlink/health.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
1b4740e3aSMoshe Shemesh // SPDX-License-Identifier: GPL-2.0-or-later
2b4740e3aSMoshe Shemesh /*
3b4740e3aSMoshe Shemesh  * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4b4740e3aSMoshe Shemesh  * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5b4740e3aSMoshe Shemesh  */
6b4740e3aSMoshe Shemesh 
7b4740e3aSMoshe Shemesh #include <net/genetlink.h>
87004c6c4SMoshe Shemesh #include <net/sock.h>
955b9b249SMoshe Shemesh #include <trace/events/devlink.h>
10b4740e3aSMoshe Shemesh #include "devl_internal.h"
11b4740e3aSMoshe Shemesh 
12a929df7fSMoshe Shemesh struct devlink_fmsg_item {
13a929df7fSMoshe Shemesh 	struct list_head list;
14a929df7fSMoshe Shemesh 	int attrtype;
15a929df7fSMoshe Shemesh 	u8 nla_type;
16a929df7fSMoshe Shemesh 	u16 len;
17a929df7fSMoshe Shemesh 	int value[];
18a929df7fSMoshe Shemesh };
19a929df7fSMoshe Shemesh 
20a929df7fSMoshe Shemesh struct devlink_fmsg {
21a929df7fSMoshe Shemesh 	struct list_head item_list;
22db80d3b2SPrzemek Kitszel 	int err; /* first error encountered on some devlink_fmsg_XXX() call */
23a929df7fSMoshe Shemesh 	bool putting_binary; /* This flag forces enclosing of binary data
24a929df7fSMoshe Shemesh 			      * in an array brackets. It forces using
25a929df7fSMoshe Shemesh 			      * of designated API:
26a929df7fSMoshe Shemesh 			      * devlink_fmsg_binary_pair_nest_start()
27a929df7fSMoshe Shemesh 			      * devlink_fmsg_binary_pair_nest_end()
28a929df7fSMoshe Shemesh 			      */
29a929df7fSMoshe Shemesh };
30a929df7fSMoshe Shemesh 
devlink_fmsg_alloc(void)3112af29e7SMoshe Shemesh static struct devlink_fmsg *devlink_fmsg_alloc(void)
32a929df7fSMoshe Shemesh {
33a929df7fSMoshe Shemesh 	struct devlink_fmsg *fmsg;
34a929df7fSMoshe Shemesh 
35a929df7fSMoshe Shemesh 	fmsg = kzalloc(sizeof(*fmsg), GFP_KERNEL);
36a929df7fSMoshe Shemesh 	if (!fmsg)
37a929df7fSMoshe Shemesh 		return NULL;
38a929df7fSMoshe Shemesh 
39a929df7fSMoshe Shemesh 	INIT_LIST_HEAD(&fmsg->item_list);
40a929df7fSMoshe Shemesh 
41a929df7fSMoshe Shemesh 	return fmsg;
42a929df7fSMoshe Shemesh }
43a929df7fSMoshe Shemesh 
devlink_fmsg_free(struct devlink_fmsg * fmsg)4412af29e7SMoshe Shemesh static void devlink_fmsg_free(struct devlink_fmsg *fmsg)
45a929df7fSMoshe Shemesh {
46a929df7fSMoshe Shemesh 	struct devlink_fmsg_item *item, *tmp;
47a929df7fSMoshe Shemesh 
48a929df7fSMoshe Shemesh 	list_for_each_entry_safe(item, tmp, &fmsg->item_list, list) {
49a929df7fSMoshe Shemesh 		list_del(&item->list);
50a929df7fSMoshe Shemesh 		kfree(item);
51a929df7fSMoshe Shemesh 	}
52a929df7fSMoshe Shemesh 	kfree(fmsg);
53a929df7fSMoshe Shemesh }
54a929df7fSMoshe Shemesh 
5512af29e7SMoshe Shemesh struct devlink_health_reporter {
5612af29e7SMoshe Shemesh 	struct list_head list;
5712af29e7SMoshe Shemesh 	void *priv;
5812af29e7SMoshe Shemesh 	const struct devlink_health_reporter_ops *ops;
5912af29e7SMoshe Shemesh 	struct devlink *devlink;
6012af29e7SMoshe Shemesh 	struct devlink_port *devlink_port;
6112af29e7SMoshe Shemesh 	struct devlink_fmsg *dump_fmsg;
6212af29e7SMoshe Shemesh 	u64 graceful_period;
6312af29e7SMoshe Shemesh 	bool auto_recover;
6412af29e7SMoshe Shemesh 	bool auto_dump;
6512af29e7SMoshe Shemesh 	u8 health_state;
6612af29e7SMoshe Shemesh 	u64 dump_ts;
6712af29e7SMoshe Shemesh 	u64 dump_real_ts;
6812af29e7SMoshe Shemesh 	u64 error_count;
6912af29e7SMoshe Shemesh 	u64 recovery_count;
7012af29e7SMoshe Shemesh 	u64 last_recovery_ts;
7112af29e7SMoshe Shemesh };
7212af29e7SMoshe Shemesh 
73b4740e3aSMoshe Shemesh void *
devlink_health_reporter_priv(struct devlink_health_reporter * reporter)74b4740e3aSMoshe Shemesh devlink_health_reporter_priv(struct devlink_health_reporter *reporter)
75b4740e3aSMoshe Shemesh {
76b4740e3aSMoshe Shemesh 	return reporter->priv;
77b4740e3aSMoshe Shemesh }
78b4740e3aSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_health_reporter_priv);
79b4740e3aSMoshe Shemesh 
80b4740e3aSMoshe Shemesh static struct devlink_health_reporter *
__devlink_health_reporter_find_by_name(struct list_head * reporter_list,const char * reporter_name)81b4740e3aSMoshe Shemesh __devlink_health_reporter_find_by_name(struct list_head *reporter_list,
82b4740e3aSMoshe Shemesh 				       const char *reporter_name)
83b4740e3aSMoshe Shemesh {
84b4740e3aSMoshe Shemesh 	struct devlink_health_reporter *reporter;
85b4740e3aSMoshe Shemesh 
86b4740e3aSMoshe Shemesh 	list_for_each_entry(reporter, reporter_list, list)
87b4740e3aSMoshe Shemesh 		if (!strcmp(reporter->ops->name, reporter_name))
88b4740e3aSMoshe Shemesh 			return reporter;
89b4740e3aSMoshe Shemesh 	return NULL;
90b4740e3aSMoshe Shemesh }
91b4740e3aSMoshe Shemesh 
9212af29e7SMoshe Shemesh static struct devlink_health_reporter *
devlink_health_reporter_find_by_name(struct devlink * devlink,const char * reporter_name)93b4740e3aSMoshe Shemesh devlink_health_reporter_find_by_name(struct devlink *devlink,
94b4740e3aSMoshe Shemesh 				     const char *reporter_name)
95b4740e3aSMoshe Shemesh {
96b4740e3aSMoshe Shemesh 	return __devlink_health_reporter_find_by_name(&devlink->reporter_list,
97b4740e3aSMoshe Shemesh 						      reporter_name);
98b4740e3aSMoshe Shemesh }
99b4740e3aSMoshe Shemesh 
10012af29e7SMoshe Shemesh static struct devlink_health_reporter *
devlink_port_health_reporter_find_by_name(struct devlink_port * devlink_port,const char * reporter_name)101b4740e3aSMoshe Shemesh devlink_port_health_reporter_find_by_name(struct devlink_port *devlink_port,
102b4740e3aSMoshe Shemesh 					  const char *reporter_name)
103b4740e3aSMoshe Shemesh {
104b4740e3aSMoshe Shemesh 	return __devlink_health_reporter_find_by_name(&devlink_port->reporter_list,
105b4740e3aSMoshe Shemesh 						      reporter_name);
106b4740e3aSMoshe Shemesh }
107b4740e3aSMoshe Shemesh 
108b4740e3aSMoshe Shemesh static struct devlink_health_reporter *
__devlink_health_reporter_create(struct devlink * devlink,const struct devlink_health_reporter_ops * ops,u64 graceful_period,void * priv)109b4740e3aSMoshe Shemesh __devlink_health_reporter_create(struct devlink *devlink,
110b4740e3aSMoshe Shemesh 				 const struct devlink_health_reporter_ops *ops,
111b4740e3aSMoshe Shemesh 				 u64 graceful_period, void *priv)
112b4740e3aSMoshe Shemesh {
113b4740e3aSMoshe Shemesh 	struct devlink_health_reporter *reporter;
114b4740e3aSMoshe Shemesh 
115b4740e3aSMoshe Shemesh 	if (WARN_ON(graceful_period && !ops->recover))
116b4740e3aSMoshe Shemesh 		return ERR_PTR(-EINVAL);
117b4740e3aSMoshe Shemesh 
118b4740e3aSMoshe Shemesh 	reporter = kzalloc(sizeof(*reporter), GFP_KERNEL);
119b4740e3aSMoshe Shemesh 	if (!reporter)
120b4740e3aSMoshe Shemesh 		return ERR_PTR(-ENOMEM);
121b4740e3aSMoshe Shemesh 
122b4740e3aSMoshe Shemesh 	reporter->priv = priv;
123b4740e3aSMoshe Shemesh 	reporter->ops = ops;
124b4740e3aSMoshe Shemesh 	reporter->devlink = devlink;
125b4740e3aSMoshe Shemesh 	reporter->graceful_period = graceful_period;
126b4740e3aSMoshe Shemesh 	reporter->auto_recover = !!ops->recover;
127b4740e3aSMoshe Shemesh 	reporter->auto_dump = !!ops->dump;
128b4740e3aSMoshe Shemesh 	return reporter;
129b4740e3aSMoshe Shemesh }
130b4740e3aSMoshe Shemesh 
131b4740e3aSMoshe Shemesh /**
132b4740e3aSMoshe Shemesh  * devl_port_health_reporter_create() - create devlink health reporter for
133b4740e3aSMoshe Shemesh  *                                      specified port instance
134b4740e3aSMoshe Shemesh  *
135b4740e3aSMoshe Shemesh  * @port: devlink_port to which health reports will relate
136b4740e3aSMoshe Shemesh  * @ops: devlink health reporter ops
137b4740e3aSMoshe Shemesh  * @graceful_period: min time (in msec) between recovery attempts
138b4740e3aSMoshe Shemesh  * @priv: driver priv pointer
139b4740e3aSMoshe Shemesh  */
140b4740e3aSMoshe Shemesh 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)141b4740e3aSMoshe Shemesh devl_port_health_reporter_create(struct devlink_port *port,
142b4740e3aSMoshe Shemesh 				 const struct devlink_health_reporter_ops *ops,
143b4740e3aSMoshe Shemesh 				 u64 graceful_period, void *priv)
144b4740e3aSMoshe Shemesh {
145b4740e3aSMoshe Shemesh 	struct devlink_health_reporter *reporter;
146b4740e3aSMoshe Shemesh 
147b4740e3aSMoshe Shemesh 	devl_assert_locked(port->devlink);
148b4740e3aSMoshe Shemesh 
149b4740e3aSMoshe Shemesh 	if (__devlink_health_reporter_find_by_name(&port->reporter_list,
150b4740e3aSMoshe Shemesh 						   ops->name))
151b4740e3aSMoshe Shemesh 		return ERR_PTR(-EEXIST);
152b4740e3aSMoshe Shemesh 
153b4740e3aSMoshe Shemesh 	reporter = __devlink_health_reporter_create(port->devlink, ops,
154b4740e3aSMoshe Shemesh 						    graceful_period, priv);
155b4740e3aSMoshe Shemesh 	if (IS_ERR(reporter))
156b4740e3aSMoshe Shemesh 		return reporter;
157b4740e3aSMoshe Shemesh 
158b4740e3aSMoshe Shemesh 	reporter->devlink_port = port;
159b4740e3aSMoshe Shemesh 	list_add_tail(&reporter->list, &port->reporter_list);
160b4740e3aSMoshe Shemesh 	return reporter;
161b4740e3aSMoshe Shemesh }
162b4740e3aSMoshe Shemesh EXPORT_SYMBOL_GPL(devl_port_health_reporter_create);
163b4740e3aSMoshe Shemesh 
164b4740e3aSMoshe Shemesh 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)165b4740e3aSMoshe Shemesh devlink_port_health_reporter_create(struct devlink_port *port,
166b4740e3aSMoshe Shemesh 				    const struct devlink_health_reporter_ops *ops,
167b4740e3aSMoshe Shemesh 				    u64 graceful_period, void *priv)
168b4740e3aSMoshe Shemesh {
169b4740e3aSMoshe Shemesh 	struct devlink_health_reporter *reporter;
170b4740e3aSMoshe Shemesh 	struct devlink *devlink = port->devlink;
171b4740e3aSMoshe Shemesh 
172b4740e3aSMoshe Shemesh 	devl_lock(devlink);
173b4740e3aSMoshe Shemesh 	reporter = devl_port_health_reporter_create(port, ops,
174b4740e3aSMoshe Shemesh 						    graceful_period, priv);
175b4740e3aSMoshe Shemesh 	devl_unlock(devlink);
176b4740e3aSMoshe Shemesh 	return reporter;
177b4740e3aSMoshe Shemesh }
178b4740e3aSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_port_health_reporter_create);
179b4740e3aSMoshe Shemesh 
180b4740e3aSMoshe Shemesh /**
181b4740e3aSMoshe Shemesh  * devl_health_reporter_create - create devlink health reporter
182b4740e3aSMoshe Shemesh  *
183b4740e3aSMoshe Shemesh  * @devlink: devlink instance which the health reports will relate
184b4740e3aSMoshe Shemesh  * @ops: devlink health reporter ops
185b4740e3aSMoshe Shemesh  * @graceful_period: min time (in msec) between recovery attempts
186b4740e3aSMoshe Shemesh  * @priv: driver priv pointer
187b4740e3aSMoshe Shemesh  */
188b4740e3aSMoshe Shemesh struct devlink_health_reporter *
devl_health_reporter_create(struct devlink * devlink,const struct devlink_health_reporter_ops * ops,u64 graceful_period,void * priv)189b4740e3aSMoshe Shemesh devl_health_reporter_create(struct devlink *devlink,
190b4740e3aSMoshe Shemesh 			    const struct devlink_health_reporter_ops *ops,
191b4740e3aSMoshe Shemesh 			    u64 graceful_period, void *priv)
192b4740e3aSMoshe Shemesh {
193b4740e3aSMoshe Shemesh 	struct devlink_health_reporter *reporter;
194b4740e3aSMoshe Shemesh 
195b4740e3aSMoshe Shemesh 	devl_assert_locked(devlink);
196b4740e3aSMoshe Shemesh 
197b4740e3aSMoshe Shemesh 	if (devlink_health_reporter_find_by_name(devlink, ops->name))
198b4740e3aSMoshe Shemesh 		return ERR_PTR(-EEXIST);
199b4740e3aSMoshe Shemesh 
200b4740e3aSMoshe Shemesh 	reporter = __devlink_health_reporter_create(devlink, ops,
201b4740e3aSMoshe Shemesh 						    graceful_period, priv);
202b4740e3aSMoshe Shemesh 	if (IS_ERR(reporter))
203b4740e3aSMoshe Shemesh 		return reporter;
204b4740e3aSMoshe Shemesh 
205b4740e3aSMoshe Shemesh 	list_add_tail(&reporter->list, &devlink->reporter_list);
206b4740e3aSMoshe Shemesh 	return reporter;
207b4740e3aSMoshe Shemesh }
208b4740e3aSMoshe Shemesh EXPORT_SYMBOL_GPL(devl_health_reporter_create);
209b4740e3aSMoshe Shemesh 
210b4740e3aSMoshe Shemesh struct devlink_health_reporter *
devlink_health_reporter_create(struct devlink * devlink,const struct devlink_health_reporter_ops * ops,u64 graceful_period,void * priv)211b4740e3aSMoshe Shemesh devlink_health_reporter_create(struct devlink *devlink,
212b4740e3aSMoshe Shemesh 			       const struct devlink_health_reporter_ops *ops,
213b4740e3aSMoshe Shemesh 			       u64 graceful_period, void *priv)
214b4740e3aSMoshe Shemesh {
215b4740e3aSMoshe Shemesh 	struct devlink_health_reporter *reporter;
216b4740e3aSMoshe Shemesh 
217b4740e3aSMoshe Shemesh 	devl_lock(devlink);
218b4740e3aSMoshe Shemesh 	reporter = devl_health_reporter_create(devlink, ops,
219b4740e3aSMoshe Shemesh 					       graceful_period, priv);
220b4740e3aSMoshe Shemesh 	devl_unlock(devlink);
221b4740e3aSMoshe Shemesh 	return reporter;
222b4740e3aSMoshe Shemesh }
223b4740e3aSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_health_reporter_create);
224b4740e3aSMoshe Shemesh 
225b4740e3aSMoshe Shemesh static void
devlink_health_reporter_free(struct devlink_health_reporter * reporter)226b4740e3aSMoshe Shemesh devlink_health_reporter_free(struct devlink_health_reporter *reporter)
227b4740e3aSMoshe Shemesh {
228b4740e3aSMoshe Shemesh 	if (reporter->dump_fmsg)
229b4740e3aSMoshe Shemesh 		devlink_fmsg_free(reporter->dump_fmsg);
230b4740e3aSMoshe Shemesh 	kfree(reporter);
231b4740e3aSMoshe Shemesh }
232b4740e3aSMoshe Shemesh 
233b4740e3aSMoshe Shemesh /**
234b4740e3aSMoshe Shemesh  * devl_health_reporter_destroy() - destroy devlink health reporter
235b4740e3aSMoshe Shemesh  *
236b4740e3aSMoshe Shemesh  * @reporter: devlink health reporter to destroy
237b4740e3aSMoshe Shemesh  */
238b4740e3aSMoshe Shemesh void
devl_health_reporter_destroy(struct devlink_health_reporter * reporter)239b4740e3aSMoshe Shemesh devl_health_reporter_destroy(struct devlink_health_reporter *reporter)
240b4740e3aSMoshe Shemesh {
241b4740e3aSMoshe Shemesh 	devl_assert_locked(reporter->devlink);
242b4740e3aSMoshe Shemesh 
243b4740e3aSMoshe Shemesh 	list_del(&reporter->list);
244b4740e3aSMoshe Shemesh 	devlink_health_reporter_free(reporter);
245b4740e3aSMoshe Shemesh }
246b4740e3aSMoshe Shemesh EXPORT_SYMBOL_GPL(devl_health_reporter_destroy);
247b4740e3aSMoshe Shemesh 
248b4740e3aSMoshe Shemesh void
devlink_health_reporter_destroy(struct devlink_health_reporter * reporter)249b4740e3aSMoshe Shemesh devlink_health_reporter_destroy(struct devlink_health_reporter *reporter)
250b4740e3aSMoshe Shemesh {
251b4740e3aSMoshe Shemesh 	struct devlink *devlink = reporter->devlink;
252b4740e3aSMoshe Shemesh 
253b4740e3aSMoshe Shemesh 	devl_lock(devlink);
254b4740e3aSMoshe Shemesh 	devl_health_reporter_destroy(reporter);
255b4740e3aSMoshe Shemesh 	devl_unlock(devlink);
256b4740e3aSMoshe Shemesh }
257b4740e3aSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_health_reporter_destroy);
258db6b5f3eSMoshe Shemesh 
25912af29e7SMoshe Shemesh 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)260db6b5f3eSMoshe Shemesh devlink_nl_health_reporter_fill(struct sk_buff *msg,
261db6b5f3eSMoshe Shemesh 				struct devlink_health_reporter *reporter,
262db6b5f3eSMoshe Shemesh 				enum devlink_command cmd, u32 portid,
263db6b5f3eSMoshe Shemesh 				u32 seq, int flags)
264db6b5f3eSMoshe Shemesh {
265db6b5f3eSMoshe Shemesh 	struct devlink *devlink = reporter->devlink;
266db6b5f3eSMoshe Shemesh 	struct nlattr *reporter_attr;
267db6b5f3eSMoshe Shemesh 	void *hdr;
268db6b5f3eSMoshe Shemesh 
269db6b5f3eSMoshe Shemesh 	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
270db6b5f3eSMoshe Shemesh 	if (!hdr)
271db6b5f3eSMoshe Shemesh 		return -EMSGSIZE;
272db6b5f3eSMoshe Shemesh 
273db6b5f3eSMoshe Shemesh 	if (devlink_nl_put_handle(msg, devlink))
274db6b5f3eSMoshe Shemesh 		goto genlmsg_cancel;
275db6b5f3eSMoshe Shemesh 
276db6b5f3eSMoshe Shemesh 	if (reporter->devlink_port) {
277db6b5f3eSMoshe Shemesh 		if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, reporter->devlink_port->index))
278db6b5f3eSMoshe Shemesh 			goto genlmsg_cancel;
279db6b5f3eSMoshe Shemesh 	}
280db6b5f3eSMoshe Shemesh 	reporter_attr = nla_nest_start_noflag(msg,
281db6b5f3eSMoshe Shemesh 					      DEVLINK_ATTR_HEALTH_REPORTER);
282db6b5f3eSMoshe Shemesh 	if (!reporter_attr)
283db6b5f3eSMoshe Shemesh 		goto genlmsg_cancel;
284db6b5f3eSMoshe Shemesh 	if (nla_put_string(msg, DEVLINK_ATTR_HEALTH_REPORTER_NAME,
285db6b5f3eSMoshe Shemesh 			   reporter->ops->name))
286db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
287db6b5f3eSMoshe Shemesh 	if (nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_STATE,
288db6b5f3eSMoshe Shemesh 		       reporter->health_state))
289db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
290db6b5f3eSMoshe Shemesh 	if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT,
291db6b5f3eSMoshe Shemesh 			      reporter->error_count, DEVLINK_ATTR_PAD))
292db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
293db6b5f3eSMoshe Shemesh 	if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT,
294db6b5f3eSMoshe Shemesh 			      reporter->recovery_count, DEVLINK_ATTR_PAD))
295db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
296db6b5f3eSMoshe Shemesh 	if (reporter->ops->recover &&
297db6b5f3eSMoshe Shemesh 	    nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD,
298db6b5f3eSMoshe Shemesh 			      reporter->graceful_period,
299db6b5f3eSMoshe Shemesh 			      DEVLINK_ATTR_PAD))
300db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
301db6b5f3eSMoshe Shemesh 	if (reporter->ops->recover &&
302db6b5f3eSMoshe Shemesh 	    nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
303db6b5f3eSMoshe Shemesh 		       reporter->auto_recover))
304db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
305db6b5f3eSMoshe Shemesh 	if (reporter->dump_fmsg &&
306db6b5f3eSMoshe Shemesh 	    nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS,
307db6b5f3eSMoshe Shemesh 			      jiffies_to_msecs(reporter->dump_ts),
308db6b5f3eSMoshe Shemesh 			      DEVLINK_ATTR_PAD))
309db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
310db6b5f3eSMoshe Shemesh 	if (reporter->dump_fmsg &&
311db6b5f3eSMoshe Shemesh 	    nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS,
312db6b5f3eSMoshe Shemesh 			      reporter->dump_real_ts, DEVLINK_ATTR_PAD))
313db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
314db6b5f3eSMoshe Shemesh 	if (reporter->ops->dump &&
315db6b5f3eSMoshe Shemesh 	    nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP,
316db6b5f3eSMoshe Shemesh 		       reporter->auto_dump))
317db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
318db6b5f3eSMoshe Shemesh 
319db6b5f3eSMoshe Shemesh 	nla_nest_end(msg, reporter_attr);
320db6b5f3eSMoshe Shemesh 	genlmsg_end(msg, hdr);
321db6b5f3eSMoshe Shemesh 	return 0;
322db6b5f3eSMoshe Shemesh 
323db6b5f3eSMoshe Shemesh reporter_nest_cancel:
324db6b5f3eSMoshe Shemesh 	nla_nest_cancel(msg, reporter_attr);
325db6b5f3eSMoshe Shemesh genlmsg_cancel:
326db6b5f3eSMoshe Shemesh 	genlmsg_cancel(msg, hdr);
327db6b5f3eSMoshe Shemesh 	return -EMSGSIZE;
328db6b5f3eSMoshe Shemesh }
329db6b5f3eSMoshe Shemesh 
33012af29e7SMoshe Shemesh static struct devlink_health_reporter *
devlink_health_reporter_get_from_attrs(struct devlink * devlink,struct nlattr ** attrs)331db6b5f3eSMoshe Shemesh devlink_health_reporter_get_from_attrs(struct devlink *devlink,
332db6b5f3eSMoshe Shemesh 				       struct nlattr **attrs)
333db6b5f3eSMoshe Shemesh {
334db6b5f3eSMoshe Shemesh 	struct devlink_port *devlink_port;
335db6b5f3eSMoshe Shemesh 	char *reporter_name;
336db6b5f3eSMoshe Shemesh 
337db6b5f3eSMoshe Shemesh 	if (!attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME])
338db6b5f3eSMoshe Shemesh 		return NULL;
339db6b5f3eSMoshe Shemesh 
340db6b5f3eSMoshe Shemesh 	reporter_name = nla_data(attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME]);
341db6b5f3eSMoshe Shemesh 	devlink_port = devlink_port_get_from_attrs(devlink, attrs);
342db6b5f3eSMoshe Shemesh 	if (IS_ERR(devlink_port))
343db6b5f3eSMoshe Shemesh 		return devlink_health_reporter_find_by_name(devlink,
344db6b5f3eSMoshe Shemesh 							    reporter_name);
345db6b5f3eSMoshe Shemesh 	else
346db6b5f3eSMoshe Shemesh 		return devlink_port_health_reporter_find_by_name(devlink_port,
347db6b5f3eSMoshe Shemesh 								 reporter_name);
348db6b5f3eSMoshe Shemesh }
349db6b5f3eSMoshe Shemesh 
35012af29e7SMoshe Shemesh static struct devlink_health_reporter *
devlink_health_reporter_get_from_info(struct devlink * devlink,struct genl_info * info)351db6b5f3eSMoshe Shemesh devlink_health_reporter_get_from_info(struct devlink *devlink,
352db6b5f3eSMoshe Shemesh 				      struct genl_info *info)
353db6b5f3eSMoshe Shemesh {
354db6b5f3eSMoshe Shemesh 	return devlink_health_reporter_get_from_attrs(devlink, info->attrs);
355db6b5f3eSMoshe Shemesh }
356db6b5f3eSMoshe Shemesh 
devlink_nl_health_reporter_get_doit(struct sk_buff * skb,struct genl_info * info)3578fa995adSJiri Pirko int devlink_nl_health_reporter_get_doit(struct sk_buff *skb,
358db6b5f3eSMoshe Shemesh 					struct genl_info *info)
359db6b5f3eSMoshe Shemesh {
360db6b5f3eSMoshe Shemesh 	struct devlink *devlink = info->user_ptr[0];
361db6b5f3eSMoshe Shemesh 	struct devlink_health_reporter *reporter;
362db6b5f3eSMoshe Shemesh 	struct sk_buff *msg;
363db6b5f3eSMoshe Shemesh 	int err;
364db6b5f3eSMoshe Shemesh 
365db6b5f3eSMoshe Shemesh 	reporter = devlink_health_reporter_get_from_info(devlink, info);
366db6b5f3eSMoshe Shemesh 	if (!reporter)
367db6b5f3eSMoshe Shemesh 		return -EINVAL;
368db6b5f3eSMoshe Shemesh 
369db6b5f3eSMoshe Shemesh 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
370db6b5f3eSMoshe Shemesh 	if (!msg)
371db6b5f3eSMoshe Shemesh 		return -ENOMEM;
372db6b5f3eSMoshe Shemesh 
373db6b5f3eSMoshe Shemesh 	err = devlink_nl_health_reporter_fill(msg, reporter,
374db6b5f3eSMoshe Shemesh 					      DEVLINK_CMD_HEALTH_REPORTER_GET,
375db6b5f3eSMoshe Shemesh 					      info->snd_portid, info->snd_seq,
376db6b5f3eSMoshe Shemesh 					      0);
377db6b5f3eSMoshe Shemesh 	if (err) {
378db6b5f3eSMoshe Shemesh 		nlmsg_free(msg);
379db6b5f3eSMoshe Shemesh 		return err;
380db6b5f3eSMoshe Shemesh 	}
381db6b5f3eSMoshe Shemesh 
382db6b5f3eSMoshe Shemesh 	return genlmsg_reply(msg, info);
383db6b5f3eSMoshe Shemesh }
384db6b5f3eSMoshe Shemesh 
devlink_nl_health_reporter_get_dump_one(struct sk_buff * msg,struct devlink * devlink,struct netlink_callback * cb,int flags)38524c8e56dSJiri Pirko static int devlink_nl_health_reporter_get_dump_one(struct sk_buff *msg,
386db6b5f3eSMoshe Shemesh 						   struct devlink *devlink,
3877d3c6fecSJiri Pirko 						   struct netlink_callback *cb,
3887d3c6fecSJiri Pirko 						   int flags)
389db6b5f3eSMoshe Shemesh {
390db6b5f3eSMoshe Shemesh 	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
3917288dd2fSJakub Kicinski 	const struct genl_info *info = genl_info_dump(cb);
392db6b5f3eSMoshe Shemesh 	struct devlink_health_reporter *reporter;
393b03f13cbSJiri Pirko 	unsigned long port_index_end = ULONG_MAX;
394b03f13cbSJiri Pirko 	struct nlattr **attrs = info->attrs;
395b03f13cbSJiri Pirko 	unsigned long port_index_start = 0;
396db6b5f3eSMoshe Shemesh 	struct devlink_port *port;
397db6b5f3eSMoshe Shemesh 	unsigned long port_index;
398db6b5f3eSMoshe Shemesh 	int idx = 0;
399db6b5f3eSMoshe Shemesh 	int err;
400db6b5f3eSMoshe Shemesh 
401b03f13cbSJiri Pirko 	if (attrs && attrs[DEVLINK_ATTR_PORT_INDEX]) {
402b03f13cbSJiri Pirko 		port_index_start = nla_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]);
403b03f13cbSJiri Pirko 		port_index_end = port_index_start;
404b03f13cbSJiri Pirko 		flags |= NLM_F_DUMP_FILTERED;
405b03f13cbSJiri Pirko 		goto per_port_dump;
406b03f13cbSJiri Pirko 	}
407b03f13cbSJiri Pirko 
408db6b5f3eSMoshe Shemesh 	list_for_each_entry(reporter, &devlink->reporter_list, list) {
409db6b5f3eSMoshe Shemesh 		if (idx < state->idx) {
410db6b5f3eSMoshe Shemesh 			idx++;
411db6b5f3eSMoshe Shemesh 			continue;
412db6b5f3eSMoshe Shemesh 		}
413db6b5f3eSMoshe Shemesh 		err = devlink_nl_health_reporter_fill(msg, reporter,
414db6b5f3eSMoshe Shemesh 						      DEVLINK_CMD_HEALTH_REPORTER_GET,
415db6b5f3eSMoshe Shemesh 						      NETLINK_CB(cb->skb).portid,
416db6b5f3eSMoshe Shemesh 						      cb->nlh->nlmsg_seq,
4177d3c6fecSJiri Pirko 						      flags);
418db6b5f3eSMoshe Shemesh 		if (err) {
419db6b5f3eSMoshe Shemesh 			state->idx = idx;
420db6b5f3eSMoshe Shemesh 			return err;
421db6b5f3eSMoshe Shemesh 		}
422db6b5f3eSMoshe Shemesh 		idx++;
423db6b5f3eSMoshe Shemesh 	}
424b03f13cbSJiri Pirko per_port_dump:
425b03f13cbSJiri Pirko 	xa_for_each_range(&devlink->ports, port_index, port,
426b03f13cbSJiri Pirko 			  port_index_start, port_index_end) {
427db6b5f3eSMoshe Shemesh 		list_for_each_entry(reporter, &port->reporter_list, list) {
428db6b5f3eSMoshe Shemesh 			if (idx < state->idx) {
429db6b5f3eSMoshe Shemesh 				idx++;
430db6b5f3eSMoshe Shemesh 				continue;
431db6b5f3eSMoshe Shemesh 			}
432db6b5f3eSMoshe Shemesh 			err = devlink_nl_health_reporter_fill(msg, reporter,
433db6b5f3eSMoshe Shemesh 							      DEVLINK_CMD_HEALTH_REPORTER_GET,
434db6b5f3eSMoshe Shemesh 							      NETLINK_CB(cb->skb).portid,
435db6b5f3eSMoshe Shemesh 							      cb->nlh->nlmsg_seq,
4367d3c6fecSJiri Pirko 							      flags);
437db6b5f3eSMoshe Shemesh 			if (err) {
438db6b5f3eSMoshe Shemesh 				state->idx = idx;
439db6b5f3eSMoshe Shemesh 				return err;
440db6b5f3eSMoshe Shemesh 			}
441db6b5f3eSMoshe Shemesh 			idx++;
442db6b5f3eSMoshe Shemesh 		}
443db6b5f3eSMoshe Shemesh 	}
444db6b5f3eSMoshe Shemesh 
445db6b5f3eSMoshe Shemesh 	return 0;
446db6b5f3eSMoshe Shemesh }
447db6b5f3eSMoshe Shemesh 
devlink_nl_health_reporter_get_dumpit(struct sk_buff * skb,struct netlink_callback * cb)44824c8e56dSJiri Pirko int devlink_nl_health_reporter_get_dumpit(struct sk_buff *skb,
44924c8e56dSJiri Pirko 					  struct netlink_callback *cb)
45024c8e56dSJiri Pirko {
45124c8e56dSJiri Pirko 	return devlink_nl_dumpit(skb, cb,
45224c8e56dSJiri Pirko 				 devlink_nl_health_reporter_get_dump_one);
45324c8e56dSJiri Pirko }
454db6b5f3eSMoshe Shemesh 
devlink_nl_health_reporter_set_doit(struct sk_buff * skb,struct genl_info * info)45553590934SJiri Pirko int devlink_nl_health_reporter_set_doit(struct sk_buff *skb,
456db6b5f3eSMoshe Shemesh 					struct genl_info *info)
457db6b5f3eSMoshe Shemesh {
458db6b5f3eSMoshe Shemesh 	struct devlink *devlink = info->user_ptr[0];
459db6b5f3eSMoshe Shemesh 	struct devlink_health_reporter *reporter;
460db6b5f3eSMoshe Shemesh 
461db6b5f3eSMoshe Shemesh 	reporter = devlink_health_reporter_get_from_info(devlink, info);
462db6b5f3eSMoshe Shemesh 	if (!reporter)
463db6b5f3eSMoshe Shemesh 		return -EINVAL;
464db6b5f3eSMoshe Shemesh 
465db6b5f3eSMoshe Shemesh 	if (!reporter->ops->recover &&
466db6b5f3eSMoshe Shemesh 	    (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] ||
467db6b5f3eSMoshe Shemesh 	     info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]))
468db6b5f3eSMoshe Shemesh 		return -EOPNOTSUPP;
469db6b5f3eSMoshe Shemesh 
470db6b5f3eSMoshe Shemesh 	if (!reporter->ops->dump &&
471db6b5f3eSMoshe Shemesh 	    info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
472db6b5f3eSMoshe Shemesh 		return -EOPNOTSUPP;
473db6b5f3eSMoshe Shemesh 
474db6b5f3eSMoshe Shemesh 	if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD])
475db6b5f3eSMoshe Shemesh 		reporter->graceful_period =
476db6b5f3eSMoshe Shemesh 			nla_get_u64(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD]);
477db6b5f3eSMoshe Shemesh 
478db6b5f3eSMoshe Shemesh 	if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])
479db6b5f3eSMoshe Shemesh 		reporter->auto_recover =
480db6b5f3eSMoshe Shemesh 			nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]);
481db6b5f3eSMoshe Shemesh 
482db6b5f3eSMoshe Shemesh 	if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
483db6b5f3eSMoshe Shemesh 		reporter->auto_dump =
484db6b5f3eSMoshe Shemesh 		nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP]);
485db6b5f3eSMoshe Shemesh 
486db6b5f3eSMoshe Shemesh 	return 0;
487db6b5f3eSMoshe Shemesh }
48855b9b249SMoshe Shemesh 
devlink_recover_notify(struct devlink_health_reporter * reporter,enum devlink_command cmd)48955b9b249SMoshe Shemesh static void devlink_recover_notify(struct devlink_health_reporter *reporter,
49055b9b249SMoshe Shemesh 				   enum devlink_command cmd)
49155b9b249SMoshe Shemesh {
49255b9b249SMoshe Shemesh 	struct devlink *devlink = reporter->devlink;
493*ded6f77cSJiri Pirko 	struct devlink_obj_desc desc;
49455b9b249SMoshe Shemesh 	struct sk_buff *msg;
49555b9b249SMoshe Shemesh 	int err;
49655b9b249SMoshe Shemesh 
49755b9b249SMoshe Shemesh 	WARN_ON(cmd != DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
4986f4b9814SJakub Kicinski 	ASSERT_DEVLINK_REGISTERED(devlink);
49955b9b249SMoshe Shemesh 
500cddbff47SJiri Pirko 	if (!devlink_nl_notify_need(devlink))
501cddbff47SJiri Pirko 		return;
502cddbff47SJiri Pirko 
50355b9b249SMoshe Shemesh 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
50455b9b249SMoshe Shemesh 	if (!msg)
50555b9b249SMoshe Shemesh 		return;
50655b9b249SMoshe Shemesh 
50755b9b249SMoshe Shemesh 	err = devlink_nl_health_reporter_fill(msg, reporter, cmd, 0, 0, 0);
50855b9b249SMoshe Shemesh 	if (err) {
50955b9b249SMoshe Shemesh 		nlmsg_free(msg);
51055b9b249SMoshe Shemesh 		return;
51155b9b249SMoshe Shemesh 	}
51255b9b249SMoshe Shemesh 
513*ded6f77cSJiri Pirko 	devlink_nl_obj_desc_init(&desc, devlink);
514*ded6f77cSJiri Pirko 	if (reporter->devlink_port)
515*ded6f77cSJiri Pirko 		devlink_nl_obj_desc_port_set(&desc, reporter->devlink_port);
516*ded6f77cSJiri Pirko 	devlink_nl_notify_send_desc(devlink, msg, &desc);
51755b9b249SMoshe Shemesh }
51855b9b249SMoshe Shemesh 
51955b9b249SMoshe Shemesh void
devlink_health_reporter_recovery_done(struct devlink_health_reporter * reporter)52055b9b249SMoshe Shemesh devlink_health_reporter_recovery_done(struct devlink_health_reporter *reporter)
52155b9b249SMoshe Shemesh {
52255b9b249SMoshe Shemesh 	reporter->recovery_count++;
52355b9b249SMoshe Shemesh 	reporter->last_recovery_ts = jiffies;
52455b9b249SMoshe Shemesh }
52555b9b249SMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_health_reporter_recovery_done);
52655b9b249SMoshe Shemesh 
52755b9b249SMoshe Shemesh static int
devlink_health_reporter_recover(struct devlink_health_reporter * reporter,void * priv_ctx,struct netlink_ext_ack * extack)52855b9b249SMoshe Shemesh devlink_health_reporter_recover(struct devlink_health_reporter *reporter,
52955b9b249SMoshe Shemesh 				void *priv_ctx, struct netlink_ext_ack *extack)
53055b9b249SMoshe Shemesh {
53155b9b249SMoshe Shemesh 	int err;
53255b9b249SMoshe Shemesh 
53355b9b249SMoshe Shemesh 	if (reporter->health_state == DEVLINK_HEALTH_REPORTER_STATE_HEALTHY)
53455b9b249SMoshe Shemesh 		return 0;
53555b9b249SMoshe Shemesh 
53655b9b249SMoshe Shemesh 	if (!reporter->ops->recover)
53755b9b249SMoshe Shemesh 		return -EOPNOTSUPP;
53855b9b249SMoshe Shemesh 
53955b9b249SMoshe Shemesh 	err = reporter->ops->recover(reporter, priv_ctx, extack);
54055b9b249SMoshe Shemesh 	if (err)
54155b9b249SMoshe Shemesh 		return err;
54255b9b249SMoshe Shemesh 
54355b9b249SMoshe Shemesh 	devlink_health_reporter_recovery_done(reporter);
54455b9b249SMoshe Shemesh 	reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_HEALTHY;
54555b9b249SMoshe Shemesh 	devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
54655b9b249SMoshe Shemesh 
54755b9b249SMoshe Shemesh 	return 0;
54855b9b249SMoshe Shemesh }
54955b9b249SMoshe Shemesh 
5507004c6c4SMoshe Shemesh static void
devlink_health_dump_clear(struct devlink_health_reporter * reporter)5517004c6c4SMoshe Shemesh devlink_health_dump_clear(struct devlink_health_reporter *reporter)
5527004c6c4SMoshe Shemesh {
5537004c6c4SMoshe Shemesh 	if (!reporter->dump_fmsg)
5547004c6c4SMoshe Shemesh 		return;
5557004c6c4SMoshe Shemesh 	devlink_fmsg_free(reporter->dump_fmsg);
5567004c6c4SMoshe Shemesh 	reporter->dump_fmsg = NULL;
5577004c6c4SMoshe Shemesh }
5587004c6c4SMoshe Shemesh 
devlink_health_do_dump(struct devlink_health_reporter * reporter,void * priv_ctx,struct netlink_ext_ack * extack)55912af29e7SMoshe Shemesh static int devlink_health_do_dump(struct devlink_health_reporter *reporter,
5607004c6c4SMoshe Shemesh 				  void *priv_ctx,
5617004c6c4SMoshe Shemesh 				  struct netlink_ext_ack *extack)
5627004c6c4SMoshe Shemesh {
5637004c6c4SMoshe Shemesh 	int err;
5647004c6c4SMoshe Shemesh 
5657004c6c4SMoshe Shemesh 	if (!reporter->ops->dump)
5667004c6c4SMoshe Shemesh 		return 0;
5677004c6c4SMoshe Shemesh 
5687004c6c4SMoshe Shemesh 	if (reporter->dump_fmsg)
5697004c6c4SMoshe Shemesh 		return 0;
5707004c6c4SMoshe Shemesh 
5717004c6c4SMoshe Shemesh 	reporter->dump_fmsg = devlink_fmsg_alloc();
572db80d3b2SPrzemek Kitszel 	if (!reporter->dump_fmsg)
573db80d3b2SPrzemek Kitszel 		return -ENOMEM;
5747004c6c4SMoshe Shemesh 
5750050629cSPrzemek Kitszel 	devlink_fmsg_obj_nest_start(reporter->dump_fmsg);
5767004c6c4SMoshe Shemesh 
5777004c6c4SMoshe Shemesh 	err = reporter->ops->dump(reporter, reporter->dump_fmsg,
5787004c6c4SMoshe Shemesh 				  priv_ctx, extack);
5797004c6c4SMoshe Shemesh 	if (err)
5807004c6c4SMoshe Shemesh 		goto dump_err;
5817004c6c4SMoshe Shemesh 
5820050629cSPrzemek Kitszel 	devlink_fmsg_obj_nest_end(reporter->dump_fmsg);
5830050629cSPrzemek Kitszel 	err = reporter->dump_fmsg->err;
5847004c6c4SMoshe Shemesh 	if (err)
5857004c6c4SMoshe Shemesh 		goto dump_err;
5867004c6c4SMoshe Shemesh 
5877004c6c4SMoshe Shemesh 	reporter->dump_ts = jiffies;
5887004c6c4SMoshe Shemesh 	reporter->dump_real_ts = ktime_get_real_ns();
5897004c6c4SMoshe Shemesh 
5907004c6c4SMoshe Shemesh 	return 0;
5917004c6c4SMoshe Shemesh 
5927004c6c4SMoshe Shemesh dump_err:
5937004c6c4SMoshe Shemesh 	devlink_health_dump_clear(reporter);
5947004c6c4SMoshe Shemesh 	return err;
5957004c6c4SMoshe Shemesh }
5967004c6c4SMoshe Shemesh 
devlink_health_report(struct devlink_health_reporter * reporter,const char * msg,void * priv_ctx)59755b9b249SMoshe Shemesh int devlink_health_report(struct devlink_health_reporter *reporter,
59855b9b249SMoshe Shemesh 			  const char *msg, void *priv_ctx)
59955b9b249SMoshe Shemesh {
60055b9b249SMoshe Shemesh 	enum devlink_health_reporter_state prev_health_state;
60155b9b249SMoshe Shemesh 	struct devlink *devlink = reporter->devlink;
60255b9b249SMoshe Shemesh 	unsigned long recover_ts_threshold;
60355b9b249SMoshe Shemesh 	int ret;
60455b9b249SMoshe Shemesh 
60555b9b249SMoshe Shemesh 	/* write a log message of the current error */
60655b9b249SMoshe Shemesh 	WARN_ON(!msg);
60755b9b249SMoshe Shemesh 	trace_devlink_health_report(devlink, reporter->ops->name, msg);
60855b9b249SMoshe Shemesh 	reporter->error_count++;
60955b9b249SMoshe Shemesh 	prev_health_state = reporter->health_state;
61055b9b249SMoshe Shemesh 	reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_ERROR;
61155b9b249SMoshe Shemesh 	devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
61255b9b249SMoshe Shemesh 
61355b9b249SMoshe Shemesh 	/* abort if the previous error wasn't recovered */
61455b9b249SMoshe Shemesh 	recover_ts_threshold = reporter->last_recovery_ts +
61555b9b249SMoshe Shemesh 			       msecs_to_jiffies(reporter->graceful_period);
61655b9b249SMoshe Shemesh 	if (reporter->auto_recover &&
61755b9b249SMoshe Shemesh 	    (prev_health_state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY ||
61855b9b249SMoshe Shemesh 	     (reporter->last_recovery_ts && reporter->recovery_count &&
61955b9b249SMoshe Shemesh 	      time_is_after_jiffies(recover_ts_threshold)))) {
62055b9b249SMoshe Shemesh 		trace_devlink_health_recover_aborted(devlink,
62155b9b249SMoshe Shemesh 						     reporter->ops->name,
62255b9b249SMoshe Shemesh 						     reporter->health_state,
62355b9b249SMoshe Shemesh 						     jiffies -
62455b9b249SMoshe Shemesh 						     reporter->last_recovery_ts);
62555b9b249SMoshe Shemesh 		return -ECANCELED;
62655b9b249SMoshe Shemesh 	}
62755b9b249SMoshe Shemesh 
62855b9b249SMoshe Shemesh 	if (reporter->auto_dump) {
629aba0e909SMoshe Shemesh 		devl_lock(devlink);
63055b9b249SMoshe Shemesh 		/* store current dump of current error, for later analysis */
63155b9b249SMoshe Shemesh 		devlink_health_do_dump(reporter, priv_ctx, NULL);
632aba0e909SMoshe Shemesh 		devl_unlock(devlink);
63355b9b249SMoshe Shemesh 	}
63455b9b249SMoshe Shemesh 
63555b9b249SMoshe Shemesh 	if (!reporter->auto_recover)
63655b9b249SMoshe Shemesh 		return 0;
63755b9b249SMoshe Shemesh 
63855b9b249SMoshe Shemesh 	devl_lock(devlink);
63955b9b249SMoshe Shemesh 	ret = devlink_health_reporter_recover(reporter, priv_ctx, NULL);
64055b9b249SMoshe Shemesh 	devl_unlock(devlink);
64155b9b249SMoshe Shemesh 
64255b9b249SMoshe Shemesh 	return ret;
64355b9b249SMoshe Shemesh }
64455b9b249SMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_health_report);
64555b9b249SMoshe Shemesh 
64655b9b249SMoshe Shemesh void
devlink_health_reporter_state_update(struct devlink_health_reporter * reporter,enum devlink_health_reporter_state state)64755b9b249SMoshe Shemesh devlink_health_reporter_state_update(struct devlink_health_reporter *reporter,
64855b9b249SMoshe Shemesh 				     enum devlink_health_reporter_state state)
64955b9b249SMoshe Shemesh {
65055b9b249SMoshe Shemesh 	if (WARN_ON(state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY &&
65155b9b249SMoshe Shemesh 		    state != DEVLINK_HEALTH_REPORTER_STATE_ERROR))
65255b9b249SMoshe Shemesh 		return;
65355b9b249SMoshe Shemesh 
65455b9b249SMoshe Shemesh 	if (reporter->health_state == state)
65555b9b249SMoshe Shemesh 		return;
65655b9b249SMoshe Shemesh 
65755b9b249SMoshe Shemesh 	reporter->health_state = state;
65855b9b249SMoshe Shemesh 	trace_devlink_health_reporter_state_update(reporter->devlink,
65955b9b249SMoshe Shemesh 						   reporter->ops->name, state);
66055b9b249SMoshe Shemesh 	devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
66155b9b249SMoshe Shemesh }
66255b9b249SMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_health_reporter_state_update);
66355b9b249SMoshe Shemesh 
devlink_nl_health_reporter_recover_doit(struct sk_buff * skb,struct genl_info * info)66453590934SJiri Pirko int devlink_nl_health_reporter_recover_doit(struct sk_buff *skb,
66555b9b249SMoshe Shemesh 					    struct genl_info *info)
66655b9b249SMoshe Shemesh {
66755b9b249SMoshe Shemesh 	struct devlink *devlink = info->user_ptr[0];
66855b9b249SMoshe Shemesh 	struct devlink_health_reporter *reporter;
66955b9b249SMoshe Shemesh 
67055b9b249SMoshe Shemesh 	reporter = devlink_health_reporter_get_from_info(devlink, info);
67155b9b249SMoshe Shemesh 	if (!reporter)
67255b9b249SMoshe Shemesh 		return -EINVAL;
67355b9b249SMoshe Shemesh 
67455b9b249SMoshe Shemesh 	return devlink_health_reporter_recover(reporter, NULL, info->extack);
67555b9b249SMoshe Shemesh }
676a929df7fSMoshe Shemesh 
devlink_fmsg_err_if_binary(struct devlink_fmsg * fmsg)677db80d3b2SPrzemek Kitszel static void devlink_fmsg_err_if_binary(struct devlink_fmsg *fmsg)
678db80d3b2SPrzemek Kitszel {
679db80d3b2SPrzemek Kitszel 	if (!fmsg->err && fmsg->putting_binary)
680db80d3b2SPrzemek Kitszel 		fmsg->err = -EINVAL;
681db80d3b2SPrzemek Kitszel }
682db80d3b2SPrzemek Kitszel 
devlink_fmsg_nest_common(struct devlink_fmsg * fmsg,int attrtype)6830050629cSPrzemek Kitszel static void devlink_fmsg_nest_common(struct devlink_fmsg *fmsg, int attrtype)
684a929df7fSMoshe Shemesh {
685a929df7fSMoshe Shemesh 	struct devlink_fmsg_item *item;
686a929df7fSMoshe Shemesh 
687db80d3b2SPrzemek Kitszel 	if (fmsg->err)
6880050629cSPrzemek Kitszel 		return;
689db80d3b2SPrzemek Kitszel 
690a929df7fSMoshe Shemesh 	item = kzalloc(sizeof(*item), GFP_KERNEL);
691db80d3b2SPrzemek Kitszel 	if (!item) {
692db80d3b2SPrzemek Kitszel 		fmsg->err = -ENOMEM;
6930050629cSPrzemek Kitszel 		return;
694db80d3b2SPrzemek Kitszel 	}
695a929df7fSMoshe Shemesh 
696a929df7fSMoshe Shemesh 	item->attrtype = attrtype;
697a929df7fSMoshe Shemesh 	list_add_tail(&item->list, &fmsg->item_list);
698a929df7fSMoshe Shemesh }
699a929df7fSMoshe Shemesh 
devlink_fmsg_obj_nest_start(struct devlink_fmsg * fmsg)7000050629cSPrzemek Kitszel void devlink_fmsg_obj_nest_start(struct devlink_fmsg *fmsg)
701a929df7fSMoshe Shemesh {
702db80d3b2SPrzemek Kitszel 	devlink_fmsg_err_if_binary(fmsg);
7030050629cSPrzemek Kitszel 	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_OBJ_NEST_START);
704a929df7fSMoshe Shemesh }
705a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_start);
706a929df7fSMoshe Shemesh 
devlink_fmsg_nest_end(struct devlink_fmsg * fmsg)7070050629cSPrzemek Kitszel static void devlink_fmsg_nest_end(struct devlink_fmsg *fmsg)
708a929df7fSMoshe Shemesh {
709db80d3b2SPrzemek Kitszel 	devlink_fmsg_err_if_binary(fmsg);
7100050629cSPrzemek Kitszel 	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_NEST_END);
711a929df7fSMoshe Shemesh }
712a929df7fSMoshe Shemesh 
devlink_fmsg_obj_nest_end(struct devlink_fmsg * fmsg)7130050629cSPrzemek Kitszel void devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg)
714a929df7fSMoshe Shemesh {
7150050629cSPrzemek Kitszel 	devlink_fmsg_nest_end(fmsg);
716a929df7fSMoshe Shemesh }
717a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_end);
718a929df7fSMoshe Shemesh 
719a929df7fSMoshe Shemesh #define DEVLINK_FMSG_MAX_SIZE (GENLMSG_DEFAULT_SIZE - GENL_HDRLEN - NLA_HDRLEN)
720a929df7fSMoshe Shemesh 
devlink_fmsg_put_name(struct devlink_fmsg * fmsg,const char * name)7210050629cSPrzemek Kitszel static void devlink_fmsg_put_name(struct devlink_fmsg *fmsg, const char *name)
722a929df7fSMoshe Shemesh {
723a929df7fSMoshe Shemesh 	struct devlink_fmsg_item *item;
724a929df7fSMoshe Shemesh 
725db80d3b2SPrzemek Kitszel 	devlink_fmsg_err_if_binary(fmsg);
726db80d3b2SPrzemek Kitszel 	if (fmsg->err)
7270050629cSPrzemek Kitszel 		return;
728a929df7fSMoshe Shemesh 
729db80d3b2SPrzemek Kitszel 	if (strlen(name) + 1 > DEVLINK_FMSG_MAX_SIZE) {
730db80d3b2SPrzemek Kitszel 		fmsg->err = -EMSGSIZE;
7310050629cSPrzemek Kitszel 		return;
732db80d3b2SPrzemek Kitszel 	}
733a929df7fSMoshe Shemesh 
734a929df7fSMoshe Shemesh 	item = kzalloc(sizeof(*item) + strlen(name) + 1, GFP_KERNEL);
735db80d3b2SPrzemek Kitszel 	if (!item) {
736db80d3b2SPrzemek Kitszel 		fmsg->err = -ENOMEM;
7370050629cSPrzemek Kitszel 		return;
738db80d3b2SPrzemek Kitszel 	}
739a929df7fSMoshe Shemesh 
740a929df7fSMoshe Shemesh 	item->nla_type = NLA_NUL_STRING;
741a929df7fSMoshe Shemesh 	item->len = strlen(name) + 1;
742a929df7fSMoshe Shemesh 	item->attrtype = DEVLINK_ATTR_FMSG_OBJ_NAME;
743a929df7fSMoshe Shemesh 	memcpy(&item->value, name, item->len);
744a929df7fSMoshe Shemesh 	list_add_tail(&item->list, &fmsg->item_list);
745a929df7fSMoshe Shemesh }
746a929df7fSMoshe Shemesh 
devlink_fmsg_pair_nest_start(struct devlink_fmsg * fmsg,const char * name)7470050629cSPrzemek Kitszel void devlink_fmsg_pair_nest_start(struct devlink_fmsg *fmsg, const char *name)
748a929df7fSMoshe Shemesh {
749db80d3b2SPrzemek Kitszel 	devlink_fmsg_err_if_binary(fmsg);
750db80d3b2SPrzemek Kitszel 	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_PAIR_NEST_START);
7510050629cSPrzemek Kitszel 	devlink_fmsg_put_name(fmsg, name);
752a929df7fSMoshe Shemesh }
753a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_start);
754a929df7fSMoshe Shemesh 
devlink_fmsg_pair_nest_end(struct devlink_fmsg * fmsg)7550050629cSPrzemek Kitszel void devlink_fmsg_pair_nest_end(struct devlink_fmsg *fmsg)
756a929df7fSMoshe Shemesh {
7570050629cSPrzemek Kitszel 	devlink_fmsg_nest_end(fmsg);
758a929df7fSMoshe Shemesh }
759a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_end);
760a929df7fSMoshe Shemesh 
devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg * fmsg,const char * name)7610050629cSPrzemek Kitszel void devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg *fmsg,
762a929df7fSMoshe Shemesh 				      const char *name)
763a929df7fSMoshe Shemesh {
764db80d3b2SPrzemek Kitszel 	devlink_fmsg_pair_nest_start(fmsg, name);
7650050629cSPrzemek Kitszel 	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_ARR_NEST_START);
766a929df7fSMoshe Shemesh }
767a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_start);
768a929df7fSMoshe Shemesh 
devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg * fmsg)7690050629cSPrzemek Kitszel void devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg *fmsg)
770a929df7fSMoshe Shemesh {
771db80d3b2SPrzemek Kitszel 	devlink_fmsg_nest_end(fmsg);
7720050629cSPrzemek Kitszel 	devlink_fmsg_nest_end(fmsg);
773a929df7fSMoshe Shemesh }
774a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_end);
775a929df7fSMoshe Shemesh 
devlink_fmsg_binary_pair_nest_start(struct devlink_fmsg * fmsg,const char * name)7760050629cSPrzemek Kitszel void devlink_fmsg_binary_pair_nest_start(struct devlink_fmsg *fmsg,
777a929df7fSMoshe Shemesh 					 const char *name)
778a929df7fSMoshe Shemesh {
7790050629cSPrzemek Kitszel 	devlink_fmsg_arr_pair_nest_start(fmsg, name);
780a929df7fSMoshe Shemesh 	fmsg->putting_binary = true;
781a929df7fSMoshe Shemesh }
782a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_start);
783a929df7fSMoshe Shemesh 
devlink_fmsg_binary_pair_nest_end(struct devlink_fmsg * fmsg)7840050629cSPrzemek Kitszel void devlink_fmsg_binary_pair_nest_end(struct devlink_fmsg *fmsg)
785a929df7fSMoshe Shemesh {
786db80d3b2SPrzemek Kitszel 	if (fmsg->err)
7870050629cSPrzemek Kitszel 		return;
788db80d3b2SPrzemek Kitszel 
7890050629cSPrzemek Kitszel 	if (!fmsg->putting_binary)
790db80d3b2SPrzemek Kitszel 		fmsg->err = -EINVAL;
791a929df7fSMoshe Shemesh 
792a929df7fSMoshe Shemesh 	fmsg->putting_binary = false;
7930050629cSPrzemek Kitszel 	devlink_fmsg_arr_pair_nest_end(fmsg);
794a929df7fSMoshe Shemesh }
795a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_end);
796a929df7fSMoshe Shemesh 
devlink_fmsg_put_value(struct devlink_fmsg * fmsg,const void * value,u16 value_len,u8 value_nla_type)7970050629cSPrzemek Kitszel static void devlink_fmsg_put_value(struct devlink_fmsg *fmsg,
798a929df7fSMoshe Shemesh 				   const void *value, u16 value_len,
799a929df7fSMoshe Shemesh 				   u8 value_nla_type)
800a929df7fSMoshe Shemesh {
801a929df7fSMoshe Shemesh 	struct devlink_fmsg_item *item;
802a929df7fSMoshe Shemesh 
8030050629cSPrzemek Kitszel 	if (fmsg->err)
8040050629cSPrzemek Kitszel 		return;
8050050629cSPrzemek Kitszel 
806db80d3b2SPrzemek Kitszel 	if (value_len > DEVLINK_FMSG_MAX_SIZE) {
807db80d3b2SPrzemek Kitszel 		fmsg->err = -EMSGSIZE;
8080050629cSPrzemek Kitszel 		return;
809db80d3b2SPrzemek Kitszel 	}
810a929df7fSMoshe Shemesh 
811a929df7fSMoshe Shemesh 	item = kzalloc(sizeof(*item) + value_len, GFP_KERNEL);
812db80d3b2SPrzemek Kitszel 	if (!item) {
813db80d3b2SPrzemek Kitszel 		fmsg->err = -ENOMEM;
8140050629cSPrzemek Kitszel 		return;
815db80d3b2SPrzemek Kitszel 	}
816a929df7fSMoshe Shemesh 
817a929df7fSMoshe Shemesh 	item->nla_type = value_nla_type;
818a929df7fSMoshe Shemesh 	item->len = value_len;
819a929df7fSMoshe Shemesh 	item->attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
820a929df7fSMoshe Shemesh 	memcpy(&item->value, value, item->len);
821a929df7fSMoshe Shemesh 	list_add_tail(&item->list, &fmsg->item_list);
822a929df7fSMoshe Shemesh }
823a929df7fSMoshe Shemesh 
devlink_fmsg_bool_put(struct devlink_fmsg * fmsg,bool value)8240050629cSPrzemek Kitszel static void devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value)
825a929df7fSMoshe Shemesh {
826db80d3b2SPrzemek Kitszel 	devlink_fmsg_err_if_binary(fmsg);
8270050629cSPrzemek Kitszel 	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_FLAG);
828a929df7fSMoshe Shemesh }
829a929df7fSMoshe Shemesh 
devlink_fmsg_u8_put(struct devlink_fmsg * fmsg,u8 value)8300050629cSPrzemek Kitszel static void devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value)
831a929df7fSMoshe Shemesh {
832db80d3b2SPrzemek Kitszel 	devlink_fmsg_err_if_binary(fmsg);
8330050629cSPrzemek Kitszel 	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U8);
834a929df7fSMoshe Shemesh }
835a929df7fSMoshe Shemesh 
devlink_fmsg_u32_put(struct devlink_fmsg * fmsg,u32 value)8360050629cSPrzemek Kitszel void devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value)
837a929df7fSMoshe Shemesh {
838db80d3b2SPrzemek Kitszel 	devlink_fmsg_err_if_binary(fmsg);
8390050629cSPrzemek Kitszel 	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U32);
840a929df7fSMoshe Shemesh }
841a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_u32_put);
842a929df7fSMoshe Shemesh 
devlink_fmsg_u64_put(struct devlink_fmsg * fmsg,u64 value)8430050629cSPrzemek Kitszel static void devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value)
844a929df7fSMoshe Shemesh {
845db80d3b2SPrzemek Kitszel 	devlink_fmsg_err_if_binary(fmsg);
8460050629cSPrzemek Kitszel 	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U64);
847a929df7fSMoshe Shemesh }
848a929df7fSMoshe Shemesh 
devlink_fmsg_string_put(struct devlink_fmsg * fmsg,const char * value)8490050629cSPrzemek Kitszel void devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value)
850a929df7fSMoshe Shemesh {
851db80d3b2SPrzemek Kitszel 	devlink_fmsg_err_if_binary(fmsg);
8520050629cSPrzemek Kitszel 	devlink_fmsg_put_value(fmsg, value, strlen(value) + 1, NLA_NUL_STRING);
853a929df7fSMoshe Shemesh }
854a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_string_put);
855a929df7fSMoshe Shemesh 
devlink_fmsg_binary_put(struct devlink_fmsg * fmsg,const void * value,u16 value_len)8560050629cSPrzemek Kitszel void devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value,
857a929df7fSMoshe Shemesh 			     u16 value_len)
858a929df7fSMoshe Shemesh {
8590050629cSPrzemek Kitszel 	if (!fmsg->err && !fmsg->putting_binary)
8600050629cSPrzemek Kitszel 		fmsg->err = -EINVAL;
861a929df7fSMoshe Shemesh 
8620050629cSPrzemek Kitszel 	devlink_fmsg_put_value(fmsg, value, value_len, NLA_BINARY);
863a929df7fSMoshe Shemesh }
864a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_binary_put);
865a929df7fSMoshe Shemesh 
devlink_fmsg_bool_pair_put(struct devlink_fmsg * fmsg,const char * name,bool value)8660050629cSPrzemek Kitszel void devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name,
867a929df7fSMoshe Shemesh 				bool value)
868a929df7fSMoshe Shemesh {
869db80d3b2SPrzemek Kitszel 	devlink_fmsg_pair_nest_start(fmsg, name);
870db80d3b2SPrzemek Kitszel 	devlink_fmsg_bool_put(fmsg, value);
8710050629cSPrzemek Kitszel 	devlink_fmsg_pair_nest_end(fmsg);
872a929df7fSMoshe Shemesh }
873a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_bool_pair_put);
874a929df7fSMoshe Shemesh 
devlink_fmsg_u8_pair_put(struct devlink_fmsg * fmsg,const char * name,u8 value)8750050629cSPrzemek Kitszel void devlink_fmsg_u8_pair_put(struct devlink_fmsg *fmsg, const char *name,
876a929df7fSMoshe Shemesh 			      u8 value)
877a929df7fSMoshe Shemesh {
878db80d3b2SPrzemek Kitszel 	devlink_fmsg_pair_nest_start(fmsg, name);
879db80d3b2SPrzemek Kitszel 	devlink_fmsg_u8_put(fmsg, value);
8800050629cSPrzemek Kitszel 	devlink_fmsg_pair_nest_end(fmsg);
881a929df7fSMoshe Shemesh }
882a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_u8_pair_put);
883a929df7fSMoshe Shemesh 
devlink_fmsg_u32_pair_put(struct devlink_fmsg * fmsg,const char * name,u32 value)8840050629cSPrzemek Kitszel void devlink_fmsg_u32_pair_put(struct devlink_fmsg *fmsg, const char *name,
885a929df7fSMoshe Shemesh 			       u32 value)
886a929df7fSMoshe Shemesh {
887db80d3b2SPrzemek Kitszel 	devlink_fmsg_pair_nest_start(fmsg, name);
888db80d3b2SPrzemek Kitszel 	devlink_fmsg_u32_put(fmsg, value);
8890050629cSPrzemek Kitszel 	devlink_fmsg_pair_nest_end(fmsg);
890a929df7fSMoshe Shemesh }
891a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_u32_pair_put);
892a929df7fSMoshe Shemesh 
devlink_fmsg_u64_pair_put(struct devlink_fmsg * fmsg,const char * name,u64 value)8930050629cSPrzemek Kitszel void devlink_fmsg_u64_pair_put(struct devlink_fmsg *fmsg, const char *name,
894a929df7fSMoshe Shemesh 			       u64 value)
895a929df7fSMoshe Shemesh {
896db80d3b2SPrzemek Kitszel 	devlink_fmsg_pair_nest_start(fmsg, name);
897db80d3b2SPrzemek Kitszel 	devlink_fmsg_u64_put(fmsg, value);
8980050629cSPrzemek Kitszel 	devlink_fmsg_pair_nest_end(fmsg);
899a929df7fSMoshe Shemesh }
900a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_u64_pair_put);
901a929df7fSMoshe Shemesh 
devlink_fmsg_string_pair_put(struct devlink_fmsg * fmsg,const char * name,const char * value)9020050629cSPrzemek Kitszel void devlink_fmsg_string_pair_put(struct devlink_fmsg *fmsg, const char *name,
903a929df7fSMoshe Shemesh 				  const char *value)
904a929df7fSMoshe Shemesh {
905db80d3b2SPrzemek Kitszel 	devlink_fmsg_pair_nest_start(fmsg, name);
906db80d3b2SPrzemek Kitszel 	devlink_fmsg_string_put(fmsg, value);
9070050629cSPrzemek Kitszel 	devlink_fmsg_pair_nest_end(fmsg);
908a929df7fSMoshe Shemesh }
909a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_string_pair_put);
910a929df7fSMoshe Shemesh 
devlink_fmsg_binary_pair_put(struct devlink_fmsg * fmsg,const char * name,const void * value,u32 value_len)9110050629cSPrzemek Kitszel void devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name,
912a929df7fSMoshe Shemesh 				  const void *value, u32 value_len)
913a929df7fSMoshe Shemesh {
914a929df7fSMoshe Shemesh 	u32 data_size;
915a929df7fSMoshe Shemesh 	u32 offset;
916a929df7fSMoshe Shemesh 
9170050629cSPrzemek Kitszel 	devlink_fmsg_binary_pair_nest_start(fmsg, name);
918a929df7fSMoshe Shemesh 
919a929df7fSMoshe Shemesh 	for (offset = 0; offset < value_len; offset += data_size) {
920a929df7fSMoshe Shemesh 		data_size = value_len - offset;
921a929df7fSMoshe Shemesh 		if (data_size > DEVLINK_FMSG_MAX_SIZE)
922a929df7fSMoshe Shemesh 			data_size = DEVLINK_FMSG_MAX_SIZE;
9230050629cSPrzemek Kitszel 
9240050629cSPrzemek Kitszel 		devlink_fmsg_binary_put(fmsg, value + offset, data_size);
925a929df7fSMoshe Shemesh 	}
926a929df7fSMoshe Shemesh 
9270050629cSPrzemek Kitszel 	devlink_fmsg_binary_pair_nest_end(fmsg);
928db80d3b2SPrzemek Kitszel 	fmsg->putting_binary = false;
929a929df7fSMoshe Shemesh }
930a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_put);
931a929df7fSMoshe Shemesh 
932a929df7fSMoshe Shemesh static int
devlink_fmsg_item_fill_type(struct devlink_fmsg_item * msg,struct sk_buff * skb)933a929df7fSMoshe Shemesh devlink_fmsg_item_fill_type(struct devlink_fmsg_item *msg, struct sk_buff *skb)
934a929df7fSMoshe Shemesh {
935a929df7fSMoshe Shemesh 	switch (msg->nla_type) {
936a929df7fSMoshe Shemesh 	case NLA_FLAG:
937a929df7fSMoshe Shemesh 	case NLA_U8:
938a929df7fSMoshe Shemesh 	case NLA_U32:
939a929df7fSMoshe Shemesh 	case NLA_U64:
940a929df7fSMoshe Shemesh 	case NLA_NUL_STRING:
941a929df7fSMoshe Shemesh 	case NLA_BINARY:
942a929df7fSMoshe Shemesh 		return nla_put_u8(skb, DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE,
943a929df7fSMoshe Shemesh 				  msg->nla_type);
944a929df7fSMoshe Shemesh 	default:
945a929df7fSMoshe Shemesh 		return -EINVAL;
946a929df7fSMoshe Shemesh 	}
947a929df7fSMoshe Shemesh }
948a929df7fSMoshe Shemesh 
949a929df7fSMoshe Shemesh static int
devlink_fmsg_item_fill_data(struct devlink_fmsg_item * msg,struct sk_buff * skb)950a929df7fSMoshe Shemesh devlink_fmsg_item_fill_data(struct devlink_fmsg_item *msg, struct sk_buff *skb)
951a929df7fSMoshe Shemesh {
952a929df7fSMoshe Shemesh 	int attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
953a929df7fSMoshe Shemesh 	u8 tmp;
954a929df7fSMoshe Shemesh 
955a929df7fSMoshe Shemesh 	switch (msg->nla_type) {
956a929df7fSMoshe Shemesh 	case NLA_FLAG:
957a929df7fSMoshe Shemesh 		/* Always provide flag data, regardless of its value */
958a929df7fSMoshe Shemesh 		tmp = *(bool *)msg->value;
959a929df7fSMoshe Shemesh 
960a929df7fSMoshe Shemesh 		return nla_put_u8(skb, attrtype, tmp);
961a929df7fSMoshe Shemesh 	case NLA_U8:
962a929df7fSMoshe Shemesh 		return nla_put_u8(skb, attrtype, *(u8 *)msg->value);
963a929df7fSMoshe Shemesh 	case NLA_U32:
964a929df7fSMoshe Shemesh 		return nla_put_u32(skb, attrtype, *(u32 *)msg->value);
965a929df7fSMoshe Shemesh 	case NLA_U64:
966a929df7fSMoshe Shemesh 		return nla_put_u64_64bit(skb, attrtype, *(u64 *)msg->value,
967a929df7fSMoshe Shemesh 					 DEVLINK_ATTR_PAD);
968a929df7fSMoshe Shemesh 	case NLA_NUL_STRING:
969a929df7fSMoshe Shemesh 		return nla_put_string(skb, attrtype, (char *)&msg->value);
970a929df7fSMoshe Shemesh 	case NLA_BINARY:
971a929df7fSMoshe Shemesh 		return nla_put(skb, attrtype, msg->len, (void *)&msg->value);
972a929df7fSMoshe Shemesh 	default:
973a929df7fSMoshe Shemesh 		return -EINVAL;
974a929df7fSMoshe Shemesh 	}
975a929df7fSMoshe Shemesh }
976a929df7fSMoshe Shemesh 
977a929df7fSMoshe Shemesh static int
devlink_fmsg_prepare_skb(struct devlink_fmsg * fmsg,struct sk_buff * skb,int * start)978a929df7fSMoshe Shemesh devlink_fmsg_prepare_skb(struct devlink_fmsg *fmsg, struct sk_buff *skb,
979a929df7fSMoshe Shemesh 			 int *start)
980a929df7fSMoshe Shemesh {
981a929df7fSMoshe Shemesh 	struct devlink_fmsg_item *item;
982a929df7fSMoshe Shemesh 	struct nlattr *fmsg_nlattr;
983a929df7fSMoshe Shemesh 	int err = 0;
984a929df7fSMoshe Shemesh 	int i = 0;
985a929df7fSMoshe Shemesh 
986a929df7fSMoshe Shemesh 	fmsg_nlattr = nla_nest_start_noflag(skb, DEVLINK_ATTR_FMSG);
987a929df7fSMoshe Shemesh 	if (!fmsg_nlattr)
988a929df7fSMoshe Shemesh 		return -EMSGSIZE;
989a929df7fSMoshe Shemesh 
990a929df7fSMoshe Shemesh 	list_for_each_entry(item, &fmsg->item_list, list) {
991a929df7fSMoshe Shemesh 		if (i < *start) {
992a929df7fSMoshe Shemesh 			i++;
993a929df7fSMoshe Shemesh 			continue;
994a929df7fSMoshe Shemesh 		}
995a929df7fSMoshe Shemesh 
996a929df7fSMoshe Shemesh 		switch (item->attrtype) {
997a929df7fSMoshe Shemesh 		case DEVLINK_ATTR_FMSG_OBJ_NEST_START:
998a929df7fSMoshe Shemesh 		case DEVLINK_ATTR_FMSG_PAIR_NEST_START:
999a929df7fSMoshe Shemesh 		case DEVLINK_ATTR_FMSG_ARR_NEST_START:
1000a929df7fSMoshe Shemesh 		case DEVLINK_ATTR_FMSG_NEST_END:
1001a929df7fSMoshe Shemesh 			err = nla_put_flag(skb, item->attrtype);
1002a929df7fSMoshe Shemesh 			break;
1003a929df7fSMoshe Shemesh 		case DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA:
1004a929df7fSMoshe Shemesh 			err = devlink_fmsg_item_fill_type(item, skb);
1005a929df7fSMoshe Shemesh 			if (err)
1006a929df7fSMoshe Shemesh 				break;
1007a929df7fSMoshe Shemesh 			err = devlink_fmsg_item_fill_data(item, skb);
1008a929df7fSMoshe Shemesh 			break;
1009a929df7fSMoshe Shemesh 		case DEVLINK_ATTR_FMSG_OBJ_NAME:
1010a929df7fSMoshe Shemesh 			err = nla_put_string(skb, item->attrtype,
1011a929df7fSMoshe Shemesh 					     (char *)&item->value);
1012a929df7fSMoshe Shemesh 			break;
1013a929df7fSMoshe Shemesh 		default:
1014a929df7fSMoshe Shemesh 			err = -EINVAL;
1015a929df7fSMoshe Shemesh 			break;
1016a929df7fSMoshe Shemesh 		}
1017a929df7fSMoshe Shemesh 		if (!err)
1018a929df7fSMoshe Shemesh 			*start = ++i;
1019a929df7fSMoshe Shemesh 		else
1020a929df7fSMoshe Shemesh 			break;
1021a929df7fSMoshe Shemesh 	}
1022a929df7fSMoshe Shemesh 
1023a929df7fSMoshe Shemesh 	nla_nest_end(skb, fmsg_nlattr);
1024a929df7fSMoshe Shemesh 	return err;
1025a929df7fSMoshe Shemesh }
1026a929df7fSMoshe Shemesh 
devlink_fmsg_snd(struct devlink_fmsg * fmsg,struct genl_info * info,enum devlink_command cmd,int flags)1027a929df7fSMoshe Shemesh static int devlink_fmsg_snd(struct devlink_fmsg *fmsg,
1028a929df7fSMoshe Shemesh 			    struct genl_info *info,
1029a929df7fSMoshe Shemesh 			    enum devlink_command cmd, int flags)
1030a929df7fSMoshe Shemesh {
1031a929df7fSMoshe Shemesh 	struct nlmsghdr *nlh;
1032a929df7fSMoshe Shemesh 	struct sk_buff *skb;
1033a929df7fSMoshe Shemesh 	bool last = false;
1034a929df7fSMoshe Shemesh 	int index = 0;
1035a929df7fSMoshe Shemesh 	void *hdr;
1036a929df7fSMoshe Shemesh 	int err;
1037a929df7fSMoshe Shemesh 
10380050629cSPrzemek Kitszel 	if (fmsg->err)
10390050629cSPrzemek Kitszel 		return fmsg->err;
10400050629cSPrzemek Kitszel 
1041a929df7fSMoshe Shemesh 	while (!last) {
1042a929df7fSMoshe Shemesh 		int tmp_index = index;
1043a929df7fSMoshe Shemesh 
1044a929df7fSMoshe Shemesh 		skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1045a929df7fSMoshe Shemesh 		if (!skb)
1046a929df7fSMoshe Shemesh 			return -ENOMEM;
1047a929df7fSMoshe Shemesh 
1048a929df7fSMoshe Shemesh 		hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
1049a929df7fSMoshe Shemesh 				  &devlink_nl_family, flags | NLM_F_MULTI, cmd);
1050a929df7fSMoshe Shemesh 		if (!hdr) {
1051a929df7fSMoshe Shemesh 			err = -EMSGSIZE;
1052a929df7fSMoshe Shemesh 			goto nla_put_failure;
1053a929df7fSMoshe Shemesh 		}
1054a929df7fSMoshe Shemesh 
1055a929df7fSMoshe Shemesh 		err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1056a929df7fSMoshe Shemesh 		if (!err)
1057a929df7fSMoshe Shemesh 			last = true;
1058a929df7fSMoshe Shemesh 		else if (err != -EMSGSIZE || tmp_index == index)
1059a929df7fSMoshe Shemesh 			goto nla_put_failure;
1060a929df7fSMoshe Shemesh 
1061a929df7fSMoshe Shemesh 		genlmsg_end(skb, hdr);
1062a929df7fSMoshe Shemesh 		err = genlmsg_reply(skb, info);
1063a929df7fSMoshe Shemesh 		if (err)
1064a929df7fSMoshe Shemesh 			return err;
1065a929df7fSMoshe Shemesh 	}
1066a929df7fSMoshe Shemesh 
1067a929df7fSMoshe Shemesh 	skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1068a929df7fSMoshe Shemesh 	if (!skb)
1069a929df7fSMoshe Shemesh 		return -ENOMEM;
1070a929df7fSMoshe Shemesh 	nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
1071a929df7fSMoshe Shemesh 			NLMSG_DONE, 0, flags | NLM_F_MULTI);
1072a929df7fSMoshe Shemesh 	if (!nlh) {
1073a929df7fSMoshe Shemesh 		err = -EMSGSIZE;
1074a929df7fSMoshe Shemesh 		goto nla_put_failure;
1075a929df7fSMoshe Shemesh 	}
1076a929df7fSMoshe Shemesh 
1077a929df7fSMoshe Shemesh 	return genlmsg_reply(skb, info);
1078a929df7fSMoshe Shemesh 
1079a929df7fSMoshe Shemesh nla_put_failure:
1080a929df7fSMoshe Shemesh 	nlmsg_free(skb);
1081a929df7fSMoshe Shemesh 	return err;
1082a929df7fSMoshe Shemesh }
1083a929df7fSMoshe Shemesh 
devlink_fmsg_dumpit(struct devlink_fmsg * fmsg,struct sk_buff * skb,struct netlink_callback * cb,enum devlink_command cmd)108412af29e7SMoshe Shemesh static int devlink_fmsg_dumpit(struct devlink_fmsg *fmsg, struct sk_buff *skb,
1085a929df7fSMoshe Shemesh 			       struct netlink_callback *cb,
1086a929df7fSMoshe Shemesh 			       enum devlink_command cmd)
1087a929df7fSMoshe Shemesh {
1088a929df7fSMoshe Shemesh 	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
1089a929df7fSMoshe Shemesh 	int index = state->idx;
1090a929df7fSMoshe Shemesh 	int tmp_index = index;
1091a929df7fSMoshe Shemesh 	void *hdr;
1092a929df7fSMoshe Shemesh 	int err;
1093a929df7fSMoshe Shemesh 
10940050629cSPrzemek Kitszel 	if (fmsg->err)
10950050629cSPrzemek Kitszel 		return fmsg->err;
10960050629cSPrzemek Kitszel 
1097a929df7fSMoshe Shemesh 	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
1098a929df7fSMoshe Shemesh 			  &devlink_nl_family, NLM_F_ACK | NLM_F_MULTI, cmd);
1099a929df7fSMoshe Shemesh 	if (!hdr) {
1100a929df7fSMoshe Shemesh 		err = -EMSGSIZE;
1101a929df7fSMoshe Shemesh 		goto nla_put_failure;
1102a929df7fSMoshe Shemesh 	}
1103a929df7fSMoshe Shemesh 
1104a929df7fSMoshe Shemesh 	err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1105a929df7fSMoshe Shemesh 	if ((err && err != -EMSGSIZE) || tmp_index == index)
1106a929df7fSMoshe Shemesh 		goto nla_put_failure;
1107a929df7fSMoshe Shemesh 
1108a929df7fSMoshe Shemesh 	state->idx = index;
1109a929df7fSMoshe Shemesh 	genlmsg_end(skb, hdr);
1110a929df7fSMoshe Shemesh 	return skb->len;
1111a929df7fSMoshe Shemesh 
1112a929df7fSMoshe Shemesh nla_put_failure:
1113a929df7fSMoshe Shemesh 	genlmsg_cancel(skb, hdr);
1114a929df7fSMoshe Shemesh 	return err;
1115a929df7fSMoshe Shemesh }
1116a929df7fSMoshe Shemesh 
devlink_nl_health_reporter_diagnose_doit(struct sk_buff * skb,struct genl_info * info)111753590934SJiri Pirko int devlink_nl_health_reporter_diagnose_doit(struct sk_buff *skb,
1118a929df7fSMoshe Shemesh 					     struct genl_info *info)
1119a929df7fSMoshe Shemesh {
1120a929df7fSMoshe Shemesh 	struct devlink *devlink = info->user_ptr[0];
1121a929df7fSMoshe Shemesh 	struct devlink_health_reporter *reporter;
1122a929df7fSMoshe Shemesh 	struct devlink_fmsg *fmsg;
1123a929df7fSMoshe Shemesh 	int err;
1124a929df7fSMoshe Shemesh 
1125a929df7fSMoshe Shemesh 	reporter = devlink_health_reporter_get_from_info(devlink, info);
1126a929df7fSMoshe Shemesh 	if (!reporter)
1127a929df7fSMoshe Shemesh 		return -EINVAL;
1128a929df7fSMoshe Shemesh 
1129a929df7fSMoshe Shemesh 	if (!reporter->ops->diagnose)
1130a929df7fSMoshe Shemesh 		return -EOPNOTSUPP;
1131a929df7fSMoshe Shemesh 
1132a929df7fSMoshe Shemesh 	fmsg = devlink_fmsg_alloc();
1133a929df7fSMoshe Shemesh 	if (!fmsg)
1134a929df7fSMoshe Shemesh 		return -ENOMEM;
1135a929df7fSMoshe Shemesh 
11360050629cSPrzemek Kitszel 	devlink_fmsg_obj_nest_start(fmsg);
1137a929df7fSMoshe Shemesh 
1138a929df7fSMoshe Shemesh 	err = reporter->ops->diagnose(reporter, fmsg, info->extack);
1139a929df7fSMoshe Shemesh 	if (err)
1140a929df7fSMoshe Shemesh 		goto out;
1141a929df7fSMoshe Shemesh 
11420050629cSPrzemek Kitszel 	devlink_fmsg_obj_nest_end(fmsg);
1143a929df7fSMoshe Shemesh 
1144a929df7fSMoshe Shemesh 	err = devlink_fmsg_snd(fmsg, info,
1145a929df7fSMoshe Shemesh 			       DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, 0);
1146a929df7fSMoshe Shemesh 
1147a929df7fSMoshe Shemesh out:
1148a929df7fSMoshe Shemesh 	devlink_fmsg_free(fmsg);
1149a929df7fSMoshe Shemesh 	return err;
1150a929df7fSMoshe Shemesh }
11517004c6c4SMoshe Shemesh 
11527004c6c4SMoshe Shemesh static struct devlink_health_reporter *
devlink_health_reporter_get_from_cb_lock(struct netlink_callback * cb)1153aba0e909SMoshe Shemesh devlink_health_reporter_get_from_cb_lock(struct netlink_callback *cb)
11547004c6c4SMoshe Shemesh {
11557288dd2fSJakub Kicinski 	const struct genl_info *info = genl_info_dump(cb);
11567004c6c4SMoshe Shemesh 	struct devlink_health_reporter *reporter;
11577004c6c4SMoshe Shemesh 	struct nlattr **attrs = info->attrs;
11587004c6c4SMoshe Shemesh 	struct devlink *devlink;
11597004c6c4SMoshe Shemesh 
1160d32c3825SIdo Schimmel 	devlink = devlink_get_from_attrs_lock(sock_net(cb->skb->sk), attrs,
1161d32c3825SIdo Schimmel 					      false);
11627004c6c4SMoshe Shemesh 	if (IS_ERR(devlink))
11637004c6c4SMoshe Shemesh 		return NULL;
11647004c6c4SMoshe Shemesh 
11657004c6c4SMoshe Shemesh 	reporter = devlink_health_reporter_get_from_attrs(devlink, attrs);
1166aba0e909SMoshe Shemesh 	if (!reporter) {
1167aba0e909SMoshe Shemesh 		devl_unlock(devlink);
11687004c6c4SMoshe Shemesh 		devlink_put(devlink);
1169aba0e909SMoshe Shemesh 	}
11707004c6c4SMoshe Shemesh 	return reporter;
11717004c6c4SMoshe Shemesh }
11727004c6c4SMoshe Shemesh 
devlink_nl_health_reporter_dump_get_dumpit(struct sk_buff * skb,struct netlink_callback * cb)117353590934SJiri Pirko int devlink_nl_health_reporter_dump_get_dumpit(struct sk_buff *skb,
11747004c6c4SMoshe Shemesh 					       struct netlink_callback *cb)
11757004c6c4SMoshe Shemesh {
11767004c6c4SMoshe Shemesh 	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
11777004c6c4SMoshe Shemesh 	struct devlink_health_reporter *reporter;
1178aba0e909SMoshe Shemesh 	struct devlink *devlink;
11797004c6c4SMoshe Shemesh 	int err;
11807004c6c4SMoshe Shemesh 
1181aba0e909SMoshe Shemesh 	reporter = devlink_health_reporter_get_from_cb_lock(cb);
11827004c6c4SMoshe Shemesh 	if (!reporter)
11837004c6c4SMoshe Shemesh 		return -EINVAL;
11847004c6c4SMoshe Shemesh 
1185aba0e909SMoshe Shemesh 	devlink = reporter->devlink;
1186aba0e909SMoshe Shemesh 	if (!reporter->ops->dump) {
1187aba0e909SMoshe Shemesh 		devl_unlock(devlink);
1188aba0e909SMoshe Shemesh 		devlink_put(devlink);
11897004c6c4SMoshe Shemesh 		return -EOPNOTSUPP;
1190aba0e909SMoshe Shemesh 	}
11917004c6c4SMoshe Shemesh 
11927004c6c4SMoshe Shemesh 	if (!state->idx) {
11937004c6c4SMoshe Shemesh 		err = devlink_health_do_dump(reporter, NULL, cb->extack);
11947004c6c4SMoshe Shemesh 		if (err)
11957004c6c4SMoshe Shemesh 			goto unlock;
11967004c6c4SMoshe Shemesh 		state->dump_ts = reporter->dump_ts;
11977004c6c4SMoshe Shemesh 	}
11987004c6c4SMoshe Shemesh 	if (!reporter->dump_fmsg || state->dump_ts != reporter->dump_ts) {
11997004c6c4SMoshe Shemesh 		NL_SET_ERR_MSG(cb->extack, "Dump trampled, please retry");
12007004c6c4SMoshe Shemesh 		err = -EAGAIN;
12017004c6c4SMoshe Shemesh 		goto unlock;
12027004c6c4SMoshe Shemesh 	}
12037004c6c4SMoshe Shemesh 
12047004c6c4SMoshe Shemesh 	err = devlink_fmsg_dumpit(reporter->dump_fmsg, skb, cb,
12057004c6c4SMoshe Shemesh 				  DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET);
12067004c6c4SMoshe Shemesh unlock:
1207aba0e909SMoshe Shemesh 	devl_unlock(devlink);
1208aba0e909SMoshe Shemesh 	devlink_put(devlink);
12097004c6c4SMoshe Shemesh 	return err;
12107004c6c4SMoshe Shemesh }
12117004c6c4SMoshe Shemesh 
devlink_nl_health_reporter_dump_clear_doit(struct sk_buff * skb,struct genl_info * info)121253590934SJiri Pirko int devlink_nl_health_reporter_dump_clear_doit(struct sk_buff *skb,
12137004c6c4SMoshe Shemesh 					       struct genl_info *info)
12147004c6c4SMoshe Shemesh {
12157004c6c4SMoshe Shemesh 	struct devlink *devlink = info->user_ptr[0];
12167004c6c4SMoshe Shemesh 	struct devlink_health_reporter *reporter;
12177004c6c4SMoshe Shemesh 
12187004c6c4SMoshe Shemesh 	reporter = devlink_health_reporter_get_from_info(devlink, info);
12197004c6c4SMoshe Shemesh 	if (!reporter)
12207004c6c4SMoshe Shemesh 		return -EINVAL;
12217004c6c4SMoshe Shemesh 
12227004c6c4SMoshe Shemesh 	if (!reporter->ops->dump)
12237004c6c4SMoshe Shemesh 		return -EOPNOTSUPP;
12247004c6c4SMoshe Shemesh 
12257004c6c4SMoshe Shemesh 	devlink_health_dump_clear(reporter);
12267004c6c4SMoshe Shemesh 	return 0;
12277004c6c4SMoshe Shemesh }
1228c9311ee1SMoshe Shemesh 
devlink_nl_health_reporter_test_doit(struct sk_buff * skb,struct genl_info * info)122953590934SJiri Pirko int devlink_nl_health_reporter_test_doit(struct sk_buff *skb,
1230c9311ee1SMoshe Shemesh 					 struct genl_info *info)
1231c9311ee1SMoshe Shemesh {
1232c9311ee1SMoshe Shemesh 	struct devlink *devlink = info->user_ptr[0];
1233c9311ee1SMoshe Shemesh 	struct devlink_health_reporter *reporter;
1234c9311ee1SMoshe Shemesh 
1235c9311ee1SMoshe Shemesh 	reporter = devlink_health_reporter_get_from_info(devlink, info);
1236c9311ee1SMoshe Shemesh 	if (!reporter)
1237c9311ee1SMoshe Shemesh 		return -EINVAL;
1238c9311ee1SMoshe Shemesh 
1239c9311ee1SMoshe Shemesh 	if (!reporter->ops->test)
1240c9311ee1SMoshe Shemesh 		return -EOPNOTSUPP;
1241c9311ee1SMoshe Shemesh 
1242c9311ee1SMoshe Shemesh 	return reporter->ops->test(reporter, info->extack);
1243c9311ee1SMoshe Shemesh }
1244