[PATCH] IB/ipath: fix lost interrupts on HT-400
authorBryan O'Sullivan <bos@pathscale.com>
Sat, 1 Jul 2006 11:36:05 +0000 (04:36 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Sat, 1 Jul 2006 16:56:00 +0000 (09:56 -0700)
Do an extra check to see if in-memory tail changed while processing packets,
and if so, going back through the loop again (but only once per call to
ipath_kreceive()).  In practice, this seems to be enough to guarantee that if
we crossed the clearing of an interrupt at start of ipath_intr with a
scheduled tail register update, that we'll process the "extra" packet that
lost the interrupt because we cleared it just as it was about to arrive.

Signed-off-by: Dave Olson <dave.olson@qlogic.com>
Signed-off-by: Bryan O'Sullivan <bryan.osullivan@qlogic.com>
Cc: "Michael S. Tsirkin" <mst@mellanox.co.il>
Cc: Roland Dreier <rolandd@cisco.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
drivers/infiniband/hw/ipath/ipath_driver.c
drivers/infiniband/hw/ipath/ipath_intr.c

index e2b0bc8..66b41ec 100644 (file)
@@ -870,7 +870,7 @@ void ipath_kreceive(struct ipath_devdata *dd)
        const u32 maxcnt = dd->ipath_rcvhdrcnt * rsize; /* words */
        u32 etail = -1, l, hdrqtail;
        struct ips_message_header *hdr;
-       u32 eflags, i, etype, tlen, pkttot = 0, updegr=0;
+       u32 eflags, i, etype, tlen, pkttot = 0, updegr=0, reloop=0;
        static u64 totcalls;    /* stats, may eventually remove */
        char emsg[128];
 
@@ -885,9 +885,11 @@ void ipath_kreceive(struct ipath_devdata *dd)
                goto bail;
 
        l = dd->ipath_port0head;
-       if (l == (u32)le64_to_cpu(*dd->ipath_hdrqtailptr))
+       hdrqtail = (u32) le64_to_cpu(*dd->ipath_hdrqtailptr);
+       if (l == hdrqtail)
                goto done;
 
+reloop:
        /* read only once at start for performance */
        hdrqtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr);
 
@@ -1011,7 +1013,7 @@ void ipath_kreceive(struct ipath_devdata *dd)
                 */
                if (l == hdrqtail || (i && !(i&0xf))) {
                        u64 lval;
-                       if (l == hdrqtail) /* want interrupt only on last */
+                       if (l == hdrqtail) /* PE-800 interrupt only on last */
                                lval = dd->ipath_rhdrhead_intr_off | l;
                        else
                                lval = l;
@@ -1024,6 +1026,23 @@ void ipath_kreceive(struct ipath_devdata *dd)
                }
        }
 
+       if (!dd->ipath_rhdrhead_intr_off && !reloop) {
+               /* HT-400 workaround; we can have a race clearing chip
+                * interrupt with another interrupt about to be delivered,
+                * and can clear it before it is delivered on the GPIO
+                * workaround.  By doing the extra check here for the
+                * in-memory tail register updating while we were doing
+                * earlier packets, we "almost" guarantee we have covered
+                * that case.
+                */
+               u32 hqtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr);
+               if (hqtail != hdrqtail) {
+                       hdrqtail = hqtail;
+                       reloop = 1; /* loop 1 extra time at most */
+                       goto reloop;
+               }
+       }
+
        pkttot += i;
 
        dd->ipath_port0head = l;
index bad20a3..83b51ed 100644 (file)
@@ -766,7 +766,7 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
        u32 istat, chk0rcv = 0;
        ipath_err_t estat = 0;
        irqreturn_t ret;
-       u32 p0bits, oldhead;
+       u32 oldhead, curtail;
        static unsigned unexpected = 0;
        static const u32 port0rbits = (1U<<INFINIPATH_I_RCVAVAIL_SHIFT) |
                 (1U<<INFINIPATH_I_RCVURG_SHIFT);
@@ -809,15 +809,16 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
         * lose intr for later packets that arrive while we are processing.
         */
        oldhead = dd->ipath_port0head;
-       if (oldhead != (u32) le64_to_cpu(*dd->ipath_hdrqtailptr)) {
+       curtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr);
+       if (oldhead != curtail) {
                if (dd->ipath_flags & IPATH_GPIO_INTR) {
                        ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
                                         (u64) (1 << 2));
-                       p0bits = port0rbits | INFINIPATH_I_GPIO;
+                       istat = port0rbits | INFINIPATH_I_GPIO;
                }
                else
-                       p0bits = port0rbits;
-               ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, p0bits);
+                       istat = port0rbits;
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, istat);
                ipath_kreceive(dd);
                if (oldhead != dd->ipath_port0head) {
                        ipath_stats.sps_fastrcvint++;
@@ -827,7 +828,6 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
        }
 
        istat = ipath_read_kreg32(dd, dd->ipath_kregs->kr_intstatus);
-       p0bits = port0rbits;
 
        if (unlikely(!istat)) {
                ipath_stats.sps_nullintr++;
@@ -890,19 +890,19 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
                else {
                        /* Clear GPIO status bit 2 */
                        ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
-                                        (u64) (1 << 2));
-                       p0bits |= INFINIPATH_I_GPIO;
+                                       (u64) (1 << 2));
                        chk0rcv = 1;
                }
        }
-       chk0rcv |= istat & p0bits;
+       chk0rcv |= istat & port0rbits;
 
        /*
-        * clear the ones we will deal with on this round
-        * We clear it early, mostly for receive interrupts, so we
-        * know the chip will have seen this by the time we process
-        * the queue, and will re-interrupt if necessary.  The processor
-        * itself won't take the interrupt again until we return.
+        * Clear the interrupt bits we found set, unless they are receive
+        * related, in which case we already cleared them above, and don't
+        * want to clear them again, because we might lose an interrupt.
+        * Clear it early, so we "know" know the chip will have seen this by
+        * the time we process the queue, and will re-interrupt if necessary.
+        * The processor itself won't take the interrupt again until we return.
         */
        ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, istat);