|
@@ -50,6 +50,47 @@ static inline bool migrate_async_suitable(int migratetype)
|
|
return is_migrate_cma(migratetype) || migratetype == MIGRATE_MOVABLE;
|
|
return is_migrate_cma(migratetype) || migratetype == MIGRATE_MOVABLE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Compaction requires the taking of some coarse locks that are potentially
|
|
|
|
+ * very heavily contended. Check if the process needs to be scheduled or
|
|
|
|
+ * if the lock is contended. For async compaction, back out in the event
|
|
|
|
+ * if contention is severe. For sync compaction, schedule.
|
|
|
|
+ *
|
|
|
|
+ * Returns true if the lock is held.
|
|
|
|
+ * Returns false if the lock is released and compaction should abort
|
|
|
|
+ */
|
|
|
|
+static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags,
|
|
|
|
+ bool locked, struct compact_control *cc)
|
|
|
|
+{
|
|
|
|
+ if (need_resched() || spin_is_contended(lock)) {
|
|
|
|
+ if (locked) {
|
|
|
|
+ spin_unlock_irqrestore(lock, *flags);
|
|
|
|
+ locked = false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* async aborts if taking too long or contended */
|
|
|
|
+ if (!cc->sync) {
|
|
|
|
+ if (cc->contended)
|
|
|
|
+ *cc->contended = true;
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cond_resched();
|
|
|
|
+ if (fatal_signal_pending(current))
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!locked)
|
|
|
|
+ spin_lock_irqsave(lock, *flags);
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline bool compact_trylock_irqsave(spinlock_t *lock,
|
|
|
|
+ unsigned long *flags, struct compact_control *cc)
|
|
|
|
+{
|
|
|
|
+ return compact_checklock_irqsave(lock, flags, false, cc);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Isolate free pages onto a private freelist. Caller must hold zone->lock.
|
|
* 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
|
|
* If @strict is true, will abort returning 0 on any invalid PFNs or non-free
|
|
@@ -173,7 +214,7 @@ isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn)
|
|
}
|
|
}
|
|
|
|
|
|
/* Update the number of anon and file isolated pages in the zone */
|
|
/* Update the number of anon and file isolated pages in the zone */
|
|
-static void acct_isolated(struct zone *zone, struct compact_control *cc)
|
|
|
|
|
|
+static void acct_isolated(struct zone *zone, bool locked, struct compact_control *cc)
|
|
{
|
|
{
|
|
struct page *page;
|
|
struct page *page;
|
|
unsigned int count[2] = { 0, };
|
|
unsigned int count[2] = { 0, };
|
|
@@ -181,8 +222,14 @@ static void acct_isolated(struct zone *zone, struct compact_control *cc)
|
|
list_for_each_entry(page, &cc->migratepages, lru)
|
|
list_for_each_entry(page, &cc->migratepages, lru)
|
|
count[!!page_is_file_cache(page)]++;
|
|
count[!!page_is_file_cache(page)]++;
|
|
|
|
|
|
- __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
|
|
|
|
- __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
|
|
|
|
|
|
+ /* If locked we can use the interrupt unsafe versions */
|
|
|
|
+ if (locked) {
|
|
|
|
+ __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
|
|
|
|
+ __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
|
|
|
|
+ } else {
|
|
|
|
+ mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
|
|
|
|
+ mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/* Similar to reclaim, but different enough that they don't share logic */
|
|
/* Similar to reclaim, but different enough that they don't share logic */
|
|
@@ -228,6 +275,8 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
|
|
struct list_head *migratelist = &cc->migratepages;
|
|
struct list_head *migratelist = &cc->migratepages;
|
|
isolate_mode_t mode = 0;
|
|
isolate_mode_t mode = 0;
|
|
struct lruvec *lruvec;
|
|
struct lruvec *lruvec;
|
|
|
|
+ unsigned long flags;
|
|
|
|
+ bool locked;
|
|
|
|
|
|
/*
|
|
/*
|
|
* Ensure that there are not too many pages isolated from the LRU
|
|
* Ensure that there are not too many pages isolated from the LRU
|
|
@@ -247,25 +296,22 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
|
|
|
|
|
|
/* Time to isolate some pages for migration */
|
|
/* Time to isolate some pages for migration */
|
|
cond_resched();
|
|
cond_resched();
|
|
- spin_lock_irq(&zone->lru_lock);
|
|
|
|
|
|
+ spin_lock_irqsave(&zone->lru_lock, flags);
|
|
|
|
+ locked = true;
|
|
for (; low_pfn < end_pfn; low_pfn++) {
|
|
for (; low_pfn < end_pfn; low_pfn++) {
|
|
struct page *page;
|
|
struct page *page;
|
|
- bool locked = true;
|
|
|
|
|
|
|
|
/* give a chance to irqs before checking need_resched() */
|
|
/* give a chance to irqs before checking need_resched() */
|
|
if (!((low_pfn+1) % SWAP_CLUSTER_MAX)) {
|
|
if (!((low_pfn+1) % SWAP_CLUSTER_MAX)) {
|
|
- spin_unlock_irq(&zone->lru_lock);
|
|
|
|
|
|
+ spin_unlock_irqrestore(&zone->lru_lock, flags);
|
|
locked = false;
|
|
locked = false;
|
|
}
|
|
}
|
|
- if (need_resched() || spin_is_contended(&zone->lru_lock)) {
|
|
|
|
- if (locked)
|
|
|
|
- spin_unlock_irq(&zone->lru_lock);
|
|
|
|
- cond_resched();
|
|
|
|
- spin_lock_irq(&zone->lru_lock);
|
|
|
|
- if (fatal_signal_pending(current))
|
|
|
|
- break;
|
|
|
|
- } else if (!locked)
|
|
|
|
- spin_lock_irq(&zone->lru_lock);
|
|
|
|
|
|
+
|
|
|
|
+ /* Check if it is ok to still hold the lock */
|
|
|
|
+ locked = compact_checklock_irqsave(&zone->lru_lock, &flags,
|
|
|
|
+ locked, cc);
|
|
|
|
+ if (!locked)
|
|
|
|
+ break;
|
|
|
|
|
|
/*
|
|
/*
|
|
* migrate_pfn does not necessarily start aligned to a
|
|
* migrate_pfn does not necessarily start aligned to a
|
|
@@ -349,9 +395,10 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- acct_isolated(zone, cc);
|
|
|
|
|
|
+ acct_isolated(zone, locked, cc);
|
|
|
|
|
|
- spin_unlock_irq(&zone->lru_lock);
|
|
|
|
|
|
+ if (locked)
|
|
|
|
+ spin_unlock_irqrestore(&zone->lru_lock, flags);
|
|
|
|
|
|
trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated);
|
|
trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated);
|
|
|
|
|
|
@@ -461,7 +508,16 @@ static void isolate_freepages(struct zone *zone,
|
|
* are disabled
|
|
* are disabled
|
|
*/
|
|
*/
|
|
isolated = 0;
|
|
isolated = 0;
|
|
- spin_lock_irqsave(&zone->lock, flags);
|
|
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * The zone lock must be held to isolate freepages. This
|
|
|
|
+ * unfortunately this is a very coarse lock and can be
|
|
|
|
+ * heavily contended if there are parallel allocations
|
|
|
|
+ * or parallel compactions. For async compaction do not
|
|
|
|
+ * spin on the lock
|
|
|
|
+ */
|
|
|
|
+ if (!compact_trylock_irqsave(&zone->lock, &flags, cc))
|
|
|
|
+ break;
|
|
if (suitable_migration_target(page)) {
|
|
if (suitable_migration_target(page)) {
|
|
end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn);
|
|
end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn);
|
|
isolated = isolate_freepages_block(pfn, end_pfn,
|
|
isolated = isolate_freepages_block(pfn, end_pfn,
|
|
@@ -773,7 +829,7 @@ out:
|
|
|
|
|
|
static unsigned long compact_zone_order(struct zone *zone,
|
|
static unsigned long compact_zone_order(struct zone *zone,
|
|
int order, gfp_t gfp_mask,
|
|
int order, gfp_t gfp_mask,
|
|
- bool sync)
|
|
|
|
|
|
+ bool sync, bool *contended)
|
|
{
|
|
{
|
|
struct compact_control cc = {
|
|
struct compact_control cc = {
|
|
.nr_freepages = 0,
|
|
.nr_freepages = 0,
|
|
@@ -782,6 +838,7 @@ static unsigned long compact_zone_order(struct zone *zone,
|
|
.migratetype = allocflags_to_migratetype(gfp_mask),
|
|
.migratetype = allocflags_to_migratetype(gfp_mask),
|
|
.zone = zone,
|
|
.zone = zone,
|
|
.sync = sync,
|
|
.sync = sync,
|
|
|
|
+ .contended = contended,
|
|
};
|
|
};
|
|
INIT_LIST_HEAD(&cc.freepages);
|
|
INIT_LIST_HEAD(&cc.freepages);
|
|
INIT_LIST_HEAD(&cc.migratepages);
|
|
INIT_LIST_HEAD(&cc.migratepages);
|
|
@@ -803,7 +860,7 @@ int sysctl_extfrag_threshold = 500;
|
|
*/
|
|
*/
|
|
unsigned long try_to_compact_pages(struct zonelist *zonelist,
|
|
unsigned long try_to_compact_pages(struct zonelist *zonelist,
|
|
int order, gfp_t gfp_mask, nodemask_t *nodemask,
|
|
int order, gfp_t gfp_mask, nodemask_t *nodemask,
|
|
- bool sync)
|
|
|
|
|
|
+ bool sync, bool *contended)
|
|
{
|
|
{
|
|
enum zone_type high_zoneidx = gfp_zone(gfp_mask);
|
|
enum zone_type high_zoneidx = gfp_zone(gfp_mask);
|
|
int may_enter_fs = gfp_mask & __GFP_FS;
|
|
int may_enter_fs = gfp_mask & __GFP_FS;
|
|
@@ -827,7 +884,8 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,
|
|
nodemask) {
|
|
nodemask) {
|
|
int status;
|
|
int status;
|
|
|
|
|
|
- status = compact_zone_order(zone, order, gfp_mask, sync);
|
|
|
|
|
|
+ status = compact_zone_order(zone, order, gfp_mask, sync,
|
|
|
|
+ contended);
|
|
rc = max(status, rc);
|
|
rc = max(status, rc);
|
|
|
|
|
|
/* If a normal allocation would succeed, stop compacting */
|
|
/* If a normal allocation would succeed, stop compacting */
|