From 0a5c0de8b66f9789422f04de7304e374a2ea8df5 Mon Sep 17 00:00:00 2001 From: Stephen Veiss Date: Thu, 27 Apr 2023 15:53:32 -0700 Subject: selftests/bpf: Extract insert_test from parse_test_list Split the logic to insert new tests into test filter sets out from parse_test_list. Fix the subtest insertion logic to reuse an existing top-level test filter, which prevents the creation of duplicate top-level test filters each with a single subtest. Signed-off-by: Stephen Veiss Signed-off-by: Andrii Nakryiko Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20230427225333.3506052-2-sveiss@meta.com --- tools/testing/selftests/bpf/testing_helpers.c | 160 +++++++++++++++----------- 1 file changed, 95 insertions(+), 65 deletions(-) (limited to 'tools/testing/selftests/bpf/testing_helpers.c') diff --git a/tools/testing/selftests/bpf/testing_helpers.c b/tools/testing/selftests/bpf/testing_helpers.c index 0b5e0829e5be..fca617e87710 100644 --- a/tools/testing/selftests/bpf/testing_helpers.c +++ b/tools/testing/selftests/bpf/testing_helpers.c @@ -70,92 +70,122 @@ int parse_num_list(const char *s, bool **num_set, int *num_set_len) return 0; } -int parse_test_list(const char *s, - struct test_filter_set *set, - bool is_glob_pattern) +static int do_insert_test(struct test_filter_set *set, + char *test_str, + char *subtest_str) { - char *input, *state = NULL, *next; - struct test_filter *tmp, *tests = NULL; - int i, j, cnt = 0; + struct test_filter *tmp, *test; + char **ctmp; + int i; - input = strdup(s); - if (!input) + for (i = 0; i < set->cnt; i++) { + test = &set->tests[i]; + + if (strcmp(test_str, test->name) == 0) { + free(test_str); + goto subtest; + } + } + + tmp = realloc(set->tests, sizeof(*test) * (set->cnt + 1)); + if (!tmp) return -ENOMEM; - while ((next = strtok_r(state ? NULL : input, ",", &state))) { - char *subtest_str = strchr(next, '/'); - char *pattern = NULL; - int glob_chars = 0; + set->tests = tmp; + test = &set->tests[set->cnt]; - tmp = realloc(tests, sizeof(*tests) * (cnt + 1)); - if (!tmp) - goto err; - tests = tmp; + test->name = test_str; + test->subtests = NULL; + test->subtest_cnt = 0; - tests[cnt].subtest_cnt = 0; - tests[cnt].subtests = NULL; + set->cnt++; - if (is_glob_pattern) { - pattern = "%s"; - } else { - pattern = "*%s*"; - glob_chars = 2; - } +subtest: + if (!subtest_str) + return 0; - if (subtest_str) { - char **tmp_subtests = NULL; - int subtest_cnt = tests[cnt].subtest_cnt; - - *subtest_str = '\0'; - subtest_str += 1; - tmp_subtests = realloc(tests[cnt].subtests, - sizeof(*tmp_subtests) * - (subtest_cnt + 1)); - if (!tmp_subtests) - goto err; - tests[cnt].subtests = tmp_subtests; - - tests[cnt].subtests[subtest_cnt] = - malloc(strlen(subtest_str) + glob_chars + 1); - if (!tests[cnt].subtests[subtest_cnt]) - goto err; - sprintf(tests[cnt].subtests[subtest_cnt], - pattern, - subtest_str); - - tests[cnt].subtest_cnt++; + for (i = 0; i < test->subtest_cnt; i++) { + if (strcmp(subtest_str, test->subtests[i]) == 0) { + free(subtest_str); + return 0; } + } - tests[cnt].name = malloc(strlen(next) + glob_chars + 1); - if (!tests[cnt].name) - goto err; - sprintf(tests[cnt].name, pattern, next); + ctmp = realloc(test->subtests, + sizeof(*test->subtests) * (test->subtest_cnt + 1)); + if (!ctmp) + return -ENOMEM; + + test->subtests = ctmp; + test->subtests[test->subtest_cnt] = subtest_str; - cnt++; + test->subtest_cnt++; + + return 0; +} + +static int insert_test(struct test_filter_set *set, + char *test_spec, + bool is_glob_pattern) +{ + char *pattern, *subtest_str, *ext_test_str, *ext_subtest_str = NULL; + int glob_chars = 0; + + if (is_glob_pattern) { + pattern = "%s"; + } else { + pattern = "*%s*"; + glob_chars = 2; } - tmp = realloc(set->tests, sizeof(*tests) * (cnt + set->cnt)); - if (!tmp) + subtest_str = strchr(test_spec, '/'); + if (subtest_str) { + *subtest_str = '\0'; + subtest_str += 1; + } + + ext_test_str = malloc(strlen(test_spec) + glob_chars + 1); + if (!ext_test_str) goto err; - memcpy(tmp + set->cnt, tests, sizeof(*tests) * cnt); - set->tests = tmp; - set->cnt += cnt; + sprintf(ext_test_str, pattern, test_spec); - free(tests); - free(input); - return 0; + if (subtest_str) { + ext_subtest_str = malloc(strlen(subtest_str) + glob_chars + 1); + if (!ext_subtest_str) + goto err; + + sprintf(ext_subtest_str, pattern, subtest_str); + } + + return do_insert_test(set, ext_test_str, ext_subtest_str); err: - for (i = 0; i < cnt; i++) { - for (j = 0; j < tests[i].subtest_cnt; j++) - free(tests[i].subtests[j]); + free(ext_test_str); + free(ext_subtest_str); + + return -ENOMEM; +} - free(tests[i].name); +int parse_test_list(const char *s, + struct test_filter_set *set, + bool is_glob_pattern) +{ + char *input, *state = NULL, *test_spec; + int err = 0; + + input = strdup(s); + if (!input) + return -ENOMEM; + + while ((test_spec = strtok_r(state ? NULL : input, ",", &state))) { + err = insert_test(set, test_spec, is_glob_pattern); + if (err) + break; } - free(tests); + free(input); - return -ENOMEM; + return err; } __u32 link_info_prog_id(const struct bpf_link *link, struct bpf_link_info *info) -- cgit From 64276f01dce85f72853617e513622b5645497d81 Mon Sep 17 00:00:00 2001 From: Stephen Veiss Date: Thu, 27 Apr 2023 15:53:33 -0700 Subject: selftests/bpf: Test_progs can read test lists from file Improve test selection logic when using -a/-b/-d/-t options. The list of tests to include or exclude can now be read from a file, specified as @. The file contains one name (or wildcard pattern) per line, and comments beginning with # are ignored. These options can be passed multiple times to read more than one file. Signed-off-by: Stephen Veiss Signed-off-by: Andrii Nakryiko Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20230427225333.3506052-3-sveiss@meta.com --- tools/testing/selftests/bpf/testing_helpers.c | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'tools/testing/selftests/bpf/testing_helpers.c') diff --git a/tools/testing/selftests/bpf/testing_helpers.c b/tools/testing/selftests/bpf/testing_helpers.c index fca617e87710..dc9595ade8de 100644 --- a/tools/testing/selftests/bpf/testing_helpers.c +++ b/tools/testing/selftests/bpf/testing_helpers.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) /* Copyright (C) 2019 Netronome Systems, Inc. */ /* Copyright (C) 2020 Facebook, Inc. */ +#include #include #include #include @@ -167,6 +168,52 @@ err: return -ENOMEM; } +int parse_test_list_file(const char *path, + struct test_filter_set *set, + bool is_glob_pattern) +{ + char *buf = NULL, *capture_start, *capture_end, *scan_end; + size_t buflen = 0; + int err = 0; + FILE *f; + + f = fopen(path, "r"); + if (!f) { + err = -errno; + fprintf(stderr, "Failed to open '%s': %d\n", path, err); + return err; + } + + while (getline(&buf, &buflen, f) != -1) { + capture_start = buf; + + while (isspace(*capture_start)) + ++capture_start; + + capture_end = capture_start; + scan_end = capture_start; + + while (*scan_end && *scan_end != '#') { + if (!isspace(*scan_end)) + capture_end = scan_end; + + ++scan_end; + } + + if (capture_end == capture_start) + continue; + + *(++capture_end) = '\0'; + + err = insert_test(set, capture_start, is_glob_pattern); + if (err) + break; + } + + fclose(f); + return err; +} + int parse_test_list(const char *s, struct test_filter_set *set, bool is_glob_pattern) -- cgit From 45db310984bfea977177fb5fc0ea23ab430129bd Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 15 May 2023 15:37:49 +0200 Subject: selftests/bpf: Move test_progs helpers to testing_helpers object Moving test_progs helpers to testing_helpers object so they can be used from test_verifier in following changes. Also adding missing ifndef header guard to testing_helpers.h header. Using stderr instead of env.stderr because un/load_bpf_testmod helpers will be used outside test_progs. Also at the point of calling them in test_progs the std files are not hijacked yet and stderr is the same as env.stderr. Acked-by: David Vernet Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20230515133756.1658301-4-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/testing_helpers.c | 63 +++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) (limited to 'tools/testing/selftests/bpf/testing_helpers.c') diff --git a/tools/testing/selftests/bpf/testing_helpers.c b/tools/testing/selftests/bpf/testing_helpers.c index dc9595ade8de..648c7d3eb319 100644 --- a/tools/testing/selftests/bpf/testing_helpers.c +++ b/tools/testing/selftests/bpf/testing_helpers.c @@ -9,6 +9,7 @@ #include #include "test_progs.h" #include "testing_helpers.h" +#include int parse_num_list(const char *s, bool **num_set, int *num_set_len) { @@ -326,3 +327,65 @@ __u64 read_perf_max_sample_freq(void) fclose(f); return sample_freq; } + +static int finit_module(int fd, const char *param_values, int flags) +{ + return syscall(__NR_finit_module, fd, param_values, flags); +} + +static int delete_module(const char *name, int flags) +{ + return syscall(__NR_delete_module, name, flags); +} + +void unload_bpf_testmod(bool verbose) +{ + if (kern_sync_rcu()) + fprintf(stderr, "Failed to trigger kernel-side RCU sync!\n"); + if (delete_module("bpf_testmod", 0)) { + if (errno == ENOENT) { + if (verbose) + fprintf(stdout, "bpf_testmod.ko is already unloaded.\n"); + return; + } + fprintf(stderr, "Failed to unload bpf_testmod.ko from kernel: %d\n", -errno); + return; + } + if (verbose) + fprintf(stdout, "Successfully unloaded bpf_testmod.ko.\n"); +} + +int load_bpf_testmod(bool verbose) +{ + int fd; + + /* ensure previous instance of the module is unloaded */ + unload_bpf_testmod(verbose); + + if (verbose) + fprintf(stdout, "Loading bpf_testmod.ko...\n"); + + fd = open("bpf_testmod.ko", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Can't find bpf_testmod.ko kernel module: %d\n", -errno); + return -ENOENT; + } + if (finit_module(fd, "", 0)) { + fprintf(stderr, "Failed to load bpf_testmod.ko into the kernel: %d\n", -errno); + close(fd); + return -EINVAL; + } + close(fd); + + if (verbose) + fprintf(stdout, "Successfully loaded bpf_testmod.ko.\n"); + return 0; +} + +/* + * Trigger synchronize_rcu() in kernel. + */ +int kern_sync_rcu(void) +{ + return syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, 0, 0); +} -- cgit From d18decca69e36480247a73086336f4a44a08e36f Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 15 May 2023 15:37:50 +0200 Subject: selftests/bpf: Use only stdout in un/load_bpf_testmod functions We are about to use un/load_bpf_testmod functions in couple tests and it's better to print output to stdout, so it's aligned with tests ASSERT macros output, which use stdout as well. Acked-by: David Vernet Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20230515133756.1658301-5-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/testing_helpers.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tools/testing/selftests/bpf/testing_helpers.c') diff --git a/tools/testing/selftests/bpf/testing_helpers.c b/tools/testing/selftests/bpf/testing_helpers.c index 648c7d3eb319..f73bc88f3eb6 100644 --- a/tools/testing/selftests/bpf/testing_helpers.c +++ b/tools/testing/selftests/bpf/testing_helpers.c @@ -341,14 +341,14 @@ static int delete_module(const char *name, int flags) void unload_bpf_testmod(bool verbose) { if (kern_sync_rcu()) - fprintf(stderr, "Failed to trigger kernel-side RCU sync!\n"); + fprintf(stdout, "Failed to trigger kernel-side RCU sync!\n"); if (delete_module("bpf_testmod", 0)) { if (errno == ENOENT) { if (verbose) fprintf(stdout, "bpf_testmod.ko is already unloaded.\n"); return; } - fprintf(stderr, "Failed to unload bpf_testmod.ko from kernel: %d\n", -errno); + fprintf(stdout, "Failed to unload bpf_testmod.ko from kernel: %d\n", -errno); return; } if (verbose) @@ -367,11 +367,11 @@ int load_bpf_testmod(bool verbose) fd = open("bpf_testmod.ko", O_RDONLY); if (fd < 0) { - fprintf(stderr, "Can't find bpf_testmod.ko kernel module: %d\n", -errno); + fprintf(stdout, "Can't find bpf_testmod.ko kernel module: %d\n", -errno); return -ENOENT; } if (finit_module(fd, "", 0)) { - fprintf(stderr, "Failed to load bpf_testmod.ko into the kernel: %d\n", -errno); + fprintf(stdout, "Failed to load bpf_testmod.ko into the kernel: %d\n", -errno); close(fd); return -EINVAL; } -- cgit From b58f3f0e6f3cbea56be39d8fb190b37f049505bb Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 15 May 2023 15:37:51 +0200 Subject: selftests/bpf: Do not unload bpf_testmod in load_bpf_testmod Do not unload bpf_testmod in load_bpf_testmod, instead call unload_bpf_testmod separatelly. This way we will be able use un/load_bpf_testmod functions in other tests that un/load bpf_testmod module. Acked-by: David Vernet Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20230515133756.1658301-6-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/testing_helpers.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'tools/testing/selftests/bpf/testing_helpers.c') diff --git a/tools/testing/selftests/bpf/testing_helpers.c b/tools/testing/selftests/bpf/testing_helpers.c index f73bc88f3eb6..e01d7a62306c 100644 --- a/tools/testing/selftests/bpf/testing_helpers.c +++ b/tools/testing/selftests/bpf/testing_helpers.c @@ -359,9 +359,6 @@ int load_bpf_testmod(bool verbose) { int fd; - /* ensure previous instance of the module is unloaded */ - unload_bpf_testmod(verbose); - if (verbose) fprintf(stdout, "Loading bpf_testmod.ko...\n"); -- cgit From 11642eb92b3bc67171b23abff0d062758c5a4730 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 15 May 2023 15:37:52 +0200 Subject: selftests/bpf: Use un/load_bpf_testmod functions in tests Now that we have un/load_bpf_testmod helpers in testing_helpers.h, we can use it in other tests and save some lines. Acked-by: David Vernet Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20230515133756.1658301-7-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/testing_helpers.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'tools/testing/selftests/bpf/testing_helpers.c') diff --git a/tools/testing/selftests/bpf/testing_helpers.c b/tools/testing/selftests/bpf/testing_helpers.c index e01d7a62306c..8d994884c7b4 100644 --- a/tools/testing/selftests/bpf/testing_helpers.c +++ b/tools/testing/selftests/bpf/testing_helpers.c @@ -338,7 +338,7 @@ static int delete_module(const char *name, int flags) return syscall(__NR_delete_module, name, flags); } -void unload_bpf_testmod(bool verbose) +int unload_bpf_testmod(bool verbose) { if (kern_sync_rcu()) fprintf(stdout, "Failed to trigger kernel-side RCU sync!\n"); @@ -346,13 +346,14 @@ void unload_bpf_testmod(bool verbose) if (errno == ENOENT) { if (verbose) fprintf(stdout, "bpf_testmod.ko is already unloaded.\n"); - return; + return -1; } fprintf(stdout, "Failed to unload bpf_testmod.ko from kernel: %d\n", -errno); - return; + return -1; } if (verbose) fprintf(stdout, "Successfully unloaded bpf_testmod.ko.\n"); + return 0; } int load_bpf_testmod(bool verbose) -- cgit