Skip to content

Commit a26ca7c

Browse files
iamkafaiborkmann
authored andcommitted
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} ... Signed-off-by: Martin KaFai Lau <[email protected]> Acked-by: Alexei Starovoitov <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]>
1 parent 60197cf commit a26ca7c

File tree

5 files changed

+254
-7
lines changed

5 files changed

+254
-7
lines changed

include/linux/bpf.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ struct perf_event;
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 {
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 {
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)
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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,9 @@ union bpf_attr {
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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
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)
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 = {
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

Lines changed: 153 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,15 +150,162 @@ static int bpf_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
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);
158304
if (IS_ERR(inode))
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,
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

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
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)
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)
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)
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 @@ static int map_create(union bpf_attr *attr)
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
}

0 commit comments

Comments
 (0)