Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a23967c1 authored by Alexei Starovoitov's avatar Alexei Starovoitov
Browse files

Merge branch 'bpf-tracing-multiprog-tp-query'



Yonghong Song says:

====================
Commit e87c6bc3 ("bpf: permit multiple bpf attachments
for a single perf event") added support to attach multiple
bpf programs to a single perf event. Given a perf event
(kprobe, uprobe, or kernel tracepoint), the perf ioctl interface
is used to query bpf programs attached to the same trace event.

There already exists a BPF_PROG_QUERY command for introspection
currently used by cgroup+bpf. We did have an implementation for
querying tracepoint+bpf through the same interface. However, it
looks cleaner to use ioctl() style of api here, since attaching
bpf prog to tracepoint/kuprobe is also done via ioctl.

Patch #1 had the core implementation and patch #2 added
a test case in tools bpf selftests suite.

Changelogs:
v3 -> v4:
  - Fix a compilation error with newer gcc like 6.3.1 while
    old gcc 4.8.5 is okay. I was using &uquery->ids to represent
    the address to the ids array to make it explicit that the
    address is passed, and this syntax is rightly rejected
    by gcc 6.3.1.
v2 -> v3:
  - Change uapi structure perf_event_query_bpf to be more
    clearer based on Peter's suggestion, and adjust
    other codes accordingly.
v1 -> v2:
  - Rebase on top of net-next.
  - Use existing bpf_prog_array_length function instead of
    implementing the same functionality in function
    bpf_prog_array_copy_info.
====================

Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 63060c39 d279f1f8
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -254,6 +254,7 @@ typedef unsigned long (*bpf_ctx_copy_t)(void *dst, const void *src,

u64 bpf_event_output(struct bpf_map *map, u64 flags, void *meta, u64 meta_size,
		     void *ctx, u64 ctx_size, bpf_ctx_copy_t ctx_copy);
int bpf_event_query_prog_array(struct perf_event *event, void __user *info);

int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr,
			  union bpf_attr __user *uattr);
@@ -285,6 +286,9 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs,

void bpf_prog_array_delete_safe(struct bpf_prog_array __rcu *progs,
				struct bpf_prog *old_prog);
int bpf_prog_array_copy_info(struct bpf_prog_array __rcu *array,
			     __u32 __user *prog_ids, u32 request_cnt,
			     __u32 __user *prog_cnt);
int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array,
			struct bpf_prog *exclude_prog,
			struct bpf_prog *include_prog,
+22 −0
Original line number Diff line number Diff line
@@ -418,6 +418,27 @@ struct perf_event_attr {
	__u16	__reserved_2;	/* align to __u64 */
};

/*
 * Structure used by below PERF_EVENT_IOC_QUERY_BPF command
 * to query bpf programs attached to the same perf tracepoint
 * as the given perf event.
 */
struct perf_event_query_bpf {
	/*
	 * The below ids array length
	 */
	__u32	ids_len;
	/*
	 * Set by the kernel to indicate the number of
	 * available programs
	 */
	__u32	prog_cnt;
	/*
	 * User provided buffer to store program ids
	 */
	__u32	ids[0];
};

#define perf_flags(attr)	(*(&(attr)->read_format + 1))

/*
@@ -433,6 +454,7 @@ struct perf_event_attr {
#define PERF_EVENT_IOC_ID		_IOR('$', 7, __u64 *)
#define PERF_EVENT_IOC_SET_BPF		_IOW('$', 8, __u32)
#define PERF_EVENT_IOC_PAUSE_OUTPUT	_IOW('$', 9, __u32)
#define PERF_EVENT_IOC_QUERY_BPF	_IOWR('$', 10, struct perf_event_query_bpf *)

enum perf_event_ioc_flags {
	PERF_IOC_FLAG_GROUP		= 1U << 0,
+21 −0
Original line number Diff line number Diff line
@@ -1462,6 +1462,8 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs,
	rcu_read_lock();
	prog = rcu_dereference(progs)->progs;
	for (; *prog; prog++) {
		if (*prog == &dummy_bpf_prog.prog)
			continue;
		id = (*prog)->aux->id;
		if (copy_to_user(prog_ids + i, &id, sizeof(id))) {
			rcu_read_unlock();
@@ -1545,6 +1547,25 @@ int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array,
	return 0;
}

int bpf_prog_array_copy_info(struct bpf_prog_array __rcu *array,
			     __u32 __user *prog_ids, u32 request_cnt,
			     __u32 __user *prog_cnt)
{
	u32 cnt = 0;

	if (array)
		cnt = bpf_prog_array_length(array);

	if (copy_to_user(prog_cnt, &cnt, sizeof(cnt)))
		return -EFAULT;

	/* return early if user requested only program count or nothing to copy */
	if (!request_cnt || !cnt)
		return 0;

	return bpf_prog_array_copy_to_user(array, prog_ids, request_cnt);
}

static void bpf_prog_free_deferred(struct work_struct *work)
{
	struct bpf_prog_aux *aux;
+3 −0
Original line number Diff line number Diff line
@@ -4723,6 +4723,9 @@ static long _perf_ioctl(struct perf_event *event, unsigned int cmd, unsigned lon
		rcu_read_unlock();
		return 0;
	}

	case PERF_EVENT_IOC_QUERY_BPF:
		return bpf_event_query_prog_array(event, (void __user *)arg);
	default:
		return -ENOTTY;
	}
+23 −0
Original line number Diff line number Diff line
@@ -820,3 +820,26 @@ void perf_event_detach_bpf_prog(struct perf_event *event)
unlock:
	mutex_unlock(&bpf_event_mutex);
}

int bpf_event_query_prog_array(struct perf_event *event, void __user *info)
{
	struct perf_event_query_bpf __user *uquery = info;
	struct perf_event_query_bpf query = {};
	int ret;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;
	if (event->attr.type != PERF_TYPE_TRACEPOINT)
		return -EINVAL;
	if (copy_from_user(&query, uquery, sizeof(query)))
		return -EFAULT;

	mutex_lock(&bpf_event_mutex);
	ret = bpf_prog_array_copy_info(event->tp_event->prog_array,
				       uquery->ids,
				       query.ids_len,
				       &uquery->prog_cnt);
	mutex_unlock(&bpf_event_mutex);

	return ret;
}
Loading