# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1260268388 0
# Node ID 2d92ad3ef517208240aa3d00e22516cf885ef351
# Parent 3122518646d3ac44e4d0c76ac83228acc4d31dbe
hvm: Pull SVM ASID management into common HVM code where it can be shared.
Signed-off-by: Keir Fraser <keir.fraser@xxxxxxxxxx>
---
xen/arch/x86/hvm/Makefile | 1
xen/arch/x86/hvm/asid.c | 150 +++++++++++++++++++++++++++
xen/arch/x86/hvm/svm/asid.c | 204 ++-----------------------------------
xen/arch/x86/hvm/svm/svm.c | 6 -
xen/arch/x86/hvm/svm/vmcb.c | 2
xen/include/asm-x86/hvm/asid.h | 50 +++++++++
xen/include/asm-x86/hvm/svm/asid.h | 6 -
xen/include/asm-x86/hvm/svm/vmcb.h | 1
xen/include/asm-x86/hvm/vcpu.h | 3
9 files changed, 224 insertions(+), 199 deletions(-)
diff -r 3122518646d3 -r 2d92ad3ef517 xen/arch/x86/hvm/Makefile
--- a/xen/arch/x86/hvm/Makefile Tue Dec 08 07:55:21 2009 +0000
+++ b/xen/arch/x86/hvm/Makefile Tue Dec 08 10:33:08 2009 +0000
@@ -1,6 +1,7 @@ subdir-y += svm
subdir-y += svm
subdir-y += vmx
+obj-y += asid.o
obj-y += emulate.o
obj-y += hpet.o
obj-y += hvm.o
diff -r 3122518646d3 -r 2d92ad3ef517 xen/arch/x86/hvm/asid.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/arch/x86/hvm/asid.c Tue Dec 08 10:33:08 2009 +0000
@@ -0,0 +1,150 @@
+/*
+ * asid.c: ASID management
+ * Copyright (c) 2007, Advanced Micro Devices, Inc.
+ * Copyright (c) 2009, Citrix Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <xen/config.h>
+#include <xen/init.h>
+#include <xen/lib.h>
+#include <xen/perfc.h>
+#include <asm/hvm/asid.h>
+
+/*
+ * ASIDs partition the physical TLB. In the current implementation ASIDs are
+ * introduced to reduce the number of TLB flushes. Each time the guest's
+ * virtual address space changes (e.g. due to an INVLPG, MOV-TO-{CR3, CR4}
+ * operation), instead of flushing the TLB, a new ASID is assigned. This
+ * reduces the number of TLB flushes to at most 1/#ASIDs. The biggest
+ * advantage is that hot parts of the hypervisor's code and data retain in
+ * the TLB.
+ *
+ * Sketch of the Implementation:
+ *
+ * ASIDs are a CPU-local resource. As preemption of ASIDs is not possible,
+ * ASIDs are assigned in a round-robin scheme. To minimize the overhead of
+ * ASID invalidation, at the time of a TLB flush, ASIDs are tagged with a
+ * 64-bit generation. Only on a generation overflow the code needs to
+ * invalidate all ASID information stored at the VCPUs with are run on the
+ * specific physical processor. This overflow appears after about 2^80
+ * host processor cycles, so we do not optimize this case, but simply disable
+ * ASID useage to retain correctness.
+ */
+
+/* Per-CPU ASID management. */
+struct hvm_asid_data {
+ u64 core_asid_generation;
+ u32 next_asid;
+ u32 max_asid;
+ bool_t disabled;
+ bool_t initialised;
+};
+
+static DEFINE_PER_CPU(struct hvm_asid_data, hvm_asid_data);
+
+void hvm_asid_init(int nasids)
+{
+ struct hvm_asid_data *data = &this_cpu(hvm_asid_data);
+
+ /*
+ * If already initialised, we just bump the generation to force a TLB
+ * flush. Resetting the generation could be dangerous, if VCPUs still
+ * exist that reference earlier generations on this CPU.
+ */
+ if ( test_and_set_bool(data->initialised) )
+ return hvm_asid_flush_core();
+
+ data->max_asid = nasids - 1;
+ data->disabled = (nasids <= 1);
+
+ printk("HVM: ASIDs %s \n",
+ (data->disabled ? "disabled." : "enabled."));
+
+ /* Zero indicates 'invalid generation', so we start the count at one. */
+ data->core_asid_generation = 1;
+
+ /* Zero indicates 'ASIDs disabled', so we start the count at one. */
+ data->next_asid = 1;
+}
+
+void hvm_asid_invalidate_asid(struct vcpu *v)
+{
+ v->arch.hvm_vcpu.asid_generation = 0;
+}
+
+void hvm_asid_flush_core(void)
+{
+ struct hvm_asid_data *data = &this_cpu(hvm_asid_data);
+
+ if ( data->disabled )
+ return;
+
+ if ( likely(++data->core_asid_generation != 0) )
+ {
+ data->next_asid = 1;
+ return;
+ }
+
+ /*
+ * ASID generations are 64 bit. Overflow of generations never happens.
+ * For safety, we simply disable ASIDs, so correctness is established; it
+ * only runs a bit slower.
+ */
+ printk("HVM: ASID generation overrun. Disabling ASIDs.\n");
+ data->disabled = 1;
+}
+
+bool_t hvm_asid_handle_vmenter(void)
+{
+ struct vcpu *curr = current;
+ struct hvm_asid_data *data = &this_cpu(hvm_asid_data);
+
+ /* On erratum #170 systems we must flush the TLB.
+ * Generation overruns are taken here, too. */
+ if ( data->disabled )
+ {
+ curr->arch.hvm_vcpu.asid = 0;
+ return 0;
+ }
+
+ /* Test if VCPU has valid ASID. */
+ if ( curr->arch.hvm_vcpu.asid_generation == data->core_asid_generation )
+ return 0;
+
+ /* If there are no free ASIDs, need to go to a new generation */
+ if ( unlikely(data->next_asid > data->max_asid) )
+ hvm_asid_flush_core();
+
+ /* Now guaranteed to be a free ASID. */
+ curr->arch.hvm_vcpu.asid = data->next_asid++;
+ curr->arch.hvm_vcpu.asid_generation = data->core_asid_generation;
+
+ /*
+ * When we assign ASID 1, flush all TLB entries as we are starting a new
+ * generation, and all old ASID allocations are now stale.
+ */
+ return (curr->arch.hvm_vcpu.asid == 1);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff -r 3122518646d3 -r 2d92ad3ef517 xen/arch/x86/hvm/svm/asid.c
--- a/xen/arch/x86/hvm/svm/asid.c Tue Dec 08 07:55:21 2009 +0000
+++ b/xen/arch/x86/hvm/svm/asid.c Tue Dec 08 10:33:08 2009 +0000
@@ -22,164 +22,16 @@
#include <xen/perfc.h>
#include <asm/hvm/svm/asid.h>
-/*
- * This is the interface to SVM's ASID management. ASIDs partition the
- * physical TLB for SVM. In the current implementation ASIDs are introduced
- * to reduce the number of TLB flushes. Each time the guest's virtual
- * address space changes (e.g. due to an INVLPG, MOV-TO-{CR3, CR4} operation),
- * instead of flushing the TLB, a new ASID is assigned. This reduces the
- * number of TLB flushes to at most 1/#ASIDs (currently 1/64). The biggest
- * advantage is that hot parts of the hypervisor's code and data retain in
- * the TLB.
- *
- * Sketch of the Implementation:
- *
- * ASIDs are a CPU-local resource. As preemption of ASIDs is not possible,
- * ASIDs are assigned in a round-robin scheme. To minimize the overhead of
- * ASID invalidation, at the time of a TLB flush, ASIDs are tagged with a
- * 64-bit generation. Only on a generation overflow the code needs to
- * invalidate all ASID information stored at the VCPUs with are run on the
- * specific physical processor. This overflow appears after about 2^80
- * host processor cycles, so we do not optimize this case, but simply disable
- * ASID useage to retain correctness.
- */
-
-/* usable guest asids [ 1 .. get_max_asid() ) */
-#define SVM_ASID_FIRST_GUEST_ASID 1
-
-#define SVM_ASID_FIRST_GENERATION 0
-
-/* triggers the flush of all generations on all VCPUs */
-#define SVM_ASID_LAST_GENERATION (0xfffffffffffffffd)
-
-/* triggers assignment of new ASID to a VCPU */
-#define SVM_ASID_INVALID_GENERATION (SVM_ASID_LAST_GENERATION + 1)
-
-/* Per-CPU ASID management. */
-struct svm_asid_data {
- u64 core_asid_generation;
- u32 next_asid;
- u32 max_asid;
- u32 erratum170:1;
- u32 initialised:1;
-};
-
-static DEFINE_PER_CPU(struct svm_asid_data, svm_asid_data);
-
-/*
- * Get handle to CPU-local ASID management data.
- */
-static struct svm_asid_data *svm_asid_core_data(void)
-{
- return &this_cpu(svm_asid_data);
-}
-
-/*
- * Init ASID management for the current physical CPU.
- */
void svm_asid_init(struct cpuinfo_x86 *c)
{
- int nasids;
- struct svm_asid_data *data = svm_asid_core_data();
+ int nasids = 0;
- /*
- * If already initialised, we just bump the generation to force a TLB
- * flush. Resetting the generation could be dangerous, if VCPUs still
- * exist that reference earlier generations on this CPU.
- */
- if ( data->initialised )
- return svm_asid_inc_generation();
- data->initialised = 1;
+ /* Check for erratum #170, and leave ASIDs disabled if it's present. */
+ if ( (c->x86 == 0x10) ||
+ ((c->x86 == 0xf) && (c->x86_model >= 0x68) && (c->x86_mask >= 1)) )
+ nasids = cpuid_ebx(0x8000000A);
- /* Find #ASID. */
- nasids = cpuid_ebx(0x8000000A);
- data->max_asid = nasids - 1;
-
- /* Check if we can use ASIDs. */
- data->erratum170 =
- !((c->x86 == 0x10) ||
- ((c->x86 == 0xf) && (c->x86_model >= 0x68) && (c->x86_mask >= 1)));
-
- printk("AMD SVM: ASIDs %s \n",
- (data->erratum170 ? "disabled." : "enabled."));
-
- /* Initialize ASID assigment. */
- if ( data->erratum170 )
- {
- /* On errata #170, VCPUs and phys processors should have same
- generation. We set both to invalid. */
- data->core_asid_generation = SVM_ASID_INVALID_GENERATION;
- }
- else
- {
- data->core_asid_generation = SVM_ASID_FIRST_GENERATION;
- }
-
- /* ASIDs are assigned round-robin. Start with the first. */
- data->next_asid = SVM_ASID_FIRST_GUEST_ASID;
-}
-
-/*
- * Force VCPU to fetch a new ASID.
- */
-void svm_asid_init_vcpu(struct vcpu *v)
-{
- struct svm_asid_data *data = svm_asid_core_data();
-
- /* Trigger asignment of a new ASID. */
- v->arch.hvm_svm.asid_generation = SVM_ASID_INVALID_GENERATION;
-
- /*
- * This erratum is bound to a physical processor. The tlb_control
- * field is not changed by the processor. We only set tlb_control
- * on VMCB creation and on a migration.
- */
- if ( data->erratum170 )
- {
- /* Flush TLB every VMRUN to handle Errata #170. */
- v->arch.hvm_svm.vmcb->tlb_control = 1;
- /* All guests use same ASID. */
- v->arch.hvm_svm.vmcb->guest_asid = 1;
- }
- else
- {
- /* These fields are handled on VMRUN */
- v->arch.hvm_svm.vmcb->tlb_control = 0;
- v->arch.hvm_svm.vmcb->guest_asid = 0;
- }
-}
-
-/*
- * Increase the Generation to make free ASIDs, and indirectly cause a
- * TLB flush of all ASIDs on the next vmrun.
- */
-void svm_asid_inc_generation(void)
-{
- struct svm_asid_data *data = svm_asid_core_data();
-
- if ( likely(data->core_asid_generation < SVM_ASID_LAST_GENERATION) )
- {
- /* Move to the next generation. We can't flush the TLB now
- * because you need to vmrun to do that, and current might not
- * be a HVM vcpu, but the first HVM vcpu that runs after this
- * will pick up ASID 1 and flush the TLBs. */
- data->core_asid_generation++;
- data->next_asid = SVM_ASID_FIRST_GUEST_ASID;
- return;
- }
-
- /*
- * ASID generations are 64 bit. Overflow of generations never happens.
- * For safety, we simply disable ASIDs and switch to erratum #170 mode on
- * this core (flushing TLB always). So correctness is established; it
- * only runs a bit slower.
- */
- if ( !data->erratum170 )
- {
- printk("AMD SVM: ASID generation overrun. Disabling ASIDs.\n");
- data->erratum170 = 1;
- data->core_asid_generation = SVM_ASID_INVALID_GENERATION;
- }
+ hvm_asid_init(nasids);
}
/*
@@ -188,47 +40,19 @@ void svm_asid_inc_generation(void)
*/
asmlinkage void svm_asid_handle_vmrun(void)
{
- struct vcpu *v = current;
- struct svm_asid_data *data = svm_asid_core_data();
+ struct vcpu *curr = current;
+ bool_t need_flush = hvm_asid_handle_vmenter();
- /* On erratum #170 systems we must flush the TLB.
- * Generation overruns are taken here, too. */
- if ( data->erratum170 )
+ /* ASID 0 indicates that ASIDs are disabled. */
+ if ( curr->arch.hvm_vcpu.asid == 0 )
{
- v->arch.hvm_svm.vmcb->guest_asid = 1;
- v->arch.hvm_svm.vmcb->tlb_control = 1;
+ curr->arch.hvm_svm.vmcb->guest_asid = 1;
+ curr->arch.hvm_svm.vmcb->tlb_control = 1;
return;
}
- /* Test if VCPU has valid ASID. */
- if ( likely(v->arch.hvm_svm.asid_generation ==
- data->core_asid_generation) )
- {
- /* May revert previous TLB-flush command. */
- v->arch.hvm_svm.vmcb->tlb_control = 0;
- return;
- }
-
- /* If there are no free ASIDs, need to go to a new generation */
- if ( unlikely(data->next_asid > data->max_asid) )
- svm_asid_inc_generation();
-
- /* Now guaranteed to be a free ASID. */
- v->arch.hvm_svm.vmcb->guest_asid = data->next_asid++;
- v->arch.hvm_svm.asid_generation = data->core_asid_generation;
-
- /* When we assign ASID 1, flush all TLB entries. We need to do it
- * here because svm_asid_inc_generation() can be called at any time,
- * but the TLB flush can only happen on vmrun. */
- if ( v->arch.hvm_svm.vmcb->guest_asid == SVM_ASID_FIRST_GUEST_ASID )
- v->arch.hvm_svm.vmcb->tlb_control = 1;
- else
- v->arch.hvm_svm.vmcb->tlb_control = 0;
-}
-
-void svm_asid_inv_asid(struct vcpu *v)
-{
- v->arch.hvm_svm.asid_generation = SVM_ASID_INVALID_GENERATION;
+ curr->arch.hvm_svm.vmcb->guest_asid = curr->arch.hvm_vcpu.asid;
+ curr->arch.hvm_svm.vmcb->tlb_control = need_flush;
}
/*
diff -r 3122518646d3 -r 2d92ad3ef517 xen/arch/x86/hvm/svm/svm.c
--- a/xen/arch/x86/hvm/svm/svm.c Tue Dec 08 07:55:21 2009 +0000
+++ b/xen/arch/x86/hvm/svm/svm.c Tue Dec 08 10:33:08 2009 +0000
@@ -424,7 +424,7 @@ static void svm_update_guest_cr(struct v
break;
case 3:
vmcb->cr3 = v->arch.hvm_vcpu.hw_cr[3];
- svm_asid_inv_asid(v);
+ hvm_asid_invalidate_asid(v);
break;
case 4:
vmcb->cr4 = HVM_CR4_HOST_MASK;
@@ -460,7 +460,7 @@ static void svm_flush_guest_tlbs(void)
/* Roll over the CPU's ASID generation, so it gets a clean TLB when we
* next VMRUN. (If ASIDs are disabled, the whole TLB is flushed on
* VMRUN anyway). */
- svm_asid_inc_generation();
+ hvm_asid_flush_core();
}
static void svm_sync_vmcb(struct vcpu *v)
@@ -704,7 +704,7 @@ static void svm_do_resume(struct vcpu *v
hvm_migrate_timers(v);
/* Migrating to another ASID domain. Request a new ASID. */
- svm_asid_init_vcpu(v);
+ hvm_asid_invalidate_asid(v);
}
/* Reflect the vlapic's TPR in the hardware vtpr */
diff -r 3122518646d3 -r 2d92ad3ef517 xen/arch/x86/hvm/svm/vmcb.c
--- a/xen/arch/x86/hvm/svm/vmcb.c Tue Dec 08 07:55:21 2009 +0000
+++ b/xen/arch/x86/hvm/svm/vmcb.c Tue Dec 08 10:33:08 2009 +0000
@@ -115,7 +115,7 @@ static int construct_vmcb(struct vcpu *v
struct vmcb_struct *vmcb = arch_svm->vmcb;
/* TLB control, and ASID assigment. */
- svm_asid_init_vcpu(v);
+ hvm_asid_invalidate_asid(v);
vmcb->general1_intercepts =
GENERAL1_INTERCEPT_INTR | GENERAL1_INTERCEPT_NMI |
diff -r 3122518646d3 -r 2d92ad3ef517 xen/include/asm-x86/hvm/asid.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/include/asm-x86/hvm/asid.h Tue Dec 08 10:33:08 2009 +0000
@@ -0,0 +1,50 @@
+/*
+ * asid.h: ASID management
+ * Copyright (c) 2007, Advanced Micro Devices, Inc.
+ * Copyright (c) 2009, Citrix Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef __ASM_X86_HVM_ASID_H__
+#define __ASM_X86_HVM_ASID_H__
+
+#include <xen/config.h>
+#include <xen/sched.h>
+#include <asm/processor.h>
+
+/* Initialise ASID management for the current physical CPU. */
+void hvm_asid_init(int nasids);
+
+/* Invalidate a VCPU's current ASID allocation: forces re-allocation. */
+void hvm_asid_invalidate_asid(struct vcpu *v);
+
+/* Flush all ASIDs on this processor core. */
+void hvm_asid_flush_core(void);
+
+/* Called before entry to guest context. Checks ASID allocation, returns a
+ * boolean indicating whether all ASIDs must be flushed. */
+bool_t hvm_asid_handle_vmenter(void);
+
+#endif /* __ASM_X86_HVM_ASID_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff -r 3122518646d3 -r 2d92ad3ef517 xen/include/asm-x86/hvm/svm/asid.h
--- a/xen/include/asm-x86/hvm/svm/asid.h Tue Dec 08 07:55:21 2009 +0000
+++ b/xen/include/asm-x86/hvm/svm/asid.h Tue Dec 08 10:33:08 2009 +0000
@@ -22,15 +22,13 @@
#include <xen/config.h>
#include <asm/types.h>
#include <asm/hvm/hvm.h>
+#include <asm/hvm/asid.h>
#include <asm/hvm/support.h>
#include <asm/hvm/svm/svm.h>
#include <asm/hvm/svm/vmcb.h>
#include <asm/percpu.h>
void svm_asid_init(struct cpuinfo_x86 *c);
-void svm_asid_init_vcpu(struct vcpu *v);
-void svm_asid_inv_asid(struct vcpu *v);
-void svm_asid_inc_generation(void);
static inline void svm_asid_g_invlpg(struct vcpu *v, unsigned long g_vaddr)
{
@@ -43,7 +41,7 @@ static inline void svm_asid_g_invlpg(str
#endif
/* Safe fallback. Take a new ASID. */
- svm_asid_inv_asid(v);
+ hvm_asid_invalidate_asid(v);
}
#endif /* __ASM_X86_HVM_SVM_ASID_H__ */
diff -r 3122518646d3 -r 2d92ad3ef517 xen/include/asm-x86/hvm/svm/vmcb.h
--- a/xen/include/asm-x86/hvm/svm/vmcb.h Tue Dec 08 07:55:21 2009 +0000
+++ b/xen/include/asm-x86/hvm/svm/vmcb.h Tue Dec 08 10:33:08 2009 +0000
@@ -457,7 +457,6 @@ struct arch_svm_struct {
struct arch_svm_struct {
struct vmcb_struct *vmcb;
u64 vmcb_pa;
- u64 asid_generation; /* ASID tracking, moved here for cache locality. */
unsigned long *msrpm;
int launch_core;
bool_t vmcb_in_sync; /* VMCB sync'ed with VMSAVE? */
diff -r 3122518646d3 -r 2d92ad3ef517 xen/include/asm-x86/hvm/vcpu.h
--- a/xen/include/asm-x86/hvm/vcpu.h Tue Dec 08 07:55:21 2009 +0000
+++ b/xen/include/asm-x86/hvm/vcpu.h Tue Dec 08 10:33:08 2009 +0000
@@ -70,6 +70,9 @@ struct hvm_vcpu {
bool_t debug_state_latch;
bool_t single_step;
+ u64 asid_generation;
+ u32 asid;
+
union {
struct arch_vmx_struct vmx;
struct arch_svm_struct svm;
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|