mm: page_alloc: fix zone allocation fairness on UP

The zone allocation batches can easily underflow due to higher-order
allocations or spills to remote nodes. On SMP that's fine, because
underflows are expected from concurrency and dealt with by returning 0.
But on UP, zone_page_state will just return a wrapped unsigned long,
which will get past the <= 0 check and then consider the zone eligible
until its watermarks are hit.

Commit 3a025760fc15 ("mm: page_alloc: spill to remote nodes before
waking kswapd") already made the counter-resetting use
atomic_long_read() to accomodate underflows from remote spills, but it
didn't go all the way with it.

Make it clear that these batches are expected to go negative regardless
of concurrency, and use atomic_long_read() everywhere.

Fixes: 81c0a2bb515f ("mm: page_alloc: fair zone allocator policy")
Reported-by: Vlastimil Babka <vbabka@suse.cz>
Reported-by: Leon Romanovsky <leon@leon.nu>
Signed-off-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: Mel Gorman <mgorman@suse.de>
Cc: <stable@vger.kernel.org> [3.12+]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by Johannes Weiner and committed by Linus Torvalds abe5f972 6c72e350

Changed files
+3 -4
mm
+3 -4
mm/page_alloc.c
··· 1612 1612 } 1613 1613 1614 1614 __mod_zone_page_state(zone, NR_ALLOC_BATCH, -(1 << order)); 1615 - if (zone_page_state(zone, NR_ALLOC_BATCH) == 0 && 1615 + if (atomic_long_read(&zone->vm_stat[NR_ALLOC_BATCH]) <= 0 && 1616 1616 !zone_is_fair_depleted(zone)) 1617 1617 zone_set_flag(zone, ZONE_FAIR_DEPLETED); 1618 1618 ··· 5701 5701 zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + (tmp >> 1); 5702 5702 5703 5703 __mod_zone_page_state(zone, NR_ALLOC_BATCH, 5704 - high_wmark_pages(zone) - 5705 - low_wmark_pages(zone) - 5706 - zone_page_state(zone, NR_ALLOC_BATCH)); 5704 + high_wmark_pages(zone) - low_wmark_pages(zone) - 5705 + atomic_long_read(&zone->vm_stat[NR_ALLOC_BATCH])); 5707 5706 5708 5707 setup_zone_migrate_reserve(zone); 5709 5708 spin_unlock_irqrestore(&zone->lock, flags);