|
@@ -32,6 +32,7 @@
|
|
|
#include <linux/topology.h>
|
|
|
#include <linux/cpu.h>
|
|
|
#include <linux/cpuset.h>
|
|
|
+#include <linux/compaction.h>
|
|
|
#include <linux/notifier.h>
|
|
|
#include <linux/rwsem.h>
|
|
|
#include <linux/delay.h>
|
|
@@ -59,12 +60,15 @@
|
|
|
* LUMPY_MODE_CONTIGRECLAIM: For high-order allocations, take a reference
|
|
|
* page from the LRU and reclaim all pages within a
|
|
|
* naturally aligned range
|
|
|
+ * LUMPY_MODE_COMPACTION: For high-order allocations, reclaim a number of
|
|
|
+ * order-0 pages and then compact the zone
|
|
|
*/
|
|
|
typedef unsigned __bitwise__ lumpy_mode;
|
|
|
#define LUMPY_MODE_SINGLE ((__force lumpy_mode)0x01u)
|
|
|
#define LUMPY_MODE_ASYNC ((__force lumpy_mode)0x02u)
|
|
|
#define LUMPY_MODE_SYNC ((__force lumpy_mode)0x04u)
|
|
|
#define LUMPY_MODE_CONTIGRECLAIM ((__force lumpy_mode)0x08u)
|
|
|
+#define LUMPY_MODE_COMPACTION ((__force lumpy_mode)0x10u)
|
|
|
|
|
|
struct scan_control {
|
|
|
/* Incremented by the number of inactive pages that were scanned */
|
|
@@ -286,18 +290,20 @@ static void set_lumpy_reclaim_mode(int priority, struct scan_control *sc,
|
|
|
lumpy_mode syncmode = sync ? LUMPY_MODE_SYNC : LUMPY_MODE_ASYNC;
|
|
|
|
|
|
/*
|
|
|
- * Some reclaim have alredy been failed. No worth to try synchronous
|
|
|
- * lumpy reclaim.
|
|
|
+ * Initially assume we are entering either lumpy reclaim or
|
|
|
+ * reclaim/compaction.Depending on the order, we will either set the
|
|
|
+ * sync mode or just reclaim order-0 pages later.
|
|
|
*/
|
|
|
- if (sync && sc->lumpy_reclaim_mode & LUMPY_MODE_SINGLE)
|
|
|
- return;
|
|
|
+ if (COMPACTION_BUILD)
|
|
|
+ sc->lumpy_reclaim_mode = LUMPY_MODE_COMPACTION;
|
|
|
+ else
|
|
|
+ sc->lumpy_reclaim_mode = LUMPY_MODE_CONTIGRECLAIM;
|
|
|
|
|
|
/*
|
|
|
- * If we need a large contiguous chunk of memory, or have
|
|
|
- * trouble getting a small set of contiguous pages, we
|
|
|
- * will reclaim both active and inactive pages.
|
|
|
+ * Avoid using lumpy reclaim or reclaim/compaction if possible by
|
|
|
+ * restricting when its set to either costly allocations or when
|
|
|
+ * under memory pressure
|
|
|
*/
|
|
|
- sc->lumpy_reclaim_mode = LUMPY_MODE_CONTIGRECLAIM;
|
|
|
if (sc->order > PAGE_ALLOC_COSTLY_ORDER)
|
|
|
sc->lumpy_reclaim_mode |= syncmode;
|
|
|
else if (sc->order && priority < DEF_PRIORITY - 2)
|
|
@@ -1385,8 +1391,8 @@ shrink_inactive_list(unsigned long nr_to_scan, struct zone *zone,
|
|
|
if (scanning_global_lru(sc)) {
|
|
|
nr_taken = isolate_pages_global(nr_to_scan,
|
|
|
&page_list, &nr_scanned, sc->order,
|
|
|
- sc->lumpy_reclaim_mode & LUMPY_MODE_SINGLE ?
|
|
|
- ISOLATE_INACTIVE : ISOLATE_BOTH,
|
|
|
+ sc->lumpy_reclaim_mode & LUMPY_MODE_CONTIGRECLAIM ?
|
|
|
+ ISOLATE_BOTH : ISOLATE_INACTIVE,
|
|
|
zone, 0, file);
|
|
|
zone->pages_scanned += nr_scanned;
|
|
|
if (current_is_kswapd())
|
|
@@ -1398,8 +1404,8 @@ shrink_inactive_list(unsigned long nr_to_scan, struct zone *zone,
|
|
|
} else {
|
|
|
nr_taken = mem_cgroup_isolate_pages(nr_to_scan,
|
|
|
&page_list, &nr_scanned, sc->order,
|
|
|
- sc->lumpy_reclaim_mode & LUMPY_MODE_SINGLE ?
|
|
|
- ISOLATE_INACTIVE : ISOLATE_BOTH,
|
|
|
+ sc->lumpy_reclaim_mode & LUMPY_MODE_CONTIGRECLAIM ?
|
|
|
+ ISOLATE_BOTH : ISOLATE_INACTIVE,
|
|
|
zone, sc->mem_cgroup,
|
|
|
0, file);
|
|
|
/*
|
|
@@ -1814,6 +1820,57 @@ out:
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Reclaim/compaction depends on a number of pages being freed. To avoid
|
|
|
+ * disruption to the system, a small number of order-0 pages continue to be
|
|
|
+ * rotated and reclaimed in the normal fashion. However, by the time we get
|
|
|
+ * back to the allocator and call try_to_compact_zone(), we ensure that
|
|
|
+ * there are enough free pages for it to be likely successful
|
|
|
+ */
|
|
|
+static inline bool should_continue_reclaim(struct zone *zone,
|
|
|
+ unsigned long nr_reclaimed,
|
|
|
+ unsigned long nr_scanned,
|
|
|
+ struct scan_control *sc)
|
|
|
+{
|
|
|
+ unsigned long pages_for_compaction;
|
|
|
+ unsigned long inactive_lru_pages;
|
|
|
+
|
|
|
+ /* If not in reclaim/compaction mode, stop */
|
|
|
+ if (!(sc->lumpy_reclaim_mode & LUMPY_MODE_COMPACTION))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If we failed to reclaim and have scanned the full list, stop.
|
|
|
+ * NOTE: Checking just nr_reclaimed would exit reclaim/compaction far
|
|
|
+ * faster but obviously would be less likely to succeed
|
|
|
+ * allocation. If this is desirable, use GFP_REPEAT to decide
|
|
|
+ * if both reclaimed and scanned should be checked or just
|
|
|
+ * reclaimed
|
|
|
+ */
|
|
|
+ if (!nr_reclaimed && !nr_scanned)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If we have not reclaimed enough pages for compaction and the
|
|
|
+ * inactive lists are large enough, continue reclaiming
|
|
|
+ */
|
|
|
+ pages_for_compaction = (2UL << sc->order);
|
|
|
+ inactive_lru_pages = zone_nr_lru_pages(zone, sc, LRU_INACTIVE_ANON) +
|
|
|
+ zone_nr_lru_pages(zone, sc, LRU_INACTIVE_FILE);
|
|
|
+ if (sc->nr_reclaimed < pages_for_compaction &&
|
|
|
+ inactive_lru_pages > pages_for_compaction)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ /* If compaction would go ahead or the allocation would succeed, stop */
|
|
|
+ switch (compaction_suitable(zone, sc->order)) {
|
|
|
+ case COMPACT_PARTIAL:
|
|
|
+ case COMPACT_CONTINUE:
|
|
|
+ return false;
|
|
|
+ default:
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* This is a basic per-zone page freer. Used by both kswapd and direct reclaim.
|
|
|
*/
|
|
@@ -1823,9 +1880,12 @@ static void shrink_zone(int priority, struct zone *zone,
|
|
|
unsigned long nr[NR_LRU_LISTS];
|
|
|
unsigned long nr_to_scan;
|
|
|
enum lru_list l;
|
|
|
- unsigned long nr_reclaimed = sc->nr_reclaimed;
|
|
|
+ unsigned long nr_reclaimed;
|
|
|
unsigned long nr_to_reclaim = sc->nr_to_reclaim;
|
|
|
+ unsigned long nr_scanned = sc->nr_scanned;
|
|
|
|
|
|
+restart:
|
|
|
+ nr_reclaimed = 0;
|
|
|
get_scan_count(zone, sc, nr, priority);
|
|
|
|
|
|
while (nr[LRU_INACTIVE_ANON] || nr[LRU_ACTIVE_FILE] ||
|
|
@@ -1851,8 +1911,7 @@ static void shrink_zone(int priority, struct zone *zone,
|
|
|
if (nr_reclaimed >= nr_to_reclaim && priority < DEF_PRIORITY)
|
|
|
break;
|
|
|
}
|
|
|
-
|
|
|
- sc->nr_reclaimed = nr_reclaimed;
|
|
|
+ sc->nr_reclaimed += nr_reclaimed;
|
|
|
|
|
|
/*
|
|
|
* Even if we did not try to evict anon pages at all, we want to
|
|
@@ -1861,6 +1920,11 @@ static void shrink_zone(int priority, struct zone *zone,
|
|
|
if (inactive_anon_is_low(zone, sc))
|
|
|
shrink_active_list(SWAP_CLUSTER_MAX, zone, sc, priority, 0);
|
|
|
|
|
|
+ /* reclaim/compaction might need reclaim to continue */
|
|
|
+ if (should_continue_reclaim(zone, nr_reclaimed,
|
|
|
+ sc->nr_scanned - nr_scanned, sc))
|
|
|
+ goto restart;
|
|
|
+
|
|
|
throttle_vm_writeout(sc->gfp_mask);
|
|
|
}
|
|
|
|
|
@@ -2307,6 +2371,14 @@ loop_again:
|
|
|
total_scanned > sc.nr_reclaimed + sc.nr_reclaimed / 2)
|
|
|
sc.may_writepage = 1;
|
|
|
|
|
|
+ /*
|
|
|
+ * Compact the zone for higher orders to reduce
|
|
|
+ * latencies for higher-order allocations that
|
|
|
+ * would ordinarily call try_to_compact_pages()
|
|
|
+ */
|
|
|
+ if (sc.order > PAGE_ALLOC_COSTLY_ORDER)
|
|
|
+ compact_zone_order(zone, sc.order, sc.gfp_mask);
|
|
|
+
|
|
|
if (!zone_watermark_ok_safe(zone, order,
|
|
|
high_wmark_pages(zone), end_zone, 0)) {
|
|
|
all_zones_ok = 0;
|