diff options
author | Daniel Borkmann <daniel@iogearbox.net> | 2018-08-10 14:54:08 -0400 |
---|---|---|
committer | Daniel Borkmann <daniel@iogearbox.net> | 2018-08-10 14:54:09 -0400 |
commit | 74b247f4c36315e5c08580700a68e0eb3b72de03 (patch) | |
tree | 007389b3325d0ab9c5ce2a15f8b2116e1fbf34c8 | |
parent | 60afdf066a35317efd5d1d7ae7c7f4ef2b32601f (diff) | |
parent | af2a81dab44758de0b94679615ea75e8ee30aace (diff) |
Merge branch 'bpf-btf-for-htab-lru'
Yonghong Song says:
====================
Commit a26ca7c982cb ("bpf: btf: Add pretty print support to
the basic arraymap") added pretty print support to array map.
This patch adds pretty print for hash and lru_hash maps.
The following example shows the pretty-print result of a pinned
hashmap. Without this patch set, user will get an error instead.
struct map_value {
int count_a;
int count_b;
};
cat /sys/fs/bpf/pinned_hash_map:
87907: {87907,87908}
57354: {37354,57355}
76625: {76625,76626}
...
Patch #1 fixed a bug in bpffs map_seq_next() function so that
all elements in the hash table will be traversed.
Patch #2 implemented map_seq_show_elem() and map_check_btf()
callback functions for hash and lru hash maps.
Patch #3 enhanced tools/testing/selftests/bpf/test_btf.c to
test bpffs hash and lru hash map pretty print.
====================
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
-rw-r--r-- | kernel/bpf/hashtab.c | 44 | ||||
-rw-r--r-- | kernel/bpf/inode.c | 8 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_btf.c | 87 |
3 files changed, 121 insertions, 18 deletions
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index 513d9dfcf4ee..d6110042e0d9 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c | |||
@@ -11,9 +11,11 @@ | |||
11 | * General Public License for more details. | 11 | * General Public License for more details. |
12 | */ | 12 | */ |
13 | #include <linux/bpf.h> | 13 | #include <linux/bpf.h> |
14 | #include <linux/btf.h> | ||
14 | #include <linux/jhash.h> | 15 | #include <linux/jhash.h> |
15 | #include <linux/filter.h> | 16 | #include <linux/filter.h> |
16 | #include <linux/rculist_nulls.h> | 17 | #include <linux/rculist_nulls.h> |
18 | #include <uapi/linux/btf.h> | ||
17 | #include "percpu_freelist.h" | 19 | #include "percpu_freelist.h" |
18 | #include "bpf_lru_list.h" | 20 | #include "bpf_lru_list.h" |
19 | #include "map_in_map.h" | 21 | #include "map_in_map.h" |
@@ -1162,6 +1164,44 @@ static void htab_map_free(struct bpf_map *map) | |||
1162 | kfree(htab); | 1164 | kfree(htab); |
1163 | } | 1165 | } |
1164 | 1166 | ||
1167 | static void htab_map_seq_show_elem(struct bpf_map *map, void *key, | ||
1168 | struct seq_file *m) | ||
1169 | { | ||
1170 | void *value; | ||
1171 | |||
1172 | rcu_read_lock(); | ||
1173 | |||
1174 | value = htab_map_lookup_elem(map, key); | ||
1175 | if (!value) { | ||
1176 | rcu_read_unlock(); | ||
1177 | return; | ||
1178 | } | ||
1179 | |||
1180 | btf_type_seq_show(map->btf, map->btf_key_type_id, key, m); | ||
1181 | seq_puts(m, ": "); | ||
1182 | btf_type_seq_show(map->btf, map->btf_value_type_id, value, m); | ||
1183 | seq_puts(m, "\n"); | ||
1184 | |||
1185 | rcu_read_unlock(); | ||
1186 | } | ||
1187 | |||
1188 | static int htab_map_check_btf(const struct bpf_map *map, const struct btf *btf, | ||
1189 | u32 btf_key_id, u32 btf_value_id) | ||
1190 | { | ||
1191 | const struct btf_type *key_type, *value_type; | ||
1192 | u32 key_size, value_size; | ||
1193 | |||
1194 | key_type = btf_type_id_size(btf, &btf_key_id, &key_size); | ||
1195 | if (!key_type || key_size != map->key_size) | ||
1196 | return -EINVAL; | ||
1197 | |||
1198 | value_type = btf_type_id_size(btf, &btf_value_id, &value_size); | ||
1199 | if (!value_type || value_size != map->value_size) | ||
1200 | return -EINVAL; | ||
1201 | |||
1202 | return 0; | ||
1203 | } | ||
1204 | |||
1165 | const struct bpf_map_ops htab_map_ops = { | 1205 | const struct bpf_map_ops htab_map_ops = { |
1166 | .map_alloc_check = htab_map_alloc_check, | 1206 | .map_alloc_check = htab_map_alloc_check, |
1167 | .map_alloc = htab_map_alloc, | 1207 | .map_alloc = htab_map_alloc, |
@@ -1171,6 +1211,8 @@ const struct bpf_map_ops htab_map_ops = { | |||
1171 | .map_update_elem = htab_map_update_elem, | 1211 | .map_update_elem = htab_map_update_elem, |
1172 | .map_delete_elem = htab_map_delete_elem, | 1212 | .map_delete_elem = htab_map_delete_elem, |
1173 | .map_gen_lookup = htab_map_gen_lookup, | 1213 | .map_gen_lookup = htab_map_gen_lookup, |
1214 | .map_seq_show_elem = htab_map_seq_show_elem, | ||
1215 | .map_check_btf = htab_map_check_btf, | ||
1174 | }; | 1216 | }; |
1175 | 1217 | ||
1176 | const struct bpf_map_ops htab_lru_map_ops = { | 1218 | const struct bpf_map_ops htab_lru_map_ops = { |
@@ -1182,6 +1224,8 @@ const struct bpf_map_ops htab_lru_map_ops = { | |||
1182 | .map_update_elem = htab_lru_map_update_elem, | 1224 | .map_update_elem = htab_lru_map_update_elem, |
1183 | .map_delete_elem = htab_lru_map_delete_elem, | 1225 | .map_delete_elem = htab_lru_map_delete_elem, |
1184 | .map_gen_lookup = htab_lru_map_gen_lookup, | 1226 | .map_gen_lookup = htab_lru_map_gen_lookup, |
1227 | .map_seq_show_elem = htab_map_seq_show_elem, | ||
1228 | .map_check_btf = htab_map_check_btf, | ||
1185 | }; | 1229 | }; |
1186 | 1230 | ||
1187 | /* Called from eBPF program */ | 1231 | /* Called from eBPF program */ |
diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c index 76efe9a183f5..fc5b103512e7 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c | |||
@@ -196,19 +196,21 @@ static void *map_seq_next(struct seq_file *m, void *v, loff_t *pos) | |||
196 | { | 196 | { |
197 | struct bpf_map *map = seq_file_to_map(m); | 197 | struct bpf_map *map = seq_file_to_map(m); |
198 | void *key = map_iter(m)->key; | 198 | void *key = map_iter(m)->key; |
199 | void *prev_key; | ||
199 | 200 | ||
200 | if (map_iter(m)->done) | 201 | if (map_iter(m)->done) |
201 | return NULL; | 202 | return NULL; |
202 | 203 | ||
203 | if (unlikely(v == SEQ_START_TOKEN)) | 204 | if (unlikely(v == SEQ_START_TOKEN)) |
204 | goto done; | 205 | prev_key = NULL; |
206 | else | ||
207 | prev_key = key; | ||
205 | 208 | ||
206 | if (map->ops->map_get_next_key(map, key, key)) { | 209 | if (map->ops->map_get_next_key(map, prev_key, key)) { |
207 | map_iter(m)->done = true; | 210 | map_iter(m)->done = true; |
208 | return NULL; | 211 | return NULL; |
209 | } | 212 | } |
210 | 213 | ||
211 | done: | ||
212 | ++(*pos); | 214 | ++(*pos); |
213 | return key; | 215 | return key; |
214 | } | 216 | } |
diff --git a/tools/testing/selftests/bpf/test_btf.c b/tools/testing/selftests/bpf/test_btf.c index ffdd27737c9e..7fa8c800c540 100644 --- a/tools/testing/selftests/bpf/test_btf.c +++ b/tools/testing/selftests/bpf/test_btf.c | |||
@@ -131,6 +131,8 @@ struct btf_raw_test { | |||
131 | __u32 max_entries; | 131 | __u32 max_entries; |
132 | bool btf_load_err; | 132 | bool btf_load_err; |
133 | bool map_create_err; | 133 | bool map_create_err; |
134 | bool ordered_map; | ||
135 | bool lossless_map; | ||
134 | int hdr_len_delta; | 136 | int hdr_len_delta; |
135 | int type_off_delta; | 137 | int type_off_delta; |
136 | int str_off_delta; | 138 | int str_off_delta; |
@@ -2093,8 +2095,7 @@ struct pprint_mapv { | |||
2093 | } aenum; | 2095 | } aenum; |
2094 | }; | 2096 | }; |
2095 | 2097 | ||
2096 | static struct btf_raw_test pprint_test = { | 2098 | static struct btf_raw_test pprint_test_template = { |
2097 | .descr = "BTF pretty print test #1", | ||
2098 | .raw_types = { | 2099 | .raw_types = { |
2099 | /* unsighed char */ /* [1] */ | 2100 | /* unsighed char */ /* [1] */ |
2100 | BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 8, 1), | 2101 | BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 8, 1), |
@@ -2146,8 +2147,6 @@ static struct btf_raw_test pprint_test = { | |||
2146 | }, | 2147 | }, |
2147 | .str_sec = "\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum", | 2148 | .str_sec = "\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum", |
2148 | .str_sec_size = sizeof("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum"), | 2149 | .str_sec_size = sizeof("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum"), |
2149 | .map_type = BPF_MAP_TYPE_ARRAY, | ||
2150 | .map_name = "pprint_test", | ||
2151 | .key_size = sizeof(unsigned int), | 2150 | .key_size = sizeof(unsigned int), |
2152 | .value_size = sizeof(struct pprint_mapv), | 2151 | .value_size = sizeof(struct pprint_mapv), |
2153 | .key_type_id = 3, /* unsigned int */ | 2152 | .key_type_id = 3, /* unsigned int */ |
@@ -2155,6 +2154,40 @@ static struct btf_raw_test pprint_test = { | |||
2155 | .max_entries = 128 * 1024, | 2154 | .max_entries = 128 * 1024, |
2156 | }; | 2155 | }; |
2157 | 2156 | ||
2157 | static struct btf_pprint_test_meta { | ||
2158 | const char *descr; | ||
2159 | enum bpf_map_type map_type; | ||
2160 | const char *map_name; | ||
2161 | bool ordered_map; | ||
2162 | bool lossless_map; | ||
2163 | } pprint_tests_meta[] = { | ||
2164 | { | ||
2165 | .descr = "BTF pretty print array", | ||
2166 | .map_type = BPF_MAP_TYPE_ARRAY, | ||
2167 | .map_name = "pprint_test_array", | ||
2168 | .ordered_map = true, | ||
2169 | .lossless_map = true, | ||
2170 | }, | ||
2171 | |||
2172 | { | ||
2173 | .descr = "BTF pretty print hash", | ||
2174 | .map_type = BPF_MAP_TYPE_HASH, | ||
2175 | .map_name = "pprint_test_hash", | ||
2176 | .ordered_map = false, | ||
2177 | .lossless_map = true, | ||
2178 | }, | ||
2179 | |||
2180 | { | ||
2181 | .descr = "BTF pretty print lru hash", | ||
2182 | .map_type = BPF_MAP_TYPE_LRU_HASH, | ||
2183 | .map_name = "pprint_test_lru_hash", | ||
2184 | .ordered_map = false, | ||
2185 | .lossless_map = false, | ||
2186 | }, | ||
2187 | |||
2188 | }; | ||
2189 | |||
2190 | |||
2158 | static void set_pprint_mapv(struct pprint_mapv *v, uint32_t i) | 2191 | static void set_pprint_mapv(struct pprint_mapv *v, uint32_t i) |
2159 | { | 2192 | { |
2160 | v->ui32 = i; | 2193 | v->ui32 = i; |
@@ -2166,10 +2199,12 @@ static void set_pprint_mapv(struct pprint_mapv *v, uint32_t i) | |||
2166 | v->aenum = i & 0x03; | 2199 | v->aenum = i & 0x03; |
2167 | } | 2200 | } |
2168 | 2201 | ||
2169 | static int test_pprint(void) | 2202 | static int do_test_pprint(void) |
2170 | { | 2203 | { |
2171 | const struct btf_raw_test *test = &pprint_test; | 2204 | const struct btf_raw_test *test = &pprint_test_template; |
2172 | struct bpf_create_map_attr create_attr = {}; | 2205 | struct bpf_create_map_attr create_attr = {}; |
2206 | unsigned int key, nr_read_elems; | ||
2207 | bool ordered_map, lossless_map; | ||
2173 | int map_fd = -1, btf_fd = -1; | 2208 | int map_fd = -1, btf_fd = -1; |
2174 | struct pprint_mapv mapv = {}; | 2209 | struct pprint_mapv mapv = {}; |
2175 | unsigned int raw_btf_size; | 2210 | unsigned int raw_btf_size; |
@@ -2178,7 +2213,6 @@ static int test_pprint(void) | |||
2178 | char pin_path[255]; | 2213 | char pin_path[255]; |
2179 | size_t line_len = 0; | 2214 | size_t line_len = 0; |
2180 | char *line = NULL; | 2215 | char *line = NULL; |
2181 | unsigned int key; | ||
2182 | uint8_t *raw_btf; | 2216 | uint8_t *raw_btf; |
2183 | ssize_t nread; | 2217 | ssize_t nread; |
2184 | int err, ret; | 2218 | int err, ret; |
@@ -2251,14 +2285,18 @@ static int test_pprint(void) | |||
2251 | goto done; | 2285 | goto done; |
2252 | } | 2286 | } |
2253 | 2287 | ||
2254 | key = 0; | 2288 | nr_read_elems = 0; |
2289 | ordered_map = test->ordered_map; | ||
2290 | lossless_map = test->lossless_map; | ||
2255 | do { | 2291 | do { |
2256 | ssize_t nexpected_line; | 2292 | ssize_t nexpected_line; |
2293 | unsigned int next_key; | ||
2257 | 2294 | ||
2258 | set_pprint_mapv(&mapv, key); | 2295 | next_key = ordered_map ? nr_read_elems : atoi(line); |
2296 | set_pprint_mapv(&mapv, next_key); | ||
2259 | nexpected_line = snprintf(expected_line, sizeof(expected_line), | 2297 | nexpected_line = snprintf(expected_line, sizeof(expected_line), |
2260 | "%u: {%u,0,%d,0x%x,0x%x,0x%x,{%lu|[%u,%u,%u,%u,%u,%u,%u,%u]},%s}\n", | 2298 | "%u: {%u,0,%d,0x%x,0x%x,0x%x,{%lu|[%u,%u,%u,%u,%u,%u,%u,%u]},%s}\n", |
2261 | key, | 2299 | next_key, |
2262 | mapv.ui32, mapv.si32, | 2300 | mapv.ui32, mapv.si32, |
2263 | mapv.unused_bits2a, mapv.bits28, mapv.unused_bits2b, | 2301 | mapv.unused_bits2a, mapv.bits28, mapv.unused_bits2b, |
2264 | mapv.ui64, | 2302 | mapv.ui64, |
@@ -2281,11 +2319,12 @@ static int test_pprint(void) | |||
2281 | } | 2319 | } |
2282 | 2320 | ||
2283 | nread = getline(&line, &line_len, pin_file); | 2321 | nread = getline(&line, &line_len, pin_file); |
2284 | } while (++key < test->max_entries && nread > 0); | 2322 | } while (++nr_read_elems < test->max_entries && nread > 0); |
2285 | 2323 | ||
2286 | if (CHECK(key < test->max_entries, | 2324 | if (lossless_map && |
2287 | "Unexpected EOF. key:%u test->max_entries:%u", | 2325 | CHECK(nr_read_elems < test->max_entries, |
2288 | key, test->max_entries)) { | 2326 | "Unexpected EOF. nr_read_elems:%u test->max_entries:%u", |
2327 | nr_read_elems, test->max_entries)) { | ||
2289 | err = -1; | 2328 | err = -1; |
2290 | goto done; | 2329 | goto done; |
2291 | } | 2330 | } |
@@ -2314,6 +2353,24 @@ done: | |||
2314 | return err; | 2353 | return err; |
2315 | } | 2354 | } |
2316 | 2355 | ||
2356 | static int test_pprint(void) | ||
2357 | { | ||
2358 | unsigned int i; | ||
2359 | int err = 0; | ||
2360 | |||
2361 | for (i = 0; i < ARRAY_SIZE(pprint_tests_meta); i++) { | ||
2362 | pprint_test_template.descr = pprint_tests_meta[i].descr; | ||
2363 | pprint_test_template.map_type = pprint_tests_meta[i].map_type; | ||
2364 | pprint_test_template.map_name = pprint_tests_meta[i].map_name; | ||
2365 | pprint_test_template.ordered_map = pprint_tests_meta[i].ordered_map; | ||
2366 | pprint_test_template.lossless_map = pprint_tests_meta[i].lossless_map; | ||
2367 | |||
2368 | err |= count_result(do_test_pprint()); | ||
2369 | } | ||
2370 | |||
2371 | return err; | ||
2372 | } | ||
2373 | |||
2317 | static void usage(const char *cmd) | 2374 | static void usage(const char *cmd) |
2318 | { | 2375 | { |
2319 | fprintf(stderr, "Usage: %s [-l] [[-r test_num (1 - %zu)] | [-g test_num (1 - %zu)] | [-f test_num (1 - %zu)] | [-p]]\n", | 2376 | fprintf(stderr, "Usage: %s [-l] [[-r test_num (1 - %zu)] | [-g test_num (1 - %zu)] | [-f test_num (1 - %zu)] | [-p]]\n", |
@@ -2409,7 +2466,7 @@ int main(int argc, char **argv) | |||
2409 | err |= test_file(); | 2466 | err |= test_file(); |
2410 | 2467 | ||
2411 | if (args.pprint_test) | 2468 | if (args.pprint_test) |
2412 | err |= count_result(test_pprint()); | 2469 | err |= test_pprint(); |
2413 | 2470 | ||
2414 | if (args.raw_test || args.get_info_test || args.file_test || | 2471 | if (args.raw_test || args.get_info_test || args.file_test || |
2415 | args.pprint_test) | 2472 | args.pprint_test) |