Xen-pv_ops: Xen guest implementation for paravirt_ops interface
Subsystems (15)
cover Xen-pv_ops: Xen guest implementation for paravirt_ops interface
Hi Andi,
This patch series implements the Linux Xen guest as a paravirt_ops backend. The features in implemented this patch series are:
* domU only
* UP and SMP guest support (NEW!)
* dynamic ticks (NEW!)
* writable pagetables, with late pinning/early unpinning (no shadow pagetable support)
* supports both PAE and non-PAE modes
(non-PAE may be broken at the moment)
* xen hvc console (console=hvc0)
* virtual block device (blockfront)
* virtual network device (netfront)
The patch series is in three parts:
1-2 Cleanup patches to various parts of the kernel
3-19 Extensions to the core code and/or paravirt ops, needed to support
Xen. Includes hooks into get/setting pte values and the new patching machinery.
19-34 The Xen paravirt_ops implementation itself.
(Some of the earlier patches in the series have already been posted,
but are included to make the series self-contained.)
I've tried to make each patch as self-explanatory as possible. The series is based on 2.6.21-rc3-git7.
Changes since the previous posting:
- SMP support
- Implemented xen_clockevent, so Xen is fully compatible with CONFIG_NO_HZ and HIGH_RES_TIMERS.
- Finished implementing late pinning/early unpinning of pagetables, which should now be equivalent to xen-unstable in performance.
- Add kmap_atomic_pte for mapping ptes, and therefore support for HIGHPTE
- Fixed crash on systems with >= 4G memory
- Cleanup netfront (Stephen Hemminger)
- Add MAINTAINERS entries
- Fold kill-HAVE_ARCH_MM_LIFETIME.patch into mm-lifetime-hooks.patch
- rebased
Thanks,
J
PATCH 1/34 Xen-pv_ops: Fix typo in sync_constant_test_bit()s name.
--- include/asm-i386/sync_bitops.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
===================================================================
include/asm-i386/sync_bitops.h +1 -1
@@ -130,7 +130,7 @@ static inline int sync_test_and_change_breturn oldbit; } -static __always_inline int sync_const_test_bit(int nr, const volatile unsigned long *addr) +static __always_inline int sync_constant_test_bit(int nr, const volatile unsigned long *addr) {return ((1UL << (nr &31 )) & (((const volatile unsigned int *)addr)[nr >>5 ])) !=0 ;
PATCH 2/34 Xen-pv_ops: ignore vgacon if hardware not present
In reply to Jeremy FitzhardingeAvoid trying to set up vgacon if there's no vga hardware present.
From: Gerd Hoffmann <[email protected]>
Cc: Alan <[email protected]>
--- drivers/video/console/vgacon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
===================================================================
drivers/video/console/vgacon.c +2 -1
@@ -372,7 +372,8 @@ static const char *vgacon_startup(void) }/* VGA16 modes are not handled by VGACON */ - if ((ORIG_VIDEO_MODE == 0x0D) || /* 320x200/4 */ + if ((ORIG_VIDEO_MODE == 0x00) || /* SCREEN_INFO not initialized */ + (ORIG_VIDEO_MODE == 0x0D) || /* 320x200/4 */ (ORIG_VIDEO_MODE == 0x0E) || /* 640x200/4 */ (ORIG_VIDEO_MODE == 0x10) || /* 640x350/4 */ (ORIG_VIDEO_MODE == 0x12) || /* 640x480/4 */
PATCH 3/34 Xen-pv_ops: update MAINTAINERS
In reply to Jeremy Fitzhardinge--- MAINTAINERS | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
===================================================================
MAINTAINERS +22 -0
@@ -2583,6 +2583,19 @@ T: cvs cvs.parisc-linux.org:/var/cvs/lin T: cvs cvs.parisc-linux.org:/var/cvs/linux-2.6 S: Maintained +PARAVIRT_OPS INTERFACE +P: Jeremy Fitzhardinge +M: [email protected] +P: Chris Wright +M: [email protected] +P: Zachary Amsden +M: [email protected] +P: Rusty Russell +M: [email protected] +L: [email protected] +L: [email protected] +S: Supported + PC87360 HARDWARE MONITORING DRIVER P: Jim Cromie M: [email protected]
@@ -3780,6 +3793,15 @@ L: [email protected] L: [email protected] S: Maintained +XEN HYPERVISOR INTERFACE +P: Jeremy Fitzhardinge +M: [email protected] +P: Chris Wright +M: [email protected] +L: [email protected] +L: [email protected] +S: Supported + XFS FILESYSTEM P: Silicon Graphics Inc P: Tim Shimmin, David Chatterton
PATCH 4/34 Xen-pv_ops: use paravirt_nop to consistently mark no-op operations
In reply to Jeremy FitzhardingeAdd a _paravirt_nop function for use as a stub for no-op operations, and paravirt_nop #defined void * version to make using it easier (since all its uses are as a void *).
This is useful to allow the patcher to automatically identify noop operations so it can simply nop out the callsite.
--- arch/i386/kernel/paravirt.c | 26 +++++++++++++------------- include/asm-i386/paravirt.h | 3 +++ 2 files changed, 16 insertions(+), 13 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +13 -13
@@ -35,7 +35,7 @@#include <asm/timer.h> /* nop stub */ -static void native_nop (void ) +void _paravirt_nop (void ) { }
@@ -490,7 +490,7 @@ struct paravirt_ops paravirt_ops = { .patch = native_patch, .banner = default_banner, - .arch_setup = native_nop, + .arch_setup = paravirt_nop, .memory_setup = machine_specific_memory_setup, .get_wallclock = native_get_wallclock, .set_wallclock = native_set_wallclock,
@@ -546,25 +546,25 @@ struct paravirt_ops paravirt_ops = { .setup_boot_clock = setup_boot_APIC_clock, .setup_secondary_clock = setup_secondary_APIC_clock, #endif - .set_lazy_mode = (void *)native_nop, + .set_lazy_mode = paravirt_nop, .flush_tlb_user = native_flush_tlb, .flush_tlb_kernel = native_flush_tlb_global, .flush_tlb_single = native_flush_tlb_single, - .map_pt_hook = (void *)native_nop, - - .alloc_pt = (void *)native_nop, - .alloc_pd = (void *)native_nop, - .alloc_pd_clone = (void *)native_nop, - .release_pt = (void *)native_nop, - .release_pd = (void *)native_nop, + .map_pt_hook = paravirt_nop, + + .alloc_pt = paravirt_nop, + .alloc_pd = paravirt_nop, + .alloc_pd_clone = paravirt_nop, + .release_pt = paravirt_nop, + .release_pd = paravirt_nop, .set_pte = native_set_pte, .set_pte_at = native_set_pte_at, .set_pmd = native_set_pmd, - .pte_update = (void *)native_nop, - .pte_update_defer = (void *)native_nop, + .pte_update = paravirt_nop, + .pte_update_defer = paravirt_nop, #ifdef CONFIG_X86_PAE .set_pte_atomic = native_set_pte_atomic, .set_pte_present = native_set_pte_present,
@@ -576,7 +576,7 @@ struct paravirt_ops paravirt_ops = { .irq_enable_sysexit = native_irq_enable_sysexit, .iret = native_iret, - .startup_ipi_hook = (void *)native_nop, + .startup_ipi_hook = paravirt_nop, };/*
===================================================================
include/asm-i386/paravirt.h +3 -0
@@ -430,6 +430,9 @@ static inline void pmd_clear(pmd_t *pmdp#define arch_enter_lazy_mmu_mode () paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_MMU)#define arch_leave_lazy_mmu_mode () paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_NONE) +void _paravirt_nop (void ); +#define paravirt_nop ((void *)_paravirt_nop) +/* These all sit in the .parainstructions section to tell us what to patch. */ struct paravirt_patch {u8 *instr;/* original instructions */
PATCH 5/34 Xen-pv_ops: Add pagetable accessors to pack and unpack pagetable entries
In reply to Jeremy FitzhardingeAdd a set of accessors to pack, unpack and modify page table entries (at all levels). This allows a paravirt implementation to control the contents of pgd/pmd/pte entries. For example, Xen uses this to convert the (pseudo-)physical address into a machine address when populating a pagetable entry, and converting back to pphys address when an entry is read.
--- arch/i386/kernel/paravirt.c | 84 +++++-------------------------------- arch/i386/kernel/vmi.c | 6 +- include/asm-i386/page.h | 71 ++++++++++++++++++++++++++----- include/asm-i386/paravirt.h | 54 ++++++++++++++++++----- include/asm-i386/pgtable-2level.h | 28 +++++++++--- include/asm-i386/pgtable-3level.h | 65 +++++++++++++++++----------- include/asm-i386/pgtable.h | 2 7 files changed, 179 insertions(+), 131 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +12 -72
@@ -399,78 +399,6 @@ static void native_flush_tlb_single(u32 {__native_flush_tlb_single (addr); } - -#ifndef CONFIG_X86_PAE -static void native_set_pte (pte_t *ptep,pte_t pteval) -{ - *ptep = pteval; -} - -static void native_set_pte_at (struct mm_struct *mm,u32 addr,pte_t *ptep,pte_t pteval) -{ - *ptep = pteval; -} - -static void native_set_pmd (pmd_t *pmdp,pmd_t pmdval) -{ - *pmdp = pmdval; -} - -#else /* CONFIG_X86_PAE */ - -static void native_set_pte (pte_t *ptep,pte_t pte) -{ - ptep->pte_high = pte.pte_high; -smp_wmb (); - ptep->pte_low = pte.pte_low; -} - -static void native_set_pte_at (struct mm_struct *mm,u32 addr,pte_t *ptep,pte_t pte) -{ - ptep->pte_high = pte.pte_high; -smp_wmb (); - ptep->pte_low = pte.pte_low; -} - -static void native_set_pte_present (struct mm_struct *mm,unsigned long addr,pte_t *ptep,pte_t pte) -{ - ptep->pte_low =0 ; -smp_wmb (); - ptep->pte_high = pte.pte_high; -smp_wmb (); - ptep->pte_low = pte.pte_low; -} - -static void native_set_pte_atomic (pte_t *ptep,pte_t pteval) -{ -set_64bit ((unsigned long long *)ptep,pte_val (pteval)); -} - -static void native_set_pmd (pmd_t *pmdp,pmd_t pmdval) -{ -set_64bit ((unsigned long long *)pmdp,pmd_val (pmdval)); -} - -static void native_set_pud (pud_t *pudp,pud_t pudval) -{ - *pudp = pudval; -} - -static void native_pte_clear (struct mm_struct *mm,unsigned long addr,pte_t *ptep) -{ - ptep->pte_low =0 ; -smp_wmb (); - ptep->pte_high =0 ; -} - -static void native_pmd_clear (pmd_t *pmd) -{ -u32 *tmp = (u32 *)pmd; - *tmp =0 ; -smp_wmb (); - *(tmp +1 ) =0 ; -} -#endif /* CONFIG_X86_PAE */ /* These are in entry.S */ extern void native_iret (void );
@@ -565,13 +493,25 @@ struct paravirt_ops paravirt_ops = { .set_pmd = native_set_pmd, .pte_update = paravirt_nop, .pte_update_defer = paravirt_nop, + + .ptep_get_and_clear = native_ptep_get_and_clear, + #ifdef CONFIG_X86_PAE .set_pte_atomic = native_set_pte_atomic, .set_pte_present = native_set_pte_present, .set_pud = native_set_pud, .pte_clear = native_pte_clear, .pmd_clear = native_pmd_clear, + + .pmd_val = native_pmd_val, + .make_pmd = native_make_pmd, #endif + + .pte_val = native_pte_val, + .pgd_val = native_pgd_val, + + .make_pte = native_make_pte, + .make_pgd = native_make_pgd, .irq_enable_sysexit = native_irq_enable_sysexit, .iret = native_iret,
===================================================================
arch/i386/kernel/vmi.c +3 -3
@@ -443,13 +443,13 @@ static void vmi_release_pd(u32 pfn) ((level) | (is_current_as (mm, user) ? \ (VMI_PAGE_DEFER | VMI_PAGE_CURRENT_AS | ((addr) & VMI_PAGE_VA_MASK)) : 0)) -static void vmi_update_pte (struct mm_struct *mm,u32 addr,pte_t *ptep) +static void vmi_update_pte (struct mm_struct *mm,unsigned long addr,pte_t *ptep) {vmi_check_page_type (__pa (ptep) >>PAGE_SHIFT ,VMI_PAGE_PTE ); vmi_ops.update_pte (ptep,vmi_flags_addr (mm, addr,VMI_PAGE_PT ,0 )); } -static void vmi_update_pte_defer (struct mm_struct *mm,u32 addr,pte_t *ptep) +static void vmi_update_pte_defer (struct mm_struct *mm,unsigned long addr,pte_t *ptep) {vmi_check_page_type (__pa (ptep) >>PAGE_SHIFT ,VMI_PAGE_PTE ); vmi_ops.update_pte (ptep,vmi_flags_addr_defer (mm, addr,VMI_PAGE_PT ,0 ));
@@ -462,7 +462,7 @@ static void vmi_set_pte(pte_t *ptep, pte vmi_ops.set_pte (pte, ptep,VMI_PAGE_PT ); } -static void vmi_set_pte_at (struct mm_struct *mm,u32 addr,pte_t *ptep,pte_t pte) +static void vmi_set_pte_at (struct mm_struct *mm,unsigned long addr,pte_t *ptep,pte_t pte) {vmi_check_page_type (__pa (ptep) >>PAGE_SHIFT ,VMI_PAGE_PTE ); vmi_ops.set_pte (pte, ptep,vmi_flags_addr (mm, addr,VMI_PAGE_PT ,0 ));
===================================================================
include/asm-i386/page.h +60 -11
105 lines not on this page · raw message
===================================================================
include/asm-i386/paravirt.h +41 -13
113 lines not on this page · raw message
===================================================================
include/asm-i386/pgtable-2level.h +22 -6
@@ -11,11 +11,24 @@* within a page table are directly modified. Thus, the following * hook is made available. */ +static inline void native_set_pte (pte_t *ptep ,pte_t pte) +{ + *ptep = pte; +} +static inline void native_set_pte_at (struct mm_struct *mm,unsigned long addr, +pte_t *ptep ,pte_t pte) +{ +native_set_pte (ptep, pte); +} +static inline void native_set_pmd (pmd_t *pmdp,pmd_t pmd) +{ + *pmdp = pmd; +}#ifndef CONFIG_PARAVIRT -#define set_pte (pteptr, pteval) (*(pteptr) = pteval) -#define set_pte_at (mm,addr,ptep,pteval) set_pte(ptep,pteval) -#define set_pmd (pmdptr, pmdval) (*(pmdptr) = (pmdval)) -#endif +#define set_pte (pteptr, pteval) native_set_pte(pteptr, pteval) +#define set_pte_at (mm,addr,ptep,pteval) native_set_pte_at(mm, addr, ptep, pteval) +#define set_pmd (pmdptr, pmdval) native_set_pmd(pmdptr, pmdval) +#endif /* CONFIG_PARAVIRT */ #define set_pte_atomic (pteptr, pteval) set_pte(pteptr,pteval)#define set_pte_present (mm,addr,ptep,pteval) set_pte_at(mm,addr,ptep,pteval)
@@ -23,11 +36,14 @@#define pte_clear (mm,addr,xp) do { set_pte_at(mm, addr, xp, __pte(0)); } while (0)#define pmd_clear (xp) do { set_pmd(xp, __pmd(0)); } while (0) -#define raw_ptep_get_and_clear (xp) __pte(xchg(&(xp)->pte_low, 0)) +static inline pte_t native_ptep_get_and_clear (pte_t *xp) +{ +return __pte (xchg (&xp->pte_low,0 )); +}#define pte_page (x) pfn_to_page(pte_pfn(x))#define pte_none (x) (!(x).pte_low) -#define pte_pfn (x) ((unsigned long)(((x).pte_low >> PAGE_SHIFT))) +#define pte_pfn (x) (pte_val(x) >> PAGE_SHIFT)#define pfn_pte (pfn, prot) __pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot))#define pfn_pmd (pfn, prot) __pmd(((pfn) << PAGE_SHIFT) | pgprot_val(prot))
===================================================================
include/asm-i386/pgtable-3level.h +39 -26
134 lines not on this page · raw message
===================================================================
include/asm-i386/pgtable.h +2 -0
@@ -264,6 +264,8 @@ static inline pte_t pte_mkhuge(pte_t pte#define pte_update (mm, addr, ptep) do { } while (0)#define pte_update_defer (mm, addr, ptep) do { } while (0)#define paravirt_map_pt_hook (slot, va, pfn) do { } while (0) + +#define raw_ptep_get_and_clear (xp) native_ptep_get_and_clear(xp)#endif /*
PATCH 6/34 Xen-pv_ops: Hooks to set up initial pagetable
In reply to Jeremy FitzhardingeThis patch introduces paravirt_ops hooks to control how the kernel's initial pagetable is set up.
In the case of a native boot, the very early bootstrap code creates a simple non-PAE pagetable to map the kernel and physical memory. When the VM subsystem is initialized, it creates a proper pagetable which respects the PAE mode, large pages, etc.
When booting under a hypervisor, there are many possibilities for what paging environment the hypervisor establishes for the guest kernel, so the constructon of the kernel's pagetable depends on the hypervisor.
In the case of Xen, the hypervisor boots the kernel with a fully constructed pagetable, which is already using PAE if necessary. Also, Xen requires particular care when constructing pagetables to make sure all pagetables are always mapped read-only.
In order to make this easier, kernel's initial pagetable construction has been changed to only allocate and initialize a pagetable page if there's no page already present in the pagetable. This allows the Xen paravirt backend to make a copy of the hypervisor-provided pagetable, allowing the kernel to establish any more mappings it needs while keeping the existing ones.
A slightly subtle point which is worth highlighting here is that Xen requires all kernel mappings to share the same pte_t pages between all pagetables, so that updating a kernel page's mapping in one pagetable is reflected in all other pagetables. This makes it possible to allocate a page and attach it to a pagetable without having to explicitly enumerate that page's mapping in all pagetables.
--- arch/i386/kernel/paravirt.c | 3 arch/i386/mm/init.c | 159 +++++++++++++++++++++++++++++-------------- include/asm-i386/paravirt.h | 17 ++++ include/asm-i386/pgtable.h | 16 ++++ 4 files changed, 143 insertions(+), 52 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +3 -0
@@ -549,6 +549,9 @@ struct paravirt_ops paravirt_ops = {#endif .set_lazy_mode = paravirt_nop, + .pagetable_setup_start = native_pagetable_setup_start, + .pagetable_setup_done = native_pagetable_setup_done, + .flush_tlb_user = native_flush_tlb, .flush_tlb_kernel = native_flush_tlb_global, .flush_tlb_single = native_flush_tlb_single,
===================================================================
arch/i386/mm/init.c +108 -51
218 lines not on this page · raw message
===================================================================
include/asm-i386/paravirt.h +16 -1
@@ -2,10 +2,11 @@#define __ASM_PARAVIRT_H/* Various instructions on x86 need to be replaced for * para-virtualization: those hooks are defined here. */ + +#ifdef CONFIG_PARAVIRT #include <linux/stringify.h> #include <asm/page.h> -#ifdef CONFIG_PARAVIRT /* These are the most performance critical ops, so we want to be able to patch * callers */ #define PARAVIRT_IRQ_DISABLE 0
@@ -48,6 +49,9 @@ struct paravirt_opsvoid (*arch_setup)(void );char *(*memory_setup)(void );void (*init_IRQ)(void ); + +void (*pagetable_setup_start)(pgd_t *pgd_base); +void (*pagetable_setup_done)(pgd_t *pgd_base);void (*banner)(void );
@@ -363,6 +367,17 @@ static inline void setup_secondary_clock }#endif +static inline void paravirt_pagetable_setup_start (pgd_t *base) +{ +if (paravirt_ops.pagetable_setup_start) + (*paravirt_ops.pagetable_setup_start)(base); +} + +static inline void paravirt_pagetable_setup_done (pgd_t *base) +{ +if (paravirt_ops.pagetable_setup_done) + (*paravirt_ops.pagetable_setup_done)(base); +}void native_set_pte (pte_t *ptep,pte_t pteval);void native_set_pte_at (struct mm_struct *mm,u32 addr,pte_t *ptep,pte_t pteval);
===================================================================
include/asm-i386/pgtable.h +16 -0
@@ -497,6 +497,22 @@ do { \* tables contain all the necessary information. */ #define update_mmu_cache (vma,address,pte) do { } while (0) + +void native_pagetable_setup_start (pgd_t *base); +void native_pagetable_setup_done (pgd_t *base); + +#ifndef CONFIG_PARAVIRT +static inline void paravirt_pagetable_setup_start (pgd_t *base) +{ +native_pagetable_setup_start (base); +} + +static inline void paravirt_pagetable_setup_done (pgd_t *base) +{ +native_pagetable_setup_done (base); +} +#endif /* !CONFIG_PARAVIRT */ +#endif /* !__ASSEMBLY__ */ #ifdef CONFIG_FLATMEM
PATCH 7/34 Xen-pv_ops: Allocate a fixmap slot
In reply to Jeremy FitzhardingeAllocate a fixmap slot for use by a paravirt_ops implementation. This is intended for early-boot bootstrap mappings. Once the zones and allocator have been set up, it would be better to use get_vm_area() to allocate some virtual space.
Xen uses this to map the hypervisor's shared info page, which doesn't have a pseudo-physical page number, and therefore can't be mapped ordinarily. It is needed early because it contains the vcpu state, including the interrupt mask.
--- include/asm-i386/fixmap.h | 3 +++ 1 file changed, 3 insertions(+)
===================================================================
include/asm-i386/fixmap.h +3 -0
@@ -86,6 +86,9 @@ enum fixed_addresses {#ifdef CONFIG_PCI_MMCONFIG FIX_PCIE_MCFG,#endif +#ifdef CONFIG_PARAVIRT + FIX_PARAVIRT_BOOTMAP, +#endif __end_of_permanent_fixed_addresses,/* temporary boot-time mappings, used before ioremap() is functional */ #define NR_FIX_BTMAPS 16
PATCH 8/34 Xen-pv_ops: Allow paravirt backend to choose kernel PMD sharing
In reply to Jeremy FitzhardingeNormally when running in PAE mode, the 4th PMD maps the kernel address space, which can be shared among all processes (since they all need the same kernel mappings).
Xen, however, does not allow guests to have the kernel pmd shared between page tables, so parameterize pgtable.c to allow both modes of operation.
There are several side-effects of this. One is that vmalloc will update the kernel address space mappings, and those updates need to be propagated into all processes if the kernel mappings are not intrinsically shared. In the non-PAE case, this is done by maintaining a pgd_list of all processes; this list is used when all process pagetables must be updated. pgd_list is threaded via otherwise unused entries in the page structure for the pgd, which means that the pgd must be page-sized for this to work.
Normally the PAE pgd is only 4x64 byte entries large, but Xen requires the PAE pgd to page aligned anyway, so this patch forces the pgd to be page aligned+sized when the kernel pmd is unshared, to accomodate both these requirements.
Also, since there may be several distinct kernel pmds (if the user/kernel split is below 3G), there's no point in allocating them from a slab cache; they're just allocated with get_free_page and initialized appropriately. (Of course the could be cached if there is just a single kernel pmd - which is the default with a 3G user/kernel split - but it doesn't seem worthwhile to add yet another case into this code).
[ Many thanks to wli for review comments. ]
--- arch/i386/kernel/paravirt.c | 1 arch/i386/mm/fault.c | 6 +- arch/i386/mm/init.c | 18 +++++- arch/i386/mm/pageattr.c | 2 arch/i386/mm/pgtable.c | 84 ++++++++++++++++++++++++++------ include/asm-i386/paravirt.h | 1 include/asm-i386/pgtable-2level-defs.h | 2 include/asm-i386/pgtable-2level.h | 2 include/asm-i386/pgtable-3level-defs.h | 6 ++ include/asm-i386/pgtable-3level.h | 2 include/asm-i386/pgtable.h | 7 ++ 11 files changed, 105 insertions(+), 26 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +1 -0
@@ -604,6 +604,7 @@ struct paravirt_ops paravirt_ops = { .name = "bare hardware", .paravirt_enabled = 0, .kernel_rpl = 0, + .shared_kernel_pmd = 1, /* Only used when CONFIG_X86_PAE is set */ .patch = native_patch, .banner = default_banner,
===================================================================
arch/i386/mm/fault.c +3 -3
@@ -588,8 +588,7 @@ do_sigbus:force_sig_info_fault (SIGBUS ,BUS_ADRERR , address, tsk); } -#ifndef CONFIG_X86_PAE -void vmalloc_sync_all (void ) +void _vmalloc_sync_all (void ) {/* * Note that races in the updates of insync and start aren't
@@ -600,6 +599,8 @@ void vmalloc_sync_all(void) static DECLARE_BITMAP(insync, PTRS_PER_PGD);static unsigned long start =TASK_SIZE ;unsigned long address; + +BUG_ON (SHARED_KERNEL_PMD );BUILD_BUG_ON (TASK_SIZE & ~PGDIR_MASK );for (address = start; address >=TASK_SIZE ; address +=PGDIR_SIZE ) {
@@ -623,4 +624,3 @@ void vmalloc_sync_all(void) start = address +PGDIR_SIZE ; } } -#endif
===================================================================
arch/i386/mm/init.c +15 -3
@@ -715,6 +715,8 @@ struct kmem_cache *pmd_cache;void __init pgtable_cache_init (void ) { +size_t pgd_size =PTRS_PER_PGD *sizeof (pgd_t); +if (PTRS_PER_PMD >1 ) { pmd_cache = kmem_cache_create("pmd", PTRS_PER_PMD*sizeof(pmd_t),
@@ -724,13 +726,23 @@ void __init pgtable_cache_init(void) NULL);if (!pmd_cache)panic ("pgtable_cache_init(): cannot create pmd cache" ); + +if (!SHARED_KERNEL_PMD ) { +/* If we're in PAE mode and have a non-shared +kernel pmd, then the pgd size must be a +page size. This is because the pgd_list +links through the page structure, so there +can only be one pgd per page for this to +work. */ + pgd_size =PAGE_SIZE ; + } } pgd_cache =kmem_cache_create ("pgd" , -PTRS_PER_PGD *sizeof (pgd_t), -PTRS_PER_PGD *sizeof (pgd_t), + pgd_size, + pgd_size,0 , pgd_ctor, -PTRS_PER_PMD ==1 ? pgd_dtor :NULL ); + (!SHARED_KERNEL_PMD ) ? pgd_dtor :NULL );if (!pgd_cache)panic ("pgtable_cache_init(): Cannot create pgd cache" ); }
===================================================================
arch/i386/mm/pageattr.c +1 -1
@@ -91,7 +91,7 @@ static void set_pmd_pte(pte_t *kpte, unsunsigned long flags;set_pte_atomic (kpte, pte);/* change init_mm */ -if (PTRS_PER_PMD >1 ) +if (SHARED_KERNEL_PMD )return ;spin_lock_irqsave (&pgd_lock, flags);
===================================================================
arch/i386/mm/pgtable.c +69 -15
138 lines not on this page · raw message
===================================================================
include/asm-i386/paravirt.h +1 -0
@@ -34,6 +34,7 @@ struct paravirt_opsstruct paravirt_ops {unsigned int kernel_rpl; +int shared_kernel_pmd;int paravirt_enabled;const char *name;
===================================================================
include/asm-i386/pgtable-2level-defs.h +2 -0
@@ -1,5 +1,7 @@#ifndef _I386_PGTABLE_2LEVEL_DEFS_H#define _I386_PGTABLE_2LEVEL_DEFS_H + +#define SHARED_KERNEL_PMD 0/* * traditional i386 two-level paging structure:
===================================================================
include/asm-i386/pgtable-2level.h +0 -2
@@ -65,6 +65,4 @@ static inline int pte_exec_kernel(pte_t#define __pte_to_swp_entry (pte) ((swp_entry_t) { (pte).pte_low })#define __swp_entry_to_pte (x) ((pte_t) { (x).val }) -void vmalloc_sync_all (void ); -#endif /* _I386_PGTABLE_2LEVEL_H */
===================================================================
include/asm-i386/pgtable-3level-defs.h +6 -0
@@ -1,5 +1,11 @@#ifndef _I386_PGTABLE_3LEVEL_DEFS_H#define _I386_PGTABLE_3LEVEL_DEFS_H + +#ifdef CONFIG_PARAVIRT +#define SHARED_KERNEL_PMD (paravirt_ops.shared_kernel_pmd) +#else +#define SHARED_KERNEL_PMD 1 +#endif /* * PGDIR_SHIFT determines what a top-level page table entry can map
===================================================================
include/asm-i386/pgtable-3level.h +0 -2
@@ -180,6 +180,4 @@ static inline pmd_t pfn_pmd(unsigned lon#define __pmd_free_tlb (tlb, x) do { } while (0) -#define vmalloc_sync_all () ((void)0) -#endif /* _I386_PGTABLE_3LEVEL_H */
===================================================================
include/asm-i386/pgtable.h +7 -0
@@ -244,6 +244,13 @@ static inline pte_t pte_mkwrite(pte_t ptstatic inline pte_t pte_mkwrite (pte_t pte) { (pte).pte_low |= _PAGE_RW;return pte; }static inline pte_t pte_mkhuge (pte_t pte) { (pte).pte_low |= _PAGE_PSE;return pte; } +extern void _vmalloc_sync_all (void ); +static inline void vmalloc_sync_all (void ) +{ +if (!SHARED_KERNEL_PMD ) +_vmalloc_sync_all (); +} +#ifdef CONFIG_X86_PAE # include <asm/pgtable-3level.h> #else
PATCH 9/34 Xen-pv_ops: add hooks to intercept mm creation and destruction
In reply to Jeremy FitzhardingeAdd hooks to allow a paravirt implementation to track the lifetime of an mm.
--- arch/i386/kernel/paravirt.c | 4 ++++ include/asm-alpha/mmu_context.h | 1 + include/asm-arm/mmu_context.h | 1 + include/asm-arm26/mmu_context.h | 2 ++ include/asm-avr32/mmu_context.h | 1 + include/asm-cris/mmu_context.h | 2 ++ include/asm-frv/mmu_context.h | 1 + include/asm-generic/mm_hooks.h | 18 ++++++++++++++++++ include/asm-h8300/mmu_context.h | 1 + include/asm-i386/mmu_context.h | 17 +++++++++++++++-- include/asm-i386/paravirt.h | 23 +++++++++++++++++++++++ include/asm-ia64/mmu_context.h | 1 + include/asm-m32r/mmu_context.h | 1 + include/asm-m68k/mmu_context.h | 1 + include/asm-m68knommu/mmu_context.h | 1 + include/asm-mips/mmu_context.h | 1 + include/asm-parisc/mmu_context.h | 1 + include/asm-powerpc/mmu_context.h | 1 + include/asm-ppc/mmu_context.h | 1 + include/asm-s390/mmu_context.h | 2 ++ include/asm-sh/mmu_context.h | 1 + include/asm-sh64/mmu_context.h | 2 +- include/asm-sparc/mmu_context.h | 2 ++ include/asm-sparc64/mmu_context.h | 1 + include/asm-um/mmu_context.h | 2 ++ include/asm-v850/mmu_context.h | 2 ++ include/asm-x86_64/mmu_context.h | 1 + include/asm-xtensa/mmu_context.h | 1 + kernel/fork.c | 2 ++ mm/mmap.c | 4 ++++ 30 files changed, 96 insertions(+), 3 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +4 -0
@@ -520,6 +520,10 @@ struct paravirt_ops paravirt_ops = { .irq_enable_sysexit = native_irq_enable_sysexit, .iret = native_iret, + .dup_mmap = paravirt_nop, + .exit_mmap = paravirt_nop, + .activate_mm = paravirt_nop, + .startup_ipi_hook = paravirt_nop, };
===================================================================
include/asm-alpha/mmu_context.h +1 -0
@@ -10,6 +10,7 @@#include <asm/system.h> #include <asm/machvec.h> #include <asm/compiler.h> +#include <asm-generic/mm_hooks.h> /* * Force a context reload. This is needed when we change the page
===================================================================
include/asm-arm/mmu_context.h +1 -0
@@ -16,6 +16,7 @@#include <linux/compiler.h> #include <asm/cacheflush.h> #include <asm/proc-fns.h> +#include <asm-generic/mm_hooks.h> void __check_kvm_seq (struct mm_struct *mm);
===================================================================
include/asm-arm26/mmu_context.h +2 -0
@@ -12,6 +12,8 @@*/ #ifndef __ASM_ARM_MMU_CONTEXT_H#define __ASM_ARM_MMU_CONTEXT_H + +#include <asm-generic/mm_hooks.h> #define init_new_context (tsk,mm) 0#define destroy_context (mm) do { } while(0)
===================================================================
include/asm-avr32/mmu_context.h +1 -0
@@ -15,6 +15,7 @@#include <asm/tlbflush.h> #include <asm/pgalloc.h> #include <asm/sysreg.h> +#include <asm-generic/mm_hooks.h> /* * The MMU "context" consists of two things:
===================================================================
include/asm-cris/mmu_context.h +2 -0
@@ -1,5 +1,7 @@#ifndef __CRIS_MMU_CONTEXT_H#define __CRIS_MMU_CONTEXT_H + +#include <asm-generic/mm_hooks.h> extern int init_new_context (struct task_struct *tsk,struct mm_struct *mm);extern void get_mmu_context (struct mm_struct *mm);
===================================================================
include/asm-frv/mmu_context.h +1 -0
@@ -15,6 +15,7 @@#include <asm/setup.h> #include <asm/page.h> #include <asm/pgalloc.h> +#include <asm-generic/mm_hooks.h> static inline void enter_lazy_tlb (struct mm_struct *mm,struct task_struct *tsk) {
===================================================================
include/asm-generic/mm_hooks.h new file +18 -0
@@ -0,0 +1,18 @@ +/* +* Define generic no-op hooks for arch_dup_mmap and arch_exit_mmap, to +* be included in asm-FOO/mmu_context.h for any arch FOO which doesn't +* need to hook these. +*/ +#ifndef _ASM_GENERIC_MM_HOOKS_H +#define _ASM_GENERIC_MM_HOOKS_H + +static inline void arch_dup_mmap (struct mm_struct *oldmm, +struct mm_struct *mm) +{ +} + +static inline void arch_exit_mmap (struct mm_struct *mm) +{ +} + +#endif /* _ASM_GENERIC_MM_HOOKS_H */
===================================================================
include/asm-h8300/mmu_context.h +1 -0
@@ -4,6 +4,7 @@#include <asm/setup.h> #include <asm/page.h> #include <asm/pgalloc.h> +#include <asm-generic/mm_hooks.h> static inline void enter_lazy_tlb (struct mm_struct *mm,struct task_struct *tsk) {
===================================================================
include/asm-i386/mmu_context.h +15 -2
@@ -5,6 +5,16 @@#include <asm/atomic.h> #include <asm/pgalloc.h> #include <asm/tlbflush.h> +#include <asm/paravirt.h> +#ifndef CONFIG_PARAVIRT +#include <asm-generic/mm_hooks.h> + +static inline void paravirt_activate_mm (struct mm_struct *prev, +struct mm_struct *next) +{ +} +#endif /* !CONFIG_PARAVIRT */ +/* * Used for LDT copy/destruction.
@@ -65,7 +75,10 @@ static inline void switch_mm(struct mm_s#define deactivate_mm (tsk, mm) \ asm("movl %0,%%gs": :"r" (0)); -#define activate_mm (prev, next) \ - switch_mm((prev),(next),NULL) +#define activate_mm (prev, next) \ + do { \ + paravirt_activate_mm(prev, next); \ + switch_mm((prev),(next),NULL); \ + } while(0);#endif
===================================================================
include/asm-i386/paravirt.h +23 -0
@@ -119,6 +119,12 @@ struct paravirt_opsvoid (*io_delay)(void ); +void (*activate_mm)(struct mm_struct *prev, +struct mm_struct *next); +void (*dup_mmap)(struct mm_struct *oldmm, +struct mm_struct *mm); +void (*exit_mmap)(struct mm_struct *mm); +#ifdef CONFIG_X86_LOCAL_APIC void (*apic_write)(unsigned long reg,unsigned long v);void (*apic_write_atomic)(unsigned long reg,unsigned long v);
@@ -394,6 +400,23 @@ static inline void startup_ipi_hook(int }#endif +static inline void paravirt_activate_mm (struct mm_struct *prev, +struct mm_struct *next) +{ + paravirt_ops.activate_mm (prev, next); +} + +static inline void arch_dup_mmap (struct mm_struct *oldmm, +struct mm_struct *mm) +{ + paravirt_ops.dup_mmap (oldmm, mm); +} + +static inline void arch_exit_mmap (struct mm_struct *mm) +{ + paravirt_ops.exit_mmap (mm); +} +#define __flush_tlb () paravirt_ops.flush_tlb_user()#define __flush_tlb_global () paravirt_ops.flush_tlb_kernel()#define __flush_tlb_single (addr) paravirt_ops.flush_tlb_single(addr)
===================================================================
include/asm-ia64/mmu_context.h +1 -0
@@ -29,6 +29,7 @@#include <linux/spinlock.h> #include <asm/processor.h> +#include <asm-generic/mm_hooks.h> struct ia64_ctx {spinlock_t lock;
===================================================================
include/asm-m32r/mmu_context.h +1 -0
@@ -15,6 +15,7 @@#include <asm/pgalloc.h> #include <asm/mmu.h> #include <asm/tlbflush.h> +#include <asm-generic/mm_hooks.h> /* * Cache of MMU context last used.
===================================================================
include/asm-m68k/mmu_context.h +1 -0
@@ -1,6 +1,7 @@#ifndef __M68K_MMU_CONTEXT_H#define __M68K_MMU_CONTEXT_H +#include <asm-generic/mm_hooks.h> static inline void enter_lazy_tlb (struct mm_struct *mm,struct task_struct *tsk) {
===================================================================
include/asm-m68knommu/mmu_context.h +1 -0
@@ -4,6 +4,7 @@#include <asm/setup.h> #include <asm/page.h> #include <asm/pgalloc.h> +#include <asm-generic/mm_hooks.h> static inline void enter_lazy_tlb (struct mm_struct *mm,struct task_struct *tsk) {
===================================================================
include/asm-mips/mmu_context.h +1 -0
@@ -20,6 +20,7 @@#include <asm/mipsmtregs.h> #include <asm/smtc.h> #endif /* SMTC */ +#include <asm-generic/mm_hooks.h> /* * For the fast tlb miss handlers, we keep a per cpu array of pointers
===================================================================
include/asm-parisc/mmu_context.h +1 -0
@@ -5,6 +5,7 @@#include <asm/atomic.h> #include <asm/pgalloc.h> #include <asm/pgtable.h> +#include <asm-generic/mm_hooks.h> static inline void enter_lazy_tlb (struct mm_struct *mm,struct task_struct *tsk) {
===================================================================
include/asm-powerpc/mmu_context.h +1 -0
@@ -10,6 +10,7 @@#include <linux/mm.h> #include <asm/mmu.h> #include <asm/cputable.h> +#include <asm-generic/mm_hooks.h> /* * Copyright (C) 2001 PPC 64 Team, IBM Corp
===================================================================
include/asm-ppc/mmu_context.h +1 -0
@@ -6,6 +6,7 @@#include <asm/bitops.h> #include <asm/mmu.h> #include <asm/cputable.h> +#include <asm-generic/mm_hooks.h> /* * On 32-bit PowerPC 6xx/7xx/7xxx CPUs, we use a set of 16 VSIDs
===================================================================
include/asm-s390/mmu_context.h +2 -0
@@ -10,6 +10,8 @@#define __S390_MMU_CONTEXT_H#include <asm/pgalloc.h> +#include <asm-generic/mm_hooks.h> +/* * get a new mmu context.. S390 don't know about contexts. */
===================================================================
include/asm-sh/mmu_context.h +1 -0
@@ -12,6 +12,7 @@#include <asm/tlbflush.h> #include <asm/uaccess.h> #include <asm/io.h> +#include <asm-generic/mm_hooks.h> /* * The MMU "context" consists of two things:
===================================================================
include/asm-sh64/mmu_context.h +1 -1
@@ -27,7 +27,7 @@ extern unsigned long mmu_context_cache;extern unsigned long mmu_context_cache;#include <asm/page.h> - +#include <asm-generic/mm_hooks.h> /* Current mm's pgd */ extern pgd_t *mmu_pdtp_cache;
===================================================================
include/asm-sparc/mmu_context.h +2 -0
@@ -4,6 +4,8 @@#include <asm/btfixup.h> #ifndef __ASSEMBLY__ + +#include <asm-generic/mm_hooks.h> static inline void enter_lazy_tlb (struct mm_struct *mm,struct task_struct *tsk) {
===================================================================
include/asm-sparc64/mmu_context.h +1 -0
@@ -9,6 +9,7 @@#include <linux/spinlock.h> #include <asm/system.h> #include <asm/spitfire.h> +#include <asm-generic/mm_hooks.h> static inline void enter_lazy_tlb (struct mm_struct *mm,struct task_struct *tsk) {
===================================================================
include/asm-um/mmu_context.h +2 -0
@@ -5,6 +5,8 @@#ifndef __UM_MMU_CONTEXT_H#define __UM_MMU_CONTEXT_H + +#include <asm-generic/mm_hooks.h> #include "linux/sched.h" #include "choose-mode.h"
===================================================================
include/asm-v850/mmu_context.h +2 -0
@@ -1,5 +1,7 @@#ifndef __V850_MMU_CONTEXT_H__#define __V850_MMU_CONTEXT_H__ + +#include <asm-generic/mm_hooks.h> #define destroy_context (mm) ((void)0)#define init_new_context (tsk,mm) 0
===================================================================
include/asm-x86_64/mmu_context.h +1 -0
@@ -7,6 +7,7 @@#include <asm/pda.h> #include <asm/pgtable.h> #include <asm/tlbflush.h> +#include <asm-generic/mm_hooks.h> /* * possibly do the LDT unload here?
===================================================================
include/asm-xtensa/mmu_context.h +1 -0
@@ -18,6 +18,7 @@#include <asm/pgtable.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> +#include <asm-generic/mm_hooks.h> #define XCHAL_MMU_ASID_BITS 8
===================================================================
kernel/fork.c +2 -0
@@ -286,6 +286,8 @@ static inline int dup_mmap(struct mm_strif (retval) goto out; } +/* a new mm has just been created */ +arch_dup_mmap (oldmm, mm); retval =0 ; out:up_write (&mm->mmap_sem);
===================================================================
mm/mmap.c +4 -0
@@ -29,6 +29,7 @@#include <asm/uaccess.h> #include <asm/cacheflush.h> #include <asm/tlb.h> +#include <asm/mmu_context.h> #ifndef arch_mmap_check#define arch_mmap_check (addr, len, flags) (0)
@@ -1976,6 +1977,9 @@ void exit_mmap(struct mm_struct *mm)struct vm_area_struct *vma = mm->mmap;unsigned long nr_accounted =0 ;unsigned long end; + +/* mm's last user has gone, and its about to be pulled down */ +arch_exit_mmap (mm);lru_add_drain ();flush_cache_mm (mm);
PATCH 10/34 Xen-pv_ops: Simplify smp_call_function*() by using common implementation
In reply to Jeremy Fitzhardingesmp_call_function and smp_call_function_single are almost complete duplicates of the same logic. This patch combines them by implementing them in terms of the more general smp_call_function_mask().
--- arch/i386/kernel/smp.c | 213 ++++++++++++++++++++++-------------------------- 1 file changed, 102 insertions(+), 111 deletions(-)
===================================================================
arch/i386/kernel/smp.c +102 -111
@@ -515,6 +515,73 @@ void unlock_ipi_call_lock(void)static struct call_data_struct *call_data; + +/** +* smp_call_function_mask(): Run a function on a set of other CPUs. +* @mask: The set of cpus to run on. Must not include the current cpu. +* @func: The function to run. This must be fast and non-blocking. +* @info: An arbitrary pointer to pass to the function. +* @wait: If true, wait (atomically) until function has completed on other CPUs. +* +* Returns 0 on success, else a negative status code. Does not return until +* remote CPUs are nearly ready to execute <<func>> or are or have finished. +* +* You must not call this function with disabled interrupts or from a +* hardware interrupt handler or from a bottom half handler. +*/ +int smp_call_function_mask (cpumask_t mask, +void (*func)(void *),void *info, +int wait) +{ +struct call_data_struct data; +cpumask_t allbutself; +int cpus; + +/* Can deadlock when called with interrupts disabled */ +WARN_ON (irqs_disabled ()); + +/* Holding any lock stops cpus from going down. */ +spin_lock (&call_lock); + + allbutself = cpu_online_map; +cpu_clear (smp_processor_id (), allbutself); + +cpus_and (mask, mask, allbutself); + cpus =cpus_weight (mask); + +if (!cpus) { +spin_unlock (&call_lock); +return 0 ; + } + + data.func = func; + data.info = info; +atomic_set (&data.started,0 ); + data.wait = wait; +if (wait) +atomic_set (&data.finished,0 ); + + call_data = &data; +mb (); + +/* Send a message to other CPUs */ +if (cpus_equal (mask, allbutself)) +send_IPI_allbutself (CALL_FUNCTION_VECTOR ); +else +send_IPI_mask (mask,CALL_FUNCTION_VECTOR ); + +/* Wait for response */ +while (atomic_read (&data.started) != cpus) +cpu_relax (); + +if (wait) +while (atomic_read (&data.finished) != cpus) +cpu_relax (); +spin_unlock (&call_lock); + +return 0 ; +} +/** * smp_call_function(): Run a function on all other CPUs. * @func: The function to run. This must be fast and non-blocking.
@@ -528,48 +595,43 @@ static struct call_data_struct *call_dat* You must not call this function with disabled interrupts or from a * hardware interrupt handler or from a bottom half handler. */ -int smp_call_function (void (*func) (void *info),void *info,int nonatomic, -int wait) -{ -struct call_data_struct data; -int cpus; - -/* Holding any lock stops cpus from going down. */ -spin_lock (&call_lock); - cpus =num_online_cpus () -1 ; -if (!cpus) { -spin_unlock (&call_lock); -return 0 ; - } - -/* Can deadlock when called with interrupts disabled */ -WARN_ON (irqs_disabled ()); - - data.func = func; - data.info = info; -atomic_set (&data.started,0 ); - data.wait = wait; -if (wait) -atomic_set (&data.finished,0 ); - - call_data = &data; -mb (); - -/* Send a message to all other CPUs and wait for them to respond */ -send_IPI_allbutself (CALL_FUNCTION_VECTOR ); - -/* Wait for response */ -while (atomic_read (&data.started) != cpus) -cpu_relax (); - -if (wait) -while (atomic_read (&data.finished) != cpus) -cpu_relax (); -spin_unlock (&call_lock); - -return 0 ; +int smp_call_function (void (*func) (void *info),void *info,int nonatomic, +int wait) +{ +return smp_call_function_mask (cpu_online_map, func, info, wait); }EXPORT_SYMBOL (smp_call_function); + +/* +* smp_call_function_single - Run a function on another CPU +* @func: The function to run. This must be fast and non-blocking. +* @info: An arbitrary pointer to pass to the function. +* @nonatomic: Currently unused. +* @wait: If true, wait until function has completed on other CPUs. +* +* Retrurns 0 on success, else a negative status code. +* +* Does not return until the remote CPU is nearly ready to execute <func> +* or is or has executed. +*/ +int smp_call_function_single (int cpu,void (*func) (void *info),void *info, +int nonatomic,int wait) +{ +/* prevent preemption and reschedule on another processor */ +int ret; +int me =get_cpu (); +if (cpu == me) { +WARN_ON (1 ); +put_cpu (); +return -EBUSY ; + } + + ret =smp_call_function_mask (cpumask_of_cpu (cpu), func, info, wait); + +put_cpu (); +return ret; +} +EXPORT_SYMBOL (smp_call_function_single);static void stop_this_cpu (void * dummy) {
@@ -633,77 +695,6 @@ fastcall void smp_call_function_interrup } } -/* -* this function sends a 'generic call function' IPI to one other CPU -* in the system. -* -* cpu is a standard Linux logical CPU number. -*/ -static void -__smp_call_function_single (int cpu,void (*func) (void *info),void *info, -int nonatomic,int wait) -{ -struct call_data_struct data; -int cpus =1 ; - - data.func = func; - data.info = info; -atomic_set (&data.started,0 ); - data.wait = wait; -if (wait) -atomic_set (&data.finished,0 ); - - call_data = &data; -wmb (); -/* Send a message to all other CPUs and wait for them to respond */ -send_IPI_mask (cpumask_of_cpu (cpu),CALL_FUNCTION_VECTOR ); - -/* Wait for response */ -while (atomic_read (&data.started) != cpus) -cpu_relax (); - -if (!wait) -return ; - -while (atomic_read (&data.finished) != cpus) -cpu_relax (); -} - -/* -* smp_call_function_single - Run a function on another CPU -* @func: The function to run. This must be fast and non-blocking. -* @info: An arbitrary pointer to pass to the function. -* @nonatomic: Currently unused. -* @wait: If true, wait until function has completed on other CPUs. -* -* Retrurns 0 on success, else a negative status code. -* -* Does not return until the remote CPU is nearly ready to execute <func> -* or is or has executed. -*/ - -int smp_call_function_single (int cpu,void (*func) (void *info),void *info, -int nonatomic,int wait) -{ -/* prevent preemption and reschedule on another processor */ -int me =get_cpu (); -if (cpu == me) { -WARN_ON (1 ); -put_cpu (); -return -EBUSY ; - } - -/* Can deadlock when called with interrupts disabled */ -WARN_ON (irqs_disabled ()); - -spin_lock_bh (&call_lock); -__smp_call_function_single (cpu, func, info, nonatomic, wait); -spin_unlock_bh (&call_lock); -put_cpu (); -return 0 ; -} -EXPORT_SYMBOL (smp_call_function_single); -static int convert_apicid_to_cpu (int apic_id) {int i;
Re: [patch 10/34] Xen-pv_ops: Simplify smp_call_function*() by using common implementation
Quoted 4 lines · On Tue, 13 Mar 2007 16:30:27 -0700 Jeremy Fitzhardinge wrote:
smp_call_functionandsmp_call_function_singleare almost complete duplicates of the same logic. This patch combines them by implementing them in terms of the more generalsmp_call_function_mask().
The kernel-doc is still not quite correct. Patch below applies on top of this patch from Jeremy.
---
From: Randy Dunlap <[email protected]>
Clean up arch/i386/kernel/smp.c after the Xen pv_ops patches for smp_call_function variants.
--- arch/i386/kernel/smp.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
arch/i386/kernel/smp.c +7 -6
@@ -517,14 +517,14 @@ static struct call_data_struct *call_dat/** -* smp_call_function_mask(): Run a function on a set of other CPUs. +* smp_call_function_mask - Run a function on a set of other CPUs. * @mask: The set of cpus to run on. Must not include the current cpu. * @func: The function to run. This must be fast and non-blocking. * @info: An arbitrary pointer to pass to the function. * @wait: If true, wait (atomically) until function has completed on other CPUs. * * Returns 0 on success, else a negative status code. Does not return until -* remote CPUs are nearly ready to execute <<func>> or are or have finished. +* remote CPUs are nearly ready to execute func() or are or have finished. * * You must not call this function with disabled interrupts or from a * hardware interrupt handler or from a bottom half handler.
@@ -583,14 +583,14 @@ int smp_call_function_mask(cpumask_t mas }/** -* smp_call_function(): Run a function on all other CPUs. +* smp_call_function - Run a function on all other CPUs. * @func: The function to run. This must be fast and non-blocking. * @info: An arbitrary pointer to pass to the function. * @nonatomic: currently unused. * @wait: If true, wait (atomically) until function has completed on other CPUs. * * Returns 0 on success, else a negative status code. Does not return until -* remote CPUs are nearly ready to execute <<func>> or are or have executed. +* remote CPUs are nearly ready to execute func() or are or have executed. * * You must not call this function with disabled interrupts or from a * hardware interrupt handler or from a bottom half handler.
@@ -602,8 +602,9 @@ int smp_call_function(void (*func) (void }EXPORT_SYMBOL (smp_call_function); -/* +/** * smp_call_function_single - Run a function on another CPU +* @cpu: The target (destination) CPU number. * @func: The function to run. This must be fast and non-blocking. * @info: An arbitrary pointer to pass to the function. * @nonatomic: Currently unused.
@@ -611,7 +612,7 @@ EXPORT_SYMBOL(smp_call_function);* * Retrurns 0 on success, else a negative status code. * -* Does not return until the remote CPU is nearly ready to execute <func> +* Does not return until the remote CPU is nearly ready to execute func() * or is or has executed. */ int smp_call_function_single(int cpu, void (*func) (void *info), void *info,
PATCH 11/34 Xen-pv_ops: Add smp_ops interface
In reply to Jeremy FitzhardingeAdd a smp_ops interface. This abstracts the API defined by <linux/smp.h> for use within arch/i386. The primary intent is that it be used by a paravirtualizing hypervisor to implement SMP, but it could also be used by non-APIC-using sub-archtectures.
This is related to CONFIG_PARAVIRT, but is implemented unconditionally since it is simpler that way and not a highly performance-sensitive interface.
--- arch/i386/kernel/smp.c | 21 +++++++++++++---- arch/i386/kernel/smpboot.c | 8 +++--- include/asm-i386/smp.h | 53 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 9 deletions(-)
===================================================================
arch/i386/kernel/smp.c +16 -5
@@ -483,7 +483,7 @@ void flush_tlb_all(void)* it goes straight through and wastes no time serializing * anything. Worst case is that we lose a reschedule ... */ -void smp_send_reschedule (int cpu) +void native_smp_send_reschedule (int cpu) {WARN_ON (cpu_is_offline (cpu));send_IPI_mask (cpumask_of_cpu (cpu),RESCHEDULE_VECTOR );
@@ -529,9 +529,9 @@ static struct call_data_struct *call_dat* You must not call this function with disabled interrupts or from a * hardware interrupt handler or from a bottom half handler. */ -int smp_call_function_mask (cpumask_t mask, -void (*func)(void *),void *info, -int wait) +static int native_smp_call_function_mask (cpumask_t mask, +void (*func)(void *),void *info, +int wait) {struct call_data_struct data;cpumask_t allbutself;
@@ -650,7 +650,7 @@ static void stop_this_cpu (void * dummy)* this function calls the 'stop' function on all other CPUs in the system. */ -void smp_send_stop (void ) +void native_smp_send_stop (void ) {smp_call_function (stop_this_cpu,NULL ,1 ,0 );
@@ -721,3 +721,14 @@ int safe_smp_processor_id(void)return cpuid >=0 ? cpuid :0 ; } + +struct smp_ops smp_ops = { + .smp_prepare_boot_cpu = native_smp_prepare_boot_cpu, + .smp_prepare_cpus = native_smp_prepare_cpus, + .cpu_up = native_cpu_up, + .smp_cpus_done = native_smp_cpus_done, + + .smp_send_stop = native_smp_send_stop, + .smp_send_reschedule = native_smp_send_reschedule, + .smp_call_function_mask = native_smp_call_function_mask, +};
===================================================================
arch/i386/kernel/smpboot.c +4 -4
@@ -1173,7 +1173,7 @@ static void __init smp_boot_cpus(unsigne/* These are wrappers to interface to the new boot process. Someone who understands all this stuff should rewrite it properly. --RR 15/Jul/02 */ -void __init smp_prepare_cpus (unsigned int max_cpus) +void __init native_smp_prepare_cpus (unsigned int max_cpus) { smp_commenced_mask =cpumask_of_cpu (0 ); cpu_callin_map =cpumask_of_cpu (0 );
@@ -1181,7 +1181,7 @@ void __init smp_prepare_cpus(unsigned insmp_boot_cpus (max_cpus); } -void __devinit smp_prepare_boot_cpu (void ) +void __devinit native_smp_prepare_boot_cpu (void ) {cpu_set (smp_processor_id (), cpu_online_map);cpu_set (smp_processor_id (), cpu_callout_map);
@@ -1277,7 +1277,7 @@ void __cpu_die(unsigned int cpu) }#endif /* CONFIG_HOTPLUG_CPU */ -int __cpuinit __cpu_up (unsigned int cpu) +int __cpuinit native_cpu_up (unsigned int cpu) {unsigned long flags;#ifdef CONFIG_HOTPLUG_CPU
@@ -1327,7 +1327,7 @@ int __cpuinit __cpu_up(unsigned int cpu)return 0 ; } -void __init smp_cpus_done (unsigned int max_cpus) +void __init native_smp_cpus_done (unsigned int max_cpus) {#ifdef CONFIG_X86_IO_APIC setup_ioapic_dest ();
===================================================================
include/asm-i386/smp.h +53 -0
59 lines not on this page · raw message
PATCH 12/34 Xen-pv_ops: rename struct paravirt_patch to paravirt_patch_site for clarity
In reply to Jeremy FitzhardingeRename struct paravirt_patch to paravirt_patch_site, so that it clearly refers to a callsite, and not the patch which may be applied to that callsite.
--- arch/i386/kernel/alternative.c | 8 +++----- arch/i386/kernel/vmi.c | 4 ---- include/asm-i386/alternative.h | 6 +++--- include/asm-i386/paravirt.h | 5 ++++- 4 files changed, 10 insertions(+), 13 deletions(-)
===================================================================
arch/i386/kernel/alternative.c +3 -5
@@ -350,9 +350,9 @@ void alternatives_smp_switch(int smp)#endif #ifdef CONFIG_PARAVIRT -void apply_paravirt (struct paravirt_patch *start,struct paravirt_patch *end) -{ -struct paravirt_patch *p; +void apply_paravirt (struct paravirt_patch_site *start,struct paravirt_patch_site *end) +{ +struct paravirt_patch_site *p;for (p = start; p < end; p++) {unsigned int used;
@@ -379,8 +379,6 @@ void apply_paravirt(struct paravirt_patc/* Sync to be conservative, in case we patched following instructions */ sync_core(); } -extern struct paravirt_patch __start_parainstructions[], - __stop_parainstructions[];#endif /* CONFIG_PARAVIRT */ void __init alternative_instructions (void )
===================================================================
arch/i386/kernel/vmi.c +0 -4
@@ -70,10 +70,6 @@ struct {void (*set_initial_ap_state)(int ,int );void (*halt)(void ); } vmi_ops; - -/* XXX move this to alternative.h */ -extern struct paravirt_patch __start_parainstructions[], - __stop_parainstructions[];/* * VMI patching routines.
===================================================================
include/asm-i386/alternative.h +3 -3
@@ -118,12 +118,12 @@ static inline void alternatives_smp_swit#define LOCK_PREFIX ""#endif -struct paravirt_patch ; +struct paravirt_patch_site ;#ifdef CONFIG_PARAVIRT -void apply_paravirt (struct paravirt_patch *start,struct paravirt_patch *end); +void apply_paravirt (struct paravirt_patch_site *start,struct paravirt_patch_site *end);#else static inline void -apply_paravirt (struct paravirt_patch *start,struct paravirt_patch *end) +apply_paravirt (struct paravirt_patch_site *start,struct paravirt_patch_site *end) {}#define __start_parainstructions NULL#define __stop_parainstructions NULL
===================================================================
include/asm-i386/paravirt.h +4 -1
@@ -501,12 +501,15 @@ void _paravirt_nop(void);#define paravirt_nop ((void *)_paravirt_nop)/* These all sit in the .parainstructions section to tell us what to patch. */ -struct paravirt_patch { +struct paravirt_patch_site {u8 *instr;/* original instructions */ u8 instrtype;/* type of this instruction */ u8 len;/* length of original instruction */ u16 clobbers;/* what registers you may clobber */ }; + +extern struct paravirt_patch_site __start_parainstructions[], + __stop_parainstructions[];#define paravirt_alt (insn_string, typenum, clobber) \ "771:\n\t" insn_string "\n" "772:\n" \
PATCH 13/34 Xen-pv_ops: Use patch site IDs computed from offset in paravirt_ops structure
In reply to Jeremy FitzhardingeUse patch type identifiers derived from the offset of the operation in the paravirt_ops structure. This avoids having to maintain a separate enum for patch site types.
This patch also drops the fused save_fl+cli operation, which doesn't really add much and makes things more complex.
--- arch/i386/kernel/asm-offsets.c | 5 + arch/i386/kernel/paravirt.c | 14 ++--- arch/i386/kernel/vmi.c | 39 ++------------ include/asm-i386/paravirt.h | 107 +++++++++++++++++++--------------------- 4 files changed, 70 insertions(+), 95 deletions(-)
===================================================================
arch/i386/kernel/asm-offsets.c +5 -0
@@ -110,5 +110,10 @@ void foo(void)OFFSET (PARAVIRT_irq_enable_sysexit, paravirt_ops, irq_enable_sysexit);OFFSET (PARAVIRT_iret, paravirt_ops, iret);OFFSET (PARAVIRT_read_cr0, paravirt_ops, read_cr0); + +DEFINE (PARAVIRT_IRQ_DISABLE ,PARAVIRT_PATCH (irq_disable)); +DEFINE (PARAVIRT_IRQ_ENABLE ,PARAVIRT_PATCH (irq_enable)); +DEFINE (PARAVIRT_STI_SYSEXIT ,PARAVIRT_PATCH (irq_enable_sysexit)); +DEFINE (PARAVIRT_INTERRUPT_RETURN ,PARAVIRT_PATCH (iret));#endif }
===================================================================
arch/i386/kernel/paravirt.c +6 -8
@@ -58,7 +58,6 @@ DEF_NATIVE(sti, "sti");DEF_NATIVE (sti,"sti" );DEF_NATIVE (popf,"push %eax; popf" );DEF_NATIVE (pushf,"pushf; pop %eax" ); -DEF_NATIVE (pushf_cli,"pushf; pop %eax; cli" );DEF_NATIVE (iret,"iret" );DEF_NATIVE (sti_sysexit,"sti; sysexit" );
@@ -66,13 +65,12 @@ static const struct native_insns {const char *start, *end; } native_insns[] = { - [PARAVIRT_IRQ_DISABLE] = { start_cli, end_cli }, - [PARAVIRT_IRQ_ENABLE] = { start_sti, end_sti }, - [PARAVIRT_RESTORE_FLAGS] = { start_popf, end_popf }, - [PARAVIRT_SAVE_FLAGS] = { start_pushf, end_pushf }, - [PARAVIRT_SAVE_FLAGS_IRQ_DISABLE] = { start_pushf_cli, end_pushf_cli }, - [PARAVIRT_INTERRUPT_RETURN] = { start_iret, end_iret }, - [PARAVIRT_STI_SYSEXIT] = { start_sti_sysexit, end_sti_sysexit }, + [PARAVIRT_PATCH(irq_disable)] = { start_cli, end_cli }, + [PARAVIRT_PATCH(irq_enable)] = { start_sti, end_sti }, + [PARAVIRT_PATCH(restore_fl)] = { start_popf, end_popf }, + [PARAVIRT_PATCH(save_fl)] = { start_pushf, end_pushf }, + [PARAVIRT_PATCH(iret)] = { start_iret, end_iret }, + [PARAVIRT_PATCH(irq_enable_sysexit)] = { start_sti_sysexit, end_sti_sysexit }, };static unsigned native_patch (u8 type,u16 clobbers,void *insns,unsigned len)
===================================================================
arch/i386/kernel/vmi.c +6 -33
@@ -80,11 +80,6 @@ struct {#define MNEM_JMP 0xe9#define MNEM_RET 0xc3 -static char irq_save_disable_callout[] = { -MNEM_CALL ,0 ,0 ,0 ,0 , -MNEM_CALL ,0 ,0 ,0 ,0 , -MNEM_RET -};#define IRQ_PATCH_INT_MASK 0#define IRQ_PATCH_DISABLE 5
@@ -132,33 +127,17 @@ static unsigned vmi_patch(u8 type, u16 cstatic unsigned vmi_patch (u8 type,u16 clobbers,void *insns,unsigned len) {switch (type) { -case PARAVIRT_IRQ_DISABLE : +case PARAVIRT_PATCH (irq_disable):return patch_internal (VMI_CALL_DisableInterrupts, len, insns); -case PARAVIRT_IRQ_ENABLE : +case PARAVIRT_PATCH (irq_enable):return patch_internal (VMI_CALL_EnableInterrupts, len, insns); -case PARAVIRT_RESTORE_FLAGS : +case PARAVIRT_PATCH (restore_fl):return patch_internal (VMI_CALL_SetInterruptMask, len, insns); -case PARAVIRT_SAVE_FLAGS : +case PARAVIRT_PATCH (save_fl):return patch_internal (VMI_CALL_GetInterruptMask, len, insns); -case PARAVIRT_SAVE_FLAGS_IRQ_DISABLE : -if (len >=10 ) { -patch_internal (VMI_CALL_GetInterruptMask, len, insns); -patch_internal (VMI_CALL_DisableInterrupts, len-5 , insns+5 ); -return 10 ; - }else { -/* -* You bastards didn't leave enough room to -* patch save_flags_irq_disable inline. Patch -* to a helper -*/ -BUG_ON (len <5 ); - *(char *)insns =MNEM_CALL ; -patch_offset (insns, irq_save_disable_callout); -return 5 ; - } -case PARAVIRT_INTERRUPT_RETURN : +case PARAVIRT_PATCH (iret):return patch_internal (VMI_CALL_IRET , len, insns); -case PARAVIRT_STI_SYSEXIT : +case PARAVIRT_PATCH (irq_enable_sysexit):return patch_internal (VMI_CALL_SYSEXIT , len, insns);default :break ;
@@ -768,12 +747,6 @@ static inline int __init activate_vmi(vopara_fill (restore_fl, SetInterruptMask);para_fill (irq_disable, DisableInterrupts);para_fill (irq_enable, EnableInterrupts); - -/* irq_save_disable !!! sheer pain */ -patch_offset (&irq_save_disable_callout[IRQ_PATCH_INT_MASK ], - (char *)paravirt_ops.save_fl); -patch_offset (&irq_save_disable_callout[IRQ_PATCH_DISABLE ], - (char *)paravirt_ops.irq_disable);para_fill (wbinvd,WBINVD );para_fill (read_tsc,RDTSC );
===================================================================
include/asm-i386/paravirt.h +53 -54
161 lines not on this page · raw message
PATCH 14/34 Xen-pv_ops: Fix patch site clobbers to include return register
In reply to Jeremy FitzhardingeFix a few clobbers to include the return register. The clobbers set is the set of all registers modified (or may be modified) by the code snippet, regardless of whether it was deliberate or accidental.
--- include/asm-i386/paravirt.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
===================================================================
include/asm-i386/paravirt.h +2 -2
@@ -556,7 +556,7 @@ static inline unsigned long __raw_local_ "popl %%edx; popl %%ecx") : "=a"(f): "m"(paravirt_ops.save_fl), paravirt_type(PARAVIRT_PATCH(save_fl)), - paravirt_clobber(CLBR_NONE) + paravirt_clobber(CLBR_EAX) : "memory", "cc");return f; }
@@ -610,7 +610,7 @@ static inline unsigned long __raw_local_ "%c[paravirt_cli_type]", "%c[paravirt_clobber]")#define STI_STRING _paravirt_alt("pushl %%ecx; pushl %%edx;" \ - "call *paravirt_ops+%c[irq_enable];" \ + "call *paravirt_ops+%c[irq_enable];" \ "popl %%edx; popl %%ecx", \ "%c[paravirt_cli_type]", "%c[paravirt_clobber]")
PATCH 15/34 Xen-pv_ops: Consistently wrap paravirt ops callsites to make them patchable
In reply to Jeremy FitzhardingeWrap a set of interesting paravirt_ops calls in a wrapper which makes the callsites available for patching. Unfortunately this is pretty ugly because there's no way to get gcc to generate a function call, but also wrap just the callsite itself with the necessary labels.
This patch supports functions with 0-4 arguments, and either void or returning a value. 64-bit arguments must be split into a pair of 32-bit arguments (lower word first). Small structures are returned in registers.
--- include/asm-i386/paravirt.h | 580 ++++++++++++++++++++++++++++++++----------- 1 file changed, 445 insertions(+), 135 deletions(-)
===================================================================
include/asm-i386/paravirt.h +445 -135
@@ -95,14 +95,10 @@ struct paravirt_opsvoid (*set_ldt)(const void *desc,unsigned entries);unsigned long (*store_tr)(void );void (*load_tls)(struct thread_struct *t,unsigned int cpu); -void (*write_ldt_entry)(void *dt,int entrynum, -u32 low,u32 high); -void (*write_gdt_entry)(void *dt,int entrynum, -u32 low,u32 high); -void (*write_idt_entry)(void *dt,int entrynum, -u32 low,u32 high); -void (*load_esp0)(struct tss_struct *tss, -struct thread_struct *thread); +void (*write_ldt_entry)(void *dt,int entrynum,u32 low,u32 high); +void (*write_gdt_entry)(void *dt,int entrynum,u32 low,u32 high); +void (*write_idt_entry)(void *dt,int entrynum,u32 low,u32 high); +void (*load_esp0)(struct tss_struct *tss,struct thread_struct *thread);void (*set_iopl_mask)(unsigned mask);
@@ -197,23 +193,242 @@ extern struct paravirt_ops paravirt_ops;#define paravirt_alt (insn_string) \ _paravirt_alt(insn_string, "%c[paravirt_typenum]", "%c[paravirt_clobber]") -#define paravirt_enabled () (paravirt_ops.paravirt_enabled) +#define PVOP_CALL0 (__rettype, __op) \ + ({ \ + __rettype __ret; \ + if (sizeof(__rettype) > sizeof(unsigned long)) { \ + unsigned long long __tmp; \ + unsigned long __ecx; \ + asm volatile(paravirt_alt("call *%[op]") \ + : "=A" (__tmp), "=c" (__ecx) \ + : [op] "m" (paravirt_ops.__op), \ + paravirt_type(PARAVIRT_PATCH(__op)), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + __ret = (__rettype)__tmp; \ + } else { \ + unsigned long __tmp, __edx, __ecx; \ + asm volatile(paravirt_alt("call *%[op]") \ + : "=a" (__tmp), "=d" (__edx), "=c" (__ecx) \ + : [op] "m" (paravirt_ops.__op), \ + paravirt_type(PARAVIRT_PATCH(__op)), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + __ret = (__rettype)__tmp; \ + } \ + __ret; \ + }) +#define PVOP_VCALL0 (__op) \ + ({ \ + unsigned long __eax, __edx, __ecx; \ + asm volatile(paravirt_alt("call *%[op]") \ + : "=a" (__eax), "=d" (__edx), "=c" (__ecx) \ + : [op] "m" (paravirt_ops.__op), \ + paravirt_type(PARAVIRT_PATCH(__op)), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + }) + +#define PVOP_CALL1 (__rettype, __op, arg1) \ + ({ \ + __rettype __ret; \ + if (sizeof(__rettype) > sizeof(unsigned long)) { \ + unsigned long long __tmp; \ + unsigned long __ecx; \ + asm volatile(paravirt_alt("call *%[op]") \ + : "=A" (__tmp), "=c" (__ecx) \ + : [op] "m" (paravirt_ops.__op), \ + "a" ((u32)(arg1)), \ + paravirt_type(PARAVIRT_PATCH(__op)), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + __ret = (__rettype)__tmp; \ + } else { \ + unsigned long __tmp, __edx, __ecx; \ + asm volatile(paravirt_alt("call *%[op]") \ + : "=a" (__tmp), "=d" (__edx), "=c" (__ecx) \ + : [op] "m" (paravirt_ops.__op), \ + "0" ((u32)(arg1)), \ + paravirt_type(PARAVIRT_PATCH(__op)), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + __ret = (__rettype)__tmp; \ + } \ + __ret; \ + }) +#define PVOP_VCALL1 (__op, arg1) \ + ({ \ + unsigned long __eax, __edx, __ecx; \ + asm volatile(paravirt_alt("call *%[op]") \ + : "=a" (__eax), "=d" (__edx), "=c" (__ecx) \ + : [op] "m" (paravirt_ops.__op), \ + "0" ((u32)(arg1)), \ + paravirt_type(PARAVIRT_PATCH(__op)), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + }) + +#define PVOP_CALL2 (__rettype, __op, arg1, arg2) \ + ({ \ + __rettype __ret; \ + if (sizeof(__rettype) > sizeof(unsigned long)) { \ + unsigned long long __tmp; \ + unsigned long __ecx; \ + asm volatile(paravirt_alt("call *%[op]") \ + : "=A" (__tmp), "=c" (__ecx) \ + : [op] "m" (paravirt_ops.__op), \ + "a" ((u32)(arg1)), \ + "d" ((u32)(arg2)), \ + paravirt_type(PARAVIRT_PATCH(__op)), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + __ret = (__rettype)__tmp; \ + } else { \ + unsigned long __tmp, __edx, __ecx; \ + asm volatile(paravirt_alt("call *%[op]") \ + : "=a" (__tmp), "=d" (__edx), "=c" (__ecx) \ + : [op] "m" (paravirt_ops.__op), \ + "0" ((u32)(arg1)), \ + "1" ((u32)(arg2)), \ + paravirt_type(PARAVIRT_PATCH(__op)), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + __ret = (__rettype)__tmp; \ + } \ + __ret; \ + }) +#define PVOP_VCALL2 (__op, arg1, arg2) \ + ({ \ + unsigned long __eax, __edx, __ecx; \ + asm volatile(paravirt_alt("call *%[op]") \ + : "=a" (__eax), "=d" (__edx), "=c" (__ecx) \ + : [op] "m" (paravirt_ops.__op), \ + "0" ((u32)(arg1)), \ + "1" ((u32)(arg2)), \ + paravirt_type(PARAVIRT_PATCH(__op)), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + }) + +#define PVOP_CALL3 (__rettype, __op, arg1, arg2, arg3) \ + ({ \ + __rettype __ret; \ + if (sizeof(__rettype) > sizeof(unsigned long)) { \ + unsigned long long __tmp; \ + unsigned long __ecx; \ + asm volatile(paravirt_alt("call *%[op]") \ + : "=A" (__tmp), "=c" (__ecx) \ + : [op] "m" (paravirt_ops.__op), \ + "a" ((u32)(arg1)), \ + "d" ((u32)(arg2)), \ + "1" ((u32)(arg3)), \ + paravirt_type(PARAVIRT_PATCH(__op)), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + __ret = (__rettype)__tmp; \ + } else { \ + unsigned long __tmp, __edx, __ecx; \ + asm volatile(paravirt_alt("call *%[op]") \ + : "=a" (__tmp), "=d" (__edx), "=c" (__ecx) \ + : [op] "m" (paravirt_ops.__op), \ + "0" ((u32)(arg1)), \ + "1" ((u32)(arg2)), \ + "2" ((u32)(arg3)), \ + paravirt_type(PARAVIRT_PATCH(__op)), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + __ret = (__rettype)__tmp; \ + } \ + __ret; \ + }) +#define PVOP_VCALL3 (__op, arg1, arg2, arg3) \ + ({ \ + unsigned long __eax, __edx, __ecx; \ + asm volatile(paravirt_alt("call *%[op]") \ + : "=a" (__eax), "=d" (__edx), "=c" (__ecx) \ + : [op] "m" (paravirt_ops.__op), \ + "0" ((u32)(arg1)), \ + "1" ((u32)(arg2)), \ + "2" ((u32)(arg3)), \ + paravirt_type(PARAVIRT_PATCH(__op)), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + }) + +#define PVOP_CALL4 (__rettype, __op, arg1, arg2, arg3, arg4) \ + ({ \ + __rettype __ret; \ + if (sizeof(__rettype) > sizeof(unsigned long)) { \ + unsigned long long __tmp; \ + unsigned long __ecx; \ + asm volatile("push %[_arg4]; " \ + paravirt_alt("call *%[op]") \ + "lea 4(%%esp),%%esp" \ + : "=A" (__tmp), "=c" (__ecx) \ + : [op] "m" (paravirt_ops.__op), \ + "a" ((u32)(arg1)), \ + "d" ((u32)(arg2)), \ + "1" ((u32)(arg3)), \ + [_arg4] "mr" ((u32)(arg4)), \ + paravirt_type(PARAVIRT_PATCH(__op)), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc",); \ + __ret = (__rettype)__tmp; \ + } else { \ + unsigned long __tmp, __edx, __ecx; \ + asm volatile("push %[_arg4]; " \ + paravirt_alt("call *%[op]") \ + "lea 4(%%esp),%%esp" \ + : "=a" (__tmp), "=d" (__edx), "=c" (__ecx) \ + : [op] "m" (paravirt_ops.__op), \ + "0" ((u32)(arg1)), \ + "1" ((u32)(arg2)), \ + "2" ((u32)(arg3)), \ + [_arg4]"mr" ((u32)(arg4)), \ + paravirt_type(PARAVIRT_PATCH(__op)), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + __ret = (__rettype)__tmp; \ + } \ + __ret; \ + }) +#define PVOP_VCALL4 (__op, arg1, arg2, arg3, arg4) \ + ({ \ + unsigned long __eax, __edx, __ecx; \ + asm volatile("push %[_arg4]; " \ + paravirt_alt("call *%[op]") \ + "lea 4(%%esp),%%esp" \ + : "=a" (__eax), "=d" (__edx), "=c" (__ecx) \ + : [op] "m" (paravirt_ops.__op), \ + "0" ((u32)(arg1)), \ + "1" ((u32)(arg2)), \ + "2" ((u32)(arg3)), \ + [_arg4]"mr" ((u32)(arg4)), \ + paravirt_type(PARAVIRT_PATCH(__op)), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + }) + +static inline int paravirt_enabled (void ) +{ +return paravirt_ops.paravirt_enabled; +}static inline void load_esp0 (struct tss_struct *tss,struct thread_struct *thread) { - paravirt_ops.load_esp0 (tss, thread); -} - -#define ARCH_SETUP paravirt_ops.arch_setup(); +PVOP_VCALL2 (load_esp0, tss, thread); +} + +#define ARCH_SETUP PVOP_VCALL0(arch_setup);static inline unsigned long get_wallclock (void ) { -return paravirt_ops.get_wallclock (); + return PVOP_CALL0(unsigned long, get_wallclock); }static inline int set_wallclock (unsigned long nowtime) { -return paravirt_ops.set_wallclock (nowtime); +return PVOP_CALL1 (int, set_wallclock, nowtime); }static inline void (*choose_time_init (void ))(void )
@@ -225,88 +440,80 @@ static inline void __cpuid(unsigned intstatic inline void __cpuid (unsigned int *eax,unsigned int *ebx,unsigned int *ecx,unsigned int *edx) { - paravirt_ops.cpuid (eax, ebx, ecx, edx); +PVOP_VCALL4 (cpuid, eax, ebx, ecx, edx); }/* * These special macros can be used to get or set a debugging register */ -#define get_debugreg (var, reg) var = paravirt_ops.get_debugreg(reg) -#define set_debugreg (val, reg) paravirt_ops.set_debugreg(reg, val) - -#define clts () paravirt_ops.clts() - -#define read_cr0 () paravirt_ops.read_cr0() -#define write_cr0 (x) paravirt_ops.write_cr0(x) - -#define read_cr2 () paravirt_ops.read_cr2() -#define write_cr2 (x) paravirt_ops.write_cr2(x) - -#define read_cr3 () paravirt_ops.read_cr3() -#define write_cr3 (x) paravirt_ops.write_cr3(x) - -#define read_cr4 () paravirt_ops.read_cr4() -#define read_cr4_safe (x) paravirt_ops.read_cr4_safe() -#define write_cr4 (x) paravirt_ops.write_cr4(x) - -#define raw_ptep_get_and_clear (xp) (paravirt_ops.ptep_get_and_clear(xp)) +#define get_debugreg (var, reg) var = PVOP_CALL1(unsigned long, get_debugreg, reg) +#define set_debugreg (val, reg) PVOP_VCALL2(set_debugreg, reg, val) + +#define clts () PVOP_VCALL0(clts) + +#define read_cr0 () PVOP_CALL0(unsigned long, read_cr0) +#define write_cr0 (x) PVOP_VCALL1(write_cr0, x) + +#define read_cr2 () PVOP_CALL0(unsigned long, read_cr2) +#define write_cr2 (x) PVOP_VCALL1(write_cr2, x) + +#define read_cr3 () PVOP_CALL0(unsigned long, read_cr3) +#define write_cr3 (x) PVOP_VCALL1(write_cr3, x) + +#define read_cr4 () PVOP_CALL0(unsigned long, read_cr4) +#define read_cr4_safe (x) PVOP_CALL0(unsigned long, read_cr4_safe) +#define write_cr4 (x) PVOP_VCALL1(write_cr4, x)static inline void raw_safe_halt (void ) { - paravirt_ops.safe_halt (); +PVOP_VCALL0 (safe_halt); }static inline void halt (void ) { - paravirt_ops.safe_halt (); -} -#define wbinvd () paravirt_ops.wbinvd() +PVOP_VCALL0 (safe_halt); +} +#define wbinvd () PVOP_VCALL0(wbinvd)#define get_kernel_rpl () (paravirt_ops.kernel_rpl)#define rdmsr (msr,val1,val2) do { \ int _err; \ - u64 _l = paravirt_ops.read_msr(msr,&_err); \ + u64 _l = PVOP_CALL2(u64, read_msr, msr, &_err); \ val1 = (u32)_l; \ val2 = _l >> 32; \ } while(0) -#define wrmsr (msr,val1,val2) do { \ - u64 _l = ((u64)(val2) << 32) | (val1); \ - paravirt_ops.write_msr((msr), _l); \ -} while(0) +#define wrmsr (msr,val1,val2) PVOP_VCALL3(write_msr, msr, val1, val2)#define rdmsrl (msr,val) do { \ int _err; \ val = paravirt_ops.read_msr((msr),&_err); \ } while(0) -#define wrmsrl (msr,val) (paravirt_ops.write_msr((msr),(val))) -#define wrmsr_safe (msr,a,b) ({ \ - u64 _l = ((u64)(b) << 32) | (a); \ - paravirt_ops.write_msr((msr),_l); \ -}) +#define wrmsrl (msr,val) PVOP_CALL3(int, write_msr, msr, val, 0) +#define wrmsr_safe (msr,a,b) PVOP_CALL3(int, write_msr, msr, a, b)/* rdmsr with exception handling */ #define rdmsr_safe (msr,a,b) ({ \ int _err; \ - u64 _l = paravirt_ops.read_msr(msr,&_err); \ + u64 _l = PVOP_CALL2(u64, read_msr, msr, &_err); \ (*a) = (u32)_l; \ (*b) = _l >> 32; \ _err; })#define rdtsc (low,high) do { \ - u64 _l = paravirt_ops.read_tsc(); \ + u64 _l = PVOP_CALL0(u64, read_tsc); \ low = (u32)_l; \ high = _l >> 32; \ } while(0)#define rdtscl (low) do { \ - u64 _l = paravirt_ops.read_tsc(); \ + u64 _l = PVOP_CALL0(u64, read_tsc); \ low = (int)_l; \ } while(0) -#define rdtscll (val) (val = paravirt_ops.read_tsc()) +#define rdtscll (val) (val = PVOP_CALL0(u64, read_tsc))#define get_scheduled_cycles (val) (val = paravirt_ops.get_scheduled_cycles())#define calculate_cpu_khz () (paravirt_ops.get_cpu_khz())
@@ -314,37 +521,56 @@ static inline void halt(void)#define write_tsc (val1,val2) wrmsr(0x10, val1, val2)#define rdpmc (counter,low,high) do { \ - u64 _l = paravirt_ops.read_pmc(); \ + u64 _l = PVOP_CALL0(u64, read_pmc); \ low = (u32)_l; \ high = _l >> 32; \ } while(0) -#define load_TR_desc () (paravirt_ops.load_tr_desc()) -#define load_gdt (dtr) (paravirt_ops.load_gdt(dtr)) -#define load_idt (dtr) (paravirt_ops.load_idt(dtr)) -#define set_ldt (addr, entries) (paravirt_ops.set_ldt((addr), (entries))) -#define store_gdt (dtr) (paravirt_ops.store_gdt(dtr)) -#define store_idt (dtr) (paravirt_ops.store_idt(dtr)) -#define store_tr (tr) ((tr) = paravirt_ops.store_tr()) -#define load_TLS (t,cpu) (paravirt_ops.load_tls((t),(cpu))) -#define write_ldt_entry (dt, entry, low, high) \ - (paravirt_ops.write_ldt_entry((dt), (entry), (low), (high))) -#define write_gdt_entry (dt, entry, low, high) \ - (paravirt_ops.write_gdt_entry((dt), (entry), (low), (high))) -#define write_idt_entry (dt, entry, low, high) \ - (paravirt_ops.write_idt_entry((dt), (entry), (low), (high))) -#define set_iopl_mask (mask) (paravirt_ops.set_iopl_mask(mask)) - -#define __pte (x) paravirt_ops.make_pte(x) -#define __pgd (x) paravirt_ops.make_pgd(x) - -#define pte_val (x) paravirt_ops.pte_val(x) -#define pgd_val (x) paravirt_ops.pgd_val(x) - -#ifdef CONFIG_X86_PAE -#define __pmd (x) paravirt_ops.make_pmd(x) -#define pmd_val (x) paravirt_ops.pmd_val(x) -#endif +static inline void load_TR_desc (void ) +{ +PVOP_VCALL0 (load_tr_desc); +} +static inline void load_gdt (const struct Xgt_desc_struct *dtr) +{ +PVOP_VCALL1 (load_gdt, dtr); +} +static inline void load_idt (const struct Xgt_desc_struct *dtr) +{ +PVOP_VCALL1 (load_idt, dtr); +} +static inline void set_ldt (const void *addr,unsigned entries) +{ +PVOP_VCALL2 (set_ldt, addr, entries); +} +static inline void store_gdt (struct Xgt_desc_struct *dtr) +{ +PVOP_VCALL1 (store_gdt, dtr); +} +static inline void store_idt (struct Xgt_desc_struct *dtr) +{ +PVOP_VCALL1 (store_idt, dtr); +} +#define store_tr (tr) ((tr) = PVOP_CALL0(unsigned long, store_tr)) +static inline void load_TLS (struct thread_struct *t,unsigned cpu) +{ +PVOP_VCALL2 (load_tls, t, cpu); +} +static inline void write_ldt_entry (void *dt,int entry,u32 low,u32 high) +{ +PVOP_VCALL4 (write_ldt_entry, dt, entry, low, high); +} +static inline void write_gdt_entry (void *dt,int entry,u32 low,u32 high) +{ +PVOP_VCALL4 (write_gdt_entry, dt, entry, low, high); +} +static inline void write_idt_entry (void *dt,int entry,u32 low,u32 high) +{ +PVOP_VCALL4 (write_idt_entry, dt, entry, low, high); +} +static inline void set_iopl_mask (unsigned mask) +{ +PVOP_VCALL1 (set_iopl_mask, mask); +}/* The paravirtualized I/O functions */ static inline void slow_down_io (void ) {
@@ -362,27 +588,27 @@ static inline void slow_down_io(void) {*/ static inline void apic_write (unsigned long reg,unsigned long v) { - paravirt_ops.apic_write (reg,v); +PVOP_VCALL2 (apic_write, reg, v); }static inline void apic_write_atomic (unsigned long reg,unsigned long v) { - paravirt_ops.apic_write_atomic (reg,v); +PVOP_VCALL2 (apic_write_atomic, reg, v); }static inline unsigned long apic_read (unsigned long reg) { -return paravirt_ops.apic_read (reg); + return PVOP_CALL1(unsigned long, apic_read, reg); }static inline void setup_boot_clock (void ) { - paravirt_ops.setup_boot_clock (); +PVOP_VCALL0 (setup_boot_clock); }static inline void setup_secondary_clock (void ) { - paravirt_ops.setup_secondary_clock (); +PVOP_VCALL0 (setup_secondary_clock); }#endif
@@ -402,93 +628,177 @@ static inline void startup_ipi_hook(intstatic inline void startup_ipi_hook (int phys_apicid,unsigned long start_eip,unsigned long start_esp) { -return paravirt_ops.startup_ipi_hook (phys_apicid, start_eip, start_esp); +PVOP_VCALL3 (startup_ipi_hook, phys_apicid, start_eip, start_esp); }#endif static inline void paravirt_activate_mm (struct mm_struct *prev,struct mm_struct *next) { - paravirt_ops.activate_mm (prev, next); +PVOP_VCALL2 (activate_mm, prev, next); }static inline void arch_dup_mmap (struct mm_struct *oldmm,struct mm_struct *mm) { - paravirt_ops.dup_mmap (oldmm, mm); +PVOP_VCALL2 (dup_mmap, oldmm, mm); }static inline void arch_exit_mmap (struct mm_struct *mm) { - paravirt_ops.exit_mmap (mm); -} - -#define __flush_tlb () paravirt_ops.flush_tlb_user() -#define __flush_tlb_global () paravirt_ops.flush_tlb_kernel() -#define __flush_tlb_single (addr) paravirt_ops.flush_tlb_single(addr) - -#define paravirt_map_pt_hook (type, va, pfn) paravirt_ops.map_pt_hook(type, va, pfn) - -#define paravirt_alloc_pt (pfn) paravirt_ops.alloc_pt(pfn) -#define paravirt_release_pt (pfn) paravirt_ops.release_pt(pfn) - -#define paravirt_alloc_pd (pfn) paravirt_ops.alloc_pd(pfn) +PVOP_VCALL1 (exit_mmap, mm); +} + +#define __flush_tlb () PVOP_VCALL0(flush_tlb_user) +#define __flush_tlb_global () PVOP_VCALL0(flush_tlb_kernel) +#define __flush_tlb_single (addr) PVOP_VCALL1(flush_tlb_single, addr) + +static inline void paravirt_map_pt_hook (int type,pte_t *va,u32 pfn) +{ +PVOP_VCALL3 (map_pt_hook, type, va, pfn); +} + +#define paravirt_alloc_pt (pfn) PVOP_VCALL1(alloc_pt, pfn) +#define paravirt_release_pt (pfn) PVOP_VCALL1(release_pt, pfn) + +#define paravirt_alloc_pd (pfn) PVOP_VCALL1(alloc_pd, pfn)#define paravirt_alloc_pd_clone (pfn, clonepfn, start, count) \ - paravirt_ops.alloc_pd_clone(pfn, clonepfn, start, count) -#define paravirt_release_pd (pfn) paravirt_ops.release_pd(pfn) + PVOP_VCALL4(alloc_pd_clone, pfn, clonepfn, start, count) +#define paravirt_release_pd (pfn) PVOP_VCALL1(release_pd, pfn) + +static inline void pte_update (struct mm_struct *mm,unsigned long addr,pte_t *ptep) +{ +PVOP_VCALL3 (pte_update, mm, addr, ptep); +} + +static inline void pte_update_defer (struct mm_struct *mm,unsigned long addr,pte_t *ptep) +{ +PVOP_VCALL3 (pte_update_defer, mm, addr, ptep); +} + +#ifdef CONFIG_X86_PAE +static inline pte_t __pte (unsigned long long val) +{ + unsigned long long ret = PVOP_CALL2(unsigned long long, make_pte, val, val >> 32); +return (pte_t ) { ret, ret >>32 }; +} + +static inline pmd_t __pmd (unsigned long long val) +{ + return (pmd_t) { PVOP_CALL2(unsigned long long, make_pmd, val, val >> 32) }; +} + +static inline pgd_t __pgd (unsigned long long val) +{ + return (pgd_t) { PVOP_CALL2(unsigned long long, make_pgd, val, val >> 32) }; +} + +static inline unsigned long long pte_val (pte_t x) +{ + return PVOP_CALL2(unsigned long long, pte_val, x.pte_low, x.pte_high); +} + +static inline unsigned long long pmd_val (pmd_t x) +{ + return PVOP_CALL2(unsigned long long, pmd_val, x.pmd, x.pmd >> 32); +} + +static inline unsigned long long pgd_val (pgd_t x) +{ + return PVOP_CALL2(unsigned long long, pgd_val, x.pgd, x.pgd >> 32); +}static inline void set_pte (pte_t *ptep,pte_t pteval) { - paravirt_ops.set_pte (ptep, pteval); +PVOP_VCALL3 (set_pte, ptep, pteval.pte_low, pteval.pte_high); }static inline void set_pte_at (struct mm_struct *mm,unsigned long addr,pte_t *ptep,pte_t pteval) { +/* 5 arg words */ paravirt_ops.set_pte_at (mm, addr, ptep, pteval); } +static inline void set_pte_atomic (pte_t *ptep,pte_t pteval) +{ +PVOP_VCALL3 (set_pte_atomic, ptep, pteval.pte_low, pteval.pte_high); +} + +static inline void set_pte_present (struct mm_struct *mm,unsigned long addr, +pte_t *ptep,pte_t pte) +{ +/* 5 arg words */ + paravirt_ops.set_pte_present (mm, addr, ptep, pte); +} +static inline void set_pmd (pmd_t *pmdp,pmd_t pmdval) { - paravirt_ops.set_pmd (pmdp, pmdval); -} - -static inline void pte_update (struct mm_struct *mm,u32 addr,pte_t *ptep) -{ - paravirt_ops.pte_update (mm, addr, ptep); -} - -static inline void pte_update_defer (struct mm_struct *mm,u32 addr,pte_t *ptep) -{ - paravirt_ops.pte_update_defer (mm, addr, ptep); -} - -#ifdef CONFIG_X86_PAE -static inline void set_pte_atomic (pte_t *ptep,pte_t pteval) -{ - paravirt_ops.set_pte_atomic (ptep, pteval); -} - -static inline void set_pte_present (struct mm_struct *mm,unsigned long addr,pte_t *ptep,pte_t pte) -{ - paravirt_ops.set_pte_present (mm, addr, ptep, pte); +PVOP_VCALL3 (set_pmd, pmdp, pmdval.pmd, pmdval.pmd >>32 ); }static inline void set_pud (pud_t *pudp,pud_t pudval) { - paravirt_ops.set_pud (pudp, pudval); +PVOP_VCALL3 (set_pud, pudp, pudval.pgd.pgd, pudval.pgd.pgd >>32 ); }static inline void pte_clear (struct mm_struct *mm,unsigned long addr,pte_t *ptep) { - paravirt_ops.pte_clear (mm, addr, ptep); +PVOP_VCALL3 (pte_clear, mm, addr, ptep); }static inline void pmd_clear (pmd_t *pmdp) { - paravirt_ops.pmd_clear (pmdp); -} -#endif +PVOP_VCALL1 (pmd_clear, pmdp); +} + +static inline pte_t raw_ptep_get_and_clear (pte_t *p) +{ + unsigned long long val = PVOP_CALL1(unsigned long long, ptep_get_and_clear, p); +return (pte_t ) { val, val >>32 }; +} +#else /* !CONFIG_X86_PAE */ +static inline pte_t __pte (unsigned long val) +{ + return (pte_t) { PVOP_CALL1(unsigned long, make_pte, val) }; +} + +static inline pgd_t __pgd (unsigned long val) +{ + return (pgd_t) { PVOP_CALL1(unsigned long, make_pgd, val) }; +} + +static inline unsigned long pte_val (pte_t x) +{ + return PVOP_CALL1(unsigned long, pte_val, x.pte_low); +} + +static inline unsigned long pgd_val (pgd_t x) +{ + return PVOP_CALL1(unsigned long, pgd_val, x.pgd); +} + +static inline void set_pte (pte_t *ptep,pte_t pteval) +{ +PVOP_VCALL2 (set_pte, ptep, pteval.pte_low); +} + +static inline void set_pte_at (struct mm_struct *mm,unsigned long addr, +pte_t *ptep,pte_t pteval) +{ +PVOP_VCALL4 (set_pte_at, mm, addr, ptep, pteval.pte_low); +} + +static inline void set_pmd (pmd_t *pmdp,pmd_t pmdval) +{ +PVOP_VCALL2 (set_pmd, pmdp, pmdval.pud.pgd.pgd); +} + +static inline pte_t raw_ptep_get_and_clear (pte_t *p) +{ + return (pte_t) { PVOP_CALL1(unsigned long, ptep_get_and_clear, p) }; +} +#endif /* CONFIG_X86_PAE */ /* Lazy mode for batching updates / context switch */ #define PARAVIRT_LAZY_NONE 0
@@ -496,12 +806,12 @@ static inline void pmd_clear(pmd_t *pmdp#define PARAVIRT_LAZY_CPU 2#define __HAVE_ARCH_ENTER_LAZY_CPU_MODE -#define arch_enter_lazy_cpu_mode () paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_CPU) -#define arch_leave_lazy_cpu_mode () paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_NONE) +#define arch_enter_lazy_cpu_mode () PVOP_VCALL1(set_lazy_mode, PARAVIRT_LAZY_CPU) +#define arch_leave_lazy_cpu_mode () PVOP_VCALL1(set_lazy_mode, PARAVIRT_LAZY_NONE)#define __HAVE_ARCH_ENTER_LAZY_MMU_MODE -#define arch_enter_lazy_mmu_mode () paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_MMU) -#define arch_leave_lazy_mmu_mode () paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_NONE) +#define arch_enter_lazy_mmu_mode () PVOP_VCALL1(set_lazy_mode, PARAVIRT_LAZY_MMU) +#define arch_leave_lazy_mmu_mode () PVOP_VCALL1(set_lazy_mode, PARAVIRT_LAZY_NONE)void _paravirt_nop (void );#define paravirt_nop ((void *)_paravirt_nop)
PATCH 16/34 Xen-pv_ops: add common patching machinery
In reply to Jeremy FitzhardingeImplement the actual patching machinery. paravirt_patch_default() contains the logic to automatically patch a callsite based on a few simple rules:
- if the paravirt_op function is paravirt_nop, then patch nops
- if the paravirt_op function is a jmp target, then jmp to it
- if the paravirt_op function is callable and doesn't clobber too much for the callsite, call it directly
paravirt_patch_default is suitable as a default implementation of paravirt_ops.patch, will remove most of the expensive indirect calls in favour of either a direct call or a pile of nops.
Backends may implement their own patcher, however. There are several helper functions to help with this:
paravirt_patch_nop nop out a callsiteparavirt_patch_ignore leave the callsite as-isparavirt_patch_call patch a call if the caller and callee
have compatible clobbersparavirt_patch_jmp patch in a jmpparavirt_patch_insns patch some literal instructions over
the callsite, if they fit
This patch also implements more direct patches for the native case, so that when running on native hardware many common operations are implemented inline.
--- arch/i386/kernel/alternative.c | 3 arch/i386/kernel/paravirt.c | 163 ++++++++++++++++++++++++++++++++-------- include/asm-i386/paravirt.h | 11 ++ 3 files changed, 147 insertions(+), 30 deletions(-)
===================================================================
arch/i386/kernel/alternative.c +3 -0
@@ -359,6 +359,9 @@ void apply_paravirt(struct paravirt_patc used = paravirt_ops.patch (p->instrtype, p->clobbers, p->instr, p->len); + +BUG_ON (used > p->len); +#ifdef CONFIG_DEBUG_PARAVIRT {int i;
===================================================================
arch/i386/kernel/paravirt.c +133 -30
173 lines not on this page · raw message
===================================================================
include/asm-i386/paravirt.h +11 -0
@@ -821,6 +821,17 @@ struct paravirt_patch_site {u16 clobbers;/* what registers you may clobber */ }; +unsigned paravirt_patch_nop (void ); +unsigned paravirt_patch_ignore (unsigned len); +unsigned paravirt_patch_call (void *target,u16 tgt_clobbers, +void *site,u16 site_clobbers, +unsigned len); +unsigned paravirt_patch_jmp (void *target,void *site,unsigned len); +unsigned paravirt_patch_default (u8 type,u16 clobbers,void *site,unsigned len); + +unsigned paravirt_patch_insns (void *site,unsigned len, +const char *start,const char *end); +extern struct paravirt_patch_site __start_parainstructions[], __stop_parainstructions[];
PATCH 17/34 Xen-pv_ops: add flush_tlb_others paravirt_op
In reply to Jeremy FitzhardingeThis patch adds a pv_op for flush_tlb_others. Some hypervisors, such as Xen, have much more efficient ways to implement cross-cpu tlb shootdown than IPI, so expose an appropriate operation to allow it to be used.
--- arch/i386/kernel/paravirt.c | 1 + arch/i386/kernel/smp.c | 15 ++++++++------- include/asm-i386/paravirt.h | 8 ++++++++ include/asm-i386/tlbflush.h | 19 +++++++++++++++++-- 4 files changed, 34 insertions(+), 9 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +1 -0
@@ -584,6 +584,7 @@ struct paravirt_ops paravirt_ops = { .flush_tlb_user = native_flush_tlb, .flush_tlb_kernel = native_flush_tlb_global, .flush_tlb_single = native_flush_tlb_single, + .flush_tlb_others = native_flush_tlb_others, .map_pt_hook = paravirt_nop,
===================================================================
arch/i386/kernel/smp.c +8 -7
@@ -256,7 +256,6 @@ static struct mm_struct * flush_mm;static struct mm_struct * flush_mm;static unsigned long flush_va;static DEFINE_SPINLOCK (tlbstate_lock); -#define FLUSH_ALL 0xffffffff/* * We cannot call mmdrop() because we are in interrupt context,
@@ -338,7 +337,7 @@ fastcall void smp_invalidate_interrupt(sif (flush_mm ==per_cpu (cpu_tlbstate, cpu).active_mm) {if (per_cpu (cpu_tlbstate, cpu).state ==TLBSTATE_OK ) { -if (flush_va ==FLUSH_ALL ) +if (flush_va ==TLB_FLUSH_ALL )local_flush_tlb ();else __flush_tlb_one (flush_va);
@@ -353,9 +352,11 @@ out:put_cpu_no_resched (); } -static void flush_tlb_others (cpumask_t cpumask,struct mm_struct *mm, -unsigned long va) -{ +void native_flush_tlb_others (const cpumask_t *cpumaskp,struct mm_struct *mm, +unsigned long va) +{ +cpumask_t cpumask = *cpumaskp; +/* * A couple of (to be removed) sanity checks: *
@@ -417,7 +418,7 @@ void flush_tlb_current_task(void)local_flush_tlb ();if (!cpus_empty (cpu_mask)) -flush_tlb_others (cpu_mask, mm,FLUSH_ALL ); +flush_tlb_others (cpu_mask, mm,TLB_FLUSH_ALL );preempt_enable (); }
@@ -436,7 +437,7 @@ void flush_tlb_mm (struct mm_struct * mmleave_mm (smp_processor_id ()); }if (!cpus_empty (cpu_mask)) -flush_tlb_others (cpu_mask, mm,FLUSH_ALL ); +flush_tlb_others (cpu_mask, mm,TLB_FLUSH_ALL );preempt_enable (); }
===================================================================
include/asm-i386/paravirt.h +8 -0
@@ -15,6 +15,7 @@#ifndef __ASSEMBLY__#include <linux/types.h> +#include <linux/cpumask.h> struct thread_struct ;struct Xgt_desc_struct ;
@@ -121,6 +122,8 @@ struct paravirt_opsvoid (*flush_tlb_user)(void );void (*flush_tlb_kernel)(void );void (*flush_tlb_single)(u32 addr); +void (*flush_tlb_others)(const cpumask_t *cpus,struct mm_struct *mm, +unsigned long va);void (*map_pt_hook)(int type,pte_t *va,u32 pfn);
@@ -652,6 +655,11 @@ static inline void arch_exit_mmap(struct#define __flush_tlb () PVOP_VCALL0(flush_tlb_user)#define __flush_tlb_global () PVOP_VCALL0(flush_tlb_kernel)#define __flush_tlb_single (addr) PVOP_VCALL1(flush_tlb_single, addr) +static inline void flush_tlb_others (cpumask_t cpumask,struct mm_struct *mm, +unsigned long va) +{ +PVOP_VCALL3 (flush_tlb_others, &cpumask, mm, va); +}static inline void paravirt_map_pt_hook (int type,pte_t *va,u32 pfn) {
===================================================================
include/asm-i386/tlbflush.h +17 -2
@@ -79,10 +79,14 @@* - flush_tlb_range(vma, start, end) flushes a range of pages * - flush_tlb_kernel_range(start, end) flushes a range of kernel pages * - flush_tlb_pgtables(mm, start, end) flushes a range of page tables +* - flush_tlb_others(cpumask, mm, va) flushes a TLBs on other cpus * * ..but the i386 has somewhat limited tlb flushing capabilities, * and page-granular flushes are available only on i486 and up. */ + +#define TLB_FLUSH_ALL 0xffffffff +#ifndef CONFIG_SMP
@@ -110,7 +114,12 @@ static inline void flush_tlb_range(struc __flush_tlb(); } -#else +static inline void native_flush_tlb_others (const cpumask_t *cpumask, +struct mm_struct *mm,unsigned long va) +{ +} + +#else /* SMP */ #include <asm/smp.h>
@@ -129,6 +138,9 @@ static inline void flush_tlb_range(strucflush_tlb_mm (vma->vm_mm); } +void native_flush_tlb_others (const cpumask_t *cpumask,struct mm_struct *mm, +unsigned long va); +#define TLBSTATE_OK 1#define TLBSTATE_LAZY 2
@@ -139,8 +151,11 @@ struct tlb_statechar __cacheline_padding[L1_CACHE_BYTES -8 ]; }; DECLARE_PER_CPU(struct tlb_state, cpu_tlbstate); +#endif /* SMP */ - +#ifndef CONFIG_PARAVIRT +#define flush_tlb_others (mask, mm, va) \ + native_flush_tlb_others(&mask, mm, va)#endif #define flush_tlb_kernel_range (start, end) flush_tlb_all()
PATCH 18/34 Xen-pv_ops: revert map_pt_hook.
In reply to Jeremy FitzhardingeBack out the map_pt_hook to clear the way for kmap_atomic_pte.
--- arch/i386/kernel/paravirt.c | 2 -- arch/i386/kernel/vmi.c | 2 ++ include/asm-i386/paravirt.h | 7 ------- include/asm-i386/pgtable.h | 23 ++++------------------- 4 files changed, 6 insertions(+), 28 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +0 -2
@@ -586,8 +586,6 @@ struct paravirt_ops paravirt_ops = { .flush_tlb_single = native_flush_tlb_single, .flush_tlb_others = native_flush_tlb_others, - .map_pt_hook = paravirt_nop, - .alloc_pt = paravirt_nop, .alloc_pd = paravirt_nop, .alloc_pd_clone = paravirt_nop,
===================================================================
arch/i386/kernel/vmi.c +2 -0
@@ -824,8 +824,10 @@ static inline int __init activate_vmi(vo paravirt_ops.release_pt = vmi_release_pt; paravirt_ops.release_pd = vmi_release_pd; } +#if 0 para_wrap (map_pt_hook, vmi_map_pt_hook, set_linear_mapping, SetLinearMapping); +#endif /* * These MUST always be patched. Don't support indirect jumps
===================================================================
include/asm-i386/paravirt.h +0 -7
@@ -124,8 +124,6 @@ struct paravirt_opsvoid (*flush_tlb_single)(u32 addr);void (*flush_tlb_others)(const cpumask_t *cpus,struct mm_struct *mm,unsigned long va); - -void (*map_pt_hook)(int type,pte_t *va,u32 pfn);void (*alloc_pt)(u32 pfn);void (*alloc_pd)(u32 pfn);
@@ -661,11 +659,6 @@ static inline void flush_tlb_others(cpumPVOP_VCALL3 (flush_tlb_others, &cpumask, mm, va); } -static inline void paravirt_map_pt_hook (int type,pte_t *va,u32 pfn) -{ -PVOP_VCALL3 (map_pt_hook, type, va, pfn); -} -#define paravirt_alloc_pt (pfn) PVOP_VCALL1(alloc_pt, pfn)#define paravirt_release_pt (pfn) PVOP_VCALL1(release_pt, pfn)
===================================================================
include/asm-i386/pgtable.h +4 -19
@@ -270,7 +270,6 @@ static inline void vmalloc_sync_all(void*/ #define pte_update (mm, addr, ptep) do { } while (0)#define pte_update_defer (mm, addr, ptep) do { } while (0) -#define paravirt_map_pt_hook (slot, va, pfn) do { } while (0)#define raw_ptep_get_and_clear (xp) native_ptep_get_and_clear(xp)#endif
@@ -479,24 +478,10 @@ extern pte_t *lookup_address(unsigned lo#endif #if defined(CONFIG_HIGHPTE ) -#define pte_offset_map (dir, address) \ -({ \ - pte_t *__ptep; \ - unsigned pfn = pmd_val(*(dir)) >> PAGE_SHIFT; \ - __ptep = (pte_t *)kmap_atomic(pfn_to_page(pfn),KM_PTE0);\ - paravirt_map_pt_hook(KM_PTE0,__ptep, pfn); \ - __ptep = __ptep + pte_index(address); \ - __ptep; \ -}) -#define pte_offset_map_nested (dir, address) \ -({ \ - pte_t *__ptep; \ - unsigned pfn = pmd_val(*(dir)) >> PAGE_SHIFT; \ - __ptep = (pte_t *)kmap_atomic(pfn_to_page(pfn),KM_PTE1);\ - paravirt_map_pt_hook(KM_PTE1,__ptep, pfn); \ - __ptep = __ptep + pte_index(address); \ - __ptep; \ -}) +#define pte_offset_map (dir, address) \ + ((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE0) + pte_index(address)) +#define pte_offset_map_nested (dir, address) \ + ((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE1) + pte_index(address))#define pte_unmap (pte) kunmap_atomic(pte, KM_PTE0)#define pte_unmap_nested (pte) kunmap_atomic(pte, KM_PTE1)#else
PATCH 19/34 Xen-pv_ops: add kmap_atomic_pte for mapping highpte pages
In reply to Jeremy FitzhardingeXen and VMI both have special requirements when mapping a highmem pte page into the kernel address space. These can be dealt with by adding a new kmap_atomic_pte() function for mapping highptes, and hooking it into the paravirt_ops infrastructure.
Xen specifically wants to map the pte page RO, so this patch exposes a helper function, _kmap_atomic, which maps the page with the specified page protections.
This also adds a kmap_flush_unused() function to clear out the cached kmap mappings. Xen needs this to clear out any potential stray RW mappings of pages which will become part of a pagetable.
--- arch/i386/kernel/paravirt.c | 7 +++++++ arch/i386/mm/highmem.c | 9 +++++++-- include/asm-i386/highmem.h | 11 +++++++++++ include/asm-i386/paravirt.h | 14 +++++++++++++- include/asm-i386/pgtable.h | 4 ++-- include/linux/highmem.h | 6 ++++++ mm/highmem.c | 9 +++++++++ 7 files changed, 55 insertions(+), 5 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +7 -0
@@ -20,6 +20,7 @@#include <linux/efi.h> #include <linux/bcd.h> #include <linux/start_kernel.h> +#include <linux/highmem.h> #include <asm/bug.h> #include <asm/paravirt.h>
@@ -600,6 +601,12 @@ struct paravirt_ops paravirt_ops = { .ptep_get_and_clear = native_ptep_get_and_clear, +#ifdef CONFIG_HIGHPTE + .kmap_atomic_pte = native_kmap_atomic_pte, +#else + .kmap_atomic_pte = paravirt_nop, +#endif +#ifdef CONFIG_X86_PAE .set_pte_atomic = native_set_pte_atomic, .set_pte_present = native_set_pte_present,
===================================================================
arch/i386/mm/highmem.c +7 -2
@@ -26,7 +26,7 @@ void kunmap(struct page *page)* However when holding an atomic kmap is is not legal to sleep, so atomic * kmaps are appropriate for short, tight code paths only. */ -void *kmap_atomic (struct page *page,enum km_type type) +void *_kmap_atomic (struct page *page,enum km_type type,pgprot_t prot) {enum fixed_addresses idx;unsigned long vaddr;
@@ -41,9 +41,14 @@ void *kmap_atomic(struct page *page, enureturn page_address (page); vaddr =__fix_to_virt (FIX_KMAP_BEGIN + idx); -set_pte (kmap_pte-idx,mk_pte (page, kmap_prot)); +set_pte (kmap_pte-idx,mk_pte (page, prot));return (void *) vaddr; +} + +void *kmap_atomic (struct page *page,enum km_type type) +{ +return _kmap_atomic (page, type, kmap_prot); }void kunmap_atomic (void *kvaddr,enum km_type type)
===================================================================
include/asm-i386/highmem.h +11 -0
@@ -24,6 +24,7 @@#include <linux/threads.h> #include <asm/kmap_types.h> #include <asm/tlbflush.h> +#include <asm/paravirt.h> /* declarations for highmem.c */ extern unsigned long highstart_pfn, highend_pfn;
@@ -67,10 +68,20 @@ extern void FASTCALL(kunmap_high(structvoid *kmap (struct page *page);void kunmap (struct page *page); +void *_kmap_atomic (struct page *page,enum km_type type,pgprot_t prot);void *kmap_atomic (struct page *page,enum km_type type);void kunmap_atomic (void *kvaddr,enum km_type type);void *kmap_atomic_pfn (unsigned long pfn,enum km_type type);struct page *kmap_atomic_to_page (void *ptr); + +static inline void *native_kmap_atomic_pte (struct page *page,enum km_type type) +{ +return kmap_atomic (page, type); +} + +#ifndef CONFIG_PARAVIRT +#define kmap_atomic_pte (page, type) kmap_atomic(page, type) +#endif #define flush_cache_kmaps () do { } while (0)
===================================================================
include/asm-i386/paravirt.h +13 -1
@@ -16,11 +16,14 @@ #ifndef __ASSEMBLY__ #include <linux/types.h> #include <linux/cpumask.h> - +#include <asm/kmap_types.h> + +struct page; struct thread_struct; struct Xgt_desc_struct; struct tss_struct; struct mm_struct; + struct paravirt_ops { unsigned int kernel_rpl;
@@ -138,6 +141,8 @@ struct paravirt_opsvoid (*pte_update_defer)(struct mm_struct *mm,unsigned long addr,pte_t *ptep);pte_t (*ptep_get_and_clear)(pte_t *ptep); + +void *(*kmap_atomic_pte)(struct page *page,enum km_type type);#ifdef CONFIG_X86_PAE void (*set_pte_atomic)(pte_t *ptep,pte_t pteval);
@@ -667,6 +672,13 @@ static inline void flush_tlb_others(cpum PVOP_VCALL4(alloc_pd_clone, pfn, clonepfn, start, count)#define paravirt_release_pd (pfn) PVOP_VCALL1(release_pd, pfn) +static inline void *kmap_atomic_pte (struct page *page,enum km_type type) +{ +unsigned long ret; + ret = PVOP_CALL2(unsigned long, kmap_atomic_pte, page, type); +return (void *)ret; +} +static inline void pte_update (struct mm_struct *mm,unsigned long addr,pte_t *ptep) {PVOP_VCALL3 (pte_update, mm, addr, ptep);
===================================================================
include/asm-i386/pgtable.h +2 -2
@@ -479,9 +479,9 @@ extern pte_t *lookup_address(unsigned lo#if defined(CONFIG_HIGHPTE )#define pte_offset_map (dir, address) \ - ((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE0) + pte_index(address)) + ((pte_t *)kmap_atomic_pte(pmd_page(*(dir)),KM_PTE0) + pte_index(address))#define pte_offset_map_nested (dir, address) \ - ((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE1) + pte_index(address)) + ((pte_t *)kmap_atomic_pte(pmd_page(*(dir)),KM_PTE1) + pte_index(address))#define pte_unmap (pte) kunmap_atomic(pte, KM_PTE0)#define pte_unmap_nested (pte) kunmap_atomic(pte, KM_PTE1)#else
===================================================================
include/linux/highmem.h +6 -0
@@ -27,6 +27,8 @@ unsigned int nr_free_highpages(void);unsigned int nr_free_highpages (void );extern unsigned long totalhigh_pages; +void kmap_flush_unused (void ); +#else /* CONFIG_HIGHMEM */ static inline unsigned int nr_free_highpages (void ) {return 0 ; }
@@ -44,9 +46,13 @@ static inline void *kmap(struct page *pa#define kmap_atomic (page, idx) \ ({ pagefault_disable(); page_address(page); }) +#define _kmap_atomic (page, idx, prot) kmap_atomic(page, idx) +#define kunmap_atomic (addr, idx) do { pagefault_enable(); } while (0)#define kmap_atomic_pfn (pfn, idx) kmap_atomic(pfn_to_page(pfn), (idx))#define kmap_atomic_to_page (ptr) virt_to_page(ptr) + +#define kmap_flush_unused () do {} while(0)#endif #endif /* CONFIG_HIGHMEM */
===================================================================
mm/highmem.c +9 -0
@@ -97,6 +97,15 @@ static void flush_all_zero_pkmaps(void)set_page_address (page,NULL ); }flush_tlb_kernel_range (PKMAP_ADDR (0 ),PKMAP_ADDR (LAST_PKMAP )); +} + +/* Flush all unused kmap mappings in order to remove stray +mappings. */ +void kmap_flush_unused (void ) +{ +spin_lock (&kmap_lock); +flush_all_zero_pkmaps (); +spin_unlock (&kmap_lock); }static inline unsigned long map_new_virtual (struct page *page)
PATCH 20/34 Xen-pv_ops: Add apply_to_page_range() which applies a function to a pte range.
In reply to Jeremy FitzhardingeAdd a new mm function apply_to_page_range() which applies a given function to every pte in a given virtual address range in a given mm structure. This is a generic alternative to cut-and-pasting the Linux idiomatic pagetable walking code in every place that a sequence of PTEs must be accessed.
Although this interface is intended to be useful in a wide range of situations, it is currently used specifically by several Xen subsystems, for example: to ensure that pagetables have been allocated for a virtual address range, and to construct batched special pagetable update requests to map I/O memory (in ioremap()).
--- include/linux/mm.h | 5 ++ mm/memory.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+)
===================================================================
include/linux/mm.h +5 -0
@@ -1135,6 +1135,11 @@ struct page *follow_page(struct vm_area_#define FOLL_GET 0x04/* do get_page on page */ #define FOLL_ANON 0x08/* give ZERO_PAGE if no pgtable */ +typedef int (*pte_fn_t )(pte_t *pte,struct page *pmd_page,unsigned long addr, +void *data); +extern int apply_to_page_range (struct mm_struct *mm,unsigned long address, +unsigned long size,pte_fn_t fn,void *data); +#ifdef CONFIG_PROC_FS void vm_stat_account (struct mm_struct *,unsigned long ,struct file *,long );#else
===================================================================
mm/memory.c +94 -0
100 lines not on this page · raw message
PATCH 21/34 Xen-pv_ops: Allocate and free vmalloc areas
In reply to Jeremy FitzhardingeAllocate/destroy a 'vmalloc' VM area: alloc_vm_area and free_vm_area The alloc function ensures that page tables are constructed for the region of kernel virtual address space and mapped into init_mm.
--- arch/i386/mm/fault.c | 43 +++++++++++++++++++++++++++++++++++++++++++ include/linux/vmalloc.h | 4 ++++ 2 files changed, 47 insertions(+)
===================================================================
arch/i386/mm/fault.c +43 -0
@@ -23,6 +23,7 @@#include <linux/module.h> #include <linux/kprobes.h> #include <linux/uaccess.h> +#include <linux/vmalloc.h> #include <asm/system.h> #include <asm/desc.h>
@@ -624,3 +625,45 @@ void _vmalloc_sync_all(void) start = address +PGDIR_SIZE ; } } + + +static int f (pte_t *pte,struct page *pmd_page,unsigned long addr,void *data) +{ +/* apply_to_page_range() does all the hard work. */ +return 0 ; +} + +struct vm_struct *alloc_vm_area (unsigned long size) +{ +struct vm_struct *area; + + area =get_vm_area (size,VM_IOREMAP ); +if (area ==NULL ) +return NULL ; + +/* +* This ensures that page tables are constructed for this region +* of kernel virtual address space and mapped into init_mm. +*/ +if (apply_to_page_range (&init_mm, (unsigned long )area->addr, + area->size, f,NULL )) { +free_vm_area (area); +return NULL ; + } + +/* Make sure the pagetables are constructed in process kernel +mappings */ +vmalloc_sync_all (); + +return area; +} +EXPORT_SYMBOL_GPL (alloc_vm_area); + +void free_vm_area (struct vm_struct *area) +{ +struct vm_struct *ret; + ret =remove_vm_area (area->addr); +BUG_ON (ret != area); +kfree (area); +} +EXPORT_SYMBOL_GPL (free_vm_area);
===================================================================
include/linux/vmalloc.h +4 -0
@@ -68,6 +68,10 @@ extern int map_vm_area(struct vm_struct struct page ***pages);extern void unmap_vm_area (struct vm_struct *area); +/* Allocate/destroy a 'vmalloc' VM area. */ +extern struct vm_struct *alloc_vm_area (unsigned long size); +extern void free_vm_area (struct vm_struct *area); +/* * Internals. Dont't use.. */
PATCH 22/34 Xen-pv_ops: Add nosegneg capability to the vsyscall page notes
In reply to Jeremy FitzhardingeAdd the "nosegneg" fake capabilty to the vsyscall page notes. This is used by the runtime linker to select a glibc version which then disables negative-offset accesses to the thread-local segment via %gs. These accesses require emulation in Xen (because segments are truncated to protect the hypervisor address space) and avoiding them provides a measurable performance boost.
--- arch/i386/kernel/vsyscall-note.S | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
===================================================================
arch/i386/kernel/vsyscall-note.S +28 -0
@@ -23,3 +24,31 @@ 3: .balign 4; /* pad out section */ ASM_ELF_NOTE_BEGIN(".note.kernel-version", "a", UTS_SYSNAME, 0) .long LINUX_VERSION_CODE ASM_ELF_NOTE_END + +#ifdef CONFIG_XEN +/* + * Add a special note telling glibc's dynamic linker a fake hardware + * flavor that it will use to choose the search path for libraries in the + * same way it uses real hardware capabilities like "mmx". + * We supply "nosegneg" as the fake capability, to indicate that we + * do not like negative offsets in instructions using segment overrides, + * since we implement those inefficiently. This makes it possible to + * install libraries optimized to avoid those access patterns in someplace + * like /lib/i686/tls/nosegneg. Note that an /etc/ld.so.conf.d/file + * corresponding to the bits here is needed to make ldconfig work right. + * It should contain: + * hwcap 0 nosegneg + * to match the mapping of bit to name that we give here. + */ +#define NOTE_KERNELCAP_BEGIN(ncaps, mask) \ + ASM_ELF_NOTE_BEGIN(".note.kernelcap", "a", "GNU", 2) \ + .long ncaps, mask +#define NOTE_KERNELCAP(bit, name) \ + .byte bit; .asciz name +#define NOTE_KERNELCAP_END ASM_ELF_NOTE_END + +NOTE_KERNELCAP_BEGIN(1, 1) +NOTE_KERNELCAP(1, "nosegneg") +NOTE_KERNELCAP_END +#endif +
PATCH 23/34 Xen-pv_ops: Add XEN config options
In reply to Jeremy FitzhardingeThe XEN config option enables the Xen paravirt_ops interface, which is installed when the kernel finds itself running under Xen.
Xen is no longer a sub-architecture, so the X86_XEN subarch config option has gone.
Xen is currently incompatible with:
- PREEMPT
- HZ: set to 100Hz for now, to cut down on VCPU context switch rate. This will be adapted to use tickless later.
--- arch/i386/Kconfig | 2 ++ arch/i386/xen/Kconfig | 10 ++++++++++ 2 files changed, 12 insertions(+)
===================================================================
arch/i386/Kconfig +2 -0
@@ -216,6 +216,8 @@ config PARAVIRT under a hypervisor, improving performance significantly. However, when run without a hypervisor the kernel is theoretically slower. If in doubt, say N. + +source "arch/i386/xen/Kconfig" config VMI bool "VMI Paravirt-ops support"
===================================================================
arch/i386/xen/Kconfig new file +10 -0
@@ -0,0 +1,10 @@ +# +# This Kconfig describes xen options +# + +config XEN + bool "Enable support for Xen hypervisor" + depends on PARAVIRT && HZ_100 && !PREEMPT && !NO_HZ + default y + help + This is the Linux Xen port.
PATCH 24/34 Xen-pv_ops: Add Xen interface header files
In reply to Jeremy FitzhardingeAdd Xen interface header files. These are taken fairly directly from the Xen tree, but somewhat rearranged to suit the kernel's conventions.
Define macros and inline functions for doing hypercalls into the hypervisor.
--- include/asm-i386/xen/hypercall.h | 413 ++++++++++++++++++++++++++++++ include/asm-i386/xen/hypervisor.h | 72 +++++ include/asm-i386/xen/interface.h | 187 +++++++++++++ include/xen/interface/elfnote.h | 133 +++++++++ include/xen/interface/event_channel.h | 195 ++++++++++++++ include/xen/interface/features.h | 43 +++ include/xen/interface/grant_table.h | 301 ++++++++++++++++++++++ include/xen/interface/io/blkif.h | 96 +++++++ include/xen/interface/io/console.h | 23 + include/xen/interface/io/netif.h | 152 +++++++++++ include/xen/interface/io/ring.h | 260 +++++++++++++++++++ include/xen/interface/io/xenbus.h | 42 +++ include/xen/interface/io/xs_wire.h | 87 ++++++ include/xen/interface/memory.h | 145 ++++++++++ include/xen/interface/physdev.h | 61 ++++ include/xen/interface/sched.h | 77 +++++ include/xen/interface/vcpu.h | 164 ++++++++++++ include/xen/interface/version.h | 60 ++++ include/xen/interface/xen.h | 446 +++++++++++++++++++++++++++++++++ 19 files changed, 2957 insertions(+)
===================================================================
include/asm-i386/xen/hypercall.h new file +413 -0
@@ -0,0 +1,413 @@ +/****************************************************************************** +* hypercall.h +* +* Linux-specific hypervisor handling. +* +* Copyright (c) 2002-2004, K A Fraser +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License version 2 +* as published by the Free Software Foundation; or, when distributed +* separately from the Linux kernel or incorporated into other +* software packages, subject to the following license: +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this source file (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, +* merge, publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +* IN THE SOFTWARE. +*/ + +#ifndef __HYPERCALL_H__ +#define __HYPERCALL_H__ + +#include <linux/errno.h> +#include <linux/string.h> + +#include <xen/interface/xen.h> +#include <xen/interface/sched.h> +#include <xen/interface/physdev.h> + +extern struct {char _entry[32 ]; } hypercall_page[]; + +#define _hypercall0 (type, name) \ +({ \ + long __res; \ + asm volatile ( \ + "call %[call]" \ + : "=a" (__res) \ + : [call] "m" (hypercall_page[__HYPERVISOR_##name]) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall1 (type, name, a1) \ +({ \ + long __res, __ign1; \ + asm volatile ( \ + "call %[call]" \ + : "=a" (__res), "=b" (__ign1) \ + : "1" ((long)(a1)), \ + [call] "m" (hypercall_page[__HYPERVISOR_##name]) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall2 (type, name, a1, a2) \ +({ \ + long __res, __ign1, __ign2; \ + asm volatile ( \ + "call %[call]" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2) \ + : "1" ((long)(a1)), "2" ((long)(a2)), \ + [call] "m" (hypercall_page[__HYPERVISOR_##name]) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall3 (type, name, a1, a2, a3) \ +({ \ + long __res, __ign1, __ign2, __ign3; \ + asm volatile ( \ + "call %[call]" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3) \ + : "1" ((long)(a1)), "2" ((long)(a2)), \ + "3" ((long)(a3)), \ + [call] "m" (hypercall_page[__HYPERVISOR_##name]) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall4 (type, name, a1, a2, a3, a4) \ +({ \ + long __res, __ign1, __ign2, __ign3, __ign4; \ + asm volatile ( \ + "call %[call]" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3), "=S" (__ign4) \ + : "1" ((long)(a1)), "2" ((long)(a2)), \ + "3" ((long)(a3)), "4" ((long)(a4)), \ + [call] "m" (hypercall_page[__HYPERVISOR_##name]) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall5 (type, name, a1, a2, a3, a4, a5) \ +({ \ + long __res, __ign1, __ign2, __ign3, __ign4, __ign5; \ + asm volatile ( \ + "call %[call]" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3), "=S" (__ign4), "=D" (__ign5) \ + : "1" ((long)(a1)), "2" ((long)(a2)), \ + "3" ((long)(a3)), "4" ((long)(a4)), \ + "5" ((long)(a5)), \ + [call] "m" (hypercall_page[__HYPERVISOR_##name]) \ + : "memory" ); \ + (type)__res; \ +}) + +static inline int +HYPERVISOR_set_trap_table ( +struct trap_info *table) +{ +return _hypercall1 (int, set_trap_table, table); +} + +static inline int +HYPERVISOR_mmu_update ( +struct mmu_update *req,int count,int *success_count,domid_t domid) +{ +return _hypercall4 (int, mmu_update, req, count, success_count, domid); +} + +static inline int +HYPERVISOR_mmuext_op ( +struct mmuext_op *op,int count,int *success_count,domid_t domid) +{ +return _hypercall4 (int, mmuext_op, op, count, success_count, domid); +} + +static inline int +HYPERVISOR_set_gdt ( +unsigned long *frame_list,int entries) +{ +return _hypercall2 (int, set_gdt, frame_list, entries); +} + +static inline int +HYPERVISOR_stack_switch ( +unsigned long ss,unsigned long esp) +{ +return _hypercall2 (int, stack_switch, ss, esp); +} + +static inline int +HYPERVISOR_set_callbacks ( +unsigned long event_selector,unsigned long event_address, +unsigned long failsafe_selector,unsigned long failsafe_address) +{ +return _hypercall4 (int, set_callbacks, + event_selector, event_address, + failsafe_selector, failsafe_address); +} + +static inline int +HYPERVISOR_fpu_taskswitch ( +int set) +{ +return _hypercall1 (int, fpu_taskswitch, set); +} + +static inline int +HYPERVISOR_sched_op ( +int cmd,unsigned long arg) +{ +return _hypercall2 (int, sched_op, cmd, arg); +} + +static inline long +HYPERVISOR_set_timer_op ( +u64 timeout) +{ +unsigned long timeout_hi = (unsigned long )(timeout>>32 ); +unsigned long timeout_lo = (unsigned long )timeout; +return _hypercall2 (long, set_timer_op, timeout_lo, timeout_hi); +} + +static inline int +HYPERVISOR_set_debugreg ( +int reg,unsigned long value) +{ +return _hypercall2 (int, set_debugreg, reg, value); +} + +static inline unsigned long +HYPERVISOR_get_debugreg ( +int reg) +{ + return _hypercall1(unsigned long, get_debugreg, reg); +} + +static inline int +HYPERVISOR_update_descriptor ( +u64 ma,u64 desc) +{ +return _hypercall4 (int, update_descriptor, ma, ma>>32 , desc, desc>>32 ); +} + +static inline int +HYPERVISOR_memory_op ( +unsigned int cmd,void *arg) +{ +return _hypercall2 (int, memory_op, cmd, arg); +} + +static inline int +HYPERVISOR_multicall ( +void *call_list,int nr_calls) +{ +return _hypercall2 (int, multicall, call_list, nr_calls); +} + +static inline int +HYPERVISOR_update_va_mapping ( +unsigned long va,pte_t new_val,unsigned long flags) +{ +unsigned long pte_hi =0 ; +#ifdef CONFIG_X86_PAE + pte_hi = new_val.pte_high; +#endif +return _hypercall4 (int, update_va_mapping, va, + new_val.pte_low, pte_hi, flags); +} + +static inline int +HYPERVISOR_event_channel_op ( +int cmd,void *arg) +{ +int rc =_hypercall2 (int, event_channel_op, cmd, arg); +if (unlikely (rc == -ENOSYS )) { +struct evtchn_op op; + op.cmd = cmd; +memcpy (&op.u, arg,sizeof (op.u)); + rc =_hypercall1 (int, event_channel_op_compat, &op); +memcpy (arg, &op.u,sizeof (op.u)); + } +return rc; +} + +static inline int +HYPERVISOR_xen_version ( +int cmd,void *arg) +{ +return _hypercall2 (int, xen_version, cmd, arg); +} + +static inline int +HYPERVISOR_console_io ( +int cmd,int count,char *str) +{ +return _hypercall3 (int, console_io, cmd, count, str); +} + +static inline int +HYPERVISOR_physdev_op (int cmd,void *arg) +{ +int rc =_hypercall2 (int, physdev_op, cmd, arg); +if (unlikely (rc == -ENOSYS )) { +struct physdev_op op; + op.cmd = cmd; +memcpy (&op.u, arg,sizeof (op.u)); + rc =_hypercall1 (int, physdev_op_compat, &op); +memcpy (arg, &op.u,sizeof (op.u)); + } +return rc; +} + +static inline int +HYPERVISOR_grant_table_op ( +unsigned int cmd,void *uop,unsigned int count) +{ +return _hypercall3 (int, grant_table_op, cmd, uop, count); +} + +static inline int +HYPERVISOR_update_va_mapping_otherdomain ( +unsigned long va,pte_t new_val,unsigned long flags,domid_t domid) +{ +unsigned long pte_hi =0 ; +#ifdef CONFIG_X86_PAE + pte_hi = new_val.pte_high; +#endif +return _hypercall5 (int, update_va_mapping_otherdomain, va, + new_val.pte_low, pte_hi, flags, domid); +} + +static inline int +HYPERVISOR_vm_assist ( +unsigned int cmd,unsigned int type) +{ +return _hypercall2 (int, vm_assist, cmd, type); +} + +static inline int +HYPERVISOR_vcpu_op ( +int cmd,int vcpuid,void *extra_args) +{ +return _hypercall3 (int, vcpu_op, cmd, vcpuid, extra_args); +} + +static inline int +HYPERVISOR_suspend ( +unsigned long srec) +{ +return _hypercall3 (int, sched_op, SCHEDOP_shutdown, + SHUTDOWN_suspend, srec); +} + +static inline int +HYPERVISOR_nmi_op ( +unsigned long op, +unsigned long arg) +{ +return _hypercall2 (int, nmi_op, op, arg); +} + +static inline void +MULTI_update_va_mapping (struct multicall_entry *mcl,unsigned long va, +pte_t new_val,unsigned long flags) +{ + mcl->op = __HYPERVISOR_update_va_mapping; + mcl->args[0 ] = va; +#ifdef CONFIG_X86_PAE + mcl->args[1 ] = new_val.pte_low; + mcl->args[2 ] = new_val.pte_high; +#else + mcl->args[1 ] = new_val.pte_low; + mcl->args[2 ] =0 ; +#endif + mcl->args[3 ] = flags; +} + +static inline void +MULTI_grant_table_op (struct multicall_entry *mcl,unsigned int cmd, +void *uop,unsigned int count) +{ + mcl->op = __HYPERVISOR_grant_table_op; + mcl->args[0 ] = cmd; + mcl->args[1 ] = (unsigned long )uop; + mcl->args[2 ] = count; +} + +static inline void +MULTI_update_va_mapping_otherdomain (struct multicall_entry *mcl,unsigned long va, +pte_t new_val,unsigned long flags,domid_t domid) +{ + mcl->op = __HYPERVISOR_update_va_mapping_otherdomain; + mcl->args[0 ] = va; +#ifdef CONFIG_X86_PAE + mcl->args[1 ] = new_val.pte_low; + mcl->args[2 ] = new_val.pte_high; +#else + mcl->args[1 ] = new_val.pte_low; + mcl->args[2 ] =0 ; +#endif + mcl->args[3 ] = flags; + mcl->args[4 ] = domid; +} + +static inline void +MULTI_update_descriptor (struct multicall_entry *mcl,u64 maddr, +struct desc_struct desc) +{ + mcl->op = __HYPERVISOR_update_descriptor; + mcl->args[0 ] = maddr; + mcl->args[1 ] = maddr >>32 ; + mcl->args[2 ] = desc.a; + mcl->args[3 ] = desc.b; +} + +static inline void +MULTI_memory_op (struct multicall_entry *mcl,unsigned int cmd,void *arg) +{ + mcl->op = __HYPERVISOR_memory_op; + mcl->args[0 ] = cmd; + mcl->args[1 ] = (unsigned long )arg; +} + +static inline void +MULTI_mmu_update (struct multicall_entry *mcl,struct mmu_update *req, +int count,int *success_count,domid_t domid) +{ + mcl->op = __HYPERVISOR_mmu_update; + mcl->args[0 ] = (unsigned long )req; + mcl->args[1 ] = count; + mcl->args[2 ] = (unsigned long )success_count; + mcl->args[3 ] = domid; +} + +static inline void +MULTI_mmuext_op (struct multicall_entry *mcl,struct mmuext_op *op,int count, +int *success_count,domid_t domid) +{ + mcl->op = __HYPERVISOR_mmuext_op; + mcl->args[0 ] = (unsigned long )op; + mcl->args[1 ] = count; + mcl->args[2 ] = (unsigned long )success_count; + mcl->args[3 ] = domid; +} +#endif /* __HYPERCALL_H__ */
===================================================================
include/asm-i386/xen/hypervisor.h new file +72 -0
72 lines not on this page · raw message
===================================================================
include/asm-i386/xen/interface.h new file +187 -0
187 lines not on this page · raw message
===================================================================
include/xen/interface/elfnote.h new file +133 -0
133 lines not on this page · raw message
===================================================================
include/xen/interface/event_channel.h new file +195 -0
195 lines not on this page · raw message
===================================================================
include/xen/interface/features.h new file +43 -0
43 lines not on this page · raw message
===================================================================
include/xen/interface/grant_table.h new file +301 -0
301 lines not on this page · raw message
===================================================================
include/xen/interface/io/blkif.h new file +96 -0
96 lines not on this page · raw message
===================================================================
include/xen/interface/io/console.h new file +23 -0
@@ -0,0 +1,23 @@ +/****************************************************************************** +* console.h +* +* Console I/O interface for Xen guest OSes. +* +* Copyright (c) 2005, Keir Fraser +*/ + +#ifndef __XEN_PUBLIC_IO_CONSOLE_H__ +#define __XEN_PUBLIC_IO_CONSOLE_H__ + +typedef uint32_t XENCONS_RING_IDX ; + +#define MASK_XENCONS_IDX (idx, ring) ((idx) & (sizeof(ring)-1)) + +struct xencons_interface { +char in[1024 ]; +char out[2048 ]; +XENCONS_RING_IDX in_cons, in_prod; +XENCONS_RING_IDX out_cons, out_prod; +}; + +#endif /* __XEN_PUBLIC_IO_CONSOLE_H__ */
===================================================================
include/xen/interface/io/netif.h new file +152 -0
152 lines not on this page · raw message
===================================================================
include/xen/interface/io/ring.h new file +260 -0
260 lines not on this page · raw message
===================================================================
include/xen/interface/io/xenbus.h new file +42 -0
42 lines not on this page · raw message
===================================================================
include/xen/interface/io/xs_wire.h new file +87 -0
87 lines not on this page · raw message
===================================================================
include/xen/interface/memory.h new file +145 -0
145 lines not on this page · raw message
===================================================================
include/xen/interface/physdev.h new file +61 -0
61 lines not on this page · raw message
===================================================================
include/xen/interface/sched.h new file +77 -0
77 lines not on this page · raw message
===================================================================
include/xen/interface/vcpu.h new file +164 -0
164 lines not on this page · raw message
===================================================================
include/xen/interface/version.h new file +60 -0
60 lines not on this page · raw message
===================================================================
include/xen/interface/xen.h new file +446 -0
446 lines not on this page · raw message
PATCH 25/34 Xen-pv_ops: Core Xen implementation
In reply to Jeremy FitzhardingeCore Xen Implementation
This patch is a rollup of all the core pieces of the Xen implementation, including booting, memory management, interrupts, time and so on.
This adds a second entry point to head.S, which is jumped to when booted by Xen. This allows startup under Xen to be easily detected.
Because Xen starts the kernel in a fairly sane state, very little setup is needed here; it just needs to jump into xen_start_kernel to init the paravirt_ops structure, and then jump into start_kernel proper.
This also makes a few small adjustments to the gdt tables to make them properly suited to Xen.
This patch also contains an initial set of paravirt ops for Xen, mostly ones which can just be implemented with the generic native_* version. At boot time, the global paravirt_ops structure is populated with the Xen pointers in xen_start_kernel, which then jumps to the standard start_kernel.
Also implements Xen pagetable handling, including the machinery to implement direct pagetables. This means that pte update operations are intercepted so that pseudo-physical addresses can be converted to machine addresses. It also implements late pinning/early unpinning so that pagetables are initialized while they're normal read-write memory as much as possible, yet pinned to make cr3 reloads as efficient as possible.
Xen implements interrupts in terms of event channels. This patch maps an event through an event channel to an irq, and then feeds it through the normal interrupt path, via a Xen irq_chip implementation.
Context switches are optimised with the use of multicalls, so that the multiple hypercalls needed to perform the switch can be implemented with a single hypercall. In order to simplify the source changes, this is done via a generic multicall batching mechanism, which can be used to batch arbitrary series of hypercalls into a multicall.
Time is implemented by using a clocksource which is driven from the hypervisor's nanosecond timebase. Xen implements time by extrapolating from known timestamps using the tsc; the hypervisor is responsible for making sure that the tsc is constant rate and synchronized between vcpus.
--- arch/i386/Makefile | 3 arch/i386/kernel/cpu/common.c | 3 arch/i386/kernel/entry.S | 79 ++++ arch/i386/kernel/head.S | 13 arch/i386/kernel/paravirt.c | 48 +- arch/i386/kernel/vmlinux.lds.S | 1 arch/i386/xen/Makefile | 2 arch/i386/xen/enlighten.c | 743 ++++++++++++++++++++++++++++++++++++++ arch/i386/xen/events.c | 483 ++++++++++++++++++++++++ arch/i386/xen/features.c | 29 + arch/i386/xen/mmu.c | 405 ++++++++++++++++++++ arch/i386/xen/mmu.h | 49 ++ arch/i386/xen/multicalls.c | 61 +++ arch/i386/xen/multicalls.h | 26 + arch/i386/xen/setup.c | 95 ++++ arch/i386/xen/time.c | 445 ++++++++++++++++++++++ arch/i386/xen/xen-head.S | 30 + arch/i386/xen/xen-ops.h | 32 + include/asm-i386/irq.h | 1 include/asm-i386/paravirt.h | 39 + include/asm-i386/pda.h | 18 include/asm-i386/xen/hypercall.h | 18 include/xen/events.h | 28 + include/xen/features.h | 26 + include/xen/page.h | 178 +++++++++ 25 files changed, 2829 insertions(+), 26 deletions(-)
===================================================================
arch/i386/Makefile +3 -0
@@ -93,6 +93,9 @@ mcore-$(CONFIG_X86_ES7000) := mach-defau mcore-$(CONFIG_X86_ES7000) := mach-default core-$(CONFIG_X86_ES7000) := arch/i386/mach-es7000/ +# Xen paravirtualization support +core-$(CONFIG_XEN) += arch/i386/xen/ + # default subarch .h files mflags-y += -Iinclude/asm-i386/mach-default
===================================================================
arch/i386/kernel/cpu/common.c +3 -0
@@ -19,6 +19,7 @@#include <mach_apic.h> #endif #include <asm/pda.h> +#include <asm/paravirt.h> #include "cpu.h"
@@ -707,6 +708,8 @@ __cpuinit int init_gdt(int cpu, struct t pda->cpu_number = cpu; pda->pcurrent = idle; +paravirt_init_pda (pda, cpu); +return 1 ; }
===================================================================
arch/i386/kernel/entry.S +79 -0
85 lines not on this page · raw message
===================================================================
arch/i386/kernel/head.S +11 -2
@@ -535,6 +535,10 @@ unhandled_paravirt: ud2 #endif +#ifdef CONFIG_XEN +#include "../xen/xen-head.S" +#endif + /* * Real beginning of normal "text" segment */
@@ -544,8 +548,9 @@ ENTRY(_stext) /* * BSS section */ -.section ".bss.page_aligned","w" +.section ".bss.page_aligned","wa" ENTRY(swapper_pg_dir) + .align PAGE_SIZE_asm .fill 1024,4,0 ENTRY(empty_zero_page) .fill 4096,1,0
@@ -614,7 +619,8 @@ ENTRY(boot_gdt_table) /* * The Global Descriptor Table contains 28 quadwords, per-CPU. */ - .align L1_CACHE_BYTES + .section ".data.page_aligned" + .align PAGE_SIZE_asm ENTRY(cpu_gdt_table) .quad 0x0000000000000000 /* NULL descriptor */ .quad 0x0000000000000000 /* 0x0b reserved */
@@ -663,3 +669,6 @@ ENTRY(cpu_gdt_table) .quad 0x0000000000000000 /* 0xf0 - unused */ .quad 0x0000000000000000 /* 0xf8 - GDT entry 31: double-fault TSS */ + /* Be sure this is zeroed to avoid false validations in Xen */ + .fill PAGE_SIZE_asm / 8 - GDT_ENTRIES,8,0 + .previous
===================================================================
arch/i386/kernel/paravirt.c +24 -24
162 lines not on this page · raw message
===================================================================
arch/i386/kernel/vmlinux.lds.S +1 -0
@@ -95,6 +95,7 @@ SECTIONS . = ALIGN(4096); .data.page_aligned : AT(ADDR(.data.page_aligned) - LOAD_OFFSET) { + *(.data.page_aligned) *(.data.idt) }
===================================================================
arch/i386/xen/Makefile new file +2 -0
@@ -0,0 +1,2 @@ +obj-y := enlighten.o setup.o events.o time.o \ + features.o mmu.o multicalls.o
===================================================================
arch/i386/xen/enlighten.c new file +743 -0
743 lines not on this page · raw message
===================================================================
arch/i386/xen/events.c new file +483 -0
483 lines not on this page · raw message
===================================================================
arch/i386/xen/features.c new file +29 -0
@@ -0,0 +1,29 @@ +/****************************************************************************** +* features.c +* +* Xen feature flags. +* +* Copyright (c) 2006, Ian Campbell, XenSource Inc. +*/ +#include <linux/types.h> +#include <linux/cache.h> +#include <linux/module.h> +#include <asm/xen/hypervisor.h> +#include <xen/features.h> + +u8 xen_features[XENFEAT_NR_SUBMAPS *32 ]__read_mostly ; +EXPORT_SYMBOL_GPL (xen_features); + +void xen_setup_features (void ) +{ +struct xen_feature_info fi; +int i, j; + +for (i =0 ; i <XENFEAT_NR_SUBMAPS ; i++) { + fi.submap_idx = i; +if (HYPERVISOR_xen_version (XENVER_get_features, &fi) <0 ) +break ; +for (j=0 ; j<32 ; j++) + xen_features[i*32 +j] = !!(fi.submap &1 <<j); + } +}
===================================================================
arch/i386/xen/mmu.c new file +405 -0
405 lines not on this page · raw message
===================================================================
arch/i386/xen/mmu.h new file +49 -0
49 lines not on this page · raw message
===================================================================
arch/i386/xen/multicalls.c new file +61 -0
61 lines not on this page · raw message
===================================================================
arch/i386/xen/multicalls.h new file +26 -0
@@ -0,0 +1,26 @@ +#ifndef _XEN_MULTICALLS_H +#define _XEN_MULTICALLS_H + +#include "xen-ops.h" + +/* Multicalls */ +struct multicall_space +{ +struct multicall_entry *mc; +void *args; +}; + +/* Allocate room for a multicall and its args */ +struct multicall_space xen_mc_entry (size_t args); + +/* Flush all pending multicalls */ +void xen_mc_flush (void ); + +/* Issue a multicall if we're not in lazy mode */ +static inline void xen_mc_issue (void ) +{ +if (xen_get_lazy_mode () ==PARAVIRT_LAZY_NONE ) +xen_mc_flush (); +} + +#endif /* _XEN_MULTICALLS_H */
===================================================================
arch/i386/xen/setup.c new file +95 -0
95 lines not on this page · raw message
===================================================================
arch/i386/xen/time.c new file +445 -0
445 lines not on this page · raw message
===================================================================
arch/i386/xen/xen-head.S new file +30 -0
@@ -0,0 +1,30 @@ +/* Xen-specific pieces of head.S, intended to be included in the right + place in head.S */ + +#include <linux/elfnote.h> +#include <asm/boot.h> +#include <xen/interface/elfnote.h> + +ENTRY(startup_xen) + movl %esi,xen_start_info + jmp startup_paravirt + +.pushsection ".bss.page_aligned" +ENTRY(hypercall_page) + .align PAGE_SIZE_asm + .skip 0x1000 +.popsection + + ELFNOTE(Xen, XEN_ELFNOTE_GUEST_OS, .asciz, "linux") + ELFNOTE(Xen, XEN_ELFNOTE_GUEST_VERSION, .asciz, "2.6") + ELFNOTE(Xen, XEN_ELFNOTE_XEN_VERSION, .asciz, "xen-3.0") + ELFNOTE(Xen, XEN_ELFNOTE_VIRT_BASE, .long, __PAGE_OFFSET) + ELFNOTE(Xen, XEN_ELFNOTE_ENTRY, .long, startup_xen) + ELFNOTE(Xen, XEN_ELFNOTE_HYPERCALL_PAGE, .long, hypercall_page) + ELFNOTE(Xen, XEN_ELFNOTE_FEATURES, .asciz, "!writable_page_tables|pae_pgdir_above_4gb") +#ifdef CONFIG_X86_PAE + ELFNOTE(Xen, XEN_ELFNOTE_PAE_MODE, .asciz, "yes") +#else + ELFNOTE(Xen, XEN_ELFNOTE_PAE_MODE, .asciz, "no") +#endif + ELFNOTE(Xen, XEN_ELFNOTE_LOADER, .asciz, "generic")
===================================================================
arch/i386/xen/xen-ops.h new file +32 -0
@@ -0,0 +1,32 @@ +#ifndef XEN_OPS_H +#define XEN_OPS_H + +#include <linux/init.h> + +extern struct start_info *xen_start_info; +extern struct shared_info *HYPERVISOR_shared_info; + +char *__init xen_memory_setup (void ); +void __init xen_arch_setup (void ); +void __init xen_init_IRQ (void ); + +unsigned long xen_cpu_khz (void ); +void __init xen_time_init (void ); +unsigned long xen_get_wallclock (void ); +int xen_set_wallclock (unsigned long time); + +void stop_hz_timer (void ); +void start_hz_timer (void ); + +DECLARE_PER_CPU (unsigned, xen_lazy_mode); + +static inline unsigned xen_get_lazy_mode (void ) +{ +unsigned ret =get_cpu_var (xen_lazy_mode); +put_cpu_var (xen_lazy_mode); + +return ret; +} + + +#endif /* XEN_OPS_H */
===================================================================
include/asm-i386/irq.h +1 -0
@@ -43,6 +43,7 @@ extern void fixup_irqs(cpumask_t map);extern void fixup_irqs (cpumask_t map);#endif +unsigned int do_IRQ (struct pt_regs *regs);void init_IRQ (void );void __init native_init_IRQ (void );
===================================================================
include/asm-i386/paravirt.h +39 -0
@@ -23,6 +23,7 @@ struct Xgt_desc_struct;struct Xgt_desc_struct ;struct tss_struct ;struct mm_struct ; +struct i386_pda ;struct paravirt_ops {
@@ -43,6 +44,7 @@ struct paravirt_opsvoid (*arch_setup)(void );char *(*memory_setup)(void );void (*init_IRQ)(void ); +void (*init_pda)(struct i386_pda *,int cpu);void (*pagetable_setup_start)(pgd_t *pgd_base);void (*pagetable_setup_done)(pgd_t *pgd_base);
@@ -174,6 +176,30 @@ struct paravirt_opsvoid (*startup_ipi_hook)(int phys_apicid,unsigned long start_eip,unsigned long start_esp); }; + +/* Non-paravirtualized implementations of various operations for +back-ends which don't need their own version. */ +void native_clts (void ); + +unsigned long native_read_cr0 (void ); +void native_write_cr0 (unsigned long val); + +unsigned long native_read_cr2 (void ); +void native_write_cr2 (unsigned long val); + +unsigned long native_read_cr3 (void ); +void native_write_cr3 (unsigned long val); + +unsigned long native_read_cr4 (void ); +unsigned long native_read_cr4_safe (void ); +void native_write_cr4 (unsigned long val); + +void native_wbinvd (void ); + +unsigned long long native_read_msr (unsigned int msr,int *err); +int native_write_msr (unsigned int msr,unsigned long long val); +unsigned long long native_read_tsc (void ); +unsigned long long native_read_pmc (void );/* Mark a paravirt probe function. */ #define paravirt_probe (fn) \
@@ -532,6 +558,13 @@ static inline void halt(void) high = _l >>32 ; \ }while (0 ) +void native_write_ldt_entry(void *dt, int entrynum, u32 low, u32 high); +void native_write_gdt_entry (void *dt,int entrynum,u32 low,u32 high); +void native_write_idt_entry (void *dt,int entrynum,u32 low,u32 high); +void native_store_gdt (struct Xgt_desc_struct *dtr); +void native_store_idt (struct Xgt_desc_struct *dtr); +unsigned long native_store_tr (void ); +static inline void load_TR_desc (void ) {PVOP_VCALL0 (load_tr_desc);
@@ -653,6 +686,12 @@ static inline void arch_exit_mmap(structstatic inline void arch_exit_mmap (struct mm_struct *mm) {PVOP_VCALL1 (exit_mmap, mm); +} + +static inline void paravirt_init_pda (struct i386_pda *pda,int cpu) +{ +if (paravirt_ops.init_pda) + (*paravirt_ops.init_pda)(pda, cpu); }#define __flush_tlb () PVOP_VCALL0(flush_tlb_user)
===================================================================
include/asm-i386/pda.h +18 -0
@@ -16,8 +16,26 @@ struct i386_pdaint cpu_number;struct task_struct *pcurrent;/* current process */ struct pt_regs *irq_regs; + +#ifdef CONFIG_PARAVIRT +union { +#ifdef CONFIG_XEN +struct { +struct vcpu_info *vcpu; +unsigned long cr3; + } xen; +#endif /* CONFIG_XEN */ + }; +#endif /* CONFIG_PARAVIRT */ }; +#ifndef CONFIG_PARAVIRT +static inline void paravirt_init_pda (struct i386_pda *pda,int cpu) +{ +} +#endif + +extern struct i386_pda boot_pda;extern struct i386_pda *_cpu_pda[];#define cpu_pda (i) (_cpu_pda[i])
===================================================================
include/asm-i386/xen/hypercall.h +18 -0
@@ -410,4 +410,22 @@ MULTI_mmuext_op(struct multicall_entry * mcl->args[2 ] = (unsigned long )success_count; mcl->args[3] = domid; } + +static inline void +MULTI_set_gdt (struct multicall_entry *mcl,unsigned long *frames,int entries) +{ + mcl->op = __HYPERVISOR_set_gdt; + mcl->args[0 ] = (unsigned long )frames; + mcl->args[1 ] = entries; +} + +static inline void +MULTI_stack_switch (struct multicall_entry *mcl, +unsigned long ss,unsigned long esp) +{ + mcl->op = __HYPERVISOR_stack_switch; + mcl->args[0 ] = ss; + mcl->args[1 ] = esp; +} +#endif /* __HYPERCALL_H__ */
===================================================================
include/xen/events.h new file +28 -0
@@ -0,0 +1,28 @@ +#ifndef _XEN_EVENTS_H +#define _XEN_EVENTS_H + +#include <linux/irq.h> + +int bind_evtchn_to_irqhandler (unsigned int evtchn, +irqreturn_t (*handler)(int ,void *), +unsigned long irqflags,const char *devname, +void *dev_id); +int bind_virq_to_irqhandler (unsigned int virq,unsigned int cpu, +irqreturn_t (*handler)(int ,void *), +unsigned long irqflags,const char *devname,void *dev_id); + +/* +* Common unbind function for all event sources. Takes IRQ to unbind from. +* Automatically closes the underlying event channel (even for bindings +* made with bind_evtchn_to_irqhandler()). +*/ +void unbind_from_irqhandler (unsigned int irq,void *dev_id); + +static inline void notify_remote_via_evtchn (int port) +{ +struct evtchn_send send = { .port = port }; + (void )HYPERVISOR_event_channel_op (EVTCHNOP_send, &send); +} + +extern void notify_remote_via_irq (int irq); +#endif /* _XEN_EVENTS_H */
===================================================================
include/xen/features.h new file +26 -0
@@ -0,0 +1,26 @@ +/****************************************************************************** +* features.h +* +* Query the features reported by Xen. +* +* Copyright (c) 2006, Ian Campbell +*/ + +#ifndef __XEN_FEATURES_H__ +#define __XEN_FEATURES_H__ + +#include <xen/interface/features.h> + +void xen_setup_features (void ); + +extern u8 xen_features[XENFEAT_NR_SUBMAPS *32 ]; + +static inline int xen_feature (int flag) +{ +switch (flag) { + } + +return xen_features[flag]; +} + +#endif /* __ASM_XEN_FEATURES_H__ */
===================================================================
include/xen/page.h new file +178 -0
109 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.
PATCH 26/34 Xen-pv_ops: Complete pagetable pinning for Xen
In reply to Jeremy FitzhardingeXen has a notion of pinned pagetables, which are pagetables that remain read-only to the guest and are validated by the hypervisor. This makes context switches much cheaper, because the hypervisor doesn't need to revalidate the pagetable each time.
This patch adds a PG_pinned flag for pagetable pages so we can tell if it has been pinned or not. This allows various pagetable update optimisations.
This also adds a mm parameter to the alloc_pt pv_op, so that Xen can see if we're adding a page to a pinned pagetable. This is not necessary for alloc_pd or release_p[dt], which is fortunate because it isn't available at all callsites.
This also adds a new paravirt hook which is called during setup once the zones and memory allocator have been initialized. When the init_mm pagetable is first built, the struct page array does not yet exist, and so there's nowhere to put he init_mm pagetable's PG_pinned flags. Once the zones are initialized and the struct page array exists, we can set the PG_pinned flags for those pages.
This patch also adds the Xen support for pte pages allocated out of highmem (highpte), principly by implementing xen_kmap_atomic_pte.
--- arch/i386/kernel/setup.c | 3 arch/i386/kernel/vmi.c | 2 arch/i386/mm/init.c | 2 arch/i386/mm/pageattr.c | 2 arch/i386/xen/enlighten.c | 104 +++++++++++---- arch/i386/xen/mmu.c | 290 ++++++++++++++++++++++++++----------------- arch/i386/xen/mmu.h | 2 arch/i386/xen/xen-ops.h | 2 include/asm-i386/paravirt.h | 12 + include/asm-i386/pgalloc.h | 6 include/asm-i386/setup.h | 4 include/linux/page-flags.h | 5 12 files changed, 284 insertions(+), 150 deletions(-)
===================================================================
arch/i386/kernel/setup.c +3 -0
@@ -607,9 +607,12 @@ void __init setup_arch(char **cmdline_p)sparse_init ();zone_sizes_init (); +/* * NOTE: at this point the bootmem allocator is fully available. */ + +paravirt_post_allocator_init ();dmi_scan_machine ();
===================================================================
arch/i386/kernel/vmi.c +1 -1
@@ -364,7 +364,7 @@ static void vmi_map_pt_hook(int type, pt vmi_ops.set_linear_mapping ((type -KM_PTE0 )+1 , (u32 )va,1 , pfn); } -static void vmi_allocate_pt (u32 pfn) +static void vmi_allocate_pt (struct mm_struct *mm,u32 pfn) {vmi_set_page_type (pfn,VMI_PAGE_L1 ); vmi_ops.allocate_page (pfn,VMI_PAGE_L1 ,0 ,0 ,0 );
===================================================================
arch/i386/mm/init.c +1 -1
@@ -86,7 +86,7 @@ static pte_t * __init one_page_table_iniif (pmd_none (*pmd)) {pte_t *page_table = (pte_t *)alloc_bootmem_low_pages (PAGE_SIZE ); -paravirt_alloc_pt (__pa (page_table) >>PAGE_SHIFT ); +paravirt_alloc_pt (&init_mm,__pa (page_table) >>PAGE_SHIFT );set_pmd (pmd,__pmd (__pa (page_table) | _PAGE_TABLE));BUG_ON (page_table !=pte_offset_kernel (pmd,0 )); }
===================================================================
arch/i386/mm/pageattr.c +1 -1
@@ -60,7 +60,7 @@ static struct page *split_large_page(uns address =__pa (address); addr = address &LARGE_PAGE_MASK ; pbase = (pte_t *)page_address (base); -paravirt_alloc_pt (page_to_pfn (base)); +paravirt_alloc_pt (&init_mm,page_to_pfn (base));for (i =0 ; i <PTRS_PER_PTE ; i++, addr +=PAGE_SIZE ) {set_pte (&pbase[i],pfn_pte (addr >>PAGE_SHIFT , addr == address ? prot : ref_prot));
===================================================================
arch/i386/xen/enlighten.c +76 -28
152 lines not on this page · raw message
===================================================================
arch/i386/xen/mmu.c +177 -113
392 lines not on this page · raw message
===================================================================
arch/i386/xen/mmu.h +1 -1
@@ -17,7 +17,7 @@ pte_t xen_ptep_get_and_clear(pte_t *pteppte_t xen_ptep_get_and_clear (pte_t *ptep);void xen_pgd_pin (pgd_t *pgd); -void xen_pgd_unpin (pgd_t *pgd); +//void xen_pgd_unpin(pgd_t *pgd); #ifdef CONFIG_X86_PAE unsigned long long xen_pte_val (pte_t );
===================================================================
arch/i386/xen/xen-ops.h +2 -0
@@ -15,6 +15,8 @@ unsigned long xen_get_wallclock(void);unsigned long xen_get_wallclock (void );int xen_set_wallclock (unsigned long time); +void xen_mark_init_mm_pinned (void ); +void stop_hz_timer (void );void start_hz_timer (void );
===================================================================
include/asm-i386/paravirt.h +10 -2
@@ -275,6 +275,8 @@ struct paravirt_opsvoid (*arch_setup)(void );char *(*memory_setup)(void ); +void (*post_allocator_init)(void ); +void (*init_IRQ)(void );void (*init_pda)(struct i386_pda *,int cpu);
@@ -360,7 +362,7 @@ struct paravirt_opsvoid (*flush_tlb_kernel)(void );void (*flush_tlb_single)(u32 addr); -void (*alloc_pt)(u32 pfn); +void (*alloc_pt)(struct mm_struct *mm,u32 pfn);void (*alloc_pd)(u32 pfn);void (*alloc_pd_clone)(u32 pfn,u32 clonepfn,u32 start,u32 count);void (*release_pt)(u32 pfn);
@@ -650,6 +652,12 @@ static inline void setup_secondary_clock }#endif +static inline void paravirt_post_allocator_init (void ) +{ +if (paravirt_ops.post_allocator_init) + (*paravirt_ops.post_allocator_init)(); +} +static inline void paravirt_pagetable_setup_start (pgd_t *base) {if (paravirt_ops.pagetable_setup_start)
@@ -697,7 +705,7 @@ static inline void paravirt_init_pda(str#define __flush_tlb_global () PVOP_VCALL0(flush_tlb_kernel)#define __flush_tlb_single (addr) PVOP_VCALL1(flush_tlb_single, addr) -#define paravirt_alloc_pt (pfn) PVOP_VCALL1(alloc_pt, pfn) +#define paravirt_alloc_pt (mm, pfn) PVOP_VCALL2(alloc_pt, mm, pfn)#define paravirt_release_pt (pfn) PVOP_VCALL1(release_pt, pfn)#define paravirt_alloc_pd (pfn) PVOP_VCALL1(alloc_pd, pfn)
===================================================================
include/asm-i386/pgalloc.h +3 -3
@@ -8,7 +8,7 @@#ifdef CONFIG_PARAVIRT #include <asm/paravirt.h> #else -#define paravirt_alloc_pt (pfn) do { } while (0) +#define paravirt_alloc_pt (mm, pfn) do { } while (0)#define paravirt_alloc_pd (pfn) do { } while (0)#define paravirt_alloc_pd (pfn) do { } while (0)#define paravirt_alloc_pd_clone (pfn, clonepfn, start, count) do { } while (0)
@@ -18,13 +18,13 @@#define pmd_populate_kernel (mm, pmd, pte) \ do { \ - paravirt_alloc_pt(__pa(pte) >> PAGE_SHIFT); \ + paravirt_alloc_pt(mm, __pa(pte) >> PAGE_SHIFT); \ set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte))); \ } while (0)#define pmd_populate (mm, pmd, pte) \ do { \ - paravirt_alloc_pt(page_to_pfn(pte)); \ + paravirt_alloc_pt(mm, page_to_pfn(pte)); \ set_pmd(pmd, __pmd(_PAGE_TABLE + \ ((unsigned long long)page_to_pfn(pte) << \ (unsigned long long) PAGE_SHIFT))); \
===================================================================
include/asm-i386/setup.h +4 -0
@@ -79,6 +79,10 @@ void __init add_memory_region(unsigned lextern unsigned long init_pg_tables_end; +#ifndef CONFIG_PARAVIRT +#define paravirt_post_allocator_init () do {} while(0) +#endif +#endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */
===================================================================
include/linux/page-flags.h +5 -0
@@ -93,6 +93,7 @@/* PG_owner_priv_1 users should have descriptive aliases */ #define PG_checked PG_owner_priv_1/* Used by some filesystems */ +#define PG_pinned PG_owner_priv_1/* Xen pinned pagetable */ #if (BITS_PER_LONG >32 )/*
@@ -170,6 +171,10 @@ static inline void SetPageUptodate(struc#define PageChecked (page) test_bit(PG_checked, &(page)->flags)#define SetPageChecked (page) set_bit(PG_checked, &(page)->flags)#define ClearPageChecked (page) clear_bit(PG_checked, &(page)->flags) + +#define PagePinned (page) test_bit(PG_pinned, &(page)->flags) +#define SetPagePinned (page) set_bit(PG_pinned, &(page)->flags) +#define ClearPagePinned (page) clear_bit(PG_pinned, &(page)->flags)#define PageReserved (page) test_bit(PG_reserved, &(page)->flags)#define SetPageReserved (page) set_bit(PG_reserved, &(page)->flags)
PATCH 27/34 Xen-pv_ops: Implement Xen clockevent device
In reply to Jeremy FitzhardingeThis patch implements a Xen clockevent device, using the set_timer_op hypercall. Unfortunately this will generate timer events on the same event channel as the 100Hz tick which Xen helpfully generates for us. This doesn't matter, other than generating a stream of what appear to be spurious timer events. The clockevent infrastucture can deal with this, though it may be worth suppressing them by checking the current time against the event time.
This patch does not attempt to account for lost/stolen time. That will have to be done in a more general way.
--- arch/i386/xen/Kconfig | 2 arch/i386/xen/enlighten.c | 2 arch/i386/xen/time.c | 482 ++++++++++++++++++++------------------------- arch/i386/xen/xen-ops.h | 3 4 files changed, 225 insertions(+), 264 deletions(-)
===================================================================
arch/i386/xen/Kconfig +1 -1
@@ -4,7 +4,7 @@ config XEN bool "Enable support for Xen hypervisor" - depends on PARAVIRT && HZ_100 && !PREEMPT && !NO_HZ + depends on PARAVIRT && !PREEMPT default y help This is the Linux Xen port.
===================================================================
arch/i386/xen/enlighten.c +0 -2
@@ -149,11 +149,9 @@ static void xen_irq_enable(void)static void xen_safe_halt (void ) { -stop_hz_timer ();/* Blocking includes an implicit local_irq_enable(). */ if (HYPERVISOR_sched_op (SCHEDOP_block,0 ) !=0 )BUG (); -start_hz_timer (); }static void xen_halt (void )
===================================================================
arch/i386/xen/time.c +224 -258
534 lines not on this page · raw message
===================================================================
arch/i386/xen/xen-ops.h +0 -3
@@ -17,9 +17,6 @@ int xen_set_wallclock(unsigned long timevoid xen_mark_init_mm_pinned (void ); -void stop_hz_timer (void ); -void start_hz_timer (void ); -DECLARE_PER_CPU (unsigned, xen_lazy_mode);static inline unsigned xen_get_lazy_mode (void )
PATCH 28/34 Xen-pv_ops: Xen SMP guest support
In reply to Jeremy FitzhardingeThis is a fairly straightforward Xen implementation of smp_ops. One thing this must to is carefully set up all the various sibling and core maps so that the smp scheduler setup works properly (the setup is very simple, since vcpus don't have any siblings or multiple cores).
Xen has its own IPI mechanisms, and has no dependency on any APIC-based IPI. The smp_ops hooks and the flush_tlb_others pv_op allow a Xen guest to avoid all APIC code in arch/i386 (the only apic operation is a single apic_read for the apic version number).
One subtle point which needs to be addressed is unpinning pagetables when another cpu may have a lazy tlb reference to the pagetable. Xen will not allow an in-use pagetable to be unpinned, so we must find any other cpus with a reference to the pagetable and get them to shoot down their references.
[ brcl, mingo: Would you mind having a close look at the code in
xen_exit_mm and drop_mm_ref? - Thanks ]
--- arch/i386/kernel/smpboot.c | 2 arch/i386/xen/Makefile | 6 arch/i386/xen/enlighten.c | 115 ++++-- arch/i386/xen/events.c | 78 ++++ arch/i386/xen/mmu.c | 41 +- arch/i386/xen/mmu.h | 9 arch/i386/xen/setup.c | 9 arch/i386/xen/smp.c | 382 ++++++++++++++++++++ arch/i386/xen/time.c | 9 arch/i386/xen/xen-ops.h | 24 + include/asm-i386/mach-default/irq_vectors_limits.h | 2 include/asm-i386/smp.h | 2 include/xen/events.h | 27 + 13 files changed, 650 insertions(+), 56 deletions(-)
===================================================================
arch/i386/kernel/smpboot.c +1 -1
@@ -150,7 +150,7 @@ void __init smp_alloc_memory(void)* a given CPU */ -static void __cpuinit smp_store_cpu_info (int id) +void __cpuinit smp_store_cpu_info (int id) {struct cpuinfo_x86 *c = cpu_data + id;
===================================================================
arch/i386/xen/Makefile +4 -2
@@ -1,2 +1,4 @@ obj-y := enlighten.o setup.o events.o t -obj-y := enlighten.o setup.o events.o time.o \ - features.o mmu.o multicalls.o +obj-y := enlighten.o setup.o events.o time.o \ + features.o mmu.o multicalls.o + +obj-$(CONFIG_SMP) += smp.o
===================================================================
arch/i386/xen/enlighten.c +91 -24
195 lines not on this page · raw message
===================================================================
arch/i386/xen/events.c +77 -1
108 lines not on this page · raw message
===================================================================
arch/i386/xen/mmu.c +26 -15
53 lines not on this page · raw message
===================================================================
arch/i386/xen/mmu.h +9 -0
@@ -2,6 +2,15 @@#include <linux/linkage.h> #include <asm/page.h> + +/* +* Page-directory addresses above 4GB do not fit into architectural %cr3. +* When accessing %cr3, or equivalent field in vcpu_guest_context, guests +* must use the following accessor macros to pack/unpack valid MFNs. +*/ +#define xen_pfn_to_cr3 (pfn) (((unsigned)(pfn) << 12) | ((unsigned)(pfn) >> 20)) +#define xen_cr3_to_pfn (cr3) (((unsigned)(cr3) >> 12) | ((unsigned)(cr3) << 20)) +void set_pte_mfn (unsigned long vaddr,unsigned long pfn,pgprot_t flags);
===================================================================
arch/i386/xen/setup.c +5 -4
@@ -15,10 +15,6 @@#include <xen/features.h> #include "xen-ops.h" - -/* These are code, but not functions. Defined in entry.S */ -extern const char xen_hypervisor_callback[]; -extern const char xen_failsafe_callback[];static __initdata struct shared_info init_shared;
@@ -92,4 +88,9 @@ void __init xen_arch_setup(void) pm_idle = xen_idle; vdso_enabled =1 ;/* enable by default */ + +#ifdef CONFIG_SMP +/* fill cpus_possible with all available cpus */ +xen_fill_possible_map (); +#endif }
===================================================================
arch/i386/xen/smp.c new file +382 -0
382 lines not on this page · raw message
===================================================================
arch/i386/xen/time.c +5 -4
@@ -359,8 +359,9 @@ static const struct clock_event_device *static const struct clock_event_device *xen_clockevent = &xen_timerop_clockevent;static irq_handler_t xen_timer_interrupt = xen_timerop_timer_interrupt; -static void xen_setup_timer (int cpu) -{ +void xen_setup_timer (void ) +{ +int cpu =smp_processor_id ();const char *name;struct clock_event_device *evt;int irq;
@@ -407,5 +408,5 @@ __init void xen_time_init(void) tsc_disable =0 ; -xen_setup_timer (cpu); -} +xen_setup_timer (); +}
===================================================================
arch/i386/xen/xen-ops.h +24 -0
@@ -2,6 +2,13 @@#define XEN_OPS_H #include <linux/init.h> +#include <linux/percpu.h> + +/* These are code, but not functions. Defined in entry.S */ +extern const char xen_hypervisor_callback[]; +extern const char xen_failsafe_callback[]; + +void xen_copy_trap_info (struct trap_info *traps);extern struct start_info *xen_start_info;extern struct shared_info *HYPERVISOR_shared_info;
@@ -14,6 +21,7 @@ void __init xen_time_init(void);void __init xen_time_init (void );unsigned long xen_get_wallclock (void );int xen_set_wallclock (unsigned long time); +void xen_setup_timer (void );void xen_mark_init_mm_pinned (void );
@@ -27,5 +35,21 @@ static inline unsigned xen_get_lazy_modereturn ret; } +void __init xen_fill_possible_map (void ); + +void xen_smp_prepare_boot_cpu (void ); +void xen_smp_prepare_cpus (unsigned int max_cpus); +int xen_cpu_up (unsigned int cpu); +void xen_smp_cpus_done (unsigned int max_cpus); + +void xen_smp_send_stop (void ); +void xen_smp_send_reschedule (int cpu); +int xen_smp_call_function (void (*func) (void *info),void *info,int nonatomic, +int wait); +int xen_smp_call_function_single (int cpu,void (*func) (void *info),void *info, +int nonatomic,int wait); + +int xen_smp_call_function_mask (cpumask_t mask,void (*func)(void *), +void *info,int wait);#endif /* XEN_OPS_H */
===================================================================
include/asm-i386/mach-default/irq_vectors_limits.h +1 -1
@@ -1,7 +1,7 @@#ifndef _ASM_IRQ_VECTORS_LIMITS_H#define _ASM_IRQ_VECTORS_LIMITS_H -#ifdef CONFIG_X86_IO_APIC +#if defined(CONFIG_X86_IO_APIC ) || defined(CONFIG_PARAVIRT )#define NR_IRQS 224# if (224 >=32 *NR_CPUS )# define NR_IRQ_VECTORS NR_IRQS
===================================================================
include/asm-i386/smp.h +2 -0
@@ -146,6 +146,8 @@ extern void __cpu_die(unsigned int cpu);extern void __cpu_die (unsigned int cpu);extern unsigned int num_processors; +void __cpuinit smp_store_cpu_info (int id); +#endif /* !__ASSEMBLY__ */ #else /* CONFIG_SMP */
===================================================================
include/xen/events.h +23 -4
@@ -1,15 +1,32 @@#ifndef _XEN_EVENTS_H#define _XEN_EVENTS_H -#include <linux/irq.h> +#include <linux/interrupt.h> + +#include <xen/interface/event_channel.h> +#include <asm/xen/hypercall.h> + +enum ipi_vector { +XEN_RESCHEDULE_VECTOR , +XEN_CALL_FUNCTION_VECTOR , + +XEN_NR_IPIS , +};int bind_evtchn_to_irqhandler (unsigned int evtchn, -irqreturn_t (*handler)(int ,void *), +irq_handler_t handler,unsigned long irqflags,const char *devname,void *dev_id);int bind_virq_to_irqhandler (unsigned int virq,unsigned int cpu, -irqreturn_t (*handler)(int ,void *), -unsigned long irqflags,const char *devname,void *dev_id); +irq_handler_t handler, +unsigned long irqflags,const char *devname, +void *dev_id); +int bind_ipi_to_irqhandler (enum ipi_vector ipi, +unsigned int cpu, +irq_handler_t handler, +unsigned long irqflags, +const char *devname, +void *dev_id);/* * Common unbind function for all event sources. Takes IRQ to unbind from.
@@ -17,6 +34,8 @@ int bind_virq_to_irqhandler(unsigned int* made with bind_evtchn_to_irqhandler()). */ void unbind_from_irqhandler (unsigned int irq,void *dev_id); + +void xen_send_IPI_one (unsigned int cpu,enum ipi_vector vector);static inline void notify_remote_via_evtchn (int port) {
Re: [patch 28/34] Xen-pv_ops: Xen SMP guest support
Quoted 2 lines · Jeremy Fitzhardinge
[ brcl, mingo: Would you mind having a close look at the code in
xen_exit_mmanddrop_mm_ref? - Thanks ]
I see what you're doing here, and this comment seems to allude to the reasons behind it without spelling it out.
Quoted 4 lines
+/*
+ * We aggressively remove defunct pgd from cr3. We executeunmap_vmas()
+ * *much* faster this way.
+ */
Are you switching mms in order to avoid all of the overhead associated with flushing an active mm during task exit? If so, it makes much more sense to do this in the generic helper, as it should be a win on x86 and x86-64 where switching to init_mm is relatively low cost given the support for global pages. I'll run some tests on a couple of systems this evening and post the results.
-ben
Signature · 2 lines
"Time is of no importance, Mr. President, only life is important." Don't Email: <[email protected]>.
Re: [patch 28/34] Xen-pv_ops: Xen SMP guest support
Quoted 6 lines · Benjamin LaHaise
Are you switching mms in order to avoid all of the overhead associated with flushing an active mm during task exit? If so, it makes much more sense to do this in the generic helper, as it should be a win on x86 and x86-64 where switching to
init_mmis relatively low cost given the support for global pages. I'll run some tests on a couple of systems this evening and post the results.
Under Xen, the pagetables are mapped read-only to the guest, so all pagetable updates need to go via the hypervisor. If we switch to init_mm and unpin (make all the pages RW) before pulling the pagetable apart, then we can just traverse and modify the pagetable memory as normal RW memory. It avoids a heap of traps into the hypervisor.
J
Re: [patch 28/34] Xen-pv_ops: Xen SMP guest support
In reply to Benjamin LaHaiseQuoted 9 lines · Benjamin LaHaise
Quoted 6 lines
+/*
+ * We aggressively remove defunct pgd from cr3. We executeunmap_vmas()
+ * *much* faster this way.
+ */Are you switching mms in order to avoid all of the overhead associated with flushing an active mm during task exit? If so, it makes much more sense to do this in the generic helper, as it should be a win on x86 and x86-64 where switching to
init_mmis relatively low cost given the support for global pages. I'll run some tests on a couple of systems this evening and post the results.
I tried this experiment on both x86 and x86-64 and I recall not seeing any effect. The mmu_gather optimization does most of this already.
Zach
PATCH 29/34 Xen-pv_ops: Use the hvc console infrastructure for Xen console
In reply to Jeremy FitzhardingeImplement a Xen back-end for hvc console.
From: Gerd Hoffmann <[email protected]>
--- arch/i386/xen/Kconfig | 1 arch/i386/xen/events.c | 3 - drivers/Makefile | 3 + drivers/xen/Makefile | 1 drivers/xen/hvc-console.c | 134 +++++++++++++++++++++++++++++++++++++++++++++ include/xen/events.h | 1 6 files changed, 142 insertions(+), 1 deletion(-)
===================================================================
arch/i386/xen/Kconfig +1 -0
@@ -5,6 +5,7 @@ config XEN config XEN bool "Enable support for Xen hypervisor" depends on PARAVIRT && !PREEMPT + select HVC_DRIVER default y help This is the Linux Xen port.
===================================================================
arch/i386/xen/events.c +2 -1
@@ -219,7 +219,7 @@ static int find_unbound_irq(void)return irq; } -static int bind_evtchn_to_irq (unsigned int evtchn) +int bind_evtchn_to_irq (unsigned int evtchn) {int irq;
@@ -244,6 +244,7 @@ static int bind_evtchn_to_irq(unsigned ireturn irq; } +EXPORT_SYMBOL_GPL (bind_evtchn_to_irq);static int bind_ipi_to_irq (unsigned int ipi,unsigned int cpu) {
===================================================================
drivers/Makefile +3 -0
@@ -14,6 +14,9 @@ obj-$(CONFIG_ACPI) += acpi/ # was used and do nothing if so obj-$(CONFIG_PNP) += pnp/ obj-$(CONFIG_ARM_AMBA) += amba/ + +# Xen is the default console when running as a guest +obj-$(CONFIG_XEN) += xen/ # char/ comes before serial/ etc so that the VT console is the boot-time # default.
===================================================================
drivers/xen/Makefile new file +1 -0
@@ -0,0 +1,1 @@ +obj-y += hvc-console.o
===================================================================
drivers/xen/hvc-console.c new file +134 -0
134 lines not on this page · raw message
===================================================================
include/xen/events.h +1 -0
@@ -13,6 +13,7 @@ enum ipi_vector { XEN_NR_IPIS, }; +int bind_evtchn_to_irq (unsigned int evtchn); int bind_evtchn_to_irqhandler(unsigned int evtchn, irq_handler_t handler, unsigned long irqflags, const char *devname,
PATCH 30/34 Xen-pv_ops: Add early printk support via hvc console
In reply to Jeremy FitzhardingeAdd early printk support via hvc console, enable using "earlyprintk=xen" on the kernel command line.
From: Gerd Hoffmann <[email protected]>
--- arch/x86_64/kernel/early_printk.c | 5 +++++ drivers/xen/hvc-console.c | 25 +++++++++++++++++++++++++ include/xen/hvc-console.h | 6 ++++++ 3 files changed, 36 insertions(+)
===================================================================
arch/x86_64/kernel/early_printk.c +5 -0
@@ -6,6 +6,7 @@#include <asm/io.h> #include <asm/processor.h> #include <asm/fcntl.h> +#include <xen/hvc-console.h> /* Simple VGA output */
@@ -243,6 +244,10 @@ static int __init setup_early_printk(chasimnow_init (buf +6 ); early_console = &simnow_console; keep_early =1 ; +#ifdef CONFIG_XEN + }else if (!strncmp (buf,"xen" ,3 )) { + early_console = &xenboot_console; +#endif }register_console (early_console);return 0 ;
===================================================================
drivers/xen/hvc-console.c +25 -0
@@ -28,6 +28,7 @@#include <xen/page.h> #include <xen/events.h> #include <xen/interface/io/console.h> +#include <xen/hvc-console.h> #include "../char/hvc_console.h"
@@ -132,3 +133,27 @@ module_init(xen_init);module_init (xen_init);module_exit (xen_fini);console_initcall (xen_cons_init); + +static void xenboot_write_console (struct console *console,const char *string, +unsigned len) +{ +unsigned int linelen, off =0 ; +const char *pos; + +while (off < len &&NULL != (pos =strchr (string+off,'\n' ))) { + linelen = pos-string+off; +if (off + linelen > len) +break ; +write_console (0 , string+off, linelen); +write_console (0 ,"\r\n" ,2 ); + off += linelen +1 ; + } +if (off < len) +write_console (0 , string+off, len-off); +} + +struct console xenboot_console = { + .name ="xenboot" , + .write = xenboot_write_console, + .flags =CON_PRINTBUFFER |CON_BOOT , +};
===================================================================
include/xen/hvc-console.h new file +6 -0
@@ -0,0 +1,6 @@ +#ifndef XEN_HVC_CONSOLE_H +#define XEN_HVC_CONSOLE_H + +extern struct console xenboot_console; + +#endif /* XEN_HVC_CONSOLE_H */
PATCH 31/34 Xen-pv_ops: Add Xen grant table support
In reply to Jeremy FitzhardingeAdd Xen 'grant table' driver which allows granting of access to selected local memory pages by other virtual machines and, symmetrically, the mapping of remote memory pages which other virtual machines have granted access to.
This driver is a prerequisite for many of the Xen virtual device drivers, which grant the 'device driver domain' restricted and temporary access to only those memory pages that are currently involved in I/O operations.
--- drivers/xen/Makefile | 1 drivers/xen/grant-table.c | 454 +++++++++++++++++++++++++++++++++++++++++++++ include/xen/grant_table.h | 107 ++++++++++ 3 files changed, 562 insertions(+)
===================================================================
drivers/xen/Makefile +1 -0
@@ -1,1 +1,2 @@ obj-y += hvc-console.o +obj-y += grant-table.o obj-y += hvc-console.o
===================================================================
drivers/xen/grant-table.c new file +454 -0
454 lines not on this page · raw message
===================================================================
include/xen/grant_table.h new file +107 -0
107 lines not on this page · raw message
PATCH 32/34 Xen-pv_ops: Add the Xenbus sysfs and virtual device hotplug driver.
In reply to Jeremy FitzhardingeThis communicates with the machine control software via a registry residing in a controlling virtual machine. This allows dynamic creation, destruction and modification of virtual device configurations (network devices, block devices and CPUS, to name some examples).
--- arch/i386/xen/Kconfig | 5 drivers/xen/Makefile | 1 drivers/xen/xenbus/Makefile | 9 drivers/xen/xenbus/xenbus_client.c | 563 ++++++++++++++++++ drivers/xen/xenbus/xenbus_comms.c | 218 +++++++ drivers/xen/xenbus/xenbus_comms.h | 44 + drivers/xen/xenbus/xenbus_probe.c | 891 +++++++++++++++++++++++++++++ drivers/xen/xenbus/xenbus_probe.h | 72 ++ drivers/xen/xenbus/xenbus_probe_backend.c | 269 ++++++++ drivers/xen/xenbus/xenbus_xs.c | 840 +++++++++++++++++++++++++++ include/asm-i386/xen/hypervisor.h | 1 include/xen/xenbus.h | 208 ++++++ 12 files changed, 3121 insertions(+)
===================================================================
arch/i386/xen/Kconfig +5 -0
@@ -9,3 +9,8 @@ config XEN default y help This is the Linux Xen port. + +config XEN_BACKEND + bool + depends XEN + default n
===================================================================
drivers/xen/Makefile +1 -0
@@ -1,2 +1,3 @@ obj-y += grant-table.o obj-y += grant-table.o obj-y += hvc-console.o +obj-y += xenbus/
===================================================================
drivers/xen/xenbus/Makefile new file +9 -0
@@ -0,0 +1,9 @@ +obj-y += xenbus.o + +xenbus-objs = +xenbus-objs += xenbus_client.o +xenbus-objs += xenbus_comms.o +xenbus-objs += xenbus_xs.o +xenbus-objs += xenbus_probe.o + +obj-$(CONFIG_XEN_BACKEND) += xenbus_probe_backend.o
===================================================================
drivers/xen/xenbus/xenbus_client.c new file +563 -0
563 lines not on this page · raw message
===================================================================
drivers/xen/xenbus/xenbus_comms.c new file +218 -0
218 lines not on this page · raw message
===================================================================
drivers/xen/xenbus/xenbus_comms.h new file +44 -0
44 lines not on this page · raw message
===================================================================
drivers/xen/xenbus/xenbus_probe.c new file +891 -0
891 lines not on this page · raw message
===================================================================
drivers/xen/xenbus/xenbus_probe.h new file +72 -0
72 lines not on this page · raw message
===================================================================
drivers/xen/xenbus/xenbus_probe_backend.c new file +269 -0
269 lines not on this page · raw message
===================================================================
drivers/xen/xenbus/xenbus_xs.c new file +840 -0
840 lines not on this page · raw message
===================================================================
include/asm-i386/xen/hypervisor.h +1 -0
@@ -42,6 +42,7 @@#include <asm/ptrace.h> #include <asm/page.h> +#include <asm/desc.h> #if defined(__i386__)# ifdef CONFIG_X86_PAE # include <asm-generic/pgtable-nopud.h>
===================================================================
include/xen/xenbus.h new file +208 -0
76 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.
PATCH 33/34 Xen-pv_ops: Add Xen virtual block device driver.
In reply to Jeremy FitzhardingeThe block device frontend driver allows the kernel to access block devices exported exported by a virtual machine containing a physical block device driver.
--- drivers/block/Kconfig | 1 drivers/block/Makefile | 1 drivers/block/xen/Kconfig | 14 drivers/block/xen/Makefile | 5 drivers/block/xen/blkfront.c | 844 ++++++++++++++++++++++++++++++++++++++++++ drivers/block/xen/block.h | 135 ++++++ drivers/block/xen/vbd.c | 230 +++++++++++ include/linux/major.h | 2 8 files changed, 1232 insertions(+)
===================================================================
drivers/block/Kconfig +1 -0
@@ -461,6 +461,7 @@ config CDROM_PKTCDVD_WCACHE don't do deferred write error handling yet. source "drivers/s390/block/Kconfig" +source "drivers/block/xen/Kconfig" config ATA_OVER_ETH tristate "ATA over Ethernet support"
===================================================================
drivers/block/Makefile +1 -0
@@ -29,3 +29,4 @@ obj-$(CONFIG_BLK_DEV_SX8) += sx8.o obj-$(CONFIG_BLK_DEV_SX8) += sx8.o obj-$(CONFIG_BLK_DEV_UB) += ub.o +obj-$(CONFIG_XEN) += xen/
===================================================================
drivers/block/xen/Kconfig new file +14 -0
@@ -0,0 +1,14 @@ +menu "Xen block device drivers" + depends on XEN + +config XEN_BLKDEV_FRONTEND + tristate "Block device frontend driver" + depends on XEN + default y + help + The block device frontend driver allows the kernel to access block + devices exported from a device driver virtual machine. Unless you + are building a dedicated device driver virtual machine, then you + almost certainly want to say Y here. + +endmenu
===================================================================
drivers/block/xen/Makefile new file +5 -0
@@ -0,0 +1,5 @@ + +obj-$(CONFIG_XEN_BLKDEV_FRONTEND) := xenblk.o + +xenblk-objs := blkfront.o vbd.o +
===================================================================
drivers/block/xen/blkfront.c new file +844 -0
844 lines not on this page · raw message
===================================================================
drivers/block/xen/block.h new file +135 -0
135 lines not on this page · raw message
===================================================================
drivers/block/xen/vbd.c new file +230 -0
230 lines not on this page · raw message
===================================================================
include/linux/major.h +2 -0
@@ -156,6 +156,8 @@#define VXSPEC_MAJOR 200/* VERITAS volume config driver */ #define VXDMP_MAJOR 201/* VERITAS volume multipath driver */ +#define XENVBD_MAJOR 202/* Xen virtual block device */ +#define MSR_MAJOR 202#define CPUID_MAJOR 203
PATCH 34/34 Xen-pv_ops: Add the Xen virtual network device driver.
In reply to Jeremy FitzhardingeThe network device frontend driver allows the kernel to access network devices exported exported by a virtual machine containing a physical network device driver.
--- drivers/net/Kconfig | 12 drivers/net/Makefile | 2 drivers/net/xen-netfront.c | 1983 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1997 insertions(+)
===================================================================
drivers/net/Kconfig +12 -0
@@ -2506,6 +2506,18 @@ source "drivers/atm/Kconfig" source "drivers/s390/net/Kconfig" +config XEN_NETDEV_FRONTEND + tristate "Xen network device frontend driver" + depends on XEN + default y + help + The network device frontend driver allows the kernel to + access network devices exported exported by a virtual + machine containing a physical network device driver. The + frontend driver is intended for unprivileged guest domains; + if you are compiling a kernel for a Xen guest, you almost + certainly want to enable this. + config ISERIES_VETH tristate "iSeries Virtual Ethernet driver support" depends on PPC_ISERIES
===================================================================
drivers/net/Makefile +2 -0
@@ -218,3 +218,5 @@ obj-$(CONFIG_FS_ENET) += fs_enet/ obj-$(CONFIG_FS_ENET) += fs_enet/ obj-$(CONFIG_NETXEN_NIC) += netxen/ + +obj-$(CONFIG_XEN_NETDEV_FRONTEND) += xen-netfront.o
===================================================================
drivers/net/xen-netfront.c new file +1983 -0
1,983 lines not on this page · raw message