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

Commit 17d48610 authored by Li Zhong's avatar Li Zhong Committed by Paul Mackerras
Browse files

KVM: PPC: Book 3S: XICS: Implement ICS P/Q states



This patch implements P(Presented)/Q(Queued) states for ICS irqs.

When the interrupt is presented, set P. Present if P was not set.
If P is already set, don't present again, set Q.
When the interrupt is EOI'ed, move Q into P (and clear Q). If it is
set, re-present.

The asserted flag used by LSI is also incorporated into the P bit.

When the irq state is saved, P/Q bits are also saved, they need some
qemu modifications to be recognized and passed around to be restored.
KVM_XICS_PENDING bit set and saved should also indicate
KVM_XICS_PRESENTED bit set and saved. But it is possible some old
code doesn't have/recognize the P bit, so when we restore, we set P
for PENDING bit, too.

The idea and much of the code come from Ben.

Signed-off-by: default avatarLi Zhong <zhong@linux.vnet.ibm.com>
Signed-off-by: default avatarPaul Mackerras <paulus@ozlabs.org>
parent bf5a71d5
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -613,5 +613,7 @@ struct kvm_get_htab_header {
#define  KVM_XICS_LEVEL_SENSITIVE	(1ULL << 40)
#define  KVM_XICS_MASKED		(1ULL << 41)
#define  KVM_XICS_PENDING		(1ULL << 42)
#define  KVM_XICS_PRESENTED		(1ULL << 43)
#define  KVM_XICS_QUEUED		(1ULL << 44)

#endif /* __LINUX_KVM_POWERPC_H */
+70 −30
Original line number Diff line number Diff line
@@ -672,51 +672,39 @@ int kvmppc_rm_h_cppr(struct kvm_vcpu *vcpu, unsigned long cppr)
	return check_too_hard(xics, icp);
}

int kvmppc_rm_h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr)
static int ics_rm_eoi(struct kvm_vcpu *vcpu, u32 irq)
{
	struct kvmppc_xics *xics = vcpu->kvm->arch.xics;
	struct kvmppc_icp *icp = vcpu->arch.icp;
	struct kvmppc_ics *ics;
	struct ics_irq_state *state;
	u32 irq = xirr & 0x00ffffff;
	u16 src;

	if (!xics || !xics->real_mode)
		return H_TOO_HARD;
	u32 pq_old, pq_new;

	/*
	 * ICP State: EOI
	 *
	 * Note: If EOI is incorrectly used by SW to lower the CPPR
	 * value (ie more favored), we do not check for rejection of
	 * a pending interrupt, this is a SW error and PAPR sepcifies
	 * that we don't have to deal with it.
	 * ICS EOI handling: For LSI, if P bit is still set, we need to
	 * resend it.
	 *
	 * The sending of an EOI to the ICS is handled after the
	 * CPPR update
	 *
	 * ICP State: Down_CPPR which we handle
	 * in a separate function as it's shared with H_CPPR.
	 * For MSI, we move Q bit into P (and clear Q). If it is set,
	 * resend it.
	 */
	icp_rm_down_cppr(xics, icp, xirr >> 24);

	/* IPIs have no EOI */
	if (irq == XICS_IPI)
		goto bail;
	/*
	 * EOI handling: If the interrupt is still asserted, we need to
	 * resend it. We can take a lockless "peek" at the ICS state here.
	 *
	 * "Message" interrupts will never have "asserted" set
	 */
	ics = kvmppc_xics_find_ics(xics, irq, &src);
	if (!ics)
		goto bail;

	state = &ics->irq_state[src];

	/* Still asserted, resend it */
	if (state->asserted)
		icp_rm_deliver_irq(xics, icp, irq);
	if (state->lsi)
		pq_new = state->pq_state;
	else
		do {
			pq_old = state->pq_state;
			pq_new = pq_old >> 1;
		} while (cmpxchg(&state->pq_state, pq_old, pq_new) != pq_old);

	if (pq_new & PQ_PRESENTED)
		icp_rm_deliver_irq(xics, NULL, irq);

	if (!hlist_empty(&vcpu->kvm->irq_ack_notifier_list)) {
		icp->rm_action |= XICS_RM_NOTIFY_EOI;
@@ -737,10 +725,43 @@ int kvmppc_rm_h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr)
			state->intr_cpu = -1;
		}
	}

 bail:
	return check_too_hard(xics, icp);
}

int kvmppc_rm_h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr)
{
	struct kvmppc_xics *xics = vcpu->kvm->arch.xics;
	struct kvmppc_icp *icp = vcpu->arch.icp;
	u32 irq = xirr & 0x00ffffff;

	if (!xics || !xics->real_mode)
		return H_TOO_HARD;

	/*
	 * ICP State: EOI
	 *
	 * Note: If EOI is incorrectly used by SW to lower the CPPR
	 * value (ie more favored), we do not check for rejection of
	 * a pending interrupt, this is a SW error and PAPR specifies
	 * that we don't have to deal with it.
	 *
	 * The sending of an EOI to the ICS is handled after the
	 * CPPR update
	 *
	 * ICP State: Down_CPPR which we handle
	 * in a separate function as it's shared with H_CPPR.
	 */
	icp_rm_down_cppr(xics, icp, xirr >> 24);

	/* IPIs have no EOI */
	if (irq == XICS_IPI)
		return check_too_hard(xics, icp);

	return ics_rm_eoi(vcpu, irq);
}

unsigned long eoi_rc;

static void icp_eoi(struct irq_chip *c, u32 hwirq, __be32 xirr, bool *again)
@@ -827,13 +848,32 @@ long kvmppc_deliver_irq_passthru(struct kvm_vcpu *vcpu,
{
	struct kvmppc_xics *xics;
	struct kvmppc_icp *icp;
	struct kvmppc_ics *ics;
	struct ics_irq_state *state;
	u32 irq;
	u16 src;
	u32 pq_old, pq_new;

	irq = irq_map->v_hwirq;
	xics = vcpu->kvm->arch.xics;
	icp = vcpu->arch.icp;

	kvmppc_rm_handle_irq_desc(irq_map->desc);

	ics = kvmppc_xics_find_ics(xics, irq, &src);
	if (!ics)
		return 2;

	state = &ics->irq_state[src];

	/* only MSIs register bypass producers, so it must be MSI here */
	do {
		pq_old = state->pq_state;
		pq_new = ((pq_old << 1) & 3) | PQ_PRESENTED;
	} while (cmpxchg(&state->pq_state, pq_old, pq_new) != pq_old);

	/* Test P=1, Q=0, this is the only case where we present */
	if (pq_new == PQ_PRESENTED)
		icp_rm_deliver_irq(xics, icp, irq);

	/* EOI the interrupt */
+85 −40
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ static int ics_deliver_irq(struct kvmppc_xics *xics, u32 irq, u32 level)
	struct ics_irq_state *state;
	struct kvmppc_ics *ics;
	u16 src;
	u32 pq_old, pq_new;

	XICS_DBG("ics deliver %#x (level: %d)\n", irq, level);

@@ -87,25 +88,41 @@ static int ics_deliver_irq(struct kvmppc_xics *xics, u32 irq, u32 level)
	if (!state->exists)
		return -EINVAL;

	if (level == KVM_INTERRUPT_SET_LEVEL || level == KVM_INTERRUPT_SET)
		level = 1;
	else if (level == KVM_INTERRUPT_UNSET)
		level = 0;
	/*
	 * We set state->asserted locklessly. This should be fine as
	 * we are the only setter, thus concurrent access is undefined
	 * to begin with.
	 * Take other values the same as 1, consistent with original code.
	 * maybe WARN here?
	 */
	if ((level == 1 && state->lsi) || level == KVM_INTERRUPT_SET_LEVEL)
		state->asserted = 1;
	else if (level == 0 || level == KVM_INTERRUPT_UNSET) {
		state->asserted = 0;

	if (!state->lsi && level == 0) /* noop for MSI */
		return 0;
	}

	do {
		pq_old = state->pq_state;
		if (state->lsi) {
			if (level) {
				if (pq_old & PQ_PRESENTED)
					/* Setting already set LSI ... */
					return 0;

				pq_new = PQ_PRESENTED;
			} else
				pq_new = 0;
		} else
			pq_new = ((pq_old << 1) & 3) | PQ_PRESENTED;
	} while (cmpxchg(&state->pq_state, pq_old, pq_new) != pq_old);

	/* Test P=1, Q=0, this is the only case where we present */
	if (pq_new == PQ_PRESENTED)
		icp_deliver_irq(xics, NULL, irq);

	/* Record which CPU this arrived on for passed-through interrupts */
	if (state->host_irq)
		state->intr_cpu = raw_smp_processor_id();

	/* Attempt delivery */
	icp_deliver_irq(xics, NULL, irq);

	return 0;
}

@@ -768,14 +785,51 @@ static noinline void kvmppc_h_cppr(struct kvm_vcpu *vcpu, unsigned long cppr)
		icp_deliver_irq(xics, icp, reject);
}

static noinline int kvmppc_h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr)
static int ics_eoi(struct kvm_vcpu *vcpu, u32 irq)
{
	struct kvmppc_xics *xics = vcpu->kvm->arch.xics;
	struct kvmppc_icp *icp = vcpu->arch.icp;
	struct kvmppc_ics *ics;
	struct ics_irq_state *state;
	u32 irq = xirr & 0x00ffffff;
	u16 src;
	u32 pq_old, pq_new;

	/*
	 * ICS EOI handling: For LSI, if P bit is still set, we need to
	 * resend it.
	 *
	 * For MSI, we move Q bit into P (and clear Q). If it is set,
	 * resend it.
	 */

	ics = kvmppc_xics_find_ics(xics, irq, &src);
	if (!ics) {
		XICS_DBG("ios_eoi: IRQ 0x%06x not found !\n", irq);
		return H_PARAMETER;
	}
	state = &ics->irq_state[src];

	if (state->lsi)
		pq_new = state->pq_state;
	else
		do {
			pq_old = state->pq_state;
			pq_new = pq_old >> 1;
		} while (cmpxchg(&state->pq_state, pq_old, pq_new) != pq_old);

	if (pq_new & PQ_PRESENTED)
		icp_deliver_irq(xics, icp, irq);

	kvm_notify_acked_irq(vcpu->kvm, 0, irq);

	return H_SUCCESS;
}

static noinline int kvmppc_h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr)
{
	struct kvmppc_xics *xics = vcpu->kvm->arch.xics;
	struct kvmppc_icp *icp = vcpu->arch.icp;
	u32 irq = xirr & 0x00ffffff;

	XICS_DBG("h_eoi vcpu %d eoi %#lx\n", vcpu->vcpu_id, xirr);

@@ -798,26 +852,8 @@ static noinline int kvmppc_h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr)
	/* IPIs have no EOI */
	if (irq == XICS_IPI)
		return H_SUCCESS;
	/*
	 * EOI handling: If the interrupt is still asserted, we need to
	 * resend it. We can take a lockless "peek" at the ICS state here.
	 *
	 * "Message" interrupts will never have "asserted" set
	 */
	ics = kvmppc_xics_find_ics(xics, irq, &src);
	if (!ics) {
		XICS_DBG("h_eoi: IRQ 0x%06x not found !\n", irq);
		return H_PARAMETER;
	}
	state = &ics->irq_state[src];

	/* Still asserted, resend it */
	if (state->asserted)
		icp_deliver_irq(xics, icp, irq);

	kvm_notify_acked_irq(vcpu->kvm, 0, irq);

	return H_SUCCESS;
	return ics_eoi(vcpu, irq);
}

int kvmppc_xics_rm_complete(struct kvm_vcpu *vcpu, u32 hcall)
@@ -975,9 +1011,9 @@ static int xics_debug_show(struct seq_file *m, void *private)
		for (i = 0; i < KVMPPC_XICS_IRQ_PER_ICS; i++) {
			struct ics_irq_state *irq = &ics->irq_state[i];

			seq_printf(m, "irq 0x%06x: server %#x prio %#x save prio %#x asserted %d resend %d masked pending %d\n",
			seq_printf(m, "irq 0x%06x: server %#x prio %#x save prio %#x pq_state %d resend %d masked pending %d\n",
				   irq->number, irq->server, irq->priority,
				   irq->saved_priority, irq->asserted,
				   irq->saved_priority, irq->pq_state,
				   irq->resend, irq->masked_pending);

		}
@@ -1196,10 +1232,17 @@ static int xics_get_source(struct kvmppc_xics *xics, long irq, u64 addr)
		val |= prio << KVM_XICS_PRIORITY_SHIFT;
		if (irqp->lsi) {
			val |= KVM_XICS_LEVEL_SENSITIVE;
			if (irqp->asserted)
			if (irqp->pq_state & PQ_PRESENTED)
				val |= KVM_XICS_PENDING;
		} else if (irqp->masked_pending || irqp->resend)
			val |= KVM_XICS_PENDING;

		if (irqp->pq_state & PQ_PRESENTED)
			val |= KVM_XICS_PRESENTED;

		if (irqp->pq_state & PQ_QUEUED)
			val |= KVM_XICS_QUEUED;

		ret = 0;
	}
	arch_spin_unlock(&ics->lock);
@@ -1251,12 +1294,14 @@ static int xics_set_source(struct kvmppc_xics *xics, long irq, u64 addr)
	irqp->resend = 0;
	irqp->masked_pending = 0;
	irqp->lsi = 0;
	irqp->asserted = 0;
	if (val & KVM_XICS_LEVEL_SENSITIVE) {
	irqp->pq_state = 0;
	if (val & KVM_XICS_LEVEL_SENSITIVE)
		irqp->lsi = 1;
		if (val & KVM_XICS_PENDING)
			irqp->asserted = 1;
	}
	/* If PENDING, set P in case P is not saved because of old code */
	if (val & KVM_XICS_PRESENTED || val & KVM_XICS_PENDING)
		irqp->pq_state |= PQ_PRESENTED;
	if (val & KVM_XICS_QUEUED)
		irqp->pq_state |= PQ_QUEUED;
	irqp->exists = 1;
	arch_spin_unlock(&ics->lock);
	local_irq_restore(flags);
+4 −1
Original line number Diff line number Diff line
@@ -31,16 +31,19 @@
/* Priority value to use for disabling an interrupt */
#define MASKED	0xff

#define PQ_PRESENTED	1
#define PQ_QUEUED	2

/* State for one irq source */
struct ics_irq_state {
	u32 number;
	u32 server;
	u32 pq_state;
	u8  priority;
	u8  saved_priority;
	u8  resend;
	u8  masked_pending;
	u8  lsi;		/* level-sensitive interrupt */
	u8  asserted; /* Only for LSI */
	u8  exists;
	int intr_cpu;
	u32 host_irq;