IB/ipath: Use counters in ipath_poll and cleanup interrupts in ipath_close
authorArthur Jones <arthur.jones@qlogic.com>
Fri, 14 Sep 2007 19:22:49 +0000 (12:22 -0700)
committerRoland Dreier <rolandd@cisco.com>
Wed, 10 Oct 2007 03:56:23 +0000 (20:56 -0700)
ipath_poll() suffered from a couple subtle bugs.  Under the right
conditions we could leave recv interrupts enabled on an ipath user
context on close, thereby taking potentially unwanted interrupts on the
next open -- this is fixed by unconditionally turning off recv
interrupts on close.  Also, we now use counters rather than set/clear
bits which allows us to make sure we catch all interrupts at the cost of
changing the semantics slightly (it's now give me all events since the
last time I called poll() rather than give me all events since I called
_this_ poll routine).  We also added some memory barriers which may help
ensure we get all notifications in a timely manner.

Signed-off-by: Arthur Jones <arthur.jones@qlogic.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
drivers/infiniband/hw/ipath/ipath_file_ops.c
drivers/infiniband/hw/ipath/ipath_intr.c
drivers/infiniband/hw/ipath/ipath_kernel.h

index 33ab0d6b80ff3085a0ed423b4fe980fd1c938807..016e7c4e366b0f761402d30b039948275d75b7a3 100644 (file)
@@ -1341,6 +1341,19 @@ bail:
        return ret;
 }
 
+static unsigned ipath_poll_hdrqfull(struct ipath_portdata *pd)
+{
+       unsigned pollflag = 0;
+
+       if ((pd->poll_type & IPATH_POLL_TYPE_OVERFLOW) &&
+           pd->port_hdrqfull != pd->port_hdrqfull_poll) {
+               pollflag |= POLLIN | POLLRDNORM;
+               pd->port_hdrqfull_poll = pd->port_hdrqfull;
+       }
+
+       return pollflag;
+}
+
 static unsigned int ipath_poll_urgent(struct ipath_portdata *pd,
                                      struct file *fp,
                                      struct poll_table_struct *pt)
@@ -1350,22 +1363,20 @@ static unsigned int ipath_poll_urgent(struct ipath_portdata *pd,
 
        dd = pd->port_dd;
 
-       if (test_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag)) {
-               pollflag |= POLLERR;
-               clear_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag);
-       }
+       /* variable access in ipath_poll_hdrqfull() needs this */
+       rmb();
+       pollflag = ipath_poll_hdrqfull(pd);
 
-       if (test_bit(IPATH_PORT_WAITING_URG, &pd->int_flag)) {
+       if (pd->port_urgent != pd->port_urgent_poll) {
                pollflag |= POLLIN | POLLRDNORM;
-               clear_bit(IPATH_PORT_WAITING_URG, &pd->int_flag);
+               pd->port_urgent_poll = pd->port_urgent;
        }
 
        if (!pollflag) {
+               /* this saves a spin_lock/unlock in interrupt handler... */
                set_bit(IPATH_PORT_WAITING_URG, &pd->port_flag);
-               if (pd->poll_type & IPATH_POLL_TYPE_OVERFLOW)
-                       set_bit(IPATH_PORT_WAITING_OVERFLOW,
-                               &pd->port_flag);
-
+               /* flush waiting flag so don't miss an event... */
+               wmb();
                poll_wait(fp, &pd->port_wait, pt);
        }
 
@@ -1376,31 +1387,27 @@ static unsigned int ipath_poll_next(struct ipath_portdata *pd,
                                    struct file *fp,
                                    struct poll_table_struct *pt)
 {
-       u32 head, tail;
+       u32 head;
+       u32 tail;
        unsigned pollflag = 0;
        struct ipath_devdata *dd;
 
        dd = pd->port_dd;
 
+       /* variable access in ipath_poll_hdrqfull() needs this */
+       rmb();
+       pollflag = ipath_poll_hdrqfull(pd);
+
        head = ipath_read_ureg32(dd, ur_rcvhdrhead, pd->port_port);
        tail = *(volatile u64 *)pd->port_rcvhdrtail_kvaddr;
 
-       if (test_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag)) {
-               pollflag |= POLLERR;
-               clear_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag);
-       }
-
-       if (tail != head ||
-           test_bit(IPATH_PORT_WAITING_RCV, &pd->int_flag)) {
+       if (head != tail)
                pollflag |= POLLIN | POLLRDNORM;
-               clear_bit(IPATH_PORT_WAITING_RCV, &pd->int_flag);
-       }
-
-       if (!pollflag) {
+       else {
+               /* this saves a spin_lock/unlock in interrupt handler */
                set_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag);
-               if (pd->poll_type & IPATH_POLL_TYPE_OVERFLOW)
-                       set_bit(IPATH_PORT_WAITING_OVERFLOW,
-                               &pd->port_flag);
+               /* flush waiting flag so we don't miss an event */
+               wmb();
 
                set_bit(pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT,
                        &dd->ipath_rcvctrl);
@@ -1917,6 +1924,12 @@ static int ipath_do_user_init(struct file *fp,
        ipath_cdbg(VERBOSE, "Wrote port%d egrhead %x from tail regs\n",
                pd->port_port, head32);
        pd->port_tidcursor = 0; /* start at beginning after open */
+
+       /* initialize poll variables... */
+       pd->port_urgent = 0;
+       pd->port_urgent_poll = 0;
+       pd->port_hdrqfull_poll = pd->port_hdrqfull;
+
        /*
         * now enable the port; the tail registers will be written to memory
         * by the chip as soon as it sees the write to
@@ -2039,9 +2052,11 @@ static int ipath_close(struct inode *in, struct file *fp)
 
        if (dd->ipath_kregbase) {
                int i;
-               /* atomically clear receive enable port. */
+               /* atomically clear receive enable port and intr avail. */
                clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + port,
                          &dd->ipath_rcvctrl);
+               clear_bit(pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT,
+                         &dd->ipath_rcvctrl);
                ipath_write_kreg( dd, dd->ipath_kregs->kr_rcvctrl,
                        dd->ipath_rcvctrl);
                /* and read back from chip to be sure that nothing
index 11b361408ae6a6b5497d87263adf9dc0e7e55525..61eac8cc0d9f1836909087149c81e8f697fe2d3d 100644 (file)
@@ -688,17 +688,9 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
                                        chkerrpkts = 1;
                                dd->ipath_lastrcvhdrqtails[i] = tl;
                                pd->port_hdrqfull++;
-                               if (test_bit(IPATH_PORT_WAITING_OVERFLOW,
-                                            &pd->port_flag)) {
-                                       clear_bit(
-                                         IPATH_PORT_WAITING_OVERFLOW,
-                                         &pd->port_flag);
-                                       set_bit(
-                                         IPATH_PORT_WAITING_OVERFLOW,
-                                         &pd->int_flag);
-                                       wake_up_interruptible(
-                                         &pd->port_wait);
-                               }
+                               /* flush hdrqfull so that poll() sees it */
+                               wmb();
+                               wake_up_interruptible(&pd->port_wait);
                        }
                }
        }
@@ -960,6 +952,8 @@ 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) &
@@ -967,22 +961,15 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat)
        for (i = 1; i < dd->ipath_cfgports; i++) {
                struct ipath_portdata *pd = dd->ipath_pd[i];
                if (portr & (1 << i) && pd && pd->port_cnt) {
-                       if (test_bit(IPATH_PORT_WAITING_RCV,
-                                    &pd->port_flag)) {
-                               clear_bit(IPATH_PORT_WAITING_RCV,
-                                         &pd->port_flag);
-                               set_bit(IPATH_PORT_WAITING_RCV,
-                                       &pd->int_flag);
+                       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_bit(IPATH_PORT_WAITING_URG,
-                                           &pd->port_flag)) {
-                               clear_bit(IPATH_PORT_WAITING_URG,
-                                         &pd->port_flag);
-                               set_bit(IPATH_PORT_WAITING_URG,
-                                       &pd->int_flag);
+                       } else if (test_and_clear_bit(IPATH_PORT_WAITING_URG,
+                                                     &pd->port_flag)) {
+                               pd->port_urgent++;
                                wake_up_interruptible(&pd->port_wait);
                        }
                }
index d983f92b9bcbe2569393f9189c1bf1e224fd4c7a..872fb36703d973250b601c6dff670d4dbf158773 100644 (file)
@@ -139,6 +139,12 @@ struct ipath_portdata {
        u32 port_pionowait;
        /* total number of rcvhdrqfull errors */
        u32 port_hdrqfull;
+       /* saved total number of rcvhdrqfull errors for poll edge trigger */
+       u32 port_hdrqfull_poll;
+       /* total number of polled urgent packets */
+       u32 port_urgent;
+       /* saved total number of polled urgent packets for poll edge trigger */
+       u32 port_urgent_poll;
        /* pid of process using this port */
        pid_t port_pid;
        /* same size as task_struct .comm[] */
@@ -757,8 +763,6 @@ int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv);
 #define IPATH_PORT_MASTER_UNINIT 4
                /* waiting for an urgent packet to arrive */
 #define IPATH_PORT_WAITING_URG 5
-               /* waiting for a header overflow */
-#define IPATH_PORT_WAITING_OVERFLOW 6
 
 /* free up any allocated data at closes */
 void ipath_free_data(struct ipath_portdata *dd);