forked from mirrors/linux
		
	xen/balloon: set a mapping for ballooned out pages
Currently ballooned out pages are mapped to 0 and have INVALID_P2M_ENTRY in the p2m. These ballooned out pages are used to map foreign grants by gntdev and blkback (see alloc_xenballooned_pages). Allocate a page per cpu and map all the ballooned out pages to the corresponding mfn. Set the p2m accordingly. This way reading from a ballooned out page won't cause a kernel crash (see http://lists.xen.org/archives/html/xen-devel/2012-12/msg01154.html). Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> Reviewed-by: David Vrabel <david.vrabel@citrix.com> CC: alex@alex.org.uk CC: dcrisan@flexiant.com Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
This commit is contained in:
		
							parent
							
								
									73cc4bb0c7
								
							
						
					
					
						commit
						cd9151e26d
					
				
					 2 changed files with 69 additions and 3 deletions
				
			
		|  | @ -38,6 +38,7 @@ | ||||||
| 
 | 
 | ||||||
| #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt | #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt | ||||||
| 
 | 
 | ||||||
|  | #include <linux/cpu.h> | ||||||
| #include <linux/kernel.h> | #include <linux/kernel.h> | ||||||
| #include <linux/sched.h> | #include <linux/sched.h> | ||||||
| #include <linux/errno.h> | #include <linux/errno.h> | ||||||
|  | @ -52,6 +53,7 @@ | ||||||
| #include <linux/notifier.h> | #include <linux/notifier.h> | ||||||
| #include <linux/memory.h> | #include <linux/memory.h> | ||||||
| #include <linux/memory_hotplug.h> | #include <linux/memory_hotplug.h> | ||||||
|  | #include <linux/percpu-defs.h> | ||||||
| 
 | 
 | ||||||
| #include <asm/page.h> | #include <asm/page.h> | ||||||
| #include <asm/pgalloc.h> | #include <asm/pgalloc.h> | ||||||
|  | @ -90,6 +92,8 @@ EXPORT_SYMBOL_GPL(balloon_stats); | ||||||
| 
 | 
 | ||||||
| /* We increase/decrease in batches which fit in a page */ | /* We increase/decrease in batches which fit in a page */ | ||||||
| static xen_pfn_t frame_list[PAGE_SIZE / sizeof(unsigned long)]; | static xen_pfn_t frame_list[PAGE_SIZE / sizeof(unsigned long)]; | ||||||
|  | static DEFINE_PER_CPU(struct page *, balloon_scratch_page); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| /* List of ballooned pages, threaded through the mem_map array. */ | /* List of ballooned pages, threaded through the mem_map array. */ | ||||||
| static LIST_HEAD(ballooned_pages); | static LIST_HEAD(ballooned_pages); | ||||||
|  | @ -412,7 +416,8 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp) | ||||||
| 		if (xen_pv_domain() && !PageHighMem(page)) { | 		if (xen_pv_domain() && !PageHighMem(page)) { | ||||||
| 			ret = HYPERVISOR_update_va_mapping( | 			ret = HYPERVISOR_update_va_mapping( | ||||||
| 				(unsigned long)__va(pfn << PAGE_SHIFT), | 				(unsigned long)__va(pfn << PAGE_SHIFT), | ||||||
| 				__pte_ma(0), 0); | 				pfn_pte(page_to_pfn(__get_cpu_var(balloon_scratch_page)), | ||||||
|  | 					PAGE_KERNEL_RO), 0); | ||||||
| 			BUG_ON(ret); | 			BUG_ON(ret); | ||||||
| 		} | 		} | ||||||
| #endif | #endif | ||||||
|  | @ -425,7 +430,8 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp) | ||||||
| 	/* No more mappings: invalidate P2M and add to balloon. */ | 	/* No more mappings: invalidate P2M and add to balloon. */ | ||||||
| 	for (i = 0; i < nr_pages; i++) { | 	for (i = 0; i < nr_pages; i++) { | ||||||
| 		pfn = mfn_to_pfn(frame_list[i]); | 		pfn = mfn_to_pfn(frame_list[i]); | ||||||
| 		__set_phys_to_machine(pfn, INVALID_P2M_ENTRY); | 		__set_phys_to_machine(pfn, | ||||||
|  | 				pfn_to_mfn(page_to_pfn(__get_cpu_var(balloon_scratch_page)))); | ||||||
| 		balloon_append(pfn_to_page(pfn)); | 		balloon_append(pfn_to_page(pfn)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -480,6 +486,18 @@ static void balloon_process(struct work_struct *work) | ||||||
| 	mutex_unlock(&balloon_mutex); | 	mutex_unlock(&balloon_mutex); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | struct page *get_balloon_scratch_page(void) | ||||||
|  | { | ||||||
|  | 	struct page *ret = get_cpu_var(balloon_scratch_page); | ||||||
|  | 	BUG_ON(ret == NULL); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void put_balloon_scratch_page(void) | ||||||
|  | { | ||||||
|  | 	put_cpu_var(balloon_scratch_page); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* Resets the Xen limit, sets new target, and kicks off processing. */ | /* Resets the Xen limit, sets new target, and kicks off processing. */ | ||||||
| void balloon_set_new_target(unsigned long target) | void balloon_set_new_target(unsigned long target) | ||||||
| { | { | ||||||
|  | @ -573,13 +591,47 @@ static void __init balloon_add_region(unsigned long start_pfn, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int __cpuinit 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); | ||||||
|  | 			return NOTIFY_BAD; | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	return NOTIFY_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct notifier_block balloon_cpu_notifier __cpuinitdata = { | ||||||
|  | 	.notifier_call	= balloon_cpu_notify, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| static int __init balloon_init(void) | static int __init balloon_init(void) | ||||||
| { | { | ||||||
| 	int i; | 	int i, cpu; | ||||||
| 
 | 
 | ||||||
| 	if (!xen_domain()) | 	if (!xen_domain()) | ||||||
| 		return -ENODEV; | 		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; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	register_cpu_notifier(&balloon_cpu_notifier); | ||||||
|  | 
 | ||||||
| 	pr_info("Initialising balloon driver\n"); | 	pr_info("Initialising balloon driver\n"); | ||||||
| 
 | 
 | ||||||
| 	balloon_stats.current_pages = xen_pv_domain() | 	balloon_stats.current_pages = xen_pv_domain() | ||||||
|  | @ -616,4 +668,15 @@ static int __init balloon_init(void) | ||||||
| 
 | 
 | ||||||
| subsys_initcall(balloon_init); | subsys_initcall(balloon_init); | ||||||
| 
 | 
 | ||||||
|  | static int __init balloon_clear(void) | ||||||
|  | { | ||||||
|  | 	int cpu; | ||||||
|  | 
 | ||||||
|  | 	for_each_possible_cpu(cpu) | ||||||
|  | 		per_cpu(balloon_scratch_page, cpu) = NULL; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | early_initcall(balloon_clear); | ||||||
|  | 
 | ||||||
| MODULE_LICENSE("GPL"); | MODULE_LICENSE("GPL"); | ||||||
|  |  | ||||||
|  | @ -29,6 +29,9 @@ int alloc_xenballooned_pages(int nr_pages, struct page **pages, | ||||||
| 		bool highmem); | 		bool highmem); | ||||||
| void free_xenballooned_pages(int nr_pages, struct page **pages); | void free_xenballooned_pages(int nr_pages, struct page **pages); | ||||||
| 
 | 
 | ||||||
|  | struct page *get_balloon_scratch_page(void); | ||||||
|  | void put_balloon_scratch_page(void); | ||||||
|  | 
 | ||||||
| struct device; | struct device; | ||||||
| #ifdef CONFIG_XEN_SELFBALLOONING | #ifdef CONFIG_XEN_SELFBALLOONING | ||||||
| extern int register_xen_selfballooning(struct device *dev); | extern int register_xen_selfballooning(struct device *dev); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Stefano Stabellini
						Stefano Stabellini