From: Jeremy Fitzhardinge <jeremy@goop.org>
To: Andi Kleen <ak@suse.de>
Cc: virtualization@lists.osdl.org, Ingo Molnar <mingo@elte.hu>,
Anthony Liguori <anthony@codemonkey.ws>,
Andrew Morton <akpm@linux-foundation.org>,
lkml <linux-kernel@vger.kernel.org>
Subject: [patch 13/17] add common patching machinery
Date: Sun, 01 Apr 2007 22:57:05 -0700 [thread overview]
Message-ID: <20070402055705.199510785@goop.org> (raw)
In-Reply-To: 20070402055652.610711908@goop.org
[-- Attachment #1: paravirt-patch-machinery.patch --]
[-- Type: text/plain, Size: 8352 bytes --]
Implement the actual patching machinery. paravirt_patch_default()
contains the logic to automatically patch a callsite based on a few
simple rules:
- if the paravirt_op function is paravirt_nop, then patch nops
- if the paravirt_op function is a jmp target, then jmp to it
- if the paravirt_op function is callable and doesn't clobber too much
for the callsite, call it directly
paravirt_patch_default is suitable as a default implementation of
paravirt_ops.patch, will remove most of the expensive indirect calls
in favour of either a direct call or a pile of nops.
Backends may implement their own patcher, however. There are several
helper functions to help with this:
paravirt_patch_nop nop out a callsite
paravirt_patch_ignore leave the callsite as-is
paravirt_patch_call patch a call if the caller and callee
have compatible clobbers
paravirt_patch_jmp patch in a jmp
paravirt_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.
Signed-off-by: Jeremy Fitzhardinge <jeremy@xensource.com>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Zachary Amsden <zach@vmware.com>
Cc: Anthony Liguori <anthony@codemonkey.ws>
Acked-by: Ingo Molnar <mingo@elte.hu>
---
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(-)
===================================================================
--- a/arch/i386/kernel/alternative.c
+++ b/arch/i386/kernel/alternative.c
@@ -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 */
===================================================================
--- a/arch/i386/kernel/paravirt.c
+++ b/arch/i386/kernel/paravirt.c
@@ -54,40 +54,143 @@ char *memory_setup(void)
#define DEF_NATIVE(name, code) \
extern const char start_##name[], end_##name[]; \
asm("start_" #name ": " code "; end_" #name ":")
-DEF_NATIVE(cli, "cli");
-DEF_NATIVE(sti, "sti");
-DEF_NATIVE(popf, "push %eax; popf");
-DEF_NATIVE(pushf, "pushf; pop %eax");
+
+DEF_NATIVE(irq_disable, "cli");
+DEF_NATIVE(irq_enable, "sti");
+DEF_NATIVE(restore_fl, "push %eax; popf");
+DEF_NATIVE(save_fl, "pushf; pop %eax");
DEF_NATIVE(iret, "iret");
-DEF_NATIVE(sti_sysexit, "sti; sysexit");
-
-static const struct native_insns
-{
- const char *start, *end;
-} native_insns[] = {
- [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 },
-};
+DEF_NATIVE(irq_enable_sysexit, "sti; sysexit");
+DEF_NATIVE(read_cr2, "mov %cr2, %eax");
+DEF_NATIVE(write_cr3, "mov %eax, %cr3");
+DEF_NATIVE(read_cr3, "mov %cr3, %eax");
+DEF_NATIVE(clts, "clts");
+DEF_NATIVE(read_tsc, "rdtsc");
+
+DEF_NATIVE(pushf_cli, "pushf; pop %eax; cli");
+DEF_NATIVE(ud2a, "ud2a");
static unsigned native_patch(u8 type, u16 clobbers, void *insns, unsigned len)
{
- unsigned int insn_len;
-
- /* Don't touch it if we don't have a replacement */
- if (type >= ARRAY_SIZE(native_insns) || !native_insns[type].start)
- return len;
-
- insn_len = native_insns[type].end - native_insns[type].start;
-
- /* Similarly if we can't fit replacement. */
- if (len < insn_len)
- return len;
-
- memcpy(insns, native_insns[type].start, insn_len);
+ const unsigned char *start, *end;
+ unsigned ret;
+
+ switch(type) {
+#define SITE(x) case PARAVIRT_PATCH(x): start = start_##x; end = end_##x; goto patch_site
+ SITE(irq_disable);
+ SITE(irq_enable);
+ SITE(restore_fl);
+ SITE(save_fl);
+ SITE(iret);
+ SITE(irq_enable_sysexit);
+ SITE(read_cr2);
+ SITE(read_cr3);
+ SITE(write_cr3);
+ SITE(clts);
+ SITE(read_tsc);
+#undef SITE
+
+ patch_site:
+ ret = paravirt_patch_insns(insns, len, start, end);
+ break;
+
+ case PARAVIRT_PATCH(make_pgd):
+ case PARAVIRT_PATCH(make_pte):
+ case PARAVIRT_PATCH(pgd_val):
+ case PARAVIRT_PATCH(pte_val):
+#ifdef CONFIG_X86_PAE
+ case PARAVIRT_PATCH(make_pmd):
+ case PARAVIRT_PATCH(pmd_val):
+#endif
+ /* These functions end up returning exactly what
+ they're passed, in the same registers. */
+ ret = paravirt_patch_nop();
+ break;
+
+ default:
+ ret = paravirt_patch_default(type, clobbers, insns, len);
+ break;
+ }
+
+ return ret;
+}
+
+unsigned paravirt_patch_nop(void)
+{
+ return 0;
+}
+
+unsigned paravirt_patch_ignore(unsigned len)
+{
+ return len;
+}
+
+unsigned paravirt_patch_call(void *target, u16 tgt_clobbers,
+ void *site, u16 site_clobbers,
+ unsigned len)
+{
+ unsigned char *call = site;
+ unsigned long delta = (unsigned long)target - (unsigned long)(call+5);
+
+ if (tgt_clobbers & ~site_clobbers)
+ return len; /* target would clobber too much for this site */
+ if (len < 5)
+ return len; /* call too long for patch site */
+
+ *call++ = 0xe8; /* call */
+ *(unsigned long *)call = delta;
+
+ return 5;
+}
+
+unsigned paravirt_patch_jmp(void *target, void *site, unsigned len)
+{
+ unsigned char *jmp = site;
+ unsigned long delta = (unsigned long)target - (unsigned long)(jmp+5);
+
+ if (len < 5)
+ return len; /* call too long for patch site */
+
+ *jmp++ = 0xe9; /* jmp */
+ *(unsigned long *)jmp = delta;
+
+ return 5;
+}
+
+unsigned paravirt_patch_default(u8 type, u16 clobbers, void *site, unsigned len)
+{
+ void *opfunc = *((void **)¶virt_ops + type);
+ unsigned ret;
+
+ if (opfunc == NULL)
+ /* If there's no function, patch it with a ud2a (BUG) */
+ ret = paravirt_patch_insns(site, len, start_ud2a, end_ud2a);
+ else if (opfunc == paravirt_nop)
+ /* If the operation is a nop, then nop the callsite */
+ ret = paravirt_patch_nop();
+ else if (type == PARAVIRT_PATCH(iret) ||
+ type == PARAVIRT_PATCH(irq_enable_sysexit))
+ /* If operation requires a jmp, then jmp */
+ ret = paravirt_patch_jmp(opfunc, site, len);
+ else
+ /* Otherwise call the function; assume target could
+ clobber any caller-save reg */
+ ret = paravirt_patch_call(opfunc, CLBR_ANY,
+ site, clobbers, len);
+
+ return ret;
+}
+
+unsigned paravirt_patch_insns(void *site, unsigned len,
+ const char *start, const char *end)
+{
+ unsigned insn_len = end - start;
+
+ if (insn_len > len || start == NULL)
+ insn_len = len;
+ else
+ memcpy(site, start, insn_len);
+
return insn_len;
}
@@ -110,7 +213,7 @@ static void native_flush_tlb_global(void
__native_flush_tlb_global();
}
-static void native_flush_tlb_single(u32 addr)
+static void native_flush_tlb_single(unsigned long addr)
{
__native_flush_tlb_single(addr);
}
===================================================================
--- a/include/asm-i386/paravirt.h
+++ b/include/asm-i386/paravirt.h
@@ -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[];
--
next prev parent reply other threads:[~2007-04-02 5:57 UTC|newest]
Thread overview: 36+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-04-02 5:56 [patch 00/17] paravirt_ops updates Jeremy Fitzhardinge
2007-04-02 5:56 ` [patch 01/17] update MAINTAINERS Jeremy Fitzhardinge
2007-04-02 5:56 ` [patch 02/17] Remove CONFIG_DEBUG_PARAVIRT Jeremy Fitzhardinge
2007-04-02 5:56 ` [patch 03/17] use paravirt_nop to consistently mark no-op operations Jeremy Fitzhardinge
2007-04-02 5:56 ` [patch 04/17] Add pagetable accessors to pack and unpack pagetable entries Jeremy Fitzhardinge
2007-04-02 6:12 ` Andi Kleen
2007-04-02 6:35 ` Rusty Russell
2007-04-02 6:48 ` Jeremy Fitzhardinge
2007-04-04 9:25 ` Jeremy Fitzhardinge
2007-04-04 11:47 ` Andi Kleen
2007-04-04 15:45 ` Jeremy Fitzhardinge
2007-04-04 15:56 ` Andi Kleen
2007-04-04 16:04 ` Jeremy Fitzhardinge
2007-04-04 22:59 ` Rusty Russell
2007-04-04 23:39 ` Jeremy Fitzhardinge
2007-04-02 5:56 ` [patch 05/17] Hooks to set up initial pagetable Jeremy Fitzhardinge
2007-04-02 5:56 ` [patch 06/17] Allocate a fixmap slot Jeremy Fitzhardinge
2007-04-02 5:56 ` [patch 07/17] Allow paravirt backend to choose kernel PMD sharing Jeremy Fitzhardinge
2007-04-02 5:57 ` [patch 08/17] add hooks to intercept mm creation and destruction Jeremy Fitzhardinge
2007-04-02 5:57 ` [patch 09/17] rename struct paravirt_patch to paravirt_patch_site for clarity Jeremy Fitzhardinge
2007-04-02 5:57 ` [patch 10/17] Use patch site IDs computed from offset in paravirt_ops structure Jeremy Fitzhardinge
2007-04-02 5:57 ` [patch 11/17] Fix patch site clobbers to include return register Jeremy Fitzhardinge
2007-04-02 5:57 ` [patch 12/17] Consistently wrap paravirt ops callsites to make them patchable Jeremy Fitzhardinge
2007-04-02 7:11 ` Andi Kleen
2007-04-02 7:20 ` Jeremy Fitzhardinge
2007-04-02 5:57 ` Jeremy Fitzhardinge [this message]
2007-04-02 5:57 ` [patch 14/17] add flush_tlb_others paravirt_op Jeremy Fitzhardinge
2007-04-02 5:57 ` [patch 15/17] revert map_pt_hook Jeremy Fitzhardinge
2007-04-02 5:57 ` [patch 16/17] add kmap_atomic_pte for mapping highpte pages Jeremy Fitzhardinge
2007-04-02 7:18 ` Andi Kleen
2007-04-02 7:22 ` Jeremy Fitzhardinge
2007-04-02 5:57 ` [patch 17/17] Add a sched_clock paravirt_op Jeremy Fitzhardinge
2007-04-02 6:09 ` Andi Kleen
2007-04-02 6:47 ` Jeremy Fitzhardinge
2007-04-02 6:50 ` Andi Kleen
2007-04-02 7:06 ` Jeremy Fitzhardinge
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20070402055705.199510785@goop.org \
--to=jeremy@goop.org \
--cc=ak@suse.de \
--cc=akpm@linux-foundation.org \
--cc=anthony@codemonkey.ws \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@elte.hu \
--cc=virtualization@lists.osdl.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).