On Fri, Dec 07, 2018 at 04:53:14PM -0800, Roman Gushchin wrote: > Implement bpffs pretty printing for cgroup local storage maps > (both shared and per-cpu). > Output example (captured for tools/testing/selftests/bpf/netcnt_prog.c): > > Shared: > $ cat /sys/fs/bpf/map_2 > # WARNING!! The output is for debug purpose only > # WARNING!! The output format will change > {4294968594,1}: {9999,1039896} > > Per-cpu: > $ cat /sys/fs/bpf/map_1 > # WARNING!! The output is for debug purpose only > # WARNING!! The output format will change > {4294968594,1}: { > cpu0: {0,0,0,0,0} > cpu1: {0,0,0,0,0} > cpu2: {1,104,0,0,0} > cpu3: {0,0,0,0,0} > } > > Signed-off-by: Roman Gushchin <g...@fb.com> > Cc: Alexei Starovoitov <a...@kernel.org> > Cc: Daniel Borkmann <dan...@iogearbox.net> > --- > include/linux/btf.h | 10 +++++ > kernel/bpf/local_storage.c | 90 +++++++++++++++++++++++++++++++++++++- > 2 files changed, 99 insertions(+), 1 deletion(-) > > diff --git a/include/linux/btf.h b/include/linux/btf.h > index 8c2199b5d250..ac67bc4cbfd9 100644 > --- a/include/linux/btf.h > +++ b/include/linux/btf.h > @@ -5,6 +5,7 @@ > #define _LINUX_BTF_H 1 > > #include <linux/types.h> > +#include <uapi/linux/btf.h> > > struct btf; > struct btf_type; > @@ -63,4 +64,13 @@ static inline const char *btf_name_by_offset(const struct > btf *btf, > } > #endif > > +static inline const struct btf_type *btf_orig_type(const struct btf *btf, > + const struct btf_type *t) > +{ > + while (t && BTF_INFO_KIND(t->info) == BTF_KIND_TYPEDEF) > + t = btf_type_by_id(btf, t->type); Only typedef is allowed? Not even const? If that is not the case, can btf_type_id_size() be reused? The "type following" has already been done once and then remembered in the verification time.
If cgroup_storage_check_btf() can only allow typedef, please move "btf_orig_type()" to the local_storage.c. > + > + return t; > +} > + > #endif > diff --git a/kernel/bpf/local_storage.c b/kernel/bpf/local_storage.c > index b65017dead44..7b51fe1aba3c 100644 > --- a/kernel/bpf/local_storage.c > +++ b/kernel/bpf/local_storage.c > @@ -1,11 +1,13 @@ > //SPDX-License-Identifier: GPL-2.0 > #include <linux/bpf-cgroup.h> > #include <linux/bpf.h> > +#include <linux/btf.h> > #include <linux/bug.h> > #include <linux/filter.h> > #include <linux/mm.h> > #include <linux/rbtree.h> > #include <linux/slab.h> > +#include <uapi/linux/btf.h> > > DEFINE_PER_CPU(struct bpf_cgroup_storage*, > bpf_cgroup_storage[MAX_BPF_CGROUP_STORAGE_TYPE]); > > @@ -308,6 +310,91 @@ static int cgroup_storage_delete_elem(struct bpf_map > *map, void *key) > return -EINVAL; > } > > +static int cgroup_storage_check_btf(const struct bpf_map *map, > + const struct btf *btf, > + const struct btf_type *key_type, Actually, in "map_check_btf()" (just before cgroup_storage_check_btf() is called), btf_type_id_size() has already been called to get the true size and the resolved type (i.e. BTF_INFO_KIND_STRUCT here) in order to reject "key_size != map->key_size". Hence, the key_type passed to cgroup_storage_check_btf() here will not be KIND_TYPEDEF or KIND_CONST. > + const struct btf_type *value_type) > +{ > + const struct btf_type *st, *t; > + struct btf_member *m; > + > + /* Key is expected to be of struct bpf_cgroup_storage_key type, > + * which is: > + * struct bpf_cgroup_storage_key { > + * __u64 cgroup_inode_id; > + * __u32 attach_type; > + * }; > + */ > + > + /* > + * Key_type must be a structure (or a typedef of a structure) with > + * two members. > + */ > + st = btf_orig_type(btf, key_type); > + if (BTF_INFO_KIND(st->info) != BTF_KIND_STRUCT || > + BTF_INFO_VLEN(st->info) != 2) > + return -EINVAL; > + > + /* > + * The first field must be a 64 bit integer at 0 offset. > + */ > + m = (struct btf_member *)(st + 1); > + t = btf_orig_type(btf, btf_type_by_id(btf, m->type)); > + if (!t || BTF_INFO_KIND(t->info) != BTF_KIND_INT || m->offset || > + t->size != > + FIELD_SIZEOF(struct bpf_cgroup_storage_key, cgroup_inode_id)) Instead of t->size, BTF_INT_BITS() and BTF_INT_OFFSET() need to be checked (please refer to the key_type check in array_map_check_btf()). I think exposing "btf_type_int_is_regular()" from btf.c will be easier here. Actually, add "btf_type_is_reg_int(t, expected_size)" to btf.h and btf.c, like (uncompiled and untested code): /* * Not a bit field and it must be the expected size. */ bool btf_type_is_reg_int(const struct btf_type *t, u32 expected_size) { u8 nr_bits, nr_bytes; u32 int_data; if (!btf_type_is_int(t)) return false; int_data = btf_type_int(t); nr_bits = BTF_INT_BITS(int_data); nr_bytes = BITS_ROUNDUP_BYTES(nr_bits); if (BITS_PER_BYTE_MASKED(nr_bits) || BTF_INT_OFFSET(int_data) || nr_bytes != expected_size) return false; return true; } > + return -EINVAL; > + > + /* > + * The second field must be a 32 bit integer at 0 offset. > + */ > + m = m + 1; > + t = btf_orig_type(btf, btf_type_by_id(btf, m->type)); > + if (!t || BTF_INFO_KIND(t->info) != BTF_KIND_INT || > + m->offset != offsetof(struct bpf_cgroup_storage_key, attach_type) * > + BITS_PER_BYTE || t->size != > + FIELD_SIZEOF(struct bpf_cgroup_storage_key, attach_type)) > + return -EINVAL; > + > + return 0; > +} > + > +static void cgroup_storage_seq_show_elem(struct bpf_map *map, void *_key, > + struct seq_file *m) > +{ > + enum bpf_cgroup_storage_type stype = cgroup_storage_type(map); > + struct bpf_cgroup_storage_key *key = _key; > + struct bpf_cgroup_storage *storage; > + int cpu; > + > + rcu_read_lock(); > + storage = cgroup_storage_lookup(map_to_storage(map), key, false); > + if (!storage) { > + rcu_read_unlock(); > + return; > + } > + > + btf_type_seq_show(map->btf, map->btf_key_type_id, key, m); > + stype = cgroup_storage_type(map); > + if (stype == BPF_CGROUP_STORAGE_SHARED) { > + seq_puts(m, ": "); > + btf_type_seq_show(map->btf, map->btf_value_type_id, > + &READ_ONCE(storage->buf)->data[0], m); > + seq_puts(m, "\n"); > + } else { > + seq_puts(m, ": {\n"); > + for_each_possible_cpu(cpu) { > + seq_printf(m, "\tcpu%d: ", cpu); > + btf_type_seq_show(map->btf, map->btf_value_type_id, > + per_cpu_ptr(storage->percpu_buf, cpu), > + m); > + seq_puts(m, "\n"); > + } > + seq_puts(m, "}\n"); > + } > + rcu_read_unlock(); > +} > + > const struct bpf_map_ops cgroup_storage_map_ops = { > .map_alloc = cgroup_storage_map_alloc, > .map_free = cgroup_storage_map_free, > @@ -315,7 +402,8 @@ const struct bpf_map_ops cgroup_storage_map_ops = { > .map_lookup_elem = cgroup_storage_lookup_elem, > .map_update_elem = cgroup_storage_update_elem, > .map_delete_elem = cgroup_storage_delete_elem, > - .map_check_btf = map_check_no_btf, > + .map_check_btf = cgroup_storage_check_btf, > + .map_seq_show_elem = cgroup_storage_seq_show_elem, > }; > > int bpf_cgroup_storage_assign(struct bpf_prog *prog, struct bpf_map *_map) > -- > 2.19.2 >