Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Add threads to consumer test

With recent uprobe fix [1] the sync time after unregistering uprobe is
much longer and prolongs the consumer test which creates and destroys
hundreds of uprobes.

This change adds 16 threads (which fits the test logic) and speeds up
the test.

Before the change:

# perf stat --null ./test_progs -t uprobe_multi_test/consumers
#421/9 uprobe_multi_test/consumers:OK
#421 uprobe_multi_test:OK
Summary: 1/1 PASSED, 0 SKIPPED, 0 FAILED

Performance counter stats for './test_progs -t uprobe_multi_test/consumers':

28.818778973 seconds time elapsed

0.745518000 seconds user
0.919186000 seconds sys

After the change:

# perf stat --null ./test_progs -t uprobe_multi_test/consumers 2>&1
#421/9 uprobe_multi_test/consumers:OK
#421 uprobe_multi_test:OK
Summary: 1/1 PASSED, 0 SKIPPED, 0 FAILED

Performance counter stats for './test_progs -t uprobe_multi_test/consumers':

3.504790814 seconds time elapsed

0.012141000 seconds user
0.751760000 seconds sys

[1] commit 87195a1ee332 ("uprobes: switch to RCU Tasks Trace flavor for better performance")

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20241108134544.480660-14-jolsa@kernel.org

authored by

Jiri Olsa and committed by
Andrii Nakryiko
abaec834 b1c570ad

+80 -18
+80 -18
tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
··· 789 789 } 790 790 } 791 791 792 - static int uprobe_attach(struct uprobe_multi_consumers *skel, int idx) 792 + static int uprobe_attach(struct uprobe_multi_consumers *skel, int idx, unsigned long offset) 793 793 { 794 794 struct bpf_program *prog = get_program(skel, idx); 795 795 struct bpf_link **link = get_link(skel, idx); ··· 797 797 798 798 if (!prog || !link) 799 799 return -1; 800 + 801 + opts.offsets = &offset; 802 + opts.cnt = 1; 800 803 801 804 /* 802 805 * bit/prog: 0 uprobe entry ··· 810 807 opts.retprobe = idx == 1; 811 808 opts.session = idx == 2 || idx == 3; 812 809 813 - *link = bpf_program__attach_uprobe_multi(prog, 0, "/proc/self/exe", 814 - "uprobe_consumer_test", 815 - &opts); 810 + *link = bpf_program__attach_uprobe_multi(prog, 0, "/proc/self/exe", NULL, &opts); 816 811 if (!ASSERT_OK_PTR(*link, "bpf_program__attach_uprobe_multi")) 817 812 return -1; 818 813 return 0; ··· 831 830 832 831 noinline int 833 832 uprobe_consumer_test(struct uprobe_multi_consumers *skel, 834 - unsigned long before, unsigned long after) 833 + unsigned long before, unsigned long after, 834 + unsigned long offset) 835 835 { 836 836 int idx; 837 837 ··· 845 843 /* ... and attach all new programs in 'after' state */ 846 844 for (idx = 0; idx < 4; idx++) { 847 845 if (!test_bit(idx, before) && test_bit(idx, after)) { 848 - if (!ASSERT_OK(uprobe_attach(skel, idx), "uprobe_attach_after")) 846 + if (!ASSERT_OK(uprobe_attach(skel, idx, offset), "uprobe_attach_after")) 849 847 return -1; 850 848 } 851 849 } 852 850 return 0; 853 851 } 854 852 853 + /* 854 + * We generate 16 consumer_testX functions that will have uprobe installed on 855 + * and will be called in separate threads. All function pointer are stored in 856 + * "consumers" section and each thread will pick one function based on index. 857 + */ 858 + 859 + extern const void *__start_consumers; 860 + 861 + #define __CONSUMER_TEST(func) \ 862 + noinline int func(struct uprobe_multi_consumers *skel, unsigned long before, \ 863 + unsigned long after, unsigned long offset) \ 864 + { \ 865 + return uprobe_consumer_test(skel, before, after, offset); \ 866 + } \ 867 + void *__ ## func __used __attribute__((section("consumers"))) = (void *) func; 868 + 869 + #define CONSUMER_TEST(func) __CONSUMER_TEST(func) 870 + 871 + #define C1 CONSUMER_TEST(__PASTE(consumer_test, __COUNTER__)) 872 + #define C4 C1 C1 C1 C1 873 + #define C16 C4 C4 C4 C4 874 + 875 + C16 876 + 877 + typedef int (*test_t)(struct uprobe_multi_consumers *, unsigned long, 878 + unsigned long, unsigned long); 879 + 855 880 static int consumer_test(struct uprobe_multi_consumers *skel, 856 - unsigned long before, unsigned long after) 881 + unsigned long before, unsigned long after, 882 + test_t test, unsigned long offset) 857 883 { 858 884 int err, idx, ret = -1; 859 885 ··· 890 860 /* 'before' is each, we attach uprobe for every set idx */ 891 861 for (idx = 0; idx < 4; idx++) { 892 862 if (test_bit(idx, before)) { 893 - if (!ASSERT_OK(uprobe_attach(skel, idx), "uprobe_attach_before")) 863 + if (!ASSERT_OK(uprobe_attach(skel, idx, offset), "uprobe_attach_before")) 894 864 goto cleanup; 895 865 } 896 866 } 897 867 898 - err = uprobe_consumer_test(skel, before, after); 868 + err = test(skel, before, after, offset); 899 869 if (!ASSERT_EQ(err, 0, "uprobe_consumer_test")) 900 870 goto cleanup; 901 871 ··· 964 934 return ret; 965 935 } 966 936 967 - static void test_consumers(void) 937 + #define CONSUMER_MAX 16 938 + 939 + /* 940 + * Each thread runs 1/16 of the load by running test for single 941 + * 'before' number (based on thread index) and full scale of 942 + * 'after' numbers. 943 + */ 944 + static void *consumer_thread(void *arg) 968 945 { 946 + unsigned long idx = (unsigned long) arg; 969 947 struct uprobe_multi_consumers *skel; 970 - int before, after; 948 + unsigned long offset; 949 + const void *func; 950 + int after; 971 951 972 952 skel = uprobe_multi_consumers__open_and_load(); 973 953 if (!ASSERT_OK_PTR(skel, "uprobe_multi_consumers__open_and_load")) 974 - return; 954 + return NULL; 955 + 956 + func = *((&__start_consumers) + idx); 957 + 958 + offset = get_uprobe_offset(func); 959 + if (!ASSERT_GE(offset, 0, "uprobe_offset")) 960 + goto out; 961 + 962 + for (after = 0; after < CONSUMER_MAX; after++) 963 + if (consumer_test(skel, idx, after, func, offset)) 964 + goto out; 965 + 966 + out: 967 + uprobe_multi_consumers__destroy(skel); 968 + return NULL; 969 + } 970 + 971 + 972 + static void test_consumers(void) 973 + { 974 + pthread_t pt[CONSUMER_MAX]; 975 + unsigned long idx; 976 + int err; 975 977 976 978 /* 977 979 * The idea of this test is to try all possible combinations of ··· 1044 982 * before/after bits. 1045 983 */ 1046 984 1047 - for (before = 0; before < 16; before++) { 1048 - for (after = 0; after < 16; after++) 1049 - if (consumer_test(skel, before, after)) 1050 - goto out; 985 + for (idx = 0; idx < CONSUMER_MAX; idx++) { 986 + err = pthread_create(&pt[idx], NULL, consumer_thread, (void *) idx); 987 + if (!ASSERT_OK(err, "pthread_create")) 988 + break; 1051 989 } 1052 990 1053 - out: 1054 - uprobe_multi_consumers__destroy(skel); 991 + while (idx) 992 + pthread_join(pt[--idx], NULL); 1055 993 } 1056 994 1057 995 static struct bpf_program *uprobe_multi_program(struct uprobe_multi_pid_filter *skel, int idx)