]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - mm/vmscan.c
mm: vmscan: reclaim order-0 and use compaction instead of lumpy reclaim
[mv-sheeva.git] / mm / vmscan.c
index 3464312bde0789624bda6db304e85b4e169c0b9e..10ebd74a423ce999d47675e03d6ff96798192c88 100644 (file)
@@ -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>
  * 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;