* [PATCH v2 0/2] target: riscv: support TLB page flush for sfence.vma
@ 2026-09-04 18:43 Trevor Gamblin
2026-09-04 18:43 ` [PATCH v2 1/2] target/riscv: extract check_sfence_vma() from helper_tlb_flush() Trevor Gamblin
` (2 more replies)
0 siblings, 3 replies; 17+ messages in thread
From: Trevor Gamblin @ 2026-09-04 18:43 UTC (permalink / raw)
To: qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, qemu-riscv,
Trevor Gamblin
Introduce a new function, check_sfence_vma(), to handle
privilege/hypervisor checks currently performed in helper_tlb_flush(),
and make use of it in that function. Then, add helper_tlb_flush_page()
for flushing only a single TLB page, using check_sfence_vma() inside it
similarly to helper_tlb_flush(). Finally, rework the trans_sfence_vma()
function to use helper_tlb_flush_page() when rs1 != 0.
The second patch also includes the fix for a regression in the
'test_tuxrun.TuxRunRiscV64Test.test_riscv64_maxcpu' test encountered
with the draft version of this series, which hanged indefinitely until
killed when the patches were applied. This was fixed by making sure to
use get_address() to canonicalize the address being passed to
helper_tlb_flush_page().
make-check and make-functional were both run with the patches applied on
master, and no regressions for riscv64 were observed (beyond the one
mentioned as fixed above).
Daniel Barboza helped me with initial triage, direction, and cleaning up
this series for submission - thanks again!
Some background context: as part of the RISE Project[1] mission to
improve the RISC-V software ecosystem, the Yocto Project[2] is receiving
assistance with triaging bugs and enhancing RISC-V support in the core
layer. The 'qemuriscv32' and 'qemuriscv64' targets were enabled as part
of the Yocto's CI system, known as the Autobuilder[3] in 2025, and since
that time various intermittent failures have been observed on the
'ptest' ('package test', where a package's test suites are bundled into
an image and then booted to do basic runtime compatibility testing)
case, 'qemuriscv64-ptest'. These almost always manifest as timeouts in
the guest, usually around heavier and/or more complex test cases, and
they additionally seem to occur only under heavy host system (x86_64)
load.
Comparing to other major architectures like x86_64 and arm64, the
riscv64 guests are always emulated in TCG mode, since the Yocto
Autobuilder cluster does not (yet) have native riscv64 hosts to dedicate
for runtime testing with kvm support. While this is likely a major
factor in why qemuriscv64 runs fail at higher rates than other
architectures, it was noted after compiling the triage data[4] that
local comparisons of the same ptest images running on TCG-mode qemuarm64
and qemuriscv64 showed the latter taking significantly longer to
complete (using some of the test suites which have been observed failing
as examples), even with no other meaningful system load on the host. In
particular, the coreutils case showed over 70% longer runtime for
qemuriscv64 compared to qemuarm64 (~1000s vs ~580s).
Fully investigating the source of this discrepancy was a long process.
For Yocto builds, native QEMU binaries are built from source as part of
the overall image build, so the first place to look was in QEMU's
configuration and boot options. Other suggestions included trying to
disable TCG plugins or alter the QEMU and guest package configs (Yocto's
QEMU is RVA23S64 profile by default, while the runtime packages are
built for riscv64gc). However, these changes either produced no
meaningful effect or actually increased runtime. The next step was to
investigate the QEMU source, which is what ultimately led to this patch
series. It turns out that these changes significantly improve the
reference runtime test case for coreutils in Yocto, although it is still
somewhat slower than in qemuarm64. It remains to be seen whether these
changes to the QEMU source eliminate the intermittent issues in Yocto's
testing, but it is a meaningful improvement which opens the door to
further optimizations for RISC-V.
As an example, compare the before and after results on a local run of
core-image-ptest-coreutils for qemuriscv64:
|============================================================================
|Testsuite summary for GNU coreutils 9.11
|============================================================================
|# TOTAL: 733
|# PASS: 561
|# SKIP: 172
|# XFAIL: 0
|# FAIL: 0
|# XPASS: 0
|# ERROR: 0
|============================================================================
|make[1]: Leaving directory '/usr/lib/coreutils/ptest'
|DURATION: 1049
|END: /usr/lib/coreutils/ptest
|2026-08-31T17:23
|STOP: ptest-runner
|TOTAL: 1 FAIL: 0
versus:
|============================================================================
|Testsuite summary for GNU coreutils 9.11
|============================================================================
|# TOTAL: 733
|# PASS: 561
|# SKIP: 172
|# XFAIL: 0
|# FAIL: 0
|# XPASS: 0
|# ERROR: 0
|============================================================================
|make[1]: Leaving directory '/usr/lib/coreutils/ptest'
|DURATION: 646
|END: /usr/lib/coreutils/ptest
|2026-08-31T16:10
|STOP: ptest-runner
|TOTAL: 1 FAIL: 0
|root@qemuriscv64:~#
Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
[1]: https://riseproject.dev/
[2]: https://www.yoctoproject.org/
[3]: https://autobuilder.yoctoproject.org/valkyrie/
[4]: https://docs.google.com/document/d/1HUk-RvMDdW2EKV-qE1VNfZBycPscQUsayEZ5AgJ85C8
---
Changes in v2:
- Renamed sfence_vma_allowed() to check_sfence_vma()
- Squashed patches 2 and 3 together
- Reworked check_sfence_vma() and helpers using it to be consistent with
rest of codebase (e.g. no unnecessary returns)
- Adjusted cover letter to match check_sfence_vma() naming
- Link to v1: https://lore.kernel.org/qemu-devel/20260831-riscv_tlb_page_flush-v1-0-4fc6af95673a@baylibre.com
To: qemu-devel@nongnu.org
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Alistair Francis <alistair.francis@wdc.com>
Cc: Weiwei Li <liwei1518@gmail.com>
Cc: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
Cc: Liu Zhiwei <zhiwei_liu@linux.alibaba.com>
Cc: Chao Liu <chao.liu@processmission.com>
Cc: qemu-riscv@nongnu.org
---
Trevor Gamblin (2):
target/riscv: extract check_sfence_vma() from helper_tlb_flush()
target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand
target/riscv/helper.h | 1 +
target/riscv/tcg/insn_trans/trans_privileged.c.inc | 6 +++++-
target/riscv/tcg/op_helper.c | 22 ++++++++++++++++------
3 files changed, 22 insertions(+), 7 deletions(-)
---
base-commit: c513597761d6fc06690b5b7cf10dfb7ab05815d0
change-id: 20260831-riscv_tlb_page_flush-11a83fe9e7b1
Best regards,
--
Trevor Gamblin <tgamblin@baylibre.com>
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v2 1/2] target/riscv: extract check_sfence_vma() from helper_tlb_flush()
2026-09-04 18:43 [PATCH v2 0/2] target: riscv: support TLB page flush for sfence.vma Trevor Gamblin
@ 2026-09-04 18:43 ` Trevor Gamblin
2026-09-07 13:05 ` Daniel Henrique Barboza
` (2 more replies)
2026-09-04 18:43 ` [PATCH v2 2/2] target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand Trevor Gamblin
2026-09-07 13:14 ` [PATCH v2 0/2] target: riscv: support TLB page flush for sfence.vma Daniel Henrique Barboza
2 siblings, 3 replies; 17+ messages in thread
From: Trevor Gamblin @ 2026-09-04 18:43 UTC (permalink / raw)
To: qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, qemu-riscv,
Trevor Gamblin
Create a new function to encapsulate the privilege/hypervisor checks
performed inside helper_tlb_flush(). The idea is to pass GETPC() as an
argument directly to it inside the helper_tlb_flush() function. We'll
use this function in the following patch for helper_tlb_flush_page().
Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
---
target/riscv/tcg/op_helper.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/target/riscv/tcg/op_helper.c b/target/riscv/tcg/op_helper.c
index 3e94005d2b..7f63f3ada5 100644
--- a/target/riscv/tcg/op_helper.c
+++ b/target/riscv/tcg/op_helper.c
@@ -588,21 +588,25 @@ void helper_wrs_nto(CPURISCVState *env)
}
}
-void helper_tlb_flush(CPURISCVState *env)
+static void check_sfence_vma(CPURISCVState *env, uintptr_t ra)
{
- CPUState *cs = env_cpu(env);
if (!env->virt_enabled &&
(env->priv == PRV_U ||
(env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)))) {
- riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra);
} else if (env->virt_enabled &&
(env->priv == PRV_U || get_field(env->hstatus, HSTATUS_VTVM))) {
- riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
- } else {
- tlb_flush(cs);
+ riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, ra);
}
}
+void helper_tlb_flush(CPURISCVState *env)
+{
+ CPUState *cs = env_cpu(env);
+ check_sfence_vma(env, GETPC());
+ tlb_flush(cs);
+}
+
void helper_tlb_flush_all(CPURISCVState *env)
{
CPUState *cs = env_cpu(env);
--
2.55.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 2/2] target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand
2026-09-04 18:43 [PATCH v2 0/2] target: riscv: support TLB page flush for sfence.vma Trevor Gamblin
2026-09-04 18:43 ` [PATCH v2 1/2] target/riscv: extract check_sfence_vma() from helper_tlb_flush() Trevor Gamblin
@ 2026-09-04 18:43 ` Trevor Gamblin
2026-09-07 13:05 ` Daniel Henrique Barboza
` (3 more replies)
2026-09-07 13:14 ` [PATCH v2 0/2] target: riscv: support TLB page flush for sfence.vma Daniel Henrique Barboza
2 siblings, 4 replies; 17+ messages in thread
From: Trevor Gamblin @ 2026-09-04 18:43 UTC (permalink / raw)
To: qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, qemu-riscv,
Trevor Gamblin
sfence.vma unconditionally called helper_tlb_flush(), regardless of the
rs1 (vaddr) and rs2 (asid) operands, forcing a page-table walk on the
next access. Create a page-level counterpart to helper_tlb_flush()
called helper_tlb_flush_page(), and use it whenever rs1 != 0, falling
back to the existing full flush for rs1 == 0. This makes RISC-V
behaviour more similar to ARM's equivalent (tlbi_aa64_vae1_write), which
already does tlb_flush_page_by_mmuidx() instead of a full flush.
Pass get_address(ctx, a->rs1, 0) to gen_helper_tlb_flush_page() rather
than get_gpr(), so that the address being passed matches the current
addr_xl width regardless of '-cpu' input. Otherwise, the raw register
value can carry garbage above that width and never match the address the
TLB entry was actually filled under, so the flush silently misses and a
stale mapping survives.
Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
---
target/riscv/helper.h | 1 +
target/riscv/tcg/insn_trans/trans_privileged.c.inc | 6 +++++-
target/riscv/tcg/op_helper.c | 6 ++++++
3 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
index 4fc2d3a155..652f85a5c7 100644
--- a/target/riscv/helper.h
+++ b/target/riscv/helper.h
@@ -137,6 +137,7 @@ DEF_HELPER_1(ctr_clear, void, env)
DEF_HELPER_1(wfi, void, env)
DEF_HELPER_1(wrs_nto, void, env)
DEF_HELPER_1(tlb_flush, void, env)
+DEF_HELPER_2(tlb_flush_page, void, env, tl)
DEF_HELPER_1(tlb_flush_all, void, env)
DEF_HELPER_4(ctr_add_entry, void, env, tl, tl, tl)
/* Native Debug */
diff --git a/target/riscv/tcg/insn_trans/trans_privileged.c.inc b/target/riscv/tcg/insn_trans/trans_privileged.c.inc
index a8eaccef67..8655fa332e 100644
--- a/target/riscv/tcg/insn_trans/trans_privileged.c.inc
+++ b/target/riscv/tcg/insn_trans/trans_privileged.c.inc
@@ -155,7 +155,11 @@ static bool trans_sfence_vma(DisasContext *ctx, arg_sfence_vma *a)
{
#ifndef CONFIG_USER_ONLY
decode_save_opc(ctx, 0);
- gen_helper_tlb_flush(tcg_env);
+ if (a->rs1 == 0) {
+ gen_helper_tlb_flush(tcg_env);
+ } else {
+ gen_helper_tlb_flush_page(tcg_env, get_address(ctx, a->rs1, 0));
+ }
return true;
#endif
return false;
diff --git a/target/riscv/tcg/op_helper.c b/target/riscv/tcg/op_helper.c
index 7f63f3ada5..178b4d271b 100644
--- a/target/riscv/tcg/op_helper.c
+++ b/target/riscv/tcg/op_helper.c
@@ -607,6 +607,12 @@ void helper_tlb_flush(CPURISCVState *env)
tlb_flush(cs);
}
+void helper_tlb_flush_page(CPURISCVState *env, target_ulong addr)
+{
+ check_sfence_vma(env, GETPC());
+ tlb_flush_page(env_cpu(env), addr);
+}
+
void helper_tlb_flush_all(CPURISCVState *env)
{
CPUState *cs = env_cpu(env);
--
2.55.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v2 1/2] target/riscv: extract check_sfence_vma() from helper_tlb_flush()
2026-09-04 18:43 ` [PATCH v2 1/2] target/riscv: extract check_sfence_vma() from helper_tlb_flush() Trevor Gamblin
@ 2026-09-07 13:05 ` Daniel Henrique Barboza
2026-09-14 0:59 ` Richard Henderson
2026-10-01 5:13 ` Alistair Francis
2 siblings, 0 replies; 17+ messages in thread
From: Daniel Henrique Barboza @ 2026-09-07 13:05 UTC (permalink / raw)
To: Trevor Gamblin, qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei, Chao Liu,
qemu-riscv
On 9/4/2026 3:43 PM, Trevor Gamblin wrote:
> Create a new function to encapsulate the privilege/hypervisor checks
> performed inside helper_tlb_flush(). The idea is to pass GETPC() as an
> argument directly to it inside the helper_tlb_flush() function. We'll
> use this function in the following patch for helper_tlb_flush_page().
>
> Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
> ---
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
> target/riscv/tcg/op_helper.c | 16 ++++++++++------
> 1 file changed, 10 insertions(+), 6 deletions(-)
>
> diff --git a/target/riscv/tcg/op_helper.c b/target/riscv/tcg/op_helper.c
> index 3e94005d2b..7f63f3ada5 100644
> --- a/target/riscv/tcg/op_helper.c
> +++ b/target/riscv/tcg/op_helper.c
> @@ -588,21 +588,25 @@ void helper_wrs_nto(CPURISCVState *env)
> }
> }
>
> -void helper_tlb_flush(CPURISCVState *env)
> +static void check_sfence_vma(CPURISCVState *env, uintptr_t ra)
> {
> - CPUState *cs = env_cpu(env);
> if (!env->virt_enabled &&
> (env->priv == PRV_U ||
> (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)))) {
> - riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra);
> } else if (env->virt_enabled &&
> (env->priv == PRV_U || get_field(env->hstatus, HSTATUS_VTVM))) {
> - riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
> - } else {
> - tlb_flush(cs);
> + riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, ra);
> }
> }
>
> +void helper_tlb_flush(CPURISCVState *env)
> +{
> + CPUState *cs = env_cpu(env);
> + check_sfence_vma(env, GETPC());
> + tlb_flush(cs);
> +}
> +
> void helper_tlb_flush_all(CPURISCVState *env)
> {
> CPUState *cs = env_cpu(env);
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 2/2] target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand
2026-09-04 18:43 ` [PATCH v2 2/2] target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand Trevor Gamblin
@ 2026-09-07 13:05 ` Daniel Henrique Barboza
2026-09-14 1:06 ` Richard Henderson
` (2 subsequent siblings)
3 siblings, 0 replies; 17+ messages in thread
From: Daniel Henrique Barboza @ 2026-09-07 13:05 UTC (permalink / raw)
To: Trevor Gamblin, qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei, Chao Liu,
qemu-riscv
On 9/4/2026 3:43 PM, Trevor Gamblin wrote:
> sfence.vma unconditionally called helper_tlb_flush(), regardless of the
> rs1 (vaddr) and rs2 (asid) operands, forcing a page-table walk on the
> next access. Create a page-level counterpart to helper_tlb_flush()
> called helper_tlb_flush_page(), and use it whenever rs1 != 0, falling
> back to the existing full flush for rs1 == 0. This makes RISC-V
> behaviour more similar to ARM's equivalent (tlbi_aa64_vae1_write), which
> already does tlb_flush_page_by_mmuidx() instead of a full flush.
>
> Pass get_address(ctx, a->rs1, 0) to gen_helper_tlb_flush_page() rather
> than get_gpr(), so that the address being passed matches the current
> addr_xl width regardless of '-cpu' input. Otherwise, the raw register
> value can carry garbage above that width and never match the address the
> TLB entry was actually filled under, so the flush silently misses and a
> stale mapping survives.
>
> Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
> ---
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
> target/riscv/helper.h | 1 +
> target/riscv/tcg/insn_trans/trans_privileged.c.inc | 6 +++++-
> target/riscv/tcg/op_helper.c | 6 ++++++
> 3 files changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> index 4fc2d3a155..652f85a5c7 100644
> --- a/target/riscv/helper.h
> +++ b/target/riscv/helper.h
> @@ -137,6 +137,7 @@ DEF_HELPER_1(ctr_clear, void, env)
> DEF_HELPER_1(wfi, void, env)
> DEF_HELPER_1(wrs_nto, void, env)
> DEF_HELPER_1(tlb_flush, void, env)
> +DEF_HELPER_2(tlb_flush_page, void, env, tl)
> DEF_HELPER_1(tlb_flush_all, void, env)
> DEF_HELPER_4(ctr_add_entry, void, env, tl, tl, tl)
> /* Native Debug */
> diff --git a/target/riscv/tcg/insn_trans/trans_privileged.c.inc b/target/riscv/tcg/insn_trans/trans_privileged.c.inc
> index a8eaccef67..8655fa332e 100644
> --- a/target/riscv/tcg/insn_trans/trans_privileged.c.inc
> +++ b/target/riscv/tcg/insn_trans/trans_privileged.c.inc
> @@ -155,7 +155,11 @@ static bool trans_sfence_vma(DisasContext *ctx, arg_sfence_vma *a)
> {
> #ifndef CONFIG_USER_ONLY
> decode_save_opc(ctx, 0);
> - gen_helper_tlb_flush(tcg_env);
> + if (a->rs1 == 0) {
> + gen_helper_tlb_flush(tcg_env);
> + } else {
> + gen_helper_tlb_flush_page(tcg_env, get_address(ctx, a->rs1, 0));
> + }
> return true;
> #endif
> return false;
> diff --git a/target/riscv/tcg/op_helper.c b/target/riscv/tcg/op_helper.c
> index 7f63f3ada5..178b4d271b 100644
> --- a/target/riscv/tcg/op_helper.c
> +++ b/target/riscv/tcg/op_helper.c
> @@ -607,6 +607,12 @@ void helper_tlb_flush(CPURISCVState *env)
> tlb_flush(cs);
> }
>
> +void helper_tlb_flush_page(CPURISCVState *env, target_ulong addr)
> +{
> + check_sfence_vma(env, GETPC());
> + tlb_flush_page(env_cpu(env), addr);
> +}
> +
> void helper_tlb_flush_all(CPURISCVState *env)
> {
> CPUState *cs = env_cpu(env);
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 0/2] target: riscv: support TLB page flush for sfence.vma
2026-09-04 18:43 [PATCH v2 0/2] target: riscv: support TLB page flush for sfence.vma Trevor Gamblin
2026-09-04 18:43 ` [PATCH v2 1/2] target/riscv: extract check_sfence_vma() from helper_tlb_flush() Trevor Gamblin
2026-09-04 18:43 ` [PATCH v2 2/2] target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand Trevor Gamblin
@ 2026-09-07 13:14 ` Daniel Henrique Barboza
2026-09-14 1:14 ` Richard Henderson
2 siblings, 1 reply; 17+ messages in thread
From: Daniel Henrique Barboza @ 2026-09-07 13:14 UTC (permalink / raw)
To: Trevor Gamblin, qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei, Chao Liu,
qemu-riscv, Richard Henderson
Richard,
This tlb_flush change is giving a +20% performance boost in riscv64-tuxrun. From
this:
23/23 func-thorough+func-riscv64-thorough+thorough - qemu:func-riscv64-tuxrun OK 92.71s 4 subtests passed
To this:
23/23 func-thorough+func-riscv64-thorough+thorough - qemu:func-riscv64-tuxrun OK 73.45s 4 subtests passed
Other tests weren't that affected - I take it has to do with the amount of
sfence.vma being issued. They look fine in my own testing with an emulated
Ubuntu host as well.
This is all fantastic but I wonder if we're missing something. If you could
please take a look at these 2 patches and see if we're handling the tlb flushes
according that would be terrific.
Thanks,
Daniel
On 9/4/2026 3:43 PM, Trevor Gamblin wrote:
> Introduce a new function, check_sfence_vma(), to handle
> privilege/hypervisor checks currently performed in helper_tlb_flush(),
> and make use of it in that function. Then, add helper_tlb_flush_page()
> for flushing only a single TLB page, using check_sfence_vma() inside it
> similarly to helper_tlb_flush(). Finally, rework the trans_sfence_vma()
> function to use helper_tlb_flush_page() when rs1 != 0.
>
> The second patch also includes the fix for a regression in the
> 'test_tuxrun.TuxRunRiscV64Test.test_riscv64_maxcpu' test encountered
> with the draft version of this series, which hanged indefinitely until
> killed when the patches were applied. This was fixed by making sure to
> use get_address() to canonicalize the address being passed to
> helper_tlb_flush_page().
>
> make-check and make-functional were both run with the patches applied on
> master, and no regressions for riscv64 were observed (beyond the one
> mentioned as fixed above).
>
> Daniel Barboza helped me with initial triage, direction, and cleaning up
> this series for submission - thanks again!
>
> Some background context: as part of the RISE Project[1] mission to
> improve the RISC-V software ecosystem, the Yocto Project[2] is receiving
> assistance with triaging bugs and enhancing RISC-V support in the core
> layer. The 'qemuriscv32' and 'qemuriscv64' targets were enabled as part
> of the Yocto's CI system, known as the Autobuilder[3] in 2025, and since
> that time various intermittent failures have been observed on the
> 'ptest' ('package test', where a package's test suites are bundled into
> an image and then booted to do basic runtime compatibility testing)
> case, 'qemuriscv64-ptest'. These almost always manifest as timeouts in
> the guest, usually around heavier and/or more complex test cases, and
> they additionally seem to occur only under heavy host system (x86_64)
> load.
>
> Comparing to other major architectures like x86_64 and arm64, the
> riscv64 guests are always emulated in TCG mode, since the Yocto
> Autobuilder cluster does not (yet) have native riscv64 hosts to dedicate
> for runtime testing with kvm support. While this is likely a major
> factor in why qemuriscv64 runs fail at higher rates than other
> architectures, it was noted after compiling the triage data[4] that
> local comparisons of the same ptest images running on TCG-mode qemuarm64
> and qemuriscv64 showed the latter taking significantly longer to
> complete (using some of the test suites which have been observed failing
> as examples), even with no other meaningful system load on the host. In
> particular, the coreutils case showed over 70% longer runtime for
> qemuriscv64 compared to qemuarm64 (~1000s vs ~580s).
>
> Fully investigating the source of this discrepancy was a long process.
> For Yocto builds, native QEMU binaries are built from source as part of
> the overall image build, so the first place to look was in QEMU's
> configuration and boot options. Other suggestions included trying to
> disable TCG plugins or alter the QEMU and guest package configs (Yocto's
> QEMU is RVA23S64 profile by default, while the runtime packages are
> built for riscv64gc). However, these changes either produced no
> meaningful effect or actually increased runtime. The next step was to
> investigate the QEMU source, which is what ultimately led to this patch
> series. It turns out that these changes significantly improve the
> reference runtime test case for coreutils in Yocto, although it is still
> somewhat slower than in qemuarm64. It remains to be seen whether these
> changes to the QEMU source eliminate the intermittent issues in Yocto's
> testing, but it is a meaningful improvement which opens the door to
> further optimizations for RISC-V.
>
> As an example, compare the before and after results on a local run of
> core-image-ptest-coreutils for qemuriscv64:
>
> |============================================================================
> |Testsuite summary for GNU coreutils 9.11
> |============================================================================
> |# TOTAL: 733
> |# PASS: 561
> |# SKIP: 172
> |# XFAIL: 0
> |# FAIL: 0
> |# XPASS: 0
> |# ERROR: 0
> |============================================================================
> |make[1]: Leaving directory '/usr/lib/coreutils/ptest'
> |DURATION: 1049
> |END: /usr/lib/coreutils/ptest
> |2026-08-31T17:23
> |STOP: ptest-runner
> |TOTAL: 1 FAIL: 0
>
> versus:
>
> |============================================================================
> |Testsuite summary for GNU coreutils 9.11
> |============================================================================
> |# TOTAL: 733
> |# PASS: 561
> |# SKIP: 172
> |# XFAIL: 0
> |# FAIL: 0
> |# XPASS: 0
> |# ERROR: 0
> |============================================================================
> |make[1]: Leaving directory '/usr/lib/coreutils/ptest'
> |DURATION: 646
> |END: /usr/lib/coreutils/ptest
> |2026-08-31T16:10
> |STOP: ptest-runner
> |TOTAL: 1 FAIL: 0
> |root@qemuriscv64:~#
>
> Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
>
> [1]: https://riseproject.dev/
> [2]: https://www.yoctoproject.org/
> [3]: https://autobuilder.yoctoproject.org/valkyrie/
> [4]: https://docs.google.com/document/d/1HUk-RvMDdW2EKV-qE1VNfZBycPscQUsayEZ5AgJ85C8
>
> ---
> Changes in v2:
> - Renamed sfence_vma_allowed() to check_sfence_vma()
> - Squashed patches 2 and 3 together
> - Reworked check_sfence_vma() and helpers using it to be consistent with
> rest of codebase (e.g. no unnecessary returns)
> - Adjusted cover letter to match check_sfence_vma() naming
> - Link to v1: https://lore.kernel.org/qemu-devel/20260831-riscv_tlb_page_flush-v1-0-4fc6af95673a@baylibre.com
>
> To: qemu-devel@nongnu.org
> Cc: Palmer Dabbelt <palmer@dabbelt.com>
> Cc: Alistair Francis <alistair.francis@wdc.com>
> Cc: Weiwei Li <liwei1518@gmail.com>
> Cc: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
> Cc: Liu Zhiwei <zhiwei_liu@linux.alibaba.com>
> Cc: Chao Liu <chao.liu@processmission.com>
> Cc: qemu-riscv@nongnu.org
>
> ---
> Trevor Gamblin (2):
> target/riscv: extract check_sfence_vma() from helper_tlb_flush()
> target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand
>
> target/riscv/helper.h | 1 +
> target/riscv/tcg/insn_trans/trans_privileged.c.inc | 6 +++++-
> target/riscv/tcg/op_helper.c | 22 ++++++++++++++++------
> 3 files changed, 22 insertions(+), 7 deletions(-)
> ---
> base-commit: c513597761d6fc06690b5b7cf10dfb7ab05815d0
> change-id: 20260831-riscv_tlb_page_flush-11a83fe9e7b1
>
> Best regards,
> --
> Trevor Gamblin <tgamblin@baylibre.com>
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 1/2] target/riscv: extract check_sfence_vma() from helper_tlb_flush()
2026-09-04 18:43 ` [PATCH v2 1/2] target/riscv: extract check_sfence_vma() from helper_tlb_flush() Trevor Gamblin
2026-09-07 13:05 ` Daniel Henrique Barboza
@ 2026-09-14 0:59 ` Richard Henderson
2026-10-01 5:13 ` Alistair Francis
2 siblings, 0 replies; 17+ messages in thread
From: Richard Henderson @ 2026-09-14 0:59 UTC (permalink / raw)
To: Trevor Gamblin, qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, qemu-riscv
On 9/4/26 08:43, Trevor Gamblin wrote:
> Create a new function to encapsulate the privilege/hypervisor checks
> performed inside helper_tlb_flush(). The idea is to pass GETPC() as an
> argument directly to it inside the helper_tlb_flush() function. We'll
> use this function in the following patch for helper_tlb_flush_page().
>
> Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
> ---
> target/riscv/tcg/op_helper.c | 16 ++++++++++------
> 1 file changed, 10 insertions(+), 6 deletions(-)
>
> diff --git a/target/riscv/tcg/op_helper.c b/target/riscv/tcg/op_helper.c
> index 3e94005d2b..7f63f3ada5 100644
> --- a/target/riscv/tcg/op_helper.c
> +++ b/target/riscv/tcg/op_helper.c
> @@ -588,21 +588,25 @@ void helper_wrs_nto(CPURISCVState *env)
> }
> }
>
> -void helper_tlb_flush(CPURISCVState *env)
> +static void check_sfence_vma(CPURISCVState *env, uintptr_t ra)
> {
> - CPUState *cs = env_cpu(env);
> if (!env->virt_enabled &&
> (env->priv == PRV_U ||
> (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)))) {
> - riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra);
> } else if (env->virt_enabled &&
> (env->priv == PRV_U || get_field(env->hstatus, HSTATUS_VTVM))) {
> - riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
> - } else {
> - tlb_flush(cs);
> + riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, ra);
> }
> }
The priv and virt_enabled tests can be done in the translator.
Honestly, I'm surprised that PRV_U isn't already tested there.
PRV_M, could avoid the test.
PRV_S could perform the TVM test at runtime.
ctx->virt_inst_excp = ctx->virt_enabled;
if (ctx->priv == PRV_U) {
gen_exception_illegal(ctx);
} else if (ctx->priv == PRV_S) {
TCGv_i64 t = tcg_temp_new_i64();
TCGLabel *l = gen_new_label();
uint64_t mask;
ptrdiff_t ofs;
if (ctx->virt_enabled) {
ofs = offsetof(CPUArchState, hstatus);
mask = HSTATUS_VTVM;
} else {
ofs = offsetof(CPUArchState, mstatus);
mask = MSTATUS_TVM;
}
tcg_gen_ld_i64(t, tcg_env, ofs);
gen_update_pc(ctx, 0);
tcg_gen_brcondi_i64(TCG_COND_TSTEQ, t, mask, l);
gen_exception_illegal(ctx);
gen_set_label(l);
}
Although the subtlety of virt_inst_excp vs virt_enabled wrt which
exception is raised confuses me.
It appears this criticism is true for all privileged instructions, so
perhaps this should be done as part of a more global cleanup.
r~
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 2/2] target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand
2026-09-04 18:43 ` [PATCH v2 2/2] target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand Trevor Gamblin
2026-09-07 13:05 ` Daniel Henrique Barboza
@ 2026-09-14 1:06 ` Richard Henderson
2026-09-14 1:17 ` Richard Henderson
2026-10-01 5:14 ` Alistair Francis
3 siblings, 0 replies; 17+ messages in thread
From: Richard Henderson @ 2026-09-14 1:06 UTC (permalink / raw)
To: Trevor Gamblin, qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, qemu-riscv
On 9/4/26 08:43, Trevor Gamblin wrote:
> sfence.vma unconditionally called helper_tlb_flush(), regardless of the
> rs1 (vaddr) and rs2 (asid) operands, forcing a page-table walk on the
> next access. Create a page-level counterpart to helper_tlb_flush()
> called helper_tlb_flush_page(), and use it whenever rs1 != 0, falling
> back to the existing full flush for rs1 == 0. This makes RISC-V
> behaviour more similar to ARM's equivalent (tlbi_aa64_vae1_write), which
> already does tlb_flush_page_by_mmuidx() instead of a full flush.
>
> Pass get_address(ctx, a->rs1, 0) to gen_helper_tlb_flush_page() rather
> than get_gpr(), so that the address being passed matches the current
> addr_xl width regardless of '-cpu' input. Otherwise, the raw register
> value can carry garbage above that width and never match the address the
> TLB entry was actually filled under, so the flush silently misses and a
> stale mapping survives.
>
> Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
> ---
> target/riscv/helper.h | 1 +
> target/riscv/tcg/insn_trans/trans_privileged.c.inc | 6 +++++-
> target/riscv/tcg/op_helper.c | 6 ++++++
> 3 files changed, 12 insertions(+), 1 deletion(-)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
r~
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 0/2] target: riscv: support TLB page flush for sfence.vma
2026-09-07 13:14 ` [PATCH v2 0/2] target: riscv: support TLB page flush for sfence.vma Daniel Henrique Barboza
@ 2026-09-14 1:14 ` Richard Henderson
2026-09-15 20:40 ` Daniel Henrique Barboza
0 siblings, 1 reply; 17+ messages in thread
From: Richard Henderson @ 2026-09-14 1:14 UTC (permalink / raw)
To: Daniel Henrique Barboza, Trevor Gamblin, qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei, Chao Liu,
qemu-riscv
On 9/7/26 03:14, Daniel Henrique Barboza wrote:
> Richard,
>
>
> This tlb_flush change is giving a +20% performance boost in riscv64-
> tuxrun. From
> this:
>
> 23/23 func-thorough+func-riscv64-thorough+thorough - qemu:func-riscv64-
> tuxrun OK 92.71s 4 subtests passed
>
> To this:
>
> 23/23 func-thorough+func-riscv64-thorough+thorough - qemu:func-riscv64-
> tuxrun OK 73.45s 4 subtests passed
>
> Other tests weren't that affected - I take it has to do with the amount of
> sfence.vma being issued. They look fine in my own testing with an emulated
> Ubuntu host as well.
>
> This is all fantastic but I wonder if we're missing something. If you
> could
> please take a look at these 2 patches and see if we're handling the tlb
> flushes
> according that would be terrific.
It looks good.
The only possible improvement would be to avoid flushing the PRV_M 1-1
tlb, using tlb_flush_by_mmuidx / tlb_flush_page_by_mmuidx.
You could also rename MMUIdx_M to MMUIdx_PHYS and use it in modes other
than PRV_M when the mmu is disabled, simplifying the test at the start
of get_physical_address.
r~
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 2/2] target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand
2026-09-04 18:43 ` [PATCH v2 2/2] target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand Trevor Gamblin
2026-09-07 13:05 ` Daniel Henrique Barboza
2026-09-14 1:06 ` Richard Henderson
@ 2026-09-14 1:17 ` Richard Henderson
2026-09-14 1:34 ` Richard Henderson
2026-10-01 5:14 ` Alistair Francis
3 siblings, 1 reply; 17+ messages in thread
From: Richard Henderson @ 2026-09-14 1:17 UTC (permalink / raw)
To: qemu-devel
On 9/4/26 08:43, Trevor Gamblin wrote:
> sfence.vma unconditionally called helper_tlb_flush(), regardless of the
> rs1 (vaddr) and rs2 (asid) operands, forcing a page-table walk on the
> next access. Create a page-level counterpart to helper_tlb_flush()
> called helper_tlb_flush_page(), and use it whenever rs1 != 0, falling
> back to the existing full flush for rs1 == 0. This makes RISC-V
> behaviour more similar to ARM's equivalent (tlbi_aa64_vae1_write), which
> already does tlb_flush_page_by_mmuidx() instead of a full flush.
>
> Pass get_address(ctx, a->rs1, 0) to gen_helper_tlb_flush_page() rather
> than get_gpr(), so that the address being passed matches the current
> addr_xl width regardless of '-cpu' input. Otherwise, the raw register
> value can carry garbage above that width and never match the address the
> TLB entry was actually filled under, so the flush silently misses and a
> stale mapping survives.
>
> Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
> ---
> target/riscv/helper.h | 1 +
> target/riscv/tcg/insn_trans/trans_privileged.c.inc | 6 +++++-
> target/riscv/tcg/op_helper.c | 6 ++++++
> 3 files changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> index 4fc2d3a155..652f85a5c7 100644
> --- a/target/riscv/helper.h
> +++ b/target/riscv/helper.h
> @@ -137,6 +137,7 @@ DEF_HELPER_1(ctr_clear, void, env)
> DEF_HELPER_1(wfi, void, env)
> DEF_HELPER_1(wrs_nto, void, env)
> DEF_HELPER_1(tlb_flush, void, env)
> +DEF_HELPER_2(tlb_flush_page, void, env, tl)
> DEF_HELPER_1(tlb_flush_all, void, env)
> DEF_HELPER_4(ctr_add_entry, void, env, tl, tl, tl)
> /* Native Debug */
> diff --git a/target/riscv/tcg/insn_trans/trans_privileged.c.inc b/target/riscv/tcg/insn_trans/trans_privileged.c.inc
> index a8eaccef67..8655fa332e 100644
> --- a/target/riscv/tcg/insn_trans/trans_privileged.c.inc
> +++ b/target/riscv/tcg/insn_trans/trans_privileged.c.inc
> @@ -155,7 +155,11 @@ static bool trans_sfence_vma(DisasContext *ctx, arg_sfence_vma *a)
> {
> #ifndef CONFIG_USER_ONLY
> decode_save_opc(ctx, 0);
> - gen_helper_tlb_flush(tcg_env);
> + if (a->rs1 == 0) {
> + gen_helper_tlb_flush(tcg_env);
> + } else {
> + gen_helper_tlb_flush_page(tcg_env, get_address(ctx, a->rs1, 0));
> + }
> return true;
> #endif
> return false;
> diff --git a/target/riscv/tcg/op_helper.c b/target/riscv/tcg/op_helper.c
> index 7f63f3ada5..178b4d271b 100644
> --- a/target/riscv/tcg/op_helper.c
> +++ b/target/riscv/tcg/op_helper.c
> @@ -607,6 +607,12 @@ void helper_tlb_flush(CPURISCVState *env)
> tlb_flush(cs);
> }
>
> +void helper_tlb_flush_page(CPURISCVState *env, target_ulong addr)
> +{
> + check_sfence_vma(env, GETPC());
> + tlb_flush_page(env_cpu(env), addr);
> +}
> +
> void helper_tlb_flush_all(CPURISCVState *env)
> {
> CPUState *cs = env_cpu(env);
>
You're missing the similar changes to sinval.vma and hinval.*
r~
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 2/2] target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand
2026-09-14 1:17 ` Richard Henderson
@ 2026-09-14 1:34 ` Richard Henderson
0 siblings, 0 replies; 17+ messages in thread
From: Richard Henderson @ 2026-09-14 1:34 UTC (permalink / raw)
To: qemu-devel
On 9/13/26 15:17, Richard Henderson wrote:
> You're missing the similar changes to sinval.vma and hinval.*
In addition, th.sfence.vmas. Moreover, for that one, if rs2 != 0, we
don't need a synced global flush.
r~
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 0/2] target: riscv: support TLB page flush for sfence.vma
2026-09-14 1:14 ` Richard Henderson
@ 2026-09-15 20:40 ` Daniel Henrique Barboza
2026-10-08 7:00 ` Wang Yechao
0 siblings, 1 reply; 17+ messages in thread
From: Daniel Henrique Barboza @ 2026-09-15 20:40 UTC (permalink / raw)
To: Richard Henderson, Trevor Gamblin, qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei, Chao Liu,
qemu-riscv
On 9/13/2026 10:14 PM, Richard Henderson wrote:
> On 9/7/26 03:14, Daniel Henrique Barboza wrote:
>> Richard,
>>
>>
>> This tlb_flush change is giving a +20% performance boost in riscv64- tuxrun. From
>> this:
>>
>> 23/23 func-thorough+func-riscv64-thorough+thorough - qemu:func-riscv64- tuxrun OK 92.71s 4 subtests passed
>>
>> To this:
>>
>> 23/23 func-thorough+func-riscv64-thorough+thorough - qemu:func-riscv64- tuxrun OK 73.45s 4 subtests passed
>>
>> Other tests weren't that affected - I take it has to do with the amount of
>> sfence.vma being issued. They look fine in my own testing with an emulated
>> Ubuntu host as well.
>>
>> This is all fantastic but I wonder if we're missing something. If you could
>> please take a look at these 2 patches and see if we're handling the tlb flushes
>> according that would be terrific.
> It looks good.
>
> The only possible improvement would be to avoid flushing the PRV_M 1-1 tlb, using tlb_flush_by_mmuidx / tlb_flush_page_by_mmuidx.
>
> You could also rename MMUIdx_M to MMUIdx_PHYS and use it in modes other than PRV_M when the mmu is disabled, simplifying the test at the start of get_physical_address.
>
Thanks for having a look! I believe we have a potential cleanup to do based on
what you said in patch 1. Probably follow-up material since it'll be a bit
too much in the context handled here.
Cheers,
Daniel
>
> r~
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 1/2] target/riscv: extract check_sfence_vma() from helper_tlb_flush()
2026-09-04 18:43 ` [PATCH v2 1/2] target/riscv: extract check_sfence_vma() from helper_tlb_flush() Trevor Gamblin
2026-09-07 13:05 ` Daniel Henrique Barboza
2026-09-14 0:59 ` Richard Henderson
@ 2026-10-01 5:13 ` Alistair Francis
2 siblings, 0 replies; 17+ messages in thread
From: Alistair Francis @ 2026-10-01 5:13 UTC (permalink / raw)
To: tgamblin@baylibre.com, qemu-devel@nongnu.org
Cc: palmer@dabbelt.com, qemu-riscv@nongnu.org,
daniel.barboza@oss.qualcomm.com, chao.liu@processmission.com,
liwei1518@gmail.com, zhiwei_liu@linux.alibaba.com
On Fri, 2026-09-04 at 14:43 -0400, Trevor Gamblin wrote:
> Create a new function to encapsulate the privilege/hypervisor checks
> performed inside helper_tlb_flush(). The idea is to pass GETPC() as
> an
> argument directly to it inside the helper_tlb_flush() function. We'll
> use this function in the following patch for helper_tlb_flush_page().
>
> Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> target/riscv/tcg/op_helper.c | 16 ++++++++++------
> 1 file changed, 10 insertions(+), 6 deletions(-)
>
> diff --git a/target/riscv/tcg/op_helper.c
> b/target/riscv/tcg/op_helper.c
> index 3e94005d2b..7f63f3ada5 100644
> --- a/target/riscv/tcg/op_helper.c
> +++ b/target/riscv/tcg/op_helper.c
> @@ -588,21 +588,25 @@ void helper_wrs_nto(CPURISCVState *env)
> }
> }
>
> -void helper_tlb_flush(CPURISCVState *env)
> +static void check_sfence_vma(CPURISCVState *env, uintptr_t ra)
> {
> - CPUState *cs = env_cpu(env);
> if (!env->virt_enabled &&
> (env->priv == PRV_U ||
> (env->priv == PRV_S && get_field(env->mstatus,
> MSTATUS_TVM)))) {
> - riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST,
> GETPC());
> + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra);
> } else if (env->virt_enabled &&
> (env->priv == PRV_U || get_field(env->hstatus,
> HSTATUS_VTVM))) {
> - riscv_raise_exception(env,
> RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
> - } else {
> - tlb_flush(cs);
> + riscv_raise_exception(env,
> RISCV_EXCP_VIRT_INSTRUCTION_FAULT, ra);
> }
> }
>
> +void helper_tlb_flush(CPURISCVState *env)
> +{
> + CPUState *cs = env_cpu(env);
> + check_sfence_vma(env, GETPC());
> + tlb_flush(cs);
> +}
> +
> void helper_tlb_flush_all(CPURISCVState *env)
> {
> CPUState *cs = env_cpu(env);
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 2/2] target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand
2026-09-04 18:43 ` [PATCH v2 2/2] target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand Trevor Gamblin
` (2 preceding siblings ...)
2026-09-14 1:17 ` Richard Henderson
@ 2026-10-01 5:14 ` Alistair Francis
2026-10-01 17:02 ` Trevor Gamblin
3 siblings, 1 reply; 17+ messages in thread
From: Alistair Francis @ 2026-10-01 5:14 UTC (permalink / raw)
To: tgamblin@baylibre.com, qemu-devel@nongnu.org
Cc: palmer@dabbelt.com, qemu-riscv@nongnu.org,
daniel.barboza@oss.qualcomm.com, chao.liu@processmission.com,
liwei1518@gmail.com, zhiwei_liu@linux.alibaba.com
On Fri, 2026-09-04 at 14:43 -0400, Trevor Gamblin wrote:
> sfence.vma unconditionally called helper_tlb_flush(), regardless of
> the
> rs1 (vaddr) and rs2 (asid) operands, forcing a page-table walk on the
> next access. Create a page-level counterpart to helper_tlb_flush()
> called helper_tlb_flush_page(), and use it whenever rs1 != 0, falling
> back to the existing full flush for rs1 == 0. This makes RISC-V
> behaviour more similar to ARM's equivalent (tlbi_aa64_vae1_write),
> which
> already does tlb_flush_page_by_mmuidx() instead of a full flush.
>
> Pass get_address(ctx, a->rs1, 0) to gen_helper_tlb_flush_page()
> rather
> than get_gpr(), so that the address being passed matches the current
> addr_xl width regardless of '-cpu' input. Otherwise, the raw register
> value can carry garbage above that width and never match the address
> the
> TLB entry was actually filled under, so the flush silently misses and
> a
> stale mapping survives.
>
> Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Can you fixup sinval.vma and hinval.* as well please?
Alistair
> ---
> target/riscv/helper.h | 1 +
> target/riscv/tcg/insn_trans/trans_privileged.c.inc | 6 +++++-
> target/riscv/tcg/op_helper.c | 6 ++++++
> 3 files changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> index 4fc2d3a155..652f85a5c7 100644
> --- a/target/riscv/helper.h
> +++ b/target/riscv/helper.h
> @@ -137,6 +137,7 @@ DEF_HELPER_1(ctr_clear, void, env)
> DEF_HELPER_1(wfi, void, env)
> DEF_HELPER_1(wrs_nto, void, env)
> DEF_HELPER_1(tlb_flush, void, env)
> +DEF_HELPER_2(tlb_flush_page, void, env, tl)
> DEF_HELPER_1(tlb_flush_all, void, env)
> DEF_HELPER_4(ctr_add_entry, void, env, tl, tl, tl)
> /* Native Debug */
> diff --git a/target/riscv/tcg/insn_trans/trans_privileged.c.inc
> b/target/riscv/tcg/insn_trans/trans_privileged.c.inc
> index a8eaccef67..8655fa332e 100644
> --- a/target/riscv/tcg/insn_trans/trans_privileged.c.inc
> +++ b/target/riscv/tcg/insn_trans/trans_privileged.c.inc
> @@ -155,7 +155,11 @@ static bool trans_sfence_vma(DisasContext *ctx,
> arg_sfence_vma *a)
> {
> #ifndef CONFIG_USER_ONLY
> decode_save_opc(ctx, 0);
> - gen_helper_tlb_flush(tcg_env);
> + if (a->rs1 == 0) {
> + gen_helper_tlb_flush(tcg_env);
> + } else {
> + gen_helper_tlb_flush_page(tcg_env, get_address(ctx, a->rs1,
> 0));
> + }
> return true;
> #endif
> return false;
> diff --git a/target/riscv/tcg/op_helper.c
> b/target/riscv/tcg/op_helper.c
> index 7f63f3ada5..178b4d271b 100644
> --- a/target/riscv/tcg/op_helper.c
> +++ b/target/riscv/tcg/op_helper.c
> @@ -607,6 +607,12 @@ void helper_tlb_flush(CPURISCVState *env)
> tlb_flush(cs);
> }
>
> +void helper_tlb_flush_page(CPURISCVState *env, target_ulong addr)
> +{
> + check_sfence_vma(env, GETPC());
> + tlb_flush_page(env_cpu(env), addr);
> +}
> +
> void helper_tlb_flush_all(CPURISCVState *env)
> {
> CPUState *cs = env_cpu(env);
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 2/2] target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand
2026-10-01 5:14 ` Alistair Francis
@ 2026-10-01 17:02 ` Trevor Gamblin
0 siblings, 0 replies; 17+ messages in thread
From: Trevor Gamblin @ 2026-10-01 17:02 UTC (permalink / raw)
To: Alistair Francis, tgamblin@baylibre.com, qemu-devel@nongnu.org
Cc: palmer@dabbelt.com, qemu-riscv@nongnu.org,
daniel.barboza@oss.qualcomm.com, chao.liu@processmission.com,
liwei1518@gmail.com, zhiwei_liu@linux.alibaba.com,
qemu-riscv-bounces+qemu-riscv=archiver.kernel.org
On Thu Oct 1, 2026 at 1:14 AM EDT, Alistair Francis wrote:
> On Fri, 2026-09-04 at 14:43 -0400, Trevor Gamblin wrote:
>> sfence.vma unconditionally called helper_tlb_flush(), regardless of
>> the
>> rs1 (vaddr) and rs2 (asid) operands, forcing a page-table walk on the
>> next access. Create a page-level counterpart to helper_tlb_flush()
>> called helper_tlb_flush_page(), and use it whenever rs1 != 0, falling
>> back to the existing full flush for rs1 == 0. This makes RISC-V
>> behaviour more similar to ARM's equivalent (tlbi_aa64_vae1_write),
>> which
>> already does tlb_flush_page_by_mmuidx() instead of a full flush.
>>
>> Pass get_address(ctx, a->rs1, 0) to gen_helper_tlb_flush_page()
>> rather
>> than get_gpr(), so that the address being passed matches the current
>> addr_xl width regardless of '-cpu' input. Otherwise, the raw register
>> value can carry garbage above that width and never match the address
>> the
>> TLB entry was actually filled under, so the flush silently misses and
>> a
>> stale mapping survives.
>>
>> Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
>
> Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
>
> Can you fixup sinval.vma and hinval.* as well please?
Sure! I'm travelling next week, but I'll see if I can get patches written in
downtime or right afterward.
Trevor
>
> Alistair
>
>> ---
>> target/riscv/helper.h | 1 +
>> target/riscv/tcg/insn_trans/trans_privileged.c.inc | 6 +++++-
>> target/riscv/tcg/op_helper.c | 6 ++++++
>> 3 files changed, 12 insertions(+), 1 deletion(-)
>>
>> diff --git a/target/riscv/helper.h b/target/riscv/helper.h
>> index 4fc2d3a155..652f85a5c7 100644
>> --- a/target/riscv/helper.h
>> +++ b/target/riscv/helper.h
>> @@ -137,6 +137,7 @@ DEF_HELPER_1(ctr_clear, void, env)
>> DEF_HELPER_1(wfi, void, env)
>> DEF_HELPER_1(wrs_nto, void, env)
>> DEF_HELPER_1(tlb_flush, void, env)
>> +DEF_HELPER_2(tlb_flush_page, void, env, tl)
>> DEF_HELPER_1(tlb_flush_all, void, env)
>> DEF_HELPER_4(ctr_add_entry, void, env, tl, tl, tl)
>> /* Native Debug */
>> diff --git a/target/riscv/tcg/insn_trans/trans_privileged.c.inc
>> b/target/riscv/tcg/insn_trans/trans_privileged.c.inc
>> index a8eaccef67..8655fa332e 100644
>> --- a/target/riscv/tcg/insn_trans/trans_privileged.c.inc
>> +++ b/target/riscv/tcg/insn_trans/trans_privileged.c.inc
>> @@ -155,7 +155,11 @@ static bool trans_sfence_vma(DisasContext *ctx,
>> arg_sfence_vma *a)
>> {
>> #ifndef CONFIG_USER_ONLY
>> decode_save_opc(ctx, 0);
>> - gen_helper_tlb_flush(tcg_env);
>> + if (a->rs1 == 0) {
>> + gen_helper_tlb_flush(tcg_env);
>> + } else {
>> + gen_helper_tlb_flush_page(tcg_env, get_address(ctx, a->rs1,
>> 0));
>> + }
>> return true;
>> #endif
>> return false;
>> diff --git a/target/riscv/tcg/op_helper.c
>> b/target/riscv/tcg/op_helper.c
>> index 7f63f3ada5..178b4d271b 100644
>> --- a/target/riscv/tcg/op_helper.c
>> +++ b/target/riscv/tcg/op_helper.c
>> @@ -607,6 +607,12 @@ void helper_tlb_flush(CPURISCVState *env)
>> tlb_flush(cs);
>> }
>>
>> +void helper_tlb_flush_page(CPURISCVState *env, target_ulong addr)
>> +{
>> + check_sfence_vma(env, GETPC());
>> + tlb_flush_page(env_cpu(env), addr);
>> +}
>> +
>> void helper_tlb_flush_all(CPURISCVState *env)
>> {
>> CPUState *cs = env_cpu(env);
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 0/2] target: riscv: support TLB page flush for sfence.vma
2026-09-15 20:40 ` Daniel Henrique Barboza
@ 2026-10-08 7:00 ` Wang Yechao
2026-10-19 17:14 ` Trevor Gamblin
0 siblings, 1 reply; 17+ messages in thread
From: Wang Yechao @ 2026-10-08 7:00 UTC (permalink / raw)
To: daniel.barboza, richard.henderson, tgamblin
Cc: alistair.francis, chao.liu, liwei1518, palmer, qemu-devel,
qemu-riscv, zhiwei_liu, Wang Yechao
Hi all,
Patch 1 decides whether to use tlb_flush_page based on whether rs1
is 0. This is similar to a patch I submitted last year [1]. Under
normal circumstances this works fine, but it can leave stale TLB
entries when the guest kernel uses THP.
The reason is that with THP, the guest's mapping is at the PMD level.
When flushing the TLB, the guest executes sfence.vma only once for the
whole huge page, rather than 512 times to flush each individual 4K page.
Patch 1 does not account for this: it only flushes the single 4K page
corresponding to the address in rs1, leaving the other 511 pages of the
huge page with stale entries. As a result, guest memory access faults
can occur after Patch 1 is merged, unless huge-page TLB support is added
for the RISC-V TCG case.
Please correct me if I've misunderstood anything.
[1] https://lore.kernel.org/qemu-devel/20250930174849833XG1Q1ETbNvg66WU2UVBTV@zte.com.cn/
Yechao,
Regards
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 0/2] target: riscv: support TLB page flush for sfence.vma
2026-10-08 7:00 ` Wang Yechao
@ 2026-10-19 17:14 ` Trevor Gamblin
0 siblings, 0 replies; 17+ messages in thread
From: Trevor Gamblin @ 2026-10-19 17:14 UTC (permalink / raw)
To: Wang Yechao, daniel.barboza, richard.henderson, tgamblin
Cc: alistair.francis, chao.liu, liwei1518, palmer, qemu-devel,
qemu-riscv, zhiwei_liu,
qemu-riscv-bounces+qemu-riscv=archiver.kernel.org
On Thu Oct 8, 2026 at 3:00 AM EDT, Wang Yechao wrote:
> Hi all,
>
> Patch 1 decides whether to use tlb_flush_page based on whether rs1
> is 0. This is similar to a patch I submitted last year [1]. Under
> normal circumstances this works fine, but it can leave stale TLB
> entries when the guest kernel uses THP.
>
> The reason is that with THP, the guest's mapping is at the PMD level.
> When flushing the TLB, the guest executes sfence.vma only once for the
> whole huge page, rather than 512 times to flush each individual 4K page.
> Patch 1 does not account for this: it only flushes the single 4K page
> corresponding to the address in rs1, leaving the other 511 pages of the
> huge page with stale entries. As a result, guest memory access faults
> can occur after Patch 1 is merged, unless huge-page TLB support is added
> for the RISC-V TCG case.
>
> Please correct me if I've misunderstood anything.
Hi,
Thanks for letting me know about this. I'm working on a local test setup to be
able to reproduce the problem, and I'll look into a more complete fix if so.
>
> [1] https://lore.kernel.org/qemu-devel/20250930174849833XG1Q1ETbNvg66WU2UVBTV@zte.com.cn/
>
> Yechao,
> Regards
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2026-10-19 17:14 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-04 18:43 [PATCH v2 0/2] target: riscv: support TLB page flush for sfence.vma Trevor Gamblin
2026-09-04 18:43 ` [PATCH v2 1/2] target/riscv: extract check_sfence_vma() from helper_tlb_flush() Trevor Gamblin
2026-09-07 13:05 ` Daniel Henrique Barboza
2026-09-14 0:59 ` Richard Henderson
2026-10-01 5:13 ` Alistair Francis
2026-09-04 18:43 ` [PATCH v2 2/2] target/riscv: add helper_tlb_flush_page(), use for sfence.vma with vaddr operand Trevor Gamblin
2026-09-07 13:05 ` Daniel Henrique Barboza
2026-09-14 1:06 ` Richard Henderson
2026-09-14 1:17 ` Richard Henderson
2026-09-14 1:34 ` Richard Henderson
2026-10-01 5:14 ` Alistair Francis
2026-10-01 17:02 ` Trevor Gamblin
2026-09-07 13:14 ` [PATCH v2 0/2] target: riscv: support TLB page flush for sfence.vma Daniel Henrique Barboza
2026-09-14 1:14 ` Richard Henderson
2026-09-15 20:40 ` Daniel Henrique Barboza
2026-10-08 7:00 ` Wang Yechao
2026-10-19 17:14 ` Trevor Gamblin
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.