Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/devlink/health.c
26278 views
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 <[email protected]>
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
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
31
static 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
44
static 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
55
struct 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
73
void *
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 *
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 *
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 *
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 *
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
*/
140
struct devlink_health_reporter *
141
devl_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
}
162
EXPORT_SYMBOL_GPL(devl_port_health_reporter_create);
163
164
struct devlink_health_reporter *
165
devlink_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
}
178
EXPORT_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
*/
188
struct devlink_health_reporter *
189
devl_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
}
208
EXPORT_SYMBOL_GPL(devl_health_reporter_create);
209
210
struct devlink_health_reporter *
211
devlink_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
}
223
EXPORT_SYMBOL_GPL(devlink_health_reporter_create);
224
225
static void
226
devlink_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
*/
238
void
239
devl_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
}
246
EXPORT_SYMBOL_GPL(devl_health_reporter_destroy);
247
248
void
249
devlink_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
}
257
EXPORT_SYMBOL_GPL(devlink_health_reporter_destroy);
258
259
static int
260
devlink_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 (devlink_nl_put_u64(msg, DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT,
291
reporter->error_count))
292
goto reporter_nest_cancel;
293
if (devlink_nl_put_u64(msg, DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT,
294
reporter->recovery_count))
295
goto reporter_nest_cancel;
296
if (reporter->ops->recover &&
297
devlink_nl_put_u64(msg, DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD,
298
reporter->graceful_period))
299
goto reporter_nest_cancel;
300
if (reporter->ops->recover &&
301
nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
302
reporter->auto_recover))
303
goto reporter_nest_cancel;
304
if (reporter->dump_fmsg &&
305
devlink_nl_put_u64(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS,
306
jiffies_to_msecs(reporter->dump_ts)))
307
goto reporter_nest_cancel;
308
if (reporter->dump_fmsg &&
309
devlink_nl_put_u64(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS,
310
reporter->dump_real_ts))
311
goto reporter_nest_cancel;
312
if (reporter->ops->dump &&
313
nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP,
314
reporter->auto_dump))
315
goto reporter_nest_cancel;
316
317
nla_nest_end(msg, reporter_attr);
318
genlmsg_end(msg, hdr);
319
return 0;
320
321
reporter_nest_cancel:
322
nla_nest_cancel(msg, reporter_attr);
323
genlmsg_cancel:
324
genlmsg_cancel(msg, hdr);
325
return -EMSGSIZE;
326
}
327
328
static struct devlink_health_reporter *
329
devlink_health_reporter_get_from_attrs(struct devlink *devlink,
330
struct nlattr **attrs)
331
{
332
struct devlink_port *devlink_port;
333
char *reporter_name;
334
335
if (!attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME])
336
return NULL;
337
338
reporter_name = nla_data(attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME]);
339
devlink_port = devlink_port_get_from_attrs(devlink, attrs);
340
if (IS_ERR(devlink_port))
341
return devlink_health_reporter_find_by_name(devlink,
342
reporter_name);
343
else
344
return devlink_port_health_reporter_find_by_name(devlink_port,
345
reporter_name);
346
}
347
348
static struct devlink_health_reporter *
349
devlink_health_reporter_get_from_info(struct devlink *devlink,
350
struct genl_info *info)
351
{
352
return devlink_health_reporter_get_from_attrs(devlink, info->attrs);
353
}
354
355
int devlink_nl_health_reporter_get_doit(struct sk_buff *skb,
356
struct genl_info *info)
357
{
358
struct devlink *devlink = info->user_ptr[0];
359
struct devlink_health_reporter *reporter;
360
struct sk_buff *msg;
361
int err;
362
363
reporter = devlink_health_reporter_get_from_info(devlink, info);
364
if (!reporter)
365
return -EINVAL;
366
367
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
368
if (!msg)
369
return -ENOMEM;
370
371
err = devlink_nl_health_reporter_fill(msg, reporter,
372
DEVLINK_CMD_HEALTH_REPORTER_GET,
373
info->snd_portid, info->snd_seq,
374
0);
375
if (err) {
376
nlmsg_free(msg);
377
return err;
378
}
379
380
return genlmsg_reply(msg, info);
381
}
382
383
static int devlink_nl_health_reporter_get_dump_one(struct sk_buff *msg,
384
struct devlink *devlink,
385
struct netlink_callback *cb,
386
int flags)
387
{
388
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
389
const struct genl_info *info = genl_info_dump(cb);
390
struct devlink_health_reporter *reporter;
391
unsigned long port_index_end = ULONG_MAX;
392
struct nlattr **attrs = info->attrs;
393
unsigned long port_index_start = 0;
394
struct devlink_port *port;
395
unsigned long port_index;
396
int idx = 0;
397
int err;
398
399
if (attrs && attrs[DEVLINK_ATTR_PORT_INDEX]) {
400
port_index_start = nla_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]);
401
port_index_end = port_index_start;
402
flags |= NLM_F_DUMP_FILTERED;
403
goto per_port_dump;
404
}
405
406
list_for_each_entry(reporter, &devlink->reporter_list, list) {
407
if (idx < state->idx) {
408
idx++;
409
continue;
410
}
411
err = devlink_nl_health_reporter_fill(msg, reporter,
412
DEVLINK_CMD_HEALTH_REPORTER_GET,
413
NETLINK_CB(cb->skb).portid,
414
cb->nlh->nlmsg_seq,
415
flags);
416
if (err) {
417
state->idx = idx;
418
return err;
419
}
420
idx++;
421
}
422
per_port_dump:
423
xa_for_each_range(&devlink->ports, port_index, port,
424
port_index_start, port_index_end) {
425
list_for_each_entry(reporter, &port->reporter_list, list) {
426
if (idx < state->idx) {
427
idx++;
428
continue;
429
}
430
err = devlink_nl_health_reporter_fill(msg, reporter,
431
DEVLINK_CMD_HEALTH_REPORTER_GET,
432
NETLINK_CB(cb->skb).portid,
433
cb->nlh->nlmsg_seq,
434
flags);
435
if (err) {
436
state->idx = idx;
437
return err;
438
}
439
idx++;
440
}
441
}
442
443
return 0;
444
}
445
446
int devlink_nl_health_reporter_get_dumpit(struct sk_buff *skb,
447
struct netlink_callback *cb)
448
{
449
return devlink_nl_dumpit(skb, cb,
450
devlink_nl_health_reporter_get_dump_one);
451
}
452
453
int devlink_nl_health_reporter_set_doit(struct sk_buff *skb,
454
struct genl_info *info)
455
{
456
struct devlink *devlink = info->user_ptr[0];
457
struct devlink_health_reporter *reporter;
458
459
reporter = devlink_health_reporter_get_from_info(devlink, info);
460
if (!reporter)
461
return -EINVAL;
462
463
if (!reporter->ops->recover &&
464
(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] ||
465
info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]))
466
return -EOPNOTSUPP;
467
468
if (!reporter->ops->dump &&
469
info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
470
return -EOPNOTSUPP;
471
472
if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD])
473
reporter->graceful_period =
474
nla_get_u64(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD]);
475
476
if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])
477
reporter->auto_recover =
478
nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]);
479
480
if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
481
reporter->auto_dump =
482
nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP]);
483
484
return 0;
485
}
486
487
static void devlink_recover_notify(struct devlink_health_reporter *reporter,
488
enum devlink_command cmd)
489
{
490
struct devlink *devlink = reporter->devlink;
491
struct devlink_obj_desc desc;
492
struct sk_buff *msg;
493
int err;
494
495
WARN_ON(cmd != DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
496
ASSERT_DEVLINK_REGISTERED(devlink);
497
498
if (!devlink_nl_notify_need(devlink))
499
return;
500
501
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
502
if (!msg)
503
return;
504
505
err = devlink_nl_health_reporter_fill(msg, reporter, cmd, 0, 0, 0);
506
if (err) {
507
nlmsg_free(msg);
508
return;
509
}
510
511
devlink_nl_obj_desc_init(&desc, devlink);
512
if (reporter->devlink_port)
513
devlink_nl_obj_desc_port_set(&desc, reporter->devlink_port);
514
devlink_nl_notify_send_desc(devlink, msg, &desc);
515
}
516
517
void
518
devlink_health_reporter_recovery_done(struct devlink_health_reporter *reporter)
519
{
520
reporter->recovery_count++;
521
reporter->last_recovery_ts = jiffies;
522
}
523
EXPORT_SYMBOL_GPL(devlink_health_reporter_recovery_done);
524
525
static int
526
devlink_health_reporter_recover(struct devlink_health_reporter *reporter,
527
void *priv_ctx, struct netlink_ext_ack *extack)
528
{
529
int err;
530
531
if (reporter->health_state == DEVLINK_HEALTH_REPORTER_STATE_HEALTHY)
532
return 0;
533
534
if (!reporter->ops->recover)
535
return -EOPNOTSUPP;
536
537
err = reporter->ops->recover(reporter, priv_ctx, extack);
538
if (err)
539
return err;
540
541
devlink_health_reporter_recovery_done(reporter);
542
reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_HEALTHY;
543
devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
544
545
return 0;
546
}
547
548
static void
549
devlink_health_dump_clear(struct devlink_health_reporter *reporter)
550
{
551
if (!reporter->dump_fmsg)
552
return;
553
devlink_fmsg_free(reporter->dump_fmsg);
554
reporter->dump_fmsg = NULL;
555
}
556
557
static int devlink_health_do_dump(struct devlink_health_reporter *reporter,
558
void *priv_ctx,
559
struct netlink_ext_ack *extack)
560
{
561
int err;
562
563
if (!reporter->ops->dump)
564
return 0;
565
566
if (reporter->dump_fmsg)
567
return 0;
568
569
reporter->dump_fmsg = devlink_fmsg_alloc();
570
if (!reporter->dump_fmsg)
571
return -ENOMEM;
572
573
devlink_fmsg_obj_nest_start(reporter->dump_fmsg);
574
575
err = reporter->ops->dump(reporter, reporter->dump_fmsg,
576
priv_ctx, extack);
577
if (err)
578
goto dump_err;
579
580
devlink_fmsg_obj_nest_end(reporter->dump_fmsg);
581
err = reporter->dump_fmsg->err;
582
if (err)
583
goto dump_err;
584
585
reporter->dump_ts = jiffies;
586
reporter->dump_real_ts = ktime_get_real_ns();
587
588
return 0;
589
590
dump_err:
591
devlink_health_dump_clear(reporter);
592
return err;
593
}
594
595
int devlink_health_report(struct devlink_health_reporter *reporter,
596
const char *msg, void *priv_ctx)
597
{
598
enum devlink_health_reporter_state prev_health_state;
599
struct devlink *devlink = reporter->devlink;
600
unsigned long recover_ts_threshold;
601
int ret;
602
603
/* write a log message of the current error */
604
WARN_ON(!msg);
605
trace_devlink_health_report(devlink, reporter->ops->name, msg);
606
reporter->error_count++;
607
prev_health_state = reporter->health_state;
608
reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_ERROR;
609
devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
610
611
/* abort if the previous error wasn't recovered */
612
recover_ts_threshold = reporter->last_recovery_ts +
613
msecs_to_jiffies(reporter->graceful_period);
614
if (reporter->auto_recover &&
615
(prev_health_state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY ||
616
(reporter->last_recovery_ts && reporter->recovery_count &&
617
time_is_after_jiffies(recover_ts_threshold)))) {
618
trace_devlink_health_recover_aborted(devlink,
619
reporter->ops->name,
620
reporter->health_state,
621
jiffies -
622
reporter->last_recovery_ts);
623
return -ECANCELED;
624
}
625
626
if (reporter->auto_dump) {
627
devl_lock(devlink);
628
/* store current dump of current error, for later analysis */
629
devlink_health_do_dump(reporter, priv_ctx, NULL);
630
devl_unlock(devlink);
631
}
632
633
if (!reporter->auto_recover)
634
return 0;
635
636
devl_lock(devlink);
637
ret = devlink_health_reporter_recover(reporter, priv_ctx, NULL);
638
devl_unlock(devlink);
639
640
return ret;
641
}
642
EXPORT_SYMBOL_GPL(devlink_health_report);
643
644
void
645
devlink_health_reporter_state_update(struct devlink_health_reporter *reporter,
646
enum devlink_health_reporter_state state)
647
{
648
if (WARN_ON(state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY &&
649
state != DEVLINK_HEALTH_REPORTER_STATE_ERROR))
650
return;
651
652
if (reporter->health_state == state)
653
return;
654
655
reporter->health_state = state;
656
trace_devlink_health_reporter_state_update(reporter->devlink,
657
reporter->ops->name, state);
658
devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
659
}
660
EXPORT_SYMBOL_GPL(devlink_health_reporter_state_update);
661
662
int devlink_nl_health_reporter_recover_doit(struct sk_buff *skb,
663
struct genl_info *info)
664
{
665
struct devlink *devlink = info->user_ptr[0];
666
struct devlink_health_reporter *reporter;
667
668
reporter = devlink_health_reporter_get_from_info(devlink, info);
669
if (!reporter)
670
return -EINVAL;
671
672
return devlink_health_reporter_recover(reporter, NULL, info->extack);
673
}
674
675
static void devlink_fmsg_err_if_binary(struct devlink_fmsg *fmsg)
676
{
677
if (!fmsg->err && fmsg->putting_binary)
678
fmsg->err = -EINVAL;
679
}
680
681
static void devlink_fmsg_nest_common(struct devlink_fmsg *fmsg, int attrtype)
682
{
683
struct devlink_fmsg_item *item;
684
685
if (fmsg->err)
686
return;
687
688
item = kzalloc(sizeof(*item), GFP_KERNEL);
689
if (!item) {
690
fmsg->err = -ENOMEM;
691
return;
692
}
693
694
item->attrtype = attrtype;
695
list_add_tail(&item->list, &fmsg->item_list);
696
}
697
698
void devlink_fmsg_obj_nest_start(struct devlink_fmsg *fmsg)
699
{
700
devlink_fmsg_err_if_binary(fmsg);
701
devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_OBJ_NEST_START);
702
}
703
EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_start);
704
705
static void devlink_fmsg_nest_end(struct devlink_fmsg *fmsg)
706
{
707
devlink_fmsg_err_if_binary(fmsg);
708
devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_NEST_END);
709
}
710
711
void devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg)
712
{
713
devlink_fmsg_nest_end(fmsg);
714
}
715
EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_end);
716
717
#define DEVLINK_FMSG_MAX_SIZE (GENLMSG_DEFAULT_SIZE - GENL_HDRLEN - NLA_HDRLEN)
718
719
static void devlink_fmsg_put_name(struct devlink_fmsg *fmsg, const char *name)
720
{
721
struct devlink_fmsg_item *item;
722
723
devlink_fmsg_err_if_binary(fmsg);
724
if (fmsg->err)
725
return;
726
727
if (strlen(name) + 1 > DEVLINK_FMSG_MAX_SIZE) {
728
fmsg->err = -EMSGSIZE;
729
return;
730
}
731
732
item = kzalloc(sizeof(*item) + strlen(name) + 1, GFP_KERNEL);
733
if (!item) {
734
fmsg->err = -ENOMEM;
735
return;
736
}
737
738
item->nla_type = DEVLINK_VAR_ATTR_TYPE_NUL_STRING;
739
item->len = strlen(name) + 1;
740
item->attrtype = DEVLINK_ATTR_FMSG_OBJ_NAME;
741
memcpy(&item->value, name, item->len);
742
list_add_tail(&item->list, &fmsg->item_list);
743
}
744
745
void devlink_fmsg_pair_nest_start(struct devlink_fmsg *fmsg, const char *name)
746
{
747
devlink_fmsg_err_if_binary(fmsg);
748
devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_PAIR_NEST_START);
749
devlink_fmsg_put_name(fmsg, name);
750
}
751
EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_start);
752
753
void devlink_fmsg_pair_nest_end(struct devlink_fmsg *fmsg)
754
{
755
devlink_fmsg_nest_end(fmsg);
756
}
757
EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_end);
758
759
void devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg *fmsg,
760
const char *name)
761
{
762
devlink_fmsg_pair_nest_start(fmsg, name);
763
devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_ARR_NEST_START);
764
}
765
EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_start);
766
767
void devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg *fmsg)
768
{
769
devlink_fmsg_nest_end(fmsg);
770
devlink_fmsg_nest_end(fmsg);
771
}
772
EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_end);
773
774
void devlink_fmsg_binary_pair_nest_start(struct devlink_fmsg *fmsg,
775
const char *name)
776
{
777
devlink_fmsg_arr_pair_nest_start(fmsg, name);
778
fmsg->putting_binary = true;
779
}
780
EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_start);
781
782
void devlink_fmsg_binary_pair_nest_end(struct devlink_fmsg *fmsg)
783
{
784
if (fmsg->err)
785
return;
786
787
if (!fmsg->putting_binary)
788
fmsg->err = -EINVAL;
789
790
fmsg->putting_binary = false;
791
devlink_fmsg_arr_pair_nest_end(fmsg);
792
}
793
EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_end);
794
795
static void devlink_fmsg_put_value(struct devlink_fmsg *fmsg,
796
const void *value, u16 value_len,
797
u8 value_nla_type)
798
{
799
struct devlink_fmsg_item *item;
800
801
if (fmsg->err)
802
return;
803
804
if (value_len > DEVLINK_FMSG_MAX_SIZE) {
805
fmsg->err = -EMSGSIZE;
806
return;
807
}
808
809
item = kzalloc(sizeof(*item) + value_len, GFP_KERNEL);
810
if (!item) {
811
fmsg->err = -ENOMEM;
812
return;
813
}
814
815
item->nla_type = value_nla_type;
816
item->len = value_len;
817
item->attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
818
memcpy(&item->value, value, item->len);
819
list_add_tail(&item->list, &fmsg->item_list);
820
}
821
822
static void devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value)
823
{
824
devlink_fmsg_err_if_binary(fmsg);
825
devlink_fmsg_put_value(fmsg, &value, sizeof(value),
826
DEVLINK_VAR_ATTR_TYPE_FLAG);
827
}
828
829
static void devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value)
830
{
831
devlink_fmsg_err_if_binary(fmsg);
832
devlink_fmsg_put_value(fmsg, &value, sizeof(value),
833
DEVLINK_VAR_ATTR_TYPE_U8);
834
}
835
836
void 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),
840
DEVLINK_VAR_ATTR_TYPE_U32);
841
}
842
EXPORT_SYMBOL_GPL(devlink_fmsg_u32_put);
843
844
static void devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value)
845
{
846
devlink_fmsg_err_if_binary(fmsg);
847
devlink_fmsg_put_value(fmsg, &value, sizeof(value),
848
DEVLINK_VAR_ATTR_TYPE_U64);
849
}
850
851
void devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value)
852
{
853
devlink_fmsg_err_if_binary(fmsg);
854
devlink_fmsg_put_value(fmsg, value, strlen(value) + 1,
855
DEVLINK_VAR_ATTR_TYPE_NUL_STRING);
856
}
857
EXPORT_SYMBOL_GPL(devlink_fmsg_string_put);
858
859
void devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value,
860
u16 value_len)
861
{
862
if (!fmsg->err && !fmsg->putting_binary)
863
fmsg->err = -EINVAL;
864
865
devlink_fmsg_put_value(fmsg, value, value_len,
866
DEVLINK_VAR_ATTR_TYPE_BINARY);
867
}
868
EXPORT_SYMBOL_GPL(devlink_fmsg_binary_put);
869
870
void devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name,
871
bool value)
872
{
873
devlink_fmsg_pair_nest_start(fmsg, name);
874
devlink_fmsg_bool_put(fmsg, value);
875
devlink_fmsg_pair_nest_end(fmsg);
876
}
877
EXPORT_SYMBOL_GPL(devlink_fmsg_bool_pair_put);
878
879
void devlink_fmsg_u8_pair_put(struct devlink_fmsg *fmsg, const char *name,
880
u8 value)
881
{
882
devlink_fmsg_pair_nest_start(fmsg, name);
883
devlink_fmsg_u8_put(fmsg, value);
884
devlink_fmsg_pair_nest_end(fmsg);
885
}
886
EXPORT_SYMBOL_GPL(devlink_fmsg_u8_pair_put);
887
888
void devlink_fmsg_u32_pair_put(struct devlink_fmsg *fmsg, const char *name,
889
u32 value)
890
{
891
devlink_fmsg_pair_nest_start(fmsg, name);
892
devlink_fmsg_u32_put(fmsg, value);
893
devlink_fmsg_pair_nest_end(fmsg);
894
}
895
EXPORT_SYMBOL_GPL(devlink_fmsg_u32_pair_put);
896
897
void devlink_fmsg_u64_pair_put(struct devlink_fmsg *fmsg, const char *name,
898
u64 value)
899
{
900
devlink_fmsg_pair_nest_start(fmsg, name);
901
devlink_fmsg_u64_put(fmsg, value);
902
devlink_fmsg_pair_nest_end(fmsg);
903
}
904
EXPORT_SYMBOL_GPL(devlink_fmsg_u64_pair_put);
905
906
void devlink_fmsg_string_pair_put(struct devlink_fmsg *fmsg, const char *name,
907
const char *value)
908
{
909
devlink_fmsg_pair_nest_start(fmsg, name);
910
devlink_fmsg_string_put(fmsg, value);
911
devlink_fmsg_pair_nest_end(fmsg);
912
}
913
EXPORT_SYMBOL_GPL(devlink_fmsg_string_pair_put);
914
915
void devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name,
916
const void *value, u32 value_len)
917
{
918
u32 data_size;
919
u32 offset;
920
921
devlink_fmsg_binary_pair_nest_start(fmsg, name);
922
923
for (offset = 0; offset < value_len; offset += data_size) {
924
data_size = value_len - offset;
925
if (data_size > DEVLINK_FMSG_MAX_SIZE)
926
data_size = DEVLINK_FMSG_MAX_SIZE;
927
928
devlink_fmsg_binary_put(fmsg, value + offset, data_size);
929
}
930
931
devlink_fmsg_binary_pair_nest_end(fmsg);
932
fmsg->putting_binary = false;
933
}
934
EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_put);
935
936
static int
937
devlink_fmsg_item_fill_data(struct devlink_fmsg_item *msg, struct sk_buff *skb)
938
{
939
int attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
940
u8 tmp;
941
942
switch (msg->nla_type) {
943
case DEVLINK_VAR_ATTR_TYPE_FLAG:
944
/* Always provide flag data, regardless of its value */
945
tmp = *(bool *)msg->value;
946
947
return nla_put_u8(skb, attrtype, tmp);
948
case DEVLINK_VAR_ATTR_TYPE_U8:
949
return nla_put_u8(skb, attrtype, *(u8 *)msg->value);
950
case DEVLINK_VAR_ATTR_TYPE_U32:
951
return nla_put_u32(skb, attrtype, *(u32 *)msg->value);
952
case DEVLINK_VAR_ATTR_TYPE_U64:
953
return devlink_nl_put_u64(skb, attrtype, *(u64 *)msg->value);
954
case DEVLINK_VAR_ATTR_TYPE_NUL_STRING:
955
return nla_put_string(skb, attrtype, (char *)&msg->value);
956
case DEVLINK_VAR_ATTR_TYPE_BINARY:
957
return nla_put(skb, attrtype, msg->len, (void *)&msg->value);
958
default:
959
return -EINVAL;
960
}
961
}
962
963
static int
964
devlink_fmsg_prepare_skb(struct devlink_fmsg *fmsg, struct sk_buff *skb,
965
int *start)
966
{
967
struct devlink_fmsg_item *item;
968
struct nlattr *fmsg_nlattr;
969
int err = 0;
970
int i = 0;
971
972
fmsg_nlattr = nla_nest_start_noflag(skb, DEVLINK_ATTR_FMSG);
973
if (!fmsg_nlattr)
974
return -EMSGSIZE;
975
976
list_for_each_entry(item, &fmsg->item_list, list) {
977
if (i < *start) {
978
i++;
979
continue;
980
}
981
982
switch (item->attrtype) {
983
case DEVLINK_ATTR_FMSG_OBJ_NEST_START:
984
case DEVLINK_ATTR_FMSG_PAIR_NEST_START:
985
case DEVLINK_ATTR_FMSG_ARR_NEST_START:
986
case DEVLINK_ATTR_FMSG_NEST_END:
987
err = nla_put_flag(skb, item->attrtype);
988
break;
989
case DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA:
990
err = nla_put_u8(skb, DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE,
991
item->nla_type);
992
if (err)
993
break;
994
err = devlink_fmsg_item_fill_data(item, skb);
995
break;
996
case DEVLINK_ATTR_FMSG_OBJ_NAME:
997
err = nla_put_string(skb, item->attrtype,
998
(char *)&item->value);
999
break;
1000
default:
1001
err = -EINVAL;
1002
break;
1003
}
1004
if (!err)
1005
*start = ++i;
1006
else
1007
break;
1008
}
1009
1010
nla_nest_end(skb, fmsg_nlattr);
1011
return err;
1012
}
1013
1014
static int devlink_fmsg_snd(struct devlink_fmsg *fmsg,
1015
struct genl_info *info,
1016
enum devlink_command cmd, int flags)
1017
{
1018
struct nlmsghdr *nlh;
1019
struct sk_buff *skb;
1020
bool last = false;
1021
int index = 0;
1022
void *hdr;
1023
int err;
1024
1025
if (fmsg->err)
1026
return fmsg->err;
1027
1028
while (!last) {
1029
int tmp_index = index;
1030
1031
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1032
if (!skb)
1033
return -ENOMEM;
1034
1035
hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
1036
&devlink_nl_family, flags | NLM_F_MULTI, cmd);
1037
if (!hdr) {
1038
err = -EMSGSIZE;
1039
goto nla_put_failure;
1040
}
1041
1042
err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1043
if (!err)
1044
last = true;
1045
else if (err != -EMSGSIZE || tmp_index == index)
1046
goto nla_put_failure;
1047
1048
genlmsg_end(skb, hdr);
1049
err = genlmsg_reply(skb, info);
1050
if (err)
1051
return err;
1052
}
1053
1054
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1055
if (!skb)
1056
return -ENOMEM;
1057
nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
1058
NLMSG_DONE, 0, flags | NLM_F_MULTI);
1059
if (!nlh) {
1060
err = -EMSGSIZE;
1061
goto nla_put_failure;
1062
}
1063
1064
return genlmsg_reply(skb, info);
1065
1066
nla_put_failure:
1067
nlmsg_free(skb);
1068
return err;
1069
}
1070
1071
static int devlink_fmsg_dumpit(struct devlink_fmsg *fmsg, struct sk_buff *skb,
1072
struct netlink_callback *cb,
1073
enum devlink_command cmd)
1074
{
1075
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
1076
int index = state->idx;
1077
int tmp_index = index;
1078
void *hdr;
1079
int err;
1080
1081
if (fmsg->err)
1082
return fmsg->err;
1083
1084
hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
1085
&devlink_nl_family, NLM_F_ACK | NLM_F_MULTI, cmd);
1086
if (!hdr) {
1087
err = -EMSGSIZE;
1088
goto nla_put_failure;
1089
}
1090
1091
err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1092
if ((err && err != -EMSGSIZE) || tmp_index == index)
1093
goto nla_put_failure;
1094
1095
state->idx = index;
1096
genlmsg_end(skb, hdr);
1097
return skb->len;
1098
1099
nla_put_failure:
1100
genlmsg_cancel(skb, hdr);
1101
return err;
1102
}
1103
1104
int devlink_nl_health_reporter_diagnose_doit(struct sk_buff *skb,
1105
struct genl_info *info)
1106
{
1107
struct devlink *devlink = info->user_ptr[0];
1108
struct devlink_health_reporter *reporter;
1109
struct devlink_fmsg *fmsg;
1110
int err;
1111
1112
reporter = devlink_health_reporter_get_from_info(devlink, info);
1113
if (!reporter)
1114
return -EINVAL;
1115
1116
if (!reporter->ops->diagnose)
1117
return -EOPNOTSUPP;
1118
1119
fmsg = devlink_fmsg_alloc();
1120
if (!fmsg)
1121
return -ENOMEM;
1122
1123
devlink_fmsg_obj_nest_start(fmsg);
1124
1125
err = reporter->ops->diagnose(reporter, fmsg, info->extack);
1126
if (err)
1127
goto out;
1128
1129
devlink_fmsg_obj_nest_end(fmsg);
1130
1131
err = devlink_fmsg_snd(fmsg, info,
1132
DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, 0);
1133
1134
out:
1135
devlink_fmsg_free(fmsg);
1136
return err;
1137
}
1138
1139
static struct devlink_health_reporter *
1140
devlink_health_reporter_get_from_cb_lock(struct netlink_callback *cb)
1141
{
1142
const struct genl_info *info = genl_info_dump(cb);
1143
struct devlink_health_reporter *reporter;
1144
struct nlattr **attrs = info->attrs;
1145
struct devlink *devlink;
1146
1147
devlink = devlink_get_from_attrs_lock(sock_net(cb->skb->sk), attrs,
1148
false);
1149
if (IS_ERR(devlink))
1150
return NULL;
1151
1152
reporter = devlink_health_reporter_get_from_attrs(devlink, attrs);
1153
if (!reporter) {
1154
devl_unlock(devlink);
1155
devlink_put(devlink);
1156
}
1157
return reporter;
1158
}
1159
1160
int devlink_nl_health_reporter_dump_get_dumpit(struct sk_buff *skb,
1161
struct netlink_callback *cb)
1162
{
1163
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
1164
struct devlink_health_reporter *reporter;
1165
struct devlink *devlink;
1166
int err;
1167
1168
reporter = devlink_health_reporter_get_from_cb_lock(cb);
1169
if (!reporter)
1170
return -EINVAL;
1171
1172
devlink = reporter->devlink;
1173
if (!reporter->ops->dump) {
1174
devl_unlock(devlink);
1175
devlink_put(devlink);
1176
return -EOPNOTSUPP;
1177
}
1178
1179
if (!state->idx) {
1180
err = devlink_health_do_dump(reporter, NULL, cb->extack);
1181
if (err)
1182
goto unlock;
1183
state->dump_ts = reporter->dump_ts;
1184
}
1185
if (!reporter->dump_fmsg || state->dump_ts != reporter->dump_ts) {
1186
NL_SET_ERR_MSG(cb->extack, "Dump trampled, please retry");
1187
err = -EAGAIN;
1188
goto unlock;
1189
}
1190
1191
err = devlink_fmsg_dumpit(reporter->dump_fmsg, skb, cb,
1192
DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET);
1193
unlock:
1194
devl_unlock(devlink);
1195
devlink_put(devlink);
1196
return err;
1197
}
1198
1199
int devlink_nl_health_reporter_dump_clear_doit(struct sk_buff *skb,
1200
struct genl_info *info)
1201
{
1202
struct devlink *devlink = info->user_ptr[0];
1203
struct devlink_health_reporter *reporter;
1204
1205
reporter = devlink_health_reporter_get_from_info(devlink, info);
1206
if (!reporter)
1207
return -EINVAL;
1208
1209
if (!reporter->ops->dump)
1210
return -EOPNOTSUPP;
1211
1212
devlink_health_dump_clear(reporter);
1213
return 0;
1214
}
1215
1216
int devlink_nl_health_reporter_test_doit(struct sk_buff *skb,
1217
struct genl_info *info)
1218
{
1219
struct devlink *devlink = info->user_ptr[0];
1220
struct devlink_health_reporter *reporter;
1221
1222
reporter = devlink_health_reporter_get_from_info(devlink, info);
1223
if (!reporter)
1224
return -EINVAL;
1225
1226
if (!reporter->ops->test)
1227
return -EOPNOTSUPP;
1228
1229
return reporter->ops->test(reporter, info->extack);
1230
}
1231
1232
/**
1233
* devlink_fmsg_dump_skb - Dump sk_buffer structure
1234
* @fmsg: devlink formatted message pointer
1235
* @skb: pointer to skb
1236
*
1237
* Dump diagnostic information about sk_buff structure, like headroom, length,
1238
* tailroom, MAC, etc.
1239
*/
1240
void devlink_fmsg_dump_skb(struct devlink_fmsg *fmsg, const struct sk_buff *skb)
1241
{
1242
struct skb_shared_info *sh = skb_shinfo(skb);
1243
struct sock *sk = skb->sk;
1244
bool has_mac, has_trans;
1245
1246
has_mac = skb_mac_header_was_set(skb);
1247
has_trans = skb_transport_header_was_set(skb);
1248
1249
devlink_fmsg_pair_nest_start(fmsg, "skb");
1250
devlink_fmsg_obj_nest_start(fmsg);
1251
devlink_fmsg_put(fmsg, "actual len", skb->len);
1252
devlink_fmsg_put(fmsg, "head len", skb_headlen(skb));
1253
devlink_fmsg_put(fmsg, "data len", skb->data_len);
1254
devlink_fmsg_put(fmsg, "tail len", skb_tailroom(skb));
1255
devlink_fmsg_put(fmsg, "MAC", has_mac ? skb->mac_header : -1);
1256
devlink_fmsg_put(fmsg, "MAC len",
1257
has_mac ? skb_mac_header_len(skb) : -1);
1258
devlink_fmsg_put(fmsg, "network hdr", skb->network_header);
1259
devlink_fmsg_put(fmsg, "network hdr len",
1260
has_trans ? skb_network_header_len(skb) : -1);
1261
devlink_fmsg_put(fmsg, "transport hdr",
1262
has_trans ? skb->transport_header : -1);
1263
devlink_fmsg_put(fmsg, "csum", (__force u32)skb->csum);
1264
devlink_fmsg_put(fmsg, "csum_ip_summed", (u8)skb->ip_summed);
1265
devlink_fmsg_put(fmsg, "csum_complete_sw", !!skb->csum_complete_sw);
1266
devlink_fmsg_put(fmsg, "csum_valid", !!skb->csum_valid);
1267
devlink_fmsg_put(fmsg, "csum_level", (u8)skb->csum_level);
1268
devlink_fmsg_put(fmsg, "sw_hash", !!skb->sw_hash);
1269
devlink_fmsg_put(fmsg, "l4_hash", !!skb->l4_hash);
1270
devlink_fmsg_put(fmsg, "proto", ntohs(skb->protocol));
1271
devlink_fmsg_put(fmsg, "pkt_type", (u8)skb->pkt_type);
1272
devlink_fmsg_put(fmsg, "iif", skb->skb_iif);
1273
1274
if (sk) {
1275
devlink_fmsg_pair_nest_start(fmsg, "sk");
1276
devlink_fmsg_obj_nest_start(fmsg);
1277
devlink_fmsg_put(fmsg, "family", sk->sk_type);
1278
devlink_fmsg_put(fmsg, "type", sk->sk_type);
1279
devlink_fmsg_put(fmsg, "proto", sk->sk_protocol);
1280
devlink_fmsg_obj_nest_end(fmsg);
1281
devlink_fmsg_pair_nest_end(fmsg);
1282
}
1283
1284
devlink_fmsg_obj_nest_end(fmsg);
1285
devlink_fmsg_pair_nest_end(fmsg);
1286
1287
devlink_fmsg_pair_nest_start(fmsg, "shinfo");
1288
devlink_fmsg_obj_nest_start(fmsg);
1289
devlink_fmsg_put(fmsg, "tx_flags", sh->tx_flags);
1290
devlink_fmsg_put(fmsg, "nr_frags", sh->nr_frags);
1291
devlink_fmsg_put(fmsg, "gso_size", sh->gso_size);
1292
devlink_fmsg_put(fmsg, "gso_type", sh->gso_type);
1293
devlink_fmsg_put(fmsg, "gso_segs", sh->gso_segs);
1294
devlink_fmsg_obj_nest_end(fmsg);
1295
devlink_fmsg_pair_nest_end(fmsg);
1296
}
1297
EXPORT_SYMBOL_GPL(devlink_fmsg_dump_skb);
1298
1299