Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/bpf/bpftool/struct_ops.c
26282 views
1
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2
/* Copyright (C) 2020 Facebook */
3
4
#include <errno.h>
5
#include <stdio.h>
6
#include <unistd.h>
7
8
#include <linux/err.h>
9
10
#include <bpf/bpf.h>
11
#include <bpf/btf.h>
12
#include <bpf/libbpf.h>
13
14
#include "json_writer.h"
15
#include "main.h"
16
17
#define STRUCT_OPS_VALUE_PREFIX "bpf_struct_ops_"
18
19
static const struct btf_type *map_info_type;
20
static __u32 map_info_alloc_len;
21
static struct btf *btf_vmlinux;
22
static __s32 map_info_type_id;
23
24
struct res {
25
unsigned int nr_maps;
26
unsigned int nr_errs;
27
};
28
29
static const struct btf *get_btf_vmlinux(void)
30
{
31
if (btf_vmlinux)
32
return btf_vmlinux;
33
34
btf_vmlinux = libbpf_find_kernel_btf();
35
if (!btf_vmlinux)
36
p_err("struct_ops requires kernel CONFIG_DEBUG_INFO_BTF=y");
37
38
return btf_vmlinux;
39
}
40
41
static const char *get_kern_struct_ops_name(const struct bpf_map_info *info)
42
{
43
const struct btf *kern_btf;
44
const struct btf_type *t;
45
const char *st_ops_name;
46
47
kern_btf = get_btf_vmlinux();
48
if (!kern_btf)
49
return "<btf_vmlinux_not_found>";
50
51
t = btf__type_by_id(kern_btf, info->btf_vmlinux_value_type_id);
52
st_ops_name = btf__name_by_offset(kern_btf, t->name_off);
53
st_ops_name += strlen(STRUCT_OPS_VALUE_PREFIX);
54
55
return st_ops_name;
56
}
57
58
static __s32 get_map_info_type_id(void)
59
{
60
const struct btf *kern_btf;
61
62
if (map_info_type_id)
63
return map_info_type_id;
64
65
kern_btf = get_btf_vmlinux();
66
if (!kern_btf)
67
return 0;
68
69
map_info_type_id = btf__find_by_name_kind(kern_btf, "bpf_map_info",
70
BTF_KIND_STRUCT);
71
if (map_info_type_id < 0) {
72
p_err("can't find bpf_map_info from btf_vmlinux");
73
return map_info_type_id;
74
}
75
map_info_type = btf__type_by_id(kern_btf, map_info_type_id);
76
77
/* Ensure map_info_alloc() has at least what the bpftool needs */
78
map_info_alloc_len = map_info_type->size;
79
if (map_info_alloc_len < sizeof(struct bpf_map_info))
80
map_info_alloc_len = sizeof(struct bpf_map_info);
81
82
return map_info_type_id;
83
}
84
85
/* If the subcmd needs to print out the bpf_map_info,
86
* it should always call map_info_alloc to allocate
87
* a bpf_map_info object instead of allocating it
88
* on the stack.
89
*
90
* map_info_alloc() will take the running kernel's btf
91
* into account. i.e. it will consider the
92
* sizeof(struct bpf_map_info) of the running kernel.
93
*
94
* It will enable the "struct_ops" cmd to print the latest
95
* "struct bpf_map_info".
96
*
97
* [ Recall that "struct_ops" requires the kernel's btf to
98
* be available ]
99
*/
100
static struct bpf_map_info *map_info_alloc(__u32 *alloc_len)
101
{
102
struct bpf_map_info *info;
103
104
if (get_map_info_type_id() < 0)
105
return NULL;
106
107
info = calloc(1, map_info_alloc_len);
108
if (!info)
109
p_err("mem alloc failed");
110
else
111
*alloc_len = map_info_alloc_len;
112
113
return info;
114
}
115
116
/* It iterates all struct_ops maps of the system.
117
* It returns the fd in "*res_fd" and map_info in "*info".
118
* In the very first iteration, info->id should be 0.
119
* An optional map "*name" filter can be specified.
120
* The filter can be made more flexible in the future.
121
* e.g. filter by kernel-struct-ops-name, regex-name, glob-name, ...etc.
122
*
123
* Return value:
124
* 1: A struct_ops map found. It is returned in "*res_fd" and "*info".
125
* The caller can continue to call get_next in the future.
126
* 0: No struct_ops map is returned.
127
* All struct_ops map has been found.
128
* -1: Error and the caller should abort the iteration.
129
*/
130
static int get_next_struct_ops_map(const char *name, int *res_fd,
131
struct bpf_map_info *info, __u32 info_len)
132
{
133
__u32 id = info->id;
134
int err, fd;
135
136
while (true) {
137
err = bpf_map_get_next_id(id, &id);
138
if (err) {
139
if (errno == ENOENT)
140
return 0;
141
p_err("can't get next map: %s", strerror(errno));
142
return -1;
143
}
144
145
fd = bpf_map_get_fd_by_id(id);
146
if (fd < 0) {
147
if (errno == ENOENT)
148
continue;
149
p_err("can't get map by id (%u): %s",
150
id, strerror(errno));
151
return -1;
152
}
153
154
err = bpf_map_get_info_by_fd(fd, info, &info_len);
155
if (err) {
156
p_err("can't get map info: %s", strerror(errno));
157
close(fd);
158
return -1;
159
}
160
161
if (info->type == BPF_MAP_TYPE_STRUCT_OPS &&
162
(!name || !strcmp(name, info->name))) {
163
*res_fd = fd;
164
return 1;
165
}
166
close(fd);
167
}
168
}
169
170
static int cmd_retval(const struct res *res, bool must_have_one_map)
171
{
172
if (res->nr_errs || (!res->nr_maps && must_have_one_map))
173
return -1;
174
175
return 0;
176
}
177
178
/* "data" is the work_func private storage */
179
typedef int (*work_func)(int fd, const struct bpf_map_info *info, void *data,
180
struct json_writer *wtr);
181
182
/* Find all struct_ops map in the system.
183
* Filter out by "name" (if specified).
184
* Then call "func(fd, info, data, wtr)" on each struct_ops map found.
185
*/
186
static struct res do_search(const char *name, work_func func, void *data,
187
struct json_writer *wtr)
188
{
189
struct bpf_map_info *info;
190
struct res res = {};
191
__u32 info_len;
192
int fd, err;
193
194
info = map_info_alloc(&info_len);
195
if (!info) {
196
res.nr_errs++;
197
return res;
198
}
199
200
if (wtr)
201
jsonw_start_array(wtr);
202
while ((err = get_next_struct_ops_map(name, &fd, info, info_len)) == 1) {
203
res.nr_maps++;
204
err = func(fd, info, data, wtr);
205
if (err)
206
res.nr_errs++;
207
close(fd);
208
}
209
if (wtr)
210
jsonw_end_array(wtr);
211
212
if (err)
213
res.nr_errs++;
214
215
if (!wtr && name && !res.nr_errs && !res.nr_maps)
216
/* It is not printing empty [].
217
* Thus, needs to specifically say nothing found
218
* for "name" here.
219
*/
220
p_err("no struct_ops found for %s", name);
221
else if (!wtr && json_output && !res.nr_errs)
222
/* The "func()" above is not writing any json (i.e. !wtr
223
* test here).
224
*
225
* However, "-j" is enabled and there is no errs here,
226
* so call json_null() as the current convention of
227
* other cmds.
228
*/
229
jsonw_null(json_wtr);
230
231
free(info);
232
return res;
233
}
234
235
static struct res do_one_id(const char *id_str, work_func func, void *data,
236
struct json_writer *wtr)
237
{
238
struct bpf_map_info *info;
239
struct res res = {};
240
unsigned long id;
241
__u32 info_len;
242
char *endptr;
243
int fd;
244
245
id = strtoul(id_str, &endptr, 0);
246
if (*endptr || !id || id > UINT32_MAX) {
247
p_err("invalid id %s", id_str);
248
res.nr_errs++;
249
return res;
250
}
251
252
fd = bpf_map_get_fd_by_id(id);
253
if (fd < 0) {
254
p_err("can't get map by id (%lu): %s", id, strerror(errno));
255
res.nr_errs++;
256
return res;
257
}
258
259
info = map_info_alloc(&info_len);
260
if (!info) {
261
res.nr_errs++;
262
goto done;
263
}
264
265
if (bpf_map_get_info_by_fd(fd, info, &info_len)) {
266
p_err("can't get map info: %s", strerror(errno));
267
res.nr_errs++;
268
goto done;
269
}
270
271
if (info->type != BPF_MAP_TYPE_STRUCT_OPS) {
272
p_err("%s id %u is not a struct_ops map", info->name, info->id);
273
res.nr_errs++;
274
goto done;
275
}
276
277
res.nr_maps++;
278
279
if (wtr)
280
jsonw_start_array(wtr);
281
282
if (func(fd, info, data, wtr))
283
res.nr_errs++;
284
else if (!wtr && json_output)
285
/* The "func()" above is not writing any json (i.e. !wtr
286
* test here).
287
*
288
* However, "-j" is enabled and there is no errs here,
289
* so call json_null() as the current convention of
290
* other cmds.
291
*/
292
jsonw_null(json_wtr);
293
294
if (wtr)
295
jsonw_end_array(wtr);
296
297
done:
298
free(info);
299
close(fd);
300
301
return res;
302
}
303
304
static struct res do_work_on_struct_ops(const char *search_type,
305
const char *search_term,
306
work_func func, void *data,
307
struct json_writer *wtr)
308
{
309
if (search_type) {
310
if (is_prefix(search_type, "id"))
311
return do_one_id(search_term, func, data, wtr);
312
else if (!is_prefix(search_type, "name"))
313
usage();
314
}
315
316
return do_search(search_term, func, data, wtr);
317
}
318
319
static int __do_show(int fd, const struct bpf_map_info *info, void *data,
320
struct json_writer *wtr)
321
{
322
if (wtr) {
323
jsonw_start_object(wtr);
324
jsonw_uint_field(wtr, "id", info->id);
325
jsonw_string_field(wtr, "name", info->name);
326
jsonw_string_field(wtr, "kernel_struct_ops",
327
get_kern_struct_ops_name(info));
328
jsonw_end_object(wtr);
329
} else {
330
printf("%u: %-15s %-32s\n", info->id, info->name,
331
get_kern_struct_ops_name(info));
332
}
333
334
return 0;
335
}
336
337
static int do_show(int argc, char **argv)
338
{
339
const char *search_type = NULL, *search_term = NULL;
340
struct res res;
341
342
if (argc && argc != 2)
343
usage();
344
345
if (argc == 2) {
346
search_type = GET_ARG();
347
search_term = GET_ARG();
348
}
349
350
res = do_work_on_struct_ops(search_type, search_term, __do_show,
351
NULL, json_wtr);
352
353
return cmd_retval(&res, !!search_term);
354
}
355
356
static int __do_dump(int fd, const struct bpf_map_info *info, void *data,
357
struct json_writer *wtr)
358
{
359
struct btf_dumper *d = (struct btf_dumper *)data;
360
const struct btf_type *struct_ops_type;
361
const struct btf *kern_btf = d->btf;
362
const char *struct_ops_name;
363
int zero = 0;
364
void *value;
365
366
/* note: d->jw == wtr */
367
368
kern_btf = d->btf;
369
370
/* The kernel supporting BPF_MAP_TYPE_STRUCT_OPS must have
371
* btf_vmlinux_value_type_id.
372
*/
373
struct_ops_type = btf__type_by_id(kern_btf,
374
info->btf_vmlinux_value_type_id);
375
struct_ops_name = btf__name_by_offset(kern_btf,
376
struct_ops_type->name_off);
377
value = calloc(1, info->value_size);
378
if (!value) {
379
p_err("mem alloc failed");
380
return -1;
381
}
382
383
if (bpf_map_lookup_elem(fd, &zero, value)) {
384
p_err("can't lookup struct_ops map %s id %u",
385
info->name, info->id);
386
free(value);
387
return -1;
388
}
389
390
jsonw_start_object(wtr);
391
jsonw_name(wtr, "bpf_map_info");
392
btf_dumper_type(d, map_info_type_id, (void *)info);
393
jsonw_end_object(wtr);
394
395
jsonw_start_object(wtr);
396
jsonw_name(wtr, struct_ops_name);
397
btf_dumper_type(d, info->btf_vmlinux_value_type_id, value);
398
jsonw_end_object(wtr);
399
400
free(value);
401
402
return 0;
403
}
404
405
static int do_dump(int argc, char **argv)
406
{
407
const char *search_type = NULL, *search_term = NULL;
408
json_writer_t *wtr = json_wtr;
409
const struct btf *kern_btf;
410
struct btf_dumper d = {};
411
struct res res;
412
413
if (argc && argc != 2)
414
usage();
415
416
if (argc == 2) {
417
search_type = GET_ARG();
418
search_term = GET_ARG();
419
}
420
421
kern_btf = get_btf_vmlinux();
422
if (!kern_btf)
423
return -1;
424
425
if (!json_output) {
426
wtr = jsonw_new(stdout);
427
if (!wtr) {
428
p_err("can't create json writer");
429
return -1;
430
}
431
jsonw_pretty(wtr, true);
432
}
433
434
d.btf = kern_btf;
435
d.jw = wtr;
436
d.is_plain_text = !json_output;
437
d.prog_id_as_func_ptr = true;
438
439
res = do_work_on_struct_ops(search_type, search_term, __do_dump, &d,
440
wtr);
441
442
if (!json_output)
443
jsonw_destroy(&wtr);
444
445
return cmd_retval(&res, !!search_term);
446
}
447
448
static int __do_unregister(int fd, const struct bpf_map_info *info, void *data,
449
struct json_writer *wtr)
450
{
451
int zero = 0;
452
453
if (bpf_map_delete_elem(fd, &zero)) {
454
p_err("can't unload %s %s id %u: %s",
455
get_kern_struct_ops_name(info), info->name,
456
info->id, strerror(errno));
457
return -1;
458
}
459
460
p_info("Unregistered %s %s id %u",
461
get_kern_struct_ops_name(info), info->name,
462
info->id);
463
464
return 0;
465
}
466
467
static int do_unregister(int argc, char **argv)
468
{
469
const char *search_type, *search_term;
470
struct res res;
471
472
if (argc != 2)
473
usage();
474
475
search_type = GET_ARG();
476
search_term = GET_ARG();
477
478
res = do_work_on_struct_ops(search_type, search_term,
479
__do_unregister, NULL, NULL);
480
481
return cmd_retval(&res, true);
482
}
483
484
static int pin_link(struct bpf_link *link, const char *pindir,
485
const char *name)
486
{
487
char pinfile[PATH_MAX];
488
int err;
489
490
err = pathname_concat(pinfile, sizeof(pinfile), pindir, name);
491
if (err)
492
return -1;
493
494
return bpf_link__pin(link, pinfile);
495
}
496
497
static int do_register(int argc, char **argv)
498
{
499
LIBBPF_OPTS(bpf_object_open_opts, open_opts);
500
__u32 link_info_len = sizeof(struct bpf_link_info);
501
struct bpf_link_info link_info = {};
502
struct bpf_map_info info = {};
503
__u32 info_len = sizeof(info);
504
int nr_errs = 0, nr_maps = 0;
505
const char *linkdir = NULL;
506
struct bpf_object *obj;
507
struct bpf_link *link;
508
struct bpf_map *map;
509
const char *file;
510
511
if (argc != 1 && argc != 2)
512
usage();
513
514
file = GET_ARG();
515
if (argc == 1)
516
linkdir = GET_ARG();
517
518
if (linkdir && create_and_mount_bpffs_dir(linkdir)) {
519
p_err("can't mount bpffs for pinning");
520
return -1;
521
}
522
523
if (verifier_logs)
524
/* log_level1 + log_level2 + stats, but not stable UAPI */
525
open_opts.kernel_log_level = 1 + 2 + 4;
526
527
obj = bpf_object__open_file(file, &open_opts);
528
if (!obj)
529
return -1;
530
531
set_max_rlimit();
532
533
if (bpf_object__load(obj)) {
534
bpf_object__close(obj);
535
return -1;
536
}
537
538
bpf_object__for_each_map(map, obj) {
539
if (bpf_map__type(map) != BPF_MAP_TYPE_STRUCT_OPS)
540
continue;
541
542
link = bpf_map__attach_struct_ops(map);
543
if (!link) {
544
p_err("can't register struct_ops %s: %s",
545
bpf_map__name(map), strerror(errno));
546
nr_errs++;
547
continue;
548
}
549
nr_maps++;
550
551
if (bpf_map_get_info_by_fd(bpf_map__fd(map), &info,
552
&info_len)) {
553
/* Not p_err. The struct_ops was attached
554
* successfully.
555
*/
556
p_info("Registered %s but can't find id: %s",
557
bpf_map__name(map), strerror(errno));
558
goto clean_link;
559
}
560
if (!(bpf_map__map_flags(map) & BPF_F_LINK)) {
561
p_info("Registered %s %s id %u",
562
get_kern_struct_ops_name(&info),
563
info.name,
564
info.id);
565
goto clean_link;
566
}
567
if (bpf_link_get_info_by_fd(bpf_link__fd(link),
568
&link_info,
569
&link_info_len)) {
570
p_err("Registered %s but can't find link id: %s",
571
bpf_map__name(map), strerror(errno));
572
nr_errs++;
573
goto clean_link;
574
}
575
if (linkdir && pin_link(link, linkdir, info.name)) {
576
p_err("can't pin link %u for %s: %s",
577
link_info.id, info.name,
578
strerror(errno));
579
nr_errs++;
580
goto clean_link;
581
}
582
p_info("Registered %s %s map id %u link id %u",
583
get_kern_struct_ops_name(&info),
584
info.name, info.id, link_info.id);
585
586
clean_link:
587
bpf_link__disconnect(link);
588
bpf_link__destroy(link);
589
}
590
591
bpf_object__close(obj);
592
593
if (nr_errs)
594
return -1;
595
596
if (!nr_maps) {
597
p_err("no struct_ops found in %s", file);
598
return -1;
599
}
600
601
if (json_output)
602
jsonw_null(json_wtr);
603
604
return 0;
605
}
606
607
static int do_help(int argc, char **argv)
608
{
609
if (json_output) {
610
jsonw_null(json_wtr);
611
return 0;
612
}
613
614
fprintf(stderr,
615
"Usage: %1$s %2$s { show | list } [STRUCT_OPS_MAP]\n"
616
" %1$s %2$s dump [STRUCT_OPS_MAP]\n"
617
" %1$s %2$s register OBJ [LINK_DIR]\n"
618
" %1$s %2$s unregister STRUCT_OPS_MAP\n"
619
" %1$s %2$s help\n"
620
"\n"
621
" STRUCT_OPS_MAP := [ id STRUCT_OPS_MAP_ID | name STRUCT_OPS_MAP_NAME ]\n"
622
" " HELP_SPEC_OPTIONS " }\n"
623
"",
624
bin_name, argv[-2]);
625
626
return 0;
627
}
628
629
static const struct cmd cmds[] = {
630
{ "show", do_show },
631
{ "list", do_show },
632
{ "register", do_register },
633
{ "unregister", do_unregister },
634
{ "dump", do_dump },
635
{ "help", do_help },
636
{ 0 }
637
};
638
639
int do_struct_ops(int argc, char **argv)
640
{
641
int err;
642
643
err = cmd_select(cmds, argc, argv, do_help);
644
645
btf__free(btf_vmlinux);
646
647
return err;
648
}
649
650