]>
Commit | Line | Data |
---|---|---|
c4bcfb38 YS |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (c) 2022 Meta Platforms, Inc. and affiliates. | |
4 | */ | |
5 | ||
6 | #include <linux/types.h> | |
7 | #include <linux/bpf.h> | |
8 | #include <linux/bpf_local_storage.h> | |
9 | #include <uapi/linux/btf.h> | |
10 | #include <linux/btf_ids.h> | |
11 | ||
12 | DEFINE_BPF_STORAGE_CACHE(cgroup_cache); | |
13 | ||
14 | static DEFINE_PER_CPU(int, bpf_cgrp_storage_busy); | |
15 | ||
16 | static void bpf_cgrp_storage_lock(void) | |
17 | { | |
25dc65f7 | 18 | cant_migrate(); |
c4bcfb38 YS |
19 | this_cpu_inc(bpf_cgrp_storage_busy); |
20 | } | |
21 | ||
22 | static void bpf_cgrp_storage_unlock(void) | |
23 | { | |
24 | this_cpu_dec(bpf_cgrp_storage_busy); | |
c4bcfb38 YS |
25 | } |
26 | ||
27 | static bool bpf_cgrp_storage_trylock(void) | |
28 | { | |
25dc65f7 | 29 | cant_migrate(); |
c4bcfb38 YS |
30 | if (unlikely(this_cpu_inc_return(bpf_cgrp_storage_busy) != 1)) { |
31 | this_cpu_dec(bpf_cgrp_storage_busy); | |
c4bcfb38 YS |
32 | return false; |
33 | } | |
34 | return true; | |
35 | } | |
36 | ||
37 | static struct bpf_local_storage __rcu **cgroup_storage_ptr(void *owner) | |
38 | { | |
39 | struct cgroup *cg = owner; | |
40 | ||
41 | return &cg->bpf_cgrp_storage; | |
42 | } | |
43 | ||
44 | void bpf_cgrp_storage_free(struct cgroup *cgroup) | |
45 | { | |
46 | struct bpf_local_storage *local_storage; | |
c4bcfb38 | 47 | |
25dc65f7 | 48 | migrate_disable(); |
c4bcfb38 YS |
49 | rcu_read_lock(); |
50 | local_storage = rcu_dereference(cgroup->bpf_cgrp_storage); | |
25dc65f7 HT |
51 | if (!local_storage) |
52 | goto out; | |
c4bcfb38 YS |
53 | |
54 | bpf_cgrp_storage_lock(); | |
2ffcb6fc | 55 | bpf_local_storage_destroy(local_storage); |
c4bcfb38 | 56 | bpf_cgrp_storage_unlock(); |
25dc65f7 | 57 | out: |
c4bcfb38 | 58 | rcu_read_unlock(); |
25dc65f7 | 59 | migrate_enable(); |
c4bcfb38 YS |
60 | } |
61 | ||
62 | static struct bpf_local_storage_data * | |
63 | cgroup_storage_lookup(struct cgroup *cgroup, struct bpf_map *map, bool cacheit_lockit) | |
64 | { | |
65 | struct bpf_local_storage *cgroup_storage; | |
66 | struct bpf_local_storage_map *smap; | |
67 | ||
68 | cgroup_storage = rcu_dereference_check(cgroup->bpf_cgrp_storage, | |
69 | bpf_rcu_lock_held()); | |
70 | if (!cgroup_storage) | |
71 | return NULL; | |
72 | ||
73 | smap = (struct bpf_local_storage_map *)map; | |
74 | return bpf_local_storage_lookup(cgroup_storage, smap, cacheit_lockit); | |
75 | } | |
76 | ||
77 | static void *bpf_cgrp_storage_lookup_elem(struct bpf_map *map, void *key) | |
78 | { | |
79 | struct bpf_local_storage_data *sdata; | |
80 | struct cgroup *cgroup; | |
81 | int fd; | |
82 | ||
83 | fd = *(int *)key; | |
73d9eb34 | 84 | cgroup = cgroup_v1v2_get_from_fd(fd); |
c4bcfb38 YS |
85 | if (IS_ERR(cgroup)) |
86 | return ERR_CAST(cgroup); | |
87 | ||
88 | bpf_cgrp_storage_lock(); | |
89 | sdata = cgroup_storage_lookup(cgroup, map, true); | |
90 | bpf_cgrp_storage_unlock(); | |
91 | cgroup_put(cgroup); | |
92 | return sdata ? sdata->data : NULL; | |
93 | } | |
94 | ||
d7ba4cc9 JK |
95 | static long bpf_cgrp_storage_update_elem(struct bpf_map *map, void *key, |
96 | void *value, u64 map_flags) | |
c4bcfb38 YS |
97 | { |
98 | struct bpf_local_storage_data *sdata; | |
99 | struct cgroup *cgroup; | |
100 | int fd; | |
101 | ||
102 | fd = *(int *)key; | |
73d9eb34 | 103 | cgroup = cgroup_v1v2_get_from_fd(fd); |
c4bcfb38 YS |
104 | if (IS_ERR(cgroup)) |
105 | return PTR_ERR(cgroup); | |
106 | ||
107 | bpf_cgrp_storage_lock(); | |
108 | sdata = bpf_local_storage_update(cgroup, (struct bpf_local_storage_map *)map, | |
b9a5a07a | 109 | value, map_flags, false, GFP_ATOMIC); |
c4bcfb38 YS |
110 | bpf_cgrp_storage_unlock(); |
111 | cgroup_put(cgroup); | |
112 | return PTR_ERR_OR_ZERO(sdata); | |
113 | } | |
114 | ||
115 | static int cgroup_storage_delete(struct cgroup *cgroup, struct bpf_map *map) | |
116 | { | |
117 | struct bpf_local_storage_data *sdata; | |
118 | ||
119 | sdata = cgroup_storage_lookup(cgroup, map, false); | |
120 | if (!sdata) | |
121 | return -ENOENT; | |
122 | ||
a47eabf2 | 123 | bpf_selem_unlink(SELEM(sdata), false); |
c4bcfb38 YS |
124 | return 0; |
125 | } | |
126 | ||
d7ba4cc9 | 127 | static long bpf_cgrp_storage_delete_elem(struct bpf_map *map, void *key) |
c4bcfb38 YS |
128 | { |
129 | struct cgroup *cgroup; | |
130 | int err, fd; | |
131 | ||
132 | fd = *(int *)key; | |
73d9eb34 | 133 | cgroup = cgroup_v1v2_get_from_fd(fd); |
c4bcfb38 YS |
134 | if (IS_ERR(cgroup)) |
135 | return PTR_ERR(cgroup); | |
136 | ||
137 | bpf_cgrp_storage_lock(); | |
138 | err = cgroup_storage_delete(cgroup, map); | |
139 | bpf_cgrp_storage_unlock(); | |
140 | cgroup_put(cgroup); | |
141 | return err; | |
142 | } | |
143 | ||
144 | static int notsupp_get_next_key(struct bpf_map *map, void *key, void *next_key) | |
145 | { | |
146 | return -ENOTSUPP; | |
147 | } | |
148 | ||
149 | static struct bpf_map *cgroup_storage_map_alloc(union bpf_attr *attr) | |
150 | { | |
08a7ce38 | 151 | return bpf_local_storage_map_alloc(attr, &cgroup_cache, true); |
c4bcfb38 YS |
152 | } |
153 | ||
154 | static void cgroup_storage_map_free(struct bpf_map *map) | |
155 | { | |
156 | bpf_local_storage_map_free(map, &cgroup_cache, NULL); | |
157 | } | |
158 | ||
159 | /* *gfp_flags* is a hidden argument provided by the verifier */ | |
160 | BPF_CALL_5(bpf_cgrp_storage_get, struct bpf_map *, map, struct cgroup *, cgroup, | |
161 | void *, value, u64, flags, gfp_t, gfp_flags) | |
162 | { | |
163 | struct bpf_local_storage_data *sdata; | |
164 | ||
165 | WARN_ON_ONCE(!bpf_rcu_lock_held()); | |
166 | if (flags & ~(BPF_LOCAL_STORAGE_GET_F_CREATE)) | |
167 | return (unsigned long)NULL; | |
168 | ||
169 | if (!cgroup) | |
170 | return (unsigned long)NULL; | |
171 | ||
172 | if (!bpf_cgrp_storage_trylock()) | |
173 | return (unsigned long)NULL; | |
174 | ||
175 | sdata = cgroup_storage_lookup(cgroup, map, true); | |
176 | if (sdata) | |
177 | goto unlock; | |
178 | ||
179 | /* only allocate new storage, when the cgroup is refcounted */ | |
180 | if (!percpu_ref_is_dying(&cgroup->self.refcnt) && | |
181 | (flags & BPF_LOCAL_STORAGE_GET_F_CREATE)) | |
182 | sdata = bpf_local_storage_update(cgroup, (struct bpf_local_storage_map *)map, | |
b9a5a07a | 183 | value, BPF_NOEXIST, false, gfp_flags); |
c4bcfb38 YS |
184 | |
185 | unlock: | |
186 | bpf_cgrp_storage_unlock(); | |
187 | return IS_ERR_OR_NULL(sdata) ? (unsigned long)NULL : (unsigned long)sdata->data; | |
188 | } | |
189 | ||
190 | BPF_CALL_2(bpf_cgrp_storage_delete, struct bpf_map *, map, struct cgroup *, cgroup) | |
191 | { | |
192 | int ret; | |
193 | ||
194 | WARN_ON_ONCE(!bpf_rcu_lock_held()); | |
195 | if (!cgroup) | |
196 | return -EINVAL; | |
197 | ||
198 | if (!bpf_cgrp_storage_trylock()) | |
199 | return -EBUSY; | |
200 | ||
201 | ret = cgroup_storage_delete(cgroup, map); | |
202 | bpf_cgrp_storage_unlock(); | |
203 | return ret; | |
204 | } | |
205 | ||
c4bcfb38 YS |
206 | const struct bpf_map_ops cgrp_storage_map_ops = { |
207 | .map_meta_equal = bpf_map_meta_equal, | |
208 | .map_alloc_check = bpf_local_storage_map_alloc_check, | |
209 | .map_alloc = cgroup_storage_map_alloc, | |
210 | .map_free = cgroup_storage_map_free, | |
211 | .map_get_next_key = notsupp_get_next_key, | |
212 | .map_lookup_elem = bpf_cgrp_storage_lookup_elem, | |
213 | .map_update_elem = bpf_cgrp_storage_update_elem, | |
214 | .map_delete_elem = bpf_cgrp_storage_delete_elem, | |
215 | .map_check_btf = bpf_local_storage_map_check_btf, | |
7490b7f1 | 216 | .map_mem_usage = bpf_local_storage_map_mem_usage, |
3144bfa5 | 217 | .map_btf_id = &bpf_local_storage_map_btf_id[0], |
c4bcfb38 YS |
218 | .map_owner_storage_ptr = cgroup_storage_ptr, |
219 | }; | |
220 | ||
221 | const struct bpf_func_proto bpf_cgrp_storage_get_proto = { | |
222 | .func = bpf_cgrp_storage_get, | |
223 | .gpl_only = false, | |
224 | .ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL, | |
225 | .arg1_type = ARG_CONST_MAP_PTR, | |
91571a51 | 226 | .arg2_type = ARG_PTR_TO_BTF_ID_OR_NULL, |
c4bcfb38 YS |
227 | .arg2_btf_id = &bpf_cgroup_btf_id[0], |
228 | .arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL, | |
229 | .arg4_type = ARG_ANYTHING, | |
230 | }; | |
231 | ||
232 | const struct bpf_func_proto bpf_cgrp_storage_delete_proto = { | |
233 | .func = bpf_cgrp_storage_delete, | |
234 | .gpl_only = false, | |
235 | .ret_type = RET_INTEGER, | |
236 | .arg1_type = ARG_CONST_MAP_PTR, | |
91571a51 | 237 | .arg2_type = ARG_PTR_TO_BTF_ID_OR_NULL, |
c4bcfb38 YS |
238 | .arg2_btf_id = &bpf_cgroup_btf_id[0], |
239 | }; |