brintos

brintos / linux-shallow public Read only

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