bpf: permit multiple bpf attachments for a single perf event
This patch enables multiple bpf attachments for a kprobe/uprobe/tracepoint single trace event. Each trace_event keeps a list of attached perf events. When an event happens, all attached bpf programs will be executed based on the order of attachment. A global bpf_event_mutex lock is introduced to protect prog_array attaching and detaching. An alternative will be introduce a mutex lock in every trace_event_call structure, but it takes a lot of extra memory. So a global bpf_event_mutex lock is a good compromise. The bpf prog detachment involves allocation of memory. If the allocation fails, a dummy do-nothing program will replace to-be-detached program in-place. Signed-off-by: Yonghong Song <yhs@fb.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:

committed by
David S. Miller

parent
0b4c6841fe
commit
e87c6bc385
@@ -1394,6 +1394,20 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bpf_prog_select_runtime);
|
||||
|
||||
static unsigned int __bpf_prog_ret1(const void *ctx,
|
||||
const struct bpf_insn *insn)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct bpf_prog_dummy {
|
||||
struct bpf_prog prog;
|
||||
} dummy_bpf_prog = {
|
||||
.prog = {
|
||||
.bpf_func = __bpf_prog_ret1,
|
||||
},
|
||||
};
|
||||
|
||||
/* to avoid allocating empty bpf_prog_array for cgroups that
|
||||
* don't have bpf program attached use one global 'empty_prog_array'
|
||||
* It will not be modified the caller of bpf_prog_array_alloc()
|
||||
@@ -1463,6 +1477,73 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bpf_prog_array_delete_safe(struct bpf_prog_array __rcu *progs,
|
||||
struct bpf_prog *old_prog)
|
||||
{
|
||||
struct bpf_prog **prog = progs->progs;
|
||||
|
||||
for (; *prog; prog++)
|
||||
if (*prog == old_prog) {
|
||||
WRITE_ONCE(*prog, &dummy_bpf_prog.prog);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array,
|
||||
struct bpf_prog *exclude_prog,
|
||||
struct bpf_prog *include_prog,
|
||||
struct bpf_prog_array **new_array)
|
||||
{
|
||||
int new_prog_cnt, carry_prog_cnt = 0;
|
||||
struct bpf_prog **existing_prog;
|
||||
struct bpf_prog_array *array;
|
||||
int new_prog_idx = 0;
|
||||
|
||||
/* Figure out how many existing progs we need to carry over to
|
||||
* the new array.
|
||||
*/
|
||||
if (old_array) {
|
||||
existing_prog = old_array->progs;
|
||||
for (; *existing_prog; existing_prog++) {
|
||||
if (*existing_prog != exclude_prog &&
|
||||
*existing_prog != &dummy_bpf_prog.prog)
|
||||
carry_prog_cnt++;
|
||||
if (*existing_prog == include_prog)
|
||||
return -EEXIST;
|
||||
}
|
||||
}
|
||||
|
||||
/* How many progs (not NULL) will be in the new array? */
|
||||
new_prog_cnt = carry_prog_cnt;
|
||||
if (include_prog)
|
||||
new_prog_cnt += 1;
|
||||
|
||||
/* Do we have any prog (not NULL) in the new array? */
|
||||
if (!new_prog_cnt) {
|
||||
*new_array = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* +1 as the end of prog_array is marked with NULL */
|
||||
array = bpf_prog_array_alloc(new_prog_cnt + 1, GFP_KERNEL);
|
||||
if (!array)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Fill in the new prog array */
|
||||
if (carry_prog_cnt) {
|
||||
existing_prog = old_array->progs;
|
||||
for (; *existing_prog; existing_prog++)
|
||||
if (*existing_prog != exclude_prog &&
|
||||
*existing_prog != &dummy_bpf_prog.prog)
|
||||
array->progs[new_prog_idx++] = *existing_prog;
|
||||
}
|
||||
if (include_prog)
|
||||
array->progs[new_prog_idx++] = include_prog;
|
||||
array->progs[new_prog_idx] = NULL;
|
||||
*new_array = array;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bpf_prog_free_deferred(struct work_struct *work)
|
||||
{
|
||||
struct bpf_prog_aux *aux;
|
||||
|
Reference in New Issue
Block a user