# HG changeset patch # User yamahata@xxxxxxxxxxxxx # Node ID ed0baafd91fb6379a9111c6388e6a07c865d6d88 # Parent 29386e8978218fe9c110b6894ade9e8034318dc5 dma paravirtualization PATCHNAME: dma_paravirtualization_linux Signed-off-by: Isaku Yamahata diff -r 29386e897821 -r ed0baafd91fb linux-2.6-xen-sparse/arch/ia64/kernel/setup.c --- a/linux-2.6-xen-sparse/arch/ia64/kernel/setup.c Mon Apr 24 22:28:06 2006 +0900 +++ b/linux-2.6-xen-sparse/arch/ia64/kernel/setup.c Mon Apr 24 22:28:08 2006 +0900 @@ -64,6 +64,7 @@ #ifdef CONFIG_XEN #include #endif +#include #if defined(CONFIG_SMP) && (IA64_CPU_SIZE > PAGE_SIZE) # error "struct cpuinfo_ia64 too big!" @@ -534,6 +535,7 @@ setup_arch (char **cmdline_p) platform_setup(cmdline_p); paging_init(); + contiguous_bitmap_init(max_pfn); } /* diff -r 29386e897821 -r ed0baafd91fb linux-2.6-xen-sparse/include/asm-ia64/agp.h --- a/linux-2.6-xen-sparse/include/asm-ia64/agp.h Mon Apr 24 22:28:06 2006 +0900 +++ b/linux-2.6-xen-sparse/include/asm-ia64/agp.h Mon Apr 24 22:28:08 2006 +0900 @@ -19,13 +19,44 @@ #define flush_agp_cache() mb() /* Convert a physical address to an address suitable for the GART. */ +#ifndef CONFIG_XEN_IA64_DOM0_VP #define phys_to_gart(x) (x) #define gart_to_phys(x) (x) +#else +#define phys_to_gart(x) phys_to_machine_for_dma(x) +#define gart_to_phys(x) machine_to_phys_for_dma(x) +#endif /* GATT allocation. Returns/accepts GATT kernel virtual address. */ +#ifndef CONFIG_XEN_IA64_DOM0_VP #define alloc_gatt_pages(order) \ ((char *)__get_free_pages(GFP_KERNEL, (order))) #define free_gatt_pages(table, order) \ free_pages((unsigned long)(table), (order)) +#else +#include +static inline char* +alloc_gatt_pages(unsigned int order) +{ + unsigned long error; + unsigned long ret = __get_free_pages(GFP_KERNEL, (order)); + if (ret == 0) { + goto out; + } + error = xen_create_contiguous_region(ret, order, 0); + if (error) { + free_pages(ret, order); + ret = 0; + } +out: + return (char*)ret; +} +static inline void +free_gatt_pages(void* table, unsigned int order) +{ + xen_destroy_contiguous_region((unsigned long)table, order); + free_pages((unsigned long)table, order); +} +#endif /* CONFIG_XEN_IA64_DOM0_VP */ #endif /* _ASM_IA64_AGP_H */ diff -r 29386e897821 -r ed0baafd91fb linux-2.6-xen-sparse/include/asm-ia64/dma-mapping.h --- a/linux-2.6-xen-sparse/include/asm-ia64/dma-mapping.h Mon Apr 24 22:28:06 2006 +0900 +++ b/linux-2.6-xen-sparse/include/asm-ia64/dma-mapping.h Mon Apr 24 22:28:08 2006 +0900 @@ -7,7 +7,13 @@ */ #include #include +#ifdef CONFIG_XEN_IA64_DOM0_VP +#include //XXX to compile arch/i386/kernel/swiotlb.c + // and arch/i386/kernel/pci-dma-xen.c +#include //XXX to compile arch/i386/kernel/swiotlb.c +#endif +#ifndef CONFIG_XEN_IA64_DOM0_VP #define dma_alloc_coherent platform_dma_alloc_coherent #define dma_alloc_noncoherent platform_dma_alloc_coherent /* coherent mem. is cheap */ #define dma_free_coherent platform_dma_free_coherent @@ -21,6 +27,46 @@ #define dma_sync_single_for_device platform_dma_sync_single_for_device #define dma_sync_sg_for_device platform_dma_sync_sg_for_device #define dma_mapping_error platform_dma_mapping_error +#else +int dma_map_sg(struct device *hwdev, struct scatterlist *sg, int nents, + enum dma_data_direction direction); +void dma_unmap_sg(struct device *hwdev, struct scatterlist *sg, int nents, + enum dma_data_direction direction); +int dma_supported(struct device *dev, u64 mask); +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp); +void dma_free_coherent(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle); +dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, + enum dma_data_direction direction); +void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, + enum dma_data_direction direction); +void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction direction); +void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, + size_t size, + enum dma_data_direction direction); +int dma_mapping_error(dma_addr_t dma_addr); + +#define flush_write_buffers() do { } while (0) +static inline void +dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + if (swiotlb) + swiotlb_sync_sg_for_cpu(dev,sg,nelems,direction); + flush_write_buffers(); +} + +static inline void +dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + if (swiotlb) + swiotlb_sync_sg_for_device(dev,sg,nelems,direction); + flush_write_buffers(); +} +#endif #define dma_map_page(dev, pg, off, size, dir) \ dma_map_single(dev, page_address(pg) + (off), (size), (dir)) @@ -62,4 +108,29 @@ dma_cache_sync (void *vaddr, size_t size #define dma_is_consistent(dma_handle) (1) /* all we do is coherent memory... */ +#ifdef CONFIG_XEN_IA64_DOM0_VP +// arch/i386/kernel/swiotlb.o requires +void contiguous_bitmap_init(unsigned long end_pfn); + +static inline int +address_needs_mapping(struct device *hwdev, dma_addr_t addr) +{ + dma_addr_t mask = DMA_64BIT_MASK; + /* If the device has a mask, use it, otherwise default to 64 bits */ + if (hwdev && hwdev->dma_mask) + mask = *hwdev->dma_mask; + return (addr & ~mask) != 0; +} + +static inline int +range_straddles_page_boundary(void *p, size_t size) +{ + extern unsigned long *contiguous_bitmap; + return (((((unsigned long)p & ~PAGE_MASK) + size) > PAGE_SIZE) && + !test_bit(__pa(p) >> PAGE_SHIFT, contiguous_bitmap)); +} +#else +#define contiguous_bitmap_init(end_pfn) ((void)end_pfn) +#endif + #endif /* _ASM_IA64_DMA_MAPPING_H */ diff -r 29386e897821 -r ed0baafd91fb linux-2.6-xen-sparse/include/asm-ia64/hypercall.h --- a/linux-2.6-xen-sparse/include/asm-ia64/hypercall.h Mon Apr 24 22:28:06 2006 +0900 +++ b/linux-2.6-xen-sparse/include/asm-ia64/hypercall.h Mon Apr 24 22:28:08 2006 +0900 @@ -36,13 +36,6 @@ #ifndef __HYPERVISOR_H__ # error "please don't include this file directly" #endif - -/* FIXME: temp place to hold these page related macros */ -#include -#define virt_to_machine(v) __pa(v) -#define machine_to_virt(m) __va(m) -#define virt_to_mfn(v) ((__pa(v)) >> PAGE_SHIFT) -#define mfn_to_virt(m) (__va((m) << PAGE_SHIFT)) /* * Assembler stubs for hyper-calls. diff -r 29386e897821 -r ed0baafd91fb linux-2.6-xen-sparse/include/asm-ia64/hypervisor.h --- a/linux-2.6-xen-sparse/include/asm-ia64/hypervisor.h Mon Apr 24 22:28:06 2006 +0900 +++ b/linux-2.6-xen-sparse/include/asm-ia64/hypervisor.h Mon Apr 24 22:28:08 2006 +0900 @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include // for running_on_xen @@ -54,8 +55,6 @@ int xen_init(void); /* Turn jiffies into Xen system time. XXX Implement me. */ #define jiffies_to_st(j) 0 - -#include static inline int HYPERVISOR_yield( @@ -117,10 +116,12 @@ HYPERVISOR_poll( // for drivers/xen/privcmd/privcmd.c #define direct_remap_pfn_range(a,b,c,d,e,f) remap_pfn_range(a,b,c,d,e) +#define machine_to_phys_mapping 0 +#define xen_arch_privcmd_mmap(file, vma) (0) +#ifndef CONFIG_XEN_IA64_DOM0_VP #define pfn_to_mfn(x) (x) #define mfn_to_pfn(x) (x) -#define machine_to_phys_mapping 0 -#define xen_arch_privcmd_mmap(file, vma) (0) +#endif // for drivers/xen/balloon/balloon.c #ifdef CONFIG_XEN_SCRUB_PAGES @@ -129,12 +130,44 @@ HYPERVISOR_poll( #define scrub_pages(_p,_n) ((void)0) #endif #define pte_mfn(_x) pte_pfn(_x) -#define INVALID_P2M_ENTRY (~0UL) #define __pte_ma(_x) ((pte_t) {(_x)}) #define phys_to_machine_mapping_valid(_x) (1) #define kmap_flush_unused() do {} while (0) +#define pfn_pte_ma(_x,_y) __pte_ma(0) +#ifndef CONFIG_XEN_IA64_DOM0_VP //XXX #define set_phys_to_machine(_x,_y) do {} while (0) #define xen_machphys_update(_x,_y) do {} while (0) -#define pfn_pte_ma(_x,_y) __pte_ma(0) +#endif + +#ifdef CONFIG_XEN_IA64_DOM0_VP +int __xen_create_contiguous_region(unsigned long vstart, unsigned int order, unsigned int address_bits); +static inline int +xen_create_contiguous_region(unsigned long vstart, + unsigned int order, unsigned int address_bits) +{ + int ret = 0; + if (running_on_xen) { + ret = __xen_create_contiguous_region(vstart, order, + address_bits); + } + return ret; +} + +void __xen_destroy_contiguous_region(unsigned long vstart, unsigned int order); +static inline void +xen_destroy_contiguous_region(unsigned long vstart, unsigned int order) +{ + if (running_on_xen) + __xen_destroy_contiguous_region(vstart, order); +} +#else +#define xen_create_contiguous_region(vstart, order, address_bits) ({0;}) +#define xen_destroy_contiguous_region(vstart, order) do {} while (0) +#endif + +// for debug +asmlinkage int xprintk(const char *fmt, ...); +#define xprintd(fmt, ...) xprintk("%s:%d " fmt, __func__, __LINE__, \ + ##__VA_ARGS__) #endif /* __HYPERVISOR_H__ */ diff -r 29386e897821 -r ed0baafd91fb linux-2.6-xen-sparse/include/asm-ia64/io.h --- a/linux-2.6-xen-sparse/include/asm-ia64/io.h Mon Apr 24 22:28:06 2006 +0900 +++ b/linux-2.6-xen-sparse/include/asm-ia64/io.h Mon Apr 24 22:28:08 2006 +0900 @@ -71,6 +71,10 @@ extern unsigned int num_io_spaces; #include #include #include +#ifdef CONFIG_XEN +#include +#include +#endif /* * Change virtual addresses to physical addresses and vv. @@ -95,9 +99,39 @@ extern int valid_mmap_phys_addr_range (u * The following two macros are deprecated and scheduled for removal. * Please use the PCI-DMA interface defined in instead. */ +#ifndef CONFIG_XEN_IA64_DOM0_VP #define bus_to_virt phys_to_virt #define virt_to_bus virt_to_phys #define page_to_bus page_to_phys +#define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) +#define page_to_pseudophys(page) page_to_phys(page) +#else +#define bus_to_virt(bus) \ + phys_to_virt(machine_to_phys_for_dma(bus)) +#define virt_to_bus(virt) \ + phys_to_machine_for_dma(virt_to_phys(virt)) +#define page_to_bus(page) \ + phys_to_machine_for_dma(page_to_pseudophys(page)) + +#define page_to_pseudophys(page) \ + ((dma_addr_t)page_to_pfn(page) << PAGE_SHIFT) +// XXX +// the following drivers are broken because they use page_to_phys() to +// get bus address. fix them. +// drivers/ide/cris/ide-cris.c +// drivers/scsi/dec_esp.c +#define page_to_phys(page) (page_to_pseudophys(page)) +#define bvec_to_bus(bv) (page_to_bus((bv)->bv_page) + \ + (unsigned long) (bv)->bv_offset) +#define bio_to_pseudophys(bio) (page_to_pseudophys(bio_page((bio))) + \ + (unsigned long) bio_offset((bio))) +#define bvec_to_pseudophys(bv) (page_to_pseudophys((bv)->bv_page) + \ + (unsigned long) (bv)->bv_offset) +#define BIOVEC_PHYS_MERGEABLE(vec1, vec2) \ + (((bvec_to_bus((vec1)) + (vec1)->bv_len) == bvec_to_bus((vec2))) && \ + ((bvec_to_pseudophys((vec1)) + (vec1)->bv_len) == \ + bvec_to_pseudophys((vec2)))) +#endif # endif /* KERNEL */ @@ -425,6 +459,9 @@ static inline void __iomem * static inline void __iomem * ioremap (unsigned long offset, unsigned long size) { +#ifdef CONFIG_XEN + offset = HYPERVISOR_ioremap(offset, size); +#endif return (void __iomem *) (__IA64_UNCACHED_OFFSET | (offset)); } diff -r 29386e897821 -r ed0baafd91fb linux-2.6-xen-sparse/include/asm-ia64/machvec.h --- a/linux-2.6-xen-sparse/include/asm-ia64/machvec.h Mon Apr 24 22:28:06 2006 +0900 +++ b/linux-2.6-xen-sparse/include/asm-ia64/machvec.h Mon Apr 24 22:28:08 2006 +0900 @@ -247,6 +247,21 @@ extern void machvec_init (const char *na # error Unknown configuration. Update asm-ia64/machvec.h. # endif /* CONFIG_IA64_GENERIC */ +#ifdef CONFIG_XEN_IA64_DOM0_VP +# define platform_dma_map_sg dma_map_sg +# define platform_dma_unmap_sg dma_unmap_sg +# define platform_dma_mapping_error dma_mapping_error +# define platform_dma_supported dma_supported +# define platform_dma_alloc_coherent dma_alloc_coherent +# define platform_dma_free_coherent dma_free_coherent +# define platform_dma_map_single dma_map_single +# define platform_dma_unmap_single dma_unmap_single +# define platform_dma_sync_single_for_cpu \ + dma_sync_single_for_cpu +# define platform_dma_sync_single_for_device \ + dma_sync_single_for_device +#endif + /* * Declare default routines which aren't declared anywhere else: */ diff -r 29386e897821 -r ed0baafd91fb linux-2.6-xen-sparse/include/asm-ia64/page.h --- a/linux-2.6-xen-sparse/include/asm-ia64/page.h Mon Apr 24 22:28:06 2006 +0900 +++ b/linux-2.6-xen-sparse/include/asm-ia64/page.h Mon Apr 24 22:28:08 2006 +0900 @@ -117,7 +117,6 @@ extern unsigned long max_low_pfn; # define pfn_to_page(pfn) (vmem_map + (pfn)) #endif -#define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) @@ -219,4 +218,75 @@ get_order (unsigned long size) (((current->personality & READ_IMPLIES_EXEC) != 0) \ ? VM_EXEC : 0)) +#ifndef __ASSEMBLY__ +#ifdef CONFIG_XEN + +#define INVALID_P2M_ENTRY (~0UL) + +#ifndef CONFIG_XEN_IA64_DOM0_VP + +#define virt_to_machine(v) __pa(v) +#define machine_to_virt(m) __va(m) +#define virt_to_mfn(v) ((__pa(v)) >> PAGE_SHIFT) +#define mfn_to_virt(m) (__va((m) << PAGE_SHIFT)) + +#else + +#include +#include + +//XXX xen page size != page size + +static inline unsigned long +pfn_to_mfn_for_dma(unsigned long pfn) +{ + unsigned long mfn; + mfn = HYPERVISOR_phystomach(pfn); + BUG_ON(mfn == 0); // XXX + BUG_ON(mfn == INVALID_P2M_ENTRY); // XXX + BUG_ON(mfn == INVALID_MFN); + return mfn; +} + +static inline unsigned long +phys_to_machine_for_dma(unsigned long phys) +{ + unsigned long machine = + pfn_to_mfn_for_dma(phys >> PAGE_SHIFT) << PAGE_SHIFT; + machine |= (phys & ~PAGE_MASK); + return machine; +} + +static inline unsigned long +mfn_to_pfn_for_dma(unsigned long mfn) +{ + unsigned long pfn; + pfn = HYPERVISOR_machtophys(mfn); + BUG_ON(pfn == 0); + //BUG_ON(pfn == INVALID_M2P_ENTRY); + return pfn; +} + +static inline unsigned long +machine_to_phys_for_dma(unsigned long machine) +{ + unsigned long phys = + mfn_to_pfn_for_dma(machine >> PAGE_SHIFT) << PAGE_SHIFT; + phys |= (machine & ~PAGE_MASK); + return phys; +} + +#define set_phys_to_machine(pfn, mfn) do { } while (0) +#define xen_machphys_update(mfn, pfn) do { } while (0) + +#define mfn_to_pfn(mfn) ({(mfn);}) +#define mfn_to_virt(mfn) ({__va((mfn) << PAGE_SHIFT);}) +#define pfn_to_mfn(pfn) ({(pfn);}) +#define virt_to_mfn(virt) ({__pa(virt) >> PAGE_SHIFT;}) +#define virt_to_machine(virt) ({__pa(virt);}) // for tpmfront.c + +#endif /* CONFIG_XEN_IA64_DOM0_VP */ +#endif /* CONFIG_XEN */ +#endif /* __ASSEMBLY__ */ + #endif /* _ASM_IA64_PAGE_H */ diff -r 29386e897821 -r ed0baafd91fb linux-2.6-xen-sparse/include/asm-ia64/pgalloc.h --- a/linux-2.6-xen-sparse/include/asm-ia64/pgalloc.h Mon Apr 24 22:28:06 2006 +0900 +++ b/linux-2.6-xen-sparse/include/asm-ia64/pgalloc.h Mon Apr 24 22:28:08 2006 +0900 @@ -126,7 +126,7 @@ static inline void static inline void pmd_populate(struct mm_struct *mm, pmd_t * pmd_entry, struct page *pte) { - pmd_val(*pmd_entry) = page_to_phys(pte); + pmd_val(*pmd_entry) = page_to_pseudophys(pte); } static inline void diff -r 29386e897821 -r ed0baafd91fb linux-2.6-xen-sparse/arch/ia64/xen/hypervisor.c --- /dev/null Thu Jan 1 00:00:00 1970 +0000 +++ b/linux-2.6-xen-sparse/arch/ia64/xen/hypervisor.c Mon Apr 24 22:28:08 2006 +0900 @@ -0,0 +1,234 @@ +/****************************************************************************** + * include/asm-ia64/shadow.h + * + * Copyright (c) 2006 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +//#include +#include +#include +#include +#include +#include + +#define XEN_IA64_BALLOON_IS_NOT_YET +#ifndef XEN_IA64_BALLOON_IS_NOT_YET +#include +#else +#define balloon_lock(flags) ((void)flags) +#define balloon_unlock(flags) ((void)flags) +#endif + + +//XXX same as i386, x86_64 contiguous_bitmap_set(), contiguous_bitmap_clear() +// move those to lib/contiguous_bitmap? +//XXX discontigmem/sparsemem + +/* + * Bitmap is indexed by page number. If bit is set, the page is part of a + * xen_create_contiguous_region() area of memory. + */ +unsigned long *contiguous_bitmap; + +void +contiguous_bitmap_init(unsigned long end_pfn) +{ + unsigned long size = (end_pfn + 2 * BITS_PER_LONG) >> 3; + contiguous_bitmap = alloc_bootmem_low_pages(size); + BUG_ON(!contiguous_bitmap); + memset(contiguous_bitmap, 0, size); +} + +#if 0 +int +contiguous_bitmap_test(void* p) +{ + return test_bit(__pa(p) >> PAGE_SHIFT, contiguous_bitmap); +} +#endif + +static void contiguous_bitmap_set( + unsigned long first_page, unsigned long nr_pages) +{ + unsigned long start_off, end_off, curr_idx, end_idx; + + curr_idx = first_page / BITS_PER_LONG; + start_off = first_page & (BITS_PER_LONG-1); + end_idx = (first_page + nr_pages) / BITS_PER_LONG; + end_off = (first_page + nr_pages) & (BITS_PER_LONG-1); + + if (curr_idx == end_idx) { + contiguous_bitmap[curr_idx] |= + ((1UL<> PAGE_SHIFT; + unsigned long num_pfn = 1 << order; + unsigned long i; + unsigned long flags; + + scrub_pages(vstart, 1 << order); + + balloon_lock(flags); + + //XXX order + for (i = 0; i < num_pfn; i++) { + error = HYPERVISOR_zap_physmap(start_gpfn + i, 0); + if (error) { + goto out; + } + } + + error = HYPERVISOR_populate_physmap(start_gpfn, order, address_bits); + contiguous_bitmap_set(start_gpfn, 1UL << order); +#if 0 + { + unsigned long mfn; + unsigned long mfn_prev = ~0UL; + for (i = 0; i < 1 << order; i++) { + mfn = pfn_to_mfn_for_dma(start_gpfn + i); + if (mfn_prev != ~0UL && mfn != mfn_prev + 1) { + xprintk("\n"); + xprintk("%s:%d order %d " + "start 0x%lx bus 0x%lx machine 0x%lx\n", + __func__, __LINE__, order, + vstart, virt_to_bus((void*)vstart), + phys_to_machine_for_dma(gphys)); + xprintk("mfn: "); + for (i = 0; i < 1 << order; i++) { + mfn = pfn_to_mfn_for_dma(start_gpfn + i); + xprintk("0x%lx ", mfn); + } + xprintk("\n"); + goto out; + } + mfn_prev = mfn; + } + } +#endif +out: + balloon_unlock(flags); + return error; +} + +void +__xen_destroy_contiguous_region(unsigned long vstart, unsigned int order) +{ + unsigned long error = 0; + unsigned long gphys = __pa(vstart); + unsigned long start_gpfn = gphys >> PAGE_SHIFT; + unsigned long num_pfn = 1 << order; + unsigned long i; + unsigned long flags; + + scrub_pages(vstart, 1 << order); + + balloon_lock(flags); + + contiguous_bitmap_clear(start_gpfn, 1UL << order); + + //XXX order + for (i = 0; i < num_pfn; i++) { + error = HYPERVISOR_zap_physmap(start_gpfn + i, 0); + if (error) { + goto out; + } + } + + for (i = 0; i < num_pfn; i++) { + error = HYPERVISOR_populate_physmap(start_gpfn + i, 0, 0); + if (error) { + goto out; + } + } + +out: + balloon_unlock(flags); + if (error) { + //XXX + } +} + + +/////////////////////////////////////////////////////////////////////////// +//XXX taken from balloon.c +// temporal hack until balloon driver support. +#include + +struct page *balloon_alloc_empty_page_range(unsigned long nr_pages) +{ + unsigned long vstart; + unsigned int order = get_order(nr_pages * PAGE_SIZE); + + vstart = __get_free_pages(GFP_KERNEL, order); + if (vstart == 0) + return NULL; + + return virt_to_page(vstart); +} + +void balloon_dealloc_empty_page_range( + struct page *page, unsigned long nr_pages) +{ + __free_pages(page, get_order(nr_pages * PAGE_SIZE)); +} + +void balloon_update_driver_allowance(long delta) +{ +} + +EXPORT_SYMBOL(balloon_alloc_empty_page_range); +EXPORT_SYMBOL(balloon_dealloc_empty_page_range); +EXPORT_SYMBOL(balloon_update_driver_allowance); + +