[PATCH v2 01/15] riscv: introduce raw PTE helpers
Yunhui Cui <[email protected]> Thu, 16 Jul 2026 20:41:36 +0800
| Newsgroups | org.kernel.vger.linux-efi,org.infradead.lists.kvm-riscv,org.infradead.lists.linux-arm-kernel,org.infradead.lists.linux-riscv,org.kernel.vger.kvm,org.kernel.vger.linux-arch,org.kernel.vger.linux-kernel,org.kernel.vger.linux-perf-users,org.kvack.linux-mm |
|---|---|
| Message-ID | <2bde82cde4c262d35663d386e8a16455c76ec3e6.1784201104.git.cuiyunhui@bytedance.com> |
Svnapot needs RISC-V to distinguish between two PTE views: the raw encoded entries used by architecture code, and the logical per-page PTE view that will later be exposed to generic MM paths. Split the low-level RISC-V PTE operations from the public helpers so the raw helpers can keep operating on the hardware encoding, while follow-up changes build the Svnapot-aware public view on top. Signed-off-by: Yunhui Cui <[email protected]> --- arch/riscv/include/asm/pgtable.h | 129 +++++++++++++++++++++++++++---- 1 file changed, 114 insertions(+), 15 deletions(-) diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index a810655ce8f9b..8ca5da9534967 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -9,6 +9,7 @@ #include <linux/mmzone.h> #include <linux/sizes.h> +#include <asm/cmpxchg.h> #include <asm/pgtable-bits.h> #ifndef CONFIG_MMU @@ -628,11 +629,13 @@ static inline int pte_same(pte_t pte_a, pte_t pte_b) * a page table are directly modified. Thus, the following hook is * made available. */ -static inline void set_pte(pte_t *ptep, pte_t pteval) +static inline void __set_pte(pte_t *ptep, pte_t pteval) { WRITE_ONCE(*ptep, pteval); } +#define __set_pte __set_pte + void flush_icache_pte(struct mm_struct *mm, pte_t pte); static inline void __set_pte_at(struct mm_struct *mm, pte_t *ptep, pte_t pteval) @@ -640,13 +643,13 @@ static inline void __set_pte_at(struct mm_struct *mm, pte_t *ptep, pte_t pteval) if (pte_present(pteval) && pte_exec(pteval)) flush_icache_pte(mm, pteval); - set_pte(ptep, pteval); + __set_pte(ptep, pteval); } #define PFN_PTE_SHIFT _PAGE_PFN_SHIFT -static inline void set_ptes(struct mm_struct *mm, unsigned long addr, - pte_t *ptep, pte_t pteval, unsigned int nr) +static inline void __set_ptes(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pteval, unsigned int nr) { page_table_check_ptes_set(mm, addr, ptep, pteval, nr); @@ -658,24 +661,85 @@ static inline void set_ptes(struct mm_struct *mm, unsigned long addr, pte_val(pteval) += 1 << _PAGE_PFN_SHIFT; } } -#define set_ptes set_ptes -static inline void pte_clear(struct mm_struct *mm, - unsigned long addr, pte_t *ptep) +#define __set_ptes __set_ptes + +static inline void __pte_clear(struct mm_struct *mm, + unsigned long addr, pte_t *ptep) { __set_pte_at(mm, ptep, __pte(0)); } +#define __pte_clear __pte_clear + +#define __ptep_get __ptep_get +static inline pte_t __ptep_get(pte_t *ptep) +{ + return READ_ONCE(*ptep); +} + +#define __ptep_get_lockless __ptep_get_lockless +static inline pte_t __ptep_get_lockless(pte_t *ptep) +{ + return __ptep_get(ptep); +} + +static inline void __clear_young_dirty_pte(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep, + pte_t pte, cydp_t flags) +{ + pte_t old_pte; + + do { + old_pte = pte; + + if (flags & CYDP_CLEAR_YOUNG) + pte = pte_mkold(pte); + if (flags & CYDP_CLEAR_DIRTY) + pte = pte_mkclean(pte); + + pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep), + pte_val(old_pte), + pte_val(pte)); + } while (pte_val(pte) != pte_val(old_pte)); +} + +static inline void __clear_young_dirty_ptes(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep, + unsigned int nr, cydp_t flags) +{ + pte_t pte; + + for (;;) { + pte = __ptep_get(ptep); + + if (flags == (CYDP_CLEAR_YOUNG | CYDP_CLEAR_DIRTY)) + __set_pte(ptep, pte_mkclean(pte_mkold(pte))); + else + __clear_young_dirty_pte(vma, addr, ptep, pte, flags); + + if (--nr == 0) + break; + ptep++; + addr += PAGE_SIZE; + } +} + #define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS /* defined in mm/pgtable.c */ extern int ptep_set_access_flags(struct vm_area_struct *vma, unsigned long address, pte_t *ptep, pte_t entry, int dirty); +int __ptep_set_access_flags(struct vm_area_struct *vma, + unsigned long address, pte_t *ptep, + pte_t entry, int dirty); #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG /* defined in mm/pgtable.c */ bool ptep_test_and_clear_young(struct vm_area_struct *vma, - unsigned long address, pte_t *ptep); + unsigned long address, pte_t *ptep); +bool __ptep_test_and_clear_young(struct vm_area_struct *vma, + unsigned long address, pte_t *ptep); #define __HAVE_ARCH_PTEP_GET_AND_CLEAR -static inline pte_t ptep_get_and_clear(struct mm_struct *mm, - unsigned long address, pte_t *ptep) +static inline pte_t __ptep_get_and_clear(struct mm_struct *mm, + unsigned long address, pte_t *ptep) { #ifdef CONFIG_SMP pte_t pte = __pte(xchg(&ptep->pte, 0)); @@ -690,9 +754,24 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm, return pte; } +#define __ptep_get_and_clear __ptep_get_and_clear + +static inline pte_t __ptep_clear_flush(struct vm_area_struct *vma, + unsigned long address, pte_t *ptep) +{ + pte_t pte = __ptep_get_and_clear(vma->vm_mm, address, ptep); + + if (pte_accessible(vma->vm_mm, pte)) + flush_tlb_page(vma, address); + + return pte; +} + +#define __ptep_clear_flush __ptep_clear_flush + #define __HAVE_ARCH_PTEP_SET_WRPROTECT -static inline void ptep_set_wrprotect(struct mm_struct *mm, - unsigned long address, pte_t *ptep) +static inline void __ptep_set_wrprotect(struct mm_struct *mm, + unsigned long address, pte_t *ptep) { pte_t read_pte = READ_ONCE(*ptep); /* @@ -705,9 +784,11 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, ((pte_val(read_pte) & ~(unsigned long)_PAGE_WRITE) | _PAGE_READ)); } +#define __ptep_set_wrprotect __ptep_set_wrprotect + #define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH -static inline bool ptep_clear_flush_young(struct vm_area_struct *vma, - unsigned long address, pte_t *ptep) +static inline bool __ptep_clear_flush_young(struct vm_area_struct *vma, + unsigned long address, pte_t *ptep) { /* * This comment is borrowed from x86, but applies equally to RISC-V: @@ -724,9 +805,27 @@ static inline bool ptep_clear_flush_young(struct vm_area_struct *vma, * shouldn't really matter because there's no real memory * pressure for swapout to react to. ] */ - return ptep_test_and_clear_young(vma, address, ptep); + return __ptep_test_and_clear_young(vma, address, ptep); } +#define __ptep_clear_flush_young __ptep_clear_flush_young + +#define ptep_get __ptep_get +#define ptep_get_lockless __ptep_get_lockless +#define set_pte __set_pte +#define set_ptes __set_ptes + +static inline void pte_clear(struct mm_struct *mm, + unsigned long addr, pte_t *ptep) +{ + __pte_clear(mm, addr, ptep); +} + +#define ptep_get_and_clear __ptep_get_and_clear +#define clear_young_dirty_ptes __clear_young_dirty_ptes +#define ptep_set_wrprotect __ptep_set_wrprotect +#define ptep_clear_flush_young __ptep_clear_flush_young + #define pgprot_nx pgprot_nx static inline pgprot_t pgprot_nx(pgprot_t _prot) { -- 2.39.5