From patchwork Mon Jul 9 14:42:32 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Kiszka X-Patchwork-Id: 169867 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id CCBD12C01D3 for ; Tue, 10 Jul 2012 00:43:31 +1000 (EST) Received: from localhost ([::1]:58629 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SoFBR-0001PM-MS for incoming@patchwork.ozlabs.org; Mon, 09 Jul 2012 10:43:29 -0400 Received: from eggs.gnu.org ([208.118.235.92]:37328) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SoFAd-0007sN-Vk for qemu-devel@nongnu.org; Mon, 09 Jul 2012 10:42:45 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SoFAb-0001G5-Ql for qemu-devel@nongnu.org; Mon, 09 Jul 2012 10:42:39 -0400 Received: from thoth.sbs.de ([192.35.17.2]:22198) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SoFAb-0001FI-H0 for qemu-devel@nongnu.org; Mon, 09 Jul 2012 10:42:37 -0400 Received: from mail1.siemens.de (localhost [127.0.0.1]) by thoth.sbs.de (8.13.6/8.13.6) with ESMTP id q69EgXk1018713; Mon, 9 Jul 2012 16:42:33 +0200 Received: from mchn199C.mchp.siemens.de ([139.25.109.49]) by mail1.siemens.de (8.13.6/8.13.6) with ESMTP id q69EgWkC022363; Mon, 9 Jul 2012 16:42:33 +0200 From: Jan Kiszka To: qemu-devel Date: Mon, 9 Jul 2012 16:42:32 +0200 Message-Id: <46ec348fd92de64a39cd5c359f63905c8badfe74.1341844944.git.jan.kiszka@siemens.com> X-Mailer: git-send-email 1.7.3.4 In-Reply-To: References: In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) X-Received-From: 192.35.17.2 Cc: Gleb Natapov , Marcelo Tosatti , Avi Kivity , kvm , Anthony Liguori Subject: [Qemu-devel] [PATCH 3/3] apic: Defer interrupt updates to VCPU thread X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org KVM performs TPR raising asynchronously to QEMU, specifically outside QEMU's global lock. When an interrupt is injected into the APIC and TPR is checked to decide if this can be delivered, a stale TPR value may be used, causing spurious interrupts in the end. Fix this by deferring apic_update_irq to the context of the target VCPU. We introduce a new interrupt flag for this, CPU_INTERRUPT_POLL. When it is set, the VCPU calls apic_poll_irq before checking for further pending interrupts. To avoid special-casing KVM, we also implement this logic for TCG mode. Signed-off-by: Jan Kiszka --- cpu-exec.c | 6 ++++++ hw/apic.c | 5 ++++- hw/apic.h | 1 + hw/apic_internal.h | 1 - target-i386/cpu.h | 4 +++- target-i386/kvm.c | 4 ++++ 6 files changed, 18 insertions(+), 3 deletions(-) diff --git a/cpu-exec.c b/cpu-exec.c index 08c35f7..fc185a4 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -288,6 +288,12 @@ int cpu_exec(CPUArchState *env) } #endif #if defined(TARGET_I386) +#if !defined(CONFIG_USER_ONLY) + if (interrupt_request & CPU_INTERRUPT_POLL) { + env->interrupt_request &= ~CPU_INTERRUPT_POLL; + apic_poll_irq(env->apic_state); + } +#endif if (interrupt_request & CPU_INTERRUPT_INIT) { cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0); diff --git a/hw/apic.c b/hw/apic.c index 5b8f3e8..385555e 100644 --- a/hw/apic.c +++ b/hw/apic.c @@ -16,6 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see */ +#include "qemu-thread.h" #include "apic_internal.h" #include "apic.h" #include "ioapic.h" @@ -361,7 +362,9 @@ static void apic_update_irq(APICCommonState *s) if (!(s->spurious_vec & APIC_SV_ENABLE)) { return; } - if (apic_irq_pending(s) > 0) { + if (!qemu_cpu_is_self(s->cpu_env)) { + cpu_interrupt(s->cpu_env, CPU_INTERRUPT_POLL); + } else if (apic_irq_pending(s) > 0) { cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); } } diff --git a/hw/apic.h b/hw/apic.h index 62179ce..a89542b 100644 --- a/hw/apic.h +++ b/hw/apic.h @@ -20,6 +20,7 @@ void apic_init_reset(DeviceState *s); void apic_sipi(DeviceState *s); void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip, TPRAccess access); +void apic_poll_irq(DeviceState *d); /* pc.c */ int cpu_is_bsp(CPUX86State *env); diff --git a/hw/apic_internal.h b/hw/apic_internal.h index 60a6a8b..4d8ff49 100644 --- a/hw/apic_internal.h +++ b/hw/apic_internal.h @@ -141,7 +141,6 @@ void apic_report_irq_delivered(int delivered); bool apic_next_timer(APICCommonState *s, int64_t current_time); void apic_enable_tpr_access_reporting(DeviceState *d, bool enable); void apic_enable_vapic(DeviceState *d, target_phys_addr_t paddr); -void apic_poll_irq(DeviceState *d); void vapic_report_tpr_access(DeviceState *dev, void *cpu, target_ulong ip, TPRAccess access); diff --git a/target-i386/cpu.h b/target-i386/cpu.h index f257c97..1f6f14f 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -477,6 +477,7 @@ for syscall instruction */ /* i386-specific interrupt pending bits. */ +#define CPU_INTERRUPT_POLL CPU_INTERRUPT_TGT_EXT_1 #define CPU_INTERRUPT_SMI CPU_INTERRUPT_TGT_EXT_2 #define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3 #define CPU_INTERRUPT_MCE CPU_INTERRUPT_TGT_EXT_4 @@ -1047,7 +1048,8 @@ static inline void cpu_clone_regs(CPUX86State *env, target_ulong newsp) static inline bool cpu_has_work(CPUX86State *env) { - return ((env->interrupt_request & CPU_INTERRUPT_HARD) && + return ((env->interrupt_request & (CPU_INTERRUPT_HARD | + CPU_INTERRUPT_POLL)) && (env->eflags & IF_MASK)) || (env->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_INIT | diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 0d0d8f6..cfe60bc 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -1727,6 +1727,10 @@ int kvm_arch_process_async_events(CPUX86State *env) return 0; } + if (env->interrupt_request & CPU_INTERRUPT_POLL) { + env->interrupt_request &= ~CPU_INTERRUPT_POLL; + apic_poll_irq(env->apic_state); + } if (((env->interrupt_request & CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) || (env->interrupt_request & CPU_INTERRUPT_NMI)) {