mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
mm: pgtable: introduce pagetable_dtor()
The pagetable_p*_dtor() are exactly the same except for the handling of ptlock. If we make ptlock_free() handle the case where ptdesc->ptl is NULL and remove VM_BUG_ON_PAGE() from pmd_ptlock_free(), we can unify pagetable_p*_dtor() into one function. Let's introduce pagetable_dtor() to do this. Later, pagetable_dtor() will be moved to tlb_remove_ptdesc(), so that ptlock and page table pages can be freed together (regardless of whether RCU is used). This prevents the use-after-free problem where the ptlock is freed immediately but the page table pages is freed later via RCU. Link: https://lkml.kernel.org/r/47f44fff9dc68d9d9e9a0d6c036df275f820598a.1736317725.git.zhengqi.arch@bytedance.com Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com> Originally-by: Peter Zijlstra (Intel) <peterz@infradead.org> Reviewed-by: Kevin Brodsky <kevin.brodsky@arm.com> Acked-by: Alexander Gordeev <agordeev@linux.ibm.com> [s390] Cc: Alexandre Ghiti <alex@ghiti.fr> Cc: Alexandre Ghiti <alexghiti@rivosinc.com> Cc: Andreas Larsson <andreas@gaisler.com> Cc: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: David Hildenbrand <david@redhat.com> Cc: David Rientjes <rientjes@google.com> Cc: Hugh Dickins <hughd@google.com> Cc: Jann Horn <jannh@google.com> Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com> Cc: Matthew Wilcox (Oracle) <willy@infradead.org> Cc: Mike Rapoport (Microsoft) <rppt@kernel.org> Cc: Muchun Song <muchun.song@linux.dev> Cc: Nicholas Piggin <npiggin@gmail.com> Cc: Palmer Dabbelt <palmer@dabbelt.com> Cc: Ryan Roberts <ryan.roberts@arm.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Vishal Moola (Oracle) <vishal.moola@gmail.com> Cc: Will Deacon <will@kernel.org> Cc: Yu Zhao <yuzhao@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
@@ -62,7 +62,7 @@ Support of split page table lock by an architecture
|
||||
===================================================
|
||||
|
||||
There's no need in special enabling of PTE split page table lock: everything
|
||||
required is done by pagetable_pte_ctor() and pagetable_pte_dtor(), which
|
||||
required is done by pagetable_pte_ctor() and pagetable_dtor(), which
|
||||
must be called on PTE table allocation / freeing.
|
||||
|
||||
Make sure the architecture doesn't use slab allocator for page table
|
||||
@@ -73,7 +73,7 @@ PMD split lock only makes sense if you have more than two page table
|
||||
levels.
|
||||
|
||||
PMD split lock enabling requires pagetable_pmd_ctor() call on PMD table
|
||||
allocation and pagetable_pmd_dtor() on freeing.
|
||||
allocation and pagetable_dtor() on freeing.
|
||||
|
||||
Allocation usually happens in pmd_alloc_one(), freeing in pmd_free() and
|
||||
pmd_free_tlb(), but make sure you cover all PMD table allocation / freeing
|
||||
|
||||
@@ -41,7 +41,7 @@ __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte, unsigned long addr)
|
||||
{
|
||||
struct ptdesc *ptdesc = page_ptdesc(pte);
|
||||
|
||||
pagetable_pte_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
|
||||
#ifndef CONFIG_ARM_LPAE
|
||||
/*
|
||||
@@ -61,7 +61,7 @@ __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp, unsigned long addr)
|
||||
#ifdef CONFIG_ARM_LPAE
|
||||
struct ptdesc *ptdesc = virt_to_ptdesc(pmdp);
|
||||
|
||||
pagetable_pmd_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
tlb_remove_ptdesc(tlb, ptdesc);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
|
||||
{
|
||||
struct ptdesc *ptdesc = page_ptdesc(pte);
|
||||
|
||||
pagetable_pte_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
tlb_remove_ptdesc(tlb, ptdesc);
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
|
||||
{
|
||||
struct ptdesc *ptdesc = virt_to_ptdesc(pmdp);
|
||||
|
||||
pagetable_pmd_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
tlb_remove_ptdesc(tlb, ptdesc);
|
||||
}
|
||||
#endif
|
||||
@@ -106,7 +106,7 @@ static inline void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pudp,
|
||||
if (!pgtable_l4_enabled())
|
||||
return;
|
||||
|
||||
pagetable_pud_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
tlb_remove_ptdesc(tlb, ptdesc);
|
||||
}
|
||||
#endif
|
||||
@@ -120,7 +120,7 @@ static inline void __p4d_free_tlb(struct mmu_gather *tlb, p4d_t *p4dp,
|
||||
if (!pgtable_l5_enabled())
|
||||
return;
|
||||
|
||||
pagetable_p4d_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
tlb_remove_ptdesc(tlb, ptdesc);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -63,7 +63,7 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm)
|
||||
|
||||
#define __pte_free_tlb(tlb, pte, address) \
|
||||
do { \
|
||||
pagetable_pte_dtor(page_ptdesc(pte)); \
|
||||
pagetable_dtor(page_ptdesc(pte)); \
|
||||
tlb_remove_page_ptdesc(tlb, page_ptdesc(pte)); \
|
||||
} while (0)
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd,
|
||||
|
||||
#define __pte_free_tlb(tlb, pte, addr) \
|
||||
do { \
|
||||
pagetable_pte_dtor((page_ptdesc(pte))); \
|
||||
pagetable_dtor((page_ptdesc(pte))); \
|
||||
tlb_remove_page_ptdesc((tlb), (page_ptdesc(pte))); \
|
||||
} while (0)
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm)
|
||||
|
||||
#define __pte_free_tlb(tlb, pte, address) \
|
||||
do { \
|
||||
pagetable_pte_dtor(page_ptdesc(pte)); \
|
||||
pagetable_dtor(page_ptdesc(pte)); \
|
||||
tlb_remove_page_ptdesc((tlb), page_ptdesc(pte)); \
|
||||
} while (0)
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pgtable,
|
||||
{
|
||||
struct ptdesc *ptdesc = virt_to_ptdesc(pgtable);
|
||||
|
||||
pagetable_pte_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
pagetable_free(ptdesc);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ static inline void pte_free(struct mm_struct *mm, pgtable_t pgtable)
|
||||
{
|
||||
struct ptdesc *ptdesc = virt_to_ptdesc(pgtable);
|
||||
|
||||
pagetable_pte_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
pagetable_free(ptdesc);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ extern const char bad_pmd_string[];
|
||||
|
||||
#define __pte_free_tlb(tlb, pte, addr) \
|
||||
do { \
|
||||
pagetable_pte_dtor(page_ptdesc(pte)); \
|
||||
pagetable_dtor(page_ptdesc(pte)); \
|
||||
tlb_remove_page_ptdesc((tlb), page_ptdesc(pte)); \
|
||||
} while (0)
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ int free_pointer_table(void *table, int type)
|
||||
list_del(dp);
|
||||
mmu_page_dtor((void *)page);
|
||||
if (type == TABLE_PTE)
|
||||
pagetable_pte_dtor(virt_to_ptdesc((void *)page));
|
||||
pagetable_dtor(virt_to_ptdesc((void *)page));
|
||||
free_page (page);
|
||||
return 1;
|
||||
} else if (ptable_list[type].next != dp) {
|
||||
|
||||
@@ -56,7 +56,7 @@ static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
|
||||
|
||||
#define __pte_free_tlb(tlb, pte, address) \
|
||||
do { \
|
||||
pagetable_pte_dtor(page_ptdesc(pte)); \
|
||||
pagetable_dtor(page_ptdesc(pte)); \
|
||||
tlb_remove_page_ptdesc((tlb), page_ptdesc(pte)); \
|
||||
} while (0)
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ extern pgd_t *pgd_alloc(struct mm_struct *mm);
|
||||
|
||||
#define __pte_free_tlb(tlb, pte, addr) \
|
||||
do { \
|
||||
pagetable_pte_dtor(page_ptdesc(pte)); \
|
||||
pagetable_dtor(page_ptdesc(pte)); \
|
||||
tlb_remove_page_ptdesc((tlb), (page_ptdesc(pte))); \
|
||||
} while (0)
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ extern pte_t *pte_alloc_one_kernel(struct mm_struct *mm);
|
||||
|
||||
#define __pte_free_tlb(tlb, pte, addr) \
|
||||
do { \
|
||||
pagetable_pte_dtor(page_ptdesc(pte)); \
|
||||
pagetable_dtor(page_ptdesc(pte)); \
|
||||
tlb_remove_page_ptdesc((tlb), (page_ptdesc(pte))); \
|
||||
} while (0)
|
||||
|
||||
|
||||
@@ -253,7 +253,7 @@ static void pmd_frag_destroy(void *pmd_frag)
|
||||
count = ((unsigned long)pmd_frag & ~PAGE_MASK) >> PMD_FRAG_SIZE_SHIFT;
|
||||
/* We allow PTE_FRAG_NR fragments from a PTE page */
|
||||
if (atomic_sub_and_test(PMD_FRAG_NR - count, &ptdesc->pt_frag_refcount)) {
|
||||
pagetable_pmd_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
pagetable_free(ptdesc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,7 +477,7 @@ void pmd_fragment_free(unsigned long *pmd)
|
||||
|
||||
BUG_ON(atomic_read(&ptdesc->pt_frag_refcount) <= 0);
|
||||
if (atomic_dec_and_test(&ptdesc->pt_frag_refcount)) {
|
||||
pagetable_pmd_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
pagetable_free(ptdesc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ void pte_frag_destroy(void *pte_frag)
|
||||
count = ((unsigned long)pte_frag & ~PAGE_MASK) >> PTE_FRAG_SIZE_SHIFT;
|
||||
/* We allow PTE_FRAG_NR fragments from a PTE page */
|
||||
if (atomic_sub_and_test(PTE_FRAG_NR - count, &ptdesc->pt_frag_refcount)) {
|
||||
pagetable_pte_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
pagetable_free(ptdesc);
|
||||
}
|
||||
}
|
||||
@@ -111,7 +111,7 @@ static void pte_free_now(struct rcu_head *head)
|
||||
struct ptdesc *ptdesc;
|
||||
|
||||
ptdesc = container_of(head, struct ptdesc, pt_rcu_head);
|
||||
pagetable_pte_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
pagetable_free(ptdesc);
|
||||
}
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ static inline void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pud,
|
||||
if (pgtable_l4_enabled) {
|
||||
struct ptdesc *ptdesc = virt_to_ptdesc(pud);
|
||||
|
||||
pagetable_pud_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
riscv_tlb_remove_ptdesc(tlb, ptdesc);
|
||||
}
|
||||
}
|
||||
@@ -111,7 +111,7 @@ static inline void __p4d_free_tlb(struct mmu_gather *tlb, p4d_t *p4d,
|
||||
if (pgtable_l5_enabled) {
|
||||
struct ptdesc *ptdesc = virt_to_ptdesc(p4d);
|
||||
|
||||
pagetable_p4d_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
riscv_tlb_remove_ptdesc(tlb, virt_to_ptdesc(p4d));
|
||||
}
|
||||
}
|
||||
@@ -144,7 +144,7 @@ static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd,
|
||||
{
|
||||
struct ptdesc *ptdesc = virt_to_ptdesc(pmd);
|
||||
|
||||
pagetable_pmd_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
riscv_tlb_remove_ptdesc(tlb, ptdesc);
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
|
||||
{
|
||||
struct ptdesc *ptdesc = page_ptdesc(pte);
|
||||
|
||||
pagetable_pte_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
riscv_tlb_remove_ptdesc(tlb, ptdesc);
|
||||
}
|
||||
#endif /* CONFIG_MMU */
|
||||
|
||||
@@ -1558,7 +1558,7 @@ static void __meminit free_pte_table(pte_t *pte_start, pmd_t *pmd)
|
||||
return;
|
||||
}
|
||||
|
||||
pagetable_pte_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
if (PageReserved(page))
|
||||
free_reserved_page(page);
|
||||
else
|
||||
@@ -1580,7 +1580,7 @@ static void __meminit free_pmd_table(pmd_t *pmd_start, pud_t *pud, bool is_vmemm
|
||||
}
|
||||
|
||||
if (!is_vmemmap)
|
||||
pagetable_pmd_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
if (PageReserved(page))
|
||||
free_reserved_page(page);
|
||||
else
|
||||
|
||||
@@ -66,7 +66,7 @@ static inline void p4d_free(struct mm_struct *mm, p4d_t *p4d)
|
||||
if (mm_p4d_folded(mm))
|
||||
return;
|
||||
|
||||
pagetable_p4d_dtor(virt_to_ptdesc(p4d));
|
||||
pagetable_dtor(virt_to_ptdesc(p4d));
|
||||
crst_table_free(mm, (unsigned long *) p4d);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ static inline void pud_free(struct mm_struct *mm, pud_t *pud)
|
||||
if (mm_pud_folded(mm))
|
||||
return;
|
||||
|
||||
pagetable_pud_dtor(virt_to_ptdesc(pud));
|
||||
pagetable_dtor(virt_to_ptdesc(pud));
|
||||
crst_table_free(mm, (unsigned long *) pud);
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
|
||||
{
|
||||
if (mm_pmd_folded(mm))
|
||||
return;
|
||||
pagetable_pmd_dtor(virt_to_ptdesc(pmd));
|
||||
pagetable_dtor(virt_to_ptdesc(pmd));
|
||||
crst_table_free(mm, (unsigned long *) pmd);
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd,
|
||||
{
|
||||
if (mm_pmd_folded(tlb->mm))
|
||||
return;
|
||||
pagetable_pmd_dtor(virt_to_ptdesc(pmd));
|
||||
pagetable_dtor(virt_to_ptdesc(pmd));
|
||||
__tlb_adjust_range(tlb, address, PAGE_SIZE);
|
||||
tlb->mm->context.flush_mm = 1;
|
||||
tlb->freed_tables = 1;
|
||||
@@ -122,7 +122,7 @@ static inline void p4d_free_tlb(struct mmu_gather *tlb, p4d_t *p4d,
|
||||
{
|
||||
if (mm_p4d_folded(tlb->mm))
|
||||
return;
|
||||
pagetable_p4d_dtor(virt_to_ptdesc(p4d));
|
||||
pagetable_dtor(virt_to_ptdesc(p4d));
|
||||
__tlb_adjust_range(tlb, address, PAGE_SIZE);
|
||||
tlb->mm->context.flush_mm = 1;
|
||||
tlb->freed_tables = 1;
|
||||
@@ -141,7 +141,7 @@ static inline void pud_free_tlb(struct mmu_gather *tlb, pud_t *pud,
|
||||
{
|
||||
if (mm_pud_folded(tlb->mm))
|
||||
return;
|
||||
pagetable_pud_dtor(virt_to_ptdesc(pud));
|
||||
pagetable_dtor(virt_to_ptdesc(pud));
|
||||
tlb->mm->context.flush_mm = 1;
|
||||
tlb->freed_tables = 1;
|
||||
tlb->cleared_p4ds = 1;
|
||||
|
||||
@@ -182,7 +182,7 @@ unsigned long *page_table_alloc(struct mm_struct *mm)
|
||||
|
||||
static void pagetable_pte_dtor_free(struct ptdesc *ptdesc)
|
||||
{
|
||||
pagetable_pte_dtor(ptdesc);
|
||||
pagetable_dtor(ptdesc);
|
||||
pagetable_free(ptdesc);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user