]> err.no Git - linux-2.6/blobdiff - drivers/infiniband/hw/ipath/ipath_driver.c
[PATCH] IB/ipath: fix lost interrupts on HT-400
[linux-2.6] / drivers / infiniband / hw / ipath / ipath_driver.c
index e2b0bc8ebaf412fdefa9303ee0716a236bd367eb..66b41ecf898b734b5dd7bcb97bffd35c4c7f55f4 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;