diff options
Diffstat (limited to 'mm/compaction.c')
| -rw-r--r-- | mm/compaction.c | 102 | 
1 files changed, 60 insertions, 42 deletions
diff --git a/mm/compaction.c b/mm/compaction.c index ca1603524bbe..5a9501e0ae01 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -122,7 +122,6 @@ bool PageMovable(struct page *page)  	return false;  } -EXPORT_SYMBOL(PageMovable);  void __SetPageMovable(struct page *page, const struct movable_operations *mops)  { @@ -977,7 +976,7 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,  					locked = NULL;  				} -				if (!isolate_movable_page(page, mode)) +				if (isolate_movable_page(page, mode))  					goto isolate_success;  			} @@ -1102,12 +1101,12 @@ isolate_success_no_list:  		/*  		 * Avoid isolating too much unless this block is being -		 * rescanned (e.g. dirty/writeback pages, parallel allocation) +		 * fully scanned (e.g. dirty/writeback pages, parallel allocation)  		 * or a lock is contended. For contention, isolate quickly to  		 * potentially remove one source of contention.  		 */  		if (cc->nr_migratepages >= COMPACT_CLUSTER_MAX && -		    !cc->rescan && !cc->contended) { +		    !cc->finish_pageblock && !cc->contended) {  			++low_pfn;  			break;  		} @@ -1172,14 +1171,14 @@ isolate_abort:  	}  	/* -	 * Updated the cached scanner pfn once the pageblock has been scanned +	 * Update the cached scanner pfn once the pageblock has been scanned.  	 * Pages will either be migrated in which case there is no point  	 * scanning in the near future or migration failed in which case the  	 * failure reason may persist. The block is marked for skipping if  	 * there were no pages isolated in the block or if the block is  	 * rescanned twice in a row.  	 */ -	if (low_pfn == end_pfn && (!nr_isolated || cc->rescan)) { +	if (low_pfn == end_pfn && (!nr_isolated || cc->finish_pageblock)) {  		if (valid_page && !skip_updated)  			set_pageblock_skip(valid_page);  		update_cached_migrate(cc, low_pfn); @@ -1763,6 +1762,13 @@ static unsigned long fast_find_migrateblock(struct compact_control *cc)  		return pfn;  	/* +	 * If the pageblock should be finished then do not select a different +	 * pageblock. +	 */ +	if (cc->finish_pageblock) +		return pfn; + +	/*  	 * If the migrate_pfn is not at the start of a zone or the start  	 * of a pageblock then assume this is a continuation of a previous  	 * scan restarted due to COMPACT_CLUSTER_MAX. @@ -1839,6 +1845,7 @@ static unsigned long fast_find_migrateblock(struct compact_control *cc)  					pfn = cc->zone->zone_start_pfn;  				cc->fast_search_fail = 0;  				found_block = true; +				set_pageblock_skip(freepage);  				break;  			}  		} @@ -2026,6 +2033,8 @@ static unsigned int fragmentation_score_node(pg_data_t *pgdat)  		struct zone *zone;  		zone = &pgdat->node_zones[zoneid]; +		if (!populated_zone(zone)) +			continue;  		score += fragmentation_score_zone_weighted(zone);  	} @@ -2314,9 +2323,6 @@ compact_zone(struct compact_control *cc, struct capture_control *capc)  	if (ret == COMPACT_SUCCESS || ret == COMPACT_SKIPPED)  		return ret; -	/* huh, compaction_suitable is returning something unexpected */ -	VM_BUG_ON(ret != COMPACT_CONTINUE); -  	/*  	 * Clear pageblock skip if there were failures recently and compaction  	 * is about to be retried after being deferred. @@ -2374,19 +2380,20 @@ compact_zone(struct compact_control *cc, struct capture_control *capc)  		unsigned long iteration_start_pfn = cc->migrate_pfn;  		/* -		 * Avoid multiple rescans which can happen if a page cannot be -		 * isolated (dirty/writeback in async mode) or if the migrated -		 * pages are being allocated before the pageblock is cleared. -		 * The first rescan will capture the entire pageblock for -		 * migration. If it fails, it'll be marked skip and scanning -		 * will proceed as normal. +		 * Avoid multiple rescans of the same pageblock which can +		 * happen if a page cannot be isolated (dirty/writeback in +		 * async mode) or if the migrated pages are being allocated +		 * before the pageblock is cleared.  The first rescan will +		 * capture the entire pageblock for migration. If it fails, +		 * it'll be marked skip and scanning will proceed as normal.  		 */ -		cc->rescan = false; +		cc->finish_pageblock = false;  		if (pageblock_start_pfn(last_migrated_pfn) ==  		    pageblock_start_pfn(iteration_start_pfn)) { -			cc->rescan = true; +			cc->finish_pageblock = true;  		} +rescan:  		switch (isolate_migratepages(cc)) {  		case ISOLATE_ABORT:  			ret = COMPACT_CONTENDED; @@ -2429,18 +2436,37 @@ compact_zone(struct compact_control *cc, struct capture_control *capc)  				goto out;  			}  			/* -			 * We failed to migrate at least one page in the current -			 * order-aligned block, so skip the rest of it. +			 * If an ASYNC or SYNC_LIGHT fails to migrate a page +			 * within the current order-aligned block, scan the +			 * remainder of the pageblock. This will mark the +			 * pageblock "skip" to avoid rescanning in the near +			 * future. This will isolate more pages than necessary +			 * for the request but avoid loops due to +			 * fast_find_migrateblock revisiting blocks that were +			 * recently partially scanned.  			 */ -			if (cc->direct_compaction && -						(cc->mode == MIGRATE_ASYNC)) { -				cc->migrate_pfn = block_end_pfn( -						cc->migrate_pfn - 1, cc->order); -				/* Draining pcplists is useless in this case */ -				last_migrated_pfn = 0; +			if (cc->direct_compaction && !cc->finish_pageblock && +						(cc->mode < MIGRATE_SYNC)) { +				cc->finish_pageblock = true; + +				/* +				 * Draining pcplists does not help THP if +				 * any page failed to migrate. Even after +				 * drain, the pageblock will not be free. +				 */ +				if (cc->order == COMPACTION_HPAGE_ORDER) +					last_migrated_pfn = 0; + +				goto rescan;  			}  		} +		/* Stop if a page has been captured */ +		if (capc && capc->page) { +			ret = COMPACT_SUCCESS; +			break; +		} +  check_drain:  		/*  		 * Has the migration scanner moved away from the previous @@ -2459,12 +2485,6 @@ check_drain:  				last_migrated_pfn = 0;  			}  		} - -		/* Stop if a page has been captured */ -		if (capc && capc->page) { -			ret = COMPACT_SUCCESS; -			break; -		}  	}  out: @@ -2492,6 +2512,9 @@ out:  	trace_mm_compaction_end(cc, start_pfn, end_pfn, sync, ret); +	VM_BUG_ON(!list_empty(&cc->freepages)); +	VM_BUG_ON(!list_empty(&cc->migratepages)); +  	return ret;  } @@ -2530,9 +2553,6 @@ static enum compact_result compact_zone_order(struct zone *zone, int order,  	ret = compact_zone(&cc, &capc); -	VM_BUG_ON(!list_empty(&cc.freepages)); -	VM_BUG_ON(!list_empty(&cc.migratepages)); -  	/*  	 * Make sure we hide capture control first before we read the captured  	 * page pointer, otherwise an interrupt could free and capture a page @@ -2664,8 +2684,10 @@ static void proactive_compact_node(pg_data_t *pgdat)  		compact_zone(&cc, NULL); -		VM_BUG_ON(!list_empty(&cc.freepages)); -		VM_BUG_ON(!list_empty(&cc.migratepages)); +		count_compact_events(KCOMPACTD_MIGRATE_SCANNED, +				     cc.total_migrate_scanned); +		count_compact_events(KCOMPACTD_FREE_SCANNED, +				     cc.total_free_scanned);  	}  } @@ -2693,9 +2715,6 @@ static void compact_node(int nid)  		cc.zone = zone;  		compact_zone(&cc, NULL); - -		VM_BUG_ON(!list_empty(&cc.freepages)); -		VM_BUG_ON(!list_empty(&cc.migratepages));  	}  } @@ -2735,6 +2754,8 @@ int compaction_proactiveness_sysctl_handler(struct ctl_table *table, int write,  				continue;  			pgdat->proactive_compact_trigger = true; +			trace_mm_compaction_wakeup_kcompactd(pgdat->node_id, -1, +							     pgdat->nr_zones - 1);  			wake_up_interruptible(&pgdat->kcompactd_wait);  		}  	} @@ -2872,9 +2893,6 @@ static void kcompactd_do_work(pg_data_t *pgdat)  				     cc.total_migrate_scanned);  		count_compact_events(KCOMPACTD_FREE_SCANNED,  				     cc.total_free_scanned); - -		VM_BUG_ON(!list_empty(&cc.freepages)); -		VM_BUG_ON(!list_empty(&cc.migratepages));  	}  	/*  |