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