paravirt_ops updates
Subsystems (8)
cover paravirt_ops updates
Hi Andi,
This series of patches updates paravirt_ops in various ways. Some of the changes are plain cleanups and improvements, and some add some interfaces necessary for Xen.
The brief overview:
add-MAINTAINERS.patch - obvious
remove-CONFIG_DEBUG_PARAVIRT.patch - no longer needed
paravirt-nop.patch - mark nop operations consistently
paravirt-pte-accessors.patch - operations to pack/unpack ptes
paravirt-memory-init.patch - hooks for memory/pagetable init
paravirt-fixmap.patch - add fixmap for paravirt boot
shared-kernel-pmd.patch - allow selectable shared kernel pmd
mm-lifetime-hooks.patch - hooks for mm dup/activate/exit
paravirt-patch-rename-paravirt_patch.patch - rename patch structure for clarity
paravirt-use-offset-site-ids.patch - use patch-site id derived from offset
paravirt-fix-clobbers.patch - fix register clobbers for patch sites
paravirt-patchable-call-wrappers.patch - wrap paravirt calls for patching
paravirt-patch-machinery.patch - common patch machinery
paravirt-flush_tlb_others.patch - paravirt hook for cross-cpu tlb flushing
revert-map_pt_hook.patch - back out map_pt_hook
paravirt-kmap_atomic_pte.patch - add kmap_atomic_pte for highpte
paravirt-sched-clock.patch - hook to measure schedulable time
Thanks,
J
PATCH 1/17 update MAINTAINERS
--- MAINTAINERS | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
===================================================================
MAINTAINERS +22 -0
@@ -2583,6 +2583,19 @@ T: cvs cvs.parisc-linux.org:/var/cvs/lin T: cvs cvs.parisc-linux.org:/var/cvs/linux-2.6 S: Maintained +PARAVIRT_OPS INTERFACE +P: Jeremy Fitzhardinge +M: [email protected] +P: Chris Wright +M: [email protected] +P: Zachary Amsden +M: [email protected] +P: Rusty Russell +M: [email protected] +L: [email protected] +L: [email protected] +S: Supported + PC87360 HARDWARE MONITORING DRIVER P: Jim Cromie M: [email protected]
@@ -3780,6 +3793,15 @@ L: [email protected] L: [email protected] S: Maintained +XEN HYPERVISOR INTERFACE +P: Jeremy Fitzhardinge +M: [email protected] +P: Chris Wright +M: [email protected] +L: [email protected] +L: [email protected] +S: Supported + XFS FILESYSTEM P: Silicon Graphics Inc P: Tim Shimmin, David Chatterton
PATCH 2/17 Remove CONFIG_DEBUG_PARAVIRT
In reply to Jeremy FitzhardingeRemove 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
@@ -359,19 +359,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/17 use paravirt_nop to consistently mark no-op operations
In reply to Jeremy FitzhardingeAdd a _paravirt_nop function for use as a stub for no-op operations, and paravirt_nop #defined void * version to make using it easier (since all its uses are as a void *).
This is useful to allow the patcher to automatically identify noop operations so it can simply nop out the callsite.
[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 | 26 +++++++++++++------------- include/asm-i386/paravirt.h | 3 +++ 2 files changed, 16 insertions(+), 13 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +13 -13
@@ -35,7 +35,7 @@#include <asm/timer.h> /* nop stub */ -static void native_nop (void ) +void _paravirt_nop (void ) { }
@@ -490,7 +490,7 @@ struct paravirt_ops paravirt_ops = { .patch = native_patch, .banner = default_banner, - .arch_setup = native_nop, + .arch_setup = paravirt_nop, .memory_setup = machine_specific_memory_setup, .get_wallclock = native_get_wallclock, .set_wallclock = native_set_wallclock,
@@ -546,25 +546,25 @@ struct paravirt_ops paravirt_ops = { .setup_boot_clock = setup_boot_APIC_clock, .setup_secondary_clock = setup_secondary_APIC_clock, #endif - .set_lazy_mode = (void *)native_nop, + .set_lazy_mode = paravirt_nop, .flush_tlb_user = native_flush_tlb, .flush_tlb_kernel = native_flush_tlb_global, .flush_tlb_single = native_flush_tlb_single, - .map_pt_hook = (void *)native_nop, - - .alloc_pt = (void *)native_nop, - .alloc_pd = (void *)native_nop, - .alloc_pd_clone = (void *)native_nop, - .release_pt = (void *)native_nop, - .release_pd = (void *)native_nop, + .map_pt_hook = paravirt_nop, + + .alloc_pt = paravirt_nop, + .alloc_pd = paravirt_nop, + .alloc_pd_clone = paravirt_nop, + .release_pt = paravirt_nop, + .release_pd = paravirt_nop, .set_pte = native_set_pte, .set_pte_at = native_set_pte_at, .set_pmd = native_set_pmd, - .pte_update = (void *)native_nop, - .pte_update_defer = (void *)native_nop, + .pte_update = paravirt_nop, + .pte_update_defer = paravirt_nop, #ifdef CONFIG_X86_PAE .set_pte_atomic = native_set_pte_atomic, .set_pte_present = native_set_pte_present,
@@ -576,7 +576,7 @@ struct paravirt_ops paravirt_ops = { .irq_enable_sysexit = native_irq_enable_sysexit, .iret = native_iret, - .startup_ipi_hook = (void *)native_nop, + .startup_ipi_hook = paravirt_nop, };/*
===================================================================
include/asm-i386/paravirt.h +3 -0
@@ -430,6 +430,9 @@ static inline void pmd_clear(pmd_t *pmdp#define arch_enter_lazy_mmu_mode () paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_MMU)#define arch_leave_lazy_mmu_mode () paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_NONE) +void _paravirt_nop (void ); +#define paravirt_nop ((void *)_paravirt_nop) +/* These all sit in the .parainstructions section to tell us what to patch. */ struct paravirt_patch {u8 *instr;/* original instructions */
PATCH 4/17 Add pagetable accessors to pack and unpack pagetable entries
In reply to Jeremy FitzhardingeAdd a set of accessors to pack, unpack and modify page table entries (at all levels). This allows a paravirt implementation to control the contents of pgd/pmd/pte entries. For example, Xen uses this to convert the (pseudo-)physical address into a machine address when populating a pagetable entry, and converting back to pphys address when an entry is read.
--- arch/i386/kernel/paravirt.c | 84 +++++-------------------------------- arch/i386/kernel/vmi.c | 6 +- include/asm-i386/page.h | 79 +++++++++++++++++++++++++++++----- include/asm-i386/paravirt.h | 52 +++++++++++++++++----- include/asm-i386/pgtable-2level.h | 28 +++++++++--- include/asm-i386/pgtable-3level.h | 65 +++++++++++++++++----------- include/asm-i386/pgtable.h | 2 7 files changed, 186 insertions(+), 130 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +12 -72
@@ -399,78 +399,6 @@ static void native_flush_tlb_single(u32 {__native_flush_tlb_single (addr); } - -#ifndef CONFIG_X86_PAE -static void native_set_pte (pte_t *ptep,pte_t pteval) -{ - *ptep = pteval; -} - -static void native_set_pte_at (struct mm_struct *mm,u32 addr,pte_t *ptep,pte_t pteval) -{ - *ptep = pteval; -} - -static void native_set_pmd (pmd_t *pmdp,pmd_t pmdval) -{ - *pmdp = pmdval; -} - -#else /* CONFIG_X86_PAE */ - -static void native_set_pte (pte_t *ptep,pte_t pte) -{ - ptep->pte_high = pte.pte_high; -smp_wmb (); - ptep->pte_low = pte.pte_low; -} - -static void native_set_pte_at (struct mm_struct *mm,u32 addr,pte_t *ptep,pte_t pte) -{ - ptep->pte_high = pte.pte_high; -smp_wmb (); - ptep->pte_low = pte.pte_low; -} - -static void native_set_pte_present (struct mm_struct *mm,unsigned long addr,pte_t *ptep,pte_t pte) -{ - ptep->pte_low =0 ; -smp_wmb (); - ptep->pte_high = pte.pte_high; -smp_wmb (); - ptep->pte_low = pte.pte_low; -} - -static void native_set_pte_atomic (pte_t *ptep,pte_t pteval) -{ -set_64bit ((unsigned long long *)ptep,pte_val (pteval)); -} - -static void native_set_pmd (pmd_t *pmdp,pmd_t pmdval) -{ -set_64bit ((unsigned long long *)pmdp,pmd_val (pmdval)); -} - -static void native_set_pud (pud_t *pudp,pud_t pudval) -{ - *pudp = pudval; -} - -static void native_pte_clear (struct mm_struct *mm,unsigned long addr,pte_t *ptep) -{ - ptep->pte_low =0 ; -smp_wmb (); - ptep->pte_high =0 ; -} - -static void native_pmd_clear (pmd_t *pmd) -{ -u32 *tmp = (u32 *)pmd; - *tmp =0 ; -smp_wmb (); - *(tmp +1 ) =0 ; -} -#endif /* CONFIG_X86_PAE */ /* These are in entry.S */ extern void native_iret (void );
@@ -565,13 +493,25 @@ struct paravirt_ops paravirt_ops = { .set_pmd = native_set_pmd, .pte_update = paravirt_nop, .pte_update_defer = paravirt_nop, + + .ptep_get_and_clear = native_ptep_get_and_clear, + #ifdef CONFIG_X86_PAE .set_pte_atomic = native_set_pte_atomic, .set_pte_present = native_set_pte_present, .set_pud = native_set_pud, .pte_clear = native_pte_clear, .pmd_clear = native_pmd_clear, + + .pmd_val = native_pmd_val, + .make_pmd = native_make_pmd, #endif + + .pte_val = native_pte_val, + .pgd_val = native_pgd_val, + + .make_pte = native_make_pte, + .make_pgd = native_make_pgd, .irq_enable_sysexit = native_irq_enable_sysexit, .iret = native_iret,
===================================================================
arch/i386/kernel/vmi.c +3 -3
@@ -444,13 +444,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 ));
@@ -463,7 +463,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
113 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 +22 -6
@@ -11,11 +11,24 @@* within a page table are directly modified. Thus, the following * hook is made available. */ +static inline void native_set_pte (pte_t *ptep ,pte_t pte) +{ + *ptep = pte; +} +static inline void native_set_pte_at (struct mm_struct *mm,unsigned long addr, +pte_t *ptep ,pte_t pte) +{ +native_set_pte (ptep, pte); +} +static inline void native_set_pmd (pmd_t *pmdp,pmd_t pmd) +{ + *pmdp = pmd; +}#ifndef CONFIG_PARAVIRT -#define set_pte (pteptr, pteval) (*(pteptr) = pteval) -#define set_pte_at (mm,addr,ptep,pteval) set_pte(ptep,pteval) -#define set_pmd (pmdptr, pmdval) (*(pmdptr) = (pmdval)) -#endif +#define set_pte (pteptr, pteval) native_set_pte(pteptr, pteval) +#define set_pte_at (mm,addr,ptep,pteval) native_set_pte_at(mm, addr, ptep, pteval) +#define set_pmd (pmdptr, pmdval) native_set_pmd(pmdptr, pmdval) +#endif #define set_pte_atomic (pteptr, pteval) set_pte(pteptr,pteval)#define set_pte_present (mm,addr,ptep,pteval) set_pte_at(mm,addr,ptep,pteval)
@@ -23,11 +36,14 @@#define pte_clear (mm,addr,xp) do { set_pte_at(mm, addr, xp, __pte(0)); } while (0)#define pmd_clear (xp) do { set_pmd(xp, __pmd(0)); } while (0) -#define raw_ptep_get_and_clear (xp) __pte(xchg(&(xp)->pte_low, 0)) +static inline pte_t native_ptep_get_and_clear (pte_t *xp) +{ +return __pte (xchg (&xp->pte_low,0 )); +}#define pte_page (x) pfn_to_page(pte_pfn(x))#define pte_none (x) (!(x).pte_low) -#define pte_pfn (x) ((unsigned long)(((x).pte_low >> PAGE_SHIFT))) +#define pte_pfn (x) (pte_val(x) >> PAGE_SHIFT)#define pfn_pte (pfn, prot) __pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot))#define pfn_pmd (pfn, prot) __pmd(((pfn) << PAGE_SHIFT) | pgprot_val(prot))
===================================================================
include/asm-i386/pgtable-3level.h +39 -26
134 lines not on this page · raw message
===================================================================
include/asm-i386/pgtable.h +2 -0
@@ -264,6 +264,8 @@ static inline pte_t pte_mkhuge(pte_t pte#define pte_update (mm, addr, ptep) do { } while (0)#define pte_update_defer (mm, addr, ptep) do { } while (0)#define paravirt_map_pt_hook (slot, va, pfn) do { } while (0) + +#define raw_ptep_get_and_clear (xp) native_ptep_get_and_clear(xp)#endif /*
Re: [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries
Quoted 6 lines · Jeremy Fitzhardinge
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.
What do the benchmarks say with CONFIG_PARAVIRT on native hardware compared to !CONFIG_PARAVIRT. e.g. does lmbench suffer?
-Andi
Re: [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries
Quoted 10 lines · Andi Kleen
Quoted 6 lines · Jeremy Fitzhardinge
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.
What do the benchmarks say with
CONFIG_PARAVIRTon native hardware compared to !CONFIG_PARAVIRT. e.g. does lmbench suffer?
This patch by itself, definitely: I gathered stats previously simply measuring the number of calls, and it's comparable to the other calls we had to patch.
So it's clear that the patch infrastructure needs to cover them. We can debate whether we should extend patching to everything as 1/17 does, or just to mkpte and pteval.
I'd still like to see new microbench stats tho.
Cheers!
Rusty.
Re: [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries
In reply to Andi KleenQuoted 3 lines · Andi Kleen
What do the benchmarks say with
CONFIG_PARAVIRTon native hardware compared to !CONFIG_PARAVIRT. e.g. does lmbench suffer?
I haven't measured it, but I'll do some experiments.
J
Re: [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries
In reply to Andi KleenQuoted 2 lines · Andi Kleen
What do the benchmarks say with
CONFIG_PARAVIRTon native hardware compared to !CONFIG_PARAVIRT. e.g. does lmbench suffer?
Barely. There's a slight hit for not using patching, and patching is almost identical to native performance. The most noticeable difference is in the null syscall microbenchmark, but once you get to complex things the difference is in the noise.
Processor, Processes - times in microseconds - smaller is better
------------------------------------------------------------------------------
Host OS Mhz null null open slct sig sig fork exec sh
call I/O stat clos TCP inst hndl proc proc proc
--------- ------------- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- non-paravirt
ezr Linux 2.6.21- 1000 0.25 0.52 31.6 34.7 10.3 1.03 5.31 726. 1565 4520 ezr Linux 2.6.21- 1000 0.25 0.52 31.8 34.7 12.6 1.03 5.41 725. 1564 4585 ezr Linux 2.6.21- 1000 0.25 0.55 31.7 34.5 11.8 1.02 5.47 720. 1595 4518
paravirt, no patching
ezr Linux 2.6.21- 1000 0.28 0.55 31.3 34.3 10.0 1.05 5.56 747. 1621 4675 ezr Linux 2.6.21- 1000 0.28 0.56 31.5 34.3 12.9 1.05 5.66 755. 1629 4684 ezr Linux 2.6.21- 1000 0.28 0.55 31.8 34.5 12.5 1.05 5.45 747. 1622 4695
paravirt, patching
ezr Linux 2.6.21- 1000 0.25 0.53 31.8 34.4 10.1 1.04 5.44 730. 1583 4600 ezr Linux 2.6.21- 1000 0.26 0.55 32.1 35.2 13.3 1.03 5.48 748. 1589 4606 ezr Linux 2.6.21- 1000 0.26 0.54 32.0 34.9 14.1 1.04 5.43 752. 1606 4647
J
Re: [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries
Quoted 7 lines · Jeremy Fitzhardinge
Quoted 2 lines
What do the benchmarks say with
CONFIG_PARAVIRTon native hardware compared to !CONFIG_PARAVIRT. e.g. does lmbench suffer?Barely. There's a slight hit for not using patching, and patching is almost identical to native performance. The most noticeable difference is in the null syscall microbenchmark, but once you get to complex things the difference is in the noise.
i'd not call this 'barely' or 'noise'! Look:
Quoted 6 lines
Processor, Processes - times in microseconds - smaller is better
------------------------------------------------------------------------------
Host OS Mhz null null open slct sig sig fork exec sh
call I/O stat clos TCP inst hndl proc proc proc
--------- ------------- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- non-paravirt
Quoted 8 lines
ezr Linux 2.6.21- 1000 0.25 0.52 31.6 34.7 10.3 1.03 5.31 726. 1565 4520 ezr Linux 2.6.21- 1000 0.25 0.52 31.8 34.7 12.6 1.03 5.41 725. 1564 4585 ezr Linux 2.6.21- 1000 0.25 0.55 31.7 34.5 11.8 1.02 5.47 720. 1595 4518paravirt, no patching
ezr Linux 2.6.21- 1000 0.28 0.55 31.3 34.3 10.0 1.05 5.56 747. 1621 4675 ezr Linux 2.6.21- 1000 0.28 0.56 31.5 34.3 12.9 1.05 5.66 755. 1629 4684 ezr Linux 2.6.21- 1000 0.28 0.55 31.8 34.5 12.5 1.05 5.45 747. 1622 4695
the main metric we are interested in is the overhead for people who just want to run the non-patched native kernel that has CONFIG_PARAVIRT enabled (99%+ of the users at the moment), so the delta is:
null: +12.0%
null IO: +7.5%
stat: within noise
open/close: within noise
TCP: ~5.0%
signal install: 2.0% signal handle: 4.7% fork: 2.7% exec: 3.6% shell: 3.6%
this is not 'barely measurable' but 'BLOODY LARGE' overhead. Really. I mean for something as complex as exec we still are 'a couple of percent' slower? Linux has literally _dozens_ of important but inactive features in every critical fastpath and in every critical system call, and if each took 'just a few percent', we'd be a few _times_ slower as an end result, for features that are not even used! The answer to any such overhead is: "get your act together and stop burdening those who dont want to use that feature" ... This is a basic engineering requirement.
Quoted 4 lines
paravirt, patching
ezr Linux 2.6.21- 1000 0.25 0.53 31.8 34.4 10.1 1.04 5.44 730. 1583 4600 ezr Linux 2.6.21- 1000 0.26 0.55 32.1 35.2 13.3 1.03 5.48 748. 1589 4606 ezr Linux 2.6.21- 1000 0.26 0.54 32.0 34.9 14.1 1.04 5.43 752. 1606 4647
i guess this pretty much makes the case for patching ...
Ingo
Re: [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries
Quoted 7 lines
Quoted 5 lines
paravirt, patching
ezr Linux 2.6.21- 1000 0.25 0.53 31.8 34.4 10.1 1.04 5.44 730. 1583 4600 ezr Linux 2.6.21- 1000 0.26 0.55 32.1 35.2 13.3 1.03 5.48 748. 1589 4606 ezr Linux 2.6.21- 1000 0.26 0.54 32.0 34.9 14.1 1.04 5.43 752. 1606 4647i guess this pretty much makes the case for patching ...
Yes, we definitely want to patch. Still it's unclear why it still shows slower stat. Or could that be benchmark noise?
-Andi
Re: [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries
In reply to Ingo MolnarQuoted 9 lines · Ingo Molnar
the main metric we are interested in is the overhead for people who just want to run the non-patched native kernel that has
CONFIG_PARAVIRTenabled (99%+ of the users at the moment), so the delta is:null: +12.0%
null IO: +7.5%
stat: within noise
open/close: within noise
TCP: ~5.0%
Quoted 6 lines
signal install: 2.0% signal handle: 4.7% fork: 2.7% exec: 3.6% shell: 3.6%
Hm, I don't think you can get this much precision out of these numbers. I noticed larger variations from boot-to-boot running the same test.
Quoted 1 line
this is not 'barely measurable' but 'BLOODY LARGE' overhead.
Yes. Fortunately there's a noticable difference between native and unpatched paravirt, because it shows all the effort we put into patching is worthwhile.
Quoted 8 lines
Quoted 5 lines
paravirt, patching
ezr Linux 2.6.21- 1000 0.25 0.53 31.8 34.4 10.1 1.04 5.44 730. 1583 4600 ezr Linux 2.6.21- 1000 0.26 0.55 32.1 35.2 13.3 1.03 5.48 748. 1589 4606 ezr Linux 2.6.21- 1000 0.26 0.54 32.0 34.9 14.1 1.04 5.43 752. 1606 4647i guess this pretty much makes the case for patching ...
Right, that's why there's patching.
J
Re: [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries
Quoted 17 lines · Jeremy Fitzhardinge
Quoted 9 lines · Ingo Molnar
the main metric we are interested in is the overhead for people who just want to run the non-patched native kernel that has
CONFIG_PARAVIRTenabled (99%+ of the users at the moment), so the delta is:null: +12.0%
null IO: +7.5%
stat: within noise
open/close: within noise
TCP: ~5.0%Quoted 6 lines
signal install: 2.0% signal handle: 4.7% fork: 2.7% exec: 3.6% shell: 3.6%
Quoted 3 lines
Hm, I don't think you can get this much precision out of these numbers. I noticed larger variations from boot-to-boot running the same test.
sure. i simply took the middle numbers. But there's definitely a 'few percents' trend in the numbers.
Quoted 5 lines
Quoted 1 line
this is not 'barely measurable' but 'BLOODY LARGE' overhead.
Yes. Fortunately there's a noticable difference between native and unpatched paravirt, because it shows all the effort we put into patching is worthwhile.
if only it were not such an ugly piece of code? ;)
Ingo
Re: [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries
Quoted 3 lines · Ingo Molnar
sure. i simply took the middle numbers. But there's definitely a 'few percents' trend in the numbers.
Yep. I guess the thing that coloured my summary is that I found overhead of unpatched paravirt_ops calls surprisingly small. I was really expecting more like ~10% hit, but only null syscall approached that.
Quoted 9 lines
Quoted 6 lines
Quoted 2 lines
this is not 'barely measurable' but 'BLOODY LARGE' overhead.
Yes. Fortunately there's a noticable difference between native and unpatched paravirt, because it shows all the effort we put into patching is worthwhile.
if only it were not such an ugly piece of code? ;)
Sigh, yes. I've cleaned it up a bit since the last post, and commented it, but I couldn't do much with its essential ugliness. But I guess it just got promoted from "ugly" to "ugly but necessary".
J
Re: [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries
In reply to Jeremy FitzhardingeQuoted 8 lines · On Wednesday 04 April 2007 11:25:57 Jeremy Fitzhardinge wrote:
Quoted 2 lines · Andi Kleen
What do the benchmarks say with
CONFIG_PARAVIRTon native hardware compared to !CONFIG_PARAVIRT. e.g. does lmbench suffer?Barely. There's a slight hit for not using patching, and patching is almost identical to native performance. The most noticeable difference is in the null syscall microbenchmark, but once you get to complex things the difference is in the noise.
Why is there a difference for null syscall? I had assumed we patched all the fast path cases relevant there. Do you have an idea where it comes from?
-Andi
Re: [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries
Quoted 2 lines · Andi Kleen
Why is there a difference for null syscall? I had assumed we patched all the fast path cases relevant there. Do you have an idea where it comes from?
Sure. There's indirect calls for things like sti/cli/iret. It goes back to native speed when you patch the real instructions inline.
J
Re: [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries
Quoted 6 lines · On Wednesday 04 April 2007 17:45:44 Jeremy Fitzhardinge wrote:
Quoted 2 lines · Andi Kleen
Why is there a difference for null syscall? I had assumed we patched all the fast path cases relevant there. Do you have an idea where it comes from?
Sure. There's indirect calls for things like sti/cli/iret. It goes back to native speed when you patch the real instructions inline.
I was talking about the patched case. It seemed to be a little slower too, but in theory it shouldn't have been, no?
-Andi
Re: [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries
Quoted 2 lines · Andi Kleen
I was talking about the patched case. It seemed to be a little slower too, but in theory it shouldn't have been, no?
Oh, the .25 vs .26? That's definitely noise; on other runs I see no difference:
paravirt, patched, busy machine
ezr Linux 2.6.21- 1000 0.25 0.52 31.3 34.4 12.7 1.02 5.44 743. 1578 4582 ezr Linux 2.6.21- 1000 0.25 0.53 31.4 34.2 12.1 1.02 5.27 736. 1594 4806 ezr Linux 2.6.21- 1000 0.25 0.52 31.5 34.4 14.3 1.02 5.49 736. 1587 4601
(I didn't use this result because I did it on a fairly busy machine, but the best-case numbers are actually somewhat better than the run on a completely quiet machine.)
J
Re: [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries
In reply to Andi KleenQuoted 10 lines · Andi Kleen
Quoted 6 lines · On Wednesday 04 April 2007 17:45:44 Jeremy Fitzhardinge wrote:
Quoted 2 lines · Andi Kleen
Why is there a difference for null syscall? I had assumed we patched all the fast path cases relevant there. Do you have an idea where it comes from?
Sure. There's indirect calls for things like sti/cli/iret. It goes back to native speed when you patch the real instructions inline.
I was talking about the patched case. It seemed to be a little slower too, but in theory it shouldn't have been, no?
You'll still have the damage inflicted on gcc's optimizer, though.
Rusty.
Re: [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries
Quoted 1 line · Rusty Russell
You'll still have the damage inflicted on gcc's optimizer, though.
Well, I could remove the clobbers for PVOP_CALL[0-2] and add the appropriate push/pops, and put similar push/pop wrappers around all the called functions. But it doesn't make it any prettier.
J
PATCH 5/17 Hooks to set up initial pagetable
In reply to Jeremy FitzhardingeThis patch introduces paravirt_ops hooks to control how the kernel's initial pagetable is set up.
In the case of a native boot, the very early bootstrap code creates a simple non-PAE pagetable to map the kernel and physical memory. When the VM subsystem is initialized, it creates a proper pagetable which respects the PAE mode, large pages, etc.
When booting under a hypervisor, there are many possibilities for what paging environment the hypervisor establishes for the guest kernel, so the constructon of the kernel's pagetable depends on the hypervisor.
In the case of Xen, the hypervisor boots the kernel with a fully constructed pagetable, which is already using PAE if necessary. Also, Xen requires particular care when constructing pagetables to make sure all pagetables are always mapped read-only.
In order to make this easier, kernel's initial pagetable construction has been changed to only allocate and initialize a pagetable page if there's no page already present in the pagetable. This allows the Xen paravirt backend to make a copy of the hypervisor-provided pagetable, allowing the kernel to establish any more mappings it needs while keeping the existing ones.
A slightly subtle point which is worth highlighting here is that Xen requires all kernel mappings to share the same pte_t pages between all pagetables, so that updating a kernel page's mapping in one pagetable is reflected in all other pagetables. This makes it possible to allocate a page and attach it to a pagetable without having to explicitly enumerate that page's mapping in all pagetables.
--- arch/i386/kernel/paravirt.c | 3 arch/i386/mm/init.c | 158 +++++++++++++++++++++++++++++-------------- include/asm-i386/paravirt.h | 17 ++++ include/asm-i386/pgtable.h | 16 ++++ 4 files changed, 142 insertions(+), 52 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +3 -0
@@ -476,6 +476,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 +107 -51
217 lines not on this page · raw message
===================================================================
include/asm-i386/paravirt.h +16 -1
@@ -2,10 +2,11 @@#define __ASM_PARAVIRT_H/* Various instructions on x86 need to be replaced for * para-virtualization: those hooks are defined here. */ + +#ifdef CONFIG_PARAVIRT #include <linux/stringify.h> #include <asm/page.h> -#ifdef CONFIG_PARAVIRT /* These are the most performance critical ops, so we want to be able to patch * callers */ #define PARAVIRT_IRQ_DISABLE 0
@@ -48,6 +49,9 @@ struct paravirt_opsvoid (*arch_setup)(void );char *(*memory_setup)(void );void (*init_IRQ)(void ); + +void (*pagetable_setup_start)(pgd_t *pgd_base); +void (*pagetable_setup_done)(pgd_t *pgd_base);void (*banner)(void );
@@ -369,6 +373,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
@@ -512,6 +512,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/17 Allocate a fixmap slot
In reply to Jeremy FitzhardingeAllocate a fixmap slot for use by a paravirt_ops implementation. This is intended for early-boot bootstrap mappings. Once the zones and allocator have been set up, it would be better to use get_vm_area() to allocate some virtual space.
Xen uses this to map the hypervisor's shared info page, which doesn't have a pseudo-physical page number, and therefore can't be mapped ordinarily. It is needed early because it contains the vcpu state, including the interrupt mask.
--- include/asm-i386/fixmap.h | 3 +++ 1 file changed, 3 insertions(+)
===================================================================
include/asm-i386/fixmap.h +3 -0
@@ -86,6 +86,9 @@ enum fixed_addresses {#ifdef CONFIG_PCI_MMCONFIG FIX_PCIE_MCFG,#endif +#ifdef CONFIG_PARAVIRT + FIX_PARAVIRT_BOOTMAP, +#endif __end_of_permanent_fixed_addresses,/* temporary boot-time mappings, used before ioremap() is functional */ #define NR_FIX_BTMAPS 16
PATCH 7/17 Allow paravirt backend to choose kernel PMD sharing
In reply to Jeremy FitzhardingeNormally when running in PAE mode, the 4th PMD maps the kernel address space, which can be shared among all processes (since they all need the same kernel mappings).
Xen, however, does not allow guests to have the kernel pmd shared between page tables, so parameterize pgtable.c to allow both modes of operation.
There are several side-effects of this. One is that vmalloc will update the kernel address space mappings, and those updates need to be propagated into all processes if the kernel mappings are not intrinsically shared. In the non-PAE case, this is done by maintaining a pgd_list of all processes; this list is used when all process pagetables must be updated. pgd_list is threaded via otherwise unused entries in the page structure for the pgd, which means that the pgd must be page-sized for this to work.
Normally the PAE pgd is only 4x64 byte entries large, but Xen requires the PAE pgd to page aligned anyway, so this patch forces the pgd to be page aligned+sized when the kernel pmd is unshared, to accomodate both these requirements.
Also, since there may be several distinct kernel pmds (if the user/kernel split is below 3G), there's no point in allocating them from a slab cache; they're just allocated with get_free_page and initialized appropriately. (Of course the could be cached if there is just a single kernel pmd - which is the default with a 3G user/kernel split - but it doesn't seem worthwhile to add yet another case into this code).
[ Many thanks to wli for review comments. ]
--- arch/i386/kernel/paravirt.c | 1 arch/i386/mm/fault.c | 6 +- arch/i386/mm/init.c | 18 +++++- arch/i386/mm/pageattr.c | 2 arch/i386/mm/pgtable.c | 84 ++++++++++++++++++++++++++------ include/asm-i386/paravirt.h | 1 include/asm-i386/pgtable-2level-defs.h | 2 include/asm-i386/pgtable-2level.h | 2 include/asm-i386/pgtable-3level-defs.h | 6 ++ include/asm-i386/pgtable-3level.h | 2 include/asm-i386/pgtable.h | 7 ++ 11 files changed, 105 insertions(+), 26 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +1 -0
@@ -604,6 +604,7 @@ struct paravirt_ops paravirt_ops = { .name = "bare hardware", .paravirt_enabled = 0, .kernel_rpl = 0, + .shared_kernel_pmd = 1, /* Only used when CONFIG_X86_PAE is set */ .patch = native_patch, .banner = default_banner,
===================================================================
arch/i386/mm/fault.c +3 -3
@@ -588,8 +588,7 @@ do_sigbus:force_sig_info_fault (SIGBUS ,BUS_ADRERR , address, tsk); } -#ifndef CONFIG_X86_PAE -void vmalloc_sync_all (void ) +void _vmalloc_sync_all (void ) {/* * Note that races in the updates of insync and start aren't
@@ -600,6 +599,8 @@ void vmalloc_sync_all(void) static DECLARE_BITMAP(insync, PTRS_PER_PGD);static unsigned long start =TASK_SIZE ;unsigned long address; + +BUG_ON (SHARED_KERNEL_PMD );BUILD_BUG_ON (TASK_SIZE & ~PGDIR_MASK );for (address = start; address >=TASK_SIZE ; address +=PGDIR_SIZE ) {
@@ -623,4 +624,3 @@ void vmalloc_sync_all(void) start = address +PGDIR_SIZE ; } } -#endif
===================================================================
arch/i386/mm/init.c +15 -3
@@ -715,6 +715,8 @@ struct kmem_cache *pmd_cache;void __init pgtable_cache_init (void ) { +size_t pgd_size =PTRS_PER_PGD *sizeof (pgd_t); +if (PTRS_PER_PMD >1 ) { pmd_cache = kmem_cache_create("pmd", PTRS_PER_PMD*sizeof(pmd_t),
@@ -724,13 +726,23 @@ void __init pgtable_cache_init(void) NULL);if (!pmd_cache)panic ("pgtable_cache_init(): cannot create pmd cache" ); + +if (!SHARED_KERNEL_PMD ) { +/* If we're in PAE mode and have a non-shared +kernel pmd, then the pgd size must be a +page size. This is because the pgd_list +links through the page structure, so there +can only be one pgd per page for this to +work. */ + pgd_size =PAGE_SIZE ; + } } pgd_cache =kmem_cache_create ("pgd" , -PTRS_PER_PGD *sizeof (pgd_t), -PTRS_PER_PGD *sizeof (pgd_t), + pgd_size, + pgd_size,0 , pgd_ctor, -PTRS_PER_PMD ==1 ? pgd_dtor :NULL ); + (!SHARED_KERNEL_PMD ) ? pgd_dtor :NULL );if (!pgd_cache)panic ("pgtable_cache_init(): Cannot create pgd cache" ); }
===================================================================
arch/i386/mm/pageattr.c +1 -1
@@ -91,7 +91,7 @@ static void set_pmd_pte(pte_t *kpte, unsunsigned long flags;set_pte_atomic (kpte, pte);/* change init_mm */ -if (PTRS_PER_PMD >1 ) +if (SHARED_KERNEL_PMD )return ;spin_lock_irqsave (&pgd_lock, flags);
===================================================================
arch/i386/mm/pgtable.c +69 -15
138 lines not on this page · raw message
===================================================================
include/asm-i386/paravirt.h +1 -0
@@ -34,6 +34,7 @@ struct paravirt_opsstruct paravirt_ops {unsigned int kernel_rpl; +int shared_kernel_pmd;int paravirt_enabled;const char *name;
===================================================================
include/asm-i386/pgtable-2level-defs.h +2 -0
@@ -1,5 +1,7 @@#ifndef _I386_PGTABLE_2LEVEL_DEFS_H#define _I386_PGTABLE_2LEVEL_DEFS_H + +#define SHARED_KERNEL_PMD 0/* * traditional i386 two-level paging structure:
===================================================================
include/asm-i386/pgtable-2level.h +0 -2
@@ -65,6 +65,4 @@ static inline int pte_exec_kernel(pte_t#define __pte_to_swp_entry (pte) ((swp_entry_t) { (pte).pte_low })#define __swp_entry_to_pte (x) ((pte_t) { (x).val }) -void vmalloc_sync_all (void ); -#endif /* _I386_PGTABLE_2LEVEL_H */
===================================================================
include/asm-i386/pgtable-3level-defs.h +6 -0
@@ -1,5 +1,11 @@#ifndef _I386_PGTABLE_3LEVEL_DEFS_H#define _I386_PGTABLE_3LEVEL_DEFS_H + +#ifdef CONFIG_PARAVIRT +#define SHARED_KERNEL_PMD (paravirt_ops.shared_kernel_pmd) +#else +#define SHARED_KERNEL_PMD 1 +#endif /* * PGDIR_SHIFT determines what a top-level page table entry can map
===================================================================
include/asm-i386/pgtable-3level.h +0 -2
@@ -180,6 +180,4 @@ static inline pmd_t pfn_pmd(unsigned lon#define __pmd_free_tlb (tlb, x) do { } while (0) -#define vmalloc_sync_all () ((void)0) -#endif /* _I386_PGTABLE_3LEVEL_H */
===================================================================
include/asm-i386/pgtable.h +7 -0
@@ -244,6 +244,13 @@ static inline pte_t pte_mkwrite(pte_t ptstatic inline pte_t pte_mkwrite (pte_t pte) { (pte).pte_low |= _PAGE_RW;return pte; }static inline pte_t pte_mkhuge (pte_t pte) { (pte).pte_low |= _PAGE_PSE;return pte; } +extern void _vmalloc_sync_all (void ); +static inline void vmalloc_sync_all (void ) +{ +if (!SHARED_KERNEL_PMD ) +_vmalloc_sync_all (); +} +#ifdef CONFIG_X86_PAE # include <asm/pgtable-3level.h> #else
PATCH 8/17 add hooks to intercept mm creation and destruction
In reply to Jeremy FitzhardingeAdd hooks to allow a paravirt implementation to track the lifetime of an mm. 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
@@ -520,6 +520,10 @@ struct paravirt_ops paravirt_ops = { .irq_enable_sysexit = native_irq_enable_sysexit, .iret = native_iret, + .dup_mmap = paravirt_nop, + .exit_mmap = paravirt_nop, + .activate_mm = paravirt_nop, + .startup_ipi_hook = paravirt_nop, };
===================================================================
include/asm-alpha/mmu_context.h +1 -0
@@ -10,6 +10,7 @@#include <asm/system.h> #include <asm/machvec.h> #include <asm/compiler.h> +#include <asm-generic/mm_hooks.h> /* * Force a context reload. This is needed when we change the page
===================================================================
include/asm-arm/mmu_context.h +1 -0
@@ -16,6 +16,7 @@#include <linux/compiler.h> #include <asm/cacheflush.h> #include <asm/proc-fns.h> +#include <asm-generic/mm_hooks.h> void __check_kvm_seq (struct mm_struct *mm);
===================================================================
include/asm-arm26/mmu_context.h +2 -0
@@ -12,6 +12,8 @@*/ #ifndef __ASM_ARM_MMU_CONTEXT_H#define __ASM_ARM_MMU_CONTEXT_H + +#include <asm-generic/mm_hooks.h> #define init_new_context (tsk,mm) 0#define destroy_context (mm) do { } while(0)
===================================================================
include/asm-avr32/mmu_context.h +1 -0
@@ -15,6 +15,7 @@#include <asm/tlbflush.h> #include <asm/pgalloc.h> #include <asm/sysreg.h> +#include <asm-generic/mm_hooks.h> /* * The MMU "context" consists of two things:
===================================================================
include/asm-cris/mmu_context.h +2 -0
@@ -1,5 +1,7 @@#ifndef __CRIS_MMU_CONTEXT_H#define __CRIS_MMU_CONTEXT_H + +#include <asm-generic/mm_hooks.h> extern int init_new_context (struct task_struct *tsk,struct mm_struct *mm);extern void get_mmu_context (struct mm_struct *mm);
===================================================================
include/asm-frv/mmu_context.h +1 -0
@@ -15,6 +15,7 @@#include <asm/setup.h> #include <asm/page.h> #include <asm/pgalloc.h> +#include <asm-generic/mm_hooks.h> static inline void enter_lazy_tlb (struct mm_struct *mm,struct task_struct *tsk) {
===================================================================
include/asm-generic/mm_hooks.h new file +18 -0
@@ -0,0 +1,18 @@ +/* +* Define generic no-op hooks for arch_dup_mmap and arch_exit_mmap, to +* be included in asm-FOO/mmu_context.h for any arch FOO which doesn't +* need to hook these. +*/ +#ifndef _ASM_GENERIC_MM_HOOKS_H +#define _ASM_GENERIC_MM_HOOKS_H + +static inline void arch_dup_mmap (struct mm_struct *oldmm, +struct mm_struct *mm) +{ +} + +static inline void arch_exit_mmap (struct mm_struct *mm) +{ +} + +#endif /* _ASM_GENERIC_MM_HOOKS_H */
===================================================================
include/asm-h8300/mmu_context.h +1 -0
@@ -4,6 +4,7 @@#include <asm/setup.h> #include <asm/page.h> #include <asm/pgalloc.h> +#include <asm-generic/mm_hooks.h> static inline void enter_lazy_tlb (struct mm_struct *mm,struct task_struct *tsk) {
===================================================================
include/asm-i386/mmu_context.h +15 -2
@@ -5,6 +5,16 @@#include <asm/atomic.h> #include <asm/pgalloc.h> #include <asm/tlbflush.h> +#include <asm/paravirt.h> +#ifndef CONFIG_PARAVIRT +#include <asm-generic/mm_hooks.h> + +static inline void paravirt_activate_mm (struct mm_struct *prev, +struct mm_struct *next) +{ +} +#endif /* !CONFIG_PARAVIRT */ +/* * Used for LDT copy/destruction.
@@ -65,7 +75,10 @@ static inline void switch_mm(struct mm_s#define deactivate_mm (tsk, mm) \ asm("movl %0,%%gs": :"r" (0)); -#define activate_mm (prev, next) \ - switch_mm((prev),(next),NULL) +#define activate_mm (prev, next) \ + do { \ + paravirt_activate_mm(prev, next); \ + switch_mm((prev),(next),NULL); \ + } while(0);#endif
===================================================================
include/asm-i386/paravirt.h +23 -0
@@ -119,6 +119,12 @@ struct paravirt_opsvoid (*io_delay)(void ); +void (*activate_mm)(struct mm_struct *prev, +struct mm_struct *next); +void (*dup_mmap)(struct mm_struct *oldmm, +struct mm_struct *mm); +void (*exit_mmap)(struct mm_struct *mm); +#ifdef CONFIG_X86_LOCAL_APIC void (*apic_write)(unsigned long reg,unsigned long v);void (*apic_write_atomic)(unsigned long reg,unsigned long v);
@@ -394,6 +400,23 @@ static inline void startup_ipi_hook(int }#endif +static inline void paravirt_activate_mm (struct mm_struct *prev, +struct mm_struct *next) +{ + paravirt_ops.activate_mm (prev, next); +} + +static inline void arch_dup_mmap (struct mm_struct *oldmm, +struct mm_struct *mm) +{ + paravirt_ops.dup_mmap (oldmm, mm); +} + +static inline void arch_exit_mmap (struct mm_struct *mm) +{ + paravirt_ops.exit_mmap (mm); +} +#define __flush_tlb () paravirt_ops.flush_tlb_user()#define __flush_tlb_global () paravirt_ops.flush_tlb_kernel()#define __flush_tlb_single (addr) paravirt_ops.flush_tlb_single(addr)
===================================================================
include/asm-ia64/mmu_context.h +1 -0
@@ -29,6 +29,7 @@#include <linux/spinlock.h> #include <asm/processor.h> +#include <asm-generic/mm_hooks.h> struct ia64_ctx {spinlock_t lock;
===================================================================
include/asm-m32r/mmu_context.h +1 -0
@@ -15,6 +15,7 @@#include <asm/pgalloc.h> #include <asm/mmu.h> #include <asm/tlbflush.h> +#include <asm-generic/mm_hooks.h> /* * Cache of MMU context last used.
===================================================================
include/asm-m68k/mmu_context.h +1 -0
@@ -1,6 +1,7 @@#ifndef __M68K_MMU_CONTEXT_H#define __M68K_MMU_CONTEXT_H +#include <asm-generic/mm_hooks.h> static inline void enter_lazy_tlb (struct mm_struct *mm,struct task_struct *tsk) {
===================================================================
include/asm-m68knommu/mmu_context.h +1 -0
@@ -4,6 +4,7 @@#include <asm/setup.h> #include <asm/page.h> #include <asm/pgalloc.h> +#include <asm-generic/mm_hooks.h> static inline void enter_lazy_tlb (struct mm_struct *mm,struct task_struct *tsk) {
===================================================================
include/asm-mips/mmu_context.h +1 -0
@@ -20,6 +20,7 @@#include <asm/mipsmtregs.h> #include <asm/smtc.h> #endif /* SMTC */ +#include <asm-generic/mm_hooks.h> /* * For the fast tlb miss handlers, we keep a per cpu array of pointers
===================================================================
include/asm-parisc/mmu_context.h +1 -0
@@ -5,6 +5,7 @@#include <asm/atomic.h> #include <asm/pgalloc.h> #include <asm/pgtable.h> +#include <asm-generic/mm_hooks.h> static inline void enter_lazy_tlb (struct mm_struct *mm,struct task_struct *tsk) {
===================================================================
include/asm-powerpc/mmu_context.h +1 -0
@@ -10,6 +10,7 @@#include <linux/mm.h> #include <asm/mmu.h> #include <asm/cputable.h> +#include <asm-generic/mm_hooks.h> /* * Copyright (C) 2001 PPC 64 Team, IBM Corp
===================================================================
include/asm-ppc/mmu_context.h +1 -0
@@ -6,6 +6,7 @@#include <asm/bitops.h> #include <asm/mmu.h> #include <asm/cputable.h> +#include <asm-generic/mm_hooks.h> /* * On 32-bit PowerPC 6xx/7xx/7xxx CPUs, we use a set of 16 VSIDs
===================================================================
include/asm-s390/mmu_context.h +2 -0
@@ -10,6 +10,8 @@#define __S390_MMU_CONTEXT_H#include <asm/pgalloc.h> +#include <asm-generic/mm_hooks.h> +/* * get a new mmu context.. S390 don't know about contexts. */
===================================================================
include/asm-sh/mmu_context.h +1 -0
@@ -12,6 +12,7 @@#include <asm/tlbflush.h> #include <asm/uaccess.h> #include <asm/io.h> +#include <asm-generic/mm_hooks.h> /* * The MMU "context" consists of two things:
===================================================================
include/asm-sh64/mmu_context.h +1 -1
@@ -27,7 +27,7 @@ extern unsigned long mmu_context_cache;extern unsigned long mmu_context_cache;#include <asm/page.h> - +#include <asm-generic/mm_hooks.h> /* Current mm's pgd */ extern pgd_t *mmu_pdtp_cache;
===================================================================
include/asm-sparc/mmu_context.h +2 -0
@@ -4,6 +4,8 @@#include <asm/btfixup.h> #ifndef __ASSEMBLY__ + +#include <asm-generic/mm_hooks.h> static inline void enter_lazy_tlb (struct mm_struct *mm,struct task_struct *tsk) {
===================================================================
include/asm-sparc64/mmu_context.h +1 -0
@@ -9,6 +9,7 @@#include <linux/spinlock.h> #include <asm/system.h> #include <asm/spitfire.h> +#include <asm-generic/mm_hooks.h> static inline void enter_lazy_tlb (struct mm_struct *mm,struct task_struct *tsk) {
===================================================================
include/asm-um/mmu_context.h +2 -0
@@ -5,6 +5,8 @@#ifndef __UM_MMU_CONTEXT_H#define __UM_MMU_CONTEXT_H + +#include <asm-generic/mm_hooks.h> #include "linux/sched.h" #include "choose-mode.h"
===================================================================
include/asm-v850/mmu_context.h +2 -0
@@ -1,5 +1,7 @@#ifndef __V850_MMU_CONTEXT_H__#define __V850_MMU_CONTEXT_H__ + +#include <asm-generic/mm_hooks.h> #define destroy_context (mm) ((void)0)#define init_new_context (tsk,mm) 0
===================================================================
include/asm-x86_64/mmu_context.h +1 -0
@@ -7,6 +7,7 @@#include <asm/pda.h> #include <asm/pgtable.h> #include <asm/tlbflush.h> +#include <asm-generic/mm_hooks.h> /* * possibly do the LDT unload here?
===================================================================
include/asm-xtensa/mmu_context.h +1 -0
@@ -18,6 +18,7 @@#include <asm/pgtable.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> +#include <asm-generic/mm_hooks.h> #define XCHAL_MMU_ASID_BITS 8
===================================================================
kernel/fork.c +2 -0
@@ -286,6 +286,8 @@ static inline int dup_mmap(struct mm_strif (retval) goto out; } +/* a new mm has just been created */ +arch_dup_mmap (oldmm, mm); retval =0 ; out:up_write (&mm->mmap_sem);
===================================================================
mm/mmap.c +4 -0
@@ -29,6 +29,7 @@#include <asm/uaccess.h> #include <asm/cacheflush.h> #include <asm/tlb.h> +#include <asm/mmu_context.h> #ifndef arch_mmap_check#define arch_mmap_check (addr, len, flags) (0)
@@ -1976,6 +1977,9 @@ void exit_mmap(struct mm_struct *mm)struct vm_area_struct *vma = mm->mmap;unsigned long nr_accounted =0 ;unsigned long end; + +/* mm's last user has gone, and its about to be pulled down */ +arch_exit_mmap (mm);lru_add_drain ();flush_cache_mm (mm);
PATCH 9/17 rename struct paravirt_patch to paravirt_patch_site for clarity
In reply to Jeremy FitzhardingeRename struct paravirt_patch to paravirt_patch_site, so that it clearly refers to a callsite, and not the patch which may be applied to that callsite.
--- arch/i386/kernel/alternative.c | 9 ++++----- arch/i386/kernel/vmi.c | 4 ---- include/asm-i386/alternative.h | 12 +++++++----- include/asm-i386/paravirt.h | 5 ++++- 4 files changed, 15 insertions(+), 15 deletions(-)
===================================================================
arch/i386/kernel/alternative.c +4 -5
@@ -359,9 +359,10 @@ void alternatives_smp_switch(int smp)#endif #ifdef CONFIG_PARAVIRT -void apply_paravirt (struct paravirt_patch *start,struct paravirt_patch *end) -{ -struct paravirt_patch *p; +void apply_paravirt (struct paravirt_patch_site *start, +struct paravirt_patch_site *end) +{ +struct paravirt_patch_site *p;for (p = start; p < end; p++) {unsigned int used;
@@ -388,8 +389,6 @@ void apply_paravirt(struct paravirt_patc/* Sync to be conservative, in case we patched following instructions */ sync_core(); } -extern struct paravirt_patch __start_parainstructions[], - __stop_parainstructions[];#endif /* CONFIG_PARAVIRT */ void __init alternative_instructions (void )
===================================================================
arch/i386/kernel/vmi.c +0 -4
@@ -70,10 +70,6 @@ static struct {void (*set_initial_ap_state)(int ,int );void (*halt)(void ); } vmi_ops; - -/* XXX move this to alternative.h */ -extern struct paravirt_patch __start_parainstructions[], - __stop_parainstructions[];/* * VMI patching routines.
===================================================================
include/asm-i386/alternative.h +7 -5
@@ -154,13 +154,15 @@ 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) -{} +static inline void apply_paravirt (struct paravirt_patch_site *start, +struct paravirt_patch_site *end) +{ +}#define __start_parainstructions NULL#define __stop_parainstructions NULL#endif
===================================================================
include/asm-i386/paravirt.h +4 -1
@@ -502,12 +502,15 @@ void _paravirt_nop(void);#define paravirt_nop ((void *)_paravirt_nop)/* These all sit in the .parainstructions section to tell us what to patch. */ -struct paravirt_patch { +struct paravirt_patch_site {u8 *instr;/* original instructions */ u8 instrtype;/* type of this instruction */ u8 len;/* length of original instruction */ u16 clobbers;/* what registers you may clobber */ }; + +extern struct paravirt_patch_site __start_parainstructions[], + __stop_parainstructions[];#define paravirt_alt (insn_string, typenum, clobber) \ "771:\n\t" insn_string "\n" "772:\n" \
PATCH 10/17 Use patch site IDs computed from offset in paravirt_ops structure
In reply to Jeremy FitzhardingeUse patch type identifiers derived from the offset of the operation in the paravirt_ops structure. This avoids having to maintain a separate enum for patch site types.
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(sti, "sti");DEF_NATIVE (sti,"sti" );DEF_NATIVE (popf,"push %eax; popf" );DEF_NATIVE (pushf,"pushf; pop %eax" ); -DEF_NATIVE (pushf_cli,"pushf; pop %eax; cli" );DEF_NATIVE (iret,"iret" );DEF_NATIVE (sti_sysexit,"sti; sysexit" );
@@ -66,13 +65,12 @@ static const struct native_insns {const char *start, *end; } native_insns[] = { - [PARAVIRT_IRQ_DISABLE] = { start_cli, end_cli }, - [PARAVIRT_IRQ_ENABLE] = { start_sti, end_sti }, - [PARAVIRT_RESTORE_FLAGS] = { start_popf, end_popf }, - [PARAVIRT_SAVE_FLAGS] = { start_pushf, end_pushf }, - [PARAVIRT_SAVE_FLAGS_IRQ_DISABLE] = { start_pushf_cli, end_pushf_cli }, - [PARAVIRT_INTERRUPT_RETURN] = { start_iret, end_iret }, - [PARAVIRT_STI_SYSEXIT] = { start_sti_sysexit, end_sti_sysexit }, + [PARAVIRT_PATCH(irq_disable)] = { start_cli, end_cli }, + [PARAVIRT_PATCH(irq_enable)] = { start_sti, end_sti }, + [PARAVIRT_PATCH(restore_fl)] = { start_popf, end_popf }, + [PARAVIRT_PATCH(save_fl)] = { start_pushf, end_pushf }, + [PARAVIRT_PATCH(iret)] = { start_iret, end_iret }, + [PARAVIRT_PATCH(irq_enable_sysexit)] = { start_sti_sysexit, end_sti_sysexit }, };static unsigned native_patch (u8 type,u16 clobbers,void *insns,unsigned len)
===================================================================
arch/i386/kernel/vmi.c +6 -33
@@ -78,11 +78,6 @@ static struct {#define MNEM_JMP 0xe9#define MNEM_RET 0xc3 -static char irq_save_disable_callout[] = { -MNEM_CALL ,0 ,0 ,0 ,0 , -MNEM_CALL ,0 ,0 ,0 ,0 , -MNEM_RET -};#define IRQ_PATCH_INT_MASK 0#define IRQ_PATCH_DISABLE 5
@@ -130,33 +125,17 @@ static unsigned vmi_patch(u8 type, u16 cstatic unsigned vmi_patch (u8 type,u16 clobbers,void *insns,unsigned len) {switch (type) { -case PARAVIRT_IRQ_DISABLE : +case PARAVIRT_PATCH (irq_disable):return patch_internal (VMI_CALL_DisableInterrupts, len, insns); -case PARAVIRT_IRQ_ENABLE : +case PARAVIRT_PATCH (irq_enable):return patch_internal (VMI_CALL_EnableInterrupts, len, insns); -case PARAVIRT_RESTORE_FLAGS : +case PARAVIRT_PATCH (restore_fl):return patch_internal (VMI_CALL_SetInterruptMask, len, insns); -case PARAVIRT_SAVE_FLAGS : +case PARAVIRT_PATCH (save_fl):return patch_internal (VMI_CALL_GetInterruptMask, len, insns); -case PARAVIRT_SAVE_FLAGS_IRQ_DISABLE : -if (len >=10 ) { -patch_internal (VMI_CALL_GetInterruptMask, len, insns); -patch_internal (VMI_CALL_DisableInterrupts, len-5 , insns+5 ); -return 10 ; - }else { -/* -* You bastards didn't leave enough room to -* patch save_flags_irq_disable inline. Patch -* to a helper -*/ -BUG_ON (len <5 ); - *(char *)insns =MNEM_CALL ; -patch_offset (insns, irq_save_disable_callout); -return 5 ; - } -case PARAVIRT_INTERRUPT_RETURN : +case PARAVIRT_PATCH (iret):return patch_internal (VMI_CALL_IRET , len, insns); -case PARAVIRT_STI_SYSEXIT : +case PARAVIRT_PATCH (irq_enable_sysexit):return patch_internal (VMI_CALL_SYSEXIT , len, insns);default :break ;
@@ -763,12 +742,6 @@ static inline int __init activate_vmi(vopara_fill (restore_fl, SetInterruptMask);para_fill (irq_disable, DisableInterrupts);para_fill (irq_enable, EnableInterrupts); - -/* irq_save_disable !!! sheer pain */ -patch_offset (&irq_save_disable_callout[IRQ_PATCH_INT_MASK ], - (char *)paravirt_ops.save_fl); -patch_offset (&irq_save_disable_callout[IRQ_PATCH_DISABLE ], - (char *)paravirt_ops.irq_disable);para_fill (wbinvd,WBINVD );para_fill (read_tsc,RDTSC );
===================================================================
include/asm-i386/paravirt.h +93 -86
235 lines not on this page · raw message
PATCH 11/17 Fix patch site clobbers to include return register
In reply to Jeremy FitzhardingeFix a few clobbers to include the return register. The clobbers set is the set of all registers modified (or may be modified) by the code snippet, regardless of whether it was deliberate or accidental.
--- arch/i386/kernel/entry.S | 2 +- include/asm-i386/paravirt.h | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-)
===================================================================
arch/i386/kernel/entry.S +1 -1
@@ -342,7 +342,7 @@ 1: movl (%ebp),%ebp 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 +5 -5
@@ -532,7 +532,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; }
@@ -622,15 +622,15 @@ 772:; \#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, \
PATCH 12/17 Consistently wrap paravirt ops callsites to make them patchable
In reply to Jeremy FitzhardingeWrap a set of interesting paravirt_ops calls in a wrapper which makes the callsites available for patching. Unfortunately this is pretty ugly because there's no way to get gcc to generate a function call, but also wrap just the callsite itself with the necessary labels.
This patch supports functions with 0-4 arguments, and either void or returning a value. 64-bit arguments must be split into a pair of 32-bit arguments (lower word first). Small structures are returned in registers.
--- include/asm-i386/paravirt.h | 715 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 569 insertions(+), 146 deletions(-)
===================================================================
include/asm-i386/paravirt.h +569 -146
@@ -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 __cpuid(unsigned intstatic inline void __cpuid (unsigned int *eax,unsigned int *ebx,unsigned int *ecx,unsigned int *edx) { - paravirt_ops.cpuid (eax, ebx, ecx, edx); +PVOP_VCALL4 (cpuid, eax, ebx, ecx, edx); }/* * These special macros can be used to get or set a debugging register */ -#define get_debugreg (var, reg) var = paravirt_ops.get_debugreg(reg) -#define set_debugreg (val, reg) paravirt_ops.set_debugreg(reg, val) - -#define clts () paravirt_ops.clts() - -#define read_cr0 () paravirt_ops.read_cr0() -#define write_cr0 (x) paravirt_ops.write_cr0(x) - -#define read_cr2 () paravirt_ops.read_cr2() -#define write_cr2 (x) paravirt_ops.write_cr2(x) - -#define read_cr3 () paravirt_ops.read_cr3() -#define write_cr3 (x) paravirt_ops.write_cr3(x) - -#define read_cr4 () paravirt_ops.read_cr4() -#define read_cr4_safe (x) paravirt_ops.read_cr4_safe() -#define write_cr4 (x) paravirt_ops.write_cr4(x) - -#define raw_ptep_get_and_clear (xp) (paravirt_ops.ptep_get_and_clear(xp)) +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); +} + +static inline void clts (void ) +{ +PVOP_VCALL0 (clts); +} + +static inline unsigned long read_cr0 (void ) +{ + return PVOP_CALL0(unsigned long, read_cr0); +} + +static inline void write_cr0 (unsigned long x) +{ +PVOP_VCALL1 (write_cr0, x); +} + +static inline unsigned long read_cr2 (void ) +{ + return PVOP_CALL0(unsigned long, read_cr2); +} + +static inline void write_cr2 (unsigned long x) +{ +PVOP_VCALL1 (write_cr2, x); +} + +static inline unsigned long read_cr3 (void ) +{ + return PVOP_CALL0(unsigned long, read_cr3); +} + +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 (); -} -#define wbinvd () paravirt_ops.wbinvd() +PVOP_VCALL0 (safe_halt); +} + +static inline void wbinvd (void ) +{ +PVOP_VCALL0 (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; \ +static inline unsigned long long paravirt_read_pmc (int counter) +{ +return PVOP_CALL1 (u64, read_pmc, counter); +} + +#define rdpmc (counter,low,high) do { \ + u64 _l = paravirt_read_pmc(counter); \ + low = (u32)_l; \ + high = _l >> 32; \ } while(0) -#define load_TR_desc () (paravirt_ops.load_tr_desc()) -#define load_gdt (dtr) (paravirt_ops.load_gdt(dtr)) -#define load_idt (dtr) (paravirt_ops.load_idt(dtr)) -#define set_ldt (addr, entries) (paravirt_ops.set_ldt((addr), (entries))) -#define store_gdt (dtr) (paravirt_ops.store_gdt(dtr)) -#define store_idt (dtr) (paravirt_ops.store_idt(dtr)) -#define store_tr (tr) ((tr) = paravirt_ops.store_tr()) -#define load_TLS (t,cpu) (paravirt_ops.load_tls((t),(cpu))) -#define write_ldt_entry (dt, entry, low, high) \ - (paravirt_ops.write_ldt_entry((dt), (entry), (low), (high))) -#define write_gdt_entry (dt, entry, low, high) \ - (paravirt_ops.write_gdt_entry((dt), (entry), (low), (high))) -#define write_idt_entry (dt, entry, low, high) \ - (paravirt_ops.write_idt_entry((dt), (entry), (low), (high))) -#define set_iopl_mask (mask) (paravirt_ops.set_iopl_mask(mask)) - -#define __pte (x) paravirt_ops.make_pte(x) -#define __pgd (x) paravirt_ops.make_pgd(x) - -#define pte_val (x) paravirt_ops.pte_val(x) -#define pgd_val (x) paravirt_ops.pgd_val(x) - -#ifdef CONFIG_X86_PAE -#define __pmd (x) paravirt_ops.make_pmd(x) -#define pmd_val (x) paravirt_ops.pmd_val(x) -#endif +static inline void load_TR_desc (void ) +{ +PVOP_VCALL0 (load_tr_desc); +} +static inline void load_gdt (const struct Xgt_desc_struct *dtr) +{ +PVOP_VCALL1 (load_gdt, dtr); +} +static inline void load_idt (const struct Xgt_desc_struct *dtr) +{ +PVOP_VCALL1 (load_idt, dtr); +} +static inline void set_ldt (const void *addr,unsigned entries) +{ +PVOP_VCALL2 (set_ldt, addr, entries); +} +static inline void store_gdt (struct Xgt_desc_struct *dtr) +{ +PVOP_VCALL1 (store_gdt, dtr); +} +static inline void store_idt (struct Xgt_desc_struct *dtr) +{ +PVOP_VCALL1 (store_idt, dtr); +} +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 startup_ipi_hook(intstatic inline void startup_ipi_hook (int phys_apicid,unsigned long start_eip,unsigned long start_esp) { -return paravirt_ops.startup_ipi_hook (phys_apicid, start_eip, start_esp); +PVOP_VCALL3 (startup_ipi_hook, phys_apicid, start_eip, start_esp); }#endif static inline void paravirt_activate_mm (struct mm_struct *prev,struct mm_struct *next) { - paravirt_ops.activate_mm (prev, next); +PVOP_VCALL2 (activate_mm, prev, next); }static inline void arch_dup_mmap (struct mm_struct *oldmm,struct mm_struct *mm) { - paravirt_ops.dup_mmap (oldmm, mm); +PVOP_VCALL2 (dup_mmap, oldmm, mm); }static inline void arch_exit_mmap (struct mm_struct *mm) { - paravirt_ops.exit_mmap (mm); -} - -#define __flush_tlb () paravirt_ops.flush_tlb_user() -#define __flush_tlb_global () paravirt_ops.flush_tlb_kernel() -#define __flush_tlb_single (addr) paravirt_ops.flush_tlb_single(addr) - -#define paravirt_map_pt_hook (type, va, pfn) paravirt_ops.map_pt_hook(type, va, pfn) - -#define paravirt_alloc_pt (pfn) paravirt_ops.alloc_pt(pfn) -#define paravirt_release_pt (pfn) paravirt_ops.release_pt(pfn) - -#define paravirt_alloc_pd (pfn) paravirt_ops.alloc_pd(pfn) -#define paravirt_alloc_pd_clone (pfn, clonepfn, start, count) \ - paravirt_ops.alloc_pd_clone(pfn, clonepfn, start, count) -#define paravirt_release_pd (pfn) paravirt_ops.release_pd(pfn) +PVOP_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); +} + +static inline void paravirt_alloc_pd (unsigned pfn) +{ +PVOP_VCALL1 (alloc_pd, 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); +} + +static inline void pte_update (struct mm_struct *mm,unsigned long addr, +pte_t *ptep) +{ +PVOP_VCALL3 (pte_update, mm, addr, ptep); +} + +static inline void pte_update_defer (struct mm_struct *mm,unsigned long addr, +pte_t *ptep) +{ +PVOP_VCALL3 (pte_update_defer, mm, addr, ptep); +} + +#ifdef CONFIG_X86_PAE +static inline pte_t __pte (unsigned long long val) +{ + unsigned long long ret = PVOP_CALL2(unsigned long long, make_pte, + val, val >>32 ); +return (pte_t ) { ret, ret >>32 }; +} + +static inline pmd_t __pmd (unsigned long long val) +{ + return (pmd_t) { PVOP_CALL2(unsigned long long, make_pmd, val, val >> 32) }; +} + +static inline pgd_t __pgd (unsigned long long val) +{ + return (pgd_t) { PVOP_CALL2(unsigned long long, make_pgd, val, val >> 32) }; +} + +static inline unsigned long long pte_val (pte_t x) +{ + return PVOP_CALL2(unsigned long long, pte_val, x.pte_low, x.pte_high); +} + +static inline unsigned long long pmd_val (pmd_t x) +{ + return PVOP_CALL2(unsigned long long, pmd_val, x.pmd, x.pmd >> 32); +} + +static inline unsigned long long pgd_val (pgd_t x) +{ + return PVOP_CALL2(unsigned long long, pgd_val, x.pgd, x.pgd >> 32); +}static inline void set_pte (pte_t *ptep,pte_t pteval) { - paravirt_ops.set_pte (ptep, pteval); +PVOP_VCALL3 (set_pte, ptep, pteval.pte_low, pteval.pte_high); }static inline void set_pte_at (struct mm_struct *mm,unsigned long addr,pte_t *ptep,pte_t pteval) { +/* 5 arg words */ paravirt_ops.set_pte_at (mm, addr, ptep, pteval); } +static inline void set_pte_atomic (pte_t *ptep,pte_t pteval) +{ +PVOP_VCALL3 (set_pte_atomic, ptep, pteval.pte_low, pteval.pte_high); +} + +static inline void set_pte_present (struct mm_struct *mm,unsigned long addr, +pte_t *ptep,pte_t pte) +{ +/* 5 arg words */ + paravirt_ops.set_pte_present (mm, addr, ptep, pte); +} +static inline void set_pmd (pmd_t *pmdp,pmd_t pmdval) { - paravirt_ops.set_pmd (pmdp, pmdval); -} - -static inline void pte_update (struct mm_struct *mm,u32 addr,pte_t *ptep) -{ - paravirt_ops.pte_update (mm, addr, ptep); -} - -static inline void pte_update_defer (struct mm_struct *mm,u32 addr,pte_t *ptep) -{ - paravirt_ops.pte_update_defer (mm, addr, ptep); -} - -#ifdef CONFIG_X86_PAE -static inline void set_pte_atomic (pte_t *ptep,pte_t pteval) -{ - paravirt_ops.set_pte_atomic (ptep, pteval); -} - -static inline void set_pte_present (struct mm_struct *mm,unsigned long addr,pte_t *ptep,pte_t pte) -{ - paravirt_ops.set_pte_present (mm, addr, ptep, pte); +PVOP_VCALL3 (set_pmd, pmdp, pmdval.pmd, pmdval.pmd >>32 ); }static inline void set_pud (pud_t *pudp,pud_t pudval) { - paravirt_ops.set_pud (pudp, pudval); +PVOP_VCALL3 (set_pud, pudp, pudval.pgd.pgd, pudval.pgd.pgd >>32 ); }static inline void pte_clear (struct mm_struct *mm,unsigned long addr,pte_t *ptep) { - paravirt_ops.pte_clear (mm, addr, ptep); +PVOP_VCALL3 (pte_clear, mm, addr, ptep); }static inline void pmd_clear (pmd_t *pmdp) { - paravirt_ops.pmd_clear (pmdp); -} -#endif +PVOP_VCALL1 (pmd_clear, pmdp); +} + +static inline pte_t raw_ptep_get_and_clear (pte_t *p) +{ + unsigned long long val = PVOP_CALL1(unsigned long long, ptep_get_and_clear, p); +return (pte_t ) { val, val >>32 }; +} +#else /* !CONFIG_X86_PAE */ +static inline pte_t __pte (unsigned long val) +{ + return (pte_t) { PVOP_CALL1(unsigned long, make_pte, val) }; +} + +static inline pgd_t __pgd (unsigned long val) +{ + return (pgd_t) { PVOP_CALL1(unsigned long, make_pgd, val) }; +} + +static inline unsigned long pte_val (pte_t x) +{ + return PVOP_CALL1(unsigned long, pte_val, x.pte_low); +} + +static inline unsigned long pgd_val (pgd_t x) +{ + return PVOP_CALL1(unsigned long, pgd_val, x.pgd); +} + +static inline void set_pte (pte_t *ptep,pte_t pteval) +{ +PVOP_VCALL2 (set_pte, ptep, pteval.pte_low); +} + +static inline void set_pte_at (struct mm_struct *mm,unsigned long addr, +pte_t *ptep,pte_t pteval) +{ +PVOP_VCALL4 (set_pte_at, mm, addr, ptep, pteval.pte_low); +} + +static inline void set_pmd (pmd_t *pmdp,pmd_t pmdval) +{ +PVOP_VCALL2 (set_pmd, pmdp, pmdval.pud.pgd.pgd); +} + +static inline pte_t raw_ptep_get_and_clear (pte_t *p) +{ + return (pte_t) { PVOP_CALL1(unsigned long, ptep_get_and_clear, p) }; +} +#endif /* CONFIG_X86_PAE */ /* Lazy mode for batching updates / context switch */ #define PARAVIRT_LAZY_NONE 0
@@ -502,12 +903,24 @@ static inline void pmd_clear(pmd_t *pmdp#define PARAVIRT_LAZY_CPU 2#define __HAVE_ARCH_ENTER_LAZY_CPU_MODE -#define arch_enter_lazy_cpu_mode () paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_CPU) -#define arch_leave_lazy_cpu_mode () paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_NONE) +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 ); +}#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) +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 ); +}void _paravirt_nop (void );#define paravirt_nop ((void *)_paravirt_nop)
@@ -600,6 +1013,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__ */
Re: [patch 12/17] Consistently wrap paravirt ops callsites to make them patchable
Quoted 9 lines · Jeremy Fitzhardinge
Wrap a set of interesting
paravirt_opscalls 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.
Can you please add some comments to the code explaining this a little? Best would be perhaps a overview document in Documentation too.
Quoted 1 line
+#define
PVOP_CALL0(__rettype, __op)\
The __s shouldn't be needed for the macro arguments because there is no shared name space with the caller.
include/asm-i386/paravirt.h extern struct paravirt_ops paravirt_ops; · line 211
Quoted 6 lines
include/asm-i386/paravirt.h +6
-0+ ({ \ + __rettype __ret; \ + if (sizeof(__rettype) > sizeof(unsigned long)) { \ + unsigned long long __tmp; \ + unsigned long __ecx; \ + asm volatile(paravirt_alt(PARAVIRT_CALL) \
Not having the volatile would probably generate better code, but it seems much safer for now.
include/asm-i386/paravirt.h extern struct paravirt_ops paravirt_ops; · line 215
Quoted 4 lines
include/asm-i386/paravirt.h +4
-0+ : "=A" (__tmp), "=c" (__ecx) \ + : paravirt_type(__op), \ + paravirt_clobber(CLBR_ANY) \ + : "memory", "cc"); \
And the cc clobber is also not needed
-Andi
Re: [patch 12/17] Consistently wrap paravirt ops callsites to make them patchable
Quoted 3 lines · Andi Kleen
Can you please add some comments to the code explaining this a little? Best would be perhaps a overview document in Documentation too.
Yes, OK.
Quoted 6 lines
Quoted 2 lines
+#define
PVOP_CALL0(__rettype, __op)\The
__sshouldn't be needed for the macro arguments because there is no shared name space with the caller.
Yes. I was more concerned about "op" clashing with variables used within the macro, but that's not an issue now.
Quoted 11 lines
include/asm-i386/paravirt.h extern struct paravirt_ops paravirt_ops; · line 211
Quoted 7 lines
include/asm-i386/paravirt.h +6
-0+ ({ \ + __rettype __ret; \ + if (sizeof(__rettype) > sizeof(unsigned long)) { \ + unsigned long long __tmp; \ + unsigned long __ecx; \ + asm volatile(paravirt_alt(PARAVIRT_CALL) \Not having the volatile would probably generate better code, but it seems much safer for now.
I think it has to be there, though perhaps the "memory" is sufficient to make sure all the calls get generated. We don't want gcc to eliminate any of these calls because it thinks they can be CSEd.
Quoted 2 lines
And the cc clobber is also not needed
I get conflicting advice about this. I think Linus mentioned that the gcc people would prefer it to be there explicitly, even though its currently implied.
J
PATCH 13/17 add common patching machinery
In reply to Jeremy FitzhardingeImplement the actual patching machinery. paravirt_patch_default() contains the logic to automatically patch a callsite based on a few simple rules:
- if the paravirt_op function is paravirt_nop, then patch nops
- if the paravirt_op function is a jmp target, then jmp to it
- if the paravirt_op function is callable and doesn't clobber too much for the callsite, call it directly
paravirt_patch_default is suitable as a default implementation of paravirt_ops.patch, will remove most of the expensive indirect calls in favour of either a direct call or a pile of nops.
Backends may implement their own patcher, however. There are several helper functions to help with this:
paravirt_patch_nop nop out a callsiteparavirt_patch_ignore leave the callsite as-isparavirt_patch_call patch a call if the caller and callee
have compatible clobbersparavirt_patch_jmp patch in a jmpparavirt_patch_insns patch some literal instructions over
the callsite, if they fit
This patch also implements more direct patches for the native case, so that when running on native hardware many common operations are implemented inline.
--- arch/i386/kernel/alternative.c | 5 - arch/i386/kernel/paravirt.c | 165 ++++++++++++++++++++++++++++++++-------- include/asm-i386/paravirt.h | 11 ++ 3 files changed, 149 insertions(+), 32 deletions(-)
===================================================================
arch/i386/kernel/alternative.c +4 -1
@@ -361,11 +361,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 (); }#endif /* CONFIG_PARAVIRT */
===================================================================
arch/i386/kernel/paravirt.c +134 -31
181 lines not on this page · raw message
===================================================================
include/asm-i386/paravirt.h +11 -0
@@ -933,6 +933,17 @@ struct paravirt_patch_site {u16 clobbers;/* what registers you may clobber */ }; +unsigned paravirt_patch_nop (void ); +unsigned paravirt_patch_ignore (unsigned len); +unsigned paravirt_patch_call (void *target,u16 tgt_clobbers, +void *site,u16 site_clobbers, +unsigned len); +unsigned paravirt_patch_jmp (void *target,void *site,unsigned len); +unsigned paravirt_patch_default (u8 type,u16 clobbers,void *site,unsigned len); + +unsigned paravirt_patch_insns (void *site,unsigned len, +const char *start,const char *end); +extern struct paravirt_patch_site __start_parainstructions[], __stop_parainstructions[];
PATCH 14/17 add flush_tlb_others paravirt_op
In reply to Jeremy FitzhardingeThis 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 | 15 ++++++++------- include/asm-i386/paravirt.h | 9 +++++++++ include/asm-i386/tlbflush.h | 19 +++++++++++++++++-- 4 files changed, 35 insertions(+), 9 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +1 -0
@@ -301,6 +301,7 @@ struct paravirt_ops paravirt_ops = { .flush_tlb_user = native_flush_tlb, .flush_tlb_kernel = native_flush_tlb_global, .flush_tlb_single = native_flush_tlb_single, + .flush_tlb_others = native_flush_tlb_others, .map_pt_hook = paravirt_nop,
===================================================================
arch/i386/kernel/smp.c +8 -7
@@ -256,7 +256,6 @@ static struct mm_struct * flush_mm;static struct mm_struct * flush_mm;static unsigned long flush_va;static DEFINE_SPINLOCK (tlbstate_lock); -#define FLUSH_ALL 0xffffffff/* * We cannot call mmdrop() because we are in interrupt context,
@@ -338,7 +337,7 @@ fastcall void smp_invalidate_interrupt(sif (flush_mm ==per_cpu (cpu_tlbstate, cpu).active_mm) {if (per_cpu (cpu_tlbstate, cpu).state ==TLBSTATE_OK ) { -if (flush_va ==FLUSH_ALL ) +if (flush_va ==TLB_FLUSH_ALL )local_flush_tlb ();else __flush_tlb_one (flush_va);
@@ -353,9 +352,11 @@ out:put_cpu_no_resched (); } -static void flush_tlb_others (cpumask_t cpumask,struct mm_struct *mm, -unsigned long va) -{ +void native_flush_tlb_others (const cpumask_t *cpumaskp,struct mm_struct *mm, +unsigned long va) +{ +cpumask_t cpumask = *cpumaskp; +/* * A couple of (to be removed) sanity checks: *
@@ -417,7 +418,7 @@ void flush_tlb_current_task(void)local_flush_tlb ();if (!cpus_empty (cpu_mask)) -flush_tlb_others (cpu_mask, mm,FLUSH_ALL ); +flush_tlb_others (cpu_mask, mm,TLB_FLUSH_ALL );preempt_enable (); }
@@ -436,7 +437,7 @@ void flush_tlb_mm (struct mm_struct * mmleave_mm (smp_processor_id ()); }if (!cpus_empty (cpu_mask)) -flush_tlb_others (cpu_mask, mm,FLUSH_ALL ); +flush_tlb_others (cpu_mask, mm,TLB_FLUSH_ALL );preempt_enable (); }
===================================================================
include/asm-i386/paravirt.h +9 -0
@@ -15,6 +15,7 @@#ifndef __ASSEMBLY__#include <linux/types.h> +#include <linux/cpumask.h> struct thread_struct ;struct Xgt_desc_struct ;
@@ -125,6 +126,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);
@@ -731,6 +734,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,10 +79,14 @@* - flush_tlb_range(vma, start, end) flushes a range of pages * - flush_tlb_kernel_range(start, end) flushes a range of kernel pages * - flush_tlb_pgtables(mm, start, end) flushes a range of page tables +* - flush_tlb_others(cpumask, mm, va) flushes a TLBs on other cpus * * ..but the i386 has somewhat limited tlb flushing capabilities, * and page-granular flushes are available only on i486 and up. */ + +#define TLB_FLUSH_ALL 0xffffffff +#ifndef CONFIG_SMP
@@ -110,7 +114,12 @@ static inline void flush_tlb_range(struc __flush_tlb(); } -#else +static inline void native_flush_tlb_others (const cpumask_t *cpumask, +struct mm_struct *mm,unsigned long va) +{ +} + +#else /* SMP */ #include <asm/smp.h>
@@ -129,6 +138,9 @@ static inline void flush_tlb_range(strucflush_tlb_mm (vma->vm_mm); } +void native_flush_tlb_others (const cpumask_t *cpumask,struct mm_struct *mm, +unsigned long va); +#define TLBSTATE_OK 1#define TLBSTATE_LAZY 2
@@ -139,8 +151,11 @@ struct tlb_statechar __cacheline_padding[L1_CACHE_BYTES -8 ]; }; DECLARE_PER_CPU(struct tlb_state, cpu_tlbstate); +#endif /* SMP */ - +#ifndef CONFIG_PARAVIRT +#define flush_tlb_others (mask, mm, va) \ + native_flush_tlb_others(&mask, mm, va)#endif #define flush_tlb_kernel_range (start, end) flush_tlb_all()
PATCH 15/17 revert map_pt_hook.
In reply to Jeremy FitzhardingeBack out the map_pt_hook to clear the way for kmap_atomic_pte.
--- arch/i386/kernel/paravirt.c | 2 -- arch/i386/kernel/vmi.c | 2 ++ include/asm-i386/paravirt.h | 7 ------- include/asm-i386/pgtable.h | 23 ++++------------------- 4 files changed, 6 insertions(+), 28 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +0 -2
@@ -303,8 +303,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
@@ -819,8 +819,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
@@ -128,8 +128,6 @@ struct paravirt_opsvoid (*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);void (*alloc_pt)(u32 pfn);void (*alloc_pd)(u32 pfn);
@@ -740,11 +738,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
@@ -272,7 +272,6 @@ static inline void vmalloc_sync_all(void*/ #define pte_update (mm, addr, ptep) do { } while (0)#define pte_update_defer (mm, addr, ptep) do { } while (0) -#define paravirt_map_pt_hook (slot, va, pfn) do { } while (0)#define raw_ptep_get_and_clear (xp) native_ptep_get_and_clear(xp)#endif
@@ -481,24 +480,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 16/17 add kmap_atomic_pte for mapping highpte pages
In reply to Jeremy FitzhardingeXen and VMI both have special requirements when mapping a highmem pte page into the kernel address space. These can be dealt with by adding a new kmap_atomic_pte() function for mapping highptes, and hooking it into the paravirt_ops infrastructure.
Xen specifically wants to map the pte page RO, so this patch exposes a helper function, kmap_atomic_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 | 7 +++++++ arch/i386/mm/highmem.c | 9 +++++++-- include/asm-i386/highmem.h | 11 +++++++++++ include/asm-i386/paravirt.h | 13 ++++++++++++- include/asm-i386/pgtable.h | 4 ++-- include/linux/highmem.h | 6 ++++++ mm/highmem.c | 9 +++++++++ 7 files changed, 54 insertions(+), 5 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +7 -0
@@ -20,6 +20,7 @@#include <linux/efi.h> #include <linux/bcd.h> #include <linux/start_kernel.h> +#include <linux/highmem.h> #include <asm/bug.h> #include <asm/paravirt.h>
@@ -318,6 +319,12 @@ struct paravirt_ops paravirt_ops = { .ptep_get_and_clear = native_ptep_get_and_clear, +#ifdef CONFIG_HIGHPTE + .kmap_atomic_pte = native_kmap_atomic_pte, +#else + .kmap_atomic_pte = paravirt_nop, +#endif +#ifdef CONFIG_X86_PAE .set_pte_atomic = native_set_pte_atomic, .set_pte_present = native_set_pte_present,
===================================================================
arch/i386/mm/highmem.c +7 -2
@@ -26,7 +26,7 @@ void kunmap(struct page *page)* However when holding an atomic kmap is is not legal to sleep, so atomic * kmaps are appropriate for short, tight code paths only. */ -void *kmap_atomic (struct page *page,enum km_type type) +void *kmap_atomic_prot (struct page *page,enum km_type type,pgprot_t prot) {enum fixed_addresses idx;unsigned long vaddr;
@@ -41,9 +41,14 @@ void *kmap_atomic(struct page *page, enureturn page_address (page); vaddr =__fix_to_virt (FIX_KMAP_BEGIN + idx); -set_pte (kmap_pte-idx,mk_pte (page, kmap_prot)); +set_pte (kmap_pte-idx,mk_pte (page, prot));return (void *) vaddr; +} + +void *kmap_atomic (struct page *page,enum km_type type) +{ +return kmap_atomic_prot (page, type, kmap_prot); }void kunmap_atomic (void *kvaddr,enum km_type type)
===================================================================
include/asm-i386/highmem.h +11 -0
@@ -24,6 +24,7 @@#include <linux/threads.h> #include <asm/kmap_types.h> #include <asm/tlbflush.h> +#include <asm/paravirt.h> /* declarations for highmem.c */ extern unsigned long highstart_pfn, highend_pfn;
@@ -67,10 +68,20 @@ extern void FASTCALL(kunmap_high(structvoid *kmap (struct page *page);void kunmap (struct page *page); +void *kmap_atomic_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); + +static inline void *native_kmap_atomic_pte (struct page *page,enum km_type type) +{ +return kmap_atomic (page, type); +} + +#ifndef CONFIG_PARAVIRT +#define kmap_atomic_pte (page, type) kmap_atomic(page, type) +#endif #define flush_cache_kmaps () do { } while (0)
===================================================================
include/asm-i386/paravirt.h +12 -1
@@ -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 ;
@@ -143,6 +145,8 @@ struct paravirt_opsvoid (*pte_update_defer)(struct mm_struct *mm,unsigned long addr,pte_t *ptep);pte_t (*ptep_get_and_clear)(pte_t *ptep); + +void *(*kmap_atomic_pte)(struct page *page,enum km_type type);#ifdef CONFIG_X86_PAE void (*set_pte_atomic)(pte_t *ptep,pte_t pteval);
@@ -768,6 +772,13 @@ static inline void paravirt_release_pd(uPVOP_VCALL1 (release_pd, pfn); } +static inline void *kmap_atomic_pte (struct page *page,enum km_type type) +{ +unsigned long ret; + ret = PVOP_CALL2(unsigned long, kmap_atomic_pte, page, type); +return (void *)ret; +} +static inline void pte_update (struct mm_struct *mm,unsigned long addr,pte_t *ptep) {
===================================================================
include/asm-i386/pgtable.h +2 -2
@@ -481,9 +481,9 @@ extern pte_t *lookup_address(unsigned lo#if defined(CONFIG_HIGHPTE )#define pte_offset_map (dir, address) \ - ((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE0) + pte_index(address)) + ((pte_t *)kmap_atomic_pte(pmd_page(*(dir)),KM_PTE0) + pte_index(address))#define pte_offset_map_nested (dir, address) \ - ((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE1) + pte_index(address)) + ((pte_t *)kmap_atomic_pte(pmd_page(*(dir)),KM_PTE1) + pte_index(address))#define pte_unmap (pte) kunmap_atomic(pte, KM_PTE0)#define pte_unmap_nested (pte) kunmap_atomic(pte, KM_PTE1)#else
===================================================================
include/linux/highmem.h +6 -0
@@ -27,6 +27,8 @@ unsigned int nr_free_highpages(void);unsigned int nr_free_highpages (void );extern unsigned long totalhigh_pages; +void kmap_flush_unused (void ); +#else /* CONFIG_HIGHMEM */ static inline unsigned int nr_free_highpages (void ) {return 0 ; }
@@ -44,9 +46,13 @@ static inline void *kmap(struct page *pa#define kmap_atomic (page, idx) \ ({ pagefault_disable(); page_address(page); }) +#define kmap_atomic_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
@@ -97,6 +97,15 @@ static void flush_all_zero_pkmaps(void)set_page_address (page,NULL ); }flush_tlb_kernel_range (PKMAP_ADDR (0 ),PKMAP_ADDR (LAST_PKMAP )); +} + +/* Flush all unused kmap mappings in order to remove stray +mappings. */ +void kmap_flush_unused (void ) +{ +spin_lock (&kmap_lock); +flush_all_zero_pkmaps (); +spin_unlock (&kmap_lock); }static inline unsigned long map_new_virtual (struct page *page)
Re: [patch 16/17] add kmap_atomic_pte for mapping highpte pages
mm/highmem.c static void flush_all_zero_pkmaps(void) · line 109
Quoted 11 lines
mm/highmem.c +9
-0+} + +/* 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); }
Who calls this now?
-Andi
Re: [patch 16/17] add kmap_atomic_pte for mapping highpte pages
Quoted 5 lines · Andi Kleen
Quoted 5 lines
mm/highmem.c +5
-0+} + +/* Flush all unused kmap mappings in order to remove stray +mappings. */ +void kmap_flush_unused (void )
Quoted 8 lines
mm/highmem.c static void flush_all_zero_pkmaps(void) · line 109
Quoted 6 lines
mm/highmem.c +4
-0+{ +spin_lock (&kmap_lock); +flush_all_zero_pkmaps (); +spin_unlock (&kmap_lock); }Who calls this now?
Nobody in kernel, or in this patchset. The Xen code calls it to make sure there are no stray RW mappings of a page it has freshly allocated for use in a pagetable.
J
PATCH 17/17 Add a sched_clock paravirt_op
In reply to Jeremy FitzhardingeThe tsc-based get_scheduled_cycles interface is not a good match for Xen's runstate accounting, which reports everything in nanoseconds.
This patch replaces this interface with a sched_clock interface, which matches both Xen and VMI's requirements.
In order to do this, we:
1. replace get_scheduled_cycles with sched_clock
2. hoist cycles_2_ns into a common header
3. update vmi accordingly
One thing to note: because sched_clock is implemented as a weak function in kernel/sched.c, we must define a real function in order to override this weak binding. This means the usual paravirt_ops technique of using an inline function won't work in this case.
--- arch/i386/kernel/paravirt.c | 2 - arch/i386/kernel/sched-clock.c | 39 ++++++++++++++-------------------- arch/i386/kernel/vmi.c | 2 - arch/i386/kernel/vmitime.c | 6 ++--- include/asm-i386/paravirt.h | 7 ++++-- include/asm-i386/timer.h | 45 +++++++++++++++++++++++++++++++++++++++- include/asm-i386/vmi_time.h | 2 - 7 files changed, 71 insertions(+), 32 deletions(-)
===================================================================
arch/i386/kernel/paravirt.c +1 -1
@@ -269,7 +269,7 @@ struct paravirt_ops paravirt_ops = { .write_msr = native_write_msr_safe, .read_tsc = native_read_tsc, .read_pmc = native_read_pmc, - .get_scheduled_cycles = native_read_tsc, + .sched_clock = native_sched_clock, .get_cpu_khz = native_calculate_cpu_khz, .load_tr_desc = native_load_tr_desc, .set_ldt = native_set_ldt,
===================================================================
arch/i386/kernel/sched-clock.c +16 -23
@@ -37,26 +37,7 @@#define CYC2NS_SCALE_FACTOR 10/* 2^10, carefully chosen */ -struct sc_data { -unsigned int cyc2ns_scale; -unsigned char unstable; -unsigned long long last_tsc; -unsigned long long ns_base; -}; - -static DEFINE_PER_CPU(struct sc_data, sc_data); - -static inline unsigned long long cycles_2_ns (unsigned long long cyc) -{ -const struct sc_data *sc = &__get_cpu_var (sc_data); -unsigned long long ns; - - cyc -= sc->last_tsc; - ns = (cyc * sc->cyc2ns_scale) >>CYC2NS_SCALE_FACTOR ; - ns += sc->ns_base; - -return ns; -} +DEFINE_PER_CPU(struct sc_data, sc_data);/* * Scheduler clock - returns current time in nanosec units.
@@ -66,7 +47,7 @@ static inline unsigned long long cycles_* [1] no attempt to stop CPU instruction reordering, which can hit * in a 100 instruction window or so. */ -unsigned long long sched_clock (void ) +unsigned long long native_sched_clock (void ) {unsigned long long r;const struct sc_data *sc = &get_cpu_var (sc_data);
@@ -75,7 +56,7 @@ unsigned long long sched_clock(void)/* TBD find a cheaper fallback timer than this */ r =ktime_to_ns (ktime_get ()); }else { -get_scheduled_cycles (r); +rdtscll (r); r =cycles_2_ns (r); }
@@ -83,6 +64,18 @@ unsigned long long sched_clock(void)return r; } + +/* We need to define a real function for sched_clock, to override the +weak default version */ +#ifdef CONFIG_PARAVIRT +unsigned long long sched_clock (void ) +{ +return paravirt_sched_clock (); +} +#else +unsigned long long sched_clock (void ) + __attribute__((alias ("native_sched_clock" ))); +#endif /* Resync with new CPU frequency */ static void resync_sc_freq (struct sc_data *sc,unsigned int newfreq)
@@ -95,7 +88,7 @@ static void resync_sc_freq(struct sc_datbecause sched_clock callers should be able to tolerate small errors. */ sc->ns_base =ktime_to_ns (ktime_get ()); -get_scheduled_cycles (sc->last_tsc); +rdtscll (sc->last_tsc); sc->cyc2ns_scale = (1000000 <<CYC2NS_SCALE_FACTOR ) / newfreq; sc->unstable =0 ; }
===================================================================
arch/i386/kernel/vmi.c +1 -1
@@ -866,7 +866,7 @@ static inline int __init activate_vmi(vo paravirt_ops.setup_boot_clock = vmi_timer_setup_boot_alarm; paravirt_ops.setup_secondary_clock = vmi_timer_setup_secondary_alarm;#endif - paravirt_ops.get_scheduled_cycles = vmi_get_sched_cycles; + paravirt_ops.sched_clock = vmi_sched_clock; paravirt_ops.get_cpu_khz = vmi_cpu_khz;/* We have true wallclock functions; disable CMOS clock sync */
===================================================================
arch/i386/kernel/vmitime.c +3 -3
@@ -163,9 +163,9 @@ int vmi_set_wallclock(unsigned long now)return -1 ; } -unsigned long long vmi_get_sched_cycles (void ) -{ -return read_available_cycles (); +unsigned long long vmi_sched_clock (void ) +{ +return cycles_2_ns (read_available_cycles ()); }unsigned long vmi_cpu_khz (void )
===================================================================
include/asm-i386/paravirt.h +5 -2
@@ -88,7 +88,7 @@ struct paravirt_opsu64 (*read_tsc)(void);u64 (*read_pmc)(void); -u64 (*get_scheduled_cycles)(void); +unsigned long long (*sched_clock)(void );unsigned long (*get_cpu_khz)(void );void (*load_tr_desc)(void );
@@ -529,7 +529,10 @@ static inline void halt(void)#define rdtscll (val) (val = PVOP_CALL0(u64, read_tsc)) -#define get_scheduled_cycles (val) (val = paravirt_ops.get_scheduled_cycles()) +static inline unsigned long long paravirt_sched_clock (void ) +{ + return PVOP_CALL0(unsigned long long, sched_clock); +}#define calculate_cpu_khz () (paravirt_ops.get_cpu_khz())#define write_tsc (val1,val2) wrmsr(0x10, val1, val2)
===================================================================
include/asm-i386/timer.h +44 -1
52 lines not on this page · raw message
===================================================================
include/asm-i386/vmi_time.h +1 -1
@@ -49,7 +49,7 @@ extern void __init vmi_time_init(void);extern void __init vmi_time_init (void );extern unsigned long vmi_get_wallclock (void );extern int vmi_set_wallclock (unsigned long now); -extern unsigned long long vmi_get_sched_cycles (void ); +extern unsigned long long vmi_sched_clock (void );extern unsigned long vmi_cpu_khz (void );#ifdef CONFIG_X86_LOCAL_APIC
Re: [patch 17/17] Add a sched_clock paravirt_op
Quoted 11 lines · Jeremy Fitzhardinge
The tsc-based
get_scheduled_cyclesinterface is not a good match for Xen's runstate accounting, which reports everything in nanoseconds.This patch replaces this interface with a
sched_clockinterface, which matches both Xen and VMI's requirements.In order to do this, we:
1. replaceget_scheduled_cycleswithsched_clock
2. hoistcycles_2_nsinto a common header
3. update vmi accordingly
Quoted 4 lines
One thing to note: because
sched_clockis implemented as a weak function inkernel/sched.c, we must define a real function in order to override this weak binding. This means the usualparavirt_opstechnique of using an inline function won't work in this case.
I think it would be much cleaner if you didn't implement your own sched_clock, but you adjust ns_base/last_tsc to account for your lost cycles. This could be done cleanly by adding a new function to sched-clock.c Possibly such a function could be used by other parts of the kernel in the future too.
-Andi
Re: [patch 17/17] Add a sched_clock paravirt_op
Quoted 6 lines · Andi Kleen
I think it would be much cleaner if you didn't implement your own
sched_clock, but you adjust ns_base/last_tsc to account for your lost cycles. This could be done cleanly by adding a new function tosched-clock.cPossibly such a function could be used by other parts of the kernel in the future too.
Cleaner how? This seems pretty straightforward to me. Xen can return a clock measuring unstolen nanoseconds, which maps directly to the sched_clock interface, doesn't need any of the existing sched_clock code. I suppose I could map the Xen interface onto some abstract "cycles" notion and hook it into the tsc machinery, but it seems like it would be a forced fit. In general, my approach has been to choose the higher-level interface over a lower-level one, all other things being equal.
The only reason I hoisted the cycles_2_ns stuff was for vmi. It returns a tsc-like cycles interface, and so it can make use of the existing cycles_2_ns code (though I don't think a changing timebase is an issue).
J
Re: [patch 17/17] Add a sched_clock paravirt_op
Quoted 10 lines · Jeremy Fitzhardinge
Quoted 6 lines · Andi Kleen
I think it would be much cleaner if you didn't implement your own
sched_clock, but you adjust ns_base/last_tsc to account for your lost cycles. This could be done cleanly by adding a new function tosched-clock.cPossibly such a function could be used by other parts of the kernel in the future too.Cleaner how? This seems pretty to me. Xen can return a clock measuring unstolen nanoseconds,
Do you also get a clock for stolen nanoseconds?
Quoted 6 lines
which maps directly to the
sched_clockinterface, doesn't need any of the existingsched_clockcode. I suppose I could map the Xen interface onto some abstract "cycles" notion and hook it into the tsc machinery, but it seems like it would be a forced fit. In general, my approach has been to choose the higher-level interface over a lower-level one, all other things being equal.
No need for cycles, you could just subtract the stolen ns if you can get those.
-Andi
Re: [patch 17/17] Add a sched_clock paravirt_op
Quoted 2 lines · Andi Kleen
Do you also get a clock for stolen nanoseconds?
What you actually get is how many ns the CPU spent in each state. Stolen is runnable+offline.
Quoted 2 lines
No need for cycles, you could just subtract the stolen ns if you can get those.
It just seems like a simpler interface to just allow overriding sched_clock, rather than exposing sched_clock's internals and fiddling with those. There's really nothing in the existing sched_clock which can be profitably reused in the Xen case. VMI can make use of the cycles_2_ns conversion, which is why I made it available for its use.
To be specific, this is the whole Xen sched_clock function:
/* Xen sched_clock implementation. Returns the number of RUNNING ns */ unsigned long long xen_sched_clock(void)
{
struct vcpu_runstate_info state;
cycle_t now;
unsigned long long ret;
preempt_disable();
now = xen_clocksource_read();
get_runstate_snapshot(&state);
WARN_ON(state.state != RUNSTATE_running);
ret = state.time[RUNSTATE_running] + (now - state.state_entry_time);
preempt_enable();
return ret;
}J