[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [RFC-for-4.20 v2 1/1] x86/hvm: Introduce Xen-wide ASID allocator
Currently ASID generation and management is done per-PCPU. This scheme is incompatible with SEV technologies as SEV VMs need to have a fixed ASID associated with all vcpus of the VM throughout it's lifetime. This commit introduces a Xen-wide allocator which initializes the asids at the start of xen and allows to have a fixed asids throughout the lifecycle of all domains. Having a fixed asid for non-SEV domains also presents us with the opportunity to further take use of AMD instructions like TLBSYNC and INVLPGB for broadcasting the TLB invalidations. Signed-off-by: Vaishali Thakkar <vaishali.thakkar@xxxxxxxxxx> --- Changes since v1: - Introudce hvm_asid_bitmap as discussed at Xen-summit - Introduce hvm_reclaim_bitmap for reusing ASIDs - Assign the asid to the domain at the domain creation via hvm_asid_domain_create - Corrected the use of CPUID in the svm_asid_init function - Adjusted the code in nested virtualization related files to use new scheme. As discussed at the Xen-summit, this is not tested. - Addressed Jan's comments about using uniform style for accessing domains via v->domain - Allow to flush at the vcpu level in HAP code - Documented the sketch of implementation for the new scheme - Remove min_asid as for this patch, we are not demonstarting it's usecase - Arrange includes in multiple files as per Jan's feedback --- xen/arch/x86/domain.c | 7 ++ xen/arch/x86/flushtlb.c | 4 - xen/arch/x86/hvm/asid.c | 136 +++++++++++++------------ xen/arch/x86/hvm/hvm.c | 6 +- xen/arch/x86/hvm/nestedhvm.c | 4 +- xen/arch/x86/hvm/svm/asid.c | 37 ++++--- xen/arch/x86/hvm/svm/nestedsvm.c | 5 +- xen/arch/x86/hvm/svm/svm.c | 32 ++---- xen/arch/x86/hvm/svm/svm.h | 3 - xen/arch/x86/hvm/vmx/vmcs.c | 2 - xen/arch/x86/hvm/vmx/vmx.c | 37 ++----- xen/arch/x86/hvm/vmx/vvmx.c | 7 +- xen/arch/x86/include/asm/hvm/asid.h | 24 ++--- xen/arch/x86/include/asm/hvm/domain.h | 7 ++ xen/arch/x86/include/asm/hvm/hvm.h | 11 -- xen/arch/x86/include/asm/hvm/svm/svm.h | 2 + xen/arch/x86/include/asm/hvm/vcpu.h | 9 -- xen/arch/x86/include/asm/hvm/vmx/vmx.h | 3 +- xen/arch/x86/mm/hap/hap.c | 5 +- xen/arch/x86/mm/p2m.c | 6 +- xen/arch/x86/mm/paging.c | 3 +- xen/arch/x86/pv/domain.c | 1 + xen/arch/x86/setup.c | 10 ++ 23 files changed, 173 insertions(+), 188 deletions(-) diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c index d977ec71ca..75868a2063 100644 --- a/xen/arch/x86/domain.c +++ b/xen/arch/x86/domain.c @@ -52,6 +52,7 @@ #include <asm/ldt.h> #include <asm/hvm/hvm.h> #include <asm/hvm/nestedhvm.h> +#include <asm/hvm/asid.h> #include <asm/hvm/svm/svm.h> #include <asm/hvm/viridian.h> #include <asm/debugreg.h> @@ -784,6 +785,7 @@ int arch_domain_create(struct domain *d, unsigned int flags) { bool paging_initialised = false; + struct hvm_domain_asid *p_asid; uint32_t emflags; int rc; @@ -898,6 +900,11 @@ int arch_domain_create(struct domain *d, spec_ctrl_init_domain(d); + if ( is_hvm_domain(d) ) { + p_asid = &d->arch.hvm.n1asid; + hvm_asid_domain_create(p_asid); + } + return 0; fail: diff --git a/xen/arch/x86/flushtlb.c b/xen/arch/x86/flushtlb.c index 18748b2bc8..7316b177d7 100644 --- a/xen/arch/x86/flushtlb.c +++ b/xen/arch/x86/flushtlb.c @@ -124,7 +124,6 @@ void switch_cr3_cr4(unsigned long cr3, unsigned long cr4) if ( tlb_clk_enabled ) t = pre_flush(); - hvm_flush_guest_tlbs(); old_cr4 = read_cr4(); ASSERT(!(old_cr4 & X86_CR4_PCIDE) || !(old_cr4 & X86_CR4_PGE)); @@ -229,9 +228,6 @@ unsigned int flush_area_local(const void *va, unsigned int flags) do_tlb_flush(); } - if ( flags & FLUSH_HVM_ASID_CORE ) - hvm_flush_guest_tlbs(); - if ( flags & FLUSH_CACHE ) { const struct cpuinfo_x86 *c = ¤t_cpu_data; diff --git a/xen/arch/x86/hvm/asid.c b/xen/arch/x86/hvm/asid.c index 8d27b7dba1..a916cb865b 100644 --- a/xen/arch/x86/hvm/asid.c +++ b/xen/arch/x86/hvm/asid.c @@ -11,51 +11,66 @@ #include <xen/sched.h> #include <xen/smp.h> #include <xen/percpu.h> + +#include <asm/bitops.h> #include <asm/hvm/asid.h> /* Xen command-line option to enable ASIDs */ static bool __read_mostly opt_asid_enabled = true; boolean_param("asid", opt_asid_enabled); +unsigned long *hvm_asid_bitmap; +unsigned long *hvm_reclaim_asid_bitmap; +static DEFINE_SPINLOCK(hvm_asid_lock); + /* - * 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. + * ASIDs are assigned in a round-robin scheme per domain as part of + * global allocator scheme and doesn't change during the lifecycle of + * the domain. Once vcpus are initialized and are up, we assign the + * same ASID to all vcpus of that domain at the first VMRUN. With the + * new scheme, we don't need to assign the new ASID during MOV-TO-{CR3, + * CR4}. In the case of INVLPG, we flush the TLB entries belonging to + * the vcpu and do the reassignment of the ASID belonging to the given + * domain. Currently we do not do anything special for flushing guest + * TLBs in flush_area_local as wbinvd() should able to handle this. In + * the future Xen should be able to take an advantage of TLBSYNC and + * INVLPGB (AMD SVM) with this scheme. + + * When the domain is destroyed, ASID goes to the reclaimable ASID pool + * for the reuse. We only go for checking in the reclaimable ASID pool + * when we run out of ASIDs in the hvm_asid_bitmap. */ -/* Per-CPU ASID management. */ +/* Xen-wide ASID management */ struct hvm_asid_data { - uint64_t core_asid_generation; uint32_t next_asid; uint32_t max_asid; bool disabled; }; -static DEFINE_PER_CPU(struct hvm_asid_data, hvm_asid_data); +static struct hvm_asid_data asid_data; -void hvm_asid_init(int nasids) +int hvm_asid_init(int nasids) { + struct hvm_asid_data *data = &asid_data; static int8_t g_disabled = -1; - struct hvm_asid_data *data = &this_cpu(hvm_asid_data); data->max_asid = nasids - 1; data->disabled = !opt_asid_enabled || (nasids <= 1); + hvm_asid_bitmap = xzalloc_array(unsigned long, + BITS_TO_LONGS(data->max_asid)); + if ( !hvm_asid_bitmap ) + return -ENOMEM; + + hvm_reclaim_asid_bitmap = xzalloc_array(unsigned long, + BITS_TO_LONGS(data->max_asid)); + if ( !hvm_reclaim_asid_bitmap ) { + xfree(hvm_asid_bitmap); + hvm_asid_bitmap = NULL; + } + if ( g_disabled != data->disabled ) { printk("HVM: ASIDs %sabled.\n", data->disabled ? "dis" : "en"); @@ -63,74 +78,67 @@ void hvm_asid_init(int nasids) g_disabled = data->disabled; } - /* Zero indicates 'invalid generation', so we start the count at one. */ - data->core_asid_generation = 1; + /* ASID 0 is reserved, so we start the counting from 1 */ + data->next_asid = find_first_zero_bit(hvm_asid_bitmap, data->max_asid ) + 1; - /* Zero indicates 'ASIDs disabled', so we start the count at one. */ - data->next_asid = 1; + return 0; } -void hvm_asid_flush_vcpu_asid(struct hvm_vcpu_asid *asid) +void hvm_asid_flush_domain_asid(struct hvm_domain_asid *asid) { write_atomic(&asid->generation, 0); } -void hvm_asid_flush_vcpu(struct vcpu *v) +void hvm_asid_flush_domain(struct domain *d) { - hvm_asid_flush_vcpu_asid(&v->arch.hvm.n1asid); - hvm_asid_flush_vcpu_asid(&vcpu_nestedhvm(v).nv_n2asid); + hvm_asid_flush_domain_asid(&d->arch.hvm.n1asid); + hvm_asid_flush_domain_asid(&d->arch.hvm.nv_n2asid); } -void hvm_asid_flush_core(void) +/* We still allow flushing on vcpu level for non-SEV domain */ +void hvm_asid_flush_vcpu(struct vcpu *v) { - struct hvm_asid_data *data = &this_cpu(hvm_asid_data); - - if ( data->disabled ) - return; - - if ( likely(++data->core_asid_generation != 0) ) - 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; + hvm_asid_flush_domain_asid(&v->domain->arch.hvm.n1asid); + hvm_asid_flush_domain_asid(&v->domain->arch.hvm.nv_n2asid); } -bool hvm_asid_handle_vmenter(struct hvm_vcpu_asid *asid) +/* This function is called while creating a new domain */ +bool hvm_asid_domain_create(struct hvm_domain_asid *asid) { - struct hvm_asid_data *data = &this_cpu(hvm_asid_data); + struct hvm_asid_data *data = &asid_data; /* On erratum #170 systems we must flush the TLB. * Generation overruns are taken here, too. */ if ( data->disabled ) goto disabled; - /* Test if VCPU has valid ASID. */ - if ( read_atomic(&asid->generation) == data->core_asid_generation ) - return 0; + spin_lock(&hvm_asid_lock); + + /* We assume that next_asid > max_asid is unlikely at this point*/ + arch__test_and_set_bit(data->next_asid, hvm_asid_bitmap); + + /* Find the next available asid to assign to the domain*/ + data->next_asid = find_next_zero_bit(hvm_asid_bitmap, data->next_asid, + data->max_asid) + 1; + + /* Check if there are any ASIDs to reclaim */ + if ( data->next_asid > data->max_asid ) { + data->next_asid = find_next_bit(hvm_reclaim_asid_bitmap, 0, + data->max_asid+1); + spin_unlock(&hvm_asid_lock); - /* 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(); - data->next_asid = 1; if ( data->disabled ) goto disabled; + + if ( data->next_asid > data->max_asid ) + return -EBUSY; } - /* Now guaranteed to be a free ASID. */ - asid->asid = data->next_asid++; - write_atomic(&asid->generation, data->core_asid_generation); + spin_unlock(&hvm_asid_lock); - /* - * 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 (asid->asid == 1); + asid->asid = data->next_asid; + + return 0; disabled: asid->asid = 0; diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c index f49e29faf7..fbcceb374e 100644 --- a/xen/arch/x86/hvm/hvm.c +++ b/xen/arch/x86/hvm/hvm.c @@ -777,6 +777,10 @@ void hvm_domain_destroy(struct domain *d) } destroy_vpci_mmcfg(d); + + /* Clear the asid and put it in the reclaimable ASID pool */ + clear_bit(d->arch.hvm.n1asid.asid, hvm_asid_bitmap); + set_bit(d->arch.hvm.n1asid.asid, hvm_reclaim_asid_bitmap); } static int cf_check hvm_save_tsc_adjust(struct vcpu *v, hvm_domain_context_t *h) @@ -1585,8 +1589,6 @@ int hvm_vcpu_initialise(struct vcpu *v) int rc; struct domain *d = v->domain; - hvm_asid_flush_vcpu(v); - spin_lock_init(&v->arch.hvm.tm_lock); INIT_LIST_HEAD(&v->arch.hvm.tm_list); diff --git a/xen/arch/x86/hvm/nestedhvm.c b/xen/arch/x86/hvm/nestedhvm.c index bddd77d810..f1d17becd6 100644 --- a/xen/arch/x86/hvm/nestedhvm.c +++ b/xen/arch/x86/hvm/nestedhvm.c @@ -41,8 +41,6 @@ nestedhvm_vcpu_reset(struct vcpu *v) nv->stale_np2m = false; nv->np2m_generation = 0; - hvm_asid_flush_vcpu_asid(&nv->nv_n2asid); - alternative_vcall(hvm_funcs.nhvm_vcpu_reset, v); /* vcpu is in host mode */ @@ -86,7 +84,7 @@ static void cf_check nestedhvm_flushtlb_ipi(void *info) * This is cheaper than flush_tlb_local() and has * the same desired effect. */ - hvm_asid_flush_core(); + hvm_asid_flush_domain(d); vcpu_nestedhvm(v).nv_p2m = NULL; vcpu_nestedhvm(v).stale_np2m = true; } diff --git a/xen/arch/x86/hvm/svm/asid.c b/xen/arch/x86/hvm/svm/asid.c index 7977a8e86b..63f4d15a15 100644 --- a/xen/arch/x86/hvm/svm/asid.c +++ b/xen/arch/x86/hvm/svm/asid.c @@ -4,16 +4,23 @@ * Copyright (c) 2007, Advanced Micro Devices, Inc. */ +#include <xen/cpumask.h> + +#include <asm/processor.h> #include <asm/amd.h> #include <asm/hvm/nestedhvm.h> #include <asm/hvm/svm/svm.h> #include "svm.h" -void svm_asid_init(const struct cpuinfo_x86 *c) +void __init svm_asid_init(void) { + unsigned int cpu = smp_processor_id(); + const struct cpuinfo_x86 *c; int nasids = 0; + c = &cpu_data[cpu]; + /* Check for erratum #170, and leave ASIDs disabled if it's present. */ if ( !cpu_has_amd_erratum(c, AMD_ERRATUM_170) ) nasids = cpuid_ebx(0x8000000aU); @@ -22,33 +29,31 @@ void svm_asid_init(const struct cpuinfo_x86 *c) } /* - * Called directly before VMRUN. Checks if the VCPU needs a new ASID, - * assigns it, and if required, issues required TLB flushes. + * Called directly at the VMRUN of a domain to assign + * the asid to all associated vcpus of that domain */ -void svm_asid_handle_vmrun(void) +void svm_vcpu_assign_asid(struct vcpu *v) { - struct vcpu *curr = current; - struct vmcb_struct *vmcb = curr->arch.hvm.svm.vmcb; - struct hvm_vcpu_asid *p_asid = - nestedhvm_vcpu_in_guestmode(curr) - ? &vcpu_nestedhvm(curr).nv_n2asid : &curr->arch.hvm.n1asid; - bool need_flush = hvm_asid_handle_vmenter(p_asid); + struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb; + struct hvm_domain_asid *p_asid = &v->domain->arch.hvm.n1asid; /* ASID 0 indicates that ASIDs are disabled. */ if ( p_asid->asid == 0 ) { vmcb_set_asid(vmcb, true); vmcb->tlb_control = - cpu_has_svm_flushbyasid ? TLB_CTRL_FLUSH_ASID : TLB_CTRL_FLUSH_ALL; + cpu_has_svm_flushbyasid ? TLB_CTRL_FLUSH_ASID : + TLB_CTRL_FLUSH_ALL; return; } - if ( vmcb_get_asid(vmcb) != p_asid->asid ) + if ( vmcb_get_asid(vmcb) != p_asid->asid ) { vmcb_set_asid(vmcb, p_asid->asid); - - vmcb->tlb_control = - !need_flush ? TLB_CTRL_NO_FLUSH : - cpu_has_svm_flushbyasid ? TLB_CTRL_FLUSH_ASID : TLB_CTRL_FLUSH_ALL; + vmcb->tlb_control = cpu_has_svm_flushbyasid ? TLB_CTRL_FLUSH_ASID : + TLB_CTRL_FLUSH_ALL; + } + else + return; } /* diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c index 35a2cbfd7d..102b1a9328 100644 --- a/xen/arch/x86/hvm/svm/nestedsvm.c +++ b/xen/arch/x86/hvm/svm/nestedsvm.c @@ -486,7 +486,7 @@ static int nsvm_vmcb_prepare4vmrun(struct vcpu *v, struct cpu_user_regs *regs) if ( rc ) return rc; - /* ASID - Emulation handled in hvm_asid_handle_vmenter() */ + /* ASID - Emulation handled in hvm_asid_domain_create */ /* TLB control */ n2vmcb->tlb_control = ns_vmcb->tlb_control; @@ -681,6 +681,7 @@ nsvm_vcpu_vmentry(struct vcpu *v, struct cpu_user_regs *regs, struct nestedvcpu *nv = &vcpu_nestedhvm(v); struct nestedsvm *svm = &vcpu_nestedsvm(v); struct vmcb_struct *ns_vmcb; + struct domain *d = v->domain; ns_vmcb = nv->nv_vvmcx; ASSERT(ns_vmcb != NULL); @@ -699,7 +700,7 @@ nsvm_vcpu_vmentry(struct vcpu *v, struct cpu_user_regs *regs, if ( svm->ns_asid != vmcb_get_asid(ns_vmcb)) { nv->nv_flushp2m = 1; - hvm_asid_flush_vcpu_asid(&vcpu_nestedhvm(v).nv_n2asid); + hvm_asid_flush_domain_asid(&d->arch.hvm.nv_n2asid); svm->ns_asid = vmcb_get_asid(ns_vmcb); } diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c index 92bb10c504..49fac7782f 100644 --- a/xen/arch/x86/hvm/svm/svm.c +++ b/xen/arch/x86/hvm/svm/svm.c @@ -25,6 +25,7 @@ #include <asm/hvm/monitor.h> #include <asm/hvm/nestedhvm.h> #include <asm/hvm/support.h> +#include <asm/hvm/asid.h> #include <asm/hvm/svm/svm.h> #include <asm/hvm/svm/svmdebug.h> #include <asm/hvm/svm/vmcb.h> @@ -179,17 +180,7 @@ static void cf_check svm_update_guest_cr( break; case 3: vmcb_set_cr3(vmcb, v->arch.hvm.hw_cr[3]); - if ( !nestedhvm_enabled(v->domain) ) - { - if ( !(flags & HVM_UPDATE_GUEST_CR3_NOFLUSH) ) - hvm_asid_flush_vcpu(v); - } - else if ( nestedhvm_vmswitch_in_progress(v) ) - ; /* CR3 switches during VMRUN/VMEXIT do not flush the TLB. */ - else if ( !(flags & HVM_UPDATE_GUEST_CR3_NOFLUSH) ) - hvm_asid_flush_vcpu_asid( - nestedhvm_vcpu_in_guestmode(v) - ? &vcpu_nestedhvm(v).nv_n2asid : &v->arch.hvm.n1asid); + /* Do not flush during the CR3 switch */ break; case 4: value = HVM_CR4_HOST_MASK; @@ -989,8 +980,6 @@ static void noreturn cf_check svm_do_resume(void) v->arch.hvm.svm.launch_core = smp_processor_id(); hvm_migrate_timers(v); hvm_migrate_pirqs(v); - /* Migrating to another ASID domain. Request a new ASID. */ - hvm_asid_flush_vcpu(v); } if ( !vcpu_guestmode && !vlapic_hw_disabled(vlapic) ) @@ -1017,7 +1006,7 @@ void asmlinkage svm_vmenter_helper(void) ASSERT(hvmemul_cache_disabled(curr)); - svm_asid_handle_vmrun(); + svm_vcpu_assign_asid(curr); TRACE_TIME(TRC_HVM_VMENTRY | (nestedhvm_vcpu_in_guestmode(curr) ? TRC_HVM_NESTEDFLAG : 0)); @@ -1571,9 +1560,6 @@ static int _svm_cpu_up(bool bsp) /* check for erratum 383 */ svm_init_erratum_383(c); - /* Initialize core's ASID handling. */ - svm_asid_init(c); - /* Initialize OSVW bits to be used by guests */ svm_host_osvw_init(); @@ -2333,12 +2319,12 @@ static void svm_vmexit_do_invalidate_cache(struct cpu_user_regs *regs, } static void svm_invlpga_intercept( - struct vcpu *v, unsigned long linear, uint32_t asid) + struct domain *d, unsigned long linear, uint32_t asid) { svm_invlpga(linear, (asid == 0) - ? v->arch.hvm.n1asid.asid - : vcpu_nestedhvm(v).nv_n2asid.asid); + ? d->arch.hvm.n1asid.asid + : d->arch.hvm.nv_n2asid.asid); } static void svm_invlpg_intercept(unsigned long linear) @@ -2359,8 +2345,8 @@ static bool cf_check is_invlpg( static void cf_check svm_invlpg(struct vcpu *v, unsigned long linear) { - /* Safe fallback. Take a new ASID. */ - hvm_asid_flush_vcpu(v); + /* Flush the TLB entries belonging to the vcpu and reassign the asid. */ + hvm_asid_flush_domain(v->domain); } static bool cf_check svm_get_pending_event( @@ -2929,7 +2915,7 @@ void asmlinkage svm_vmexit_handler(void) } if ( (insn_len = svm_get_insn_len(v, INSTR_INVLPGA)) == 0 ) break; - svm_invlpga_intercept(v, regs->rax, regs->ecx); + svm_invlpga_intercept(v->domain, regs->rax, regs->ecx); __update_guest_eip(regs, insn_len); break; diff --git a/xen/arch/x86/hvm/svm/svm.h b/xen/arch/x86/hvm/svm/svm.h index 8dbf37ff49..b285e3c7d9 100644 --- a/xen/arch/x86/hvm/svm/svm.h +++ b/xen/arch/x86/hvm/svm/svm.h @@ -15,9 +15,6 @@ struct cpu_user_regs; struct cpuinfo_x86; struct vcpu; -void svm_asid_init(const struct cpuinfo_x86 *c); -void svm_asid_handle_vmrun(void); - unsigned long *svm_msrbit(unsigned long *msr_bitmap, uint32_t msr); void __update_guest_eip(struct cpu_user_regs *regs, unsigned int inst_len); diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c index 5787110a56..5585f2c4b3 100644 --- a/xen/arch/x86/hvm/vmx/vmcs.c +++ b/xen/arch/x86/hvm/vmx/vmcs.c @@ -1957,8 +1957,6 @@ void cf_check vmx_do_resume(void) * but the action of another VMCS is deferred till it is switched in. */ v->arch.hvm.vmx.hostenv_migrated = 1; - - hvm_asid_flush_vcpu(v); } debug_state = v->domain->debugger_attached diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c index 2f7add834a..382b35595a 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -1463,7 +1463,7 @@ static void cf_check vmx_handle_cd(struct vcpu *v, unsigned long value) vmx_set_msr_intercept(v, MSR_IA32_CR_PAT, VMX_MSR_RW); wbinvd(); /* flush possibly polluted cache */ - hvm_asid_flush_vcpu(v); /* invalidate memory type cached in TLB */ + hvm_asid_flush_domain(v->domain); /* invalidate memory type cached in TLB */ v->arch.hvm.cache_mode = NO_FILL_CACHE_MODE; } else @@ -1472,7 +1472,7 @@ static void cf_check vmx_handle_cd(struct vcpu *v, unsigned long value) vmx_set_guest_pat(v, *pat); if ( !is_iommu_enabled(v->domain) || iommu_snoop ) vmx_clear_msr_intercept(v, MSR_IA32_CR_PAT, VMX_MSR_RW); - hvm_asid_flush_vcpu(v); /* no need to flush cache */ + hvm_asid_flush_domain(v->domain); } } } @@ -1832,8 +1832,6 @@ static void cf_check vmx_update_guest_cr( __vmwrite(GUEST_CR3, v->arch.hvm.hw_cr[3]); - if ( !(flags & HVM_UPDATE_GUEST_CR3_NOFLUSH) ) - hvm_asid_flush_vcpu(v); break; default: @@ -4809,9 +4807,8 @@ bool asmlinkage vmx_vmenter_helper(const struct cpu_user_regs *regs) { struct vcpu *curr = current; struct domain *currd = curr->domain; - u32 new_asid, old_asid; - struct hvm_vcpu_asid *p_asid; - bool need_flush; + u32 asid; + struct hvm_domain_asid *p_asid; ASSERT(hvmemul_cache_disabled(curr)); @@ -4825,36 +4822,22 @@ bool asmlinkage vmx_vmenter_helper(const struct cpu_user_regs *regs) if ( !cpu_has_vmx_vpid ) goto out; if ( nestedhvm_vcpu_in_guestmode(curr) ) - p_asid = &vcpu_nestedhvm(curr).nv_n2asid; + p_asid = &currd->arch.hvm.nv_n2asid; else - p_asid = &curr->arch.hvm.n1asid; + p_asid = &currd->arch.hvm.n1asid; - old_asid = p_asid->asid; - need_flush = hvm_asid_handle_vmenter(p_asid); - new_asid = p_asid->asid; - - if ( unlikely(new_asid != old_asid) ) + if ( unlikely(asid = p_asid->asid) ) { - __vmwrite(VIRTUAL_PROCESSOR_ID, new_asid); - if ( !old_asid && new_asid ) + __vmwrite(VIRTUAL_PROCESSOR_ID, asid); + if (!asid) { - /* VPID was disabled: now enabled. */ + /* VPID was disabled: now enabled */ curr->arch.hvm.vmx.secondary_exec_control |= SECONDARY_EXEC_ENABLE_VPID; vmx_update_secondary_exec_control(curr); } - else if ( old_asid && !new_asid ) - { - /* VPID was enabled: now disabled. */ - curr->arch.hvm.vmx.secondary_exec_control &= - ~SECONDARY_EXEC_ENABLE_VPID; - vmx_update_secondary_exec_control(curr); - } } - if ( unlikely(need_flush) ) - vpid_sync_all(); - if ( paging_mode_hap(curr->domain) ) { struct ept_data *ept = &p2m_get_hostp2m(currd)->ept; diff --git a/xen/arch/x86/hvm/vmx/vvmx.c b/xen/arch/x86/hvm/vmx/vvmx.c index c05e0e9326..facd7c3a47 100644 --- a/xen/arch/x86/hvm/vmx/vvmx.c +++ b/xen/arch/x86/hvm/vmx/vvmx.c @@ -1254,7 +1254,7 @@ static void virtual_vmentry(struct cpu_user_regs *regs) if ( nvmx->guest_vpid != new_vpid ) { - hvm_asid_flush_vcpu_asid(&vcpu_nestedhvm(v).nv_n2asid); + hvm_asid_flush_domain_asid(&v->domain->arch.hvm.nv_n2asid); nvmx->guest_vpid = new_vpid; } } @@ -2044,6 +2044,7 @@ static int nvmx_handle_invvpid(struct cpu_user_regs *regs) { struct vmx_inst_decoded decode; unsigned long vpid; + struct domain *currd = current->domain; int ret; if ( (ret = decode_vmx_inst(regs, &decode, &vpid)) != X86EMUL_OKAY ) @@ -2055,8 +2056,8 @@ static int nvmx_handle_invvpid(struct cpu_user_regs *regs) case INVVPID_INDIVIDUAL_ADDR: case INVVPID_SINGLE_CONTEXT: case INVVPID_ALL_CONTEXT: - hvm_asid_flush_vcpu_asid(&vcpu_nestedhvm(current).nv_n2asid); - break; + hvm_asid_flush_domain_asid(&currd->arch.hvm.nv_n2asid); + break; default: vmfail(regs, VMX_INSN_INVEPT_INVVPID_INVALID_OP); return X86EMUL_OKAY; diff --git a/xen/arch/x86/include/asm/hvm/asid.h b/xen/arch/x86/include/asm/hvm/asid.h index 17c58353d1..c71d17209a 100644 --- a/xen/arch/x86/include/asm/hvm/asid.h +++ b/xen/arch/x86/include/asm/hvm/asid.h @@ -8,25 +8,25 @@ #ifndef __ASM_X86_HVM_ASID_H__ #define __ASM_X86_HVM_ASID_H__ +struct hvm_domain_asid; +extern unsigned long *hvm_asid_bitmap; +extern unsigned long *hvm_reclaim_asid_bitmap; -struct vcpu; -struct hvm_vcpu_asid; -/* Initialise ASID management for the current physical CPU. */ -void hvm_asid_init(int nasids); +/* Initialise ASID management distributed across all CPUs. */ +int hvm_asid_init(int nasids); /* Invalidate a particular ASID allocation: forces re-allocation. */ -void hvm_asid_flush_vcpu_asid(struct hvm_vcpu_asid *asid); +void hvm_asid_flush_domain_asid(struct hvm_domain_asid *asid); -/* Invalidate all ASID allocations for specified VCPU: forces re-allocation. */ -void hvm_asid_flush_vcpu(struct vcpu *v); +/* Invalidate all ASID allocations for specified domain */ +void hvm_asid_flush_domain(struct domain *d); -/* Flush all ASIDs on this processor core. */ -void hvm_asid_flush_core(void); +/* Invalidate ASID allocation for the specified vcpu */ +void hvm_asid_flush_vcpu(struct vcpu *v); -/* Called before entry to guest context. Checks ASID allocation, returns a - * boolean indicating whether all ASIDs must be flushed. */ -bool hvm_asid_handle_vmenter(struct hvm_vcpu_asid *asid); +/* Called while creating a domain to assign an ASID */ +bool hvm_asid_domain_create(struct hvm_domain_asid *asid); #endif /* __ASM_X86_HVM_ASID_H__ */ diff --git a/xen/arch/x86/include/asm/hvm/domain.h b/xen/arch/x86/include/asm/hvm/domain.h index dd9d837e84..4d12bafcd1 100644 --- a/xen/arch/x86/include/asm/hvm/domain.h +++ b/xen/arch/x86/include/asm/hvm/domain.h @@ -50,6 +50,11 @@ struct hvm_pi_ops { void (*vcpu_block)(struct vcpu *v); }; +struct hvm_domain_asid { + uint64_t generation; + uint32_t asid; +}; + struct hvm_domain { /* Guest page range used for non-default ioreq servers */ struct { @@ -141,6 +146,8 @@ struct hvm_domain { } write_map; struct hvm_pi_ops pi_ops; + struct hvm_domain_asid n1asid; + struct hvm_domain_asid nv_n2asid; union { struct vmx_domain vmx; diff --git a/xen/arch/x86/include/asm/hvm/hvm.h b/xen/arch/x86/include/asm/hvm/hvm.h index 518ba5319b..afd0e580ca 100644 --- a/xen/arch/x86/include/asm/hvm/hvm.h +++ b/xen/arch/x86/include/asm/hvm/hvm.h @@ -466,17 +466,6 @@ static inline void hvm_set_tsc_offset(struct vcpu *v, uint64_t offset, alternative_vcall(hvm_funcs.set_tsc_offset, v, offset, at_tsc); } -/* - * Called to ensure than all guest-specific mappings in a tagged TLB are - * flushed; does *not* flush Xen's TLB entries, and on processors without a - * tagged TLB it will be a noop. - */ -static inline void hvm_flush_guest_tlbs(void) -{ - if ( hvm_enabled ) - hvm_asid_flush_core(); -} - static inline unsigned int hvm_get_cpl(struct vcpu *v) { diff --git a/xen/arch/x86/include/asm/hvm/svm/svm.h b/xen/arch/x86/include/asm/hvm/svm/svm.h index 4eeeb25da9..aeeae6c86c 100644 --- a/xen/arch/x86/include/asm/hvm/svm/svm.h +++ b/xen/arch/x86/include/asm/hvm/svm/svm.h @@ -21,6 +21,8 @@ bool svm_load_segs(unsigned int ldt_ents, unsigned long ldt_base, unsigned long fs_base, unsigned long gs_base, unsigned long gs_shadow); +extern void svm_asid_init(void); +extern void svm_vcpu_assign_asid(struct vcpu *v); extern u32 svm_feature_flags; #define SVM_FEATURE_NPT 0 /* Nested page table support */ diff --git a/xen/arch/x86/include/asm/hvm/vcpu.h b/xen/arch/x86/include/asm/hvm/vcpu.h index 64c7a6fede..b951e5f1dd 100644 --- a/xen/arch/x86/include/asm/hvm/vcpu.h +++ b/xen/arch/x86/include/asm/hvm/vcpu.h @@ -17,11 +17,6 @@ #include <asm/mtrr.h> #include <public/hvm/ioreq.h> -struct hvm_vcpu_asid { - uint64_t generation; - uint32_t asid; -}; - /* * We may read or write up to m512 as a number of device-model * transactions. @@ -90,8 +85,6 @@ struct nestedvcpu { bool stale_np2m; /* True when p2m_base in VMCx02 is no longer valid */ uint64_t np2m_generation; - struct hvm_vcpu_asid nv_n2asid; - bool nv_vmentry_pending; bool nv_vmexit_pending; bool nv_vmswitch_in_progress; /* true during vmentry/vmexit emulation */ @@ -152,8 +145,6 @@ struct hvm_vcpu { /* (MFN) hypervisor page table */ pagetable_t monitor_table; - struct hvm_vcpu_asid n1asid; - u64 msr_tsc_adjust; union { diff --git a/xen/arch/x86/include/asm/hvm/vmx/vmx.h b/xen/arch/x86/include/asm/hvm/vmx/vmx.h index f0ec459622..87bf7f8e0f 100644 --- a/xen/arch/x86/include/asm/hvm/vmx/vmx.h +++ b/xen/arch/x86/include/asm/hvm/vmx/vmx.h @@ -525,6 +525,7 @@ void ept_sync_domain(struct p2m_domain *p2m); static inline void vpid_sync_vcpu_gva(struct vcpu *v, unsigned long gva) { + struct domain *d = v->domain; int type = INVVPID_INDIVIDUAL_ADDR; /* @@ -544,7 +545,7 @@ static inline void vpid_sync_vcpu_gva(struct vcpu *v, unsigned long gva) type = INVVPID_ALL_CONTEXT; execute_invvpid: - __invvpid(type, v->arch.hvm.n1asid.asid, (u64)gva); + __invvpid(type, d->arch.hvm.n1asid.asid, (u64)gva); } static inline void vpid_sync_all(void) diff --git a/xen/arch/x86/mm/hap/hap.c b/xen/arch/x86/mm/hap/hap.c index d2011fde24..d52af69160 100644 --- a/xen/arch/x86/mm/hap/hap.c +++ b/xen/arch/x86/mm/hap/hap.c @@ -739,13 +739,14 @@ static bool cf_check flush_tlb(const unsigned long *vcpu_bitmap) if ( !flush_vcpu(v, vcpu_bitmap) ) continue; - hvm_asid_flush_vcpu(v); - cpu = read_atomic(&v->dirty_cpu); if ( cpu != this_cpu && is_vcpu_dirty_cpu(cpu) && v->is_running ) __cpumask_set_cpu(cpu, mask); } + printk(XENLOG_INFO "hvm_asid_flush_vcpu called in HAP code"); + hvm_asid_flush_vcpu(v); + /* * Trigger a vmexit on all pCPUs with dirty vCPU state in order to force an * ASID/VPID change and hence accomplish a guest TLB flush. Note that vCPUs diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c index 1739133fc2..c13d1dd080 100644 --- a/xen/arch/x86/mm/p2m.c +++ b/xen/arch/x86/mm/p2m.c @@ -1395,7 +1395,8 @@ p2m_flush(struct vcpu *v, struct p2m_domain *p2m) ASSERT(v->domain == p2m->domain); vcpu_nestedhvm(v).nv_p2m = NULL; p2m_flush_table(p2m); - hvm_asid_flush_vcpu(v); + printk(XENLOG_INFO "hvm_asid_flush_domain called in p2m_flush"); + hvm_asid_flush_domain(v->domain); } void @@ -1573,8 +1574,7 @@ void np2m_schedule(int dir) { if ( !np2m_valid ) { - /* This vCPU's np2m was flushed while it was not runnable */ - hvm_asid_flush_core(); + /* This vCPU's np2m was flushed while it was not runnable */ vcpu_nestedhvm(curr).nv_p2m = NULL; } else diff --git a/xen/arch/x86/mm/paging.c b/xen/arch/x86/mm/paging.c index dd47bde5ce..96ce752439 100644 --- a/xen/arch/x86/mm/paging.c +++ b/xen/arch/x86/mm/paging.c @@ -964,7 +964,8 @@ void paging_update_nestedmode(struct vcpu *v) else /* TODO: shadow-on-shadow */ v->arch.paging.nestedmode = NULL; - hvm_asid_flush_vcpu(v); + printk(XENLOG_INFO "hvm_flush_doamin called in paging_update_nestedmode"); + hvm_asid_flush_domain(v->domain); } int __init paging_set_allocation(struct domain *d, unsigned int pages, diff --git a/xen/arch/x86/pv/domain.c b/xen/arch/x86/pv/domain.c index 2a445bb17b..4d3b67eb32 100644 --- a/xen/arch/x86/pv/domain.c +++ b/xen/arch/x86/pv/domain.c @@ -305,6 +305,7 @@ int pv_vcpu_initialise(struct vcpu *v) struct domain *d = v->domain; int rc; + printk(XENLOG_INFO "pv_vcpu_initialise called for domain %u", d->domain_id); ASSERT(!is_idle_domain(d)); rc = pv_create_gdt_ldt_l1tab(v); diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c index eee20bb175..f8b1c2548e 100644 --- a/xen/arch/x86/setup.c +++ b/xen/arch/x86/setup.c @@ -59,6 +59,7 @@ #include <asm/microcode.h> #include <asm/prot-key.h> #include <asm/pv/domain.h> +#include <asm/hvm/svm/svm.h> /* opt_nosmp: If true, secondary processors are ignored. */ static bool __initdata opt_nosmp; @@ -2015,6 +2016,15 @@ void asmlinkage __init noreturn __start_xen(unsigned long mbi_p) printk(XENLOG_INFO "Parked %u CPUs\n", num_parked); smp_cpus_done(); + /* Initialize xen-wide ASID handling */ + #ifdef CONFIG_HVM + for_each_present_cpu ( i ) + { + if ( cpu_has_svm ) + svm_asid_init(); + } + #endif + do_initcalls(); if ( opt_watchdog ) -- 2.46.0
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |