diff options
Diffstat (limited to 'arch/x86/xen/setup.c')
| -rw-r--r-- | arch/x86/xen/setup.c | 171 | 
1 files changed, 144 insertions, 27 deletions
diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c index 1ba8dff2675..3ebba0753d3 100644 --- a/arch/x86/xen/setup.c +++ b/arch/x86/xen/setup.c @@ -26,7 +26,6 @@  #include <xen/interface/memory.h>  #include <xen/interface/physdev.h>  #include <xen/features.h> -  #include "xen-ops.h"  #include "vdso.h" @@ -84,8 +83,8 @@ static void __init xen_add_extra_mem(u64 start, u64 size)  		__set_phys_to_machine(pfn, INVALID_P2M_ENTRY);  } -static unsigned long __init xen_release_chunk(unsigned long start, -					      unsigned long end) +static unsigned long __init xen_do_chunk(unsigned long start, +					 unsigned long end, bool release)  {  	struct xen_memory_reservation reservation = {  		.address_bits = 0, @@ -96,30 +95,138 @@ static unsigned long __init xen_release_chunk(unsigned long start,  	unsigned long pfn;  	int ret; -	for(pfn = start; pfn < end; pfn++) { +	for (pfn = start; pfn < end; pfn++) { +		unsigned long frame;  		unsigned long mfn = pfn_to_mfn(pfn); -		/* Make sure pfn exists to start with */ -		if (mfn == INVALID_P2M_ENTRY || mfn_to_pfn(mfn) != pfn) -			continue; - -		set_xen_guest_handle(reservation.extent_start, &mfn); +		if (release) { +			/* Make sure pfn exists to start with */ +			if (mfn == INVALID_P2M_ENTRY || mfn_to_pfn(mfn) != pfn) +				continue; +			frame = mfn; +		} else { +			if (mfn != INVALID_P2M_ENTRY) +				continue; +			frame = pfn; +		} +		set_xen_guest_handle(reservation.extent_start, &frame);  		reservation.nr_extents = 1; -		ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, +		ret = HYPERVISOR_memory_op(release ? XENMEM_decrease_reservation : XENMEM_populate_physmap,  					   &reservation); -		WARN(ret != 1, "Failed to release pfn %lx err=%d\n", pfn, ret); +		WARN(ret != 1, "Failed to %s pfn %lx err=%d\n", +		     release ? "release" : "populate", pfn, ret); +  		if (ret == 1) { -			__set_phys_to_machine(pfn, INVALID_P2M_ENTRY); +			if (!early_set_phys_to_machine(pfn, release ? INVALID_P2M_ENTRY : frame)) { +				if (release) +					break; +				set_xen_guest_handle(reservation.extent_start, &frame); +				reservation.nr_extents = 1; +				ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, +							   &reservation); +				break; +			}  			len++; -		} +		} else +			break;  	} -	printk(KERN_INFO "Freeing  %lx-%lx pfn range: %lu pages freed\n", -	       start, end, len); +	if (len) +		printk(KERN_INFO "%s %lx-%lx pfn range: %lu pages %s\n", +		       release ? "Freeing" : "Populating", +		       start, end, len, +		       release ? "freed" : "added");  	return len;  } +static unsigned long __init xen_release_chunk(unsigned long start, +					      unsigned long end) +{ +	return xen_do_chunk(start, end, true); +} + +static unsigned long __init xen_populate_chunk( +	const struct e820entry *list, size_t map_size, +	unsigned long max_pfn, unsigned long *last_pfn, +	unsigned long credits_left) +{ +	const struct e820entry *entry; +	unsigned int i; +	unsigned long done = 0; +	unsigned long dest_pfn; + +	for (i = 0, entry = list; i < map_size; i++, entry++) { +		unsigned long credits = credits_left; +		unsigned long s_pfn; +		unsigned long e_pfn; +		unsigned long pfns; +		long capacity; + +		if (credits <= 0) +			break; + +		if (entry->type != E820_RAM) +			continue; + +		e_pfn = PFN_UP(entry->addr + entry->size); + +		/* We only care about E820 after the xen_start_info->nr_pages */ +		if (e_pfn <= max_pfn) +			continue; + +		s_pfn = PFN_DOWN(entry->addr); +		/* If the E820 falls within the nr_pages, we want to start +		 * at the nr_pages PFN. +		 * If that would mean going past the E820 entry, skip it +		 */ +		if (s_pfn <= max_pfn) { +			capacity = e_pfn - max_pfn; +			dest_pfn = max_pfn; +		} else { +			/* last_pfn MUST be within E820_RAM regions */ +			if (*last_pfn && e_pfn >= *last_pfn) +				s_pfn = *last_pfn; +			capacity = e_pfn - s_pfn; +			dest_pfn = s_pfn; +		} +		/* If we had filled this E820_RAM entry, go to the next one. */ +		if (capacity <= 0) +			continue; + +		if (credits > capacity) +			credits = capacity; + +		pfns = xen_do_chunk(dest_pfn, dest_pfn + credits, false); +		done += pfns; +		credits_left -= pfns; +		*last_pfn = (dest_pfn + pfns); +	} +	return done; +} + +static void __init xen_set_identity_and_release_chunk( +	unsigned long start_pfn, unsigned long end_pfn, unsigned long nr_pages, +	unsigned long *released, unsigned long *identity) +{ +	unsigned long pfn; + +	/* +	 * If the PFNs are currently mapped, the VA mapping also needs +	 * to be updated to be 1:1. +	 */ +	for (pfn = start_pfn; pfn <= max_pfn_mapped && pfn < end_pfn; pfn++) +		(void)HYPERVISOR_update_va_mapping( +			(unsigned long)__va(pfn << PAGE_SHIFT), +			mfn_pte(pfn, PAGE_KERNEL_IO), 0); + +	if (start_pfn < nr_pages) +		*released += xen_release_chunk( +			start_pfn, min(end_pfn, nr_pages)); + +	*identity += set_phys_range_identity(start_pfn, end_pfn); +} +  static unsigned long __init xen_set_identity_and_release(  	const struct e820entry *list, size_t map_size, unsigned long nr_pages)  { @@ -142,7 +249,6 @@ static unsigned long __init xen_set_identity_and_release(  	 */  	for (i = 0, entry = list; i < map_size; i++, entry++) {  		phys_addr_t end = entry->addr + entry->size; -  		if (entry->type == E820_RAM || i == map_size - 1) {  			unsigned long start_pfn = PFN_DOWN(start);  			unsigned long end_pfn = PFN_UP(end); @@ -150,20 +256,19 @@ static unsigned long __init xen_set_identity_and_release(  			if (entry->type == E820_RAM)  				end_pfn = PFN_UP(entry->addr); -			if (start_pfn < end_pfn) { -				if (start_pfn < nr_pages) -					released += xen_release_chunk( -						start_pfn, min(end_pfn, nr_pages)); +			if (start_pfn < end_pfn) +				xen_set_identity_and_release_chunk( +					start_pfn, end_pfn, nr_pages, +					&released, &identity); -				identity += set_phys_range_identity( -					start_pfn, end_pfn); -			}  			start = end;  		}  	} -	printk(KERN_INFO "Released %lu pages of unused memory\n", released); -	printk(KERN_INFO "Set %ld page(s) to 1-1 mapping\n", identity); +	if (released) +		printk(KERN_INFO "Released %lu pages of unused memory\n", released); +	if (identity) +		printk(KERN_INFO "Set %ld page(s) to 1-1 mapping\n", identity);  	return released;  } @@ -217,7 +322,9 @@ char * __init xen_memory_setup(void)  	int rc;  	struct xen_memory_map memmap;  	unsigned long max_pages; +	unsigned long last_pfn = 0;  	unsigned long extra_pages = 0; +	unsigned long populated;  	int i;  	int op; @@ -257,9 +364,20 @@ char * __init xen_memory_setup(void)  	 */  	xen_released_pages = xen_set_identity_and_release(  		map, memmap.nr_entries, max_pfn); -	extra_pages += xen_released_pages;  	/* +	 * Populate back the non-RAM pages and E820 gaps that had been +	 * released. */ +	populated = xen_populate_chunk(map, memmap.nr_entries, +			max_pfn, &last_pfn, xen_released_pages); + +	extra_pages += (xen_released_pages - populated); + +	if (last_pfn > max_pfn) { +		max_pfn = min(MAX_DOMAIN_PAGES, last_pfn); +		mem_end = PFN_PHYS(max_pfn); +	} +	/*  	 * Clamp the amount of extra memory to a EXTRA_MEM_RATIO  	 * factor the base size.  On non-highmem systems, the base  	 * size is the full initial memory allocation; on highmem it @@ -272,7 +390,6 @@ char * __init xen_memory_setup(void)  	 */  	extra_pages = min(EXTRA_MEM_RATIO * min(max_pfn, PFN_DOWN(MAXMEM)),  			  extra_pages); -  	i = 0;  	while (i < memmap.nr_entries) {  		u64 addr = map[i].addr;  |