]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/infiniband/hw/ipath/ipath_intr.c
IB/ipath: Use counters in ipath_poll and cleanup interrupts in ipath_close
[linux-2.6-omap-h63xx.git] / drivers / infiniband / hw / ipath / ipath_intr.c
index a90d3b5699c46cc1169faf99ef3c72fbc4a92772..61eac8cc0d9f1836909087149c81e8f697fe2d3d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 QLogic, Inc. All rights reserved.
+ * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
  * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
@@ -70,7 +70,7 @@ static void ipath_clrpiobuf(struct ipath_devdata *dd, u32 pnum)
  * If rewrite is true, and bits are set in the sendbufferror registers,
  * we'll write to the buffer, for error recovery on parity errors.
  */
-void ipath_disarm_senderrbufs(struct ipath_devdata *dd, int rewrite)
+static void ipath_disarm_senderrbufs(struct ipath_devdata *dd, int rewrite)
 {
        u32 piobcnt;
        unsigned long sbuf[4];
@@ -93,7 +93,8 @@ void ipath_disarm_senderrbufs(struct ipath_devdata *dd, int rewrite)
 
        if (sbuf[0] || sbuf[1] || (piobcnt > 128 && (sbuf[2] || sbuf[3]))) {
                int i;
-               if (ipath_debug & (__IPATH_PKTDBG|__IPATH_DBG)) {
+               if (ipath_debug & (__IPATH_PKTDBG|__IPATH_DBG) &&
+                       dd->ipath_lastcancel > jiffies) {
                        __IPATH_DBG_WHICH(__IPATH_PKTDBG|__IPATH_DBG,
                                          "SendbufErrs %lx %lx", sbuf[0],
                                          sbuf[1]);
@@ -108,7 +109,8 @@ void ipath_disarm_senderrbufs(struct ipath_devdata *dd, int rewrite)
                                        ipath_clrpiobuf(dd, i);
                                ipath_disarm_piobufs(dd, i, 1);
                        }
-               dd->ipath_lastcancel = jiffies+3; /* no armlaunch for a bit */
+               /* ignore armlaunch errs for a bit */
+               dd->ipath_lastcancel = jiffies+3;
        }
 }
 
@@ -130,6 +132,17 @@ void ipath_disarm_senderrbufs(struct ipath_devdata *dd, int rewrite)
         INFINIPATH_E_SMINPKTLEN | INFINIPATH_E_SPKTLEN | \
         INFINIPATH_E_INVALIDADDR)
 
+/*
+ * this is similar to E_SUM_ERRS, but can't ignore armlaunch, don't ignore
+ * errors not related to freeze and cancelling buffers.  Can't ignore
+ * armlaunch because could get more while still cleaning up, and need
+ * to cancel those as they happen.
+ */
+#define E_SPKT_ERRS_IGNORE \
+        (INFINIPATH_E_SDROPPEDDATAPKT | INFINIPATH_E_SDROPPEDSMPPKT | \
+        INFINIPATH_E_SMAXPKTLEN | INFINIPATH_E_SMINPKTLEN | \
+        INFINIPATH_E_SPKTLEN)
+
 /*
  * these are errors that can occur when the link changes state while
  * a packet is being sent or received.  This doesn't cover things
@@ -290,12 +303,7 @@ static void handle_e_ibstatuschanged(struct ipath_devdata *dd,
                 * Flush all queued sends when link went to DOWN or INIT,
                 * to be sure that they don't block SMA and other MAD packets
                 */
-               ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
-                                INFINIPATH_S_ABORT);
-               ipath_disarm_piobufs(dd, dd->ipath_lastport_piobuf,
-                                                       (unsigned)(dd->ipath_piobcnt2k +
-                                       dd->ipath_piobcnt4k) -
-                                       dd->ipath_lastport_piobuf);
+               ipath_cancel_sends(dd, 1);
        }
        else if (lstate == IPATH_IBSTATE_INIT || lstate == IPATH_IBSTATE_ARM ||
            lstate == IPATH_IBSTATE_ACTIVE) {
@@ -505,19 +513,24 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
        int i, iserr = 0;
        int chkerrpkts = 0, noprint = 0;
        unsigned supp_msgs;
+       int log_idx;
 
        supp_msgs = handle_frequent_errors(dd, errs, msg, &noprint);
 
-       /*
-        * don't report errors that are masked (includes those always
-        * ignored)
-        */
+       /* don't report errors that are masked */
        errs &= ~dd->ipath_maskederrs;
 
        /* do these first, they are most important */
        if (errs & INFINIPATH_E_HARDWARE) {
                /* reuse same msg buf */
                dd->ipath_f_handle_hwerrors(dd, msg, sizeof msg);
+       } else {
+               u64 mask;
+               for (log_idx = 0; log_idx < IPATH_EEP_LOG_CNT; ++log_idx) {
+                       mask = dd->ipath_eep_st_masks[log_idx].errs_to_log;
+                       if (errs & mask)
+                               ipath_inc_eeprom_err(dd, log_idx, 1);
+               }
        }
 
        if (!noprint && (errs & ~dd->ipath_e_bitsextant))
@@ -550,19 +563,19 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
                 * ones on this particular interrupt, which also isn't great
                 */
                dd->ipath_maskederrs |= dd->ipath_lasterror | errs;
+               dd->ipath_errormask &= ~dd->ipath_maskederrs;
                ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask,
-                                ~dd->ipath_maskederrs);
+                       dd->ipath_errormask);
                s_iserr = ipath_decode_err(msg, sizeof msg,
-                                (dd->ipath_maskederrs & ~dd->
-                                 ipath_ignorederrs));
+                       dd->ipath_maskederrs);
 
-               if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) &
+               if (dd->ipath_maskederrs &
                        ~(INFINIPATH_E_RRCVEGRFULL |
                        INFINIPATH_E_RRCVHDRFULL | INFINIPATH_E_PKTERRS))
                        ipath_dev_err(dd, "Temporarily disabling "
                            "error(s) %llx reporting; too frequent (%s)\n",
-                               (unsigned long long) (dd->ipath_maskederrs &
-                               ~dd->ipath_ignorederrs), msg);
+                               (unsigned long long)dd->ipath_maskederrs,
+                               msg);
                else {
                        /*
                         * rcvegrfull and rcvhdrqfull are "normal",
@@ -675,6 +688,9 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
                                        chkerrpkts = 1;
                                dd->ipath_lastrcvhdrqtails[i] = tl;
                                pd->port_hdrqfull++;
+                               /* flush hdrqfull so that poll() sees it */
+                               wmb();
+                               wake_up_interruptible(&pd->port_wait);
                        }
                }
        }
@@ -744,6 +760,76 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
        return chkerrpkts;
 }
 
+
+/*
+ * try to cleanup as much as possible for anything that might have gone
+ * wrong while in freeze mode, such as pio buffers being written by user
+ * processes (causing armlaunch), send errors due to going into freeze mode,
+ * etc., and try to avoid causing extra interrupts while doing so.
+ * Forcibly update the in-memory pioavail register copies after cleanup
+ * because the chip won't do it for anything changing while in freeze mode
+ * (we don't want to wait for the next pio buffer state change).
+ * Make sure that we don't lose any important interrupts by using the chip
+ * feature that says that writing 0 to a bit in *clear that is set in
+ * *status will cause an interrupt to be generated again (if allowed by
+ * the *mask value).
+ */
+void ipath_clear_freeze(struct ipath_devdata *dd)
+{
+       int i, im;
+       __le64 val;
+
+       /* disable error interrupts, to avoid confusion */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask, 0ULL);
+
+       /* also disable interrupts; errormask is sometimes overwriten */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_intmask, 0ULL);
+
+       /*
+        * clear all sends, because they have may been
+        * completed by usercode while in freeze mode, and
+        * therefore would not be sent, and eventually
+        * might cause the process to run out of bufs
+        */
+       ipath_cancel_sends(dd, 0);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_control,
+                        dd->ipath_control);
+
+       /* ensure pio avail updates continue */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
+                dd->ipath_sendctrl & ~INFINIPATH_S_PIOBUFAVAILUPD);
+       ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
+                dd->ipath_sendctrl);
+
+       /*
+        * We just enabled pioavailupdate, so dma copy is almost certainly
+        * not yet right, so read the registers directly.  Similar to init
+        */
+       for (i = 0; i < dd->ipath_pioavregs; i++) {
+               /* deal with 6110 chip bug */
+               im = i > 3 ? ((i&1) ? i-1 : i+1) : i;
+               val = ipath_read_kreg64(dd, (0x1000/sizeof(u64))+im);
+               dd->ipath_pioavailregs_dma[i] = dd->ipath_pioavailshadow[i]
+                       = le64_to_cpu(val);
+       }
+
+       /*
+        * force new interrupt if any hwerr, error or interrupt bits are
+        * still set, and clear "safe" send packet errors related to freeze
+        * and cancelling sends.  Re-enable error interrupts before possible
+        * force of re-interrupt on pending interrupts.
+        */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear, 0ULL);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear,
+               E_SPKT_ERRS_IGNORE);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask,
+               dd->ipath_errormask);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_intmask, -1LL);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, 0ULL);
+}
+
+
 /* this is separate to allow for better optimization of ipath_intr() */
 
 static void ipath_bad_intr(struct ipath_devdata *dd, u32 * unexpectp)
@@ -866,20 +952,26 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat)
        int i;
        int rcvdint = 0;
 
+       /* test_bit below needs this... */
+       rmb();
        portr = ((istat >> INFINIPATH_I_RCVAVAIL_SHIFT) &
                 dd->ipath_i_rcvavail_mask)
                | ((istat >> INFINIPATH_I_RCVURG_SHIFT) &
                   dd->ipath_i_rcvurg_mask);
        for (i = 1; i < dd->ipath_cfgports; i++) {
                struct ipath_portdata *pd = dd->ipath_pd[i];
-               if (portr & (1 << i) && pd && pd->port_cnt &&
-                       test_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag)) {
-                       clear_bit(IPATH_PORT_WAITING_RCV,
-                                 &pd->port_flag);
-                       clear_bit(i + INFINIPATH_R_INTRAVAIL_SHIFT,
-                                 &dd->ipath_rcvctrl);
-                       wake_up_interruptible(&pd->port_wait);
-                       rcvdint = 1;
+               if (portr & (1 << i) && pd && pd->port_cnt) {
+                       if (test_and_clear_bit(IPATH_PORT_WAITING_RCV,
+                                              &pd->port_flag)) {
+                               clear_bit(i + INFINIPATH_R_INTRAVAIL_SHIFT,
+                                         &dd->ipath_rcvctrl);
+                               wake_up_interruptible(&pd->port_wait);
+                               rcvdint = 1;
+                       } else if (test_and_clear_bit(IPATH_PORT_WAITING_URG,
+                                                     &pd->port_flag)) {
+                               pd->port_urgent++;
+                               wake_up_interruptible(&pd->port_wait);
+                       }
                }
        }
        if (rcvdint) {
@@ -898,13 +990,15 @@ irqreturn_t ipath_intr(int irq, void *data)
        u32 istat, chk0rcv = 0;
        ipath_err_t estat = 0;
        irqreturn_t ret;
-       u32 oldhead, curtail;
        static unsigned unexpected = 0;
        static const u32 port0rbits = (1U<<INFINIPATH_I_RCVAVAIL_SHIFT) |
                 (1U<<INFINIPATH_I_RCVURG_SHIFT);
 
        ipath_stats.sps_ints++;
 
+       if (dd->ipath_int_counter != (u32) -1)
+               dd->ipath_int_counter++;
+
        if (!(dd->ipath_flags & IPATH_PRESENT)) {
                /*
                 * This return value is not great, but we do not want the
@@ -928,36 +1022,6 @@ irqreturn_t ipath_intr(int irq, void *data)
                goto bail;
        }
 
-       /*
-        * We try to avoid reading the interrupt status register, since
-        * that's a PIO read, and stalls the processor for up to about
-        * ~0.25 usec. The idea is that if we processed a port0 packet,
-        * we blindly clear the  port 0 receive interrupt bits, and nothing
-        * else, then return.  If other interrupts are pending, the chip
-        * will re-interrupt us as soon as we write the intclear register.
-        * We then won't process any more kernel packets (if not the 2nd
-        * time, then the 3rd or 4th) and we'll then handle the other
-        * interrupts.   We clear the interrupts first so that we don't
-        * lose intr for later packets that arrive while we are processing.
-        */
-       oldhead = dd->ipath_port0head;
-       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 << IPATH_GPIO_PORT0_BIT));
-                       istat = port0rbits | INFINIPATH_I_GPIO;
-               }
-               else
-                       istat = port0rbits;
-               ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, istat);
-               ipath_kreceive(dd);
-               if (oldhead != dd->ipath_port0head) {
-                       ipath_stats.sps_fastrcvint++;
-                       goto done;
-               }
-       }
-
        istat = ipath_read_kreg32(dd, dd->ipath_kregs->kr_intstatus);
 
        if (unlikely(!istat)) {
@@ -1008,8 +1072,8 @@ irqreturn_t ipath_intr(int irq, void *data)
                 * GPIO_2 indicates (on some HT4xx boards) that a packet
                 *        has arrived for Port 0. Checking for this
                 *        is controlled by flag IPATH_GPIO_INTR.
-                * GPIO_3..5 on IBA6120 Rev2 chips indicate errors
-                *        that we need to count. Checking for this
+                * GPIO_3..5 on IBA6120 Rev2 and IBA6110 Rev4 chips indicate
+                *        errors that we need to count. Checking for this
                 *        is controlled by flag IPATH_GPIO_ERRINTRS.
                 */
                u32 gpiostatus;
@@ -1118,7 +1182,6 @@ irqreturn_t ipath_intr(int irq, void *data)
                handle_layer_pioavail(dd);
        }
 
-done:
        ret = IRQ_HANDLED;
 
 bail: