This is a note to let you know that I've just added the patch titled mm: page_alloc: reduce cost of the fair zone allocation policy to the 3.14-stable tree which can be found at: http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary The filename of the patch is: mm-page_alloc-reduce-cost-of-the-fair-zone-allocation-policy.patch and it can be found in the queue-3.14 subdirectory. If you, or anyone else, feels it should not be added to the stable tree, please let <stable@xxxxxxxxxxxxxxx> know about it. >From stable-owner@xxxxxxxxxxxxxxx Thu Aug 28 11:44:22 2014 From: Mel Gorman <mgorman@xxxxxxx> Date: Thu, 28 Aug 2014 19:35:45 +0100 Subject: mm: page_alloc: reduce cost of the fair zone allocation policy From: Mel Gorman <mgorman@xxxxxxx> commit 4ffeaf3560a52b4a69cc7909873d08c0ef5909d4 upstream. The fair zone allocation policy round-robins allocations between zones within a node to avoid age inversion problems during reclaim. If the first allocation fails, the batch counts are reset and a second attempt made before entering the slow path. One assumption made with this scheme is that batches expire at roughly the same time and the resets each time are justified. This assumption does not hold when zones reach their low watermark as the batches will be consumed at uneven rates. Allocation failure due to watermark depletion result in additional zonelist scans for the reset and another watermark check before hitting the slowpath. On UMA, the benefit is negligible -- around 0.25%. On 4-socket NUMA machine it's variable due to the variability of measuring overhead with the vmstat changes. The system CPU overhead comparison looks like 3.16.0-rc3 3.16.0-rc3 3.16.0-rc3 vanilla vmstat-v5 lowercost-v5 User 746.94 774.56 802.00 System 65336.22 32847.27 40852.33 Elapsed 27553.52 27415.04 27368.46 However it is worth noting that the overall benchmark still completed faster and intuitively it makes sense to take as few passes as possible through the zonelists. Signed-off-by: Mel Gorman <mgorman@xxxxxxx> Acked-by: Johannes Weiner <hannes@xxxxxxxxxxx> Signed-off-by: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx> Signed-off-by: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx> Signed-off-by: Mel Gorman <mgorman@xxxxxxx> Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> --- include/linux/mmzone.h | 6 ++ mm/page_alloc.c | 101 +++++++++++++++++++++++++------------------------ 2 files changed, 59 insertions(+), 48 deletions(-) --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -529,6 +529,7 @@ typedef enum { ZONE_WRITEBACK, /* reclaim scanning has recently found * many pages under writeback */ + ZONE_FAIR_DEPLETED, /* fair zone policy batch depleted */ } zone_flags_t; static inline void zone_set_flag(struct zone *zone, zone_flags_t flag) @@ -566,6 +567,11 @@ static inline int zone_is_reclaim_locked return test_bit(ZONE_RECLAIM_LOCKED, &zone->flags); } +static inline int zone_is_fair_depleted(const struct zone *zone) +{ + return test_bit(ZONE_FAIR_DEPLETED, &zone->flags); +} + static inline int zone_is_oom_locked(const struct zone *zone) { return test_bit(ZONE_OOM_LOCKED, &zone->flags); --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1614,6 +1614,9 @@ again: } __mod_zone_page_state(zone, NR_ALLOC_BATCH, -(1 << order)); + if (zone_page_state(zone, NR_ALLOC_BATCH) == 0 && + !zone_is_fair_depleted(zone)) + zone_set_flag(zone, ZONE_FAIR_DEPLETED); __count_zone_vm_events(PGALLOC, zone, 1 << order); zone_statistics(preferred_zone, zone, gfp_flags); @@ -1938,6 +1941,18 @@ static inline void init_zone_allows_recl } #endif /* CONFIG_NUMA */ +static void reset_alloc_batches(struct zone *preferred_zone) +{ + struct zone *zone = preferred_zone->zone_pgdat->node_zones; + + do { + mod_zone_page_state(zone, NR_ALLOC_BATCH, + high_wmark_pages(zone) - low_wmark_pages(zone) - + atomic_long_read(&zone->vm_stat[NR_ALLOC_BATCH])); + zone_clear_flag(zone, ZONE_FAIR_DEPLETED); + } while (zone++ != preferred_zone); +} + /* * get_page_from_freelist goes through the zonelist trying to allocate * a page. @@ -1955,8 +1970,12 @@ get_page_from_freelist(gfp_t gfp_mask, n int did_zlc_setup = 0; /* just call zlc_setup() one time */ bool consider_zone_dirty = (alloc_flags & ALLOC_WMARK_LOW) && (gfp_mask & __GFP_WRITE); + int nr_fair_skipped = 0; + bool zonelist_rescan; zonelist_scan: + zonelist_rescan = false; + /* * Scan zonelist, looking for a zone with enough free. * See also __cpuset_node_allowed_softwall() comment in kernel/cpuset.c. @@ -1981,8 +2000,10 @@ zonelist_scan: if (alloc_flags & ALLOC_FAIR) { if (!zone_local(preferred_zone, zone)) break; - if (atomic_long_read(&zone->vm_stat[NR_ALLOC_BATCH]) <= 0) + if (zone_is_fair_depleted(zone)) { + nr_fair_skipped++; continue; + } } /* * When allocating a page cache page for writing, we @@ -2088,13 +2109,7 @@ this_zone_full: zlc_mark_zone_full(zonelist, z); } - if (unlikely(IS_ENABLED(CONFIG_NUMA) && page == NULL && zlc_active)) { - /* Disable zlc cache for second zonelist scan */ - zlc_active = 0; - goto zonelist_scan; - } - - if (page) + if (page) { /* * page->pfmemalloc is set when ALLOC_NO_WATERMARKS was * necessary to allocate the page. The expectation is @@ -2103,8 +2118,37 @@ this_zone_full: * for !PFMEMALLOC purposes. */ page->pfmemalloc = !!(alloc_flags & ALLOC_NO_WATERMARKS); + return page; + } - return page; + /* + * The first pass makes sure allocations are spread fairly within the + * local node. However, the local node might have free pages left + * after the fairness batches are exhausted, and remote zones haven't + * even been considered yet. Try once more without fairness, and + * include remote zones now, before entering the slowpath and waking + * kswapd: prefer spilling to a remote zone over swapping locally. + */ + if (alloc_flags & ALLOC_FAIR) { + alloc_flags &= ~ALLOC_FAIR; + if (nr_fair_skipped) { + zonelist_rescan = true; + reset_alloc_batches(preferred_zone); + } + if (nr_online_nodes > 1) + zonelist_rescan = true; + } + + if (unlikely(IS_ENABLED(CONFIG_NUMA) && zlc_active)) { + /* Disable zlc cache for second zonelist scan */ + zlc_active = 0; + zonelist_rescan = true; + } + + if (zonelist_rescan) + goto zonelist_scan; + + return NULL; } /* @@ -2433,28 +2477,6 @@ __alloc_pages_high_priority(gfp_t gfp_ma return page; } -static void reset_alloc_batches(struct zonelist *zonelist, - enum zone_type high_zoneidx, - struct zone *preferred_zone) -{ - struct zoneref *z; - struct zone *zone; - - for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) { - /* - * Only reset the batches of zones that were actually - * considered in the fairness pass, we don't want to - * trash fairness information for zones that are not - * actually part of this zonelist's round-robin cycle. - */ - if (!zone_local(preferred_zone, zone)) - continue; - mod_zone_page_state(zone, NR_ALLOC_BATCH, - high_wmark_pages(zone) - low_wmark_pages(zone) - - atomic_long_read(&zone->vm_stat[NR_ALLOC_BATCH])); - } -} - static void wake_all_kswapds(unsigned int order, struct zonelist *zonelist, enum zone_type high_zoneidx, @@ -2792,29 +2814,12 @@ retry_cpuset: if (allocflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE) alloc_flags |= ALLOC_CMA; #endif -retry: /* First allocation attempt */ page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, nodemask, order, zonelist, high_zoneidx, alloc_flags, preferred_zone, classzone_idx, migratetype); if (unlikely(!page)) { /* - * The first pass makes sure allocations are spread - * fairly within the local node. However, the local - * node might have free pages left after the fairness - * batches are exhausted, and remote zones haven't - * even been considered yet. Try once more without - * fairness, and include remote zones now, before - * entering the slowpath and waking kswapd: prefer - * spilling to a remote zone over swapping locally. - */ - if (alloc_flags & ALLOC_FAIR) { - reset_alloc_batches(zonelist, high_zoneidx, - preferred_zone); - alloc_flags &= ~ALLOC_FAIR; - goto retry; - } - /* * Runtime PM, block IO and its error handling path * can deadlock because I/O on the device might not * complete. Patches currently in stable-queue which might be from mgorman@xxxxxxx are queue-3.14/mm-move-zone-pages_scanned-into-a-vmstat-counter.patch queue-3.14/mm-vmscan-only-update-per-cpu-thresholds-for-online-cpu.patch queue-3.14/mm-page_alloc-use-jump-labels-to-avoid-checking-number_of_cpusets.patch queue-3.14/mm-page_alloc-do-not-treat-a-zone-that-cannot-be-used-for-dirty-pages-as-full.patch queue-3.14/mm-non-atomically-mark-page-accessed-during-page-cache-allocation-where-possible.patch queue-3.14/mm-page_alloc-convert-hot-cold-parameter-and-immediate-callers-to-bool.patch queue-3.14/memcg-vmscan-fix-forced-scan-of-anonymous-pages.patch queue-3.14/mm-thp-only-collapse-hugepages-to-nodes-with-affinity-for-zone_reclaim_mode.patch queue-3.14/mm-page_alloc-only-check-the-zone-id-check-if-pages-are-buddies.patch queue-3.14/mm-page_alloc-only-check-the-alloc-flags-and-gfp_mask-for-dirty-once.patch queue-3.14/mm-page_alloc-do-not-update-zlc-unless-the-zlc-is-active.patch queue-3.14/mm-swap.c-clean-up-lru_cache_add-functions.patch queue-3.14/mm-page_alloc-take-the-alloc_no_watermark-check-out-of-the-fast-path.patch queue-3.14/fs-buffer-do-not-use-unnecessary-atomic-operations-when-discarding-buffers.patch queue-3.14/mm-do-not-use-atomic-operations-when-releasing-pages.patch queue-3.14/mm-page_alloc-reduce-number-of-times-page_to_pfn-is-called.patch queue-3.14/mm-page_alloc-use-unsigned-int-for-order-in-more-places.patch queue-3.14/mm-avoid-unnecessary-atomic-operations-during-end_page_writeback.patch queue-3.14/vmalloc-use-rcu-list-iterator-to-reduce-vmap_area_lock-contention.patch queue-3.14/mm-page_alloc-abort-fair-zone-allocation-policy-when-remotes-nodes-are-encountered.patch queue-3.14/mm-page_alloc-reduce-cost-of-the-fair-zone-allocation-policy.patch queue-3.14/mm-shmem-avoid-atomic-operation-during-shmem_getpage_gfp.patch queue-3.14/mm-pagemap-avoid-unnecessary-overhead-when-tracepoints-are-deactivated.patch queue-3.14/shmem-fix-init_page_accessed-use-to-stop-pagelru-bug.patch queue-3.14/mm-memory.c-use-entry-access_once-pte-in-handle_pte_fault.patch queue-3.14/mm-make-copy_pte_range-static-again.patch queue-3.14/mm-rearrange-zone-fields-into-read-only-page-alloc-statistics-and-page-reclaim-lines.patch queue-3.14/mm-do-not-use-unnecessary-atomic-operations-when-adding-pages-to-the-lru.patch queue-3.14/include-linux-jump_label.h-expose-the-reference-count.patch queue-3.14/mm-page_alloc-calculate-classzone_idx-once-from-the.patch queue-3.14/mm-page_alloc-lookup-pageblock-migratetype-with-irqs-enabled-during-free.patch -- To unsubscribe from this list: send the line "unsubscribe stable" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html