diff options
Diffstat (limited to 'arch/x86/xen/grant-table.c')
| -rw-r--r-- | arch/x86/xen/grant-table.c | 204 | 
1 files changed, 151 insertions, 53 deletions
diff --git a/arch/x86/xen/grant-table.c b/arch/x86/xen/grant-table.c index 3a5f55d5190..ebfa9b2c871 100644 --- a/arch/x86/xen/grant-table.c +++ b/arch/x86/xen/grant-table.c @@ -36,92 +36,190 @@  #include <linux/sched.h>  #include <linux/mm.h> +#include <linux/slab.h>  #include <linux/vmalloc.h>  #include <xen/interface/xen.h>  #include <xen/page.h>  #include <xen/grant_table.h> +#include <xen/xen.h>  #include <asm/pgtable.h> -static int map_pte_fn(pte_t *pte, struct page *pmd_page, -		      unsigned long addr, void *data) +static struct gnttab_vm_area { +	struct vm_struct *area; +	pte_t **ptes; +} gnttab_shared_vm_area, gnttab_status_vm_area; + +int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, +			   unsigned long max_nr_gframes, +			   void **__shared)  { -	unsigned long **frames = (unsigned long **)data; +	void *shared = *__shared; +	unsigned long addr; +	unsigned long i; + +	if (shared == NULL) +		*__shared = shared = gnttab_shared_vm_area.area->addr; + +	addr = (unsigned long)shared; + +	for (i = 0; i < nr_gframes; i++) { +		set_pte_at(&init_mm, addr, gnttab_shared_vm_area.ptes[i], +			   mfn_pte(frames[i], PAGE_KERNEL)); +		addr += PAGE_SIZE; +	} -	set_pte_at(&init_mm, addr, pte, mfn_pte((*frames)[0], PAGE_KERNEL)); -	(*frames)++;  	return 0;  } -/* - * This function is used to map shared frames to store grant status. It is - * different from map_pte_fn above, the frames type here is uint64_t. - */ -static int map_pte_fn_status(pte_t *pte, struct page *pmd_page, -			     unsigned long addr, void *data) +int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, +			   unsigned long max_nr_gframes, +			   grant_status_t **__shared)  { -	uint64_t **frames = (uint64_t **)data; +	grant_status_t *shared = *__shared; +	unsigned long addr; +	unsigned long i; + +	if (shared == NULL) +		*__shared = shared = gnttab_status_vm_area.area->addr; + +	addr = (unsigned long)shared; + +	for (i = 0; i < nr_gframes; i++) { +		set_pte_at(&init_mm, addr, gnttab_status_vm_area.ptes[i], +			   mfn_pte(frames[i], PAGE_KERNEL)); +		addr += PAGE_SIZE; +	} -	set_pte_at(&init_mm, addr, pte, mfn_pte((*frames)[0], PAGE_KERNEL)); -	(*frames)++;  	return 0;  } -static int unmap_pte_fn(pte_t *pte, struct page *pmd_page, -			unsigned long addr, void *data) +void arch_gnttab_unmap(void *shared, unsigned long nr_gframes) +{ +	pte_t **ptes; +	unsigned long addr; +	unsigned long i; + +	if (shared == gnttab_status_vm_area.area->addr) +		ptes = gnttab_status_vm_area.ptes; +	else +		ptes = gnttab_shared_vm_area.ptes; + +	addr = (unsigned long)shared; + +	for (i = 0; i < nr_gframes; i++) { +		set_pte_at(&init_mm, addr, ptes[i], __pte(0)); +		addr += PAGE_SIZE; +	} +} + +static int arch_gnttab_valloc(struct gnttab_vm_area *area, unsigned nr_frames)  { +	area->ptes = kmalloc(sizeof(pte_t *) * nr_frames, GFP_KERNEL); +	if (area->ptes == NULL) +		return -ENOMEM; + +	area->area = alloc_vm_area(PAGE_SIZE * nr_frames, area->ptes); +	if (area->area == NULL) { +		kfree(area->ptes); +		return -ENOMEM; +	} -	set_pte_at(&init_mm, addr, pte, __pte(0));  	return 0;  } -int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, -			   unsigned long max_nr_gframes, -			   void **__shared) +static void arch_gnttab_vfree(struct gnttab_vm_area *area)  { -	int rc; -	void *shared = *__shared; +	free_vm_area(area->area); +	kfree(area->ptes); +} -	if (shared == NULL) { -		struct vm_struct *area = -			alloc_vm_area(PAGE_SIZE * max_nr_gframes, NULL); -		BUG_ON(area == NULL); -		shared = area->addr; -		*__shared = shared; -	} +int arch_gnttab_init(unsigned long nr_shared, unsigned long nr_status) +{ +	int ret; + +	if (!xen_pv_domain()) +		return 0; + +	ret = arch_gnttab_valloc(&gnttab_shared_vm_area, nr_shared); +	if (ret < 0) +		return ret; + +	/* +	 * Always allocate the space for the status frames in case +	 * we're migrated to a host with V2 support. +	 */ +	ret = arch_gnttab_valloc(&gnttab_status_vm_area, nr_status); +	if (ret < 0) +		goto err; -	rc = apply_to_page_range(&init_mm, (unsigned long)shared, -				 PAGE_SIZE * nr_gframes, -				 map_pte_fn, &frames); -	return rc; +	return 0; +  err: +	arch_gnttab_vfree(&gnttab_shared_vm_area); +	return -ENOMEM;  } -int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, -			   unsigned long max_nr_gframes, -			   grant_status_t **__shared) +#ifdef CONFIG_XEN_PVH +#include <xen/balloon.h> +#include <xen/events.h> +#include <linux/slab.h> +static int __init xlated_setup_gnttab_pages(void)  { +	struct page **pages; +	xen_pfn_t *pfns;  	int rc; -	grant_status_t *shared = *__shared; - -	if (shared == NULL) { -		/* No need to pass in PTE as we are going to do it -		 * in apply_to_page_range anyhow. */ -		struct vm_struct *area = -			alloc_vm_area(PAGE_SIZE * max_nr_gframes, NULL); -		BUG_ON(area == NULL); -		shared = area->addr; -		*__shared = shared; +	unsigned int i; +	unsigned long nr_grant_frames = gnttab_max_grant_frames(); + +	BUG_ON(nr_grant_frames == 0); +	pages = kcalloc(nr_grant_frames, sizeof(pages[0]), GFP_KERNEL); +	if (!pages) +		return -ENOMEM; + +	pfns = kcalloc(nr_grant_frames, sizeof(pfns[0]), GFP_KERNEL); +	if (!pfns) { +		kfree(pages); +		return -ENOMEM;  	} +	rc = alloc_xenballooned_pages(nr_grant_frames, pages, 0 /* lowmem */); +	if (rc) { +		pr_warn("%s Couldn't balloon alloc %ld pfns rc:%d\n", __func__, +			nr_grant_frames, rc); +		kfree(pages); +		kfree(pfns); +		return rc; +	} +	for (i = 0; i < nr_grant_frames; i++) +		pfns[i] = page_to_pfn(pages[i]); + +	rc = arch_gnttab_map_shared(pfns, nr_grant_frames, nr_grant_frames, +				    &xen_auto_xlat_grant_frames.vaddr); + +	if (rc) { +		pr_warn("%s Couldn't map %ld pfns rc:%d\n", __func__, +			nr_grant_frames, rc); +		free_xenballooned_pages(nr_grant_frames, pages); +		kfree(pages); +		kfree(pfns); +		return rc; +	} +	kfree(pages); + +	xen_auto_xlat_grant_frames.pfn = pfns; +	xen_auto_xlat_grant_frames.count = nr_grant_frames; -	rc = apply_to_page_range(&init_mm, (unsigned long)shared, -				 PAGE_SIZE * nr_gframes, -				 map_pte_fn_status, &frames); -	return rc; +	return 0;  } -void arch_gnttab_unmap(void *shared, unsigned long nr_gframes) +static int __init xen_pvh_gnttab_setup(void)  { -	apply_to_page_range(&init_mm, (unsigned long)shared, -			    PAGE_SIZE * nr_gframes, unmap_pte_fn, NULL); +	if (!xen_pvh_domain()) +		return -ENODEV; + +	return xlated_setup_gnttab_pages();  } +/* Call it _before_ __gnttab_init as we need to initialize the + * xen_auto_xlat_grant_frames first. */ +core_initcall(xen_pvh_gnttab_setup); +#endif  | 
