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