This patch adds code to enable the APIC assist enlightenment which, under certain conditions, means that the guest can avoid an EOI of the local APIC and thereby avoid a VMEXIT.
Use of the enlightenment by the hypervisor is under control of the toolstack, and is added to the default set. Signed-off-by: Paul Durrant <paul.durr...@citrix.com> Cc: Ian Jackson <ian.jack...@eu.citrix.com> Cc: Stefano Stabellini <stefano.stabell...@eu.citrix.com> Cc: Wei Liu <wei.l...@citrix.com> Cc: Keir Fraser <k...@xen.org> Cc: Jan Beulich <jbeul...@suse.com> Cc: Andrew Cooper <andrew.coop...@citrix.com> --- v6: - Addressed various comments from Jan v4: - Re-worded xl.cfg text and added missing LIBXL_HAVE_ definition v3: - Re-instated read-modify-write for forwards compatibility - Fix a coding style issue v2: - Removed some code duplication and unnecessary read-modify-write operations on the APIC assist word. - Stated in the xl.cfg text that the enlightenment has no effect if posted interrupts are in use. - Added the enlightenment to the default set. --- docs/man/xl.cfg.pod.5 | 12 +++++++- tools/libxl/libxl.h | 6 ++++ tools/libxl/libxl_dom.c | 4 +++ tools/libxl/libxl_types.idl | 1 + xen/arch/x86/hvm/viridian.c | 62 ++++++++++++++++++++++++++++++++++---- xen/arch/x86/hvm/vlapic.c | 60 ++++++++++++++++++++++++++++++++---- xen/include/asm-x86/hvm/hvm.h | 3 ++ xen/include/asm-x86/hvm/viridian.h | 5 +++ xen/include/public/hvm/params.h | 7 ++++- 9 files changed, 146 insertions(+), 14 deletions(-) diff --git a/docs/man/xl.cfg.pod.5 b/docs/man/xl.cfg.pod.5 index 56b1117..abf638c 100644 --- a/docs/man/xl.cfg.pod.5 +++ b/docs/man/xl.cfg.pod.5 @@ -1484,10 +1484,20 @@ This set incorporates use of hypercalls for remote TLB flushing. This enlightenment may improve performance of Windows guests running on hosts with higher levels of (physical) CPU contention. +=item B<apic_assist> + +This set incorporates use of the APIC assist page to avoid EOI of +the local APIC. +This enlightenment may improve performance of guests that make use of +per-vcpu event channel upcall vectors. +Note that this enlightenment will have no effect if the guest is +using APICv posted interrupts. + =item B<defaults> This is a special value that enables the default set of groups, which -is currently the B<base>, B<freq> and B<time_ref_count> groups. +is currently the B<base>, B<freq>, B<time_ref_count> and B<apic_assist> +groups. =item B<all> diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h index f9e3ef5..055a7b4 100644 --- a/tools/libxl/libxl.h +++ b/tools/libxl/libxl.h @@ -218,6 +218,12 @@ #define LIBXL_HAVE_SOFT_RESET 1 /* + * LIBXL_HAVE_APIC_ASSIST indicates that the 'apic_assist' value + * is present in the viridian enlightenment enumeration. + */ +#define LIBXL_HAVE_APIC_ASSIST 1 + +/* * libxl ABI compatibility * * The only guarantee which libxl makes regarding ABI compatibility diff --git a/tools/libxl/libxl_dom.c b/tools/libxl/libxl_dom.c index b825b98..09d3bca 100644 --- a/tools/libxl/libxl_dom.c +++ b/tools/libxl/libxl_dom.c @@ -211,6 +211,7 @@ static int hvm_set_viridian_features(libxl__gc *gc, uint32_t domid, libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE); libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_FREQ); libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_TIME_REF_COUNT); + libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_APIC_ASSIST); } libxl_for_each_set_bit(v, info->u.hvm.viridian_enable) { @@ -253,6 +254,9 @@ static int hvm_set_viridian_features(libxl__gc *gc, uint32_t domid, if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_HCALL_REMOTE_TLB_FLUSH)) mask |= HVMPV_hcall_remote_tlb_flush; + if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_APIC_ASSIST)) + mask |= HVMPV_apic_assist; + if (mask != 0 && xc_hvm_param_set(CTX->xch, domid, diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl index 632c009..e3be957 100644 --- a/tools/libxl/libxl_types.idl +++ b/tools/libxl/libxl_types.idl @@ -221,6 +221,7 @@ libxl_viridian_enlightenment = Enumeration("viridian_enlightenment", [ (2, "time_ref_count"), (3, "reference_tsc"), (4, "hcall_remote_tlb_flush"), + (5, "apic_assist"), ]) libxl_hdtype = Enumeration("hdtype", [ diff --git a/xen/arch/x86/hvm/viridian.c b/xen/arch/x86/hvm/viridian.c index f726df1..410320c 100644 --- a/xen/arch/x86/hvm/viridian.c +++ b/xen/arch/x86/hvm/viridian.c @@ -227,11 +227,6 @@ static void initialize_apic_assist(struct vcpu *v) void *va; /* - * We don't yet make use of the APIC assist page but by setting - * the CPUID3A_MSR_APIC_ACCESS bit in CPUID leaf 40000003 we are duty - * bound to support the MSR. We therefore do just enough to keep windows - * happy. - * * See section 13.3.4.1 of the specification for details of this * enlightenment. */ @@ -254,7 +249,15 @@ static void initialize_apic_assist(struct vcpu *v) *(uint32_t *)va = 0; - v->arch.hvm_vcpu.viridian.apic_assist.va = va; + if ( viridian_feature_mask(v->domain) & HVMPV_apic_assist ) + { + v->arch.hvm_vcpu.viridian.apic_assist.va = va; + v->arch.hvm_vcpu.viridian.apic_assist.vector = -1; + return; + } + + unmap_domain_page_global(va); + put_page_and_type(page); return; fail: @@ -278,6 +281,53 @@ static void teardown_apic_assist(struct vcpu *v) put_page_and_type(page); } +void viridian_start_apic_assist(struct vcpu *v, int vector) +{ + uint32_t *va = v->arch.hvm_vcpu.viridian.apic_assist.va; + + if ( !va ) + return; + + /* + * If there is already an assist pending then something has gone + * wrong and the VM will most likely hang so force a crash now + * to make the problem clear. + */ + if ( v->arch.hvm_vcpu.viridian.apic_assist.vector >= 0 ) + domain_crash(v->domain); + + v->arch.hvm_vcpu.viridian.apic_assist.vector = vector; + *va |= 1u; +} + +int viridian_complete_apic_assist(struct vcpu *v) +{ + uint32_t *va = v->arch.hvm_vcpu.viridian.apic_assist.va; + int vector; + + if ( !va ) + return -1; + + if ( *va & 1u ) + return -1; /* Interrupt not yet processed by the guest. */ + + vector = v->arch.hvm_vcpu.viridian.apic_assist.vector; + v->arch.hvm_vcpu.viridian.apic_assist.vector = -1; + + return vector; +} + +void viridian_abort_apic_assist(struct vcpu *v) +{ + uint32_t *va = v->arch.hvm_vcpu.viridian.apic_assist.va; + + if ( !va ) + return; + + *va &= ~1u; + v->arch.hvm_vcpu.viridian.apic_assist.vector = -1; +} + static void update_reference_tsc(struct domain *d, bool_t initialize) { unsigned long gmfn = d->arch.hvm_domain.viridian.reference_tsc.fields.pfn; diff --git a/xen/arch/x86/hvm/vlapic.c b/xen/arch/x86/hvm/vlapic.c index 01a8430..4af2107 100644 --- a/xen/arch/x86/hvm/vlapic.c +++ b/xen/arch/x86/hvm/vlapic.c @@ -38,6 +38,7 @@ #include <asm/hvm/support.h> #include <asm/hvm/vmx/vmx.h> #include <asm/hvm/nestedhvm.h> +#include <asm/hvm/viridian.h> #include <public/hvm/ioreq.h> #include <public/hvm/params.h> @@ -95,6 +96,18 @@ static int vlapic_find_highest_vector(const void *bitmap) return (fls(word[word_offset*4]) - 1) + (word_offset * 32); } +static int vlapic_find_lowest_vector(const void *bitmap) +{ + const uint32_t *word = bitmap; + unsigned int word_offset; + + /* Work forwards through the bitmap (first 32-bit word in every four). */ + for ( word_offset = 0; word_offset < NR_VECTORS / 32; word_offset++) + if ( word[word_offset * 4] ) + return (ffs(word[word_offset * 4]) - 1) + (word_offset * 32); + + return -1; +} /* * IRR-specific bitmap update & search routines. @@ -1157,7 +1170,7 @@ int vlapic_virtual_intr_delivery_enabled(void) int vlapic_has_pending_irq(struct vcpu *v) { struct vlapic *vlapic = vcpu_vlapic(v); - int irr, isr; + int irr, vector, isr; if ( !vlapic_enabled(vlapic) ) return -1; @@ -1170,10 +1183,27 @@ int vlapic_has_pending_irq(struct vcpu *v) !nestedhvm_vcpu_in_guestmode(v) ) return irr; + /* + * If APIC assist was used then there may have been no EOI so + * we need to clear the requisite bit from the ISR here, before + * comparing with the IRR. + */ + vector = viridian_complete_apic_assist(v); + if ( vector != -1 ) + vlapic_clear_vector(vector, &vlapic->regs->data[APIC_ISR]); + isr = vlapic_find_highest_isr(vlapic); isr = (isr != -1) ? isr : 0; if ( (isr & 0xf0) >= (irr & 0xf0) ) + { + /* + * There's already a higher priority vector pending so + * we need to abort any previous APIC assist to ensure there + * is an EOI. + */ + viridian_abort_apic_assist(v); return -1; + } return irr; } @@ -1181,13 +1211,31 @@ int vlapic_has_pending_irq(struct vcpu *v) int vlapic_ack_pending_irq(struct vcpu *v, int vector, bool_t force_ack) { struct vlapic *vlapic = vcpu_vlapic(v); + int isr; - if ( force_ack || !vlapic_virtual_intr_delivery_enabled() ) - { - vlapic_set_vector(vector, &vlapic->regs->data[APIC_ISR]); - vlapic_clear_irr(vector, vlapic); - } + if ( !force_ack && + vlapic_virtual_intr_delivery_enabled() ) + return 1; + + /* If there's no chance of using APIC assist then bail now. */ + if ( !has_viridian_apic_assist(v->domain) || + vlapic_test_vector(vector, &vlapic->regs->data[APIC_TMR]) ) + goto done; + + isr = vlapic_find_lowest_vector(&vlapic->regs->data[APIC_ISR]); + if ( isr >= 0 && isr < vector ) + goto done; + + /* + * This vector is edge triggered and there are no lower priority + * vectors pending, so we can use APIC assist to avoid exiting + * for EOI. + */ + viridian_start_apic_assist(v, vector); + done: + vlapic_set_vector(vector, &vlapic->regs->data[APIC_ISR]); + vlapic_clear_irr(vector, vlapic); return 1; } diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h index b8ab5b1..751ab1c 100644 --- a/xen/include/asm-x86/hvm/hvm.h +++ b/xen/include/asm-x86/hvm/hvm.h @@ -402,6 +402,9 @@ static inline unsigned long hvm_get_shadow_gs_base(struct vcpu *v) #define has_viridian_time_ref_count(d) \ (is_viridian_domain(d) && (viridian_feature_mask(d) & HVMPV_time_ref_count)) +#define has_viridian_apic_assist(d) \ + (is_viridian_domain(d) && (viridian_feature_mask(d) & HVMPV_apic_assist)) + void hvm_hypervisor_cpuid_leaf(uint32_t sub_idx, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); diff --git a/xen/include/asm-x86/hvm/viridian.h b/xen/include/asm-x86/hvm/viridian.h index 19ecc06..7f281b2 100644 --- a/xen/include/asm-x86/hvm/viridian.h +++ b/xen/include/asm-x86/hvm/viridian.h @@ -24,6 +24,7 @@ struct viridian_vcpu struct { union viridian_apic_assist msr; void *va; + int vector; } apic_assist; }; @@ -122,6 +123,10 @@ void viridian_time_ref_count_thaw(struct domain *d); void viridian_vcpu_deinit(struct vcpu *v); +void viridian_start_apic_assist(struct vcpu *v, int vector); +int viridian_complete_apic_assist(struct vcpu *v); +void viridian_abort_apic_assist(struct vcpu *v); + #endif /* __ASM_X86_HVM_VIRIDIAN_H__ */ /* diff --git a/xen/include/public/hvm/params.h b/xen/include/public/hvm/params.h index 73d4718..e69c72c 100644 --- a/xen/include/public/hvm/params.h +++ b/xen/include/public/hvm/params.h @@ -115,12 +115,17 @@ #define _HVMPV_hcall_remote_tlb_flush 4 #define HVMPV_hcall_remote_tlb_flush (1 << _HVMPV_hcall_remote_tlb_flush) +/* Use APIC assist */ +#define _HVMPV_apic_assist 5 +#define HVMPV_apic_assist (1 << _HVMPV_apic_assist) + #define HVMPV_feature_mask \ (HVMPV_base_freq | \ HVMPV_no_freq | \ HVMPV_time_ref_count | \ HVMPV_reference_tsc | \ - HVMPV_hcall_remote_tlb_flush) + HVMPV_hcall_remote_tlb_flush | \ + HVMPV_apic_assist) #endif -- 2.1.4 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xen.org http://lists.xen.org/xen-devel