bpf: extend bpf_prog_array to store pointers to the cgroup storage

This patch converts bpf_prog_array from an array of prog pointers
to the array of struct bpf_prog_array_item elements.

This allows to save a cgroup storage pointer for each bpf program
efficiently attached to a cgroup.

Signed-off-by: Roman Gushchin <guro@fb.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
This commit is contained in:
Roman Gushchin 2018-08-02 14:27:21 -07:00 коммит произвёл Daniel Borkmann
Родитель d7bf2c10af
Коммит 394e40a297
4 изменённых файлов: 70 добавлений и 56 удалений

Просмотреть файл

@ -195,14 +195,16 @@ void lirc_bpf_run(struct rc_dev *rcdev, u32 sample)
*/ */
void lirc_bpf_free(struct rc_dev *rcdev) void lirc_bpf_free(struct rc_dev *rcdev)
{ {
struct bpf_prog **progs; struct bpf_prog_array_item *item;
if (!rcdev->raw->progs) if (!rcdev->raw->progs)
return; return;
progs = rcu_dereference(rcdev->raw->progs)->progs; item = rcu_dereference(rcdev->raw->progs)->items;
while (*progs) while (item->prog) {
bpf_prog_put(*progs++); bpf_prog_put(item->prog);
item++;
}
bpf_prog_array_free(rcdev->raw->progs); bpf_prog_array_free(rcdev->raw->progs);
} }

Просмотреть файл

@ -349,9 +349,14 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
* The 'struct bpf_prog_array *' should only be replaced with xchg() * The 'struct bpf_prog_array *' should only be replaced with xchg()
* since other cpus are walking the array of pointers in parallel. * since other cpus are walking the array of pointers in parallel.
*/ */
struct bpf_prog_array_item {
struct bpf_prog *prog;
struct bpf_cgroup_storage *cgroup_storage;
};
struct bpf_prog_array { struct bpf_prog_array {
struct rcu_head rcu; struct rcu_head rcu;
struct bpf_prog *progs[0]; struct bpf_prog_array_item items[0];
}; };
struct bpf_prog_array *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags); struct bpf_prog_array *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags);
@ -372,7 +377,8 @@ int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array,
#define __BPF_PROG_RUN_ARRAY(array, ctx, func, check_non_null) \ #define __BPF_PROG_RUN_ARRAY(array, ctx, func, check_non_null) \
({ \ ({ \
struct bpf_prog **_prog, *__prog; \ struct bpf_prog_array_item *_item; \
struct bpf_prog *_prog; \
struct bpf_prog_array *_array; \ struct bpf_prog_array *_array; \
u32 _ret = 1; \ u32 _ret = 1; \
preempt_disable(); \ preempt_disable(); \
@ -380,10 +386,11 @@ int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array,
_array = rcu_dereference(array); \ _array = rcu_dereference(array); \
if (unlikely(check_non_null && !_array))\ if (unlikely(check_non_null && !_array))\
goto _out; \ goto _out; \
_prog = _array->progs; \ _item = &_array->items[0]; \
while ((__prog = READ_ONCE(*_prog))) { \ while ((_prog = READ_ONCE(_item->prog))) { \
_ret &= func(__prog, ctx); \ bpf_cgroup_storage_set(_item->cgroup_storage); \
_prog++; \ _ret &= func(_prog, ctx); \
_item++; \
} \ } \
_out: \ _out: \
rcu_read_unlock(); \ rcu_read_unlock(); \

Просмотреть файл

@ -117,15 +117,18 @@ static int compute_effective_progs(struct cgroup *cgrp,
cnt = 0; cnt = 0;
p = cgrp; p = cgrp;
do { do {
if (cnt == 0 || (p->bpf.flags[type] & BPF_F_ALLOW_MULTI)) if (cnt > 0 && !(p->bpf.flags[type] & BPF_F_ALLOW_MULTI))
list_for_each_entry(pl, continue;
&p->bpf.progs[type], node) {
list_for_each_entry(pl, &p->bpf.progs[type], node) {
if (!pl->prog) if (!pl->prog)
continue; continue;
progs->progs[cnt++] = pl->prog;
progs->items[cnt].prog = pl->prog;
progs->items[cnt].cgroup_storage = pl->storage;
cnt++;
} }
p = cgroup_parent(p); } while ((p = cgroup_parent(p)));
} while (p);
rcu_assign_pointer(*array, progs); rcu_assign_pointer(*array, progs);
return 0; return 0;

Просмотреть файл

@ -1542,7 +1542,8 @@ struct bpf_prog_array *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags)
{ {
if (prog_cnt) if (prog_cnt)
return kzalloc(sizeof(struct bpf_prog_array) + return kzalloc(sizeof(struct bpf_prog_array) +
sizeof(struct bpf_prog *) * (prog_cnt + 1), sizeof(struct bpf_prog_array_item) *
(prog_cnt + 1),
flags); flags);
return &empty_prog_array.hdr; return &empty_prog_array.hdr;
@ -1556,43 +1557,45 @@ void bpf_prog_array_free(struct bpf_prog_array __rcu *progs)
kfree_rcu(progs, rcu); kfree_rcu(progs, rcu);
} }
int bpf_prog_array_length(struct bpf_prog_array __rcu *progs) int bpf_prog_array_length(struct bpf_prog_array __rcu *array)
{ {
struct bpf_prog **prog; struct bpf_prog_array_item *item;
u32 cnt = 0; u32 cnt = 0;
rcu_read_lock(); rcu_read_lock();
prog = rcu_dereference(progs)->progs; item = rcu_dereference(array)->items;
for (; *prog; prog++) for (; item->prog; item++)
if (*prog != &dummy_bpf_prog.prog) if (item->prog != &dummy_bpf_prog.prog)
cnt++; cnt++;
rcu_read_unlock(); rcu_read_unlock();
return cnt; return cnt;
} }
static bool bpf_prog_array_copy_core(struct bpf_prog **prog,
static bool bpf_prog_array_copy_core(struct bpf_prog_array __rcu *array,
u32 *prog_ids, u32 *prog_ids,
u32 request_cnt) u32 request_cnt)
{ {
struct bpf_prog_array_item *item;
int i = 0; int i = 0;
for (; *prog; prog++) { item = rcu_dereference(array)->items;
if (*prog == &dummy_bpf_prog.prog) for (; item->prog; item++) {
if (item->prog == &dummy_bpf_prog.prog)
continue; continue;
prog_ids[i] = (*prog)->aux->id; prog_ids[i] = item->prog->aux->id;
if (++i == request_cnt) { if (++i == request_cnt) {
prog++; item++;
break; break;
} }
} }
return !!(*prog); return !!(item->prog);
} }
int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs, int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *array,
__u32 __user *prog_ids, u32 cnt) __u32 __user *prog_ids, u32 cnt)
{ {
struct bpf_prog **prog;
unsigned long err = 0; unsigned long err = 0;
bool nospc; bool nospc;
u32 *ids; u32 *ids;
@ -1611,8 +1614,7 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs,
if (!ids) if (!ids)
return -ENOMEM; return -ENOMEM;
rcu_read_lock(); rcu_read_lock();
prog = rcu_dereference(progs)->progs; nospc = bpf_prog_array_copy_core(array, ids, cnt);
nospc = bpf_prog_array_copy_core(prog, ids, cnt);
rcu_read_unlock(); rcu_read_unlock();
err = copy_to_user(prog_ids, ids, cnt * sizeof(u32)); err = copy_to_user(prog_ids, ids, cnt * sizeof(u32));
kfree(ids); kfree(ids);
@ -1623,14 +1625,14 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs,
return 0; return 0;
} }
void bpf_prog_array_delete_safe(struct bpf_prog_array __rcu *progs, void bpf_prog_array_delete_safe(struct bpf_prog_array __rcu *array,
struct bpf_prog *old_prog) struct bpf_prog *old_prog)
{ {
struct bpf_prog **prog = progs->progs; struct bpf_prog_array_item *item = array->items;
for (; *prog; prog++) for (; item->prog; item++)
if (*prog == old_prog) { if (item->prog == old_prog) {
WRITE_ONCE(*prog, &dummy_bpf_prog.prog); WRITE_ONCE(item->prog, &dummy_bpf_prog.prog);
break; break;
} }
} }
@ -1641,7 +1643,7 @@ int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array,
struct bpf_prog_array **new_array) struct bpf_prog_array **new_array)
{ {
int new_prog_cnt, carry_prog_cnt = 0; int new_prog_cnt, carry_prog_cnt = 0;
struct bpf_prog **existing_prog; struct bpf_prog_array_item *existing;
struct bpf_prog_array *array; struct bpf_prog_array *array;
bool found_exclude = false; bool found_exclude = false;
int new_prog_idx = 0; int new_prog_idx = 0;
@ -1650,15 +1652,15 @@ int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array,
* the new array. * the new array.
*/ */
if (old_array) { if (old_array) {
existing_prog = old_array->progs; existing = old_array->items;
for (; *existing_prog; existing_prog++) { for (; existing->prog; existing++) {
if (*existing_prog == exclude_prog) { if (existing->prog == exclude_prog) {
found_exclude = true; found_exclude = true;
continue; continue;
} }
if (*existing_prog != &dummy_bpf_prog.prog) if (existing->prog != &dummy_bpf_prog.prog)
carry_prog_cnt++; carry_prog_cnt++;
if (*existing_prog == include_prog) if (existing->prog == include_prog)
return -EEXIST; return -EEXIST;
} }
} }
@ -1684,15 +1686,17 @@ int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array,
/* Fill in the new prog array */ /* Fill in the new prog array */
if (carry_prog_cnt) { if (carry_prog_cnt) {
existing_prog = old_array->progs; existing = old_array->items;
for (; *existing_prog; existing_prog++) for (; existing->prog; existing++)
if (*existing_prog != exclude_prog && if (existing->prog != exclude_prog &&
*existing_prog != &dummy_bpf_prog.prog) existing->prog != &dummy_bpf_prog.prog) {
array->progs[new_prog_idx++] = *existing_prog; array->items[new_prog_idx++].prog =
existing->prog;
}
} }
if (include_prog) if (include_prog)
array->progs[new_prog_idx++] = include_prog; array->items[new_prog_idx++].prog = include_prog;
array->progs[new_prog_idx] = NULL; array->items[new_prog_idx].prog = NULL;
*new_array = array; *new_array = array;
return 0; return 0;
} }
@ -1701,7 +1705,6 @@ int bpf_prog_array_copy_info(struct bpf_prog_array __rcu *array,
u32 *prog_ids, u32 request_cnt, u32 *prog_ids, u32 request_cnt,
u32 *prog_cnt) u32 *prog_cnt)
{ {
struct bpf_prog **prog;
u32 cnt = 0; u32 cnt = 0;
if (array) if (array)
@ -1714,8 +1717,7 @@ int bpf_prog_array_copy_info(struct bpf_prog_array __rcu *array,
return 0; return 0;
/* this function is called under trace/bpf_trace.c: bpf_event_mutex */ /* this function is called under trace/bpf_trace.c: bpf_event_mutex */
prog = rcu_dereference_check(array, 1)->progs; return bpf_prog_array_copy_core(array, prog_ids, request_cnt) ? -ENOSPC
return bpf_prog_array_copy_core(prog, prog_ids, request_cnt) ? -ENOSPC
: 0; : 0;
} }