* [PATCH v5] riscv: mm: Trace TLB flush requests
@ 2026-10-17 13:49 ` Roman 'Hedin' Storozhenko
0 siblings, 0 replies; 3+ messages in thread
From: Roman 'Hedin' Storozhenko @ 2026-10-17 13:49 UTC (permalink / raw)
To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers
Cc: Jisheng Zhang, Radim Krčmář, linux-riscv,
linux-kernel, linux-trace-kernel,
Roman 'Hedin' Storozhenko
Make RISC-V TLB flush requests observable together with the
architecture-specific context available when the request reaches
the RISC-V implementation.
The generic tlb:tlb_flush event provides reason and page-count
information. At the RISC-V implementation level, additional
context is available, including the invalidation range and stride,
ASID, target CPU mask, and invalidation scope.
This context is useful for debugging and workload analysis. The
target CPU mask shows the address-space CPU footprint involved in
an invalidation, while range and stride distinguish requests at
different mapping granularities. The ASID and scope help correlate
requests with MM, scheduler, IPI, and firmware activity.
Add riscv_tlb:riscv_tlb_flush_request in flush_tlb_all() and
__flush_tlb_range(). Record start, size, stride, ASID (or the
no-ASID sentinel), whether an mm is associated with the request,
the target CPU mask and its weight, and the derived scope.
Record the complete target mask because CPU identity cannot be
reconstructed from a count. Calculate its weight from the recorded
bitmap so both fields agree even if mm_cpumask() changes during
recording.
The event describes an invalidation request before dispatch. It
does not report whether a remote flush uses SBI RFENCE or Linux
cross-CPU coordination, since that choice is effectively established
during initialization rather than made for each request.
The address field (start) may contain kernel virtual addresses
for flushes involving kernel mappings. Require perf tracepoint
permissions even for counting-only events, since perf filters
could otherwise reveal address information through event counts.
Tested on QEMU virt with OpenSBI using local and shared-mm
mprotect()/munmap() workloads, exercising single, range, and
address-space invalidations. A MADV_PAGEOUT reclaim workload
exercised global flushes. All four scope values were observed.
Confirmed that trace-cmd records the event and that perf can count
it with sufficient privileges. With perf_event_paranoid=0,
unprivileged attempts to count the event using perf were denied,
as intended.
Built with and without CONFIG_PERF_EVENTS.
Signed-off-by: Roman 'Hedin' Storozhenko <romeusmeister@gmail.com>
---
Add a RISC-V tracepoint for observing TLB invalidation requests
before they are dispatched by the architecture-specific
implementation.
The generic tlb:tlb_flush event describes TLB flush activity using
reason and page-count information. The RISC-V event records
additional context, including the invalidation range and stride,
ASID, target CPU mask and its weight, and derived request scope.
This context helps correlate TLB invalidation activity with MM,
scheduler, IPI, and firmware activity. The event describes the
request rather than the execution backend. SBI RFENCE versus Linux
cross-CPU coordination is effectively determined at initialization
rather than selected independently for each request.
The target CPU mask weight is derived from the recorded bitmap to
keep both fields consistent under concurrent mm_cpumask() updates.
Since the address field (start) may contain kernel virtual
addresses, the event requires perf tracepoint permissions even
for counting-only access.
Tested on QEMU virt with OpenSBI using local and shared-mm
mprotect()/munmap() workloads. A MADV_PAGEOUT reclaim workload
on QEMU virt with APLIC+IMSIC exercised global flushes. All four
scope values -- single, range, address-space, and all -- were
observed.
Confirmed trace-cmd recording and privileged perf counting.
With perf_event_paranoid=0, unprivileged attempts to count the
event using perf were denied, as intended. The kernel was built
with and without CONFIG_PERF_EVENTS.
---
Changes in v5:
- Move the architecture-specific trace event definition under
arch/riscv/, as requested by Steven Rostedt.
- Calculate target_mask_weight from the recorded CPU mask to
keep both fields consistent under concurrent updates.
- Require perf tracepoint permissions for counting-only access,
since the event may expose kernel virtual addresses.
- Link to v4: https://lore.kernel.org/r/20261015-tlb_tracepoint-v4-1-f238a2bd8f7e@gmail.com
Changes in v4:
- Drop unrelated whitespace-only changes in arch/riscv/mm/tlbflush.c.
- Link to v3: https://lore.kernel.org/all/20261015-tlb_tracepoint-v3-1-eef305a3bb63@gmail.com/
Changes in v3:
- Rebase on current RISC-V for-next.
- Thanks to Jisheng Zhang for pointing out that SBI RFENCE versus Linux
cross-CPU coordination is effectively a system-wide choice established
during initialization rather than a per-request decision.
- Drop the local/SBI RFENCE/cross-CPU path field.
- Rename the event from riscv_tlb_flush_path to
riscv_tlb_flush_request and refocus it on the semantic TLB
invalidation request.
- Emit one request event independently of the mechanism subsequently
used to perform the invalidation.
- Address checkpatch.pl --strict alignment issues.
- Link to v2: https://lore.kernel.org/r/20260830-tlb_tracepoint-v2-1-e3c88c24df5b@gmail.com
Changes in v2:
- Use trace_call__riscv_tlb_flush_path() after the explicit
trace_riscv_tlb_flush_path_enabled() check to avoid a second
tracepoint static-key test, as suggested by Steven Rostedt.
- Link to v1: https://lore.kernel.org/r/20260829-tlb_tracepoint-v1-1-dfdaede7e741@gmail.com
---
arch/riscv/include/asm/riscv_tlb.h | 103 +++++++++++++++++++++++++++++++++++++
arch/riscv/mm/tlbflush.c | 45 ++++++++++++++--
2 files changed, 144 insertions(+), 4 deletions(-)
diff --git a/arch/riscv/include/asm/riscv_tlb.h b/arch/riscv/include/asm/riscv_tlb.h
new file mode 100644
index 000000000000..2c438f6bc0d4
--- /dev/null
+++ b/arch/riscv/include/asm/riscv_tlb.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM riscv_tlb
+
+#if !defined(_TRACE_RISCV_TLB_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_RISCV_TLB_H
+
+#include <linux/cpumask.h>
+#include <linux/tracepoint.h>
+#include <linux/perf_event.h>
+
+#ifndef _TRACE_RISCV_TLB_ENUMS
+#define _TRACE_RISCV_TLB_ENUMS
+
+enum riscv_tlb_flush_scope {
+ RISCV_TLB_FLUSH_SCOPE_SINGLE,
+ RISCV_TLB_FLUSH_SCOPE_RANGE,
+ RISCV_TLB_FLUSH_SCOPE_ADDRESS_SPACE,
+ RISCV_TLB_FLUSH_SCOPE_ALL,
+};
+
+#endif /* _TRACE_RISCV_TLB_ENUMS */
+
+TRACE_DEFINE_ENUM(RISCV_TLB_FLUSH_SCOPE_SINGLE);
+TRACE_DEFINE_ENUM(RISCV_TLB_FLUSH_SCOPE_RANGE);
+TRACE_DEFINE_ENUM(RISCV_TLB_FLUSH_SCOPE_ADDRESS_SPACE);
+TRACE_DEFINE_ENUM(RISCV_TLB_FLUSH_SCOPE_ALL);
+
+#define show_riscv_tlb_flush_scope(scope) \
+ __print_symbolic(scope, \
+ { RISCV_TLB_FLUSH_SCOPE_SINGLE, "single" }, \
+ { RISCV_TLB_FLUSH_SCOPE_RANGE, "range" }, \
+ { RISCV_TLB_FLUSH_SCOPE_ADDRESS_SPACE, "address-space" }, \
+ { RISCV_TLB_FLUSH_SCOPE_ALL, "all" })
+
+/*
+ * Record the invalidation request received by the RISC-V architecture code
+ *
+ * The target CPU mask represents the CPUs Linux intends to cover for the
+ * request. It can be correlated with per-CPU activity, but does not describe
+ * which harts ultimately performed an invalidation.
+ *
+ * The ASID is hardware-visible and may be reused. It must not be treated as a
+ * persistent identifier for an mm.
+ *
+ * The stride describes the invalidation granularity supplied to the RISC-V
+ * implementation.
+ *
+ * The event records the invalidation request before it is dispatched. It does
+ * not describe the mechanism used to execute the request.
+ *
+ */
+TRACE_EVENT(riscv_tlb_flush_request,
+ TP_PROTO(unsigned long start, unsigned long size,
+ unsigned long stride, unsigned long asid, bool has_mm,
+ const struct cpumask *cmask,
+ enum riscv_tlb_flush_scope scope),
+
+ TP_ARGS(start, size, stride, asid, has_mm, cmask, scope),
+
+ TP_STRUCT__entry(__field(unsigned long, start)
+ __field(unsigned long, size)
+ __field(unsigned long, stride)
+ __field(unsigned long, asid)
+ __field(bool, has_mm)
+ __field(unsigned int, target_mask_weight)
+ __cpumask(target_cpus)
+ __field(u8, scope)
+ ),
+
+ TP_fast_assign(__entry->start = start;
+ __entry->size = size;
+ __entry->stride = stride;
+ __entry->asid = asid;
+ __entry->has_mm = has_mm;
+ __assign_cpumask(target_cpus, cpumask_bits(cmask));
+ __entry->target_mask_weight = cpumask_weight((const struct cpumask *)
+ __get_cpumask(target_cpus));
+ __entry->scope = scope;
+ ),
+
+ TP_printk("start=%#lx size=%#lx stride=%#lx asid=%#lx has_mm=%d target_mask_weight=%u target_cpus=%s scope=%s",
+ __entry->start, __entry->size, __entry->stride,
+ __entry->asid, __entry->has_mm,
+ __entry->target_mask_weight, __get_cpumask(target_cpus),
+ show_riscv_tlb_flush_scope(__entry->scope))
+);
+
+#ifdef CONFIG_PERF_EVENTS
+TRACE_EVENT_PERF_PERM(riscv_tlb_flush_request,
+ perf_allow_tracepoint());
+#endif
+
+#endif /* _TRACE_RISCV_TLB_H */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+
+#define TRACE_INCLUDE_PATH asm
+#define TRACE_INCLUDE_FILE riscv_tlb
+
+/* This part must be outside protection. */
+#include <trace/define_trace.h>
diff --git a/arch/riscv/mm/tlbflush.c b/arch/riscv/mm/tlbflush.c
index 962db300a166..f66348a9b999 100644
--- a/arch/riscv/mm/tlbflush.c
+++ b/arch/riscv/mm/tlbflush.c
@@ -9,6 +9,9 @@
#include <asm/mmu_context.h>
#include <asm/cpufeature.h>
+#define CREATE_TRACE_POINTS
+#include <asm/riscv_tlb.h>
+
#define has_svinval() riscv_has_extension_unlikely(RISCV_ISA_EXT_SVINVAL)
/*
@@ -63,6 +66,32 @@ void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
local_flush_tlb_range_asid(start, end - start, PAGE_SIZE, FLUSH_TLB_NO_ASID);
}
+static enum riscv_tlb_flush_scope
+riscv_tlb_get_flush_scope(unsigned long size, unsigned long stride, bool has_mm)
+{
+ if (size == FLUSH_TLB_MAX_SIZE)
+ return has_mm ? RISCV_TLB_FLUSH_SCOPE_ADDRESS_SPACE :
+ RISCV_TLB_FLUSH_SCOPE_ALL;
+
+ return size <= stride ? RISCV_TLB_FLUSH_SCOPE_SINGLE :
+ RISCV_TLB_FLUSH_SCOPE_RANGE;
+}
+
+static __always_inline void
+riscv_tlb_trace_flush_request(const struct cpumask *cmask, unsigned long start,
+ unsigned long size, unsigned long stride,
+ unsigned long asid, bool has_mm)
+{
+ enum riscv_tlb_flush_scope scope;
+
+ if (!trace_riscv_tlb_flush_request_enabled())
+ return;
+
+ scope = riscv_tlb_get_flush_scope(size, stride, has_mm);
+ trace_call__riscv_tlb_flush_request(start, size, stride, asid, has_mm,
+ cmask, scope);
+}
+
static void __ipi_flush_tlb_all(void *info)
{
local_flush_tlb_all();
@@ -70,12 +99,18 @@ static void __ipi_flush_tlb_all(void *info)
void flush_tlb_all(void)
{
- if (num_online_cpus() < 2)
+ riscv_tlb_trace_flush_request(cpu_online_mask, 0,
+ FLUSH_TLB_MAX_SIZE, 0,
+ FLUSH_TLB_NO_ASID, false);
+
+ if (num_online_cpus() < 2) {
local_flush_tlb_all();
- else if (riscv_use_sbi_for_rfence())
- sbi_remote_sfence_vma_asid(NULL, 0, FLUSH_TLB_MAX_SIZE, FLUSH_TLB_NO_ASID);
- else
+ } else if (riscv_use_sbi_for_rfence()) {
+ sbi_remote_sfence_vma_asid(NULL, 0, FLUSH_TLB_MAX_SIZE,
+ FLUSH_TLB_NO_ASID);
+ } else {
on_each_cpu(__ipi_flush_tlb_all, NULL, 1);
+ }
}
struct flush_tlb_range_data {
@@ -105,6 +140,8 @@ static void __flush_tlb_range(struct mm_struct *mm,
cpu = get_cpu();
+ riscv_tlb_trace_flush_request(cmask, start, size, stride, asid, !!mm);
+
/* Check if the TLB flush needs to be sent to other CPUs. */
if (cpumask_any_but(cmask, cpu) >= nr_cpu_ids) {
local_flush_tlb_range_asid(start, size, stride, asid);
---
base-commit: a5ae1bf65cee84bcc121d659e7e22a767853f5f5
change-id: 20260829-tlb_tracepoint-844105ab5092
Best regards,
--
Roman 'Hedin' Storozhenko <romeusmeister@gmail.com>
^ permalink raw reply related [flat|nested] 3+ messages in thread* [PATCH v5] riscv: mm: Trace TLB flush requests
@ 2026-10-17 13:49 ` Roman 'Hedin' Storozhenko
0 siblings, 0 replies; 3+ messages in thread
From: Roman 'Hedin' Storozhenko @ 2026-10-17 13:49 UTC (permalink / raw)
To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers
Cc: Jisheng Zhang, Radim Krčmář, linux-riscv,
linux-kernel, linux-trace-kernel,
Roman 'Hedin' Storozhenko
Make RISC-V TLB flush requests observable together with the
architecture-specific context available when the request reaches
the RISC-V implementation.
The generic tlb:tlb_flush event provides reason and page-count
information. At the RISC-V implementation level, additional
context is available, including the invalidation range and stride,
ASID, target CPU mask, and invalidation scope.
This context is useful for debugging and workload analysis. The
target CPU mask shows the address-space CPU footprint involved in
an invalidation, while range and stride distinguish requests at
different mapping granularities. The ASID and scope help correlate
requests with MM, scheduler, IPI, and firmware activity.
Add riscv_tlb:riscv_tlb_flush_request in flush_tlb_all() and
__flush_tlb_range(). Record start, size, stride, ASID (or the
no-ASID sentinel), whether an mm is associated with the request,
the target CPU mask and its weight, and the derived scope.
Record the complete target mask because CPU identity cannot be
reconstructed from a count. Calculate its weight from the recorded
bitmap so both fields agree even if mm_cpumask() changes during
recording.
The event describes an invalidation request before dispatch. It
does not report whether a remote flush uses SBI RFENCE or Linux
cross-CPU coordination, since that choice is effectively established
during initialization rather than made for each request.
The address field (start) may contain kernel virtual addresses
for flushes involving kernel mappings. Require perf tracepoint
permissions even for counting-only events, since perf filters
could otherwise reveal address information through event counts.
Tested on QEMU virt with OpenSBI using local and shared-mm
mprotect()/munmap() workloads, exercising single, range, and
address-space invalidations. A MADV_PAGEOUT reclaim workload
exercised global flushes. All four scope values were observed.
Confirmed that trace-cmd records the event and that perf can count
it with sufficient privileges. With perf_event_paranoid=0,
unprivileged attempts to count the event using perf were denied,
as intended.
Built with and without CONFIG_PERF_EVENTS.
Signed-off-by: Roman 'Hedin' Storozhenko <romeusmeister@gmail.com>
---
Add a RISC-V tracepoint for observing TLB invalidation requests
before they are dispatched by the architecture-specific
implementation.
The generic tlb:tlb_flush event describes TLB flush activity using
reason and page-count information. The RISC-V event records
additional context, including the invalidation range and stride,
ASID, target CPU mask and its weight, and derived request scope.
This context helps correlate TLB invalidation activity with MM,
scheduler, IPI, and firmware activity. The event describes the
request rather than the execution backend. SBI RFENCE versus Linux
cross-CPU coordination is effectively determined at initialization
rather than selected independently for each request.
The target CPU mask weight is derived from the recorded bitmap to
keep both fields consistent under concurrent mm_cpumask() updates.
Since the address field (start) may contain kernel virtual
addresses, the event requires perf tracepoint permissions even
for counting-only access.
Tested on QEMU virt with OpenSBI using local and shared-mm
mprotect()/munmap() workloads. A MADV_PAGEOUT reclaim workload
on QEMU virt with APLIC+IMSIC exercised global flushes. All four
scope values -- single, range, address-space, and all -- were
observed.
Confirmed trace-cmd recording and privileged perf counting.
With perf_event_paranoid=0, unprivileged attempts to count the
event using perf were denied, as intended. The kernel was built
with and without CONFIG_PERF_EVENTS.
---
Changes in v5:
- Move the architecture-specific trace event definition under
arch/riscv/, as requested by Steven Rostedt.
- Calculate target_mask_weight from the recorded CPU mask to
keep both fields consistent under concurrent updates.
- Require perf tracepoint permissions for counting-only access,
since the event may expose kernel virtual addresses.
- Link to v4: https://lore.kernel.org/r/20261015-tlb_tracepoint-v4-1-f238a2bd8f7e@gmail.com
Changes in v4:
- Drop unrelated whitespace-only changes in arch/riscv/mm/tlbflush.c.
- Link to v3: https://lore.kernel.org/all/20261015-tlb_tracepoint-v3-1-eef305a3bb63@gmail.com/
Changes in v3:
- Rebase on current RISC-V for-next.
- Thanks to Jisheng Zhang for pointing out that SBI RFENCE versus Linux
cross-CPU coordination is effectively a system-wide choice established
during initialization rather than a per-request decision.
- Drop the local/SBI RFENCE/cross-CPU path field.
- Rename the event from riscv_tlb_flush_path to
riscv_tlb_flush_request and refocus it on the semantic TLB
invalidation request.
- Emit one request event independently of the mechanism subsequently
used to perform the invalidation.
- Address checkpatch.pl --strict alignment issues.
- Link to v2: https://lore.kernel.org/r/20260830-tlb_tracepoint-v2-1-e3c88c24df5b@gmail.com
Changes in v2:
- Use trace_call__riscv_tlb_flush_path() after the explicit
trace_riscv_tlb_flush_path_enabled() check to avoid a second
tracepoint static-key test, as suggested by Steven Rostedt.
- Link to v1: https://lore.kernel.org/r/20260829-tlb_tracepoint-v1-1-dfdaede7e741@gmail.com
---
arch/riscv/include/asm/riscv_tlb.h | 103 +++++++++++++++++++++++++++++++++++++
arch/riscv/mm/tlbflush.c | 45 ++++++++++++++--
2 files changed, 144 insertions(+), 4 deletions(-)
diff --git a/arch/riscv/include/asm/riscv_tlb.h b/arch/riscv/include/asm/riscv_tlb.h
new file mode 100644
index 000000000000..2c438f6bc0d4
--- /dev/null
+++ b/arch/riscv/include/asm/riscv_tlb.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM riscv_tlb
+
+#if !defined(_TRACE_RISCV_TLB_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_RISCV_TLB_H
+
+#include <linux/cpumask.h>
+#include <linux/tracepoint.h>
+#include <linux/perf_event.h>
+
+#ifndef _TRACE_RISCV_TLB_ENUMS
+#define _TRACE_RISCV_TLB_ENUMS
+
+enum riscv_tlb_flush_scope {
+ RISCV_TLB_FLUSH_SCOPE_SINGLE,
+ RISCV_TLB_FLUSH_SCOPE_RANGE,
+ RISCV_TLB_FLUSH_SCOPE_ADDRESS_SPACE,
+ RISCV_TLB_FLUSH_SCOPE_ALL,
+};
+
+#endif /* _TRACE_RISCV_TLB_ENUMS */
+
+TRACE_DEFINE_ENUM(RISCV_TLB_FLUSH_SCOPE_SINGLE);
+TRACE_DEFINE_ENUM(RISCV_TLB_FLUSH_SCOPE_RANGE);
+TRACE_DEFINE_ENUM(RISCV_TLB_FLUSH_SCOPE_ADDRESS_SPACE);
+TRACE_DEFINE_ENUM(RISCV_TLB_FLUSH_SCOPE_ALL);
+
+#define show_riscv_tlb_flush_scope(scope) \
+ __print_symbolic(scope, \
+ { RISCV_TLB_FLUSH_SCOPE_SINGLE, "single" }, \
+ { RISCV_TLB_FLUSH_SCOPE_RANGE, "range" }, \
+ { RISCV_TLB_FLUSH_SCOPE_ADDRESS_SPACE, "address-space" }, \
+ { RISCV_TLB_FLUSH_SCOPE_ALL, "all" })
+
+/*
+ * Record the invalidation request received by the RISC-V architecture code
+ *
+ * The target CPU mask represents the CPUs Linux intends to cover for the
+ * request. It can be correlated with per-CPU activity, but does not describe
+ * which harts ultimately performed an invalidation.
+ *
+ * The ASID is hardware-visible and may be reused. It must not be treated as a
+ * persistent identifier for an mm.
+ *
+ * The stride describes the invalidation granularity supplied to the RISC-V
+ * implementation.
+ *
+ * The event records the invalidation request before it is dispatched. It does
+ * not describe the mechanism used to execute the request.
+ *
+ */
+TRACE_EVENT(riscv_tlb_flush_request,
+ TP_PROTO(unsigned long start, unsigned long size,
+ unsigned long stride, unsigned long asid, bool has_mm,
+ const struct cpumask *cmask,
+ enum riscv_tlb_flush_scope scope),
+
+ TP_ARGS(start, size, stride, asid, has_mm, cmask, scope),
+
+ TP_STRUCT__entry(__field(unsigned long, start)
+ __field(unsigned long, size)
+ __field(unsigned long, stride)
+ __field(unsigned long, asid)
+ __field(bool, has_mm)
+ __field(unsigned int, target_mask_weight)
+ __cpumask(target_cpus)
+ __field(u8, scope)
+ ),
+
+ TP_fast_assign(__entry->start = start;
+ __entry->size = size;
+ __entry->stride = stride;
+ __entry->asid = asid;
+ __entry->has_mm = has_mm;
+ __assign_cpumask(target_cpus, cpumask_bits(cmask));
+ __entry->target_mask_weight = cpumask_weight((const struct cpumask *)
+ __get_cpumask(target_cpus));
+ __entry->scope = scope;
+ ),
+
+ TP_printk("start=%#lx size=%#lx stride=%#lx asid=%#lx has_mm=%d target_mask_weight=%u target_cpus=%s scope=%s",
+ __entry->start, __entry->size, __entry->stride,
+ __entry->asid, __entry->has_mm,
+ __entry->target_mask_weight, __get_cpumask(target_cpus),
+ show_riscv_tlb_flush_scope(__entry->scope))
+);
+
+#ifdef CONFIG_PERF_EVENTS
+TRACE_EVENT_PERF_PERM(riscv_tlb_flush_request,
+ perf_allow_tracepoint());
+#endif
+
+#endif /* _TRACE_RISCV_TLB_H */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+
+#define TRACE_INCLUDE_PATH asm
+#define TRACE_INCLUDE_FILE riscv_tlb
+
+/* This part must be outside protection. */
+#include <trace/define_trace.h>
diff --git a/arch/riscv/mm/tlbflush.c b/arch/riscv/mm/tlbflush.c
index 962db300a166..f66348a9b999 100644
--- a/arch/riscv/mm/tlbflush.c
+++ b/arch/riscv/mm/tlbflush.c
@@ -9,6 +9,9 @@
#include <asm/mmu_context.h>
#include <asm/cpufeature.h>
+#define CREATE_TRACE_POINTS
+#include <asm/riscv_tlb.h>
+
#define has_svinval() riscv_has_extension_unlikely(RISCV_ISA_EXT_SVINVAL)
/*
@@ -63,6 +66,32 @@ void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
local_flush_tlb_range_asid(start, end - start, PAGE_SIZE, FLUSH_TLB_NO_ASID);
}
+static enum riscv_tlb_flush_scope
+riscv_tlb_get_flush_scope(unsigned long size, unsigned long stride, bool has_mm)
+{
+ if (size == FLUSH_TLB_MAX_SIZE)
+ return has_mm ? RISCV_TLB_FLUSH_SCOPE_ADDRESS_SPACE :
+ RISCV_TLB_FLUSH_SCOPE_ALL;
+
+ return size <= stride ? RISCV_TLB_FLUSH_SCOPE_SINGLE :
+ RISCV_TLB_FLUSH_SCOPE_RANGE;
+}
+
+static __always_inline void
+riscv_tlb_trace_flush_request(const struct cpumask *cmask, unsigned long start,
+ unsigned long size, unsigned long stride,
+ unsigned long asid, bool has_mm)
+{
+ enum riscv_tlb_flush_scope scope;
+
+ if (!trace_riscv_tlb_flush_request_enabled())
+ return;
+
+ scope = riscv_tlb_get_flush_scope(size, stride, has_mm);
+ trace_call__riscv_tlb_flush_request(start, size, stride, asid, has_mm,
+ cmask, scope);
+}
+
static void __ipi_flush_tlb_all(void *info)
{
local_flush_tlb_all();
@@ -70,12 +99,18 @@ static void __ipi_flush_tlb_all(void *info)
void flush_tlb_all(void)
{
- if (num_online_cpus() < 2)
+ riscv_tlb_trace_flush_request(cpu_online_mask, 0,
+ FLUSH_TLB_MAX_SIZE, 0,
+ FLUSH_TLB_NO_ASID, false);
+
+ if (num_online_cpus() < 2) {
local_flush_tlb_all();
- else if (riscv_use_sbi_for_rfence())
- sbi_remote_sfence_vma_asid(NULL, 0, FLUSH_TLB_MAX_SIZE, FLUSH_TLB_NO_ASID);
- else
+ } else if (riscv_use_sbi_for_rfence()) {
+ sbi_remote_sfence_vma_asid(NULL, 0, FLUSH_TLB_MAX_SIZE,
+ FLUSH_TLB_NO_ASID);
+ } else {
on_each_cpu(__ipi_flush_tlb_all, NULL, 1);
+ }
}
struct flush_tlb_range_data {
@@ -105,6 +140,8 @@ static void __flush_tlb_range(struct mm_struct *mm,
cpu = get_cpu();
+ riscv_tlb_trace_flush_request(cmask, start, size, stride, asid, !!mm);
+
/* Check if the TLB flush needs to be sent to other CPUs. */
if (cpumask_any_but(cmask, cpu) >= nr_cpu_ids) {
local_flush_tlb_range_asid(start, size, stride, asid);
---
base-commit: a5ae1bf65cee84bcc121d659e7e22a767853f5f5
change-id: 20260829-tlb_tracepoint-844105ab5092
Best regards,
--
Roman 'Hedin' Storozhenko <romeusmeister@gmail.com>
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH v5] riscv: mm: Trace TLB flush requests
2026-10-17 13:49 ` Roman 'Hedin' Storozhenko
(?)
@ 2026-10-17 14:03 ` sashiko-bot
-1 siblings, 0 replies; 3+ messages in thread
From: sashiko-bot @ 2026-10-17 14:03 UTC (permalink / raw)
To: Roman 'Hedin' Storozhenko; +Cc: linux-trace-kernel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] riscv: mm: unaligned cpumask_weight() access in riscv_tlb_flush_request
--
commit d6aa8f46821c6cdff87980399804155c8220831a
Author: Roman 'Hedin' Storozhenko <romeusmeister@gmail.com>
riscv: mm: Trace TLB flush requests
This commit adds the riscv_tlb:riscv_tlb_flush_request tracepoint in
flush_tlb_all() and __flush_tlb_range() to record TLB invalidation
requests along with range, stride, ASID, target CPU mask, and derived
invalidation scope.
> diff --git a/arch/riscv/include/asm/riscv_tlb.h b/arch/riscv/include/asm/riscv_tlb.h
> new file mode 100644
> index 0000000000000..2c438f6bc0d42
> --- /dev/null
> +++ b/arch/riscv/include/asm/riscv_tlb.h
> @@ -0,0 +1,103 @@
[ ... ]
> +TRACE_EVENT(riscv_tlb_flush_request,
> + TP_PROTO(unsigned long start, unsigned long size,
> + unsigned long stride, unsigned long asid, bool has_mm,
> + const struct cpumask *cmask,
> + enum riscv_tlb_flush_scope scope),
> +
> + TP_ARGS(start, size, stride, asid, has_mm, cmask, scope),
> +
> + TP_STRUCT__entry(__field(unsigned long, start)
> + __field(unsigned long, size)
> + __field(unsigned long, stride)
> + __field(unsigned long, asid)
> + __field(bool, has_mm)
> + __field(unsigned int, target_mask_weight)
> + __cpumask(target_cpus)
> + __field(u8, scope)
> + ),
> +
> + TP_fast_assign(__entry->start = start;
> + __entry->size = size;
> + __entry->stride = stride;
> + __entry->asid = asid;
> + __entry->has_mm = has_mm;
> + __assign_cpumask(target_cpus, cpumask_bits(cmask));
> + __entry->target_mask_weight = cpumask_weight((const struct cpumask *)
> + __get_cpumask(target_cpus));
> + __entry->scope = scope;
> + ),
[Severity: High]
Does casting __get_cpumask(target_cpus) to const struct cpumask * and
passing it to cpumask_weight() perform unaligned unsigned long loads?
In DECLARE_EVENT_CLASS() in include/trace/trace_events.h, char __data[]
immediately follows the fields declared in TP_STRUCT__entry():
struct trace_event_raw_##name {
struct trace_entry ent;
tstruct
char __data[];
};
Because __field(u8, scope) is placed at the end of TP_STRUCT__entry(),
offsetof(struct trace_event_raw_riscv_tlb_flush_request, __data) is 53 on
RV64 (and 37 on RV32). In include/trace/stages/stage5_get_offsets.h,
__dynamic_array() places target_cpus at __data_size +
offsetof(typeof(*entry), __data) without aligning the offset to
sizeof(unsigned long). (Even without the trailing u8 field, the offset on
RV64 would be 52, and ring_buffer_event_data() only guarantees 4-byte
alignment rather than 8-byte alignment.)
When riscv_tlb_flush_request is enabled and triggered via flush_tlb_all()
or __flush_tlb_range(), TP_fast_assign() copies cmask into that unaligned
offset with memcpy() and then calls:
cpumask_weight((const struct cpumask *)__get_cpumask(target_cpus))
-> bitmap_weight(cpumask_bits(srcp), small_cpumask_bits)
which dereferences const unsigned long * from an odd byte address. In
addition to triggering UBSAN alignment warnings under
CONFIG_UBSAN_ALIGNMENT, on RISC-V systems without hardware unaligned
access support this raises a misaligned load trap on every tracepoint hit
(and during TP_printk() formatting via trace_print_bitmask_seq()), causing
a kernel oops in do_trap_misaligned() if handle_scalar_misaligned_load()
is disabled (CONFIG_RISCV_SCALAR_MISALIGNED=n or unaligned_enabled=0).
[ ... ]
> + TP_printk("start=%#lx size=%#lx stride=%#lx asid=%#lx has_mm=%d target_mask_weight=%u target_cpus=%s scope=%s",
> + __entry->start, __entry->size, __entry->stride,
> + __entry->asid, __entry->has_mm,
> + __entry->target_mask_weight, __get_cpumask(target_cpus),
> + show_riscv_tlb_flush_scope(__entry->scope))
> +);
[ ... ]
> --
> 2.47.3
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261017-tlb_tracepoint-v5-1-256fb5393a7f@gmail.com?part=1
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-17 14:03 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-17 13:49 [PATCH v5] riscv: mm: Trace TLB flush requests Roman 'Hedin' Storozhenko
2026-10-17 13:49 ` Roman 'Hedin' Storozhenko
2026-10-17 14:03 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.