bpf: Implement bpf iterator for array maps
authorYonghong Song <yhs@fb.com>
Thu, 23 Jul 2020 18:41:15 +0000 (11:41 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Sun, 26 Jul 2020 03:16:33 +0000 (20:16 -0700)
The bpf iterators for array and percpu array
are implemented. Similar to hash maps, for percpu
array map, bpf program will receive values
from all cpus.

Signed-off-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20200723184115.590532-1-yhs@fb.com
kernel/bpf/arraymap.c
kernel/bpf/map_iter.c

index c66e827..8ff419b 100644 (file)
@@ -487,6 +487,142 @@ static int array_map_mmap(struct bpf_map *map, struct vm_area_struct *vma)
                                   vma->vm_pgoff + pgoff);
 }
 
+struct bpf_iter_seq_array_map_info {
+       struct bpf_map *map;
+       void *percpu_value_buf;
+       u32 index;
+};
+
+static void *bpf_array_map_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       struct bpf_iter_seq_array_map_info *info = seq->private;
+       struct bpf_map *map = info->map;
+       struct bpf_array *array;
+       u32 index;
+
+       if (info->index >= map->max_entries)
+               return NULL;
+
+       if (*pos == 0)
+               ++*pos;
+       array = container_of(map, struct bpf_array, map);
+       index = info->index & array->index_mask;
+       if (info->percpu_value_buf)
+              return array->pptrs[index];
+       return array->value + array->elem_size * index;
+}
+
+static void *bpf_array_map_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       struct bpf_iter_seq_array_map_info *info = seq->private;
+       struct bpf_map *map = info->map;
+       struct bpf_array *array;
+       u32 index;
+
+       ++*pos;
+       ++info->index;
+       if (info->index >= map->max_entries)
+               return NULL;
+
+       array = container_of(map, struct bpf_array, map);
+       index = info->index & array->index_mask;
+       if (info->percpu_value_buf)
+              return array->pptrs[index];
+       return array->value + array->elem_size * index;
+}
+
+static int __bpf_array_map_seq_show(struct seq_file *seq, void *v)
+{
+       struct bpf_iter_seq_array_map_info *info = seq->private;
+       struct bpf_iter__bpf_map_elem ctx = {};
+       struct bpf_map *map = info->map;
+       struct bpf_iter_meta meta;
+       struct bpf_prog *prog;
+       int off = 0, cpu = 0;
+       void __percpu **pptr;
+       u32 size;
+
+       meta.seq = seq;
+       prog = bpf_iter_get_info(&meta, v == NULL);
+       if (!prog)
+               return 0;
+
+       ctx.meta = &meta;
+       ctx.map = info->map;
+       if (v) {
+               ctx.key = &info->index;
+
+               if (!info->percpu_value_buf) {
+                       ctx.value = v;
+               } else {
+                       pptr = v;
+                       size = round_up(map->value_size, 8);
+                       for_each_possible_cpu(cpu) {
+                               bpf_long_memcpy(info->percpu_value_buf + off,
+                                               per_cpu_ptr(pptr, cpu),
+                                               size);
+                               off += size;
+                       }
+                       ctx.value = info->percpu_value_buf;
+               }
+       }
+
+       return bpf_iter_run_prog(prog, &ctx);
+}
+
+static int bpf_array_map_seq_show(struct seq_file *seq, void *v)
+{
+       return __bpf_array_map_seq_show(seq, v);
+}
+
+static void bpf_array_map_seq_stop(struct seq_file *seq, void *v)
+{
+       if (!v)
+               (void)__bpf_array_map_seq_show(seq, NULL);
+}
+
+static int bpf_iter_init_array_map(void *priv_data,
+                                  struct bpf_iter_aux_info *aux)
+{
+       struct bpf_iter_seq_array_map_info *seq_info = priv_data;
+       struct bpf_map *map = aux->map;
+       void *value_buf;
+       u32 buf_size;
+
+       if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) {
+               buf_size = round_up(map->value_size, 8) * num_possible_cpus();
+               value_buf = kmalloc(buf_size, GFP_USER | __GFP_NOWARN);
+               if (!value_buf)
+                       return -ENOMEM;
+
+               seq_info->percpu_value_buf = value_buf;
+       }
+
+       seq_info->map = map;
+       return 0;
+}
+
+static void bpf_iter_fini_array_map(void *priv_data)
+{
+       struct bpf_iter_seq_array_map_info *seq_info = priv_data;
+
+       kfree(seq_info->percpu_value_buf);
+}
+
+static const struct seq_operations bpf_array_map_seq_ops = {
+       .start  = bpf_array_map_seq_start,
+       .next   = bpf_array_map_seq_next,
+       .stop   = bpf_array_map_seq_stop,
+       .show   = bpf_array_map_seq_show,
+};
+
+static const struct bpf_iter_seq_info iter_seq_info = {
+       .seq_ops                = &bpf_array_map_seq_ops,
+       .init_seq_private       = bpf_iter_init_array_map,
+       .fini_seq_private       = bpf_iter_fini_array_map,
+       .seq_priv_size          = sizeof(struct bpf_iter_seq_array_map_info),
+};
+
 static int array_map_btf_id;
 const struct bpf_map_ops array_map_ops = {
        .map_alloc_check = array_map_alloc_check,
@@ -506,6 +642,7 @@ const struct bpf_map_ops array_map_ops = {
        .map_update_batch = generic_map_update_batch,
        .map_btf_name = "bpf_array",
        .map_btf_id = &array_map_btf_id,
+       .iter_seq_info = &iter_seq_info,
 };
 
 static int percpu_array_map_btf_id;
@@ -521,6 +658,7 @@ const struct bpf_map_ops percpu_array_map_ops = {
        .map_check_btf = array_map_check_btf,
        .map_btf_name = "bpf_array",
        .map_btf_id = &percpu_array_map_btf_id,
+       .iter_seq_info = &iter_seq_info,
 };
 
 static int fd_array_map_alloc_check(union bpf_attr *attr)
index bcb68b5..fbe1f55 100644 (file)
@@ -106,10 +106,12 @@ static int bpf_iter_check_map(struct bpf_prog *prog,
        bool is_percpu = false;
 
        if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH ||
-           map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH)
+           map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH ||
+           map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY)
                is_percpu = true;
        else if (map->map_type != BPF_MAP_TYPE_HASH &&
-                map->map_type != BPF_MAP_TYPE_LRU_HASH)
+                map->map_type != BPF_MAP_TYPE_LRU_HASH &&
+                map->map_type != BPF_MAP_TYPE_ARRAY)
                return -EINVAL;
 
        key_acc_size = prog->aux->max_rdonly_access;