The benchmark runs a loop 5000 times. In the loop it reads the file name from kprobe argument into stack by using bpf_probe_read_kernel_str(), and compares the file name with a target character or string. Three cases are compared: only compare one character, compare the whole string by a home-made strncmp() and compare the whole string by bpf_strcmp(). The following is the result: x86-64 host: one character: 2613499 ns whole str by strncmp: 2920348 ns whole str by helper: 2779332 ns arm64 host: one character: 3898867 ns whole str by strncmp: 4396787 ns whole str by helper: 3968113 ns Compared with home-made strncmp, the performance of bpf_strncmp helper improves 80% under x86-64 and 600% under arm64. The big performance win on arm64 may comes from its arch-optimized strncmp(). Signed-off-by: Hou Tao <houtao1@xxxxxxxxxx> --- .../bpf/prog_tests/test_strncmp_helper.c | 75 ++++++++++++ .../selftests/bpf/progs/strncmp_helper.c | 109 ++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/test_strncmp_helper.c create mode 100644 tools/testing/selftests/bpf/progs/strncmp_helper.c diff --git a/tools/testing/selftests/bpf/prog_tests/test_strncmp_helper.c b/tools/testing/selftests/bpf/prog_tests/test_strncmp_helper.c new file mode 100644 index 000000000000..1b48ef3402ff --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/test_strncmp_helper.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2021. Huawei Technologies Co., Ltd */ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <test_progs.h> + +#include "strncmp_helper.skel.h" + +static void run_strncmp_bench(struct strncmp_helper *skel, const char *name, + int fd, int loop) +{ + struct timespec begin, end; + struct stat stat; + double nsecs; + int i; + + skel->bss->equal = 0; + clock_gettime(CLOCK_MONOTONIC, &begin); + for (i = 0; i < loop; i++) + fstat(fd, &stat); + clock_gettime(CLOCK_MONOTONIC, &end); + + nsecs = (end.tv_sec - begin.tv_sec) * 1e9 + (end.tv_nsec - begin.tv_nsec); + fprintf(stdout, "%s: loop %d nsecs %.0f\n", name, loop, nsecs); + fprintf(stdout, "equal nr %u\n", skel->bss->equal); +} + +void test_test_strncmp_helper(void) +{ + const char *fpath = "/tmp/1234123412341234123412341234123412341234"; + struct strncmp_helper *skel; + struct bpf_link *link; + int fd, loop; + + skel = strncmp_helper__open_and_load(); + if (!ASSERT_OK_PTR(skel, "helper load")) + return; + + fd = open(fpath, O_CREAT | O_RDONLY, 0644); + if (!ASSERT_GE(fd, 0, "create file")) + goto close_prog; + + loop = 5000; + skel->bss->pid = getpid(); + + link = bpf_program__attach(skel->progs.vfs_getattr_nocmp); + if (!ASSERT_EQ(libbpf_get_error(link), 0, "attach nocmp")) + goto clean_file; + + run_strncmp_bench(skel, "nocmp", fd, loop); + bpf_link__destroy(link); + + link = bpf_program__attach(skel->progs.vfs_getattr_cmp); + if (!ASSERT_EQ(libbpf_get_error(link), 0, "attach cmp")) + goto clean_file; + + run_strncmp_bench(skel, "cmp", fd, loop); + bpf_link__destroy(link); + + link = bpf_program__attach(skel->progs.vfs_getattr_cmp_v2); + if (!ASSERT_EQ(libbpf_get_error(link), 0, "attach cmp_v2")) + goto clean_file; + + run_strncmp_bench(skel, "cmp_v2", fd, loop); + bpf_link__destroy(link); + +clean_file: + close(fd); + unlink(fpath); +close_prog: + strncmp_helper__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/strncmp_helper.c b/tools/testing/selftests/bpf/progs/strncmp_helper.c new file mode 100644 index 000000000000..f212c1f50099 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/strncmp_helper.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2021. Huawei Technologies Co., Ltd */ +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/bpf.h> +#include <bpf/bpf_core_read.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> + +#define TARGET_FILE_NAME "1234123412341234123412341234123412341234" + +char _license[] SEC("license") = "GPL"; + +__u32 pid = 0; +__u32 equal = 0; + +struct qstr { + const char *name; +} __attribute__((preserve_access_index)); + +struct dentry { + struct qstr d_name; +} __attribute__((preserve_access_index)); + +struct path { + struct dentry *dentry; +} __attribute__((preserve_access_index)); + +static __always_inline int read_file_name(const struct path *path, char *buf, + unsigned int len) +{ + const char *name; + int err; + + if ((bpf_get_current_pid_tgid() >> 32) != pid) + return -1; + + name = BPF_CORE_READ(path, dentry, d_name.name); + err = bpf_probe_read_kernel_str(buf, len, name); + if (err <= 0) + return -1; + + return err; +} + +static __always_inline int local_strncmp(const char *s1, const char *s2, + unsigned int sz) +{ + int ret = 0; + unsigned int i; + + for (i = 0; i < sz; i++) { + ret = s1[i] - s2[i]; + if (ret || !s1[i]) + break; + } + + return ret; +} + +SEC("kprobe/vfs_getattr") +int BPF_KPROBE(vfs_getattr_nocmp, const struct path *path) +{ + char buf[64] = {0}; + int err; + + err = read_file_name(path, buf, sizeof(buf)); + if (err < 0) + return 0; + + if (buf[0] == '1') + equal++; + + return 0; +} + +SEC("kprobe/vfs_getattr") +int BPF_KPROBE(vfs_getattr_cmp, const struct path *path) +{ + char buf[64] = {0}; + int err; + + err = read_file_name(path, buf, sizeof(buf)); + if (err < 0) + return 0; + + err = local_strncmp(TARGET_FILE_NAME, buf, sizeof(buf)); + if (!err) + equal++; + + return 0; +} + +SEC("kprobe/vfs_getattr") +int BPF_KPROBE(vfs_getattr_cmp_v2, const struct path *path) +{ + char buf[64] = {0}; + int err; + + err = read_file_name(path, buf, sizeof(buf)); + if (err < 0) + return 0; + + err = bpf_strncmp(TARGET_FILE_NAME, buf, sizeof(buf)); + if (!err) + equal++; + + return 0; +} -- 2.29.2