diff options
Diffstat (limited to 'drivers/xen/balloon.c')
| -rw-r--r-- | drivers/xen/balloon.c | 130 | 
1 files changed, 75 insertions, 55 deletions
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index b232908a619..5c660c77f03 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -157,13 +157,6 @@ static struct page *balloon_retrieve(bool prefer_highmem)  	return page;  } -static struct page *balloon_first_page(void) -{ -	if (list_empty(&ballooned_pages)) -		return NULL; -	return list_entry(ballooned_pages.next, struct page, lru); -} -  static struct page *balloon_next_page(struct page *page)  {  	struct list_head *next = page->lru.next; @@ -328,7 +321,7 @@ static enum bp_state increase_reservation(unsigned long nr_pages)  	if (nr_pages > ARRAY_SIZE(frame_list))  		nr_pages = ARRAY_SIZE(frame_list); -	page = balloon_first_page(); +	page = list_first_entry_or_null(&ballooned_pages, struct page, lru);  	for (i = 0; i < nr_pages; i++) {  		if (!page) {  			nr_pages = i; @@ -350,17 +343,19 @@ static enum bp_state increase_reservation(unsigned long nr_pages)  		pfn = page_to_pfn(page); -		set_phys_to_machine(pfn, frame_list[i]); -  #ifdef CONFIG_XEN_HAVE_PVMMU -		/* Link back into the page tables if not highmem. */ -		if (xen_pv_domain() && !PageHighMem(page)) { -			int ret; -			ret = HYPERVISOR_update_va_mapping( -				(unsigned long)__va(pfn << PAGE_SHIFT), -				mfn_pte(frame_list[i], PAGE_KERNEL), -				0); -			BUG_ON(ret); +		if (!xen_feature(XENFEAT_auto_translated_physmap)) { +			set_phys_to_machine(pfn, frame_list[i]); + +			/* Link back into the page tables if not highmem. */ +			if (!PageHighMem(page)) { +				int ret; +				ret = HYPERVISOR_update_va_mapping( +						(unsigned long)__va(pfn << PAGE_SHIFT), +						mfn_pte(frame_list[i], PAGE_KERNEL), +						0); +				BUG_ON(ret); +			}  		}  #endif @@ -378,7 +373,6 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp)  	enum bp_state state = BP_DONE;  	unsigned long  pfn, i;  	struct page   *page; -	struct page   *scratch_page;  	int ret;  	struct xen_memory_reservation reservation = {  		.address_bits = 0, @@ -405,39 +399,51 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp)  			state = BP_EAGAIN;  			break;  		} +		scrub_page(page); -		pfn = page_to_pfn(page); -		frame_list[i] = pfn_to_mfn(pfn); +		frame_list[i] = page_to_pfn(page); +	} -		scrub_page(page); +	/* +	 * Ensure that ballooned highmem pages don't have kmaps. +	 * +	 * Do this before changing the p2m as kmap_flush_unused() +	 * reads PTEs to obtain pages (and hence needs the original +	 * p2m entry). +	 */ +	kmap_flush_unused(); +	/* Update direct mapping, invalidate P2M, and add to balloon. */ +	for (i = 0; i < nr_pages; i++) { +		pfn = frame_list[i]; +		frame_list[i] = pfn_to_mfn(pfn); +		page = pfn_to_page(pfn); + +#ifdef CONFIG_XEN_HAVE_PVMMU  		/*  		 * Ballooned out frames are effectively replaced with  		 * a scratch frame.  Ensure direct mappings and the  		 * p2m are consistent.  		 */ -		scratch_page = get_balloon_scratch_page(); -#ifdef CONFIG_XEN_HAVE_PVMMU -		if (xen_pv_domain() && !PageHighMem(page)) { -			ret = HYPERVISOR_update_va_mapping( -				(unsigned long)__va(pfn << PAGE_SHIFT), -				pfn_pte(page_to_pfn(scratch_page), -					PAGE_KERNEL_RO), 0); -			BUG_ON(ret); -		} -#endif  		if (!xen_feature(XENFEAT_auto_translated_physmap)) { -			unsigned long p; -			p = page_to_pfn(scratch_page); -			__set_phys_to_machine(pfn, pfn_to_mfn(p)); +			if (!PageHighMem(page)) { +				struct page *scratch_page = get_balloon_scratch_page(); + +				ret = HYPERVISOR_update_va_mapping( +						(unsigned long)__va(pfn << PAGE_SHIFT), +						pfn_pte(page_to_pfn(scratch_page), +							PAGE_KERNEL_RO), 0); +				BUG_ON(ret); + +				put_balloon_scratch_page(); +			} +			__set_phys_to_machine(pfn, INVALID_P2M_ENTRY);  		} -		put_balloon_scratch_page(); +#endif -		balloon_append(pfn_to_page(pfn)); +		balloon_append(page);  	} -	/* Ensure that ballooned highmem pages don't have kmaps. */ -	kmap_flush_unused();  	flush_tlb_all();  	set_xen_guest_handle(reservation.extent_start, frame_list); @@ -596,19 +602,29 @@ static void __init balloon_add_region(unsigned long start_pfn,  	}  } -static int __cpuinit balloon_cpu_notify(struct notifier_block *self, +static int alloc_balloon_scratch_page(int cpu) +{ +	if (per_cpu(balloon_scratch_page, cpu) != NULL) +		return 0; + +	per_cpu(balloon_scratch_page, cpu) = alloc_page(GFP_KERNEL); +	if (per_cpu(balloon_scratch_page, cpu) == NULL) { +		pr_warn("Failed to allocate balloon_scratch_page for cpu %d\n", cpu); +		return -ENOMEM; +	} + +	return 0; +} + + +static int balloon_cpu_notify(struct notifier_block *self,  				    unsigned long action, void *hcpu)  {  	int cpu = (long)hcpu;  	switch (action) {  	case CPU_UP_PREPARE: -		if (per_cpu(balloon_scratch_page, cpu) != NULL) -			break; -		per_cpu(balloon_scratch_page, cpu) = alloc_page(GFP_KERNEL); -		if (per_cpu(balloon_scratch_page, cpu) == NULL) { -			pr_warn("Failed to allocate balloon_scratch_page for cpu %d\n", cpu); +		if (alloc_balloon_scratch_page(cpu))  			return NOTIFY_BAD; -		}  		break;  	default:  		break; @@ -616,7 +632,7 @@ static int __cpuinit balloon_cpu_notify(struct notifier_block *self,  	return NOTIFY_OK;  } -static struct notifier_block balloon_cpu_notifier __cpuinitdata = { +static struct notifier_block balloon_cpu_notifier = {  	.notifier_call	= balloon_cpu_notify,  }; @@ -627,21 +643,25 @@ static int __init balloon_init(void)  	if (!xen_domain())  		return -ENODEV; -	for_each_online_cpu(cpu) -	{ -		per_cpu(balloon_scratch_page, cpu) = alloc_page(GFP_KERNEL); -		if (per_cpu(balloon_scratch_page, cpu) == NULL) { -			pr_warn("Failed to allocate balloon_scratch_page for cpu %d\n", cpu); -			return -ENOMEM; +	if (!xen_feature(XENFEAT_auto_translated_physmap)) { +		register_cpu_notifier(&balloon_cpu_notifier); + +		get_online_cpus(); +		for_each_online_cpu(cpu) { +			if (alloc_balloon_scratch_page(cpu)) { +				put_online_cpus(); +				unregister_cpu_notifier(&balloon_cpu_notifier); +				return -ENOMEM; +			}  		} +		put_online_cpus();  	} -	register_cpu_notifier(&balloon_cpu_notifier);  	pr_info("Initialising balloon driver\n");  	balloon_stats.current_pages = xen_pv_domain()  		? min(xen_start_info->nr_pages - xen_released_pages, max_pfn) -		: max_pfn; +		: get_num_physpages();  	balloon_stats.target_pages  = balloon_stats.current_pages;  	balloon_stats.balloon_low   = 0;  	balloon_stats.balloon_high  = 0;  | 
