xref: /linux-6.15/kernel/bpf/arraymap.c (revision 32852649)
128fbcfa0SAlexei Starovoitov /* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
281ed18abSAlexei Starovoitov  * Copyright (c) 2016,2017 Facebook
328fbcfa0SAlexei Starovoitov  *
428fbcfa0SAlexei Starovoitov  * This program is free software; you can redistribute it and/or
528fbcfa0SAlexei Starovoitov  * modify it under the terms of version 2 of the GNU General Public
628fbcfa0SAlexei Starovoitov  * License as published by the Free Software Foundation.
728fbcfa0SAlexei Starovoitov  *
828fbcfa0SAlexei Starovoitov  * This program is distributed in the hope that it will be useful, but
928fbcfa0SAlexei Starovoitov  * WITHOUT ANY WARRANTY; without even the implied warranty of
1028fbcfa0SAlexei Starovoitov  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1128fbcfa0SAlexei Starovoitov  * General Public License for more details.
1228fbcfa0SAlexei Starovoitov  */
1328fbcfa0SAlexei Starovoitov #include <linux/bpf.h>
1428fbcfa0SAlexei Starovoitov #include <linux/err.h>
1528fbcfa0SAlexei Starovoitov #include <linux/slab.h>
1628fbcfa0SAlexei Starovoitov #include <linux/mm.h>
1704fd61abSAlexei Starovoitov #include <linux/filter.h>
180cdf5640SDaniel Borkmann #include <linux/perf_event.h>
1928fbcfa0SAlexei Starovoitov 
2056f668dfSMartin KaFai Lau #include "map_in_map.h"
2156f668dfSMartin KaFai Lau 
226e71b04aSChenbo Feng #define ARRAY_CREATE_FLAG_MASK \
236e71b04aSChenbo Feng 	(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
246e71b04aSChenbo Feng 
25a10423b8SAlexei Starovoitov static void bpf_array_free_percpu(struct bpf_array *array)
26a10423b8SAlexei Starovoitov {
27a10423b8SAlexei Starovoitov 	int i;
28a10423b8SAlexei Starovoitov 
29a10423b8SAlexei Starovoitov 	for (i = 0; i < array->map.max_entries; i++)
30a10423b8SAlexei Starovoitov 		free_percpu(array->pptrs[i]);
31a10423b8SAlexei Starovoitov }
32a10423b8SAlexei Starovoitov 
33a10423b8SAlexei Starovoitov static int bpf_array_alloc_percpu(struct bpf_array *array)
34a10423b8SAlexei Starovoitov {
35a10423b8SAlexei Starovoitov 	void __percpu *ptr;
36a10423b8SAlexei Starovoitov 	int i;
37a10423b8SAlexei Starovoitov 
38a10423b8SAlexei Starovoitov 	for (i = 0; i < array->map.max_entries; i++) {
39a10423b8SAlexei Starovoitov 		ptr = __alloc_percpu_gfp(array->elem_size, 8,
40a10423b8SAlexei Starovoitov 					 GFP_USER | __GFP_NOWARN);
41a10423b8SAlexei Starovoitov 		if (!ptr) {
42a10423b8SAlexei Starovoitov 			bpf_array_free_percpu(array);
43a10423b8SAlexei Starovoitov 			return -ENOMEM;
44a10423b8SAlexei Starovoitov 		}
45a10423b8SAlexei Starovoitov 		array->pptrs[i] = ptr;
46a10423b8SAlexei Starovoitov 	}
47a10423b8SAlexei Starovoitov 
48a10423b8SAlexei Starovoitov 	return 0;
49a10423b8SAlexei Starovoitov }
50a10423b8SAlexei Starovoitov 
5128fbcfa0SAlexei Starovoitov /* Called from syscall */
52ad46061fSJakub Kicinski static int array_map_alloc_check(union bpf_attr *attr)
53ad46061fSJakub Kicinski {
54ad46061fSJakub Kicinski 	bool percpu = attr->map_type == BPF_MAP_TYPE_PERCPU_ARRAY;
55ad46061fSJakub Kicinski 	int numa_node = bpf_map_attr_numa_node(attr);
56ad46061fSJakub Kicinski 
57ad46061fSJakub Kicinski 	/* check sanity of attributes */
58ad46061fSJakub Kicinski 	if (attr->max_entries == 0 || attr->key_size != 4 ||
59ad46061fSJakub Kicinski 	    attr->value_size == 0 ||
60ad46061fSJakub Kicinski 	    attr->map_flags & ~ARRAY_CREATE_FLAG_MASK ||
61ad46061fSJakub Kicinski 	    (percpu && numa_node != NUMA_NO_NODE))
62ad46061fSJakub Kicinski 		return -EINVAL;
63ad46061fSJakub Kicinski 
64ad46061fSJakub Kicinski 	if (attr->value_size > KMALLOC_MAX_SIZE)
65ad46061fSJakub Kicinski 		/* if value_size is bigger, the user space won't be able to
66ad46061fSJakub Kicinski 		 * access the elements.
67ad46061fSJakub Kicinski 		 */
68ad46061fSJakub Kicinski 		return -E2BIG;
69ad46061fSJakub Kicinski 
70ad46061fSJakub Kicinski 	return 0;
71ad46061fSJakub Kicinski }
72ad46061fSJakub Kicinski 
7328fbcfa0SAlexei Starovoitov static struct bpf_map *array_map_alloc(union bpf_attr *attr)
7428fbcfa0SAlexei Starovoitov {
75a10423b8SAlexei Starovoitov 	bool percpu = attr->map_type == BPF_MAP_TYPE_PERCPU_ARRAY;
7696eabe7aSMartin KaFai Lau 	int numa_node = bpf_map_attr_numa_node(attr);
77b2157399SAlexei Starovoitov 	u32 elem_size, index_mask, max_entries;
78b2157399SAlexei Starovoitov 	bool unpriv = !capable(CAP_SYS_ADMIN);
7928fbcfa0SAlexei Starovoitov 	struct bpf_array *array;
80bbeb6e43SDaniel Borkmann 	u64 array_size, mask64;
8128fbcfa0SAlexei Starovoitov 
8228fbcfa0SAlexei Starovoitov 	elem_size = round_up(attr->value_size, 8);
8328fbcfa0SAlexei Starovoitov 
84b2157399SAlexei Starovoitov 	max_entries = attr->max_entries;
85b2157399SAlexei Starovoitov 
86bbeb6e43SDaniel Borkmann 	/* On 32 bit archs roundup_pow_of_two() with max_entries that has
87bbeb6e43SDaniel Borkmann 	 * upper most bit set in u32 space is undefined behavior due to
88bbeb6e43SDaniel Borkmann 	 * resulting 1U << 32, so do it manually here in u64 space.
89bbeb6e43SDaniel Borkmann 	 */
90bbeb6e43SDaniel Borkmann 	mask64 = fls_long(max_entries - 1);
91bbeb6e43SDaniel Borkmann 	mask64 = 1ULL << mask64;
92bbeb6e43SDaniel Borkmann 	mask64 -= 1;
93bbeb6e43SDaniel Borkmann 
94bbeb6e43SDaniel Borkmann 	index_mask = mask64;
95bbeb6e43SDaniel Borkmann 	if (unpriv) {
96b2157399SAlexei Starovoitov 		/* round up array size to nearest power of 2,
97b2157399SAlexei Starovoitov 		 * since cpu will speculate within index_mask limits
98b2157399SAlexei Starovoitov 		 */
99b2157399SAlexei Starovoitov 		max_entries = index_mask + 1;
100bbeb6e43SDaniel Borkmann 		/* Check for overflows. */
101bbeb6e43SDaniel Borkmann 		if (max_entries < attr->max_entries)
102bbeb6e43SDaniel Borkmann 			return ERR_PTR(-E2BIG);
103bbeb6e43SDaniel Borkmann 	}
104b2157399SAlexei Starovoitov 
105a10423b8SAlexei Starovoitov 	array_size = sizeof(*array);
106a10423b8SAlexei Starovoitov 	if (percpu)
107b2157399SAlexei Starovoitov 		array_size += (u64) max_entries * sizeof(void *);
108a10423b8SAlexei Starovoitov 	else
109b2157399SAlexei Starovoitov 		array_size += (u64) max_entries * elem_size;
110a10423b8SAlexei Starovoitov 
111a10423b8SAlexei Starovoitov 	/* make sure there is no u32 overflow later in round_up() */
112a10423b8SAlexei Starovoitov 	if (array_size >= U32_MAX - PAGE_SIZE)
113daaf427cSAlexei Starovoitov 		return ERR_PTR(-ENOMEM);
114daaf427cSAlexei Starovoitov 
11528fbcfa0SAlexei Starovoitov 	/* allocate all map elements and zero-initialize them */
11696eabe7aSMartin KaFai Lau 	array = bpf_map_area_alloc(array_size, numa_node);
11728fbcfa0SAlexei Starovoitov 	if (!array)
11828fbcfa0SAlexei Starovoitov 		return ERR_PTR(-ENOMEM);
119b2157399SAlexei Starovoitov 	array->index_mask = index_mask;
120b2157399SAlexei Starovoitov 	array->map.unpriv_array = unpriv;
12128fbcfa0SAlexei Starovoitov 
12228fbcfa0SAlexei Starovoitov 	/* copy mandatory map attributes */
123*32852649SJakub Kicinski 	bpf_map_init_from_attr(&array->map, attr);
12428fbcfa0SAlexei Starovoitov 	array->elem_size = elem_size;
12528fbcfa0SAlexei Starovoitov 
126a10423b8SAlexei Starovoitov 	if (!percpu)
127a10423b8SAlexei Starovoitov 		goto out;
128a10423b8SAlexei Starovoitov 
129a10423b8SAlexei Starovoitov 	array_size += (u64) attr->max_entries * elem_size * num_possible_cpus();
130a10423b8SAlexei Starovoitov 
131a10423b8SAlexei Starovoitov 	if (array_size >= U32_MAX - PAGE_SIZE ||
132bc6d5031SDaniel Borkmann 	    bpf_array_alloc_percpu(array)) {
133d407bd25SDaniel Borkmann 		bpf_map_area_free(array);
134a10423b8SAlexei Starovoitov 		return ERR_PTR(-ENOMEM);
135a10423b8SAlexei Starovoitov 	}
136a10423b8SAlexei Starovoitov out:
137a10423b8SAlexei Starovoitov 	array->map.pages = round_up(array_size, PAGE_SIZE) >> PAGE_SHIFT;
138a10423b8SAlexei Starovoitov 
13928fbcfa0SAlexei Starovoitov 	return &array->map;
14028fbcfa0SAlexei Starovoitov }
14128fbcfa0SAlexei Starovoitov 
14228fbcfa0SAlexei Starovoitov /* Called from syscall or from eBPF program */
14328fbcfa0SAlexei Starovoitov static void *array_map_lookup_elem(struct bpf_map *map, void *key)
14428fbcfa0SAlexei Starovoitov {
14528fbcfa0SAlexei Starovoitov 	struct bpf_array *array = container_of(map, struct bpf_array, map);
14628fbcfa0SAlexei Starovoitov 	u32 index = *(u32 *)key;
14728fbcfa0SAlexei Starovoitov 
148a10423b8SAlexei Starovoitov 	if (unlikely(index >= array->map.max_entries))
14928fbcfa0SAlexei Starovoitov 		return NULL;
15028fbcfa0SAlexei Starovoitov 
151b2157399SAlexei Starovoitov 	return array->value + array->elem_size * (index & array->index_mask);
15228fbcfa0SAlexei Starovoitov }
15328fbcfa0SAlexei Starovoitov 
15481ed18abSAlexei Starovoitov /* emit BPF instructions equivalent to C code of array_map_lookup_elem() */
15581ed18abSAlexei Starovoitov static u32 array_map_gen_lookup(struct bpf_map *map, struct bpf_insn *insn_buf)
15681ed18abSAlexei Starovoitov {
157b2157399SAlexei Starovoitov 	struct bpf_array *array = container_of(map, struct bpf_array, map);
15881ed18abSAlexei Starovoitov 	struct bpf_insn *insn = insn_buf;
159fad73a1aSMartin KaFai Lau 	u32 elem_size = round_up(map->value_size, 8);
16081ed18abSAlexei Starovoitov 	const int ret = BPF_REG_0;
16181ed18abSAlexei Starovoitov 	const int map_ptr = BPF_REG_1;
16281ed18abSAlexei Starovoitov 	const int index = BPF_REG_2;
16381ed18abSAlexei Starovoitov 
16481ed18abSAlexei Starovoitov 	*insn++ = BPF_ALU64_IMM(BPF_ADD, map_ptr, offsetof(struct bpf_array, value));
16581ed18abSAlexei Starovoitov 	*insn++ = BPF_LDX_MEM(BPF_W, ret, index, 0);
166b2157399SAlexei Starovoitov 	if (map->unpriv_array) {
167b2157399SAlexei Starovoitov 		*insn++ = BPF_JMP_IMM(BPF_JGE, ret, map->max_entries, 4);
168b2157399SAlexei Starovoitov 		*insn++ = BPF_ALU32_IMM(BPF_AND, ret, array->index_mask);
169b2157399SAlexei Starovoitov 	} else {
170fad73a1aSMartin KaFai Lau 		*insn++ = BPF_JMP_IMM(BPF_JGE, ret, map->max_entries, 3);
171b2157399SAlexei Starovoitov 	}
172fad73a1aSMartin KaFai Lau 
173fad73a1aSMartin KaFai Lau 	if (is_power_of_2(elem_size)) {
17481ed18abSAlexei Starovoitov 		*insn++ = BPF_ALU64_IMM(BPF_LSH, ret, ilog2(elem_size));
17581ed18abSAlexei Starovoitov 	} else {
17681ed18abSAlexei Starovoitov 		*insn++ = BPF_ALU64_IMM(BPF_MUL, ret, elem_size);
17781ed18abSAlexei Starovoitov 	}
17881ed18abSAlexei Starovoitov 	*insn++ = BPF_ALU64_REG(BPF_ADD, ret, map_ptr);
17981ed18abSAlexei Starovoitov 	*insn++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);
18081ed18abSAlexei Starovoitov 	*insn++ = BPF_MOV64_IMM(ret, 0);
18181ed18abSAlexei Starovoitov 	return insn - insn_buf;
18281ed18abSAlexei Starovoitov }
18381ed18abSAlexei Starovoitov 
184a10423b8SAlexei Starovoitov /* Called from eBPF program */
185a10423b8SAlexei Starovoitov static void *percpu_array_map_lookup_elem(struct bpf_map *map, void *key)
186a10423b8SAlexei Starovoitov {
187a10423b8SAlexei Starovoitov 	struct bpf_array *array = container_of(map, struct bpf_array, map);
188a10423b8SAlexei Starovoitov 	u32 index = *(u32 *)key;
189a10423b8SAlexei Starovoitov 
190a10423b8SAlexei Starovoitov 	if (unlikely(index >= array->map.max_entries))
191a10423b8SAlexei Starovoitov 		return NULL;
192a10423b8SAlexei Starovoitov 
193b2157399SAlexei Starovoitov 	return this_cpu_ptr(array->pptrs[index & array->index_mask]);
194a10423b8SAlexei Starovoitov }
195a10423b8SAlexei Starovoitov 
19615a07b33SAlexei Starovoitov int bpf_percpu_array_copy(struct bpf_map *map, void *key, void *value)
19715a07b33SAlexei Starovoitov {
19815a07b33SAlexei Starovoitov 	struct bpf_array *array = container_of(map, struct bpf_array, map);
19915a07b33SAlexei Starovoitov 	u32 index = *(u32 *)key;
20015a07b33SAlexei Starovoitov 	void __percpu *pptr;
20115a07b33SAlexei Starovoitov 	int cpu, off = 0;
20215a07b33SAlexei Starovoitov 	u32 size;
20315a07b33SAlexei Starovoitov 
20415a07b33SAlexei Starovoitov 	if (unlikely(index >= array->map.max_entries))
20515a07b33SAlexei Starovoitov 		return -ENOENT;
20615a07b33SAlexei Starovoitov 
20715a07b33SAlexei Starovoitov 	/* per_cpu areas are zero-filled and bpf programs can only
20815a07b33SAlexei Starovoitov 	 * access 'value_size' of them, so copying rounded areas
20915a07b33SAlexei Starovoitov 	 * will not leak any kernel data
21015a07b33SAlexei Starovoitov 	 */
21115a07b33SAlexei Starovoitov 	size = round_up(map->value_size, 8);
21215a07b33SAlexei Starovoitov 	rcu_read_lock();
213b2157399SAlexei Starovoitov 	pptr = array->pptrs[index & array->index_mask];
21415a07b33SAlexei Starovoitov 	for_each_possible_cpu(cpu) {
21515a07b33SAlexei Starovoitov 		bpf_long_memcpy(value + off, per_cpu_ptr(pptr, cpu), size);
21615a07b33SAlexei Starovoitov 		off += size;
21715a07b33SAlexei Starovoitov 	}
21815a07b33SAlexei Starovoitov 	rcu_read_unlock();
21915a07b33SAlexei Starovoitov 	return 0;
22015a07b33SAlexei Starovoitov }
22115a07b33SAlexei Starovoitov 
22228fbcfa0SAlexei Starovoitov /* Called from syscall */
22328fbcfa0SAlexei Starovoitov static int array_map_get_next_key(struct bpf_map *map, void *key, void *next_key)
22428fbcfa0SAlexei Starovoitov {
22528fbcfa0SAlexei Starovoitov 	struct bpf_array *array = container_of(map, struct bpf_array, map);
2268fe45924STeng Qin 	u32 index = key ? *(u32 *)key : U32_MAX;
22728fbcfa0SAlexei Starovoitov 	u32 *next = (u32 *)next_key;
22828fbcfa0SAlexei Starovoitov 
22928fbcfa0SAlexei Starovoitov 	if (index >= array->map.max_entries) {
23028fbcfa0SAlexei Starovoitov 		*next = 0;
23128fbcfa0SAlexei Starovoitov 		return 0;
23228fbcfa0SAlexei Starovoitov 	}
23328fbcfa0SAlexei Starovoitov 
23428fbcfa0SAlexei Starovoitov 	if (index == array->map.max_entries - 1)
23528fbcfa0SAlexei Starovoitov 		return -ENOENT;
23628fbcfa0SAlexei Starovoitov 
23728fbcfa0SAlexei Starovoitov 	*next = index + 1;
23828fbcfa0SAlexei Starovoitov 	return 0;
23928fbcfa0SAlexei Starovoitov }
24028fbcfa0SAlexei Starovoitov 
24128fbcfa0SAlexei Starovoitov /* Called from syscall or from eBPF program */
24228fbcfa0SAlexei Starovoitov static int array_map_update_elem(struct bpf_map *map, void *key, void *value,
24328fbcfa0SAlexei Starovoitov 				 u64 map_flags)
24428fbcfa0SAlexei Starovoitov {
24528fbcfa0SAlexei Starovoitov 	struct bpf_array *array = container_of(map, struct bpf_array, map);
24628fbcfa0SAlexei Starovoitov 	u32 index = *(u32 *)key;
24728fbcfa0SAlexei Starovoitov 
248a10423b8SAlexei Starovoitov 	if (unlikely(map_flags > BPF_EXIST))
24928fbcfa0SAlexei Starovoitov 		/* unknown flags */
25028fbcfa0SAlexei Starovoitov 		return -EINVAL;
25128fbcfa0SAlexei Starovoitov 
252a10423b8SAlexei Starovoitov 	if (unlikely(index >= array->map.max_entries))
25328fbcfa0SAlexei Starovoitov 		/* all elements were pre-allocated, cannot insert a new one */
25428fbcfa0SAlexei Starovoitov 		return -E2BIG;
25528fbcfa0SAlexei Starovoitov 
256a10423b8SAlexei Starovoitov 	if (unlikely(map_flags == BPF_NOEXIST))
257daaf427cSAlexei Starovoitov 		/* all elements already exist */
25828fbcfa0SAlexei Starovoitov 		return -EEXIST;
25928fbcfa0SAlexei Starovoitov 
260a10423b8SAlexei Starovoitov 	if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY)
261b2157399SAlexei Starovoitov 		memcpy(this_cpu_ptr(array->pptrs[index & array->index_mask]),
262a10423b8SAlexei Starovoitov 		       value, map->value_size);
263a10423b8SAlexei Starovoitov 	else
264b2157399SAlexei Starovoitov 		memcpy(array->value +
265b2157399SAlexei Starovoitov 		       array->elem_size * (index & array->index_mask),
266a10423b8SAlexei Starovoitov 		       value, map->value_size);
26728fbcfa0SAlexei Starovoitov 	return 0;
26828fbcfa0SAlexei Starovoitov }
26928fbcfa0SAlexei Starovoitov 
27015a07b33SAlexei Starovoitov int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value,
27115a07b33SAlexei Starovoitov 			    u64 map_flags)
27215a07b33SAlexei Starovoitov {
27315a07b33SAlexei Starovoitov 	struct bpf_array *array = container_of(map, struct bpf_array, map);
27415a07b33SAlexei Starovoitov 	u32 index = *(u32 *)key;
27515a07b33SAlexei Starovoitov 	void __percpu *pptr;
27615a07b33SAlexei Starovoitov 	int cpu, off = 0;
27715a07b33SAlexei Starovoitov 	u32 size;
27815a07b33SAlexei Starovoitov 
27915a07b33SAlexei Starovoitov 	if (unlikely(map_flags > BPF_EXIST))
28015a07b33SAlexei Starovoitov 		/* unknown flags */
28115a07b33SAlexei Starovoitov 		return -EINVAL;
28215a07b33SAlexei Starovoitov 
28315a07b33SAlexei Starovoitov 	if (unlikely(index >= array->map.max_entries))
28415a07b33SAlexei Starovoitov 		/* all elements were pre-allocated, cannot insert a new one */
28515a07b33SAlexei Starovoitov 		return -E2BIG;
28615a07b33SAlexei Starovoitov 
28715a07b33SAlexei Starovoitov 	if (unlikely(map_flags == BPF_NOEXIST))
28815a07b33SAlexei Starovoitov 		/* all elements already exist */
28915a07b33SAlexei Starovoitov 		return -EEXIST;
29015a07b33SAlexei Starovoitov 
29115a07b33SAlexei Starovoitov 	/* the user space will provide round_up(value_size, 8) bytes that
29215a07b33SAlexei Starovoitov 	 * will be copied into per-cpu area. bpf programs can only access
29315a07b33SAlexei Starovoitov 	 * value_size of it. During lookup the same extra bytes will be
29415a07b33SAlexei Starovoitov 	 * returned or zeros which were zero-filled by percpu_alloc,
29515a07b33SAlexei Starovoitov 	 * so no kernel data leaks possible
29615a07b33SAlexei Starovoitov 	 */
29715a07b33SAlexei Starovoitov 	size = round_up(map->value_size, 8);
29815a07b33SAlexei Starovoitov 	rcu_read_lock();
299b2157399SAlexei Starovoitov 	pptr = array->pptrs[index & array->index_mask];
30015a07b33SAlexei Starovoitov 	for_each_possible_cpu(cpu) {
30115a07b33SAlexei Starovoitov 		bpf_long_memcpy(per_cpu_ptr(pptr, cpu), value + off, size);
30215a07b33SAlexei Starovoitov 		off += size;
30315a07b33SAlexei Starovoitov 	}
30415a07b33SAlexei Starovoitov 	rcu_read_unlock();
30515a07b33SAlexei Starovoitov 	return 0;
30615a07b33SAlexei Starovoitov }
30715a07b33SAlexei Starovoitov 
30828fbcfa0SAlexei Starovoitov /* Called from syscall or from eBPF program */
30928fbcfa0SAlexei Starovoitov static int array_map_delete_elem(struct bpf_map *map, void *key)
31028fbcfa0SAlexei Starovoitov {
31128fbcfa0SAlexei Starovoitov 	return -EINVAL;
31228fbcfa0SAlexei Starovoitov }
31328fbcfa0SAlexei Starovoitov 
31428fbcfa0SAlexei Starovoitov /* Called when map->refcnt goes to zero, either from workqueue or from syscall */
31528fbcfa0SAlexei Starovoitov static void array_map_free(struct bpf_map *map)
31628fbcfa0SAlexei Starovoitov {
31728fbcfa0SAlexei Starovoitov 	struct bpf_array *array = container_of(map, struct bpf_array, map);
31828fbcfa0SAlexei Starovoitov 
31928fbcfa0SAlexei Starovoitov 	/* at this point bpf_prog->aux->refcnt == 0 and this map->refcnt == 0,
32028fbcfa0SAlexei Starovoitov 	 * so the programs (can be more than one that used this map) were
32128fbcfa0SAlexei Starovoitov 	 * disconnected from events. Wait for outstanding programs to complete
32228fbcfa0SAlexei Starovoitov 	 * and free the array
32328fbcfa0SAlexei Starovoitov 	 */
32428fbcfa0SAlexei Starovoitov 	synchronize_rcu();
32528fbcfa0SAlexei Starovoitov 
326a10423b8SAlexei Starovoitov 	if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY)
327a10423b8SAlexei Starovoitov 		bpf_array_free_percpu(array);
328a10423b8SAlexei Starovoitov 
329d407bd25SDaniel Borkmann 	bpf_map_area_free(array);
33028fbcfa0SAlexei Starovoitov }
33128fbcfa0SAlexei Starovoitov 
33240077e0cSJohannes Berg const struct bpf_map_ops array_map_ops = {
333ad46061fSJakub Kicinski 	.map_alloc_check = array_map_alloc_check,
33428fbcfa0SAlexei Starovoitov 	.map_alloc = array_map_alloc,
33528fbcfa0SAlexei Starovoitov 	.map_free = array_map_free,
33628fbcfa0SAlexei Starovoitov 	.map_get_next_key = array_map_get_next_key,
33728fbcfa0SAlexei Starovoitov 	.map_lookup_elem = array_map_lookup_elem,
33828fbcfa0SAlexei Starovoitov 	.map_update_elem = array_map_update_elem,
33928fbcfa0SAlexei Starovoitov 	.map_delete_elem = array_map_delete_elem,
34081ed18abSAlexei Starovoitov 	.map_gen_lookup = array_map_gen_lookup,
34128fbcfa0SAlexei Starovoitov };
34228fbcfa0SAlexei Starovoitov 
34340077e0cSJohannes Berg const struct bpf_map_ops percpu_array_map_ops = {
344ad46061fSJakub Kicinski 	.map_alloc_check = array_map_alloc_check,
345a10423b8SAlexei Starovoitov 	.map_alloc = array_map_alloc,
346a10423b8SAlexei Starovoitov 	.map_free = array_map_free,
347a10423b8SAlexei Starovoitov 	.map_get_next_key = array_map_get_next_key,
348a10423b8SAlexei Starovoitov 	.map_lookup_elem = percpu_array_map_lookup_elem,
349a10423b8SAlexei Starovoitov 	.map_update_elem = array_map_update_elem,
350a10423b8SAlexei Starovoitov 	.map_delete_elem = array_map_delete_elem,
351a10423b8SAlexei Starovoitov };
352a10423b8SAlexei Starovoitov 
353ad46061fSJakub Kicinski static int fd_array_map_alloc_check(union bpf_attr *attr)
35404fd61abSAlexei Starovoitov {
3552a36f0b9SWang Nan 	/* only file descriptors can be stored in this type of map */
35604fd61abSAlexei Starovoitov 	if (attr->value_size != sizeof(u32))
357ad46061fSJakub Kicinski 		return -EINVAL;
358ad46061fSJakub Kicinski 	return array_map_alloc_check(attr);
35904fd61abSAlexei Starovoitov }
36004fd61abSAlexei Starovoitov 
3612a36f0b9SWang Nan static void fd_array_map_free(struct bpf_map *map)
36204fd61abSAlexei Starovoitov {
36304fd61abSAlexei Starovoitov 	struct bpf_array *array = container_of(map, struct bpf_array, map);
36404fd61abSAlexei Starovoitov 	int i;
36504fd61abSAlexei Starovoitov 
36604fd61abSAlexei Starovoitov 	synchronize_rcu();
36704fd61abSAlexei Starovoitov 
36804fd61abSAlexei Starovoitov 	/* make sure it's empty */
36904fd61abSAlexei Starovoitov 	for (i = 0; i < array->map.max_entries; i++)
3702a36f0b9SWang Nan 		BUG_ON(array->ptrs[i] != NULL);
371d407bd25SDaniel Borkmann 
372d407bd25SDaniel Borkmann 	bpf_map_area_free(array);
37304fd61abSAlexei Starovoitov }
37404fd61abSAlexei Starovoitov 
3752a36f0b9SWang Nan static void *fd_array_map_lookup_elem(struct bpf_map *map, void *key)
37604fd61abSAlexei Starovoitov {
37704fd61abSAlexei Starovoitov 	return NULL;
37804fd61abSAlexei Starovoitov }
37904fd61abSAlexei Starovoitov 
38004fd61abSAlexei Starovoitov /* only called from syscall */
38114dc6f04SMartin KaFai Lau int bpf_fd_array_map_lookup_elem(struct bpf_map *map, void *key, u32 *value)
38214dc6f04SMartin KaFai Lau {
38314dc6f04SMartin KaFai Lau 	void **elem, *ptr;
38414dc6f04SMartin KaFai Lau 	int ret =  0;
38514dc6f04SMartin KaFai Lau 
38614dc6f04SMartin KaFai Lau 	if (!map->ops->map_fd_sys_lookup_elem)
38714dc6f04SMartin KaFai Lau 		return -ENOTSUPP;
38814dc6f04SMartin KaFai Lau 
38914dc6f04SMartin KaFai Lau 	rcu_read_lock();
39014dc6f04SMartin KaFai Lau 	elem = array_map_lookup_elem(map, key);
39114dc6f04SMartin KaFai Lau 	if (elem && (ptr = READ_ONCE(*elem)))
39214dc6f04SMartin KaFai Lau 		*value = map->ops->map_fd_sys_lookup_elem(ptr);
39314dc6f04SMartin KaFai Lau 	else
39414dc6f04SMartin KaFai Lau 		ret = -ENOENT;
39514dc6f04SMartin KaFai Lau 	rcu_read_unlock();
39614dc6f04SMartin KaFai Lau 
39714dc6f04SMartin KaFai Lau 	return ret;
39814dc6f04SMartin KaFai Lau }
39914dc6f04SMartin KaFai Lau 
40014dc6f04SMartin KaFai Lau /* only called from syscall */
401d056a788SDaniel Borkmann int bpf_fd_array_map_update_elem(struct bpf_map *map, struct file *map_file,
402d056a788SDaniel Borkmann 				 void *key, void *value, u64 map_flags)
40304fd61abSAlexei Starovoitov {
40404fd61abSAlexei Starovoitov 	struct bpf_array *array = container_of(map, struct bpf_array, map);
4052a36f0b9SWang Nan 	void *new_ptr, *old_ptr;
40604fd61abSAlexei Starovoitov 	u32 index = *(u32 *)key, ufd;
40704fd61abSAlexei Starovoitov 
40804fd61abSAlexei Starovoitov 	if (map_flags != BPF_ANY)
40904fd61abSAlexei Starovoitov 		return -EINVAL;
41004fd61abSAlexei Starovoitov 
41104fd61abSAlexei Starovoitov 	if (index >= array->map.max_entries)
41204fd61abSAlexei Starovoitov 		return -E2BIG;
41304fd61abSAlexei Starovoitov 
41404fd61abSAlexei Starovoitov 	ufd = *(u32 *)value;
415d056a788SDaniel Borkmann 	new_ptr = map->ops->map_fd_get_ptr(map, map_file, ufd);
4162a36f0b9SWang Nan 	if (IS_ERR(new_ptr))
4172a36f0b9SWang Nan 		return PTR_ERR(new_ptr);
41804fd61abSAlexei Starovoitov 
4192a36f0b9SWang Nan 	old_ptr = xchg(array->ptrs + index, new_ptr);
4202a36f0b9SWang Nan 	if (old_ptr)
4212a36f0b9SWang Nan 		map->ops->map_fd_put_ptr(old_ptr);
42204fd61abSAlexei Starovoitov 
42304fd61abSAlexei Starovoitov 	return 0;
42404fd61abSAlexei Starovoitov }
42504fd61abSAlexei Starovoitov 
4262a36f0b9SWang Nan static int fd_array_map_delete_elem(struct bpf_map *map, void *key)
42704fd61abSAlexei Starovoitov {
42804fd61abSAlexei Starovoitov 	struct bpf_array *array = container_of(map, struct bpf_array, map);
4292a36f0b9SWang Nan 	void *old_ptr;
43004fd61abSAlexei Starovoitov 	u32 index = *(u32 *)key;
43104fd61abSAlexei Starovoitov 
43204fd61abSAlexei Starovoitov 	if (index >= array->map.max_entries)
43304fd61abSAlexei Starovoitov 		return -E2BIG;
43404fd61abSAlexei Starovoitov 
4352a36f0b9SWang Nan 	old_ptr = xchg(array->ptrs + index, NULL);
4362a36f0b9SWang Nan 	if (old_ptr) {
4372a36f0b9SWang Nan 		map->ops->map_fd_put_ptr(old_ptr);
43804fd61abSAlexei Starovoitov 		return 0;
43904fd61abSAlexei Starovoitov 	} else {
44004fd61abSAlexei Starovoitov 		return -ENOENT;
44104fd61abSAlexei Starovoitov 	}
44204fd61abSAlexei Starovoitov }
44304fd61abSAlexei Starovoitov 
444d056a788SDaniel Borkmann static void *prog_fd_array_get_ptr(struct bpf_map *map,
445d056a788SDaniel Borkmann 				   struct file *map_file, int fd)
4462a36f0b9SWang Nan {
4472a36f0b9SWang Nan 	struct bpf_array *array = container_of(map, struct bpf_array, map);
4482a36f0b9SWang Nan 	struct bpf_prog *prog = bpf_prog_get(fd);
449d056a788SDaniel Borkmann 
4502a36f0b9SWang Nan 	if (IS_ERR(prog))
4512a36f0b9SWang Nan 		return prog;
4522a36f0b9SWang Nan 
4532a36f0b9SWang Nan 	if (!bpf_prog_array_compatible(array, prog)) {
4542a36f0b9SWang Nan 		bpf_prog_put(prog);
4552a36f0b9SWang Nan 		return ERR_PTR(-EINVAL);
4562a36f0b9SWang Nan 	}
457d056a788SDaniel Borkmann 
4582a36f0b9SWang Nan 	return prog;
4592a36f0b9SWang Nan }
4602a36f0b9SWang Nan 
4612a36f0b9SWang Nan static void prog_fd_array_put_ptr(void *ptr)
4622a36f0b9SWang Nan {
4631aacde3dSDaniel Borkmann 	bpf_prog_put(ptr);
4642a36f0b9SWang Nan }
4652a36f0b9SWang Nan 
46614dc6f04SMartin KaFai Lau static u32 prog_fd_array_sys_lookup_elem(void *ptr)
46714dc6f04SMartin KaFai Lau {
46814dc6f04SMartin KaFai Lau 	return ((struct bpf_prog *)ptr)->aux->id;
46914dc6f04SMartin KaFai Lau }
47014dc6f04SMartin KaFai Lau 
47104fd61abSAlexei Starovoitov /* decrement refcnt of all bpf_progs that are stored in this map */
4722a36f0b9SWang Nan void bpf_fd_array_map_clear(struct bpf_map *map)
47304fd61abSAlexei Starovoitov {
47404fd61abSAlexei Starovoitov 	struct bpf_array *array = container_of(map, struct bpf_array, map);
47504fd61abSAlexei Starovoitov 	int i;
47604fd61abSAlexei Starovoitov 
47704fd61abSAlexei Starovoitov 	for (i = 0; i < array->map.max_entries; i++)
4782a36f0b9SWang Nan 		fd_array_map_delete_elem(map, &i);
47904fd61abSAlexei Starovoitov }
48004fd61abSAlexei Starovoitov 
48140077e0cSJohannes Berg const struct bpf_map_ops prog_array_map_ops = {
482ad46061fSJakub Kicinski 	.map_alloc_check = fd_array_map_alloc_check,
483ad46061fSJakub Kicinski 	.map_alloc = array_map_alloc,
4842a36f0b9SWang Nan 	.map_free = fd_array_map_free,
48504fd61abSAlexei Starovoitov 	.map_get_next_key = array_map_get_next_key,
4862a36f0b9SWang Nan 	.map_lookup_elem = fd_array_map_lookup_elem,
4872a36f0b9SWang Nan 	.map_delete_elem = fd_array_map_delete_elem,
4882a36f0b9SWang Nan 	.map_fd_get_ptr = prog_fd_array_get_ptr,
4892a36f0b9SWang Nan 	.map_fd_put_ptr = prog_fd_array_put_ptr,
49014dc6f04SMartin KaFai Lau 	.map_fd_sys_lookup_elem = prog_fd_array_sys_lookup_elem,
49104fd61abSAlexei Starovoitov };
49204fd61abSAlexei Starovoitov 
4933b1efb19SDaniel Borkmann static struct bpf_event_entry *bpf_event_entry_gen(struct file *perf_file,
4943b1efb19SDaniel Borkmann 						   struct file *map_file)
495ea317b26SKaixu Xia {
4963b1efb19SDaniel Borkmann 	struct bpf_event_entry *ee;
4973b1efb19SDaniel Borkmann 
498858d68f1SDaniel Borkmann 	ee = kzalloc(sizeof(*ee), GFP_ATOMIC);
4993b1efb19SDaniel Borkmann 	if (ee) {
5003b1efb19SDaniel Borkmann 		ee->event = perf_file->private_data;
5013b1efb19SDaniel Borkmann 		ee->perf_file = perf_file;
5023b1efb19SDaniel Borkmann 		ee->map_file = map_file;
5033b1efb19SDaniel Borkmann 	}
5043b1efb19SDaniel Borkmann 
5053b1efb19SDaniel Borkmann 	return ee;
5063b1efb19SDaniel Borkmann }
5073b1efb19SDaniel Borkmann 
5083b1efb19SDaniel Borkmann static void __bpf_event_entry_free(struct rcu_head *rcu)
5093b1efb19SDaniel Borkmann {
5103b1efb19SDaniel Borkmann 	struct bpf_event_entry *ee;
5113b1efb19SDaniel Borkmann 
5123b1efb19SDaniel Borkmann 	ee = container_of(rcu, struct bpf_event_entry, rcu);
5133b1efb19SDaniel Borkmann 	fput(ee->perf_file);
5143b1efb19SDaniel Borkmann 	kfree(ee);
5153b1efb19SDaniel Borkmann }
5163b1efb19SDaniel Borkmann 
5173b1efb19SDaniel Borkmann static void bpf_event_entry_free_rcu(struct bpf_event_entry *ee)
5183b1efb19SDaniel Borkmann {
5193b1efb19SDaniel Borkmann 	call_rcu(&ee->rcu, __bpf_event_entry_free);
520ea317b26SKaixu Xia }
521ea317b26SKaixu Xia 
522d056a788SDaniel Borkmann static void *perf_event_fd_array_get_ptr(struct bpf_map *map,
523d056a788SDaniel Borkmann 					 struct file *map_file, int fd)
524ea317b26SKaixu Xia {
5253b1efb19SDaniel Borkmann 	struct bpf_event_entry *ee;
5263b1efb19SDaniel Borkmann 	struct perf_event *event;
5273b1efb19SDaniel Borkmann 	struct file *perf_file;
528f91840a3SAlexei Starovoitov 	u64 value;
529ea317b26SKaixu Xia 
5303b1efb19SDaniel Borkmann 	perf_file = perf_event_get(fd);
5313b1efb19SDaniel Borkmann 	if (IS_ERR(perf_file))
5323b1efb19SDaniel Borkmann 		return perf_file;
533e03e7ee3SAlexei Starovoitov 
534f91840a3SAlexei Starovoitov 	ee = ERR_PTR(-EOPNOTSUPP);
5353b1efb19SDaniel Borkmann 	event = perf_file->private_data;
53697562633SYonghong Song 	if (perf_event_read_local(event, &value, NULL, NULL) == -EOPNOTSUPP)
5373b1efb19SDaniel Borkmann 		goto err_out;
538ea317b26SKaixu Xia 
5393b1efb19SDaniel Borkmann 	ee = bpf_event_entry_gen(perf_file, map_file);
5403b1efb19SDaniel Borkmann 	if (ee)
5413b1efb19SDaniel Borkmann 		return ee;
5423b1efb19SDaniel Borkmann 	ee = ERR_PTR(-ENOMEM);
5433b1efb19SDaniel Borkmann err_out:
5443b1efb19SDaniel Borkmann 	fput(perf_file);
5453b1efb19SDaniel Borkmann 	return ee;
546ea317b26SKaixu Xia }
547ea317b26SKaixu Xia 
548ea317b26SKaixu Xia static void perf_event_fd_array_put_ptr(void *ptr)
549ea317b26SKaixu Xia {
5503b1efb19SDaniel Borkmann 	bpf_event_entry_free_rcu(ptr);
5513b1efb19SDaniel Borkmann }
5523b1efb19SDaniel Borkmann 
5533b1efb19SDaniel Borkmann static void perf_event_fd_array_release(struct bpf_map *map,
5543b1efb19SDaniel Borkmann 					struct file *map_file)
5553b1efb19SDaniel Borkmann {
5563b1efb19SDaniel Borkmann 	struct bpf_array *array = container_of(map, struct bpf_array, map);
5573b1efb19SDaniel Borkmann 	struct bpf_event_entry *ee;
5583b1efb19SDaniel Borkmann 	int i;
5593b1efb19SDaniel Borkmann 
5603b1efb19SDaniel Borkmann 	rcu_read_lock();
5613b1efb19SDaniel Borkmann 	for (i = 0; i < array->map.max_entries; i++) {
5623b1efb19SDaniel Borkmann 		ee = READ_ONCE(array->ptrs[i]);
5633b1efb19SDaniel Borkmann 		if (ee && ee->map_file == map_file)
5643b1efb19SDaniel Borkmann 			fd_array_map_delete_elem(map, &i);
5653b1efb19SDaniel Borkmann 	}
5663b1efb19SDaniel Borkmann 	rcu_read_unlock();
567ea317b26SKaixu Xia }
568ea317b26SKaixu Xia 
56940077e0cSJohannes Berg const struct bpf_map_ops perf_event_array_map_ops = {
570ad46061fSJakub Kicinski 	.map_alloc_check = fd_array_map_alloc_check,
571ad46061fSJakub Kicinski 	.map_alloc = array_map_alloc,
5723b1efb19SDaniel Borkmann 	.map_free = fd_array_map_free,
573ea317b26SKaixu Xia 	.map_get_next_key = array_map_get_next_key,
574ea317b26SKaixu Xia 	.map_lookup_elem = fd_array_map_lookup_elem,
575ea317b26SKaixu Xia 	.map_delete_elem = fd_array_map_delete_elem,
576ea317b26SKaixu Xia 	.map_fd_get_ptr = perf_event_fd_array_get_ptr,
577ea317b26SKaixu Xia 	.map_fd_put_ptr = perf_event_fd_array_put_ptr,
5783b1efb19SDaniel Borkmann 	.map_release = perf_event_fd_array_release,
579ea317b26SKaixu Xia };
580ea317b26SKaixu Xia 
58160d20f91SSargun Dhillon #ifdef CONFIG_CGROUPS
5824ed8ec52SMartin KaFai Lau static void *cgroup_fd_array_get_ptr(struct bpf_map *map,
5834ed8ec52SMartin KaFai Lau 				     struct file *map_file /* not used */,
5844ed8ec52SMartin KaFai Lau 				     int fd)
5854ed8ec52SMartin KaFai Lau {
5864ed8ec52SMartin KaFai Lau 	return cgroup_get_from_fd(fd);
5874ed8ec52SMartin KaFai Lau }
5884ed8ec52SMartin KaFai Lau 
5894ed8ec52SMartin KaFai Lau static void cgroup_fd_array_put_ptr(void *ptr)
5904ed8ec52SMartin KaFai Lau {
5914ed8ec52SMartin KaFai Lau 	/* cgroup_put free cgrp after a rcu grace period */
5924ed8ec52SMartin KaFai Lau 	cgroup_put(ptr);
5934ed8ec52SMartin KaFai Lau }
5944ed8ec52SMartin KaFai Lau 
5954ed8ec52SMartin KaFai Lau static void cgroup_fd_array_free(struct bpf_map *map)
5964ed8ec52SMartin KaFai Lau {
5974ed8ec52SMartin KaFai Lau 	bpf_fd_array_map_clear(map);
5984ed8ec52SMartin KaFai Lau 	fd_array_map_free(map);
5994ed8ec52SMartin KaFai Lau }
6004ed8ec52SMartin KaFai Lau 
60140077e0cSJohannes Berg const struct bpf_map_ops cgroup_array_map_ops = {
602ad46061fSJakub Kicinski 	.map_alloc_check = fd_array_map_alloc_check,
603ad46061fSJakub Kicinski 	.map_alloc = array_map_alloc,
6044ed8ec52SMartin KaFai Lau 	.map_free = cgroup_fd_array_free,
6054ed8ec52SMartin KaFai Lau 	.map_get_next_key = array_map_get_next_key,
6064ed8ec52SMartin KaFai Lau 	.map_lookup_elem = fd_array_map_lookup_elem,
6074ed8ec52SMartin KaFai Lau 	.map_delete_elem = fd_array_map_delete_elem,
6084ed8ec52SMartin KaFai Lau 	.map_fd_get_ptr = cgroup_fd_array_get_ptr,
6094ed8ec52SMartin KaFai Lau 	.map_fd_put_ptr = cgroup_fd_array_put_ptr,
6104ed8ec52SMartin KaFai Lau };
6114ed8ec52SMartin KaFai Lau #endif
61256f668dfSMartin KaFai Lau 
61356f668dfSMartin KaFai Lau static struct bpf_map *array_of_map_alloc(union bpf_attr *attr)
61456f668dfSMartin KaFai Lau {
61556f668dfSMartin KaFai Lau 	struct bpf_map *map, *inner_map_meta;
61656f668dfSMartin KaFai Lau 
61756f668dfSMartin KaFai Lau 	inner_map_meta = bpf_map_meta_alloc(attr->inner_map_fd);
61856f668dfSMartin KaFai Lau 	if (IS_ERR(inner_map_meta))
61956f668dfSMartin KaFai Lau 		return inner_map_meta;
62056f668dfSMartin KaFai Lau 
621ad46061fSJakub Kicinski 	map = array_map_alloc(attr);
62256f668dfSMartin KaFai Lau 	if (IS_ERR(map)) {
62356f668dfSMartin KaFai Lau 		bpf_map_meta_free(inner_map_meta);
62456f668dfSMartin KaFai Lau 		return map;
62556f668dfSMartin KaFai Lau 	}
62656f668dfSMartin KaFai Lau 
62756f668dfSMartin KaFai Lau 	map->inner_map_meta = inner_map_meta;
62856f668dfSMartin KaFai Lau 
62956f668dfSMartin KaFai Lau 	return map;
63056f668dfSMartin KaFai Lau }
63156f668dfSMartin KaFai Lau 
63256f668dfSMartin KaFai Lau static void array_of_map_free(struct bpf_map *map)
63356f668dfSMartin KaFai Lau {
63456f668dfSMartin KaFai Lau 	/* map->inner_map_meta is only accessed by syscall which
63556f668dfSMartin KaFai Lau 	 * is protected by fdget/fdput.
63656f668dfSMartin KaFai Lau 	 */
63756f668dfSMartin KaFai Lau 	bpf_map_meta_free(map->inner_map_meta);
63856f668dfSMartin KaFai Lau 	bpf_fd_array_map_clear(map);
63956f668dfSMartin KaFai Lau 	fd_array_map_free(map);
64056f668dfSMartin KaFai Lau }
64156f668dfSMartin KaFai Lau 
64256f668dfSMartin KaFai Lau static void *array_of_map_lookup_elem(struct bpf_map *map, void *key)
64356f668dfSMartin KaFai Lau {
64456f668dfSMartin KaFai Lau 	struct bpf_map **inner_map = array_map_lookup_elem(map, key);
64556f668dfSMartin KaFai Lau 
64656f668dfSMartin KaFai Lau 	if (!inner_map)
64756f668dfSMartin KaFai Lau 		return NULL;
64856f668dfSMartin KaFai Lau 
64956f668dfSMartin KaFai Lau 	return READ_ONCE(*inner_map);
65056f668dfSMartin KaFai Lau }
65156f668dfSMartin KaFai Lau 
6527b0c2a05SDaniel Borkmann static u32 array_of_map_gen_lookup(struct bpf_map *map,
6537b0c2a05SDaniel Borkmann 				   struct bpf_insn *insn_buf)
6547b0c2a05SDaniel Borkmann {
655b2157399SAlexei Starovoitov 	struct bpf_array *array = container_of(map, struct bpf_array, map);
6567b0c2a05SDaniel Borkmann 	u32 elem_size = round_up(map->value_size, 8);
6577b0c2a05SDaniel Borkmann 	struct bpf_insn *insn = insn_buf;
6587b0c2a05SDaniel Borkmann 	const int ret = BPF_REG_0;
6597b0c2a05SDaniel Borkmann 	const int map_ptr = BPF_REG_1;
6607b0c2a05SDaniel Borkmann 	const int index = BPF_REG_2;
6617b0c2a05SDaniel Borkmann 
6627b0c2a05SDaniel Borkmann 	*insn++ = BPF_ALU64_IMM(BPF_ADD, map_ptr, offsetof(struct bpf_array, value));
6637b0c2a05SDaniel Borkmann 	*insn++ = BPF_LDX_MEM(BPF_W, ret, index, 0);
664b2157399SAlexei Starovoitov 	if (map->unpriv_array) {
665b2157399SAlexei Starovoitov 		*insn++ = BPF_JMP_IMM(BPF_JGE, ret, map->max_entries, 6);
666b2157399SAlexei Starovoitov 		*insn++ = BPF_ALU32_IMM(BPF_AND, ret, array->index_mask);
667b2157399SAlexei Starovoitov 	} else {
6687b0c2a05SDaniel Borkmann 		*insn++ = BPF_JMP_IMM(BPF_JGE, ret, map->max_entries, 5);
669b2157399SAlexei Starovoitov 	}
6707b0c2a05SDaniel Borkmann 	if (is_power_of_2(elem_size))
6717b0c2a05SDaniel Borkmann 		*insn++ = BPF_ALU64_IMM(BPF_LSH, ret, ilog2(elem_size));
6727b0c2a05SDaniel Borkmann 	else
6737b0c2a05SDaniel Borkmann 		*insn++ = BPF_ALU64_IMM(BPF_MUL, ret, elem_size);
6747b0c2a05SDaniel Borkmann 	*insn++ = BPF_ALU64_REG(BPF_ADD, ret, map_ptr);
6757b0c2a05SDaniel Borkmann 	*insn++ = BPF_LDX_MEM(BPF_DW, ret, ret, 0);
6767b0c2a05SDaniel Borkmann 	*insn++ = BPF_JMP_IMM(BPF_JEQ, ret, 0, 1);
6777b0c2a05SDaniel Borkmann 	*insn++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);
6787b0c2a05SDaniel Borkmann 	*insn++ = BPF_MOV64_IMM(ret, 0);
6797b0c2a05SDaniel Borkmann 
6807b0c2a05SDaniel Borkmann 	return insn - insn_buf;
6817b0c2a05SDaniel Borkmann }
6827b0c2a05SDaniel Borkmann 
68340077e0cSJohannes Berg const struct bpf_map_ops array_of_maps_map_ops = {
684ad46061fSJakub Kicinski 	.map_alloc_check = fd_array_map_alloc_check,
68556f668dfSMartin KaFai Lau 	.map_alloc = array_of_map_alloc,
68656f668dfSMartin KaFai Lau 	.map_free = array_of_map_free,
68756f668dfSMartin KaFai Lau 	.map_get_next_key = array_map_get_next_key,
68856f668dfSMartin KaFai Lau 	.map_lookup_elem = array_of_map_lookup_elem,
68956f668dfSMartin KaFai Lau 	.map_delete_elem = fd_array_map_delete_elem,
69056f668dfSMartin KaFai Lau 	.map_fd_get_ptr = bpf_map_fd_get_ptr,
69156f668dfSMartin KaFai Lau 	.map_fd_put_ptr = bpf_map_fd_put_ptr,
69214dc6f04SMartin KaFai Lau 	.map_fd_sys_lookup_elem = bpf_map_fd_sys_lookup_elem,
6937b0c2a05SDaniel Borkmann 	.map_gen_lookup = array_of_map_gen_lookup,
69456f668dfSMartin KaFai Lau };
695