x86 candidate patches for review V: paravirt patches
Subsystems (11)
cover x86 candidate patches for review V: paravirt patches
Lots of paravirt patches from Jeremy and Zach. I think most of them have hit the list already.
Mostly Xen preparation, but lots of generic cleanups
Happy reviewing!
-Andi
PATCH 1/40 x86_64: update MAINTAINERS
From: Jeremy Fitzhardinge <[email protected]>
--- MAINTAINERS | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
===================================================================
MAINTAINERS +22 -0
@@ -2616,6 +2616,19 @@ T: git kernel.org:/pub/scm/linux/kernel/ 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]
@@ -3831,6 +3844,15 @@ M: [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 2/40 i386: Remove CONFIG_DEBUG_PARAVIRT
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Remove CONFIG_DEBUG_PARAVIRT. When inlining code, this option attempts to trash registers in the patch-site's "clobber" field, on the grounds that this should find bugs with incorrect clobbers. Unfortunately, the clobber field really means "registers modified by this patch site", which includes return values.
Because of this, this option has outlived its usefulness, so remove it.
--- arch/i386/Kconfig.debug | 10 ---------- arch/i386/kernel/alternative.c | 14 +------------- 2 files changed, 1 insertion(+), 23 deletions(-)
===================================================================
arch/i386/Kconfig.debug +0 -10
@@ -85,14 +85,4 @@ config DOUBLEFAULT option saves about 4k and might cause you much additional grey hair. -config DEBUG_PARAVIRT - bool "Enable some paravirtualization debugging" - default n - depends on PARAVIRT && DEBUG_KERNEL - help - Currently deliberately clobbers regs which are allowed to be - clobbered in inlined paravirt hooks, even in native mode. - If turning this off solves a problem, then DISABLE_INTERRUPTS() or - ENABLE_INTERRUPTS() is lying about what registers can be clobbered. - endmenu
arch/i386/kernel/alternative.c +1 -13
@@ -334,19 +334,7 @@ void apply_paravirt(struct paravirt_patc used = paravirt_ops.patch (p->instrtype, p->clobbers, p->instr, p->len); -#ifdef CONFIG_DEBUG_PARAVIRT - { -int i; -/* Deliberately clobber regs using "not %reg" to find bugs. */ -for (i =0 ; i <3 ; i++) { -if (p->len - used >=2 && (p->clobbers & (1 << i))) { -memcpy (p->instr + used,"\xf7\xd0" ,2 ); - p->instr[used+1 ] |= i; - used +=2 ; - } - } - } -#endif +/* Pad the rest with nops */ nop_out (p->instr + used, p->len - used); }
PATCH 3/40 i386: use paravirt_nop to consistently mark no-op operations
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Add 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.
[mingo] but only as a cleanup of the current open-coded (void *) casts. My problem with this is that it loses the types. Not that there is much to check for, but still, this adds some assumptions about how function calls look like
--- arch/i386/kernel/paravirt.c | 24 ++++++++++++------------ include/asm-i386/paravirt.h | 3 +++ 2 files changed, 15 insertions(+), 12 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +12 -12
@@ -35,7 +35,7 @@#include <asm/timer.h> /* nop stub */ -static void native_nop (void ) +void _paravirt_nop (void ) { }
@@ -207,7 +207,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,
@@ -263,25 +263,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, + .map_pt_hook = paravirt_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, + .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,
@@ -293,7 +293,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
@@ -434,6 +434,9 @@ static inline void pmd_clear(pmd_t *pmdp#define arch_leave_lazy_mmu_mode () paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_NONE)#define arch_flush_lazy_mmu_mode () paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_FLUSH) +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 4/40 i386: Add pagetable accessors to pack and unpack pagetable entries
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Add 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 | 79 ++++++++++++++++++++++++++++++----- include/asm-i386/paravirt.h | 52 ++++++++++++++++++----- include/asm-i386/pgtable-2level.h | 26 +++++++++-- include/asm-i386/pgtable-3level.h | 63 +++++++++++++++++----------- include/asm-i386/pgtable.h | 2 7 files changed, 184 insertions(+), 128 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +12 -72
@@ -117,78 +117,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 );extern void native_irq_enable_sysexit (void );
@@ -282,14 +210,26 @@ 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 +68 -11
114 lines not on this page · raw message
include/asm-i386/paravirt.h +40 -12
105 lines not on this page · raw message
include/asm-i386/pgtable-2level.h +21 -5
@@ -11,10 +11,23 @@* 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)) +#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 #define set_pte_atomic (pteptr, pteval) set_pte(pteptr,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 +38 -25
132 lines not on this page · raw message
include/asm-i386/pgtable.h +2 -0
@@ -266,6 +266,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 5/40 i386: Hooks to set up initial pagetable
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
This 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 | 128 +++++++++++++++++++++++++++++++------------- include/asm-i386/paravirt.h | 17 +++++ include/asm-i386/pgtable.h | 16 +++++ 4 files changed, 127 insertions(+), 37 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +3 -0
@@ -193,6 +193,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 +92 -36
194 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
@@ -50,6 +51,9 @@ struct paravirt_opschar *(*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 );unsigned long (*get_wallclock)(void );
@@ -370,6 +374,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); +}#ifdef CONFIG_SMP static inline void startup_ipi_hook(int phys_apicid, unsigned long start_eip,
include/asm-i386/pgtable.h +16 -0
@@ -514,6 +514,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 6/40 i386: Allocate a fixmap slot
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Allocate 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
@@ -84,6 +84,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 7/40 i386: Allow paravirt backend to choose kernel PMD sharing
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Normally 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 | 5 + arch/i386/mm/init.c | 18 +++++- arch/i386/mm/pageattr.c | 2 arch/i386/mm/pgtable.c | 88 ++++++++++++++++++++++++++------- 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 | 2 11 files changed, 101 insertions(+), 28 deletions(-)
arch/i386/kernel/paravirt.c +1 -0
@@ -132,6 +132,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 -2
@@ -603,7 +603,6 @@ do_sigbus:force_sig_info_fault (SIGBUS ,BUS_ADRERR , address, tsk); } -#ifndef CONFIG_X86_PAE void vmalloc_sync_all (void ) {/*
@@ -616,6 +615,9 @@ void vmalloc_sync_all(void)static unsigned long start =TASK_SIZE ;unsigned long address; +if (SHARED_KERNEL_PMD ) +return ; +BUILD_BUG_ON (TASK_SIZE & ~PGDIR_MASK );for (address = start; address >=TASK_SIZE ; address +=PGDIR_SIZE ) {if (!test_bit (pgd_index (address), insync)) {
@@ -638,4 +640,3 @@ void vmalloc_sync_all(void) start = address +PGDIR_SIZE ; } } -#endif
arch/i386/mm/init.c +15 -3
@@ -757,6 +757,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),
@@ -766,13 +768,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 +70 -18
140 lines not on this page · raw message
include/asm-i386/paravirt.h +1 -0
@@ -35,6 +35,7 @@ struct desc_struct;struct 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,6 +1,8 @@#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
@@ -82,6 +82,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,6 +1,12 @@#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
@@ -200,6 +200,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 +2 -0
@@ -243,6 +243,8 @@ static inline pte_t pte_mkyoung(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 ); +#ifdef CONFIG_X86_PAE # include <asm/pgtable-3level.h> #else
PATCH 8/40 x86: add hooks to intercept mm creation and destruction
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Add hooks to allow a paravirt implementation to track the lifetime of an mm. Paravirtualization requires three hooks, but only two are needed in common code. They are:
arch_dup_mmap, which is called when a new mmap is created at fork
arch_exit_mmap, which is called when the last process reference to an
mm is dropped, which typically happens on exit and exec.
The third hook is activate_mm, which is called from the arch-specific activate_mm() macro/function, and so doesn't need stub versions for other architectures. It's called when an mm is first used.
--- 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
@@ -237,6 +237,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
@@ -13,6 +13,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,6 +1,8 @@#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);extern void destroy_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);
@@ -395,6 +401,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;#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
@@ -5,6 +5,8 @@#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
@@ -6,6 +6,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 "um_mmu.h"
include/asm-v850/mmu_context.h +2 -0
@@ -1,6 +1,8 @@#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#define switch_mm (prev,next,tsk) ((void)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)
@@ -1979,6 +1980,9 @@ void exit_mmap(struct mm_struct *mm)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); tlb =tlb_gather_mmu (mm,1 );
PATCH 9/40 i386: rename struct paravirt_patch to paravirt_patch_site for clarity
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Rename 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 | 7 ++++--- include/asm-i386/alternative.h | 8 +++++--- include/asm-i386/paravirt.h | 5 ++++- 3 files changed, 13 insertions(+), 7 deletions(-)
===================================================================
arch/i386/kernel/alternative.c +4 -3
@@ -325,9 +325,10 @@ void alternatives_smp_switch(int smp)#endif #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) { -struct paravirt_patch *p; +struct paravirt_patch_site *p;for (p = start; p < end; p++) {unsigned int used;
@@ -342,7 +343,7 @@ void apply_paravirt(struct paravirt_patc/* Sync to be conservative, in case we patched following instructions */ sync_core(); } -extern struct paravirt_patch __start_parainstructions[], +extern struct paravirt_patch_site __start_parainstructions[], __stop_parainstructions[];#endif /* CONFIG_PARAVIRT */
include/asm-i386/alternative.h +5 -3
@@ -115,12 +115,14 @@ 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
@@ -505,13 +505,16 @@ 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 __parainstructions[], + __parainstructions_end[]; +#define paravirt_alt (insn_string, typenum, clobber) \ "771:\n\t" insn_string "\n" "772:\n" \ ".pushsection .parainstructions,\"a\"\n" \
PATCH 10/40 i386: Use patch site IDs computed from offset in paravirt_ops structure
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Use 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.
Also, since the identifier is derived from the offset into paravirt_ops, the offset can be derived from the identifier. This is used to remove replicated information in the various callsite macros, which has been a source of bugs in the past.
This patch also drops the fused save_fl+cli operation, which doesn't really add much and makes things more complex - specifically because it breaks the 1:1 relationship between identifiers and offsets. If this operation turns out to be particularly beneficial, then the right answer is to define a new entrypoint for it.
--- arch/i386/kernel/paravirt.c | 14 +-- arch/i386/kernel/vmi.c | 39 +-------- include/asm-i386/paravirt.h | 179 ++++++++++++++++++++++---------------------- 3 files changed, 105 insertions(+), 127 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +6 -8
@@ -58,7 +58,6 @@ DEF_NATIVE(cli, "cli");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
@@ -83,11 +83,6 @@ extern struct paravirt_patch __start_par#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
@@ -135,33 +130,17 @@ static unsigned patch_internal(int call,static 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 ;
@@ -796,12 +775,6 @@ static inline int __init activate_vmi(vopara_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 +93 -86
236 lines not on this page · raw message
PATCH 11/40 i386: Fix patch site clobbers to include return register
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Fix 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.
Also, make sure that callsites which are used in contexts which don't allow clobbers actually save and restore all clobberable registers.
--- arch/i386/kernel/entry.S | 2 +- include/asm-i386/paravirt.h | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-)
===================================================================
arch/i386/kernel/entry.S +1 -1
@@ -338,7 +338,7 @@ sysenter_past_esp: jae syscall_badsys call *sys_call_table(,%eax,4) movl %eax,PT_EAX(%esp) - DISABLE_INTERRUPTS(CLBR_ECX|CLBR_EDX) + DISABLE_INTERRUPTS(CLBR_ANY) TRACE_IRQS_OFF movl TI_flags(%ebp), %ecx testw $_TIF_ALLWORK_MASK, %cx
include/asm-i386/paravirt.h +10 -8
@@ -535,7 +535,7 @@ static inline unsigned long __raw_local_ "popl %%edx; popl %%ecx") : "=a"(f) : paravirt_type(save_fl), - paravirt_clobber(CLBR_NONE) + paravirt_clobber(CLBR_EAX) : "memory", "cc");return f; }
@@ -620,27 +620,29 @@ static inline unsigned long __raw_local_ .popsection#define INTERRUPT_RETURN \ - PARA_SITE(PARA_PATCH(PARAVIRT_iret), CLBR_ANY, \ + PARA_SITE(PARA_PATCH(PARAVIRT_iret), CLBR_NONE, \ jmp *%cs:paravirt_ops+PARAVIRT_iret)#define DISABLE_INTERRUPTS (clobbers) \ PARA_SITE(PARA_PATCH(PARAVIRT_irq_disable), clobbers, \ - pushl %ecx; pushl %edx; \ + pushl %eax; pushl %ecx; pushl %edx; \ call *%cs:paravirt_ops+PARAVIRT_irq_disable; \ - popl %edx; popl %ecx) \ + popl %edx; popl %ecx; popl %eax) \#define ENABLE_INTERRUPTS (clobbers) \ PARA_SITE(PARA_PATCH(PARAVIRT_irq_enable), clobbers, \ - pushl %ecx; pushl %edx; \ + pushl %eax; pushl %ecx; pushl %edx; \ call *%cs:paravirt_ops+PARAVIRT_irq_enable; \ - popl %edx; popl %ecx) + popl %edx; popl %ecx; popl %eax)#define ENABLE_INTERRUPTS_SYSEXIT \ - PARA_SITE(PARA_PATCH(PARAVIRT_irq_enable_sysexit), CLBR_ANY, \ + PARA_SITE(PARA_PATCH(PARAVIRT_irq_enable_sysexit), CLBR_NONE, \ jmp *%cs:paravirt_ops+PARAVIRT_irq_enable_sysexit)#define GET_CR0_INTO_EAX \ - call *paravirt_ops+PARAVIRT_read_cr0 + push %ecx; push %edx; \ + call *paravirt_ops+PARAVIRT_read_cr0; \ + pop %edx; pop %ecx#endif /* __ASSEMBLY__ */ #endif /* CONFIG_PARAVIRT */
PATCH 12/40 i386: Consistently wrap paravirt ops callsites to make them patchable
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Wrap 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 | 686 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 560 insertions(+), 126 deletions(-)
===================================================================
include/asm-i386/paravirt.h +560 -126
@@ -124,7 +124,7 @@ struct paravirt_opsvoid (*flush_tlb_user)(void );void (*flush_tlb_kernel)(void ); -void (*flush_tlb_single)(u32 addr); +void (*flush_tlb_single)(unsigned long addr);void (*map_pt_hook)(int type,pte_t *va,u32 pfn);
@@ -188,7 +188,7 @@ extern struct paravirt_ops paravirt_ops;#define paravirt_clobber (clobber) \ [paravirt_clobber] "i" (clobber) -#define PARAVIRT_CALL "call *paravirt_ops+%c[paravirt_typenum]*4;" +#define PARAVIRT_CALL "call *(paravirt_ops+%c[paravirt_typenum]*4);"#define _paravirt_alt (insn_string, type, clobber) \ "771:\n\t" insn_string "\n" "772:\n" \
@@ -199,26 +199,234 @@ extern struct paravirt_ops paravirt_ops;" .short " clobber"\n" \ ".popsection\n" -#define paravirt_alt (insn_string) \ +#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(PARAVIRT_CALL) \ + : "=A" (__tmp), "=c" (__ecx) \ + : paravirt_type(__op), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + __ret = (__rettype)__tmp; \ + } else { \ + unsigned long __tmp, __edx, __ecx; \ + asm volatile(paravirt_alt(PARAVIRT_CALL) \ + : "=a" (__tmp), "=d" (__edx), \ + "=c" (__ecx) \ + : paravirt_type(__op), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + __ret = (__rettype)__tmp; \ + } \ + __ret; \ + }) +#define PVOP_VCALL0 (__op) \ + ({ \ + unsigned long __eax, __edx, __ecx; \ + asm volatile(paravirt_alt(PARAVIRT_CALL) \ + : "=a" (__eax), "=d" (__edx), "=c" (__ecx) \ + : paravirt_type(__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(PARAVIRT_CALL) \ + : "=A" (__tmp), "=c" (__ecx) \ + : "a" ((u32)(arg1)), \ + paravirt_type(__op), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + __ret = (__rettype)__tmp; \ + } else { \ + unsigned long __tmp, __edx, __ecx; \ + asm volatile(paravirt_alt(PARAVIRT_CALL) \ + : "=a" (__tmp), "=d" (__edx), \ + "=c" (__ecx) \ + : "0" ((u32)(arg1)), \ + paravirt_type(__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(PARAVIRT_CALL) \ + : "=a" (__eax), "=d" (__edx), "=c" (__ecx) \ + : "0" ((u32)(arg1)), \ + paravirt_type(__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(PARAVIRT_CALL) \ + : "=A" (__tmp), "=c" (__ecx) \ + : "a" ((u32)(arg1)), \ + "d" ((u32)(arg2)), \ + paravirt_type(__op), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + __ret = (__rettype)__tmp; \ + } else { \ + unsigned long __tmp, __edx, __ecx; \ + asm volatile(paravirt_alt(PARAVIRT_CALL) \ + : "=a" (__tmp), "=d" (__edx), \ + "=c" (__ecx) \ + : "0" ((u32)(arg1)), \ + "1" ((u32)(arg2)), \ + paravirt_type(__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(PARAVIRT_CALL) \ + : "=a" (__eax), "=d" (__edx), "=c" (__ecx) \ + : "0" ((u32)(arg1)), \ + "1" ((u32)(arg2)), \ + paravirt_type(__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(PARAVIRT_CALL) \ + : "=A" (__tmp), "=c" (__ecx) \ + : "a" ((u32)(arg1)), \ + "d" ((u32)(arg2)), \ + "1" ((u32)(arg3)), \ + paravirt_type(__op), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \ + __ret = (__rettype)__tmp; \ + } else { \ + unsigned long __tmp, __edx, __ecx; \ + asm volatile(paravirt_alt(PARAVIRT_CALL) \ + : "=a" (__tmp), "=d" (__edx), \ + "=c" (__ecx) \ + : "0" ((u32)(arg1)), \ + "1" ((u32)(arg2)), \ + "2" ((u32)(arg3)), \ + paravirt_type(__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(PARAVIRT_CALL) \ + : "=a" (__eax), "=d" (__edx), "=c" (__ecx) \ + : "0" ((u32)(arg1)), \ + "1" ((u32)(arg2)), \ + "2" ((u32)(arg3)), \ + paravirt_type(__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(PARAVIRT_CALL) \ + "lea 4(%%esp),%%esp" \ + : "=A" (__tmp), "=c" (__ecx) \ + : "a" ((u32)(arg1)), \ + "d" ((u32)(arg2)), \ + "1" ((u32)(arg3)), \ + [_arg4] "mr" ((u32)(arg4)), \ + paravirt_type(__op), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc",); \ + __ret = (__rettype)__tmp; \ + } else { \ + unsigned long __tmp, __edx, __ecx; \ + asm volatile("push %[_arg4]; " \ + paravirt_alt(PARAVIRT_CALL) \ + "lea 4(%%esp),%%esp" \ + : "=a" (__tmp), "=d" (__edx), "=c" (__ecx) \ + : "0" ((u32)(arg1)), \ + "1" ((u32)(arg2)), \ + "2" ((u32)(arg3)), \ + [_arg4]"mr" ((u32)(arg4)), \ + paravirt_type(__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(PARAVIRT_CALL) \ + "lea 4(%%esp),%%esp" \ + : "=a" (__eax), "=d" (__edx), "=c" (__ecx) \ + : "0" ((u32)(arg1)), \ + "1" ((u32)(arg2)), \ + "2" ((u32)(arg3)), \ + [_arg4]"mr" ((u32)(arg4)), \ + paravirt_type(__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); +PVOP_VCALL2 (load_esp0, tss, thread); }#define ARCH_SETUP paravirt_ops.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 )
@@ -230,127 +438,208 @@ static inline void (*choose_time_init(vostatic 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) +static inline unsigned long paravirt_get_debugreg (int reg) +{ + return PVOP_CALL1(unsigned long, get_debugreg, reg); +} +#define get_debugreg (var, reg) var = paravirt_get_debugreg(reg) +static inline void set_debugreg (unsigned long val,int reg) +{ +PVOP_VCALL2 (set_debugreg, reg, val); +} -#define clts () paravirt_ops.clts() +static inline void clts (void ) +{ +PVOP_VCALL0 (clts); +} + +static inline unsigned long read_cr0 (void ) +{ + return PVOP_CALL0(unsigned long, read_cr0); +} -#define read_cr0 () paravirt_ops.read_cr0() -#define write_cr0 (x) paravirt_ops.write_cr0(x) +static inline void write_cr0 (unsigned long x) +{ +PVOP_VCALL1 (write_cr0, x); +} -#define read_cr2 () paravirt_ops.read_cr2() -#define write_cr2 (x) paravirt_ops.write_cr2(x) +static inline unsigned long read_cr2 (void ) +{ + return PVOP_CALL0(unsigned long, read_cr2); +} -#define read_cr3 () paravirt_ops.read_cr3() -#define write_cr3 (x) paravirt_ops.write_cr3(x) +static inline void write_cr2 (unsigned long x) +{ +PVOP_VCALL1 (write_cr2, 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) +static inline unsigned long read_cr3 (void ) +{ + return PVOP_CALL0(unsigned long, read_cr3); +} -#define raw_ptep_get_and_clear (xp) (paravirt_ops.ptep_get_and_clear(xp)) +static inline void write_cr3 (unsigned long x) +{ +PVOP_VCALL1 (write_cr3, x); +} + +static inline unsigned long read_cr4 (void ) +{ + return PVOP_CALL0(unsigned long, read_cr4); +} +static inline unsigned long read_cr4_safe (void ) +{ + return PVOP_CALL0(unsigned long, read_cr4_safe); +} + +static inline void write_cr4 (unsigned long 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 (); +PVOP_VCALL0 (safe_halt); +} + +static inline void wbinvd (void ) +{ +PVOP_VCALL0 (wbinvd); } -#define wbinvd () paravirt_ops.wbinvd()#define get_kernel_rpl () (paravirt_ops.kernel_rpl) +static inline u64 paravirt_read_msr (unsigned msr,int *err) +{ +return PVOP_CALL2 (u64, read_msr, msr, err); +} +static inline int paravirt_write_msr (unsigned msr,unsigned low,unsigned high) +{ +return PVOP_CALL3 (int, write_msr, msr, low, high); +} +/* These should all do BUG_ON(_err), but our headers are too tangled. */ -#define rdmsr (msr,val1,val2) do { \ - int _err; \ - u64 _l = paravirt_ops.read_msr(msr,&_err); \ - val1 = (u32)_l; \ - val2 = _l >> 32; \ +#define rdmsr (msr,val1,val2) do { \ + int _err; \ + u64 _l = paravirt_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); \ +#define wrmsr (msr,val1,val2) do { \ + paravirt_write_msr(msr, val1, val2); \ } while(0) -#define rdmsrl (msr,val) do { \ - int _err; \ - val = paravirt_ops.read_msr((msr),&_err); \ +#define rdmsrl (msr,val) do { \ + int _err; \ + val = paravirt_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) ((void)paravirt_write_msr(msr, val, 0)) +#define wrmsr_safe (msr,a,b) paravirt_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); \ - (*a) = (u32)_l; \ - (*b) = _l >> 32; \ +#define rdmsr_safe (msr,a,b) ({ \ + int _err; \ + u64 _l = paravirt_read_msr(msr, &_err); \ + (*a) = (u32)_l; \ + (*b) = _l >> 32; \ _err; }) -#define rdtsc (low,high) do { \ - u64 _l = paravirt_ops.read_tsc(); \ - low = (u32)_l; \ - high = _l >> 32; \ + +static inline u64 paravirt_read_tsc (void ) +{ +return PVOP_CALL0 (u64, read_tsc); +} +#define rdtsc (low,high) do { \ + u64 _l = paravirt_read_tsc(); \ + low = (u32)_l; \ + high = _l >> 32; \ } while(0) -#define rdtscl (low) do { \ - u64 _l = paravirt_ops.read_tsc(); \ - low = (int)_l; \ +#define rdtscl (low) do { \ + u64 _l = paravirt_read_tsc(); \ + low = (int)_l; \ } while(0) -#define rdtscll (val) (val = paravirt_ops.read_tsc()) +#define rdtscll (val) (val = paravirt_read_tsc())#define get_scheduled_cycles (val) (val = paravirt_ops.get_scheduled_cycles())#define calculate_cpu_khz () (paravirt_ops.get_cpu_khz())#define write_tsc (val1,val2) wrmsr(0x10, val1, val2) -#define rdpmc (counter,low,high) do { \ - u64 _l = paravirt_ops.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) +static inline unsigned long long paravirt_read_pmc (int counter) +{ +return PVOP_CALL1 (u64, read_pmc, counter); +} -#define pte_val (x) paravirt_ops.pte_val(x) -#define pgd_val (x) paravirt_ops.pgd_val(x) +#define rdpmc (counter,low,high) do { \ + u64 _l = paravirt_read_pmc(counter); \ + low = (u32)_l; \ + high = _l >> 32; \ +} while(0) -#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); +} +static inline unsigned long paravirt_store_tr (void ) +{ + return PVOP_CALL0(unsigned long, store_tr); +} +#define store_tr (tr) ((tr) = paravirt_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 ) {
@@ -368,27 +657,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
@@ -408,93 +697,205 @@ static inline void paravirt_pagetable_sestatic 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); +PVOP_VCALL1 (exit_mmap, mm); +} + +static inline void __flush_tlb (void ) +{ +PVOP_VCALL0 (flush_tlb_user); +} +static inline void __flush_tlb_global (void ) +{ +PVOP_VCALL0 (flush_tlb_kernel); +} +static inline void __flush_tlb_single (unsigned long 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); +} + +static inline void paravirt_alloc_pt (unsigned pfn) +{ +PVOP_VCALL1 (alloc_pt, pfn); +} +static inline void paravirt_release_pt (unsigned pfn) +{ +PVOP_VCALL1 (release_pt, pfn); } -#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) +static inline void paravirt_alloc_pd (unsigned pfn) +{ +PVOP_VCALL1 (alloc_pd, pfn); +} -#define paravirt_map_pt_hook (type, va, pfn) paravirt_ops.map_pt_hook(type, va, pfn) +static inline void paravirt_alloc_pd_clone (unsigned pfn,unsigned clonepfn, +unsigned start,unsigned count) +{ +PVOP_VCALL4 (alloc_pd_clone, pfn, clonepfn, start, count); +} +static inline void paravirt_release_pd (unsigned pfn) +{ +PVOP_VCALL1 (release_pd, pfn); +} -#define paravirt_alloc_pt (pfn) paravirt_ops.alloc_pt(pfn) -#define paravirt_release_pt (pfn) paravirt_ops.release_pt(pfn) +static inline void pte_update (struct mm_struct *mm,unsigned long addr, +pte_t *ptep) +{ +PVOP_VCALL3 (pte_update, mm, addr, ptep); +} -#define paravirt_alloc_pd (pfn) paravirt_ops.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) +static inline void pte_update_defer (struct mm_struct *mm,unsigned long addr, +pte_t *ptep) +{ +PVOP_VCALL3 (pte_update_defer, mm, addr, ptep); +} -static inline void set_pte (pte_t *ptep,pte_t pteval) +#ifdef CONFIG_X86_PAE +static inline pte_t __pte (unsigned long long val) { - paravirt_ops.set_pte (ptep, pteval); + unsigned long long ret = PVOP_CALL2(unsigned long long, make_pte, + val, val >>32 ); +return (pte_t ) { ret, ret >>32 }; } -static inline void set_pte_at (struct mm_struct *mm,unsigned long addr, -pte_t *ptep,pte_t pteval) +static inline pmd_t __pmd (unsigned long long val) { - paravirt_ops.set_pte_at (mm, addr, ptep, pteval); + return (pmd_t) { PVOP_CALL2(unsigned long long, make_pmd, val, val >> 32) }; } -static inline void set_pmd (pmd_t *pmdp,pmd_t pmdval) +static inline pgd_t __pgd (unsigned long long val) { - paravirt_ops.set_pmd (pmdp, pmdval); + return (pgd_t) { PVOP_CALL2(unsigned long long, make_pgd, val, val >> 32) }; } -static inline void pte_update (struct mm_struct *mm,u32 addr,pte_t *ptep) +static inline unsigned long long pte_val (pte_t x) { - paravirt_ops.pte_update (mm, addr, ptep); + return PVOP_CALL2(unsigned long long, pte_val, x.pte_low, x.pte_high); } -static inline void pte_update_defer (struct mm_struct *mm,u32 addr,pte_t *ptep) +static inline unsigned long long pmd_val (pmd_t x) { - paravirt_ops.pte_update_defer (mm, addr, ptep); + 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) +{ +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); } -#ifdef CONFIG_X86_PAE static inline void set_pte_atomic (pte_t *ptep,pte_t pteval) { - paravirt_ops.set_pte_atomic (ptep, 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) +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) +{ +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); +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) }; } -#endif + +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
@@ -503,14 +904,37 @@ static inline void pmd_clear(pmd_t *pmdp#define PARAVIRT_LAZY_FLUSH 3#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_flush_lazy_cpu_mode () paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_FLUSH) +static inline void arch_enter_lazy_cpu_mode (void ) +{ +PVOP_VCALL1 (set_lazy_mode,PARAVIRT_LAZY_CPU ); +} + +static inline void arch_leave_lazy_cpu_mode (void ) +{ +PVOP_VCALL1 (set_lazy_mode,PARAVIRT_LAZY_NONE ); +} + +static inline void arch_flush_lazy_cpu_mode (void ) +{ +PVOP_VCALL1 (set_lazy_mode,PARAVIRT_LAZY_FLUSH ); +} +#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_flush_lazy_mmu_mode () paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_FLUSH) +static inline void arch_enter_lazy_mmu_mode (void ) +{ +PVOP_VCALL1 (set_lazy_mode,PARAVIRT_LAZY_MMU ); +} + +static inline void arch_leave_lazy_mmu_mode (void ) +{ +PVOP_VCALL1 (set_lazy_mode,PARAVIRT_LAZY_NONE ); +} + +static inline void arch_flush_lazy_mmu_mode (void ) +{ +PVOP_VCALL1 (set_lazy_mode,PARAVIRT_LAZY_FLUSH ); +}void _paravirt_nop (void );#define paravirt_nop ((void *)_paravirt_nop)
@@ -603,6 +1027,16 @@ static inline unsigned long __raw_local_ paravirt_clobber(CLBR_EAX)#undef PARAVIRT_CALL +#undef PVOP_VCALL0 +#undef PVOP_CALL0 +#undef PVOP_VCALL1 +#undef PVOP_CALL1 +#undef PVOP_VCALL2 +#undef PVOP_CALL2 +#undef PVOP_VCALL3 +#undef PVOP_CALL3 +#undef PVOP_VCALL4 +#undef PVOP_CALL4#else /* __ASSEMBLY__ */
PATCH 13/40 i386: Document asm-i386/paravirt.h
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Clean things up, and broadly document:
- the paravirt_ops functions themselves
- the patching mechanism
--- include/asm-i386/paravirt.h | 131 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 121 insertions(+), 10 deletions(-)
===================================================================
include/asm-i386/paravirt.h +121 -10
@@ -21,6 +21,14 @@ struct Xgt_desc_struct;struct tss_struct ;struct mm_struct ;struct desc_struct ; + +/* Lazy mode for batching updates / context switch */ +enum paravirt_lazy_mode { +PARAVIRT_LAZY_NONE =0 , +PARAVIRT_LAZY_MMU =1 , +PARAVIRT_LAZY_CPU =2 , +}; +struct paravirt_ops {unsigned int kernel_rpl;
@@ -37,22 +45,33 @@ struct paravirt_ops*/ unsigned (*patch)(u8 type,u16 clobber,void *firstinsn,unsigned len); +/* Basic arch-specific setup */ void (*arch_setup)(void );char *(*memory_setup)(void );void (*init_IRQ)(void ); +void (*time_init)(void ); +/* +* Called before/after init_mm pagetable setup. setup_start +* may reset %cr3, and may pre-install parts of the pagetable; +* pagetable setup is expected to preserve any existing +* mapping. +*/ void (*pagetable_setup_start)(pgd_t *pgd_base);void (*pagetable_setup_done)(pgd_t *pgd_base); +/* Print a banner to identify the environment */ void (*banner)(void ); +/* Set and set time of day */ unsigned long (*get_wallclock)(void );int (*set_wallclock)(unsigned long ); -void (*time_init)(void ); +/* cpuid emulation, mostly so that caps bits can be disabled */ void (*cpuid)(unsigned int *eax,unsigned int *ebx,unsigned int *ecx,unsigned int *edx); +/* hooks for various privileged instructions */ unsigned long (*get_debugreg)(int regno);void (*set_debugreg)(int regno,unsigned long value);
@@ -71,15 +90,23 @@ struct paravirt_opsunsigned long (*read_cr4)(void );void (*write_cr4)(unsigned long ); +/* +* Get/set interrupt state. save_fl and restore_fl are only +* expected to use X86_EFLAGS_IF; all other bits +* returned from save_fl are undefined, and may be ignored by +* restore_fl. +*/ unsigned long (*save_fl)(void );void (*restore_fl)(unsigned long );void (*irq_disable)(void );void (*irq_enable)(void );void (*safe_halt)(void );void (*halt)(void ); +void (*wbinvd)(void ); -/* err = 0/-EFAULT. wrmsr returns 0/-EFAULT. */ +/* MSR, PMC and TSR operations. +err = 0/-EFAULT. wrmsr returns 0/-EFAULT. */ u64 (*read_msr)(unsigned int msr,int *err);int (*write_msr)(unsigned int msr,u64 val);
@@ -88,6 +115,7 @@ struct paravirt_opsu64 (*get_scheduled_cycles)(void);unsigned long (*get_cpu_khz)(void ); +/* Segment descriptor handling */ void (*load_tr_desc)(void );void (*load_gdt)(const struct Xgt_desc_struct *);void (*load_idt)(const struct Xgt_desc_struct *);
@@ -105,9 +133,12 @@ struct paravirt_opsvoid (*load_esp0)(struct tss_struct *tss,struct thread_struct *t);void (*set_iopl_mask)(unsigned mask); -void (*io_delay)(void ); +/* +* Hooks for intercepting the creation/use/destruction of an +* mm_struct. +*/ void (*activate_mm)(struct mm_struct *prev,struct mm_struct *next); void (*dup_mmap)(struct mm_struct *oldmm,
@@ -115,30 +146,43 @@ struct paravirt_opsvoid (*exit_mmap)(struct mm_struct *mm);#ifdef CONFIG_X86_LOCAL_APIC +/* +* Direct APIC operations, principally for VMI. Ideally +* these shouldn't be in this interface. +*/ void (*apic_write)(unsigned long reg,unsigned long v);void (*apic_write_atomic)(unsigned long reg,unsigned long v);unsigned long (*apic_read)(unsigned long reg);void (*setup_boot_clock)(void );void (*setup_secondary_clock)(void ); + +void (*startup_ipi_hook)(int phys_apicid, +unsigned long start_eip, +unsigned long start_esp);#endif +/* TLB operations */ void (*flush_tlb_user)(void );void (*flush_tlb_kernel)(void );void (*flush_tlb_single)(unsigned long addr);void (*map_pt_hook)(int type,pte_t *va,u32 pfn); +/* Hooks for allocating/releasing pagetable pages */ void (*alloc_pt)(u32 pfn);void (*alloc_pd)(u32 pfn);void (*alloc_pd_clone)(u32 pfn,u32 clonepfn,u32 start,u32 count);void (*release_pt)(u32 pfn);void (*release_pd)(u32 pfn); +/* Pagetable manipulation functions */ void (*set_pte)(pte_t *ptep,pte_t pteval); -void (*set_pte_at)(struct mm_struct *mm,unsigned long addr,pte_t *ptep,pte_t pteval); +void (*set_pte_at)(struct mm_struct *mm,unsigned long addr, +pte_t *ptep,pte_t pteval);void (*set_pmd)(pmd_t *pmdp,pmd_t pmdval);void (*pte_update)(struct mm_struct *mm,unsigned long addr,pte_t *ptep); -void (*pte_update_defer)(struct mm_struct *mm,unsigned long addr,pte_t *ptep); +void (*pte_update_defer)(struct mm_struct *mm, +unsigned long addr,pte_t *ptep);pte_t (*ptep_get_and_clear)(pte_t *ptep);
@@ -164,13 +208,12 @@ struct paravirt_opspgd_t (*make_pgd)(unsigned long pgd);#endif -void (*set_lazy_mode)(int mode); +/* Set deferred update mode, used for batching operations. */ +void (*set_lazy_mode)(enum paravirt_lazy_mode mode);/* These two are jmp to, not actually called. */ void (*irq_enable_sysexit)(void );void (*iret)(void ); - -void (*startup_ipi_hook)(int phys_apicid,unsigned long start_eip,unsigned long start_esp); };/* Mark a paravirt probe function. */
@@ -188,8 +231,10 @@ extern struct paravirt_ops paravirt_ops;#define paravirt_clobber (clobber) \ [paravirt_clobber] "i" (clobber) -#define PARAVIRT_CALL "call *(paravirt_ops+%c[paravirt_typenum]*4);" - +/* +* Generate some code, and mark it as patchable by the +* apply_paravirt() alternate instruction patcher. +*/ #define _paravirt_alt (insn_string, type, clobber) \ "771:\n\t" insn_string "\n" "772:\n" \ ".pushsection .parainstructions,\"a\"\n" \
@@ -199,9 +244,74 @@ extern struct paravirt_ops paravirt_ops;" .short " clobber"\n" \".popsection\n" +/* Generate patchable code, with the default asm parameters. */#define paravirt_alt (insn_string) \ _paravirt_alt(insn_string, "%c[paravirt_typenum]", "%c[paravirt_clobber]") +/* +* This generates an indirect call based on the operation type number. +* The type number, computed in PARAVIRT_PATCH, is derived from the +* offset into the paravirt_ops structure, and can therefore be freely +* converted back into a structure offset. +*/ +#define PARAVIRT_CALL "call *(paravirt_ops+%c[paravirt_typenum]*4);" + +/* +* These macros are intended to wrap calls into a paravirt_ops +* operation, so that they can be later identified and patched at +* runtime. +* +* Normally, a call to a pv_op function is a simple indirect call: +* (paravirt_ops.operations)(args...). +* +* Unfortunately, this is a relatively slow operation for modern CPUs, +* because it cannot necessarily determine what the destination +* address is. In this case, the address is a runtime constant, so at +* the very least we can patch the call to e a simple direct call, or +* ideally, patch an inline implementation into the callsite. (Direct +* calls are essentially free, because the call and return addresses +* are completely predictable.) +* +* These macros rely on the standard gcc "regparm(3)" calling +* convention, in which the first three arguments are placed in %eax, +* %edx, %ecx (in that order), and the remaining arguments are placed +* on the stack. All caller-save registers (eax,edx,ecx) are expected +* to be modified (either clobbered or used for return values). +* +* The call instruction itself is marked by placing its start address +* and size into the .parainstructions section, so that +* apply_paravirt() in arch/i386/kernel/alternative.c can do the +* appropriate patching under the control of the backend paravirt_ops +* implementation. +* +* Unfortunately there's no way to get gcc to generate the args setup +* for the call, and then allow the call itself to be generated by an +* inline asm. Because of this, we must do the complete arg setup and +* return value handling from within these macros. This is fairly +* cumbersome. +* +* There are 5 sets of PVOP_* macros for dealing with 0-4 arguments. +* It could be extended to more arguments, but there would be little +* to be gained from that. For each number of arguments, there are +* the two VCALL and CALL variants for void and non-void functions. +* +* When there is a return value, the invoker of the macro must specify +* the return type. The macro then uses sizeof() on that type to +* determine whether its a 32 or 64 bit value, and places the return +* in the right register(s) (just %eax for 32-bit, and %edx:%eax for +* 64-bit). +* +* 64-bit arguments are passed as a pair of adjacent 32-bit arguments +* in low,high order. +* +* Small structures are passed and returned in registers. The macro +* calling convention can't directly deal with this, so the wrapper +* functions must do this. +* +* These PVOP_* macros are only defined within this header. This +* means that all uses must be wrapped in inline functions. This also +* makes sure the incoming and outgoing types are always correct. +*/ #define PVOP_CALL0 (__rettype, __op) \ ({ \ __rettype __ret; \
@@ -1026,6 +1136,7 @@ static inline unsigned long __raw_local_ [paravirt_sti_type] "i" (PARAVIRT_PATCH(irq_enable)), \paravirt_clobber (CLBR_EAX) +/* Make sure as little as possible of this mess escapes. */#undef PARAVIRT_CALL#undef PVOP_VCALL0#undef PVOP_CALL0
PATCH 14/40 i386: add common patching machinery
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Implement 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 | 5 + arch/i386/kernel/paravirt.c | 154 ++++++++++++++++++++++++++++++++++------- include/asm-i386/paravirt.h | 12 +++ 3 files changed, 144 insertions(+), 27 deletions(-)
===================================================================
arch/i386/kernel/alternative.c +4 -1
@@ -336,11 +336,14 @@ void apply_paravirt(struct paravirt_patc used = paravirt_ops.patch (p->instrtype, p->clobbers, p->instr, p->len); +BUG_ON (used > p->len); +/* Pad the rest with nops */ nop_out (p->instr + used, p->len - used); } -/* Sync to be conservative, in case we patched following instructions */ +/* Sync to be conservative, in case we patched following +* instructions */ sync_core (); } extern struct paravirt_patch_site __start_parainstructions[],
arch/i386/kernel/paravirt.c +128 -26
175 lines not on this page · raw message
include/asm-i386/paravirt.h +12 -0
@@ -248,6 +248,18 @@ extern struct paravirt_ops paravirt_ops;#define paravirt_alt (insn_string) \ _paravirt_alt(insn_string, "%c[paravirt_typenum]", "%c[paravirt_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); + +/* * This generates an indirect call based on the operation type number. * The type number, computed in PARAVIRT_PATCH, is derived from the
PATCH 15/40 i386: add flush_tlb_others paravirt_op
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
This patch adds a pv_op for flush_tlb_others. Linux running on native hardware uses cross-CPU IPIs to flush the TLB on any CPU which may have a particular mm's pagetable entries cached in its TLB. This is inefficient in a paravirtualized environment, since the hypervisor knows which real CPUs actually contain cached mappings, which may be a small subset of a guest's VCPUs.
--- arch/i386/kernel/paravirt.c | 1 + arch/i386/kernel/smp.c | 13 +++++++------ include/asm-i386/paravirt.h | 9 +++++++++ include/asm-i386/tlbflush.h | 19 +++++++++++++++++-- 4 files changed, 34 insertions(+), 8 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +1 -0
@@ -300,6 +300,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 +7 -6
@@ -256,7 +256,6 @@ static cpumask_t flush_cpumask;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 +9 -0
@@ -15,6 +15,7 @@#ifndef __ASSEMBLY__#include <linux/types.h> +#include <linux/cpumask.h> struct thread_struct ;struct Xgt_desc_struct ;
@@ -165,6 +166,8 @@ struct paravirt_opsvoid (*flush_tlb_user)(void );void (*flush_tlb_kernel)(void );void (*flush_tlb_single)(unsigned long 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);
@@ -853,6 +856,12 @@ static inline void __flush_tlb_single(unPVOP_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) {PVOP_VCALL3 (map_pt_hook, type, va, pfn);
include/asm-i386/tlbflush.h +17 -2
@@ -79,11 +79,15 @@* - 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 #define flush_tlb () __flush_tlb()
@@ -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 16/40 i386: revert map_pt_hook.
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Back 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
@@ -302,8 +302,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
@@ -851,8 +851,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
@@ -169,8 +169,6 @@ struct paravirt_opsvoid (*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); -/* Hooks for allocating/releasing pagetable pages */ void (*alloc_pt)(u32 pfn);void (*alloc_pd)(u32 pfn);
@@ -862,11 +860,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); -} -static inline void paravirt_alloc_pt (unsigned pfn) {PVOP_VCALL1 (alloc_pt, pfn);
include/asm-i386/pgtable.h +4 -19
@@ -267,7 +267,6 @@ extern 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
@@ -476,24 +475,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 17/40 i386: add kmap_atomic_pte for mapping highpte pages
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Xen 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_prot, 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.
[ Zach - vmi.c will need some attention after this patch. It wasn't
immediately obvious to me what needs to be done. ]
--- arch/i386/kernel/paravirt.c | 5 +++++ arch/i386/mm/highmem.c | 9 +++++++-- include/asm-i386/highmem.h | 6 ++++++ include/asm-i386/paravirt.h | 15 +++++++++++++++ include/asm-i386/pgtable.h | 4 ++-- include/linux/highmem.h | 6 ++++++ mm/highmem.c | 9 +++++++++ 7 files changed, 50 insertions(+), 4 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +5 -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>
@@ -316,6 +317,10 @@ struct paravirt_ops paravirt_ops = { .ptep_get_and_clear = native_ptep_get_and_clear, +#ifdef CONFIG_HIGHPTE + .kmap_atomic_pte = kmap_atomic, +#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_prot (struct page *page,enum km_type type,pgprot_t prot) {enum fixed_addresses idx;unsigned long vaddr;
@@ -41,12 +41,17 @@ 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));arch_flush_lazy_mmu_mode ();return (void *) vaddr; } +void *kmap_atomic (struct page *page,enum km_type type) +{ +return kmap_atomic_prot (page, type, kmap_prot); +} +void kunmap_atomic (void *kvaddr,enum km_type type) {unsigned long vaddr = (unsigned long ) kvaddr &PAGE_MASK ;
include/asm-i386/highmem.h +6 -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,11 +68,16 @@ extern void FASTCALL(kunmap_high(structvoid *kmap (struct page *page);void kunmap (struct page *page); +void *kmap_atomic_prot (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); +#ifndef CONFIG_PARAVIRT +#define kmap_atomic_pte (page, type) kmap_atomic(page, type) +#endif +#define flush_cache_kmaps () do { } while (0)#endif /* __KERNEL__ */
include/asm-i386/paravirt.h +15 -0
@@ -16,7 +16,9 @@#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 ;
@@ -187,6 +189,10 @@ struct paravirt_opspte_t (*ptep_get_and_clear)(pte_t *ptep); +#ifdef CONFIG_HIGHPTE +void *(*kmap_atomic_pte)(struct page *page,enum km_type type); +#endif +#ifdef CONFIG_X86_PAE void (*set_pte_atomic)(pte_t *ptep,pte_t pteval);void (*set_pte_present)(struct mm_struct *mm,unsigned long addr,pte_t *ptep,pte_t pte);
@@ -884,6 +890,15 @@ static inline void paravirt_release_pd(uPVOP_VCALL1 (release_pd, pfn); } +#ifdef CONFIG_HIGHPTE +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; +} +#endif +static inline void pte_update (struct mm_struct *mm,unsigned long addr,pte_t *ptep) {
include/asm-i386/pgtable.h +2 -2
@@ -476,9 +476,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 @@ static inline void flush_kernel_dcache_punsigned 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_prot (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
@@ -99,6 +99,15 @@ static void flush_all_zero_pkmaps(void)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) {unsigned long vaddr;
PATCH 18/40 i386: flush lazy mmu updates on kunmap_atomic
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>kunmap_atomic should flush any pending lazy mmu updates, mainly to be consistent with kmap_atomic, and to preserve its normal behaviour.
--- arch/i386/mm/highmem.c | 1 + 1 file changed, 1 insertion(+)
===================================================================
arch/i386/mm/highmem.c +1 -0
@@ -72,6 +72,7 @@ void kunmap_atomic(void *kvaddr, enum km#endif } +arch_flush_lazy_mmu_mode ();pagefault_enable (); }
PATCH 19/40 i386: fix paravirt-documentation
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]> Remove #defines, add enum for PARAVIRT_LAZY_FLUSH.
--- include/asm-i386/paravirt.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-)
===================================================================
include/asm-i386/paravirt.h +1 -6
@@ -30,6 +30,7 @@ enum paravirt_lazy_mode {PARAVIRT_LAZY_NONE =0 ,PARAVIRT_LAZY_MMU =1 ,PARAVIRT_LAZY_CPU =2 , + PARAVIRT_LAZY_FLUSH = 3, };struct paravirt_ops
@@ -1036,12 +1037,6 @@ static inline pte_t raw_ptep_get_and_cle }#endif /* CONFIG_X86_PAE */ -/* Lazy mode for batching updates / context switch */ -#define PARAVIRT_LAZY_NONE 0 -#define PARAVIRT_LAZY_MMU 1 -#define PARAVIRT_LAZY_CPU 2 -#define PARAVIRT_LAZY_FLUSH 3 -#define __HAVE_ARCH_ENTER_LAZY_CPU_MODEstatic inline void arch_enter_lazy_cpu_mode (void ) {
Re: [patches] [PATCH] [19/40] i386: fix paravirt-documentation
Hi.
Quoted 26 lines · Andi Kleen
From: Jeremy Fitzhardinge <[email protected]> Remove #defines, add enum for
PARAVIRT_LAZY_FLUSH.Signed-off-by Jeremy Fitzhardinge <[email protected]>Signed-off-by Andi Kleen <[email protected]>--- include/asm-i386/paravirt.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-)===================================================================
Index:linux/include/asm-i386/paravirt.h
===================================================================include/asm-i386/paravirt.h +1
-0@@ -30,6 +30,7 @@ enum paravirt_lazy_mode {PARAVIRT_LAZY_NONE =0 ,PARAVIRT_LAZY_MMU =1 ,PARAVIRT_LAZY_CPU =2 , + PARAVIRT_LAZY_FLUSH = 3, };struct paravirt_ops @@ -1036,12 +1037,6 @@ static inline pte_t raw_ptep_get_and_cle }#endif /* CONFIG_X86_PAE */
include/asm-i386/paravirt.h static inline pte_t raw_ptep_get_and_cle · line 1042
Quoted 10 lines
include/asm-i386/paravirt.h +0
-6-/* Lazy mode for batching updates / context switch */ -#define PARAVIRT_LAZY_NONE 0 -#define PARAVIRT_LAZY_MMU 1 -#define PARAVIRT_LAZY_CPU 2 -#define PARAVIRT_LAZY_FLUSH 3 -#define __HAVE_ARCH_ENTER_LAZY_CPU_MODEstatic inline void arch_enter_lazy_cpu_mode (void ) {
Is the subject for this right?
Regards,
Nigel
Re: [patches] [PATCH] [19/40] i386: fix paravirt-documentation
Quoted 31 lines · Nigel Cunningham
Hi.
Quoted 27 lines · Andi Kleen
From: Jeremy Fitzhardinge <[email protected]> Remove #defines, add enum for
PARAVIRT_LAZY_FLUSH.Signed-off-by Jeremy Fitzhardinge <[email protected]>Signed-off-by Andi Kleen <[email protected]>--- include/asm-i386/paravirt.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-)===================================================================
Index:linux/include/asm-i386/paravirt.h
===================================================================include/asm-i386/paravirt.h +1
-0@@ -30,6 +30,7 @@ enum paravirt_lazy_mode {PARAVIRT_LAZY_NONE =0 ,PARAVIRT_LAZY_MMU =1 ,PARAVIRT_LAZY_CPU =2 , + PARAVIRT_LAZY_FLUSH = 3, };struct paravirt_ops @@ -1036,12 +1037,6 @@ static inline pte_t raw_ptep_get_and_cle }#endif /* CONFIG_X86_PAE */
Quoted 12 lines
include/asm-i386/paravirt.h static inline pte_t raw_ptep_get_and_cle · line 1042
Quoted 10 lines
include/asm-i386/paravirt.h +0
-6-/* Lazy mode for batching updates / context switch */ -#define PARAVIRT_LAZY_NONE 0 -#define PARAVIRT_LAZY_MMU 1 -#define PARAVIRT_LAZY_CPU 2 -#define PARAVIRT_LAZY_FLUSH 3 -#define __HAVE_ARCH_ENTER_LAZY_CPU_MODEstatic inline void arch_enter_lazy_cpu_mode (void ) {Is the subject for this right?
Yes. As part of the documentation I converted the #defines to enums, but at some point it clashed with the patch which added PARAVIRT_LAZY_FLUSH.
J
Re: [patches] [PATCH] [19/40] i386: fix paravirt-documentation
In reply to Nigel CunninghamQuoted 3 lines
Is the subject for this right?
Obviously not. Fixed.
-Andi
PATCH 20/40 i386: Clean up paravirt patchable wrappers
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Replace all the open-coded macros for generating calls with a pair of more general macros (__PVOP_CALL/VCALL), and redefine all the PVOP_V?CALL[0-4] in terms of them.
[ Andrew, Andi: this should slot in immediately after "Document asm-i386/paravirt.h"
(paravirt_ops-document-asm-i386-paravirth.patch) ]
--- include/asm-i386/paravirt.h | 248 ++++++++++---------------------------------- 1 file changed, 60 insertions(+), 188 deletions(-)
===================================================================
include/asm-i386/paravirt.h +60 -188
@@ -332,211 +332,81 @@ unsigned paravirt_patch_insns(void *site* means that all uses must be wrapped in inline functions. This also * makes sure the incoming and outgoing types are always correct. */ -#define PVOP_CALL0 (__rettype, __op) \ - ({ \ - __rettype __ret; \ - if (sizeof(__rettype) > sizeof(unsigned long)) { \ - unsigned long long __tmp; \ - unsigned long __ecx; \ - asm volatile(paravirt_alt(PARAVIRT_CALL) \ - : "=A" (__tmp), "=c" (__ecx) \ - : paravirt_type(__op), \ - paravirt_clobber(CLBR_ANY) \ - : "memory", "cc"); \ - __ret = (__rettype)__tmp; \ - } else { \ - unsigned long __tmp, __edx, __ecx; \ - asm volatile(paravirt_alt(PARAVIRT_CALL) \ - : "=a" (__tmp), "=d" (__edx), \ - "=c" (__ecx) \ - : paravirt_type(__op), \ - paravirt_clobber(CLBR_ANY) \ - : "memory", "cc"); \ - __ret = (__rettype)__tmp; \ - } \ - __ret; \ - }) -#define PVOP_VCALL0 (__op) \ +#define __PVOP_CALL (rettype, op, pre, post, ...) \ ({ \ + rettype __ret; \ unsigned long __eax, __edx, __ecx; \ - asm volatile(paravirt_alt(PARAVIRT_CALL) \ - : "=a" (__eax), "=d" (__edx), "=c" (__ecx) \ - : paravirt_type(__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(PARAVIRT_CALL) \ - : "=A" (__tmp), "=c" (__ecx) \ - : "a" ((u32)(arg1)), \ - paravirt_type(__op), \ - paravirt_clobber(CLBR_ANY) \ - : "memory", "cc"); \ - __ret = (__rettype)__tmp; \ - } else { \ - unsigned long __tmp, __edx, __ecx; \ - asm volatile(paravirt_alt(PARAVIRT_CALL) \ - : "=a" (__tmp), "=d" (__edx), \ - "=c" (__ecx) \ - : "0" ((u32)(arg1)), \ - paravirt_type(__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(PARAVIRT_CALL) \ - : "=a" (__eax), "=d" (__edx), "=c" (__ecx) \ - : "0" ((u32)(arg1)), \ - paravirt_type(__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(PARAVIRT_CALL) \ - : "=A" (__tmp), "=c" (__ecx) \ - : "a" ((u32)(arg1)), \ - "d" ((u32)(arg2)), \ - paravirt_type(__op), \ - paravirt_clobber(CLBR_ANY) \ - : "memory", "cc"); \ - __ret = (__rettype)__tmp; \ - } else { \ - unsigned long __tmp, __edx, __ecx; \ - asm volatile(paravirt_alt(PARAVIRT_CALL) \ - : "=a" (__tmp), "=d" (__edx), \ - "=c" (__ecx) \ - : "0" ((u32)(arg1)), \ - "1" ((u32)(arg2)), \ - paravirt_type(__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(PARAVIRT_CALL) \ - : "=a" (__eax), "=d" (__edx), "=c" (__ecx) \ - : "0" ((u32)(arg1)), \ - "1" ((u32)(arg2)), \ - paravirt_type(__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(PARAVIRT_CALL) \ - : "=A" (__tmp), "=c" (__ecx) \ - : "a" ((u32)(arg1)), \ - "d" ((u32)(arg2)), \ - "1" ((u32)(arg3)), \ - paravirt_type(__op), \ - paravirt_clobber(CLBR_ANY) \ - : "memory", "cc"); \ - __ret = (__rettype)__tmp; \ - } else { \ - unsigned long __tmp, __edx, __ecx; \ - asm volatile(paravirt_alt(PARAVIRT_CALL) \ - : "=a" (__tmp), "=d" (__edx), \ + if (sizeof(rettype) > sizeof(unsigned long)) { \ + asm volatile(pre \ + paravirt_alt(PARAVIRT_CALL) \ + post \ + : "=a" (__eax), "=d" (__edx), \ "=c" (__ecx) \ - : "0" ((u32)(arg1)), \ - "1" ((u32)(arg2)), \ - "2" ((u32)(arg3)), \ - paravirt_type(__op), \ - paravirt_clobber(CLBR_ANY) \ + : paravirt_type(op), \ + paravirt_clobber(CLBR_ANY), \ + ##__VA_ARGS__ \ : "memory", "cc"); \ - __ret = (__rettype)__tmp; \ - } \ - __ret; \ - }) -#define PVOP_VCALL3 (__op, arg1, arg2, arg3) \ - ({ \ - unsigned long __eax, __edx, __ecx; \ - asm volatile(paravirt_alt(PARAVIRT_CALL) \ - : "=a" (__eax), "=d" (__edx), "=c" (__ecx) \ - : "0" ((u32)(arg1)), \ - "1" ((u32)(arg2)), \ - "2" ((u32)(arg3)), \ - paravirt_type(__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(PARAVIRT_CALL) \ - "lea 4(%%esp),%%esp" \ - : "=A" (__tmp), "=c" (__ecx) \ - : "a" ((u32)(arg1)), \ - "d" ((u32)(arg2)), \ - "1" ((u32)(arg3)), \ - [_arg4] "mr" ((u32)(arg4)), \ - paravirt_type(__op), \ - paravirt_clobber(CLBR_ANY) \ - : "memory", "cc",); \ - __ret = (__rettype)__tmp; \ + __ret = (rettype)((((u64)__edx) << 32) | __eax); \ } else { \ - unsigned long __tmp, __edx, __ecx; \ - asm volatile("push %[_arg4]; " \ + asm volatile(pre \ paravirt_alt(PARAVIRT_CALL) \ - "lea 4(%%esp),%%esp" \ - : "=a" (__tmp), "=d" (__edx), "=c" (__ecx) \ - : "0" ((u32)(arg1)), \ - "1" ((u32)(arg2)), \ - "2" ((u32)(arg3)), \ - [_arg4]"mr" ((u32)(arg4)), \ - paravirt_type(__op), \ - paravirt_clobber(CLBR_ANY) \ + post \ + : "=a" (__eax), "=d" (__edx), \ + "=c" (__ecx) \ + : paravirt_type(op), \ + paravirt_clobber(CLBR_ANY), \ + ##__VA_ARGS__ \ : "memory", "cc"); \ - __ret = (__rettype)__tmp; \ + __ret = (rettype)__eax; \ } \ __ret; \ }) -#define PVOP_VCALL4 (__op, arg1, arg2, arg3, arg4) \ +#define __PVOP_VCALL (op, pre, post, ...) \ ({ \ unsigned long __eax, __edx, __ecx; \ - asm volatile("push %[_arg4]; " \ + asm volatile(pre \ paravirt_alt(PARAVIRT_CALL) \ - "lea 4(%%esp),%%esp" \ + post \ : "=a" (__eax), "=d" (__edx), "=c" (__ecx) \ - : "0" ((u32)(arg1)), \ - "1" ((u32)(arg2)), \ - "2" ((u32)(arg3)), \ - [_arg4]"mr" ((u32)(arg4)), \ - paravirt_type(__op), \ - paravirt_clobber(CLBR_ANY) \ + : paravirt_type(op), \ + paravirt_clobber(CLBR_ANY), \ + ##__VA_ARGS__ \ : "memory", "cc"); \ }) +#define PVOP_CALL0 (rettype, op) \ + __PVOP_CALL(rettype, op, "", "") +#define PVOP_VCALL0 (op) \ + __PVOP_VCALL(op, "", "") + +#define PVOP_CALL1 (rettype, op, arg1) \ + __PVOP_CALL(rettype, op, "", "", "0" ((u32)(arg1))) +#define PVOP_VCALL1 (op, arg1) \ + __PVOP_VCALL(op, "", "", "0" ((u32)(arg1))) + +#define PVOP_CALL2 (rettype, op, arg1, arg2) \ + __PVOP_CALL(rettype, op, "", "", "0" ((u32)(arg1)), "1" ((u32)(arg2))) +#define PVOP_VCALL2 (op, arg1, arg2) \ + __PVOP_VCALL(op, "", "", "0" ((u32)(arg1)), "1" ((u32)(arg2))) + +#define PVOP_CALL3 (rettype, op, arg1, arg2, arg3) \ + __PVOP_CALL(rettype, op, "", "", "0" ((u32)(arg1)), \ + "1"((u32)(arg2)), "2"((u32)(arg3))) +#define PVOP_VCALL3 (op, arg1, arg2, arg3) \ + __PVOP_VCALL(op, "", "", "0" ((u32)(arg1)), "1"((u32)(arg2)), \ + "2"((u32)(arg3))) + +#define PVOP_CALL4 (rettype, op, arg1, arg2, arg3, arg4) \ + __PVOP_CALL(rettype, op, \ + "push %[_arg4];", "lea 4(%%esp),%%esp;", \ + "0" ((u32)(arg1)), "1" ((u32)(arg2)), \ + "2" ((u32)(arg3)), [_arg4] "mr" ((u32)(arg4))) +#define PVOP_VCALL4 (op, arg1, arg2, arg3, arg4) \ + __PVOP_VCALL(op, \ + "push %[_arg4];", "lea 4(%%esp),%%esp;", \ + "0" ((u32)(arg1)), "1" ((u32)(arg2)), \ + "2" ((u32)(arg3)), [_arg4] "mr" ((u32)(arg4))) +static inline int paravirt_enabled (void ) {return paravirt_ops.paravirt_enabled;
@@ -1162,6 +1032,8 @@ static inline unsigned long __raw_local_/* Make sure as little as possible of this mess escapes. */ #undef PARAVIRT_CALL +#undef __PVOP_CALL +#undef __PVOP_VCALL#undef PVOP_VCALL0#undef PVOP_CALL0#undef PVOP_VCALL1
PATCH 21/40 i386: drop unused ptep_get_and_clear
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
In shadow mode hypervisors, ptep_get_and_clear achieves the desired purpose of keeping the shadows in sync by issuing a native_get_and_clear, followed by a call to pte_update, which indicates the PTE has been modified.
Direct mode hypervisors (Xen) have no need for this anyway, and will trap the update using writable pagetables.
This means no hypervisor makes use of ptep_get_and_clear; there is no reason to have it in the paravirt-ops structure. Change confusing terminology about raw vs. native functions into consistent use of native_pte_xxx for operations which do not invoke paravirt-ops.
--- arch/i386/kernel/paravirt.c | 2 -- include/asm-i386/paravirt.h | 13 +------------ include/asm-i386/pgtable.h | 4 +--- 3 files changed, 2 insertions(+), 17 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +0 -2
@@ -315,8 +315,6 @@ struct paravirt_ops paravirt_ops = { .pte_update = paravirt_nop, .pte_update_defer = paravirt_nop, - .ptep_get_and_clear = native_ptep_get_and_clear, -#ifdef CONFIG_HIGHPTE .kmap_atomic_pte = kmap_atomic,#endif
include/asm-i386/paravirt.h +1 -12
@@ -188,8 +188,6 @@ 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); -#ifdef CONFIG_HIGHPTE void *(*kmap_atomic_pte)(struct page *page,enum km_type type);#endif
@@ -859,12 +857,8 @@ static inline void pmd_clear(pmd_t *pmdpPVOP_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) };
@@ -900,11 +894,6 @@ static inline void set_pmd(pmd_t *pmdp, {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 */ #define __HAVE_ARCH_ENTER_LAZY_CPU_MODE
include/asm-i386/pgtable.h +1 -3
@@ -267,8 +267,6 @@ extern 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 raw_ptep_get_and_clear (xp) native_ptep_get_and_clear(xp)#endif /*
@@ -335,7 +333,7 @@ do { \#define __HAVE_ARCH_PTEP_GET_AND_CLEARstatic inline pte_t ptep_get_and_clear (struct mm_struct *mm,unsigned long addr,pte_t *ptep) { -pte_t pte =raw_ptep_get_and_clear (ptep); +pte_t pte =native_ptep_get_and_clear (ptep);pte_update (mm, addr, ptep);return pte; }
PATCH 22/40 x86: deflate stack usage in lib/inflate.c
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>inflate_fixed and huft_build together use around 2.7k of stack. When using 4k stacks, I saw stack overflows from interrupts arriving while unpacking the root initrd:
do_IRQ: stack overflow: 384
[<c0106b64>] show_trace_log_lvl+0x1a/0x30 [<c01075e6>] show_trace+0x12/0x14 [<c010763f>] dump_stack+0x16/0x18 [<c0107ca4>] do_IRQ+0x6d/0xd9 [<c010202b>] xen_evtchn_do_upcall+0x6e/0xa2 [<c0106781>] xen_hypervisor_callback+0x25/0x2c [<c010116c>] xen_restore_fl+0x27/0x29 [<c0330f63>] _spin_unlock_irqrestore+0x4a/0x50 [<c0117aab>] change_page_attr+0x577/0x584 [<c0117b45>] kernel_map_pages+0x8d/0xb4 [<c016a314>] cache_alloc_refill+0x53f/0x632 [<c016a6c2>] __kmalloc+0xc1/0x10d [<c0463d34>] malloc+0x10/0x12 [<c04641c1>] huft_build+0x2a7/0x5fa [<c04645a5>] inflate_fixed+0x91/0x136 [<c04657e2>] unpack_to_rootfs+0x5f2/0x8c1 [<c0465acf>] populate_rootfs+0x1e/0xe4
(This was under Xen, but there's no reason it couldn't happen on bare
hardware.)
This patch mallocs the local variables, thereby reducing the stack usage to sane levels.
Also, up the heap size for the kernel decompressor to deal with the extra allocation.
--- arch/alpha/boot/misc.c | 2 - arch/arm/boot/compressed/misc.c | 2 - arch/arm26/boot/compressed/misc.c | 2 - arch/i386/boot/compressed/misc.c | 2 - arch/x86_64/boot/compressed/misc.c | 2 - lib/inflate.c | 66 +++++++++++++++++++++++++++---------- 6 files changed, 54 insertions(+), 22 deletions(-)
===================================================================
arch/alpha/boot/misc.c +1 -1
@@ -98,7 +98,7 @@ extern int end;static ulg free_mem_ptr;static ulg free_mem_ptr_end; -#define HEAP_SIZE 0x2000 +#define HEAP_SIZE 0x3000#include "../../../lib/inflate.c"
arch/arm/boot/compressed/misc.c +1 -1
@@ -239,7 +239,7 @@ extern int end;static ulg free_mem_ptr;static ulg free_mem_ptr_end; -#define HEAP_SIZE 0x2000 +#define HEAP_SIZE 0x3000#include "../../../../lib/inflate.c"
arch/arm26/boot/compressed/misc.c +1 -1
@@ -182,7 +182,7 @@ extern int end;static ulg free_mem_ptr;static ulg free_mem_ptr_end; -#define HEAP_SIZE 0x2000 +#define HEAP_SIZE 0x3000#include "../../../../lib/inflate.c"
arch/i386/boot/compressed/misc.c +1 -1
@@ -189,7 +189,7 @@ static void putstr(const char *);static unsigned long free_mem_ptr;static unsigned long free_mem_end_ptr; -#define HEAP_SIZE 0x3000 +#define HEAP_SIZE 0x4000static char *vidmem = (char *)0xb8000 ;static int vidport;
arch/x86_64/boot/compressed/misc.c +1 -1
@@ -189,7 +189,7 @@ static void putstr(const char *);static long free_mem_ptr;static long free_mem_end_ptr; -#define HEAP_SIZE 0x6000 +#define HEAP_SIZE 0x7000static char *vidmem = (char *)0xb8000 ;static int vidport;
lib/inflate.c +49 -17
141 lines not on this page · raw message
PATCH 23/40 x86_64: deflate inflate_dynamic too
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]> inflate_dynamic() has piggy stack usage too, so heap allocate it too. I'm not sure it actually gets used, but it shows up large in "make checkstack".
--- lib/inflate.c | 63 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 21 deletions(-)
===================================================================
lib/inflate.c +42 -21
@@ -798,16 +798,19 @@ STATIC int noinline INIT inflate_dynamicunsigned nb;/* number of bit length codes */ unsigned nl;/* number of literal/length codes */ unsigned nd;/* number of distance codes */ -#ifdef PKZIP_BUG_WORKAROUND -unsigned ll[288 +32 ];/* literal/length and distance code lengths */ -#else -unsigned ll[286 +30 ];/* literal/length and distance code lengths */ -#endif +unsigned *ll;/* literal/length and distance code lengths */ registerulg b;/* bit buffer */ registerunsigned k;/* number of bits in bit buffer */ +int ret;DEBG ("<dyn" ); +#ifdef PKZIP_BUG_WORKAROUND + ll =malloc (sizeof (*ll) * (288 +32 ));/* literal/length and distance code lengths */ +#else + ll =malloc (sizeof (*ll) * (286 +30 ));/* literal/length and distance code lengths */ +#endif +/* make local bit buffer */ b = bb; k = bk;
@@ -828,7 +831,10 @@ DEBG("<dyn");#else if (nl > 286 || nd > 30)#endif -return 1 ;/* bad lengths */ + { + ret =1 ;/* bad lengths */ + goto out; + }DEBG ("dyn1 " );
@@ -850,7 +856,8 @@ DEBG("dyn2 "); {if (i ==1 )huft_free (tl); -return i;/* incomplete code set */ + ret = i;/* incomplete code set */ + goto out; }DEBG ("dyn3 " );
@@ -872,8 +879,10 @@ DEBG("dyn3 ");NEEDBITS (2 ) j = 3 + ((unsigned)b & 3); DUMPBITS(2) -if ((unsigned )i + j > n) -return 1 ; +if ((unsigned )i + j > n) { + ret =1 ; + goto out; + }while (j--) ll[i++] = l; }
@@ -882,8 +891,10 @@ DEBG("dyn3 ");NEEDBITS (3 ) j = 3 + ((unsigned)b & 7); DUMPBITS(3) -if ((unsigned )i + j > n) -return 1 ; +if ((unsigned )i + j > n) { + ret =1 ; + goto out; + }while (j--) ll[i++] =0 ; l =0 ;
@@ -893,8 +904,10 @@ DEBG("dyn3 ");NEEDBITS (7 ) j = 11 + ((unsigned)b & 0x7f); DUMPBITS(7) -if ((unsigned )i + j > n) -return 1 ; +if ((unsigned )i + j > n) { + ret =1 ; + goto out; + }while (j--) ll[i++] =0 ; l =0 ;
@@ -923,7 +936,8 @@ DEBG("dyn5b ");error ("incomplete literal tree" );huft_free (tl); } -return i;/* incomplete code set */ + ret = i;/* incomplete code set */ + goto out; }DEBG ("dyn5c " ); bd = dbits;
@@ -939,15 +953,18 @@ DEBG("dyn5d "); huft_free(td); }huft_free (tl); -return i;/* incomplete code set */ + ret = i;/* incomplete code set */ + goto out;#endif }DEBG ("dyn6 " );/* decompress until an end-of-block code */ -if (inflate_codes (tl, td, bl, bd)) -return 1 ; +if (inflate_codes (tl, td, bl, bd)) { + ret =1 ; + goto out; + }DEBG ("dyn7 " );
@@ -956,10 +973,14 @@ DEBG("dyn7 ");huft_free (td);DEBG (">" ); -return 0 ; - - underrun: -return 4 ;/* Input underrun */ + ret =0 ; +out: +free (ll); +return ret; + +underrun: + ret =4 ;/* Input underrun */ + goto out; }
PATCH 24/40 i386: Page-align the GDT
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Xen wants a dedicated page for the GDT. I believe VMI likes it too. lguest, KVM and native don't care.
Simple transformation to page-aligned "struct gdt_page".
--- arch/i386/kernel/cpu/common.c | 6 +++--- arch/i386/kernel/entry.S | 2 +- arch/i386/kernel/head.S | 2 +- arch/i386/kernel/traps.c | 2 +- include/asm-i386/desc.h | 9 +++++++-- 5 files changed, 13 insertions(+), 8 deletions(-)
===================================================================
arch/i386/kernel/cpu/common.c +3 -3
@@ -22,7 +22,7 @@#include "cpu.h" -DEFINE_PER_CPU(struct desc_struct, cpu_gdt[GDT_ENTRIES]) = { +DEFINE_PER_CPU(struct gdt_page, gdt_page) = { .gdt = { [GDT_ENTRY_KERNEL_CS ] = {0x0000ffff ,0x00cf9a00 }, [GDT_ENTRY_KERNEL_DS ] = {0x0000ffff ,0x00cf9200 }, [GDT_ENTRY_DEFAULT_USER_CS ] = {0x0000ffff ,0x00cffa00 },
@@ -48,8 +48,8 @@ DEFINE_PER_CPU(struct desc_struct, cpu_g [GDT_ENTRY_ESPFIX_SS] = { 0x00000000, 0x00c09200 }, [GDT_ENTRY_PDA ] = {0x00000000 ,0x00c09200 },/* set in setup_pda */ -}; -EXPORT_PER_CPU_SYMBOL_GPL (cpu_gdt ); +} }; +EXPORT_PER_CPU_SYMBOL_GPL (gdt_page ); DEFINE_PER_CPU(struct i386_pda, _cpu_pda);EXPORT_PER_CPU_SYMBOL (_cpu_pda);
arch/i386/kernel/entry.S +1 -1
@@ -557,7 +557,7 @@ END(syscall_badsys) #define FIXUP_ESPFIX_STACK \ /* since we are on a wrong stack, we cant make it a C code :( */ \ movl %fs:PDA_cpu, %ebx; \ - PER_CPU(cpu_gdt, %ebx); \ + PER_CPU(gdt_page, %ebx); \ GET_DESC_BASE(GDT_ENTRY_ESPFIX_SS, %ebx, %eax, %ax, %al, %ah); \ addl %esp, %eax; \ pushl $__KERNEL_DS; \
arch/i386/kernel/head.S +1 -1
@@ -598,7 +598,7 @@ idt_descr: .word 0 # 32 bit align gdt_desc.address ENTRY(early_gdt_descr) .word GDT_ENTRIES*8-1 - .long per_cpu__cpu_gdt /* Overwritten for secondary CPUs */ + .long per_cpu__gdt_page /* Overwritten for secondary CPUs */ /* * The boot_gdt must mirror the equivalent in setup.S and is
arch/i386/kernel/traps.c +1 -1
@@ -1030,7 +1030,7 @@ fastcall void do_spurious_interrupt_bug(fastcall unsigned long patch_espfix_desc (unsigned long uesp,unsigned long kesp) { -struct desc_struct *gdt =__get_cpu_var (cpu_gdt); +struct desc_struct *gdt =__get_cpu_var (gdt_page).gdt;unsigned long base = (kesp - uesp) & -THREAD_SIZE ;unsigned long new_kesp = kesp - base;unsigned long lim_pages = (new_kesp | (THREAD_SIZE -1 )) >>PAGE_SHIFT ;
include/asm-i386/desc.h +7 -2
@@ -18,10 +18,15 @@ struct Xgt_desc_struct {unsigned short pad; } __attribute__ ((packed)); -DECLARE_PER_CPU(struct desc_struct, cpu_gdt[GDT_ENTRIES]); +struct gdt_page +{ +struct desc_struct gdt[GDT_ENTRIES ]; +} __attribute__((aligned (PAGE_SIZE ))); +DECLARE_PER_CPU(struct gdt_page, gdt_page); +static inline struct desc_struct *get_cpu_gdt_table (unsigned int cpu) { -return per_cpu (cpu_gdt, cpu); +return per_cpu (gdt_page, cpu).gdt; }extern struct Xgt_desc_struct idt_descr;
PATCH 25/40 i386: Convert PDA into the percpu section
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Currently x86 (similar to x84-64) has a special per-cpu structure called "i386_pda" which can be easily and efficiently referenced via the %fs register. An ELF section is more flexible than a structure, allowing any piece of code to use this area. Indeed, such a section already exists: the per-cpu area.
So this patch:
(1) Removes the PDA and uses per-cpu variables for each current member.
(2) Replaces the __KERNEL_PDA segment with __KERNEL_PERCPU.
(3) Creates a per-cpu mirror of __per_cpu_offset called this_cpu_off, which can be used to calculate addresses for this CPU's variables.
(4) Simplifies startup, because %fs doesn't need to be loaded with a special segment at early boot; it can be deferred until the first percpu area is allocated (or never for UP).
The result is less code and one less x86-specific concept.
--- arch/i386/kernel/asm-offsets.c | 5 - arch/i386/kernel/cpu/common.c | 17 ----- arch/i386/kernel/entry.S | 5 - arch/i386/kernel/head.S | 31 +-------- arch/i386/kernel/i386_ksyms.c | 2 arch/i386/kernel/irq.c | 3 arch/i386/kernel/process.c | 12 ++- arch/i386/kernel/smpboot.c | 32 ++++----- arch/i386/kernel/vmi.c | 6 - arch/i386/kernel/vmlinux.lds.S | 1 include/asm-i386/current.h | 5 - include/asm-i386/irq_regs.h | 12 ++- include/asm-i386/pda.h | 99 ------------------------------ include/asm-i386/percpu.h | 132 ++++++++++++++++++++++++++++++++++++++--- include/asm-i386/processor.h | 2 include/asm-i386/segment.h | 6 - include/asm-i386/smp.h | 4 - 17 files changed, 178 insertions(+), 196 deletions(-)
===================================================================
arch/i386/kernel/asm-offsets.c +0 -5
@@ -15,7 +15,6 @@#include <asm/processor.h> #include <asm/thread_info.h> #include <asm/elf.h> -#include <asm/pda.h> #define DEFINE (sym, val) \ asm volatile("\n->" #sym " %0 " #val : : "i" (val))
@@ -101,10 +100,6 @@ void foo(void)OFFSET (crypto_tfm_ctx_offset, crypto_tfm, __crt_ctx); -BLANK (); -OFFSET (PDA_cpu, i386_pda, cpu_number); -OFFSET (PDA_pcurrent, i386_pda, pcurrent); -#ifdef CONFIG_PARAVIRT BLANK ();OFFSET (PARAVIRT_enabled, paravirt_ops, paravirt_enabled);
arch/i386/kernel/cpu/common.c +3 -14
@@ -18,7 +18,6 @@#include <asm/apic.h> #include <mach_apic.h> #endif -#include <asm/pda.h> #include "cpu.h"
@@ -47,13 +46,10 @@ DEFINE_PER_CPU(struct gdt_page, gdt_page [GDT_ENTRY_APMBIOS_BASE+2] = { 0x0000ffff, 0x00409200 }, /* data */ [GDT_ENTRY_ESPFIX_SS] = { 0x00000000, 0x00c09200 }, - [GDT_ENTRY_PDA ] = {0x00000000 ,0x00c09200 },/* set in setup_pda */ + [GDT_ENTRY_PERCPU ] = {0x00000000 ,0x00000000 }, } };EXPORT_PER_CPU_SYMBOL_GPL (gdt_page ); -DEFINE_PER_CPU(struct i386_pda, _cpu_pda); -EXPORT_PER_CPU_SYMBOL (_cpu_pda); -static int cachesize_override__cpuinitdata =-1 ;static int disable_x86_fxsr__cpuinitdata ;static int disable_x86_serial_nr__cpuinitdata =1 ;
@@ -634,21 +630,14 @@ void __init early_cpu_init(void)#endif } -/* Make sure %gs is initialized properly in idle threads */ +/* Make sure %fs is initialized properly in idle threads */ struct pt_regs *__devinit idle_regs (struct pt_regs *regs) {memset (regs,0 ,sizeof (struct pt_regs )); - regs->xfs = __KERNEL_PDA; + regs->xfs = __KERNEL_PERCPU;return regs; } -/* Initial PDA used by boot CPU */ -struct i386_pda boot_pda = { - ._pda = &boot_pda, - .cpu_number =0 , - .pcurrent = &init_task, -}; -/* * cpu_init() initializes state that is per-CPU. Some data is already * initialized (naturally) in the bootstrap process, such as the GDT
arch/i386/kernel/entry.S +2 -3
@@ -132,7 +132,7 @@ VM_MASK = 0x00020000 movl $(__USER_DS), %edx; \ movl %edx, %ds; \ movl %edx, %es; \ - movl $(__KERNEL_PDA), %edx; \ + movl $(__KERNEL_PERCPU), %edx; \ movl %edx, %fs #define RESTORE_INT_REGS \
@@ -556,7 +556,6 @@ END(syscall_badsys) #define FIXUP_ESPFIX_STACK \ /* since we are on a wrong stack, we cant make it a C code :( */ \ - movl %fs:PDA_cpu, %ebx; \ PER_CPU(gdt_page, %ebx); \ GET_DESC_BASE(GDT_ENTRY_ESPFIX_SS, %ebx, %eax, %ax, %al, %ah); \ addl %esp, %eax; \
@@ -681,7 +680,7 @@ error_code: pushl %fs CFI_ADJUST_CFA_OFFSET 4 /*CFI_REL_OFFSET fs, 0*/ - movl $(__KERNEL_PDA), %ecx + movl $(__KERNEL_PERCPU), %ecx movl %ecx, %fs UNWIND_ESPFIX_STACK popl %ecx
arch/i386/kernel/head.S +6 -25
@@ -317,12 +317,12 @@ is386: movl $2,%ecx # set MP movl %eax,%cr0 call check_x87 - call setup_pda lgdt early_gdt_descr lidt idt_descr ljmp $(__KERNEL_CS),$1f 1: movl $(__KERNEL_DS),%eax # reload all the segment registers movl %eax,%ss # after changing gdt. + movl %eax,%fs # gets reset once there's real percpu movl $(__USER_DS),%eax # DS/ES contains default USER segment movl %eax,%ds
@@ -332,16 +332,17 @@ is386: movl $2,%ecx # set MP movl %eax,%gs lldt %ax - movl $(__KERNEL_PDA),%eax - mov %eax,%fs - cld # gcc2 wants the direction flag cleared at all times pushl $0 # fake return address for unwinder #ifdef CONFIG_SMP movb ready, %cl movb $1, ready cmpb $0,%cl # the first CPU calls start_kernel - jne initialize_secondary # all other CPUs call initialize_secondary + je 1f + movl $(__KERNEL_PERCPU), %eax + movl %eax,%fs # set this cpu's percpu + jmp initialize_secondary # all other CPUs call initialize_secondary +1: #endif /* CONFIG_SMP */ jmp start_kernel
@@ -365,23 +366,6 @@ check_x87: ret /* - * Point the GDT at this CPU's PDA. On boot this will be - * cpu_gdt_table and boot_pda; for secondary CPUs, these will be - * that CPU's GDT and PDA. - */ -ENTRY(setup_pda) - /* get the PDA pointer */ - movl start_pda, %eax - - /* slot the PDA address into the GDT */ - mov early_gdt_descr+2, %ecx - mov %ax, (__KERNEL_PDA+0+2)(%ecx) /* base & 0x0000ffff */ - shr $16, %eax - mov %al, (__KERNEL_PDA+4+0)(%ecx) /* base & 0x00ff0000 */ - mov %ah, (__KERNEL_PDA+4+3)(%ecx) /* base & 0xff000000 */ - ret - -/* * setup_idt * * sets up a idt with 256 entries pointing to
@@ -553,9 +537,6 @@ ENTRY(empty_zero_page) * This starts the data section. */ .data -ENTRY(start_pda) - .long boot_pda - ENTRY(stack_start) .long init_thread_union+THREAD_SIZE .long __BOOT_DS
arch/i386/kernel/i386_ksyms.c +0 -2
@@ -28,5 +28,3 @@ EXPORT_SYMBOL(__read_lock_failed);#endif EXPORT_SYMBOL (csum_partial); - -EXPORT_SYMBOL (_proxy_pda);
arch/i386/kernel/irq.c +3 -0
@@ -24,6 +24,9 @@DEFINE_PER_CPU (irq_cpustat_t, irq_stat)____cacheline_internodealigned_in_smp ;EXPORT_PER_CPU_SYMBOL (irq_stat); +DEFINE_PER_CPU(struct pt_regs *, irq_regs); +EXPORT_PER_CPU_SYMBOL (irq_regs); +/* * 'what should we do if we get a hw irq event on an illegal vector'. * each architecture has to answer this themselves.
arch/i386/kernel/process.c +9 -3
@@ -39,6 +39,7 @@#include <linux/random.h> #include <linux/personality.h> #include <linux/tick.h> +#include <linux/percpu.h> #include <asm/uaccess.h> #include <asm/pgtable.h>
@@ -57,7 +58,6 @@#include <asm/tlbflush.h> #include <asm/cpu.h> -#include <asm/pda.h> asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
@@ -66,6 +66,12 @@ static int hlt_counter;unsigned long boot_option_idle_override =0 ;EXPORT_SYMBOL (boot_option_idle_override); +DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task; +EXPORT_PER_CPU_SYMBOL (current_task); + +DEFINE_PER_CPU (int, cpu_number); +EXPORT_PER_CPU_SYMBOL (cpu_number); +/* * Return saved PC of a blocked thread. */
@@ -342,7 +348,7 @@ int kernel_thread(int (*fn)(void *), voi regs.xds = __USER_DS; regs.xes = __USER_DS; - regs.xfs = __KERNEL_PDA; + regs.xfs = __KERNEL_PERCPU; regs.orig_eax =-1 ; regs.eip = (unsigned long ) kernel_thread_helper; regs.xcs = __KERNEL_CS |get_kernel_rpl ();
@@ -711,7 +717,7 @@ struct task_struct fastcall * __switch_tif (prev->gs | next->gs)loadsegment (gs, next->gs); -write_pda (pcurrent, next_p); +x86_write_percpu (current_task, next_p);return prev_p; }
arch/i386/kernel/smpboot.c +15 -17
@@ -53,7 +53,6 @@#include <asm/desc.h> #include <asm/arch_hooks.h> #include <asm/nmi.h> -#include <asm/pda.h> #include <mach_apic.h> #include <mach_wakecpu.h>
@@ -99,6 +98,9 @@ EXPORT_SYMBOL(x86_cpu_to_apicid);u8 apicid_2_node[MAX_APICID ]; +DEFINE_PER_CPU(unsigned long, this_cpu_off); +EXPORT_PER_CPU_SYMBOL (this_cpu_off); +/* * Trampoline 80x86 program as an array. */
@@ -456,7 +458,6 @@ extern struct {void * esp;unsigned short ss; } stack_start; -extern struct i386_pda *start_pda;#ifdef CONFIG_NUMA
@@ -784,20 +785,17 @@ static inline struct task_struct * alloc/* Initialize the CPU's GDT. This is either the boot CPU doing itself (still using the master per-cpu area), or a CPU doing it for a secondary which will soon come up. */ -static __cpuinit void init_gdt (int cpu,struct task_struct *idle) +static __cpuinit void init_gdt (int cpu) {struct desc_struct *gdt =get_cpu_gdt_table (cpu); -struct i386_pda *pda = &per_cpu (_cpu_pda, cpu); -pack_descriptor ((u32 *)&gdt[GDT_ENTRY_PDA ].a, - (u32 *)&gdt[GDT_ENTRY_PDA ].b, - (unsigned long )pda,sizeof (*pda) -1 , -0x80 |DESCTYPE_S |0x2 ,0 );/* present read-write data segment */ - -memset (pda,0 ,sizeof (*pda)); - pda->_pda = pda; - pda->cpu_number = cpu; - pda->pcurrent = idle; +pack_descriptor ((u32 *)&gdt[GDT_ENTRY_PERCPU ].a, + (u32 *)&gdt[GDT_ENTRY_PERCPU ].b, + __per_cpu_offset[cpu],0xFFFFF , +0x80 |DESCTYPE_S |0x2 ,0x8 ); + +per_cpu (this_cpu_off, cpu) = __per_cpu_offset[cpu]; +per_cpu (cpu_number, cpu) = cpu; }/* Defined in head.S */
@@ -824,9 +822,9 @@ static int __cpuinit do_boot_cpu(int apiif (IS_ERR (idle))panic ("failed fork for CPU %d" , cpu); -init_gdt (cpu, idle); +init_gdt (cpu); +per_cpu (current_task, cpu) = idle; early_gdt_descr.address = (unsigned long )get_cpu_gdt_table (cpu); - start_pda =cpu_pda (cpu); idle->thread.eip = (unsigned long ) start_secondary;/* start_eip had better be page-aligned! */
@@ -1188,14 +1186,14 @@ static inline void switch_to_new_gdt(voi gdt_descr.address = (long )get_cpu_gdt_table (smp_processor_id ()); gdt_descr.size =GDT_SIZE -1 ;load_gdt (&gdt_descr); - asmvolatile ("mov %0, %%fs" : :"r" (__KERNEL_PDA) :"memory" ); + asm("mov %0, %%fs" : :"r" (__KERNEL_PERCPU) :"memory" ); }void __init native_smp_prepare_boot_cpu (void ) {unsigned int cpu =smp_processor_id (); -init_gdt (cpu, current); +init_gdt (cpu);switch_to_new_gdt ();cpu_set (cpu, cpu_online_map);
arch/i386/kernel/vmi.c +1 -5
@@ -504,8 +504,6 @@ static void vmi_pmd_clear(pmd_t *pmd)#endif #ifdef CONFIG_SMP -extern void setup_pda (void ); -static void __devinit vmi_startup_ipi_hook (int phys_apicid,unsigned long start_eip,unsigned long start_esp)
@@ -530,13 +528,11 @@ vmi_startup_ipi_hook(int phys_apicid, un ap.ds = __USER_DS; ap.es = __USER_DS; - ap.fs = __KERNEL_PDA; + ap.fs = __KERNEL_PERCPU; ap.gs =0 ; ap.eflags =0 ; -setup_pda (); -#ifdef CONFIG_X86_PAE /* efer should match BSP efer. */ if (cpu_has_nx) {
arch/i386/kernel/vmlinux.lds.S +0 -1
@@ -26,7 +26,6 @@ OUTPUT_FORMAT("elf32-i386", "elf32-i386" OUTPUT_ARCH(i386) ENTRY(phys_startup_32) jiffies = jiffies_64; -_proxy_pda = 1; PHDRS { text PT_LOAD FLAGS(5); /* R_E */
include/asm-i386/current.h +3 -2
@@ -1,14 +1,15 @@#ifndef _I386_CURRENT_H#define _I386_CURRENT_H -#include <asm/pda.h> #include <linux/compiler.h> +#include <asm/percpu.h> struct task_struct ; +DECLARE_PER_CPU(struct task_struct *, current_task); static __always_inline struct task_struct *get_current(void) { -return read_pda (pcurrent); +return x86_read_percpu (current_task); }#define current get_current()
include/asm-i386/irq_regs.h +7 -5
@@ -1,25 +1,27 @@/* * Per-cpu current frame pointer - the location of the last exception frame on -* the stack, stored in the PDA. +* the stack, stored in the per-cpu area. * * Jeremy Fitzhardinge <[email protected]> */ #ifndef _ASM_I386_IRQ_REGS_H#define _ASM_I386_IRQ_REGS_H -#include <asm/pda.h> +#include <asm/percpu.h> + +DECLARE_PER_CPU(struct pt_regs *, irq_regs);static inline struct pt_regs *get_irq_regs (void ) { -return read_pda (irq_regs); +return x86_read_percpu (irq_regs); }static inline struct pt_regs *set_irq_regs (struct pt_regs *new_regs) {struct pt_regs *old_regs; - old_regs =read_pda (irq_regs); -write_pda (irq_regs, new_regs); + old_regs =get_irq_regs (); +x86_write_percpu (irq_regs, new_regs);return old_regs; }
include/asm-i386/pda.h deleted +0 -99
99 lines not on this page · raw message
include/asm-i386/percpu.h +123 -9
146 lines not on this page · raw message
include/asm-i386/processor.h +1 -1
@@ -377,7 +377,7 @@ struct thread_struct { .vm86_info = NULL, \ .sysenter_cs = __KERNEL_CS, \ .io_bitmap_ptr = NULL, \ - .fs = __KERNEL_PDA, \ + .fs = __KERNEL_PERCPU, \ }/*
include/asm-i386/segment.h +3 -3
@@ -39,7 +39,7 @@* 25 - APM BIOS support * * 26 - ESPFIX small SS -* 27 - PDA [ per-cpu private data area ] +* 27 - per-cpu [ offset to per-cpu data area ] * 28 - unused * 29 - unused * 30 - unused
@@ -74,8 +74,8 @@#define GDT_ENTRY_ESPFIX_SS (GDT_ENTRY_KERNEL_BASE + 14)#define __ESPFIX_SS (GDT_ENTRY_ESPFIX_SS * 8) -#define GDT_ENTRY_PDA (GDT_ENTRY_KERNEL_BASE + 15) -#define __KERNEL_PDA (GDT_ENTRY_PDA * 8) +#define GDT_ENTRY_PERCPU (GDT_ENTRY_KERNEL_BASE + 15) +#define __KERNEL_PERCPU (GDT_ENTRY_PERCPU * 8)#define GDT_ENTRY_DOUBLEFAULT_TSS 31
include/asm-i386/smp.h +2 -2
@@ -8,7 +8,6 @@#include <linux/kernel.h> #include <linux/threads.h> #include <linux/cpumask.h> -#include <asm/pda.h> #endif #if defined(CONFIG_X86_LOCAL_APIC ) && !defined(__ASSEMBLY__)
@@ -112,7 +111,8 @@ do { } while (0)* from the initial startup. We map APIC_BASE very early in page_setup(), * so this is correct in the x86 case. */ -#define raw_smp_processor_id () (read_pda(cpu_number)) +DECLARE_PER_CPU (int, cpu_number); +#define raw_smp_processor_id () (x86_read_percpu(cpu_number))extern cpumask_t cpu_callout_map;extern cpumask_t cpu_callin_map;
PATCH 26/40 i386: cleanups to help using per-cpu variables from asm
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
This patch does a few small cleanups:
- use PER_CPU_NAME to generate the names of per-cpu variables
- use lea to add the per_cpu offset in PER_CPU(), because it doesn't affect condition flags
- add PER_CPU_VAR which allows direct access to pre-cpu variables with the %fs: prefix on SMP.
--- include/asm-i386/percpu.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
===================================================================
include/asm-i386/percpu.h +7 -5
@@ -16,12 +16,14 @@* PER_CPU(cpu_gdt_descr, %ebx) */ #ifdef CONFIG_SMP -#define PER_CPU (var, reg) \ - movl %fs:per_cpu__this_cpu_off, reg; \ - addl $per_cpu__##var, reg +#define PER_CPU (var, reg) \ + movl %fs:per_cpu__##this_cpu_off, reg; \ + lea per_cpu__##var(reg), reg +#define PER_CPU_VAR (var) %fs:per_cpu__##var#else /* ! SMP */ -#define PER_CPU (var, reg) \ - movl $per_cpu__##var, reg; +#define PER_CPU (var, reg) \ + movl $per_cpu__##var, reg +#define PER_CPU_VAR (var) per_cpu__##var#endif /* SMP */ #else /* ...!ASSEMBLY */
PATCH 27/40 i386: Define per_cpu_offset
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Define per_cpu_offset in asm-i386/percpu.h when SMP defined, like asm-generic/percpu.h does for UP.
--- include/asm-i386/percpu.h | 2 ++ 1 file changed, 2 insertions(+)
===================================================================
include/asm-i386/percpu.h +2 -0
@@ -47,6 +47,8 @@/* This is used for other cpus to find our section. */ extern unsigned long __per_cpu_offset[]; +#define per_cpu_offset (x) (__per_cpu_offset[x]) +/* Separate out the type, so (int[3], foo) works. */ #define DECLARE_PER_CPU (type, name) extern __typeof__(type) per_cpu__##name#define DEFINE_PER_CPU (type, name) \
PATCH 28/40 i386: Fix UP gdt bugs
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Fixes two problems with the GDT when compiling for uniprocessor:
- There's no percpu segment, so trying to load its selector into %fs fails. Use a null selector instead.
- The real gdt needs to be loaded at some point. Do it in cpu_init().
--- arch/i386/kernel/cpu/common.c | 13 +++++++++++++ arch/i386/kernel/smpboot.c | 12 ------------ include/asm-i386/processor.h | 1 + include/asm-i386/segment.h | 4 ++++ 4 files changed, 18 insertions(+), 12 deletions(-)
===================================================================
arch/i386/kernel/cpu/common.c +13 -0
@@ -638,6 +638,18 @@ struct pt_regs * __devinit idle_regs(strreturn regs; } +/* Current gdt points %fs at the "master" per-cpu area: after this, +* it's on the real one. */ +void switch_to_new_gdt (void ) +{ +struct Xgt_desc_struct gdt_descr; + + gdt_descr.address = (long )get_cpu_gdt_table (smp_processor_id ()); + gdt_descr.size =GDT_SIZE -1 ; +load_gdt (&gdt_descr); + asm("mov %0, %%fs" : :"r" (__KERNEL_PERCPU) :"memory" ); +} +/* * cpu_init() initializes state that is per-CPU. Some data is already * initialized (naturally) in the bootstrap process, such as the GDT
@@ -668,6 +680,7 @@ void __cpuinit cpu_init(void) }load_idt (&idt_descr); +switch_to_new_gdt ();/* * Set up and load the per-CPU TSS and LDT
arch/i386/kernel/smpboot.c +0 -12
@@ -1177,18 +1177,6 @@ void __init native_smp_prepare_cpus(unsismp_boot_cpus (max_cpus); } -/* Current gdt points %fs at the "master" per-cpu area: after this, -* it's on the real one. */ -static inline void switch_to_new_gdt (void ) -{ -struct Xgt_desc_struct gdt_descr; - - gdt_descr.address = (long )get_cpu_gdt_table (smp_processor_id ()); - gdt_descr.size =GDT_SIZE -1 ; -load_gdt (&gdt_descr); - asm("mov %0, %%fs" : :"r" (__KERNEL_PERCPU) :"memory" ); -} -void __init native_smp_prepare_boot_cpu (void ) {unsigned int cpu =smp_processor_id ();
include/asm-i386/processor.h +1 -0
@@ -750,6 +750,7 @@ extern void enable_sep_cpu(void);extern int sysenter_setup (void );extern void cpu_set_gdt (int ); +extern void switch_to_new_gdt (void );extern void cpu_init (void );extern int force_mwait;
include/asm-i386/segment.h +4 -0
@@ -75,7 +75,11 @@#define __ESPFIX_SS (GDT_ENTRY_ESPFIX_SS * 8)#define GDT_ENTRY_PERCPU (GDT_ENTRY_KERNEL_BASE + 15) +#ifdef CONFIG_SMP #define __KERNEL_PERCPU (GDT_ENTRY_PERCPU * 8) +#else +#define __KERNEL_PERCPU 0 +#endif #define GDT_ENTRY_DOUBLEFAULT_TSS 31
PATCH 29/40 i386: map enough initial memory to create lowmem mappings
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>head.S creates the very initial pagetable for the kernel. This just maps enough space for the kernel itself, and an allocation bitmap. The amount of mapped memory is rounded up to 4Mbytes, and so this typically ends up mapping 8Mbytes of memory.
When booting, pagetable_init() needs to create mappings for all lowmem, and the pagetables for these mappings are allocated from the free pages around the kernel in low memory. If the number of pagetable pages + kernel size exceeds head.S's initial mapping, it will end up faulting on an unmapped page. This will only happen with specific combinations of kernel size and memory size.
This patch makes sure that head.S also maps enough space to fit the kernel pagetables as well as the kernel itself. It ends up using an additional two pages of unreclaimable memory.
--- arch/i386/kernel/asm-offsets.c | 6 ++++++ arch/i386/kernel/head.S | 23 +++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-)
===================================================================
arch/i386/kernel/asm-offsets.c +6 -0
@@ -11,6 +11,7 @@#include <linux/suspend.h> #include <asm/ucontext.h> #include "sigframe.h" +#include <asm/pgtable.h> #include <asm/fixmap.h> #include <asm/processor.h> #include <asm/thread_info.h>
@@ -96,6 +97,11 @@ void foo(void) sizeof(struct tss_struct));DEFINE (PAGE_SIZE_asm,PAGE_SIZE ); +DEFINE (PAGE_SHIFT_asm,PAGE_SHIFT ); +DEFINE (PTRS_PER_PTE ,PTRS_PER_PTE ); +DEFINE (PTRS_PER_PMD ,PTRS_PER_PMD ); +DEFINE (PTRS_PER_PGD ,PTRS_PER_PGD ); +DEFINE (VDSO_PRELINK_asm,VDSO_PRELINK );OFFSET (crypto_tfm_ctx_offset, crypto_tfm, __crt_ctx);
arch/i386/kernel/head.S +19 -4
@@ -34,17 +34,32 @@ /* * This is how much memory *in addition to the memory covered up to - * and including _end* we need mapped initially. We need one bit for - * each possible page, but only in low memory, which means - * 2^32/4096/8 = 128K worst case (4G/4G split.) + * and including _end* we need mapped initially. + * We need: + * - one bit for each possible page, but only in low memory, which means + * 2^32/4096/8 = 128K worst case (4G/4G split.) + * - enough space to map all low memory, which means + * (2^32/4096) / 1024 pages (worst case, non PAE) + * (2^32/4096) / 512 + 4 pages (worst case for PAE) + * - a few pages for allocator use before the kernel pagetable has + * been set up * * Modulo rounding, each megabyte assigned here requires a kilobyte of * memory, which is currently unreclaimed. * * This should be a multiple of a page. */ -#define INIT_MAP_BEYOND_END (128*1024) +LOW_PAGES = 1<<(32-PAGE_SHIFT_asm) +#if PTRS_PER_PMD > 1 +PAGE_TABLE_SIZE = (LOW_PAGES / PTRS_PER_PMD) + PTRS_PER_PGD +#else +PAGE_TABLE_SIZE = (LOW_PAGES / PTRS_PER_PGD) +#endif +BOOTBITMAP_SIZE = LOW_PAGES / 8 +ALLOCATOR_SLOP = 4 + +INIT_MAP_BEYOND_END = BOOTBITMAP_SIZE + (PAGE_TABLE_SIZE + ALLOCATOR_SLOP)*PAGE_SIZE_asm /* * 32-bit kernel entrypoint; only used by the boot CPU. On entry,
PATCH 30/40 x86: update for i386 and x86-64 check_bugs
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Remove spurious comments, headers and keywords from x86-64 bugs.[ch].
Use identify_boot_cpu()
AK: merged with other patch
--- arch/x86_64/kernel/bugs.c | 9 +-------- include/asm-i386/bugs.h | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-)
===================================================================
arch/x86_64/kernel/bugs.c +1 -8
@@ -3,19 +3,12 @@* * Copyright (C) 1994 Linus Torvalds * Copyright (C) 2000 SuSE -* -* This is included by init/main.c to check for architecture-dependent bugs. -* -* Needs: -* void check_bugs(void); */ #include <linux/kernel.h> +#include <linux/init.h> #include <asm/alternative.h> #include <asm/processor.h> -#include <asm/i387.h> -#include <asm/msr.h> -#include <asm/pda.h> void __init check_bugs (void ) {
include/asm-i386/bugs.h +1 -1
@@ -7,6 +7,6 @@#ifndef _ASM_I386_BUG_H#define _ASM_I386_BUG_H -extern void __init check_bugs (void ); +void check_bugs (void );#endif /* _ASM_I386_BUG_H */
PATCH 31/40 i386: In compat mode, the return value here was uninitialized.
In reply to Andi KleenFrom: Zachary Amsden <[email protected]>
--- arch/i386/kernel/sysenter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
===================================================================
arch/i386/kernel/sysenter.c +1 -1
@@ -268,7 +268,7 @@ int arch_setup_additional_pages(struct l {struct mm_struct *mm = current->mm;unsigned long addr; -int ret; +int ret =0 ;bool compat;down_write (&mm->mmap_sem);
PATCH 32/40 i386: kRemove a warning about unused variable in !CONFIG_ACPI compilation.
In reply to Andi KleenFrom: Zachary Amsden <[email protected]>
--- arch/i386/kernel/acpi/earlyquirk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
===================================================================
arch/i386/kernel/acpi/earlyquirk.c +1 -1
@@ -21,8 +21,8 @@ static int __init nvidia_hpet_check(strustatic int __init check_bridge (int vendor,int device) { -static int warned;#ifdef CONFIG_ACPI +static int warned;/* According to Nvidia all timer overrides are bogus unless HPET is enabled. */ if (!acpi_use_timer_override && vendor ==PCI_VENDOR_ID_NVIDIA ) {
PATCH 33/40 i386: Allow boot-time disable of paravirt_ops patching
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Add "noreplace-paravirt" to disable paravirt_ops patching.
---
Documentation/kernel-parameters.txt | 3 +++ arch/i386/kernel/alternative.c | 13 +++++++++++++ 2 files changed, 16 insertions(+)
Documentation/kernel-parameters.txt +3 -0
@@ -64,6 +64,7 @@ parameter is applicable: GENERIC_TIME The generic timeofday code is enabled. NFS Appropriate NFS support is enabled. OSS OSS sound support is enabled. + PV_OPS A paravirtualized kernel PARIDE The ParIDE subsystem is enabled. PARISC The PA-RISC architecture is enabled. PCI PCI bus support is enabled.
@@ -1142,6 +1143,8 @@ and is between 256 and 4096 characters. nomce [IA-32] Machine Check Exception + noreplace-paravirt [IA-32,PV_OPS] Don't patch paravirt_ops + noreplace-smp [IA-32,SMP] Don't replace SMP instructions with UP alternatives
arch/i386/kernel/alternative.c +13 -0
@@ -30,6 +30,16 @@ static int __init setup_noreplace_smp(ch }__setup ("noreplace-smp" , setup_noreplace_smp); +#ifdef CONFIG_PARAVIRT +static int noreplace_paravirt =0 ; + +static int __init setup_noreplace_paravirt (char *str) +{ + noreplace_paravirt =1 ; +return 1 ; +} +__setup ("noreplace-paravirt" , setup_noreplace_paravirt); +#endif #define DPRINTK(fmt, args...) if (debug_alternative) \ printk(KERN_DEBUG fmt, args)
@@ -330,6 +340,9 @@ void apply_paravirt(struct paravirt_patc {struct paravirt_patch_site *p; +if (noreplace_paravirt) +return ; +for (p = start; p < end; p++) {unsigned int used;
PATCH 34/40 i386: Clean up arch/i386/kernel/cpu/mcheck/p4.c
In reply to Andi KleenFrom: Zachary Amsden <[email protected]>
No, just no. You do not use goto to skip a code block. You do not return an obvious variable from a singly-inlined function and give the function a return value. You don't put unexplained comments about kmalloc in code which doesn't do dynamic allocation. And you don't leave stray warnings around for no good reason.
Also, when possible, it is better to use block scoped variables because gcc can sometime generate better code.
--- arch/i386/kernel/cpu/mcheck/p4.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-)
===================================================================
arch/i386/kernel/cpu/mcheck/p4.c +4 -12
@@ -124,13 +124,10 @@ static void intel_init_thermal(struct cp/* P4/Xeon Extended MCE MSR retrieval, return 0 if unsupported */ -static inline int intel_get_extended_msrs (struct intel_mce_extended_msrs *r) +static inline void intel_get_extended_msrs (struct intel_mce_extended_msrs *r) {u32 h; -if (mce_num_extended_msrs ==0 ) - goto done; -rdmsr (MSR_IA32_MCG_EAX , r->eax, h);rdmsr (MSR_IA32_MCG_EBX , r->ebx, h);rdmsr (MSR_IA32_MCG_ECX , r->ecx, h);
@@ -141,12 +138,6 @@ static inline int intel_get_extended_msrrdmsr (MSR_IA32_MCG_ESP , r->esp, h);rdmsr (MSR_IA32_MCG_EFLAGS , r->eflags, h);rdmsr (MSR_IA32_MCG_EIP , r->eip, h); - -/* can we rely on kmalloc to do a dynamic -* allocation for the reserved registers? -*/ -done: -return mce_num_extended_msrs; } static fastcall void intel_machine_check(struct pt_regs * regs, long error_code)
@@ -155,7 +146,6 @@ static fastcall void intel_machine_checku32 alow, ahigh, high, low;u32 mcgstl, mcgsth;int i; -struct intel_mce_extended_msrs dbg;rdmsr (MSR_IA32_MCG_STATUS , mcgstl, mcgsth);if (mcgstl & (1 <<0 ))/* Recoverable ? */
@@ -164,7 +154,9 @@ static fastcall void intel_machine_checkprintk (KERN_EMERG "CPU %d: Machine Check Exception: %08x%08x\n" ,smp_processor_id (), mcgsth, mcgstl); -if (intel_get_extended_msrs (&dbg)) { +if (mce_num_extended_msrs >0 ) { +struct intel_mce_extended_msrs dbg; +intel_get_extended_msrs (&dbg);printk (KERN_DEBUG "CPU %d: EIP: %08x EFLAGS: %08x\n" ,smp_processor_id (), dbg.eip, dbg.eflags); printk (KERN_DEBUG "\teax: %08x ebx: %08x ecx: %08x edx: %08x\n",
PATCH 35/40 i386: Now that the VDSO can be relocated, we can support it in VMI configurations.
In reply to Andi KleenFrom: Zachary Amsden <[email protected]>
--- arch/i386/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
===================================================================
arch/i386/Kconfig +1 -1
@@ -220,7 +220,7 @@ config PARAVIRT config VMI bool "VMI Paravirt-ops support" - depends on PARAVIRT && !COMPAT_VDSO + depends on PARAVIRT help VMI provides a paravirtualized interface to the VMware ESX server (it could be used by other hypervisors in theory too, but is not
PATCH 36/40 i386: Implement vmi_kmap_atomic_pte
In reply to Andi KleenFrom: Zachary Amsden <[email protected]>
Implement vmi_kmap_atomic_pte in terms of the backend set_linear_mapping operation. The conversion is rather straighforward; call kmap_atomic and then inform the hypervisor of the page mapping.
The _flush_tlb damage is due to macros being pulled in from highmem.h.
--- arch/i386/kernel/vmi.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-)
===================================================================
arch/i386/kernel/vmi.c +22 -12
@@ -26,6 +26,7 @@#include <linux/cpu.h> #include <linux/bootmem.h> #include <linux/mm.h> +#include <linux/highmem.h> #include <asm/vmi.h> #include <asm/io.h> #include <asm/fixmap.h>
@@ -65,8 +66,8 @@ static struct {void (*release_page)(u32 ,u32 );void (*set_pte)(pte_t ,pte_t *,unsigned );void (*update_pte)(pte_t *,unsigned ); -void (*set_linear_mapping)(int ,u32 ,u32 ,u32 ); -void (*flush_tlb)(int ); +void (*set_linear_mapping)(int ,void *,u32 ,u32 ); +void (*_flush_tlb)(int );void (*set_initial_ap_state)(int ,int );void (*halt)(void );void (*set_lazy_mode)(int mode);
@@ -221,12 +222,12 @@ static void vmi_load_esp0(struct tss_strstatic void vmi_flush_tlb_user (void ) { - vmi_ops.flush_tlb (VMI_FLUSH_TLB ); + vmi_ops._flush_tlb (VMI_FLUSH_TLB ); }static void vmi_flush_tlb_kernel (void ) { - vmi_ops.flush_tlb (VMI_FLUSH_TLB |VMI_FLUSH_GLOBAL ); + vmi_ops._flush_tlb (VMI_FLUSH_TLB |VMI_FLUSH_GLOBAL ); }/* Stub to do nothing at all; used for delays and unimplemented calls */
@@ -349,8 +350,11 @@ static void vmi_check_page_type(u32 pfn,#define vmi_check_page_type (p,t) do { } while (0)#endif -static void vmi_map_pt_hook (int type,pte_t *va,u32 pfn) +#ifdef CONFIG_HIGHPTE +static void *vmi_kmap_atomic_pte (struct page *page,enum km_type type) { +void *va =kmap_atomic (page, type); +/* * Internally, the VMI ROM must map virtual addresses to physical * addresses for processing MMU updates. By the time MMU updates
@@ -364,8 +368,11 @@ static void vmi_map_pt_hook(int type, pt* args: SLOT VA COUNT PFN */ BUG_ON (type !=KM_PTE0 && type !=KM_PTE1 ); - vmi_ops.set_linear_mapping ((type -KM_PTE0 )+1 , (u32 )va,1 , pfn); + vmi_ops.set_linear_mapping ((type -KM_PTE0 )+1 , va,1 ,page_to_pfn (page)); + + return va; } +#endif static void vmi_allocate_pt (u32 pfn) {
@@ -660,7 +667,7 @@ void vmi_bringup(void) {/* We must establish the lowmem mapping for MMU ops to work */ if (vmi_ops.set_linear_mapping) - vmi_ops.set_linear_mapping (0 , __PAGE_OFFSET, max_low_pfn,0 ); + vmi_ops.set_linear_mapping (0 , (void *)__PAGE_OFFSET, max_low_pfn,0 ); }/*
@@ -800,8 +807,8 @@ static inline int __init activate_vmi(vopara_wrap (set_lazy_mode, vmi_set_lazy_mode, set_lazy_mode, SetLazyMode);/* user and kernel flush are just handled with different flags to FlushTLB */ -para_wrap (flush_tlb_user, vmi_flush_tlb_user, flush_tlb, FlushTLB); -para_wrap (flush_tlb_kernel, vmi_flush_tlb_kernel, flush_tlb, FlushTLB); +para_wrap (flush_tlb_user, vmi_flush_tlb_user, _flush_tlb, FlushTLB); +para_wrap (flush_tlb_kernel, vmi_flush_tlb_kernel, _flush_tlb, FlushTLB);para_fill (flush_tlb_single, InvalPage);/*
@@ -847,9 +854,12 @@ 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); + +/* Set linear is needed in all cases */ + vmi_ops.set_linear_mapping =vmi_get_function (VMI_CALL_SetLinearMapping); +#ifdef CONFIG_HIGHPTE +if (vmi_ops.set_linear_mapping) + paravirt_ops.kmap_atomic_pte = vmi_kmap_atomic_pte;#endif /*
PATCH 37/40 i386: Convert VMI timer to use clock events
In reply to Andi KleenFrom: Zachary Amsden <[email protected]>
Convert VMI timer to use clock events, making it properly able to use the NO_HZ infrastructure. On UP systems, with no local APIC, we just continue to route these events through the PIT. On systems with a local APIC, or SMP, we provide a single source interrupt chip which creates the local timer IRQ. It actually gets delivered by the APIC hardware, but we don't want to use the same local APIC clocksource processing, so we create our own handler here.
--- arch/i386/kernel/Makefile | 2 arch/i386/kernel/entry.S | 5 arch/i386/kernel/vmi.c | 26 -- arch/i386/kernel/vmiclock.c | 318 +++++++++++++++++++++++++++++ arch/i386/kernel/vmitime.c | 482 -------------------------------------------- include/asm-i386/vmi_time.h | 18 - 6 files changed, 327 insertions(+), 524 deletions(-)
===================================================================
arch/i386/kernel/Makefile +1 -1
@@ -40,7 +40,7 @@ obj-$(CONFIG_EARLY_PRINTK) += early_prin obj-$(CONFIG_HPET_TIMER) += hpet.o obj-$(CONFIG_K8_NB) += k8.o -obj-$(CONFIG_VMI) += vmi.o vmitime.o +obj-$(CONFIG_VMI) += vmi.o vmiclock.o obj-$(CONFIG_PARAVIRT) += paravirt.o obj-y += pcspeaker.o
arch/i386/kernel/vmi.c +6 -20
@@ -77,6 +77,9 @@ static struct {extern struct paravirt_patch __start_parainstructions[], __stop_parainstructions[]; +/* Cached VMI operations */ +struct vmi_timer_ops vmi_timer_ops; +/* * VMI patching routines. */
@@ -235,18 +238,6 @@ static void vmi_nop(void) { } -/* For NO_IDLE_HZ, we stop the clock when halting the kernel */ -static fastcall void vmi_safe_halt(void) -{ -int idle =vmi_stop_hz_timer (); - vmi_ops.halt (); -if (idle) { -local_irq_disable (); -vmi_account_time_restart_hz_timer (); -local_irq_enable (); - } -} -#ifdef CONFIG_DEBUG_PAGE_TYPE #ifdef CONFIG_X86_PAE
@@ -722,7 +713,6 @@ do { \ } \ }while (0 ) -/* * Activate the VMI interface and switch into paravirtualized mode */
@@ -901,8 +891,8 @@ static inline int __init activate_vmi(vo paravirt_ops.get_wallclock = vmi_get_wallclock; paravirt_ops.set_wallclock = vmi_set_wallclock;#ifdef CONFIG_X86_LOCAL_APIC - paravirt_ops.setup_boot_clock = vmi_timer_setup_boot_alarm; - paravirt_ops.setup_secondary_clock = vmi_timer_setup_secondary_alarm; + paravirt_ops.setup_boot_clock = vmi_time_bsp_init; + paravirt_ops.setup_secondary_clock = vmi_time_ap_init;#endif paravirt_ops.get_scheduled_cycles = vmi_get_sched_cycles; paravirt_ops.get_cpu_khz = vmi_cpu_khz;
@@ -914,11 +904,7 @@ static inline int __init activate_vmi(vo disable_vmi_timer =1 ; } -/* No idle HZ mode only works if VMI timer and no idle is enabled */ -if (disable_noidle || disable_vmi_timer) -para_fill (safe_halt, Halt); -else -para_wrap (safe_halt, vmi_safe_halt, halt, Halt); +para_fill (safe_halt, Halt);/* * Alternative instruction rewriting doesn't happen soon enough
include/asm-i386/vmi_time.h +2 -16
@@ -53,22 +53,8 @@ extern unsigned long long vmi_get_sched_extern unsigned long vmi_cpu_khz (void );#ifdef CONFIG_X86_LOCAL_APIC -extern void __init vmi_timer_setup_boot_alarm (void ); -extern void __devinit vmi_timer_setup_secondary_alarm (void ); -extern void apic_vmi_timer_interrupt (void ); -#endif - -#ifdef CONFIG_NO_IDLE_HZ -extern int vmi_stop_hz_timer (void ); -extern void vmi_account_time_restart_hz_timer (void ); -#else -static inline int vmi_stop_hz_timer (void ) -{ -return 0 ; -} -static inline void vmi_account_time_restart_hz_timer (void ) -{ -} +extern void __devinit vmi_time_bsp_init (void ); +extern void __devinit vmi_time_ap_init (void );#endif /*
arch/i386/kernel/entry.S +0 -5
@@ -637,11 +637,6 @@ ENDPROC(name) /* The include is where all of the SMP etc. interrupts come from */ #include "entry_arch.h" -/* This alternate entry is needed because we hijack the apic LVTT */ -#if defined(CONFIG_VMI) && defined(CONFIG_X86_LOCAL_APIC) -BUILD_INTERRUPT(apic_vmi_timer_interrupt,LOCAL_TIMER_VECTOR) -#endif - KPROBE_ENTRY(page_fault) RING0_EC_FRAME pushl $do_page_fault
arch/i386/kernel/vmiclock.c new file +318 -0
318 lines not on this page · raw message
arch/i386/kernel/vmitime.c deleted +0 -482
482 lines not on this page · raw message
PATCH 38/40 x86: Jeremy Fitzhardinge <[email protected]>
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
The other symbols used to delineate the alt-instructions sections have the form __foo/__foo_end. Rename parainstructions to match.
--- arch/i386/kernel/alternative.c | 2 +- arch/i386/kernel/vmi.c | 10 +++------- arch/i386/kernel/vmlinux.lds.S | 4 ++-- include/asm-i386/alternative.h | 4 ++-- include/asm-x86_64/alternative.h | 4 ++-- 5 files changed, 10 insertions(+), 14 deletions(-)
===================================================================
arch/i386/kernel/alternative.c +1 -1
@@ -399,6 +399,6 @@ void __init alternative_instructions(voi alternatives_smp_switch(0); }#endif -apply_paravirt (__start_parainstructions, __stop_parainstructions); +apply_paravirt (__parainstructions, __parainstructions_end);local_irq_restore (flags); }
arch/i386/kernel/vmi.c +3 -7
@@ -73,10 +73,6 @@ static struct {void (*set_lazy_mode)(int mode); } vmi_ops; -/* XXX move this to alternative.h */ -extern struct paravirt_patch __start_parainstructions[], - __stop_parainstructions[]; -/* Cached VMI operations */ struct vmi_timer_ops vmi_timer_ops;
@@ -548,9 +544,9 @@ vmi_startup_ipi_hook(int phys_apicid, un }#endif -static void vmi_set_lazy_mode (int mode) +static void vmi_set_lazy_mode (enum paravirt_lazy_mode mode) { - static DEFINE_PER_CPU(int, lazy_mode); + static DEFINE_PER_CPU(enum paravirt_lazy_mode, lazy_mode);if (!vmi_ops.set_lazy_mode)return ;
@@ -912,7 +908,7 @@ static inline int __init activate_vmi(vo* to do this before IRQs get reenabled. Fortunately, it is * idempotent. */ -apply_paravirt (__start_parainstructions, __stop_parainstructions); +apply_paravirt (__parainstructions, __parainstructions_end);vmi_bringup ();
arch/i386/kernel/vmlinux.lds.S +2 -2
@@ -166,9 +166,9 @@ SECTIONS } . = ALIGN(4); .parainstructions : AT(ADDR(.parainstructions) - LOAD_OFFSET) { - __start_parainstructions = .; + __parainstructions = .; *(.parainstructions) - __stop_parainstructions = .; + __parainstructions_end = .; } /* .exit.text is discard at runtime, not link time, to deal with references from .altinstructions and .eh_frame */
include/asm-i386/alternative.h +2 -2
@@ -124,8 +124,8 @@ static inline void apply_paravirt(struct paravirt_patch_site *start, struct paravirt_patch_site *end) {} -#define __start_parainstructions NULL -#define __stop_parainstructions NULL +#define __parainstructions NULL +#define __parainstructions_end NULL #endif #endif /* _I386_ALTERNATIVE_H */
include/asm-x86_64/alternative.h +2 -2
@@ -142,8 +142,8 @@ void apply_paravirt(struct paravirt_patcstatic inline void apply_paravirt (struct paravirt_patch *start,struct paravirt_patch *end) {} -#define __start_parainstructions NULL -#define __stop_parainstructions NULL +#define __parainstructions NULL +#define __parainstructions_end NULL#endif #endif /* _X86_64_ALTERNATIVE_H */
PATCH 39/40 i386: Export paravirt_ops for non GPL modules too
In reply to Andi KleenOtherwise non GPL modules cannot even do basic operations like disabling interrupts anymore, which would be excessive.
Longer term should split the single structure up into internal and external symbols and not export the internal ones at all.
--- arch/i386/kernel/paravirt.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
arch/i386/kernel/paravirt.c +1 -7
@@ -346,10 +346,4 @@ struct paravirt_ops paravirt_ops = { .startup_ipi_hook = paravirt_nop, }; -/* -* NOTE: CONFIG_PARAVIRT is experimental and the paravirt_ops -* semantics are subject to change. Hence we only do this -* internal-only export of this, until it gets sorted out and -* all lowlevel CPU ops used by modules are separately exported. -*/ -EXPORT_SYMBOL_GPL (paravirt_ops); +EXPORT_SYMBOL (paravirt_ops);
Re: [PATCH] [39/40] i386: Export paravirt_ops for non GPL modules too
Quoted 9 lines · Andi Kleen
Otherwise non GPL modules cannot even do basic operations like disabling interrupts anymore, which would be excessive.
Longer term should split the single structure up into internal and external symbols and not export the internal ones at all.
Signed-off-by Andi Kleen <[email protected]>
Ingo was dead-set against this and I kinda agree.
Re: [PATCH] [39/40] i386: Export paravirt_ops for non GPL modules too
Quoted 5 lines · On Monday 30 April 2007 12:50:09 Christoph Hellwig wrote:
Quoted 4 lines · Andi Kleen
Otherwise non GPL modules cannot even do basic operations like disabling interrupts anymore, which would be excessive.
Quoted 7 lines
Quoted 5 lines
Longer term should split the single structure up into internal and external symbols and not export the internal ones at all.
Signed-off-by Andi Kleen <[email protected]>Ingo was dead-set against this and I kinda agree.
The problem is that without this non GPL modules cannot even disable interrupts anymore. That is imho too radical.
-Andi
Re: [PATCH] [39/40] i386: Export paravirt_ops for non GPL modules too
Quoted 10 lines · Andi Kleen
Quoted 9 lines · On Monday 30 April 2007 12:50:09 Christoph Hellwig wrote:
Quoted 8 lines · Andi Kleen
Otherwise non GPL modules cannot even do basic operations like disabling interrupts anymore, which would be excessive.
Longer term should split the single structure up into internal and external symbols and not export the internal ones at all.
Quoted 6 lines
Quoted 3 lines
Quoted 1 line
Signed-off-by Andi Kleen <[email protected]>Ingo was dead-set against this and I kinda agree.
The problem is that without this non GPL modules cannot even disable interrupts anymore. That is imho too radical.
Perhaps we can have a paravirt_ops2 that specifically deals with interrupt en/disable, and export that instead?
Jan
Re: [PATCH] [39/40] i386: Export paravirt_ops for non GPL modules too
Quoted 14 lines · On Monday 30 April 2007 13:15:36 Jan Engelhardt wrote:
Quoted 12 lines · Andi Kleen
Quoted 11 lines · On Monday 30 April 2007 12:50:09 Christoph Hellwig wrote:
Quoted 9 lines · Andi Kleen
Otherwise non GPL modules cannot even do basic operations like disabling interrupts anymore, which would be excessive.
Longer term should split the single structure up into internal and external symbols and not export the internal ones at all.
Signed-off-by Andi Kleen <[email protected]>
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
Ingo was dead-set against this and I kinda agree.
The problem is that without this non GPL modules cannot even disable interrupts anymore. That is imho too radical.
Perhaps we can have a
paravirt_ops2that specifically deals with interrupt en/disable, and export that instead?
Yes that is what the "Longer term ..." paragraph above refers to. However it would need some restructuring in the code.
-Andi
Re: [PATCH] [39/40] i386: Export paravirt_ops for non GPL modules too
Quoted 17 lines · Andi Kleen
Quoted 16 lines · On Monday 30 April 2007 13:15:36 Jan Engelhardt wrote:
Quoted 14 lines · Andi Kleen
Quoted 12 lines · On Monday 30 April 2007 12:50:09 Christoph Hellwig wrote:
Quoted 9 lines · Andi Kleen
Otherwise non GPL modules cannot even do basic operations like disabling interrupts anymore, which would be excessive.
Longer term should split the single structure up into internal and external symbols and not export the internal ones at all.
Signed-off-by Andi Kleen <[email protected]>Ingo was dead-set against this and I kinda agree.
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
The problem is that without this non GPL modules cannot even disable interrupts anymore. That is imho too radical.
Perhaps we can have a
paravirt_ops2that specifically deals with interrupt en/disable, and export that instead?Yes that is what the "Longer term ..." paragraph above refers to. However it would need some restructuring in the code.
FWIW I think doing this first will be better, exposing _all_ to non GNU modules will weaken whatever case we might have to take it away later.
So, NACK from me too.
I don't want to hear the whining; but it was allowed in .22, so why should we not be able to do this in .23.... or whatever.
Re: [PATCH] [39/40] i386: Export paravirt_ops for non GPL modules too
Quoted 2 lines
FWIW I think doing this first will be better, exposing _all_ to non GNU modules will weaken whatever case we might have to take it away later.
I have no problems taking it away later again. Or rather taking away the symbols where non GPL code clearly has no business messing with.
I don't think that applies to save_fl/restore_fl/irq_disable/irq_enable though. Undecided yet about the page table manipulation code (set_pte etc.)
-Andi
RE: [PATCH] [39/40] i386: Export paravirt_ops for non GPL modulestoo
In reply to Peter ZijlstraQuoted 8 lines
FWIW I think doing this first will be better, exposing _all_ to non GNU modules will weaken whatever case we might have to take it away later.
So, NACK from me too.
I don't want to hear the whining; but it was allowed in .22, so why should we not be able to do this in .23.... or whatever.
This is the most illogical and perverse argument I think I have *ever* seen on this mailing list, and that's saying something.
If people find legitimate uses for it while it's available, that's a proof that taking it away is wrong. Symbols are supposed to be marked GPL if and only if it's not possible to use them in non-GPL'd works. So, yeah, let's not risk letting people prove your position is wrong before you have a chance to enforce it.
DS
Re: [PATCH] [39/40] i386: Export paravirt_ops for non GPL modules too
In reply to Christoph HellwigQuoted 5 lines · Christoph Hellwig
Quoted 4 lines · Andi Kleen
Otherwise non GPL modules cannot even do basic operations like disabling interrupts anymore, which would be excessive.
Quoted 7 lines
Quoted 5 lines
Longer term should split the single structure up into internal and external symbols and not export the internal ones at all.
Signed-off-by Andi Kleen <[email protected]>Ingo was dead-set against this and I kinda agree.
So would I, as unimportant as I am in that virtualization area.
Jan
Re: [PATCH] [39/40] i386: Export paravirt_ops for non GPL modules too
In reply to Christoph HellwigQuoted 5 lines · On Mon, 30 Apr 2007 11:50:09 +0100 Christoph Hellwig <[email protected]> wrote:
Quoted 4 lines · Andi Kleen
Otherwise non GPL modules cannot even do basic operations like disabling interrupts anymore, which would be excessive.
Quoted 7 lines
Quoted 5 lines
Longer term should split the single structure up into internal and external symbols and not export the internal ones at all.
Signed-off-by Andi Kleen <[email protected]>Ingo was dead-set against this and I kinda agree.
Ditto - do the work first then merge it.
NAK the patch
Re: [PATCH] [39/40] i386: Export paravirt_ops for non GPL modules too
Quoted 12 lines · On Monday 30 April 2007 16:55:48 Alan Cox wrote:
Quoted 9 lines · On Mon, 30 Apr 2007 11:50:09 +0100 Christoph Hellwig <[email protected]> wrote:
Quoted 8 lines · Andi Kleen
Otherwise non GPL modules cannot even do basic operations like disabling interrupts anymore, which would be excessive.
Longer term should split the single structure up into internal and external symbols and not export the internal ones at all.
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
Signed-off-by Andi Kleen <[email protected]>Ingo was dead-set against this and I kinda agree.
Ditto - do the work first then merge it.
Ok. Jeremy has come up with a ingenious simple solution. He promised a patch.
-Andi
Re: [PATCH] [39/40] i386: Export paravirt_ops for non GPL modules too
In reply to Alan CoxQuoted 13 lines · Alan Cox
Quoted 9 lines · On Mon, 30 Apr 2007 11:50:09 +0100 Christoph Hellwig <[email protected]> wrote:
Quoted 7 lines · Andi Kleen
Otherwise non GPL modules cannot even do basic operations like disabling interrupts anymore, which would be excessive.
Longer term should split the single structure up into internal and external symbols and not export the internal ones at all.
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
Signed-off-by Andi Kleen <[email protected]>Ingo was dead-set against this and I kinda agree.
Ditto - do the work first then merge it.
The majority of paravirt_ops entrypoints are things which are currently exported as inline functions in headers anyway. There isn't a lot which would become available to modules under a CONFIG_PARAVIRT kernel which isn't already available to a non-CONFIG_PARAVIRT kernel.
We can hide the few remaining entries, I suppose. But any module which used them would only work with a PARAVIRT kernel anyway, so its hardly going to be the best course for a module author - assuming they're at all useful anyway (ooh, look, we can activate an mm!).
If we want to address this consistently, then we could scatter a pile of #ifndef MODULEs around the headers to make sure the inlines are not visible to modules in either case. And we could correspondingly nobble paravirt_ops by masking unexported entries with #ifndef MODULE.
So it isn't worth worrying about the paravirt_ops export unless you also deal with the non-PARAVIRT case.
J
PATCH 40/40 i386: Clean up ELF note generation
In reply to Andi KleenFrom: Jeremy Fitzhardinge <[email protected]>
Three cleanups:
1: ELF notes are never mapped, so there's no need to have any access flags in their phdr.
2: When generating them from asm, tell the assembler to use a SHT_NOTE section type. There doesn't seem to be a way to do this from C.
3: Use ANSI rather than traditional cpp behaviour to stringify the macro argument.
--- arch/i386/kernel/vmlinux.lds.S | 2 +- include/asm-generic/vmlinux.lds.h | 2 +- include/linux/elfnote.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-)
===================================================================
arch/i386/kernel/vmlinux.lds.S +1 -1
@@ -30,7 +30,7 @@ jiffies = jiffies_64; PHDRS { text PT_LOAD FLAGS(5); /* R_E */ data PT_LOAD FLAGS(7); /* RWE */ - note PT_NOTE FLAGS(4); /* R__ */ + note PT_NOTE FLAGS(0); /* ___ */ } SECTIONS {
include/asm-generic/vmlinux.lds.h +1 -1
@@ -208,7 +208,7 @@ }#define NOTES \ - .notes : { *(.note.*) } :note + .notes : { *(.note.*) } :note#define INITCALLS \ *(.initcall0.init) \
include/linux/elfnote.h +2 -2
@@ -39,12 +39,12 @@* ELFNOTE(XYZCo, 12, .long, 0xdeadbeef) */ #define ELFNOTE(name, type, desctype, descdata) \ -.pushsection .note.name ; \ +.pushsection .note.name, "",@note ; \ .align 4 ; \ .long 2f - 1f /* namesz */ ; \ .long 4f - 3f /* descsz */ ; \ .long type ; \ -1:.asciz "name" ; \ +1:.asciz #name ; \ 2:.align 4 ; \ 3:desctype descdata ; \ 4:.align 4 ; \