[ofa-general] [PATCH 15/23] IB/ipath - use counters in ipath_poll and cleanup interrupts in ipath_close

Arthur Jones arthur.jones at qlogic.com
Tue Oct 9 13:00:32 PDT 2007


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 at qlogic.com>
---

 drivers/infiniband/hw/ipath/ipath_file_ops.c |   67 ++++++++++++++++----------
 drivers/infiniband/hw/ipath/ipath_intr.c     |   33 ++++---------
 drivers/infiniband/hw/ipath/ipath_kernel.h   |    8 ++-
 3 files changed, 57 insertions(+), 51 deletions(-)

diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c
index 33ab0d6..016e7c4 100644
--- a/drivers/infiniband/hw/ipath/ipath_file_ops.c
+++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c
@@ -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
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 @@ 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);
 			}
 		}
diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h
index d983f92..872fb36 100644
--- a/drivers/infiniband/hw/ipath/ipath_kernel.h
+++ b/drivers/infiniband/hw/ipath/ipath_kernel.h
@@ -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);




More information about the general mailing list