Commit 70c51da2 authored by Arthur Jones's avatar Arthur Jones Committed by Roland Dreier

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: default avatarArthur Jones <arthur.jones@qlogic.com>
Signed-off-by: default avatarRoland Dreier <rolandd@cisco.com>
parent 542869a1
...@@ -1341,6 +1341,19 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma) ...@@ -1341,6 +1341,19 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma)
return ret; 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, static unsigned int ipath_poll_urgent(struct ipath_portdata *pd,
struct file *fp, struct file *fp,
struct poll_table_struct *pt) struct poll_table_struct *pt)
...@@ -1350,22 +1363,20 @@ static unsigned int ipath_poll_urgent(struct ipath_portdata *pd, ...@@ -1350,22 +1363,20 @@ static unsigned int ipath_poll_urgent(struct ipath_portdata *pd,
dd = pd->port_dd; dd = pd->port_dd;
if (test_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag)) { /* variable access in ipath_poll_hdrqfull() needs this */
pollflag |= POLLERR; rmb();
clear_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag); 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; pollflag |= POLLIN | POLLRDNORM;
clear_bit(IPATH_PORT_WAITING_URG, &pd->int_flag); pd->port_urgent_poll = pd->port_urgent;
} }
if (!pollflag) { if (!pollflag) {
/* this saves a spin_lock/unlock in interrupt handler... */
set_bit(IPATH_PORT_WAITING_URG, &pd->port_flag); set_bit(IPATH_PORT_WAITING_URG, &pd->port_flag);
if (pd->poll_type & IPATH_POLL_TYPE_OVERFLOW) /* flush waiting flag so don't miss an event... */
set_bit(IPATH_PORT_WAITING_OVERFLOW, wmb();
&pd->port_flag);
poll_wait(fp, &pd->port_wait, pt); poll_wait(fp, &pd->port_wait, pt);
} }
...@@ -1376,31 +1387,27 @@ static unsigned int ipath_poll_next(struct ipath_portdata *pd, ...@@ -1376,31 +1387,27 @@ static unsigned int ipath_poll_next(struct ipath_portdata *pd,
struct file *fp, struct file *fp,
struct poll_table_struct *pt) struct poll_table_struct *pt)
{ {
u32 head, tail; u32 head;
u32 tail;
unsigned pollflag = 0; unsigned pollflag = 0;
struct ipath_devdata *dd; struct ipath_devdata *dd;
dd = pd->port_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); head = ipath_read_ureg32(dd, ur_rcvhdrhead, pd->port_port);
tail = *(volatile u64 *)pd->port_rcvhdrtail_kvaddr; tail = *(volatile u64 *)pd->port_rcvhdrtail_kvaddr;
if (test_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag)) { if (head != tail)
pollflag |= POLLERR;
clear_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag);
}
if (tail != head ||
test_bit(IPATH_PORT_WAITING_RCV, &pd->int_flag)) {
pollflag |= POLLIN | POLLRDNORM; pollflag |= POLLIN | POLLRDNORM;
clear_bit(IPATH_PORT_WAITING_RCV, &pd->int_flag); else {
} /* this saves a spin_lock/unlock in interrupt handler */
if (!pollflag) {
set_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag); set_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag);
if (pd->poll_type & IPATH_POLL_TYPE_OVERFLOW) /* flush waiting flag so we don't miss an event */
set_bit(IPATH_PORT_WAITING_OVERFLOW, wmb();
&pd->port_flag);
set_bit(pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT, set_bit(pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT,
&dd->ipath_rcvctrl); &dd->ipath_rcvctrl);
...@@ -1917,6 +1924,12 @@ static int ipath_do_user_init(struct file *fp, ...@@ -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", ipath_cdbg(VERBOSE, "Wrote port%d egrhead %x from tail regs\n",
pd->port_port, head32); pd->port_port, head32);
pd->port_tidcursor = 0; /* start at beginning after open */ 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 * now enable the port; the tail registers will be written to memory
* by the chip as soon as it sees the write to * 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) ...@@ -2039,9 +2052,11 @@ static int ipath_close(struct inode *in, struct file *fp)
if (dd->ipath_kregbase) { if (dd->ipath_kregbase) {
int i; int i;
/* atomically clear receive enable port. */ /* atomically clear receive enable port and intr avail. */
clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + port, clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + port,
&dd->ipath_rcvctrl); &dd->ipath_rcvctrl);
clear_bit(pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT,
&dd->ipath_rcvctrl);
ipath_write_kreg( dd, dd->ipath_kregs->kr_rcvctrl, ipath_write_kreg( dd, dd->ipath_kregs->kr_rcvctrl,
dd->ipath_rcvctrl); dd->ipath_rcvctrl);
/* and read back from chip to be sure that nothing /* and read back from chip to be sure that nothing
......
...@@ -688,17 +688,9 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs) ...@@ -688,17 +688,9 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
chkerrpkts = 1; chkerrpkts = 1;
dd->ipath_lastrcvhdrqtails[i] = tl; dd->ipath_lastrcvhdrqtails[i] = tl;
pd->port_hdrqfull++; pd->port_hdrqfull++;
if (test_bit(IPATH_PORT_WAITING_OVERFLOW, /* flush hdrqfull so that poll() sees it */
&pd->port_flag)) { wmb();
clear_bit( wake_up_interruptible(&pd->port_wait);
IPATH_PORT_WAITING_OVERFLOW,
&pd->port_flag);
set_bit(
IPATH_PORT_WAITING_OVERFLOW,
&pd->int_flag);
wake_up_interruptible(
&pd->port_wait);
}
} }
} }
} }
...@@ -960,6 +952,8 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat) ...@@ -960,6 +952,8 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat)
int i; int i;
int rcvdint = 0; int rcvdint = 0;
/* test_bit below needs this... */
rmb();
portr = ((istat >> INFINIPATH_I_RCVAVAIL_SHIFT) & portr = ((istat >> INFINIPATH_I_RCVAVAIL_SHIFT) &
dd->ipath_i_rcvavail_mask) dd->ipath_i_rcvavail_mask)
| ((istat >> INFINIPATH_I_RCVURG_SHIFT) & | ((istat >> INFINIPATH_I_RCVURG_SHIFT) &
...@@ -967,22 +961,15 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat) ...@@ -967,22 +961,15 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat)
for (i = 1; i < dd->ipath_cfgports; i++) { for (i = 1; i < dd->ipath_cfgports; i++) {
struct ipath_portdata *pd = dd->ipath_pd[i]; struct ipath_portdata *pd = dd->ipath_pd[i];
if (portr & (1 << i) && pd && pd->port_cnt) { if (portr & (1 << i) && pd && pd->port_cnt) {
if (test_bit(IPATH_PORT_WAITING_RCV, if (test_and_clear_bit(IPATH_PORT_WAITING_RCV,
&pd->port_flag)) { &pd->port_flag)) {
clear_bit(IPATH_PORT_WAITING_RCV,
&pd->port_flag);
set_bit(IPATH_PORT_WAITING_RCV,
&pd->int_flag);
clear_bit(i + INFINIPATH_R_INTRAVAIL_SHIFT, clear_bit(i + INFINIPATH_R_INTRAVAIL_SHIFT,
&dd->ipath_rcvctrl); &dd->ipath_rcvctrl);
wake_up_interruptible(&pd->port_wait); wake_up_interruptible(&pd->port_wait);
rcvdint = 1; rcvdint = 1;
} else if (test_bit(IPATH_PORT_WAITING_URG, } else if (test_and_clear_bit(IPATH_PORT_WAITING_URG,
&pd->port_flag)) { &pd->port_flag)) {
clear_bit(IPATH_PORT_WAITING_URG, pd->port_urgent++;
&pd->port_flag);
set_bit(IPATH_PORT_WAITING_URG,
&pd->int_flag);
wake_up_interruptible(&pd->port_wait); wake_up_interruptible(&pd->port_wait);
} }
} }
......
...@@ -139,6 +139,12 @@ struct ipath_portdata { ...@@ -139,6 +139,12 @@ struct ipath_portdata {
u32 port_pionowait; u32 port_pionowait;
/* total number of rcvhdrqfull errors */ /* total number of rcvhdrqfull errors */
u32 port_hdrqfull; 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 of process using this port */
pid_t port_pid; pid_t port_pid;
/* same size as task_struct .comm[] */ /* same size as task_struct .comm[] */
...@@ -757,8 +763,6 @@ int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv); ...@@ -757,8 +763,6 @@ int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv);
#define IPATH_PORT_MASTER_UNINIT 4 #define IPATH_PORT_MASTER_UNINIT 4
/* waiting for an urgent packet to arrive */ /* waiting for an urgent packet to arrive */
#define IPATH_PORT_WAITING_URG 5 #define IPATH_PORT_WAITING_URG 5
/* waiting for a header overflow */
#define IPATH_PORT_WAITING_OVERFLOW 6
/* free up any allocated data at closes */ /* free up any allocated data at closes */
void ipath_free_data(struct ipath_portdata *dd); void ipath_free_data(struct ipath_portdata *dd);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment