[PATCH v2 13/15] riscv/mm: remove redundant TLB flush in napotpte_convert
Yunhui Cui
cuiyunhui at bytedance.com
Thu Jul 16 05:41:48 PDT 2026
napotpte_convert() switches a NAPOT block between the Svnapot encoding
and the equivalent set of base PTEs. The conversion itself preserves the
effective translations: PFNs and permissions are kept the same while the
page-table representation changes.
Do not issue an SFENCE.VMA in the middle of this representation change.
For partial updates, such as unfolding a writable NAPOT block before
write-protecting one subrange, a flush at conversion time would not be the
operation that enforces the final permission downgrade. Writable base-PTE
translations could still be refilled after the conversion and before the
subrange is write-protected. The required TLB maintenance belongs to the
operation that actually changes the effective mapping.
The RISC-V privileged architecture discussion clarifies that Svnapot is
an encoding of a naturally aligned range. When napotpte_convert() only
switches between the NAPOT encoding and the corresponding set of base PTEs
for the same effective translations, the conversion itself does not
require an extra TLB invalidation beyond the usual page-table update
rules.
Link: https://lists.riscv.org/g/tech-privileged/message/2950
Signed-off-by: Yunhui Cui <cuiyunhui at bytedance.com>
---
arch/riscv/mm/contpte.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/arch/riscv/mm/contpte.c b/arch/riscv/mm/contpte.c
index b0b8ff0aade3a..8289f39d6e188 100644
--- a/arch/riscv/mm/contpte.c
+++ b/arch/riscv/mm/contpte.c
@@ -129,7 +129,7 @@ static pte_t __get_and_clear_full_ptes(struct mm_struct *mm,
static void napotpte_convert(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t target)
{
- unsigned long start_addr, end, ptent_addr;
+ unsigned long start_addr, ptent_addr;
pte_t *start_ptep;
pte_t ptent, pte;
unsigned int i, nr;
@@ -137,7 +137,6 @@ static void napotpte_convert(struct mm_struct *mm, unsigned long addr,
start_addr = napot_align_addr(addr);
start_ptep = napot_align_ptep(ptep);
nr = napotpte_pte_num();
- end = start_addr + napotpte_size();
for (i = 0; i < nr; i++) {
ptent_addr = start_addr + i * PAGE_SIZE;
@@ -150,8 +149,15 @@ static void napotpte_convert(struct mm_struct *mm, unsigned long addr,
target = pte_mkyoung(target);
}
- flush_tlb_mm_range(mm, start_addr, end, PAGE_SIZE);
-
+ /*
+ * This conversion only changes the representation of the same
+ * effective translations: NAPOT encoding <-> equivalent base PTEs.
+ * A flush here would not replace the TLB maintenance required by a
+ * later permission downgrade, because writable base-PTE translations
+ * could be refilled after this conversion and before that downgrade.
+ * Leave TLB invalidation to the operation that changes the effective
+ * mapping.
+ */
page_table_check_ptes_set(mm, start_addr, start_ptep, target, nr);
if (pte_napot(target)) {
for (i = 0; i < nr; i++)
--
2.39.5
More information about the kvm-riscv
mailing list