diff options
Diffstat (limited to 'mm/huge_memory.c')
-rw-r--r-- | mm/huge_memory.c | 55 |
1 files changed, 42 insertions, 13 deletions
diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 29bc6e471df4..cb7be110cad3 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c | |||
@@ -2117,7 +2117,8 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma, | |||
2117 | { | 2117 | { |
2118 | struct page *page; | 2118 | struct page *page; |
2119 | pte_t *_pte; | 2119 | pte_t *_pte; |
2120 | int referenced = 0, none = 0; | 2120 | int none = 0; |
2121 | bool referenced = false, writable = false; | ||
2121 | for (_pte = pte; _pte < pte+HPAGE_PMD_NR; | 2122 | for (_pte = pte; _pte < pte+HPAGE_PMD_NR; |
2122 | _pte++, address += PAGE_SIZE) { | 2123 | _pte++, address += PAGE_SIZE) { |
2123 | pte_t pteval = *_pte; | 2124 | pte_t pteval = *_pte; |
@@ -2127,7 +2128,7 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma, | |||
2127 | else | 2128 | else |
2128 | goto out; | 2129 | goto out; |
2129 | } | 2130 | } |
2130 | if (!pte_present(pteval) || !pte_write(pteval)) | 2131 | if (!pte_present(pteval)) |
2131 | goto out; | 2132 | goto out; |
2132 | page = vm_normal_page(vma, address, pteval); | 2133 | page = vm_normal_page(vma, address, pteval); |
2133 | if (unlikely(!page)) | 2134 | if (unlikely(!page)) |
@@ -2137,9 +2138,6 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma, | |||
2137 | VM_BUG_ON_PAGE(!PageAnon(page), page); | 2138 | VM_BUG_ON_PAGE(!PageAnon(page), page); |
2138 | VM_BUG_ON_PAGE(!PageSwapBacked(page), page); | 2139 | VM_BUG_ON_PAGE(!PageSwapBacked(page), page); |
2139 | 2140 | ||
2140 | /* cannot use mapcount: can't collapse if there's a gup pin */ | ||
2141 | if (page_count(page) != 1) | ||
2142 | goto out; | ||
2143 | /* | 2141 | /* |
2144 | * We can do it before isolate_lru_page because the | 2142 | * We can do it before isolate_lru_page because the |
2145 | * page can't be freed from under us. NOTE: PG_lock | 2143 | * 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, | |||
2148 | */ | 2146 | */ |
2149 | if (!trylock_page(page)) | 2147 | if (!trylock_page(page)) |
2150 | goto out; | 2148 | goto out; |
2149 | |||
2150 | /* | ||
2151 | * cannot use mapcount: can't collapse if there's a gup pin. | ||
2152 | * The page must only be referenced by the scanned process | ||
2153 | * and page swap cache. | ||
2154 | */ | ||
2155 | if (page_count(page) != 1 + !!PageSwapCache(page)) { | ||
2156 | unlock_page(page); | ||
2157 | goto out; | ||
2158 | } | ||
2159 | if (pte_write(pteval)) { | ||
2160 | writable = true; | ||
2161 | } else { | ||
2162 | if (PageSwapCache(page) && !reuse_swap_page(page)) { | ||
2163 | unlock_page(page); | ||
2164 | goto out; | ||
2165 | } | ||
2166 | /* | ||
2167 | * Page is not in the swap cache. It can be collapsed | ||
2168 | * into a THP. | ||
2169 | */ | ||
2170 | } | ||
2171 | |||
2151 | /* | 2172 | /* |
2152 | * Isolate the page to avoid collapsing an hugepage | 2173 | * Isolate the page to avoid collapsing an hugepage |
2153 | * currently in use by the VM. | 2174 | * currently in use by the VM. |
@@ -2164,9 +2185,9 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma, | |||
2164 | /* If there is no mapped pte young don't collapse the page */ | 2185 | /* If there is no mapped pte young don't collapse the page */ |
2165 | if (pte_young(pteval) || PageReferenced(page) || | 2186 | if (pte_young(pteval) || PageReferenced(page) || |
2166 | mmu_notifier_test_young(vma->vm_mm, address)) | 2187 | mmu_notifier_test_young(vma->vm_mm, address)) |
2167 | referenced = 1; | 2188 | referenced = true; |
2168 | } | 2189 | } |
2169 | if (likely(referenced)) | 2190 | if (likely(referenced && writable)) |
2170 | return 1; | 2191 | return 1; |
2171 | out: | 2192 | out: |
2172 | release_pte_pages(pte, _pte); | 2193 | release_pte_pages(pte, _pte); |
@@ -2519,11 +2540,12 @@ static int khugepaged_scan_pmd(struct mm_struct *mm, | |||
2519 | { | 2540 | { |
2520 | pmd_t *pmd; | 2541 | pmd_t *pmd; |
2521 | pte_t *pte, *_pte; | 2542 | pte_t *pte, *_pte; |
2522 | int ret = 0, referenced = 0, none = 0; | 2543 | int ret = 0, none = 0; |
2523 | struct page *page; | 2544 | struct page *page; |
2524 | unsigned long _address; | 2545 | unsigned long _address; |
2525 | spinlock_t *ptl; | 2546 | spinlock_t *ptl; |
2526 | int node = NUMA_NO_NODE; | 2547 | int node = NUMA_NO_NODE; |
2548 | bool writable = false, referenced = false; | ||
2527 | 2549 | ||
2528 | VM_BUG_ON(address & ~HPAGE_PMD_MASK); | 2550 | VM_BUG_ON(address & ~HPAGE_PMD_MASK); |
2529 | 2551 | ||
@@ -2542,8 +2564,11 @@ static int khugepaged_scan_pmd(struct mm_struct *mm, | |||
2542 | else | 2564 | else |
2543 | goto out_unmap; | 2565 | goto out_unmap; |
2544 | } | 2566 | } |
2545 | if (!pte_present(pteval) || !pte_write(pteval)) | 2567 | if (!pte_present(pteval)) |
2546 | goto out_unmap; | 2568 | goto out_unmap; |
2569 | if (pte_write(pteval)) | ||
2570 | writable = true; | ||
2571 | |||
2547 | page = vm_normal_page(vma, _address, pteval); | 2572 | page = vm_normal_page(vma, _address, pteval); |
2548 | if (unlikely(!page)) | 2573 | if (unlikely(!page)) |
2549 | goto out_unmap; | 2574 | goto out_unmap; |
@@ -2560,14 +2585,18 @@ static int khugepaged_scan_pmd(struct mm_struct *mm, | |||
2560 | VM_BUG_ON_PAGE(PageCompound(page), page); | 2585 | VM_BUG_ON_PAGE(PageCompound(page), page); |
2561 | if (!PageLRU(page) || PageLocked(page) || !PageAnon(page)) | 2586 | if (!PageLRU(page) || PageLocked(page) || !PageAnon(page)) |
2562 | goto out_unmap; | 2587 | goto out_unmap; |
2563 | /* cannot use mapcount: can't collapse if there's a gup pin */ | 2588 | /* |
2564 | if (page_count(page) != 1) | 2589 | * cannot use mapcount: can't collapse if there's a gup pin. |
2590 | * The page must only be referenced by the scanned process | ||
2591 | * and page swap cache. | ||
2592 | */ | ||
2593 | if (page_count(page) != 1 + !!PageSwapCache(page)) | ||
2565 | goto out_unmap; | 2594 | goto out_unmap; |
2566 | if (pte_young(pteval) || PageReferenced(page) || | 2595 | if (pte_young(pteval) || PageReferenced(page) || |
2567 | mmu_notifier_test_young(vma->vm_mm, address)) | 2596 | mmu_notifier_test_young(vma->vm_mm, address)) |
2568 | referenced = 1; | 2597 | referenced = true; |
2569 | } | 2598 | } |
2570 | if (referenced) | 2599 | if (referenced && writable) |
2571 | ret = 1; | 2600 | ret = 1; |
2572 | out_unmap: | 2601 | out_unmap: |
2573 | pte_unmap_unlock(pte, ptl); | 2602 | pte_unmap_unlock(pte, ptl); |