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