diff options
Diffstat (limited to 'mm/memory_hotplug.c')
| -rw-r--r-- | mm/memory_hotplug.c | 56 | 
1 files changed, 38 insertions, 18 deletions
| diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index e43142c15631..b8c11e063ff0 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1033,36 +1033,39 @@ static void node_states_set_node(int node, struct memory_notify *arg)  	node_set_state(node, N_MEMORY);  } -int zone_can_shift(unsigned long pfn, unsigned long nr_pages, -		   enum zone_type target) +bool zone_can_shift(unsigned long pfn, unsigned long nr_pages, +		   enum zone_type target, int *zone_shift)  {  	struct zone *zone = page_zone(pfn_to_page(pfn));  	enum zone_type idx = zone_idx(zone);  	int i; +	*zone_shift = 0; +  	if (idx < target) {  		/* pages must be at end of current zone */  		if (pfn + nr_pages != zone_end_pfn(zone)) -			return 0; +			return false;  		/* no zones in use between current zone and target */  		for (i = idx + 1; i < target; i++)  			if (zone_is_initialized(zone - idx + i)) -				return 0; +				return false;  	}  	if (target < idx) {  		/* pages must be at beginning of current zone */  		if (pfn != zone->zone_start_pfn) -			return 0; +			return false;  		/* no zones in use between current zone and target */  		for (i = target + 1; i < idx; i++)  			if (zone_is_initialized(zone - idx + i)) -				return 0; +				return false;  	} -	return target - idx; +	*zone_shift = target - idx; +	return true;  }  /* Must be protected by mem_hotplug_begin() */ @@ -1089,10 +1092,13 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ  	    !can_online_high_movable(zone))  		return -EINVAL; -	if (online_type == MMOP_ONLINE_KERNEL) -		zone_shift = zone_can_shift(pfn, nr_pages, ZONE_NORMAL); -	else if (online_type == MMOP_ONLINE_MOVABLE) -		zone_shift = zone_can_shift(pfn, nr_pages, ZONE_MOVABLE); +	if (online_type == MMOP_ONLINE_KERNEL) { +		if (!zone_can_shift(pfn, nr_pages, ZONE_NORMAL, &zone_shift)) +			return -EINVAL; +	} else if (online_type == MMOP_ONLINE_MOVABLE) { +		if (!zone_can_shift(pfn, nr_pages, ZONE_MOVABLE, &zone_shift)) +			return -EINVAL; +	}  	zone = move_pfn_range(zone_shift, pfn, pfn + nr_pages);  	if (!zone) @@ -1477,17 +1483,20 @@ bool is_mem_section_removable(unsigned long start_pfn, unsigned long nr_pages)  }  /* - * Confirm all pages in a range [start, end) is belongs to the same zone. + * Confirm all pages in a range [start, end) belong to the same zone. + * When true, return its valid [start, end).   */ -int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn) +int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn, +			 unsigned long *valid_start, unsigned long *valid_end)  {  	unsigned long pfn, sec_end_pfn; +	unsigned long start, end;  	struct zone *zone = NULL;  	struct page *page;  	int i; -	for (pfn = start_pfn, sec_end_pfn = SECTION_ALIGN_UP(start_pfn); +	for (pfn = start_pfn, sec_end_pfn = SECTION_ALIGN_UP(start_pfn + 1);  	     pfn < end_pfn; -	     pfn = sec_end_pfn + 1, sec_end_pfn += PAGES_PER_SECTION) { +	     pfn = sec_end_pfn, sec_end_pfn += PAGES_PER_SECTION) {  		/* Make sure the memory section is present first */  		if (!present_section_nr(pfn_to_section_nr(pfn)))  			continue; @@ -1503,10 +1512,20 @@ int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)  			page = pfn_to_page(pfn + i);  			if (zone && page_zone(page) != zone)  				return 0; +			if (!zone) +				start = pfn + i;  			zone = page_zone(page); +			end = pfn + MAX_ORDER_NR_PAGES;  		}  	} -	return 1; + +	if (zone) { +		*valid_start = start; +		*valid_end = end; +		return 1; +	} else { +		return 0; +	}  }  /* @@ -1833,6 +1852,7 @@ static int __ref __offline_pages(unsigned long start_pfn,  	long offlined_pages;  	int ret, drain, retry_max, node;  	unsigned long flags; +	unsigned long valid_start, valid_end;  	struct zone *zone;  	struct memory_notify arg; @@ -1843,10 +1863,10 @@ static int __ref __offline_pages(unsigned long start_pfn,  		return -EINVAL;  	/* This makes hotplug much easier...and readable.  	   we assume this for now. .*/ -	if (!test_pages_in_a_zone(start_pfn, end_pfn)) +	if (!test_pages_in_a_zone(start_pfn, end_pfn, &valid_start, &valid_end))  		return -EINVAL; -	zone = page_zone(pfn_to_page(start_pfn)); +	zone = page_zone(pfn_to_page(valid_start));  	node = zone_to_nid(zone);  	nr_pages = end_pfn - start_pfn; |