Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[deliverable/linux.git] / mm / compaction.c
index 546e571e9d60d1df57155833ab0e2d6afcb33ac0..8c0d9459b54a02042dd2caf9489566188d9ea908 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/sysfs.h>
 #include <linux/balloon_compaction.h>
 #include <linux/page-isolation.h>
+#include <linux/kasan.h>
 #include "internal.h"
 
 #ifdef CONFIG_COMPACTION
@@ -34,6 +35,17 @@ static inline void count_compact_events(enum vm_event_item item, long delta)
 #endif
 
 #if defined CONFIG_COMPACTION || defined CONFIG_CMA
+#ifdef CONFIG_TRACEPOINTS
+static const char *const compaction_status_string[] = {
+       "deferred",
+       "skipped",
+       "continue",
+       "partial",
+       "complete",
+       "no_suitable_page",
+       "not_suitable_zone",
+};
+#endif
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/compaction.h>
@@ -61,6 +73,7 @@ static void map_pages(struct list_head *list)
        list_for_each_entry(page, list, lru) {
                arch_alloc_page(page, 0);
                kernel_map_pages(page, 1, 1);
+               kasan_alloc_pages(page, 0);
        }
 }
 
@@ -113,6 +126,77 @@ static struct page *pageblock_pfn_to_page(unsigned long start_pfn,
 }
 
 #ifdef CONFIG_COMPACTION
+
+/* Do not skip compaction more than 64 times */
+#define COMPACT_MAX_DEFER_SHIFT 6
+
+/*
+ * Compaction is deferred when compaction fails to result in a page
+ * allocation success. 1 << compact_defer_limit compactions are skipped up
+ * to a limit of 1 << COMPACT_MAX_DEFER_SHIFT
+ */
+void defer_compaction(struct zone *zone, int order)
+{
+       zone->compact_considered = 0;
+       zone->compact_defer_shift++;
+
+       if (order < zone->compact_order_failed)
+               zone->compact_order_failed = order;
+
+       if (zone->compact_defer_shift > COMPACT_MAX_DEFER_SHIFT)
+               zone->compact_defer_shift = COMPACT_MAX_DEFER_SHIFT;
+
+       trace_mm_compaction_defer_compaction(zone, order);
+}
+
+/* Returns true if compaction should be skipped this time */
+bool compaction_deferred(struct zone *zone, int order)
+{
+       unsigned long defer_limit = 1UL << zone->compact_defer_shift;
+
+       if (order < zone->compact_order_failed)
+               return false;
+
+       /* Avoid possible overflow */
+       if (++zone->compact_considered > defer_limit)
+               zone->compact_considered = defer_limit;
+
+       if (zone->compact_considered >= defer_limit)
+               return false;
+
+       trace_mm_compaction_deferred(zone, order);
+
+       return true;
+}
+
+/*
+ * Update defer tracking counters after successful compaction of given order,
+ * which means an allocation either succeeded (alloc_success == true) or is
+ * expected to succeed.
+ */
+void compaction_defer_reset(struct zone *zone, int order,
+               bool alloc_success)
+{
+       if (alloc_success) {
+               zone->compact_considered = 0;
+               zone->compact_defer_shift = 0;
+       }
+       if (order >= zone->compact_order_failed)
+               zone->compact_order_failed = order + 1;
+
+       trace_mm_compaction_defer_reset(zone, order);
+}
+
+/* Returns true if restarting compaction after many failures */
+bool compaction_restarting(struct zone *zone, int order)
+{
+       if (order < zone->compact_order_failed)
+               return false;
+
+       return zone->compact_defer_shift == COMPACT_MAX_DEFER_SHIFT &&
+               zone->compact_considered >= 1UL << zone->compact_defer_shift;
+}
+
 /* Returns true if the pageblock should be scanned for pages to isolate. */
 static inline bool isolation_suitable(struct compact_control *cc,
                                        struct page *page)
@@ -408,6 +492,13 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
 
                /* If a page was split, advance to the end of it */
                if (isolated) {
+                       cc->nr_freepages += isolated;
+                       if (!strict &&
+                               cc->nr_migratepages <= cc->nr_freepages) {
+                               blockpfn += isolated;
+                               break;
+                       }
+
                        blockpfn += isolated - 1;
                        cursor += isolated - 1;
                        continue;
@@ -421,11 +512,12 @@ isolate_fail:
 
        }
 
+       trace_mm_compaction_isolate_freepages(*start_pfn, blockpfn,
+                                       nr_scanned, total_isolated);
+
        /* Record how far we have got within the block */
        *start_pfn = blockpfn;
 
-       trace_mm_compaction_isolate_freepages(nr_scanned, total_isolated);
-
        /*
         * If strict isolation is requested by CMA then check that all the
         * pages requested were isolated. If there were any failures, 0 is
@@ -581,6 +673,7 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,
        unsigned long flags = 0;
        bool locked = false;
        struct page *page = NULL, *valid_page = NULL;
+       unsigned long start_pfn = low_pfn;
 
        /*
         * Ensure that there are not too many pages isolated from the LRU
@@ -741,7 +834,8 @@ isolate_success:
        if (low_pfn == end_pfn)
                update_pageblock_skip(cc, valid_page, nr_isolated, true);
 
-       trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated);
+       trace_mm_compaction_isolate_migratepages(start_pfn, low_pfn,
+                                               nr_scanned, nr_isolated);
 
        count_compact_events(COMPACTMIGRATE_SCANNED, nr_scanned);
        if (nr_isolated)
@@ -814,7 +908,6 @@ static void isolate_freepages(struct compact_control *cc)
        unsigned long isolate_start_pfn; /* exact pfn we start at */
        unsigned long block_end_pfn;    /* end of current pageblock */
        unsigned long low_pfn;       /* lowest pfn scanner is able to scan */
-       int nr_freepages = cc->nr_freepages;
        struct list_head *freelist = &cc->freepages;
 
        /*
@@ -839,11 +932,11 @@ static void isolate_freepages(struct compact_control *cc)
         * pages on cc->migratepages. We stop searching if the migrate
         * and free page scanners meet or enough free pages are isolated.
         */
-       for (; block_start_pfn >= low_pfn && cc->nr_migratepages > nr_freepages;
+       for (; block_start_pfn >= low_pfn &&
+                       cc->nr_migratepages > cc->nr_freepages;
                                block_end_pfn = block_start_pfn,
                                block_start_pfn -= pageblock_nr_pages,
                                isolate_start_pfn = block_start_pfn) {
-               unsigned long isolated;
 
                /*
                 * This can iterate a massively long zone without finding any
@@ -868,9 +961,8 @@ static void isolate_freepages(struct compact_control *cc)
                        continue;
 
                /* Found a block suitable for isolating free pages from. */
-               isolated = isolate_freepages_block(cc, &isolate_start_pfn,
+               isolate_freepages_block(cc, &isolate_start_pfn,
                                        block_end_pfn, freelist, false);
-               nr_freepages += isolated;
 
                /*
                 * Remember where the free scanner should restart next time,
@@ -902,8 +994,6 @@ static void isolate_freepages(struct compact_control *cc)
         */
        if (block_start_pfn < low_pfn)
                cc->free_pfn = cc->migrate_pfn;
-
-       cc->nr_freepages = nr_freepages;
 }
 
 /*
@@ -1015,8 +1105,10 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone,
                low_pfn = isolate_migratepages_block(cc, low_pfn, end_pfn,
                                                                isolate_mode);
 
-               if (!low_pfn || cc->contended)
+               if (!low_pfn || cc->contended) {
+                       acct_isolated(zone, cc);
                        return ISOLATE_ABORT;
+               }
 
                /*
                 * Either we isolated something and proceed with migration. Or
@@ -1037,7 +1129,7 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone,
        return cc->nr_migratepages ? ISOLATE_SUCCESS : ISOLATE_NONE;
 }
 
-static int compact_finished(struct zone *zone, struct compact_control *cc,
+static int __compact_finished(struct zone *zone, struct compact_control *cc,
                            const int migratetype)
 {
        unsigned int order;
@@ -1088,11 +1180,24 @@ static int compact_finished(struct zone *zone, struct compact_control *cc,
                        return COMPACT_PARTIAL;
 
                /* Job done if allocation would set block type */
-               if (cc->order >= pageblock_order && area->nr_free)
+               if (order >= pageblock_order && area->nr_free)
                        return COMPACT_PARTIAL;
        }
 
-       return COMPACT_CONTINUE;
+       return COMPACT_NO_SUITABLE_PAGE;
+}
+
+static int compact_finished(struct zone *zone, struct compact_control *cc,
+                           const int migratetype)
+{
+       int ret;
+
+       ret = __compact_finished(zone, cc, migratetype);
+       trace_mm_compaction_finished(zone, cc->order, ret);
+       if (ret == COMPACT_NO_SUITABLE_PAGE)
+               ret = COMPACT_CONTINUE;
+
+       return ret;
 }
 
 /*
@@ -1102,7 +1207,7 @@ static int compact_finished(struct zone *zone, struct compact_control *cc,
  *   COMPACT_PARTIAL  - If the allocation would succeed without compaction
  *   COMPACT_CONTINUE - If compaction should run now
  */
-unsigned long compaction_suitable(struct zone *zone, int order,
+static unsigned long __compaction_suitable(struct zone *zone, int order,
                                        int alloc_flags, int classzone_idx)
 {
        int fragindex;
@@ -1146,11 +1251,24 @@ unsigned long compaction_suitable(struct zone *zone, int order,
         */
        fragindex = fragmentation_index(zone, order);
        if (fragindex >= 0 && fragindex <= sysctl_extfrag_threshold)
-               return COMPACT_SKIPPED;
+               return COMPACT_NOT_SUITABLE_ZONE;
 
        return COMPACT_CONTINUE;
 }
 
+unsigned long compaction_suitable(struct zone *zone, int order,
+                                       int alloc_flags, int classzone_idx)
+{
+       unsigned long ret;
+
+       ret = __compaction_suitable(zone, order, alloc_flags, classzone_idx);
+       trace_mm_compaction_suitable(zone, order, ret);
+       if (ret == COMPACT_NOT_SUITABLE_ZONE)
+               ret = COMPACT_SKIPPED;
+
+       return ret;
+}
+
 static int compact_zone(struct zone *zone, struct compact_control *cc)
 {
        int ret;
@@ -1197,7 +1315,8 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
                zone->compact_cached_migrate_pfn[1] = cc->migrate_pfn;
        }
 
-       trace_mm_compaction_begin(start_pfn, cc->migrate_pfn, cc->free_pfn, end_pfn);
+       trace_mm_compaction_begin(start_pfn, cc->migrate_pfn,
+                               cc->free_pfn, end_pfn, sync);
 
        migrate_prep_local();
 
@@ -1299,7 +1418,8 @@ out:
                        zone->compact_cached_free_pfn = free_pfn;
        }
 
-       trace_mm_compaction_end(ret);
+       trace_mm_compaction_end(start_pfn, cc->migrate_pfn,
+                               cc->free_pfn, end_pfn, sync, ret);
 
        return ret;
 }
@@ -1335,22 +1455,20 @@ int sysctl_extfrag_threshold = 500;
 
 /**
  * try_to_compact_pages - Direct compact to satisfy a high-order allocation
- * @zonelist: The zonelist used for the current allocation
- * @order: The order of the current allocation
  * @gfp_mask: The GFP mask of the current allocation
- * @nodemask: The allowed nodes to allocate from
+ * @order: The order of the current allocation
+ * @alloc_flags: The allocation flags of the current allocation
+ * @ac: The context of current allocation
  * @mode: The migration mode for async, sync light, or sync migration
  * @contended: Return value that determines if compaction was aborted due to
  *            need_resched() or lock contention
  *
  * This is the main entry point for direct page compaction.
  */
-unsigned long try_to_compact_pages(struct zonelist *zonelist,
-                       int order, gfp_t gfp_mask, nodemask_t *nodemask,
-                       enum migrate_mode mode, int *contended,
-                       int alloc_flags, int classzone_idx)
+unsigned long try_to_compact_pages(gfp_t gfp_mask, unsigned int order,
+                       int alloc_flags, const struct alloc_context *ac,
+                       enum migrate_mode mode, int *contended)
 {
-       enum zone_type high_zoneidx = gfp_zone(gfp_mask);
        int may_enter_fs = gfp_mask & __GFP_FS;
        int may_perform_io = gfp_mask & __GFP_IO;
        struct zoneref *z;
@@ -1364,9 +1482,11 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,
        if (!order || !may_enter_fs || !may_perform_io)
                return COMPACT_SKIPPED;
 
+       trace_mm_compaction_try_to_compact_pages(order, gfp_mask, mode);
+
        /* Compact each zone in the list */
-       for_each_zone_zonelist_nodemask(zone, z, zonelist, high_zoneidx,
-                                                               nodemask) {
+       for_each_zone_zonelist_nodemask(zone, z, ac->zonelist, ac->high_zoneidx,
+                                                               ac->nodemask) {
                int status;
                int zone_contended;
 
@@ -1374,7 +1494,8 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,
                        continue;
 
                status = compact_zone_order(zone, order, gfp_mask, mode,
-                               &zone_contended, alloc_flags, classzone_idx);
+                               &zone_contended, alloc_flags,
+                               ac->classzone_idx);
                rc = max(status, rc);
                /*
                 * It takes at least one zone that wasn't lock contended
@@ -1384,7 +1505,7 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,
 
                /* If a normal allocation would succeed, stop compacting */
                if (zone_watermark_ok(zone, order, low_wmark_pages(zone),
-                                       classzone_idx, alloc_flags)) {
+                                       ac->classzone_idx, alloc_flags)) {
                        /*
                         * We think the allocation will succeed in this zone,
                         * but it is not certain, hence the false. The caller
This page took 0.041759 seconds and 5 git commands to generate.