KVM: PPC: Book 3S: XICS: Implement ICS P/Q states
authorLi Zhong <zhong@linux.vnet.ibm.com>
Fri, 11 Nov 2016 04:57:35 +0000 (12:57 +0800)
committerPaul Mackerras <paulus@ozlabs.org>
Thu, 26 Jan 2017 23:27:02 +0000 (10:27 +1100)
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: Li Zhong <zhong@linux.vnet.ibm.com>
Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
arch/powerpc/include/uapi/asm/kvm.h
arch/powerpc/kvm/book3s_hv_rm_xics.c
arch/powerpc/kvm/book3s_xics.c
arch/powerpc/kvm/book3s_xics.h

index 3603b6f..e3db3a5 100644 (file)
@@ -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 */
index 16349c9..30f82c7 100644 (file)
@@ -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,14 +848,33 @@ 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);
-       icp_rm_deliver_irq(xics, icp, irq);
+
+       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 */
        icp_eoi(irq_desc_get_chip(irq_map->desc), irq_map->r_hwirq, xirr,
index cdfb4ed..c762062 100644 (file)
@@ -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);
index 1d5fac8..ec5474c 100644 (file)
 /* 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;