[tip: perf/core] uprobes: Remove the spinlock within handle_singlestep()

From: tip-bot2 for Liao Chang
Date: Mon Feb 03 2025 - 07:48:33 EST


The following commit has been merged into the perf/core branch of tip:

Commit-ID: a66396c911bd662d503de3ec8f0a140b1081bde7
Gitweb: https://git.kernel.org/tip/a66396c911bd662d503de3ec8f0a140b1081bde7
Author: Liao Chang <liaochang1@xxxxxxxxxx>
AuthorDate: Fri, 24 Jan 2025 09:38:26
Committer: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
CommitterDate: Mon, 03 Feb 2025 11:46:06 +01:00

uprobes: Remove the spinlock within handle_singlestep()

This patch introduces a flag to track TIF_SIGPENDING is suppress
temporarily during the uprobe single-step. Upon uprobe singlestep is
handled and the flag is confirmed, it could resume the TIF_SIGPENDING
directly without acquiring the siglock in most case, then reducing
contention and improving overall performance.

I've use the script developed by Andrii in [1] to run benchmark. The CPU
used was Kunpeng916 (Hi1616), 4 NUMA nodes, 64 cores@2.4GHz running the
kernel on next tree + the optimization for get_xol_insn_slot() [2].

before-opt
Signed-off-by: Peter Zijlstra (Intel) <peterz@xxxxxxxxxxxxx>
Link: https://lkml.kernel.org/r/20250124093826.2123675-3-liaochang1@xxxxxxxxxx
---
include/linux/uprobes.h | 1 +
kernel/events/uprobes.c | 8 +++++---
2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index b1df7d7..a40efdd 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -143,6 +143,7 @@ struct uprobe_task {

struct uprobe *active_uprobe;
unsigned long xol_vaddr;
+ bool signal_denied;

struct arch_uprobe *auprobe;
};
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 33bd608..870f697 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -2302,6 +2302,7 @@ bool uprobe_deny_signal(void)
WARN_ON_ONCE(utask->state != UTASK_SSTEP);

if (task_sigpending(t)) {
+ utask->signal_denied = true;
clear_tsk_thread_flag(t, TIF_SIGPENDING);

if (__fatal_signal_pending(t) || arch_uprobe_xol_was_trapped(t)) {
@@ -2735,9 +2736,10 @@ static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs)
utask->state = UTASK_RUNNING;
xol_free_insn_slot(utask);

- spin_lock_irq(&current->sighand->siglock);
- recalc_sigpending(); /* see uprobe_deny_signal() */
- spin_unlock_irq(&current->sighand->siglock);
+ if (utask->signal_denied) {
+ set_thread_flag(TIF_SIGPENDING);
+ utask->signal_denied = false;
+ }

if (unlikely(err)) {
uprobe_warn(current, "execute the probed insn, sending SIGILL.");