diff options
author | Martin KaFai Lau <kafai@fb.com> | 2017-03-22 13:00:33 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-03-22 18:45:45 -0400 |
commit | 56f668dfe00dcf086734f1c42ea999398fad6572 (patch) | |
tree | d233ae9458347dccd529e8be12e24d73bcb13617 /kernel/bpf/arraymap.c | |
parent | fad73a1a35ea61f13607a391aca669caad8c04ca (diff) |
bpf: Add array of maps support
This patch adds a few helper funcs to enable map-in-map
support (i.e. outer_map->inner_map). The first outer_map type
BPF_MAP_TYPE_ARRAY_OF_MAPS is also added in this patch.
The next patch will introduce a hash of maps type.
Any bpf map type can be acted as an inner_map. The exception
is BPF_MAP_TYPE_PROG_ARRAY because the extra level of
indirection makes it harder to verify the owner_prog_type
and owner_jited.
Multi-level map-in-map is not supported (i.e. map->map is ok
but not map->map->map).
When adding an inner_map to an outer_map, it currently checks the
map_type, key_size, value_size, map_flags, max_entries and ops.
The verifier also uses those map's properties to do static analysis.
map_flags is needed because we need to ensure BPF_PROG_TYPE_PERF_EVENT
is using a preallocated hashtab for the inner_hash also. ops and
max_entries are needed to generate inlined map-lookup instructions.
For simplicity reason, a simple '==' test is used for both map_flags
and max_entries. The equality of ops is implied by the equality of
map_type.
During outer_map creation time, an inner_map_fd is needed to create an
outer_map. However, the inner_map_fd's life time does not depend on the
outer_map. The inner_map_fd is merely used to initialize
the inner_map_meta of the outer_map.
Also, for the outer_map:
* It allows element update and delete from syscall
* It allows element lookup from bpf_prog
The above is similar to the current fd_array pattern.
Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'kernel/bpf/arraymap.c')
-rw-r--r-- | kernel/bpf/arraymap.c | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 4d7d5d0ed76a..bc9da93db403 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <linux/filter.h> | 17 | #include <linux/filter.h> |
18 | #include <linux/perf_event.h> | 18 | #include <linux/perf_event.h> |
19 | 19 | ||
20 | #include "map_in_map.h" | ||
21 | |||
20 | static void bpf_array_free_percpu(struct bpf_array *array) | 22 | static void bpf_array_free_percpu(struct bpf_array *array) |
21 | { | 23 | { |
22 | int i; | 24 | int i; |
@@ -602,3 +604,64 @@ static int __init register_cgroup_array_map(void) | |||
602 | } | 604 | } |
603 | late_initcall(register_cgroup_array_map); | 605 | late_initcall(register_cgroup_array_map); |
604 | #endif | 606 | #endif |
607 | |||
608 | static struct bpf_map *array_of_map_alloc(union bpf_attr *attr) | ||
609 | { | ||
610 | struct bpf_map *map, *inner_map_meta; | ||
611 | |||
612 | inner_map_meta = bpf_map_meta_alloc(attr->inner_map_fd); | ||
613 | if (IS_ERR(inner_map_meta)) | ||
614 | return inner_map_meta; | ||
615 | |||
616 | map = fd_array_map_alloc(attr); | ||
617 | if (IS_ERR(map)) { | ||
618 | bpf_map_meta_free(inner_map_meta); | ||
619 | return map; | ||
620 | } | ||
621 | |||
622 | map->inner_map_meta = inner_map_meta; | ||
623 | |||
624 | return map; | ||
625 | } | ||
626 | |||
627 | static void array_of_map_free(struct bpf_map *map) | ||
628 | { | ||
629 | /* map->inner_map_meta is only accessed by syscall which | ||
630 | * is protected by fdget/fdput. | ||
631 | */ | ||
632 | bpf_map_meta_free(map->inner_map_meta); | ||
633 | bpf_fd_array_map_clear(map); | ||
634 | fd_array_map_free(map); | ||
635 | } | ||
636 | |||
637 | static void *array_of_map_lookup_elem(struct bpf_map *map, void *key) | ||
638 | { | ||
639 | struct bpf_map **inner_map = array_map_lookup_elem(map, key); | ||
640 | |||
641 | if (!inner_map) | ||
642 | return NULL; | ||
643 | |||
644 | return READ_ONCE(*inner_map); | ||
645 | } | ||
646 | |||
647 | static const struct bpf_map_ops array_of_map_ops = { | ||
648 | .map_alloc = array_of_map_alloc, | ||
649 | .map_free = array_of_map_free, | ||
650 | .map_get_next_key = array_map_get_next_key, | ||
651 | .map_lookup_elem = array_of_map_lookup_elem, | ||
652 | .map_delete_elem = fd_array_map_delete_elem, | ||
653 | .map_fd_get_ptr = bpf_map_fd_get_ptr, | ||
654 | .map_fd_put_ptr = bpf_map_fd_put_ptr, | ||
655 | }; | ||
656 | |||
657 | static struct bpf_map_type_list array_of_map_type __ro_after_init = { | ||
658 | .ops = &array_of_map_ops, | ||
659 | .type = BPF_MAP_TYPE_ARRAY_OF_MAPS, | ||
660 | }; | ||
661 | |||
662 | static int __init register_array_of_map(void) | ||
663 | { | ||
664 | bpf_register_map_type(&array_of_map_type); | ||
665 | return 0; | ||
666 | } | ||
667 | late_initcall(register_array_of_map); | ||