|
@@ -16,30 +16,11 @@
|
|
|
#include <linux/sysfs.h>
|
|
|
#include "internal.h"
|
|
|
|
|
|
+#if defined CONFIG_COMPACTION || defined CONFIG_CMA
|
|
|
+
|
|
|
#define CREATE_TRACE_POINTS
|
|
|
#include <trace/events/compaction.h>
|
|
|
|
|
|
-/*
|
|
|
- * compact_control is used to track pages being migrated and the free pages
|
|
|
- * they are being migrated to during memory compaction. The free_pfn starts
|
|
|
- * at the end of a zone and migrate_pfn begins at the start. Movable pages
|
|
|
- * are moved to the end of a zone during a compaction run and the run
|
|
|
- * completes when free_pfn <= migrate_pfn
|
|
|
- */
|
|
|
-struct compact_control {
|
|
|
- struct list_head freepages; /* List of free pages to migrate to */
|
|
|
- struct list_head migratepages; /* List of pages being migrated */
|
|
|
- unsigned long nr_freepages; /* Number of isolated free pages */
|
|
|
- unsigned long nr_migratepages; /* Number of pages to migrate */
|
|
|
- unsigned long free_pfn; /* isolate_freepages search base */
|
|
|
- unsigned long migrate_pfn; /* isolate_migratepages search base */
|
|
|
- bool sync; /* Synchronous migration */
|
|
|
-
|
|
|
- int order; /* order a direct compactor needs */
|
|
|
- int migratetype; /* MOVABLE, RECLAIMABLE etc */
|
|
|
- struct zone *zone;
|
|
|
-};
|
|
|
-
|
|
|
static unsigned long release_freepages(struct list_head *freelist)
|
|
|
{
|
|
|
struct page *page, *next;
|
|
@@ -54,6 +35,16 @@ static unsigned long release_freepages(struct list_head *freelist)
|
|
|
return count;
|
|
|
}
|
|
|
|
|
|
+static void map_pages(struct list_head *list)
|
|
|
+{
|
|
|
+ struct page *page;
|
|
|
+
|
|
|
+ list_for_each_entry(page, list, lru) {
|
|
|
+ arch_alloc_page(page, 0);
|
|
|
+ kernel_map_pages(page, 1, 1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Isolate free pages onto a private freelist. Caller must hold zone->lock.
|
|
|
* If @strict is true, will abort returning 0 on any invalid PFNs or non-free
|
|
@@ -122,7 +113,7 @@ static unsigned long isolate_freepages_block(unsigned long blockpfn,
|
|
|
* (which may be greater then end_pfn if end fell in a middle of
|
|
|
* a free page).
|
|
|
*/
|
|
|
-static unsigned long
|
|
|
+unsigned long
|
|
|
isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn)
|
|
|
{
|
|
|
unsigned long isolated, pfn, block_end_pfn, flags;
|
|
@@ -176,127 +167,6 @@ isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn)
|
|
|
return pfn;
|
|
|
}
|
|
|
|
|
|
-/* Returns true if the page is within a block suitable for migration to */
|
|
|
-static bool suitable_migration_target(struct page *page)
|
|
|
-{
|
|
|
-
|
|
|
- int migratetype = get_pageblock_migratetype(page);
|
|
|
-
|
|
|
- /* Don't interfere with memory hot-remove or the min_free_kbytes blocks */
|
|
|
- if (migratetype == MIGRATE_ISOLATE || migratetype == MIGRATE_RESERVE)
|
|
|
- return false;
|
|
|
-
|
|
|
- /* If the page is a large free page, then allow migration */
|
|
|
- if (PageBuddy(page) && page_order(page) >= pageblock_order)
|
|
|
- return true;
|
|
|
-
|
|
|
- /* If the block is MIGRATE_MOVABLE, allow migration */
|
|
|
- if (migratetype == MIGRATE_MOVABLE)
|
|
|
- return true;
|
|
|
-
|
|
|
- /* Otherwise skip the block */
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-static void map_pages(struct list_head *list)
|
|
|
-{
|
|
|
- struct page *page;
|
|
|
-
|
|
|
- list_for_each_entry(page, list, lru) {
|
|
|
- arch_alloc_page(page, 0);
|
|
|
- kernel_map_pages(page, 1, 1);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * Based on information in the current compact_control, find blocks
|
|
|
- * suitable for isolating free pages from and then isolate them.
|
|
|
- */
|
|
|
-static void isolate_freepages(struct zone *zone,
|
|
|
- struct compact_control *cc)
|
|
|
-{
|
|
|
- struct page *page;
|
|
|
- unsigned long high_pfn, low_pfn, pfn, zone_end_pfn, end_pfn;
|
|
|
- unsigned long flags;
|
|
|
- int nr_freepages = cc->nr_freepages;
|
|
|
- struct list_head *freelist = &cc->freepages;
|
|
|
-
|
|
|
- /*
|
|
|
- * Initialise the free scanner. The starting point is where we last
|
|
|
- * scanned from (or the end of the zone if starting). The low point
|
|
|
- * is the end of the pageblock the migration scanner is using.
|
|
|
- */
|
|
|
- pfn = cc->free_pfn;
|
|
|
- low_pfn = cc->migrate_pfn + pageblock_nr_pages;
|
|
|
-
|
|
|
- /*
|
|
|
- * Take care that if the migration scanner is at the end of the zone
|
|
|
- * that the free scanner does not accidentally move to the next zone
|
|
|
- * in the next isolation cycle.
|
|
|
- */
|
|
|
- high_pfn = min(low_pfn, pfn);
|
|
|
-
|
|
|
- zone_end_pfn = zone->zone_start_pfn + zone->spanned_pages;
|
|
|
-
|
|
|
- /*
|
|
|
- * Isolate free pages until enough are available to migrate the
|
|
|
- * pages on cc->migratepages. We stop searching if the migrate
|
|
|
- * and free page scanners meet or enough free pages are isolated.
|
|
|
- */
|
|
|
- for (; pfn > low_pfn && cc->nr_migratepages > nr_freepages;
|
|
|
- pfn -= pageblock_nr_pages) {
|
|
|
- unsigned long isolated;
|
|
|
-
|
|
|
- if (!pfn_valid(pfn))
|
|
|
- continue;
|
|
|
-
|
|
|
- /*
|
|
|
- * Check for overlapping nodes/zones. It's possible on some
|
|
|
- * configurations to have a setup like
|
|
|
- * node0 node1 node0
|
|
|
- * i.e. it's possible that all pages within a zones range of
|
|
|
- * pages do not belong to a single zone.
|
|
|
- */
|
|
|
- page = pfn_to_page(pfn);
|
|
|
- if (page_zone(page) != zone)
|
|
|
- continue;
|
|
|
-
|
|
|
- /* Check the block is suitable for migration */
|
|
|
- if (!suitable_migration_target(page))
|
|
|
- continue;
|
|
|
-
|
|
|
- /*
|
|
|
- * Found a block suitable for isolating free pages from. Now
|
|
|
- * we disabled interrupts, double check things are ok and
|
|
|
- * isolate the pages. This is to minimise the time IRQs
|
|
|
- * are disabled
|
|
|
- */
|
|
|
- isolated = 0;
|
|
|
- spin_lock_irqsave(&zone->lock, flags);
|
|
|
- if (suitable_migration_target(page)) {
|
|
|
- end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn);
|
|
|
- isolated = isolate_freepages_block(pfn, end_pfn,
|
|
|
- freelist, false);
|
|
|
- nr_freepages += isolated;
|
|
|
- }
|
|
|
- spin_unlock_irqrestore(&zone->lock, flags);
|
|
|
-
|
|
|
- /*
|
|
|
- * Record the highest PFN we isolated pages from. When next
|
|
|
- * looking for free pages, the search will restart here as
|
|
|
- * page migration may have returned some pages to the allocator
|
|
|
- */
|
|
|
- if (isolated)
|
|
|
- high_pfn = max(high_pfn, pfn);
|
|
|
- }
|
|
|
-
|
|
|
- /* split_free_page does not map the pages */
|
|
|
- map_pages(freelist);
|
|
|
-
|
|
|
- cc->free_pfn = high_pfn;
|
|
|
- cc->nr_freepages = nr_freepages;
|
|
|
-}
|
|
|
-
|
|
|
/* Update the number of anon and file isolated pages in the zone */
|
|
|
static void acct_isolated(struct zone *zone, struct compact_control *cc)
|
|
|
{
|
|
@@ -325,13 +195,6 @@ static bool too_many_isolated(struct zone *zone)
|
|
|
return isolated > (inactive + active) / 2;
|
|
|
}
|
|
|
|
|
|
-/* possible outcome of isolate_migratepages */
|
|
|
-typedef enum {
|
|
|
- ISOLATE_ABORT, /* Abort compaction now */
|
|
|
- ISOLATE_NONE, /* No pages isolated, continue scanning */
|
|
|
- ISOLATE_SUCCESS, /* Pages isolated, migrate */
|
|
|
-} isolate_migrate_t;
|
|
|
-
|
|
|
/**
|
|
|
* isolate_migratepages_range() - isolate all migrate-able pages in range.
|
|
|
* @zone: Zone pages are in.
|
|
@@ -351,7 +214,7 @@ typedef enum {
|
|
|
* does not modify any cc's fields, in particular it does not modify
|
|
|
* (or read for that matter) cc->migrate_pfn.
|
|
|
*/
|
|
|
-static unsigned long
|
|
|
+unsigned long
|
|
|
isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
|
|
|
unsigned long low_pfn, unsigned long end_pfn)
|
|
|
{
|
|
@@ -487,35 +350,118 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
|
|
|
return low_pfn;
|
|
|
}
|
|
|
|
|
|
+#endif /* CONFIG_COMPACTION || CONFIG_CMA */
|
|
|
+#ifdef CONFIG_COMPACTION
|
|
|
+
|
|
|
+/* Returns true if the page is within a block suitable for migration to */
|
|
|
+static bool suitable_migration_target(struct page *page)
|
|
|
+{
|
|
|
+
|
|
|
+ int migratetype = get_pageblock_migratetype(page);
|
|
|
+
|
|
|
+ /* Don't interfere with memory hot-remove or the min_free_kbytes blocks */
|
|
|
+ if (migratetype == MIGRATE_ISOLATE || migratetype == MIGRATE_RESERVE)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ /* If the page is a large free page, then allow migration */
|
|
|
+ if (PageBuddy(page) && page_order(page) >= pageblock_order)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ /* If the block is MIGRATE_MOVABLE, allow migration */
|
|
|
+ if (migratetype == MIGRATE_MOVABLE)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ /* Otherwise skip the block */
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
- * Isolate all pages that can be migrated from the block pointed to by
|
|
|
- * the migrate scanner within compact_control.
|
|
|
+ * Based on information in the current compact_control, find blocks
|
|
|
+ * suitable for isolating free pages from and then isolate them.
|
|
|
*/
|
|
|
-static isolate_migrate_t isolate_migratepages(struct zone *zone,
|
|
|
- struct compact_control *cc)
|
|
|
+static void isolate_freepages(struct zone *zone,
|
|
|
+ struct compact_control *cc)
|
|
|
{
|
|
|
- unsigned long low_pfn, end_pfn;
|
|
|
+ struct page *page;
|
|
|
+ unsigned long high_pfn, low_pfn, pfn, zone_end_pfn, end_pfn;
|
|
|
+ unsigned long flags;
|
|
|
+ int nr_freepages = cc->nr_freepages;
|
|
|
+ struct list_head *freelist = &cc->freepages;
|
|
|
|
|
|
- /* Do not scan outside zone boundaries */
|
|
|
- low_pfn = max(cc->migrate_pfn, zone->zone_start_pfn);
|
|
|
+ /*
|
|
|
+ * Initialise the free scanner. The starting point is where we last
|
|
|
+ * scanned from (or the end of the zone if starting). The low point
|
|
|
+ * is the end of the pageblock the migration scanner is using.
|
|
|
+ */
|
|
|
+ pfn = cc->free_pfn;
|
|
|
+ low_pfn = cc->migrate_pfn + pageblock_nr_pages;
|
|
|
|
|
|
- /* Only scan within a pageblock boundary */
|
|
|
- end_pfn = ALIGN(low_pfn + pageblock_nr_pages, pageblock_nr_pages);
|
|
|
+ /*
|
|
|
+ * Take care that if the migration scanner is at the end of the zone
|
|
|
+ * that the free scanner does not accidentally move to the next zone
|
|
|
+ * in the next isolation cycle.
|
|
|
+ */
|
|
|
+ high_pfn = min(low_pfn, pfn);
|
|
|
|
|
|
- /* Do not cross the free scanner or scan within a memory hole */
|
|
|
- if (end_pfn > cc->free_pfn || !pfn_valid(low_pfn)) {
|
|
|
- cc->migrate_pfn = end_pfn;
|
|
|
- return ISOLATE_NONE;
|
|
|
- }
|
|
|
+ zone_end_pfn = zone->zone_start_pfn + zone->spanned_pages;
|
|
|
|
|
|
- /* Perform the isolation */
|
|
|
- low_pfn = isolate_migratepages_range(zone, cc, low_pfn, end_pfn);
|
|
|
- if (!low_pfn)
|
|
|
- return ISOLATE_ABORT;
|
|
|
+ /*
|
|
|
+ * Isolate free pages until enough are available to migrate the
|
|
|
+ * pages on cc->migratepages. We stop searching if the migrate
|
|
|
+ * and free page scanners meet or enough free pages are isolated.
|
|
|
+ */
|
|
|
+ for (; pfn > low_pfn && cc->nr_migratepages > nr_freepages;
|
|
|
+ pfn -= pageblock_nr_pages) {
|
|
|
+ unsigned long isolated;
|
|
|
|
|
|
- cc->migrate_pfn = low_pfn;
|
|
|
+ if (!pfn_valid(pfn))
|
|
|
+ continue;
|
|
|
|
|
|
- return ISOLATE_SUCCESS;
|
|
|
+ /*
|
|
|
+ * Check for overlapping nodes/zones. It's possible on some
|
|
|
+ * configurations to have a setup like
|
|
|
+ * node0 node1 node0
|
|
|
+ * i.e. it's possible that all pages within a zones range of
|
|
|
+ * pages do not belong to a single zone.
|
|
|
+ */
|
|
|
+ page = pfn_to_page(pfn);
|
|
|
+ if (page_zone(page) != zone)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Check the block is suitable for migration */
|
|
|
+ if (!suitable_migration_target(page))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Found a block suitable for isolating free pages from. Now
|
|
|
+ * we disabled interrupts, double check things are ok and
|
|
|
+ * isolate the pages. This is to minimise the time IRQs
|
|
|
+ * are disabled
|
|
|
+ */
|
|
|
+ isolated = 0;
|
|
|
+ spin_lock_irqsave(&zone->lock, flags);
|
|
|
+ if (suitable_migration_target(page)) {
|
|
|
+ end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn);
|
|
|
+ isolated = isolate_freepages_block(pfn, end_pfn,
|
|
|
+ freelist, false);
|
|
|
+ nr_freepages += isolated;
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&zone->lock, flags);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Record the highest PFN we isolated pages from. When next
|
|
|
+ * looking for free pages, the search will restart here as
|
|
|
+ * page migration may have returned some pages to the allocator
|
|
|
+ */
|
|
|
+ if (isolated)
|
|
|
+ high_pfn = max(high_pfn, pfn);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* split_free_page does not map the pages */
|
|
|
+ map_pages(freelist);
|
|
|
+
|
|
|
+ cc->free_pfn = high_pfn;
|
|
|
+ cc->nr_freepages = nr_freepages;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -564,6 +510,44 @@ static void update_nr_listpages(struct compact_control *cc)
|
|
|
cc->nr_freepages = nr_freepages;
|
|
|
}
|
|
|
|
|
|
+/* possible outcome of isolate_migratepages */
|
|
|
+typedef enum {
|
|
|
+ ISOLATE_ABORT, /* Abort compaction now */
|
|
|
+ ISOLATE_NONE, /* No pages isolated, continue scanning */
|
|
|
+ ISOLATE_SUCCESS, /* Pages isolated, migrate */
|
|
|
+} isolate_migrate_t;
|
|
|
+
|
|
|
+/*
|
|
|
+ * Isolate all pages that can be migrated from the block pointed to by
|
|
|
+ * the migrate scanner within compact_control.
|
|
|
+ */
|
|
|
+static isolate_migrate_t isolate_migratepages(struct zone *zone,
|
|
|
+ struct compact_control *cc)
|
|
|
+{
|
|
|
+ unsigned long low_pfn, end_pfn;
|
|
|
+
|
|
|
+ /* Do not scan outside zone boundaries */
|
|
|
+ low_pfn = max(cc->migrate_pfn, zone->zone_start_pfn);
|
|
|
+
|
|
|
+ /* Only scan within a pageblock boundary */
|
|
|
+ end_pfn = ALIGN(low_pfn + pageblock_nr_pages, pageblock_nr_pages);
|
|
|
+
|
|
|
+ /* Do not cross the free scanner or scan within a memory hole */
|
|
|
+ if (end_pfn > cc->free_pfn || !pfn_valid(low_pfn)) {
|
|
|
+ cc->migrate_pfn = end_pfn;
|
|
|
+ return ISOLATE_NONE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Perform the isolation */
|
|
|
+ low_pfn = isolate_migratepages_range(zone, cc, low_pfn, end_pfn);
|
|
|
+ if (!low_pfn)
|
|
|
+ return ISOLATE_ABORT;
|
|
|
+
|
|
|
+ cc->migrate_pfn = low_pfn;
|
|
|
+
|
|
|
+ return ISOLATE_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
static int compact_finished(struct zone *zone,
|
|
|
struct compact_control *cc)
|
|
|
{
|
|
@@ -910,3 +894,5 @@ void compaction_unregister_node(struct node *node)
|
|
|
return device_remove_file(&node->dev, &dev_attr_compact);
|
|
|
}
|
|
|
#endif /* CONFIG_SYSFS && CONFIG_NUMA */
|
|
|
+
|
|
|
+#endif /* CONFIG_COMPACTION */
|