diff options
author | Vlastimil Babka <vbabka@suse.cz> | 2016-06-04 00:55:52 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-06-04 02:02:56 +0300 |
commit | 83b9355bf6f449c7d1231206c300ea89d3313a9d (patch) | |
tree | 7f780a66a7476fcd2410d6932b957c9e9dd0afa0 /mm/page_alloc.c | |
parent | 879be4f378cb412af3a3fe107d35835c99099add (diff) | |
download | linux-83b9355bf6f449c7d1231206c300ea89d3313a9d.tar.xz |
mm, page_alloc: prevent infinite loop in buffered_rmqueue()
In DEBUG_VM kernel, we can hit infinite loop for order == 0 in
buffered_rmqueue() when check_new_pcp() returns 1, because the bad page
is never removed from the pcp list. Fix this by removing the page
before retrying. Also we don't need to check if page is non-NULL,
because we simply grab it from the list which was just tested for being
non-empty.
Fixes: 479f854a207c ("mm, page_alloc: defer debugging checks of pages allocated from the PCP")
Link: http://lkml.kernel.org/r/20160530090154.GM2527@techsingularity.net
Signed-off-by: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Mel Gorman <mgorman@techsingularity.net>
Reported-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/page_alloc.c')
-rw-r--r-- | mm/page_alloc.c | 9 |
1 files changed, 5 insertions, 4 deletions
diff --git a/mm/page_alloc.c b/mm/page_alloc.c index d27e8b968ac3..2e860f431d6b 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2615,11 +2615,12 @@ struct page *buffered_rmqueue(struct zone *preferred_zone, page = list_last_entry(list, struct page, lru); else page = list_first_entry(list, struct page, lru); - } while (page && check_new_pcp(page)); - __dec_zone_state(zone, NR_ALLOC_BATCH); - list_del(&page->lru); - pcp->count--; + __dec_zone_state(zone, NR_ALLOC_BATCH); + list_del(&page->lru); + pcp->count--; + + } while (check_new_pcp(page)); } else { /* * We most definitely don't want callers attempting to |