IB/ipath: Use counters in ipath_poll and cleanup interrupts in ipath_close

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>
diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c
index 11b3614..61eac8c 100644
--- a/drivers/infiniband/hw/ipath/ipath_intr.c
+++ b/drivers/infiniband/hw/ipath/ipath_intr.c
@@ -688,17 +688,9 @@
 					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 @@
 	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 @@
 	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);
 			}
 		}