Author: marcel
Date: Fri Dec 11 03:08:07 2009
New Revision: 200397
URL: http://svn.freebsd.org/changeset/base/200397

Log:
  Fix interrupt handling. It started off broken and grew worse over time.
  The rewrite of the interrupt handler includes:
  o   loop until all pending interrupts are handled. This closes a
      race condition.
  o   count the number of interrupt sources we handled so that we can
      properly return FILTER_HANDLED or FILTER_STRAY when we break out
      of the loop.
  o   When matching the interrupt source to the devices that have that
      source pending, check only from the set of devices we found to
      have a pending interrupt.
  
  PR:           kern/140947
  MFC after:    3 days

Modified:
  head/sys/dev/puc/puc.c

Modified: head/sys/dev/puc/puc.c
==============================================================================
--- head/sys/dev/puc/puc.c      Fri Dec 11 02:52:14 2009        (r200396)
+++ head/sys/dev/puc/puc.c      Fri Dec 11 03:08:07 2009        (r200397)
@@ -129,62 +129,78 @@ puc_intr(void *arg)
 {
        struct puc_port *port;
        struct puc_softc *sc = arg;
-       u_long dev, devs;
-       int i, idx, ipend, isrc;
+       u_long ds, dev, devs;
+       int i, idx, ipend, isrc, nints;
        uint8_t ilr;
 
-       devs = sc->sc_serdevs;
-       if (sc->sc_ilr == PUC_ILR_DIGI) {
-               idx = 0;
-               while (devs & (0xfful << idx)) {
-                       ilr = ~bus_read_1(sc->sc_port[idx].p_rres, 7);
-                       devs &= ~0ul ^ ((u_long)ilr << idx);
-                       idx += 8;
-               }
-       } else if (sc->sc_ilr == PUC_ILR_QUATECH) {
+       nints = 0;
+       while (1) {
                /*
-                * Don't trust the value if it's the same as the option
-                * register. It may mean that the ILR is not active and
-                * we're reading the option register instead. This may
-                * lead to false positives on 8-port boards.
+                * Obtain the set of devices with pending interrupts.
                 */
-               ilr = bus_read_1(sc->sc_port[0].p_rres, 7);
-               if (ilr != (sc->sc_cfg_data & 0xff))
-                       devs &= (u_long)ilr;
-       }
-
-       ipend = 0;
-       idx = 0, dev = 1UL;
-       while (devs != 0UL) {
-               while ((devs & dev) == 0UL)
-                       idx++, dev <<= 1;
-               devs &= ~dev;
-               port = &sc->sc_port[idx];
-               port->p_ipend = SERDEV_IPEND(port->p_dev);
-               ipend |= port->p_ipend;
-       }
+               devs = sc->sc_serdevs;
+               if (sc->sc_ilr == PUC_ILR_DIGI) {
+                       idx = 0;
+                       while (devs & (0xfful << idx)) {
+                               ilr = ~bus_read_1(sc->sc_port[idx].p_rres, 7);
+                               devs &= ~0ul ^ ((u_long)ilr << idx);
+                               idx += 8;
+                       }
+               } else if (sc->sc_ilr == PUC_ILR_QUATECH) {
+                       /*
+                        * Don't trust the value if it's the same as the option
+                        * register. It may mean that the ILR is not active and
+                        * we're reading the option register instead. This may
+                        * lead to false positives on 8-port boards.
+                        */
+                       ilr = bus_read_1(sc->sc_port[0].p_rres, 7);
+                       if (ilr != (sc->sc_cfg_data & 0xff))
+                               devs &= (u_long)ilr;
+               }
+               if (devs == 0UL)
+                       break;
 
-       i = 0, isrc = SER_INT_OVERRUN;
-       while (ipend) {
-               while (i < PUC_ISRCCNT && !(ipend & isrc))
-                       i++, isrc <<= 1;
-               KASSERT(i < PUC_ISRCCNT, ("%s", __func__));
-               ipend &= ~isrc;
+               /*
+                * Obtain the set of interrupt sources from those devices
+                * that have pending interrupts.
+                */
+               ipend = 0;
                idx = 0, dev = 1UL;
-               devs = sc->sc_serdevs;
-               while (devs != 0UL) {
-                       while ((devs & dev) == 0UL)
+               ds = devs;
+               while (ds != 0UL) {
+                       while ((ds & dev) == 0UL)
                                idx++, dev <<= 1;
-                       devs &= ~dev;
+                       ds &= ~dev;
                        port = &sc->sc_port[idx];
-                       if (!(port->p_ipend & isrc))
-                               continue;
-                       if (port->p_ihsrc[i] != NULL)
-                               (*port->p_ihsrc[i])(port->p_iharg);
+                       port->p_ipend = SERDEV_IPEND(port->p_dev);
+                       ipend |= port->p_ipend;
+               }
+               if (ipend == 0)
+                       break;
+
+               i = 0, isrc = SER_INT_OVERRUN;
+               while (ipend) {
+                       while (i < PUC_ISRCCNT && !(ipend & isrc))
+                               i++, isrc <<= 1;
+                       KASSERT(i < PUC_ISRCCNT, ("%s", __func__));
+                       ipend &= ~isrc;
+                       idx = 0, dev = 1UL;
+                       ds = devs;
+                       while (ds != 0UL) {
+                               while ((ds & dev) == 0UL)
+                                       idx++, dev <<= 1;
+                               ds &= ~dev;
+                               port = &sc->sc_port[idx];
+                               if (!(port->p_ipend & isrc))
+                                       continue;
+                               if (port->p_ihsrc[i] != NULL)
+                                       (*port->p_ihsrc[i])(port->p_iharg);
+                               nints++;
+                       }
                }
-               return (FILTER_HANDLED);
        }
-       return (FILTER_STRAY);
+
+       return ((nints > 0) ? FILTER_HANDLED : FILTER_STRAY);
 }
 
 int
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to