diff options
Diffstat (limited to 'mm/compaction.c')
| -rw-r--r-- | mm/compaction.c | 139 | 
1 files changed, 93 insertions, 46 deletions
diff --git a/mm/compaction.c b/mm/compaction.c index f9792ba3537c..546e571e9d60 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -41,15 +41,17 @@ static inline void count_compact_events(enum vm_event_item item, long delta)  static unsigned long release_freepages(struct list_head *freelist)  {  	struct page *page, *next; -	unsigned long count = 0; +	unsigned long high_pfn = 0;  	list_for_each_entry_safe(page, next, freelist, lru) { +		unsigned long pfn = page_to_pfn(page);  		list_del(&page->lru);  		__free_page(page); -		count++; +		if (pfn > high_pfn) +			high_pfn = pfn;  	} -	return count; +	return high_pfn;  }  static void map_pages(struct list_head *list) @@ -195,16 +197,12 @@ static void update_pageblock_skip(struct compact_control *cc,  	/* Update where async and sync compaction should restart */  	if (migrate_scanner) { -		if (cc->finished_update_migrate) -			return;  		if (pfn > zone->compact_cached_migrate_pfn[0])  			zone->compact_cached_migrate_pfn[0] = pfn;  		if (cc->mode != MIGRATE_ASYNC &&  		    pfn > zone->compact_cached_migrate_pfn[1])  			zone->compact_cached_migrate_pfn[1] = pfn;  	} else { -		if (cc->finished_update_free) -			return;  		if (pfn < zone->compact_cached_free_pfn)  			zone->compact_cached_free_pfn = pfn;  	} @@ -715,7 +713,6 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,  		del_page_from_lru_list(page, lruvec, page_lru(page));  isolate_success: -		cc->finished_update_migrate = true;  		list_add(&page->lru, migratelist);  		cc->nr_migratepages++;  		nr_isolated++; @@ -889,15 +886,6 @@ static void isolate_freepages(struct compact_control *cc)  				block_start_pfn - pageblock_nr_pages;  		/* -		 * Set a flag that we successfully isolated in this pageblock. -		 * In the next loop iteration, zone->compact_cached_free_pfn -		 * will not be updated and thus it will effectively contain the -		 * highest pageblock we isolated pages from. -		 */ -		if (isolated) -			cc->finished_update_free = true; - -		/*  		 * isolate_freepages_block() might have aborted due to async  		 * compaction being contended  		 */ @@ -1086,9 +1074,9 @@ static int compact_finished(struct zone *zone, struct compact_control *cc,  	/* Compaction run is not finished if the watermark is not met */  	watermark = low_wmark_pages(zone); -	watermark += (1 << cc->order); -	if (!zone_watermark_ok(zone, cc->order, watermark, 0, 0)) +	if (!zone_watermark_ok(zone, cc->order, watermark, cc->classzone_idx, +							cc->alloc_flags))  		return COMPACT_CONTINUE;  	/* Direct compactor: Is a suitable page free? */ @@ -1114,7 +1102,8 @@ 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) +unsigned long compaction_suitable(struct zone *zone, int order, +					int alloc_flags, int classzone_idx)  {  	int fragindex;  	unsigned long watermark; @@ -1126,21 +1115,30 @@ unsigned long compaction_suitable(struct zone *zone, int order)  	if (order == -1)  		return COMPACT_CONTINUE; +	watermark = low_wmark_pages(zone); +	/* +	 * If watermarks for high-order allocation are already met, there +	 * should be no need for compaction at all. +	 */ +	if (zone_watermark_ok(zone, order, watermark, classzone_idx, +								alloc_flags)) +		return COMPACT_PARTIAL; +  	/*  	 * Watermarks for order-0 must be met for compaction. Note the 2UL.  	 * This is because during migration, copies of pages need to be  	 * allocated and for a short time, the footprint is higher  	 */ -	watermark = low_wmark_pages(zone) + (2UL << order); -	if (!zone_watermark_ok(zone, 0, watermark, 0, 0)) +	watermark += (2UL << order); +	if (!zone_watermark_ok(zone, 0, watermark, classzone_idx, alloc_flags))  		return COMPACT_SKIPPED;  	/*  	 * fragmentation index determines if allocation failures are due to  	 * low memory or external fragmentation  	 * -	 * index of -1000 implies allocations might succeed depending on -	 * watermarks +	 * index of -1000 would imply allocations might succeed depending on +	 * watermarks, but we already failed the high-order watermark check  	 * index towards 0 implies failure is due to lack of memory  	 * index towards 1000 implies failure is due to fragmentation  	 * @@ -1150,10 +1148,6 @@ unsigned long compaction_suitable(struct zone *zone, int order)  	if (fragindex >= 0 && fragindex <= sysctl_extfrag_threshold)  		return COMPACT_SKIPPED; -	if (fragindex == -1000 && zone_watermark_ok(zone, order, watermark, -	    0, 0)) -		return COMPACT_PARTIAL; -  	return COMPACT_CONTINUE;  } @@ -1164,8 +1158,10 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)  	unsigned long end_pfn = zone_end_pfn(zone);  	const int migratetype = gfpflags_to_migratetype(cc->gfp_mask);  	const bool sync = cc->mode != MIGRATE_ASYNC; +	unsigned long last_migrated_pfn = 0; -	ret = compaction_suitable(zone, cc->order); +	ret = compaction_suitable(zone, cc->order, cc->alloc_flags, +							cc->classzone_idx);  	switch (ret) {  	case COMPACT_PARTIAL:  	case COMPACT_SKIPPED: @@ -1208,6 +1204,7 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)  	while ((ret = compact_finished(zone, cc, migratetype)) ==  						COMPACT_CONTINUE) {  		int err; +		unsigned long isolate_start_pfn = cc->migrate_pfn;  		switch (isolate_migratepages(zone, cc)) {  		case ISOLATE_ABORT: @@ -1216,7 +1213,12 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)  			cc->nr_migratepages = 0;  			goto out;  		case ISOLATE_NONE: -			continue; +			/* +			 * We haven't isolated and migrated anything, but +			 * there might still be unflushed migrations from +			 * previous cc->order aligned block. +			 */ +			goto check_drain;  		case ISOLATE_SUCCESS:  			;  		} @@ -1241,12 +1243,61 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)  				goto out;  			}  		} + +		/* +		 * Record where we could have freed pages by migration and not +		 * yet flushed them to buddy allocator. We use the pfn that +		 * isolate_migratepages() started from in this loop iteration +		 * - this is the lowest page that could have been isolated and +		 * then freed by migration. +		 */ +		if (!last_migrated_pfn) +			last_migrated_pfn = isolate_start_pfn; + +check_drain: +		/* +		 * Has the migration scanner moved away from the previous +		 * cc->order aligned block where we migrated from? If yes, +		 * flush the pages that were freed, so that they can merge and +		 * compact_finished() can detect immediately if allocation +		 * would succeed. +		 */ +		if (cc->order > 0 && last_migrated_pfn) { +			int cpu; +			unsigned long current_block_start = +				cc->migrate_pfn & ~((1UL << cc->order) - 1); + +			if (last_migrated_pfn < current_block_start) { +				cpu = get_cpu(); +				lru_add_drain_cpu(cpu); +				drain_local_pages(zone); +				put_cpu(); +				/* No more flushing until we migrate again */ +				last_migrated_pfn = 0; +			} +		} +  	}  out: -	/* Release free pages and check accounting */ -	cc->nr_freepages -= release_freepages(&cc->freepages); -	VM_BUG_ON(cc->nr_freepages != 0); +	/* +	 * Release free pages and update where the free scanner should restart, +	 * so we don't leave any returned pages behind in the next attempt. +	 */ +	if (cc->nr_freepages > 0) { +		unsigned long free_pfn = release_freepages(&cc->freepages); + +		cc->nr_freepages = 0; +		VM_BUG_ON(free_pfn == 0); +		/* The cached pfn is always the first in a pageblock */ +		free_pfn &= ~(pageblock_nr_pages-1); +		/* +		 * Only go back, not forward. The cached pfn might have been +		 * already reset to zone end in compact_finished() +		 */ +		if (free_pfn > zone->compact_cached_free_pfn) +			zone->compact_cached_free_pfn = free_pfn; +	}  	trace_mm_compaction_end(ret); @@ -1254,7 +1305,8 @@ out:  }  static unsigned long compact_zone_order(struct zone *zone, int order, -		gfp_t gfp_mask, enum migrate_mode mode, int *contended) +		gfp_t gfp_mask, enum migrate_mode mode, int *contended, +		int alloc_flags, int classzone_idx)  {  	unsigned long ret;  	struct compact_control cc = { @@ -1264,6 +1316,8 @@ static unsigned long compact_zone_order(struct zone *zone, int order,  		.gfp_mask = gfp_mask,  		.zone = zone,  		.mode = mode, +		.alloc_flags = alloc_flags, +		.classzone_idx = classzone_idx,  	};  	INIT_LIST_HEAD(&cc.freepages);  	INIT_LIST_HEAD(&cc.migratepages); @@ -1288,14 +1342,13 @@ int sysctl_extfrag_threshold = 500;   * @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 - * @candidate_zone: Return the zone where we think allocation should succeed   *   * 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, -			struct zone **candidate_zone) +			int alloc_flags, int classzone_idx)  {  	enum zone_type high_zoneidx = gfp_zone(gfp_mask);  	int may_enter_fs = gfp_mask & __GFP_FS; @@ -1303,7 +1356,6 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,  	struct zoneref *z;  	struct zone *zone;  	int rc = COMPACT_DEFERRED; -	int alloc_flags = 0;  	int all_zones_contended = COMPACT_CONTENDED_LOCK; /* init for &= op */  	*contended = COMPACT_CONTENDED_NONE; @@ -1312,10 +1364,6 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,  	if (!order || !may_enter_fs || !may_perform_io)  		return COMPACT_SKIPPED; -#ifdef CONFIG_CMA -	if (gfpflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE) -		alloc_flags |= ALLOC_CMA; -#endif  	/* Compact each zone in the list */  	for_each_zone_zonelist_nodemask(zone, z, zonelist, high_zoneidx,  								nodemask) { @@ -1326,7 +1374,7 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,  			continue;  		status = compact_zone_order(zone, order, gfp_mask, mode, -							&zone_contended); +				&zone_contended, alloc_flags, classzone_idx);  		rc = max(status, rc);  		/*  		 * It takes at least one zone that wasn't lock contended @@ -1335,9 +1383,8 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,  		all_zones_contended &= zone_contended;  		/* If a normal allocation would succeed, stop compacting */ -		if (zone_watermark_ok(zone, order, low_wmark_pages(zone), 0, -				      alloc_flags)) { -			*candidate_zone = zone; +		if (zone_watermark_ok(zone, order, low_wmark_pages(zone), +					classzone_idx, alloc_flags)) {  			/*  			 * We think the allocation will succeed in this zone,  			 * but it is not certain, hence the false. The caller @@ -1359,7 +1406,7 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,  			goto break_loop;  		} -		if (mode != MIGRATE_ASYNC) { +		if (mode != MIGRATE_ASYNC && status == COMPACT_COMPLETE) {  			/*  			 * We think that allocation won't succeed in this zone  			 * so we defer compaction there. If it ends up  | 
