History

bpf: btf: Add pretty print support to the basic arraymap

This patch adds pretty print support to the basic arraymap. Support for other bpf maps can be added later. This patch adds new attrs to the BPF_MAP_CREATE command to allow specifying the btf_fd, btf_key_id and btf_value_id. The BPF_MAP_CREATE can then associate the btf to the map if the creating map supports BTF. A BTF supported map needs to implement two new map ops, map_seq_show_elem() and map_check_btf(). This patch has implemented these new map ops for the basic arraymap. It also adds file_operations, bpffs_map_fops, to the pinned map such that the pinned map can be opened and read. After that, the user has an intuitive way to do "cat bpffs/pathto/a-pinned-map" instead of getting an error. bpffs_map_fops should not be extended further to support other operations. Other operations (e.g. write/key-lookup...) should be realized by the userspace tools (e.g. bpftool) through the BPF_OBJ_GET_INFO_BY_FD, map's lookup/update interface...etc. Follow up patches will allow the userspace to obtain the BTF from a map-fd. Here is a sample output when reading a pinned arraymap with the following map's value: struct map_value { int count_a; int count_b; }; cat /sys/fs/bpf/pinned_array_map: 0: {1,2} 1: {3,4} 2: {5,6} ...

Martin KaFai Lau committed 8 years ago

Commit a26ca7c1 parent 60197cf5 files · +254 −7Browse files

Unified · Split

5 files
  1. include/linux/bpf.h +17 −3
  2. include/uapi/linux/bpf.h +3 −0
  3. kernel/bpf/arraymap.c +50 −0
  4. kernel/bpf/inode.c +153 −3
  5. kernel/bpf/syscall.c +31 −1
include/linux/bpf.h+17−3
@@ -22,6 +22,8 @@ struct perf_event; · 21 unmodified lines
22struct bpf_prog;22struct bpf_prog;
23struct bpf_map;23struct bpf_map;
24struct sock;24struct sock;
25struct seq_file;
26struct btf;
25 27
26/* map is generic key/value storage optionally accesible by eBPF programs */28/* map is generic key/value storage optionally accesible by eBPF programs */
27struct bpf_map_ops {29struct bpf_map_ops {
@@ -43,10 +45,14 @@ struct bpf_map_ops { · 15 unmodified lines
43 void (*map_fd_put_ptr)(void *ptr);45 void (*map_fd_put_ptr)(void *ptr);
44 u32 (*map_gen_lookup)(struct bpf_map *map, struct bpf_insn *insn_buf);46 u32 (*map_gen_lookup)(struct bpf_map *map, struct bpf_insn *insn_buf);
45 u32 (*map_fd_sys_lookup_elem)(void *ptr);47 u32 (*map_fd_sys_lookup_elem)(void *ptr);
48 void (*map_seq_show_elem)(struct bpf_map *map, void *key,
49 struct seq_file *m);
50 int (*map_check_btf)(const struct bpf_map *map, const struct btf *btf,
51 u32 key_type_id, u32 value_type_id);
46};52};
47 53
48struct bpf_map {54struct bpf_map {
49 /* 1st cacheline with read-mostly members of which some55 /* The first two cachelines with read-mostly members of which some
50 * are also accessed in fast-path (e.g. ops, max_entries).56 * are also accessed in fast-path (e.g. ops, max_entries).
51 */57 */
52 const struct bpf_map_ops *ops ____cacheline_aligned;58 const struct bpf_map_ops *ops ____cacheline_aligned;
@@ -62,10 +68,13 @@ struct bpf_map { · 9 unmodified lines
62 u32 pages;68 u32 pages;
63 u32 id;69 u32 id;
64 int numa_node;70 int numa_node;
71 u32 btf_key_id;
72 u32 btf_value_id;
73 struct btf *btf;
65 bool unpriv_array;74 bool unpriv_array;
66 /* 7 bytes hole */75 /* 55 bytes hole */
67 76
68 /* 2nd cacheline with misc members to avoid false sharing77 /* The 3rd and 4th cacheline with misc members to avoid false sharing
69 * particularly with refcounting.78 * particularly with refcounting.
70 */79 */
71 struct user_struct *user ____cacheline_aligned;80 struct user_struct *user ____cacheline_aligned;
@@ -100,6 +109,11 @@ static inline struct bpf_offloaded_map *map_to_offmap(struct bpf_map *map) · 28 unmodified lines
100 return container_of(map, struct bpf_offloaded_map, map);109 return container_of(map, struct bpf_offloaded_map, map);
101}110}
102 111
112static inline bool bpf_map_support_seq_show(const struct bpf_map *map)
113{
114 return map->ops->map_seq_show_elem && map->ops->map_check_btf;
115}
116
103extern const struct bpf_map_ops bpf_map_offload_ops;117extern const struct bpf_map_ops bpf_map_offload_ops;
104 118
105/* function argument constraints */119/* function argument constraints */
include/uapi/linux/bpf.h+3−0
@@ -280,6 +280,9 @@ union bpf_attr { · 279 unmodified lines
280 */280 */
281 char map_name[BPF_OBJ_NAME_LEN];281 char map_name[BPF_OBJ_NAME_LEN];
282 __u32 map_ifindex; /* ifindex of netdev to create on */282 __u32 map_ifindex; /* ifindex of netdev to create on */
283 __u32 btf_fd; /* fd pointing to a BTF type data */
284 __u32 btf_key_id; /* BTF type_id of the key */
285 __u32 btf_value_id; /* BTF type_id of the value */
283 };286 };
284 287
285 struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */288 struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
kernel/bpf/arraymap.c+50−0
@@ -11,11 +11,13 @@ · 10 unmodified lines
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/err.h>15#include <linux/err.h>
15#include <linux/slab.h>16#include <linux/slab.h>
16#include <linux/mm.h>17#include <linux/mm.h>
17#include <linux/filter.h>18#include <linux/filter.h>
18#include <linux/perf_event.h>19#include <linux/perf_event.h>
20#include <uapi/linux/btf.h>
19 21
20#include "map_in_map.h"22#include "map_in_map.h"
21 23
@@ -336,6 +338,52 @@ static void array_map_free(struct bpf_map *map) · 314 unmodified lines
336 bpf_map_area_free(array);338 bpf_map_area_free(array);
337}339}
338 340
341static void array_map_seq_show_elem(struct bpf_map *map, void *key,
342 struct seq_file *m)
343{
344 void *value;
345
346 rcu_read_lock();
347
348 value = array_map_lookup_elem(map, key);
349 if (!value) {
350 rcu_read_unlock();
351 return;
352 }
353
354 seq_printf(m, "%u: ", *(u32 *)key);
355 btf_type_seq_show(map->btf, map->btf_value_id, value, m);
356 seq_puts(m, "\n");
357
358 rcu_read_unlock();
359}
360
361static int array_map_check_btf(const struct bpf_map *map, const struct btf *btf,
362 u32 btf_key_id, u32 btf_value_id)
363{
364 const struct btf_type *key_type, *value_type;
365 u32 key_size, value_size;
366 u32 int_data;
367
368 key_type = btf_type_id_size(btf, &btf_key_id, &key_size);
369 if (!key_type || BTF_INFO_KIND(key_type->info) != BTF_KIND_INT)
370 return -EINVAL;
371
372 int_data = *(u32 *)(key_type + 1);
373 /* bpf array can only take a u32 key. This check makes
374 * sure that the btf matches the attr used during map_create.
375 */
376 if (BTF_INT_BITS(int_data) != 32 || key_size != 4 ||
377 BTF_INT_OFFSET(int_data))
378 return -EINVAL;
379
380 value_type = btf_type_id_size(btf, &btf_value_id, &value_size);
381 if (!value_type || value_size > map->value_size)
382 return -EINVAL;
383
384 return 0;
385}
386
339const struct bpf_map_ops array_map_ops = {387const struct bpf_map_ops array_map_ops = {
340 .map_alloc_check = array_map_alloc_check,388 .map_alloc_check = array_map_alloc_check,
341 .map_alloc = array_map_alloc,389 .map_alloc = array_map_alloc,
@@ -345,6 +393,8 @@ const struct bpf_map_ops array_map_ops = { · 3 unmodified lines
345 .map_update_elem = array_map_update_elem,393 .map_update_elem = array_map_update_elem,
346 .map_delete_elem = array_map_delete_elem,394 .map_delete_elem = array_map_delete_elem,
347 .map_gen_lookup = array_map_gen_lookup,395 .map_gen_lookup = array_map_gen_lookup,
396 .map_seq_show_elem = array_map_seq_show_elem,
397 .map_check_btf = array_map_check_btf,
348};398};
349 399
350const struct bpf_map_ops percpu_array_map_ops = {400const struct bpf_map_ops percpu_array_map_ops = {
kernel/bpf/inode.c+153−3
@@ -150,8 +150,154 @@ static int bpf_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) · 149 unmodified lines
150 return 0;150 return 0;
151}151}
152 152
153struct map_iter {
154 void *key;
155 bool done;
156};
157
158static struct map_iter *map_iter(struct seq_file *m)
159{
160 return m->private;
161}
162
163static struct bpf_map *seq_file_to_map(struct seq_file *m)
164{
165 return file_inode(m->file)->i_private;
166}
167
168static void map_iter_free(struct map_iter *iter)
169{
170 if (iter) {
171 kfree(iter->key);
172 kfree(iter);
173 }
174}
175
176static struct map_iter *map_iter_alloc(struct bpf_map *map)
177{
178 struct map_iter *iter;
179
180 iter = kzalloc(sizeof(*iter), GFP_KERNEL | __GFP_NOWARN);
181 if (!iter)
182 goto error;
183
184 iter->key = kzalloc(map->key_size, GFP_KERNEL | __GFP_NOWARN);
185 if (!iter->key)
186 goto error;
187
188 return iter;
189
190error:
191 map_iter_free(iter);
192 return NULL;
193}
194
195static void *map_seq_next(struct seq_file *m, void *v, loff_t *pos)
196{
197 struct bpf_map *map = seq_file_to_map(m);
198 void *key = map_iter(m)->key;
199
200 if (map_iter(m)->done)
201 return NULL;
202
203 if (unlikely(v == SEQ_START_TOKEN))
204 goto done;
205
206 if (map->ops->map_get_next_key(map, key, key)) {
207 map_iter(m)->done = true;
208 return NULL;
209 }
210
211done:
212 ++(*pos);
213 return key;
214}
215
216static void *map_seq_start(struct seq_file *m, loff_t *pos)
217{
218 if (map_iter(m)->done)
219 return NULL;
220
221 return *pos ? map_iter(m)->key : SEQ_START_TOKEN;
222}
223
224static void map_seq_stop(struct seq_file *m, void *v)
225{
226}
227
228static int map_seq_show(struct seq_file *m, void *v)
229{
230 struct bpf_map *map = seq_file_to_map(m);
231 void *key = map_iter(m)->key;
232
233 if (unlikely(v == SEQ_START_TOKEN)) {
234 seq_puts(m, "# WARNING!! The output is for debug purpose only\n");
235 seq_puts(m, "# WARNING!! The output format will change\n");
236 } else {
237 map->ops->map_seq_show_elem(map, key, m);
238 }
239
240 return 0;
241}
242
243static const struct seq_operations bpffs_map_seq_ops = {
244 .start = map_seq_start,
245 .next = map_seq_next,
246 .show = map_seq_show,
247 .stop = map_seq_stop,
248};
249
250static int bpffs_map_open(struct inode *inode, struct file *file)
251{
252 struct bpf_map *map = inode->i_private;
253 struct map_iter *iter;
254 struct seq_file *m;
255 int err;
256
257 iter = map_iter_alloc(map);
258 if (!iter)
259 return -ENOMEM;
260
261 err = seq_open(file, &bpffs_map_seq_ops);
262 if (err) {
263 map_iter_free(iter);
264 return err;
265 }
266
267 m = file->private_data;
268 m->private = iter;
269
270 return 0;
271}
272
273static int bpffs_map_release(struct inode *inode, struct file *file)
274{
275 struct seq_file *m = file->private_data;
276
277 map_iter_free(map_iter(m));
278
279 return seq_release(inode, file);
280}
281
282/* bpffs_map_fops should only implement the basic
283 * read operation for a BPF map. The purpose is to
284 * provide a simple user intuitive way to do
285 * "cat bpffs/pathto/a-pinned-map".
286 *
287 * Other operations (e.g. write, lookup...) should be realized by
288 * the userspace tools (e.g. bpftool) through the
289 * BPF_OBJ_GET_INFO_BY_FD and the map's lookup/update
290 * interface.
291 */
292static const struct file_operations bpffs_map_fops = {
293 .open = bpffs_map_open,
294 .read = seq_read,
295 .release = bpffs_map_release,
296};
297
153static int bpf_mkobj_ops(struct dentry *dentry, umode_t mode, void *raw,298static int bpf_mkobj_ops(struct dentry *dentry, umode_t mode, void *raw,
154 const struct inode_operations *iops)299 const struct inode_operations *iops,
300 const struct file_operations *fops)
155{301{
156 struct inode *dir = dentry->d_parent->d_inode;302 struct inode *dir = dentry->d_parent->d_inode;
157 struct inode *inode = bpf_get_inode(dir->i_sb, dir, mode);303 struct inode *inode = bpf_get_inode(dir->i_sb, dir, mode);
@@ -159,6 +305,7 @@ static int bpf_mkobj_ops(struct dentry *dentry, umode_t mode, void *raw, · 1 unmodified line
159 return PTR_ERR(inode);305 return PTR_ERR(inode);
160 306
161 inode->i_op = iops;307 inode->i_op = iops;
308 inode->i_fop = fops;
162 inode->i_private = raw;309 inode->i_private = raw;
163 310
164 bpf_dentry_finalize(dentry, inode, dir);311 bpf_dentry_finalize(dentry, inode, dir);
@@ -167,12 +314,15 @@ static int bpf_mkobj_ops(struct dentry *dentry, umode_t mode, void *raw, · 2 unmodified lines
167 314
168static int bpf_mkprog(struct dentry *dentry, umode_t mode, void *arg)315static int bpf_mkprog(struct dentry *dentry, umode_t mode, void *arg)
169{316{
170 return bpf_mkobj_ops(dentry, mode, arg, &bpf_prog_iops);317 return bpf_mkobj_ops(dentry, mode, arg, &bpf_prog_iops, NULL);
171}318}
172 319
173static int bpf_mkmap(struct dentry *dentry, umode_t mode, void *arg)320static int bpf_mkmap(struct dentry *dentry, umode_t mode, void *arg)
174{321{
175 return bpf_mkobj_ops(dentry, mode, arg, &bpf_map_iops);322 struct bpf_map *map = arg;
323
324 return bpf_mkobj_ops(dentry, mode, arg, &bpf_map_iops,
325 map->btf ? &bpffs_map_fops : NULL);
176}326}
177 327
178static struct dentry *328static struct dentry *
kernel/bpf/syscall.c+31−1
@@ -27,6 +27,7 @@ · 26 unmodified lines
27#include <linux/cred.h>27#include <linux/cred.h>
28#include <linux/timekeeping.h>28#include <linux/timekeeping.h>
29#include <linux/ctype.h>29#include <linux/ctype.h>
30#include <linux/btf.h>
30 31
31#define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PROG_ARRAY || \32#define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PROG_ARRAY || \
32 (map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \33 (map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
@@ -251,6 +252,7 @@ static void bpf_map_free_deferred(struct work_struct *work) · 218 unmodified lines
251 252
252 bpf_map_uncharge_memlock(map);253 bpf_map_uncharge_memlock(map);
253 security_bpf_map_free(map);254 security_bpf_map_free(map);
255 btf_put(map->btf);
254 /* implementation dependent freeing */256 /* implementation dependent freeing */
255 map->ops->map_free(map);257 map->ops->map_free(map);
256}258}
@@ -416,7 +418,7 @@ static int bpf_obj_name_cpy(char *dst, const char *src) · 159 unmodified lines
416 return 0;418 return 0;
417}419}
418 420
419#define BPF_MAP_CREATE_LAST_FIELD map_ifindex421#define BPF_MAP_CREATE_LAST_FIELD btf_value_id
420/* called via syscall */422/* called via syscall */
421static int map_create(union bpf_attr *attr)423static int map_create(union bpf_attr *attr)
422{424{
@@ -450,6 +452,33 @@ static int map_create(union bpf_attr *attr) · 27 unmodified lines
450 atomic_set(&map->refcnt, 1);452 atomic_set(&map->refcnt, 1);
451 atomic_set(&map->usercnt, 1);453 atomic_set(&map->usercnt, 1);
452 454
455 if (bpf_map_support_seq_show(map) &&
456 (attr->btf_key_id || attr->btf_value_id)) {
457 struct btf *btf;
458
459 if (!attr->btf_key_id || !attr->btf_value_id) {
460 err = -EINVAL;
461 goto free_map_nouncharge;
462 }
463
464 btf = btf_get_by_fd(attr->btf_fd);
465 if (IS_ERR(btf)) {
466 err = PTR_ERR(btf);
467 goto free_map_nouncharge;
468 }
469
470 err = map->ops->map_check_btf(map, btf, attr->btf_key_id,
471 attr->btf_value_id);
472 if (err) {
473 btf_put(btf);
474 goto free_map_nouncharge;
475 }
476
477 map->btf = btf;
478 map->btf_key_id = attr->btf_key_id;
479 map->btf_value_id = attr->btf_value_id;
480 }
481
453 err = security_bpf_map_alloc(map);482 err = security_bpf_map_alloc(map);
454 if (err)483 if (err)
455 goto free_map_nouncharge;484 goto free_map_nouncharge;
@@ -482,6 +511,7 @@ free_map: · 26 unmodified lines
482free_map_sec:511free_map_sec:
483 security_bpf_map_free(map);512 security_bpf_map_free(map);
484free_map_nouncharge:513free_map_nouncharge:
514 btf_put(map->btf);
485 map->ops->map_free(map);515 map->ops->map_free(map);
486 return err;516 return err;
487}517}