Merge e26060d1fb ("mm: swap: make some count_mthp_stat() call-sites be THP-agnostic.") into android-mainline
Steps on the way to 6.13-rc1 Change-Id: I9d399f315947ddedc5913b7caad55c73a1312493 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
commit
e63efd43f8
24 files changed
+292
-240
No files matched your search
@@ -16,9 +16,6 @@ There are helpers to lock/unlock a table and other accessor functions:
|
||||
- pte_offset_map_lock()
|
||||
maps PTE and takes PTE table lock, returns pointer to PTE with
|
||||
pointer to its PTE table lock, or returns NULL if no PTE table;
|
||||
- pte_offset_map_nolock()
|
||||
maps PTE, returns pointer to PTE with pointer to its PTE table
|
||||
lock (not taken), or returns NULL if no PTE table;
|
||||
- pte_offset_map_ro_nolock()
|
||||
maps PTE, returns pointer to PTE with pointer to its PTE table
|
||||
lock (not taken), or returns NULL if no PTE table;
|
||||
|
||||
+22
-31
@@ -61,32 +61,8 @@ static int do_adjust_pte(struct vm_area_struct *vma, unsigned long address,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SPLIT_PTE_PTLOCKS)
|
||||
/*
|
||||
* If we are using split PTE locks, then we need to take the page
|
||||
* lock here. Otherwise we are using shared mm->page_table_lock
|
||||
* which is already locked, thus cannot take it.
|
||||
*/
|
||||
static inline void do_pte_lock(spinlock_t *ptl)
|
||||
{
|
||||
/*
|
||||
* Use nested version here to indicate that we are already
|
||||
* holding one similar spinlock.
|
||||
*/
|
||||
spin_lock_nested(ptl, SINGLE_DEPTH_NESTING);
|
||||
}
|
||||
|
||||
static inline void do_pte_unlock(spinlock_t *ptl)
|
||||
{
|
||||
spin_unlock(ptl);
|
||||
}
|
||||
#else /* !defined(CONFIG_SPLIT_PTE_PTLOCKS) */
|
||||
static inline void do_pte_lock(spinlock_t *ptl) {}
|
||||
static inline void do_pte_unlock(spinlock_t *ptl) {}
|
||||
#endif /* defined(CONFIG_SPLIT_PTE_PTLOCKS) */
|
||||
|
||||
static int adjust_pte(struct vm_area_struct *vma, unsigned long address,
|
||||
unsigned long pfn)
|
||||
unsigned long pfn, struct vm_fault *vmf)
|
||||
{
|
||||
spinlock_t *ptl;
|
||||
pgd_t *pgd;
|
||||
@@ -94,6 +70,7 @@ static int adjust_pte(struct vm_area_struct *vma, unsigned long address,
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
pte_t *pte;
|
||||
pmd_t pmdval;
|
||||
int ret;
|
||||
|
||||
pgd = pgd_offset(vma->vm_mm, address);
|
||||
@@ -112,20 +89,33 @@ static int adjust_pte(struct vm_area_struct *vma, unsigned long address,
|
||||
if (pmd_none_or_clear_bad(pmd))
|
||||
return 0;
|
||||
|
||||
again:
|
||||
/*
|
||||
* This is called while another page table is mapped, so we
|
||||
* must use the nested version. This also means we need to
|
||||
* open-code the spin-locking.
|
||||
*/
|
||||
pte = pte_offset_map_nolock(vma->vm_mm, pmd, address, &ptl);
|
||||
pte = pte_offset_map_rw_nolock(vma->vm_mm, pmd, address, &pmdval, &ptl);
|
||||
if (!pte)
|
||||
return 0;
|
||||
|
||||
do_pte_lock(ptl);
|
||||
/*
|
||||
* If we are using split PTE locks, then we need to take the page
|
||||
* lock here. Otherwise we are using shared mm->page_table_lock
|
||||
* which is already locked, thus cannot take it.
|
||||
*/
|
||||
if (ptl != vmf->ptl) {
|
||||
spin_lock_nested(ptl, SINGLE_DEPTH_NESTING);
|
||||
if (unlikely(!pmd_same(pmdval, pmdp_get_lockless(pmd)))) {
|
||||
pte_unmap_unlock(pte, ptl);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
ret = do_adjust_pte(vma, address, pfn, pte);
|
||||
|
||||
do_pte_unlock(ptl);
|
||||
if (ptl != vmf->ptl)
|
||||
spin_unlock(ptl);
|
||||
pte_unmap(pte);
|
||||
|
||||
return ret;
|
||||
@@ -133,7 +123,8 @@ static int adjust_pte(struct vm_area_struct *vma, unsigned long address,
|
||||
|
||||
static void
|
||||
make_coherent(struct address_space *mapping, struct vm_area_struct *vma,
|
||||
unsigned long addr, pte_t *ptep, unsigned long pfn)
|
||||
unsigned long addr, pte_t *ptep, unsigned long pfn,
|
||||
struct vm_fault *vmf)
|
||||
{
|
||||
struct mm_struct *mm = vma->vm_mm;
|
||||
struct vm_area_struct *mpnt;
|
||||
@@ -160,7 +151,7 @@ make_coherent(struct address_space *mapping, struct vm_area_struct *vma,
|
||||
if (!(mpnt->vm_flags & VM_MAYSHARE))
|
||||
continue;
|
||||
offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT;
|
||||
aliases += adjust_pte(mpnt, mpnt->vm_start + offset, pfn);
|
||||
aliases += adjust_pte(mpnt, mpnt->vm_start + offset, pfn, vmf);
|
||||
}
|
||||
flush_dcache_mmap_unlock(mapping);
|
||||
if (aliases)
|
||||
@@ -203,7 +194,7 @@ void update_mmu_cache_range(struct vm_fault *vmf, struct vm_area_struct *vma,
|
||||
__flush_dcache_folio(mapping, folio);
|
||||
if (mapping) {
|
||||
if (cache_is_vivt())
|
||||
make_coherent(mapping, vma, addr, ptep, pfn);
|
||||
make_coherent(mapping, vma, addr, ptep, pfn, vmf);
|
||||
else if (vma->vm_flags & VM_EXEC)
|
||||
__flush_icache_all();
|
||||
}
|
||||
|
||||
@@ -963,6 +963,25 @@ void misc_mem_init(void);
|
||||
extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
|
||||
#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page))
|
||||
|
||||
/*
|
||||
* Use set_p*_safe(), and elide TLB flushing, when confident that *no*
|
||||
* TLB flush will be required as a result of the "set". For example, use
|
||||
* in scenarios where it is known ahead of time that the routine is
|
||||
* setting non-present entries, or re-setting an existing entry to the
|
||||
* same value. Otherwise, use the typical "set" helpers and flush the
|
||||
* TLB.
|
||||
*/
|
||||
#define set_p4d_safe(p4dp, p4d) \
|
||||
({ \
|
||||
WARN_ON_ONCE(p4d_present(*p4dp) && !p4d_same(*p4dp, p4d)); \
|
||||
set_p4d(p4dp, p4d); \
|
||||
})
|
||||
|
||||
#define set_pgd_safe(pgdp, pgd) \
|
||||
({ \
|
||||
WARN_ON_ONCE(pgd_present(*pgdp) && !pgd_same(*pgdp, pgd)); \
|
||||
set_pgd(pgdp, pgd); \
|
||||
})
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
|
||||
#endif /* _ASM_RISCV_PGTABLE_H */
|
||||
@@ -1775,6 +1775,43 @@ bool arch_is_platform_page(u64 paddr);
|
||||
#define arch_is_platform_page arch_is_platform_page
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Use set_p*_safe(), and elide TLB flushing, when confident that *no*
|
||||
* TLB flush will be required as a result of the "set". For example, use
|
||||
* in scenarios where it is known ahead of time that the routine is
|
||||
* setting non-present entries, or re-setting an existing entry to the
|
||||
* same value. Otherwise, use the typical "set" helpers and flush the
|
||||
* TLB.
|
||||
*/
|
||||
#define set_pte_safe(ptep, pte) \
|
||||
({ \
|
||||
WARN_ON_ONCE(pte_present(*ptep) && !pte_same(*ptep, pte)); \
|
||||
set_pte(ptep, pte); \
|
||||
})
|
||||
|
||||
#define set_pmd_safe(pmdp, pmd) \
|
||||
({ \
|
||||
WARN_ON_ONCE(pmd_present(*pmdp) && !pmd_same(*pmdp, pmd)); \
|
||||
set_pmd(pmdp, pmd); \
|
||||
})
|
||||
|
||||
#define set_pud_safe(pudp, pud) \
|
||||
({ \
|
||||
WARN_ON_ONCE(pud_present(*pudp) && !pud_same(*pudp, pud)); \
|
||||
set_pud(pudp, pud); \
|
||||
})
|
||||
|
||||
#define set_p4d_safe(p4dp, p4d) \
|
||||
({ \
|
||||
WARN_ON_ONCE(p4d_present(*p4dp) && !p4d_same(*p4dp, p4d)); \
|
||||
set_p4d(p4dp, p4d); \
|
||||
})
|
||||
|
||||
#define set_pgd_safe(pgdp, pgd) \
|
||||
({ \
|
||||
WARN_ON_ONCE(pgd_present(*pgdp) && !pgd_same(*pgdp, pgd)); \
|
||||
set_pgd(pgdp, pgd); \
|
||||
})
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* _ASM_X86_PGTABLE_H */
|
||||
@@ -3028,8 +3028,6 @@ static inline pte_t *pte_offset_map_lock(struct mm_struct *mm, pmd_t *pmd,
|
||||
return pte;
|
||||
}
|
||||
|
||||
pte_t *pte_offset_map_nolock(struct mm_struct *mm, pmd_t *pmd,
|
||||
unsigned long addr, spinlock_t **ptlp);
|
||||
pte_t *pte_offset_map_ro_nolock(struct mm_struct *mm, pmd_t *pmd,
|
||||
unsigned long addr, spinlock_t **ptlp);
|
||||
pte_t *pte_offset_map_rw_nolock(struct mm_struct *mm, pmd_t *pmd,
|
||||
|
||||
@@ -291,6 +291,12 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void folio_migrate_refs(struct folio *new, struct folio *old)
|
||||
{
|
||||
unsigned long refs = READ_ONCE(old->flags) & LRU_REFS_MASK;
|
||||
|
||||
set_mask_bits(&new->flags, LRU_REFS_MASK, refs);
|
||||
}
|
||||
#else /* !CONFIG_LRU_GEN */
|
||||
|
||||
static inline bool lru_gen_enabled(void)
|
||||
@@ -313,6 +319,10 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void folio_migrate_refs(struct folio *new, struct folio *old)
|
||||
{
|
||||
|
||||
}
|
||||
#endif /* CONFIG_LRU_GEN */
|
||||
|
||||
static __always_inline
|
||||
|
||||
@@ -693,6 +693,13 @@ static __always_inline bool folio_test_anon(const struct folio *folio)
|
||||
return ((unsigned long)folio->mapping & PAGE_MAPPING_ANON) != 0;
|
||||
}
|
||||
|
||||
static __always_inline bool PageAnonNotKsm(const struct page *page)
|
||||
{
|
||||
unsigned long flags = (unsigned long)page_folio(page)->mapping;
|
||||
|
||||
return (flags & PAGE_MAPPING_FLAGS) == PAGE_MAPPING_ANON;
|
||||
}
|
||||
|
||||
static __always_inline bool PageAnon(const struct page *page)
|
||||
{
|
||||
return folio_test_anon(page_folio(page));
|
||||
@@ -722,13 +729,8 @@ static __always_inline bool folio_test_ksm(const struct folio *folio)
|
||||
return ((unsigned long)folio->mapping & PAGE_MAPPING_FLAGS) ==
|
||||
PAGE_MAPPING_KSM;
|
||||
}
|
||||
|
||||
static __always_inline bool PageKsm(const struct page *page)
|
||||
{
|
||||
return folio_test_ksm(page_folio(page));
|
||||
}
|
||||
#else
|
||||
TESTPAGEFLAG_FALSE(Ksm, ksm)
|
||||
FOLIO_TEST_FLAG_FALSE(ksm)
|
||||
#endif
|
||||
|
||||
u64 stable_page_flags(const struct page *page);
|
||||
@@ -1141,14 +1143,14 @@ static __always_inline int PageAnonExclusive(const struct page *page)
|
||||
|
||||
static __always_inline void SetPageAnonExclusive(struct page *page)
|
||||
{
|
||||
VM_BUG_ON_PGFLAGS(!PageAnon(page) || PageKsm(page), page);
|
||||
VM_BUG_ON_PGFLAGS(!PageAnonNotKsm(page), page);
|
||||
VM_BUG_ON_PGFLAGS(PageHuge(page) && !PageHead(page), page);
|
||||
set_bit(PG_anon_exclusive, &PF_ANY(page, 1)->flags);
|
||||
}
|
||||
|
||||
static __always_inline void ClearPageAnonExclusive(struct page *page)
|
||||
{
|
||||
VM_BUG_ON_PGFLAGS(!PageAnon(page) || PageKsm(page), page);
|
||||
VM_BUG_ON_PGFLAGS(!PageAnonNotKsm(page), page);
|
||||
VM_BUG_ON_PGFLAGS(PageHuge(page) && !PageHead(page), page);
|
||||
clear_bit(PG_anon_exclusive, &PF_ANY(page, 1)->flags);
|
||||
}
|
||||
|
||||
@@ -1056,44 +1056,6 @@ static inline int pgd_same(pgd_t pgd_a, pgd_t pgd_b)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Use set_p*_safe(), and elide TLB flushing, when confident that *no*
|
||||
* TLB flush will be required as a result of the "set". For example, use
|
||||
* in scenarios where it is known ahead of time that the routine is
|
||||
* setting non-present entries, or re-setting an existing entry to the
|
||||
* same value. Otherwise, use the typical "set" helpers and flush the
|
||||
* TLB.
|
||||
*/
|
||||
#define set_pte_safe(ptep, pte) \
|
||||
({ \
|
||||
WARN_ON_ONCE(pte_present(*ptep) && !pte_same(*ptep, pte)); \
|
||||
set_pte(ptep, pte); \
|
||||
})
|
||||
|
||||
#define set_pmd_safe(pmdp, pmd) \
|
||||
({ \
|
||||
WARN_ON_ONCE(pmd_present(*pmdp) && !pmd_same(*pmdp, pmd)); \
|
||||
set_pmd(pmdp, pmd); \
|
||||
})
|
||||
|
||||
#define set_pud_safe(pudp, pud) \
|
||||
({ \
|
||||
WARN_ON_ONCE(pud_present(*pudp) && !pud_same(*pudp, pud)); \
|
||||
set_pud(pudp, pud); \
|
||||
})
|
||||
|
||||
#define set_p4d_safe(p4dp, p4d) \
|
||||
({ \
|
||||
WARN_ON_ONCE(p4d_present(*p4dp) && !p4d_same(*p4dp, p4d)); \
|
||||
set_p4d(p4dp, p4d); \
|
||||
})
|
||||
|
||||
#define set_pgd_safe(pgdp, pgd) \
|
||||
({ \
|
||||
WARN_ON_ONCE(pgd_present(*pgdp) && !pgd_same(*pgdp, pgd)); \
|
||||
set_pgd(pgdp, pgd); \
|
||||
})
|
||||
|
||||
#ifndef __HAVE_ARCH_DO_SWAP_PAGE
|
||||
static inline void arch_do_swap_page_nr(struct mm_struct *mm,
|
||||
struct vm_area_struct *vma,
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include "cma.h"
|
||||
|
||||
struct cma cma_areas[MAX_CMA_AREAS];
|
||||
unsigned cma_area_count;
|
||||
unsigned int cma_area_count;
|
||||
static DEFINE_MUTEX(cma_mutex);
|
||||
|
||||
phys_addr_t cma_get_base(const struct cma *cma)
|
||||
@@ -139,7 +139,6 @@ out_error:
|
||||
totalcma_pages -= cma->count;
|
||||
cma->count = 0;
|
||||
pr_err("CMA area %s could not be activated\n", cma->name);
|
||||
return;
|
||||
}
|
||||
|
||||
static int __init cma_init_reserved_areas(void)
|
||||
|
||||
+2
-2
@@ -3260,8 +3260,8 @@ static vm_fault_t filemap_fault_recheck_pte_none(struct vm_fault *vmf)
|
||||
if (!(vmf->flags & FAULT_FLAG_ORIG_PTE_VALID))
|
||||
return 0;
|
||||
|
||||
ptep = pte_offset_map_nolock(vma->vm_mm, vmf->pmd, vmf->address,
|
||||
&vmf->ptl);
|
||||
ptep = pte_offset_map_ro_nolock(vma->vm_mm, vmf->pmd, vmf->address,
|
||||
&vmf->ptl);
|
||||
if (unlikely(!ptep))
|
||||
return VM_FAULT_NOPAGE;
|
||||
|
||||
|
||||
@@ -922,14 +922,14 @@ static struct page *follow_page_pte(struct vm_area_struct *vma,
|
||||
}
|
||||
if (flags & FOLL_TOUCH) {
|
||||
if ((flags & FOLL_WRITE) &&
|
||||
!pte_dirty(pte) && !PageDirty(page))
|
||||
set_page_dirty(page);
|
||||
!pte_dirty(pte) && !folio_test_dirty(folio))
|
||||
folio_mark_dirty(folio);
|
||||
/*
|
||||
* pte_mkyoung() would be more correct here, but atomic care
|
||||
* is needed to avoid losing the dirty bit: it is easier to use
|
||||
* mark_page_accessed().
|
||||
* folio_mark_accessed().
|
||||
*/
|
||||
mark_page_accessed(page);
|
||||
folio_mark_accessed(folio);
|
||||
}
|
||||
out:
|
||||
pte_unmap_unlock(ptep, ptl);
|
||||
|
||||
+1
-1
@@ -1356,7 +1356,7 @@ static inline bool gup_must_unshare(struct vm_area_struct *vma,
|
||||
smp_rmb();
|
||||
|
||||
/*
|
||||
* Note that PageKsm() pages cannot be exclusive, and consequently,
|
||||
* Note that KSM pages cannot be exclusive, and consequently,
|
||||
* cannot get pinned.
|
||||
*/
|
||||
return !PageAnonExclusive(page);
|
||||
|
||||
+18
-6
@@ -1014,7 +1014,11 @@ static int __collapse_huge_page_swapin(struct mm_struct *mm,
|
||||
};
|
||||
|
||||
if (!pte++) {
|
||||
pte = pte_offset_map_nolock(mm, pmd, address, &ptl);
|
||||
/*
|
||||
* Here the ptl is only used to check pte_same() in
|
||||
* do_swap_page(), so readonly version is enough.
|
||||
*/
|
||||
pte = pte_offset_map_ro_nolock(mm, pmd, address, &ptl);
|
||||
if (!pte) {
|
||||
mmap_read_unlock(mm);
|
||||
result = SCAN_PMD_NULL;
|
||||
@@ -1604,7 +1608,7 @@ int collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr,
|
||||
if (userfaultfd_armed(vma) && !(vma->vm_flags & VM_SHARED))
|
||||
pml = pmd_lock(mm, pmd);
|
||||
|
||||
start_pte = pte_offset_map_nolock(mm, pmd, haddr, &ptl);
|
||||
start_pte = pte_offset_map_rw_nolock(mm, pmd, haddr, &pgt_pmd, &ptl);
|
||||
if (!start_pte) /* mmap_lock + page lock should prevent this */
|
||||
goto abort;
|
||||
if (!pml)
|
||||
@@ -1612,6 +1616,9 @@ int collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr,
|
||||
else if (ptl != pml)
|
||||
spin_lock_nested(ptl, SINGLE_DEPTH_NESTING);
|
||||
|
||||
if (unlikely(!pmd_same(pgt_pmd, pmdp_get_lockless(pmd))))
|
||||
goto abort;
|
||||
|
||||
/* step 2: clear page table and adjust rmap */
|
||||
for (i = 0, addr = haddr, pte = start_pte;
|
||||
i < HPAGE_PMD_NR; i++, addr += PAGE_SIZE, pte++) {
|
||||
@@ -1644,7 +1651,6 @@ int collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr,
|
||||
nr_ptes++;
|
||||
}
|
||||
|
||||
pte_unmap(start_pte);
|
||||
if (!pml)
|
||||
spin_unlock(ptl);
|
||||
|
||||
@@ -1657,14 +1663,19 @@ int collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr,
|
||||
/* step 4: remove empty page table */
|
||||
if (!pml) {
|
||||
pml = pmd_lock(mm, pmd);
|
||||
if (ptl != pml)
|
||||
if (ptl != pml) {
|
||||
spin_lock_nested(ptl, SINGLE_DEPTH_NESTING);
|
||||
if (unlikely(!pmd_same(pgt_pmd, pmdp_get_lockless(pmd)))) {
|
||||
flush_tlb_mm(mm);
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
pgt_pmd = pmdp_collapse_flush(vma, haddr, pmd);
|
||||
pmdp_get_lockless_sync();
|
||||
pte_unmap_unlock(start_pte, ptl);
|
||||
if (ptl != pml)
|
||||
spin_unlock(ptl);
|
||||
spin_unlock(pml);
|
||||
spin_unlock(pml);
|
||||
|
||||
mmu_notifier_invalidate_range_end(&range);
|
||||
|
||||
@@ -1684,6 +1695,7 @@ abort:
|
||||
folio_ref_sub(folio, nr_ptes);
|
||||
add_mm_counter(mm, mm_counter_file(folio), -nr_ptes);
|
||||
}
|
||||
unlock:
|
||||
if (start_pte)
|
||||
pte_unmap_unlock(start_pte, ptl);
|
||||
if (pml && pml != ptl)
|
||||
|
||||
@@ -656,7 +656,7 @@ static int break_ksm(struct vm_area_struct *vma, unsigned long addr, bool lock_v
|
||||
*
|
||||
* VM_FAULT_SIGBUS could occur if we race with truncation of the
|
||||
* backing file, which also invalidates anonymous pages: that's
|
||||
* okay, that truncation will have unmapped the PageKsm for us.
|
||||
* okay, that truncation will have unmapped the KSM page for us.
|
||||
*
|
||||
* VM_FAULT_OOM: at the time of writing (late July 2009), setting
|
||||
* aside mem_cgroup limits, VM_FAULT_OOM would only be set if the
|
||||
@@ -1434,7 +1434,7 @@ out:
|
||||
* try_to_merge_one_page - take two pages and merge them into one
|
||||
* @vma: the vma that holds the pte pointing to page
|
||||
* @page: the PageAnon page that we want to replace with kpage
|
||||
* @kpage: the PageKsm page that we want to map instead of page,
|
||||
* @kpage: the KSM page that we want to map instead of page,
|
||||
* or NULL the first time when we want to use page as kpage.
|
||||
*
|
||||
* This function returns 0 if the pages were merged, -EFAULT otherwise.
|
||||
@@ -1442,28 +1442,29 @@ out:
|
||||
static int try_to_merge_one_page(struct vm_area_struct *vma,
|
||||
struct page *page, struct page *kpage)
|
||||
{
|
||||
struct folio *folio = page_folio(page);
|
||||
pte_t orig_pte = __pte(0);
|
||||
int err = -EFAULT;
|
||||
|
||||
if (page == kpage) /* ksm page forked */
|
||||
return 0;
|
||||
|
||||
if (!PageAnon(page))
|
||||
if (!folio_test_anon(folio))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* We need the folio lock to read a stable swapcache flag in
|
||||
* write_protect_page(). We use trylock_page() instead of
|
||||
* lock_page() because we don't want to wait here - we
|
||||
* prefer to continue scanning and merging different pages,
|
||||
* then come back to this page when it is unlocked.
|
||||
* write_protect_page(). We trylock because we don't want to wait
|
||||
* here - we prefer to continue scanning and merging different
|
||||
* pages, then come back to this page when it is unlocked.
|
||||
*/
|
||||
if (!trylock_page(page))
|
||||
if (!folio_trylock(folio))
|
||||
goto out;
|
||||
|
||||
if (PageTransCompound(page)) {
|
||||
if (folio_test_large(folio)) {
|
||||
if (split_huge_page(page))
|
||||
goto out_unlock;
|
||||
folio = page_folio(page);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1472,28 +1473,28 @@ static int try_to_merge_one_page(struct vm_area_struct *vma,
|
||||
* ptes are necessarily already write-protected. But in either
|
||||
* case, we need to lock and check page_count is not raised.
|
||||
*/
|
||||
if (write_protect_page(vma, page_folio(page), &orig_pte) == 0) {
|
||||
if (write_protect_page(vma, folio, &orig_pte) == 0) {
|
||||
if (!kpage) {
|
||||
/*
|
||||
* While we hold page lock, upgrade page from
|
||||
* PageAnon+anon_vma to PageKsm+NULL stable_node:
|
||||
* While we hold folio lock, upgrade folio from
|
||||
* anon to a NULL stable_node with the KSM flag set:
|
||||
* stable_tree_insert() will update stable_node.
|
||||
*/
|
||||
folio_set_stable_node(page_folio(page), NULL);
|
||||
mark_page_accessed(page);
|
||||
folio_set_stable_node(folio, NULL);
|
||||
folio_mark_accessed(folio);
|
||||
/*
|
||||
* Page reclaim just frees a clean page with no dirty
|
||||
* Page reclaim just frees a clean folio with no dirty
|
||||
* ptes: make sure that the ksm page would be swapped.
|
||||
*/
|
||||
if (!PageDirty(page))
|
||||
SetPageDirty(page);
|
||||
if (!folio_test_dirty(folio))
|
||||
folio_mark_dirty(folio);
|
||||
err = 0;
|
||||
} else if (pages_identical(page, kpage))
|
||||
err = replace_page(vma, page, kpage, orig_pte);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
@@ -1581,7 +1582,7 @@ out:
|
||||
* Note that this function upgrades page to ksm page: if one of the pages
|
||||
* is already a ksm page, try_to_merge_with_ksm_page should be used.
|
||||
*/
|
||||
static struct page *try_to_merge_two_pages(struct ksm_rmap_item *rmap_item,
|
||||
static struct folio *try_to_merge_two_pages(struct ksm_rmap_item *rmap_item,
|
||||
struct page *page,
|
||||
struct ksm_rmap_item *tree_rmap_item,
|
||||
struct page *tree_page)
|
||||
@@ -1599,7 +1600,7 @@ static struct page *try_to_merge_two_pages(struct ksm_rmap_item *rmap_item,
|
||||
if (err)
|
||||
break_cow(rmap_item);
|
||||
}
|
||||
return err ? NULL : page;
|
||||
return err ? NULL : page_folio(page);
|
||||
}
|
||||
|
||||
static __always_inline
|
||||
@@ -1786,9 +1787,9 @@ static __always_inline struct folio *chain(struct ksm_stable_node **s_n_d,
|
||||
* with identical content to the page that we are scanning right now.
|
||||
*
|
||||
* This function returns the stable tree node of identical content if found,
|
||||
* NULL otherwise.
|
||||
* -EBUSY if the stable node's page is being migrated, NULL otherwise.
|
||||
*/
|
||||
static struct page *stable_tree_search(struct page *page)
|
||||
static struct folio *stable_tree_search(struct page *page)
|
||||
{
|
||||
int nid;
|
||||
struct rb_root *root;
|
||||
@@ -1803,7 +1804,7 @@ static struct page *stable_tree_search(struct page *page)
|
||||
if (page_node && page_node->head != &migrate_nodes) {
|
||||
/* ksm page forked */
|
||||
folio_get(folio);
|
||||
return &folio->page;
|
||||
return folio;
|
||||
}
|
||||
|
||||
nid = get_kpfn_nid(folio_pfn(folio));
|
||||
@@ -1898,7 +1899,7 @@ again:
|
||||
folio_put(tree_folio);
|
||||
goto replace;
|
||||
}
|
||||
return &tree_folio->page;
|
||||
return tree_folio;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1912,7 +1913,7 @@ again:
|
||||
out:
|
||||
if (is_page_sharing_candidate(page_node)) {
|
||||
folio_get(folio);
|
||||
return &folio->page;
|
||||
return folio;
|
||||
} else
|
||||
return NULL;
|
||||
|
||||
@@ -1962,7 +1963,7 @@ replace:
|
||||
}
|
||||
stable_node_dup->head = &migrate_nodes;
|
||||
list_add(&stable_node_dup->list, stable_node_dup->head);
|
||||
return &folio->page;
|
||||
return folio;
|
||||
|
||||
chain_append:
|
||||
/*
|
||||
@@ -2216,7 +2217,7 @@ static void cmp_and_merge_page(struct page *page, struct ksm_rmap_item *rmap_ite
|
||||
struct ksm_rmap_item *tree_rmap_item;
|
||||
struct page *tree_page = NULL;
|
||||
struct ksm_stable_node *stable_node;
|
||||
struct page *kpage;
|
||||
struct folio *kfolio;
|
||||
unsigned int checksum;
|
||||
int err;
|
||||
bool max_page_sharing_bypass = false;
|
||||
@@ -2258,31 +2259,32 @@ static void cmp_and_merge_page(struct page *page, struct ksm_rmap_item *rmap_ite
|
||||
return;
|
||||
}
|
||||
|
||||
/* We first start with searching the page inside the stable tree */
|
||||
kpage = stable_tree_search(page);
|
||||
if (kpage == page && rmap_item->head == stable_node) {
|
||||
put_page(kpage);
|
||||
/* Start by searching for the folio in the stable tree */
|
||||
kfolio = stable_tree_search(page);
|
||||
if (!IS_ERR_OR_NULL(kfolio) && &kfolio->page == page &&
|
||||
rmap_item->head == stable_node) {
|
||||
folio_put(kfolio);
|
||||
return;
|
||||
}
|
||||
|
||||
remove_rmap_item_from_tree(rmap_item);
|
||||
|
||||
if (kpage) {
|
||||
if (PTR_ERR(kpage) == -EBUSY)
|
||||
if (kfolio) {
|
||||
if (kfolio == ERR_PTR(-EBUSY))
|
||||
return;
|
||||
|
||||
err = try_to_merge_with_ksm_page(rmap_item, page, kpage);
|
||||
err = try_to_merge_with_ksm_page(rmap_item, page, &kfolio->page);
|
||||
if (!err) {
|
||||
/*
|
||||
* The page was successfully merged:
|
||||
* add its rmap_item to the stable tree.
|
||||
*/
|
||||
lock_page(kpage);
|
||||
stable_tree_append(rmap_item, page_stable_node(kpage),
|
||||
folio_lock(kfolio);
|
||||
stable_tree_append(rmap_item, folio_stable_node(kfolio),
|
||||
max_page_sharing_bypass);
|
||||
unlock_page(kpage);
|
||||
folio_unlock(kfolio);
|
||||
}
|
||||
put_page(kpage);
|
||||
folio_put(kfolio);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2291,7 +2293,7 @@ static void cmp_and_merge_page(struct page *page, struct ksm_rmap_item *rmap_ite
|
||||
if (tree_rmap_item) {
|
||||
bool split;
|
||||
|
||||
kpage = try_to_merge_two_pages(rmap_item, page,
|
||||
kfolio = try_to_merge_two_pages(rmap_item, page,
|
||||
tree_rmap_item, tree_page);
|
||||
/*
|
||||
* If both pages we tried to merge belong to the same compound
|
||||
@@ -2306,20 +2308,20 @@ static void cmp_and_merge_page(struct page *page, struct ksm_rmap_item *rmap_ite
|
||||
split = PageTransCompound(page)
|
||||
&& compound_head(page) == compound_head(tree_page);
|
||||
put_page(tree_page);
|
||||
if (kpage) {
|
||||
if (kfolio) {
|
||||
/*
|
||||
* The pages were successfully merged: insert new
|
||||
* node in the stable tree and add both rmap_items.
|
||||
*/
|
||||
lock_page(kpage);
|
||||
stable_node = stable_tree_insert(page_folio(kpage));
|
||||
folio_lock(kfolio);
|
||||
stable_node = stable_tree_insert(kfolio);
|
||||
if (stable_node) {
|
||||
stable_tree_append(tree_rmap_item, stable_node,
|
||||
false);
|
||||
stable_tree_append(rmap_item, stable_node,
|
||||
false);
|
||||
}
|
||||
unlock_page(kpage);
|
||||
folio_unlock(kfolio);
|
||||
|
||||
/*
|
||||
* If we fail to insert the page into the stable tree,
|
||||
@@ -2400,10 +2402,10 @@ static unsigned int skip_age(rmap_age_t age)
|
||||
/*
|
||||
* Determines if a page should be skipped for the current scan.
|
||||
*
|
||||
* @page: page to check
|
||||
* @folio: folio containing the page to check
|
||||
* @rmap_item: associated rmap_item of page
|
||||
*/
|
||||
static bool should_skip_rmap_item(struct page *page,
|
||||
static bool should_skip_rmap_item(struct folio *folio,
|
||||
struct ksm_rmap_item *rmap_item)
|
||||
{
|
||||
rmap_age_t age;
|
||||
@@ -2416,7 +2418,7 @@ static bool should_skip_rmap_item(struct page *page,
|
||||
* will essentially ignore them, but we still have to process them
|
||||
* properly.
|
||||
*/
|
||||
if (PageKsm(page))
|
||||
if (folio_test_ksm(folio))
|
||||
return false;
|
||||
|
||||
age = rmap_item->age;
|
||||
@@ -2559,7 +2561,7 @@ next_mm:
|
||||
ksm_scan.rmap_list =
|
||||
&rmap_item->rmap_list;
|
||||
|
||||
if (should_skip_rmap_item(tmp_page, rmap_item)) {
|
||||
if (should_skip_rmap_item(folio, rmap_item)) {
|
||||
folio_put(folio);
|
||||
goto next_page;
|
||||
}
|
||||
|
||||
+22
-3
@@ -1085,6 +1085,7 @@ copy_pte_range(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma,
|
||||
struct mm_struct *src_mm = src_vma->vm_mm;
|
||||
pte_t *orig_src_pte, *orig_dst_pte;
|
||||
pte_t *src_pte, *dst_pte;
|
||||
pmd_t dummy_pmdval;
|
||||
pte_t ptent;
|
||||
spinlock_t *src_ptl, *dst_ptl;
|
||||
int progress, max_nr, ret = 0;
|
||||
@@ -1110,7 +1111,15 @@ again:
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
src_pte = pte_offset_map_nolock(src_mm, src_pmd, addr, &src_ptl);
|
||||
|
||||
/*
|
||||
* We already hold the exclusive mmap_lock, the copy_pte_range() and
|
||||
* retract_page_tables() are using vma->anon_vma to be exclusive, so
|
||||
* the PTE page is stable, and there is no need to get pmdval and do
|
||||
* pmd_same() check.
|
||||
*/
|
||||
src_pte = pte_offset_map_rw_nolock(src_mm, src_pmd, addr, &dummy_pmdval,
|
||||
&src_ptl);
|
||||
if (!src_pte) {
|
||||
pte_unmap_unlock(dst_pte, dst_ptl);
|
||||
/* ret == 0 */
|
||||
@@ -5743,14 +5752,24 @@ static vm_fault_t handle_pte_fault(struct vm_fault *vmf)
|
||||
vmf->pte = NULL;
|
||||
vmf->flags &= ~FAULT_FLAG_ORIG_PTE_VALID;
|
||||
} else {
|
||||
pmd_t dummy_pmdval;
|
||||
|
||||
/*
|
||||
* A regular pmd is established and it can't morph into a huge
|
||||
* pmd by anon khugepaged, since that takes mmap_lock in write
|
||||
* mode; but shmem or file collapse to THP could still morph
|
||||
* it into a huge pmd: just retry later if so.
|
||||
*
|
||||
* Use the maywrite version to indicate that vmf->pte may be
|
||||
* modified, but since we will use pte_same() to detect the
|
||||
* change of the !pte_none() entry, there is no need to recheck
|
||||
* the pmdval. Here we chooes to pass a dummy variable instead
|
||||
* of NULL, which helps new user think about why this place is
|
||||
* special.
|
||||
*/
|
||||
vmf->pte = pte_offset_map_nolock(vmf->vma->vm_mm, vmf->pmd,
|
||||
vmf->address, &vmf->ptl);
|
||||
vmf->pte = pte_offset_map_rw_nolock(vmf->vma->vm_mm, vmf->pmd,
|
||||
vmf->address, &dummy_pmdval,
|
||||
&vmf->ptl);
|
||||
if (unlikely(!vmf->pte))
|
||||
return 0;
|
||||
vmf->orig_pte = ptep_get_lockless(vmf->pte);
|
||||
|
||||
@@ -695,6 +695,7 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)
|
||||
if (folio_test_idle(folio))
|
||||
folio_set_idle(newfolio);
|
||||
|
||||
folio_migrate_refs(newfolio, folio);
|
||||
/*
|
||||
* Copy NUMA information to the new page, to prevent over-eager
|
||||
* future migrations of this same page.
|
||||
|
||||
+10
-1
@@ -140,6 +140,7 @@ static int move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd,
|
||||
{
|
||||
struct mm_struct *mm = vma->vm_mm;
|
||||
pte_t *old_pte, *new_pte, pte;
|
||||
pmd_t dummy_pmdval;
|
||||
spinlock_t *old_ptl, *new_ptl;
|
||||
bool force_flush = false;
|
||||
unsigned long len = old_end - old_addr;
|
||||
@@ -175,7 +176,15 @@ static int move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd,
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
new_pte = pte_offset_map_nolock(mm, new_pmd, new_addr, &new_ptl);
|
||||
/*
|
||||
* Now new_pte is none, so hpage_collapse_scan_file() path can not find
|
||||
* this by traversing file->f_mapping, so there is no concurrency with
|
||||
* retract_page_tables(). In addition, we already hold the exclusive
|
||||
* mmap_lock, so this new_pte page is stable, so there is no need to get
|
||||
* pmdval and do pmd_same() check.
|
||||
*/
|
||||
new_pte = pte_offset_map_rw_nolock(mm, new_pmd, new_addr, &dummy_pmdval,
|
||||
&new_ptl);
|
||||
if (!new_pte) {
|
||||
pte_unmap_unlock(old_pte, old_ptl);
|
||||
err = -EAGAIN;
|
||||
|
||||
+1
-1
@@ -296,8 +296,8 @@ static inline void count_swpout_vm_event(struct folio *folio)
|
||||
count_memcg_folio_events(folio, THP_SWPOUT, 1);
|
||||
count_vm_event(THP_SWPOUT);
|
||||
}
|
||||
count_mthp_stat(folio_order(folio), MTHP_STAT_SWPOUT);
|
||||
#endif
|
||||
count_mthp_stat(folio_order(folio), MTHP_STAT_SWPOUT);
|
||||
count_memcg_folio_events(folio, PSWPOUT, folio_nr_pages(folio));
|
||||
count_vm_events(PSWPOUT, folio_nr_pages(folio));
|
||||
}
|
||||
|
||||
+18
-6
@@ -13,7 +13,8 @@ static inline bool not_found(struct page_vma_mapped_walk *pvmw)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool map_pte(struct page_vma_mapped_walk *pvmw, spinlock_t **ptlp)
|
||||
static bool map_pte(struct page_vma_mapped_walk *pvmw, pmd_t *pmdvalp,
|
||||
spinlock_t **ptlp)
|
||||
{
|
||||
pte_t ptent;
|
||||
|
||||
@@ -25,6 +26,7 @@ static bool map_pte(struct page_vma_mapped_walk *pvmw, spinlock_t **ptlp)
|
||||
return !!pvmw->pte;
|
||||
}
|
||||
|
||||
again:
|
||||
/*
|
||||
* It is important to return the ptl corresponding to pte,
|
||||
* in case *pvmw->pmd changes underneath us; so we need to
|
||||
@@ -32,8 +34,8 @@ static bool map_pte(struct page_vma_mapped_walk *pvmw, spinlock_t **ptlp)
|
||||
* proceeds to loop over next ptes, and finds a match later.
|
||||
* Though, in most cases, page lock already protects this.
|
||||
*/
|
||||
pvmw->pte = pte_offset_map_nolock(pvmw->vma->vm_mm, pvmw->pmd,
|
||||
pvmw->address, ptlp);
|
||||
pvmw->pte = pte_offset_map_rw_nolock(pvmw->vma->vm_mm, pvmw->pmd,
|
||||
pvmw->address, pmdvalp, ptlp);
|
||||
if (!pvmw->pte)
|
||||
return false;
|
||||
|
||||
@@ -67,8 +69,13 @@ static bool map_pte(struct page_vma_mapped_walk *pvmw, spinlock_t **ptlp)
|
||||
} else if (!pte_present(ptent)) {
|
||||
return false;
|
||||
}
|
||||
spin_lock(*ptlp);
|
||||
if (unlikely(!pmd_same(*pmdvalp, pmdp_get_lockless(pvmw->pmd)))) {
|
||||
pte_unmap_unlock(pvmw->pte, *ptlp);
|
||||
goto again;
|
||||
}
|
||||
pvmw->ptl = *ptlp;
|
||||
spin_lock(pvmw->ptl);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -278,7 +285,7 @@ restart:
|
||||
step_forward(pvmw, PMD_SIZE);
|
||||
continue;
|
||||
}
|
||||
if (!map_pte(pvmw, &ptl)) {
|
||||
if (!map_pte(pvmw, &pmde, &ptl)) {
|
||||
if (!pvmw->pte)
|
||||
goto restart;
|
||||
goto next_pte;
|
||||
@@ -305,8 +312,13 @@ next_pte:
|
||||
} while (pte_none(ptep_get(pvmw->pte)));
|
||||
|
||||
if (!pvmw->ptl) {
|
||||
spin_lock(ptl);
|
||||
if (unlikely(!pmd_same(pmde, pmdp_get_lockless(pvmw->pmd)))) {
|
||||
pte_unmap_unlock(pvmw->pte, ptl);
|
||||
pvmw->pte = NULL;
|
||||
goto restart;
|
||||
}
|
||||
pvmw->ptl = ptl;
|
||||
spin_lock(pvmw->ptl);
|
||||
}
|
||||
goto this_pte;
|
||||
} while (pvmw->address < end);
|
||||
|
||||
@@ -305,18 +305,6 @@ nomap:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pte_t *pte_offset_map_nolock(struct mm_struct *mm, pmd_t *pmd,
|
||||
unsigned long addr, spinlock_t **ptlp)
|
||||
{
|
||||
pmd_t pmdval;
|
||||
pte_t *pte;
|
||||
|
||||
pte = __pte_offset_map(pmd, addr, &pmdval);
|
||||
if (likely(pte))
|
||||
*ptlp = pte_lockptr(mm, &pmdval);
|
||||
return pte;
|
||||
}
|
||||
|
||||
pte_t *pte_offset_map_ro_nolock(struct mm_struct *mm, pmd_t *pmd,
|
||||
unsigned long addr, spinlock_t **ptlp)
|
||||
{
|
||||
@@ -372,15 +360,6 @@ pte_t *pte_offset_map_rw_nolock(struct mm_struct *mm, pmd_t *pmd,
|
||||
* and disconnected table. Until pte_unmap(pte) unmaps and rcu_read_unlock()s
|
||||
* afterwards.
|
||||
*
|
||||
* pte_offset_map_nolock(mm, pmd, addr, ptlp), above, is like pte_offset_map();
|
||||
* but when successful, it also outputs a pointer to the spinlock in ptlp - as
|
||||
* pte_offset_map_lock() does, but in this case without locking it. This helps
|
||||
* the caller to avoid a later pte_lockptr(mm, *pmd), which might by that time
|
||||
* act on a changed *pmd: pte_offset_map_nolock() provides the correct spinlock
|
||||
* pointer for the page table that it returns. In principle, the caller should
|
||||
* recheck *pmd once the lock is taken; in practice, no callsite needs that -
|
||||
* either the mmap_lock for write, or pte_same() check on contents, is enough.
|
||||
*
|
||||
* pte_offset_map_ro_nolock(mm, pmd, addr, ptlp), above, is like pte_offset_map();
|
||||
* but when successful, it also outputs a pointer to the spinlock in ptlp - as
|
||||
* pte_offset_map_lock() does, but in this case without locking it. This helps
|
||||
|
||||
+46
-57
@@ -23,42 +23,28 @@
|
||||
#include <linux/rmap.h>
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* Regular page slots are stabilized by the page lock even without the tree
|
||||
* itself locked. These unlocked entries need verification under the tree
|
||||
* lock.
|
||||
*/
|
||||
static inline void __clear_shadow_entry(struct address_space *mapping,
|
||||
pgoff_t index, void *entry)
|
||||
{
|
||||
XA_STATE(xas, &mapping->i_pages, index);
|
||||
|
||||
xas_set_update(&xas, workingset_update_node);
|
||||
if (xas_load(&xas) != entry)
|
||||
return;
|
||||
xas_store(&xas, NULL);
|
||||
}
|
||||
|
||||
static void clear_shadow_entries(struct address_space *mapping,
|
||||
struct folio_batch *fbatch, pgoff_t *indices)
|
||||
unsigned long start, unsigned long max)
|
||||
{
|
||||
int i;
|
||||
XA_STATE(xas, &mapping->i_pages, start);
|
||||
struct folio *folio;
|
||||
|
||||
/* Handled by shmem itself, or for DAX we do nothing. */
|
||||
if (shmem_mapping(mapping) || dax_mapping(mapping))
|
||||
return;
|
||||
|
||||
xas_set_update(&xas, workingset_update_node);
|
||||
|
||||
spin_lock(&mapping->host->i_lock);
|
||||
xa_lock_irq(&mapping->i_pages);
|
||||
|
||||
for (i = 0; i < folio_batch_count(fbatch); i++) {
|
||||
struct folio *folio = fbatch->folios[i];
|
||||
xas_lock_irq(&xas);
|
||||
|
||||
/* Clear all shadow entries from start to max */
|
||||
xas_for_each(&xas, folio, max) {
|
||||
if (xa_is_value(folio))
|
||||
__clear_shadow_entry(mapping, indices[i], folio);
|
||||
xas_store(&xas, NULL);
|
||||
}
|
||||
|
||||
xa_unlock_irq(&mapping->i_pages);
|
||||
xas_unlock_irq(&xas);
|
||||
if (mapping_shrinkable(mapping))
|
||||
inode_add_lru(mapping->host);
|
||||
spin_unlock(&mapping->host->i_lock);
|
||||
@@ -68,54 +54,53 @@ static void clear_shadow_entries(struct address_space *mapping,
|
||||
* Unconditionally remove exceptional entries. Usually called from truncate
|
||||
* path. Note that the folio_batch may be altered by this function by removing
|
||||
* exceptional entries similar to what folio_batch_remove_exceptionals() does.
|
||||
* Please note that indices[] has entries in ascending order as guaranteed by
|
||||
* either find_get_entries() or find_lock_entries().
|
||||
*/
|
||||
static void truncate_folio_batch_exceptionals(struct address_space *mapping,
|
||||
struct folio_batch *fbatch, pgoff_t *indices)
|
||||
{
|
||||
XA_STATE(xas, &mapping->i_pages, indices[0]);
|
||||
int nr = folio_batch_count(fbatch);
|
||||
struct folio *folio;
|
||||
int i, j;
|
||||
bool dax;
|
||||
|
||||
/* Handled by shmem itself */
|
||||
if (shmem_mapping(mapping))
|
||||
return;
|
||||
|
||||
for (j = 0; j < folio_batch_count(fbatch); j++)
|
||||
for (j = 0; j < nr; j++)
|
||||
if (xa_is_value(fbatch->folios[j]))
|
||||
break;
|
||||
|
||||
if (j == folio_batch_count(fbatch))
|
||||
if (j == nr)
|
||||
return;
|
||||
|
||||
dax = dax_mapping(mapping);
|
||||
if (!dax) {
|
||||
spin_lock(&mapping->host->i_lock);
|
||||
xa_lock_irq(&mapping->i_pages);
|
||||
}
|
||||
|
||||
for (i = j; i < folio_batch_count(fbatch); i++) {
|
||||
struct folio *folio = fbatch->folios[i];
|
||||
pgoff_t index = indices[i];
|
||||
|
||||
if (!xa_is_value(folio)) {
|
||||
fbatch->folios[j++] = folio;
|
||||
continue;
|
||||
if (dax_mapping(mapping)) {
|
||||
for (i = j; i < nr; i++) {
|
||||
if (xa_is_value(fbatch->folios[i]))
|
||||
dax_delete_mapping_entry(mapping, indices[i]);
|
||||
}
|
||||
|
||||
if (unlikely(dax)) {
|
||||
dax_delete_mapping_entry(mapping, index);
|
||||
continue;
|
||||
}
|
||||
|
||||
__clear_shadow_entry(mapping, index, folio);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!dax) {
|
||||
xa_unlock_irq(&mapping->i_pages);
|
||||
if (mapping_shrinkable(mapping))
|
||||
inode_add_lru(mapping->host);
|
||||
spin_unlock(&mapping->host->i_lock);
|
||||
xas_set(&xas, indices[j]);
|
||||
xas_set_update(&xas, workingset_update_node);
|
||||
|
||||
spin_lock(&mapping->host->i_lock);
|
||||
xas_lock_irq(&xas);
|
||||
|
||||
xas_for_each(&xas, folio, indices[nr-1]) {
|
||||
if (xa_is_value(folio))
|
||||
xas_store(&xas, NULL);
|
||||
}
|
||||
fbatch->nr = j;
|
||||
|
||||
xas_unlock_irq(&xas);
|
||||
if (mapping_shrinkable(mapping))
|
||||
inode_add_lru(mapping->host);
|
||||
spin_unlock(&mapping->host->i_lock);
|
||||
out:
|
||||
folio_batch_remove_exceptionals(fbatch);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -481,7 +466,9 @@ unsigned long mapping_try_invalidate(struct address_space *mapping,
|
||||
|
||||
folio_batch_init(&fbatch);
|
||||
while (find_lock_entries(mapping, &index, end, &fbatch, indices)) {
|
||||
for (i = 0; i < folio_batch_count(&fbatch); i++) {
|
||||
int nr = folio_batch_count(&fbatch);
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
struct folio *folio = fbatch.folios[i];
|
||||
|
||||
/* We rely upon deletion not changing folio->index */
|
||||
@@ -508,7 +495,7 @@ unsigned long mapping_try_invalidate(struct address_space *mapping,
|
||||
}
|
||||
|
||||
if (xa_has_values)
|
||||
clear_shadow_entries(mapping, &fbatch, indices);
|
||||
clear_shadow_entries(mapping, indices[0], indices[nr-1]);
|
||||
|
||||
folio_batch_remove_exceptionals(&fbatch);
|
||||
folio_batch_release(&fbatch);
|
||||
@@ -612,7 +599,9 @@ int invalidate_inode_pages2_range(struct address_space *mapping,
|
||||
folio_batch_init(&fbatch);
|
||||
index = start;
|
||||
while (find_get_entries(mapping, &index, end, &fbatch, indices)) {
|
||||
for (i = 0; i < folio_batch_count(&fbatch); i++) {
|
||||
int nr = folio_batch_count(&fbatch);
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
struct folio *folio = fbatch.folios[i];
|
||||
|
||||
/* We rely upon deletion not changing folio->index */
|
||||
@@ -658,7 +647,7 @@ int invalidate_inode_pages2_range(struct address_space *mapping,
|
||||
}
|
||||
|
||||
if (xa_has_values)
|
||||
clear_shadow_entries(mapping, &fbatch, indices);
|
||||
clear_shadow_entries(mapping, indices[0], indices[nr-1]);
|
||||
|
||||
folio_batch_remove_exceptionals(&fbatch);
|
||||
folio_batch_release(&fbatch);
|
||||
|
||||
+12
-3
@@ -1135,7 +1135,7 @@ static int move_pages_pte(struct mm_struct *mm, pmd_t *dst_pmd, pmd_t *src_pmd,
|
||||
spinlock_t *src_ptl, *dst_ptl;
|
||||
pte_t *src_pte = NULL;
|
||||
pte_t *dst_pte = NULL;
|
||||
|
||||
pmd_t dummy_pmdval;
|
||||
struct folio *src_folio = NULL;
|
||||
struct anon_vma *src_anon_vma = NULL;
|
||||
struct mmu_notifier_range range;
|
||||
@@ -1146,7 +1146,14 @@ static int move_pages_pte(struct mm_struct *mm, pmd_t *dst_pmd, pmd_t *src_pmd,
|
||||
src_addr, src_addr + PAGE_SIZE);
|
||||
mmu_notifier_invalidate_range_start(&range);
|
||||
retry:
|
||||
dst_pte = pte_offset_map_nolock(mm, dst_pmd, dst_addr, &dst_ptl);
|
||||
/*
|
||||
* Use the maywrite version to indicate that dst_pte will be modified,
|
||||
* but since we will use pte_same() to detect the change of the pte
|
||||
* entry, there is no need to get pmdval, so just pass a dummy variable
|
||||
* to it.
|
||||
*/
|
||||
dst_pte = pte_offset_map_rw_nolock(mm, dst_pmd, dst_addr, &dummy_pmdval,
|
||||
&dst_ptl);
|
||||
|
||||
/* Retry if a huge pmd materialized from under us */
|
||||
if (unlikely(!dst_pte)) {
|
||||
@@ -1154,7 +1161,9 @@ retry:
|
||||
goto out;
|
||||
}
|
||||
|
||||
src_pte = pte_offset_map_nolock(mm, src_pmd, src_addr, &src_ptl);
|
||||
/* same as dst_pte */
|
||||
src_pte = pte_offset_map_rw_nolock(mm, src_pmd, src_addr, &dummy_pmdval,
|
||||
&src_ptl);
|
||||
|
||||
/*
|
||||
* We held the mmap_lock for reading so MADV_DONTNEED
|
||||
|
||||
+9
-2
@@ -1261,8 +1261,8 @@ retry:
|
||||
THP_SWPOUT_FALLBACK, 1);
|
||||
count_vm_event(THP_SWPOUT_FALLBACK);
|
||||
}
|
||||
count_mthp_stat(order, MTHP_STAT_SWPOUT_FALLBACK);
|
||||
#endif
|
||||
count_mthp_stat(order, MTHP_STAT_SWPOUT_FALLBACK);
|
||||
if (!add_to_swap(folio))
|
||||
goto activate_locked_split;
|
||||
}
|
||||
@@ -3394,8 +3394,10 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,
|
||||
struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec);
|
||||
DEFINE_MAX_SEQ(walk->lruvec);
|
||||
int old_gen, new_gen = lru_gen_from_seq(max_seq);
|
||||
pmd_t pmdval;
|
||||
|
||||
pte = pte_offset_map_nolock(args->mm, pmd, start & PMD_MASK, &ptl);
|
||||
pte = pte_offset_map_rw_nolock(args->mm, pmd, start & PMD_MASK, &pmdval,
|
||||
&ptl);
|
||||
if (!pte)
|
||||
return false;
|
||||
if (!spin_trylock(ptl)) {
|
||||
@@ -3403,6 +3405,11 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (unlikely(!pmd_same(pmdval, pmdp_get_lockless(pmd)))) {
|
||||
pte_unmap_unlock(pte, ptl);
|
||||
return false;
|
||||
}
|
||||
|
||||
arch_enter_lazy_mmu_mode();
|
||||
restart:
|
||||
for (i = pte_index(start), addr = start; addr != end; i++, addr += PAGE_SIZE) {
|
||||
|
||||
@@ -14,10 +14,8 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char **regions;
|
||||
clock_t start_clock;
|
||||
int nr_regions;
|
||||
int sz_region;
|
||||
int access_time_ms;
|
||||
int i;
|
||||
|
||||
if (argc != 3) {
|
||||
|
||||
Reference in new issue
Block a user