forked from Minki/linux
mm: try_to_unmap_cluster() should lock_page() before mlocking
A BUG_ON(!PageLocked) was triggered in mlock_vma_page() by Sasha Levin fuzzing with trinity. The call site try_to_unmap_cluster() does not lock the pages other than its check_page parameter (which is already locked). The BUG_ON in mlock_vma_page() is not documented and its purpose is somewhat unclear, but apparently it serializes against page migration, which could otherwise fail to transfer the PG_mlocked flag. This would not be fatal, as the page would be eventually encountered again, but NR_MLOCK accounting would become distorted nevertheless. This patch adds a comment to the BUG_ON in mlock_vma_page() and munlock_vma_page() to that effect. The call site try_to_unmap_cluster() is fixed so that for page != check_page, trylock_page() is attempted (to avoid possible deadlocks as we already have check_page locked) and mlock_vma_page() is performed only upon success. If the page lock cannot be obtained, the page is left without PG_mlocked, which is again not a problem in the whole unevictable memory design. Signed-off-by: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Bob Liu <bob.liu@oracle.com> Reported-by: Sasha Levin <sasha.levin@oracle.com> Cc: Wanpeng Li <liwanp@linux.vnet.ibm.com> Cc: Michel Lespinasse <walken@google.com> Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> Acked-by: Rik van Riel <riel@redhat.com> Cc: David Rientjes <rientjes@google.com> Cc: Mel Gorman <mgorman@suse.de> Cc: Hugh Dickins <hughd@google.com> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
3a025760fc
commit
57e68e9cd6
@ -79,6 +79,7 @@ void clear_page_mlock(struct page *page)
|
|||||||
*/
|
*/
|
||||||
void mlock_vma_page(struct page *page)
|
void mlock_vma_page(struct page *page)
|
||||||
{
|
{
|
||||||
|
/* Serialize with page migration */
|
||||||
BUG_ON(!PageLocked(page));
|
BUG_ON(!PageLocked(page));
|
||||||
|
|
||||||
if (!TestSetPageMlocked(page)) {
|
if (!TestSetPageMlocked(page)) {
|
||||||
@ -174,6 +175,7 @@ unsigned int munlock_vma_page(struct page *page)
|
|||||||
unsigned int nr_pages;
|
unsigned int nr_pages;
|
||||||
struct zone *zone = page_zone(page);
|
struct zone *zone = page_zone(page);
|
||||||
|
|
||||||
|
/* For try_to_munlock() and to serialize with page migration */
|
||||||
BUG_ON(!PageLocked(page));
|
BUG_ON(!PageLocked(page));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
14
mm/rmap.c
14
mm/rmap.c
@ -1332,9 +1332,19 @@ static int try_to_unmap_cluster(unsigned long cursor, unsigned int *mapcount,
|
|||||||
BUG_ON(!page || PageAnon(page));
|
BUG_ON(!page || PageAnon(page));
|
||||||
|
|
||||||
if (locked_vma) {
|
if (locked_vma) {
|
||||||
mlock_vma_page(page); /* no-op if already mlocked */
|
if (page == check_page) {
|
||||||
if (page == check_page)
|
/* we know we have check_page locked */
|
||||||
|
mlock_vma_page(page);
|
||||||
ret = SWAP_MLOCK;
|
ret = SWAP_MLOCK;
|
||||||
|
} else if (trylock_page(page)) {
|
||||||
|
/*
|
||||||
|
* If we can lock the page, perform mlock.
|
||||||
|
* Otherwise leave the page alone, it will be
|
||||||
|
* eventually encountered again later.
|
||||||
|
*/
|
||||||
|
mlock_vma_page(page);
|
||||||
|
unlock_page(page);
|
||||||
|
}
|
||||||
continue; /* don't unmap */
|
continue; /* don't unmap */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user