Adding new link to allow to attach program to multiple function
BTF IDs. The link is represented by struct bpf_tracing_multi_link.

To configure the link, new fields are added to bpf_attr::link_create
to pass array of BTF IDs;

  struct {
    __aligned_u64 ids;
    __u32         cnt;
  } tracing_multi;

Each BTF ID represents function (BTF_KIND_FUNC) that the link will
attach bpf program to.

We use previously added bpf_trampoline_multi_attach/detach functions
to attach/detach the link.

The linkinfo/fdinfo callbacks will be implemented in following changes.

Note this is supported only for archs (x86_64) with ftrace direct and
have single ops support.

  CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS &&
  CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS

Signed-off-by: Jiri Olsa <[email protected]>
---
 include/linux/bpf_types.h      |  1 +
 include/linux/trace_events.h   |  6 +++
 include/uapi/linux/bpf.h       |  5 ++
 kernel/bpf/syscall.c           |  2 +
 kernel/trace/bpf_trace.c       | 87 ++++++++++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h |  6 +++
 tools/lib/bpf/libbpf.c         |  1 +
 7 files changed, 108 insertions(+)

diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index b13de31e163f..c1656f026790 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -155,3 +155,4 @@ BPF_LINK_TYPE(BPF_LINK_TYPE_PERF_EVENT, perf)
 BPF_LINK_TYPE(BPF_LINK_TYPE_KPROBE_MULTI, kprobe_multi)
 BPF_LINK_TYPE(BPF_LINK_TYPE_STRUCT_OPS, struct_ops)
 BPF_LINK_TYPE(BPF_LINK_TYPE_UPROBE_MULTI, uprobe_multi)
+BPF_LINK_TYPE(BPF_LINK_TYPE_TRACING_MULTI, tracing)
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index 0a2b8229b999..7a28cc824fca 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -778,6 +778,7 @@ int bpf_get_perf_event_info(const struct perf_event *event, 
u32 *prog_id,
                            unsigned long *missed);
 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);
+int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr 
*attr);
 #else
 static inline unsigned int trace_call_bpf(struct trace_event_call *call, void 
*ctx)
 {
@@ -830,6 +831,11 @@ bpf_uprobe_multi_link_attach(const union bpf_attr *attr, 
struct bpf_prog *prog)
 {
        return -EOPNOTSUPP;
 }
+static inline int
+bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)
+{
+       return -EOPNOTSUPP;
+}
 #endif
 
 enum {
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 68600972a778..7f5c51f27a36 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -1180,6 +1180,7 @@ enum bpf_link_type {
        BPF_LINK_TYPE_UPROBE_MULTI = 12,
        BPF_LINK_TYPE_NETKIT = 13,
        BPF_LINK_TYPE_SOCKMAP = 14,
+       BPF_LINK_TYPE_TRACING_MULTI = 15,
        __MAX_BPF_LINK_TYPE,
 };
 
@@ -1863,6 +1864,10 @@ union bpf_attr {
                                };
                                __u64           expected_revision;
                        } cgroup;
+                       struct {
+                               __aligned_u64   ids;
+                               __u32           cnt;
+                       } tracing_multi;
                };
        } link_create;
 
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index ff85a9fa080e..5892dca20b7e 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -5751,6 +5751,8 @@ static int link_create(union bpf_attr *attr, bpfptr_t 
uattr)
                        ret = bpf_iter_link_attach(attr, uattr, prog);
                else if (prog->expected_attach_type == BPF_LSM_CGROUP)
                        ret = cgroup_bpf_link_attach(attr, prog);
+               else if (is_tracing_multi(prog->expected_attach_type))
+                       ret = bpf_tracing_multi_attach(prog, attr);
                else
                        ret = bpf_tracing_prog_attach(prog,
                                                      
attr->link_create.target_fd,
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index eadaef8592a3..bfae9ec5d1b1 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -42,6 +42,7 @@
 
 #define MAX_UPROBE_MULTI_CNT (1U << 20)
 #define MAX_KPROBE_MULTI_CNT (1U << 20)
+#define MAX_TRACING_MULTI_CNT (1U << 20)
 
 #ifdef CONFIG_MODULES
 struct bpf_trace_module {
@@ -3592,3 +3593,89 @@ __bpf_kfunc int 
bpf_copy_from_user_task_str_dynptr(struct bpf_dynptr *dptr, u64
 }
 
 __bpf_kfunc_end_defs();
+
+#if defined(CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS) && 
defined(CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS)
+
+static void bpf_tracing_multi_link_release(struct bpf_link *link)
+{
+       struct bpf_tracing_multi_link *tr_link =
+               container_of(link, struct bpf_tracing_multi_link, link);
+
+       WARN_ON_ONCE(bpf_trampoline_multi_detach(link->prog, tr_link));
+}
+
+static void bpf_tracing_multi_link_dealloc(struct bpf_link *link)
+{
+       struct bpf_tracing_multi_link *tr_link =
+               container_of(link, struct bpf_tracing_multi_link, link);
+
+       kfree(tr_link);
+}
+
+static const struct bpf_link_ops bpf_tracing_multi_link_lops = {
+       .release = bpf_tracing_multi_link_release,
+       .dealloc = bpf_tracing_multi_link_dealloc,
+};
+
+int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)
+{
+       struct bpf_tracing_multi_link *link = NULL;
+       struct bpf_link_primer link_primer;
+       u32 cnt, *ids = NULL;
+       u32 __user *uids;
+       int err;
+
+       uids = u64_to_user_ptr(attr->link_create.tracing_multi.ids);
+       cnt = attr->link_create.tracing_multi.cnt;
+
+       if (!cnt || !uids)
+               return -EINVAL;
+       if (cnt > MAX_TRACING_MULTI_CNT)
+               return -E2BIG;
+
+       ids = kvmalloc_array(cnt, sizeof(*ids), GFP_KERNEL);
+       if (!ids)
+               return -ENOMEM;
+
+       if (copy_from_user(ids, uids, cnt * sizeof(*ids))) {
+               err = -EFAULT;
+               goto error;
+       }
+
+       link = kzalloc(struct_size(link, nodes, cnt), GFP_KERNEL);
+       if (!link) {
+               err = -ENOMEM;
+               goto error;
+       }
+
+       bpf_link_init(&link->link, BPF_LINK_TYPE_TRACING_MULTI,
+                     &bpf_tracing_multi_link_lops, prog, 
prog->expected_attach_type);
+
+       err = bpf_link_prime(&link->link, &link_primer);
+       if (err)
+               goto error;
+
+       link->nodes_cnt = cnt;
+
+       err = bpf_trampoline_multi_attach(prog, ids, link);
+       kvfree(ids);
+       if (err) {
+               bpf_link_cleanup(&link_primer);
+               return err;
+       }
+       return bpf_link_settle(&link_primer);
+
+error:
+       kvfree(ids);
+       kfree(link);
+       return err;
+}
+
+#else
+
+int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)
+{
+       return -EOPNOTSUPP;
+}
+
+#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS) && 
CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS */
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 61f0fe5bc0aa..7f5c51f27a36 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -1180,6 +1180,7 @@ enum bpf_link_type {
        BPF_LINK_TYPE_UPROBE_MULTI = 12,
        BPF_LINK_TYPE_NETKIT = 13,
        BPF_LINK_TYPE_SOCKMAP = 14,
+       BPF_LINK_TYPE_TRACING_MULTI = 15,
        __MAX_BPF_LINK_TYPE,
 };
 
@@ -1863,6 +1864,10 @@ union bpf_attr {
                                };
                                __u64           expected_revision;
                        } cgroup;
+                       struct {
+                               __aligned_u64   ids;
+                               __u32           cnt;
+                       } tracing_multi;
                };
        } link_create;
 
@@ -7236,6 +7241,7 @@ enum {
        TCP_BPF_SOCK_OPS_CB_FLAGS = 1008, /* Get or Set TCP sock ops flags */
        SK_BPF_CB_FLAGS         = 1009, /* Get or set sock ops flags in socket 
*/
        SK_BPF_BYPASS_PROT_MEM  = 1010, /* Get or Set sk->sk_bypass_prot_mem */
+
 };
 
 enum {
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 1e19c7b861ec..74e579d7f310 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -156,6 +156,7 @@ static const char * const link_type_name[] = {
        [BPF_LINK_TYPE_UPROBE_MULTI]            = "uprobe_multi",
        [BPF_LINK_TYPE_NETKIT]                  = "netkit",
        [BPF_LINK_TYPE_SOCKMAP]                 = "sockmap",
+       [BPF_LINK_TYPE_TRACING_MULTI]           = "tracing_multi",
 };
 
 static const char * const map_type_name[] = {
-- 
2.52.0


Reply via email to