From: Jiri Olsa <olsajiri@gmail.com>
To: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Oleg Nesterov <oleg@redhat.com>,
Peter Zijlstra <peterz@infradead.org>,
Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
bpf@vger.kernel.org, Martin KaFai Lau <kafai@fb.com>,
Song Liu <songliubraving@fb.com>, Yonghong Song <yhs@fb.com>,
John Fastabend <john.fastabend@gmail.com>,
KP Singh <kpsingh@chromium.org>,
Stanislav Fomichev <sdf@google.com>, Hao Luo <haoluo@google.com>,
Steven Rostedt <rostedt@goodmis.org>,
Masami Hiramatsu <mhiramat@kernel.org>,
linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org
Subject: Re: [PATCHv2 bpf-next 1/9] uprobe: Add support for session consumer
Date: Wed, 3 Jul 2024 19:13:16 +0200 [thread overview]
Message-ID: <ZoWGrGYdyaimB_zF@krava> (raw)
In-Reply-To: <CAEf4BzZaTNTDauJYaES-q40UpvcjNyDSfSnuU+DkSuAPSuZ8Qw@mail.gmail.com>
On Tue, Jul 02, 2024 at 01:51:28PM -0700, Andrii Nakryiko wrote:
SNIP
> > #ifdef CONFIG_UPROBES
> > @@ -80,6 +83,12 @@ struct uprobe_task {
> > unsigned int depth;
> > };
> >
> > +struct session_consumer {
> > + __u64 cookie;
> > + unsigned int id;
> > + int rc;
>
> you'll be using u64 for ID, right? so this struct will be 24 bytes.
yes
> Maybe we can just use topmost bit of ID to store whether uretprobe
> should run or not? It's trivial to mask out during ID comparisons
actually.. I think we could store just consumers that need to be
executed in return probe so there will be no need for 'rc' value
>
> > +};
> > +
> > struct return_instance {
> > struct uprobe *uprobe;
> > unsigned long func;
> > @@ -88,6 +97,9 @@ struct return_instance {
> > bool chained; /* true, if instance is nested */
> >
> > struct return_instance *next; /* keep as stack */
> > +
> > + int sessions_cnt;
>
> there is 7 byte gap before next field, let's put sessions_cnt there
ok
>
> > + struct session_consumer sessions[];
> > };
> >
> > enum rp_check {
> > diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
> > index 2c83ba776fc7..4da410460f2a 100644
> > --- a/kernel/events/uprobes.c
> > +++ b/kernel/events/uprobes.c
> > @@ -63,6 +63,8 @@ struct uprobe {
> > loff_t ref_ctr_offset;
> > unsigned long flags;
> >
> > + unsigned int sessions_cnt;
> > +
> > /*
> > * The generic code assumes that it has two members of unknown type
> > * owned by the arch-specific code:
> > @@ -750,11 +752,30 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset,
> > return uprobe;
> > }
> >
> > +static void
> > +uprobe_consumer_account(struct uprobe *uprobe, struct uprobe_consumer *uc)
> > +{
> > + static unsigned int session_id;
>
> (besides what Peter mentioned about wrap around of 32-bit counter)
> let's use atomic here to not rely on any particular locking
> (unnecessarily), this might make my life easier in the future, thanks.
> This is registration time, low frequency, extra atomic won't hurt.
>
> It might be already broken, actually, for two independently registering uprobes.
ok, will try
>
> > +
> > + if (uc->session) {
> > + uprobe->sessions_cnt++;
> > + uc->session_id = ++session_id ?: ++session_id;
> > + }
> > +}
> > +
> > +static void
> > +uprobe_consumer_unaccount(struct uprobe *uprobe, struct uprobe_consumer *uc)
>
> this fits in 100 characters, keep it single line, please. Same for
> account function
ok
>
> > +{
> > + if (uc->session)
> > + uprobe->sessions_cnt--;
> > +}
> > +
> > static void consumer_add(struct uprobe *uprobe, struct uprobe_consumer *uc)
> > {
> > down_write(&uprobe->consumer_rwsem);
> > uc->next = uprobe->consumers;
> > uprobe->consumers = uc;
> > + uprobe_consumer_account(uprobe, uc);
> > up_write(&uprobe->consumer_rwsem);
> > }
> >
> > @@ -773,6 +794,7 @@ static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *uc)
> > if (*con == uc) {
> > *con = uc->next;
> > ret = true;
> > + uprobe_consumer_unaccount(uprobe, uc);
> > break;
> > }
> > }
> > @@ -1744,6 +1766,23 @@ static struct uprobe_task *get_utask(void)
> > return current->utask;
> > }
> >
> > +static size_t ri_size(int sessions_cnt)
> > +{
> > + struct return_instance *ri __maybe_unused;
> > +
> > + return sizeof(*ri) + sessions_cnt * sizeof(ri->sessions[0]);
>
> just use struct_size()?
>
> > +}
> > +
> > +static struct return_instance *alloc_return_instance(int sessions_cnt)
> > +{
> > + struct return_instance *ri;
> > +
> > + ri = kzalloc(ri_size(sessions_cnt), GFP_KERNEL);
> > + if (ri)
> > + ri->sessions_cnt = sessions_cnt;
> > + return ri;
> > +}
> > +
> > static int dup_utask(struct task_struct *t, struct uprobe_task *o_utask)
> > {
> > struct uprobe_task *n_utask;
> > @@ -1756,11 +1795,11 @@ static int dup_utask(struct task_struct *t, struct uprobe_task *o_utask)
> >
> > p = &n_utask->return_instances;
> > for (o = o_utask->return_instances; o; o = o->next) {
> > - n = kmalloc(sizeof(struct return_instance), GFP_KERNEL);
> > + n = alloc_return_instance(o->sessions_cnt);
> > if (!n)
> > return -ENOMEM;
> >
> > - *n = *o;
> > + memcpy(n, o, ri_size(o->sessions_cnt));
> > get_uprobe(n->uprobe);
> > n->next = NULL;
> >
> > @@ -1853,9 +1892,9 @@ static void cleanup_return_instances(struct uprobe_task *utask, bool chained,
> > utask->return_instances = ri;
> > }
> >
> > -static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs)
> > +static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs,
> > + struct return_instance *ri)
> > {
> > - struct return_instance *ri;
> > struct uprobe_task *utask;
> > unsigned long orig_ret_vaddr, trampoline_vaddr;
> > bool chained;
> > @@ -1874,9 +1913,11 @@ static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs)
> > return;
> > }
> >
> > - ri = kmalloc(sizeof(struct return_instance), GFP_KERNEL);
> > - if (!ri)
> > - return;
> > + if (!ri) {
> > + ri = alloc_return_instance(0);
> > + if (!ri)
> > + return;
> > + }
> >
> > trampoline_vaddr = get_trampoline_vaddr();
> > orig_ret_vaddr = arch_uretprobe_hijack_return_addr(trampoline_vaddr, regs);
> > @@ -2065,35 +2106,85 @@ static struct uprobe *find_active_uprobe(unsigned long bp_vaddr, int *is_swbp)
> > return uprobe;
> > }
> >
> > +static struct session_consumer *
> > +session_consumer_next(struct return_instance *ri, struct session_consumer *sc,
> > + int session_id)
> > +{
> > + struct session_consumer *next;
> > +
> > + next = sc ? sc + 1 : &ri->sessions[0];
> > + next->id = session_id;
>
> it's kind of unexpected that "session_consumer_next" would actually
> set an ID... Maybe drop int session_id as input argument and fill it
> out outside of this function, this function being just a simple
> iterator?
yea, I was going back and forth on what to have in that function
or not, to keep the change minimal, but makes sense, will move
>
> > + return next;
> > +}
> > +
> > +static struct session_consumer *
> > +session_consumer_find(struct return_instance *ri, int *iter, int session_id)
> > +{
> > + struct session_consumer *sc;
> > + int idx = *iter;
> > +
> > + for (sc = &ri->sessions[idx]; idx < ri->sessions_cnt; idx++, sc++) {
> > + if (sc->id == session_id) {
> > + *iter = idx + 1;
> > + return sc;
> > + }
> > + }
> > + return NULL;
> > +}
> > +
> > static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs)
> > {
> > struct uprobe_consumer *uc;
> > int remove = UPROBE_HANDLER_REMOVE;
> > + struct session_consumer *sc = NULL;
> > + struct return_instance *ri = NULL;
> > bool need_prep = false; /* prepare return uprobe, when needed */
> >
> > down_read(&uprobe->register_rwsem);
> > + if (uprobe->sessions_cnt) {
> > + ri = alloc_return_instance(uprobe->sessions_cnt);
> > + if (!ri)
> > + goto out;
> > + }
> > +
> > for (uc = uprobe->consumers; uc; uc = uc->next) {
> > + __u64 *cookie = NULL;
> > int rc = 0;
> >
> > + if (uc->session) {
> > + sc = session_consumer_next(ri, sc, uc->session_id);
> > + cookie = &sc->cookie;
> > + }
> > +
> > if (uc->handler) {
> > - rc = uc->handler(uc, regs);
> > + rc = uc->handler(uc, regs, cookie);
> > WARN(rc & ~UPROBE_HANDLER_MASK,
> > "bad rc=0x%x from %ps()\n", rc, uc->handler);
> > }
> >
> > - if (uc->ret_handler)
> > + if (uc->session) {
> > + sc->rc = rc;
> > + need_prep |= !rc;
>
> nit:
>
> if (rc == 0)
> need_prep = true;
>
> and then it's *extremely obvious* what happens and under which conditions
ok
>
> > + } else if (uc->ret_handler) {
> > need_prep = true;
> > + }
> >
> > remove &= rc;
> > }
> >
> > + /* no removal if there's at least one session consumer */
> > + remove &= !uprobe->sessions_cnt;
>
> this is counter (not error, not pointer), let's stick to ` == 0`, please
>
> is this
>
> if (uprobe->sessions_cnt != 0)
> remove = 0;
yes ;-) will change
jirka
>
> ? I can't tell (honestly), without spending ridiculous amounts of
> mental resources (for the underlying simplicity of the condition).
SNIP
next prev parent reply other threads:[~2024-07-03 17:13 UTC|newest]
Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-01 16:41 [PATCHv2 bpf-next 0/9] uprobe, bpf: Add session support Jiri Olsa
2024-07-01 16:41 ` [PATCHv2 bpf-next 1/9] uprobe: Add support for session consumer Jiri Olsa
2024-07-02 13:04 ` Peter Zijlstra
2024-07-02 16:10 ` Jiri Olsa
2024-07-02 20:52 ` Andrii Nakryiko
2024-07-03 15:31 ` Jiri Olsa
2024-07-03 16:20 ` Jiri Olsa
2024-07-03 21:41 ` Andrii Nakryiko
2024-07-02 20:51 ` Andrii Nakryiko
2024-07-03 8:10 ` Peter Zijlstra
2024-07-03 18:31 ` Andrii Nakryiko
2024-07-03 20:36 ` Kees Cook
2024-07-05 7:10 ` Peter Zijlstra
2024-07-05 23:10 ` Kees Cook
2024-07-03 17:13 ` Jiri Olsa [this message]
2024-07-03 21:48 ` Andrii Nakryiko
2024-07-05 13:29 ` Jiri Olsa
2024-07-02 23:55 ` Masami Hiramatsu
2024-07-03 0:13 ` Andrii Nakryiko
2024-07-03 16:09 ` Jiri Olsa
2024-07-03 21:43 ` Andrii Nakryiko
2024-07-05 8:35 ` Masami Hiramatsu
2024-07-05 13:38 ` Jiri Olsa
2024-07-08 9:41 ` Peter Zijlstra
2024-07-01 16:41 ` [PATCHv2 bpf-next 2/9] bpf: Add support for uprobe multi session attach Jiri Olsa
2024-07-02 21:30 ` Andrii Nakryiko
2024-07-01 16:41 ` [PATCHv2 bpf-next 3/9] bpf: Add support for uprobe multi session context Jiri Olsa
2024-07-02 21:31 ` Andrii Nakryiko
2024-07-01 16:41 ` [PATCHv2 bpf-next 4/9] libbpf: Add support for uprobe multi session attach Jiri Olsa
2024-07-02 21:34 ` Andrii Nakryiko
2024-07-03 17:14 ` Jiri Olsa
2024-07-01 16:41 ` [PATCHv2 bpf-next 5/9] libbpf: Add uprobe session attach type names to attach_type_name Jiri Olsa
2024-07-02 21:56 ` Andrii Nakryiko
2024-07-03 17:15 ` Jiri Olsa
2024-07-01 16:41 ` [PATCHv2 bpf-next 6/9] selftests/bpf: Add uprobe session test Jiri Olsa
2024-07-02 21:57 ` Andrii Nakryiko
2024-07-01 16:41 ` [PATCHv2 bpf-next 7/9] selftests/bpf: Add uprobe session cookie test Jiri Olsa
2024-07-02 21:58 ` Andrii Nakryiko
2024-07-01 16:41 ` [PATCHv2 bpf-next 8/9] selftests/bpf: Add uprobe session recursive test Jiri Olsa
2024-07-02 22:01 ` Andrii Nakryiko
2024-07-03 17:16 ` Jiri Olsa
2024-07-01 16:41 ` [PATCHv2 bpf-next 9/9] selftests/bpf: Add uprobe session consumers test Jiri Olsa
2024-07-02 22:10 ` Andrii Nakryiko
2024-07-03 17:22 ` Jiri Olsa
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ZoWGrGYdyaimB_zF@krava \
--to=olsajiri@gmail.com \
--cc=andrii.nakryiko@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=haoluo@google.com \
--cc=john.fastabend@gmail.com \
--cc=kafai@fb.com \
--cc=kpsingh@chromium.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=mhiramat@kernel.org \
--cc=oleg@redhat.com \
--cc=peterz@infradead.org \
--cc=rostedt@goodmis.org \
--cc=sdf@google.com \
--cc=songliubraving@fb.com \
--cc=yhs@fb.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.