Skip to content

Commit bd5f5f4

Browse files
iamkafaidavem330
authored andcommitted
bpf: Add BPF_MAP_GET_FD_BY_ID
Add BPF_MAP_GET_FD_BY_ID command to allow user to get a fd from a bpf_map's ID. bpf_map_inc_not_zero() is added and is called with map_idr_lock held. __bpf_map_put() is also added which has the 'bool do_idr_lock' param to decide if the map_idr_lock should be acquired when freeing the map->id. In the error path of bpf_map_inc_not_zero(), it may have to call __bpf_map_put(map, false) which does not need to take the map_idr_lock when freeing the map->id. It is currently limited to CAP_SYS_ADMIN which we can consider to lift it in followup patches. Signed-off-by: Martin KaFai Lau <kafai@fb.com> Acked-by: Alexei Starovoitov <ast@fb.com> Acked-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent b16d9aa commit bd5f5f4

File tree

2 files changed

+87
-10
lines changed

2 files changed

+87
-10
lines changed

include/uapi/linux/bpf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ enum bpf_cmd {
8585
BPF_PROG_GET_NEXT_ID,
8686
BPF_MAP_GET_NEXT_ID,
8787
BPF_PROG_GET_FD_BY_ID,
88+
BPF_MAP_GET_FD_BY_ID,
8889
};
8990

9091
enum bpf_map_type {
@@ -217,6 +218,7 @@ union bpf_attr {
217218
union {
218219
__u32 start_id;
219220
__u32 prog_id;
221+
__u32 map_id;
220222
};
221223
__u32 next_id;
222224
};

kernel/bpf/syscall.c

Lines changed: 85 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,19 @@ static int bpf_map_alloc_id(struct bpf_map *map)
135135
return id > 0 ? 0 : id;
136136
}
137137

138-
static void bpf_map_free_id(struct bpf_map *map)
138+
static void bpf_map_free_id(struct bpf_map *map, bool do_idr_lock)
139139
{
140-
spin_lock_bh(&map_idr_lock);
140+
if (do_idr_lock)
141+
spin_lock_bh(&map_idr_lock);
142+
else
143+
__acquire(&map_idr_lock);
144+
141145
idr_remove(&map_idr, map->id);
142-
spin_unlock_bh(&map_idr_lock);
146+
147+
if (do_idr_lock)
148+
spin_unlock_bh(&map_idr_lock);
149+
else
150+
__release(&map_idr_lock);
143151
}
144152

145153
/* called from workqueue */
@@ -163,16 +171,21 @@ static void bpf_map_put_uref(struct bpf_map *map)
163171
/* decrement map refcnt and schedule it for freeing via workqueue
164172
* (unrelying map implementation ops->map_free() might sleep)
165173
*/
166-
void bpf_map_put(struct bpf_map *map)
174+
static void __bpf_map_put(struct bpf_map *map, bool do_idr_lock)
167175
{
168176
if (atomic_dec_and_test(&map->refcnt)) {
169177
/* bpf_map_free_id() must be called first */
170-
bpf_map_free_id(map);
178+
bpf_map_free_id(map, do_idr_lock);
171179
INIT_WORK(&map->work, bpf_map_free_deferred);
172180
schedule_work(&map->work);
173181
}
174182
}
175183

184+
void bpf_map_put(struct bpf_map *map)
185+
{
186+
__bpf_map_put(map, true);
187+
}
188+
176189
void bpf_map_put_with_uref(struct bpf_map *map)
177190
{
178191
bpf_map_put_uref(map);
@@ -271,15 +284,20 @@ static int map_create(union bpf_attr *attr)
271284
goto free_map;
272285

273286
err = bpf_map_new_fd(map);
274-
if (err < 0)
275-
/* failed to allocate fd */
276-
goto free_id;
287+
if (err < 0) {
288+
/* failed to allocate fd.
289+
* bpf_map_put() is needed because the above
290+
* bpf_map_alloc_id() has published the map
291+
* to the userspace and the userspace may
292+
* have refcnt-ed it through BPF_MAP_GET_FD_BY_ID.
293+
*/
294+
bpf_map_put(map);
295+
return err;
296+
}
277297

278298
trace_bpf_map_create(map, err);
279299
return err;
280300

281-
free_id:
282-
bpf_map_free_id(map);
283301
free_map:
284302
bpf_map_uncharge_memlock(map);
285303
free_map_nouncharge:
@@ -331,6 +349,28 @@ struct bpf_map *bpf_map_get_with_uref(u32 ufd)
331349
return map;
332350
}
333351

352+
/* map_idr_lock should have been held */
353+
static struct bpf_map *bpf_map_inc_not_zero(struct bpf_map *map,
354+
bool uref)
355+
{
356+
int refold;
357+
358+
refold = __atomic_add_unless(&map->refcnt, 1, 0);
359+
360+
if (refold >= BPF_MAX_REFCNT) {
361+
__bpf_map_put(map, false);
362+
return ERR_PTR(-EBUSY);
363+
}
364+
365+
if (!refold)
366+
return ERR_PTR(-ENOENT);
367+
368+
if (uref)
369+
atomic_inc(&map->usercnt);
370+
371+
return map;
372+
}
373+
334374
int __weak bpf_stackmap_copy(struct bpf_map *map, void *key, void *value)
335375
{
336376
return -ENOTSUPP;
@@ -1167,6 +1207,38 @@ static int bpf_prog_get_fd_by_id(const union bpf_attr *attr)
11671207
return fd;
11681208
}
11691209

1210+
#define BPF_MAP_GET_FD_BY_ID_LAST_FIELD map_id
1211+
1212+
static int bpf_map_get_fd_by_id(const union bpf_attr *attr)
1213+
{
1214+
struct bpf_map *map;
1215+
u32 id = attr->map_id;
1216+
int fd;
1217+
1218+
if (CHECK_ATTR(BPF_MAP_GET_FD_BY_ID))
1219+
return -EINVAL;
1220+
1221+
if (!capable(CAP_SYS_ADMIN))
1222+
return -EPERM;
1223+
1224+
spin_lock_bh(&map_idr_lock);
1225+
map = idr_find(&map_idr, id);
1226+
if (map)
1227+
map = bpf_map_inc_not_zero(map, true);
1228+
else
1229+
map = ERR_PTR(-ENOENT);
1230+
spin_unlock_bh(&map_idr_lock);
1231+
1232+
if (IS_ERR(map))
1233+
return PTR_ERR(map);
1234+
1235+
fd = bpf_map_new_fd(map);
1236+
if (fd < 0)
1237+
bpf_map_put(map);
1238+
1239+
return fd;
1240+
}
1241+
11701242
SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
11711243
{
11721244
union bpf_attr attr = {};
@@ -1255,6 +1327,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
12551327
case BPF_PROG_GET_FD_BY_ID:
12561328
err = bpf_prog_get_fd_by_id(&attr);
12571329
break;
1330+
case BPF_MAP_GET_FD_BY_ID:
1331+
err = bpf_map_get_fd_by_id(&attr);
1332+
break;
12581333
default:
12591334
err = -EINVAL;
12601335
break;

0 commit comments

Comments
 (0)