mirror of
https://github.com/adulau/aha.git
synced 2025-01-04 23:23:18 +00:00
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>
This commit is contained in:
parent
542869a17e
commit
70c51da2c4
3 changed files with 57 additions and 51 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue