* [PATCH v3 03/11] dt-bindings: gpu: host1x: Document memory-regions for NVDEC
From: Thierry Reding @ 2026-07-01 16:08 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
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, Will Deacon
Cc: 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
In-Reply-To: <20260701-tegra-vpr-v3-0-d80f7b871bb4@nvidia.com>
From: Thierry Reding <treding@nvidia.com>
The video protection region is a reserved memory region that can be used
for secure video playback. NVDEC can access this region to decode images
into securely.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
.../devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml b/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml
index 4eb325cfd296..bcaaabca945d 100644
--- a/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml
+++ b/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml
@@ -60,6 +60,14 @@ properties:
- const: dma-mem
- const: write
+ memory-region:
+ items:
+ - description: reference to the video protection memory region
+
+ memory-region-names:
+ items:
+ - const: protected
+
nvidia,memory-controller:
$ref: /schemas/types.yaml#/definitions/phandle
description:
--
2.54.0
^ permalink raw reply related
* [PATCH v3 02/11] dt-bindings: display: tegra: Document memory regions
From: Thierry Reding @ 2026-07-01 16:08 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
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, Will Deacon
Cc: 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
In-Reply-To: <20260701-tegra-vpr-v3-0-d80f7b871bb4@nvidia.com>
From: Thierry Reding <treding@nvidia.com>
Add the memory-region and memory-region-names properties to the bindings
for the display controllers and the host1x engine found on various Tegra
generations. These memory regions are used to access firmware-provided
framebuffer memory as well as the video protection region.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v3:
- document properties for VIC
---
.../devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml | 8 ++++++++
.../devicetree/bindings/display/tegra/nvidia,tegra186-dc.yaml | 10 ++++++++++
.../devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml | 10 +++++++++-
.../bindings/display/tegra/nvidia,tegra20-host1x.yaml | 7 +++++++
4 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml
index 7200095ef19e..1e27a731ad9a 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml
@@ -67,6 +67,14 @@ properties:
- const: dma-mem # read
- const: write
+ memory-region:
+ items:
+ - description: reference to the video protection memory region
+
+ memory-region-names:
+ items:
+ - const: protected
+
dma-coherent: true
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra186-dc.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra186-dc.yaml
index ce4589466a18..881bfbf4764d 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra186-dc.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra186-dc.yaml
@@ -57,6 +57,16 @@ properties:
- const: dma-mem # read-0
- const: read-1
+ memory-region:
+ minItems: 1
+ maxItems: 2
+
+ memory-region-names:
+ items:
+ enum: [ framebuffer, protected ]
+ minItems: 1
+ maxItems: 2
+
nvidia,outputs:
description: A list of phandles of outputs that this display
controller can drive.
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml
index 69be95afd562..a012644eeb7d 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml
@@ -65,7 +65,15 @@ properties:
items:
- description: phandle to the core power domain
- memory-region: true
+ memory-region:
+ minItems: 1
+ maxItems: 2
+
+ memory-region-names:
+ items:
+ enum: [ framebuffer, protected ]
+ minItems: 1
+ maxitems: 2
nvidia,head:
$ref: /schemas/types.yaml#/definitions/uint32
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
index 3563378a01af..f45be30835a8 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
@@ -96,6 +96,13 @@ properties:
items:
- description: phandle to the HEG or core power domain
+ memory-region:
+ maxItems: 1
+
+ memory-region-names:
+ items:
+ - const: protected
+
required:
- compatible
- interrupts
--
2.54.0
^ permalink raw reply related
* [PATCH v3 01/11] dt-bindings: reserved-memory: Document Tegra VPR
From: Thierry Reding @ 2026-07-01 16:08 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
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, Will Deacon
Cc: 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
In-Reply-To: <20260701-tegra-vpr-v3-0-d80f7b871bb4@nvidia.com>
From: Thierry Reding <treding@nvidia.com>
The Video Protection Region (VPR) found on NVIDIA Tegra chips is a
region of memory that is protected from CPU accesses. It is used to
decode and play back DRM protected content.
It is a standard reserved memory region that can exist in two forms:
static VPR where the base address and size are fixed (uses the "reg"
property to describe the memory) and a resizable VPR where only the
size is known upfront and the OS can allocate it wherever it can be
accomodated.
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v2:
- add examples for fixed and resizable VPR
---
.../nvidia,tegra-video-protection-region.yaml | 76 ++++++++++++++++++++++
1 file changed, 76 insertions(+)
diff --git a/Documentation/devicetree/bindings/reserved-memory/nvidia,tegra-video-protection-region.yaml b/Documentation/devicetree/bindings/reserved-memory/nvidia,tegra-video-protection-region.yaml
new file mode 100644
index 000000000000..1c524bae9ce3
--- /dev/null
+++ b/Documentation/devicetree/bindings/reserved-memory/nvidia,tegra-video-protection-region.yaml
@@ -0,0 +1,76 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/reserved-memory/nvidia,tegra-video-protection-region.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NVIDIA Tegra Video Protection Region (VPR)
+
+maintainers:
+ - Thierry Reding <thierry.reding@gmail.com>
+ - Jon Hunter <jonathanh@nvidia.com>
+
+description: |
+ NVIDIA Tegra chips have long supported a mechanism to protect a single,
+ contiguous memory region from non-secure memory accesses. Typically this
+ region is used for decoding and playback of DRM protected content. Various
+ devices, such as the display controller and multimedia engines (video
+ decoder) can access this region in a secure way. Access from the CPU is
+ generally forbidden.
+
+ Two variants exist for VPR: one is fixed in both the base address and size,
+ while the other is resizable. Fixed VPR can be described by just a "reg"
+ property specifying the base address and size, whereas the resizable VPR
+ is defined by a size/alignment pair of properties. For resizable VPR the
+ memory is reusable by the rest of the system when it's unused for VPR and
+ therefore the "reusable" property must be specified along with it. For a
+ fixed VPR, the memory is permanently protected, and therefore it's not
+ reusable and must also be marked as "no-map" to prevent any (including
+ speculative) accesses to it.
+
+allOf:
+ - $ref: reserved-memory.yaml
+
+properties:
+ compatible:
+ const: nvidia,tegra-video-protection-region
+
+dependencies:
+ size: [alignment, reusable]
+ alignment: [size, reusable]
+ reusable: [alignment, size]
+
+ reg: [no-map]
+ no-map: [reg]
+
+unevaluatedProperties: false
+
+oneOf:
+ - required:
+ - compatible
+ - reg
+
+ - required:
+ - compatible
+ - size
+
+examples:
+ - |
+ /* resizable VPR */
+ protected {
+ compatible = "nvidia,tegra-video-protection-region";
+
+ size = <0x0 0x70000000>;
+ alignment = <0x0 0x100000>;
+ reusable;
+ };
+
+ - |
+ /* fixed VPR */
+ protected@2a8000000 {
+ compatible = "nvidia,tegra-video-protection-region";
+
+ /* fixed VPR */
+ reg = <0x2 0xa8000000 0x0 0x70000000>;
+ no-map;
+ };
--
2.54.0
^ permalink raw reply related
* [PATCH v3 00/11] dma-buf: heaps: Add support for Tegra VPR
From: Thierry Reding @ 2026-07-01 16:08 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
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, Will Deacon
Cc: 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
This series adds support for the video protection region (VPR) used on
Tegra SoC devices. It's a special region of memory that is protected
from accesses by the CPU and used to store DRM protected content (both
decrypted stream data as well as decoded video frames).
Patches 1 through 3 add DT binding documentation for the VPR and add the
VPR to the list of memory-region items for display, host1x and NVDEC.
New set_memory_device() and set_memory_normal() helpers are defined in
patch 4 and will subsequently be used to set the memory type of the VPR
to make sure it won't be accessed by the CPU once it's made part of the
protected region.
Patch 5 adds bitmap_allocate(), which is like bitmap_allocate_region()
but works on sizes that are not a power of two.
Patch 6 introduces new APIs needed by the Tegra VPR implementation that
allow CMA areas to be dynamically created at runtime rather than using
the fixed, system-wide list. This is used in this driver specifically
because it can use an arbitrary number of these areas (though they are
currently limited to 4).
Patch 7 adds some infrastructure for DMA heap implementations to provide
information through debugfs.
The Tegra VPR implementation is added in patch 8. See its commit message
for more details about the specifics of this implementation.
Finally, patches 9-11 add the VPR placeholder node on Tegra234 and
Tegra264 and hook it up to the host1x node so that it can make use of
this region.
Changes in v3:
- Link to v2: https://patch.msgid.link/20260122161009.3865888-1-thierry.reding@kernel.org
- introduce set_memory_device() and set_memory_normal()
- rename VPR nodes to "protected"
- add Tegra264 placeholder nodes
Changes in v2:
- Link to v1: https://patch.msgid.link/20250902154630.4032984-1-thierry.reding@gmail.com
- Tegra VPR implementation is now more optimized to reduce the number of
(very slow) resize operations, and allows cross-chunk allocations
- dynamic CMA areas are now trackd separately from static ones, but the
global number of CMA pages accounts for all areas
Thierry
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Chun Ng (1):
arm64/mm: Add set_memory_device() and set_memory_normal()
Thierry Reding (10):
dt-bindings: reserved-memory: Document Tegra VPR
dt-bindings: display: tegra: Document memory regions
dt-bindings: gpu: host1x: Document memory-regions for NVDEC
bitmap: Add bitmap_allocate() function
mm/cma: Allow dynamically creating CMA areas
dma-buf: heaps: Add debugfs support
dma-buf: heaps: Add support for Tegra VPR
arm64: tegra: Add VPR placeholder node on Tegra234
arm64: tegra: Hook up VPR to host1x
arm64: tegra: Add VPR placeholder node on Tegra264
.../display/tegra/nvidia,tegra124-vic.yaml | 8 +
.../bindings/display/tegra/nvidia,tegra186-dc.yaml | 10 +
.../bindings/display/tegra/nvidia,tegra20-dc.yaml | 10 +-
.../display/tegra/nvidia,tegra20-host1x.yaml | 7 +
.../bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml | 8 +
.../nvidia,tegra-video-protection-region.yaml | 76 ++
arch/arm/mm/dma-mapping.c | 2 +-
arch/arm64/boot/dts/nvidia/tegra234.dtsi | 45 +
arch/arm64/boot/dts/nvidia/tegra264.dtsi | 33 +
arch/arm64/include/asm/set_memory.h | 2 +
arch/arm64/mm/pageattr.c | 16 +
arch/s390/mm/init.c | 2 +-
drivers/dma-buf/dma-heap.c | 56 +
drivers/dma-buf/heaps/Kconfig | 7 +
drivers/dma-buf/heaps/Makefile | 1 +
drivers/dma-buf/heaps/tegra-vpr.c | 1242 ++++++++++++++++++++
include/linux/bitmap.h | 25 +-
include/linux/cma.h | 8 +-
include/linux/dma-heap.h | 2 +
include/linux/set_memory.h | 11 +
include/trace/events/tegra_vpr.h | 57 +
kernel/dma/contiguous.c | 2 +-
mm/cma.c | 187 ++-
mm/cma.h | 5 +-
24 files changed, 1775 insertions(+), 47 deletions(-)
---
base-commit: 703daa6d046136affd69f2a2e08f36ac4a7d5b2c
change-id: 20260507-tegra-vpr-cd4bc2509c4c
Best regards,
--
Thierry Reding <treding@nvidia.com>
^ permalink raw reply
* Re: [PATCH v8 13/46] KVM: guest_memfd: Add base support for KVM_SET_MEMORY_ATTRIBUTES2
From: Sean Christopherson @ 2026-07-01 15:35 UTC (permalink / raw)
To: Ackerley Tng
Cc: aik, andrew.jones, binbin.wu, brauner, chao.p.peng, david,
jmattson, jthoughton, michael.roth, oupton, pankaj.gupta, qperret,
rick.p.edgecombe, rientjes, shivankg, steven.price, tabba, willy,
wyihan, yan.y.zhao, forkloop, pratyush, suzuki.poulose,
aneesh.kumar, liam, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Jonathan Corbet, Shuah Khan,
Shuah Khan, Vishal Annapurve, Andrew Morton, Chris Li,
Kairui Song, Kemeng Shi, Nhat Pham, Barry Song, Axel Rasmussen,
Yuanchu Xie, Wei Xu, Youngjun Park, Qi Zheng, Shakeel Butt,
Kiryl Shutsemau, Baoquan He, Jason Gunthorpe, Vlastimil Babka,
kvm, linux-kernel, linux-trace-kernel, linux-doc, linux-kselftest,
linux-mm, linux-coco
In-Reply-To: <20260618-gmem-inplace-conversion-v8-13-9d2959357853@google.com>
On Thu, Jun 18, 2026, Ackerley Tng wrote:
> Introduce base support for KVM_SET_MEMORY_ATTRIBUTES2 in guest_memfd, which
> just updates attributes tracked by guest_memfd.
>
> Validate input fields in general. Guard usage of KVM_SET_MEMORY_ATTRIBUTES2
> by making sure requested attributes are supported for this instance of kvm.
>
> A new KVM_SET_MEMORY_ATTRIBUTES2 is defined to support writes (unlike
Phrase this as a command using imperative mood. The wording is also weird,
because "support writes" makes it sound like it allows controlling WRITE attributes,
whereas what you mean by "support writes" is "allowing KVM to write back error
information to the struct without technically violating the semantics embedded
in the ioctl". It's doubly confusing because the macros use a different polarity:
IOW means userspace is writing, but this implicitly refers to IOW as "reads".
> KVM_SET_MEMORY_ATTRIBUTES) in addition to reads so it can provide error
> details to userspace. This will be used in a later patch.
>
> The two ioctls use their corresponding structs with no overlap, but
> backward compatibility is baked in for future support of
> KVM_SET_MEMORY_ATTRIBUTES2 and struct kvm_memory_attributes2 in the VM
> ioctl.
I don't understand what this paragraph is trying to say with respect to backwards
compatibility. It's a new ioctl and struct, there's no compatibility in sight.
E.g.
Add a new ioctl (and matching struct), KVM_SET_MEMORY_ATTRIBUTES2, using
the same base ioctl number (0xd2), but with R/W semantics for the kernel
instead of just read semantics. "Officially" documenting that KVM writes
to the payload will allow KVM to support partial/incremental conversions,
instead of all-or-nothing updates (which requires complex unwinding), by
recording the failing offset if an error occurs.
Opportunistically add a new struct as well, even though KVM could squeeze
the error offset into "struct kvm_memory_attributes", as there's no cost
to doing so in practice. Pad the struct with a pile of extra space to try
and avoid ending up with "struct kvm_memory_attributes3" in the future.
Use the same layout for the fields that common to version 1 of the struct,
e.g. to ease upgrading userspace, and to provide flexibility in KVM ever
adds support for KVM_SET_MEMORY_ATTRIBUTES2 at VM scope.
> The process of setting memory attributes is set up such that the later half
> will not fail due to allocation. Any necessary checks are performed before
> the point of no return.
Explain *why*. Readers can usually understand the "what" by reading the code,
but it's much harder to discern *why* things were done a certain way. Some things
go without saying, e.g. "validate input fields", but in that case, just drop the
changelog blurb (if we _weren't_ validating input, *that* would be interesting and
worth calling out).
> Co-developed-by: Vishal Annapurve <vannapurve@google.com>
> Signed-off-by: Vishal Annapurve <vannapurve@google.com>
> Co-developed-by: Sean Christoperson <seanjc@google.com>
> Signed-off-by: Sean Christoperson <seanjc@google.com>
> Reviewed-by: Fuad Tabba <tabba@google.com>
> Signed-off-by: Ackerley Tng <ackerleytng@google.com>
> ---
> include/uapi/linux/kvm.h | 13 ++++++
> virt/kvm/Kconfig | 1 +
> virt/kvm/guest_memfd.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++
> virt/kvm/kvm_main.c | 12 +++++
> 4 files changed, 142 insertions(+)
>
> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
> index 419011097fa8e..956877a6aab05 100644
> --- a/include/uapi/linux/kvm.h
> +++ b/include/uapi/linux/kvm.h
> @@ -1649,6 +1649,19 @@ struct kvm_memory_attributes {
> __u64 flags;
> };
>
> +#define KVM_SET_MEMORY_ATTRIBUTES2 _IOWR(KVMIO, 0xd2, struct kvm_memory_attributes2)
> +
> +struct kvm_memory_attributes2 {
> + union {
> + __u64 address;
> + __u64 offset;
> + };
> + __u64 size;
> + __u64 attributes;
> + __u64 flags;
> + __u64 reserved[12];
> +};
> +
> #define KVM_MEMORY_ATTRIBUTE_PRIVATE (1ULL << 3)
>
> #define KVM_CREATE_GUEST_MEMFD _IOWR(KVMIO, 0xd4, struct kvm_create_guest_memfd)
> diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig
> index 297e4399fbd49..cfa2c78ba5fb9 100644
> --- a/virt/kvm/Kconfig
> +++ b/virt/kvm/Kconfig
> @@ -102,6 +102,7 @@ config KVM_MMU_LOCKLESS_AGING
>
> config KVM_GUEST_MEMFD
> select XARRAY_MULTI
> + select KVM_MEMORY_ATTRIBUTES
> bool
>
> config HAVE_KVM_ARCH_GMEM_PREPARE
> diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
> index 65ce795c090d9..0d14548c1ed22 100644
> --- a/virt/kvm/guest_memfd.c
> +++ b/virt/kvm/guest_memfd.c
> @@ -541,11 +541,127 @@ bool kvm_gmem_is_private(struct kvm *kvm, gfn_t gfn)
> }
> EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_gmem_is_private);
>
> +/*
> + * Preallocate memory for attributes to be stored on a maple tree, pointed to
> + * by mas. Adjacent ranges with attributes identical to the new attributes
> + * will be merged. Also sets mas's bounds up for storing attributes.
> + *
> + * This maintains the invariant that ranges with the same attributes will
> + * always be merged.
> + */
> +static int kvm_gmem_mas_preallocate(struct ma_state *mas, u64 attributes,
> + pgoff_t start, size_t nr_pages)
> +{
> + pgoff_t end = start + nr_pages;
> + pgoff_t last = end - 1;
> + void *entry;
> +
> + /* Try extending range. entry is NULL on overflow/wrap-around. */
> + mas_set(mas, end);
> + entry = mas_find(mas, end);
> + if (entry && xa_to_value(entry) == attributes)
> + last = mas->last;
> +
> + if (start > 0) {
> + mas_set(mas, start - 1);
> + entry = mas_find(mas, start - 1);
> + if (entry && xa_to_value(entry) == attributes)
> + start = mas->index;
> + }
> +
> + mas_set_range(mas, start, last);
> + return mas_preallocate(mas, xa_mk_value(attributes), GFP_KERNEL);
> +}
> +
> +static int __kvm_gmem_set_attributes(struct inode *inode, pgoff_t start,
> + size_t nr_pages, uint64_t attrs)
> +{
> + struct address_space *mapping = inode->i_mapping;
> + struct gmem_inode *gi = GMEM_I(inode);
> + pgoff_t end = start + nr_pages;
> + struct maple_tree *mt;
> + struct ma_state mas;
> + int r;
> +
> + mt = &gi->attributes;
> +
> + filemap_invalidate_lock(mapping);
> +
> + mas_init(&mas, mt, start);
> + r = kvm_gmem_mas_preallocate(&mas, attrs, start, nr_pages);
> + if (r)
> + goto out;
> +
> + /*
> + * From this point on guest_memfd has performed necessary
> + * checks and can proceed to do guest-breaking changes.
> + */
> +
> + kvm_gmem_invalidate_start(inode, start, end);
> + mas_store_prealloc(&mas, xa_mk_value(attrs));
> + kvm_gmem_invalidate_end(inode, start, end);
> +out:
> + filemap_invalidate_unlock(mapping);
> + return r;
> +}
> +
> +static long kvm_gmem_set_attributes(struct file *file, void __user *argp)
> +{
> + struct gmem_file *f = file->private_data;
> + struct inode *inode = file_inode(file);
> + struct kvm_memory_attributes2 attrs;
> + size_t nr_pages;
> + pgoff_t index;
> + int i;
> +
> + if (copy_from_user(&attrs, argp, sizeof(attrs)))
> + return -EFAULT;
> +
> + if (attrs.flags)
> + return -EINVAL;
> + for (i = 0; i < ARRAY_SIZE(attrs.reserved); i++) {
> + if (attrs.reserved[i])
> + return -EINVAL;
> + }
> + if (!kvm_arch_has_private_mem(f->kvm))
> + return -EINVAL;
> + if (attrs.attributes & ~KVM_MEMORY_ATTRIBUTE_PRIVATE)
> + return -EINVAL;
> + if (attrs.size == 0 || attrs.offset + attrs.size < attrs.offset)
> + return -EINVAL;
> + if (!PAGE_ALIGNED(attrs.offset) || !PAGE_ALIGNED(attrs.size))
> + return -EINVAL;
> +
> + if (attrs.offset >= i_size_read(inode) ||
> + attrs.offset + attrs.size > i_size_read(inode))
> + return -EINVAL;
> +
> + nr_pages = attrs.size >> PAGE_SHIFT;
> + index = attrs.offset >> PAGE_SHIFT;
> + return __kvm_gmem_set_attributes(inode, index, nr_pages,
> + attrs.attributes);
> +}
> +
> +static long kvm_gmem_ioctl(struct file *file, unsigned int ioctl,
> + unsigned long arg)
> +{
> + switch (ioctl) {
> + case KVM_SET_MEMORY_ATTRIBUTES2:
> + if (!gmem_in_place_conversion)
> + return -ENOTTY;
> +
> + return kvm_gmem_set_attributes(file, (void __user *)arg);
> + default:
> + return -ENOTTY;
> + }
> +}
> +
> static struct file_operations kvm_gmem_fops = {
> .mmap = kvm_gmem_mmap,
> .open = generic_file_open,
> .release = kvm_gmem_release,
> .fallocate = kvm_gmem_fallocate,
> + .unlocked_ioctl = kvm_gmem_ioctl,
> };
>
> static int kvm_gmem_migrate_folio(struct address_space *mapping,
> diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
> index 01761f6e25d25..a08b518cdb175 100644
> --- a/virt/kvm/kvm_main.c
> +++ b/virt/kvm/kvm_main.c
> @@ -105,6 +105,18 @@ module_param(allow_unsafe_mappings, bool, 0444);
> bool __ro_after_init gmem_in_place_conversion = false;
> #endif
>
> +#define MEMORY_ATTRIBUTES_MATCH(one, two) \
Use the same terminology as the memory region asserts, i.e.
SANITY_CHECK_MEM_ATTRIBUTES_FIELD. MEMORY_ATTRIBUTES_MATCH() reads like a helper
that checks if the two objects have the same attributes.
And put the checks where it actually matters, i.e. in the case-statement for
KVM_SET_MEMORY_ATTRIBUTES (again, same as KVM_SET_USER_MEMORY_REGION). Because
the only reason it matters for KVM is if we want to add VM-scoped support for
KVM_SET_MEMORY_ATTRIBUTES2 in the future, at which point we'll want to use the
same overlay shenanigans that we did for KVM_SET_USER_MEMORY_REGION2.
> + static_assert(offsetof(struct kvm_memory_attributes, one) == \
> + offsetof(struct kvm_memory_attributes2, two)); \
And then once these are landed in function scope, use BUILD_BUG_ON() with a
do { ... } while (0).
> + static_assert(sizeof_field(struct kvm_memory_attributes, one) ==\
> + sizeof_field(struct kvm_memory_attributes2, two))
> +
> +/* Ensure the common parts of the two structs are identical. */
> +MEMORY_ATTRIBUTES_MATCH(address, address);
> +MEMORY_ATTRIBUTES_MATCH(size, size);
> +MEMORY_ATTRIBUTES_MATCH(attributes, attributes);
> +MEMORY_ATTRIBUTES_MATCH(flags, flags);
Please put these asserts in the location where the overlay matters. Actually, I
don't think we need to enforce this?
^ permalink raw reply
* Re: [PATCH v8 07/46] KVM: Rename memory attribute APIs to prepare for in-place gmem conversion
From: Sean Christopherson @ 2026-07-01 15:07 UTC (permalink / raw)
To: Xiaoyao Li
Cc: ackerleytng, aik, andrew.jones, binbin.wu, brauner, chao.p.peng,
david, jmattson, jthoughton, michael.roth, oupton, pankaj.gupta,
qperret, rick.p.edgecombe, rientjes, shivankg, steven.price,
tabba, willy, wyihan, yan.y.zhao, forkloop, pratyush,
suzuki.poulose, aneesh.kumar, liam, Paolo Bonzini,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Shuah Khan, Shuah Khan,
Vishal Annapurve, Andrew Morton, Chris Li, Kairui Song,
Kemeng Shi, Nhat Pham, Barry Song, Axel Rasmussen, Yuanchu Xie,
Wei Xu, Youngjun Park, Qi Zheng, Shakeel Butt, Kiryl Shutsemau,
Baoquan He, Jason Gunthorpe, Vlastimil Babka, kvm, linux-kernel,
linux-trace-kernel, linux-doc, linux-kselftest, linux-mm,
linux-coco
In-Reply-To: <e5876e41-a11a-4d5e-958f-9e247c19d387@intel.com>
On Wed, Jul 01, 2026, Xiaoyao Li wrote:
> On 7/1/2026 1:30 AM, Sean Christopherson wrote:
> > On Tue, Jun 30, 2026, Xiaoyao Li wrote:
> > > On 6/19/2026 8:31 AM, Ackerley Tng via B4 Relay wrote:
> > > > -bool kvm_range_has_memory_attributes(struct kvm *kvm, gfn_t start, gfn_t end,
> > > > - unsigned long mask, unsigned long attrs);
> > > > +bool kvm_range_has_vm_memory_attributes(struct kvm *kvm, gfn_t start, gfn_t end,
> > > > + unsigned long mask, unsigned long attrs);
> > > > bool kvm_arch_pre_set_memory_attributes(struct kvm *kvm,
> > > > struct kvm_gfn_range *range);
> > > > bool kvm_arch_post_set_memory_attributes(struct kvm *kvm,
> > >
> > > We have
> > >
> > > - kvm_pre_set_memory_attributes()
> > > - kvm_arch_pre_set_memory_attributes()
> > > - kvm_arch_post_set_memory_attributes()
> >
> > Yeah, that's probably for the best.
> >
> > > left, do they need to be renamed as well?
> > >
> > > then the interesting one is kvm_vm_set_mem_attributes(), which contains "vm"
> > > already while it means "vm ioctl". Do we need to rename it to
> > > kvm_vm_set_vm_mem_attributes()?
> >
> > I say "no" on this last one, the fact that the function is scoped to a VM ioctl
> > is enough to communicate that it applies to per-VM attributes.
> >
> > Actually, since it's a local helper, we could go with kvm_set_vm_mem_attributes()
> > to be consistent with the other functions. That just leaves
> > kvm_vm_ioctl_set_mem_attributes(), which I think it appropriately scoped.
>
> If we finally choose to rename kvm_vm_set_mem_attributes() to
> kvm_set_vm_mem_attributes(), I think the trace
> trace_kvm_vm_set_mem_attributes() needs to be renamed to keep it consistent?
Ya, good catch!
^ permalink raw reply
* Re: [PATCH 2/7] i2c: nomadik: optimize layout of struct nmk_i2c_dev
From: Dmitry Guzman @ 2026-07-01 13:46 UTC (permalink / raw)
To: Linus Walleij
Cc: Andi Shyti, Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
linux-i2c, linux-kernel, linux-trace-kernel, linux-arm-kernel
In-Reply-To: <CAD++jLmqtG-j=Q-Ctr-1-GKVqYzcwOwWUF6AYHcKf5aXUSKE5A@mail.gmail.com>
On Thu, 25 Jun 2026 00:36:49 +0200
Linus Walleij <linusw@kernel.org> wrote:
> Hi Dmitry,
>
> thanks for your patch!
>
> Also nice to see some kernel contributions directly from
> MobilEye!
Thanks for you review!
> > struct nmk_i2c_dev {
> > struct i2c_vendor_data *vendor;
> > @@ -206,13 +206,13 @@ struct nmk_i2c_dev {
> > u32 clk_freq;
> > unsigned char tft;
> > unsigned char rft;
>
> ^
> Maybe you want to take the opportunity to change these
> two into u8 if you're anyway changing the layout of this
> struct?
I'm waiting for review of patch 1 in the set. If I need to submit next
version of the patchset, I'll change these two unsigned chars, as well
as `unsigned char *buffer` in `struct i2c_nmk_client`, into u8.
Best Regards,
--
Dmitry Guzman <Dmitry.Guzman@mobileye.com>
^ permalink raw reply
* Re: [RFC 0/2] arm64: kprobes: Fix single-step fault and reentry handling
From: Pu Hu @ 2026-07-01 13:56 UTC (permalink / raw)
To: Masami Hiramatsu (Google)
Cc: catalin.marinas@arm.com, will@kernel.org, naveen@kernel.org,
davem@davemloft.net, yang@os.amperecomputing.com, Hongyan Xia,
Jiazi Li, ada.coupriediaz@arm.com,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org
In-Reply-To: <20260701224345.c3a215ece3660a0cbae67645@kernel.org>
On 7/1/2026 9:43 PM, Masami Hiramatsu wrote:
> On Wed, 1 Jul 2026 12:14:54 +0000
> Pu Hu <hupu@transsion.com> wrote:
>
>> From: hupu <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 makes kprobe_fault_handler() handle a fault in
>> KPROBE_HIT_SS/KPROBE_REENTER only when the faulting PC points at the
>> current kprobe's XOL instruction. Otherwise the fault is left to the
>> normal fault handling path.
>>
>> Patch 2 allows a kprobe hit in KPROBE_HIT_SS to be handled as a
>> recoverable one-level reentry. Only a hit while already in
>> KPROBE_REENTER remains unrecoverable.
>>
>> 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")
>
> Good catch!!
> The series looks good to me.
>
> Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
>
> But it should be reviewed by arm64 maintainers too.
>
> BTW, if you are "Pu Hu", the Signed-off-by tag should be
> "Pu Hu <...>" instead of "hupu <...>".
>
Hi Masami,
Thank you for your reply and Acked-by.
Yes, thanks for pointing this out. I will fix the author name and the
Signed-off-by tags to use a consistent name in the next version of the
patchset.
Thanks,
hupu
^ permalink raw reply
* Re: [PATCH v8 24/46] KVM: guest_memfd: Make in-place conversion the default\
From: Sean Christopherson @ 2026-07-01 13:53 UTC (permalink / raw)
To: Xiaoyao Li
Cc: Yan Zhao, Ackerley Tng, aik, andrew.jones, binbin.wu, brauner,
chao.p.peng, david, jmattson, jthoughton, michael.roth, oupton,
pankaj.gupta, qperret, rick.p.edgecombe, rientjes, shivankg,
steven.price, tabba, willy, wyihan, forkloop, pratyush,
suzuki.poulose, aneesh.kumar, liam, Paolo Bonzini,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Shuah Khan, Shuah Khan,
Vishal Annapurve, Andrew Morton, Chris Li, Kairui Song,
Kemeng Shi, Nhat Pham, Barry Song, Axel Rasmussen, Yuanchu Xie,
Wei Xu, Youngjun Park, Qi Zheng, Shakeel Butt, Kiryl Shutsemau,
Baoquan He, Jason Gunthorpe, Vlastimil Babka, kvm, linux-kernel,
linux-trace-kernel, linux-doc, linux-kselftest, linux-mm,
linux-coco
In-Reply-To: <25fdb77d-20f6-4b3a-8b3a-dbba0dc47046@intel.com>
On Wed, Jul 01, 2026, Xiaoyao Li wrote:
> On 6/27/2026 3:06 AM, Sean Christopherson wrote:
> > On Fri, Jun 26, 2026, Yan Zhao wrote:
> > > My first impression of gmem_in_place_conversion=true was that it enforces gmem
> > > in-place conversion. However, it actually only enforces per-gmem private/shared
> > > attribute.
> > > My worry was that people might think it's a kernel bug if userspace can still
> > > have shared memory from other sources after they configured
> > > gmem_in_place_conversion=true.
> > Ah, I see where you're coming from. FWIW, truly enforcing in-place conversion
> > is flat out impossible. E.g. userspace can simply replace the memslot, at which
> > point the memory effectively reverts to shared.
>
> would something like below enforce the in-place conversion?
No.
> Userspace can create a memslot without gmem fd, but that memslot can only
> serve as shared memory and cannot be converted. So it doesn't violate the
> in-place conversion.
But userspace can delete said memslot and replace it with a memslot pointing at
a guest_memfd instance that was created without INIT_SHARED, at which point
userspace has effected a shared=>private conversion.
^ permalink raw reply
* [RFC PATCH 4/4] selftests/ftrace: Add a test for eBPF compiled fetchargs
From: Masami Hiramatsu (Google) @ 2026-07-01 13:46 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Shuah Khan
Cc: Mathieu Desnoyers, linux-kernel, linux-trace-kernel,
linux-kselftest, bpf
In-Reply-To: <178291352217.1566898.14481561093843379745.stgit@devnote2>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Add a selftest for trace probe BPF compilation to verify that BPF is
correctly compiled and executes properly for valid configurations,
and falls back to interpreter on unsupported cases.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
---
.../ftrace/test.d/dynevent/test_bpf_fetchargs.tc | 51 ++++++++++++++++++++
1 file changed, 51 insertions(+)
create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/test_bpf_fetchargs.tc
diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/test_bpf_fetchargs.tc b/tools/testing/selftests/ftrace/test.d/dynevent/test_bpf_fetchargs.tc
new file mode 100644
index 000000000000..6c6f4dd4517a
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/dynevent/test_bpf_fetchargs.tc
@@ -0,0 +1,51 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: Dynamic event - test eBPF compiled fetchargs for tprobe and fprobe
+# requires: dynamic_events "t[:[<group>/][<event>]] <tracepoint> [<args>]":README
+
+# Check if the sample module is loaded
+if ! lsmod | grep -q trace_events_sample; then
+ modprobe trace-events-sample || exit_unsupported
+fi
+
+echo 0 > events/enable
+echo > dynamic_events
+clear_trace
+
+# Add a tprobe to sample-trace:foo_bar
+# foo_bar args: const char *foo, int bar, ...
+# So $arg1 is char ptr (points to "hello"), $arg2 is int.
+# Let's test REG (arg2) and DEREF (+0($arg1))
+# "hello" in little-endian 32-bit: 'h'(0x68), 'e'(0x65), 'l'(0x6c), 'l'(0x6c) -> 0x6c6c6568 -> 1819043176
+echo "t:test_tprobe foo_bar mybar=\$arg2 myfoo=+0(\$arg1):u32" >> dynamic_events
+
+# Add an fprobe to __traceiter_foo_bar
+if grep -q "__traceiter_foo_bar" /proc/kallsyms; then
+ # __traceiter_foo_bar args: void *__data, const char *foo, int bar, ...
+ # $arg1 is __data, $arg2 is foo (points to "hello")
+ # "hello" in little-endian 32-bit: 'h'(0x68), 'e'(0x65), 'l'(0x6c), 'l'(0x6c) -> 0x6c6c6568 -> 1819043176
+ echo "f:test_fprobe __traceiter_foo_bar myfoo=\$arg2 myfmt=+0(\$arg2):u32" >> dynamic_events
+fi
+
+echo 1 > events/sample-trace/foo_bar/enable
+echo 1 > events/tracepoints/test_tprobe/enable
+if [ -d events/fprobes/test_fprobe ]; then
+ echo 1 > events/fprobes/test_fprobe/enable
+fi
+
+# Wait for 2 seconds to let the sample thread trigger the events
+sleep 2
+
+echo 0 > events/enable
+
+# Now verify the trace
+grep -q "test_tprobe.*myfoo=1819043176" trace || exit_fail
+
+if [ -d events/fprobes/test_fprobe ]; then
+ grep -q "test_fprobe.*myfmt=1819043176" trace || exit_fail
+fi
+
+echo > dynamic_events
+rmmod trace-events-sample
+
+exit 0
^ permalink raw reply related
* [RFC PATCH 3/4] tracing: Add disable_bpf trace option to ignore eBPF for fetchargs
From: Masami Hiramatsu (Google) @ 2026-07-01 13:45 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Shuah Khan
Cc: Mathieu Desnoyers, linux-kernel, linux-trace-kernel,
linux-kselftest, bpf
In-Reply-To: <178291352217.1566898.14481561093843379745.stgit@devnote2>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Add a trace option "disable_bpf" to disable BPF execution for fetchargs,
forcing the execution to fallback to the interpreter loop. This is useful
for evaluating BPF compilation performance impact.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
---
kernel/trace/trace.c | 7 +++++++
kernel/trace/trace.h | 8 ++++++++
kernel/trace/trace_probe_tmpl.h | 2 +-
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index c9e182d40059..7c0f7b629fcb 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -9940,6 +9940,13 @@ struct trace_array *trace_get_global_array(void)
}
#endif
+#ifdef CONFIG_BPF_SYSCALL
+bool trace_probe_bpf_disabled(void)
+{
+ return !!(global_trace.trace_flags & TRACE_ITER(DISABLE_BPF));
+}
+#endif
+
void __init early_trace_init(void)
{
if (tracepoint_printk) {
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 80fe152af1dd..bf83680e0ba7 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1503,6 +1503,7 @@ extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
C(PAUSE_ON_TRACE, "pause-on-trace"), \
C(HASH_PTR, "hash-ptr"), /* Print hashed pointer */ \
C(BITMASK_LIST, "bitmask-list"), \
+ C(DISABLE_BPF, "disable_bpf"), \
FUNCTION_FLAGS \
FGRAPH_FLAGS \
STACK_FLAGS \
@@ -2505,4 +2506,11 @@ static inline int rv_init_interface(void)
_args; \
})
+#ifdef CONFIG_BPF_SYSCALL
+bool trace_probe_bpf_disabled(void);
+#else
+static inline bool trace_probe_bpf_disabled(void) { return false; }
+#endif
+
#endif /* _LINUX_KERNEL_TRACE_H */
+
diff --git a/kernel/trace/trace_probe_tmpl.h b/kernel/trace/trace_probe_tmpl.h
index 6ca2dfe59a0f..015208aefbaf 100644
--- a/kernel/trace/trace_probe_tmpl.h
+++ b/kernel/trace/trace_probe_tmpl.h
@@ -274,7 +274,7 @@ store_trace_args(void *data, struct trace_probe *tp, void *rec, void *edata,
int ret, i;
#ifdef CONFIG_BPF_SYSCALL
- if (tp->prog) {
+ if (tp->prog && !trace_probe_bpf_disabled()) {
struct fetch_bpf_ctx ctx = {
.rec = rec,
.edata = edata,
^ permalink raw reply related
* [RFC PATCH 2/4] tracing/probes: Compile all fetchargs into a single BPF program per event
From: Masami Hiramatsu (Google) @ 2026-07-01 13:45 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Shuah Khan
Cc: Mathieu Desnoyers, linux-kernel, linux-trace-kernel,
linux-kselftest, bpf
In-Reply-To: <178291352217.1566898.14481561093843379745.stgit@devnote2>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Compile all fetch arguments of a trace probe event into a single BPF
program instead of separate programs per argument to reduce prologue
and dispatching overhead.
BPF-compatible arguments (such as register, immediate, dereferences,
and raw stores) are compiled, including registers mapping for x86_64,
arm64, and s390. If any argument requires non-BPF operations (such as
dynamic strings), we fallback to the interpreter loop for all arguments.
Also, correctly initialize prog->len to prevent invalid opcode execution in
the BPF interpreter.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
---
kernel/trace/trace_probe.c | 249 ++++++++++++++++++++++++++++++++++++++-
kernel/trace/trace_probe.h | 15 ++
kernel/trace/trace_probe_tmpl.h | 13 ++
3 files changed, 273 insertions(+), 4 deletions(-)
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index 18c212122344..0deb53c22ae3 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -2003,11 +2003,208 @@ static char *generate_probe_arg_name(const char *arg, int idx)
return name;
}
+#ifdef CONFIG_BPF_SYSCALL
+#include <linux/filter.h>
+#include <linux/uaccess.h>
+
+static int regs_get_kernel_argument_offset(unsigned int n)
+{
+#ifdef CONFIG_X86_64
+ static const int argument_offsets[] = {
+ offsetof(struct pt_regs, di),
+ offsetof(struct pt_regs, si),
+ offsetof(struct pt_regs, dx),
+ offsetof(struct pt_regs, cx),
+ offsetof(struct pt_regs, r8),
+ offsetof(struct pt_regs, r9),
+ };
+ if (n < ARRAY_SIZE(argument_offsets))
+ return argument_offsets[n];
+#elif defined(CONFIG_ARM64)
+ if (n < 8)
+ return offsetof(struct pt_regs, regs[n]);
+#elif defined(CONFIG_S390)
+ if (n < 5)
+ return offsetof(struct pt_regs, gprs[2 + n]);
+#endif
+ return -1;
+}
+
+static bool trace_probe_can_compile_bpf(struct trace_probe *tp)
+{
+ int i;
+
+ if (tp->nr_args == 0)
+ return false;
+
+ for (i = 0; i < tp->nr_args; i++) {
+ struct probe_arg *parg = &tp->args[i];
+ struct fetch_insn *code = parg->code;
+
+ while (code->op != FETCH_OP_END) {
+ switch (code->op) {
+ case FETCH_OP_REG:
+ case FETCH_OP_IMM:
+ case FETCH_OP_DEREF:
+ case FETCH_OP_ST_RAW:
+ case FETCH_OP_ST_MEM:
+ break;
+ case FETCH_OP_ARG:
+ if (regs_get_kernel_argument_offset(code->param) < 0)
+ return false;
+ break;
+ default:
+ return false;
+ }
+ code++;
+ }
+ }
+ return true;
+}
+
+static void trace_probe_compile_bpf(struct trace_probe *tp)
+{
+ struct bpf_insn *insns;
+ int i = 0;
+ struct bpf_prog *prog;
+ int err, idx;
+
+ if (!trace_probe_can_compile_bpf(tp))
+ return;
+
+ insns = kmalloc_array(512, sizeof(struct bpf_insn), GFP_KERNEL);
+ if (!insns)
+ return;
+
+ /* Prologue: R6 = ctx */
+ insns[i++] = BPF_MOV64_REG(BPF_REG_6, BPF_REG_1);
+ /* R7 = ctx->rec */
+ insns[i++] = BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6,
+ offsetof(struct fetch_bpf_ctx, rec));
+ /* R8 = ctx->data */
+ insns[i++] = BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_6,
+ offsetof(struct fetch_bpf_ctx, data));
+ /* R9 = total size (0) */
+ insns[i++] = BPF_MOV64_IMM(BPF_REG_9, 0);
+
+ for (idx = 0; idx < tp->nr_args; idx++) {
+ struct probe_arg *parg = &tp->args[idx];
+ struct fetch_insn *code = parg->code;
+
+ while (code->op != FETCH_OP_END && i < 500) {
+ switch (code->op) {
+ case FETCH_OP_REG:
+ /* R0 = *(unsigned long *)(R7 + code->param) */
+ insns[i++] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_7, code->param);
+ break;
+ case FETCH_OP_ARG: {
+ int offset = regs_get_kernel_argument_offset(code->param);
+ /* R0 = *(unsigned long *)(R7 + offset) */
+ insns[i++] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_7, offset);
+ break;
+ }
+ case FETCH_OP_IMM:
+ insns[i++] = BPF_LD_IMM64(BPF_REG_0, code->immediate);
+ break;
+ case FETCH_OP_DEREF:
+ /* Add offset: R3 = R0 + code->offset (src) */
+ insns[i++] = BPF_MOV64_REG(BPF_REG_2, BPF_REG_0);
+ if (code->offset)
+ insns[i++] = BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
+ code->offset);
+ /* R1 = dst (R10 - 8 on stack) */
+ insns[i++] = BPF_MOV64_REG(BPF_REG_1, BPF_REG_10);
+ insns[i++] = BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8);
+ /* R3 = size */
+ insns[i++] = BPF_MOV64_IMM(BPF_REG_3, sizeof(unsigned long));
+ /* Call copy_from_kernel_nofault(dst, src, size) */
+ insns[i++] = BPF_EMIT_CALL(copy_from_kernel_nofault);
+ /* if (R0 < 0) return R0; */
+ insns[i++] = BPF_JMP_IMM(BPF_JSGE, BPF_REG_0, 0, 1);
+ insns[i++] = BPF_EXIT_INSN();
+ /* R0 = *(unsigned long *)(R10 - 8) */
+ insns[i++] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8);
+ break;
+ case FETCH_OP_ST_RAW:
+ /* Store R0 into R8 (data) + parg->offset based on size */
+ switch (code->size) {
+ case 1:
+ insns[i++] = BPF_STX_MEM(BPF_B, BPF_REG_8, BPF_REG_0,
+ parg->offset);
+ break;
+ case 2:
+ insns[i++] = BPF_STX_MEM(BPF_H, BPF_REG_8, BPF_REG_0,
+ parg->offset);
+ break;
+ case 4:
+ insns[i++] = BPF_STX_MEM(BPF_W, BPF_REG_8, BPF_REG_0,
+ parg->offset);
+ break;
+ case 8:
+ insns[i++] = BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_0,
+ parg->offset);
+ break;
+ }
+ break;
+ case FETCH_OP_ST_MEM:
+ /* Add offset: R2 = R0 + code->offset (src) */
+ insns[i++] = BPF_MOV64_REG(BPF_REG_2, BPF_REG_0);
+ if (code->offset)
+ insns[i++] = BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
+ code->offset);
+ /* R1 = dst (R8 + parg->offset) */
+ insns[i++] = BPF_MOV64_REG(BPF_REG_1, BPF_REG_8);
+ if (parg->offset)
+ insns[i++] = BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
+ parg->offset);
+ /* R3 = size */
+ insns[i++] = BPF_MOV64_IMM(BPF_REG_3, code->size);
+ /* Call copy_from_kernel_nofault(dst, src, size) */
+ insns[i++] = BPF_EMIT_CALL(copy_from_kernel_nofault);
+ /* if (R0 < 0) return R0; */
+ insns[i++] = BPF_JMP_IMM(BPF_JSGE, BPF_REG_0, 0, 1);
+ insns[i++] = BPF_EXIT_INSN();
+ break;
+ default:
+ goto out;
+ }
+ code++;
+ }
+ }
+
+ if (i >= 500)
+ goto out;
+
+ /* Epilogue: return R9 (0) */
+ insns[i++] = BPF_MOV64_REG(BPF_REG_0, BPF_REG_9);
+ insns[i++] = BPF_EXIT_INSN();
+
+ prog = bpf_prog_alloc(bpf_prog_size(i), 0);
+ if (!prog)
+ goto out;
+
+ prog->len = i;
+ memcpy(prog->insnsi, insns, prog->len * sizeof(struct bpf_insn));
+ prog->type = BPF_PROG_TYPE_KPROBE;
+
+ prog = bpf_prog_select_runtime(prog, &err);
+ if (IS_ERR(prog))
+ goto out;
+ tp->prog = prog;
+
+out:
+ kfree(insns);
+}
+#endif
+
+/* Parse an argument */
+/* The caller must pass a null-terminated argument string */
int traceprobe_parse_probe_arg(struct trace_probe *tp, int i, const char *arg,
struct traceprobe_parse_context *ctx)
{
struct probe_arg *parg = &tp->args[i];
const char *body;
+ int ret;
ctx->tp = tp;
body = strchr(arg, '=');
@@ -2038,7 +2235,11 @@ int traceprobe_parse_probe_arg(struct trace_probe *tp, int i, const char *arg,
}
ctx->offset = body - arg;
/* Parse fetch argument */
- return traceprobe_parse_probe_arg_body(body, &tp->size, parg, ctx);
+ ret = traceprobe_parse_probe_arg_body(body, &tp->size, parg, ctx);
+ if (ret)
+ return ret;
+
+ return 0;
}
void traceprobe_free_probe_arg(struct probe_arg *arg)
@@ -2443,6 +2644,13 @@ void trace_probe_cleanup(struct trace_probe *tp)
for (i = 0; i < tp->nr_args; i++)
traceprobe_free_probe_arg(&tp->args[i]);
+#ifdef CONFIG_BPF_SYSCALL
+ if (tp->prog) {
+ bpf_prog_put(tp->prog);
+ tp->prog = NULL;
+ }
+#endif
+
if (tp->entry_arg) {
kfree(tp->entry_arg);
tp->entry_arg = NULL;
@@ -2531,15 +2739,32 @@ int trace_probe_register_event_call(struct trace_probe *tp)
trace_probe_name(tp)))
return -EEXIST;
+#ifdef CONFIG_BPF_SYSCALL
+ trace_probe_compile_bpf(tp);
+#endif
+
ret = register_trace_event(&call->event);
- if (!ret)
- return -ENODEV;
+ if (!ret) {
+ ret = -ENODEV;
+ goto err_free_bpf;
+ }
ret = trace_add_event_call(call);
- if (ret)
+ if (ret) {
unregister_trace_event(&call->event);
+ goto err_free_bpf;
+ }
return ret;
+
+err_free_bpf:
+#ifdef CONFIG_BPF_SYSCALL
+ if (tp->prog) {
+ bpf_prog_put(tp->prog);
+ tp->prog = NULL;
+ }
+#endif
+ return ret;
}
int trace_probe_add_file(struct trace_probe *tp, struct trace_event_file *file)
@@ -2768,5 +2993,21 @@ void trace_probe_dump_args(struct seq_file *m, struct trace_probe *tp)
for (i = 0; i < tp->nr_args; i++)
trace_probe_dump_arg(m, &tp->args[i]);
+
+#ifdef CONFIG_BPF_SYSCALL
+ if (tp->prog) {
+ seq_printf(m, "# [BPF%s]:", tp->prog->jited ? "-JIT" : "");
+ for (i = 0; i < tp->prog->len; i++) {
+ struct bpf_insn *insn = &tp->prog->insnsi[i];
+
+ seq_printf(m, " %02x %02x %04x %08x", insn->code,
+ insn->dst_reg | (insn->src_reg << 4),
+ insn->off, insn->imm);
+ if (i < tp->prog->len - 1)
+ seq_putc(m, ',');
+ }
+ seq_putc(m, '\n');
+ }
+#endif
}
#endif /* CONFIG_PROBE_EVENTS_DUMP_FETCHARG */
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index e6268a8dc378..10589414451c 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -274,6 +274,9 @@ struct trace_probe {
ssize_t size; /* trace entry size */
unsigned int nr_args;
struct probe_entry_arg *entry_arg; /* This is only for return probe */
+#ifdef CONFIG_BPF_SYSCALL
+ struct bpf_prog *prog;
+#endif
struct probe_arg args[];
};
@@ -299,6 +302,7 @@ static inline void trace_probe_set_flag(struct trace_probe *tp,
smp_store_release(&tp->event->flags, tp->event->flags | flag);
}
+
static inline void trace_probe_clear_flag(struct trace_probe *tp,
unsigned int flag)
{
@@ -631,3 +635,14 @@ struct uprobe_dispatch_data {
struct trace_uprobe *tu;
unsigned long bp_addr;
};
+
+#ifdef CONFIG_BPF_SYSCALL
+#include <linux/filter.h>
+
+struct fetch_bpf_ctx {
+ void *rec;
+ void *edata;
+ void *data;
+ void *base;
+};
+#endif
diff --git a/kernel/trace/trace_probe_tmpl.h b/kernel/trace/trace_probe_tmpl.h
index 8db12f758fda..6ca2dfe59a0f 100644
--- a/kernel/trace/trace_probe_tmpl.h
+++ b/kernel/trace/trace_probe_tmpl.h
@@ -273,6 +273,19 @@ store_trace_args(void *data, struct trace_probe *tp, void *rec, void *edata,
u32 *dl; /* Data location */
int ret, i;
+#ifdef CONFIG_BPF_SYSCALL
+ if (tp->prog) {
+ struct fetch_bpf_ctx ctx = {
+ .rec = rec,
+ .edata = edata,
+ .data = data,
+ .base = base,
+ };
+ bpf_prog_run(tp->prog, &ctx);
+ return;
+ }
+#endif
+
for (i = 0; i < tp->nr_args; i++) {
arg = tp->args + i;
dl = data + arg->offset;
^ permalink raw reply related
* [RFC PATCH 1/4] tools/tracing: Add fetcharg performance micro-benchmark
From: Masami Hiramatsu (Google) @ 2026-07-01 13:45 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Shuah Khan
Cc: Mathieu Desnoyers, linux-kernel, linux-trace-kernel,
linux-kselftest, bpf
In-Reply-To: <178291352217.1566898.14481561093843379745.stgit@devnote2>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Add a benchmark test module (fetcharg_bench) and bench_fetcharg.sh script
to measure the execution overhead of fetchargs across kprobe, fprobe,
and eprobe configurations.
The benchmark runs for a baseline (no probe events), 0-arguments,
1-argument, 2-arguments, and 3-arguments configurations, calculating
the estimated overhead in nanoseconds. It also supports a --debug option
to dump registered dynamic events.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
---
tools/tracing/benchmark/Kbuild | 3
tools/tracing/benchmark/Makefile | 12 +
tools/tracing/benchmark/bench_fetcharg.sh | 195 ++++++++++++++++++++++++
tools/tracing/benchmark/fetcharg_bench.c | 98 ++++++++++++
tools/tracing/benchmark/fetcharg_bench_trace.h | 37 +++++
5 files changed, 345 insertions(+)
create mode 100644 tools/tracing/benchmark/Kbuild
create mode 100644 tools/tracing/benchmark/Makefile
create mode 100755 tools/tracing/benchmark/bench_fetcharg.sh
create mode 100644 tools/tracing/benchmark/fetcharg_bench.c
create mode 100644 tools/tracing/benchmark/fetcharg_bench_trace.h
diff --git a/tools/tracing/benchmark/Kbuild b/tools/tracing/benchmark/Kbuild
new file mode 100644
index 000000000000..4c31b26ca51c
--- /dev/null
+++ b/tools/tracing/benchmark/Kbuild
@@ -0,0 +1,3 @@
+obj-m += fetcharg_bench.o
+
+ccflags-y += -I$(src)
diff --git a/tools/tracing/benchmark/Makefile b/tools/tracing/benchmark/Makefile
new file mode 100644
index 000000000000..bf5b3cbaff03
--- /dev/null
+++ b/tools/tracing/benchmark/Makefile
@@ -0,0 +1,12 @@
+ifeq ($(O),)
+KDIR ?= /lib/modules/$(shell uname -r)/build
+else
+KDIR := $(O)
+endif
+MDIR := $(CURDIR)
+
+all:
+ $(MAKE) -C $(KDIR) M=$(MDIR) modules
+
+clean:
+ $(MAKE) -C $(KDIR) M=$(MDIR) clean
diff --git a/tools/tracing/benchmark/bench_fetcharg.sh b/tools/tracing/benchmark/bench_fetcharg.sh
new file mode 100755
index 000000000000..0b2a2b8a896e
--- /dev/null
+++ b/tools/tracing/benchmark/bench_fetcharg.sh
@@ -0,0 +1,195 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# description: Benchmark fetcharg performance (baseline vs kprobe vs fprobe vs eprobe)
+
+DEBUG=0
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --debug|-d)
+ DEBUG=1
+ shift
+ ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 [--debug|-d]"
+ exit 1
+ ;;
+ esac
+done
+
+DEBUGFS_MOUNT=$(grep ^debugfs /proc/mounts | awk '{print $2}')
+if [ -z "$DEBUGFS_MOUNT" ]; then
+ mount -t debugfs nodev /sys/kernel/debug
+ DEBUGFS_MOUNT="/sys/kernel/debug"
+fi
+
+TRACEFS_MOUNT=$(grep ^tracefs /proc/mounts | awk '{print $2}')
+if [ -z "$TRACEFS_MOUNT" ]; then
+ mount -t tracefs nodev /sys/kernel/tracing
+ TRACEFS_MOUNT="/sys/kernel/tracing"
+fi
+
+MOD_NAME="fetcharg_bench"
+MOD_FILE="./${MOD_NAME}.ko"
+
+if [ ! -f "$MOD_FILE" ]; then
+ echo "Module $MOD_FILE not found. Please run 'make' first."
+ exit 1
+fi
+
+rmmod $MOD_NAME 2>/dev/null
+insmod $MOD_FILE || { echo "Failed to load $MOD_FILE"; exit 1; }
+
+TRIGGER_FILE="${DEBUGFS_MOUNT}/fetcharg_benchmark/trigger"
+
+if [ ! -f "$TRIGGER_FILE" ]; then
+ echo "Trigger file $TRIGGER_FILE not found."
+ rmmod $MOD_NAME
+ exit 1
+fi
+
+DYN_EVENTS="${TRACEFS_MOUNT}/dynamic_events"
+
+# Helper to clear events
+clear_events() {
+ echo 0 > "${TRACEFS_MOUNT}/events/enable"
+ echo > "$DYN_EVENTS"
+}
+
+run_bench() {
+ if [ "$DEBUG" = "1" ]; then
+ echo "=== [DEBUG] dynamic_events ===" >&2
+ cat "$DYN_EVENTS" >&2
+ echo "==============================" >&2
+ fi
+ cat "$TRIGGER_FILE"
+}
+
+calc_overhead() {
+ local lps=$1
+ local base_lps=$2
+ if [ -z "$lps" ] || [ -z "$base_lps" ] || [ "$lps" = "-" ] || [ "$base_lps" = "-" ]; then
+ echo "-"
+ return
+ fi
+ awk -v lps="$lps" -v base_lps="$base_lps" 'BEGIN {
+ if (lps == 0 || base_lps == 0) {
+ print "-"
+ exit
+ }
+ t = 1000000000.0 / lps
+ t_base = 1000000000.0 / base_lps
+ diff = t - t_base
+ printf "%.2f ns", diff
+ }'
+}
+
+echo "Running Fetcharg Micro Benchmark..."
+echo "Please wait, this may take a few seconds..."
+
+# Baseline
+clear_events
+baseline=$(run_bench)
+
+# Kprobe
+clear_events
+echo "p:bench_kprobe fetcharg_bench_target" >> "$DYN_EVENTS"
+echo 1 > "${TRACEFS_MOUNT}/events/kprobes/bench_kprobe/enable"
+kprobe_0=$(run_bench)
+
+clear_events
+echo "p:bench_kprobe fetcharg_bench_target a=\$arg1" >> "$DYN_EVENTS"
+echo 1 > "${TRACEFS_MOUNT}/events/kprobes/bench_kprobe/enable"
+kprobe_1=$(run_bench)
+
+clear_events
+echo "p:bench_kprobe fetcharg_bench_target a=\$arg1 b=+0(+0(\$arg2)):u32" >> "$DYN_EVENTS"
+echo 1 > "${TRACEFS_MOUNT}/events/kprobes/bench_kprobe/enable"
+kprobe_2=$(run_bench)
+
+clear_events
+echo "p:bench_kprobe fetcharg_bench_target a=\$arg1 b=+0(+0(\$arg2)):u32 c=+0(\$arg3):u32" \
+ >> "$DYN_EVENTS"
+echo 1 > "${TRACEFS_MOUNT}/events/kprobes/bench_kprobe/enable"
+kprobe_3=$(run_bench)
+
+# Fprobe
+clear_events
+echo "f:bench_fprobe fetcharg_bench_target" >> "$DYN_EVENTS"
+echo 1 > "${TRACEFS_MOUNT}/events/fprobes/bench_fprobe/enable"
+fprobe_0=$(run_bench)
+
+clear_events
+echo "f:bench_fprobe fetcharg_bench_target a=\$arg1" >> "$DYN_EVENTS"
+echo 1 > "${TRACEFS_MOUNT}/events/fprobes/bench_fprobe/enable"
+fprobe_1=$(run_bench)
+
+clear_events
+echo "f:bench_fprobe fetcharg_bench_target a=\$arg1 b=+0(+0(\$arg2)):u32" >> "$DYN_EVENTS"
+echo 1 > "${TRACEFS_MOUNT}/events/fprobes/bench_fprobe/enable"
+fprobe_2=$(run_bench)
+
+clear_events
+echo "f:bench_fprobe fetcharg_bench_target a=\$arg1 b=+0(+0(\$arg2)):u32 c=+0(\$arg3):u32" \
+ >> "$DYN_EVENTS"
+echo 1 > "${TRACEFS_MOUNT}/events/fprobes/bench_fprobe/enable"
+fprobe_3=$(run_bench)
+
+# Eprobe
+clear_events
+echo "e:bench_eprobe fetcharg_bench/fetcharg_bench_event" >> "$DYN_EVENTS"
+echo 1 > "${TRACEFS_MOUNT}/events/eprobes/bench_eprobe/enable"
+echo 1 > "${TRACEFS_MOUNT}/events/fetcharg_bench/fetcharg_bench_event/enable"
+eprobe_0=$(run_bench)
+
+clear_events
+echo "e:bench_eprobe fetcharg_bench/fetcharg_bench_event a=\$a" >> "$DYN_EVENTS"
+echo 1 > "${TRACEFS_MOUNT}/events/eprobes/bench_eprobe/enable"
+echo 1 > "${TRACEFS_MOUNT}/events/fetcharg_bench/fetcharg_bench_event/enable"
+eprobe_1=$(run_bench)
+
+clear_events
+echo "e:bench_eprobe fetcharg_bench/fetcharg_bench_event a=\$a b=+0(+0(\$b_ptr)):u32" \
+ >> "$DYN_EVENTS"
+echo 1 > "${TRACEFS_MOUNT}/events/eprobes/bench_eprobe/enable"
+echo 1 > "${TRACEFS_MOUNT}/events/fetcharg_bench/fetcharg_bench_event/enable"
+eprobe_2=$(run_bench)
+
+clear_events
+echo "e:bench_eprobe fetcharg_bench/fetcharg_bench_event a=\$a b=+0(+0(\$b_ptr)):u32 c=+0(\$c_ptr):u32" \
+ >> "$DYN_EVENTS"
+echo 1 > "${TRACEFS_MOUNT}/events/eprobes/bench_eprobe/enable"
+echo 1 > "${TRACEFS_MOUNT}/events/fetcharg_bench/fetcharg_bench_event/enable"
+eprobe_3=$(run_bench)
+
+echo "--------------------------------------------------------------------------------"
+echo "Configuration 0 Fetchargs 1 Fetcharg 2 Fetchargs 3 Fetchargs"
+echo "--------------------------------------------------------------------------------"
+printf "%-18s %15s %15s %18s %18s loops/sec\n" "Baseline" "$baseline" "-" "-" "-"
+printf "%-18s %15s %15s %18s %18s overhead\n" " " "-" "-" "-" "-"
+printf "%-18s %15s %15s %18s %18s loops/sec\n" \
+ "Kprobe" "$kprobe_0" "$kprobe_1" "$kprobe_2" "$kprobe_3"
+printf "%-18s %15s %15s %18s %18s overhead\n" " " \
+ "$(calc_overhead $kprobe_0 $baseline)" \
+ "$(calc_overhead $kprobe_1 $kprobe_0)" \
+ "$(calc_overhead $kprobe_2 $kprobe_0)" \
+ "$(calc_overhead $kprobe_3 $kprobe_0)"
+printf "%-18s %15s %15s %18s %18s loops/sec\n" \
+ "Fprobe" "$fprobe_0" "$fprobe_1" "$fprobe_2" "$fprobe_3"
+printf "%-18s %15s %15s %18s %18s overhead\n" " " \
+ "$(calc_overhead $fprobe_0 $baseline)" \
+ "$(calc_overhead $fprobe_1 $fprobe_0)" \
+ "$(calc_overhead $fprobe_2 $fprobe_0)" \
+ "$(calc_overhead $fprobe_3 $fprobe_0)"
+printf "%-18s %15s %15s %18s %18s loops/sec\n" \
+ "Eprobe" "$eprobe_0" "$eprobe_1" "$eprobe_2" "$eprobe_3"
+printf "%-18s %15s %15s %18s %18s overhead\n" " " \
+ "$(calc_overhead $eprobe_0 $baseline)" \
+ "$(calc_overhead $eprobe_1 $eprobe_0)" \
+ "$(calc_overhead $eprobe_2 $eprobe_0)" \
+ "$(calc_overhead $eprobe_3 $eprobe_0)"
+echo "--------------------------------------------------------------------------------"
+
+clear_events
+rmmod $MOD_NAME
+exit 0
diff --git a/tools/tracing/benchmark/fetcharg_bench.c b/tools/tracing/benchmark/fetcharg_bench.c
new file mode 100644
index 000000000000..af18183c1f5d
--- /dev/null
+++ b/tools/tracing/benchmark/fetcharg_bench.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/module.h>
+#include <linux/ktime.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+
+#define CREATE_TRACE_POINTS
+#include "fetcharg_bench_trace.h"
+
+static noinline int fetcharg_bench_target(int a, int **b, char *c)
+{
+ /* Prevent compiler from optimizing the loop out entirely */
+ asm volatile ("" : : "r"(a), "r"(b), "r"(c) : "memory");
+ trace_fetcharg_bench_event(a, b, c);
+ return a + **b;
+}
+
+/* Indirect pointer to prevent inlining */
+static int (*bench_func_ptr)(int, int **, char *) = fetcharg_bench_target;
+
+#define BENCH_ITERATIONS 1000000
+
+static ssize_t fetcharg_bench_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[64];
+ int len;
+ u64 start, current_time;
+ u64 elapsed;
+ u64 loops_per_sec;
+ int dummy = 0;
+ int a = 1;
+ int b_val = 2;
+ int *b_ptr = &b_val;
+ int **b = &b_ptr;
+ char c[] = "benchmark";
+ int i;
+
+ if (*ppos > 0)
+ return 0; /* EOF */
+
+ start = ktime_get_ns();
+ for (i = 0; i < BENCH_ITERATIONS; i++)
+ dummy += bench_func_ptr(a, b, c);
+ current_time = ktime_get_ns();
+
+ elapsed = current_time - start;
+ loops_per_sec = ((u64)BENCH_ITERATIONS * NSEC_PER_SEC) / elapsed;
+
+ len = snprintf(buf, sizeof(buf), "%llu\n", loops_per_sec);
+ if (len < 0)
+ return len;
+
+ if (copy_to_user(user_buf, buf, len))
+ return -EFAULT;
+
+ *ppos += len;
+
+ /*
+ * Use 'dummy' to ensure the compiler doesn't optimize out
+ * the call completely, though the asm volatile helps too.
+ */
+ if (dummy == 0xdeadbeef)
+ pr_info("dummy=%d\n", dummy);
+
+ return len;
+}
+
+static const struct file_operations fetcharg_bench_fops = {
+ .read = fetcharg_bench_read,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
+static struct dentry *bench_dir;
+
+static int __init fetcharg_bench_init(void)
+{
+ bench_dir = debugfs_create_dir("fetcharg_benchmark", NULL);
+ if (!bench_dir)
+ return -ENOMEM;
+
+ debugfs_create_file("trigger", 0444, bench_dir, NULL, &fetcharg_bench_fops);
+
+ return 0;
+}
+
+static void __exit fetcharg_bench_exit(void)
+{
+ debugfs_remove_recursive(bench_dir);
+}
+
+module_init(fetcharg_bench_init);
+module_exit(fetcharg_bench_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antigravity");
+MODULE_DESCRIPTION("Fetcharg performance benchmark test module");
diff --git a/tools/tracing/benchmark/fetcharg_bench_trace.h b/tools/tracing/benchmark/fetcharg_bench_trace.h
new file mode 100644
index 000000000000..6560f62337e3
--- /dev/null
+++ b/tools/tracing/benchmark/fetcharg_bench_trace.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM fetcharg_bench
+
+#if !defined(_FETCHARG_BENCH_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _FETCHARG_BENCH_TRACE_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(fetcharg_bench_event,
+
+ TP_PROTO(int a, int **b, char *c),
+
+ TP_ARGS(a, b, c),
+
+ TP_STRUCT__entry(
+ __field(int, a)
+ __field(int **, b_ptr)
+ __field(char *, c_ptr)
+ ),
+
+ TP_fast_assign(
+ __entry->a = a;
+ __entry->b_ptr = b;
+ __entry->c_ptr = c;
+ ),
+
+ TP_printk("a=%d b=%p c=%p", __entry->a, __entry->b_ptr, __entry->c_ptr)
+);
+
+#endif /* _FETCHARG_BENCH_TRACE_H */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE fetcharg_bench_trace
+#include <trace/define_trace.h>
^ permalink raw reply related
* [RFC PATCH 0/4] tracing/probes: Optimize fetcharg with BPF
From: Masami Hiramatsu (Google) @ 2026-07-01 13:45 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Shuah Khan
Cc: Mathieu Desnoyers, linux-kernel, linux-trace-kernel,
linux-kselftest, bpf
Hi,
I investigated the feasibility of optimizing `fetcharg` in probe events
using BPF conversion. The result looks promising. It can reduce about
30% of overhead (and maybe more if we have more than 3 arguments.)
I actually thought there was not such a big difference because I guessed
major overhead source is unsafe pointer dereferencing (e.g.
copy_from_kernel_nofault()). Actually without CONFIG_BPF_JIT, the overhead
is more than double. But with the JIT compiler it showed better performance.
The basic concept is quite simple. The process remains the same up until
the point where user input is converted into `fetcharg` code. It is
possible to convert some of the fundamental `fetcharg` operations into
an equivalent sequence of BPF instructions. This creates a single
`bpf_prog` for each probe event (rather than one per argument).
This program executes within the event handler, reads `pt_regs` directly,
and stores the results in the ftrace ring buffer, just as `fetcharg`
does.
So here are the benchmark results on qemu (KVM) on Intel Core i7-8565U.
When enabling BPF with JIT:
--------------------------------------------------------------------------------
Configuration 0 Fetchargs 1 Fetcharg 2 Fetchargs 3 Fetchargs
--------------------------------------------------------------------------------
Baseline 298882359 - - - loops/sec
- - - - overhead
Kprobe 9740841 8664195 7944956 7608274 loops/sec
99.31 ns 12.76 ns 23.21 ns 28.78 ns overhead
Fprobe 10827749 9220918 7992512 7683757 loops/sec
89.01 ns 16.09 ns 32.76 ns 37.79 ns overhead
Eprobe 6746389 6245994 5319037 4845406 loops/sec
144.88 ns 11.88 ns 39.78 ns 58.15 ns overhead
--------------------------------------------------------------------------------
When enabling BPF without JIT:
-----------------------------------------------------------------------------------------------
Configuration 0 Fetchargs 1 Fetcharg 2 Fetchargs 3 Fetchargs
-----------------------------------------------------------------------------------------------
Baseline 84067374 - - - loops/sec
- - - - overhead
Kprobe 7092949 5834913 3848776 3443408 loops/sec
129.09 ns 30.40 ns 118.84 ns 149.42 ns overhead
Fprobe 9426302 6441734 4350313 3710814 loops/sec
94.19 ns 49.15 ns 123.78 ns 163.40 ns overhead
Eprobe 5681716 4958113 3940999 3953434 loops/sec
164.11 ns 25.69 ns 77.74 ns 76.94 ns overhead
-----------------------------------------------------------------------------------------------
When disabling BPF (legacy fetcharg)
--------------------------------------------------------------------------------
Configuration 0 Fetchargs 1 Fetcharg 2 Fetchargs 3 Fetchargs
--------------------------------------------------------------------------------
Baseline 245433525 - - - loops/sec
- - - - overhead
Kprobe 9055348 8488351 7219595 6453928 loops/sec
106.36 ns 7.38 ns 28.08 ns 44.51 ns overhead
Fprobe 10859326 9288801 7492518 6607046 loops/sec
88.01 ns 15.57 ns 41.38 ns 59.27 ns overhead
Eprobe 6987128 5114526 5055084 4803759 loops/sec
139.05 ns 52.40 ns 54.70 ns 65.05 ns overhead
--------------------------------------------------------------------------------
The number is still unstable (because of the benchmark problem) but the
trend shows the BPF+JIT is the winner.
TODOs:
- Add a new Kconfig which depends on CONFIG_BPF_JIT=y.
- Even if a single dereference operation fails, processing of subsequent
arguments continues.
- Allow mixing with unsupported FETCH_OPs on the same event.
Thank you,
---
base-commit: c0c56fe6fb52cfb28419242cfa6235125f818f94
Masami Hiramatsu (Google) (4):
tools/tracing: Add fetcharg performance micro-benchmark
tracing/probes: Compile all fetchargs into a single BPF program per event
tracing: Add disable_bpf trace option to ignore eBPF for fetchargs
selftests/ftrace: Add a test for eBPF compiled fetchargs
kernel/trace/trace.c | 7 +
kernel/trace/trace.h | 8 +
kernel/trace/trace_probe.c | 249 ++++++++++++++++++++
kernel/trace/trace_probe.h | 15 +
kernel/trace/trace_probe_tmpl.h | 13 +
.../ftrace/test.d/dynevent/test_bpf_fetchargs.tc | 51 ++++
tools/tracing/benchmark/Kbuild | 3
tools/tracing/benchmark/Makefile | 12 +
tools/tracing/benchmark/bench_fetcharg.sh | 195 ++++++++++++++++
tools/tracing/benchmark/fetcharg_bench.c | 98 ++++++++
tools/tracing/benchmark/fetcharg_bench_trace.h | 37 +++
11 files changed, 684 insertions(+), 4 deletions(-)
create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/test_bpf_fetchargs.tc
create mode 100644 tools/tracing/benchmark/Kbuild
create mode 100644 tools/tracing/benchmark/Makefile
create mode 100755 tools/tracing/benchmark/bench_fetcharg.sh
create mode 100644 tools/tracing/benchmark/fetcharg_bench.c
create mode 100644 tools/tracing/benchmark/fetcharg_bench_trace.h
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
* Re: [RFC 0/2] arm64: kprobes: Fix single-step fault and reentry handling
From: Masami Hiramatsu @ 2026-07-01 13:43 UTC (permalink / raw)
To: Pu Hu
Cc: catalin.marinas@arm.com, will@kernel.org, naveen@kernel.org,
davem@davemloft.net, yang@os.amperecomputing.com, Hongyan Xia,
Jiazi Li, ada.coupriediaz@arm.com,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org
In-Reply-To: <20260701121448.3926-1-hupu@transsion.com>
On Wed, 1 Jul 2026 12:14:54 +0000
Pu Hu <hupu@transsion.com> wrote:
> From: hupu <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 makes kprobe_fault_handler() handle a fault in
> KPROBE_HIT_SS/KPROBE_REENTER only when the faulting PC points at the
> current kprobe's XOL instruction. Otherwise the fault is left to the
> normal fault handling path.
>
> Patch 2 allows a kprobe hit in KPROBE_HIT_SS to be handled as a
> recoverable one-level reentry. Only a hit while already in
> KPROBE_REENTER remains unrecoverable.
>
> 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")
Good catch!!
The series looks good to me.
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
But it should be reviewed by arm64 maintainers too.
BTW, if you are "Pu Hu", the Signed-off-by tag should be
"Pu Hu <...>" instead of "hupu <...>".
Thank you,
>
> 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.
>
> hupu (2):
> arm64: kprobes: Do not handle non-XOL faults as kprobe faults
> arm64: kprobes: Allow reentering kprobes while single-stepping
>
> arch/arm64/kernel/probes/kprobes.c | 22 +++++++++++++++++++++-
> 1 file changed, 21 insertions(+), 1 deletion(-)
>
> --
> 2.43.0
>
>
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
* Re: [PATCH v7 0/9] bootconfig: embed kernel.* cmdline at build time
From: Breno Leitao @ 2026-07-01 13:18 UTC (permalink / raw)
To: Masami Hiramatsu
Cc: Andrew Morton, Nathan Chancellor, paulmck, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Jonathan Corbet,
Shuah Khan, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, linux-kernel,
linux-trace-kernel, linux-kbuild, bpf, llvm, linux-doc,
kernel-team, Nicolas Schier
In-Reply-To: <20260626233327.b5c9c8de494acdde4ddf5c02@kernel.org>
On Fri, Jun 26, 2026 at 11:33:27PM +0900, Masami Hiramatsu wrote:
> On Fri, 26 Jun 2026 05:50:09 -0700
> Breno Leitao <leitao@debian.org> wrote:
>
> > The userspace pieces (xbc_snprint_cmdline() in lib/, tools/bootconfig -C)
> > already landed; this series wires the rendered cmdline into the kernel.
> >
> > Motivation: today the embedded bootconfig is parsed at runtime, after
> > parse_early_param() has already run, so early_param() handlers can't
> > see embedded values. Folding the kernel.* subtree into the cmdline at
> > build time gives a CONFIG_CMDLINE-equivalent for embedded-bootconfig
> > users without forcing them to maintain two cmdline sources.
> >
> > Behaviorally, the "kernel" subtree is rendered to a flat string at
> > build time and stashed in .init.rodata. setup_arch() prepends it to
> > boot_command_line before parse_early_param() runs. Overflow is a soft
> > error: the helper logs and leaves boot_command_line untouched rather
> > than panicking, so an oversized embedded bconf cannot brick a boot.
> >
>
> Thanks for update!! This looks good to me.
> Let me pick it and test it.
Thanks Masami,
Let me know if you need anything else from my side.
Once these land in linux-next, I'll merge them into Meta's kernel tree
and put them into production. Looking forward to seeing how it works out.
Really appreciate all your support getting this across the line.
--breno
^ permalink raw reply
* Re: [PATCH v2 4/4] rv/rtapp: Add wakeup monitor
From: Gabriele Monaco @ 2026-07-01 13:11 UTC (permalink / raw)
To: Nam Cao; +Cc: Steven Rostedt, linux-trace-kernel, linux-doc, linux-kernel
In-Reply-To: <ba5658fa13e49ada466b84a2c211f233037180b5.1781852967.git.namcao@linutronix.de>
On Fri, 2026-06-19 at 09:21 +0200, Nam Cao wrote:
> Add a wakeup monitor to detect a lower-priority task waking up a
> higher-priority task.
>
> The rtapp/sleep monitor already detects this. However, that monitor
> triggers an error in the context of the wakee task and user only gets
> the stacktrace of that task. It is also extremely useful to get the
> stacktrace of the waker task, which this monitor offers. In other
> words, this monitor complements the rtapp/sleep monitor.
>
> Signed-off-by: Nam Cao <namcao@linutronix.de>
This looks good, but if I understand it correctly, the same violation should be
spotted by both monitors from two different perspectives, but sleep catches more
things (e.g. tasks using wrong sleeping ways despite their wakeup):
# perf stat -a -e rv:error_sleep -e rv:error_wakeup -- stress-ng --cpu 5 --cpu-load 90 --sched rr -t 5
Performance counter stats for 'system wide':
285 rv:error_sleep
20 rv:error_wakeup
Provided I don't really know what's happening down there (I just let the
stressor run free), this discrepancy is expected, right?
Thanks,
Gabriele
> ---
> Documentation/trace/rv/monitor_rtapp.rst | 20 +++
> kernel/trace/rv/Kconfig | 1 +
> kernel/trace/rv/Makefile | 1 +
> kernel/trace/rv/monitors/rtapp/Kconfig | 2 +-
> kernel/trace/rv/monitors/wakeup/Kconfig | 16 ++
> kernel/trace/rv/monitors/wakeup/wakeup.c | 153 ++++++++++++++++++
> kernel/trace/rv/monitors/wakeup/wakeup.h | 92 +++++++++++
> .../trace/rv/monitors/wakeup/wakeup_trace.h | 14 ++
> kernel/trace/rv/rv_trace.h | 1 +
> tools/verification/models/rtapp/wakeup.ltl | 5 +
> 10 files changed, 304 insertions(+), 1 deletion(-)
> create mode 100644 kernel/trace/rv/monitors/wakeup/Kconfig
> create mode 100644 kernel/trace/rv/monitors/wakeup/wakeup.c
> create mode 100644 kernel/trace/rv/monitors/wakeup/wakeup.h
> create mode 100644 kernel/trace/rv/monitors/wakeup/wakeup_trace.h
> create mode 100644 tools/verification/models/rtapp/wakeup.ltl
>
> diff --git a/Documentation/trace/rv/monitor_rtapp.rst
> b/Documentation/trace/rv/monitor_rtapp.rst
> index 502d3ea412eb..238b59395ff5 100644
> --- a/Documentation/trace/rv/monitor_rtapp.rst
> +++ b/Documentation/trace/rv/monitor_rtapp.rst
> @@ -124,3 +124,23 @@ to handle some special cases:
> real-time-safe because preemption is disabled for the duration.
> - `FUTEX_LOCK_PI` is included in the allowlist for the same reason as
> `BLOCK_ON_RT_MUTEX`.
> +
> +Monitor wakeup
> +++++++++++++++
> +
> +The `wakeup` monitor reports real-time threads being woken by lower-priority
> threads,
> +which is a hint of priority inversion. Its specification is::
> +
> + RULE = always (((RT and USER_THREAD) imply
> + (not (WOKEN_BY_LOWER_PRIO or WOKEN_BY_SOFTIRQ)) or
> ALLOWLIST))
> +
> + ALLOWLIST = BLOCK_ON_RT_MUTEX
> + or FUTEX_LOCK_PI
> +
> +The `sleep` monitor already reports this type of problem. The difference is
> the
> +context in which the problem is reported. While the `sleep` monitor reports
> the problem
> +in the context of the wakee, this `wakeup` monitor reports the problem in the
> context of
> +the waker. This monitor complement the `sleep` monitor, giving user better
> +understanding of the issue. For instance, to debug a lower-priority task
> waking a
> +higher-priority task scenario, user can enable both `wakeup` monitor and
> `sleep`
> +monitor to get the stack traces of both tasks.
> diff --git a/kernel/trace/rv/Kconfig b/kernel/trace/rv/Kconfig
> index 3884b14df375..4d3a14a0bac2 100644
> --- a/kernel/trace/rv/Kconfig
> +++ b/kernel/trace/rv/Kconfig
> @@ -76,6 +76,7 @@ source "kernel/trace/rv/monitors/opid/Kconfig"
> source "kernel/trace/rv/monitors/rtapp/Kconfig"
> source "kernel/trace/rv/monitors/pagefault/Kconfig"
> source "kernel/trace/rv/monitors/sleep/Kconfig"
> +source "kernel/trace/rv/monitors/wakeup/Kconfig"
> # Add new rtapp monitors here
>
> source "kernel/trace/rv/monitors/stall/Kconfig"
> diff --git a/kernel/trace/rv/Makefile b/kernel/trace/rv/Makefile
> index 94498da35b37..c2c0e4142eb4 100644
> --- a/kernel/trace/rv/Makefile
> +++ b/kernel/trace/rv/Makefile
> @@ -20,6 +20,7 @@ obj-$(CONFIG_RV_MON_OPID) += monitors/opid/opid.o
> obj-$(CONFIG_RV_MON_STALL) += monitors/stall/stall.o
> obj-$(CONFIG_RV_MON_DEADLINE) += monitors/deadline/deadline.o
> obj-$(CONFIG_RV_MON_NOMISS) += monitors/nomiss/nomiss.o
> +obj-$(CONFIG_RV_MON_WAKEUP) += monitors/wakeup/wakeup.o
> # Add new monitors here
> obj-$(CONFIG_RV_REACTORS) += rv_reactors.o
> obj-$(CONFIG_RV_REACT_PRINTK) += reactor_printk.o
> diff --git a/kernel/trace/rv/monitors/rtapp/Kconfig
> b/kernel/trace/rv/monitors/rtapp/Kconfig
> index 1ce9370a9ba8..1fcd7a400ded 100644
> --- a/kernel/trace/rv/monitors/rtapp/Kconfig
> +++ b/kernel/trace/rv/monitors/rtapp/Kconfig
> @@ -1,6 +1,6 @@
> config RV_MON_RTAPP
> depends on RV
> - depends on RV_PER_TASK_MONITORS >= 2
> + depends on RV_PER_TASK_MONITORS >= 3
> bool "rtapp monitor"
> help
> Collection of monitors to check for common problems with real-time
> diff --git a/kernel/trace/rv/monitors/wakeup/Kconfig
> b/kernel/trace/rv/monitors/wakeup/Kconfig
> new file mode 100644
> index 000000000000..ec3a5c06a8c4
> --- /dev/null
> +++ b/kernel/trace/rv/monitors/wakeup/Kconfig
> @@ -0,0 +1,16 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +config RV_MON_WAKEUP
> + depends on RV
> + depends on RV_MON_RTAPP
> + depends on HAVE_SYSCALL_TRACEPOINTS
> + default y
> + select LTL_MON_EVENTS_ID
> + bool "wakeup monitor"
> + help
> + This monitor detects a lower-priority task waking up a
> + higher-priority task. The RV_MON_SLEEP monitor already
> + detects this case, but this monitor detects in the context
> + of the waker task instead. This and RV_MON_SLEEP can be
> + enabled together to get the stacktrace of both the waker
> + task and the wakee task.
> diff --git a/kernel/trace/rv/monitors/wakeup/wakeup.c
> b/kernel/trace/rv/monitors/wakeup/wakeup.c
> new file mode 100644
> index 000000000000..01b47416f24e
> --- /dev/null
> +++ b/kernel/trace/rv/monitors/wakeup/wakeup.c
> @@ -0,0 +1,153 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/ftrace.h>
> +#include <linux/tracepoint.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/rv.h>
> +#include <rv/instrumentation.h>
> +
> +#define MODULE_NAME "wakeup"
> +
> +#include <trace/events/syscalls.h>
> +#include <trace/events/sched.h>
> +#include <trace/events/lock.h>
> +#include <uapi/linux/futex.h>
> +
> +#include <rv_trace.h>
> +#include <monitors/rtapp/rtapp.h>
> +
> +
> +#ifndef __NR_futex
> +#define __NR_futex (-__COUNTER__)
> +#endif
> +#ifndef __NR_futex_time64
> +#define __NR_futex_time64 (-__COUNTER__)
> +#endif
> +
> +#include "wakeup.h"
> +#include <rv/ltl_monitor.h>
> +
> +static void ltl_atoms_fetch(struct task_struct *task, struct ltl_monitor
> *mon)
> +{
> + /*
> + * This includes "actual" real-time tasks and also PI-boosted
> + * tasks. A task being PI-boosted means it is blocking an "actual"
> + * real-task, therefore it should also obey the monitor's rule,
> + * otherwise the "actual" real-task may be delayed.
> + */
> + ltl_atom_set(mon, LTL_RT, rt_or_dl_task(task));
> +}
> +
> +static void ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon,
> bool task_creation)
> +{
> + ltl_atom_set(mon, LTL_WOKEN_BY_LOWER_PRIO, false);
> + ltl_atom_set(mon, LTL_WOKEN_BY_SOFTIRQ, false);
> +
> + if (task_creation) {
> + ltl_atom_set(mon, LTL_BLOCK_ON_RT_MUTEX, false);
> + ltl_atom_set(mon, LTL_FUTEX_LOCK_PI, false);
> + }
> +
> + ltl_atom_set(mon, LTL_USER_THREAD, !(task->flags & PF_KTHREAD));
> +}
> +
> +static void handle_sched_waking(void *data, struct task_struct *task)
> +{
> + if (in_task()) {
> + if (current->prio > task->prio)
> + ltl_atom_pulse(task, LTL_WOKEN_BY_LOWER_PRIO, true);
> + } else if (in_serving_softirq()) {
> + ltl_atom_pulse(task, LTL_WOKEN_BY_SOFTIRQ, true);
> + }
> +}
> +
> +static void handle_contention_begin(void *data, void *lock, unsigned int
> flags)
> +{
> + if (flags & LCB_F_RT)
> + ltl_atom_update(current, LTL_BLOCK_ON_RT_MUTEX, true);
> +}
> +
> +static void handle_contention_end(void *data, void *lock, int ret)
> +{
> + ltl_atom_update(current, LTL_BLOCK_ON_RT_MUTEX, false);
> +}
> +
> +static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
> +{
> + unsigned long args[6];
> + int op, cmd;
> +
> + switch (id) {
> + case __NR_futex:
> + case __NR_futex_time64:
> + syscall_get_arguments(current, regs, args);
> + op = args[1];
> + cmd = op & FUTEX_CMD_MASK;
> +
> + switch (cmd) {
> + case FUTEX_LOCK_PI:
> + case FUTEX_LOCK_PI2:
> + ltl_atom_update(current, LTL_FUTEX_LOCK_PI, true);
> + break;
> + }
> + break;
> + }
> +}
> +
> +static void handle_sys_exit(void *data, struct pt_regs *regs, long ret)
> +{
> + ltl_atom_update(current, LTL_FUTEX_LOCK_PI, false);
> +}
> +
> +static int enable_wakeup(void)
> +{
> + int retval;
> +
> + retval = ltl_monitor_init();
> + if (retval)
> + return retval;
> +
> + rv_attach_trace_probe("rtapp_wakeup", sched_waking,
> handle_sched_waking);
> + rv_attach_trace_probe("rtapp_wakeup", contention_begin,
> handle_contention_begin);
> + rv_attach_trace_probe("rtapp_wakeup", contention_end,
> handle_contention_end);
> + rv_attach_trace_probe("rtapp_wakeup", sys_enter, handle_sys_enter);
> + rv_attach_trace_probe("rtapp_wakeup", sys_exit, handle_sys_exit);
> +
> + return 0;
> +}
> +
> +static void disable_wakeup(void)
> +{
> + rv_detach_trace_probe("rtapp_wakeup", sched_waking,
> handle_sched_waking);
> + rv_detach_trace_probe("rtapp_wakeup", contention_begin,
> handle_contention_begin);
> + rv_detach_trace_probe("rtapp_wakeup", contention_end,
> handle_contention_end);
> + rv_detach_trace_probe("rtapp_wakeup", sys_enter, handle_sys_enter);
> + rv_detach_trace_probe("rtapp_wakeup", sys_exit, handle_sys_exit);
> +
> + ltl_monitor_destroy();
> +}
> +
> +static struct rv_monitor rv_wakeup = {
> + .name = "wakeup",
> + .description = "Monitor that real-time tasks are not woken by lower-
> priority tasks",
> + .enable = enable_wakeup,
> + .disable = disable_wakeup,
> +};
> +
> +static int __init register_wakeup(void)
> +{
> + return rv_register_monitor(&rv_wakeup, &rv_rtapp);
> +}
> +
> +static void __exit unregister_wakeup(void)
> +{
> + rv_unregister_monitor(&rv_wakeup);
> +}
> +
> +module_init(register_wakeup);
> +module_exit(unregister_wakeup);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Nam Cao <namcao@linutronix.de>");
> +MODULE_DESCRIPTION("Monitor that real-time tasks are not woken by lower-
> priority tasks");
> diff --git a/kernel/trace/rv/monitors/wakeup/wakeup.h
> b/kernel/trace/rv/monitors/wakeup/wakeup.h
> new file mode 100644
> index 000000000000..6f80da64e0e1
> --- /dev/null
> +++ b/kernel/trace/rv/monitors/wakeup/wakeup.h
> @@ -0,0 +1,92 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +/*
> + * C implementation of Buchi automaton, automatically generated by
> + * tools/verification/rvgen from the linear temporal logic specification.
> + * For further information, see kernel documentation:
> + * Documentation/trace/rv/linear_temporal_logic.rst
> + */
> +
> +#include <linux/rv.h>
> +
> +#define MONITOR_NAME wakeup
> +
> +enum ltl_atom {
> + LTL_BLOCK_ON_RT_MUTEX,
> + LTL_FUTEX_LOCK_PI,
> + LTL_RT,
> + LTL_USER_THREAD,
> + LTL_WOKEN_BY_LOWER_PRIO,
> + LTL_WOKEN_BY_SOFTIRQ,
> + LTL_NUM_ATOM
> +};
> +static_assert(LTL_NUM_ATOM <= RV_MAX_LTL_ATOM);
> +
> +static const char *ltl_atom_str(enum ltl_atom atom)
> +{
> + static const char *const names[] = {
> + "bl_on_rt_mu",
> + "fu_lo_pi",
> + "rt",
> + "us_th",
> + "wo_lo_pr",
> + "wo_so",
> + };
> +
> + return names[atom];
> +}
> +
> +enum ltl_buchi_state {
> + S0,
> + RV_NUM_BA_STATES
> +};
> +static_assert(RV_NUM_BA_STATES <= RV_MAX_BA_STATES);
> +
> +static void ltl_start(struct task_struct *task, struct ltl_monitor *mon)
> +{
> + bool woken_by_softirq = test_bit(LTL_WOKEN_BY_SOFTIRQ, mon->atoms);
> + bool woken_by_lower_prio = test_bit(LTL_WOKEN_BY_LOWER_PRIO, mon-
> >atoms);
> + bool user_thread = test_bit(LTL_USER_THREAD, mon->atoms);
> + bool rt = test_bit(LTL_RT, mon->atoms);
> + bool futex_lock_pi = test_bit(LTL_FUTEX_LOCK_PI, mon->atoms);
> + bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
> + bool val9 = block_on_rt_mutex || futex_lock_pi;
> + bool val6 = !woken_by_softirq;
> + bool val5 = !woken_by_lower_prio;
> + bool val8 = val5 && val6;
> + bool val10 = val8 || val9;
> + bool val3 = !user_thread;
> + bool val2 = !rt;
> + bool val4 = val2 || val3;
> + bool val11 = val4 || val10;
> +
> + if (val11)
> + __set_bit(S0, mon->states);
> +}
> +
> +static void
> +ltl_possible_next_states(struct ltl_monitor *mon, unsigned int state,
> unsigned long *next)
> +{
> + bool woken_by_softirq = test_bit(LTL_WOKEN_BY_SOFTIRQ, mon->atoms);
> + bool woken_by_lower_prio = test_bit(LTL_WOKEN_BY_LOWER_PRIO, mon-
> >atoms);
> + bool user_thread = test_bit(LTL_USER_THREAD, mon->atoms);
> + bool rt = test_bit(LTL_RT, mon->atoms);
> + bool futex_lock_pi = test_bit(LTL_FUTEX_LOCK_PI, mon->atoms);
> + bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
> + bool val9 = block_on_rt_mutex || futex_lock_pi;
> + bool val6 = !woken_by_softirq;
> + bool val5 = !woken_by_lower_prio;
> + bool val8 = val5 && val6;
> + bool val10 = val8 || val9;
> + bool val3 = !user_thread;
> + bool val2 = !rt;
> + bool val4 = val2 || val3;
> + bool val11 = val4 || val10;
> +
> + switch (state) {
> + case S0:
> + if (val11)
> + __set_bit(S0, next);
> + break;
> + }
> +}
> diff --git a/kernel/trace/rv/monitors/wakeup/wakeup_trace.h
> b/kernel/trace/rv/monitors/wakeup/wakeup_trace.h
> new file mode 100644
> index 000000000000..7e056183f920
> --- /dev/null
> +++ b/kernel/trace/rv/monitors/wakeup/wakeup_trace.h
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +/*
> + * Snippet to be included in rv_trace.h
> + */
> +
> +#ifdef CONFIG_RV_MON_WAKEUP
> +DEFINE_EVENT(event_ltl_monitor_id, event_wakeup,
> + TP_PROTO(struct task_struct *task, char *states, char *atoms,
> char *next),
> + TP_ARGS(task, states, atoms, next));
> +DEFINE_EVENT(error_ltl_monitor_id, error_wakeup,
> + TP_PROTO(struct task_struct *task),
> + TP_ARGS(task));
> +#endif /* CONFIG_RV_MON_WAKEUP */
> diff --git a/kernel/trace/rv/rv_trace.h b/kernel/trace/rv/rv_trace.h
> index 9622c269789c..2f8a932432c9 100644
> --- a/kernel/trace/rv/rv_trace.h
> +++ b/kernel/trace/rv/rv_trace.h
> @@ -241,6 +241,7 @@ DECLARE_EVENT_CLASS(error_ltl_monitor_id,
> );
> #include <monitors/pagefault/pagefault_trace.h>
> #include <monitors/sleep/sleep_trace.h>
> +#include <monitors/wakeup/wakeup_trace.h>
> // Add new monitors based on CONFIG_LTL_MON_EVENTS_ID here
> #endif /* CONFIG_LTL_MON_EVENTS_ID */
>
> diff --git a/tools/verification/models/rtapp/wakeup.ltl
> b/tools/verification/models/rtapp/wakeup.ltl
> new file mode 100644
> index 000000000000..a5d63ca0811a
> --- /dev/null
> +++ b/tools/verification/models/rtapp/wakeup.ltl
> @@ -0,0 +1,5 @@
> +RULE = always (((RT and USER_THREAD) imply
> + (not (WOKEN_BY_LOWER_PRIO or WOKEN_BY_SOFTIRQ)) or
> ALLOWLIST))
> +
> +ALLOWLIST = BLOCK_ON_RT_MUTEX
> + or FUTEX_LOCK_PI
^ permalink raw reply
* Re: [RFC PATCH v2 4/4] rtla/osnoise: Leverage IPI event filters when tracing a subset of CPUs
From: Steven Rostedt @ 2026-07-01 13:11 UTC (permalink / raw)
To: Tomas Glozar
Cc: Valentin Schneider, linux-kernel, linux-trace-kernel,
Masami Hiramatsu, Mathieu Desnoyers, Costa Shulyupin,
Crystal Wood, John Kacur, Ivan Pravdin, Jonathan Corbet
In-Reply-To: <CAP4=nvT5H73ptaBaZ2fWQ+SjJ-c3eEGZbrSefMV6fxqL-va2XA@mail.gmail.com>
On Wed, 1 Jul 2026 08:45:40 +0200
Tomas Glozar <tglozar@redhat.com> wrote:
> I double-checked that and you are correct that the docstring says so,
> but it's an error in the docstring. According to the manpage, it
> returns the number of bytes written (i.e. positive on success, not
> zero) [1]:
Note, the man page is considered the source of "truth".
>
> "RETURN VALUE
> ...
> tracefs_event_file_write() and tracefs_event_file_append() returns
> *the number of bytes written to the system/event file* or negative on
> error."
>
> The code agrees as well: in tracefs_event_file_write() there's the
> wrong docstring (likely copied from another function) [2]:
>
> /*
> * tracefs_event_file_write - write to an event file
> * ...
> * Return 0 on success, and -1 on error.
Ug, that's a bug and needs to be fixed.
Thanks for catching this. I need to spend some time to catch up on the user
side of tracing. There's a few new bugzillas and patches I need to apply.
-- Steve
> */
> int tracefs_event_file_write(struct tracefs_instance *instance,
> const char *system, const char *event,
> const char *file, const char *str)
> {
> ....
> ret = tracefs_instance_file_write(instance, path, str);
> free(path);
> return ret;
> }
^ permalink raw reply
* Re: [PATCH v2 3/4] rv/rtapp/sleep: Stop monitoring kernel threads
From: Gabriele Monaco @ 2026-07-01 13:02 UTC (permalink / raw)
To: Nam Cao
Cc: Steven Rostedt, linux-trace-kernel, linux-doc, linux-kernel,
Sebastian Andrzej Siewior
In-Reply-To: <eec2ca5224bcdacc45b8e1eb2f0e68109e1cae7a.1781852967.git.namcao@linutronix.de>
On Fri, 2026-06-19 at 09:21 +0200, Nam Cao wrote:
> The rtapp/sleep monitor's primary purpose is detecting common mistakes
> with user-space real-time design. Monitoring real-time issues with
> kernel threads is a bonus.
>
> However, accomodating kernel threads complicates the monitor due to
> the edge cases which is seen by the monitor as lower-priority task
> waking higher-priority task:
>
> - kthread_stop() wakes up the task in order to stop it.
>
> - The rcu thread and migration thread can be woken by any task.
>
> - The ktimerd thread is woken near the end of irq_exit_rcu(), where
> the preempt counter is "broken" and falsely says this is task
> context. This requires the monitor to use the hardirq_context flag
> instead of the preempt counter.
>
> Beside complicating the monitor, the final case also requires enabling
> CONFIG_TRACE_IRQFLAGS (so that "hardirq_context" can be used). This
> adds overhead to the kernel even when the monitor is not active. This
> may be an obstacle to enabling this monitor in distros' kernels.
Very good especially for this!
> Furthermore, kernel threads usually are started before the monitor is
> enabled. Consequently, the threads' states (i.o.w. the monitor's
> atomic propositions for the threads) are not fully known to the
> monitor. As a result, the kernel threads mostly cannot be monitored.
>
> Overall, the downsides of accomodating kernel threads outweights the
> benefits. Thus, exclude kernel threads to simplify the monitor.
>
> Signed-off-by: Nam Cao <namcao@linutronix.de>
Reviewed-by: Gabriele Monaco <gmonaco@redhat.com>
Thanks,
Gabriele
> ---
> Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> ---
> Documentation/trace/rv/monitor_rtapp.rst | 22 ++---
> kernel/trace/rv/monitors/sleep/Kconfig | 1 -
> kernel/trace/rv/monitors/sleep/sleep.c | 39 +-------
> kernel/trace/rv/monitors/sleep/sleep.h | 104 +++++++++-------------
> tools/verification/models/rtapp/sleep.ltl | 7 +-
> 5 files changed, 54 insertions(+), 119 deletions(-)
>
> diff --git a/Documentation/trace/rv/monitor_rtapp.rst
> b/Documentation/trace/rv/monitor_rtapp.rst
> index 570be67a8f3b..502d3ea412eb 100644
> --- a/Documentation/trace/rv/monitor_rtapp.rst
> +++ b/Documentation/trace/rv/monitor_rtapp.rst
> @@ -93,9 +93,9 @@ assessment.
>
> The monitor's specification is::
>
> - RULE = always ((RT and SLEEP) imply (RT_FRIENDLY_SLEEP or ALLOWLIST))
> + RULE = always ((RT and SLEEP and USER_THREAD) imply (RT_FRIENDLY_SLEEP or
> ALLOWLIST))
>
> - RT_FRIENDLY_SLEEP = (RT_VALID_SLEEP_REASON or KERNEL_THREAD)
> + RT_FRIENDLY_SLEEP = RT_VALID_SLEEP_REASON
> and ((not SCHEDULE_IN) until RT_FRIENDLY_WAKE)
>
> RT_VALID_SLEEP_REASON = FUTEX_WAIT
> @@ -110,23 +110,13 @@ The monitor's specification is::
> or WOKEN_BY_HARDIRQ
> or WOKEN_BY_NMI
> or ABORT_SLEEP
> - or KTHREAD_SHOULD_STOP
>
> ALLOWLIST = BLOCK_ON_RT_MUTEX
> or FUTEX_LOCK_PI
> - or TASK_IS_RCU
> - or TASK_IS_MIGRATION
> -
> -Beside the scenarios described above, this specification also handle some
> -special cases:
> -
> - - `KERNEL_THREAD`: kernel tasks do not have any pattern that can be
> recognized
> - as valid real-time sleeping reasons. Therefore sleeping reason is not
> - checked for kernel tasks.
> - - `KTHREAD_SHOULD_STOP`: a non-real-time thread may stop a real-time kernel
> - thread by waking it and waiting for it to exit (`kthread_stop()`). This
> - wakeup is safe for real-time.
> - - `ALLOWLIST`: to handle known false positives with the kernel.
> +
> +Beside the scenarios described above, this specification also defines an
> allow list
> +to handle some special cases:
> +
> - `BLOCK_ON_RT_MUTEX` is included in the allowlist due to its
> implementation.
> In the release path of rt_mutex, a boosted task is de-boosted before
> waking
> the rt_mutex's waiter. Consequently, the monitor may see a real-time-
> unsafe
> diff --git a/kernel/trace/rv/monitors/sleep/Kconfig
> b/kernel/trace/rv/monitors/sleep/Kconfig
> index 6b7a122e7b47..d6ec3e9a91b6 100644
> --- a/kernel/trace/rv/monitors/sleep/Kconfig
> +++ b/kernel/trace/rv/monitors/sleep/Kconfig
> @@ -5,7 +5,6 @@ config RV_MON_SLEEP
> select RV_LTL_MONITOR
> depends on HAVE_SYSCALL_TRACEPOINTS
> depends on RV_MON_RTAPP
> - select TRACE_IRQFLAGS
> default y
> select LTL_MON_EVENTS_ID
> bool "sleep monitor"
> diff --git a/kernel/trace/rv/monitors/sleep/sleep.c
> b/kernel/trace/rv/monitors/sleep/sleep.c
> index 638be7d8747f..aa5a984853b5 100644
> --- a/kernel/trace/rv/monitors/sleep/sleep.c
> +++ b/kernel/trace/rv/monitors/sleep/sleep.c
> @@ -43,7 +43,6 @@ static void ltl_atoms_init(struct task_struct *task, struct
> ltl_monitor *mon, bo
> ltl_atom_set(mon, LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO, false);
>
> if (task_creation) {
> - ltl_atom_set(mon, LTL_KTHREAD_SHOULD_STOP, false);
> ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, false);
> ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, false);
> ltl_atom_set(mon, LTL_CLOCK_NANOSLEEP, false);
> @@ -53,33 +52,7 @@ static void ltl_atoms_init(struct task_struct *task, struct
> ltl_monitor *mon, bo
> ltl_atom_set(mon, LTL_BLOCK_ON_RT_MUTEX, false);
> }
>
> - if (task->flags & PF_KTHREAD) {
> - ltl_atom_set(mon, LTL_KERNEL_THREAD, true);
> -
> - /* kernel tasks do not do syscall */
> - ltl_atom_set(mon, LTL_FUTEX_WAIT, false);
> - ltl_atom_set(mon, LTL_FUTEX_LOCK_PI, false);
> - ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, false);
> - ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, false);
> - ltl_atom_set(mon, LTL_CLOCK_NANOSLEEP, false);
> - ltl_atom_set(mon, LTL_EPOLL_WAIT, false);
> -
> - if (strstarts(task->comm, "migration/"))
> - ltl_atom_set(mon, LTL_TASK_IS_MIGRATION, true);
> - else
> - ltl_atom_set(mon, LTL_TASK_IS_MIGRATION, false);
> -
> - if (strstarts(task->comm, "rcu"))
> - ltl_atom_set(mon, LTL_TASK_IS_RCU, true);
> - else
> - ltl_atom_set(mon, LTL_TASK_IS_RCU, false);
> - } else {
> - ltl_atom_set(mon, LTL_KTHREAD_SHOULD_STOP, false);
> - ltl_atom_set(mon, LTL_KERNEL_THREAD, false);
> - ltl_atom_set(mon, LTL_TASK_IS_RCU, false);
> - ltl_atom_set(mon, LTL_TASK_IS_MIGRATION, false);
> - }
> -
> + ltl_atom_set(mon, LTL_USER_THREAD, !(task->flags & PF_KTHREAD));
> }
>
> static void handle_sched_set_state(void *data, struct task_struct *task, int
> state)
> @@ -97,7 +70,7 @@ static void handle_sched_exit(void *data, bool is_switch)
>
> static void handle_sched_waking(void *data, struct task_struct *task)
> {
> - if (this_cpu_read(hardirq_context)) {
> + if (in_hardirq()) {
> ltl_atom_pulse(task, LTL_WOKEN_BY_HARDIRQ, true);
> } else if (in_task()) {
> if (current->prio <= task->prio)
> @@ -181,12 +154,6 @@ static void handle_sys_exit(void *data, struct pt_regs
> *regs, long ret)
> ltl_atom_update(current, LTL_CLOCK_NANOSLEEP, false);
> }
>
> -static void handle_kthread_stop(void *data, struct task_struct *task)
> -{
> - /* FIXME: this could race with other tracepoint handlers */
> - ltl_atom_update(task, LTL_KTHREAD_SHOULD_STOP, true);
> -}
> -
> static int enable_sleep(void)
> {
> int retval;
> @@ -200,7 +167,6 @@ static int enable_sleep(void)
> rv_attach_trace_probe("rtapp_sleep", sched_set_state_tp,
> handle_sched_set_state);
> rv_attach_trace_probe("rtapp_sleep", contention_begin,
> handle_contention_begin);
> rv_attach_trace_probe("rtapp_sleep", contention_end,
> handle_contention_end);
> - rv_attach_trace_probe("rtapp_sleep", sched_kthread_stop,
> handle_kthread_stop);
> rv_attach_trace_probe("rtapp_sleep", sys_enter, handle_sys_enter);
> rv_attach_trace_probe("rtapp_sleep", sys_exit, handle_sys_exit);
> return 0;
> @@ -213,7 +179,6 @@ static void disable_sleep(void)
> rv_detach_trace_probe("rtapp_sleep", sched_set_state_tp,
> handle_sched_set_state);
> rv_detach_trace_probe("rtapp_sleep", contention_begin,
> handle_contention_begin);
> rv_detach_trace_probe("rtapp_sleep", contention_end,
> handle_contention_end);
> - rv_detach_trace_probe("rtapp_sleep", sched_kthread_stop,
> handle_kthread_stop);
> rv_detach_trace_probe("rtapp_sleep", sys_enter, handle_sys_enter);
> rv_detach_trace_probe("rtapp_sleep", sys_exit, handle_sys_exit);
>
> diff --git a/kernel/trace/rv/monitors/sleep/sleep.h
> b/kernel/trace/rv/monitors/sleep/sleep.h
> index 2fe2ec7edae8..44e593f41e6a 100644
> --- a/kernel/trace/rv/monitors/sleep/sleep.h
> +++ b/kernel/trace/rv/monitors/sleep/sleep.h
> @@ -18,15 +18,12 @@ enum ltl_atom {
> LTL_EPOLL_WAIT,
> LTL_FUTEX_LOCK_PI,
> LTL_FUTEX_WAIT,
> - LTL_KERNEL_THREAD,
> - LTL_KTHREAD_SHOULD_STOP,
> LTL_NANOSLEEP_CLOCK_REALTIME,
> LTL_NANOSLEEP_TIMER_ABSTIME,
> LTL_RT,
> LTL_SCHEDULE_IN,
> LTL_SLEEP,
> - LTL_TASK_IS_MIGRATION,
> - LTL_TASK_IS_RCU,
> + LTL_USER_THREAD,
> LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO,
> LTL_WOKEN_BY_HARDIRQ,
> LTL_WOKEN_BY_NMI,
> @@ -43,15 +40,12 @@ static const char *ltl_atom_str(enum ltl_atom atom)
> "ep_wa",
> "fu_lo_pi",
> "fu_wa",
> - "ker_th",
> - "kth_sh_st",
> "na_cl_re",
> "na_ti_ab",
> "rt",
> "sch_in",
> "sle",
> - "ta_mi",
> - "ta_rc",
> + "us_th",
> "wo_eq_hi_pr",
> "wo_ha",
> "wo_nm",
> @@ -79,46 +73,41 @@ static void ltl_start(struct task_struct *task, struct
> ltl_monitor *mon)
> bool woken_by_hardirq = test_bit(LTL_WOKEN_BY_HARDIRQ, mon->atoms);
> bool woken_by_equal_or_higher_prio =
> test_bit(LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO,
> mon->atoms);
> - bool task_is_rcu = test_bit(LTL_TASK_IS_RCU, mon->atoms);
> - bool task_is_migration = test_bit(LTL_TASK_IS_MIGRATION, mon->atoms);
> + bool user_thread = test_bit(LTL_USER_THREAD, mon->atoms);
> bool sleep = test_bit(LTL_SLEEP, mon->atoms);
> bool schedule_in = test_bit(LTL_SCHEDULE_IN, mon->atoms);
> bool rt = test_bit(LTL_RT, mon->atoms);
> bool nanosleep_timer_abstime = test_bit(LTL_NANOSLEEP_TIMER_ABSTIME,
> mon->atoms);
> bool nanosleep_clock_realtime =
> test_bit(LTL_NANOSLEEP_CLOCK_REALTIME, mon->atoms);
> - bool kthread_should_stop = test_bit(LTL_KTHREAD_SHOULD_STOP, mon-
> >atoms);
> - bool kernel_thread = test_bit(LTL_KERNEL_THREAD, mon->atoms);
> bool futex_wait = test_bit(LTL_FUTEX_WAIT, mon->atoms);
> bool futex_lock_pi = test_bit(LTL_FUTEX_LOCK_PI, mon->atoms);
> bool epoll_wait = test_bit(LTL_EPOLL_WAIT, mon->atoms);
> bool clock_nanosleep = test_bit(LTL_CLOCK_NANOSLEEP, mon->atoms);
> bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
> bool abort_sleep = test_bit(LTL_ABORT_SLEEP, mon->atoms);
> - bool val41 = task_is_rcu || task_is_migration;
> - bool val42 = futex_lock_pi || val41;
> - bool val5 = block_on_rt_mutex || val42;
> - bool val33 = abort_sleep || kthread_should_stop;
> - bool val34 = woken_by_nmi || val33;
> - bool val35 = woken_by_hardirq || val34;
> - bool val14 = woken_by_equal_or_higher_prio || val35;
> + bool val7 = block_on_rt_mutex || futex_lock_pi;
> + bool val32 = woken_by_nmi || abort_sleep;
> + bool val33 = woken_by_hardirq || val32;
> + bool val14 = woken_by_equal_or_higher_prio || val33;
> bool val13 = !schedule_in;
> bool val25 = !nanosleep_clock_realtime;
> bool val26 = nanosleep_timer_abstime && val25;
> bool val18 = clock_nanosleep && val26;
> bool val20 = val18 || epoll_wait;
> - bool val9 = futex_wait || val20;
> - bool val11 = val9 || kernel_thread;
> + bool val11 = futex_wait || val20;
> + bool val3 = !user_thread;
> bool val2 = !sleep;
> + bool val4 = val2 || val3;
> bool val1 = !rt;
> - bool val3 = val1 || val2;
> + bool val5 = val1 || val4;
>
> - if (val3)
> + if (val5)
> __set_bit(S0, mon->states);
> if (val11 && val13)
> __set_bit(S1, mon->states);
> if (val11 && val14)
> __set_bit(S4, mon->states);
> - if (val5)
> + if (val7)
> __set_bit(S5, mon->states);
> }
>
> @@ -129,130 +118,125 @@ ltl_possible_next_states(struct ltl_monitor *mon,
> unsigned int state, unsigned l
> bool woken_by_hardirq = test_bit(LTL_WOKEN_BY_HARDIRQ, mon->atoms);
> bool woken_by_equal_or_higher_prio =
> test_bit(LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO,
> mon->atoms);
> - bool task_is_rcu = test_bit(LTL_TASK_IS_RCU, mon->atoms);
> - bool task_is_migration = test_bit(LTL_TASK_IS_MIGRATION, mon->atoms);
> + bool user_thread = test_bit(LTL_USER_THREAD, mon->atoms);
> bool sleep = test_bit(LTL_SLEEP, mon->atoms);
> bool schedule_in = test_bit(LTL_SCHEDULE_IN, mon->atoms);
> bool rt = test_bit(LTL_RT, mon->atoms);
> bool nanosleep_timer_abstime = test_bit(LTL_NANOSLEEP_TIMER_ABSTIME,
> mon->atoms);
> bool nanosleep_clock_realtime =
> test_bit(LTL_NANOSLEEP_CLOCK_REALTIME, mon->atoms);
> - bool kthread_should_stop = test_bit(LTL_KTHREAD_SHOULD_STOP, mon-
> >atoms);
> - bool kernel_thread = test_bit(LTL_KERNEL_THREAD, mon->atoms);
> bool futex_wait = test_bit(LTL_FUTEX_WAIT, mon->atoms);
> bool futex_lock_pi = test_bit(LTL_FUTEX_LOCK_PI, mon->atoms);
> bool epoll_wait = test_bit(LTL_EPOLL_WAIT, mon->atoms);
> bool clock_nanosleep = test_bit(LTL_CLOCK_NANOSLEEP, mon->atoms);
> bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
> bool abort_sleep = test_bit(LTL_ABORT_SLEEP, mon->atoms);
> - bool val41 = task_is_rcu || task_is_migration;
> - bool val42 = futex_lock_pi || val41;
> - bool val5 = block_on_rt_mutex || val42;
> - bool val33 = abort_sleep || kthread_should_stop;
> - bool val34 = woken_by_nmi || val33;
> - bool val35 = woken_by_hardirq || val34;
> - bool val14 = woken_by_equal_or_higher_prio || val35;
> + bool val7 = block_on_rt_mutex || futex_lock_pi;
> + bool val32 = woken_by_nmi || abort_sleep;
> + bool val33 = woken_by_hardirq || val32;
> + bool val14 = woken_by_equal_or_higher_prio || val33;
> bool val13 = !schedule_in;
> bool val25 = !nanosleep_clock_realtime;
> bool val26 = nanosleep_timer_abstime && val25;
> bool val18 = clock_nanosleep && val26;
> bool val20 = val18 || epoll_wait;
> - bool val9 = futex_wait || val20;
> - bool val11 = val9 || kernel_thread;
> + bool val11 = futex_wait || val20;
> + bool val3 = !user_thread;
> bool val2 = !sleep;
> + bool val4 = val2 || val3;
> bool val1 = !rt;
> - bool val3 = val1 || val2;
> + bool val5 = val1 || val4;
>
> switch (state) {
> case S0:
> - if (val3)
> + if (val5)
> __set_bit(S0, next);
> if (val11 && val13)
> __set_bit(S1, next);
> if (val11 && val14)
> __set_bit(S4, next);
> - if (val5)
> + if (val7)
> __set_bit(S5, next);
> break;
> case S1:
> if (val11 && val13)
> __set_bit(S1, next);
> - if (val13 && val3)
> + if (val13 && val5)
> __set_bit(S2, next);
> - if (val14 && val3)
> + if (val14 && val5)
> __set_bit(S3, next);
> if (val11 && val14)
> __set_bit(S4, next);
> - if (val13 && val5)
> + if (val13 && val7)
> __set_bit(S6, next);
> - if (val14 && val5)
> + if (val14 && val7)
> __set_bit(S7, next);
> break;
> case S2:
> if (val11 && val13)
> __set_bit(S1, next);
> - if (val13 && val3)
> + if (val13 && val5)
> __set_bit(S2, next);
> - if (val14 && val3)
> + if (val14 && val5)
> __set_bit(S3, next);
> if (val11 && val14)
> __set_bit(S4, next);
> - if (val13 && val5)
> + if (val13 && val7)
> __set_bit(S6, next);
> - if (val14 && val5)
> + if (val14 && val7)
> __set_bit(S7, next);
> break;
> case S3:
> - if (val3)
> + if (val5)
> __set_bit(S0, next);
> if (val11 && val13)
> __set_bit(S1, next);
> if (val11 && val14)
> __set_bit(S4, next);
> - if (val5)
> + if (val7)
> __set_bit(S5, next);
> break;
> case S4:
> - if (val3)
> + if (val5)
> __set_bit(S0, next);
> if (val11 && val13)
> __set_bit(S1, next);
> if (val11 && val14)
> __set_bit(S4, next);
> - if (val5)
> + if (val7)
> __set_bit(S5, next);
> break;
> case S5:
> - if (val3)
> + if (val5)
> __set_bit(S0, next);
> if (val11 && val13)
> __set_bit(S1, next);
> if (val11 && val14)
> __set_bit(S4, next);
> - if (val5)
> + if (val7)
> __set_bit(S5, next);
> break;
> case S6:
> if (val11 && val13)
> __set_bit(S1, next);
> - if (val13 && val3)
> + if (val13 && val5)
> __set_bit(S2, next);
> - if (val14 && val3)
> + if (val14 && val5)
> __set_bit(S3, next);
> if (val11 && val14)
> __set_bit(S4, next);
> - if (val13 && val5)
> + if (val13 && val7)
> __set_bit(S6, next);
> - if (val14 && val5)
> + if (val14 && val7)
> __set_bit(S7, next);
> break;
> case S7:
> - if (val3)
> + if (val5)
> __set_bit(S0, next);
> if (val11 && val13)
> __set_bit(S1, next);
> if (val11 && val14)
> __set_bit(S4, next);
> - if (val5)
> + if (val7)
> __set_bit(S5, next);
> break;
> }
> diff --git a/tools/verification/models/rtapp/sleep.ltl
> b/tools/verification/models/rtapp/sleep.ltl
> index 5923e58d7810..4d78fdd204c0 100644
> --- a/tools/verification/models/rtapp/sleep.ltl
> +++ b/tools/verification/models/rtapp/sleep.ltl
> @@ -1,6 +1,6 @@
> -RULE = always ((RT and SLEEP) imply (RT_FRIENDLY_SLEEP or ALLOWLIST))
> +RULE = always ((RT and SLEEP and USER_THREAD) imply (RT_FRIENDLY_SLEEP or
> ALLOWLIST))
>
> -RT_FRIENDLY_SLEEP = (RT_VALID_SLEEP_REASON or KERNEL_THREAD)
> +RT_FRIENDLY_SLEEP = RT_VALID_SLEEP_REASON
> and ((not SCHEDULE_IN) until RT_FRIENDLY_WAKE)
>
> RT_VALID_SLEEP_REASON = FUTEX_WAIT
> @@ -15,9 +15,6 @@ RT_FRIENDLY_WAKE = WOKEN_BY_EQUAL_OR_HIGHER_PRIO
> or WOKEN_BY_HARDIRQ
> or WOKEN_BY_NMI
> or ABORT_SLEEP
> - or KTHREAD_SHOULD_STOP
>
> ALLOWLIST = BLOCK_ON_RT_MUTEX
> or FUTEX_LOCK_PI
> - or TASK_IS_RCU
> - or TASK_IS_MIGRATION
^ permalink raw reply
* Re: [PATCH v3] ufs: core: add hba parameter to trace events
From: Steven Rostedt @ 2026-07-01 12:57 UTC (permalink / raw)
To: Peter Wang (王信友)
Cc: linux-trace-kernel@vger.kernel.org,
CC Chou (周志杰), jejb@linux.ibm.com,
bvanassche@acm.org, linux-scsi@vger.kernel.org,
linux-mediatek@lists.infradead.org,
Chaotian Jing (井朝天),
Eddie Huang ( 黃智傑),
Qilin Tan ( 谭麒麟),
Lin Gui ( 桂林),
Yi-fan Peng ( 彭羿凡), alim.akhtar@samsung.com,
Jiajie Hao ( 郝加节),
Naomi Chu ( 朱詠田),
Alice Chao ( 趙珮均),
Ed Tsai ( 蔡宗軒), wsd_upstream,
avri.altman@wdc.com, martin.petersen@oracle.com,
Chun-Hung Wu ( 巫駿宏),
Tun-yu Yu ( 游敦聿)
In-Reply-To: <e4c090a5b8402fe3db137d986f9a6639de73cc67.camel@mediatek.com>
On Wed, 1 Jul 2026 06:11:37 +0000
Peter Wang (王信友) <peter.wang@mediatek.com> wrote:
> However, I am curious: if the HBA is removed, implying that the
> storage would become unusable, might the system encounter an
> I/O hang or shutdown, potentially preventing its detection?
> Perhaps it's a theoretical issue that would not manifest
> in a real-world situation?
Note, it doesn't necessarily mean that the device itself was removed. The
issue is that a pointer to an allocated descriptor is saved in the ring buffer.
Maybe once the device is created it will never go way. But what happens if
for some reason the descriptor is freed and reallocated? Now the old
descriptor pointer is still in the ring buffer.
What in the logic guarantees that the pointer will never be freed?
And lets say there is an issue and the hba is freed and you debug this by
dumping the trace buffer via ftrace_dump_on_oops. Now the dump itself may
crash and you don't have a way to debug what happened.
One other point that causes issues here. It makes user space tracing
useless. Try tracing this with "trace-cmd record". These events will not be
able to be parsed.
-- Steve
^ permalink raw reply
* [PATCH 3/6] selftests/ftrace: fix spelling error in poll test comment
From: Wang Yan @ 2026-07-01 12:35 UTC (permalink / raw)
To: Shuah Khan, Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
linux-kernel, linux-trace-kernel, linux-kselftest
Cc: Wang Yan
In-Reply-To: <20260701123520.271580-1-wangyan01@kylinos.cn>
Fix typo "happned" -> "happened" in ftrace poll test comment.
Signed-off-by: Wang Yan <wangyan01@kylinos.cn>
---
tools/testing/selftests/ftrace/poll.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/testing/selftests/ftrace/poll.c b/tools/testing/selftests/ftrace/poll.c
index 53258f7515e7..4da86a20dc85 100644
--- a/tools/testing/selftests/ftrace/poll.c
+++ b/tools/testing/selftests/ftrace/poll.c
@@ -66,7 +66,7 @@ int main(int argc, char *argv[])
}
close(pfd.fd);
- /* If timeout happned (ret == 0), exit code is 1 */
+ /* If timeout happened (ret == 0), exit code is 1 */
if (ret == 0)
return 1;
--
2.25.1
^ permalink raw reply related
* Re: [PATCH v2 1/4] rv/rtapp/sleep: Make the error more informative for user
From: Gabriele Monaco @ 2026-07-01 12:34 UTC (permalink / raw)
To: Nam Cao; +Cc: Steven Rostedt, linux-trace-kernel, linux-doc, linux-kernel
In-Reply-To: <9bc739eec41b6616e9f81cbe50759cefb1c3ad9c.camel@redhat.com>
On Wed, 2026-07-01 at 14:26 +0200, Gabriele Monaco wrote:
> On Fri, 2026-06-19 at 09:21 +0200, Nam Cao wrote:
> > -static void handle_sched_wakeup(void *data, struct task_struct *task)
> > +static void handle_sched_exit(void *data, bool is_switch)
> > {
> > - ltl_atom_pulse(task, LTL_WAKE, true);
> > + ltl_atom_pulse(current, LTL_SCHEDULE_IN, true);
> > }
>
> Well, this triggers also in a spurious schedule: current -> current, can
> that be a problem? Otherwise you may either use sched_switch over next
sched_switch would obviously be in the wrong context.. Ignore that.
> or pulse only if is_switch.
>
> It probably isn't a big deal though, since there's no SLEEP prior.. But
> we may want to avoid the noise.
>
> Looks good overall.
>
> Reviewed-by: Gabriele Monaco <gmonaco@redhat.com>
>
> Thanks,
> Gabriele
^ permalink raw reply
* Re: [RFC 0/2] arm64: kprobes: Fix single-step fault and reentry handling
From: Pu Hu @ 2026-07-01 12:30 UTC (permalink / raw)
To: catalin.marinas@arm.com, will@kernel.org, naveen@kernel.org,
davem@davemloft.net, mhiramat@kernel.org,
yang@os.amperecomputing.com, Hongyan Xia, Jiazi Li,
ada.coupriediaz@arm.com, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org
In-Reply-To: <20260701121448.3926-1-hupu@transsion.com>
Dear Maintainers,
I would like to provide some additional background for this patchset.
We observed a high-probability crash on an Android device running a
6.1.145-based kernel when recording preemptirq tracepoints for a user
space process with dwarf callchains enabled.
The command used to reproduce the issue is:
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
Here <PID> is the PID of a user space process, for example a foreground
application UI thread or RenderThread.
One important observation is that the crash does not reproduce if
"--call-graph dwarf" is removed.
The crash log shows a data abort on a user virtual address while the PC
is at a probed kernel instruction:
[ 297.177775] Unable to handle kernel paging request at virtual
address 0000007ff042e000
[ 297.177792] Mem abort info:
[ 297.177795] ESR = 0x0000000096000007
[ 297.177799] EC = 0x25: DABT (current EL), IL = 32 bits
[ 297.177803] SET = 0, FnV = 0
[ 297.177806] EA = 0, S1PTW = 0
[ 297.177808] FSC = 0x07: level 3 translation fault
[ 297.177811] Data abort info:
[ 297.177814] ISV = 0, ISS = 0x00000007
[ 297.177817] CM = 0, WnR = 0
[ 297.177820] user pgtable: 4k pages, 39-bit VAs, pgdp=000000098c9f2000
[ 297.177825] [0000007ff042e000] pgd=08000009aaaea003,
p4d=08000009aaaea003, pud=08000009aaaea003, pmd=08000000abca0003,
pte=0000000000000000
[ 297.177835] Internal error: Oops: 0000000096000007 [#1] PREEMPT SMP
[ 297.178070] Skip md ftrace buffer dump for: 0x2800d70
...
[ 297.178485] CPU: 6 PID: 10214 Comm: id.article.news Tainted: P S
W O 6.1.145-android14-11-maybe-dirty-qki-consolidate #1
[ 297.178489] Hardware name: Qualcomm Technologies, Inc. Volcano
QRD,x6878 (DT)
[ 297.178491] pstate: 22400005 (nzCv daif +PAN -UAO +TCO -DIT -SSBS
BTYPE=--)
[ 297.178493] pc : folio_wait_bit_common+0x0/0x408
[ 297.178499] lr : perf_output_sample+0x57c/0xacc
[ 297.178502] sp : ffffffc0366c2f90
[ 297.178503] x29: ffffffc0366c2fb0 x28: 0000000000001000 x27:
0000007ff042d5f8
[ 297.178507] x26: 00000000000035e7 x25: 0000000000000000 x24:
ffffff892cec3000
[ 297.178510] x23: 0000000000001000 x22: 0000000000009370 x21:
ffffffc0366c3140
[ 297.178512] x20: ffffff888aa1a180 x19: ffffffc0366c3020 x18:
ffffffe01103b340
[ 297.178515] x17: 00000000ad6b63b6 x16: 00000000ad6b63b6 x15:
0000007ff042d5f8
[ 297.178518] x14: 0000000000000000 x13: 003436737365636f x12:
72705f7070612f6e
[ 297.178520] x11: 69622f6d65747379 x10: 732f0030333d7972 x9 :
616d6972705f6c6f
[ 297.178523] x8 : 6f705f706173755f x7 : 54454b434f535f44 x6 :
ffffff892cec39d8
[ 297.178526] x5 : ffffff892cec4000 x4 : 0000000000000008 x3 :
6e6f6973736e6172
[ 297.178528] x2 : 00000000000005b8 x1 : 0000007ff042e000 x0 :
ffffff892cec3000
[ 297.178531] Call trace:
[ 297.178532] folio_wait_bit_common+0x0/0x408
[ 297.178535] perf_event_output_forward+0x90/0xdc
[ 297.178537] __perf_event_overflow+0x128/0x1e8
[ 297.178540] perf_swevent_event+0x94/0x1a0
[ 297.178543] perf_tp_event+0x140/0x270
[ 297.178545] perf_trace_run_bpf_submit+0x84/0xe0
[ 297.178547] perf_trace_preemptirq_template+0xe8/0x124
[ 297.178553] trace_preempt_on+0xec/0x150
[ 297.178555] preempt_count_sub+0xa8/0x12c
[ 297.178562] do_debug_exception+0xd0/0x148
[ 297.178568] el1_dbg+0x64/0x80
[ 297.178575] el1h_64_sync_handler+0x3c/0x90
[ 297.178577] el1h_64_sync+0x68/0x6c
[ 297.178579] folio_wait_bit_common+0x0/0x408
[ 297.178582] __get_node_page+0xdc/0x49c
[ 297.178587] f2fs_get_dnode_of_data+0x404/0x950
[ 297.178589] f2fs_map_blocks+0x1e0/0xdf8
[ 297.178591] f2fs_mpage_readpages+0x1f0/0x8d0
[ 297.178594] f2fs_readahead+0x84/0x10c
[ 297.178596] read_pages+0xb8/0x434
[ 297.178603] page_cache_ra_unbounded+0x9c/0x2f0
[ 297.178605] page_cache_ra_order+0x2b0/0x348
[ 297.178608] do_sync_mmap_readahead+0xd0/0x228
[ 297.178612] filemap_fault+0x158/0x46c
[ 297.178615] f2fs_filemap_fault+0x28/0x114
[ 297.178617] handle_mm_fault+0x4f8/0x1468
[ 297.178620] do_page_fault+0x208/0x4b8
[ 297.178622] do_translation_fault+0x38/0x54
[ 297.178624] do_mem_abort+0x58/0x118
[ 297.178626] el0_da+0x48/0xb8
[ 297.178629] el0t_64_sync_handler+0x98/0xb4
[ 297.178632] el0t_64_sync+0x1a4/0x1a8
[ 297.178634] Code: 94000004 a8c17bfd d50323bf d65f03c0 (d4200080)
[ 297.178639] ---[ end trace 0000000000000000 ]---
The instruction d4200080 is the kprobe BRK instruction. The stack also
shows that the fault happens while handling a kprobe debug exception,
and the perf/trace path is entered from that window.
From the fulldump analysis, the issue appears to be related to the arm64
kprobe single-step/reentry handling. While a kprobe is preparing or
executing its XOL single-step instruction, perf/trace code may run in
the same window. With dwarf callchains enabled, this path may also
access user memory and take a data abort. In addition, another kprobe
may be hit while the first kprobe is still in KPROBE_HIT_SS state.
This matches the type of issue that was fixed on x86 by the following
commits:
6381c24cd6d5 ("kprobes/x86: Fix page-fault handling logic")
6a5022a56ac3 ("kprobes/x86: Allow to handle reentered kprobe on
single-stepping")
This patchset applies the same idea to arm64:
- Patch 1 makes the arm64 kprobe fault handler handle a fault in
KPROBE_HIT_SS/KPROBE_REENTER only when the faulting PC is the current
kprobe's XOL instruction. Otherwise, the fault is left to the normal
fault handling path.
- Patch 2 allows a kprobe hit in KPROBE_HIT_SS to be handled as a
recoverable one-level reentry. The unrecoverable case remains a hit
while already in KPROBE_REENTER.
With both patches applied, we have kept the same stress test running for
three days and the crash is no longer reproduced.
I still have the full dmesg and fulldump from the crash device. Please
let me know if any additional information would be useful.
Thanks,
hupu
^ permalink raw reply
* Re: [PATCH v2 2/4] rv/rtapp/sleep: Update nanosleep rule
From: Gabriele Monaco @ 2026-07-01 12:29 UTC (permalink / raw)
To: Nam Cao; +Cc: Steven Rostedt, linux-trace-kernel, linux-doc, linux-kernel
In-Reply-To: <c7ceb5c6263ee8f43a2676acae669cf486b0d903.1781852967.git.namcao@linutronix.de>
On Fri, 2026-06-19 at 09:21 +0200, Nam Cao wrote:
> CLOCK_REALTIME is the only clock that often is misused in real-time
> applications. The other clocks either are safe for real-time uses
> (CLOCK_TAI, CLOCK_MONOTONIC, CLOCK_BOOTTIME) or are unlikely to be misused
> (CLOCK_AUX, CLOCK_PROCESS_CPUTIME_ID).
>
> Update the monitor to only warn about CLOCK_REALTIME.
>
> While at it, update the out-of-sync documentation.
>
> Signed-off-by: Nam Cao <namcao@linutronix.de>
Looks good, thanks.
Reviewed-by: Gabriele Monaco <gmonaco@redhat.com>
> ---
> Documentation/trace/rv/monitor_rtapp.rst | 17 +++++---
> kernel/trace/rv/monitors/sleep/sleep.c | 12 ++----
> kernel/trace/rv/monitors/sleep/sleep.h | 52 +++++++++++------------
> tools/verification/models/rtapp/sleep.ltl | 2 +-
> 4 files changed, 39 insertions(+), 44 deletions(-)
>
> diff --git a/Documentation/trace/rv/monitor_rtapp.rst
> b/Documentation/trace/rv/monitor_rtapp.rst
> index 01656bf7080a..570be67a8f3b 100644
> --- a/Documentation/trace/rv/monitor_rtapp.rst
> +++ b/Documentation/trace/rv/monitor_rtapp.rst
> @@ -51,12 +51,13 @@ The `sleep` monitor reports real-time threads sleeping in
> a manner that may
> cause undesirable latency. Real-time applications should only put a real-time
> thread to sleep for one of the following reasons:
>
> - - Cyclic work: real-time thread sleeps waiting for the next cycle. For this
> - case, only the `clock_nanosleep` syscall should be used with
> `TIMER_ABSTIME`
> - (to avoid time drift) and `CLOCK_MONOTONIC` (to avoid the clock being
> - changed). No other method is safe for real-time. For example, threads
> - waiting for timerfd can be woken by softirq which provides no real-time
> - guarantee.
> + - Cyclic work: real-time thread sleeps waiting for the next
> + cycle. For this case, only the `clock_nanosleep` syscall should be
> + used with `TIMER_ABSTIME` (to avoid time drift). Additionally,
> + `CLOCK_REALTIME` should not be used (to avoid the clock being
> + changed). No other method is safe for real-time. For example,
> + threads waiting for timerfd can be woken by softirq which provides
> + no real-time guarantee.
> - Real-time thread waiting for something to happen (e.g. another thread
> releasing shared resources, or a completion signal from another thread).
> In
> this case, only futexes (FUTEX_LOCK_PI, FUTEX_LOCK_PI2 or one of
> @@ -99,14 +100,16 @@ The monitor's specification is::
>
> RT_VALID_SLEEP_REASON = FUTEX_WAIT
> or RT_FRIENDLY_NANOSLEEP
> + or EPOLL_WAIT
>
> RT_FRIENDLY_NANOSLEEP = CLOCK_NANOSLEEP
> and NANOSLEEP_TIMER_ABSTIME
> - and NANOSLEEP_CLOCK_MONOTONIC
> + and not NANOSLEEP_CLOCK_REALTIME
>
> RT_FRIENDLY_WAKE = WOKEN_BY_EQUAL_OR_HIGHER_PRIO
> or WOKEN_BY_HARDIRQ
> or WOKEN_BY_NMI
> + or ABORT_SLEEP
> or KTHREAD_SHOULD_STOP
>
> ALLOWLIST = BLOCK_ON_RT_MUTEX
> diff --git a/kernel/trace/rv/monitors/sleep/sleep.c
> b/kernel/trace/rv/monitors/sleep/sleep.c
> index d6b677fab8f8..638be7d8747f 100644
> --- a/kernel/trace/rv/monitors/sleep/sleep.c
> +++ b/kernel/trace/rv/monitors/sleep/sleep.c
> @@ -44,8 +44,7 @@ static void ltl_atoms_init(struct task_struct *task, struct
> ltl_monitor *mon, bo
>
> if (task_creation) {
> ltl_atom_set(mon, LTL_KTHREAD_SHOULD_STOP, false);
> - ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_MONOTONIC, false);
> - ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_TAI, false);
> + ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, false);
> ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, false);
> ltl_atom_set(mon, LTL_CLOCK_NANOSLEEP, false);
> ltl_atom_set(mon, LTL_FUTEX_WAIT, false);
> @@ -60,8 +59,7 @@ static void ltl_atoms_init(struct task_struct *task, struct
> ltl_monitor *mon, bo
> /* kernel tasks do not do syscall */
> ltl_atom_set(mon, LTL_FUTEX_WAIT, false);
> ltl_atom_set(mon, LTL_FUTEX_LOCK_PI, false);
> - ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_MONOTONIC, false);
> - ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_TAI, false);
> + ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, false);
> ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, false);
> ltl_atom_set(mon, LTL_CLOCK_NANOSLEEP, false);
> ltl_atom_set(mon, LTL_EPOLL_WAIT, false);
> @@ -136,8 +134,7 @@ static void handle_sys_enter(void *data, struct pt_regs
> *regs, long id)
> case __NR_clock_nanosleep_time64:
> #endif
> syscall_get_arguments(current, regs, args);
> - ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_MONOTONIC, args[0] ==
> CLOCK_MONOTONIC);
> - ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_TAI, args[0] ==
> CLOCK_TAI);
> + ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, args[0] ==
> CLOCK_REALTIME);
> ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, args[1] ==
> TIMER_ABSTIME);
> ltl_atom_update(current, LTL_CLOCK_NANOSLEEP, true);
> break;
> @@ -178,8 +175,7 @@ static void handle_sys_exit(void *data, struct pt_regs
> *regs, long ret)
>
> ltl_atom_set(mon, LTL_FUTEX_LOCK_PI, false);
> ltl_atom_set(mon, LTL_FUTEX_WAIT, false);
> - ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_MONOTONIC, false);
> - ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_TAI, false);
> + ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, false);
> ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, false);
> ltl_atom_set(mon, LTL_EPOLL_WAIT, false);
> ltl_atom_update(current, LTL_CLOCK_NANOSLEEP, false);
> diff --git a/kernel/trace/rv/monitors/sleep/sleep.h
> b/kernel/trace/rv/monitors/sleep/sleep.h
> index 403dc2852c52..2fe2ec7edae8 100644
> --- a/kernel/trace/rv/monitors/sleep/sleep.h
> +++ b/kernel/trace/rv/monitors/sleep/sleep.h
> @@ -20,8 +20,7 @@ enum ltl_atom {
> LTL_FUTEX_WAIT,
> LTL_KERNEL_THREAD,
> LTL_KTHREAD_SHOULD_STOP,
> - LTL_NANOSLEEP_CLOCK_MONOTONIC,
> - LTL_NANOSLEEP_CLOCK_TAI,
> + LTL_NANOSLEEP_CLOCK_REALTIME,
> LTL_NANOSLEEP_TIMER_ABSTIME,
> LTL_RT,
> LTL_SCHEDULE_IN,
> @@ -46,8 +45,7 @@ static const char *ltl_atom_str(enum ltl_atom atom)
> "fu_wa",
> "ker_th",
> "kth_sh_st",
> - "na_cl_mo",
> - "na_cl_ta",
> + "na_cl_re",
> "na_ti_ab",
> "rt",
> "sch_in",
> @@ -87,8 +85,7 @@ static void ltl_start(struct task_struct *task, struct
> ltl_monitor *mon)
> bool schedule_in = test_bit(LTL_SCHEDULE_IN, mon->atoms);
> bool rt = test_bit(LTL_RT, mon->atoms);
> bool nanosleep_timer_abstime = test_bit(LTL_NANOSLEEP_TIMER_ABSTIME,
> mon->atoms);
> - bool nanosleep_clock_tai = test_bit(LTL_NANOSLEEP_CLOCK_TAI, mon-
> >atoms);
> - bool nanosleep_clock_monotonic =
> test_bit(LTL_NANOSLEEP_CLOCK_MONOTONIC, mon->atoms);
> + bool nanosleep_clock_realtime =
> test_bit(LTL_NANOSLEEP_CLOCK_REALTIME, mon->atoms);
> bool kthread_should_stop = test_bit(LTL_KTHREAD_SHOULD_STOP, mon-
> >atoms);
> bool kernel_thread = test_bit(LTL_KERNEL_THREAD, mon->atoms);
> bool futex_wait = test_bit(LTL_FUTEX_WAIT, mon->atoms);
> @@ -97,17 +94,17 @@ static void ltl_start(struct task_struct *task, struct
> ltl_monitor *mon)
> bool clock_nanosleep = test_bit(LTL_CLOCK_NANOSLEEP, mon->atoms);
> bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
> bool abort_sleep = test_bit(LTL_ABORT_SLEEP, mon->atoms);
> - bool val42 = task_is_rcu || task_is_migration;
> - bool val43 = futex_lock_pi || val42;
> - bool val5 = block_on_rt_mutex || val43;
> - bool val34 = abort_sleep || kthread_should_stop;
> - bool val35 = woken_by_nmi || val34;
> - bool val36 = woken_by_hardirq || val35;
> - bool val14 = woken_by_equal_or_higher_prio || val36;
> + bool val41 = task_is_rcu || task_is_migration;
> + bool val42 = futex_lock_pi || val41;
> + bool val5 = block_on_rt_mutex || val42;
> + bool val33 = abort_sleep || kthread_should_stop;
> + bool val34 = woken_by_nmi || val33;
> + bool val35 = woken_by_hardirq || val34;
> + bool val14 = woken_by_equal_or_higher_prio || val35;
> bool val13 = !schedule_in;
> - bool val26 = nanosleep_clock_monotonic || nanosleep_clock_tai;
> - bool val27 = nanosleep_timer_abstime && val26;
> - bool val18 = clock_nanosleep && val27;
> + bool val25 = !nanosleep_clock_realtime;
> + bool val26 = nanosleep_timer_abstime && val25;
> + bool val18 = clock_nanosleep && val26;
> bool val20 = val18 || epoll_wait;
> bool val9 = futex_wait || val20;
> bool val11 = val9 || kernel_thread;
> @@ -138,8 +135,7 @@ ltl_possible_next_states(struct ltl_monitor *mon, unsigned
> int state, unsigned l
> bool schedule_in = test_bit(LTL_SCHEDULE_IN, mon->atoms);
> bool rt = test_bit(LTL_RT, mon->atoms);
> bool nanosleep_timer_abstime = test_bit(LTL_NANOSLEEP_TIMER_ABSTIME,
> mon->atoms);
> - bool nanosleep_clock_tai = test_bit(LTL_NANOSLEEP_CLOCK_TAI, mon-
> >atoms);
> - bool nanosleep_clock_monotonic =
> test_bit(LTL_NANOSLEEP_CLOCK_MONOTONIC, mon->atoms);
> + bool nanosleep_clock_realtime =
> test_bit(LTL_NANOSLEEP_CLOCK_REALTIME, mon->atoms);
> bool kthread_should_stop = test_bit(LTL_KTHREAD_SHOULD_STOP, mon-
> >atoms);
> bool kernel_thread = test_bit(LTL_KERNEL_THREAD, mon->atoms);
> bool futex_wait = test_bit(LTL_FUTEX_WAIT, mon->atoms);
> @@ -148,17 +144,17 @@ ltl_possible_next_states(struct ltl_monitor *mon,
> unsigned int state, unsigned l
> bool clock_nanosleep = test_bit(LTL_CLOCK_NANOSLEEP, mon->atoms);
> bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
> bool abort_sleep = test_bit(LTL_ABORT_SLEEP, mon->atoms);
> - bool val42 = task_is_rcu || task_is_migration;
> - bool val43 = futex_lock_pi || val42;
> - bool val5 = block_on_rt_mutex || val43;
> - bool val34 = abort_sleep || kthread_should_stop;
> - bool val35 = woken_by_nmi || val34;
> - bool val36 = woken_by_hardirq || val35;
> - bool val14 = woken_by_equal_or_higher_prio || val36;
> + bool val41 = task_is_rcu || task_is_migration;
> + bool val42 = futex_lock_pi || val41;
> + bool val5 = block_on_rt_mutex || val42;
> + bool val33 = abort_sleep || kthread_should_stop;
> + bool val34 = woken_by_nmi || val33;
> + bool val35 = woken_by_hardirq || val34;
> + bool val14 = woken_by_equal_or_higher_prio || val35;
> bool val13 = !schedule_in;
> - bool val26 = nanosleep_clock_monotonic || nanosleep_clock_tai;
> - bool val27 = nanosleep_timer_abstime && val26;
> - bool val18 = clock_nanosleep && val27;
> + bool val25 = !nanosleep_clock_realtime;
> + bool val26 = nanosleep_timer_abstime && val25;
> + bool val18 = clock_nanosleep && val26;
> bool val20 = val18 || epoll_wait;
> bool val9 = futex_wait || val20;
> bool val11 = val9 || kernel_thread;
> diff --git a/tools/verification/models/rtapp/sleep.ltl
> b/tools/verification/models/rtapp/sleep.ltl
> index 464c84b9df87..5923e58d7810 100644
> --- a/tools/verification/models/rtapp/sleep.ltl
> +++ b/tools/verification/models/rtapp/sleep.ltl
> @@ -9,7 +9,7 @@ RT_VALID_SLEEP_REASON = FUTEX_WAIT
>
> RT_FRIENDLY_NANOSLEEP = CLOCK_NANOSLEEP
> and NANOSLEEP_TIMER_ABSTIME
> - and (NANOSLEEP_CLOCK_MONOTONIC or NANOSLEEP_CLOCK_TAI)
> + and not NANOSLEEP_CLOCK_REALTIME
>
> RT_FRIENDLY_WAKE = WOKEN_BY_EQUAL_OR_HIGHER_PRIO
> or WOKEN_BY_HARDIRQ
^ permalink raw reply
* Re: [PATCH v2 1/4] rv/rtapp/sleep: Make the error more informative for user
From: Gabriele Monaco @ 2026-07-01 12:26 UTC (permalink / raw)
To: Nam Cao; +Cc: Steven Rostedt, linux-trace-kernel, linux-doc, linux-kernel
In-Reply-To: <d97b4b5c476e5792b6875ec9bbf8dc214f999516.1781852967.git.namcao@linutronix.de>
On Fri, 2026-06-19 at 09:21 +0200, Nam Cao wrote:
> -static void handle_sched_wakeup(void *data, struct task_struct *task)
> +static void handle_sched_exit(void *data, bool is_switch)
> {
> - ltl_atom_pulse(task, LTL_WAKE, true);
> + ltl_atom_pulse(current, LTL_SCHEDULE_IN, true);
> }
Well, this triggers also in a spurious schedule: current -> current, can
that be a problem? Otherwise you may either use sched_switch over next
or pulse only if is_switch.
It probably isn't a big deal though, since there's no SLEEP prior.. But
we may want to avoid the noise.
Looks good overall.
Reviewed-by: Gabriele Monaco <gmonaco@redhat.com>
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