Commit 47d4f3ee authored by Hugh Dickins's avatar Hugh Dickins Committed by Matthew Wilcox (Oracle)

mm/thp: shrink_page_list() avoid splitting VM_LOCKED THP

4.8 commit 7751b2da ("vmscan: split file huge pages before paging
them out") inserted a split_huge_page_to_list() into shrink_page_list()
without considering the mlock case: no problem if the page has already
been marked as Mlocked (the !page_evictable check much higher up will
have skipped all this), but it has always been the case that races or
omissions in setting Mlocked can rely on page reclaim to detect this
and correct it before actually reclaiming - and that remains so, but
what a shame if a hugepage is needlessly split before discovering it.

It is surprising that page_check_references() returns PAGEREF_RECLAIM
when VM_LOCKED, but there was a good reason for that: try_to_unmap_one()
is where the condition is detected and corrected; and until now it could
not be done in page_referenced_one(), because that does not always have
the page locked.  Now that mlock's requirement for page lock has gone,
copy try_to_unmap_one()'s mlock restoration into page_referenced_one(),
and let page_check_references() return PAGEREF_ACTIVATE in this case.

But page_referenced_one() may find a pte mapping one part of a hugepage:
what hold should a pte mapped in a VM_LOCKED area exert over the entire
huge page?  That's debatable.  The approach taken here is to treat that
pte mapping in page_referenced_one() as if not VM_LOCKED, and if no
VM_LOCKED pmd mapping is found later in the walk, and lack of reference
permits, then PAGEREF_RECLAIM take it to attempted splitting as before.
Signed-off-by: default avatarHugh Dickins <hughd@google.com>
Signed-off-by: default avatarMatthew Wilcox (Oracle) <willy@infradead.org>
parent 6d9df8a5
...@@ -812,7 +812,10 @@ static bool page_referenced_one(struct page *page, struct vm_area_struct *vma, ...@@ -812,7 +812,10 @@ static bool page_referenced_one(struct page *page, struct vm_area_struct *vma,
while (page_vma_mapped_walk(&pvmw)) { while (page_vma_mapped_walk(&pvmw)) {
address = pvmw.address; address = pvmw.address;
if (vma->vm_flags & VM_LOCKED) { if ((vma->vm_flags & VM_LOCKED) &&
(!PageTransCompound(page) || !pvmw.pte)) {
/* Restore the mlock which got missed */
mlock_vma_page(page, vma, !pvmw.pte);
page_vma_mapped_walk_done(&pvmw); page_vma_mapped_walk_done(&pvmw);
pra->vm_flags |= VM_LOCKED; pra->vm_flags |= VM_LOCKED;
return false; /* To break the loop */ return false; /* To break the loop */
...@@ -851,7 +854,7 @@ static bool page_referenced_one(struct page *page, struct vm_area_struct *vma, ...@@ -851,7 +854,7 @@ static bool page_referenced_one(struct page *page, struct vm_area_struct *vma,
if (referenced) { if (referenced) {
pra->referenced++; pra->referenced++;
pra->vm_flags |= vma->vm_flags; pra->vm_flags |= vma->vm_flags & ~VM_LOCKED;
} }
if (!pra->mapcount) if (!pra->mapcount)
......
...@@ -1388,11 +1388,11 @@ static enum page_references page_check_references(struct page *page, ...@@ -1388,11 +1388,11 @@ static enum page_references page_check_references(struct page *page,
referenced_page = TestClearPageReferenced(page); referenced_page = TestClearPageReferenced(page);
/* /*
* Mlock lost the isolation race with us. Let try_to_unmap() * The supposedly reclaimable page was found to be in a VM_LOCKED vma.
* move the page to the unevictable list. * Let the page, now marked Mlocked, be moved to the unevictable list.
*/ */
if (vm_flags & VM_LOCKED) if (vm_flags & VM_LOCKED)
return PAGEREF_RECLAIM; return PAGEREF_ACTIVATE;
if (referenced_ptes) { if (referenced_ptes) {
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment