[PATCH v1 3/5] KVM: riscv: Write-protect G-stage PTEs atomically
From: SeungJu Cheon
Date: Mon Sep 21 2026 - 07:33:58 EST
GSTAGE_OP_WP clears the W bit with a read-modify-write of the entire
PTE. Lockless aging can concurrently clear the Accessed bit, allowing
write-protection to overwrite the update with a stale PTE value.
Clear W atomically with test_and_clear_bit() so that concurrent
Accessed-bit updates are preserved. Its return value also preserves
the existing behavior of requesting a TLB flush only when W was
actually cleared.
Signed-off-by: SeungJu Cheon <suunj1331@xxxxxxxxx>
---
arch/riscv/kvm/gstage.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index 944fa4c95aea..f7e4756ce15b 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -378,7 +378,7 @@ bool kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr,
pte_t *ptep, u32 ptep_level, enum kvm_riscv_gstage_op op)
{
int i, ret;
- pte_t old_pte, pte, *next_ptep;
+ pte_t pte, *next_ptep;
u32 next_ptep_level;
unsigned long next_page_size, page_size;
bool flush = false;
@@ -408,13 +408,16 @@ bool kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr,
if (op == GSTAGE_OP_CLEAR)
put_page(virt_to_page(next_ptep));
} else {
- old_pte = *ptep;
- if (op == GSTAGE_OP_CLEAR)
+ if (op == GSTAGE_OP_CLEAR) {
set_pte(ptep, __pte(0));
- else if (op == GSTAGE_OP_WP)
- set_pte(ptep, __pte(pte_val(ptep_get(ptep)) & ~_PAGE_WRITE));
- if (pte_val(*ptep) != pte_val(old_pte))
flush = true;
+ } else if (op == GSTAGE_OP_WP) {
+ /*
+ * Clear W atomically to avoid clobbering a concurrent
+ * Accessed-bit update by lockless aging.
+ */
+ flush = test_and_clear_bit(__ffs(_PAGE_WRITE), &ptep->pte);
+ }
}
return flush;
--
2.52.0