[PATCH 2/2] KVM: PPC: Book3S: Call into C interrupt handlers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Some interrupts only should be deferred when intercepted by KVM code,
not completely handled by KVM. Thus we need to call into Linux' interrupt
handling code for a few vectors.

So far, this has been done by calling right back into the original
interrupt vector once we're far enough in the guest exit code path.

However, this adds more code to be executed, because we need to save
and restore more state during the full interrupt cycle. We also lose
out on compiler optimizations, since it's all hand written asm.

So switch the code over to call into the Linux C handlers from C code,
speeding up everything along the way.

Along the way, this fixes a bug where we would have to set HSSR instead
of SSR SPRs for the interrupt vector, making hv capable hosts work again
properly.

Signed-off-by: Alexander Graf <agraf@xxxxxxx>
---
 arch/powerpc/kvm/book3s_pr.c      |   58 +++++++++++++++++++++++++++++++++++++
 arch/powerpc/kvm/book3s_segment.S |   25 +---------------
 2 files changed, 59 insertions(+), 24 deletions(-)

diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c
index dba282e..f84b7ba 100644
--- a/arch/powerpc/kvm/book3s_pr.c
+++ b/arch/powerpc/kvm/book3s_pr.c
@@ -54,6 +54,11 @@ static int kvmppc_handle_ext(struct kvm_vcpu *vcpu, unsigned int exit_nr,
 #define HW_PAGE_SIZE PAGE_SIZE
 #define __hard_irq_disable local_irq_disable
 #define __hard_irq_enable local_irq_enable
+static void soft_irq_disable(void) { }
+static void soft_irq_enable(void) { }
+#else
+#define soft_irq_disable local_irq_disable
+#define soft_irq_enable local_irq_enable
 #endif
 
 void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
@@ -538,6 +543,55 @@ static int kvmppc_handle_ext(struct kvm_vcpu *vcpu, unsigned int exit_nr,
 	return RESUME_GUEST;
 }
 
+static void kvmppc_fill_pt_regs(struct pt_regs *regs)
+{
+	ulong r1, ip, msr, lr;
+
+	asm("mr %0, 1" : "=r"(r1));
+	asm("mflr %0" : "=r"(lr));
+	asm("mfmsr %0" : "=r"(msr));
+	asm("bl 1f; 1: mflr %0" : "=r"(ip));
+
+	memset(regs, 0, sizeof(*regs));
+	regs->gpr[1] = r1;
+	regs->nip = ip;
+	regs->msr = msr;
+	regs->link = lr;
+}
+
+static void kvmppc_restart_interrupt(struct kvm_vcpu *vcpu,
+				     unsigned int exit_nr)
+{
+	struct pt_regs regs;
+
+	switch (exit_nr) {
+	case BOOK3S_INTERRUPT_EXTERNAL:
+	case BOOK3S_INTERRUPT_EXTERNAL_LEVEL:
+	case BOOK3S_INTERRUPT_EXTERNAL_HV:
+		kvmppc_fill_pt_regs(&regs);
+		soft_irq_disable();
+		do_IRQ(&regs);
+		soft_irq_enable();
+		break;
+	case BOOK3S_INTERRUPT_DECREMENTER:
+	case BOOK3S_INTERRUPT_HV_DECREMENTER:
+		kvmppc_fill_pt_regs(&regs);
+		soft_irq_disable();
+		timer_interrupt(&regs);
+		soft_irq_enable();
+		break;
+	case BOOK3S_INTERRUPT_MACHINE_CHECK:
+		/* FIXME */
+		break;
+	case BOOK3S_INTERRUPT_PERFMON:
+		kvmppc_fill_pt_regs(&regs);
+		soft_irq_disable();
+		performance_monitor_exception(&regs);
+		soft_irq_enable();
+		break;
+	}
+}
+
 int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        unsigned int exit_nr)
 {
@@ -548,6 +602,10 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
 	run->exit_reason = KVM_EXIT_UNKNOWN;
 	run->ready_for_interrupt_injection = 1;
 
+	/* restart interrupts if they were meant for the host */
+	kvmppc_restart_interrupt(vcpu, exit_nr);
+	__hard_irq_enable();
+
 	trace_kvm_book3s_exit(exit_nr, vcpu);
 	preempt_enable();
 	kvm_resched(vcpu);
diff --git a/arch/powerpc/kvm/book3s_segment.S b/arch/powerpc/kvm/book3s_segment.S
index 6bae0a9..3a78b46 100644
--- a/arch/powerpc/kvm/book3s_segment.S
+++ b/arch/powerpc/kvm/book3s_segment.S
@@ -308,28 +308,6 @@ no_dcbz32_off:
 
 #endif /* CONFIG_PPC_BOOK3S_64 */
 
-	/*
-	 * For some interrupts, we need to call the real Linux
-	 * handler, so it can do work for us. This has to happen
-	 * as if the interrupt arrived from the kernel though,
-	 * so let's fake it here where most state is restored.
-	 *
-	 * Having set up SRR0/1 with the address where we want
-	 * to continue with relocation on (potentially in module
-	 * space), we either just go straight there with rfi[d],
-	 * or we jump to an interrupt handler with bctr if there
-	 * is an interrupt to be handled first.  In the latter
-	 * case, the rfi[d] at the end of the interrupt handler
-	 * will get us back to where we want to continue.
-	 */
-
-	cmpwi	r12, BOOK3S_INTERRUPT_EXTERNAL
-	beq	1f
-	cmpwi	r12, BOOK3S_INTERRUPT_DECREMENTER
-	beq	1f
-	cmpwi	r12, BOOK3S_INTERRUPT_PERFMON
-1:	mtctr	r12
-
 	/* Register usage at this point:
 	 *
 	 * R1       = host R1
@@ -348,7 +326,6 @@ no_dcbz32_off:
 	/* Load highmem handler address */
 	mtsrr0	r8
 
-	/* RFI into the highmem handler, or jump to interrupt handler */
-	beqctr
+	/* RFI into the highmem handler */
 	RFI
 kvmppc_handler_trampoline_exit_end:
-- 
1.6.0.2

--
To unsubscribe from this list: send the line "unsubscribe kvm-ppc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM Development]     [KVM ARM]     [KVM ia64]     [Linux Virtualization]     [Linux USB Devel]     [Linux Video]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux