From: Andrii Nakryiko <andrii@kernel.org>
To: linux-trace-kernel@vger.kernel.org, peterz@infradead.org,
oleg@redhat.com
Cc: rostedt@goodmis.org, mhiramat@kernel.org, bpf@vger.kernel.org,
linux-kernel@vger.kernel.org, jolsa@kernel.org,
paulmck@kernel.org, Andrii Nakryiko <andrii@kernel.org>
Subject: [PATCH 3/3] uprobes: implement SRCU-protected lifetime for single-stepped uprobe
Date: Mon, 9 Sep 2024 15:49:03 -0700 [thread overview]
Message-ID: <20240909224903.3498207-4-andrii@kernel.org> (raw)
In-Reply-To: <20240909224903.3498207-1-andrii@kernel.org>
Similarly to how we SRCU-protect uprobe instance (and avoid refcounting
it unnecessarily) when waiting for return probe hit, use hprobe approach
to do the same with single-stepped uprobe. Same hprobe_* primitives are
used. We also reuse ri_timer() callback to expire both pending
single-step uprobe and return instances.
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
include/linux/uprobes.h | 4 +--
kernel/events/uprobes.c | 55 ++++++++++++++++++++++++-----------------
2 files changed, 34 insertions(+), 25 deletions(-)
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index 1b194c51d4d3..31cf7306cdf6 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -114,7 +114,7 @@ struct uprobe_task {
};
};
- struct uprobe *active_uprobe;
+ struct hprobe active_hprobe;
struct timer_list ri_timer;
unsigned long xol_vaddr;
@@ -122,7 +122,7 @@ struct uprobe_task {
struct return_instance *return_instances;
unsigned int depth;
-};
+} ____cacheline_aligned;
struct return_instance {
unsigned long func;
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index b047e68499d5..d9ab5e0dd9dd 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1894,11 +1894,16 @@ unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs)
return instruction_pointer(regs) - UPROBE_SWBP_INSN_SIZE;
}
+static bool utask_has_pending_sstep_uprobe(struct uprobe_task *utask)
+{
+ return utask->active_hprobe.stable != NULL;
+}
+
unsigned long uprobe_get_trap_addr(struct pt_regs *regs)
{
struct uprobe_task *utask = current->utask;
- if (unlikely(utask && utask->active_uprobe))
+ if (unlikely(utask && utask_has_pending_sstep_uprobe(utask)))
return utask->vaddr;
return instruction_pointer(regs);
@@ -1927,14 +1932,17 @@ void uprobe_free_utask(struct task_struct *t)
{
struct uprobe_task *utask = t->utask;
struct return_instance *ri;
+ struct uprobe *uprobe;
+ bool under_rcu;
if (!utask)
return;
timer_delete_sync(&utask->ri_timer);
- if (utask->active_uprobe)
- put_uprobe(utask->active_uprobe);
+ /* clean up pending single-stepped uprobe */
+ uprobe = hprobe_consume(&utask->active_hprobe, &under_rcu);
+ hprobe_finalize(&utask->active_hprobe, uprobe, under_rcu);
ri = utask->return_instances;
while (ri)
@@ -1958,6 +1966,8 @@ static void ri_timer(struct timer_list *timer)
/* RCU protects return_instance from freeing. */
guard(rcu)();
+ hprobe_expire(&utask->active_hprobe);
+
for_each_ret_instance_rcu(ri, utask->return_instances) {
hprobe_expire(&ri->hprobe);
}
@@ -2190,20 +2200,15 @@ pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
{
struct uprobe_task *utask;
unsigned long xol_vaddr;
- int err;
+ int err, srcu_idx;
utask = get_utask();
if (!utask)
return -ENOMEM;
- if (!try_get_uprobe(uprobe))
- return -EINVAL;
-
xol_vaddr = xol_get_insn_slot(uprobe);
- if (!xol_vaddr) {
- err = -ENOMEM;
- goto err_out;
- }
+ if (!xol_vaddr)
+ return -ENOMEM;
utask->xol_vaddr = xol_vaddr;
utask->vaddr = bp_vaddr;
@@ -2211,15 +2216,17 @@ pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
err = arch_uprobe_pre_xol(&uprobe->arch, regs);
if (unlikely(err)) {
xol_free_insn_slot(current);
- goto err_out;
+ return err;
}
- utask->active_uprobe = uprobe;
+ srcu_idx = __srcu_read_lock(&uretprobes_srcu);
+
+ hprobe_init_leased(&utask->active_hprobe, uprobe, srcu_idx);
utask->state = UTASK_SSTEP;
+
+ mod_timer(&utask->ri_timer, jiffies + RI_TIMER_PERIOD);
+
return 0;
-err_out:
- put_uprobe(uprobe);
- return err;
}
/*
@@ -2236,7 +2243,7 @@ bool uprobe_deny_signal(void)
struct task_struct *t = current;
struct uprobe_task *utask = t->utask;
- if (likely(!utask || !utask->active_uprobe))
+ if (likely(!utask || !utask_has_pending_sstep_uprobe(utask)))
return false;
WARN_ON_ONCE(utask->state != UTASK_SSTEP);
@@ -2553,8 +2560,10 @@ static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs)
{
struct uprobe *uprobe;
int err = 0;
+ bool under_rcu;
+
+ uprobe = hprobe_consume(&utask->active_hprobe, &under_rcu);
- uprobe = utask->active_uprobe;
if (utask->state == UTASK_SSTEP_ACK)
err = arch_uprobe_post_xol(&uprobe->arch, regs);
else if (utask->state == UTASK_SSTEP_TRAPPED)
@@ -2562,8 +2571,8 @@ static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs)
else
WARN_ON_ONCE(1);
- put_uprobe(uprobe);
- utask->active_uprobe = NULL;
+ hprobe_finalize(&utask->active_hprobe, uprobe, under_rcu);
+
utask->state = UTASK_RUNNING;
xol_free_insn_slot(current);
@@ -2580,7 +2589,7 @@ static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs)
/*
* On breakpoint hit, breakpoint notifier sets the TIF_UPROBE flag and
* allows the thread to return from interrupt. After that handle_swbp()
- * sets utask->active_uprobe.
+ * sets utask->active_hprobe.
*
* On singlestep exception, singlestep notifier sets the TIF_UPROBE flag
* and allows the thread to return from interrupt.
@@ -2595,7 +2604,7 @@ void uprobe_notify_resume(struct pt_regs *regs)
clear_thread_flag(TIF_UPROBE);
utask = current->utask;
- if (utask && utask->active_uprobe)
+ if (utask && utask_has_pending_sstep_uprobe(utask))
handle_singlestep(utask, regs);
else
handle_swbp(regs);
@@ -2626,7 +2635,7 @@ int uprobe_post_sstep_notifier(struct pt_regs *regs)
{
struct uprobe_task *utask = current->utask;
- if (!current->mm || !utask || !utask->active_uprobe)
+ if (!current->mm || !utask || !utask_has_pending_sstep_uprobe(utask))
/* task is currently not uprobed */
return 0;
--
2.43.5
next prev parent reply other threads:[~2024-09-09 22:49 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-09-09 22:49 [PATCH 0/3] SRCU-protected uretprobes hot path Andrii Nakryiko
2024-09-09 22:49 ` [PATCH 1/3] uprobes: allow put_uprobe() from non-sleepable softirq context Andrii Nakryiko
2024-09-10 2:51 ` Alexei Starovoitov
2024-09-10 5:13 ` Andrii Nakryiko
2024-09-10 15:56 ` Alexei Starovoitov
2024-09-10 17:46 ` Andrii Nakryiko
2024-09-15 14:49 ` Oleg Nesterov
2024-09-17 8:19 ` Andrii Nakryiko
2024-10-04 20:18 ` Andrii Nakryiko
2024-09-09 22:49 ` [PATCH 2/3] uprobes: SRCU-protect uretprobe lifetime (with timeout) Andrii Nakryiko
2024-09-09 22:49 ` Andrii Nakryiko [this message]
2024-09-15 14:51 ` [PATCH 3/3] uprobes: implement SRCU-protected lifetime for single-stepped uprobe Oleg Nesterov
2024-09-17 8:20 ` Andrii Nakryiko
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=20240909224903.3498207-4-andrii@kernel.org \
--to=andrii@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=mhiramat@kernel.org \
--cc=oleg@redhat.com \
--cc=paulmck@kernel.org \
--cc=peterz@infradead.org \
--cc=rostedt@goodmis.org \
/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.