diff options
author | Alexei Starovoitov <ast@kernel.org> | 2018-01-26 20:06:23 -0500 |
---|---|---|
committer | Alexei Starovoitov <ast@kernel.org> | 2018-01-26 20:06:24 -0500 |
commit | 8223967fe0b8eb2448cca5cfe3c64a0838e6f60d (patch) | |
tree | dc262d4c79d65428bb0ca56b377d732a1ff9438e /tools | |
parent | 1651e39e4a1adacd24e953aae46ba9b66d996035 (diff) | |
parent | af32efeede9e188fefe0af51d117c31cf281de65 (diff) |
Merge branch 'fix-lpm-map'
Yonghong Song says:
====================
A kernel page fault which happens in lpm map trie_get_next_key is reported
by syzbot and Eric. The issue was introduced by commit b471f2f1de8b
("bpf: implement MAP_GET_NEXT_KEY command for LPM_TRIE map").
Patch #1 fixed the issue in the kernel and patch #2 adds a multithreaded
test case in tools/testing/selftests/bpf/test_lpm_map.
====================
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/testing/selftests/bpf/Makefile | 2 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_lpm_map.c | 95 |
2 files changed, 96 insertions, 1 deletions
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 98688352208b..bf05bc5e36e5 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile | |||
@@ -11,7 +11,7 @@ ifneq ($(wildcard $(GENHDR)),) | |||
11 | endif | 11 | endif |
12 | 12 | ||
13 | CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include | 13 | CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include |
14 | LDLIBS += -lcap -lelf -lrt | 14 | LDLIBS += -lcap -lelf -lrt -lpthread |
15 | 15 | ||
16 | TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ | 16 | TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ |
17 | test_align test_verifier_log test_dev_cgroup test_tcpbpf_user | 17 | test_align test_verifier_log test_dev_cgroup test_tcpbpf_user |
diff --git a/tools/testing/selftests/bpf/test_lpm_map.c b/tools/testing/selftests/bpf/test_lpm_map.c index 081510853c6d..2be87e9ee28d 100644 --- a/tools/testing/selftests/bpf/test_lpm_map.c +++ b/tools/testing/selftests/bpf/test_lpm_map.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <errno.h> | 14 | #include <errno.h> |
15 | #include <inttypes.h> | 15 | #include <inttypes.h> |
16 | #include <linux/bpf.h> | 16 | #include <linux/bpf.h> |
17 | #include <pthread.h> | ||
17 | #include <stdio.h> | 18 | #include <stdio.h> |
18 | #include <stdlib.h> | 19 | #include <stdlib.h> |
19 | #include <string.h> | 20 | #include <string.h> |
@@ -641,6 +642,98 @@ static void test_lpm_get_next_key(void) | |||
641 | close(map_fd); | 642 | close(map_fd); |
642 | } | 643 | } |
643 | 644 | ||
645 | #define MAX_TEST_KEYS 4 | ||
646 | struct lpm_mt_test_info { | ||
647 | int cmd; /* 0: update, 1: delete, 2: lookup, 3: get_next_key */ | ||
648 | int iter; | ||
649 | int map_fd; | ||
650 | struct { | ||
651 | __u32 prefixlen; | ||
652 | __u32 data; | ||
653 | } key[MAX_TEST_KEYS]; | ||
654 | }; | ||
655 | |||
656 | static void *lpm_test_command(void *arg) | ||
657 | { | ||
658 | int i, j, ret, iter, key_size; | ||
659 | struct lpm_mt_test_info *info = arg; | ||
660 | struct bpf_lpm_trie_key *key_p; | ||
661 | |||
662 | key_size = sizeof(struct bpf_lpm_trie_key) + sizeof(__u32); | ||
663 | key_p = alloca(key_size); | ||
664 | for (iter = 0; iter < info->iter; iter++) | ||
665 | for (i = 0; i < MAX_TEST_KEYS; i++) { | ||
666 | /* first half of iterations in forward order, | ||
667 | * and second half in backward order. | ||
668 | */ | ||
669 | j = (iter < (info->iter / 2)) ? i : MAX_TEST_KEYS - i - 1; | ||
670 | key_p->prefixlen = info->key[j].prefixlen; | ||
671 | memcpy(key_p->data, &info->key[j].data, sizeof(__u32)); | ||
672 | if (info->cmd == 0) { | ||
673 | __u32 value = j; | ||
674 | /* update must succeed */ | ||
675 | assert(bpf_map_update_elem(info->map_fd, key_p, &value, 0) == 0); | ||
676 | } else if (info->cmd == 1) { | ||
677 | ret = bpf_map_delete_elem(info->map_fd, key_p); | ||
678 | assert(ret == 0 || errno == ENOENT); | ||
679 | } else if (info->cmd == 2) { | ||
680 | __u32 value; | ||
681 | ret = bpf_map_lookup_elem(info->map_fd, key_p, &value); | ||
682 | assert(ret == 0 || errno == ENOENT); | ||
683 | } else { | ||
684 | struct bpf_lpm_trie_key *next_key_p = alloca(key_size); | ||
685 | ret = bpf_map_get_next_key(info->map_fd, key_p, next_key_p); | ||
686 | assert(ret == 0 || errno == ENOENT || errno == ENOMEM); | ||
687 | } | ||
688 | } | ||
689 | |||
690 | // Pass successful exit info back to the main thread | ||
691 | pthread_exit((void *)info); | ||
692 | } | ||
693 | |||
694 | static void setup_lpm_mt_test_info(struct lpm_mt_test_info *info, int map_fd) | ||
695 | { | ||
696 | info->iter = 2000; | ||
697 | info->map_fd = map_fd; | ||
698 | info->key[0].prefixlen = 16; | ||
699 | inet_pton(AF_INET, "192.168.0.0", &info->key[0].data); | ||
700 | info->key[1].prefixlen = 24; | ||
701 | inet_pton(AF_INET, "192.168.0.0", &info->key[1].data); | ||
702 | info->key[2].prefixlen = 24; | ||
703 | inet_pton(AF_INET, "192.168.128.0", &info->key[2].data); | ||
704 | info->key[3].prefixlen = 24; | ||
705 | inet_pton(AF_INET, "192.168.1.0", &info->key[3].data); | ||
706 | } | ||
707 | |||
708 | static void test_lpm_multi_thread(void) | ||
709 | { | ||
710 | struct lpm_mt_test_info info[4]; | ||
711 | size_t key_size, value_size; | ||
712 | pthread_t thread_id[4]; | ||
713 | int i, map_fd; | ||
714 | void *ret; | ||
715 | |||
716 | /* create a trie */ | ||
717 | value_size = sizeof(__u32); | ||
718 | key_size = sizeof(struct bpf_lpm_trie_key) + value_size; | ||
719 | map_fd = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE, key_size, value_size, | ||
720 | 100, BPF_F_NO_PREALLOC); | ||
721 | |||
722 | /* create 4 threads to test update, delete, lookup and get_next_key */ | ||
723 | setup_lpm_mt_test_info(&info[0], map_fd); | ||
724 | for (i = 0; i < 4; i++) { | ||
725 | if (i != 0) | ||
726 | memcpy(&info[i], &info[0], sizeof(info[i])); | ||
727 | info[i].cmd = i; | ||
728 | assert(pthread_create(&thread_id[i], NULL, &lpm_test_command, &info[i]) == 0); | ||
729 | } | ||
730 | |||
731 | for (i = 0; i < 4; i++) | ||
732 | assert(pthread_join(thread_id[i], &ret) == 0 && ret == (void *)&info[i]); | ||
733 | |||
734 | close(map_fd); | ||
735 | } | ||
736 | |||
644 | int main(void) | 737 | int main(void) |
645 | { | 738 | { |
646 | struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY }; | 739 | struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY }; |
@@ -667,6 +760,8 @@ int main(void) | |||
667 | 760 | ||
668 | test_lpm_get_next_key(); | 761 | test_lpm_get_next_key(); |
669 | 762 | ||
763 | test_lpm_multi_thread(); | ||
764 | |||
670 | printf("test_lpm: OK\n"); | 765 | printf("test_lpm: OK\n"); |
671 | return 0; | 766 | return 0; |
672 | } | 767 | } |