From patchwork Wed Sep 1 12:32:21 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Graf X-Patchwork-Id: 63374 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 48E1CB7160 for ; Wed, 1 Sep 2010 22:33:48 +1000 (EST) Received: from localhost ([127.0.0.1]:58470 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OqmVc-0000di-Cf for incoming@patchwork.ozlabs.org; Wed, 01 Sep 2010 08:33:44 -0400 Received: from [140.186.70.92] (port=58440 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OqmUL-0000bd-6M for qemu-devel@nongnu.org; Wed, 01 Sep 2010 08:32:26 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1OqmUK-0007HC-1k for qemu-devel@nongnu.org; Wed, 01 Sep 2010 08:32:25 -0400 Received: from cantor2.suse.de ([195.135.220.15]:46166 helo=mx2.suse.de) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1OqmUJ-0007Ge-PW for qemu-devel@nongnu.org; Wed, 01 Sep 2010 08:32:24 -0400 Received: from relay2.suse.de (charybdis-ext.suse.de [195.135.221.2]) by mx2.suse.de (Postfix) with ESMTP id 6146589B67; Wed, 1 Sep 2010 14:32:22 +0200 (CEST) From: Alexander Graf To: qemu-devel List Date: Wed, 1 Sep 2010 14:32:21 +0200 Message-Id: <1283344341-4963-1-git-send-email-agraf@suse.de> X-Mailer: git-send-email 1.6.0.2 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4-2.6 Cc: Marcelo Tosatti , Avi Kivity Subject: [Qemu-devel] [PATCH] KVM: PPC: Add level based interrupt logic X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org KVM on PowerPC used to have completely broken interrupt logic. Usually, interrupts work by having a PIC that pulls a line up/down, so the CPU knows that an interrupt is active. This line stays active until some action is done to the PIC to release the line. On KVM for PPC, we just checked if there was an interrupt pending and pulled a line in the kernel module. We never released it though, hoping that kernel space would just declare an interrupt as released when injected - which is wrong. To fix this, we need to completely redesign the interrupt injection logic. Whenever an interrupt line gets triggered, we need to notify kernel space that the line is up. Whenever it gets released, we do the same. This way we can assure that the interrupt state is always known to kernel space. This fixes random stalls in KVM guests on PowerPC that were waiting for an interrupt while everyone else thought they received it already. Signed-off-by: Alexander Graf --- Ok, I hope this one works out. It doesn't touch generic code. The only unpretty part is the #ifdef CONFIG_KVM, but we'd have to have that somewhere anyways - either here or in target-ppc. Since hw/ppc.c is ppc specific anyways, it's almost the same as having it in target-ppc/. Alex v1 -> v2: - make set_interrupt call ppc specific v2 -> v3: - make set_interrupt a private ppc interface diff --git a/hw/ppc.c b/hw/ppc.c index 2a77eb9..55e3808 100644 --- a/hw/ppc.c +++ b/hw/ppc.c @@ -28,6 +28,8 @@ #include "nvram.h" #include "qemu-log.h" #include "loader.h" +#include "kvm.h" +#include "kvm_ppc.h" //#define PPC_DEBUG_IRQ //#define PPC_DEBUG_TB @@ -50,6 +52,8 @@ static void cpu_ppc_tb_start (CPUState *env); static void ppc_set_irq (CPUState *env, int n_IRQ, int level) { + unsigned int old_pending = env->pending_interrupts; + if (level) { env->pending_interrupts |= 1 << n_IRQ; cpu_interrupt(env, CPU_INTERRUPT_HARD); @@ -58,6 +62,13 @@ static void ppc_set_irq (CPUState *env, int n_IRQ, int level) if (env->pending_interrupts == 0) cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); } + + if (old_pending != env->pending_interrupts) { +#ifdef CONFIG_KVM + kvmppc_set_interrupt(env, n_IRQ, level); +#endif + } + LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32 "req %08x\n", __func__, env, n_IRQ, level, env->pending_interrupts, env->interrupt_request); diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 14d6365..5cacef7 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -37,6 +37,9 @@ do { } while (0) #endif +static int cap_interrupt_unset = false; +static int cap_interrupt_level = false; + /* XXX We have a race condition where we actually have a level triggered * interrupt, but the infrastructure can't expose that yet, so the guest * takes but ignores it, goes to sleep and never gets notified that there's @@ -55,6 +58,18 @@ static void kvm_kick_env(void *env) int kvm_arch_init(KVMState *s, int smp_cpus) { +#ifdef KVM_CAP_PPC_UNSET_IRQ + cap_interrupt_unset = kvm_check_extension(s, KVM_CAP_PPC_UNSET_IRQ); +#endif +#ifdef KVM_CAP_PPC_IRQ_LEVEL + cap_interrupt_level = kvm_check_extension(s, KVM_CAP_PPC_IRQ_LEVEL); +#endif + + if (!cap_interrupt_level) { + fprintf(stderr, "KVM: Couldn't find level irq capability. Expect the " + "VM to stall at times!\n"); + } + return 0; } @@ -178,6 +193,23 @@ int kvm_arch_get_registers(CPUState *env) return 0; } +int kvmppc_set_interrupt(CPUState *env, int irq, int level) +{ + unsigned virq = level ? KVM_INTERRUPT_SET_LEVEL : KVM_INTERRUPT_UNSET; + + if (irq != PPC_INTERRUPT_EXT) { + return 0; + } + + if (!kvm_enabled() || !cap_interrupt_unset || !cap_interrupt_level) { + return 0; + } + + kvm_vcpu_ioctl(env, KVM_INTERRUPT, &virq); + + return 0; +} + #if defined(TARGET_PPCEMB) #define PPC_INPUT_INT PPC40x_INPUT_INT #elif defined(TARGET_PPC64) @@ -193,7 +225,8 @@ int kvm_arch_pre_run(CPUState *env, struct kvm_run *run) /* PowerPC Qemu tracks the various core input pins (interrupt, critical * interrupt, reset, etc) in PPC-specific env->irq_input_state. */ - if (run->ready_for_interrupt_injection && + if (!cap_interrupt_level && + run->ready_for_interrupt_injection && (env->interrupt_request & CPU_INTERRUPT_HARD) && (env->irq_input_state & (1<