aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Borkmann <daniel@iogearbox.net>2018-08-10 14:54:08 -0400
committerDaniel Borkmann <daniel@iogearbox.net>2018-08-10 14:54:09 -0400
commit74b247f4c36315e5c08580700a68e0eb3b72de03 (patch)
tree007389b3325d0ab9c5ce2a15f8b2116e1fbf34c8
parent60afdf066a35317efd5d1d7ae7c7f4ef2b32601f (diff)
parentaf2a81dab44758de0b94679615ea75e8ee30aace (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.c44
-rw-r--r--kernel/bpf/inode.c8
-rw-r--r--tools/testing/selftests/bpf/test_btf.c87
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
1167static 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
1188static 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
1165const struct bpf_map_ops htab_map_ops = { 1205const 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
1176const struct bpf_map_ops htab_lru_map_ops = { 1218const 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
211done:
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
2096static struct btf_raw_test pprint_test = { 2098static 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
2157static 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
2158static void set_pprint_mapv(struct pprint_mapv *v, uint32_t i) 2191static 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
2169static int test_pprint(void) 2202static 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
2356static 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
2317static void usage(const char *cmd) 2374static 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)