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

Merge branch 'bpf-add-multi-uprobe-link'

Jiri Olsa says:

====================
bpf: Add multi uprobe link

hi,
this patchset is adding support to attach multiple uprobes and usdt probes
through new uprobe_multi link.

The current uprobe is attached through the perf event and attaching many
uprobes takes a lot of time because of that.

The main reason is that we need to install perf event for each probed function
and profile shows perf event installation (perf_install_in_context) as culprit.

The new uprobe_multi link just creates raw uprobes and attaches the bpf
program to them without perf event being involved.

In addition to being faster we also save file descriptors. For the current
uprobe attach we use extra perf event fd for each probed function. The new
link just need one fd that covers all the functions we are attaching to.

v7 changes:
  - fixed task release on error path and re-org the error
    path to be more straightforward [Yonghong]
  - re-organized uprobe_prog_run locking to follow general pattern
    and removed might_fault check as it's not needed in uprobe/task
    context [Yonghong]

There's support for bpftrace [2] and tetragon [1].

Also available at:
  https://git.kernel.org/pub/scm/linux/kernel/git/jolsa/perf.git
  uprobe_multi

thanks,
jirka

[1] https://github.com/cilium/tetragon/pull/936
[2] https://github.com/iovisor/bpftrace/compare/master...olsajiri:bpftrace:uprobe_multi
[3] https://lore.kernel.org/bpf/20230628115329.248450-1-laoar.shao@gmail.com/
---
====================

Link: https://lore.kernel.org/r/20230809083440.3209381-1-jolsa@kernel.org


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents acfadf25 8909a939
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -752,6 +752,7 @@ int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id,
			    u32 *fd_type, const char **buf,
			    u64 *probe_offset, u64 *probe_addr);
int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog);
int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog);
#else
static inline unsigned int trace_call_bpf(struct trace_event_call *call, void *ctx)
{
@@ -798,6 +799,11 @@ bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
{
	return -EOPNOTSUPP;
}
static inline int
bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
{
	return -EOPNOTSUPP;
}
#endif

enum {
+21 −1
Original line number Diff line number Diff line
@@ -1039,6 +1039,7 @@ enum bpf_attach_type {
	BPF_NETFILTER,
	BPF_TCX_INGRESS,
	BPF_TCX_EGRESS,
	BPF_TRACE_UPROBE_MULTI,
	__MAX_BPF_ATTACH_TYPE
};

@@ -1057,6 +1058,7 @@ enum bpf_link_type {
	BPF_LINK_TYPE_STRUCT_OPS = 9,
	BPF_LINK_TYPE_NETFILTER = 10,
	BPF_LINK_TYPE_TCX = 11,
	BPF_LINK_TYPE_UPROBE_MULTI = 12,
	MAX_BPF_LINK_TYPE,
};

@@ -1186,7 +1188,16 @@ enum bpf_perf_event_type {
/* link_create.kprobe_multi.flags used in LINK_CREATE command for
 * BPF_TRACE_KPROBE_MULTI attach type to create return probe.
 */
#define BPF_F_KPROBE_MULTI_RETURN	(1U << 0)
enum {
	BPF_F_KPROBE_MULTI_RETURN = (1U << 0)
};

/* link_create.uprobe_multi.flags used in LINK_CREATE command for
 * BPF_TRACE_UPROBE_MULTI attach type to create return probe.
 */
enum {
	BPF_F_UPROBE_MULTI_RETURN = (1U << 0)
};

/* link_create.netfilter.flags used in LINK_CREATE command for
 * BPF_PROG_TYPE_NETFILTER to enable IP packet defragmentation.
@@ -1624,6 +1635,15 @@ union bpf_attr {
				};
				__u64		expected_revision;
			} tcx;
			struct {
				__aligned_u64	path;
				__aligned_u64	offsets;
				__aligned_u64	ref_ctr_offsets;
				__aligned_u64	cookies;
				__u32		cnt;
				__u32		flags;
				__u32		pid;
			} uprobe_multi;
		};
	} link_create;

+63 −71
Original line number Diff line number Diff line
@@ -2819,6 +2819,8 @@ static void bpf_link_free_id(int id)
 * defunct, releases anon_inode file and puts reserved FD. bpf_prog's refcnt
 * is not decremented, it's the responsibility of a calling code that failed
 * to complete bpf_link initialization.
 * This helper eventually calls link's dealloc callback, but does not call
 * link's release callback.
 */
void bpf_link_cleanup(struct bpf_link_primer *primer)
{
@@ -3655,34 +3657,6 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
	return fd;
}

static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog,
					     enum bpf_attach_type attach_type)
{
	switch (prog->type) {
	case BPF_PROG_TYPE_CGROUP_SOCK:
	case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
	case BPF_PROG_TYPE_CGROUP_SOCKOPT:
	case BPF_PROG_TYPE_SK_LOOKUP:
		return attach_type == prog->expected_attach_type ? 0 : -EINVAL;
	case BPF_PROG_TYPE_CGROUP_SKB:
		if (!capable(CAP_NET_ADMIN))
			/* cg-skb progs can be loaded by unpriv user.
			 * check permissions at attach time.
			 */
			return -EPERM;
		return prog->enforce_expected_attach_type &&
			prog->expected_attach_type != attach_type ?
			-EINVAL : 0;
	case BPF_PROG_TYPE_KPROBE:
		if (prog->expected_attach_type == BPF_TRACE_KPROBE_MULTI &&
		    attach_type != BPF_TRACE_KPROBE_MULTI)
			return -EINVAL;
		return 0;
	default:
		return 0;
	}
}

static enum bpf_prog_type
attach_type_to_prog_type(enum bpf_attach_type attach_type)
{
@@ -3749,6 +3723,62 @@ attach_type_to_prog_type(enum bpf_attach_type attach_type)
	}
}

static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog,
					     enum bpf_attach_type attach_type)
{
	enum bpf_prog_type ptype;

	switch (prog->type) {
	case BPF_PROG_TYPE_CGROUP_SOCK:
	case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
	case BPF_PROG_TYPE_CGROUP_SOCKOPT:
	case BPF_PROG_TYPE_SK_LOOKUP:
		return attach_type == prog->expected_attach_type ? 0 : -EINVAL;
	case BPF_PROG_TYPE_CGROUP_SKB:
		if (!capable(CAP_NET_ADMIN))
			/* cg-skb progs can be loaded by unpriv user.
			 * check permissions at attach time.
			 */
			return -EPERM;
		return prog->enforce_expected_attach_type &&
			prog->expected_attach_type != attach_type ?
			-EINVAL : 0;
	case BPF_PROG_TYPE_EXT:
		return 0;
	case BPF_PROG_TYPE_NETFILTER:
		if (attach_type != BPF_NETFILTER)
			return -EINVAL;
		return 0;
	case BPF_PROG_TYPE_PERF_EVENT:
	case BPF_PROG_TYPE_TRACEPOINT:
		if (attach_type != BPF_PERF_EVENT)
			return -EINVAL;
		return 0;
	case BPF_PROG_TYPE_KPROBE:
		if (prog->expected_attach_type == BPF_TRACE_KPROBE_MULTI &&
		    attach_type != BPF_TRACE_KPROBE_MULTI)
			return -EINVAL;
		if (prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI &&
		    attach_type != BPF_TRACE_UPROBE_MULTI)
			return -EINVAL;
		if (attach_type != BPF_PERF_EVENT &&
		    attach_type != BPF_TRACE_KPROBE_MULTI &&
		    attach_type != BPF_TRACE_UPROBE_MULTI)
			return -EINVAL;
		return 0;
	case BPF_PROG_TYPE_SCHED_CLS:
		if (attach_type != BPF_TCX_INGRESS &&
		    attach_type != BPF_TCX_EGRESS)
			return -EINVAL;
		return 0;
	default:
		ptype = attach_type_to_prog_type(attach_type);
		if (ptype == BPF_PROG_TYPE_UNSPEC || ptype != prog->type)
			return -EINVAL;
		return 0;
	}
}

#define BPF_PROG_ATTACH_LAST_FIELD expected_revision

#define BPF_F_ATTACH_MASK_BASE	\
@@ -4852,10 +4882,9 @@ static int bpf_map_do_batch(const union bpf_attr *attr,
	return err;
}

#define BPF_LINK_CREATE_LAST_FIELD link_create.kprobe_multi.cookies
#define BPF_LINK_CREATE_LAST_FIELD link_create.uprobe_multi.pid
static int link_create(union bpf_attr *attr, bpfptr_t uattr)
{
	enum bpf_prog_type ptype;
	struct bpf_prog *prog;
	int ret;

@@ -4874,45 +4903,6 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
	if (ret)
		goto out;

	switch (prog->type) {
	case BPF_PROG_TYPE_EXT:
		break;
	case BPF_PROG_TYPE_NETFILTER:
		if (attr->link_create.attach_type != BPF_NETFILTER) {
			ret = -EINVAL;
			goto out;
		}
		break;
	case BPF_PROG_TYPE_PERF_EVENT:
	case BPF_PROG_TYPE_TRACEPOINT:
		if (attr->link_create.attach_type != BPF_PERF_EVENT) {
			ret = -EINVAL;
			goto out;
		}
		break;
	case BPF_PROG_TYPE_KPROBE:
		if (attr->link_create.attach_type != BPF_PERF_EVENT &&
		    attr->link_create.attach_type != BPF_TRACE_KPROBE_MULTI) {
			ret = -EINVAL;
			goto out;
		}
		break;
	case BPF_PROG_TYPE_SCHED_CLS:
		if (attr->link_create.attach_type != BPF_TCX_INGRESS &&
		    attr->link_create.attach_type != BPF_TCX_EGRESS) {
			ret = -EINVAL;
			goto out;
		}
		break;
	default:
		ptype = attach_type_to_prog_type(attr->link_create.attach_type);
		if (ptype == BPF_PROG_TYPE_UNSPEC || ptype != prog->type) {
			ret = -EINVAL;
			goto out;
		}
		break;
	}

	switch (prog->type) {
	case BPF_PROG_TYPE_CGROUP_SKB:
	case BPF_PROG_TYPE_CGROUP_SOCK:
@@ -4969,8 +4959,10 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
	case BPF_PROG_TYPE_KPROBE:
		if (attr->link_create.attach_type == BPF_PERF_EVENT)
			ret = bpf_perf_link_attach(attr, prog);
		else
		else if (attr->link_create.attach_type == BPF_TRACE_KPROBE_MULTI)
			ret = bpf_kprobe_multi_link_attach(attr, prog);
		else if (attr->link_create.attach_type == BPF_TRACE_UPROBE_MULTI)
			ret = bpf_uprobe_multi_link_attach(attr, prog);
		break;
	default:
		ret = -EINVAL;
+336 −6
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <linux/sort.h>
#include <linux/key.h>
#include <linux/verification.h>
#include <linux/namei.h>

#include <net/bpf_sk_storage.h>

@@ -86,6 +87,9 @@ static int bpf_btf_printf_prepare(struct btf_ptr *ptr, u32 btf_ptr_size,
static u64 bpf_kprobe_multi_cookie(struct bpf_run_ctx *ctx);
static u64 bpf_kprobe_multi_entry_ip(struct bpf_run_ctx *ctx);

static u64 bpf_uprobe_multi_cookie(struct bpf_run_ctx *ctx);
static u64 bpf_uprobe_multi_entry_ip(struct bpf_run_ctx *ctx);

/**
 * trace_call_bpf - invoke BPF program
 * @call: tracepoint event
@@ -1103,6 +1107,30 @@ static const struct bpf_func_proto bpf_get_attach_cookie_proto_kmulti = {
	.arg1_type	= ARG_PTR_TO_CTX,
};

BPF_CALL_1(bpf_get_func_ip_uprobe_multi, struct pt_regs *, regs)
{
	return bpf_uprobe_multi_entry_ip(current->bpf_ctx);
}

static const struct bpf_func_proto bpf_get_func_ip_proto_uprobe_multi = {
	.func		= bpf_get_func_ip_uprobe_multi,
	.gpl_only	= false,
	.ret_type	= RET_INTEGER,
	.arg1_type	= ARG_PTR_TO_CTX,
};

BPF_CALL_1(bpf_get_attach_cookie_uprobe_multi, struct pt_regs *, regs)
{
	return bpf_uprobe_multi_cookie(current->bpf_ctx);
}

static const struct bpf_func_proto bpf_get_attach_cookie_proto_umulti = {
	.func		= bpf_get_attach_cookie_uprobe_multi,
	.gpl_only	= false,
	.ret_type	= RET_INTEGER,
	.arg1_type	= ARG_PTR_TO_CTX,
};

BPF_CALL_1(bpf_get_attach_cookie_trace, void *, ctx)
{
	struct bpf_trace_run_ctx *run_ctx;
@@ -1545,13 +1573,17 @@ kprobe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
		return &bpf_override_return_proto;
#endif
	case BPF_FUNC_get_func_ip:
		return prog->expected_attach_type == BPF_TRACE_KPROBE_MULTI ?
			&bpf_get_func_ip_proto_kprobe_multi :
			&bpf_get_func_ip_proto_kprobe;
		if (prog->expected_attach_type == BPF_TRACE_KPROBE_MULTI)
			return &bpf_get_func_ip_proto_kprobe_multi;
		if (prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI)
			return &bpf_get_func_ip_proto_uprobe_multi;
		return &bpf_get_func_ip_proto_kprobe;
	case BPF_FUNC_get_attach_cookie:
		return prog->expected_attach_type == BPF_TRACE_KPROBE_MULTI ?
			&bpf_get_attach_cookie_proto_kmulti :
			&bpf_get_attach_cookie_proto_trace;
		if (prog->expected_attach_type == BPF_TRACE_KPROBE_MULTI)
			return &bpf_get_attach_cookie_proto_kmulti;
		if (prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI)
			return &bpf_get_attach_cookie_proto_umulti;
		return &bpf_get_attach_cookie_proto_trace;
	default:
		return bpf_tracing_func_proto(func_id, prog);
	}
@@ -2970,3 +3002,301 @@ static u64 bpf_kprobe_multi_entry_ip(struct bpf_run_ctx *ctx)
	return 0;
}
#endif

#ifdef CONFIG_UPROBES
struct bpf_uprobe_multi_link;

struct bpf_uprobe {
	struct bpf_uprobe_multi_link *link;
	loff_t offset;
	u64 cookie;
	struct uprobe_consumer consumer;
};

struct bpf_uprobe_multi_link {
	struct path path;
	struct bpf_link link;
	u32 cnt;
	struct bpf_uprobe *uprobes;
	struct task_struct *task;
};

struct bpf_uprobe_multi_run_ctx {
	struct bpf_run_ctx run_ctx;
	unsigned long entry_ip;
	struct bpf_uprobe *uprobe;
};

static void bpf_uprobe_unregister(struct path *path, struct bpf_uprobe *uprobes,
				  u32 cnt)
{
	u32 i;

	for (i = 0; i < cnt; i++) {
		uprobe_unregister(d_real_inode(path->dentry), uprobes[i].offset,
				  &uprobes[i].consumer);
	}
}

static void bpf_uprobe_multi_link_release(struct bpf_link *link)
{
	struct bpf_uprobe_multi_link *umulti_link;

	umulti_link = container_of(link, struct bpf_uprobe_multi_link, link);
	bpf_uprobe_unregister(&umulti_link->path, umulti_link->uprobes, umulti_link->cnt);
}

static void bpf_uprobe_multi_link_dealloc(struct bpf_link *link)
{
	struct bpf_uprobe_multi_link *umulti_link;

	umulti_link = container_of(link, struct bpf_uprobe_multi_link, link);
	if (umulti_link->task)
		put_task_struct(umulti_link->task);
	path_put(&umulti_link->path);
	kvfree(umulti_link->uprobes);
	kfree(umulti_link);
}

static const struct bpf_link_ops bpf_uprobe_multi_link_lops = {
	.release = bpf_uprobe_multi_link_release,
	.dealloc = bpf_uprobe_multi_link_dealloc,
};

static int uprobe_prog_run(struct bpf_uprobe *uprobe,
			   unsigned long entry_ip,
			   struct pt_regs *regs)
{
	struct bpf_uprobe_multi_link *link = uprobe->link;
	struct bpf_uprobe_multi_run_ctx run_ctx = {
		.entry_ip = entry_ip,
		.uprobe = uprobe,
	};
	struct bpf_prog *prog = link->link.prog;
	bool sleepable = prog->aux->sleepable;
	struct bpf_run_ctx *old_run_ctx;
	int err = 0;

	if (link->task && current != link->task)
		return 0;

	if (sleepable)
		rcu_read_lock_trace();
	else
		rcu_read_lock();

	migrate_disable();

	old_run_ctx = bpf_set_run_ctx(&run_ctx.run_ctx);
	err = bpf_prog_run(link->link.prog, regs);
	bpf_reset_run_ctx(old_run_ctx);

	migrate_enable();

	if (sleepable)
		rcu_read_unlock_trace();
	else
		rcu_read_unlock();
	return err;
}

static bool
uprobe_multi_link_filter(struct uprobe_consumer *con, enum uprobe_filter_ctx ctx,
			 struct mm_struct *mm)
{
	struct bpf_uprobe *uprobe;

	uprobe = container_of(con, struct bpf_uprobe, consumer);
	return uprobe->link->task->mm == mm;
}

static int
uprobe_multi_link_handler(struct uprobe_consumer *con, struct pt_regs *regs)
{
	struct bpf_uprobe *uprobe;

	uprobe = container_of(con, struct bpf_uprobe, consumer);
	return uprobe_prog_run(uprobe, instruction_pointer(regs), regs);
}

static int
uprobe_multi_link_ret_handler(struct uprobe_consumer *con, unsigned long func, struct pt_regs *regs)
{
	struct bpf_uprobe *uprobe;

	uprobe = container_of(con, struct bpf_uprobe, consumer);
	return uprobe_prog_run(uprobe, func, regs);
}

static u64 bpf_uprobe_multi_entry_ip(struct bpf_run_ctx *ctx)
{
	struct bpf_uprobe_multi_run_ctx *run_ctx;

	run_ctx = container_of(current->bpf_ctx, struct bpf_uprobe_multi_run_ctx, run_ctx);
	return run_ctx->entry_ip;
}

static u64 bpf_uprobe_multi_cookie(struct bpf_run_ctx *ctx)
{
	struct bpf_uprobe_multi_run_ctx *run_ctx;

	run_ctx = container_of(current->bpf_ctx, struct bpf_uprobe_multi_run_ctx, run_ctx);
	return run_ctx->uprobe->cookie;
}

int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
{
	struct bpf_uprobe_multi_link *link = NULL;
	unsigned long __user *uref_ctr_offsets;
	unsigned long *ref_ctr_offsets = NULL;
	struct bpf_link_primer link_primer;
	struct bpf_uprobe *uprobes = NULL;
	struct task_struct *task = NULL;
	unsigned long __user *uoffsets;
	u64 __user *ucookies;
	void __user *upath;
	u32 flags, cnt, i;
	struct path path;
	char *name;
	pid_t pid;
	int err;

	/* no support for 32bit archs yet */
	if (sizeof(u64) != sizeof(void *))
		return -EOPNOTSUPP;

	if (prog->expected_attach_type != BPF_TRACE_UPROBE_MULTI)
		return -EINVAL;

	flags = attr->link_create.uprobe_multi.flags;
	if (flags & ~BPF_F_UPROBE_MULTI_RETURN)
		return -EINVAL;

	/*
	 * path, offsets and cnt are mandatory,
	 * ref_ctr_offsets and cookies are optional
	 */
	upath = u64_to_user_ptr(attr->link_create.uprobe_multi.path);
	uoffsets = u64_to_user_ptr(attr->link_create.uprobe_multi.offsets);
	cnt = attr->link_create.uprobe_multi.cnt;

	if (!upath || !uoffsets || !cnt)
		return -EINVAL;

	uref_ctr_offsets = u64_to_user_ptr(attr->link_create.uprobe_multi.ref_ctr_offsets);
	ucookies = u64_to_user_ptr(attr->link_create.uprobe_multi.cookies);

	name = strndup_user(upath, PATH_MAX);
	if (IS_ERR(name)) {
		err = PTR_ERR(name);
		return err;
	}

	err = kern_path(name, LOOKUP_FOLLOW, &path);
	kfree(name);
	if (err)
		return err;

	if (!d_is_reg(path.dentry)) {
		err = -EBADF;
		goto error_path_put;
	}

	pid = attr->link_create.uprobe_multi.pid;
	if (pid) {
		rcu_read_lock();
		task = get_pid_task(find_vpid(pid), PIDTYPE_PID);
		rcu_read_unlock();
		if (!task)
			goto error_path_put;
	}

	err = -ENOMEM;

	link = kzalloc(sizeof(*link), GFP_KERNEL);
	uprobes = kvcalloc(cnt, sizeof(*uprobes), GFP_KERNEL);

	if (!uprobes || !link)
		goto error_free;

	if (uref_ctr_offsets) {
		ref_ctr_offsets = kvcalloc(cnt, sizeof(*ref_ctr_offsets), GFP_KERNEL);
		if (!ref_ctr_offsets)
			goto error_free;
	}

	for (i = 0; i < cnt; i++) {
		if (ucookies && __get_user(uprobes[i].cookie, ucookies + i)) {
			err = -EFAULT;
			goto error_free;
		}
		if (uref_ctr_offsets && __get_user(ref_ctr_offsets[i], uref_ctr_offsets + i)) {
			err = -EFAULT;
			goto error_free;
		}
		if (__get_user(uprobes[i].offset, uoffsets + i)) {
			err = -EFAULT;
			goto error_free;
		}

		uprobes[i].link = link;

		if (flags & BPF_F_UPROBE_MULTI_RETURN)
			uprobes[i].consumer.ret_handler = uprobe_multi_link_ret_handler;
		else
			uprobes[i].consumer.handler = uprobe_multi_link_handler;

		if (pid)
			uprobes[i].consumer.filter = uprobe_multi_link_filter;
	}

	link->cnt = cnt;
	link->uprobes = uprobes;
	link->path = path;
	link->task = task;

	bpf_link_init(&link->link, BPF_LINK_TYPE_UPROBE_MULTI,
		      &bpf_uprobe_multi_link_lops, prog);

	for (i = 0; i < cnt; i++) {
		err = uprobe_register_refctr(d_real_inode(link->path.dentry),
					     uprobes[i].offset,
					     ref_ctr_offsets ? ref_ctr_offsets[i] : 0,
					     &uprobes[i].consumer);
		if (err) {
			bpf_uprobe_unregister(&path, uprobes, i);
			goto error_free;
		}
	}

	err = bpf_link_prime(&link->link, &link_primer);
	if (err)
		goto error_free;

	kvfree(ref_ctr_offsets);
	return bpf_link_settle(&link_primer);

error_free:
	kvfree(ref_ctr_offsets);
	kvfree(uprobes);
	kfree(link);
	if (task)
		put_task_struct(task);
error_path_put:
	path_put(&path);
	return err;
}
#else /* !CONFIG_UPROBES */
int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
{
	return -EOPNOTSUPP;
}
static u64 bpf_uprobe_multi_cookie(struct bpf_run_ctx *ctx)
{
	return 0;
}
static u64 bpf_uprobe_multi_entry_ip(struct bpf_run_ctx *ctx)
{
	return 0;
}
#endif /* CONFIG_UPROBES */
+21 −1
Original line number Diff line number Diff line
@@ -1039,6 +1039,7 @@ enum bpf_attach_type {
	BPF_NETFILTER,
	BPF_TCX_INGRESS,
	BPF_TCX_EGRESS,
	BPF_TRACE_UPROBE_MULTI,
	__MAX_BPF_ATTACH_TYPE
};

@@ -1057,6 +1058,7 @@ enum bpf_link_type {
	BPF_LINK_TYPE_STRUCT_OPS = 9,
	BPF_LINK_TYPE_NETFILTER = 10,
	BPF_LINK_TYPE_TCX = 11,
	BPF_LINK_TYPE_UPROBE_MULTI = 12,
	MAX_BPF_LINK_TYPE,
};

@@ -1186,7 +1188,16 @@ enum bpf_perf_event_type {
/* link_create.kprobe_multi.flags used in LINK_CREATE command for
 * BPF_TRACE_KPROBE_MULTI attach type to create return probe.
 */
#define BPF_F_KPROBE_MULTI_RETURN	(1U << 0)
enum {
	BPF_F_KPROBE_MULTI_RETURN = (1U << 0)
};

/* link_create.uprobe_multi.flags used in LINK_CREATE command for
 * BPF_TRACE_UPROBE_MULTI attach type to create return probe.
 */
enum {
	BPF_F_UPROBE_MULTI_RETURN = (1U << 0)
};

/* link_create.netfilter.flags used in LINK_CREATE command for
 * BPF_PROG_TYPE_NETFILTER to enable IP packet defragmentation.
@@ -1624,6 +1635,15 @@ union bpf_attr {
				};
				__u64		expected_revision;
			} tcx;
			struct {
				__aligned_u64	path;
				__aligned_u64	offsets;
				__aligned_u64	ref_ctr_offsets;
				__aligned_u64	cookies;
				__u32		cnt;
				__u32		flags;
				__u32		pid;
			} uprobe_multi;
		};
	} link_create;

Loading