* Re: [PATCH 3/3] rv/reactors: add KUnit tests for reactor_panic
From: Wen Yang @ 2026-07-09 16:46 UTC (permalink / raw)
To: Gabriele Monaco; +Cc: Nam Cao, linux-trace-kernel, linux-kernel
In-Reply-To: <c8c7568dd9293b0a718584c1aa193692c128d77d.camel@redhat.com>
On 7/6/26 23:00, Gabriele Monaco wrote:
> On Tue, 2026-06-16 at 00:44 +0800, wen.yang@linux.dev wrote:
>> +/*
>> + * Test 2: panic notifier chain is reachable.
>> + *
>> + * vpanic() calls atomic_notifier_call_chain(&panic_notifier_list, ...).
>> + * Drive the chain directly to verify panic notifiers receive the
>> notification —
>> + * the observable side-effect of reactor_panic without halting the system.
>> + */
>> +static void test_panic_notifier_called(struct kunit *test)
>> +{
>> + atomic_notifier_chain_register(&panic_notifier_list, &mock_panic_nb);
>> + atomic_notifier_call_chain(&panic_notifier_list, 0,
>> + "panic violation message");
>> + atomic_notifier_chain_unregister(&panic_notifier_list,
>> &mock_panic_nb);
>
> I just realised this isn't even testing the reactor, it's testing the
> notifier_chain thing, I don't think we really need this here or am I missing
> something?
>
Thanks,
Since rv_panic_reaction calls vpanic() which is __noreturn, direct
testing is not feasible in KUnit.
A preparatory v2 drops reactor_printk_kunit.c and reactor_panic_kunit.c
entirely, replacing them with a single rv_reactors_kunit.c containing
two suites:
rv_reactor_registration: register/unregister lifecycle, duplicate
rejection (-EINVAL), name-too-long rejection, and safe unregister
of a never-registered reactor.
rv_react_dispatch: null-callback guard, callback invocation check,
and the mdelay lockdep stress test.
v2 also adds EXPORT_SYMBOL_GPL for rv_react(), rv_register_reactor(),
and rv_unregister_reactor(), and the Kconfig entry is tristate.
Additional fix: rv_unregister_reactor()
Testing exposed a real bug: rv_unregister_reactor() called list_del()
unconditionally. On a never-registered reactor the list_head is
zero-initialised, so list_del() dereferences NULL->prev and crashes.
v2 adds a patch that iterates rv_reactors_list first and only calls
list_del() when the reactor is found. test_unregister_nonexistent
documents and guards this behaviour.
--
Best wishes,
Wen
^ permalink raw reply
* Re: [PATCH 2/3] rv/reactors: add KUnit tests for reactor_printk
From: Wen Yang @ 2026-07-09 16:53 UTC (permalink / raw)
To: Gabriele Monaco; +Cc: Nam Cao, linux-trace-kernel, linux-kernel
In-Reply-To: <97dc3ac923f1910a6103665fa5d69aca28430b6a.camel@redhat.com>
On 7/6/26 22:41, Gabriele Monaco wrote:
>
>
> On Tue, 2026-06-16 at 00:44 +0800, wen.yang@linux.dev wrote:
>> From: Wen Yang <wen.yang@linux.dev>
>>
>> Add KUnit tests for the printk reactor covering:
>> - Reactor registration and unregistration lifecycle
>> - React callback invocation via rv_react()
>> - Double registration rejection
>> - Multiple register/unregister cycles
>>
>> The mock callback calls vprintk_deferred() — the same path as the real
>> reactor — then busy-waits to simulate I/O back-pressure, exercising the
>> LD_WAIT_FREE constraint of rv_react() under load.
>>
>> Signed-off-by: Wen Yang <wen.yang@linux.dev>
>> ---
>> kernel/trace/rv/Kconfig | 10 ++
>> kernel/trace/rv/Makefile | 1 +
>> kernel/trace/rv/reactor_printk_kunit.c | 123 +++++++++++++++++++++++++
>> 3 files changed, 134 insertions(+)
>> create mode 100644 kernel/trace/rv/reactor_printk_kunit.c
>>
>> diff --git a/kernel/trace/rv/Kconfig b/kernel/trace/rv/Kconfig
>> index 3884b14df375..ff47895c897f 100644
>> --- a/kernel/trace/rv/Kconfig
>> +++ b/kernel/trace/rv/Kconfig
>> @@ -104,6 +104,16 @@ config RV_REACT_PRINTK
>> Enables the printk reactor. The printk reactor emits a printk()
>> message if an exception is found.
>>
>> +config RV_REACT_PRINTK_KUNIT
>> + bool "KUnit tests for reactor_printk" if !KUNIT_ALL_TESTS
>> + depends on RV_REACT_PRINTK && KUNIT
>> + default KUNIT_ALL_TESTS
>> + help
>> + This builds KUnit tests for the printk reactor. These are only
>> + for development and testing, not for regular kernel use cases.
>> +
>> + If unsure, say N.
>> +
>> config RV_REACT_PANIC
>> bool "Panic reactor"
>> depends on RV_REACTORS
>> diff --git a/kernel/trace/rv/Makefile b/kernel/trace/rv/Makefile
>> index 94498da35b37..ef0a2dcb927c 100644
>> --- a/kernel/trace/rv/Makefile
>> +++ b/kernel/trace/rv/Makefile
>> @@ -23,4 +23,5 @@ obj-$(CONFIG_RV_MON_NOMISS) += monitors/nomiss/nomiss.o
>> # Add new monitors here
>> obj-$(CONFIG_RV_REACTORS) += rv_reactors.o
>> obj-$(CONFIG_RV_REACT_PRINTK) += reactor_printk.o
>> +obj-$(CONFIG_RV_REACT_PRINTK_KUNIT) += reactor_printk_kunit.o
>> obj-$(CONFIG_RV_REACT_PANIC) += reactor_panic.o
>> diff --git a/kernel/trace/rv/reactor_printk_kunit.c
>> b/kernel/trace/rv/reactor_printk_kunit.c
>> new file mode 100644
>> index 000000000000..933aa5602226
>> --- /dev/null
>> +++ b/kernel/trace/rv/reactor_printk_kunit.c
>> @@ -0,0 +1,123 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * KUnit tests for reactor_printk
>> + *
>> + */
>> +
>> +#include <kunit/test.h>
>> +#include <linux/rv.h>
>> +#include <linux/printk.h>
>> +#include <linux/sched/clock.h>
>> +#include <linux/processor.h>
>> +
>> +/*
>> + * Simulated execution time for mock_printk_react (sched_clock units,
>> + * nanoseconds). Models the time a real printk reactor callback may consume
>> + * under I/O pressure, exercising the LD_WAIT_FREE constraint of rv_react().
>> + */
>> +#define MOCK_REACT_DURATION_NS 5000000ULL
>> +
>> +/*
>> + * Mock react callback mirroring rv_printk_reaction().
>> + *
>> + * Calls vprintk_deferred() — the same path as the real reactor — then holds
>> + * the CPU for MOCK_REACT_DURATION_NS via a sched_clock() timed busy-loop,
>> + * simulating a callback that is slow due to I/O back-pressure.
>> + * sched_clock() is notrace and lock-free; no sleep or lock acquisition is
>> + * performed, satisfying the LD_WAIT_FREE constraint of rv_react().
>> + */
>> +__printf(1, 0) static void mock_printk_react(const char *msg, va_list args)
>> +{
>> + u64 start = sched_clock();
>> +
>
> I'm fine testing something that looks like the printk reactor rather than the
> real thing, but here sched_clock() is playing with preemption and could trigger
> one, this isn't accurate.
>
> I'm not sure all implementations are free from this problem, but why don't you
> just use mdelay() here?
>
Thanks,
v2 uses mdelay(5) for the busy-wait. The goal is simply to hold the CPU
inside rv_react()'s lockdep context; so mdelay() is a pure calibrated
spin with no scheduler interaction, making the test's causal chain
clearer than sched_clock().
The remaining comments are also very important, and we will address them
in v2.
--
Best wishes,
Wen
^ permalink raw reply
* Re: [PATCH v3 16/17] selftests/verification: Rearrange the wwnr_printk test
From: Wen Yang @ 2026-07-09 17:08 UTC (permalink / raw)
To: Gabriele Monaco, linux-trace-kernel, linux-kernel, Steven Rostedt,
Shuah Khan, linux-kselftest
Cc: Nam Cao, Thomas Weissschuh, Tomas Glozar, John Kacur
In-Reply-To: <20260625121440.116317-17-gmonaco@redhat.com>
On 6/25/26 20:14, Gabriele Monaco wrote:
> The wwnr_printk test expects no reactions in some situations, after
> fixing the bash assertion, the test is failing because expecting no
> reaction after a previous step had reactions is flaky without making
> sure all buffers are flushed.
>
> Simplify the test and run the steps expecting no reaction before the one
> expecting reactions. Also simplify the load function to stop loads as
> soon as a reaction occurs, this limits the number of lines to flush and
> makes tests overall more stable.
>
> Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
> ---
> .../verification/test.d/rv_wwnr_printk.tc | 17 ++++++++++-------
> 1 file changed, 10 insertions(+), 7 deletions(-)
>
> diff --git a/tools/testing/selftests/verification/test.d/rv_wwnr_printk.tc b/tools/testing/selftests/verification/test.d/rv_wwnr_printk.tc
> index 96de95edb5..a23d22f6ec 100644
> --- a/tools/testing/selftests/verification/test.d/rv_wwnr_printk.tc
> +++ b/tools/testing/selftests/verification/test.d/rv_wwnr_printk.tc
> @@ -4,27 +4,30 @@
> # requires: available_reactors wwnr:monitor printk:reactor stress-ng:program
>
> load() { # returns true if there was a reaction
> - local lines_before num
> + local lines_before num load_pid ret
> num=$((($(nproc) + 1) / 2))
> lines_before=$(dmesg | wc -l)
> - stress-ng --cpu-sched "$num" --timer "$num" -t 5 -q
> - dmesg | tail -n $((lines_before + 1)) | grep -q "rv: monitor wwnr does not allow event"
> + stress-ng --cpu-sched "$num" --timer "$num" -t 5 -q &
> + load_pid=$!
> + timeout 5 dmesg -w | tail -n +$((lines_before + 1)) | grep -m 1 -q "rv: monitor wwnr does not allow event"
Minor nit: could we add a small delay (e.g., sleep 0.1) before dmesg?
Reviewed-by: Wen Yang <wen.yang@linux.dev>
> + ret=$?
> + kill "$load_pid"
> + wait "$load_pid"
> + return $ret
> }
>
> echo 1 > monitors/wwnr/enable
> echo printk > monitors/wwnr/reactors
>
> -load
> -
> echo 0 > monitoring_on
> ! load || false
> echo 1 > monitoring_on
>
> -load
> -
> echo 0 > reacting_on
> ! load || false
> echo 1 > reacting_on
>
> +load
> +
> echo nop > monitors/wwnr/reactors
> echo 0 > monitors/wwnr/enable
^ permalink raw reply
* Re: [PATCH v3 15/17] selftests/verification: Fix wrong errexit assumption
From: Wen Yang @ 2026-07-09 17:10 UTC (permalink / raw)
To: Gabriele Monaco, linux-trace-kernel, linux-kernel, Steven Rostedt,
Shuah Khan, linux-kselftest
Cc: Nam Cao, Thomas Weissschuh, Tomas Glozar, John Kacur
In-Reply-To: <20260625121440.116317-16-gmonaco@redhat.com>
On 6/25/26 20:14, Gabriele Monaco wrote:
> RV selftest rely on bash errexit (set -e) to terminate with error, when
> a step is expected to return false, the following syntax is used:
>
> ! cmd
>
> This however prevents the test from exiting when cmd is false (desired)
> but doesn't exit if cmd is true, since commands prefixed with ! are
> explicitly excluded from errexit.
>
> Use the syntax
>
> ! cmd || false
>
> Which ends up checking the exit value of ! cmd and supplies a false
> command for errexit to evaluate.
>
> Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
Reviewed-by: Wen Yang <wen.yang@linux.dev>
--
Best wishes,
Wen
^ permalink raw reply
* Re: [PATCH v3 06/17] verification/rvgen: Add selftests
From: Wen Yang @ 2026-07-09 17:13 UTC (permalink / raw)
To: Gabriele Monaco, linux-trace-kernel, linux-kernel, Steven Rostedt
Cc: Nam Cao, Thomas Weissschuh, Tomas Glozar, John Kacur
In-Reply-To: <20260625121440.116317-7-gmonaco@redhat.com>
On 6/25/26 20:14, Gabriele Monaco wrote:
> The rvgen code generator needs validation to ensure it produces correct
> monitor implementations from input specifications.
>
> Add selftests with golden reference outputs covering all monitor classes
> (DA, HA, LTL) and types (global, per_cpu, per_task, per_obj), including
> optional features like descriptions and parent monitors. Container
> generation and error handling (missing files, invalid specifications,
> missing arguments) are also validated against expected output.
>
> Acked-by: Nam Cao <namcao@linutronix.de>
> Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
Reviewed-by: Wen Yang <wen.yang@linux.dev>
--
Best wishes,
Wen
^ permalink raw reply
* Re: [PATCH 1/3] rv/reactors: fix lockdep "Invalid wait context" in rv_react()
From: Nam Cao @ 2026-07-09 18:07 UTC (permalink / raw)
To: Wen Yang, Thomas Weißschuh, Gabriele Monaco
Cc: linux-trace-kernel, linux-kernel
In-Reply-To: <397cea12-e0ba-4cf5-a411-26f44bc17d01@linux.dev>
Wen Yang <wen.yang@linux.dev> writes:
> How about a context-sensitive approach, eg:
>
> NMI/hardirq (interrupts masked, scheduler cannot run):
> Keep LD_WAIT_FREE. The false positive cannot arise in this path,
> and the original constraint against raw spinlocks is preserved where
> it is meaningful.
>
> task/softirq/PREEMPT_RT irq thread (preemptible):
> Raise to LD_WAIT_SPIN. Raw spinlocks become permitted — as Gabriele
> note, this is a necessary consequence of any fix in this path.
I don't get the point. Unless the lock's type is also context-sensitive,
the reactors still cannot use raw spin locks.
Nam
^ permalink raw reply
* Re: [PATCH v3 04/11] arm64/mm: Add set_memory_device() and set_memory_normal()
From: Thierry Reding @ 2026-07-09 19:58 UTC (permalink / raw)
To: Will Deacon
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Hunter,
David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Sowjanya Komatineni, Luca Ceresoli,
Mikko Perttunen, Yury Norov, Rasmus Villemoes, Russell King,
Alexander Gordeev, Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, Andrew Morton,
David Hildenbrand, Lorenzo Stoakes, Liam R. Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Marek Szyprowski, Robin Murphy, Sumit Semwal, Benjamin Gaignard,
Brian Starkey, John Stultz, T.J. Mercier, Christian König,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Catalin Marinas, Thierry Reding, devicetree, linux-tegra,
linux-kernel, dri-devel, linux-media, linux-arm-kernel,
linux-s390, linux-mm, iommu, linaro-mm-sig, linux-trace-kernel,
Thierry Reding, Chun Ng
In-Reply-To: <akzikTrmhMsvkNVY@willie-the-truck>
[-- Attachment #1: Type: text/plain, Size: 5485 bytes --]
On Tue, Jul 07, 2026 at 12:27:13PM +0100, Will Deacon wrote:
> On Mon, Jul 06, 2026 at 03:49:24PM +0200, Thierry Reding wrote:
> > On Fri, Jul 03, 2026 at 06:13:31PM +0100, Will Deacon wrote:
> > > On Thu, Jul 02, 2026 at 06:41:23PM +0200, Thierry Reding wrote:
> > > > On Thu, Jul 02, 2026 at 03:46:44PM +0200, Thierry Reding wrote:
> > > > > On Thu, Jul 02, 2026 at 10:18:47AM +0100, Will Deacon wrote:
> > > > > > On Wed, Jul 01, 2026 at 06:08:15PM +0200, Thierry Reding wrote:
> > > > > > > From: Chun Ng <chunn@nvidia.com>
> > > > > > >
> > > > > > > Add helpers to swap PROT_NORMAL and PROT_DEVICE_nGnRnE protection bits
> > > > > > > on a kernel-linear-map range.
> > > > > >
> > > > > > That sounds like a really terrible idea. Why is this necessary and how
> > > > > > does it interact with things like load_unaligned_zeropad()?
> > > > >
> > > > > This is necessary because once the memory controller has walled off the
> > > > > new memory region the CPU must not access it under any circumstances or
> > > > > it'll cause the CPU to lock up (I think technically it'll hit an SError
> > > > > but in practice that just means it'll freeze, as far as I can tell).
> > > > >
> > > > > Probably doesn't interact well at all with load_unaligned_zeropad().
> > > > >
> > > > > > I think you should unmap the memory from the linear map and memremap()
> > > > > > it instead.
> > > > >
> > > > > Given that the memory can never be accessed by the CPU after the memory
> > > > > controller locks it down, I don't think we'll even need memremap(). The
> > > > > only thing we really need is the sg_table we hand out via the DMA BUFs
> > > > > so that they can be used by device drivers to program their DMA engines
> > > > > internally.
> > > > >
> > > > > Looking through some of the architecture code around this, shouldn't we
> > > > > simply be using set_memory_encrypted() and set_memory_decrypted() for
> > > > > this? While they might've been created for slightly other use-cases,
> > > > > they seem to be doing exactly what we want (i.e. remove the page range
> > > > > from the linear mapping and flushing it, or restoring the valid bit and
> > > > > standard permissions, respectively).
> > > >
> > > > Ah... I guess we can't do it because we're not in a realm world and so
> > > > the early checks in __set_memory_enc_dec() would return early and turn
> > > > it into a no-op.
> > > >
> > > > How about if I extract a common helper and provide set_memory_p() and
> > > > set_memory_np() in terms of those. Those are available on x86 and
> > > > PowerPC as well, so fairly standard. I suppose at that point we're
> > > > closer to set_memory_valid().
> > >
> > > Why not just call set_direct_map_invalid_noflush() +
> > > flush_tlb_kernel_range() for each page? We already have APIs for this.
> >
> > Having a "standard" helper with a fixed and documented purposed seemed
> > like a preferable approach for this particular case. We also may want to
> > make the driver that uses this buildable as a module, in which case we'd
> > need to export these rather low-level APIs. And then there's also the
> > fact that we typically call this on a rather large region of memory
> > (usually something like 512 MiB), so doing it page-by-page is rather
> > suboptimal.
> >
> > > The big challenge I see with any linear map manipulation, however, is
> > > that it will rely on can_set_direct_map() which likely means you need to
> > > give up some performance and/or security to make this work. Does memory
> > > become inaccesible dynamically at runtime? If not, the best bet would
> > > be to describe it as a carveout in the DT and mark it as "no-map" so
> > > we avoid mapping it in the first place.
> >
> > VPR exists in two modes: static and resizable. For static VPR we do
> > exactly that: describe it as carveout in DT with no-map and deal with it
> > accordingly in the driver. Resizable VPR is for device that have small
> > amounts of RAM. Content-protected video playback will in the worst case
> > consume around 1.8 GiB of RAM, so we want to be able to reuse for other
> > purposes when VPR is unused on those devices. In that case, the memory
> > is also described as a reserved-memory region in DT, but it is marked as
> > reusable so that it can be managed by CMA.
> >
> > The resize operation is fairly slow to begin with because we need to
> > stall the GPU and put it into reset before the operation, then take it
> > out of reset and resume it afterwards.
> >
> > What kind of performance impact do you expect?
>
> You'll need to measure it, but we've seen reports of double-digit
> percentage regressions in performance and power. As I said, the problem
> is that you need to split the linear map to 4k page at runtime to unmap
> the dynamic carveout, but that isn't something that can be done on most
> CPUs. Therefore you end up having to use page-granular mappings for the
> entire thing, similarly to how 'rodata_full' drives can_set_direct_map()
> and the perf/power hit affects everything.
The VPR has fairly large alignment restrictions (1 MiB) and we do unmap
in fairly large chunks (512 MiB currently, but we can change that if it
is helpful) because we really want to avoid resizing operations, so the
tradeoff is between frequency of resize vs. potential memory wasted.
Does that change anything with regards to performance?
Thierry
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [PATCH v2 1/3] tracing/remotes: Fix leak in trace_remote_alloc_buffer() error path
From: Steven Rostedt @ 2026-07-09 23:15 UTC (permalink / raw)
To: Vincent Donnefort
Cc: mhiramat, linux-trace-kernel, mathieu.desnoyers, kernel-team,
linux-kernel, Sashiko
In-Reply-To: <20260709160017.1729517-2-vdonnefort@google.com>
On Thu, 9 Jul 2026 17:00:15 +0100
Vincent Donnefort <vdonnefort@google.com> wrote:
> If page allocation fails in trace_remote_alloc_buffer(), desc->nr_cpus
> is not yet incremented for the current CPU. As a consequence, on error,
> half-allocated rb_desc will not be freed in trace_remote_free_buffer().
>
> Increment desc->nr_cpus as soon as the first allocation for the current
> CPU has succeeded.
>
> Fixes: 96e43537af54 ("tracing: Introduce trace remotes")
> Reported-by: Sashiko <sashiko-bot@kernel.org>
> Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
This patch makes Sashiko find other possible issues with the code :-p
https://sashiko.dev/#/patchset/20260709160017.1729517-2-vdonnefort%40google.com
-- Steve
^ permalink raw reply
* Re: [BUG] tracing: Too many tries to read user space
From: Masami Hiramatsu @ 2026-07-10 3:22 UTC (permalink / raw)
To: Jeongho Choi
Cc: linux-trace-kernel, linux-kernel, rostedt, ji2yoon.jo, minki.jang,
hajun.sung
In-Reply-To: <20260708123753.GB1386@KORCO121415.samsungds.net>
On Wed, 8 Jul 2026 21:37:53 +0900
Jeongho Choi <jh1012.choi@samsung.com> wrote:
> Hello,
>
> We are seeing a reproducible kernel panic related to the tracing code
> when it fails to read user-space memory.
>
> The issue was originally reported through the Android/Google Issue
> Tracker, and we were advised to report it to the upstream trace mailing
> list because the affected code is upstream.
>
> Environment:
>
> Architecture: arm64
> Kernel: Linux 6.18.21
> Base: Android Common Kernel (android17-6.18)
> Affected area: kernel/trace/
>
> The relevant error/panic log is:
> [48916.569148] [9: lmkd: 536] Error: Too many tries to read
> user space
> [48916.569156] [9: lmkd: 536] WARNING: CPU: 9 PID: 536 at
> kernel/trace/trace.c:7374 trace_user_fault_read+0x334/0x360
> [48916.569443] [9: lmkd: 536] CPU: 9 UID: 1069 PID: 536 Comm:
> lmkd Tainted: G OE 6.18.21-android17-5-ga1a8e8cab9ec-4k
> #1 PREEMPT 25372cd4750dcac3c0fe86b57d47c665f97a6046
>
> [48916.569450] [9: lmkd: 536] pstate: 63402005 (nZCv daif
> +PAN -UAO +TCO +DIT -SSBS BTYPE=--)
> [48916.569452] [9: lmkd: 536] pc :
> trace_user_fault_read+0x334/0x360
> [48916.569454] [9: lmkd: 536] lr :
> trace_user_fault_read+0x330/0x360
> [48916.569456] [9: lmkd: 536] sp : ffffffc096993cc0
> [48916.569457] [9: lmkd: 536] x29: ffffffc096993cd0 x28:
> 0000000000000065 x27: ffffff8817e45200
> [48916.569461] [9: lmkd: 536] x26: 0000000000000000 x25:
> 000000011616c082 x24: 0000000000000000
> [48916.569463] [9: lmkd: 536] x23: 0000000000000009 x22:
> 000000000000002b x21: ffffff880597f740
> [48916.569466] [9: lmkd: 536] x20: 0000007fc42dd940 x19:
> ffffff8817e45770 x18: ffffffd5947ce240
> [48916.569468] [9: lmkd: 536] x17: 2073656972742079 x16:
> 6e616d206f6f5420 x15: 3a726f727245205d
> [48916.569471] [9: lmkd: 536] x14: 36333520203a646b x13:
> 6563617073207265 x12: 0000000000000001
> [48916.569473] [9: lmkd: 536] x11: 6f74207365697274 x10:
> 0000000000000001 x9 : 4bd9ad4516d75100
> [48916.569476] [9: lmkd: 536] x8 : 4bd9ad4516d75100 x7 :
> 205d383431393635 x6 : 2e36313938345b0a
> [48916.569479] [9: lmkd: 536] x5 : ffffffc080fa5998 x4 :
> ffffffd591707202 x3 : 0001360a00000000
> [48916.569481] [9: lmkd: 536] x2 : ffffffc096993af4 x1 :
> 00000000000000c0 x0 : 0000000000000028
> [48916.569484] [9: lmkd: 536] Call trace:
> [48916.569486] [9: lmkd: 536]
> trace_user_fault_read+0x334/0x360 (P)
> [48916.569488] [9: lmkd: 536] tracing_mark_write+0x84/0x174
> [48916.569491] [9: lmkd: 536] __arm64_sys_write+0x2a0/0x5c0
> [48916.569494] [9: lmkd: 536] invoke_syscall+0x58/0xe4
> [48916.569498] [9: lmkd: 536] do_el0_svc+0x48/0xdc
> [48916.569500] [9: lmkd: 536] el0_svc+0x3c/0x98
> [48916.569503] [9: lmkd: 536]
> el0t_64_sync_handler+0x20/0x130
> [48916.569505] [9: lmkd: 536] el0t_64_sync+0x1c4/0x1c8
> [48916.569508] [9: lmkd: 536] Kernel panic - not syncing:
> kernel: panic_on_warn set ...
>
>
> The code at the WARN location mentioned in the log above is as follows.
>
> 7374 if (WARN_ONCE(trys++ > 100, "Error: Too many
> tries to read user space"))
> 7375 return NULL;
>
>
> Our current analysis is as follows:
>
> In the Gmail process, during a low memory situation, LMKD writes strings
> to /sys/kernel/tracing/trace_marker for systrace recording. At the same
> time, it broadcasts a sigkill due to low memory, which is causing the
> LMKD trace marker operation to stall.
Hm, in my view, this warning indicates that the circuit breaker has
triggered correctly, so that is not a bug. Under the heavy memory
pressure and low-memory situation, the page can be reclaimed soon
after it is copied.
However, this seems a bit strange that we only checks the CPU-wide context
switching in the loop. Instead, can we introduce a per-cpu sequence counter
to per-cpu buffer, and check it?
Could you try this ?
From f76d8e4400a5961725d17899f4290c9334987e2b Mon Sep 17 00:00:00 2001
From: Masami Hiramatsu <mhiramat@kernel.org>
Date: Fri, 10 Jul 2026 12:11:00 +0900
Subject: [PATCH] tracing: Use per-CPU sequence counter in
trace_user_fault_read
trace_user_fault_read() copies trace data from user space to the
per-CPU trace buffer. When preemption is enabled during the copy, it
checks if any context switches occurred on the current CPU via
nr_context_switches_cpu() to detect whether the buffer may have been
corrupted by another trace writer.
However, under heavy memory pressure, copying from user space can trigger
page faults (e.g., for swapped-out BSS or anonymous pages) that block and
cause a context switch. Because nr_context_switches_cpu() detects any
context switch (even unrelated ones), it mistakenly assumes the buffer was
corrupted. This leads to repeated retries (up to the 100-try limit), which
causes a WARN_ONCE backtrace and returns -EFAULT to user space, even if
no other task ever accessed the trace buffer.
To mitigate this issue, replace the CPU-wide context switch check with
a dedicated per-CPU sequence counter in struct trace_user_buf.
Since only other tasks invoking trace_user_fault_read() on the same CPU
will increment this counter, unrelated context switches (including those
from page fault sleep) will no longer trigger retries.
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
kernel/trace/trace.c | 29 ++++++++++++++++++-----------
1 file changed, 18 insertions(+), 11 deletions(-)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 1bc27c0ad029..46cec63f5798 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -5989,6 +5989,7 @@ static ssize_t write_marker_to_buffer(struct trace_array *tr, const char *buf,
struct trace_user_buf {
char *buf;
+ unsigned int sequence;
};
static DEFINE_MUTEX(trace_user_buffer_mutex);
@@ -6031,7 +6032,10 @@ static int user_fault_buffer_enable(struct trace_user_buf_info *tinfo, size_t si
/* Clear each buffer in case of error */
for_each_possible_cpu(cpu) {
- per_cpu_ptr(tinfo->tbuf, cpu)->buf = NULL;
+ struct trace_user_buf *tbuf = per_cpu_ptr(tinfo->tbuf, cpu);
+
+ tbuf->buf = NULL;
+ tbuf->sequence = 0;
}
for_each_possible_cpu(cpu) {
@@ -6196,8 +6200,9 @@ char *trace_user_fault_read(struct trace_user_buf_info *tinfo,
trace_user_buf_copy copy_func, void *data)
{
int cpu = smp_processor_id();
- char *buffer = per_cpu_ptr(tinfo->tbuf, cpu)->buf;
- unsigned int cnt;
+ struct trace_user_buf *tbuf = per_cpu_ptr(tinfo->tbuf, cpu);
+ char *buffer = tbuf->buf;
+ unsigned int seq;
int trys = 0;
int ret;
@@ -6211,10 +6216,10 @@ char *trace_user_fault_read(struct trace_user_buf_info *tinfo,
return NULL;
/*
- * This acts similar to a seqcount. The per CPU context switches are
+ * This acts similar to a seqcount. The per CPU sequence counters are
* recorded, migration is disabled and preemption is enabled. The
* read of the user space memory is copied into the per CPU buffer.
- * Preemption is disabled again, and if the per CPU context switches count
+ * Preemption is disabled again, and if the per CPU sequence count
* is still the same, it means the buffer has not been corrupted.
* If the count is different, it is assumed the buffer is corrupted
* and reading must be tried again.
@@ -6235,7 +6240,8 @@ char *trace_user_fault_read(struct trace_user_buf_info *tinfo,
preempt_enable_notrace();
preempt_disable_notrace();
cpu = smp_processor_id();
- buffer = per_cpu_ptr(tinfo->tbuf, cpu)->buf;
+ tbuf = per_cpu_ptr(tinfo->tbuf, cpu);
+ buffer = tbuf->buf;
}
/*
@@ -6250,8 +6256,9 @@ char *trace_user_fault_read(struct trace_user_buf_info *tinfo,
if (WARN_ONCE(trys++ > 100, "Error: Too many tries to read user space"))
return NULL;
- /* Read the current CPU context switch counter */
- cnt = nr_context_switches_cpu(cpu);
+ /* Increment the per-CPU buffer sequence counter */
+ tbuf->sequence++;
+ seq = tbuf->sequence;
/*
* Preemption is going to be enabled, but this task must
@@ -6282,12 +6289,12 @@ char *trace_user_fault_read(struct trace_user_buf_info *tinfo,
return NULL;
/*
- * Preemption is disabled again, now check the per CPU context
- * switch counter. If it doesn't match, then another user space
+ * Preemption is disabled again, now check the per CPU sequence
+ * counter. If it doesn't match, then another user space
* process may have schedule in and corrupted our buffer. In that
* case the copying must be retried.
*/
- } while (nr_context_switches_cpu(cpu) != cnt);
+ } while (tbuf->sequence != seq);
return buffer;
}
--
2.43.0
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply related
* Re: [RFC v2 1/3] arm64: kprobes: Only handle faults originating from XOL slot
From: Masami Hiramatsu @ 2026-07-10 4:10 UTC (permalink / raw)
To: Pu Hu
Cc: ada.coupriediaz@arm.com, catalin.marinas@arm.com,
davem@davemloft.net, Hongyan Xia, Jiazi Li,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org,
naveen@kernel.org, will@kernel.org, yang@os.amperecomputing.com
In-Reply-To: <20260709142215.226872-2-hupu@transsion.com>
On Thu, 9 Jul 2026 14:22:23 +0000
Pu Hu <hupu@transsion.com> wrote:
> From: Pu Hu <hupu@transsion.com>
>
> kprobe_fault_handler() currently treats any page fault taken while in
> KPROBE_HIT_SS or KPROBE_REENTER state as a kprobe single-step fault. This
> assumption does not hold: perf or tracing code may run from the debug
> exception path during the single-step window and take its own page fault.
>
> When the fault is handled as a kprobe fault, the PC is rewritten to the
> probe address, corrupting the exception recovery context for the real
> fault. A typical reproducer is running perf with preemptirq tracepoints
> and dwarf callchains while a kprobe is installed on a frequently
> executed function.
>
> Fix this in two layers:
>
> 1. At function entry, bail out immediately for simulated kprobes
> (ainsn.xol_insn == NULL), since they have no XOL slot and any fault
> taken during their execution cannot be a single-step fault.
>
> 2. For kprobes with an XOL slot, only handle the fault when the
> faulting PC matches the XOL instruction address. Faults from any
> other PC are left to the normal page fault handler.
>
> This follows the same principle as the x86 fix in commit 6381c24cd6d5
> ("kprobes/x86: Fix page-fault handling logic").
>
> Signed-off-by: Pu Hu <hupu@transsion.com>
> Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
This looks good to me.
Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Thanks,
> ---
> arch/arm64/kernel/probes/kprobes.c | 22 ++++++++++++++++++++++
> 1 file changed, 22 insertions(+)
>
> diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
> index 43a0361a8bf0..798e4b091d1a 100644
> --- a/arch/arm64/kernel/probes/kprobes.c
> +++ b/arch/arm64/kernel/probes/kprobes.c
> @@ -282,9 +282,31 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
> struct kprobe *cur = kprobe_running();
> struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
>
> + /*
> + * Simulated kprobes execute in the debug trap context and have no
> + * XOL slot. Any page fault taken while a simulated kprobe is in
> + * progress cannot have been caused by kprobe single-stepping and
> + * must be left alone for the normal page fault handler, including
> + * fixup_exception.
> + */
> + if (cur && !cur->ainsn.xol_insn)
> + return 0;
> +
> switch (kcb->kprobe_status) {
> case KPROBE_HIT_SS:
> case KPROBE_REENTER:
> + /*
> + * A page fault taken while in KPROBE_HIT_SS or
> + * KPROBE_REENTER state is only attributable to kprobe
> + * single-stepping if the faulting PC points to the
> + * current kprobe's XOL instruction. If the fault occurred
> + * elsewhere (e.g. in perf or tracing code invoked from the
> + * debug exception path), leave it for the normal page fault
> + * handler to process.
> + */
> + if (instruction_pointer(regs) != (unsigned long)cur->ainsn.xol_insn)
> + break;
> +
> /*
> * We are here because the instruction being single
> * stepped caused a page fault. We reset the current
> --
> 2.43.0
>
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
* Re: [RFC v2 2/3] arm64: kprobes: Allow reentering kprobes while single-stepping
From: Masami Hiramatsu @ 2026-07-10 5:09 UTC (permalink / raw)
To: Pu Hu
Cc: ada.coupriediaz@arm.com, catalin.marinas@arm.com,
davem@davemloft.net, Hongyan Xia, Jiazi Li,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org,
naveen@kernel.org, will@kernel.org, yang@os.amperecomputing.com
In-Reply-To: <20260709142215.226872-3-hupu@transsion.com>
On Thu, 9 Jul 2026 14:22:25 +0000
Pu Hu <hupu@transsion.com> wrote:
> From: Pu Hu <hupu@transsion.com>
>
> A kprobe can be hit while another kprobe is in KPROBE_HIT_SS state. This
> can happen when tracing or perf code runs from the debug exception path
> while the first kprobe is preparing or executing its out-of-line
> single-step instruction.
>
> Currently arm64 treats a kprobe hit in KPROBE_HIT_SS as unrecoverable,
> the same as a hit in KPROBE_REENTER. This is too strict. A hit in
> KPROBE_HIT_SS is still a one-level reentry and can be handled by saving
> the current kprobe state and setting up single-step for the new probe,
> just like reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.
>
> The truly unrecoverable case is hitting another kprobe while already in
> KPROBE_REENTER, because the reentry save area has already been consumed.
>
> Move KPROBE_HIT_SS to the recoverable reentry cases and leave
> KPROBE_REENTER as the unrecoverable nested reentry case.
>
> This mirrors the x86 fix in commit 6a5022a56ac3
> ("kprobes/x86: Allow to handle reentered kprobe on single-stepping").
>
Hi, as Sashiko commented, we have to save the saved_irqflag to
prev_kprobbe.
https://sashiko.dev/#/patchset/20260709142215.226872-1-hupu%40transsion.com?part=2
Thank you,
> Signed-off-by: Pu Hu <hupu@transsion.com>
> Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
> ---
> arch/arm64/kernel/probes/kprobes.c | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
> index 798e4b091d1a..2ca5916eca2f 100644
> --- a/arch/arm64/kernel/probes/kprobes.c
> +++ b/arch/arm64/kernel/probes/kprobes.c
> @@ -240,10 +240,16 @@ static int __kprobes reenter_kprobe(struct kprobe *p,
> switch (kcb->kprobe_status) {
> case KPROBE_HIT_SSDONE:
> case KPROBE_HIT_ACTIVE:
> + case KPROBE_HIT_SS:
> + /*
> + * A probe can be hit while another kprobe is preparing or
> + * executing its XOL single-step instruction. This is still a
> + * recoverable one-level reentry, so handle it in the same way as
> + * reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.
> + */
> kprobes_inc_nmissed_count(p);
> setup_singlestep(p, regs, kcb, 1);
> break;
> - case KPROBE_HIT_SS:
> case KPROBE_REENTER:
> pr_warn("Failed to recover from reentered kprobes.\n");
> dump_kprobe(p);
> --
> 2.43.0
>
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
* [PATCH v2 0/5] tracing: add ref_trace_final_put tracing
From: Eugene Mavick via B4 Relay @ 2026-07-10 5:30 UTC (permalink / raw)
To: Will Deacon, Peter Zijlstra, Boqun Feng, Mark Rutland, Gary Guo,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter
Cc: linux-kernel, linux-trace-kernel, linux-mm, Eugene Mavick
When debugging use-after-free(UAF) bugs, knowing when the object reaches
0 references and enters final release can significantly aid the
debugging process.
There is currently no universal way to trace this information.
This patch series implements tracing of the final puts in the
most widely used refcounting implementations,
refcount_t(and thus kref which uses it), and percpu-ref.
The tracepoint records three fields:
- caller: function that called the refcounting
function(refcount_sub_and_test, percpu_ref_put_many)
- fn: refcounting function(eg refcount_sub_and_test)
- obj: refcount object(struct percpu_ref, refcount_t)
Signed-off-by: Eugene Mavick <m@mavick.dev>
---
Changes in v2:
-include/linux/ref_trace.h: change macro name, use direct tracepoint
call in macro to avoid double check
-add tracepoint to refcount_dec_if_one
-kunit: make significant improvements to design, fix critical bug, add test case for
refcount_dec_if_one()
-Link to v1: https://lore.kernel.org/r/20260705-refcount-final-put-trace-v1-0-0ae936edb750@mavick.dev
---
Eugene Mavick (5):
tracing: add ref_trace_final_put tracepoint
refcount: add ref_trace_final_put tracepoint
percpu-refcount: add ref_trace_final_put trace
kunit: add test for ref_trace_final_put
MAINTAINERS: add entries for ref_trace_final_put
MAINTAINERS | 3 +
include/linux/percpu-refcount.h | 5 +-
include/linux/ref_trace.h | 26 ++++++++
include/linux/refcount.h | 2 +
include/trace/events/ref_trace.h | 46 +++++++++++++
lib/Kconfig | 10 +++
lib/Makefile | 2 +
lib/ref_trace.c | 12 ++++
lib/refcount.c | 8 ++-
lib/tests/Makefile | 1 +
lib/tests/ref_trace_kunit.c | 136 +++++++++++++++++++++++++++++++++++++++
11 files changed, 249 insertions(+), 2 deletions(-)
---
base-commit: df685633c3dbc67441cc86f1c3fee58de4652ba2
change-id: 20260624-refcount-final-put-trace-49bd7c39bd5a
Best regards,
--
Eugene Mavick <m@mavick.dev>
^ permalink raw reply
* [PATCH v2 1/5] tracing: add ref_trace_final_put tracepoint
From: Eugene Mavick via B4 Relay @ 2026-07-10 5:30 UTC (permalink / raw)
To: Will Deacon, Peter Zijlstra, Boqun Feng, Mark Rutland, Gary Guo,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter
Cc: linux-kernel, linux-trace-kernel, linux-mm, Eugene Mavick
In-Reply-To: <20260710-refcount-final-put-trace-v2-0-557cfce860a2@mavick.dev>
From: Eugene Mavick <m@mavick.dev>
Add ref_trace_final_put tracepoint and related core infrastructure
ref_trace_final_put fires when a reference
count reaches zero and the object enters its final release path.
The tracepoint records three fields:
- caller: function that called the refcounting
function(refcount_sub_and_test, percpu_ref_put_many)
- fn: refcounting function(eg refcount_sub_and_test)
- obj: refcount object(struct percpu_ref, refcount_t)
Signed-off-by: Eugene Mavick <m@mavick.dev>
---
v2:
-change trace_ref_final_put macro name to do_trace_ref_final_put to
avoid overlap with tracepoint names(trace_*)
-change, in above mentioned macro, from trace_ref_trace_final_put( to
trace_call__ref_trace_final_put( to avoid double check
-changes suggested by Steven Rostedt
-v1:https://lore.kernel.org/all/20260705-refcount-final-put-trace-v1-1-cdd0014626a9@mavick.dev/
---
include/linux/ref_trace.h | 26 +++++++++++++++++++++++
include/trace/events/ref_trace.h | 46 ++++++++++++++++++++++++++++++++++++++++
lib/Makefile | 2 ++
lib/ref_trace.c | 12 +++++++++++
4 files changed, 86 insertions(+)
diff --git a/include/linux/ref_trace.h b/include/linux/ref_trace.h
new file mode 100644
index 000000000000..6c6600938a47
--- /dev/null
+++ b/include/linux/ref_trace.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_REF_TRACE_H
+#define _LINUX_REF_TRACE_H
+
+#include <linux/tracepoint-defs.h>
+#include <linux/instruction_pointer.h>
+
+/* Declare the tracepoint so tracepoint_enabled() can be used */
+DECLARE_TRACEPOINT(ref_trace_final_put);
+
+#ifdef CONFIG_TRACEPOINTS
+/* Wrapper function implemented in lib/ref_trace.c */
+extern void do_ref_trace_final_put(unsigned long caller, const char *fn, const void *obj);
+
+#define do_trace_ref_final_put(obj) \
+ do { \
+ if (tracepoint_enabled(ref_trace_final_put)) \
+ do_ref_trace_final_put(_RET_IP_, __func__, obj); \
+ } while (0)
+
+#else /* !CONFIG_TRACEPOINTS */
+static inline void do_ref_trace_final_put(unsigned long caller, const char *fn, const void *obj) { }
+#define do_trace_ref_final_put(obj) do { } while (0)
+#endif
+
+#endif /* _LINUX_REF_TRACE_H */
diff --git a/include/trace/events/ref_trace.h b/include/trace/events/ref_trace.h
new file mode 100644
index 000000000000..e6037a325be2
--- /dev/null
+++ b/include/trace/events/ref_trace.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ref_trace
+
+#if !defined(_TRACE_REF_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_REF_TRACE_H
+
+#include <linux/tracepoint.h>
+
+/**
+ * ref_trace_final_put - trace when a reference count reaches zero
+ * @caller: function that called the refcounting
+ * function(refcount_sub_and_test, percpu_ref_put_many)
+ * @fn: refcounting function(eg refcount_sub_and_test)
+ * @obj: refcount object(struct percpu_ref, refcount_t)
+ *
+ * Tracepoint instrumentation can be added using the ref_trace_final_put
+ * macro defined in include/linux/ref_trace.h
+ * which uses _RET_IP_ and __func__ for caller and fn arguments respectively,
+ * thus only requiring obj arg to be supplied
+ */
+TRACE_EVENT(ref_trace_final_put,
+
+ TP_PROTO(unsigned long caller, const char *fn, const void *obj),
+
+ TP_ARGS(caller, fn, obj),
+
+ TP_STRUCT__entry(
+ __field(unsigned long, caller)
+ __string(fn, fn)
+ __field(const void *, obj)
+ ),
+
+ TP_fast_assign(
+ __entry->caller = caller;
+ __assign_str(fn);
+ __entry->obj = obj;
+ ),
+
+ TP_printk("caller=%pS fn=%s obj=%p", (void *)__entry->caller, __get_str(fn), __entry->obj)
+);
+
+#endif /* _TRACE_REF_TRACE_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/lib/Makefile b/lib/Makefile
index f33a24bf1c19..41737090a95d 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -335,3 +335,5 @@ CONTEXT_ANALYSIS_test_context-analysis.o := y
obj-$(CONFIG_CONTEXT_ANALYSIS_TEST) += test_context-analysis.o
subdir-$(CONFIG_FORTIFY_SOURCE) += test_fortify
+
+obj-$(CONFIG_TRACEPOINTS) += ref_trace.o
diff --git a/lib/ref_trace.c b/lib/ref_trace.c
new file mode 100644
index 000000000000..9102dc7117db
--- /dev/null
+++ b/lib/ref_trace.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+#define CREATE_TRACE_POINTS
+#include <trace/events/ref_trace.h>
+
+//Wrapper function for functions defined entirely in header files
+void do_ref_trace_final_put(unsigned long caller, const char *fn, const void *obj)
+{
+ trace_call__ref_trace_final_put(caller, fn, obj);
+}
+EXPORT_SYMBOL_GPL(do_ref_trace_final_put);
+
+EXPORT_TRACEPOINT_SYMBOL_GPL(ref_trace_final_put);
--
2.51.2
^ permalink raw reply related
* [PATCH v2 2/5] refcount: add ref_trace_final_put tracepoint
From: Eugene Mavick via B4 Relay @ 2026-07-10 5:30 UTC (permalink / raw)
To: Will Deacon, Peter Zijlstra, Boqun Feng, Mark Rutland, Gary Guo,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter
Cc: linux-kernel, linux-trace-kernel, linux-mm, Eugene Mavick
In-Reply-To: <20260710-refcount-final-put-trace-v2-0-557cfce860a2@mavick.dev>
From: Eugene Mavick <m@mavick.dev>
Add the ref_trace_final_put tracepoint to __refcount_sub_and_test() and
refcount_dec_if_one()
This tracepoint fires when a refcount_t reaches zero, capturing the caller
address, the function name, and the refcount_t address.
Signed-off-by: Eugene Mavick <m@mavick.dev>
---
v2:
-update commit message
-add tracepoint to refcount_dec_if_one(), suggested by Usuma Arif
-move MAINTAINERS file entries to new MAINTAINERS commit
-v1:https://lore.kernel.org/all/20260705-refcount-final-put-trace-v1-2-cdd0014626a9@mavick.dev/
---
include/linux/refcount.h | 2 ++
lib/refcount.c | 8 +++++++-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/include/linux/refcount.h b/include/linux/refcount.h
index ba7657ced281..70d07a462da1 100644
--- a/include/linux/refcount.h
+++ b/include/linux/refcount.h
@@ -107,6 +107,7 @@
#include <linux/limits.h>
#include <linux/refcount_types.h>
#include <linux/spinlock_types.h>
+#include <linux/ref_trace.h>
struct mutex;
@@ -393,6 +394,7 @@ bool __refcount_sub_and_test(int i, refcount_t *r, int *oldp)
if (old > 0 && old == i) {
smp_acquire__after_ctrl_dep();
+ do_trace_ref_final_put(r);
return true;
}
diff --git a/lib/refcount.c b/lib/refcount.c
index a207a8f22b3c..cd7e32df3919 100644
--- a/lib/refcount.c
+++ b/lib/refcount.c
@@ -7,6 +7,7 @@
#include <linux/refcount.h>
#include <linux/spinlock.h>
#include <linux/bug.h>
+#include <linux/ref_trace.h>
#define REFCOUNT_WARN(str) WARN_ONCE(1, "refcount_t: " str ".\n")
@@ -56,7 +57,12 @@ bool refcount_dec_if_one(refcount_t *r)
{
int val = 1;
- return atomic_try_cmpxchg_release(&r->refs, &val, 0);
+ bool ret = atomic_try_cmpxchg_release(&r->refs, &val, 0);
+
+ if (ret)
+ do_trace_ref_final_put(r);
+
+ return ret;
}
EXPORT_SYMBOL(refcount_dec_if_one);
--
2.51.2
^ permalink raw reply related
* [PATCH v2 3/5] percpu-refcount: add ref_trace_final_put trace
From: Eugene Mavick via B4 Relay @ 2026-07-10 5:30 UTC (permalink / raw)
To: Will Deacon, Peter Zijlstra, Boqun Feng, Mark Rutland, Gary Guo,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter
Cc: linux-kernel, linux-trace-kernel, linux-mm, Eugene Mavick
In-Reply-To: <20260710-refcount-final-put-trace-v2-0-557cfce860a2@mavick.dev>
From: Eugene Mavick <m@mavick.dev>
Add the ref_trace_final_put tracepoint to percpu_ref_put_many().
The tracepoint fires when the atomic counter reaches zero in the
atomic fallback path (after percpu_ref_kill() has been called).
Signed-off-by: Eugene Mavick <m@mavick.dev>
---
include/linux/percpu-refcount.h | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h
index d73a1c08c3e3..f870ac0e8c06 100644
--- a/include/linux/percpu-refcount.h
+++ b/include/linux/percpu-refcount.h
@@ -55,6 +55,7 @@
#include <linux/rcupdate.h>
#include <linux/types.h>
#include <linux/gfp.h>
+#include <linux/ref_trace.h>
struct percpu_ref;
typedef void (percpu_ref_func_t)(struct percpu_ref *);
@@ -331,8 +332,10 @@ static inline void percpu_ref_put_many(struct percpu_ref *ref, unsigned long nr)
if (__ref_is_percpu(ref, &percpu_count))
this_cpu_sub(*percpu_count, nr);
- else if (unlikely(atomic_long_sub_and_test(nr, &ref->data->count)))
+ else if (unlikely(atomic_long_sub_and_test(nr, &ref->data->count))) {
+ do_trace_ref_final_put(ref);
ref->data->release(ref);
+ }
rcu_read_unlock();
}
--
2.51.2
^ permalink raw reply related
* [PATCH v2 4/5] kunit: add test for ref_trace_final_put
From: Eugene Mavick via B4 Relay @ 2026-07-10 5:30 UTC (permalink / raw)
To: Will Deacon, Peter Zijlstra, Boqun Feng, Mark Rutland, Gary Guo,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter
Cc: linux-kernel, linux-trace-kernel, linux-mm, Eugene Mavick
In-Reply-To: <20260710-refcount-final-put-trace-v2-0-557cfce860a2@mavick.dev>
From: Eugene Mavick <m@mavick.dev>
Add a KUnit test suite for the ref_trace_final_put tracepoint.
The test registers a probe function and triggers both refcount_t and
percpu_ref final put paths, verifying that the tracepoint fires
correctly and that the recorded fields match expected values.
Signed-off-by: Eugene Mavick <m@mavick.dev>
---
v2:
-fix v1 bug where test gave consistent false pass
-make probe minimal, and only check obj and store the values
-add test_init macro to register and reset count
-move value checking to new test_exit macro
-move MAINTAINERS file entries to new MAINTAINERS commit
-v1:https://lore.kernel.org/all/20260705-refcount-final-put-trace-v1-4-cdd0014626a9@mavick.dev/
---
lib/Kconfig | 10 ++++
lib/tests/Makefile | 1 +
lib/tests/ref_trace_kunit.c | 136 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 147 insertions(+)
diff --git a/lib/Kconfig b/lib/Kconfig
index 00a9509636c1..c29b2ccb3c31 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -52,6 +52,16 @@ config PACKING_KUNIT_TEST
When in doubt, say N.
+config REF_TRACE_KUNIT_TEST
+ bool "ref_trace kunit test" if !KUNIT_ALL_TESTS
+ depends on KUNIT && FTRACE
+ default KUNIT_ALL_TESTS
+ help
+ This option enables the KUnit test suite for the ref_trace_final_put
+ tracepoint.
+
+ If unsure, say N
+
config BITREVERSE
tristate
diff --git a/lib/tests/Makefile b/lib/tests/Makefile
index 7e9c2fa52e35..828a030ad8c7 100644
--- a/lib/tests/Makefile
+++ b/lib/tests/Makefile
@@ -57,5 +57,6 @@ obj-$(CONFIG_USERCOPY_KUNIT_TEST) += usercopy_kunit.o
obj-$(CONFIG_UTIL_MACROS_KUNIT) += util_macros_kunit.o
obj-$(CONFIG_RATELIMIT_KUNIT_TEST) += test_ratelimit.o
obj-$(CONFIG_UUID_KUNIT_TEST) += uuid_kunit.o
+obj-$(CONFIG_REF_TRACE_KUNIT_TEST) += ref_trace_kunit.o
obj-$(CONFIG_TEST_RUNTIME_MODULE) += module/
diff --git a/lib/tests/ref_trace_kunit.c b/lib/tests/ref_trace_kunit.c
new file mode 100644
index 000000000000..5c7e9d29a030
--- /dev/null
+++ b/lib/tests/ref_trace_kunit.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <kunit/test.h>
+#include <linux/compiler_attributes.h>
+#include <linux/wait_bit.h>
+#include <linux/instruction_pointer.h>
+#include <linux/kallsyms.h>
+#include <linux/percpu-refcount.h>
+#include <linux/refcount.h>
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <trace/events/ref_trace.h>
+
+struct data {
+ unsigned long caller;
+ const char *fn;
+ const void *obj;
+ atomic_t count;
+};
+
+struct data capture;
+
+const void *chk_obj;
+
+#define test_init() \
+ do { \
+ KUNIT_EXPECT_FALSE( \
+ test, register_trace_ref_trace_final_put(probe, NULL)); \
+ \
+ atomic_set_release(&capture.count, 0); \
+ \
+ chk_obj = &obj; \
+ } while (0)
+
+
+#define test_exit(func_name) \
+ do { \
+ /* wait for probe completion */ \
+ wait_var_event( \
+ &capture.count, \
+ atomic_read_acquire(&capture.count) != 0); \
+ \
+ unregister_trace_ref_trace_final_put(probe, NULL); \
+ \
+ KUNIT_EXPECT_EQ(test, atomic_read_acquire(&capture.count), 1); \
+ /* \
+ * caller testing may be flaky \
+ * due to compile optimisations so omit \
+ */ \
+ KUNIT_EXPECT_STREQ(test, capture.fn, #func_name); \
+ KUNIT_EXPECT_PTR_EQ(test, capture.obj, &obj); \
+ } while (0)
+
+static void probe(
+ void *ignore,
+ unsigned long caller,
+ const char *fn,
+ const void *obj)
+{
+ //prevent non test func final_puts from changing captured values
+ if (chk_obj != obj)
+ return;
+
+ capture.caller = caller;
+ capture.fn = fn;
+ capture.obj = obj;
+
+ atomic_inc_return_release(&capture.count); //increase count
+}
+
+static noinline void test_refcount_sub_and_test(struct kunit *test)
+{
+ refcount_t obj;
+
+ test_init();
+ refcount_set(&obj, 2);
+
+ KUNIT_EXPECT_FALSE(test, refcount_dec_and_test(&obj));
+ KUNIT_EXPECT_TRUE(test, refcount_dec_and_test(&obj));
+
+ test_exit(__refcount_sub_and_test);
+}
+
+static __always_inline void test_refcount_dec_if_one(struct kunit *test)
+{
+ refcount_t obj;
+
+ test_init();
+ refcount_set(&obj, 2);
+
+ KUNIT_EXPECT_FALSE(test, refcount_dec_and_test(&obj));
+ KUNIT_EXPECT_TRUE(test, refcount_dec_if_one(&obj));
+
+ test_exit(refcount_dec_if_one);
+}
+static void dummy_release(struct percpu_ref *ref) {}
+
+static noinline void test_percpu_ref_put_many(struct kunit *test)
+{
+ struct percpu_ref obj;
+
+ test_init();
+
+ KUNIT_EXPECT_FALSE(test, percpu_ref_init(&obj, dummy_release, 0, GFP_KERNEL));
+
+ percpu_ref_get(&obj);
+ percpu_ref_get(&obj);
+
+ percpu_ref_put(&obj);
+ percpu_ref_put(&obj);
+
+ percpu_ref_switch_to_atomic_sync(&obj);
+
+ percpu_ref_put(&obj);
+
+ test_exit(percpu_ref_put_many);
+ percpu_ref_exit(&obj);
+}
+
+static struct kunit_case __refdata ref_trace_test_cases[] = {
+ KUNIT_CASE(test_refcount_sub_and_test),
+ KUNIT_CASE(test_refcount_dec_if_one),
+ KUNIT_CASE(test_percpu_ref_put_many),
+ {}
+};
+
+static struct kunit_suite ref_trace_test_suite = {
+ .name = "ref-trace",
+ .test_cases = ref_trace_test_cases
+};
+
+kunit_test_suites(&ref_trace_test_suite);
+
+MODULE_AUTHOR("Eugene Mavick <m@mavick.dev>");
+MODULE_DESCRIPTION("KUnit test for ref_trace");
+MODULE_LICENSE("GPL");
--
2.51.2
^ permalink raw reply related
* [PATCH v2 5/5] MAINTAINERS: add entries for ref_trace_final_put
From: Eugene Mavick via B4 Relay @ 2026-07-10 5:30 UTC (permalink / raw)
To: Will Deacon, Peter Zijlstra, Boqun Feng, Mark Rutland, Gary Guo,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter
Cc: linux-kernel, linux-trace-kernel, linux-mm, Eugene Mavick
In-Reply-To: <20260710-refcount-final-put-trace-v2-0-557cfce860a2@mavick.dev>
From: Eugene Mavick <m@mavick.dev>
Add new files added in the patch series to MAINTAINERS
Signed-off-by: Eugene Mavick <m@mavick.dev>
---
v2:
This patch was added in v2
---
MAINTAINERS | 3 +++
1 file changed, 3 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 10e8253181d3..2dbb7441906e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4210,7 +4210,10 @@ S: Maintained
F: Documentation/atomic_*.txt
F: arch/*/include/asm/atomic*.h
F: include/*/atomic*.h
+F: include/linux/ref_trace.h
F: include/linux/refcount.h
+F: lib/ref_trace.c
+F: lib/tests/ref_trace_kunit.c
F: scripts/atomic/
F: rust/kernel/sync/atomic.rs
F: rust/kernel/sync/atomic/
--
2.51.2
^ permalink raw reply related
* Re: [RFC v2 2/3] arm64: kprobes: Allow reentering kprobes while single-stepping
From: Pu Hu @ 2026-07-10 6:13 UTC (permalink / raw)
To: Masami Hiramatsu (Google)
Cc: ada.coupriediaz@arm.com, catalin.marinas@arm.com,
davem@davemloft.net, Hongyan Xia, Jiazi Li,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org,
naveen@kernel.org, will@kernel.org, yang@os.amperecomputing.com
In-Reply-To: <20260710140936.8788416564452e490c71634c@kernel.org>
On 7/10/2026 1:09 PM, Masami Hiramatsu wrote:
> On Thu, 9 Jul 2026 14:22:25 +0000
> Pu Hu <hupu@transsion.com> wrote:
>
>> From: Pu Hu <hupu@transsion.com>
>>
>> A kprobe can be hit while another kprobe is in KPROBE_HIT_SS state. This
>> can happen when tracing or perf code runs from the debug exception path
>> while the first kprobe is preparing or executing its out-of-line
>> single-step instruction.
>>
>> Currently arm64 treats a kprobe hit in KPROBE_HIT_SS as unrecoverable,
>> the same as a hit in KPROBE_REENTER. This is too strict. A hit in
>> KPROBE_HIT_SS is still a one-level reentry and can be handled by saving
>> the current kprobe state and setting up single-step for the new probe,
>> just like reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.
>>
>> The truly unrecoverable case is hitting another kprobe while already in
>> KPROBE_REENTER, because the reentry save area has already been consumed.
>>
>> Move KPROBE_HIT_SS to the recoverable reentry cases and leave
>> KPROBE_REENTER as the unrecoverable nested reentry case.
>>
>> This mirrors the x86 fix in commit 6a5022a56ac3
>> ("kprobes/x86: Allow to handle reentered kprobe on single-stepping").
>>
>
> Hi, as Sashiko commented, we have to save the saved_irqflag to
> prev_kprobbe.
>
> https://sashiko.dev/#/patchset/20260709142215.226872-1-hupu%40transsion.com?part=2
>
> Thank you,
>
Hi Masami,
We already added the saved_irqflag support in Patch 3 of this series:
arm64: kprobes: Save and restore saved_irqflag in prev_kprobe
However, I see the issue. Patch 2 and Patch 3 are separate commits,
so Patch 2 alone is not bisectable. I'll prepare a v3 that folds them
together, so the reentry logic and the saved_irqflag preservation land
in a single commit.
Thanks for pointing this out.
Thanks,
Pu Hu
^ permalink raw reply
* Re: [PATCH v3 16/17] selftests/verification: Rearrange the wwnr_printk test
From: Nam Cao @ 2026-07-10 6:18 UTC (permalink / raw)
To: Gabriele Monaco, linux-trace-kernel, linux-kernel, Steven Rostedt,
Gabriele Monaco, Shuah Khan, linux-kselftest
Cc: Thomas Weissschuh, Tomas Glozar, John Kacur, Wen Yang
In-Reply-To: <20260625121440.116317-17-gmonaco@redhat.com>
Gabriele Monaco <gmonaco@redhat.com> writes:
> Simplify the test and run the steps expecting no reaction before the one
> expecting reactions.
This requirement is not clear from the code. I fear a new developer who
wants to introduce a new test case will easily trip over this, or even
us a few months from now.
Instead of this, how about each test case discards or ignores all
previous reactions?
Nam
^ permalink raw reply
* [RFC v3 0/2] rm64: kprobes: Fix single-step fault and reentry handling
From: Pu Hu @ 2026-07-10 6:32 UTC (permalink / raw)
To: mhiramat@kernel.org
Cc: ada.coupriediaz@arm.com, catalin.marinas@arm.com,
davem@davemloft.net, Hongyan Xia, Pu Hu, Jiazi Li,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org,
naveen@kernel.org, will@kernel.org, yang@os.amperecomputing.com
From: Pu Hu <hupu@transsion.com>
This series fixes two arm64 kprobes issues observed when running
simpleperf with preemptirq tracepoints and dwarf callchains while a
kprobe is active on a frequently executed kernel function.
The crash happens in the kprobe debug exception path. While a kprobe is
preparing or executing its XOL single-step instruction, perf/trace code
can run in the same window. That code may either take a fault of its own
or hit another kprobe.
Patch 1 fixes kprobe_fault_handler() so that it only handles a fault
taken in KPROBE_HIT_SS or KPROBE_REENTER state when the faulting PC
points at the current kprobe's XOL instruction. Simulated kprobes,
which have no XOL slot at all, are filtered out at function entry so
that the normal page fault handler (including fixup_exception) can
process any fault without interference.
Patch 2 allows a kprobe hit in KPROBE_HIT_SS to be handled as a
recoverable one-level reentry, instead of treating it as unrecoverable.
This is safe because the reentry save area has not yet been consumed at
that point. Only a hit while already in KPROBE_REENTER remains
unrecoverable. The same patch also extends struct prev_kprobe with a
saved_irqflag field so that the outer kprobe's original DAIF state is
preserved across reentry. Without this, the nested kprobe would
overwrite kcb->saved_irqflag, and the outer kprobe would restore the
wrong DAIF mask on completion, potentially leaving interrupts
permanently disabled.
This follows the same logic as the existing x86 fixes:
6381c24cd6d5 ("kprobes/x86: Fix page-fault handling logic")
6a5022a56ac3 ("kprobes/x86: Allow to handle reentered kprobe on single-stepping")
v2 -> v3:
- Patch 1: unchanged.
- Folded Patch 3 into Patch 2 so the saved_irqflag fix lands in the
same commit as the reentry change, keeping the series bisectable.
v1 -> v2:
- Patch 1: moved simulated kprobe check to function entry (per
maintainer review); removed redundant xol_insn NULL check from
the inner branch.
- Patch 2: unchanged.
- Patch 3: new in v2; fixes the IRQ flag save/restore gap that
Patch 2 exposes.
- Removed the selftest patch (old Patch 3) from this series.
- Fixed Signed-off-by to use full name (Pu Hu) instead of username
(hupu).
- Updated comments and commit messages across all patches.
Reproducer:
simpleperf record -p <pid> -f 10000 \
-e preemptirq:preempt_disable \
-e preemptirq:preempt_enable \
--duration 9 --call-graph dwarf \
-o /data/local/tmp/perf.data
Before this series, the crash reproduced frequently. With both patches
applied, it was no longer reproduced in our testing.
Pu Hu (2):
arm64: kprobes: Only handle faults originating from XOL slot
arm64: kprobes: Allow reentering kprobes while single-stepping
arch/arm64/include/asm/kprobes.h | 6 ++++
arch/arm64/kernel/probes/kprobes.c | 45 +++++++++++++++++++++++++++++-
2 files changed, 50 insertions(+), 1 deletion(-)
--
2.43.0
^ permalink raw reply
* [RFC v3 1/2] arm64: kprobes: Only handle faults originating from XOL slot
From: Pu Hu @ 2026-07-10 6:32 UTC (permalink / raw)
To: mhiramat@kernel.org
Cc: ada.coupriediaz@arm.com, catalin.marinas@arm.com,
davem@davemloft.net, Hongyan Xia, Pu Hu, Jiazi Li,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org,
naveen@kernel.org, will@kernel.org, yang@os.amperecomputing.com
In-Reply-To: <20260710063242.228714-1-hupu@transsion.com>
From: Pu Hu <hupu@transsion.com>
kprobe_fault_handler() currently treats any page fault taken while in
KPROBE_HIT_SS or KPROBE_REENTER state as a kprobe single-step fault. This
assumption does not hold: perf or tracing code may run from the debug
exception path during the single-step window and take its own page fault.
When the fault is handled as a kprobe fault, the PC is rewritten to the
probe address, corrupting the exception recovery context for the real
fault. A typical reproducer is running perf with preemptirq tracepoints
and dwarf callchains while a kprobe is installed on a frequently
executed function.
Fix this in two layers:
1. At function entry, bail out immediately for simulated kprobes
(ainsn.xol_insn == NULL), since they have no XOL slot and any fault
taken during their execution cannot be a single-step fault.
2. For kprobes with an XOL slot, only handle the fault when the
faulting PC matches the XOL instruction address. Faults from any
other PC are left to the normal page fault handler.
This follows the same principle as the x86 fix in commit 6381c24cd6d5
("kprobes/x86: Fix page-fault handling logic").
Signed-off-by: Pu Hu <hupu@transsion.com>
Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
---
arch/arm64/kernel/probes/kprobes.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
index 43a0361a8bf0..798e4b091d1a 100644
--- a/arch/arm64/kernel/probes/kprobes.c
+++ b/arch/arm64/kernel/probes/kprobes.c
@@ -282,9 +282,31 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
struct kprobe *cur = kprobe_running();
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ /*
+ * Simulated kprobes execute in the debug trap context and have no
+ * XOL slot. Any page fault taken while a simulated kprobe is in
+ * progress cannot have been caused by kprobe single-stepping and
+ * must be left alone for the normal page fault handler, including
+ * fixup_exception.
+ */
+ if (cur && !cur->ainsn.xol_insn)
+ return 0;
+
switch (kcb->kprobe_status) {
case KPROBE_HIT_SS:
case KPROBE_REENTER:
+ /*
+ * A page fault taken while in KPROBE_HIT_SS or
+ * KPROBE_REENTER state is only attributable to kprobe
+ * single-stepping if the faulting PC points to the
+ * current kprobe's XOL instruction. If the fault occurred
+ * elsewhere (e.g. in perf or tracing code invoked from the
+ * debug exception path), leave it for the normal page fault
+ * handler to process.
+ */
+ if (instruction_pointer(regs) != (unsigned long)cur->ainsn.xol_insn)
+ break;
+
/*
* We are here because the instruction being single
* stepped caused a page fault. We reset the current
--
2.43.0
^ permalink raw reply related
* [RFC v3 2/2] arm64: kprobes: Allow reentering kprobes while single-stepping
From: Pu Hu @ 2026-07-10 6:32 UTC (permalink / raw)
To: mhiramat@kernel.org
Cc: ada.coupriediaz@arm.com, catalin.marinas@arm.com,
davem@davemloft.net, Hongyan Xia, Pu Hu, Jiazi Li,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org,
naveen@kernel.org, will@kernel.org, yang@os.amperecomputing.com
In-Reply-To: <20260710063242.228714-1-hupu@transsion.com>
From: Pu Hu <hupu@transsion.com>
A kprobe can be hit while another kprobe is in KPROBE_HIT_SS state. This
can happen when tracing or perf code runs from the debug exception path
while the first kprobe is preparing or executing its out-of-line
single-step instruction.
Currently arm64 treats a kprobe hit in KPROBE_HIT_SS as unrecoverable,
the same as a hit in KPROBE_REENTER. This is too strict. A hit in
KPROBE_HIT_SS is still a one-level reentry and can be handled by saving
the current kprobe state and setting up single-step for the new probe,
just like reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.
The truly unrecoverable case is hitting another kprobe while already in
KPROBE_REENTER, because the reentry save area has already been consumed.
Move KPROBE_HIT_SS to the recoverable reentry cases and leave
KPROBE_REENTER as the unrecoverable nested reentry case.
This change also requires saving saved_irqflag in struct prev_kprobe.
When a nested kprobe calls kprobes_save_local_irqflag(), it overwrites
kcb->saved_irqflag with the currently masked DAIF value, losing the
outer kprobe's original DAIF state. Without this fix, when the outer
kprobe's single-step finishes, kprobes_restore_local_irqflag() applies
the wrong DAIF mask and leaves interrupts permanently disabled.
Extend struct prev_kprobe with a saved_irqflag field and save/restore it
alongside kp and status. This ensures the outer kprobe's original
interrupt state is preserved across reentry.
This mirrors the x86 fix in commit 6a5022a56ac3
("kprobes/x86: Allow to handle reentered kprobe on single-stepping").
Signed-off-by: Pu Hu <hupu@transsion.com>
Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
---
arch/arm64/include/asm/kprobes.h | 6 ++++++
arch/arm64/kernel/probes/kprobes.c | 23 ++++++++++++++++++++++-
2 files changed, 28 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/kprobes.h b/arch/arm64/include/asm/kprobes.h
index f2782560647b..35ce2c94040e 100644
--- a/arch/arm64/include/asm/kprobes.h
+++ b/arch/arm64/include/asm/kprobes.h
@@ -26,6 +26,12 @@
struct prev_kprobe {
struct kprobe *kp;
unsigned int status;
+
+ /*
+ * The original DAIF state of the outer kprobe, saved here before
+ * a nested kprobe overwrites kcb->saved_irqflag during reentry.
+ */
+ unsigned long saved_irqflag;
};
/* per-cpu kprobe control block */
diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
index 798e4b091d1a..4e0efad5caf2 100644
--- a/arch/arm64/kernel/probes/kprobes.c
+++ b/arch/arm64/kernel/probes/kprobes.c
@@ -174,12 +174,27 @@ static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
{
kcb->prev_kprobe.kp = kprobe_running();
kcb->prev_kprobe.status = kcb->kprobe_status;
+
+ /*
+ * Save the outer kprobe's original DAIF flags before the nested
+ * kprobe calls kprobes_save_local_irqflag() and overwrites
+ * kcb->saved_irqflag. Without this, the outer kprobe will restore
+ * the wrong DAIF state and leave interrupts permanently masked.
+ */
+ kcb->prev_kprobe.saved_irqflag = kcb->saved_irqflag;
}
static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
{
__this_cpu_write(current_kprobe, kcb->prev_kprobe.kp);
kcb->kprobe_status = kcb->prev_kprobe.status;
+
+ /*
+ * Restore the outer kprobe's saved_irqflag so that when its
+ * single-step completes, kprobes_restore_local_irqflag() uses
+ * the correct original DAIF value.
+ */
+ kcb->saved_irqflag = kcb->prev_kprobe.saved_irqflag;
}
static void __kprobes set_current_kprobe(struct kprobe *p)
@@ -240,10 +255,16 @@ static int __kprobes reenter_kprobe(struct kprobe *p,
switch (kcb->kprobe_status) {
case KPROBE_HIT_SSDONE:
case KPROBE_HIT_ACTIVE:
+ case KPROBE_HIT_SS:
+ /*
+ * A probe can be hit while another kprobe is preparing or
+ * executing its XOL single-step instruction. This is still a
+ * recoverable one-level reentry, so handle it in the same way as
+ * reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.
+ */
kprobes_inc_nmissed_count(p);
setup_singlestep(p, regs, kcb, 1);
break;
- case KPROBE_HIT_SS:
case KPROBE_REENTER:
pr_warn("Failed to recover from reentered kprobes.\n");
dump_kprobe(p);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v2 1/3] tracing/remotes: Fix leak in trace_remote_alloc_buffer() error path
From: Vincent Donnefort @ 2026-07-10 7:58 UTC (permalink / raw)
To: Steven Rostedt
Cc: mhiramat, linux-trace-kernel, mathieu.desnoyers, kernel-team,
linux-kernel, Sashiko
In-Reply-To: <20260709191516.7a7ad6f6@gandalf.local.home>
On Thu, Jul 09, 2026 at 07:15:16PM -0400, Steven Rostedt wrote:
> On Thu, 9 Jul 2026 17:00:15 +0100
> Vincent Donnefort <vdonnefort@google.com> wrote:
>
> > If page allocation fails in trace_remote_alloc_buffer(), desc->nr_cpus
> > is not yet incremented for the current CPU. As a consequence, on error,
> > half-allocated rb_desc will not be freed in trace_remote_free_buffer().
> >
> > Increment desc->nr_cpus as soon as the first allocation for the current
> > CPU has succeeded.
> >
> > Fixes: 96e43537af54 ("tracing: Introduce trace remotes")
> > Reported-by: Sashiko <sashiko-bot@kernel.org>
> > Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
>
> This patch makes Sashiko find other possible issues with the code :-p
>
> https://sashiko.dev/#/patchset/20260709160017.1729517-2-vdonnefort%40google.com
So one of them is fixed as part of "tracing/remotes: Add printk, dump_on_panic
and boot parameters" [1]. I can pull those fixes into this series instead.
I'll look at the rest.
[1] https://lore.kernel.org/all/20260605163825.1762953-2-vdonnefort@google.com/
>
> -- Steve
^ permalink raw reply
* Re: [PATCH v2 1/3] tracing/remotes: Fix leak in trace_remote_alloc_buffer() error path
From: Vincent Donnefort @ 2026-07-10 8:01 UTC (permalink / raw)
To: Steven Rostedt
Cc: mhiramat, linux-trace-kernel, mathieu.desnoyers, kernel-team,
linux-kernel, Sashiko
In-Reply-To: <alCmOw8l4e-DS44v@google.com>
On Fri, Jul 10, 2026 at 08:58:51AM +0100, Vincent Donnefort wrote:
> On Thu, Jul 09, 2026 at 07:15:16PM -0400, Steven Rostedt wrote:
> > On Thu, 9 Jul 2026 17:00:15 +0100
> > Vincent Donnefort <vdonnefort@google.com> wrote:
> >
> > > If page allocation fails in trace_remote_alloc_buffer(), desc->nr_cpus
> > > is not yet incremented for the current CPU. As a consequence, on error,
> > > half-allocated rb_desc will not be freed in trace_remote_free_buffer().
> > >
> > > Increment desc->nr_cpus as soon as the first allocation for the current
> > > CPU has succeeded.
> > >
> > > Fixes: 96e43537af54 ("tracing: Introduce trace remotes")
> > > Reported-by: Sashiko <sashiko-bot@kernel.org>
> > > Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
> >
> > This patch makes Sashiko find other possible issues with the code :-p
> >
> > https://sashiko.dev/#/patchset/20260709160017.1729517-2-vdonnefort%40google.com
>
> So one of them is fixed as part of "tracing/remotes: Add printk, dump_on_panic
> and boot parameters" [1]. I can pull those fixes into this series instead.
>
> I'll look at the rest.
Ha yes this other one is real too. However this is in arch/arm64/kvm/ so I'll
post it separately to kvmarm.
>
> [1] https://lore.kernel.org/all/20260605163825.1762953-2-vdonnefort@google.com/
>
> >
> > -- Steve
^ permalink raw reply
* Re: [PATCH 3/3] rv/reactors: add KUnit tests for reactor_panic
From: Gabriele Monaco @ 2026-07-10 8:27 UTC (permalink / raw)
To: Wen Yang; +Cc: Nam Cao, linux-trace-kernel, linux-kernel
In-Reply-To: <18dcc24d-329e-43f4-bc69-e368c1f1d066@linux.dev>
On Fri, 2026-07-10 at 00:46 +0800, Wen Yang wrote:
> Since rv_panic_reaction calls vpanic() which is __noreturn, direct
> testing is not feasible in KUnit.
>
> A preparatory v2 drops reactor_printk_kunit.c and reactor_panic_kunit.c
> entirely, replacing them with a single rv_reactors_kunit.c containing
> two suites:
>
> rv_reactor_registration: register/unregister lifecycle, duplicate
> rejection (-EINVAL), name-too-long rejection, and safe unregister
> of a never-registered reactor.
>
> rv_react_dispatch: null-callback guard, callback invocation check,
> and the mdelay lockdep stress test.
>
>
> v2 also adds EXPORT_SYMBOL_GPL for rv_react(), rv_register_reactor(),
> and rv_unregister_reactor(), and the Kconfig entry is tristate.
Alright, sounds good.
> Additional fix: rv_unregister_reactor()
>
> Testing exposed a real bug: rv_unregister_reactor() called list_del()
> unconditionally. On a never-registered reactor the list_head is
> zero-initialised, so list_del() dereferences NULL->prev and crashes.
> v2 adds a patch that iterates rv_reactors_list first and only calls
> list_del() when the reactor is found. test_unregister_nonexistent
> documents and guards this behaviour.
Mmh, besides unit testing, when can we even see a reactor unregistration before
it's ever registered? Do we really need to actively guard against this?
What is probably an issue is to call unregistration also if registration failed
in reactors (which would cause the /bug/ you see, but shouldn't happen on
current reactors anyway), we can fix that.
I wouldn't guard against an issue that can only be triggered by broken code.
What am I missing?
Thanks,
Gabriele
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox