Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/lib/bpf/features.c
26285 views
1
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2
/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
3
#include <linux/kernel.h>
4
#include <linux/filter.h>
5
#include "bpf.h"
6
#include "libbpf.h"
7
#include "libbpf_common.h"
8
#include "libbpf_internal.h"
9
#include "str_error.h"
10
11
static inline __u64 ptr_to_u64(const void *ptr)
12
{
13
return (__u64)(unsigned long)ptr;
14
}
15
16
int probe_fd(int fd)
17
{
18
if (fd >= 0)
19
close(fd);
20
return fd >= 0;
21
}
22
23
static int probe_kern_prog_name(int token_fd)
24
{
25
const size_t attr_sz = offsetofend(union bpf_attr, prog_token_fd);
26
struct bpf_insn insns[] = {
27
BPF_MOV64_IMM(BPF_REG_0, 0),
28
BPF_EXIT_INSN(),
29
};
30
union bpf_attr attr;
31
int ret;
32
33
memset(&attr, 0, attr_sz);
34
attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
35
attr.license = ptr_to_u64("GPL");
36
attr.insns = ptr_to_u64(insns);
37
attr.insn_cnt = (__u32)ARRAY_SIZE(insns);
38
attr.prog_token_fd = token_fd;
39
if (token_fd)
40
attr.prog_flags |= BPF_F_TOKEN_FD;
41
libbpf_strlcpy(attr.prog_name, "libbpf_nametest", sizeof(attr.prog_name));
42
43
/* make sure loading with name works */
44
ret = sys_bpf_prog_load(&attr, attr_sz, PROG_LOAD_ATTEMPTS);
45
return probe_fd(ret);
46
}
47
48
static int probe_kern_global_data(int token_fd)
49
{
50
struct bpf_insn insns[] = {
51
BPF_LD_MAP_VALUE(BPF_REG_1, 0, 16),
52
BPF_ST_MEM(BPF_DW, BPF_REG_1, 0, 42),
53
BPF_MOV64_IMM(BPF_REG_0, 0),
54
BPF_EXIT_INSN(),
55
};
56
LIBBPF_OPTS(bpf_map_create_opts, map_opts,
57
.token_fd = token_fd,
58
.map_flags = token_fd ? BPF_F_TOKEN_FD : 0,
59
);
60
LIBBPF_OPTS(bpf_prog_load_opts, prog_opts,
61
.token_fd = token_fd,
62
.prog_flags = token_fd ? BPF_F_TOKEN_FD : 0,
63
);
64
int ret, map, insn_cnt = ARRAY_SIZE(insns);
65
66
map = bpf_map_create(BPF_MAP_TYPE_ARRAY, "libbpf_global", sizeof(int), 32, 1, &map_opts);
67
if (map < 0) {
68
ret = -errno;
69
pr_warn("Error in %s(): %s. Couldn't create simple array map.\n",
70
__func__, errstr(ret));
71
return ret;
72
}
73
74
insns[0].imm = map;
75
76
ret = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", insns, insn_cnt, &prog_opts);
77
close(map);
78
return probe_fd(ret);
79
}
80
81
static int probe_kern_btf(int token_fd)
82
{
83
static const char strs[] = "\0int";
84
__u32 types[] = {
85
/* int */
86
BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4),
87
};
88
89
return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
90
strs, sizeof(strs), token_fd));
91
}
92
93
static int probe_kern_btf_func(int token_fd)
94
{
95
static const char strs[] = "\0int\0x\0a";
96
/* void x(int a) {} */
97
__u32 types[] = {
98
/* int */
99
BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
100
/* FUNC_PROTO */ /* [2] */
101
BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, 1), 0),
102
BTF_PARAM_ENC(7, 1),
103
/* FUNC x */ /* [3] */
104
BTF_TYPE_ENC(5, BTF_INFO_ENC(BTF_KIND_FUNC, 0, 0), 2),
105
};
106
107
return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
108
strs, sizeof(strs), token_fd));
109
}
110
111
static int probe_kern_btf_func_global(int token_fd)
112
{
113
static const char strs[] = "\0int\0x\0a";
114
/* static void x(int a) {} */
115
__u32 types[] = {
116
/* int */
117
BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
118
/* FUNC_PROTO */ /* [2] */
119
BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, 1), 0),
120
BTF_PARAM_ENC(7, 1),
121
/* FUNC x BTF_FUNC_GLOBAL */ /* [3] */
122
BTF_TYPE_ENC(5, BTF_INFO_ENC(BTF_KIND_FUNC, 0, BTF_FUNC_GLOBAL), 2),
123
};
124
125
return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
126
strs, sizeof(strs), token_fd));
127
}
128
129
static int probe_kern_btf_datasec(int token_fd)
130
{
131
static const char strs[] = "\0x\0.data";
132
/* static int a; */
133
__u32 types[] = {
134
/* int */
135
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
136
/* VAR x */ /* [2] */
137
BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_VAR, 0, 0), 1),
138
BTF_VAR_STATIC,
139
/* DATASEC val */ /* [3] */
140
BTF_TYPE_ENC(3, BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 1), 4),
141
BTF_VAR_SECINFO_ENC(2, 0, 4),
142
};
143
144
return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
145
strs, sizeof(strs), token_fd));
146
}
147
148
static int probe_kern_btf_qmark_datasec(int token_fd)
149
{
150
static const char strs[] = "\0x\0?.data";
151
/* static int a; */
152
__u32 types[] = {
153
/* int */
154
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
155
/* VAR x */ /* [2] */
156
BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_VAR, 0, 0), 1),
157
BTF_VAR_STATIC,
158
/* DATASEC ?.data */ /* [3] */
159
BTF_TYPE_ENC(3, BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 1), 4),
160
BTF_VAR_SECINFO_ENC(2, 0, 4),
161
};
162
163
return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
164
strs, sizeof(strs), token_fd));
165
}
166
167
static int probe_kern_btf_float(int token_fd)
168
{
169
static const char strs[] = "\0float";
170
__u32 types[] = {
171
/* float */
172
BTF_TYPE_FLOAT_ENC(1, 4),
173
};
174
175
return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
176
strs, sizeof(strs), token_fd));
177
}
178
179
static int probe_kern_btf_decl_tag(int token_fd)
180
{
181
static const char strs[] = "\0tag";
182
__u32 types[] = {
183
/* int */
184
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
185
/* VAR x */ /* [2] */
186
BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_VAR, 0, 0), 1),
187
BTF_VAR_STATIC,
188
/* attr */
189
BTF_TYPE_DECL_TAG_ENC(1, 2, -1),
190
};
191
192
return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
193
strs, sizeof(strs), token_fd));
194
}
195
196
static int probe_kern_btf_type_tag(int token_fd)
197
{
198
static const char strs[] = "\0tag";
199
__u32 types[] = {
200
/* int */
201
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
202
/* attr */
203
BTF_TYPE_TYPE_TAG_ENC(1, 1), /* [2] */
204
/* ptr */
205
BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), 2), /* [3] */
206
};
207
208
return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
209
strs, sizeof(strs), token_fd));
210
}
211
212
static int probe_kern_array_mmap(int token_fd)
213
{
214
LIBBPF_OPTS(bpf_map_create_opts, opts,
215
.map_flags = BPF_F_MMAPABLE | (token_fd ? BPF_F_TOKEN_FD : 0),
216
.token_fd = token_fd,
217
);
218
int fd;
219
220
fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "libbpf_mmap", sizeof(int), sizeof(int), 1, &opts);
221
return probe_fd(fd);
222
}
223
224
static int probe_kern_exp_attach_type(int token_fd)
225
{
226
LIBBPF_OPTS(bpf_prog_load_opts, opts,
227
.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
228
.token_fd = token_fd,
229
.prog_flags = token_fd ? BPF_F_TOKEN_FD : 0,
230
);
231
struct bpf_insn insns[] = {
232
BPF_MOV64_IMM(BPF_REG_0, 0),
233
BPF_EXIT_INSN(),
234
};
235
int fd, insn_cnt = ARRAY_SIZE(insns);
236
237
/* use any valid combination of program type and (optional)
238
* non-zero expected attach type (i.e., not a BPF_CGROUP_INET_INGRESS)
239
* to see if kernel supports expected_attach_type field for
240
* BPF_PROG_LOAD command
241
*/
242
fd = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", insns, insn_cnt, &opts);
243
return probe_fd(fd);
244
}
245
246
static int probe_kern_probe_read_kernel(int token_fd)
247
{
248
LIBBPF_OPTS(bpf_prog_load_opts, opts,
249
.token_fd = token_fd,
250
.prog_flags = token_fd ? BPF_F_TOKEN_FD : 0,
251
);
252
struct bpf_insn insns[] = {
253
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), /* r1 = r10 (fp) */
254
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), /* r1 += -8 */
255
BPF_MOV64_IMM(BPF_REG_2, 8), /* r2 = 8 */
256
BPF_MOV64_IMM(BPF_REG_3, 0), /* r3 = 0 */
257
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_probe_read_kernel),
258
BPF_EXIT_INSN(),
259
};
260
int fd, insn_cnt = ARRAY_SIZE(insns);
261
262
fd = bpf_prog_load(BPF_PROG_TYPE_TRACEPOINT, NULL, "GPL", insns, insn_cnt, &opts);
263
return probe_fd(fd);
264
}
265
266
static int probe_prog_bind_map(int token_fd)
267
{
268
struct bpf_insn insns[] = {
269
BPF_MOV64_IMM(BPF_REG_0, 0),
270
BPF_EXIT_INSN(),
271
};
272
LIBBPF_OPTS(bpf_map_create_opts, map_opts,
273
.token_fd = token_fd,
274
.map_flags = token_fd ? BPF_F_TOKEN_FD : 0,
275
);
276
LIBBPF_OPTS(bpf_prog_load_opts, prog_opts,
277
.token_fd = token_fd,
278
.prog_flags = token_fd ? BPF_F_TOKEN_FD : 0,
279
);
280
int ret, map, prog, insn_cnt = ARRAY_SIZE(insns);
281
282
map = bpf_map_create(BPF_MAP_TYPE_ARRAY, "libbpf_det_bind", sizeof(int), 32, 1, &map_opts);
283
if (map < 0) {
284
ret = -errno;
285
pr_warn("Error in %s(): %s. Couldn't create simple array map.\n",
286
__func__, errstr(ret));
287
return ret;
288
}
289
290
prog = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", insns, insn_cnt, &prog_opts);
291
if (prog < 0) {
292
close(map);
293
return 0;
294
}
295
296
ret = bpf_prog_bind_map(prog, map, NULL);
297
298
close(map);
299
close(prog);
300
301
return ret >= 0;
302
}
303
304
static int probe_module_btf(int token_fd)
305
{
306
static const char strs[] = "\0int";
307
__u32 types[] = {
308
/* int */
309
BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4),
310
};
311
struct bpf_btf_info info;
312
__u32 len = sizeof(info);
313
char name[16];
314
int fd, err;
315
316
fd = libbpf__load_raw_btf((char *)types, sizeof(types), strs, sizeof(strs), token_fd);
317
if (fd < 0)
318
return 0; /* BTF not supported at all */
319
320
memset(&info, 0, sizeof(info));
321
info.name = ptr_to_u64(name);
322
info.name_len = sizeof(name);
323
324
/* check that BPF_OBJ_GET_INFO_BY_FD supports specifying name pointer;
325
* kernel's module BTF support coincides with support for
326
* name/name_len fields in struct bpf_btf_info.
327
*/
328
err = bpf_btf_get_info_by_fd(fd, &info, &len);
329
close(fd);
330
return !err;
331
}
332
333
static int probe_perf_link(int token_fd)
334
{
335
struct bpf_insn insns[] = {
336
BPF_MOV64_IMM(BPF_REG_0, 0),
337
BPF_EXIT_INSN(),
338
};
339
LIBBPF_OPTS(bpf_prog_load_opts, opts,
340
.token_fd = token_fd,
341
.prog_flags = token_fd ? BPF_F_TOKEN_FD : 0,
342
);
343
int prog_fd, link_fd, err;
344
345
prog_fd = bpf_prog_load(BPF_PROG_TYPE_TRACEPOINT, NULL, "GPL",
346
insns, ARRAY_SIZE(insns), &opts);
347
if (prog_fd < 0)
348
return -errno;
349
350
/* use invalid perf_event FD to get EBADF, if link is supported;
351
* otherwise EINVAL should be returned
352
*/
353
link_fd = bpf_link_create(prog_fd, -1, BPF_PERF_EVENT, NULL);
354
err = -errno; /* close() can clobber errno */
355
356
if (link_fd >= 0)
357
close(link_fd);
358
close(prog_fd);
359
360
return link_fd < 0 && err == -EBADF;
361
}
362
363
static int probe_uprobe_multi_link(int token_fd)
364
{
365
LIBBPF_OPTS(bpf_prog_load_opts, load_opts,
366
.expected_attach_type = BPF_TRACE_UPROBE_MULTI,
367
.token_fd = token_fd,
368
.prog_flags = token_fd ? BPF_F_TOKEN_FD : 0,
369
);
370
LIBBPF_OPTS(bpf_link_create_opts, link_opts);
371
struct bpf_insn insns[] = {
372
BPF_MOV64_IMM(BPF_REG_0, 0),
373
BPF_EXIT_INSN(),
374
};
375
int prog_fd, link_fd, err;
376
unsigned long offset = 0;
377
378
prog_fd = bpf_prog_load(BPF_PROG_TYPE_KPROBE, NULL, "GPL",
379
insns, ARRAY_SIZE(insns), &load_opts);
380
if (prog_fd < 0)
381
return -errno;
382
383
/* Creating uprobe in '/' binary should fail with -EBADF. */
384
link_opts.uprobe_multi.path = "/";
385
link_opts.uprobe_multi.offsets = &offset;
386
link_opts.uprobe_multi.cnt = 1;
387
388
link_fd = bpf_link_create(prog_fd, -1, BPF_TRACE_UPROBE_MULTI, &link_opts);
389
err = -errno; /* close() can clobber errno */
390
391
if (link_fd >= 0 || err != -EBADF) {
392
if (link_fd >= 0)
393
close(link_fd);
394
close(prog_fd);
395
return 0;
396
}
397
398
/* Initial multi-uprobe support in kernel didn't handle PID filtering
399
* correctly (it was doing thread filtering, not process filtering).
400
* So now we'll detect if PID filtering logic was fixed, and, if not,
401
* we'll pretend multi-uprobes are not supported, if not.
402
* Multi-uprobes are used in USDT attachment logic, and we need to be
403
* conservative here, because multi-uprobe selection happens early at
404
* load time, while the use of PID filtering is known late at
405
* attachment time, at which point it's too late to undo multi-uprobe
406
* selection.
407
*
408
* Creating uprobe with pid == -1 for (invalid) '/' binary will fail
409
* early with -EINVAL on kernels with fixed PID filtering logic;
410
* otherwise -ESRCH would be returned if passed correct binary path
411
* (but we'll just get -BADF, of course).
412
*/
413
link_opts.uprobe_multi.pid = -1; /* invalid PID */
414
link_opts.uprobe_multi.path = "/"; /* invalid path */
415
link_opts.uprobe_multi.offsets = &offset;
416
link_opts.uprobe_multi.cnt = 1;
417
418
link_fd = bpf_link_create(prog_fd, -1, BPF_TRACE_UPROBE_MULTI, &link_opts);
419
err = -errno; /* close() can clobber errno */
420
421
if (link_fd >= 0)
422
close(link_fd);
423
close(prog_fd);
424
425
return link_fd < 0 && err == -EINVAL;
426
}
427
428
static int probe_kern_bpf_cookie(int token_fd)
429
{
430
struct bpf_insn insns[] = {
431
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_attach_cookie),
432
BPF_EXIT_INSN(),
433
};
434
LIBBPF_OPTS(bpf_prog_load_opts, opts,
435
.token_fd = token_fd,
436
.prog_flags = token_fd ? BPF_F_TOKEN_FD : 0,
437
);
438
int ret, insn_cnt = ARRAY_SIZE(insns);
439
440
ret = bpf_prog_load(BPF_PROG_TYPE_TRACEPOINT, NULL, "GPL", insns, insn_cnt, &opts);
441
return probe_fd(ret);
442
}
443
444
static int probe_kern_btf_enum64(int token_fd)
445
{
446
static const char strs[] = "\0enum64";
447
__u32 types[] = {
448
BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 0), 8),
449
};
450
451
return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
452
strs, sizeof(strs), token_fd));
453
}
454
455
static int probe_kern_arg_ctx_tag(int token_fd)
456
{
457
static const char strs[] = "\0a\0b\0arg:ctx\0";
458
const __u32 types[] = {
459
/* [1] INT */
460
BTF_TYPE_INT_ENC(1 /* "a" */, BTF_INT_SIGNED, 0, 32, 4),
461
/* [2] PTR -> VOID */
462
BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), 0),
463
/* [3] FUNC_PROTO `int(void *a)` */
464
BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, 1), 1),
465
BTF_PARAM_ENC(1 /* "a" */, 2),
466
/* [4] FUNC 'a' -> FUNC_PROTO (main prog) */
467
BTF_TYPE_ENC(1 /* "a" */, BTF_INFO_ENC(BTF_KIND_FUNC, 0, BTF_FUNC_GLOBAL), 3),
468
/* [5] FUNC_PROTO `int(void *b __arg_ctx)` */
469
BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, 1), 1),
470
BTF_PARAM_ENC(3 /* "b" */, 2),
471
/* [6] FUNC 'b' -> FUNC_PROTO (subprog) */
472
BTF_TYPE_ENC(3 /* "b" */, BTF_INFO_ENC(BTF_KIND_FUNC, 0, BTF_FUNC_GLOBAL), 5),
473
/* [7] DECL_TAG 'arg:ctx' -> func 'b' arg 'b' */
474
BTF_TYPE_DECL_TAG_ENC(5 /* "arg:ctx" */, 6, 0),
475
};
476
const struct bpf_insn insns[] = {
477
/* main prog */
478
BPF_CALL_REL(+1),
479
BPF_EXIT_INSN(),
480
/* global subprog */
481
BPF_EMIT_CALL(BPF_FUNC_get_func_ip), /* needs PTR_TO_CTX */
482
BPF_EXIT_INSN(),
483
};
484
const struct bpf_func_info_min func_infos[] = {
485
{ 0, 4 }, /* main prog -> FUNC 'a' */
486
{ 2, 6 }, /* subprog -> FUNC 'b' */
487
};
488
LIBBPF_OPTS(bpf_prog_load_opts, opts,
489
.token_fd = token_fd,
490
.prog_flags = token_fd ? BPF_F_TOKEN_FD : 0,
491
);
492
int prog_fd, btf_fd, insn_cnt = ARRAY_SIZE(insns);
493
494
btf_fd = libbpf__load_raw_btf((char *)types, sizeof(types), strs, sizeof(strs), token_fd);
495
if (btf_fd < 0)
496
return 0;
497
498
opts.prog_btf_fd = btf_fd;
499
opts.func_info = &func_infos;
500
opts.func_info_cnt = ARRAY_SIZE(func_infos);
501
opts.func_info_rec_size = sizeof(func_infos[0]);
502
503
prog_fd = bpf_prog_load(BPF_PROG_TYPE_KPROBE, "det_arg_ctx",
504
"GPL", insns, insn_cnt, &opts);
505
close(btf_fd);
506
507
return probe_fd(prog_fd);
508
}
509
510
typedef int (*feature_probe_fn)(int /* token_fd */);
511
512
static struct kern_feature_cache feature_cache;
513
514
static struct kern_feature_desc {
515
const char *desc;
516
feature_probe_fn probe;
517
} feature_probes[__FEAT_CNT] = {
518
[FEAT_PROG_NAME] = {
519
"BPF program name", probe_kern_prog_name,
520
},
521
[FEAT_GLOBAL_DATA] = {
522
"global variables", probe_kern_global_data,
523
},
524
[FEAT_BTF] = {
525
"minimal BTF", probe_kern_btf,
526
},
527
[FEAT_BTF_FUNC] = {
528
"BTF functions", probe_kern_btf_func,
529
},
530
[FEAT_BTF_GLOBAL_FUNC] = {
531
"BTF global function", probe_kern_btf_func_global,
532
},
533
[FEAT_BTF_DATASEC] = {
534
"BTF data section and variable", probe_kern_btf_datasec,
535
},
536
[FEAT_ARRAY_MMAP] = {
537
"ARRAY map mmap()", probe_kern_array_mmap,
538
},
539
[FEAT_EXP_ATTACH_TYPE] = {
540
"BPF_PROG_LOAD expected_attach_type attribute",
541
probe_kern_exp_attach_type,
542
},
543
[FEAT_PROBE_READ_KERN] = {
544
"bpf_probe_read_kernel() helper", probe_kern_probe_read_kernel,
545
},
546
[FEAT_PROG_BIND_MAP] = {
547
"BPF_PROG_BIND_MAP support", probe_prog_bind_map,
548
},
549
[FEAT_MODULE_BTF] = {
550
"module BTF support", probe_module_btf,
551
},
552
[FEAT_BTF_FLOAT] = {
553
"BTF_KIND_FLOAT support", probe_kern_btf_float,
554
},
555
[FEAT_PERF_LINK] = {
556
"BPF perf link support", probe_perf_link,
557
},
558
[FEAT_BTF_DECL_TAG] = {
559
"BTF_KIND_DECL_TAG support", probe_kern_btf_decl_tag,
560
},
561
[FEAT_BTF_TYPE_TAG] = {
562
"BTF_KIND_TYPE_TAG support", probe_kern_btf_type_tag,
563
},
564
[FEAT_MEMCG_ACCOUNT] = {
565
"memcg-based memory accounting", probe_memcg_account,
566
},
567
[FEAT_BPF_COOKIE] = {
568
"BPF cookie support", probe_kern_bpf_cookie,
569
},
570
[FEAT_BTF_ENUM64] = {
571
"BTF_KIND_ENUM64 support", probe_kern_btf_enum64,
572
},
573
[FEAT_SYSCALL_WRAPPER] = {
574
"Kernel using syscall wrapper", probe_kern_syscall_wrapper,
575
},
576
[FEAT_UPROBE_MULTI_LINK] = {
577
"BPF multi-uprobe link support", probe_uprobe_multi_link,
578
},
579
[FEAT_ARG_CTX_TAG] = {
580
"kernel-side __arg_ctx tag", probe_kern_arg_ctx_tag,
581
},
582
[FEAT_BTF_QMARK_DATASEC] = {
583
"BTF DATASEC names starting from '?'", probe_kern_btf_qmark_datasec,
584
},
585
};
586
587
bool feat_supported(struct kern_feature_cache *cache, enum kern_feature_id feat_id)
588
{
589
struct kern_feature_desc *feat = &feature_probes[feat_id];
590
int ret;
591
592
/* assume global feature cache, unless custom one is provided */
593
if (!cache)
594
cache = &feature_cache;
595
596
if (READ_ONCE(cache->res[feat_id]) == FEAT_UNKNOWN) {
597
ret = feat->probe(cache->token_fd);
598
if (ret > 0) {
599
WRITE_ONCE(cache->res[feat_id], FEAT_SUPPORTED);
600
} else if (ret == 0) {
601
WRITE_ONCE(cache->res[feat_id], FEAT_MISSING);
602
} else {
603
pr_warn("Detection of kernel %s support failed: %s\n",
604
feat->desc, errstr(ret));
605
WRITE_ONCE(cache->res[feat_id], FEAT_MISSING);
606
}
607
}
608
609
return READ_ONCE(cache->res[feat_id]) == FEAT_SUPPORTED;
610
}
611
612