| 1 | |
| 2 | |
| 3 |
|
| 4 | #include <linux/slab.h>
|
| 5 | #include <linux/bpf.h>
|
| 6 | #include <linux/btf.h>
|
| 7 |
|
| 8 | #include "map_in_map.h"
|
| 9 |
|
| 10 | struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
|
| 11 | {
|
| 12 | struct bpf_map *inner_map, *inner_map_meta;
|
| 13 | u32 inner_map_meta_size;
|
| 14 | CLASS(fd, f)(inner_map_ufd);
|
| 15 |
|
| 16 | inner_map = __bpf_map_get(f);
|
| 17 | if (IS_ERR(inner_map))
|
| 18 | return inner_map;
|
| 19 |
|
| 20 |
|
| 21 | if (inner_map->inner_map_meta)
|
| 22 | return ERR_PTR(-EINVAL);
|
| 23 | if (inner_map->excl_prog_sha)
|
| 24 | return ERR_PTR(-ENOTSUPP);
|
| 25 | if (!inner_map->ops->map_meta_equal)
|
| 26 | return ERR_PTR(-ENOTSUPP);
|
| 27 |
|
| 28 | inner_map_meta_size = sizeof(*inner_map_meta);
|
| 29 |
|
| 30 | if (inner_map->ops == &array_map_ops || inner_map->ops == &percpu_array_map_ops)
|
| 31 | inner_map_meta_size = sizeof(struct bpf_array);
|
| 32 |
|
| 33 | inner_map_meta = kzalloc(inner_map_meta_size, GFP_USER);
|
| 34 | if (!inner_map_meta)
|
| 35 | return ERR_PTR(-ENOMEM);
|
| 36 |
|
| 37 | inner_map_meta->map_type = inner_map->map_type;
|
| 38 | inner_map_meta->key_size = inner_map->key_size;
|
| 39 | inner_map_meta->value_size = inner_map->value_size;
|
| 40 | inner_map_meta->map_flags = inner_map->map_flags;
|
| 41 | inner_map_meta->max_entries = inner_map->max_entries;
|
| 42 |
|
| 43 | inner_map_meta->record = btf_record_dup(inner_map->record);
|
| 44 | if (IS_ERR(inner_map_meta->record)) {
|
| 45 | |
| 46 | * invalid/empty/valid, but ERR_PTR in case of errors. During
|
| 47 | * equality NULL or IS_ERR is equivalent.
|
| 48 |
|
| 49 | struct bpf_map *ret = ERR_CAST(inner_map_meta->record);
|
| 50 | kfree(inner_map_meta);
|
| 51 | return ret;
|
| 52 | }
|
| 53 | |
| 54 | * which relies on BTF being same for both maps, as some members like
|
| 55 | * record->fields.list_head have pointers like value_rec pointing into
|
| 56 | * inner_map->btf.
|
| 57 |
|
| 58 | if (inner_map->btf) {
|
| 59 | btf_get(inner_map->btf);
|
| 60 | inner_map_meta->btf = inner_map->btf;
|
| 61 | }
|
| 62 |
|
| 63 |
|
| 64 | inner_map_meta->ops = inner_map->ops;
|
| 65 | if (inner_map->ops == &array_map_ops || inner_map->ops == &percpu_array_map_ops) {
|
| 66 | struct bpf_array *inner_array_meta =
|
| 67 | container_of(inner_map_meta, struct bpf_array, map);
|
| 68 | struct bpf_array *inner_array = container_of(inner_map, struct bpf_array, map);
|
| 69 |
|
| 70 | inner_array_meta->index_mask = inner_array->index_mask;
|
| 71 | inner_array_meta->elem_size = inner_array->elem_size;
|
| 72 | inner_map_meta->bypass_spec_v1 = inner_map->bypass_spec_v1;
|
| 73 | }
|
| 74 | return inner_map_meta;
|
| 75 | }
|
| 76 |
|
| 77 | void bpf_map_meta_free(struct bpf_map *map_meta)
|
| 78 | {
|
| 79 | bpf_map_free_record(map_meta);
|
| 80 | btf_put(map_meta->btf);
|
| 81 | kfree(map_meta);
|
| 82 | }
|
| 83 |
|
| 84 | bool bpf_map_meta_equal(const struct bpf_map *meta0,
|
| 85 | const struct bpf_map *meta1)
|
| 86 | {
|
| 87 |
|
| 88 | return meta0->map_type == meta1->map_type &&
|
| 89 | meta0->key_size == meta1->key_size &&
|
| 90 | meta0->value_size == meta1->value_size &&
|
| 91 | meta0->map_flags == meta1->map_flags &&
|
| 92 | btf_record_equal(meta0->record, meta1->record);
|
| 93 | }
|
| 94 |
|
| 95 | void *bpf_map_fd_get_ptr(struct bpf_map *map,
|
| 96 | struct file *map_file ,
|
| 97 | int ufd)
|
| 98 | {
|
| 99 | struct bpf_map *inner_map, *inner_map_meta;
|
| 100 | CLASS(fd, f)(ufd);
|
| 101 |
|
| 102 | inner_map = __bpf_map_get(f);
|
| 103 | if (IS_ERR(inner_map))
|
| 104 | return inner_map;
|
| 105 | if (inner_map->excl_prog_sha)
|
| 106 | return ERR_PTR(-ENOTSUPP);
|
| 107 |
|
| 108 | inner_map_meta = map->inner_map_meta;
|
| 109 | if (inner_map_meta->ops->map_meta_equal(inner_map_meta, inner_map))
|
| 110 | bpf_map_inc(inner_map);
|
| 111 | else
|
| 112 | inner_map = ERR_PTR(-EINVAL);
|
| 113 |
|
| 114 | return inner_map;
|
| 115 | }
|
| 116 |
|
| 117 | void bpf_map_fd_put_ptr(struct bpf_map *map, void *ptr, bool need_defer)
|
| 118 | {
|
| 119 | struct bpf_map *inner_map = ptr;
|
| 120 |
|
| 121 | |
| 122 | * of bpf program which owns the outer map, so unnecessary waiting for
|
| 123 | * RCU tasks trace grace period can be avoided.
|
| 124 |
|
| 125 | if (need_defer) {
|
| 126 | if (atomic64_read(&map->sleepable_refcnt))
|
| 127 | WRITE_ONCE(inner_map->free_after_mult_rcu_gp, true);
|
| 128 | else
|
| 129 | WRITE_ONCE(inner_map->free_after_rcu_gp, true);
|
| 130 | }
|
| 131 | bpf_map_put(inner_map);
|
| 132 | }
|
| 133 |
|
| 134 | u32 bpf_map_fd_sys_lookup_elem(void *ptr)
|
| 135 | {
|
| 136 | return ((struct bpf_map *)ptr)->id;
|
| 137 | }
|