xref: /openbmc/linux/net/devlink/health.c (revision e8c127b0576660da9195504fe8393fe9da3de9ce)
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;
22a929df7fSMoshe Shemesh 	bool putting_binary; /* This flag forces enclosing of binary data
23a929df7fSMoshe Shemesh 			      * in an array brackets. It forces using
24a929df7fSMoshe Shemesh 			      * of designated API:
25a929df7fSMoshe Shemesh 			      * devlink_fmsg_binary_pair_nest_start()
26a929df7fSMoshe Shemesh 			      * devlink_fmsg_binary_pair_nest_end()
27a929df7fSMoshe Shemesh 			      */
28a929df7fSMoshe Shemesh };
29a929df7fSMoshe Shemesh 
devlink_fmsg_alloc(void)3012af29e7SMoshe Shemesh static struct devlink_fmsg *devlink_fmsg_alloc(void)
31a929df7fSMoshe Shemesh {
32a929df7fSMoshe Shemesh 	struct devlink_fmsg *fmsg;
33a929df7fSMoshe Shemesh 
34a929df7fSMoshe Shemesh 	fmsg = kzalloc(sizeof(*fmsg), GFP_KERNEL);
35a929df7fSMoshe Shemesh 	if (!fmsg)
36a929df7fSMoshe Shemesh 		return NULL;
37a929df7fSMoshe Shemesh 
38a929df7fSMoshe Shemesh 	INIT_LIST_HEAD(&fmsg->item_list);
39a929df7fSMoshe Shemesh 
40a929df7fSMoshe Shemesh 	return fmsg;
41a929df7fSMoshe Shemesh }
42a929df7fSMoshe Shemesh 
devlink_fmsg_free(struct devlink_fmsg * fmsg)4312af29e7SMoshe Shemesh static void devlink_fmsg_free(struct devlink_fmsg *fmsg)
44a929df7fSMoshe Shemesh {
45a929df7fSMoshe Shemesh 	struct devlink_fmsg_item *item, *tmp;
46a929df7fSMoshe Shemesh 
47a929df7fSMoshe Shemesh 	list_for_each_entry_safe(item, tmp, &fmsg->item_list, list) {
48a929df7fSMoshe Shemesh 		list_del(&item->list);
49a929df7fSMoshe Shemesh 		kfree(item);
50a929df7fSMoshe Shemesh 	}
51a929df7fSMoshe Shemesh 	kfree(fmsg);
52a929df7fSMoshe Shemesh }
53a929df7fSMoshe Shemesh 
5412af29e7SMoshe Shemesh struct devlink_health_reporter {
5512af29e7SMoshe Shemesh 	struct list_head list;
5612af29e7SMoshe Shemesh 	void *priv;
5712af29e7SMoshe Shemesh 	const struct devlink_health_reporter_ops *ops;
5812af29e7SMoshe Shemesh 	struct devlink *devlink;
5912af29e7SMoshe Shemesh 	struct devlink_port *devlink_port;
6012af29e7SMoshe Shemesh 	struct devlink_fmsg *dump_fmsg;
6112af29e7SMoshe Shemesh 	u64 graceful_period;
6212af29e7SMoshe Shemesh 	bool auto_recover;
6312af29e7SMoshe Shemesh 	bool auto_dump;
6412af29e7SMoshe Shemesh 	u8 health_state;
6512af29e7SMoshe Shemesh 	u64 dump_ts;
6612af29e7SMoshe Shemesh 	u64 dump_real_ts;
6712af29e7SMoshe Shemesh 	u64 error_count;
6812af29e7SMoshe Shemesh 	u64 recovery_count;
6912af29e7SMoshe Shemesh 	u64 last_recovery_ts;
7012af29e7SMoshe Shemesh };
7112af29e7SMoshe Shemesh 
72b4740e3aSMoshe Shemesh void *
devlink_health_reporter_priv(struct devlink_health_reporter * reporter)73b4740e3aSMoshe Shemesh devlink_health_reporter_priv(struct devlink_health_reporter *reporter)
74b4740e3aSMoshe Shemesh {
75b4740e3aSMoshe Shemesh 	return reporter->priv;
76b4740e3aSMoshe Shemesh }
77b4740e3aSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_health_reporter_priv);
78b4740e3aSMoshe Shemesh 
79b4740e3aSMoshe Shemesh static struct devlink_health_reporter *
__devlink_health_reporter_find_by_name(struct list_head * reporter_list,const char * reporter_name)80b4740e3aSMoshe Shemesh __devlink_health_reporter_find_by_name(struct list_head *reporter_list,
81b4740e3aSMoshe Shemesh 				       const char *reporter_name)
82b4740e3aSMoshe Shemesh {
83b4740e3aSMoshe Shemesh 	struct devlink_health_reporter *reporter;
84b4740e3aSMoshe Shemesh 
85b4740e3aSMoshe Shemesh 	list_for_each_entry(reporter, reporter_list, list)
86b4740e3aSMoshe Shemesh 		if (!strcmp(reporter->ops->name, reporter_name))
87b4740e3aSMoshe Shemesh 			return reporter;
88b4740e3aSMoshe Shemesh 	return NULL;
89b4740e3aSMoshe Shemesh }
90b4740e3aSMoshe Shemesh 
9112af29e7SMoshe Shemesh static struct devlink_health_reporter *
devlink_health_reporter_find_by_name(struct devlink * devlink,const char * reporter_name)92b4740e3aSMoshe Shemesh devlink_health_reporter_find_by_name(struct devlink *devlink,
93b4740e3aSMoshe Shemesh 				     const char *reporter_name)
94b4740e3aSMoshe Shemesh {
95b4740e3aSMoshe Shemesh 	return __devlink_health_reporter_find_by_name(&devlink->reporter_list,
96b4740e3aSMoshe Shemesh 						      reporter_name);
97b4740e3aSMoshe Shemesh }
98b4740e3aSMoshe Shemesh 
9912af29e7SMoshe Shemesh static struct devlink_health_reporter *
devlink_port_health_reporter_find_by_name(struct devlink_port * devlink_port,const char * reporter_name)100b4740e3aSMoshe Shemesh devlink_port_health_reporter_find_by_name(struct devlink_port *devlink_port,
101b4740e3aSMoshe Shemesh 					  const char *reporter_name)
102b4740e3aSMoshe Shemesh {
103b4740e3aSMoshe Shemesh 	return __devlink_health_reporter_find_by_name(&devlink_port->reporter_list,
104b4740e3aSMoshe Shemesh 						      reporter_name);
105b4740e3aSMoshe Shemesh }
106b4740e3aSMoshe Shemesh 
107b4740e3aSMoshe 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)108b4740e3aSMoshe Shemesh __devlink_health_reporter_create(struct devlink *devlink,
109b4740e3aSMoshe Shemesh 				 const struct devlink_health_reporter_ops *ops,
110b4740e3aSMoshe Shemesh 				 u64 graceful_period, void *priv)
111b4740e3aSMoshe Shemesh {
112b4740e3aSMoshe Shemesh 	struct devlink_health_reporter *reporter;
113b4740e3aSMoshe Shemesh 
114b4740e3aSMoshe Shemesh 	if (WARN_ON(graceful_period && !ops->recover))
115b4740e3aSMoshe Shemesh 		return ERR_PTR(-EINVAL);
116b4740e3aSMoshe Shemesh 
117b4740e3aSMoshe Shemesh 	reporter = kzalloc(sizeof(*reporter), GFP_KERNEL);
118b4740e3aSMoshe Shemesh 	if (!reporter)
119b4740e3aSMoshe Shemesh 		return ERR_PTR(-ENOMEM);
120b4740e3aSMoshe Shemesh 
121b4740e3aSMoshe Shemesh 	reporter->priv = priv;
122b4740e3aSMoshe Shemesh 	reporter->ops = ops;
123b4740e3aSMoshe Shemesh 	reporter->devlink = devlink;
124b4740e3aSMoshe Shemesh 	reporter->graceful_period = graceful_period;
125b4740e3aSMoshe Shemesh 	reporter->auto_recover = !!ops->recover;
126b4740e3aSMoshe Shemesh 	reporter->auto_dump = !!ops->dump;
127b4740e3aSMoshe Shemesh 	return reporter;
128b4740e3aSMoshe Shemesh }
129b4740e3aSMoshe Shemesh 
130b4740e3aSMoshe Shemesh /**
131b4740e3aSMoshe Shemesh  * devl_port_health_reporter_create() - create devlink health reporter for
132b4740e3aSMoshe Shemesh  *                                      specified port instance
133b4740e3aSMoshe Shemesh  *
134b4740e3aSMoshe Shemesh  * @port: devlink_port to which health reports will relate
135b4740e3aSMoshe Shemesh  * @ops: devlink health reporter ops
136b4740e3aSMoshe Shemesh  * @graceful_period: min time (in msec) between recovery attempts
137b4740e3aSMoshe Shemesh  * @priv: driver priv pointer
138b4740e3aSMoshe Shemesh  */
139b4740e3aSMoshe 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)140b4740e3aSMoshe Shemesh devl_port_health_reporter_create(struct devlink_port *port,
141b4740e3aSMoshe Shemesh 				 const struct devlink_health_reporter_ops *ops,
142b4740e3aSMoshe Shemesh 				 u64 graceful_period, void *priv)
143b4740e3aSMoshe Shemesh {
144b4740e3aSMoshe Shemesh 	struct devlink_health_reporter *reporter;
145b4740e3aSMoshe Shemesh 
146b4740e3aSMoshe Shemesh 	devl_assert_locked(port->devlink);
147b4740e3aSMoshe Shemesh 
148b4740e3aSMoshe Shemesh 	if (__devlink_health_reporter_find_by_name(&port->reporter_list,
149b4740e3aSMoshe Shemesh 						   ops->name))
150b4740e3aSMoshe Shemesh 		return ERR_PTR(-EEXIST);
151b4740e3aSMoshe Shemesh 
152b4740e3aSMoshe Shemesh 	reporter = __devlink_health_reporter_create(port->devlink, ops,
153b4740e3aSMoshe Shemesh 						    graceful_period, priv);
154b4740e3aSMoshe Shemesh 	if (IS_ERR(reporter))
155b4740e3aSMoshe Shemesh 		return reporter;
156b4740e3aSMoshe Shemesh 
157b4740e3aSMoshe Shemesh 	reporter->devlink_port = port;
158b4740e3aSMoshe Shemesh 	list_add_tail(&reporter->list, &port->reporter_list);
159b4740e3aSMoshe Shemesh 	return reporter;
160b4740e3aSMoshe Shemesh }
161b4740e3aSMoshe Shemesh EXPORT_SYMBOL_GPL(devl_port_health_reporter_create);
162b4740e3aSMoshe Shemesh 
163b4740e3aSMoshe 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)164b4740e3aSMoshe Shemesh devlink_port_health_reporter_create(struct devlink_port *port,
165b4740e3aSMoshe Shemesh 				    const struct devlink_health_reporter_ops *ops,
166b4740e3aSMoshe Shemesh 				    u64 graceful_period, void *priv)
167b4740e3aSMoshe Shemesh {
168b4740e3aSMoshe Shemesh 	struct devlink_health_reporter *reporter;
169b4740e3aSMoshe Shemesh 	struct devlink *devlink = port->devlink;
170b4740e3aSMoshe Shemesh 
171b4740e3aSMoshe Shemesh 	devl_lock(devlink);
172b4740e3aSMoshe Shemesh 	reporter = devl_port_health_reporter_create(port, ops,
173b4740e3aSMoshe Shemesh 						    graceful_period, priv);
174b4740e3aSMoshe Shemesh 	devl_unlock(devlink);
175b4740e3aSMoshe Shemesh 	return reporter;
176b4740e3aSMoshe Shemesh }
177b4740e3aSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_port_health_reporter_create);
178b4740e3aSMoshe Shemesh 
179b4740e3aSMoshe Shemesh /**
180b4740e3aSMoshe Shemesh  * devl_health_reporter_create - create devlink health reporter
181b4740e3aSMoshe Shemesh  *
182b4740e3aSMoshe Shemesh  * @devlink: devlink instance which the health reports will relate
183b4740e3aSMoshe Shemesh  * @ops: devlink health reporter ops
184b4740e3aSMoshe Shemesh  * @graceful_period: min time (in msec) between recovery attempts
185b4740e3aSMoshe Shemesh  * @priv: driver priv pointer
186b4740e3aSMoshe Shemesh  */
187b4740e3aSMoshe Shemesh struct devlink_health_reporter *
devl_health_reporter_create(struct devlink * devlink,const struct devlink_health_reporter_ops * ops,u64 graceful_period,void * priv)188b4740e3aSMoshe Shemesh devl_health_reporter_create(struct devlink *devlink,
189b4740e3aSMoshe Shemesh 			    const struct devlink_health_reporter_ops *ops,
190b4740e3aSMoshe Shemesh 			    u64 graceful_period, void *priv)
191b4740e3aSMoshe Shemesh {
192b4740e3aSMoshe Shemesh 	struct devlink_health_reporter *reporter;
193b4740e3aSMoshe Shemesh 
194b4740e3aSMoshe Shemesh 	devl_assert_locked(devlink);
195b4740e3aSMoshe Shemesh 
196b4740e3aSMoshe Shemesh 	if (devlink_health_reporter_find_by_name(devlink, ops->name))
197b4740e3aSMoshe Shemesh 		return ERR_PTR(-EEXIST);
198b4740e3aSMoshe Shemesh 
199b4740e3aSMoshe Shemesh 	reporter = __devlink_health_reporter_create(devlink, ops,
200b4740e3aSMoshe Shemesh 						    graceful_period, priv);
201b4740e3aSMoshe Shemesh 	if (IS_ERR(reporter))
202b4740e3aSMoshe Shemesh 		return reporter;
203b4740e3aSMoshe Shemesh 
204b4740e3aSMoshe Shemesh 	list_add_tail(&reporter->list, &devlink->reporter_list);
205b4740e3aSMoshe Shemesh 	return reporter;
206b4740e3aSMoshe Shemesh }
207b4740e3aSMoshe Shemesh EXPORT_SYMBOL_GPL(devl_health_reporter_create);
208b4740e3aSMoshe Shemesh 
209b4740e3aSMoshe Shemesh struct devlink_health_reporter *
devlink_health_reporter_create(struct devlink * devlink,const struct devlink_health_reporter_ops * ops,u64 graceful_period,void * priv)210b4740e3aSMoshe Shemesh devlink_health_reporter_create(struct devlink *devlink,
211b4740e3aSMoshe Shemesh 			       const struct devlink_health_reporter_ops *ops,
212b4740e3aSMoshe Shemesh 			       u64 graceful_period, void *priv)
213b4740e3aSMoshe Shemesh {
214b4740e3aSMoshe Shemesh 	struct devlink_health_reporter *reporter;
215b4740e3aSMoshe Shemesh 
216b4740e3aSMoshe Shemesh 	devl_lock(devlink);
217b4740e3aSMoshe Shemesh 	reporter = devl_health_reporter_create(devlink, ops,
218b4740e3aSMoshe Shemesh 					       graceful_period, priv);
219b4740e3aSMoshe Shemesh 	devl_unlock(devlink);
220b4740e3aSMoshe Shemesh 	return reporter;
221b4740e3aSMoshe Shemesh }
222b4740e3aSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_health_reporter_create);
223b4740e3aSMoshe Shemesh 
224b4740e3aSMoshe Shemesh static void
devlink_health_reporter_free(struct devlink_health_reporter * reporter)225b4740e3aSMoshe Shemesh devlink_health_reporter_free(struct devlink_health_reporter *reporter)
226b4740e3aSMoshe Shemesh {
227b4740e3aSMoshe Shemesh 	if (reporter->dump_fmsg)
228b4740e3aSMoshe Shemesh 		devlink_fmsg_free(reporter->dump_fmsg);
229b4740e3aSMoshe Shemesh 	kfree(reporter);
230b4740e3aSMoshe Shemesh }
231b4740e3aSMoshe Shemesh 
232b4740e3aSMoshe Shemesh /**
233b4740e3aSMoshe Shemesh  * devl_health_reporter_destroy() - destroy devlink health reporter
234b4740e3aSMoshe Shemesh  *
235b4740e3aSMoshe Shemesh  * @reporter: devlink health reporter to destroy
236b4740e3aSMoshe Shemesh  */
237b4740e3aSMoshe Shemesh void
devl_health_reporter_destroy(struct devlink_health_reporter * reporter)238b4740e3aSMoshe Shemesh devl_health_reporter_destroy(struct devlink_health_reporter *reporter)
239b4740e3aSMoshe Shemesh {
240b4740e3aSMoshe Shemesh 	devl_assert_locked(reporter->devlink);
241b4740e3aSMoshe Shemesh 
242b4740e3aSMoshe Shemesh 	list_del(&reporter->list);
243b4740e3aSMoshe Shemesh 	devlink_health_reporter_free(reporter);
244b4740e3aSMoshe Shemesh }
245b4740e3aSMoshe Shemesh EXPORT_SYMBOL_GPL(devl_health_reporter_destroy);
246b4740e3aSMoshe Shemesh 
247b4740e3aSMoshe Shemesh void
devlink_health_reporter_destroy(struct devlink_health_reporter * reporter)248b4740e3aSMoshe Shemesh devlink_health_reporter_destroy(struct devlink_health_reporter *reporter)
249b4740e3aSMoshe Shemesh {
250b4740e3aSMoshe Shemesh 	struct devlink *devlink = reporter->devlink;
251b4740e3aSMoshe Shemesh 
252b4740e3aSMoshe Shemesh 	devl_lock(devlink);
253b4740e3aSMoshe Shemesh 	devl_health_reporter_destroy(reporter);
254b4740e3aSMoshe Shemesh 	devl_unlock(devlink);
255b4740e3aSMoshe Shemesh }
256b4740e3aSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_health_reporter_destroy);
257db6b5f3eSMoshe Shemesh 
25812af29e7SMoshe 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)259db6b5f3eSMoshe Shemesh devlink_nl_health_reporter_fill(struct sk_buff *msg,
260db6b5f3eSMoshe Shemesh 				struct devlink_health_reporter *reporter,
261db6b5f3eSMoshe Shemesh 				enum devlink_command cmd, u32 portid,
262db6b5f3eSMoshe Shemesh 				u32 seq, int flags)
263db6b5f3eSMoshe Shemesh {
264db6b5f3eSMoshe Shemesh 	struct devlink *devlink = reporter->devlink;
265db6b5f3eSMoshe Shemesh 	struct nlattr *reporter_attr;
266db6b5f3eSMoshe Shemesh 	void *hdr;
267db6b5f3eSMoshe Shemesh 
268db6b5f3eSMoshe Shemesh 	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
269db6b5f3eSMoshe Shemesh 	if (!hdr)
270db6b5f3eSMoshe Shemesh 		return -EMSGSIZE;
271db6b5f3eSMoshe Shemesh 
272db6b5f3eSMoshe Shemesh 	if (devlink_nl_put_handle(msg, devlink))
273db6b5f3eSMoshe Shemesh 		goto genlmsg_cancel;
274db6b5f3eSMoshe Shemesh 
275db6b5f3eSMoshe Shemesh 	if (reporter->devlink_port) {
276db6b5f3eSMoshe Shemesh 		if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, reporter->devlink_port->index))
277db6b5f3eSMoshe Shemesh 			goto genlmsg_cancel;
278db6b5f3eSMoshe Shemesh 	}
279db6b5f3eSMoshe Shemesh 	reporter_attr = nla_nest_start_noflag(msg,
280db6b5f3eSMoshe Shemesh 					      DEVLINK_ATTR_HEALTH_REPORTER);
281db6b5f3eSMoshe Shemesh 	if (!reporter_attr)
282db6b5f3eSMoshe Shemesh 		goto genlmsg_cancel;
283db6b5f3eSMoshe Shemesh 	if (nla_put_string(msg, DEVLINK_ATTR_HEALTH_REPORTER_NAME,
284db6b5f3eSMoshe Shemesh 			   reporter->ops->name))
285db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
286db6b5f3eSMoshe Shemesh 	if (nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_STATE,
287db6b5f3eSMoshe Shemesh 		       reporter->health_state))
288db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
289db6b5f3eSMoshe Shemesh 	if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT,
290db6b5f3eSMoshe Shemesh 			      reporter->error_count, DEVLINK_ATTR_PAD))
291db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
292db6b5f3eSMoshe Shemesh 	if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT,
293db6b5f3eSMoshe Shemesh 			      reporter->recovery_count, DEVLINK_ATTR_PAD))
294db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
295db6b5f3eSMoshe Shemesh 	if (reporter->ops->recover &&
296db6b5f3eSMoshe Shemesh 	    nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD,
297db6b5f3eSMoshe Shemesh 			      reporter->graceful_period,
298db6b5f3eSMoshe Shemesh 			      DEVLINK_ATTR_PAD))
299db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
300db6b5f3eSMoshe Shemesh 	if (reporter->ops->recover &&
301db6b5f3eSMoshe Shemesh 	    nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
302db6b5f3eSMoshe Shemesh 		       reporter->auto_recover))
303db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
304db6b5f3eSMoshe Shemesh 	if (reporter->dump_fmsg &&
305db6b5f3eSMoshe Shemesh 	    nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS,
306db6b5f3eSMoshe Shemesh 			      jiffies_to_msecs(reporter->dump_ts),
307db6b5f3eSMoshe Shemesh 			      DEVLINK_ATTR_PAD))
308db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
309db6b5f3eSMoshe Shemesh 	if (reporter->dump_fmsg &&
310db6b5f3eSMoshe Shemesh 	    nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS,
311db6b5f3eSMoshe Shemesh 			      reporter->dump_real_ts, DEVLINK_ATTR_PAD))
312db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
313db6b5f3eSMoshe Shemesh 	if (reporter->ops->dump &&
314db6b5f3eSMoshe Shemesh 	    nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP,
315db6b5f3eSMoshe Shemesh 		       reporter->auto_dump))
316db6b5f3eSMoshe Shemesh 		goto reporter_nest_cancel;
317db6b5f3eSMoshe Shemesh 
318db6b5f3eSMoshe Shemesh 	nla_nest_end(msg, reporter_attr);
319db6b5f3eSMoshe Shemesh 	genlmsg_end(msg, hdr);
320db6b5f3eSMoshe Shemesh 	return 0;
321db6b5f3eSMoshe Shemesh 
322db6b5f3eSMoshe Shemesh reporter_nest_cancel:
323db6b5f3eSMoshe Shemesh 	nla_nest_cancel(msg, reporter_attr);
324db6b5f3eSMoshe Shemesh genlmsg_cancel:
325db6b5f3eSMoshe Shemesh 	genlmsg_cancel(msg, hdr);
326db6b5f3eSMoshe Shemesh 	return -EMSGSIZE;
327db6b5f3eSMoshe Shemesh }
328db6b5f3eSMoshe Shemesh 
32912af29e7SMoshe Shemesh static struct devlink_health_reporter *
devlink_health_reporter_get_from_attrs(struct devlink * devlink,struct nlattr ** attrs)330db6b5f3eSMoshe Shemesh devlink_health_reporter_get_from_attrs(struct devlink *devlink,
331db6b5f3eSMoshe Shemesh 				       struct nlattr **attrs)
332db6b5f3eSMoshe Shemesh {
333db6b5f3eSMoshe Shemesh 	struct devlink_port *devlink_port;
334db6b5f3eSMoshe Shemesh 	char *reporter_name;
335db6b5f3eSMoshe Shemesh 
336db6b5f3eSMoshe Shemesh 	if (!attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME])
337db6b5f3eSMoshe Shemesh 		return NULL;
338db6b5f3eSMoshe Shemesh 
339db6b5f3eSMoshe Shemesh 	reporter_name = nla_data(attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME]);
340db6b5f3eSMoshe Shemesh 	devlink_port = devlink_port_get_from_attrs(devlink, attrs);
341db6b5f3eSMoshe Shemesh 	if (IS_ERR(devlink_port))
342db6b5f3eSMoshe Shemesh 		return devlink_health_reporter_find_by_name(devlink,
343db6b5f3eSMoshe Shemesh 							    reporter_name);
344db6b5f3eSMoshe Shemesh 	else
345db6b5f3eSMoshe Shemesh 		return devlink_port_health_reporter_find_by_name(devlink_port,
346db6b5f3eSMoshe Shemesh 								 reporter_name);
347db6b5f3eSMoshe Shemesh }
348db6b5f3eSMoshe Shemesh 
34912af29e7SMoshe Shemesh static struct devlink_health_reporter *
devlink_health_reporter_get_from_info(struct devlink * devlink,struct genl_info * info)350db6b5f3eSMoshe Shemesh devlink_health_reporter_get_from_info(struct devlink *devlink,
351db6b5f3eSMoshe Shemesh 				      struct genl_info *info)
352db6b5f3eSMoshe Shemesh {
353db6b5f3eSMoshe Shemesh 	return devlink_health_reporter_get_from_attrs(devlink, info->attrs);
354db6b5f3eSMoshe Shemesh }
355db6b5f3eSMoshe Shemesh 
devlink_nl_health_reporter_get_doit(struct sk_buff * skb,struct genl_info * info)3568fa995adSJiri Pirko int devlink_nl_health_reporter_get_doit(struct sk_buff *skb,
357db6b5f3eSMoshe Shemesh 					struct genl_info *info)
358db6b5f3eSMoshe Shemesh {
359db6b5f3eSMoshe Shemesh 	struct devlink *devlink = info->user_ptr[0];
360db6b5f3eSMoshe Shemesh 	struct devlink_health_reporter *reporter;
361db6b5f3eSMoshe Shemesh 	struct sk_buff *msg;
362db6b5f3eSMoshe Shemesh 	int err;
363db6b5f3eSMoshe Shemesh 
364db6b5f3eSMoshe Shemesh 	reporter = devlink_health_reporter_get_from_info(devlink, info);
365db6b5f3eSMoshe Shemesh 	if (!reporter)
366db6b5f3eSMoshe Shemesh 		return -EINVAL;
367db6b5f3eSMoshe Shemesh 
368db6b5f3eSMoshe Shemesh 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
369db6b5f3eSMoshe Shemesh 	if (!msg)
370db6b5f3eSMoshe Shemesh 		return -ENOMEM;
371db6b5f3eSMoshe Shemesh 
372db6b5f3eSMoshe Shemesh 	err = devlink_nl_health_reporter_fill(msg, reporter,
373db6b5f3eSMoshe Shemesh 					      DEVLINK_CMD_HEALTH_REPORTER_GET,
374db6b5f3eSMoshe Shemesh 					      info->snd_portid, info->snd_seq,
375db6b5f3eSMoshe Shemesh 					      0);
376db6b5f3eSMoshe Shemesh 	if (err) {
377db6b5f3eSMoshe Shemesh 		nlmsg_free(msg);
378db6b5f3eSMoshe Shemesh 		return err;
379db6b5f3eSMoshe Shemesh 	}
380db6b5f3eSMoshe Shemesh 
381db6b5f3eSMoshe Shemesh 	return genlmsg_reply(msg, info);
382db6b5f3eSMoshe Shemesh }
383db6b5f3eSMoshe Shemesh 
devlink_nl_health_reporter_get_dump_one(struct sk_buff * msg,struct devlink * devlink,struct netlink_callback * cb,int flags)38424c8e56dSJiri Pirko static int devlink_nl_health_reporter_get_dump_one(struct sk_buff *msg,
385db6b5f3eSMoshe Shemesh 						   struct devlink *devlink,
3867d3c6fecSJiri Pirko 						   struct netlink_callback *cb,
3877d3c6fecSJiri Pirko 						   int flags)
388db6b5f3eSMoshe Shemesh {
389db6b5f3eSMoshe Shemesh 	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
3907288dd2fSJakub Kicinski 	const struct genl_info *info = genl_info_dump(cb);
391db6b5f3eSMoshe Shemesh 	struct devlink_health_reporter *reporter;
392b03f13cbSJiri Pirko 	unsigned long port_index_end = ULONG_MAX;
393b03f13cbSJiri Pirko 	struct nlattr **attrs = info->attrs;
394b03f13cbSJiri Pirko 	unsigned long port_index_start = 0;
395db6b5f3eSMoshe Shemesh 	struct devlink_port *port;
396db6b5f3eSMoshe Shemesh 	unsigned long port_index;
397db6b5f3eSMoshe Shemesh 	int idx = 0;
398db6b5f3eSMoshe Shemesh 	int err;
399db6b5f3eSMoshe Shemesh 
400b03f13cbSJiri Pirko 	if (attrs && attrs[DEVLINK_ATTR_PORT_INDEX]) {
401b03f13cbSJiri Pirko 		port_index_start = nla_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]);
402b03f13cbSJiri Pirko 		port_index_end = port_index_start;
403b03f13cbSJiri Pirko 		flags |= NLM_F_DUMP_FILTERED;
404b03f13cbSJiri Pirko 		goto per_port_dump;
405b03f13cbSJiri Pirko 	}
406b03f13cbSJiri Pirko 
407db6b5f3eSMoshe Shemesh 	list_for_each_entry(reporter, &devlink->reporter_list, list) {
408db6b5f3eSMoshe Shemesh 		if (idx < state->idx) {
409db6b5f3eSMoshe Shemesh 			idx++;
410db6b5f3eSMoshe Shemesh 			continue;
411db6b5f3eSMoshe Shemesh 		}
412db6b5f3eSMoshe Shemesh 		err = devlink_nl_health_reporter_fill(msg, reporter,
413db6b5f3eSMoshe Shemesh 						      DEVLINK_CMD_HEALTH_REPORTER_GET,
414db6b5f3eSMoshe Shemesh 						      NETLINK_CB(cb->skb).portid,
415db6b5f3eSMoshe Shemesh 						      cb->nlh->nlmsg_seq,
4167d3c6fecSJiri Pirko 						      flags);
417db6b5f3eSMoshe Shemesh 		if (err) {
418db6b5f3eSMoshe Shemesh 			state->idx = idx;
419db6b5f3eSMoshe Shemesh 			return err;
420db6b5f3eSMoshe Shemesh 		}
421db6b5f3eSMoshe Shemesh 		idx++;
422db6b5f3eSMoshe Shemesh 	}
423b03f13cbSJiri Pirko per_port_dump:
424b03f13cbSJiri Pirko 	xa_for_each_range(&devlink->ports, port_index, port,
425b03f13cbSJiri Pirko 			  port_index_start, port_index_end) {
426db6b5f3eSMoshe Shemesh 		list_for_each_entry(reporter, &port->reporter_list, list) {
427db6b5f3eSMoshe Shemesh 			if (idx < state->idx) {
428db6b5f3eSMoshe Shemesh 				idx++;
429db6b5f3eSMoshe Shemesh 				continue;
430db6b5f3eSMoshe Shemesh 			}
431db6b5f3eSMoshe Shemesh 			err = devlink_nl_health_reporter_fill(msg, reporter,
432db6b5f3eSMoshe Shemesh 							      DEVLINK_CMD_HEALTH_REPORTER_GET,
433db6b5f3eSMoshe Shemesh 							      NETLINK_CB(cb->skb).portid,
434db6b5f3eSMoshe Shemesh 							      cb->nlh->nlmsg_seq,
4357d3c6fecSJiri Pirko 							      flags);
436db6b5f3eSMoshe Shemesh 			if (err) {
437db6b5f3eSMoshe Shemesh 				state->idx = idx;
438db6b5f3eSMoshe Shemesh 				return err;
439db6b5f3eSMoshe Shemesh 			}
440db6b5f3eSMoshe Shemesh 			idx++;
441db6b5f3eSMoshe Shemesh 		}
442db6b5f3eSMoshe Shemesh 	}
443db6b5f3eSMoshe Shemesh 
444db6b5f3eSMoshe Shemesh 	return 0;
445db6b5f3eSMoshe Shemesh }
446db6b5f3eSMoshe Shemesh 
devlink_nl_health_reporter_get_dumpit(struct sk_buff * skb,struct netlink_callback * cb)44724c8e56dSJiri Pirko int devlink_nl_health_reporter_get_dumpit(struct sk_buff *skb,
44824c8e56dSJiri Pirko 					  struct netlink_callback *cb)
44924c8e56dSJiri Pirko {
45024c8e56dSJiri Pirko 	return devlink_nl_dumpit(skb, cb,
45124c8e56dSJiri Pirko 				 devlink_nl_health_reporter_get_dump_one);
45224c8e56dSJiri Pirko }
453db6b5f3eSMoshe Shemesh 
devlink_nl_cmd_health_reporter_set_doit(struct sk_buff * skb,struct genl_info * info)454db6b5f3eSMoshe Shemesh int devlink_nl_cmd_health_reporter_set_doit(struct sk_buff *skb,
455db6b5f3eSMoshe Shemesh 					    struct genl_info *info)
456db6b5f3eSMoshe Shemesh {
457db6b5f3eSMoshe Shemesh 	struct devlink *devlink = info->user_ptr[0];
458db6b5f3eSMoshe Shemesh 	struct devlink_health_reporter *reporter;
459db6b5f3eSMoshe Shemesh 
460db6b5f3eSMoshe Shemesh 	reporter = devlink_health_reporter_get_from_info(devlink, info);
461db6b5f3eSMoshe Shemesh 	if (!reporter)
462db6b5f3eSMoshe Shemesh 		return -EINVAL;
463db6b5f3eSMoshe Shemesh 
464db6b5f3eSMoshe Shemesh 	if (!reporter->ops->recover &&
465db6b5f3eSMoshe Shemesh 	    (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] ||
466db6b5f3eSMoshe Shemesh 	     info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]))
467db6b5f3eSMoshe Shemesh 		return -EOPNOTSUPP;
468db6b5f3eSMoshe Shemesh 
469db6b5f3eSMoshe Shemesh 	if (!reporter->ops->dump &&
470db6b5f3eSMoshe Shemesh 	    info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
471db6b5f3eSMoshe Shemesh 		return -EOPNOTSUPP;
472db6b5f3eSMoshe Shemesh 
473db6b5f3eSMoshe Shemesh 	if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD])
474db6b5f3eSMoshe Shemesh 		reporter->graceful_period =
475db6b5f3eSMoshe Shemesh 			nla_get_u64(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD]);
476db6b5f3eSMoshe Shemesh 
477db6b5f3eSMoshe Shemesh 	if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])
478db6b5f3eSMoshe Shemesh 		reporter->auto_recover =
479db6b5f3eSMoshe Shemesh 			nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]);
480db6b5f3eSMoshe Shemesh 
481db6b5f3eSMoshe Shemesh 	if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
482db6b5f3eSMoshe Shemesh 		reporter->auto_dump =
483db6b5f3eSMoshe Shemesh 		nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP]);
484db6b5f3eSMoshe Shemesh 
485db6b5f3eSMoshe Shemesh 	return 0;
486db6b5f3eSMoshe Shemesh }
48755b9b249SMoshe Shemesh 
devlink_recover_notify(struct devlink_health_reporter * reporter,enum devlink_command cmd)48855b9b249SMoshe Shemesh static void devlink_recover_notify(struct devlink_health_reporter *reporter,
48955b9b249SMoshe Shemesh 				   enum devlink_command cmd)
49055b9b249SMoshe Shemesh {
49155b9b249SMoshe Shemesh 	struct devlink *devlink = reporter->devlink;
49255b9b249SMoshe Shemesh 	struct sk_buff *msg;
49355b9b249SMoshe Shemesh 	int err;
49455b9b249SMoshe Shemesh 
49555b9b249SMoshe Shemesh 	WARN_ON(cmd != DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
4966f4b9814SJakub Kicinski 	ASSERT_DEVLINK_REGISTERED(devlink);
49755b9b249SMoshe Shemesh 
49855b9b249SMoshe Shemesh 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
49955b9b249SMoshe Shemesh 	if (!msg)
50055b9b249SMoshe Shemesh 		return;
50155b9b249SMoshe Shemesh 
50255b9b249SMoshe Shemesh 	err = devlink_nl_health_reporter_fill(msg, reporter, cmd, 0, 0, 0);
50355b9b249SMoshe Shemesh 	if (err) {
50455b9b249SMoshe Shemesh 		nlmsg_free(msg);
50555b9b249SMoshe Shemesh 		return;
50655b9b249SMoshe Shemesh 	}
50755b9b249SMoshe Shemesh 
50855b9b249SMoshe Shemesh 	genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), msg,
50955b9b249SMoshe Shemesh 				0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
51055b9b249SMoshe Shemesh }
51155b9b249SMoshe Shemesh 
51255b9b249SMoshe Shemesh void
devlink_health_reporter_recovery_done(struct devlink_health_reporter * reporter)51355b9b249SMoshe Shemesh devlink_health_reporter_recovery_done(struct devlink_health_reporter *reporter)
51455b9b249SMoshe Shemesh {
51555b9b249SMoshe Shemesh 	reporter->recovery_count++;
51655b9b249SMoshe Shemesh 	reporter->last_recovery_ts = jiffies;
51755b9b249SMoshe Shemesh }
51855b9b249SMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_health_reporter_recovery_done);
51955b9b249SMoshe Shemesh 
52055b9b249SMoshe Shemesh static int
devlink_health_reporter_recover(struct devlink_health_reporter * reporter,void * priv_ctx,struct netlink_ext_ack * extack)52155b9b249SMoshe Shemesh devlink_health_reporter_recover(struct devlink_health_reporter *reporter,
52255b9b249SMoshe Shemesh 				void *priv_ctx, struct netlink_ext_ack *extack)
52355b9b249SMoshe Shemesh {
52455b9b249SMoshe Shemesh 	int err;
52555b9b249SMoshe Shemesh 
52655b9b249SMoshe Shemesh 	if (reporter->health_state == DEVLINK_HEALTH_REPORTER_STATE_HEALTHY)
52755b9b249SMoshe Shemesh 		return 0;
52855b9b249SMoshe Shemesh 
52955b9b249SMoshe Shemesh 	if (!reporter->ops->recover)
53055b9b249SMoshe Shemesh 		return -EOPNOTSUPP;
53155b9b249SMoshe Shemesh 
53255b9b249SMoshe Shemesh 	err = reporter->ops->recover(reporter, priv_ctx, extack);
53355b9b249SMoshe Shemesh 	if (err)
53455b9b249SMoshe Shemesh 		return err;
53555b9b249SMoshe Shemesh 
53655b9b249SMoshe Shemesh 	devlink_health_reporter_recovery_done(reporter);
53755b9b249SMoshe Shemesh 	reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_HEALTHY;
53855b9b249SMoshe Shemesh 	devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
53955b9b249SMoshe Shemesh 
54055b9b249SMoshe Shemesh 	return 0;
54155b9b249SMoshe Shemesh }
54255b9b249SMoshe Shemesh 
5437004c6c4SMoshe Shemesh static void
devlink_health_dump_clear(struct devlink_health_reporter * reporter)5447004c6c4SMoshe Shemesh devlink_health_dump_clear(struct devlink_health_reporter *reporter)
5457004c6c4SMoshe Shemesh {
5467004c6c4SMoshe Shemesh 	if (!reporter->dump_fmsg)
5477004c6c4SMoshe Shemesh 		return;
5487004c6c4SMoshe Shemesh 	devlink_fmsg_free(reporter->dump_fmsg);
5497004c6c4SMoshe Shemesh 	reporter->dump_fmsg = NULL;
5507004c6c4SMoshe Shemesh }
5517004c6c4SMoshe Shemesh 
devlink_health_do_dump(struct devlink_health_reporter * reporter,void * priv_ctx,struct netlink_ext_ack * extack)55212af29e7SMoshe Shemesh static int devlink_health_do_dump(struct devlink_health_reporter *reporter,
5537004c6c4SMoshe Shemesh 				  void *priv_ctx,
5547004c6c4SMoshe Shemesh 				  struct netlink_ext_ack *extack)
5557004c6c4SMoshe Shemesh {
5567004c6c4SMoshe Shemesh 	int err;
5577004c6c4SMoshe Shemesh 
5587004c6c4SMoshe Shemesh 	if (!reporter->ops->dump)
5597004c6c4SMoshe Shemesh 		return 0;
5607004c6c4SMoshe Shemesh 
5617004c6c4SMoshe Shemesh 	if (reporter->dump_fmsg)
5627004c6c4SMoshe Shemesh 		return 0;
5637004c6c4SMoshe Shemesh 
5647004c6c4SMoshe Shemesh 	reporter->dump_fmsg = devlink_fmsg_alloc();
5657004c6c4SMoshe Shemesh 	if (!reporter->dump_fmsg) {
5667004c6c4SMoshe Shemesh 		err = -ENOMEM;
5677004c6c4SMoshe Shemesh 		return err;
5687004c6c4SMoshe Shemesh 	}
5697004c6c4SMoshe Shemesh 
5707004c6c4SMoshe Shemesh 	err = devlink_fmsg_obj_nest_start(reporter->dump_fmsg);
5717004c6c4SMoshe Shemesh 	if (err)
5727004c6c4SMoshe Shemesh 		goto dump_err;
5737004c6c4SMoshe Shemesh 
5747004c6c4SMoshe Shemesh 	err = reporter->ops->dump(reporter, reporter->dump_fmsg,
5757004c6c4SMoshe Shemesh 				  priv_ctx, extack);
5767004c6c4SMoshe Shemesh 	if (err)
5777004c6c4SMoshe Shemesh 		goto dump_err;
5787004c6c4SMoshe Shemesh 
5797004c6c4SMoshe Shemesh 	err = devlink_fmsg_obj_nest_end(reporter->dump_fmsg);
5807004c6c4SMoshe Shemesh 	if (err)
5817004c6c4SMoshe Shemesh 		goto dump_err;
5827004c6c4SMoshe Shemesh 
5837004c6c4SMoshe Shemesh 	reporter->dump_ts = jiffies;
5847004c6c4SMoshe Shemesh 	reporter->dump_real_ts = ktime_get_real_ns();
5857004c6c4SMoshe Shemesh 
5867004c6c4SMoshe Shemesh 	return 0;
5877004c6c4SMoshe Shemesh 
5887004c6c4SMoshe Shemesh dump_err:
5897004c6c4SMoshe Shemesh 	devlink_health_dump_clear(reporter);
5907004c6c4SMoshe Shemesh 	return err;
5917004c6c4SMoshe Shemesh }
5927004c6c4SMoshe Shemesh 
devlink_health_report(struct devlink_health_reporter * reporter,const char * msg,void * priv_ctx)59355b9b249SMoshe Shemesh int devlink_health_report(struct devlink_health_reporter *reporter,
59455b9b249SMoshe Shemesh 			  const char *msg, void *priv_ctx)
59555b9b249SMoshe Shemesh {
59655b9b249SMoshe Shemesh 	enum devlink_health_reporter_state prev_health_state;
59755b9b249SMoshe Shemesh 	struct devlink *devlink = reporter->devlink;
59855b9b249SMoshe Shemesh 	unsigned long recover_ts_threshold;
59955b9b249SMoshe Shemesh 	int ret;
60055b9b249SMoshe Shemesh 
60155b9b249SMoshe Shemesh 	/* write a log message of the current error */
60255b9b249SMoshe Shemesh 	WARN_ON(!msg);
60355b9b249SMoshe Shemesh 	trace_devlink_health_report(devlink, reporter->ops->name, msg);
60455b9b249SMoshe Shemesh 	reporter->error_count++;
60555b9b249SMoshe Shemesh 	prev_health_state = reporter->health_state;
60655b9b249SMoshe Shemesh 	reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_ERROR;
60755b9b249SMoshe Shemesh 	devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
60855b9b249SMoshe Shemesh 
60955b9b249SMoshe Shemesh 	/* abort if the previous error wasn't recovered */
61055b9b249SMoshe Shemesh 	recover_ts_threshold = reporter->last_recovery_ts +
61155b9b249SMoshe Shemesh 			       msecs_to_jiffies(reporter->graceful_period);
61255b9b249SMoshe Shemesh 	if (reporter->auto_recover &&
61355b9b249SMoshe Shemesh 	    (prev_health_state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY ||
61455b9b249SMoshe Shemesh 	     (reporter->last_recovery_ts && reporter->recovery_count &&
61555b9b249SMoshe Shemesh 	      time_is_after_jiffies(recover_ts_threshold)))) {
61655b9b249SMoshe Shemesh 		trace_devlink_health_recover_aborted(devlink,
61755b9b249SMoshe Shemesh 						     reporter->ops->name,
61855b9b249SMoshe Shemesh 						     reporter->health_state,
61955b9b249SMoshe Shemesh 						     jiffies -
62055b9b249SMoshe Shemesh 						     reporter->last_recovery_ts);
62155b9b249SMoshe Shemesh 		return -ECANCELED;
62255b9b249SMoshe Shemesh 	}
62355b9b249SMoshe Shemesh 
62455b9b249SMoshe Shemesh 	if (reporter->auto_dump) {
625*aba0e909SMoshe Shemesh 		devl_lock(devlink);
62655b9b249SMoshe Shemesh 		/* store current dump of current error, for later analysis */
62755b9b249SMoshe Shemesh 		devlink_health_do_dump(reporter, priv_ctx, NULL);
628*aba0e909SMoshe Shemesh 		devl_unlock(devlink);
62955b9b249SMoshe Shemesh 	}
63055b9b249SMoshe Shemesh 
63155b9b249SMoshe Shemesh 	if (!reporter->auto_recover)
63255b9b249SMoshe Shemesh 		return 0;
63355b9b249SMoshe Shemesh 
63455b9b249SMoshe Shemesh 	devl_lock(devlink);
63555b9b249SMoshe Shemesh 	ret = devlink_health_reporter_recover(reporter, priv_ctx, NULL);
63655b9b249SMoshe Shemesh 	devl_unlock(devlink);
63755b9b249SMoshe Shemesh 
63855b9b249SMoshe Shemesh 	return ret;
63955b9b249SMoshe Shemesh }
64055b9b249SMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_health_report);
64155b9b249SMoshe Shemesh 
64255b9b249SMoshe Shemesh void
devlink_health_reporter_state_update(struct devlink_health_reporter * reporter,enum devlink_health_reporter_state state)64355b9b249SMoshe Shemesh devlink_health_reporter_state_update(struct devlink_health_reporter *reporter,
64455b9b249SMoshe Shemesh 				     enum devlink_health_reporter_state state)
64555b9b249SMoshe Shemesh {
64655b9b249SMoshe Shemesh 	if (WARN_ON(state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY &&
64755b9b249SMoshe Shemesh 		    state != DEVLINK_HEALTH_REPORTER_STATE_ERROR))
64855b9b249SMoshe Shemesh 		return;
64955b9b249SMoshe Shemesh 
65055b9b249SMoshe Shemesh 	if (reporter->health_state == state)
65155b9b249SMoshe Shemesh 		return;
65255b9b249SMoshe Shemesh 
65355b9b249SMoshe Shemesh 	reporter->health_state = state;
65455b9b249SMoshe Shemesh 	trace_devlink_health_reporter_state_update(reporter->devlink,
65555b9b249SMoshe Shemesh 						   reporter->ops->name, state);
65655b9b249SMoshe Shemesh 	devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
65755b9b249SMoshe Shemesh }
65855b9b249SMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_health_reporter_state_update);
65955b9b249SMoshe Shemesh 
devlink_nl_cmd_health_reporter_recover_doit(struct sk_buff * skb,struct genl_info * info)66055b9b249SMoshe Shemesh int devlink_nl_cmd_health_reporter_recover_doit(struct sk_buff *skb,
66155b9b249SMoshe Shemesh 						struct genl_info *info)
66255b9b249SMoshe Shemesh {
66355b9b249SMoshe Shemesh 	struct devlink *devlink = info->user_ptr[0];
66455b9b249SMoshe Shemesh 	struct devlink_health_reporter *reporter;
66555b9b249SMoshe Shemesh 
66655b9b249SMoshe Shemesh 	reporter = devlink_health_reporter_get_from_info(devlink, info);
66755b9b249SMoshe Shemesh 	if (!reporter)
66855b9b249SMoshe Shemesh 		return -EINVAL;
66955b9b249SMoshe Shemesh 
67055b9b249SMoshe Shemesh 	return devlink_health_reporter_recover(reporter, NULL, info->extack);
67155b9b249SMoshe Shemesh }
672a929df7fSMoshe Shemesh 
devlink_fmsg_nest_common(struct devlink_fmsg * fmsg,int attrtype)673a929df7fSMoshe Shemesh static int devlink_fmsg_nest_common(struct devlink_fmsg *fmsg,
674a929df7fSMoshe Shemesh 				    int attrtype)
675a929df7fSMoshe Shemesh {
676a929df7fSMoshe Shemesh 	struct devlink_fmsg_item *item;
677a929df7fSMoshe Shemesh 
678a929df7fSMoshe Shemesh 	item = kzalloc(sizeof(*item), GFP_KERNEL);
679a929df7fSMoshe Shemesh 	if (!item)
680a929df7fSMoshe Shemesh 		return -ENOMEM;
681a929df7fSMoshe Shemesh 
682a929df7fSMoshe Shemesh 	item->attrtype = attrtype;
683a929df7fSMoshe Shemesh 	list_add_tail(&item->list, &fmsg->item_list);
684a929df7fSMoshe Shemesh 
685a929df7fSMoshe Shemesh 	return 0;
686a929df7fSMoshe Shemesh }
687a929df7fSMoshe Shemesh 
devlink_fmsg_obj_nest_start(struct devlink_fmsg * fmsg)688a929df7fSMoshe Shemesh int devlink_fmsg_obj_nest_start(struct devlink_fmsg *fmsg)
689a929df7fSMoshe Shemesh {
690a929df7fSMoshe Shemesh 	if (fmsg->putting_binary)
691a929df7fSMoshe Shemesh 		return -EINVAL;
692a929df7fSMoshe Shemesh 
693a929df7fSMoshe Shemesh 	return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_OBJ_NEST_START);
694a929df7fSMoshe Shemesh }
695a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_start);
696a929df7fSMoshe Shemesh 
devlink_fmsg_nest_end(struct devlink_fmsg * fmsg)697a929df7fSMoshe Shemesh static int devlink_fmsg_nest_end(struct devlink_fmsg *fmsg)
698a929df7fSMoshe Shemesh {
699a929df7fSMoshe Shemesh 	if (fmsg->putting_binary)
700a929df7fSMoshe Shemesh 		return -EINVAL;
701a929df7fSMoshe Shemesh 
702a929df7fSMoshe Shemesh 	return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_NEST_END);
703a929df7fSMoshe Shemesh }
704a929df7fSMoshe Shemesh 
devlink_fmsg_obj_nest_end(struct devlink_fmsg * fmsg)705a929df7fSMoshe Shemesh int devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg)
706a929df7fSMoshe Shemesh {
707a929df7fSMoshe Shemesh 	if (fmsg->putting_binary)
708a929df7fSMoshe Shemesh 		return -EINVAL;
709a929df7fSMoshe Shemesh 
710a929df7fSMoshe Shemesh 	return devlink_fmsg_nest_end(fmsg);
711a929df7fSMoshe Shemesh }
712a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_end);
713a929df7fSMoshe Shemesh 
714a929df7fSMoshe Shemesh #define DEVLINK_FMSG_MAX_SIZE (GENLMSG_DEFAULT_SIZE - GENL_HDRLEN - NLA_HDRLEN)
715a929df7fSMoshe Shemesh 
devlink_fmsg_put_name(struct devlink_fmsg * fmsg,const char * name)716a929df7fSMoshe Shemesh static int devlink_fmsg_put_name(struct devlink_fmsg *fmsg, const char *name)
717a929df7fSMoshe Shemesh {
718a929df7fSMoshe Shemesh 	struct devlink_fmsg_item *item;
719a929df7fSMoshe Shemesh 
720a929df7fSMoshe Shemesh 	if (fmsg->putting_binary)
721a929df7fSMoshe Shemesh 		return -EINVAL;
722a929df7fSMoshe Shemesh 
723a929df7fSMoshe Shemesh 	if (strlen(name) + 1 > DEVLINK_FMSG_MAX_SIZE)
724a929df7fSMoshe Shemesh 		return -EMSGSIZE;
725a929df7fSMoshe Shemesh 
726a929df7fSMoshe Shemesh 	item = kzalloc(sizeof(*item) + strlen(name) + 1, GFP_KERNEL);
727a929df7fSMoshe Shemesh 	if (!item)
728a929df7fSMoshe Shemesh 		return -ENOMEM;
729a929df7fSMoshe Shemesh 
730a929df7fSMoshe Shemesh 	item->nla_type = NLA_NUL_STRING;
731a929df7fSMoshe Shemesh 	item->len = strlen(name) + 1;
732a929df7fSMoshe Shemesh 	item->attrtype = DEVLINK_ATTR_FMSG_OBJ_NAME;
733a929df7fSMoshe Shemesh 	memcpy(&item->value, name, item->len);
734a929df7fSMoshe Shemesh 	list_add_tail(&item->list, &fmsg->item_list);
735a929df7fSMoshe Shemesh 
736a929df7fSMoshe Shemesh 	return 0;
737a929df7fSMoshe Shemesh }
738a929df7fSMoshe Shemesh 
devlink_fmsg_pair_nest_start(struct devlink_fmsg * fmsg,const char * name)739a929df7fSMoshe Shemesh int devlink_fmsg_pair_nest_start(struct devlink_fmsg *fmsg, const char *name)
740a929df7fSMoshe Shemesh {
741a929df7fSMoshe Shemesh 	int err;
742a929df7fSMoshe Shemesh 
743a929df7fSMoshe Shemesh 	if (fmsg->putting_binary)
744a929df7fSMoshe Shemesh 		return -EINVAL;
745a929df7fSMoshe Shemesh 
746a929df7fSMoshe Shemesh 	err = devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_PAIR_NEST_START);
747a929df7fSMoshe Shemesh 	if (err)
748a929df7fSMoshe Shemesh 		return err;
749a929df7fSMoshe Shemesh 
750a929df7fSMoshe Shemesh 	err = devlink_fmsg_put_name(fmsg, name);
751a929df7fSMoshe Shemesh 	if (err)
752a929df7fSMoshe Shemesh 		return err;
753a929df7fSMoshe Shemesh 
754a929df7fSMoshe Shemesh 	return 0;
755a929df7fSMoshe Shemesh }
756a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_start);
757a929df7fSMoshe Shemesh 
devlink_fmsg_pair_nest_end(struct devlink_fmsg * fmsg)758a929df7fSMoshe Shemesh int devlink_fmsg_pair_nest_end(struct devlink_fmsg *fmsg)
759a929df7fSMoshe Shemesh {
760a929df7fSMoshe Shemesh 	if (fmsg->putting_binary)
761a929df7fSMoshe Shemesh 		return -EINVAL;
762a929df7fSMoshe Shemesh 
763a929df7fSMoshe Shemesh 	return devlink_fmsg_nest_end(fmsg);
764a929df7fSMoshe Shemesh }
765a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_end);
766a929df7fSMoshe Shemesh 
devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg * fmsg,const char * name)767a929df7fSMoshe Shemesh int devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg *fmsg,
768a929df7fSMoshe Shemesh 				     const char *name)
769a929df7fSMoshe Shemesh {
770a929df7fSMoshe Shemesh 	int err;
771a929df7fSMoshe Shemesh 
772a929df7fSMoshe Shemesh 	if (fmsg->putting_binary)
773a929df7fSMoshe Shemesh 		return -EINVAL;
774a929df7fSMoshe Shemesh 
775a929df7fSMoshe Shemesh 	err = devlink_fmsg_pair_nest_start(fmsg, name);
776a929df7fSMoshe Shemesh 	if (err)
777a929df7fSMoshe Shemesh 		return err;
778a929df7fSMoshe Shemesh 
779a929df7fSMoshe Shemesh 	err = devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_ARR_NEST_START);
780a929df7fSMoshe Shemesh 	if (err)
781a929df7fSMoshe Shemesh 		return err;
782a929df7fSMoshe Shemesh 
783a929df7fSMoshe Shemesh 	return 0;
784a929df7fSMoshe Shemesh }
785a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_start);
786a929df7fSMoshe Shemesh 
devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg * fmsg)787a929df7fSMoshe Shemesh int devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg *fmsg)
788a929df7fSMoshe Shemesh {
789a929df7fSMoshe Shemesh 	int err;
790a929df7fSMoshe Shemesh 
791a929df7fSMoshe Shemesh 	if (fmsg->putting_binary)
792a929df7fSMoshe Shemesh 		return -EINVAL;
793a929df7fSMoshe Shemesh 
794a929df7fSMoshe Shemesh 	err = devlink_fmsg_nest_end(fmsg);
795a929df7fSMoshe Shemesh 	if (err)
796a929df7fSMoshe Shemesh 		return err;
797a929df7fSMoshe Shemesh 
798a929df7fSMoshe Shemesh 	err = devlink_fmsg_nest_end(fmsg);
799a929df7fSMoshe Shemesh 	if (err)
800a929df7fSMoshe Shemesh 		return err;
801a929df7fSMoshe Shemesh 
802a929df7fSMoshe Shemesh 	return 0;
803a929df7fSMoshe Shemesh }
804a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_end);
805a929df7fSMoshe Shemesh 
devlink_fmsg_binary_pair_nest_start(struct devlink_fmsg * fmsg,const char * name)806a929df7fSMoshe Shemesh int devlink_fmsg_binary_pair_nest_start(struct devlink_fmsg *fmsg,
807a929df7fSMoshe Shemesh 					const char *name)
808a929df7fSMoshe Shemesh {
809a929df7fSMoshe Shemesh 	int err;
810a929df7fSMoshe Shemesh 
811a929df7fSMoshe Shemesh 	err = devlink_fmsg_arr_pair_nest_start(fmsg, name);
812a929df7fSMoshe Shemesh 	if (err)
813a929df7fSMoshe Shemesh 		return err;
814a929df7fSMoshe Shemesh 
815a929df7fSMoshe Shemesh 	fmsg->putting_binary = true;
816a929df7fSMoshe Shemesh 	return err;
817a929df7fSMoshe Shemesh }
818a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_start);
819a929df7fSMoshe Shemesh 
devlink_fmsg_binary_pair_nest_end(struct devlink_fmsg * fmsg)820a929df7fSMoshe Shemesh int devlink_fmsg_binary_pair_nest_end(struct devlink_fmsg *fmsg)
821a929df7fSMoshe Shemesh {
822a929df7fSMoshe Shemesh 	if (!fmsg->putting_binary)
823a929df7fSMoshe Shemesh 		return -EINVAL;
824a929df7fSMoshe Shemesh 
825a929df7fSMoshe Shemesh 	fmsg->putting_binary = false;
826a929df7fSMoshe Shemesh 	return devlink_fmsg_arr_pair_nest_end(fmsg);
827a929df7fSMoshe Shemesh }
828a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_end);
829a929df7fSMoshe Shemesh 
devlink_fmsg_put_value(struct devlink_fmsg * fmsg,const void * value,u16 value_len,u8 value_nla_type)830a929df7fSMoshe Shemesh static int devlink_fmsg_put_value(struct devlink_fmsg *fmsg,
831a929df7fSMoshe Shemesh 				  const void *value, u16 value_len,
832a929df7fSMoshe Shemesh 				  u8 value_nla_type)
833a929df7fSMoshe Shemesh {
834a929df7fSMoshe Shemesh 	struct devlink_fmsg_item *item;
835a929df7fSMoshe Shemesh 
836a929df7fSMoshe Shemesh 	if (value_len > DEVLINK_FMSG_MAX_SIZE)
837a929df7fSMoshe Shemesh 		return -EMSGSIZE;
838a929df7fSMoshe Shemesh 
839a929df7fSMoshe Shemesh 	item = kzalloc(sizeof(*item) + value_len, GFP_KERNEL);
840a929df7fSMoshe Shemesh 	if (!item)
841a929df7fSMoshe Shemesh 		return -ENOMEM;
842a929df7fSMoshe Shemesh 
843a929df7fSMoshe Shemesh 	item->nla_type = value_nla_type;
844a929df7fSMoshe Shemesh 	item->len = value_len;
845a929df7fSMoshe Shemesh 	item->attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
846a929df7fSMoshe Shemesh 	memcpy(&item->value, value, item->len);
847a929df7fSMoshe Shemesh 	list_add_tail(&item->list, &fmsg->item_list);
848a929df7fSMoshe Shemesh 
849a929df7fSMoshe Shemesh 	return 0;
850a929df7fSMoshe Shemesh }
851a929df7fSMoshe Shemesh 
devlink_fmsg_bool_put(struct devlink_fmsg * fmsg,bool value)852a929df7fSMoshe Shemesh static int devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value)
853a929df7fSMoshe Shemesh {
854a929df7fSMoshe Shemesh 	if (fmsg->putting_binary)
855a929df7fSMoshe Shemesh 		return -EINVAL;
856a929df7fSMoshe Shemesh 
857a929df7fSMoshe Shemesh 	return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_FLAG);
858a929df7fSMoshe Shemesh }
859a929df7fSMoshe Shemesh 
devlink_fmsg_u8_put(struct devlink_fmsg * fmsg,u8 value)860a929df7fSMoshe Shemesh static int devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value)
861a929df7fSMoshe Shemesh {
862a929df7fSMoshe Shemesh 	if (fmsg->putting_binary)
863a929df7fSMoshe Shemesh 		return -EINVAL;
864a929df7fSMoshe Shemesh 
865a929df7fSMoshe Shemesh 	return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U8);
866a929df7fSMoshe Shemesh }
867a929df7fSMoshe Shemesh 
devlink_fmsg_u32_put(struct devlink_fmsg * fmsg,u32 value)868a929df7fSMoshe Shemesh int devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value)
869a929df7fSMoshe Shemesh {
870a929df7fSMoshe Shemesh 	if (fmsg->putting_binary)
871a929df7fSMoshe Shemesh 		return -EINVAL;
872a929df7fSMoshe Shemesh 
873a929df7fSMoshe Shemesh 	return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U32);
874a929df7fSMoshe Shemesh }
875a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_u32_put);
876a929df7fSMoshe Shemesh 
devlink_fmsg_u64_put(struct devlink_fmsg * fmsg,u64 value)877a929df7fSMoshe Shemesh static int devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value)
878a929df7fSMoshe Shemesh {
879a929df7fSMoshe Shemesh 	if (fmsg->putting_binary)
880a929df7fSMoshe Shemesh 		return -EINVAL;
881a929df7fSMoshe Shemesh 
882a929df7fSMoshe Shemesh 	return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U64);
883a929df7fSMoshe Shemesh }
884a929df7fSMoshe Shemesh 
devlink_fmsg_string_put(struct devlink_fmsg * fmsg,const char * value)885a929df7fSMoshe Shemesh int devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value)
886a929df7fSMoshe Shemesh {
887a929df7fSMoshe Shemesh 	if (fmsg->putting_binary)
888a929df7fSMoshe Shemesh 		return -EINVAL;
889a929df7fSMoshe Shemesh 
890a929df7fSMoshe Shemesh 	return devlink_fmsg_put_value(fmsg, value, strlen(value) + 1,
891a929df7fSMoshe Shemesh 				      NLA_NUL_STRING);
892a929df7fSMoshe Shemesh }
893a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_string_put);
894a929df7fSMoshe Shemesh 
devlink_fmsg_binary_put(struct devlink_fmsg * fmsg,const void * value,u16 value_len)895a929df7fSMoshe Shemesh int devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value,
896a929df7fSMoshe Shemesh 			    u16 value_len)
897a929df7fSMoshe Shemesh {
898a929df7fSMoshe Shemesh 	if (!fmsg->putting_binary)
899a929df7fSMoshe Shemesh 		return -EINVAL;
900a929df7fSMoshe Shemesh 
901a929df7fSMoshe Shemesh 	return devlink_fmsg_put_value(fmsg, value, value_len, NLA_BINARY);
902a929df7fSMoshe Shemesh }
903a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_binary_put);
904a929df7fSMoshe Shemesh 
devlink_fmsg_bool_pair_put(struct devlink_fmsg * fmsg,const char * name,bool value)905a929df7fSMoshe Shemesh int devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name,
906a929df7fSMoshe Shemesh 			       bool value)
907a929df7fSMoshe Shemesh {
908a929df7fSMoshe Shemesh 	int err;
909a929df7fSMoshe Shemesh 
910a929df7fSMoshe Shemesh 	err = devlink_fmsg_pair_nest_start(fmsg, name);
911a929df7fSMoshe Shemesh 	if (err)
912a929df7fSMoshe Shemesh 		return err;
913a929df7fSMoshe Shemesh 
914a929df7fSMoshe Shemesh 	err = devlink_fmsg_bool_put(fmsg, value);
915a929df7fSMoshe Shemesh 	if (err)
916a929df7fSMoshe Shemesh 		return err;
917a929df7fSMoshe Shemesh 
918a929df7fSMoshe Shemesh 	err = devlink_fmsg_pair_nest_end(fmsg);
919a929df7fSMoshe Shemesh 	if (err)
920a929df7fSMoshe Shemesh 		return err;
921a929df7fSMoshe Shemesh 
922a929df7fSMoshe Shemesh 	return 0;
923a929df7fSMoshe Shemesh }
924a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_bool_pair_put);
925a929df7fSMoshe Shemesh 
devlink_fmsg_u8_pair_put(struct devlink_fmsg * fmsg,const char * name,u8 value)926a929df7fSMoshe Shemesh int devlink_fmsg_u8_pair_put(struct devlink_fmsg *fmsg, const char *name,
927a929df7fSMoshe Shemesh 			     u8 value)
928a929df7fSMoshe Shemesh {
929a929df7fSMoshe Shemesh 	int err;
930a929df7fSMoshe Shemesh 
931a929df7fSMoshe Shemesh 	err = devlink_fmsg_pair_nest_start(fmsg, name);
932a929df7fSMoshe Shemesh 	if (err)
933a929df7fSMoshe Shemesh 		return err;
934a929df7fSMoshe Shemesh 
935a929df7fSMoshe Shemesh 	err = devlink_fmsg_u8_put(fmsg, value);
936a929df7fSMoshe Shemesh 	if (err)
937a929df7fSMoshe Shemesh 		return err;
938a929df7fSMoshe Shemesh 
939a929df7fSMoshe Shemesh 	err = devlink_fmsg_pair_nest_end(fmsg);
940a929df7fSMoshe Shemesh 	if (err)
941a929df7fSMoshe Shemesh 		return err;
942a929df7fSMoshe Shemesh 
943a929df7fSMoshe Shemesh 	return 0;
944a929df7fSMoshe Shemesh }
945a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_u8_pair_put);
946a929df7fSMoshe Shemesh 
devlink_fmsg_u32_pair_put(struct devlink_fmsg * fmsg,const char * name,u32 value)947a929df7fSMoshe Shemesh int devlink_fmsg_u32_pair_put(struct devlink_fmsg *fmsg, const char *name,
948a929df7fSMoshe Shemesh 			      u32 value)
949a929df7fSMoshe Shemesh {
950a929df7fSMoshe Shemesh 	int err;
951a929df7fSMoshe Shemesh 
952a929df7fSMoshe Shemesh 	err = devlink_fmsg_pair_nest_start(fmsg, name);
953a929df7fSMoshe Shemesh 	if (err)
954a929df7fSMoshe Shemesh 		return err;
955a929df7fSMoshe Shemesh 
956a929df7fSMoshe Shemesh 	err = devlink_fmsg_u32_put(fmsg, value);
957a929df7fSMoshe Shemesh 	if (err)
958a929df7fSMoshe Shemesh 		return err;
959a929df7fSMoshe Shemesh 
960a929df7fSMoshe Shemesh 	err = devlink_fmsg_pair_nest_end(fmsg);
961a929df7fSMoshe Shemesh 	if (err)
962a929df7fSMoshe Shemesh 		return err;
963a929df7fSMoshe Shemesh 
964a929df7fSMoshe Shemesh 	return 0;
965a929df7fSMoshe Shemesh }
966a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_u32_pair_put);
967a929df7fSMoshe Shemesh 
devlink_fmsg_u64_pair_put(struct devlink_fmsg * fmsg,const char * name,u64 value)968a929df7fSMoshe Shemesh int devlink_fmsg_u64_pair_put(struct devlink_fmsg *fmsg, const char *name,
969a929df7fSMoshe Shemesh 			      u64 value)
970a929df7fSMoshe Shemesh {
971a929df7fSMoshe Shemesh 	int err;
972a929df7fSMoshe Shemesh 
973a929df7fSMoshe Shemesh 	err = devlink_fmsg_pair_nest_start(fmsg, name);
974a929df7fSMoshe Shemesh 	if (err)
975a929df7fSMoshe Shemesh 		return err;
976a929df7fSMoshe Shemesh 
977a929df7fSMoshe Shemesh 	err = devlink_fmsg_u64_put(fmsg, value);
978a929df7fSMoshe Shemesh 	if (err)
979a929df7fSMoshe Shemesh 		return err;
980a929df7fSMoshe Shemesh 
981a929df7fSMoshe Shemesh 	err = devlink_fmsg_pair_nest_end(fmsg);
982a929df7fSMoshe Shemesh 	if (err)
983a929df7fSMoshe Shemesh 		return err;
984a929df7fSMoshe Shemesh 
985a929df7fSMoshe Shemesh 	return 0;
986a929df7fSMoshe Shemesh }
987a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_u64_pair_put);
988a929df7fSMoshe Shemesh 
devlink_fmsg_string_pair_put(struct devlink_fmsg * fmsg,const char * name,const char * value)989a929df7fSMoshe Shemesh int devlink_fmsg_string_pair_put(struct devlink_fmsg *fmsg, const char *name,
990a929df7fSMoshe Shemesh 				 const char *value)
991a929df7fSMoshe Shemesh {
992a929df7fSMoshe Shemesh 	int err;
993a929df7fSMoshe Shemesh 
994a929df7fSMoshe Shemesh 	err = devlink_fmsg_pair_nest_start(fmsg, name);
995a929df7fSMoshe Shemesh 	if (err)
996a929df7fSMoshe Shemesh 		return err;
997a929df7fSMoshe Shemesh 
998a929df7fSMoshe Shemesh 	err = devlink_fmsg_string_put(fmsg, value);
999a929df7fSMoshe Shemesh 	if (err)
1000a929df7fSMoshe Shemesh 		return err;
1001a929df7fSMoshe Shemesh 
1002a929df7fSMoshe Shemesh 	err = devlink_fmsg_pair_nest_end(fmsg);
1003a929df7fSMoshe Shemesh 	if (err)
1004a929df7fSMoshe Shemesh 		return err;
1005a929df7fSMoshe Shemesh 
1006a929df7fSMoshe Shemesh 	return 0;
1007a929df7fSMoshe Shemesh }
1008a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_string_pair_put);
1009a929df7fSMoshe Shemesh 
devlink_fmsg_binary_pair_put(struct devlink_fmsg * fmsg,const char * name,const void * value,u32 value_len)1010a929df7fSMoshe Shemesh int devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name,
1011a929df7fSMoshe Shemesh 				 const void *value, u32 value_len)
1012a929df7fSMoshe Shemesh {
1013a929df7fSMoshe Shemesh 	u32 data_size;
1014a929df7fSMoshe Shemesh 	int end_err;
1015a929df7fSMoshe Shemesh 	u32 offset;
1016a929df7fSMoshe Shemesh 	int err;
1017a929df7fSMoshe Shemesh 
1018a929df7fSMoshe Shemesh 	err = devlink_fmsg_binary_pair_nest_start(fmsg, name);
1019a929df7fSMoshe Shemesh 	if (err)
1020a929df7fSMoshe Shemesh 		return err;
1021a929df7fSMoshe Shemesh 
1022a929df7fSMoshe Shemesh 	for (offset = 0; offset < value_len; offset += data_size) {
1023a929df7fSMoshe Shemesh 		data_size = value_len - offset;
1024a929df7fSMoshe Shemesh 		if (data_size > DEVLINK_FMSG_MAX_SIZE)
1025a929df7fSMoshe Shemesh 			data_size = DEVLINK_FMSG_MAX_SIZE;
1026a929df7fSMoshe Shemesh 		err = devlink_fmsg_binary_put(fmsg, value + offset, data_size);
1027a929df7fSMoshe Shemesh 		if (err)
1028a929df7fSMoshe Shemesh 			break;
1029a929df7fSMoshe Shemesh 		/* Exit from loop with a break (instead of
1030a929df7fSMoshe Shemesh 		 * return) to make sure putting_binary is turned off in
1031a929df7fSMoshe Shemesh 		 * devlink_fmsg_binary_pair_nest_end
1032a929df7fSMoshe Shemesh 		 */
1033a929df7fSMoshe Shemesh 	}
1034a929df7fSMoshe Shemesh 
1035a929df7fSMoshe Shemesh 	end_err = devlink_fmsg_binary_pair_nest_end(fmsg);
1036a929df7fSMoshe Shemesh 	if (end_err)
1037a929df7fSMoshe Shemesh 		err = end_err;
1038a929df7fSMoshe Shemesh 
1039a929df7fSMoshe Shemesh 	return err;
1040a929df7fSMoshe Shemesh }
1041a929df7fSMoshe Shemesh EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_put);
1042a929df7fSMoshe Shemesh 
1043a929df7fSMoshe Shemesh static int
devlink_fmsg_item_fill_type(struct devlink_fmsg_item * msg,struct sk_buff * skb)1044a929df7fSMoshe Shemesh devlink_fmsg_item_fill_type(struct devlink_fmsg_item *msg, struct sk_buff *skb)
1045a929df7fSMoshe Shemesh {
1046a929df7fSMoshe Shemesh 	switch (msg->nla_type) {
1047a929df7fSMoshe Shemesh 	case NLA_FLAG:
1048a929df7fSMoshe Shemesh 	case NLA_U8:
1049a929df7fSMoshe Shemesh 	case NLA_U32:
1050a929df7fSMoshe Shemesh 	case NLA_U64:
1051a929df7fSMoshe Shemesh 	case NLA_NUL_STRING:
1052a929df7fSMoshe Shemesh 	case NLA_BINARY:
1053a929df7fSMoshe Shemesh 		return nla_put_u8(skb, DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE,
1054a929df7fSMoshe Shemesh 				  msg->nla_type);
1055a929df7fSMoshe Shemesh 	default:
1056a929df7fSMoshe Shemesh 		return -EINVAL;
1057a929df7fSMoshe Shemesh 	}
1058a929df7fSMoshe Shemesh }
1059a929df7fSMoshe Shemesh 
1060a929df7fSMoshe Shemesh static int
devlink_fmsg_item_fill_data(struct devlink_fmsg_item * msg,struct sk_buff * skb)1061a929df7fSMoshe Shemesh devlink_fmsg_item_fill_data(struct devlink_fmsg_item *msg, struct sk_buff *skb)
1062a929df7fSMoshe Shemesh {
1063a929df7fSMoshe Shemesh 	int attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
1064a929df7fSMoshe Shemesh 	u8 tmp;
1065a929df7fSMoshe Shemesh 
1066a929df7fSMoshe Shemesh 	switch (msg->nla_type) {
1067a929df7fSMoshe Shemesh 	case NLA_FLAG:
1068a929df7fSMoshe Shemesh 		/* Always provide flag data, regardless of its value */
1069a929df7fSMoshe Shemesh 		tmp = *(bool *)msg->value;
1070a929df7fSMoshe Shemesh 
1071a929df7fSMoshe Shemesh 		return nla_put_u8(skb, attrtype, tmp);
1072a929df7fSMoshe Shemesh 	case NLA_U8:
1073a929df7fSMoshe Shemesh 		return nla_put_u8(skb, attrtype, *(u8 *)msg->value);
1074a929df7fSMoshe Shemesh 	case NLA_U32:
1075a929df7fSMoshe Shemesh 		return nla_put_u32(skb, attrtype, *(u32 *)msg->value);
1076a929df7fSMoshe Shemesh 	case NLA_U64:
1077a929df7fSMoshe Shemesh 		return nla_put_u64_64bit(skb, attrtype, *(u64 *)msg->value,
1078a929df7fSMoshe Shemesh 					 DEVLINK_ATTR_PAD);
1079a929df7fSMoshe Shemesh 	case NLA_NUL_STRING:
1080a929df7fSMoshe Shemesh 		return nla_put_string(skb, attrtype, (char *)&msg->value);
1081a929df7fSMoshe Shemesh 	case NLA_BINARY:
1082a929df7fSMoshe Shemesh 		return nla_put(skb, attrtype, msg->len, (void *)&msg->value);
1083a929df7fSMoshe Shemesh 	default:
1084a929df7fSMoshe Shemesh 		return -EINVAL;
1085a929df7fSMoshe Shemesh 	}
1086a929df7fSMoshe Shemesh }
1087a929df7fSMoshe Shemesh 
1088a929df7fSMoshe Shemesh static int
devlink_fmsg_prepare_skb(struct devlink_fmsg * fmsg,struct sk_buff * skb,int * start)1089a929df7fSMoshe Shemesh devlink_fmsg_prepare_skb(struct devlink_fmsg *fmsg, struct sk_buff *skb,
1090a929df7fSMoshe Shemesh 			 int *start)
1091a929df7fSMoshe Shemesh {
1092a929df7fSMoshe Shemesh 	struct devlink_fmsg_item *item;
1093a929df7fSMoshe Shemesh 	struct nlattr *fmsg_nlattr;
1094a929df7fSMoshe Shemesh 	int err = 0;
1095a929df7fSMoshe Shemesh 	int i = 0;
1096a929df7fSMoshe Shemesh 
1097a929df7fSMoshe Shemesh 	fmsg_nlattr = nla_nest_start_noflag(skb, DEVLINK_ATTR_FMSG);
1098a929df7fSMoshe Shemesh 	if (!fmsg_nlattr)
1099a929df7fSMoshe Shemesh 		return -EMSGSIZE;
1100a929df7fSMoshe Shemesh 
1101a929df7fSMoshe Shemesh 	list_for_each_entry(item, &fmsg->item_list, list) {
1102a929df7fSMoshe Shemesh 		if (i < *start) {
1103a929df7fSMoshe Shemesh 			i++;
1104a929df7fSMoshe Shemesh 			continue;
1105a929df7fSMoshe Shemesh 		}
1106a929df7fSMoshe Shemesh 
1107a929df7fSMoshe Shemesh 		switch (item->attrtype) {
1108a929df7fSMoshe Shemesh 		case DEVLINK_ATTR_FMSG_OBJ_NEST_START:
1109a929df7fSMoshe Shemesh 		case DEVLINK_ATTR_FMSG_PAIR_NEST_START:
1110a929df7fSMoshe Shemesh 		case DEVLINK_ATTR_FMSG_ARR_NEST_START:
1111a929df7fSMoshe Shemesh 		case DEVLINK_ATTR_FMSG_NEST_END:
1112a929df7fSMoshe Shemesh 			err = nla_put_flag(skb, item->attrtype);
1113a929df7fSMoshe Shemesh 			break;
1114a929df7fSMoshe Shemesh 		case DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA:
1115a929df7fSMoshe Shemesh 			err = devlink_fmsg_item_fill_type(item, skb);
1116a929df7fSMoshe Shemesh 			if (err)
1117a929df7fSMoshe Shemesh 				break;
1118a929df7fSMoshe Shemesh 			err = devlink_fmsg_item_fill_data(item, skb);
1119a929df7fSMoshe Shemesh 			break;
1120a929df7fSMoshe Shemesh 		case DEVLINK_ATTR_FMSG_OBJ_NAME:
1121a929df7fSMoshe Shemesh 			err = nla_put_string(skb, item->attrtype,
1122a929df7fSMoshe Shemesh 					     (char *)&item->value);
1123a929df7fSMoshe Shemesh 			break;
1124a929df7fSMoshe Shemesh 		default:
1125a929df7fSMoshe Shemesh 			err = -EINVAL;
1126a929df7fSMoshe Shemesh 			break;
1127a929df7fSMoshe Shemesh 		}
1128a929df7fSMoshe Shemesh 		if (!err)
1129a929df7fSMoshe Shemesh 			*start = ++i;
1130a929df7fSMoshe Shemesh 		else
1131a929df7fSMoshe Shemesh 			break;
1132a929df7fSMoshe Shemesh 	}
1133a929df7fSMoshe Shemesh 
1134a929df7fSMoshe Shemesh 	nla_nest_end(skb, fmsg_nlattr);
1135a929df7fSMoshe Shemesh 	return err;
1136a929df7fSMoshe Shemesh }
1137a929df7fSMoshe Shemesh 
devlink_fmsg_snd(struct devlink_fmsg * fmsg,struct genl_info * info,enum devlink_command cmd,int flags)1138a929df7fSMoshe Shemesh static int devlink_fmsg_snd(struct devlink_fmsg *fmsg,
1139a929df7fSMoshe Shemesh 			    struct genl_info *info,
1140a929df7fSMoshe Shemesh 			    enum devlink_command cmd, int flags)
1141a929df7fSMoshe Shemesh {
1142a929df7fSMoshe Shemesh 	struct nlmsghdr *nlh;
1143a929df7fSMoshe Shemesh 	struct sk_buff *skb;
1144a929df7fSMoshe Shemesh 	bool last = false;
1145a929df7fSMoshe Shemesh 	int index = 0;
1146a929df7fSMoshe Shemesh 	void *hdr;
1147a929df7fSMoshe Shemesh 	int err;
1148a929df7fSMoshe Shemesh 
1149a929df7fSMoshe Shemesh 	while (!last) {
1150a929df7fSMoshe Shemesh 		int tmp_index = index;
1151a929df7fSMoshe Shemesh 
1152a929df7fSMoshe Shemesh 		skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1153a929df7fSMoshe Shemesh 		if (!skb)
1154a929df7fSMoshe Shemesh 			return -ENOMEM;
1155a929df7fSMoshe Shemesh 
1156a929df7fSMoshe Shemesh 		hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
1157a929df7fSMoshe Shemesh 				  &devlink_nl_family, flags | NLM_F_MULTI, cmd);
1158a929df7fSMoshe Shemesh 		if (!hdr) {
1159a929df7fSMoshe Shemesh 			err = -EMSGSIZE;
1160a929df7fSMoshe Shemesh 			goto nla_put_failure;
1161a929df7fSMoshe Shemesh 		}
1162a929df7fSMoshe Shemesh 
1163a929df7fSMoshe Shemesh 		err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1164a929df7fSMoshe Shemesh 		if (!err)
1165a929df7fSMoshe Shemesh 			last = true;
1166a929df7fSMoshe Shemesh 		else if (err != -EMSGSIZE || tmp_index == index)
1167a929df7fSMoshe Shemesh 			goto nla_put_failure;
1168a929df7fSMoshe Shemesh 
1169a929df7fSMoshe Shemesh 		genlmsg_end(skb, hdr);
1170a929df7fSMoshe Shemesh 		err = genlmsg_reply(skb, info);
1171a929df7fSMoshe Shemesh 		if (err)
1172a929df7fSMoshe Shemesh 			return err;
1173a929df7fSMoshe Shemesh 	}
1174a929df7fSMoshe Shemesh 
1175a929df7fSMoshe Shemesh 	skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1176a929df7fSMoshe Shemesh 	if (!skb)
1177a929df7fSMoshe Shemesh 		return -ENOMEM;
1178a929df7fSMoshe Shemesh 	nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
1179a929df7fSMoshe Shemesh 			NLMSG_DONE, 0, flags | NLM_F_MULTI);
1180a929df7fSMoshe Shemesh 	if (!nlh) {
1181a929df7fSMoshe Shemesh 		err = -EMSGSIZE;
1182a929df7fSMoshe Shemesh 		goto nla_put_failure;
1183a929df7fSMoshe Shemesh 	}
1184a929df7fSMoshe Shemesh 
1185a929df7fSMoshe Shemesh 	return genlmsg_reply(skb, info);
1186a929df7fSMoshe Shemesh 
1187a929df7fSMoshe Shemesh nla_put_failure:
1188a929df7fSMoshe Shemesh 	nlmsg_free(skb);
1189a929df7fSMoshe Shemesh 	return err;
1190a929df7fSMoshe Shemesh }
1191a929df7fSMoshe Shemesh 
devlink_fmsg_dumpit(struct devlink_fmsg * fmsg,struct sk_buff * skb,struct netlink_callback * cb,enum devlink_command cmd)119212af29e7SMoshe Shemesh static int devlink_fmsg_dumpit(struct devlink_fmsg *fmsg, struct sk_buff *skb,
1193a929df7fSMoshe Shemesh 			       struct netlink_callback *cb,
1194a929df7fSMoshe Shemesh 			       enum devlink_command cmd)
1195a929df7fSMoshe Shemesh {
1196a929df7fSMoshe Shemesh 	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
1197a929df7fSMoshe Shemesh 	int index = state->idx;
1198a929df7fSMoshe Shemesh 	int tmp_index = index;
1199a929df7fSMoshe Shemesh 	void *hdr;
1200a929df7fSMoshe Shemesh 	int err;
1201a929df7fSMoshe Shemesh 
1202a929df7fSMoshe Shemesh 	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
1203a929df7fSMoshe Shemesh 			  &devlink_nl_family, NLM_F_ACK | NLM_F_MULTI, cmd);
1204a929df7fSMoshe Shemesh 	if (!hdr) {
1205a929df7fSMoshe Shemesh 		err = -EMSGSIZE;
1206a929df7fSMoshe Shemesh 		goto nla_put_failure;
1207a929df7fSMoshe Shemesh 	}
1208a929df7fSMoshe Shemesh 
1209a929df7fSMoshe Shemesh 	err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1210a929df7fSMoshe Shemesh 	if ((err && err != -EMSGSIZE) || tmp_index == index)
1211a929df7fSMoshe Shemesh 		goto nla_put_failure;
1212a929df7fSMoshe Shemesh 
1213a929df7fSMoshe Shemesh 	state->idx = index;
1214a929df7fSMoshe Shemesh 	genlmsg_end(skb, hdr);
1215a929df7fSMoshe Shemesh 	return skb->len;
1216a929df7fSMoshe Shemesh 
1217a929df7fSMoshe Shemesh nla_put_failure:
1218a929df7fSMoshe Shemesh 	genlmsg_cancel(skb, hdr);
1219a929df7fSMoshe Shemesh 	return err;
1220a929df7fSMoshe Shemesh }
1221a929df7fSMoshe Shemesh 
devlink_nl_cmd_health_reporter_diagnose_doit(struct sk_buff * skb,struct genl_info * info)1222a929df7fSMoshe Shemesh int devlink_nl_cmd_health_reporter_diagnose_doit(struct sk_buff *skb,
1223a929df7fSMoshe Shemesh 						 struct genl_info *info)
1224a929df7fSMoshe Shemesh {
1225a929df7fSMoshe Shemesh 	struct devlink *devlink = info->user_ptr[0];
1226a929df7fSMoshe Shemesh 	struct devlink_health_reporter *reporter;
1227a929df7fSMoshe Shemesh 	struct devlink_fmsg *fmsg;
1228a929df7fSMoshe Shemesh 	int err;
1229a929df7fSMoshe Shemesh 
1230a929df7fSMoshe Shemesh 	reporter = devlink_health_reporter_get_from_info(devlink, info);
1231a929df7fSMoshe Shemesh 	if (!reporter)
1232a929df7fSMoshe Shemesh 		return -EINVAL;
1233a929df7fSMoshe Shemesh 
1234a929df7fSMoshe Shemesh 	if (!reporter->ops->diagnose)
1235a929df7fSMoshe Shemesh 		return -EOPNOTSUPP;
1236a929df7fSMoshe Shemesh 
1237a929df7fSMoshe Shemesh 	fmsg = devlink_fmsg_alloc();
1238a929df7fSMoshe Shemesh 	if (!fmsg)
1239a929df7fSMoshe Shemesh 		return -ENOMEM;
1240a929df7fSMoshe Shemesh 
1241a929df7fSMoshe Shemesh 	err = devlink_fmsg_obj_nest_start(fmsg);
1242a929df7fSMoshe Shemesh 	if (err)
1243a929df7fSMoshe Shemesh 		goto out;
1244a929df7fSMoshe Shemesh 
1245a929df7fSMoshe Shemesh 	err = reporter->ops->diagnose(reporter, fmsg, info->extack);
1246a929df7fSMoshe Shemesh 	if (err)
1247a929df7fSMoshe Shemesh 		goto out;
1248a929df7fSMoshe Shemesh 
1249a929df7fSMoshe Shemesh 	err = devlink_fmsg_obj_nest_end(fmsg);
1250a929df7fSMoshe Shemesh 	if (err)
1251a929df7fSMoshe Shemesh 		goto out;
1252a929df7fSMoshe Shemesh 
1253a929df7fSMoshe Shemesh 	err = devlink_fmsg_snd(fmsg, info,
1254a929df7fSMoshe Shemesh 			       DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, 0);
1255a929df7fSMoshe Shemesh 
1256a929df7fSMoshe Shemesh out:
1257a929df7fSMoshe Shemesh 	devlink_fmsg_free(fmsg);
1258a929df7fSMoshe Shemesh 	return err;
1259a929df7fSMoshe Shemesh }
12607004c6c4SMoshe Shemesh 
12617004c6c4SMoshe Shemesh static struct devlink_health_reporter *
devlink_health_reporter_get_from_cb_lock(struct netlink_callback * cb)1262*aba0e909SMoshe Shemesh devlink_health_reporter_get_from_cb_lock(struct netlink_callback *cb)
12637004c6c4SMoshe Shemesh {
12647288dd2fSJakub Kicinski 	const struct genl_info *info = genl_info_dump(cb);
12657004c6c4SMoshe Shemesh 	struct devlink_health_reporter *reporter;
12667004c6c4SMoshe Shemesh 	struct nlattr **attrs = info->attrs;
12677004c6c4SMoshe Shemesh 	struct devlink *devlink;
12687004c6c4SMoshe Shemesh 
12697004c6c4SMoshe Shemesh 	devlink = devlink_get_from_attrs_lock(sock_net(cb->skb->sk), attrs);
12707004c6c4SMoshe Shemesh 	if (IS_ERR(devlink))
12717004c6c4SMoshe Shemesh 		return NULL;
12727004c6c4SMoshe Shemesh 
12737004c6c4SMoshe Shemesh 	reporter = devlink_health_reporter_get_from_attrs(devlink, attrs);
1274*aba0e909SMoshe Shemesh 	if (!reporter) {
1275*aba0e909SMoshe Shemesh 		devl_unlock(devlink);
12767004c6c4SMoshe Shemesh 		devlink_put(devlink);
1277*aba0e909SMoshe Shemesh 	}
12787004c6c4SMoshe Shemesh 	return reporter;
12797004c6c4SMoshe Shemesh }
12807004c6c4SMoshe Shemesh 
devlink_nl_cmd_health_reporter_dump_get_dumpit(struct sk_buff * skb,struct netlink_callback * cb)12817004c6c4SMoshe Shemesh int devlink_nl_cmd_health_reporter_dump_get_dumpit(struct sk_buff *skb,
12827004c6c4SMoshe Shemesh 						   struct netlink_callback *cb)
12837004c6c4SMoshe Shemesh {
12847004c6c4SMoshe Shemesh 	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
12857004c6c4SMoshe Shemesh 	struct devlink_health_reporter *reporter;
1286*aba0e909SMoshe Shemesh 	struct devlink *devlink;
12877004c6c4SMoshe Shemesh 	int err;
12887004c6c4SMoshe Shemesh 
1289*aba0e909SMoshe Shemesh 	reporter = devlink_health_reporter_get_from_cb_lock(cb);
12907004c6c4SMoshe Shemesh 	if (!reporter)
12917004c6c4SMoshe Shemesh 		return -EINVAL;
12927004c6c4SMoshe Shemesh 
1293*aba0e909SMoshe Shemesh 	devlink = reporter->devlink;
1294*aba0e909SMoshe Shemesh 	if (!reporter->ops->dump) {
1295*aba0e909SMoshe Shemesh 		devl_unlock(devlink);
1296*aba0e909SMoshe Shemesh 		devlink_put(devlink);
12977004c6c4SMoshe Shemesh 		return -EOPNOTSUPP;
1298*aba0e909SMoshe Shemesh 	}
12997004c6c4SMoshe Shemesh 
13007004c6c4SMoshe Shemesh 	if (!state->idx) {
13017004c6c4SMoshe Shemesh 		err = devlink_health_do_dump(reporter, NULL, cb->extack);
13027004c6c4SMoshe Shemesh 		if (err)
13037004c6c4SMoshe Shemesh 			goto unlock;
13047004c6c4SMoshe Shemesh 		state->dump_ts = reporter->dump_ts;
13057004c6c4SMoshe Shemesh 	}
13067004c6c4SMoshe Shemesh 	if (!reporter->dump_fmsg || state->dump_ts != reporter->dump_ts) {
13077004c6c4SMoshe Shemesh 		NL_SET_ERR_MSG(cb->extack, "Dump trampled, please retry");
13087004c6c4SMoshe Shemesh 		err = -EAGAIN;
13097004c6c4SMoshe Shemesh 		goto unlock;
13107004c6c4SMoshe Shemesh 	}
13117004c6c4SMoshe Shemesh 
13127004c6c4SMoshe Shemesh 	err = devlink_fmsg_dumpit(reporter->dump_fmsg, skb, cb,
13137004c6c4SMoshe Shemesh 				  DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET);
13147004c6c4SMoshe Shemesh unlock:
1315*aba0e909SMoshe Shemesh 	devl_unlock(devlink);
1316*aba0e909SMoshe Shemesh 	devlink_put(devlink);
13177004c6c4SMoshe Shemesh 	return err;
13187004c6c4SMoshe Shemesh }
13197004c6c4SMoshe Shemesh 
devlink_nl_cmd_health_reporter_dump_clear_doit(struct sk_buff * skb,struct genl_info * info)13207004c6c4SMoshe Shemesh int devlink_nl_cmd_health_reporter_dump_clear_doit(struct sk_buff *skb,
13217004c6c4SMoshe Shemesh 						   struct genl_info *info)
13227004c6c4SMoshe Shemesh {
13237004c6c4SMoshe Shemesh 	struct devlink *devlink = info->user_ptr[0];
13247004c6c4SMoshe Shemesh 	struct devlink_health_reporter *reporter;
13257004c6c4SMoshe Shemesh 
13267004c6c4SMoshe Shemesh 	reporter = devlink_health_reporter_get_from_info(devlink, info);
13277004c6c4SMoshe Shemesh 	if (!reporter)
13287004c6c4SMoshe Shemesh 		return -EINVAL;
13297004c6c4SMoshe Shemesh 
13307004c6c4SMoshe Shemesh 	if (!reporter->ops->dump)
13317004c6c4SMoshe Shemesh 		return -EOPNOTSUPP;
13327004c6c4SMoshe Shemesh 
13337004c6c4SMoshe Shemesh 	devlink_health_dump_clear(reporter);
13347004c6c4SMoshe Shemesh 	return 0;
13357004c6c4SMoshe Shemesh }
1336c9311ee1SMoshe Shemesh 
devlink_nl_cmd_health_reporter_test_doit(struct sk_buff * skb,struct genl_info * info)1337c9311ee1SMoshe Shemesh int devlink_nl_cmd_health_reporter_test_doit(struct sk_buff *skb,
1338c9311ee1SMoshe Shemesh 					     struct genl_info *info)
1339c9311ee1SMoshe Shemesh {
1340c9311ee1SMoshe Shemesh 	struct devlink *devlink = info->user_ptr[0];
1341c9311ee1SMoshe Shemesh 	struct devlink_health_reporter *reporter;
1342c9311ee1SMoshe Shemesh 
1343c9311ee1SMoshe Shemesh 	reporter = devlink_health_reporter_get_from_info(devlink, info);
1344c9311ee1SMoshe Shemesh 	if (!reporter)
1345c9311ee1SMoshe Shemesh 		return -EINVAL;
1346c9311ee1SMoshe Shemesh 
1347c9311ee1SMoshe Shemesh 	if (!reporter->ops->test)
1348c9311ee1SMoshe Shemesh 		return -EOPNOTSUPP;
1349c9311ee1SMoshe Shemesh 
1350c9311ee1SMoshe Shemesh 	return reporter->ops->test(reporter, info->extack);
1351c9311ee1SMoshe Shemesh }
1352