tools/bpf: add a multithreaded stress test in bpf selftests test_lpm_map
authorYonghong Song <yhs@fb.com>
Fri, 26 Jan 2018 23:06:08 +0000 (15:06 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Sat, 27 Jan 2018 01:06:22 +0000 (17:06 -0800)
The new test will spawn four threads, doing map update, delete, lookup
and get_next_key in parallel. It is able to reproduce the issue in the
previous commit found by syzbot and Eric Dumazet.

Signed-off-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/test_lpm_map.c

index 9868835..bf05bc5 100644 (file)
@@ -11,7 +11,7 @@ ifneq ($(wildcard $(GENHDR)),)
 endif
 
 CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include
-LDLIBS += -lcap -lelf -lrt
+LDLIBS += -lcap -lelf -lrt -lpthread
 
 TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
        test_align test_verifier_log test_dev_cgroup test_tcpbpf_user
index 0815108..2be87e9 100644 (file)
@@ -14,6 +14,7 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <linux/bpf.h>
+#include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -641,6 +642,98 @@ static void test_lpm_get_next_key(void)
        close(map_fd);
 }
 
+#define MAX_TEST_KEYS  4
+struct lpm_mt_test_info {
+       int cmd; /* 0: update, 1: delete, 2: lookup, 3: get_next_key */
+       int iter;
+       int map_fd;
+       struct {
+               __u32 prefixlen;
+               __u32 data;
+       } key[MAX_TEST_KEYS];
+};
+
+static void *lpm_test_command(void *arg)
+{
+       int i, j, ret, iter, key_size;
+       struct lpm_mt_test_info *info = arg;
+       struct bpf_lpm_trie_key *key_p;
+
+       key_size = sizeof(struct bpf_lpm_trie_key) + sizeof(__u32);
+       key_p = alloca(key_size);
+       for (iter = 0; iter < info->iter; iter++)
+               for (i = 0; i < MAX_TEST_KEYS; i++) {
+                       /* first half of iterations in forward order,
+                        * and second half in backward order.
+                        */
+                       j = (iter < (info->iter / 2)) ? i : MAX_TEST_KEYS - i - 1;
+                       key_p->prefixlen = info->key[j].prefixlen;
+                       memcpy(key_p->data, &info->key[j].data, sizeof(__u32));
+                       if (info->cmd == 0) {
+                               __u32 value = j;
+                               /* update must succeed */
+                               assert(bpf_map_update_elem(info->map_fd, key_p, &value, 0) == 0);
+                       } else if (info->cmd == 1) {
+                               ret = bpf_map_delete_elem(info->map_fd, key_p);
+                               assert(ret == 0 || errno == ENOENT);
+                       } else if (info->cmd == 2) {
+                               __u32 value;
+                               ret = bpf_map_lookup_elem(info->map_fd, key_p, &value);
+                               assert(ret == 0 || errno == ENOENT);
+                       } else {
+                               struct bpf_lpm_trie_key *next_key_p = alloca(key_size);
+                               ret = bpf_map_get_next_key(info->map_fd, key_p, next_key_p);
+                               assert(ret == 0 || errno == ENOENT || errno == ENOMEM);
+                       }
+               }
+
+       // Pass successful exit info back to the main thread
+       pthread_exit((void *)info);
+}
+
+static void setup_lpm_mt_test_info(struct lpm_mt_test_info *info, int map_fd)
+{
+       info->iter = 2000;
+       info->map_fd = map_fd;
+       info->key[0].prefixlen = 16;
+       inet_pton(AF_INET, "192.168.0.0", &info->key[0].data);
+       info->key[1].prefixlen = 24;
+       inet_pton(AF_INET, "192.168.0.0", &info->key[1].data);
+       info->key[2].prefixlen = 24;
+       inet_pton(AF_INET, "192.168.128.0", &info->key[2].data);
+       info->key[3].prefixlen = 24;
+       inet_pton(AF_INET, "192.168.1.0", &info->key[3].data);
+}
+
+static void test_lpm_multi_thread(void)
+{
+       struct lpm_mt_test_info info[4];
+       size_t key_size, value_size;
+       pthread_t thread_id[4];
+       int i, map_fd;
+       void *ret;
+
+       /* create a trie */
+       value_size = sizeof(__u32);
+       key_size = sizeof(struct bpf_lpm_trie_key) + value_size;
+       map_fd = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE, key_size, value_size,
+                               100, BPF_F_NO_PREALLOC);
+
+       /* create 4 threads to test update, delete, lookup and get_next_key */
+       setup_lpm_mt_test_info(&info[0], map_fd);
+       for (i = 0; i < 4; i++) {
+               if (i != 0)
+                       memcpy(&info[i], &info[0], sizeof(info[i]));
+               info[i].cmd = i;
+               assert(pthread_create(&thread_id[i], NULL, &lpm_test_command, &info[i]) == 0);
+       }
+
+       for (i = 0; i < 4; i++)
+               assert(pthread_join(thread_id[i], &ret) == 0 && ret == (void *)&info[i]);
+
+       close(map_fd);
+}
+
 int main(void)
 {
        struct rlimit limit  = { RLIM_INFINITY, RLIM_INFINITY };
@@ -667,6 +760,8 @@ int main(void)
 
        test_lpm_get_next_key();
 
+       test_lpm_multi_thread();
+
        printf("test_lpm: OK\n");
        return 0;
 }