179d49ba0SDaniel Borkmann // SPDX-License-Identifier: GPL-2.0
279d49ba0SDaniel Borkmann #include <test_progs.h>
33b037911SMaciej Fijalkowski #include <network_helpers.h>
479d49ba0SDaniel Borkmann 
579d49ba0SDaniel Borkmann /* test_tailcall_1 checks basic functionality by patching multiple locations
679d49ba0SDaniel Borkmann  * in a single program for a single tail call slot with nop->jmp, jmp->nop
779d49ba0SDaniel Borkmann  * and jmp->jmp rewrites. Also checks for nop->nop.
879d49ba0SDaniel Borkmann  */
test_tailcall_1(void)979d49ba0SDaniel Borkmann static void test_tailcall_1(void)
1079d49ba0SDaniel Borkmann {
1179d49ba0SDaniel Borkmann 	int err, map_fd, prog_fd, main_fd, i, j;
1279d49ba0SDaniel Borkmann 	struct bpf_map *prog_array;
1379d49ba0SDaniel Borkmann 	struct bpf_program *prog;
1479d49ba0SDaniel Borkmann 	struct bpf_object *obj;
1579d49ba0SDaniel Borkmann 	char prog_name[32];
1679d49ba0SDaniel Borkmann 	char buff[128] = {};
1704fcb5f9SDelyan Kratunov 	LIBBPF_OPTS(bpf_test_run_opts, topts,
1804fcb5f9SDelyan Kratunov 		.data_in = buff,
1904fcb5f9SDelyan Kratunov 		.data_size_in = sizeof(buff),
2004fcb5f9SDelyan Kratunov 		.repeat = 1,
2104fcb5f9SDelyan Kratunov 	);
2279d49ba0SDaniel Borkmann 
23afef88e6SDaniel Müller 	err = bpf_prog_test_load("tailcall1.bpf.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
2479d49ba0SDaniel Borkmann 				 &prog_fd);
2579d49ba0SDaniel Borkmann 	if (CHECK_FAIL(err))
2679d49ba0SDaniel Borkmann 		return;
2779d49ba0SDaniel Borkmann 
28c22bdd28SAndrii Nakryiko 	prog = bpf_object__find_program_by_name(obj, "entry");
2979d49ba0SDaniel Borkmann 	if (CHECK_FAIL(!prog))
3079d49ba0SDaniel Borkmann 		goto out;
3179d49ba0SDaniel Borkmann 
3279d49ba0SDaniel Borkmann 	main_fd = bpf_program__fd(prog);
3379d49ba0SDaniel Borkmann 	if (CHECK_FAIL(main_fd < 0))
3479d49ba0SDaniel Borkmann 		goto out;
3579d49ba0SDaniel Borkmann 
3679d49ba0SDaniel Borkmann 	prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
3779d49ba0SDaniel Borkmann 	if (CHECK_FAIL(!prog_array))
3879d49ba0SDaniel Borkmann 		goto out;
3979d49ba0SDaniel Borkmann 
4079d49ba0SDaniel Borkmann 	map_fd = bpf_map__fd(prog_array);
4179d49ba0SDaniel Borkmann 	if (CHECK_FAIL(map_fd < 0))
4279d49ba0SDaniel Borkmann 		goto out;
4379d49ba0SDaniel Borkmann 
448d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
45c22bdd28SAndrii Nakryiko 		snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
4679d49ba0SDaniel Borkmann 
47c22bdd28SAndrii Nakryiko 		prog = bpf_object__find_program_by_name(obj, prog_name);
4879d49ba0SDaniel Borkmann 		if (CHECK_FAIL(!prog))
4979d49ba0SDaniel Borkmann 			goto out;
5079d49ba0SDaniel Borkmann 
5179d49ba0SDaniel Borkmann 		prog_fd = bpf_program__fd(prog);
5279d49ba0SDaniel Borkmann 		if (CHECK_FAIL(prog_fd < 0))
5379d49ba0SDaniel Borkmann 			goto out;
5479d49ba0SDaniel Borkmann 
5579d49ba0SDaniel Borkmann 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
5679d49ba0SDaniel Borkmann 		if (CHECK_FAIL(err))
5779d49ba0SDaniel Borkmann 			goto out;
5879d49ba0SDaniel Borkmann 	}
5979d49ba0SDaniel Borkmann 
608d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
6104fcb5f9SDelyan Kratunov 		err = bpf_prog_test_run_opts(main_fd, &topts);
6204fcb5f9SDelyan Kratunov 		ASSERT_OK(err, "tailcall");
6304fcb5f9SDelyan Kratunov 		ASSERT_EQ(topts.retval, i, "tailcall retval");
6479d49ba0SDaniel Borkmann 
6579d49ba0SDaniel Borkmann 		err = bpf_map_delete_elem(map_fd, &i);
6679d49ba0SDaniel Borkmann 		if (CHECK_FAIL(err))
6779d49ba0SDaniel Borkmann 			goto out;
6879d49ba0SDaniel Borkmann 	}
6979d49ba0SDaniel Borkmann 
7004fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
7104fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
7204fcb5f9SDelyan Kratunov 	ASSERT_EQ(topts.retval, 3, "tailcall retval");
7379d49ba0SDaniel Borkmann 
748d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
75c22bdd28SAndrii Nakryiko 		snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
7679d49ba0SDaniel Borkmann 
77c22bdd28SAndrii Nakryiko 		prog = bpf_object__find_program_by_name(obj, prog_name);
7879d49ba0SDaniel Borkmann 		if (CHECK_FAIL(!prog))
7979d49ba0SDaniel Borkmann 			goto out;
8079d49ba0SDaniel Borkmann 
8179d49ba0SDaniel Borkmann 		prog_fd = bpf_program__fd(prog);
8279d49ba0SDaniel Borkmann 		if (CHECK_FAIL(prog_fd < 0))
8379d49ba0SDaniel Borkmann 			goto out;
8479d49ba0SDaniel Borkmann 
8579d49ba0SDaniel Borkmann 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
8679d49ba0SDaniel Borkmann 		if (CHECK_FAIL(err))
8779d49ba0SDaniel Borkmann 			goto out;
8879d49ba0SDaniel Borkmann 	}
8979d49ba0SDaniel Borkmann 
9004fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
9104fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
9204fcb5f9SDelyan Kratunov 	ASSERT_OK(topts.retval, "tailcall retval");
9379d49ba0SDaniel Borkmann 
948d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
958d6fabf1SChristy Lee 		j = bpf_map__max_entries(prog_array) - 1 - i;
96c22bdd28SAndrii Nakryiko 		snprintf(prog_name, sizeof(prog_name), "classifier_%d", j);
9779d49ba0SDaniel Borkmann 
98c22bdd28SAndrii Nakryiko 		prog = bpf_object__find_program_by_name(obj, prog_name);
9979d49ba0SDaniel Borkmann 		if (CHECK_FAIL(!prog))
10079d49ba0SDaniel Borkmann 			goto out;
10179d49ba0SDaniel Borkmann 
10279d49ba0SDaniel Borkmann 		prog_fd = bpf_program__fd(prog);
10379d49ba0SDaniel Borkmann 		if (CHECK_FAIL(prog_fd < 0))
10479d49ba0SDaniel Borkmann 			goto out;
10579d49ba0SDaniel Borkmann 
10679d49ba0SDaniel Borkmann 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
10779d49ba0SDaniel Borkmann 		if (CHECK_FAIL(err))
10879d49ba0SDaniel Borkmann 			goto out;
10979d49ba0SDaniel Borkmann 	}
11079d49ba0SDaniel Borkmann 
1118d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
1128d6fabf1SChristy Lee 		j = bpf_map__max_entries(prog_array) - 1 - i;
11379d49ba0SDaniel Borkmann 
11404fcb5f9SDelyan Kratunov 		err = bpf_prog_test_run_opts(main_fd, &topts);
11504fcb5f9SDelyan Kratunov 		ASSERT_OK(err, "tailcall");
11604fcb5f9SDelyan Kratunov 		ASSERT_EQ(topts.retval, j, "tailcall retval");
11779d49ba0SDaniel Borkmann 
11879d49ba0SDaniel Borkmann 		err = bpf_map_delete_elem(map_fd, &i);
11979d49ba0SDaniel Borkmann 		if (CHECK_FAIL(err))
12079d49ba0SDaniel Borkmann 			goto out;
12179d49ba0SDaniel Borkmann 	}
12279d49ba0SDaniel Borkmann 
12304fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
12404fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
12504fcb5f9SDelyan Kratunov 	ASSERT_EQ(topts.retval, 3, "tailcall retval");
12679d49ba0SDaniel Borkmann 
1278d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
12879d49ba0SDaniel Borkmann 		err = bpf_map_delete_elem(map_fd, &i);
12979d49ba0SDaniel Borkmann 		if (CHECK_FAIL(err >= 0 || errno != ENOENT))
13079d49ba0SDaniel Borkmann 			goto out;
13179d49ba0SDaniel Borkmann 
13204fcb5f9SDelyan Kratunov 		err = bpf_prog_test_run_opts(main_fd, &topts);
13304fcb5f9SDelyan Kratunov 		ASSERT_OK(err, "tailcall");
13404fcb5f9SDelyan Kratunov 		ASSERT_EQ(topts.retval, 3, "tailcall retval");
13579d49ba0SDaniel Borkmann 	}
13679d49ba0SDaniel Borkmann 
13779d49ba0SDaniel Borkmann out:
13879d49ba0SDaniel Borkmann 	bpf_object__close(obj);
13979d49ba0SDaniel Borkmann }
14079d49ba0SDaniel Borkmann 
14179d49ba0SDaniel Borkmann /* test_tailcall_2 checks that patching multiple programs for a single
14279d49ba0SDaniel Borkmann  * tail call slot works. It also jumps through several programs and tests
14379d49ba0SDaniel Borkmann  * the tail call limit counter.
14479d49ba0SDaniel Borkmann  */
test_tailcall_2(void)14579d49ba0SDaniel Borkmann static void test_tailcall_2(void)
14679d49ba0SDaniel Borkmann {
14779d49ba0SDaniel Borkmann 	int err, map_fd, prog_fd, main_fd, i;
14879d49ba0SDaniel Borkmann 	struct bpf_map *prog_array;
14979d49ba0SDaniel Borkmann 	struct bpf_program *prog;
15079d49ba0SDaniel Borkmann 	struct bpf_object *obj;
15179d49ba0SDaniel Borkmann 	char prog_name[32];
15279d49ba0SDaniel Borkmann 	char buff[128] = {};
15304fcb5f9SDelyan Kratunov 	LIBBPF_OPTS(bpf_test_run_opts, topts,
15404fcb5f9SDelyan Kratunov 		.data_in = buff,
15504fcb5f9SDelyan Kratunov 		.data_size_in = sizeof(buff),
15604fcb5f9SDelyan Kratunov 		.repeat = 1,
15704fcb5f9SDelyan Kratunov 	);
15879d49ba0SDaniel Borkmann 
159afef88e6SDaniel Müller 	err = bpf_prog_test_load("tailcall2.bpf.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
16079d49ba0SDaniel Borkmann 				 &prog_fd);
16179d49ba0SDaniel Borkmann 	if (CHECK_FAIL(err))
16279d49ba0SDaniel Borkmann 		return;
16379d49ba0SDaniel Borkmann 
164c22bdd28SAndrii Nakryiko 	prog = bpf_object__find_program_by_name(obj, "entry");
16579d49ba0SDaniel Borkmann 	if (CHECK_FAIL(!prog))
16679d49ba0SDaniel Borkmann 		goto out;
16779d49ba0SDaniel Borkmann 
16879d49ba0SDaniel Borkmann 	main_fd = bpf_program__fd(prog);
16979d49ba0SDaniel Borkmann 	if (CHECK_FAIL(main_fd < 0))
17079d49ba0SDaniel Borkmann 		goto out;
17179d49ba0SDaniel Borkmann 
17279d49ba0SDaniel Borkmann 	prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
17379d49ba0SDaniel Borkmann 	if (CHECK_FAIL(!prog_array))
17479d49ba0SDaniel Borkmann 		goto out;
17579d49ba0SDaniel Borkmann 
17679d49ba0SDaniel Borkmann 	map_fd = bpf_map__fd(prog_array);
17779d49ba0SDaniel Borkmann 	if (CHECK_FAIL(map_fd < 0))
17879d49ba0SDaniel Borkmann 		goto out;
17979d49ba0SDaniel Borkmann 
1808d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
181c22bdd28SAndrii Nakryiko 		snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
18279d49ba0SDaniel Borkmann 
183c22bdd28SAndrii Nakryiko 		prog = bpf_object__find_program_by_name(obj, prog_name);
18479d49ba0SDaniel Borkmann 		if (CHECK_FAIL(!prog))
18579d49ba0SDaniel Borkmann 			goto out;
18679d49ba0SDaniel Borkmann 
18779d49ba0SDaniel Borkmann 		prog_fd = bpf_program__fd(prog);
18879d49ba0SDaniel Borkmann 		if (CHECK_FAIL(prog_fd < 0))
18979d49ba0SDaniel Borkmann 			goto out;
19079d49ba0SDaniel Borkmann 
19179d49ba0SDaniel Borkmann 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
19279d49ba0SDaniel Borkmann 		if (CHECK_FAIL(err))
19379d49ba0SDaniel Borkmann 			goto out;
19479d49ba0SDaniel Borkmann 	}
19579d49ba0SDaniel Borkmann 
19604fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
19704fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
19804fcb5f9SDelyan Kratunov 	ASSERT_EQ(topts.retval, 2, "tailcall retval");
19979d49ba0SDaniel Borkmann 
20079d49ba0SDaniel Borkmann 	i = 2;
20179d49ba0SDaniel Borkmann 	err = bpf_map_delete_elem(map_fd, &i);
20279d49ba0SDaniel Borkmann 	if (CHECK_FAIL(err))
20379d49ba0SDaniel Borkmann 		goto out;
20479d49ba0SDaniel Borkmann 
20504fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
20604fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
20704fcb5f9SDelyan Kratunov 	ASSERT_EQ(topts.retval, 1, "tailcall retval");
20879d49ba0SDaniel Borkmann 
20979d49ba0SDaniel Borkmann 	i = 0;
21079d49ba0SDaniel Borkmann 	err = bpf_map_delete_elem(map_fd, &i);
21179d49ba0SDaniel Borkmann 	if (CHECK_FAIL(err))
21279d49ba0SDaniel Borkmann 		goto out;
21379d49ba0SDaniel Borkmann 
21404fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
21504fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
21604fcb5f9SDelyan Kratunov 	ASSERT_EQ(topts.retval, 3, "tailcall retval");
21779d49ba0SDaniel Borkmann out:
21879d49ba0SDaniel Borkmann 	bpf_object__close(obj);
21979d49ba0SDaniel Borkmann }
22079d49ba0SDaniel Borkmann 
test_tailcall_count(const char * which)221dbd7eb14SDaniel Borkmann static void test_tailcall_count(const char *which)
22279d49ba0SDaniel Borkmann {
22379d49ba0SDaniel Borkmann 	int err, map_fd, prog_fd, main_fd, data_fd, i, val;
22479d49ba0SDaniel Borkmann 	struct bpf_map *prog_array, *data_map;
22579d49ba0SDaniel Borkmann 	struct bpf_program *prog;
22679d49ba0SDaniel Borkmann 	struct bpf_object *obj;
22779d49ba0SDaniel Borkmann 	char buff[128] = {};
22804fcb5f9SDelyan Kratunov 	LIBBPF_OPTS(bpf_test_run_opts, topts,
22904fcb5f9SDelyan Kratunov 		.data_in = buff,
23004fcb5f9SDelyan Kratunov 		.data_size_in = sizeof(buff),
23104fcb5f9SDelyan Kratunov 		.repeat = 1,
23204fcb5f9SDelyan Kratunov 	);
23379d49ba0SDaniel Borkmann 
234cbdb1461SAndrii Nakryiko 	err = bpf_prog_test_load(which, BPF_PROG_TYPE_SCHED_CLS, &obj,
23579d49ba0SDaniel Borkmann 			    &prog_fd);
23679d49ba0SDaniel Borkmann 	if (CHECK_FAIL(err))
23779d49ba0SDaniel Borkmann 		return;
23879d49ba0SDaniel Borkmann 
239c22bdd28SAndrii Nakryiko 	prog = bpf_object__find_program_by_name(obj, "entry");
24079d49ba0SDaniel Borkmann 	if (CHECK_FAIL(!prog))
24179d49ba0SDaniel Borkmann 		goto out;
24279d49ba0SDaniel Borkmann 
24379d49ba0SDaniel Borkmann 	main_fd = bpf_program__fd(prog);
24479d49ba0SDaniel Borkmann 	if (CHECK_FAIL(main_fd < 0))
24579d49ba0SDaniel Borkmann 		goto out;
24679d49ba0SDaniel Borkmann 
24779d49ba0SDaniel Borkmann 	prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
24879d49ba0SDaniel Borkmann 	if (CHECK_FAIL(!prog_array))
24979d49ba0SDaniel Borkmann 		goto out;
25079d49ba0SDaniel Borkmann 
25179d49ba0SDaniel Borkmann 	map_fd = bpf_map__fd(prog_array);
25279d49ba0SDaniel Borkmann 	if (CHECK_FAIL(map_fd < 0))
25379d49ba0SDaniel Borkmann 		goto out;
25479d49ba0SDaniel Borkmann 
255c22bdd28SAndrii Nakryiko 	prog = bpf_object__find_program_by_name(obj, "classifier_0");
25679d49ba0SDaniel Borkmann 	if (CHECK_FAIL(!prog))
25779d49ba0SDaniel Borkmann 		goto out;
25879d49ba0SDaniel Borkmann 
25979d49ba0SDaniel Borkmann 	prog_fd = bpf_program__fd(prog);
26079d49ba0SDaniel Borkmann 	if (CHECK_FAIL(prog_fd < 0))
26179d49ba0SDaniel Borkmann 		goto out;
26279d49ba0SDaniel Borkmann 
26379d49ba0SDaniel Borkmann 	i = 0;
26479d49ba0SDaniel Borkmann 	err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
26579d49ba0SDaniel Borkmann 	if (CHECK_FAIL(err))
26679d49ba0SDaniel Borkmann 		goto out;
26779d49ba0SDaniel Borkmann 
26804fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
26904fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
27004fcb5f9SDelyan Kratunov 	ASSERT_EQ(topts.retval, 1, "tailcall retval");
27179d49ba0SDaniel Borkmann 
27279d49ba0SDaniel Borkmann 	data_map = bpf_object__find_map_by_name(obj, "tailcall.bss");
27379d49ba0SDaniel Borkmann 	if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map)))
274*f6071cf7SLeon Hwang 		goto out;
27579d49ba0SDaniel Borkmann 
27679d49ba0SDaniel Borkmann 	data_fd = bpf_map__fd(data_map);
277*f6071cf7SLeon Hwang 	if (CHECK_FAIL(data_fd < 0))
278*f6071cf7SLeon Hwang 		goto out;
27979d49ba0SDaniel Borkmann 
28079d49ba0SDaniel Borkmann 	i = 0;
28179d49ba0SDaniel Borkmann 	err = bpf_map_lookup_elem(data_fd, &i, &val);
28204fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall count");
28304fcb5f9SDelyan Kratunov 	ASSERT_EQ(val, 33, "tailcall count");
28479d49ba0SDaniel Borkmann 
28579d49ba0SDaniel Borkmann 	i = 0;
28679d49ba0SDaniel Borkmann 	err = bpf_map_delete_elem(map_fd, &i);
28779d49ba0SDaniel Borkmann 	if (CHECK_FAIL(err))
28879d49ba0SDaniel Borkmann 		goto out;
28979d49ba0SDaniel Borkmann 
29004fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
29104fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
29204fcb5f9SDelyan Kratunov 	ASSERT_OK(topts.retval, "tailcall retval");
29379d49ba0SDaniel Borkmann out:
29479d49ba0SDaniel Borkmann 	bpf_object__close(obj);
29579d49ba0SDaniel Borkmann }
29679d49ba0SDaniel Borkmann 
297dbd7eb14SDaniel Borkmann /* test_tailcall_3 checks that the count value of the tail call limit
298dbd7eb14SDaniel Borkmann  * enforcement matches with expectations. JIT uses direct jump.
299dbd7eb14SDaniel Borkmann  */
test_tailcall_3(void)300dbd7eb14SDaniel Borkmann static void test_tailcall_3(void)
301dbd7eb14SDaniel Borkmann {
302afef88e6SDaniel Müller 	test_tailcall_count("tailcall3.bpf.o");
303dbd7eb14SDaniel Borkmann }
304dbd7eb14SDaniel Borkmann 
305dbd7eb14SDaniel Borkmann /* test_tailcall_6 checks that the count value of the tail call limit
306dbd7eb14SDaniel Borkmann  * enforcement matches with expectations. JIT uses indirect jump.
307dbd7eb14SDaniel Borkmann  */
test_tailcall_6(void)308dbd7eb14SDaniel Borkmann static void test_tailcall_6(void)
309dbd7eb14SDaniel Borkmann {
310afef88e6SDaniel Müller 	test_tailcall_count("tailcall6.bpf.o");
311dbd7eb14SDaniel Borkmann }
312dbd7eb14SDaniel Borkmann 
31379d49ba0SDaniel Borkmann /* test_tailcall_4 checks that the kernel properly selects indirect jump
31479d49ba0SDaniel Borkmann  * for the case where the key is not known. Latter is passed via global
31579d49ba0SDaniel Borkmann  * data to select different targets we can compare return value of.
31679d49ba0SDaniel Borkmann  */
test_tailcall_4(void)31779d49ba0SDaniel Borkmann static void test_tailcall_4(void)
31879d49ba0SDaniel Borkmann {
31979d49ba0SDaniel Borkmann 	int err, map_fd, prog_fd, main_fd, data_fd, i;
32079d49ba0SDaniel Borkmann 	struct bpf_map *prog_array, *data_map;
32179d49ba0SDaniel Borkmann 	struct bpf_program *prog;
32279d49ba0SDaniel Borkmann 	struct bpf_object *obj;
32379d49ba0SDaniel Borkmann 	static const int zero = 0;
32479d49ba0SDaniel Borkmann 	char buff[128] = {};
32579d49ba0SDaniel Borkmann 	char prog_name[32];
32604fcb5f9SDelyan Kratunov 	LIBBPF_OPTS(bpf_test_run_opts, topts,
32704fcb5f9SDelyan Kratunov 		.data_in = buff,
32804fcb5f9SDelyan Kratunov 		.data_size_in = sizeof(buff),
32904fcb5f9SDelyan Kratunov 		.repeat = 1,
33004fcb5f9SDelyan Kratunov 	);
33179d49ba0SDaniel Borkmann 
332afef88e6SDaniel Müller 	err = bpf_prog_test_load("tailcall4.bpf.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
33379d49ba0SDaniel Borkmann 				 &prog_fd);
33479d49ba0SDaniel Borkmann 	if (CHECK_FAIL(err))
33579d49ba0SDaniel Borkmann 		return;
33679d49ba0SDaniel Borkmann 
337c22bdd28SAndrii Nakryiko 	prog = bpf_object__find_program_by_name(obj, "entry");
33879d49ba0SDaniel Borkmann 	if (CHECK_FAIL(!prog))
33979d49ba0SDaniel Borkmann 		goto out;
34079d49ba0SDaniel Borkmann 
34179d49ba0SDaniel Borkmann 	main_fd = bpf_program__fd(prog);
34279d49ba0SDaniel Borkmann 	if (CHECK_FAIL(main_fd < 0))
34379d49ba0SDaniel Borkmann 		goto out;
34479d49ba0SDaniel Borkmann 
34579d49ba0SDaniel Borkmann 	prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
34679d49ba0SDaniel Borkmann 	if (CHECK_FAIL(!prog_array))
34779d49ba0SDaniel Borkmann 		goto out;
34879d49ba0SDaniel Borkmann 
34979d49ba0SDaniel Borkmann 	map_fd = bpf_map__fd(prog_array);
35079d49ba0SDaniel Borkmann 	if (CHECK_FAIL(map_fd < 0))
35179d49ba0SDaniel Borkmann 		goto out;
35279d49ba0SDaniel Borkmann 
35379d49ba0SDaniel Borkmann 	data_map = bpf_object__find_map_by_name(obj, "tailcall.bss");
35479d49ba0SDaniel Borkmann 	if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map)))
355*f6071cf7SLeon Hwang 		goto out;
35679d49ba0SDaniel Borkmann 
35779d49ba0SDaniel Borkmann 	data_fd = bpf_map__fd(data_map);
358*f6071cf7SLeon Hwang 	if (CHECK_FAIL(data_fd < 0))
359*f6071cf7SLeon Hwang 		goto out;
36079d49ba0SDaniel Borkmann 
3618d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
362c22bdd28SAndrii Nakryiko 		snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
36379d49ba0SDaniel Borkmann 
364c22bdd28SAndrii Nakryiko 		prog = bpf_object__find_program_by_name(obj, prog_name);
36579d49ba0SDaniel Borkmann 		if (CHECK_FAIL(!prog))
36679d49ba0SDaniel Borkmann 			goto out;
36779d49ba0SDaniel Borkmann 
36879d49ba0SDaniel Borkmann 		prog_fd = bpf_program__fd(prog);
36979d49ba0SDaniel Borkmann 		if (CHECK_FAIL(prog_fd < 0))
37079d49ba0SDaniel Borkmann 			goto out;
37179d49ba0SDaniel Borkmann 
37279d49ba0SDaniel Borkmann 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
37379d49ba0SDaniel Borkmann 		if (CHECK_FAIL(err))
37479d49ba0SDaniel Borkmann 			goto out;
37579d49ba0SDaniel Borkmann 	}
37679d49ba0SDaniel Borkmann 
3778d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
37879d49ba0SDaniel Borkmann 		err = bpf_map_update_elem(data_fd, &zero, &i, BPF_ANY);
37979d49ba0SDaniel Borkmann 		if (CHECK_FAIL(err))
38079d49ba0SDaniel Borkmann 			goto out;
38179d49ba0SDaniel Borkmann 
38204fcb5f9SDelyan Kratunov 		err = bpf_prog_test_run_opts(main_fd, &topts);
38304fcb5f9SDelyan Kratunov 		ASSERT_OK(err, "tailcall");
38404fcb5f9SDelyan Kratunov 		ASSERT_EQ(topts.retval, i, "tailcall retval");
38579d49ba0SDaniel Borkmann 	}
38679d49ba0SDaniel Borkmann 
3878d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
38879d49ba0SDaniel Borkmann 		err = bpf_map_update_elem(data_fd, &zero, &i, BPF_ANY);
38979d49ba0SDaniel Borkmann 		if (CHECK_FAIL(err))
39079d49ba0SDaniel Borkmann 			goto out;
39179d49ba0SDaniel Borkmann 
39279d49ba0SDaniel Borkmann 		err = bpf_map_delete_elem(map_fd, &i);
39379d49ba0SDaniel Borkmann 		if (CHECK_FAIL(err))
39479d49ba0SDaniel Borkmann 			goto out;
39579d49ba0SDaniel Borkmann 
39604fcb5f9SDelyan Kratunov 		err = bpf_prog_test_run_opts(main_fd, &topts);
39704fcb5f9SDelyan Kratunov 		ASSERT_OK(err, "tailcall");
39804fcb5f9SDelyan Kratunov 		ASSERT_EQ(topts.retval, 3, "tailcall retval");
39979d49ba0SDaniel Borkmann 	}
40079d49ba0SDaniel Borkmann out:
40179d49ba0SDaniel Borkmann 	bpf_object__close(obj);
40279d49ba0SDaniel Borkmann }
40379d49ba0SDaniel Borkmann 
40479d49ba0SDaniel Borkmann /* test_tailcall_5 probes similarly to test_tailcall_4 that the kernel generates
40579d49ba0SDaniel Borkmann  * an indirect jump when the keys are const but different from different branches.
40679d49ba0SDaniel Borkmann  */
test_tailcall_5(void)40779d49ba0SDaniel Borkmann static void test_tailcall_5(void)
40879d49ba0SDaniel Borkmann {
40979d49ba0SDaniel Borkmann 	int err, map_fd, prog_fd, main_fd, data_fd, i, key[] = { 1111, 1234, 5678 };
41079d49ba0SDaniel Borkmann 	struct bpf_map *prog_array, *data_map;
41179d49ba0SDaniel Borkmann 	struct bpf_program *prog;
41279d49ba0SDaniel Borkmann 	struct bpf_object *obj;
41379d49ba0SDaniel Borkmann 	static const int zero = 0;
41479d49ba0SDaniel Borkmann 	char buff[128] = {};
41579d49ba0SDaniel Borkmann 	char prog_name[32];
41604fcb5f9SDelyan Kratunov 	LIBBPF_OPTS(bpf_test_run_opts, topts,
41704fcb5f9SDelyan Kratunov 		.data_in = buff,
41804fcb5f9SDelyan Kratunov 		.data_size_in = sizeof(buff),
41904fcb5f9SDelyan Kratunov 		.repeat = 1,
42004fcb5f9SDelyan Kratunov 	);
42179d49ba0SDaniel Borkmann 
422afef88e6SDaniel Müller 	err = bpf_prog_test_load("tailcall5.bpf.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
42379d49ba0SDaniel Borkmann 				 &prog_fd);
42479d49ba0SDaniel Borkmann 	if (CHECK_FAIL(err))
42579d49ba0SDaniel Borkmann 		return;
42679d49ba0SDaniel Borkmann 
427c22bdd28SAndrii Nakryiko 	prog = bpf_object__find_program_by_name(obj, "entry");
42879d49ba0SDaniel Borkmann 	if (CHECK_FAIL(!prog))
42979d49ba0SDaniel Borkmann 		goto out;
43079d49ba0SDaniel Borkmann 
43179d49ba0SDaniel Borkmann 	main_fd = bpf_program__fd(prog);
43279d49ba0SDaniel Borkmann 	if (CHECK_FAIL(main_fd < 0))
43379d49ba0SDaniel Borkmann 		goto out;
43479d49ba0SDaniel Borkmann 
43579d49ba0SDaniel Borkmann 	prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
43679d49ba0SDaniel Borkmann 	if (CHECK_FAIL(!prog_array))
43779d49ba0SDaniel Borkmann 		goto out;
43879d49ba0SDaniel Borkmann 
43979d49ba0SDaniel Borkmann 	map_fd = bpf_map__fd(prog_array);
44079d49ba0SDaniel Borkmann 	if (CHECK_FAIL(map_fd < 0))
44179d49ba0SDaniel Borkmann 		goto out;
44279d49ba0SDaniel Borkmann 
44379d49ba0SDaniel Borkmann 	data_map = bpf_object__find_map_by_name(obj, "tailcall.bss");
44479d49ba0SDaniel Borkmann 	if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map)))
445*f6071cf7SLeon Hwang 		goto out;
44679d49ba0SDaniel Borkmann 
44779d49ba0SDaniel Borkmann 	data_fd = bpf_map__fd(data_map);
448*f6071cf7SLeon Hwang 	if (CHECK_FAIL(data_fd < 0))
449*f6071cf7SLeon Hwang 		goto out;
45079d49ba0SDaniel Borkmann 
4518d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
452c22bdd28SAndrii Nakryiko 		snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
45379d49ba0SDaniel Borkmann 
454c22bdd28SAndrii Nakryiko 		prog = bpf_object__find_program_by_name(obj, prog_name);
45579d49ba0SDaniel Borkmann 		if (CHECK_FAIL(!prog))
45679d49ba0SDaniel Borkmann 			goto out;
45779d49ba0SDaniel Borkmann 
45879d49ba0SDaniel Borkmann 		prog_fd = bpf_program__fd(prog);
45979d49ba0SDaniel Borkmann 		if (CHECK_FAIL(prog_fd < 0))
46079d49ba0SDaniel Borkmann 			goto out;
46179d49ba0SDaniel Borkmann 
46279d49ba0SDaniel Borkmann 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
46379d49ba0SDaniel Borkmann 		if (CHECK_FAIL(err))
46479d49ba0SDaniel Borkmann 			goto out;
46579d49ba0SDaniel Borkmann 	}
46679d49ba0SDaniel Borkmann 
4678d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
46879d49ba0SDaniel Borkmann 		err = bpf_map_update_elem(data_fd, &zero, &key[i], BPF_ANY);
46979d49ba0SDaniel Borkmann 		if (CHECK_FAIL(err))
47079d49ba0SDaniel Borkmann 			goto out;
47179d49ba0SDaniel Borkmann 
47204fcb5f9SDelyan Kratunov 		err = bpf_prog_test_run_opts(main_fd, &topts);
47304fcb5f9SDelyan Kratunov 		ASSERT_OK(err, "tailcall");
47404fcb5f9SDelyan Kratunov 		ASSERT_EQ(topts.retval, i, "tailcall retval");
47579d49ba0SDaniel Borkmann 	}
47679d49ba0SDaniel Borkmann 
4778d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
47879d49ba0SDaniel Borkmann 		err = bpf_map_update_elem(data_fd, &zero, &key[i], BPF_ANY);
47979d49ba0SDaniel Borkmann 		if (CHECK_FAIL(err))
48079d49ba0SDaniel Borkmann 			goto out;
48179d49ba0SDaniel Borkmann 
48279d49ba0SDaniel Borkmann 		err = bpf_map_delete_elem(map_fd, &i);
48379d49ba0SDaniel Borkmann 		if (CHECK_FAIL(err))
48479d49ba0SDaniel Borkmann 			goto out;
48579d49ba0SDaniel Borkmann 
48604fcb5f9SDelyan Kratunov 		err = bpf_prog_test_run_opts(main_fd, &topts);
48704fcb5f9SDelyan Kratunov 		ASSERT_OK(err, "tailcall");
48804fcb5f9SDelyan Kratunov 		ASSERT_EQ(topts.retval, 3, "tailcall retval");
48979d49ba0SDaniel Borkmann 	}
49079d49ba0SDaniel Borkmann out:
49179d49ba0SDaniel Borkmann 	bpf_object__close(obj);
49279d49ba0SDaniel Borkmann }
49379d49ba0SDaniel Borkmann 
4943b037911SMaciej Fijalkowski /* test_tailcall_bpf2bpf_1 purpose is to make sure that tailcalls are working
4953b037911SMaciej Fijalkowski  * correctly in correlation with BPF subprograms
4963b037911SMaciej Fijalkowski  */
test_tailcall_bpf2bpf_1(void)4973b037911SMaciej Fijalkowski static void test_tailcall_bpf2bpf_1(void)
4983b037911SMaciej Fijalkowski {
4993b037911SMaciej Fijalkowski 	int err, map_fd, prog_fd, main_fd, i;
5003b037911SMaciej Fijalkowski 	struct bpf_map *prog_array;
5013b037911SMaciej Fijalkowski 	struct bpf_program *prog;
5023b037911SMaciej Fijalkowski 	struct bpf_object *obj;
5033b037911SMaciej Fijalkowski 	char prog_name[32];
50404fcb5f9SDelyan Kratunov 	LIBBPF_OPTS(bpf_test_run_opts, topts,
50504fcb5f9SDelyan Kratunov 		.data_in = &pkt_v4,
50604fcb5f9SDelyan Kratunov 		.data_size_in = sizeof(pkt_v4),
50704fcb5f9SDelyan Kratunov 		.repeat = 1,
50804fcb5f9SDelyan Kratunov 	);
5093b037911SMaciej Fijalkowski 
510afef88e6SDaniel Müller 	err = bpf_prog_test_load("tailcall_bpf2bpf1.bpf.o", BPF_PROG_TYPE_SCHED_CLS,
5113b037911SMaciej Fijalkowski 				 &obj, &prog_fd);
5123b037911SMaciej Fijalkowski 	if (CHECK_FAIL(err))
5133b037911SMaciej Fijalkowski 		return;
5143b037911SMaciej Fijalkowski 
515c22bdd28SAndrii Nakryiko 	prog = bpf_object__find_program_by_name(obj, "entry");
5163b037911SMaciej Fijalkowski 	if (CHECK_FAIL(!prog))
5173b037911SMaciej Fijalkowski 		goto out;
5183b037911SMaciej Fijalkowski 
5193b037911SMaciej Fijalkowski 	main_fd = bpf_program__fd(prog);
5203b037911SMaciej Fijalkowski 	if (CHECK_FAIL(main_fd < 0))
5213b037911SMaciej Fijalkowski 		goto out;
5223b037911SMaciej Fijalkowski 
5233b037911SMaciej Fijalkowski 	prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
5243b037911SMaciej Fijalkowski 	if (CHECK_FAIL(!prog_array))
5253b037911SMaciej Fijalkowski 		goto out;
5263b037911SMaciej Fijalkowski 
5273b037911SMaciej Fijalkowski 	map_fd = bpf_map__fd(prog_array);
5283b037911SMaciej Fijalkowski 	if (CHECK_FAIL(map_fd < 0))
5293b037911SMaciej Fijalkowski 		goto out;
5303b037911SMaciej Fijalkowski 
5313b037911SMaciej Fijalkowski 	/* nop -> jmp */
5328d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
533c22bdd28SAndrii Nakryiko 		snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
5343b037911SMaciej Fijalkowski 
535c22bdd28SAndrii Nakryiko 		prog = bpf_object__find_program_by_name(obj, prog_name);
5363b037911SMaciej Fijalkowski 		if (CHECK_FAIL(!prog))
5373b037911SMaciej Fijalkowski 			goto out;
5383b037911SMaciej Fijalkowski 
5393b037911SMaciej Fijalkowski 		prog_fd = bpf_program__fd(prog);
5403b037911SMaciej Fijalkowski 		if (CHECK_FAIL(prog_fd < 0))
5413b037911SMaciej Fijalkowski 			goto out;
5423b037911SMaciej Fijalkowski 
5433b037911SMaciej Fijalkowski 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
5443b037911SMaciej Fijalkowski 		if (CHECK_FAIL(err))
5453b037911SMaciej Fijalkowski 			goto out;
5463b037911SMaciej Fijalkowski 	}
5473b037911SMaciej Fijalkowski 
54804fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
54904fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
55004fcb5f9SDelyan Kratunov 	ASSERT_EQ(topts.retval, 1, "tailcall retval");
5513b037911SMaciej Fijalkowski 
5523b037911SMaciej Fijalkowski 	/* jmp -> nop, call subprog that will do tailcall */
5533b037911SMaciej Fijalkowski 	i = 1;
5543b037911SMaciej Fijalkowski 	err = bpf_map_delete_elem(map_fd, &i);
5553b037911SMaciej Fijalkowski 	if (CHECK_FAIL(err))
5563b037911SMaciej Fijalkowski 		goto out;
5573b037911SMaciej Fijalkowski 
55804fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
55904fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
56004fcb5f9SDelyan Kratunov 	ASSERT_OK(topts.retval, "tailcall retval");
5613b037911SMaciej Fijalkowski 
5623b037911SMaciej Fijalkowski 	/* make sure that subprog can access ctx and entry prog that
5633b037911SMaciej Fijalkowski 	 * called this subprog can properly return
5643b037911SMaciej Fijalkowski 	 */
5653b037911SMaciej Fijalkowski 	i = 0;
5663b037911SMaciej Fijalkowski 	err = bpf_map_delete_elem(map_fd, &i);
5673b037911SMaciej Fijalkowski 	if (CHECK_FAIL(err))
5683b037911SMaciej Fijalkowski 		goto out;
5693b037911SMaciej Fijalkowski 
57004fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
57104fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
57204fcb5f9SDelyan Kratunov 	ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 2, "tailcall retval");
5733b037911SMaciej Fijalkowski out:
5743b037911SMaciej Fijalkowski 	bpf_object__close(obj);
5753b037911SMaciej Fijalkowski }
5763b037911SMaciej Fijalkowski 
5773b037911SMaciej Fijalkowski /* test_tailcall_bpf2bpf_2 checks that the count value of the tail call limit
5783b037911SMaciej Fijalkowski  * enforcement matches with expectations when tailcall is preceded with
5793b037911SMaciej Fijalkowski  * bpf2bpf call.
5803b037911SMaciej Fijalkowski  */
test_tailcall_bpf2bpf_2(void)5813b037911SMaciej Fijalkowski static void test_tailcall_bpf2bpf_2(void)
5823b037911SMaciej Fijalkowski {
5833b037911SMaciej Fijalkowski 	int err, map_fd, prog_fd, main_fd, data_fd, i, val;
5843b037911SMaciej Fijalkowski 	struct bpf_map *prog_array, *data_map;
5853b037911SMaciej Fijalkowski 	struct bpf_program *prog;
5863b037911SMaciej Fijalkowski 	struct bpf_object *obj;
5873b037911SMaciej Fijalkowski 	char buff[128] = {};
58804fcb5f9SDelyan Kratunov 	LIBBPF_OPTS(bpf_test_run_opts, topts,
58904fcb5f9SDelyan Kratunov 		.data_in = buff,
59004fcb5f9SDelyan Kratunov 		.data_size_in = sizeof(buff),
59104fcb5f9SDelyan Kratunov 		.repeat = 1,
59204fcb5f9SDelyan Kratunov 	);
5933b037911SMaciej Fijalkowski 
594afef88e6SDaniel Müller 	err = bpf_prog_test_load("tailcall_bpf2bpf2.bpf.o", BPF_PROG_TYPE_SCHED_CLS,
5953b037911SMaciej Fijalkowski 				 &obj, &prog_fd);
5963b037911SMaciej Fijalkowski 	if (CHECK_FAIL(err))
5973b037911SMaciej Fijalkowski 		return;
5983b037911SMaciej Fijalkowski 
599c22bdd28SAndrii Nakryiko 	prog = bpf_object__find_program_by_name(obj, "entry");
6003b037911SMaciej Fijalkowski 	if (CHECK_FAIL(!prog))
6013b037911SMaciej Fijalkowski 		goto out;
6023b037911SMaciej Fijalkowski 
6033b037911SMaciej Fijalkowski 	main_fd = bpf_program__fd(prog);
6043b037911SMaciej Fijalkowski 	if (CHECK_FAIL(main_fd < 0))
6053b037911SMaciej Fijalkowski 		goto out;
6063b037911SMaciej Fijalkowski 
6073b037911SMaciej Fijalkowski 	prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
6083b037911SMaciej Fijalkowski 	if (CHECK_FAIL(!prog_array))
6093b037911SMaciej Fijalkowski 		goto out;
6103b037911SMaciej Fijalkowski 
6113b037911SMaciej Fijalkowski 	map_fd = bpf_map__fd(prog_array);
6123b037911SMaciej Fijalkowski 	if (CHECK_FAIL(map_fd < 0))
6133b037911SMaciej Fijalkowski 		goto out;
6143b037911SMaciej Fijalkowski 
615c22bdd28SAndrii Nakryiko 	prog = bpf_object__find_program_by_name(obj, "classifier_0");
6163b037911SMaciej Fijalkowski 	if (CHECK_FAIL(!prog))
6173b037911SMaciej Fijalkowski 		goto out;
6183b037911SMaciej Fijalkowski 
6193b037911SMaciej Fijalkowski 	prog_fd = bpf_program__fd(prog);
6203b037911SMaciej Fijalkowski 	if (CHECK_FAIL(prog_fd < 0))
6213b037911SMaciej Fijalkowski 		goto out;
6223b037911SMaciej Fijalkowski 
6233b037911SMaciej Fijalkowski 	i = 0;
6243b037911SMaciej Fijalkowski 	err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
6253b037911SMaciej Fijalkowski 	if (CHECK_FAIL(err))
6263b037911SMaciej Fijalkowski 		goto out;
6273b037911SMaciej Fijalkowski 
62804fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
62904fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
63004fcb5f9SDelyan Kratunov 	ASSERT_EQ(topts.retval, 1, "tailcall retval");
6313b037911SMaciej Fijalkowski 
6323b037911SMaciej Fijalkowski 	data_map = bpf_object__find_map_by_name(obj, "tailcall.bss");
6333b037911SMaciej Fijalkowski 	if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map)))
634*f6071cf7SLeon Hwang 		goto out;
6353b037911SMaciej Fijalkowski 
6363b037911SMaciej Fijalkowski 	data_fd = bpf_map__fd(data_map);
637*f6071cf7SLeon Hwang 	if (CHECK_FAIL(data_fd < 0))
638*f6071cf7SLeon Hwang 		goto out;
6393b037911SMaciej Fijalkowski 
6403b037911SMaciej Fijalkowski 	i = 0;
6413b037911SMaciej Fijalkowski 	err = bpf_map_lookup_elem(data_fd, &i, &val);
64204fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall count");
64304fcb5f9SDelyan Kratunov 	ASSERT_EQ(val, 33, "tailcall count");
6443b037911SMaciej Fijalkowski 
6453b037911SMaciej Fijalkowski 	i = 0;
6463b037911SMaciej Fijalkowski 	err = bpf_map_delete_elem(map_fd, &i);
6473b037911SMaciej Fijalkowski 	if (CHECK_FAIL(err))
6483b037911SMaciej Fijalkowski 		goto out;
6493b037911SMaciej Fijalkowski 
65004fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
65104fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
65204fcb5f9SDelyan Kratunov 	ASSERT_OK(topts.retval, "tailcall retval");
6533b037911SMaciej Fijalkowski out:
6543b037911SMaciej Fijalkowski 	bpf_object__close(obj);
6553b037911SMaciej Fijalkowski }
6563b037911SMaciej Fijalkowski 
6573b037911SMaciej Fijalkowski /* test_tailcall_bpf2bpf_3 checks that non-trivial amount of stack (up to
6583b037911SMaciej Fijalkowski  * 256 bytes) can be used within bpf subprograms that have the tailcalls
6593b037911SMaciej Fijalkowski  * in them
6603b037911SMaciej Fijalkowski  */
test_tailcall_bpf2bpf_3(void)6613b037911SMaciej Fijalkowski static void test_tailcall_bpf2bpf_3(void)
6623b037911SMaciej Fijalkowski {
6633b037911SMaciej Fijalkowski 	int err, map_fd, prog_fd, main_fd, i;
6643b037911SMaciej Fijalkowski 	struct bpf_map *prog_array;
6653b037911SMaciej Fijalkowski 	struct bpf_program *prog;
6663b037911SMaciej Fijalkowski 	struct bpf_object *obj;
6673b037911SMaciej Fijalkowski 	char prog_name[32];
66804fcb5f9SDelyan Kratunov 	LIBBPF_OPTS(bpf_test_run_opts, topts,
66904fcb5f9SDelyan Kratunov 		.data_in = &pkt_v4,
67004fcb5f9SDelyan Kratunov 		.data_size_in = sizeof(pkt_v4),
67104fcb5f9SDelyan Kratunov 		.repeat = 1,
67204fcb5f9SDelyan Kratunov 	);
6733b037911SMaciej Fijalkowski 
674afef88e6SDaniel Müller 	err = bpf_prog_test_load("tailcall_bpf2bpf3.bpf.o", BPF_PROG_TYPE_SCHED_CLS,
6753b037911SMaciej Fijalkowski 				 &obj, &prog_fd);
6763b037911SMaciej Fijalkowski 	if (CHECK_FAIL(err))
6773b037911SMaciej Fijalkowski 		return;
6783b037911SMaciej Fijalkowski 
679c22bdd28SAndrii Nakryiko 	prog = bpf_object__find_program_by_name(obj, "entry");
6803b037911SMaciej Fijalkowski 	if (CHECK_FAIL(!prog))
6813b037911SMaciej Fijalkowski 		goto out;
6823b037911SMaciej Fijalkowski 
6833b037911SMaciej Fijalkowski 	main_fd = bpf_program__fd(prog);
6843b037911SMaciej Fijalkowski 	if (CHECK_FAIL(main_fd < 0))
6853b037911SMaciej Fijalkowski 		goto out;
6863b037911SMaciej Fijalkowski 
6873b037911SMaciej Fijalkowski 	prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
6883b037911SMaciej Fijalkowski 	if (CHECK_FAIL(!prog_array))
6893b037911SMaciej Fijalkowski 		goto out;
6903b037911SMaciej Fijalkowski 
6913b037911SMaciej Fijalkowski 	map_fd = bpf_map__fd(prog_array);
6923b037911SMaciej Fijalkowski 	if (CHECK_FAIL(map_fd < 0))
6933b037911SMaciej Fijalkowski 		goto out;
6943b037911SMaciej Fijalkowski 
6958d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
696c22bdd28SAndrii Nakryiko 		snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
6973b037911SMaciej Fijalkowski 
698c22bdd28SAndrii Nakryiko 		prog = bpf_object__find_program_by_name(obj, prog_name);
6993b037911SMaciej Fijalkowski 		if (CHECK_FAIL(!prog))
7003b037911SMaciej Fijalkowski 			goto out;
7013b037911SMaciej Fijalkowski 
7023b037911SMaciej Fijalkowski 		prog_fd = bpf_program__fd(prog);
7033b037911SMaciej Fijalkowski 		if (CHECK_FAIL(prog_fd < 0))
7043b037911SMaciej Fijalkowski 			goto out;
7053b037911SMaciej Fijalkowski 
7063b037911SMaciej Fijalkowski 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
7073b037911SMaciej Fijalkowski 		if (CHECK_FAIL(err))
7083b037911SMaciej Fijalkowski 			goto out;
7093b037911SMaciej Fijalkowski 	}
7103b037911SMaciej Fijalkowski 
71104fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
71204fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
71304fcb5f9SDelyan Kratunov 	ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 3, "tailcall retval");
7143b037911SMaciej Fijalkowski 
7153b037911SMaciej Fijalkowski 	i = 1;
7163b037911SMaciej Fijalkowski 	err = bpf_map_delete_elem(map_fd, &i);
7173b037911SMaciej Fijalkowski 	if (CHECK_FAIL(err))
7183b037911SMaciej Fijalkowski 		goto out;
7193b037911SMaciej Fijalkowski 
72004fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
72104fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
72204fcb5f9SDelyan Kratunov 	ASSERT_EQ(topts.retval, sizeof(pkt_v4), "tailcall retval");
7233b037911SMaciej Fijalkowski 
7243b037911SMaciej Fijalkowski 	i = 0;
7253b037911SMaciej Fijalkowski 	err = bpf_map_delete_elem(map_fd, &i);
7263b037911SMaciej Fijalkowski 	if (CHECK_FAIL(err))
7273b037911SMaciej Fijalkowski 		goto out;
7283b037911SMaciej Fijalkowski 
72904fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
73004fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
73104fcb5f9SDelyan Kratunov 	ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 2, "tailcall retval");
7323b037911SMaciej Fijalkowski out:
7333b037911SMaciej Fijalkowski 	bpf_object__close(obj);
7343b037911SMaciej Fijalkowski }
7353b037911SMaciej Fijalkowski 
7361fb5ba29SJohn Fastabend #include "tailcall_bpf2bpf4.skel.h"
7371fb5ba29SJohn Fastabend 
7383b037911SMaciej Fijalkowski /* test_tailcall_bpf2bpf_4 checks that tailcall counter is correctly preserved
7393b037911SMaciej Fijalkowski  * across tailcalls combined with bpf2bpf calls. for making sure that tailcall
7403b037911SMaciej Fijalkowski  * counter behaves correctly, bpf program will go through following flow:
7413b037911SMaciej Fijalkowski  *
7423b037911SMaciej Fijalkowski  * entry -> entry_subprog -> tailcall0 -> bpf_func0 -> subprog0 ->
7433b037911SMaciej Fijalkowski  * -> tailcall1 -> bpf_func1 -> subprog1 -> tailcall2 -> bpf_func2 ->
7443b037911SMaciej Fijalkowski  * subprog2 [here bump global counter] --------^
7453b037911SMaciej Fijalkowski  *
7463b037911SMaciej Fijalkowski  * We go through first two tailcalls and start counting from the subprog2 where
7473b037911SMaciej Fijalkowski  * the loop begins. At the end of the test make sure that the global counter is
7483b037911SMaciej Fijalkowski  * equal to 31, because tailcall counter includes the first two tailcalls
7493b037911SMaciej Fijalkowski  * whereas global counter is incremented only on loop presented on flow above.
7501fb5ba29SJohn Fastabend  *
7511fb5ba29SJohn Fastabend  * The noise parameter is used to insert bpf_map_update calls into the logic
7521fb5ba29SJohn Fastabend  * to force verifier to patch instructions. This allows us to ensure jump
7531fb5ba29SJohn Fastabend  * logic remains correct with instruction movement.
7543b037911SMaciej Fijalkowski  */
test_tailcall_bpf2bpf_4(bool noise)7551fb5ba29SJohn Fastabend static void test_tailcall_bpf2bpf_4(bool noise)
7563b037911SMaciej Fijalkowski {
7571fb5ba29SJohn Fastabend 	int err, map_fd, prog_fd, main_fd, data_fd, i;
7581fb5ba29SJohn Fastabend 	struct tailcall_bpf2bpf4__bss val;
7593b037911SMaciej Fijalkowski 	struct bpf_map *prog_array, *data_map;
7603b037911SMaciej Fijalkowski 	struct bpf_program *prog;
7613b037911SMaciej Fijalkowski 	struct bpf_object *obj;
7623b037911SMaciej Fijalkowski 	char prog_name[32];
76304fcb5f9SDelyan Kratunov 	LIBBPF_OPTS(bpf_test_run_opts, topts,
76404fcb5f9SDelyan Kratunov 		.data_in = &pkt_v4,
76504fcb5f9SDelyan Kratunov 		.data_size_in = sizeof(pkt_v4),
76604fcb5f9SDelyan Kratunov 		.repeat = 1,
76704fcb5f9SDelyan Kratunov 	);
7683b037911SMaciej Fijalkowski 
769afef88e6SDaniel Müller 	err = bpf_prog_test_load("tailcall_bpf2bpf4.bpf.o", BPF_PROG_TYPE_SCHED_CLS,
7703b037911SMaciej Fijalkowski 				 &obj, &prog_fd);
7713b037911SMaciej Fijalkowski 	if (CHECK_FAIL(err))
7723b037911SMaciej Fijalkowski 		return;
7733b037911SMaciej Fijalkowski 
774c22bdd28SAndrii Nakryiko 	prog = bpf_object__find_program_by_name(obj, "entry");
7753b037911SMaciej Fijalkowski 	if (CHECK_FAIL(!prog))
7763b037911SMaciej Fijalkowski 		goto out;
7773b037911SMaciej Fijalkowski 
7783b037911SMaciej Fijalkowski 	main_fd = bpf_program__fd(prog);
7793b037911SMaciej Fijalkowski 	if (CHECK_FAIL(main_fd < 0))
7803b037911SMaciej Fijalkowski 		goto out;
7813b037911SMaciej Fijalkowski 
7823b037911SMaciej Fijalkowski 	prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
7833b037911SMaciej Fijalkowski 	if (CHECK_FAIL(!prog_array))
7843b037911SMaciej Fijalkowski 		goto out;
7853b037911SMaciej Fijalkowski 
7863b037911SMaciej Fijalkowski 	map_fd = bpf_map__fd(prog_array);
7873b037911SMaciej Fijalkowski 	if (CHECK_FAIL(map_fd < 0))
7883b037911SMaciej Fijalkowski 		goto out;
7893b037911SMaciej Fijalkowski 
7908d6fabf1SChristy Lee 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
791c22bdd28SAndrii Nakryiko 		snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
7923b037911SMaciej Fijalkowski 
793c22bdd28SAndrii Nakryiko 		prog = bpf_object__find_program_by_name(obj, prog_name);
7943b037911SMaciej Fijalkowski 		if (CHECK_FAIL(!prog))
7953b037911SMaciej Fijalkowski 			goto out;
7963b037911SMaciej Fijalkowski 
7973b037911SMaciej Fijalkowski 		prog_fd = bpf_program__fd(prog);
7983b037911SMaciej Fijalkowski 		if (CHECK_FAIL(prog_fd < 0))
7993b037911SMaciej Fijalkowski 			goto out;
8003b037911SMaciej Fijalkowski 
8013b037911SMaciej Fijalkowski 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
8023b037911SMaciej Fijalkowski 		if (CHECK_FAIL(err))
8033b037911SMaciej Fijalkowski 			goto out;
8043b037911SMaciej Fijalkowski 	}
8053b037911SMaciej Fijalkowski 
8063b037911SMaciej Fijalkowski 	data_map = bpf_object__find_map_by_name(obj, "tailcall.bss");
8073b037911SMaciej Fijalkowski 	if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map)))
808*f6071cf7SLeon Hwang 		goto out;
8093b037911SMaciej Fijalkowski 
8103b037911SMaciej Fijalkowski 	data_fd = bpf_map__fd(data_map);
811*f6071cf7SLeon Hwang 	if (CHECK_FAIL(data_fd < 0))
812*f6071cf7SLeon Hwang 		goto out;
8133b037911SMaciej Fijalkowski 
8143b037911SMaciej Fijalkowski 	i = 0;
8151fb5ba29SJohn Fastabend 	val.noise = noise;
8161fb5ba29SJohn Fastabend 	val.count = 0;
8171fb5ba29SJohn Fastabend 	err = bpf_map_update_elem(data_fd, &i, &val, BPF_ANY);
8181fb5ba29SJohn Fastabend 	if (CHECK_FAIL(err))
8191fb5ba29SJohn Fastabend 		goto out;
8201fb5ba29SJohn Fastabend 
82104fcb5f9SDelyan Kratunov 	err = bpf_prog_test_run_opts(main_fd, &topts);
82204fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall");
82304fcb5f9SDelyan Kratunov 	ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 3, "tailcall retval");
8241fb5ba29SJohn Fastabend 
8251fb5ba29SJohn Fastabend 	i = 0;
8263b037911SMaciej Fijalkowski 	err = bpf_map_lookup_elem(data_fd, &i, &val);
82704fcb5f9SDelyan Kratunov 	ASSERT_OK(err, "tailcall count");
82804fcb5f9SDelyan Kratunov 	ASSERT_EQ(val.count, 31, "tailcall count");
8293b037911SMaciej Fijalkowski 
8303b037911SMaciej Fijalkowski out:
8313b037911SMaciej Fijalkowski 	bpf_object__close(obj);
8323b037911SMaciej Fijalkowski }
8333b037911SMaciej Fijalkowski 
8345e0b0a4cSJakub Sitnicki #include "tailcall_bpf2bpf6.skel.h"
8355e0b0a4cSJakub Sitnicki 
8365e0b0a4cSJakub Sitnicki /* Tail call counting works even when there is data on stack which is
8375e0b0a4cSJakub Sitnicki  * not aligned to 8 bytes.
8385e0b0a4cSJakub Sitnicki  */
test_tailcall_bpf2bpf_6(void)8395e0b0a4cSJakub Sitnicki static void test_tailcall_bpf2bpf_6(void)
8405e0b0a4cSJakub Sitnicki {
8415e0b0a4cSJakub Sitnicki 	struct tailcall_bpf2bpf6 *obj;
8425e0b0a4cSJakub Sitnicki 	int err, map_fd, prog_fd, main_fd, data_fd, i, val;
8435e0b0a4cSJakub Sitnicki 	LIBBPF_OPTS(bpf_test_run_opts, topts,
8445e0b0a4cSJakub Sitnicki 		.data_in = &pkt_v4,
8455e0b0a4cSJakub Sitnicki 		.data_size_in = sizeof(pkt_v4),
8465e0b0a4cSJakub Sitnicki 		.repeat = 1,
8475e0b0a4cSJakub Sitnicki 	);
8485e0b0a4cSJakub Sitnicki 
8495e0b0a4cSJakub Sitnicki 	obj = tailcall_bpf2bpf6__open_and_load();
8505e0b0a4cSJakub Sitnicki 	if (!ASSERT_OK_PTR(obj, "open and load"))
8515e0b0a4cSJakub Sitnicki 		return;
8525e0b0a4cSJakub Sitnicki 
8535e0b0a4cSJakub Sitnicki 	main_fd = bpf_program__fd(obj->progs.entry);
8545e0b0a4cSJakub Sitnicki 	if (!ASSERT_GE(main_fd, 0, "entry prog fd"))
8555e0b0a4cSJakub Sitnicki 		goto out;
8565e0b0a4cSJakub Sitnicki 
8575e0b0a4cSJakub Sitnicki 	map_fd = bpf_map__fd(obj->maps.jmp_table);
8585e0b0a4cSJakub Sitnicki 	if (!ASSERT_GE(map_fd, 0, "jmp_table map fd"))
8595e0b0a4cSJakub Sitnicki 		goto out;
8605e0b0a4cSJakub Sitnicki 
8615e0b0a4cSJakub Sitnicki 	prog_fd = bpf_program__fd(obj->progs.classifier_0);
8625e0b0a4cSJakub Sitnicki 	if (!ASSERT_GE(prog_fd, 0, "classifier_0 prog fd"))
8635e0b0a4cSJakub Sitnicki 		goto out;
8645e0b0a4cSJakub Sitnicki 
8655e0b0a4cSJakub Sitnicki 	i = 0;
8665e0b0a4cSJakub Sitnicki 	err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
8675e0b0a4cSJakub Sitnicki 	if (!ASSERT_OK(err, "jmp_table map update"))
8685e0b0a4cSJakub Sitnicki 		goto out;
8695e0b0a4cSJakub Sitnicki 
8705e0b0a4cSJakub Sitnicki 	err = bpf_prog_test_run_opts(main_fd, &topts);
8715e0b0a4cSJakub Sitnicki 	ASSERT_OK(err, "entry prog test run");
8725e0b0a4cSJakub Sitnicki 	ASSERT_EQ(topts.retval, 0, "tailcall retval");
8735e0b0a4cSJakub Sitnicki 
8745e0b0a4cSJakub Sitnicki 	data_fd = bpf_map__fd(obj->maps.bss);
875*f6071cf7SLeon Hwang 	if (!ASSERT_GE(data_fd, 0, "bss map fd"))
8765e0b0a4cSJakub Sitnicki 		goto out;
8775e0b0a4cSJakub Sitnicki 
8785e0b0a4cSJakub Sitnicki 	i = 0;
8795e0b0a4cSJakub Sitnicki 	err = bpf_map_lookup_elem(data_fd, &i, &val);
8805e0b0a4cSJakub Sitnicki 	ASSERT_OK(err, "bss map lookup");
8815e0b0a4cSJakub Sitnicki 	ASSERT_EQ(val, 1, "done flag is set");
8825e0b0a4cSJakub Sitnicki 
8835e0b0a4cSJakub Sitnicki out:
8845e0b0a4cSJakub Sitnicki 	tailcall_bpf2bpf6__destroy(obj);
8855e0b0a4cSJakub Sitnicki }
8865e0b0a4cSJakub Sitnicki 
test_tailcalls(void)88779d49ba0SDaniel Borkmann void test_tailcalls(void)
88879d49ba0SDaniel Borkmann {
88979d49ba0SDaniel Borkmann 	if (test__start_subtest("tailcall_1"))
89079d49ba0SDaniel Borkmann 		test_tailcall_1();
89179d49ba0SDaniel Borkmann 	if (test__start_subtest("tailcall_2"))
89279d49ba0SDaniel Borkmann 		test_tailcall_2();
89379d49ba0SDaniel Borkmann 	if (test__start_subtest("tailcall_3"))
89479d49ba0SDaniel Borkmann 		test_tailcall_3();
89579d49ba0SDaniel Borkmann 	if (test__start_subtest("tailcall_4"))
89679d49ba0SDaniel Borkmann 		test_tailcall_4();
89779d49ba0SDaniel Borkmann 	if (test__start_subtest("tailcall_5"))
89879d49ba0SDaniel Borkmann 		test_tailcall_5();
899dbd7eb14SDaniel Borkmann 	if (test__start_subtest("tailcall_6"))
900dbd7eb14SDaniel Borkmann 		test_tailcall_6();
9013b037911SMaciej Fijalkowski 	if (test__start_subtest("tailcall_bpf2bpf_1"))
9023b037911SMaciej Fijalkowski 		test_tailcall_bpf2bpf_1();
9033b037911SMaciej Fijalkowski 	if (test__start_subtest("tailcall_bpf2bpf_2"))
9043b037911SMaciej Fijalkowski 		test_tailcall_bpf2bpf_2();
9053b037911SMaciej Fijalkowski 	if (test__start_subtest("tailcall_bpf2bpf_3"))
9063b037911SMaciej Fijalkowski 		test_tailcall_bpf2bpf_3();
9073b037911SMaciej Fijalkowski 	if (test__start_subtest("tailcall_bpf2bpf_4"))
9081fb5ba29SJohn Fastabend 		test_tailcall_bpf2bpf_4(false);
9091fb5ba29SJohn Fastabend 	if (test__start_subtest("tailcall_bpf2bpf_5"))
9101fb5ba29SJohn Fastabend 		test_tailcall_bpf2bpf_4(true);
9115e0b0a4cSJakub Sitnicki 	if (test__start_subtest("tailcall_bpf2bpf_6"))
9125e0b0a4cSJakub Sitnicki 		test_tailcall_bpf2bpf_6();
91379d49ba0SDaniel Borkmann }
914