Commit d5822035 authored by Jeremy Fitzhardinge's avatar Jeremy Fitzhardinge Committed by Andi Kleen

[PATCH] i386: PARAVIRT: Use patch site IDs computed from offset in paravirt_ops structure

Use patch type identifiers derived from the offset of the operation in
the paravirt_ops structure.  This avoids having to maintain a separate
enum for patch site types.

Also, since the identifier is derived from the offset into
paravirt_ops, the offset can be derived from the identifier.  This is
used to remove replicated information in the various callsite macros,
which has been a source of bugs in the past.

This patch also drops the fused save_fl+cli operation, which doesn't
really add much and makes things more complex - specifically because
it breaks the 1:1 relationship between identifiers and offsets.  If
this operation turns out to be particularly beneficial, then the right
answer is to define a new entrypoint for it.
Signed-off-by: default avatarJeremy Fitzhardinge <jeremy@xensource.com>
Signed-off-by: default avatarAndi Kleen <ak@suse.de>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Zachary Amsden <zach@vmware.com>
parent 98de032b
...@@ -58,7 +58,6 @@ DEF_NATIVE(cli, "cli"); ...@@ -58,7 +58,6 @@ DEF_NATIVE(cli, "cli");
DEF_NATIVE(sti, "sti"); DEF_NATIVE(sti, "sti");
DEF_NATIVE(popf, "push %eax; popf"); DEF_NATIVE(popf, "push %eax; popf");
DEF_NATIVE(pushf, "pushf; pop %eax"); DEF_NATIVE(pushf, "pushf; pop %eax");
DEF_NATIVE(pushf_cli, "pushf; pop %eax; cli");
DEF_NATIVE(iret, "iret"); DEF_NATIVE(iret, "iret");
DEF_NATIVE(sti_sysexit, "sti; sysexit"); DEF_NATIVE(sti_sysexit, "sti; sysexit");
...@@ -66,13 +65,12 @@ static const struct native_insns ...@@ -66,13 +65,12 @@ static const struct native_insns
{ {
const char *start, *end; const char *start, *end;
} native_insns[] = { } native_insns[] = {
[PARAVIRT_IRQ_DISABLE] = { start_cli, end_cli }, [PARAVIRT_PATCH(irq_disable)] = { start_cli, end_cli },
[PARAVIRT_IRQ_ENABLE] = { start_sti, end_sti }, [PARAVIRT_PATCH(irq_enable)] = { start_sti, end_sti },
[PARAVIRT_RESTORE_FLAGS] = { start_popf, end_popf }, [PARAVIRT_PATCH(restore_fl)] = { start_popf, end_popf },
[PARAVIRT_SAVE_FLAGS] = { start_pushf, end_pushf }, [PARAVIRT_PATCH(save_fl)] = { start_pushf, end_pushf },
[PARAVIRT_SAVE_FLAGS_IRQ_DISABLE] = { start_pushf_cli, end_pushf_cli }, [PARAVIRT_PATCH(iret)] = { start_iret, end_iret },
[PARAVIRT_INTERRUPT_RETURN] = { start_iret, end_iret }, [PARAVIRT_PATCH(irq_enable_sysexit)] = { start_sti_sysexit, end_sti_sysexit },
[PARAVIRT_STI_SYSEXIT] = { start_sti_sysexit, end_sti_sysexit },
}; };
static unsigned native_patch(u8 type, u16 clobbers, void *insns, unsigned len) static unsigned native_patch(u8 type, u16 clobbers, void *insns, unsigned len)
......
...@@ -83,11 +83,6 @@ extern struct paravirt_patch __start_parainstructions[], ...@@ -83,11 +83,6 @@ extern struct paravirt_patch __start_parainstructions[],
#define MNEM_JMP 0xe9 #define MNEM_JMP 0xe9
#define MNEM_RET 0xc3 #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_INT_MASK 0
#define IRQ_PATCH_DISABLE 5 #define IRQ_PATCH_DISABLE 5
...@@ -135,33 +130,17 @@ static unsigned patch_internal(int call, unsigned len, void *insns) ...@@ -135,33 +130,17 @@ static unsigned patch_internal(int call, unsigned len, void *insns)
static unsigned vmi_patch(u8 type, u16 clobbers, void *insns, unsigned len) static unsigned vmi_patch(u8 type, u16 clobbers, void *insns, unsigned len)
{ {
switch (type) { switch (type) {
case PARAVIRT_IRQ_DISABLE: case PARAVIRT_PATCH(irq_disable):
return patch_internal(VMI_CALL_DisableInterrupts, len, insns); 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); 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); 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); return patch_internal(VMI_CALL_GetInterruptMask, len, insns);
case PARAVIRT_SAVE_FLAGS_IRQ_DISABLE: case PARAVIRT_PATCH(iret):
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:
return patch_internal(VMI_CALL_IRET, len, insns); 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); return patch_internal(VMI_CALL_SYSEXIT, len, insns);
default: default:
break; break;
...@@ -796,12 +775,6 @@ static inline int __init activate_vmi(void) ...@@ -796,12 +775,6 @@ static inline int __init activate_vmi(void)
para_fill(irq_disable, DisableInterrupts); para_fill(irq_disable, DisableInterrupts);
para_fill(irq_enable, EnableInterrupts); 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(wbinvd, WBINVD);
para_fill(read_tsc, RDTSC); para_fill(read_tsc, RDTSC);
......
...@@ -4,19 +4,8 @@ ...@@ -4,19 +4,8 @@
* para-virtualization: those hooks are defined here. */ * para-virtualization: those hooks are defined here. */
#ifdef CONFIG_PARAVIRT #ifdef CONFIG_PARAVIRT
#include <linux/stringify.h>
#include <asm/page.h> #include <asm/page.h>
/* These are the most performance critical ops, so we want to be able to patch
* callers */
#define PARAVIRT_IRQ_DISABLE 0
#define PARAVIRT_IRQ_ENABLE 1
#define PARAVIRT_RESTORE_FLAGS 2
#define PARAVIRT_SAVE_FLAGS 3
#define PARAVIRT_SAVE_FLAGS_IRQ_DISABLE 4
#define PARAVIRT_INTERRUPT_RETURN 5
#define PARAVIRT_STI_SYSEXIT 6
/* Bitmask of what can be clobbered: usually at least eax. */ /* Bitmask of what can be clobbered: usually at least eax. */
#define CLBR_NONE 0x0 #define CLBR_NONE 0x0
#define CLBR_EAX 0x1 #define CLBR_EAX 0x1
...@@ -191,6 +180,28 @@ struct paravirt_ops ...@@ -191,6 +180,28 @@ struct paravirt_ops
extern struct paravirt_ops paravirt_ops; extern struct paravirt_ops paravirt_ops;
#define PARAVIRT_PATCH(x) \
(offsetof(struct paravirt_ops, x) / sizeof(void *))
#define paravirt_type(type) \
[paravirt_typenum] "i" (PARAVIRT_PATCH(type))
#define paravirt_clobber(clobber) \
[paravirt_clobber] "i" (clobber)
#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" \
".pushsection .parainstructions,\"a\"\n" \
" .long 771b\n" \
" .byte " type "\n" \
" .byte 772b-771b\n" \
" .short " clobber "\n" \
".popsection\n"
#define paravirt_alt(insn_string) \
_paravirt_alt(insn_string, "%c[paravirt_typenum]", "%c[paravirt_clobber]")
#define paravirt_enabled() (paravirt_ops.paravirt_enabled) #define paravirt_enabled() (paravirt_ops.paravirt_enabled)
static inline void load_esp0(struct tss_struct *tss, static inline void load_esp0(struct tss_struct *tss,
...@@ -515,93 +526,89 @@ struct paravirt_patch_site { ...@@ -515,93 +526,89 @@ struct paravirt_patch_site {
extern struct paravirt_patch_site __parainstructions[], extern struct paravirt_patch_site __parainstructions[],
__parainstructions_end[]; __parainstructions_end[];
#define paravirt_alt(insn_string, typenum, clobber) \
"771:\n\t" insn_string "\n" "772:\n" \
".pushsection .parainstructions,\"a\"\n" \
" .long 771b\n" \
" .byte " __stringify(typenum) "\n" \
" .byte 772b-771b\n" \
" .short " __stringify(clobber) "\n" \
".popsection"
static inline unsigned long __raw_local_save_flags(void) static inline unsigned long __raw_local_save_flags(void)
{ {
unsigned long f; unsigned long f;
__asm__ __volatile__(paravirt_alt( "pushl %%ecx; pushl %%edx;" asm volatile(paravirt_alt("pushl %%ecx; pushl %%edx;"
"call *%1;" PARAVIRT_CALL
"popl %%edx; popl %%ecx", "popl %%edx; popl %%ecx")
PARAVIRT_SAVE_FLAGS, CLBR_NONE) : "=a"(f)
: "=a"(f): "m"(paravirt_ops.save_fl) : paravirt_type(save_fl),
: "memory", "cc"); paravirt_clobber(CLBR_NONE)
: "memory", "cc");
return f; return f;
} }
static inline void raw_local_irq_restore(unsigned long f) static inline void raw_local_irq_restore(unsigned long f)
{ {
__asm__ __volatile__(paravirt_alt( "pushl %%ecx; pushl %%edx;" asm volatile(paravirt_alt("pushl %%ecx; pushl %%edx;"
"call *%1;" PARAVIRT_CALL
"popl %%edx; popl %%ecx", "popl %%edx; popl %%ecx")
PARAVIRT_RESTORE_FLAGS, CLBR_EAX) : "=a"(f)
: "=a"(f) : "m" (paravirt_ops.restore_fl), "0"(f) : "0"(f),
: "memory", "cc"); paravirt_type(restore_fl),
paravirt_clobber(CLBR_EAX)
: "memory", "cc");
} }
static inline void raw_local_irq_disable(void) static inline void raw_local_irq_disable(void)
{ {
__asm__ __volatile__(paravirt_alt( "pushl %%ecx; pushl %%edx;" asm volatile(paravirt_alt("pushl %%ecx; pushl %%edx;"
"call *%0;" PARAVIRT_CALL
"popl %%edx; popl %%ecx", "popl %%edx; popl %%ecx")
PARAVIRT_IRQ_DISABLE, CLBR_EAX) :
: : "m" (paravirt_ops.irq_disable) : paravirt_type(irq_disable),
: "memory", "eax", "cc"); paravirt_clobber(CLBR_EAX)
: "memory", "eax", "cc");
} }
static inline void raw_local_irq_enable(void) static inline void raw_local_irq_enable(void)
{ {
__asm__ __volatile__(paravirt_alt( "pushl %%ecx; pushl %%edx;" asm volatile(paravirt_alt("pushl %%ecx; pushl %%edx;"
"call *%0;" PARAVIRT_CALL
"popl %%edx; popl %%ecx", "popl %%edx; popl %%ecx")
PARAVIRT_IRQ_ENABLE, CLBR_EAX) :
: : "m" (paravirt_ops.irq_enable) : paravirt_type(irq_enable),
: "memory", "eax", "cc"); paravirt_clobber(CLBR_EAX)
: "memory", "eax", "cc");
} }
static inline unsigned long __raw_local_irq_save(void) static inline unsigned long __raw_local_irq_save(void)
{ {
unsigned long f; unsigned long f;
__asm__ __volatile__(paravirt_alt( "pushl %%ecx; pushl %%edx;" f = __raw_local_save_flags();
"call *%1; pushl %%eax;" raw_local_irq_disable();
"call *%2; popl %%eax;"
"popl %%edx; popl %%ecx",
PARAVIRT_SAVE_FLAGS_IRQ_DISABLE,
CLBR_NONE)
: "=a"(f)
: "m" (paravirt_ops.save_fl),
"m" (paravirt_ops.irq_disable)
: "memory", "cc");
return f; return f;
} }
#define CLI_STRING paravirt_alt("pushl %%ecx; pushl %%edx;" \ #define CLI_STRING \
"call *paravirt_ops+%c[irq_disable];" \ _paravirt_alt("pushl %%ecx; pushl %%edx;" \
"popl %%edx; popl %%ecx", \ "call *paravirt_ops+%c[paravirt_cli_type]*4;" \
PARAVIRT_IRQ_DISABLE, CLBR_EAX) "popl %%edx; popl %%ecx", \
"%c[paravirt_cli_type]", "%c[paravirt_clobber]")
#define STI_STRING \
_paravirt_alt("pushl %%ecx; pushl %%edx;" \
"call *paravirt_ops+%c[paravirt_sti_type]*4;" \
"popl %%edx; popl %%ecx", \
"%c[paravirt_sti_type]", "%c[paravirt_clobber]")
#define STI_STRING paravirt_alt("pushl %%ecx; pushl %%edx;" \
"call *paravirt_ops+%c[irq_enable];" \
"popl %%edx; popl %%ecx", \
PARAVIRT_IRQ_ENABLE, CLBR_EAX)
#define CLI_STI_CLOBBERS , "%eax" #define CLI_STI_CLOBBERS , "%eax"
#define CLI_STI_INPUT_ARGS \ #define CLI_STI_INPUT_ARGS \
, \ , \
[irq_disable] "i" (offsetof(struct paravirt_ops, irq_disable)), \ [paravirt_cli_type] "i" (PARAVIRT_PATCH(irq_disable)), \
[irq_enable] "i" (offsetof(struct paravirt_ops, irq_enable)) [paravirt_sti_type] "i" (PARAVIRT_PATCH(irq_enable)), \
paravirt_clobber(CLBR_EAX)
#undef PARAVIRT_CALL
#else /* __ASSEMBLY__ */ #else /* __ASSEMBLY__ */
#define PARA_PATCH(ptype, clobbers, ops) \ #define PARA_PATCH(off) ((off) / 4)
#define PARA_SITE(ptype, clobbers, ops) \
771:; \ 771:; \
ops; \ ops; \
772:; \ 772:; \
...@@ -612,25 +619,25 @@ static inline unsigned long __raw_local_irq_save(void) ...@@ -612,25 +619,25 @@ static inline unsigned long __raw_local_irq_save(void)
.short clobbers; \ .short clobbers; \
.popsection .popsection
#define INTERRUPT_RETURN \ #define INTERRUPT_RETURN \
PARA_PATCH(PARAVIRT_INTERRUPT_RETURN, CLBR_ANY, \ PARA_SITE(PARA_PATCH(PARAVIRT_iret), CLBR_ANY, \
jmp *%cs:paravirt_ops+PARAVIRT_iret) jmp *%cs:paravirt_ops+PARAVIRT_iret)
#define DISABLE_INTERRUPTS(clobbers) \ #define DISABLE_INTERRUPTS(clobbers) \
PARA_PATCH(PARAVIRT_IRQ_DISABLE, clobbers, \ PARA_SITE(PARA_PATCH(PARAVIRT_irq_disable), clobbers, \
pushl %ecx; pushl %edx; \ pushl %ecx; pushl %edx; \
call *paravirt_ops+PARAVIRT_irq_disable; \ call *%cs:paravirt_ops+PARAVIRT_irq_disable; \
popl %edx; popl %ecx) \ popl %edx; popl %ecx) \
#define ENABLE_INTERRUPTS(clobbers) \ #define ENABLE_INTERRUPTS(clobbers) \
PARA_PATCH(PARAVIRT_IRQ_ENABLE, clobbers, \ PARA_SITE(PARA_PATCH(PARAVIRT_irq_enable), clobbers, \
pushl %ecx; pushl %edx; \ pushl %ecx; pushl %edx; \
call *%cs:paravirt_ops+PARAVIRT_irq_enable; \ call *%cs:paravirt_ops+PARAVIRT_irq_enable; \
popl %edx; popl %ecx) popl %edx; popl %ecx)
#define ENABLE_INTERRUPTS_SYSEXIT \ #define ENABLE_INTERRUPTS_SYSEXIT \
PARA_PATCH(PARAVIRT_STI_SYSEXIT, CLBR_ANY, \ PARA_SITE(PARA_PATCH(PARAVIRT_irq_enable_sysexit), CLBR_ANY, \
jmp *%cs:paravirt_ops+PARAVIRT_irq_enable_sysexit) jmp *%cs:paravirt_ops+PARAVIRT_irq_enable_sysexit)
#define GET_CR0_INTO_EAX \ #define GET_CR0_INTO_EAX \
call *paravirt_ops+PARAVIRT_read_cr0 call *paravirt_ops+PARAVIRT_read_cr0
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment