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
2222 struct bpf_prog;
2323 struct bpf_map;
2424 struct sock;
25+struct seq_file;
26+struct btf;
2527
2628 /* map is generic key/value storage optionally accesible by eBPF programs */
2729 struct bpf_map_ops {
@@ -43,10 +45,14 @@ struct bpf_map_ops { · 15 unmodified lines
4345 void (*map_fd_put_ptr)(void *ptr);
4446 u32 (*map_gen_lookup)(struct bpf_map *map, struct bpf_insn *insn_buf);
4547 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);
4652 };
4753
4854 struct bpf_map {
49− /* 1st cacheline with read-mostly members of which some
55+ /* The first two cachelines with read-mostly members of which some
5056 * are also accessed in fast-path (e.g. ops, max_entries).
5157 */
5258 const struct bpf_map_ops *ops ____cacheline_aligned;
@@ -62,10 +68,13 @@ struct bpf_map { · 9 unmodified lines
6268 u32 pages;
6369 u32 id;
6470 int numa_node;
71+ u32 btf_key_id;
72+ u32 btf_value_id;
73+ struct btf *btf;
6574 bool unpriv_array;
66− /* 7 bytes hole */
75+ /* 55 bytes hole */
6776
68− /* 2nd cacheline with misc members to avoid false sharing
77+ /* The 3rd and 4th cacheline with misc members to avoid false sharing
6978 * particularly with refcounting.
7079 */
7180 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
100109 return container_of(map, struct bpf_offloaded_map, map);
101110 }
102111
112+static 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+
103117 extern const struct bpf_map_ops bpf_map_offload_ops;
104118
105119 /* function argument constraints */
include/uapi/linux/bpf.h+3−0
@@ -280,6 +280,9 @@ union bpf_attr { · 279 unmodified lines
280280 */
281281 char map_name[BPF_OBJ_NAME_LEN];
282282 __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 */
283286 };
284287
285288 struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
kernel/bpf/arraymap.c+50−0
@@ -11,11 +11,13 @@ · 10 unmodified lines
1111 * General Public License for more details.
1212 */
1313 #include <linux/bpf.h>
14+#include <linux/btf.h>
1415 #include <linux/err.h>
1516 #include <linux/slab.h>
1617 #include <linux/mm.h>
1718 #include <linux/filter.h>
1819 #include <linux/perf_event.h>
20+#include <uapi/linux/btf.h>
1921
2022 #include "map_in_map.h"
2123
@@ -336,6 +338,52 @@ static void array_map_free(struct bpf_map *map) · 314 unmodified lines
336338 bpf_map_area_free(array);
337339 }
338340
341+static 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+
361+static 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+
339387 const struct bpf_map_ops array_map_ops = {
340388 .map_alloc_check = array_map_alloc_check,
341389 .map_alloc = array_map_alloc,
@@ -345,6 +393,8 @@ const struct bpf_map_ops array_map_ops = { · 3 unmodified lines
345393 .map_update_elem = array_map_update_elem,
346394 .map_delete_elem = array_map_delete_elem,
347395 .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,
348398 };
349399
350400 const 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
150150 return 0;
151151 }
152152
153+struct map_iter {
154+ void *key;
155+ bool done;
156+};
157+
158+static struct map_iter *map_iter(struct seq_file *m)
159+{
160+ return m->private;
161+}
162+
163+static struct bpf_map *seq_file_to_map(struct seq_file *m)
164+{
165+ return file_inode(m->file)->i_private;
166+}
167+
168+static void map_iter_free(struct map_iter *iter)
169+{
170+ if (iter) {
171+ kfree(iter->key);
172+ kfree(iter);
173+ }
174+}
175+
176+static 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+
190+error:
191+ map_iter_free(iter);
192+ return NULL;
193+}
194+
195+static 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+
211+done:
212+ ++(*pos);
213+ return key;
214+}
215+
216+static 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+
224+static void map_seq_stop(struct seq_file *m, void *v)
225+{
226+}
227+
228+static 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+
243+static 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+
250+static 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+
273+static 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+ */
292+static const struct file_operations bpffs_map_fops = {
293+ .open = bpffs_map_open,
294+ .read = seq_read,
295+ .release = bpffs_map_release,
296+};
297+
153298 static 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)
155301 {
156302 struct inode *dir = dentry->d_parent->d_inode;
157303 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
159305 return PTR_ERR(inode);
160306
161307 inode->i_op = iops;
308+ inode->i_fop = fops;
162309 inode->i_private = raw;
163310
164311 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
167314
168315 static int bpf_mkprog(struct dentry *dentry, umode_t mode, void *arg)
169316 {
170− return bpf_mkobj_ops(dentry, mode, arg, &bpf_prog_iops);
317+ return bpf_mkobj_ops(dentry, mode, arg, &bpf_prog_iops, NULL);
171318 }
172319
173320 static int bpf_mkmap(struct dentry *dentry, umode_t mode, void *arg)
174321 {
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);
176326 }
177327
178328 static struct dentry *
kernel/bpf/syscall.c+31−1
@@ -27,6 +27,7 @@ · 26 unmodified lines
2727 #include <linux/cred.h>
2828 #include <linux/timekeeping.h>
2929 #include <linux/ctype.h>
30+#include <linux/btf.h>
3031
3132 #define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PROG_ARRAY || \
3233 (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
251252
252253 bpf_map_uncharge_memlock(map);
253254 security_bpf_map_free(map);
255+ btf_put(map->btf);
254256 /* implementation dependent freeing */
255257 map->ops->map_free(map);
256258 }
@@ -416,7 +418,7 @@ static int bpf_obj_name_cpy(char *dst, const char *src) · 159 unmodified lines
416418 return 0;
417419 }
418420
419−#define BPF_MAP_CREATE_LAST_FIELD map_ifindex
421+#define BPF_MAP_CREATE_LAST_FIELD btf_value_id
420422 /* called via syscall */
421423 static int map_create(union bpf_attr *attr)
422424 {
@@ -450,6 +452,33 @@ static int map_create(union bpf_attr *attr) · 27 unmodified lines
450452 atomic_set(&map->refcnt, 1);
451453 atomic_set(&map->usercnt, 1);
452454
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+
453482 err = security_bpf_map_alloc(map);
454483 if (err)
455484 goto free_map_nouncharge;
@@ -482,6 +511,7 @@ free_map: · 26 unmodified lines
482511 free_map_sec:
483512 security_bpf_map_free(map);
484513 free_map_nouncharge:
514+ btf_put(map->btf);
485515 map->ops->map_free(map);
486516 return err;
487517 }