Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 37f55d30 authored by Suresh Warrier's avatar Suresh Warrier Committed by Paul Mackerras
Browse files

KVM: PPC: Book3S HV: Convert kvmppc_read_intr to a C function



Modify kvmppc_read_intr to make it a C function.  Because it is called
from kvmppc_check_wake_reason, any of the assembler code that calls
either kvmppc_read_intr or kvmppc_check_wake_reason now has to assume
that the volatile registers might have been modified.

This also adds in the optimization of clearing saved_xirr in the case
where we completely handle and EOI an IPI.  Without this, the next
device interrupt will require two trips through the host interrupt
handling code.

[paulus@ozlabs.org - made kvmppc_check_wake_reason create a stack frame
 when it is calling kvmppc_read_intr, which means we can set r12 to
 the trap number (0x500) after the call to kvmppc_read_intr, instead
 of using r31.  Also moved the deliver_guest_interrupt label so as to
 restore XER and CTR, plus other minor tweaks.]

Signed-off-by: default avatarSuresh Warrier <warrier@linux.vnet.ibm.com>
Signed-off-by: default avatarPaul Mackerras <paulus@ozlabs.org>
parent 99212c86
Loading
Loading
Loading
Loading
+84 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <asm/xics.h>
#include <asm/dbell.h>
#include <asm/cputhreads.h>
#include <asm/io.h>

#define KVM_CMA_CHUNK_ORDER	18

@@ -286,3 +287,86 @@ void kvmhv_commence_exit(int trap)

struct kvmppc_host_rm_ops *kvmppc_host_rm_ops_hv;
EXPORT_SYMBOL_GPL(kvmppc_host_rm_ops_hv);

/*
 * Determine what sort of external interrupt is pending (if any).
 * Returns:
 *	0 if no interrupt is pending
 *	1 if an interrupt is pending that needs to be handled by the host
 *	-1 if there was a guest wakeup IPI (which has now been cleared)
 */

long kvmppc_read_intr(void)
{
	unsigned long xics_phys;
	u32 h_xirr;
	__be32 xirr;
	u32 xisr;
	u8 host_ipi;

	/* see if a host IPI is pending */
	host_ipi = local_paca->kvm_hstate.host_ipi;
	if (host_ipi)
		return 1;

	/* Now read the interrupt from the ICP */
	xics_phys = local_paca->kvm_hstate.xics_phys;
	if (unlikely(!xics_phys))
		return 1;

	/*
	 * Save XIRR for later. Since we get control in reverse endian
	 * on LE systems, save it byte reversed and fetch it back in
	 * host endian. Note that xirr is the value read from the
	 * XIRR register, while h_xirr is the host endian version.
	 */
	xirr = _lwzcix(xics_phys + XICS_XIRR);
	h_xirr = be32_to_cpu(xirr);
	local_paca->kvm_hstate.saved_xirr = h_xirr;
	xisr = h_xirr & 0xffffff;
	/*
	 * Ensure that the store/load complete to guarantee all side
	 * effects of loading from XIRR has completed
	 */
	smp_mb();

	/* if nothing pending in the ICP */
	if (!xisr)
		return 0;

	/* We found something in the ICP...
	 *
	 * If it is an IPI, clear the MFRR and EOI it.
	 */
	if (xisr == XICS_IPI) {
		_stbcix(xics_phys + XICS_MFRR, 0xff);
		_stwcix(xics_phys + XICS_XIRR, xirr);
		/*
		 * Need to ensure side effects of above stores
		 * complete before proceeding.
		 */
		smp_mb();

		/*
		 * We need to re-check host IPI now in case it got set in the
		 * meantime. If it's clear, we bounce the interrupt to the
		 * guest
		 */
		host_ipi = local_paca->kvm_hstate.host_ipi;
		if (unlikely(host_ipi != 0)) {
			/* We raced with the host,
			 * we need to resend that IPI, bummer
			 */
			_stbcix(xics_phys + XICS_MFRR, IPI_PRIORITY);
			/* Let side effects complete */
			smp_mb();
			return 1;
		}

		/* OK, it's an IPI for us */
		local_paca->kvm_hstate.saved_xirr = 0;
		return -1;
	}

	return 1;
}
+74 −84
Original line number Diff line number Diff line
@@ -221,6 +221,13 @@ kvmppc_primary_no_guest:
	li	r3, 0		/* Don't wake on privileged (OS) doorbell */
	b	kvm_do_nap

/*
 * kvm_novcpu_wakeup
 *	Entered from kvm_start_guest if kvm_hstate.napping is set
 *	to NAPPING_NOVCPU
 *		r2 = kernel TOC
 *		r13 = paca
 */
kvm_novcpu_wakeup:
	ld	r1, HSTATE_HOST_R1(r13)
	ld	r5, HSTATE_KVM_VCORE(r13)
@@ -230,6 +237,13 @@ kvm_novcpu_wakeup:
	/* check the wake reason */
	bl	kvmppc_check_wake_reason

	/*
	 * Restore volatile registers since we could have called
	 * a C routine in kvmppc_check_wake_reason.
	 *	r5 = VCORE
	 */
	ld	r5, HSTATE_KVM_VCORE(r13)

	/* see if any other thread is already exiting */
	lwz	r0, VCORE_ENTRY_EXIT(r5)
	cmpwi	r0, 0x100
@@ -322,6 +336,11 @@ kvm_start_guest:

	/* Check the wake reason in SRR1 to see why we got here */
	bl	kvmppc_check_wake_reason
	/*
	 * kvmppc_check_wake_reason could invoke a C routine, but we
	 * have no volatile registers to restore when we return.
	 */

	cmpdi	r3, 0
	bge	kvm_no_guest

@@ -881,6 +900,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
	cmpwi	r3, 512		/* 1 microsecond */
	blt	hdec_soon

deliver_guest_interrupt:
	ld	r6, VCPU_CTR(r4)
	ld	r7, VCPU_XER(r4)

@@ -895,7 +915,6 @@ kvmppc_cede_reentry: /* r4 = vcpu, r13 = paca */
	mtspr	SPRN_SRR0, r6
	mtspr	SPRN_SRR1, r7

deliver_guest_interrupt:
	/* r11 = vcpu->arch.msr & ~MSR_HV */
	rldicl	r11, r11, 63 - MSR_HV_LG, 1
	rotldi	r11, r11, 1 + MSR_HV_LG
@@ -1155,10 +1174,36 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
	 * set, we know the host wants us out so let's do it now
	 */
	bl	kvmppc_read_intr

	/*
	 * Restore the active volatile registers after returning from
	 * a C function.
	 */
	ld	r9, HSTATE_KVM_VCPU(r13)
	li	r12, BOOK3S_INTERRUPT_EXTERNAL

	/*
	 * kvmppc_read_intr return codes:
	 *
	 * Exit to host (r3 > 0)
	 *   1 An interrupt is pending that needs to be handled by the host
	 *     Exit guest and return to host by branching to guest_exit_cont
	 *
	 * Before returning to guest, we check if any CPU is heading out
	 * to the host and if so, we head out also. If no CPUs are heading
	 * check return values <= 0.
	 *
	 * Return to guest (r3 <= 0)
	 *  0 No external interrupt is pending
	 * -1 A guest wakeup IPI (which has now been cleared)
	 *    In either case, we return to guest to deliver any pending
	 *    guest interrupts.
	 */

	cmpdi	r3, 0
	bgt	guest_exit_cont

	/* Check if any CPU is heading out to the host, if so head out too */
	/* Return code <= 0 */
4:	ld	r5, HSTATE_KVM_VCORE(r13)
	lwz	r0, VCORE_ENTRY_EXIT(r5)
	cmpwi	r0, 0x100
@@ -2217,6 +2262,16 @@ END_FTR_SECTION_IFSET(CPU_FTR_TM)
	/* Check the wake reason in SRR1 to see why we got here */
	bl	kvmppc_check_wake_reason

	/*
	 * Restore volatile registers since we could have called a
	 * C routine in kvmppc_check_wake_reason
	 *	r4 = VCPU
	 * r3 tells us whether we need to return to host or not
	 * WARNING: it gets checked further down:
	 * should not modify r3 until this check is done.
	 */
	ld	r4, HSTATE_KVM_VCPU(r13)

	/* clear our bit in vcore->napping_threads */
34:	ld	r5,HSTATE_KVM_VCORE(r13)
	lbz	r7,HSTATE_PTID(r13)
@@ -2230,7 +2285,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_TM)
	li	r0,0
	stb	r0,HSTATE_NAPPING(r13)

	/* See if the wake reason means we need to exit */
	/* See if the wake reason saved in r3 means we need to exit */
	stw	r12, VCPU_TRAP(r4)
	mr	r9, r4
	cmpdi	r3, 0
@@ -2300,7 +2355,9 @@ machine_check_realmode:
 *
 * Also sets r12 to the interrupt vector for any interrupt that needs
 * to be handled now by the host (0x500 for external interrupt), or zero.
 * Modifies r0, r6, r7, r8.
 * Modifies all volatile registers (since it may call a C function).
 * This routine calls kvmppc_read_intr, a C function, if an external
 * interrupt is pending.
 */
kvmppc_check_wake_reason:
	mfspr	r6, SPRN_SRR1
@@ -2310,8 +2367,7 @@ FTR_SECTION_ELSE
	rlwinm	r6, r6, 45-31, 0xe	/* P7 wake reason field is 3 bits */
ALT_FTR_SECTION_END_IFSET(CPU_FTR_ARCH_207S)
	cmpwi	r6, 8			/* was it an external interrupt? */
	li	r12, BOOK3S_INTERRUPT_EXTERNAL
	beq	kvmppc_read_intr	/* if so, see what it was */
	beq	7f			/* if so, see what it was */
	li	r3, 0
	li	r12, 0
	cmpwi	r6, 6			/* was it the decrementer? */
@@ -2350,83 +2406,17 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
	li	r3, 1
	blr

/*
 * Determine what sort of external interrupt is pending (if any).
 * Returns:
 *	0 if no interrupt is pending
 *	1 if an interrupt is pending that needs to be handled by the host
 *	-1 if there was a guest wakeup IPI (which has now been cleared)
 * Modifies r0, r6, r7, r8, returns value in r3.
 */
kvmppc_read_intr:
	/* see if a host IPI is pending */
	li	r3, 1
	lbz	r0, HSTATE_HOST_IPI(r13)
	cmpwi	r0, 0
	bne	1f

	/* Now read the interrupt from the ICP */
	ld	r6, HSTATE_XICS_PHYS(r13)
	li	r7, XICS_XIRR
	cmpdi	r6, 0
	beq-	1f
	lwzcix	r0, r6, r7
	/*
	 * Save XIRR for later. Since we get in in reverse endian on LE
	 * systems, save it byte reversed and fetch it back in host endian.
	 */
	li	r3, HSTATE_SAVED_XIRR
	STWX_BE	r0, r3, r13
#ifdef __LITTLE_ENDIAN__
	lwz	r3, HSTATE_SAVED_XIRR(r13)
#else
	mr	r3, r0
#endif
	rlwinm.	r3, r3, 0, 0xffffff
	sync
	beq	1f			/* if nothing pending in the ICP */

	/* We found something in the ICP...
	 *
	 * If it's not an IPI, stash it in the PACA and return to
	 * the host, we don't (yet) handle directing real external
	 * interrupts directly to the guest
	 */
	cmpwi	r3, XICS_IPI		/* if there is, is it an IPI? */
	bne	42f

	/* It's an IPI, clear the MFRR and EOI it */
	li	r3, 0xff
	li	r8, XICS_MFRR
	stbcix	r3, r6, r8		/* clear the IPI */
	stwcix	r0, r6, r7		/* EOI it */
	sync

	/* We need to re-check host IPI now in case it got set in the
	 * meantime. If it's clear, we bounce the interrupt to the
	 * guest
	 */
	lbz	r0, HSTATE_HOST_IPI(r13)
	cmpwi	r0, 0
	bne-	43f

	/* OK, it's an IPI for us */
	li	r12, 0
	li	r3, -1
1:	blr

42:	/* It's not an IPI and it's for the host. We saved a copy of XIRR in
	 * the PACA earlier, it will be picked up by the host ICP driver
	 */
	li	r3, 1
	b	1b

43:	/* We raced with the host, we need to resend that IPI, bummer */
	li	r0, IPI_PRIORITY
	stbcix	r0, r6, r8		/* set the IPI */
	sync
	li	r3, 1
	b	1b
	/* external interrupt - create a stack frame so we can call C */
7:	mflr	r0
	std	r0, PPC_LR_STKOFF(r1)
	stdu	r1, -PPC_MIN_STKFRM(r1)
	bl	kvmppc_read_intr
	nop
	li	r12, BOOK3S_INTERRUPT_EXTERNAL
	ld	r0, PPC_MIN_STKFRM+PPC_LR_STKOFF(r1)
	addi	r1, r1, PPC_MIN_STKFRM
	mtlr	r0
	blr

/*
 * Save away FP, VMX and VSX registers.