|
@@ -2117,7 +2117,8 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma,
|
|
{
|
|
{
|
|
struct page *page;
|
|
struct page *page;
|
|
pte_t *_pte;
|
|
pte_t *_pte;
|
|
- int referenced = 0, none = 0;
|
|
|
|
|
|
+ int none = 0;
|
|
|
|
+ bool referenced = false, writable = false;
|
|
for (_pte = pte; _pte < pte+HPAGE_PMD_NR;
|
|
for (_pte = pte; _pte < pte+HPAGE_PMD_NR;
|
|
_pte++, address += PAGE_SIZE) {
|
|
_pte++, address += PAGE_SIZE) {
|
|
pte_t pteval = *_pte;
|
|
pte_t pteval = *_pte;
|
|
@@ -2127,7 +2128,7 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma,
|
|
else
|
|
else
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
- if (!pte_present(pteval) || !pte_write(pteval))
|
|
|
|
|
|
+ if (!pte_present(pteval))
|
|
goto out;
|
|
goto out;
|
|
page = vm_normal_page(vma, address, pteval);
|
|
page = vm_normal_page(vma, address, pteval);
|
|
if (unlikely(!page))
|
|
if (unlikely(!page))
|
|
@@ -2137,9 +2138,6 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma,
|
|
VM_BUG_ON_PAGE(!PageAnon(page), page);
|
|
VM_BUG_ON_PAGE(!PageAnon(page), page);
|
|
VM_BUG_ON_PAGE(!PageSwapBacked(page), page);
|
|
VM_BUG_ON_PAGE(!PageSwapBacked(page), page);
|
|
|
|
|
|
- /* cannot use mapcount: can't collapse if there's a gup pin */
|
|
|
|
- if (page_count(page) != 1)
|
|
|
|
- goto out;
|
|
|
|
/*
|
|
/*
|
|
* We can do it before isolate_lru_page because the
|
|
* We can do it before isolate_lru_page because the
|
|
* page can't be freed from under us. NOTE: PG_lock
|
|
* page can't be freed from under us. NOTE: PG_lock
|
|
@@ -2148,6 +2146,29 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma,
|
|
*/
|
|
*/
|
|
if (!trylock_page(page))
|
|
if (!trylock_page(page))
|
|
goto out;
|
|
goto out;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * cannot use mapcount: can't collapse if there's a gup pin.
|
|
|
|
+ * The page must only be referenced by the scanned process
|
|
|
|
+ * and page swap cache.
|
|
|
|
+ */
|
|
|
|
+ if (page_count(page) != 1 + !!PageSwapCache(page)) {
|
|
|
|
+ unlock_page(page);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ if (pte_write(pteval)) {
|
|
|
|
+ writable = true;
|
|
|
|
+ } else {
|
|
|
|
+ if (PageSwapCache(page) && !reuse_swap_page(page)) {
|
|
|
|
+ unlock_page(page);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ /*
|
|
|
|
+ * Page is not in the swap cache. It can be collapsed
|
|
|
|
+ * into a THP.
|
|
|
|
+ */
|
|
|
|
+ }
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Isolate the page to avoid collapsing an hugepage
|
|
* Isolate the page to avoid collapsing an hugepage
|
|
* currently in use by the VM.
|
|
* currently in use by the VM.
|
|
@@ -2164,9 +2185,9 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma,
|
|
/* If there is no mapped pte young don't collapse the page */
|
|
/* If there is no mapped pte young don't collapse the page */
|
|
if (pte_young(pteval) || PageReferenced(page) ||
|
|
if (pte_young(pteval) || PageReferenced(page) ||
|
|
mmu_notifier_test_young(vma->vm_mm, address))
|
|
mmu_notifier_test_young(vma->vm_mm, address))
|
|
- referenced = 1;
|
|
|
|
|
|
+ referenced = true;
|
|
}
|
|
}
|
|
- if (likely(referenced))
|
|
|
|
|
|
+ if (likely(referenced && writable))
|
|
return 1;
|
|
return 1;
|
|
out:
|
|
out:
|
|
release_pte_pages(pte, _pte);
|
|
release_pte_pages(pte, _pte);
|
|
@@ -2519,11 +2540,12 @@ static int khugepaged_scan_pmd(struct mm_struct *mm,
|
|
{
|
|
{
|
|
pmd_t *pmd;
|
|
pmd_t *pmd;
|
|
pte_t *pte, *_pte;
|
|
pte_t *pte, *_pte;
|
|
- int ret = 0, referenced = 0, none = 0;
|
|
|
|
|
|
+ int ret = 0, none = 0;
|
|
struct page *page;
|
|
struct page *page;
|
|
unsigned long _address;
|
|
unsigned long _address;
|
|
spinlock_t *ptl;
|
|
spinlock_t *ptl;
|
|
int node = NUMA_NO_NODE;
|
|
int node = NUMA_NO_NODE;
|
|
|
|
+ bool writable = false, referenced = false;
|
|
|
|
|
|
VM_BUG_ON(address & ~HPAGE_PMD_MASK);
|
|
VM_BUG_ON(address & ~HPAGE_PMD_MASK);
|
|
|
|
|
|
@@ -2542,8 +2564,11 @@ static int khugepaged_scan_pmd(struct mm_struct *mm,
|
|
else
|
|
else
|
|
goto out_unmap;
|
|
goto out_unmap;
|
|
}
|
|
}
|
|
- if (!pte_present(pteval) || !pte_write(pteval))
|
|
|
|
|
|
+ if (!pte_present(pteval))
|
|
goto out_unmap;
|
|
goto out_unmap;
|
|
|
|
+ if (pte_write(pteval))
|
|
|
|
+ writable = true;
|
|
|
|
+
|
|
page = vm_normal_page(vma, _address, pteval);
|
|
page = vm_normal_page(vma, _address, pteval);
|
|
if (unlikely(!page))
|
|
if (unlikely(!page))
|
|
goto out_unmap;
|
|
goto out_unmap;
|
|
@@ -2560,14 +2585,18 @@ static int khugepaged_scan_pmd(struct mm_struct *mm,
|
|
VM_BUG_ON_PAGE(PageCompound(page), page);
|
|
VM_BUG_ON_PAGE(PageCompound(page), page);
|
|
if (!PageLRU(page) || PageLocked(page) || !PageAnon(page))
|
|
if (!PageLRU(page) || PageLocked(page) || !PageAnon(page))
|
|
goto out_unmap;
|
|
goto out_unmap;
|
|
- /* cannot use mapcount: can't collapse if there's a gup pin */
|
|
|
|
- if (page_count(page) != 1)
|
|
|
|
|
|
+ /*
|
|
|
|
+ * cannot use mapcount: can't collapse if there's a gup pin.
|
|
|
|
+ * The page must only be referenced by the scanned process
|
|
|
|
+ * and page swap cache.
|
|
|
|
+ */
|
|
|
|
+ if (page_count(page) != 1 + !!PageSwapCache(page))
|
|
goto out_unmap;
|
|
goto out_unmap;
|
|
if (pte_young(pteval) || PageReferenced(page) ||
|
|
if (pte_young(pteval) || PageReferenced(page) ||
|
|
mmu_notifier_test_young(vma->vm_mm, address))
|
|
mmu_notifier_test_young(vma->vm_mm, address))
|
|
- referenced = 1;
|
|
|
|
|
|
+ referenced = true;
|
|
}
|
|
}
|
|
- if (referenced)
|
|
|
|
|
|
+ if (referenced && writable)
|
|
ret = 1;
|
|
ret = 1;
|
|
out_unmap:
|
|
out_unmap:
|
|
pte_unmap_unlock(pte, ptl);
|
|
pte_unmap_unlock(pte, ptl);
|