From nobody Thu Apr 2 22:22:19 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 140422E7635 for ; Fri, 13 Feb 2026 23:39:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771025979; cv=none; b=Awh+kwDCVZERYkfwergkBmeYKwbEBCnHzMgjrA/3tlSrnsIyqKG24WGFUcYiu807FAB0+FL6zb7K9x66WarhfSkRQfQUi6d0zucxTjFBZbsk+2GegyFhkkuTclUYfaTEHQ8zITES6LNRk/GBjw1EKnrNndHLwWTPzegsQla8cgU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771025979; c=relaxed/simple; bh=bpdtDwGZfD5Ie5ut+puFOWPo98vX78Bg9UmeSUckqbw=; h=Date:From:To:Cc:Subject:Message-Id:Mime-Version:Content-Type; b=jOTQvsDo6WUOD98dPDETfIiVInrGr+hftH3KcEpDJ0EWwIoCUqoITBs+BnYhcvK8fCwHmwB0qpqkoMJfVbgzaikZvZl4ELV1pMdCxLgpirRch9VS23EFNuidZksue2T9ashL16Z+OltUuUwSJwn78bGB2VhWXRmouFpWbkI5BpI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=mYdOCrsH; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="mYdOCrsH" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 44DDEC116C6; Fri, 13 Feb 2026 23:39:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771025978; bh=bpdtDwGZfD5Ie5ut+puFOWPo98vX78Bg9UmeSUckqbw=; h=Date:From:To:Cc:Subject:From; b=mYdOCrsH8t7amvVmUx4MlYzkmYT7Tmo6M6w9uZckUaBJvndeS1lx4i95jVb+D438L FM156bVXb9sbuxxLrSzFtQ3T8y9N0wra51v55AAOkmQejjNSDMsr3xSLjRTeR/gjx+ szdtmxLQtWw5IJfeiQRKotVuTZxRQRwStn2WVdKN7yBjFE32gxRYeVvIxT3zc5EXaw XITAArbxU4OXer5t1xzbjG6up3rB2/BOgY1Kd3TVrKIS3QfG67YOCVhtYiM9TGJLGR /GJk3rPV5MBr3ETwrXdEuOB9qs8rbU9c5ffxXk7S7XhVlniC2afhaR+ptIJAUJkcU2 iolV/oRqlF/nw== Date: Sat, 14 Feb 2026 08:39:35 +0900 From: Masami Hiramatsu (Google) To: Linus Torvalds Cc: Masami Hiramatsu (Google) , hongao , sunliming , Steven Rostedt , Masami Hiramatsu , linux-kernel@vger.kernel.org Subject: [GIT PULL] Probes: Updates for v7.0 Message-Id: <20260214083935.43ea83c9119361f98cf701b1@kernel.org> X-Mailer: Sylpheed 3.8.0beta1 (GTK+ 2.24.33; x86_64-pc-linux-gnu) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Hi Linus, Probes for v7.0 - kprobes: Use a dedicated kernel thread to optimize the kprobes instead of using workqueue thread. Since the kprobe optimizer waits a long time for synchronize_rcu_task(), it can block other workers in the same queue if it uses a workqueue. - kprobe-events: Returns immediately if no new probe events are specified on the kernel command line at boot time. This shorten the kernel boot time. - kprobes: When a kprobe is fully removed from the kernel code, retry optimizing another kprobe which is blocked by that kprobe. Please pull the latest probes-v7.0 tree, which can be found at: git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace.git probes-v7.0 Tag SHA1: d5984c30c657c796837a3e7667b3086246a43a68 Head SHA1: 73c12f209462d1712c5f55f3021a1b65b2e084c3 Masami Hiramatsu (Google) (1): kprobes: Use dedicated kthread for kprobe optimizer hongao (1): kprobes: retry blocked optprobe in do_free_cleaned_kprobes sunliming (1): tracing: kprobe-event: Return directly when trace kprobes is empty ---- kernel/kprobes.c | 124 ++++++++++++++++++++++++++++++++++------= ---- kernel/trace/trace_kprobe.c | 4 ++ 2 files changed, 102 insertions(+), 26 deletions(-) --------------------------- diff --git a/kernel/kprobes.c b/kernel/kprobes.c index ab8f9fc1f0d1..e2cd01cf5968 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #include #include #include +#include =20 #include #include @@ -514,8 +516,18 @@ static LIST_HEAD(optimizing_list); static LIST_HEAD(unoptimizing_list); static LIST_HEAD(freeing_list); =20 -static void kprobe_optimizer(struct work_struct *work); -static DECLARE_DELAYED_WORK(optimizing_work, kprobe_optimizer); +static void optimize_kprobe(struct kprobe *p); +static struct task_struct *kprobe_optimizer_task; +static wait_queue_head_t kprobe_optimizer_wait; +static atomic_t optimizer_state; +enum { + OPTIMIZER_ST_IDLE =3D 0, + OPTIMIZER_ST_KICKED =3D 1, + OPTIMIZER_ST_FLUSHING =3D 2, +}; + +static DECLARE_COMPLETION(optimizer_completion); + #define OPTIMIZE_DELAY 5 =20 /* @@ -593,18 +605,25 @@ static void do_free_cleaned_kprobes(void) */ continue; } + + /* + * The aggregator was holding back another probe while it sat on the + * unoptimizing/freeing lists. Now that the aggregator has been fully + * reverted we can safely retry the optimization of that sibling. + */ + + struct kprobe *_p =3D get_optimized_kprobe(op->kp.addr); + if (unlikely(_p)) + optimize_kprobe(_p); + free_aggr_kprobe(&op->kp); } } =20 -/* Start optimizer after OPTIMIZE_DELAY passed */ -static void kick_kprobe_optimizer(void) -{ - schedule_delayed_work(&optimizing_work, OPTIMIZE_DELAY); -} +static void kick_kprobe_optimizer(void); =20 /* Kprobe jump optimizer */ -static void kprobe_optimizer(struct work_struct *work) +static void kprobe_optimizer(void) { guard(mutex)(&kprobe_mutex); =20 @@ -635,9 +654,53 @@ static void kprobe_optimizer(struct work_struct *work) do_free_cleaned_kprobes(); } =20 - /* Step 5: Kick optimizer again if needed */ + /* Step 5: Kick optimizer again if needed. But if there is a flush reques= ted, */ + if (completion_done(&optimizer_completion)) + complete(&optimizer_completion); + if (!list_empty(&optimizing_list) || !list_empty(&unoptimizing_list)) - kick_kprobe_optimizer(); + kick_kprobe_optimizer(); /*normal kick*/ +} + +static int kprobe_optimizer_thread(void *data) +{ + while (!kthread_should_stop()) { + /* To avoid hung_task, wait in interruptible state. */ + wait_event_interruptible(kprobe_optimizer_wait, + atomic_read(&optimizer_state) !=3D OPTIMIZER_ST_IDLE || + kthread_should_stop()); + + if (kthread_should_stop()) + break; + + /* + * If it was a normal kick, wait for OPTIMIZE_DELAY. + * This wait can be interrupted by a flush request. + */ + if (atomic_read(&optimizer_state) =3D=3D 1) + wait_event_interruptible_timeout( + kprobe_optimizer_wait, + atomic_read(&optimizer_state) =3D=3D OPTIMIZER_ST_FLUSHING || + kthread_should_stop(), + OPTIMIZE_DELAY); + + if (kthread_should_stop()) + break; + + atomic_set(&optimizer_state, OPTIMIZER_ST_IDLE); + + kprobe_optimizer(); + } + return 0; +} + +/* Start optimizer after OPTIMIZE_DELAY passed */ +static void kick_kprobe_optimizer(void) +{ + lockdep_assert_held(&kprobe_mutex); + if (atomic_cmpxchg(&optimizer_state, + OPTIMIZER_ST_IDLE, OPTIMIZER_ST_KICKED) =3D=3D OPTIMIZER_ST_IDLE) + wake_up(&kprobe_optimizer_wait); } =20 static void wait_for_kprobe_optimizer_locked(void) @@ -645,13 +708,17 @@ static void wait_for_kprobe_optimizer_locked(void) lockdep_assert_held(&kprobe_mutex); =20 while (!list_empty(&optimizing_list) || !list_empty(&unoptimizing_list)) { - mutex_unlock(&kprobe_mutex); - - /* This will also make 'optimizing_work' execute immmediately */ - flush_delayed_work(&optimizing_work); - /* 'optimizing_work' might not have been queued yet, relax */ - cpu_relax(); + init_completion(&optimizer_completion); + /* + * Set state to OPTIMIZER_ST_FLUSHING and wake up the thread if it's + * idle. If it's already kicked, it will see the state change. + */ + if (atomic_xchg_acquire(&optimizer_state, + OPTIMIZER_ST_FLUSHING) !=3D OPTIMIZER_ST_FLUSHING) + wake_up(&kprobe_optimizer_wait); =20 + mutex_unlock(&kprobe_mutex); + wait_for_completion(&optimizer_completion); mutex_lock(&kprobe_mutex); } } @@ -1002,16 +1069,23 @@ static void __disarm_kprobe(struct kprobe *p, bool = reopt) if (unlikely(_p) && reopt) optimize_kprobe(_p); } - /* - * TODO: Since unoptimization and real disarming will be done by - * the worker thread, we can not check whether another probe are - * unoptimized because of this probe here. It should be re-optimized - * by the worker thread. - */ } =20 +static void __init init_optprobe(void) +{ +#ifdef __ARCH_WANT_KPROBES_INSN_SLOT + /* Init 'kprobe_optinsn_slots' for allocation */ + kprobe_optinsn_slots.insn_size =3D MAX_OPTINSN_SIZE; +#endif + + init_waitqueue_head(&kprobe_optimizer_wait); + atomic_set(&optimizer_state, OPTIMIZER_ST_IDLE); + kprobe_optimizer_task =3D kthread_run(kprobe_optimizer_thread, NULL, + "kprobe-optimizer"); +} #else /* !CONFIG_OPTPROBES */ =20 +#define init_optprobe() do {} while (0) #define optimize_kprobe(p) do {} while (0) #define unoptimize_kprobe(p, f) do {} while (0) #define kill_optimized_kprobe(p) do {} while (0) @@ -2694,10 +2768,8 @@ static int __init init_kprobes(void) /* By default, kprobes are armed */ kprobes_all_disarmed =3D false; =20 -#if defined(CONFIG_OPTPROBES) && defined(__ARCH_WANT_KPROBES_INSN_SLOT) - /* Init 'kprobe_optinsn_slots' for allocation */ - kprobe_optinsn_slots.insn_size =3D MAX_OPTINSN_SIZE; -#endif + /* Initialize the optimization infrastructure */ + init_optprobe(); =20 err =3D arch_init_kprobes(); if (!err) diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 9953506370a5..95f2c42603d5 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -82,6 +82,7 @@ static struct trace_kprobe *to_trace_kprobe(struct dyn_ev= ent *ev) #define for_each_trace_kprobe(pos, dpos) \ for_each_dyn_event(dpos) \ if (is_trace_kprobe(dpos) && (pos =3D to_trace_kprobe(dpos))) +#define trace_kprobe_list_empty() list_empty(&dyn_event_list) =20 static nokprobe_inline bool trace_kprobe_is_return(struct trace_kprobe *tk) { @@ -1982,6 +1983,9 @@ static __init void enable_boot_kprobe_events(void) struct trace_kprobe *tk; struct dyn_event *pos; =20 + if (trace_kprobe_list_empty()) + return; + guard(mutex)(&event_mutex); for_each_trace_kprobe(tk, pos) { list_for_each_entry(file, &tr->events, list) --=20 Masami Hiramatsu (Google)