diff options
| author | Martin KaFai Lau <kafai@fb.com> | 2017-03-22 13:00:35 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2017-03-22 18:45:45 -0400 |
| commit | fb30d4b71214aa1811e997f8f753b14b46d5b912 (patch) | |
| tree | a546eefbd355792aa544c0f220b38bab4493b1aa /samples | |
| parent | bcc6b1b7ebf857a9fe56202e2be3361131588c15 (diff) | |
bpf: Add tests for map-in-map
Test cases for array of maps and hash of maps.
Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'samples')
| -rw-r--r-- | samples/bpf/Makefile | 4 | ||||
| -rw-r--r-- | samples/bpf/bpf_helpers.h | 1 | ||||
| -rw-r--r-- | samples/bpf/bpf_load.c | 22 | ||||
| -rw-r--r-- | samples/bpf/test_map_in_map_kern.c | 173 | ||||
| -rw-r--r-- | samples/bpf/test_map_in_map_user.c | 116 |
5 files changed, 311 insertions, 5 deletions
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 09e9d535bd74..91c1d616d975 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile | |||
| @@ -34,6 +34,7 @@ hostprogs-y += sampleip | |||
| 34 | hostprogs-y += tc_l2_redirect | 34 | hostprogs-y += tc_l2_redirect |
| 35 | hostprogs-y += lwt_len_hist | 35 | hostprogs-y += lwt_len_hist |
| 36 | hostprogs-y += xdp_tx_iptunnel | 36 | hostprogs-y += xdp_tx_iptunnel |
| 37 | hostprogs-y += test_map_in_map | ||
| 37 | 38 | ||
| 38 | # Libbpf dependencies | 39 | # Libbpf dependencies |
| 39 | LIBBPF := ../../tools/lib/bpf/bpf.o | 40 | LIBBPF := ../../tools/lib/bpf/bpf.o |
| @@ -72,6 +73,7 @@ sampleip-objs := bpf_load.o $(LIBBPF) sampleip_user.o | |||
| 72 | tc_l2_redirect-objs := bpf_load.o $(LIBBPF) tc_l2_redirect_user.o | 73 | tc_l2_redirect-objs := bpf_load.o $(LIBBPF) tc_l2_redirect_user.o |
| 73 | lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o | 74 | lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o |
| 74 | xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o | 75 | xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o |
| 76 | test_map_in_map-objs := bpf_load.o $(LIBBPF) test_map_in_map_user.o | ||
| 75 | 77 | ||
| 76 | # Tell kbuild to always build the programs | 78 | # Tell kbuild to always build the programs |
| 77 | always := $(hostprogs-y) | 79 | always := $(hostprogs-y) |
| @@ -105,6 +107,7 @@ always += trace_event_kern.o | |||
| 105 | always += sampleip_kern.o | 107 | always += sampleip_kern.o |
| 106 | always += lwt_len_hist_kern.o | 108 | always += lwt_len_hist_kern.o |
| 107 | always += xdp_tx_iptunnel_kern.o | 109 | always += xdp_tx_iptunnel_kern.o |
| 110 | always += test_map_in_map_kern.o | ||
| 108 | 111 | ||
| 109 | HOSTCFLAGS += -I$(objtree)/usr/include | 112 | HOSTCFLAGS += -I$(objtree)/usr/include |
| 110 | HOSTCFLAGS += -I$(srctree)/tools/lib/ | 113 | HOSTCFLAGS += -I$(srctree)/tools/lib/ |
| @@ -139,6 +142,7 @@ HOSTLOADLIBES_sampleip += -lelf | |||
| 139 | HOSTLOADLIBES_tc_l2_redirect += -l elf | 142 | HOSTLOADLIBES_tc_l2_redirect += -l elf |
| 140 | HOSTLOADLIBES_lwt_len_hist += -l elf | 143 | HOSTLOADLIBES_lwt_len_hist += -l elf |
| 141 | HOSTLOADLIBES_xdp_tx_iptunnel += -lelf | 144 | HOSTLOADLIBES_xdp_tx_iptunnel += -lelf |
| 145 | HOSTLOADLIBES_test_map_in_map += -lelf | ||
| 142 | 146 | ||
| 143 | # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: | 147 | # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: |
| 144 | # make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang | 148 | # make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang |
diff --git a/samples/bpf/bpf_helpers.h b/samples/bpf/bpf_helpers.h index faaffe2e139a..52de9d88c021 100644 --- a/samples/bpf/bpf_helpers.h +++ b/samples/bpf/bpf_helpers.h | |||
| @@ -80,6 +80,7 @@ struct bpf_map_def { | |||
| 80 | unsigned int value_size; | 80 | unsigned int value_size; |
| 81 | unsigned int max_entries; | 81 | unsigned int max_entries; |
| 82 | unsigned int map_flags; | 82 | unsigned int map_flags; |
| 83 | unsigned int inner_map_idx; | ||
| 83 | }; | 84 | }; |
| 84 | 85 | ||
| 85 | static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = | 86 | static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = |
diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c index b86ee54da2d1..dcdce1270d38 100644 --- a/samples/bpf/bpf_load.c +++ b/samples/bpf/bpf_load.c | |||
| @@ -43,6 +43,7 @@ struct bpf_map_def { | |||
| 43 | unsigned int value_size; | 43 | unsigned int value_size; |
| 44 | unsigned int max_entries; | 44 | unsigned int max_entries; |
| 45 | unsigned int map_flags; | 45 | unsigned int map_flags; |
| 46 | unsigned int inner_map_idx; | ||
| 46 | }; | 47 | }; |
| 47 | 48 | ||
| 48 | static int populate_prog_array(const char *event, int prog_fd) | 49 | static int populate_prog_array(const char *event, int prog_fd) |
| @@ -198,11 +199,22 @@ static int load_maps(struct bpf_map_def *maps, int len) | |||
| 198 | 199 | ||
| 199 | for (i = 0; i < len / sizeof(struct bpf_map_def); i++) { | 200 | for (i = 0; i < len / sizeof(struct bpf_map_def); i++) { |
| 200 | 201 | ||
| 201 | map_fd[i] = bpf_create_map(maps[i].type, | 202 | if (maps[i].type == BPF_MAP_TYPE_ARRAY_OF_MAPS || |
| 202 | maps[i].key_size, | 203 | maps[i].type == BPF_MAP_TYPE_HASH_OF_MAPS) { |
| 203 | maps[i].value_size, | 204 | int inner_map_fd = map_fd[maps[i].inner_map_idx]; |
| 204 | maps[i].max_entries, | 205 | |
| 205 | maps[i].map_flags); | 206 | map_fd[i] = bpf_create_map_in_map(maps[i].type, |
| 207 | maps[i].key_size, | ||
| 208 | inner_map_fd, | ||
| 209 | maps[i].max_entries, | ||
| 210 | maps[i].map_flags); | ||
| 211 | } else { | ||
| 212 | map_fd[i] = bpf_create_map(maps[i].type, | ||
| 213 | maps[i].key_size, | ||
| 214 | maps[i].value_size, | ||
| 215 | maps[i].max_entries, | ||
| 216 | maps[i].map_flags); | ||
| 217 | } | ||
| 206 | if (map_fd[i] < 0) { | 218 | if (map_fd[i] < 0) { |
| 207 | printf("failed to create a map: %d %s\n", | 219 | printf("failed to create a map: %d %s\n", |
| 208 | errno, strerror(errno)); | 220 | errno, strerror(errno)); |
diff --git a/samples/bpf/test_map_in_map_kern.c b/samples/bpf/test_map_in_map_kern.c new file mode 100644 index 000000000000..42c44d091dd1 --- /dev/null +++ b/samples/bpf/test_map_in_map_kern.c | |||
| @@ -0,0 +1,173 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2017 Facebook | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or | ||
| 5 | * modify it under the terms of version 2 of the GNU General Public | ||
| 6 | * License as published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | #define KBUILD_MODNAME "foo" | ||
| 9 | #include <linux/ptrace.h> | ||
| 10 | #include <linux/version.h> | ||
| 11 | #include <uapi/linux/bpf.h> | ||
| 12 | #include <uapi/linux/in6.h> | ||
| 13 | #include "bpf_helpers.h" | ||
| 14 | |||
| 15 | #define MAX_NR_PORTS 65536 | ||
| 16 | |||
| 17 | /* map #0 */ | ||
| 18 | struct bpf_map_def SEC("maps") port_a = { | ||
| 19 | .type = BPF_MAP_TYPE_ARRAY, | ||
| 20 | .key_size = sizeof(u32), | ||
| 21 | .value_size = sizeof(int), | ||
| 22 | .max_entries = MAX_NR_PORTS, | ||
| 23 | }; | ||
| 24 | |||
| 25 | /* map #1 */ | ||
| 26 | struct bpf_map_def SEC("maps") port_h = { | ||
| 27 | .type = BPF_MAP_TYPE_HASH, | ||
| 28 | .key_size = sizeof(u32), | ||
| 29 | .value_size = sizeof(int), | ||
| 30 | .max_entries = 1, | ||
| 31 | }; | ||
| 32 | |||
| 33 | /* map #2 */ | ||
| 34 | struct bpf_map_def SEC("maps") reg_result_h = { | ||
| 35 | .type = BPF_MAP_TYPE_HASH, | ||
| 36 | .key_size = sizeof(u32), | ||
| 37 | .value_size = sizeof(int), | ||
| 38 | .max_entries = 1, | ||
| 39 | }; | ||
| 40 | |||
| 41 | /* map #3 */ | ||
| 42 | struct bpf_map_def SEC("maps") inline_result_h = { | ||
| 43 | .type = BPF_MAP_TYPE_HASH, | ||
| 44 | .key_size = sizeof(u32), | ||
| 45 | .value_size = sizeof(int), | ||
| 46 | .max_entries = 1, | ||
| 47 | }; | ||
| 48 | |||
| 49 | /* map #4 */ /* Test case #0 */ | ||
| 50 | struct bpf_map_def SEC("maps") a_of_port_a = { | ||
| 51 | .type = BPF_MAP_TYPE_ARRAY_OF_MAPS, | ||
| 52 | .key_size = sizeof(u32), | ||
| 53 | .inner_map_idx = 0, /* map_fd[0] is port_a */ | ||
| 54 | .max_entries = MAX_NR_PORTS, | ||
| 55 | }; | ||
| 56 | |||
| 57 | /* map #5 */ /* Test case #1 */ | ||
| 58 | struct bpf_map_def SEC("maps") h_of_port_a = { | ||
| 59 | .type = BPF_MAP_TYPE_HASH_OF_MAPS, | ||
| 60 | .key_size = sizeof(u32), | ||
| 61 | .inner_map_idx = 0, /* map_fd[0] is port_a */ | ||
| 62 | .max_entries = 1, | ||
| 63 | }; | ||
| 64 | |||
| 65 | /* map #6 */ /* Test case #2 */ | ||
| 66 | struct bpf_map_def SEC("maps") h_of_port_h = { | ||
| 67 | .type = BPF_MAP_TYPE_HASH_OF_MAPS, | ||
| 68 | .key_size = sizeof(u32), | ||
| 69 | .inner_map_idx = 1, /* map_fd[1] is port_h */ | ||
| 70 | .max_entries = 1, | ||
| 71 | }; | ||
| 72 | |||
| 73 | static __always_inline int do_reg_lookup(void *inner_map, u32 port) | ||
| 74 | { | ||
| 75 | int *result; | ||
| 76 | |||
| 77 | result = bpf_map_lookup_elem(inner_map, &port); | ||
| 78 | return result ? *result : -ENOENT; | ||
| 79 | } | ||
| 80 | |||
| 81 | static __always_inline int do_inline_array_lookup(void *inner_map, u32 port) | ||
| 82 | { | ||
| 83 | int *result; | ||
| 84 | |||
| 85 | if (inner_map != &port_a) | ||
| 86 | return -EINVAL; | ||
| 87 | |||
| 88 | result = bpf_map_lookup_elem(&port_a, &port); | ||
| 89 | return result ? *result : -ENOENT; | ||
| 90 | } | ||
| 91 | |||
| 92 | static __always_inline int do_inline_hash_lookup(void *inner_map, u32 port) | ||
| 93 | { | ||
| 94 | int *result; | ||
| 95 | |||
| 96 | if (inner_map != &port_h) | ||
| 97 | return -EINVAL; | ||
| 98 | |||
| 99 | result = bpf_map_lookup_elem(&port_h, &port); | ||
| 100 | return result ? *result : -ENOENT; | ||
| 101 | } | ||
| 102 | |||
| 103 | SEC("kprobe/sys_connect") | ||
| 104 | int trace_sys_connect(struct pt_regs *ctx) | ||
| 105 | { | ||
| 106 | struct sockaddr_in6 *in6; | ||
| 107 | u16 test_case, port, dst6[8]; | ||
| 108 | int addrlen, ret, inline_ret, ret_key = 0; | ||
| 109 | u32 port_key; | ||
| 110 | void *outer_map, *inner_map; | ||
| 111 | bool inline_hash = false; | ||
| 112 | |||
| 113 | in6 = (struct sockaddr_in6 *)PT_REGS_PARM2(ctx); | ||
| 114 | addrlen = (int)PT_REGS_PARM3(ctx); | ||
| 115 | |||
| 116 | if (addrlen != sizeof(*in6)) | ||
| 117 | return 0; | ||
| 118 | |||
| 119 | ret = bpf_probe_read(dst6, sizeof(dst6), &in6->sin6_addr); | ||
| 120 | if (ret) { | ||
| 121 | inline_ret = ret; | ||
| 122 | goto done; | ||
| 123 | } | ||
| 124 | |||
| 125 | if (dst6[0] != 0xdead || dst6[1] != 0xbeef) | ||
| 126 | return 0; | ||
| 127 | |||
| 128 | test_case = dst6[7]; | ||
| 129 | |||
| 130 | ret = bpf_probe_read(&port, sizeof(port), &in6->sin6_port); | ||
| 131 | if (ret) { | ||
| 132 | inline_ret = ret; | ||
| 133 | goto done; | ||
| 134 | } | ||
| 135 | |||
| 136 | port_key = port; | ||
| 137 | |||
| 138 | ret = -ENOENT; | ||
| 139 | if (test_case == 0) { | ||
| 140 | outer_map = &a_of_port_a; | ||
| 141 | } else if (test_case == 1) { | ||
| 142 | outer_map = &h_of_port_a; | ||
| 143 | } else if (test_case == 2) { | ||
| 144 | outer_map = &h_of_port_h; | ||
| 145 | } else { | ||
| 146 | ret = __LINE__; | ||
| 147 | inline_ret = ret; | ||
| 148 | goto done; | ||
| 149 | } | ||
| 150 | |||
| 151 | inner_map = bpf_map_lookup_elem(outer_map, &port_key); | ||
| 152 | if (!inner_map) { | ||
| 153 | ret = __LINE__; | ||
| 154 | inline_ret = ret; | ||
| 155 | goto done; | ||
| 156 | } | ||
| 157 | |||
| 158 | ret = do_reg_lookup(inner_map, port_key); | ||
| 159 | |||
| 160 | if (test_case == 0 || test_case == 1) | ||
| 161 | inline_ret = do_inline_array_lookup(inner_map, port_key); | ||
| 162 | else | ||
| 163 | inline_ret = do_inline_hash_lookup(inner_map, port_key); | ||
| 164 | |||
| 165 | done: | ||
| 166 | bpf_map_update_elem(®_result_h, &ret_key, &ret, BPF_ANY); | ||
| 167 | bpf_map_update_elem(&inline_result_h, &ret_key, &inline_ret, BPF_ANY); | ||
| 168 | |||
| 169 | return 0; | ||
| 170 | } | ||
| 171 | |||
| 172 | char _license[] SEC("license") = "GPL"; | ||
| 173 | u32 _version SEC("version") = LINUX_VERSION_CODE; | ||
diff --git a/samples/bpf/test_map_in_map_user.c b/samples/bpf/test_map_in_map_user.c new file mode 100644 index 000000000000..f62fdc2bd428 --- /dev/null +++ b/samples/bpf/test_map_in_map_user.c | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2017 Facebook | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or | ||
| 5 | * modify it under the terms of version 2 of the GNU General Public | ||
| 6 | * License as published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | #include <sys/resource.h> | ||
| 9 | #include <sys/socket.h> | ||
| 10 | #include <arpa/inet.h> | ||
| 11 | #include <stdint.h> | ||
| 12 | #include <assert.h> | ||
| 13 | #include <errno.h> | ||
| 14 | #include <stdlib.h> | ||
| 15 | #include <stdio.h> | ||
| 16 | #include "libbpf.h" | ||
| 17 | #include "bpf_load.h" | ||
| 18 | |||
| 19 | #define PORT_A (map_fd[0]) | ||
| 20 | #define PORT_H (map_fd[1]) | ||
| 21 | #define REG_RESULT_H (map_fd[2]) | ||
| 22 | #define INLINE_RESULT_H (map_fd[3]) | ||
| 23 | #define A_OF_PORT_A (map_fd[4]) /* Test case #0 */ | ||
| 24 | #define H_OF_PORT_A (map_fd[5]) /* Test case #1 */ | ||
| 25 | #define H_OF_PORT_H (map_fd[6]) /* Test case #2 */ | ||
| 26 | |||
| 27 | static const char * const test_names[] = { | ||
| 28 | "Array of Array", | ||
| 29 | "Hash of Array", | ||
| 30 | "Hash of Hash", | ||
| 31 | }; | ||
| 32 | |||
| 33 | #define NR_TESTS (sizeof(test_names) / sizeof(*test_names)) | ||
| 34 | |||
| 35 | static void populate_map(uint32_t port_key, int magic_result) | ||
| 36 | { | ||
| 37 | int ret; | ||
| 38 | |||
| 39 | ret = bpf_map_update_elem(PORT_A, &port_key, &magic_result, BPF_ANY); | ||
| 40 | assert(!ret); | ||
| 41 | |||
| 42 | ret = bpf_map_update_elem(PORT_H, &port_key, &magic_result, | ||
| 43 | BPF_NOEXIST); | ||
| 44 | assert(!ret); | ||
| 45 | |||
| 46 | ret = bpf_map_update_elem(A_OF_PORT_A, &port_key, &PORT_A, BPF_ANY); | ||
| 47 | assert(!ret); | ||
| 48 | |||
| 49 | ret = bpf_map_update_elem(H_OF_PORT_A, &port_key, &PORT_A, BPF_NOEXIST); | ||
| 50 | assert(!ret); | ||
| 51 | |||
| 52 | ret = bpf_map_update_elem(H_OF_PORT_H, &port_key, &PORT_H, BPF_NOEXIST); | ||
| 53 | assert(!ret); | ||
| 54 | } | ||
| 55 | |||
| 56 | static void test_map_in_map(void) | ||
| 57 | { | ||
| 58 | struct sockaddr_in6 in6 = { .sin6_family = AF_INET6 }; | ||
| 59 | uint32_t result_key = 0, port_key; | ||
| 60 | int result, inline_result; | ||
| 61 | int magic_result = 0xfaceb00c; | ||
| 62 | int ret; | ||
| 63 | int i; | ||
| 64 | |||
| 65 | port_key = rand() & 0x00FF; | ||
| 66 | populate_map(port_key, magic_result); | ||
| 67 | |||
| 68 | in6.sin6_addr.s6_addr16[0] = 0xdead; | ||
| 69 | in6.sin6_addr.s6_addr16[1] = 0xbeef; | ||
| 70 | in6.sin6_port = port_key; | ||
| 71 | |||
| 72 | for (i = 0; i < NR_TESTS; i++) { | ||
| 73 | printf("%s: ", test_names[i]); | ||
| 74 | |||
| 75 | in6.sin6_addr.s6_addr16[7] = i; | ||
| 76 | ret = connect(-1, (struct sockaddr *)&in6, sizeof(in6)); | ||
| 77 | assert(ret == -1 && errno == EBADF); | ||
| 78 | |||
| 79 | ret = bpf_map_lookup_elem(REG_RESULT_H, &result_key, &result); | ||
| 80 | assert(!ret); | ||
| 81 | |||
| 82 | ret = bpf_map_lookup_elem(INLINE_RESULT_H, &result_key, | ||
| 83 | &inline_result); | ||
| 84 | assert(!ret); | ||
| 85 | |||
| 86 | if (result != magic_result || inline_result != magic_result) { | ||
| 87 | printf("Error. result:%d inline_result:%d\n", | ||
| 88 | result, inline_result); | ||
| 89 | exit(1); | ||
| 90 | } | ||
| 91 | |||
| 92 | bpf_map_delete_elem(REG_RESULT_H, &result_key); | ||
| 93 | bpf_map_delete_elem(INLINE_RESULT_H, &result_key); | ||
| 94 | |||
| 95 | printf("Pass\n"); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | int main(int argc, char **argv) | ||
| 100 | { | ||
| 101 | struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; | ||
| 102 | char filename[256]; | ||
| 103 | |||
| 104 | assert(!setrlimit(RLIMIT_MEMLOCK, &r)); | ||
| 105 | |||
| 106 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); | ||
| 107 | |||
| 108 | if (load_bpf_file(filename)) { | ||
| 109 | printf("%s", bpf_log_buf); | ||
| 110 | return 1; | ||
| 111 | } | ||
| 112 | |||
| 113 | test_map_in_map(); | ||
| 114 | |||
| 115 | return 0; | ||
| 116 | } | ||
