diff options
author | Arthur Jones <arthur.jones@qlogic.com> | 2007-09-14 15:22:49 -0400 |
---|---|---|
committer | Roland Dreier <rolandd@cisco.com> | 2007-10-09 23:56:23 -0400 |
commit | 70c51da2c4f84317bb13a2b564600afdcebd686f (patch) | |
tree | d7c39e3efaf6c90482cfe9d0e85012c3c9c511a7 | |
parent | 542869a17eee2edf389273f40f757aa4e662b3da (diff) |
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>
-rw-r--r-- | drivers/infiniband/hw/ipath/ipath_file_ops.c | 67 | ||||
-rw-r--r-- | drivers/infiniband/hw/ipath/ipath_intr.c | 33 | ||||
-rw-r--r-- | 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 33ab0d6b80f..016e7c4e366 100644 --- a/drivers/infiniband/hw/ipath/ipath_file_ops.c +++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c | |||
@@ -1341,6 +1341,19 @@ bail: | |||
1341 | return ret; | 1341 | return ret; |
1342 | } | 1342 | } |
1343 | 1343 | ||
1344 | static unsigned ipath_poll_hdrqfull(struct ipath_portdata *pd) | ||
1345 | { | ||
1346 | unsigned pollflag = 0; | ||
1347 | |||
1348 | if ((pd->poll_type & IPATH_POLL_TYPE_OVERFLOW) && | ||
1349 | pd->port_hdrqfull != pd->port_hdrqfull_poll) { | ||
1350 | pollflag |= POLLIN | POLLRDNORM; | ||
1351 | pd->port_hdrqfull_poll = pd->port_hdrqfull; | ||
1352 | } | ||
1353 | |||
1354 | return pollflag; | ||
1355 | } | ||
1356 | |||
1344 | static unsigned int ipath_poll_urgent(struct ipath_portdata *pd, | 1357 | static unsigned int ipath_poll_urgent(struct ipath_portdata *pd, |
1345 | struct file *fp, | 1358 | struct file *fp, |
1346 | struct poll_table_struct *pt) | 1359 | struct poll_table_struct *pt) |
@@ -1350,22 +1363,20 @@ static unsigned int ipath_poll_urgent(struct ipath_portdata *pd, | |||
1350 | 1363 | ||
1351 | dd = pd->port_dd; | 1364 | dd = pd->port_dd; |
1352 | 1365 | ||
1353 | if (test_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag)) { | 1366 | /* variable access in ipath_poll_hdrqfull() needs this */ |
1354 | pollflag |= POLLERR; | 1367 | rmb(); |
1355 | clear_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag); | 1368 | pollflag = ipath_poll_hdrqfull(pd); |
1356 | } | ||
1357 | 1369 | ||
1358 | if (test_bit(IPATH_PORT_WAITING_URG, &pd->int_flag)) { | 1370 | if (pd->port_urgent != pd->port_urgent_poll) { |
1359 | pollflag |= POLLIN | POLLRDNORM; | 1371 | pollflag |= POLLIN | POLLRDNORM; |
1360 | clear_bit(IPATH_PORT_WAITING_URG, &pd->int_flag); | 1372 | pd->port_urgent_poll = pd->port_urgent; |
1361 | } | 1373 | } |
1362 | 1374 | ||
1363 | if (!pollflag) { | 1375 | if (!pollflag) { |
1376 | /* this saves a spin_lock/unlock in interrupt handler... */ | ||
1364 | set_bit(IPATH_PORT_WAITING_URG, &pd->port_flag); | 1377 | set_bit(IPATH_PORT_WAITING_URG, &pd->port_flag); |
1365 | if (pd->poll_type & IPATH_POLL_TYPE_OVERFLOW) | 1378 | /* flush waiting flag so don't miss an event... */ |
1366 | set_bit(IPATH_PORT_WAITING_OVERFLOW, | 1379 | wmb(); |
1367 | &pd->port_flag); | ||
1368 | |||
1369 | poll_wait(fp, &pd->port_wait, pt); | 1380 | poll_wait(fp, &pd->port_wait, pt); |
1370 | } | 1381 | } |
1371 | 1382 | ||
@@ -1376,31 +1387,27 @@ static unsigned int ipath_poll_next(struct ipath_portdata *pd, | |||
1376 | struct file *fp, | 1387 | struct file *fp, |
1377 | struct poll_table_struct *pt) | 1388 | struct poll_table_struct *pt) |
1378 | { | 1389 | { |
1379 | u32 head, tail; | 1390 | u32 head; |
1391 | u32 tail; | ||
1380 | unsigned pollflag = 0; | 1392 | unsigned pollflag = 0; |
1381 | struct ipath_devdata *dd; | 1393 | struct ipath_devdata *dd; |
1382 | 1394 | ||
1383 | dd = pd->port_dd; | 1395 | dd = pd->port_dd; |
1384 | 1396 | ||
1397 | /* variable access in ipath_poll_hdrqfull() needs this */ | ||
1398 | rmb(); | ||
1399 | pollflag = ipath_poll_hdrqfull(pd); | ||
1400 | |||
1385 | head = ipath_read_ureg32(dd, ur_rcvhdrhead, pd->port_port); | 1401 | head = ipath_read_ureg32(dd, ur_rcvhdrhead, pd->port_port); |
1386 | tail = *(volatile u64 *)pd->port_rcvhdrtail_kvaddr; | 1402 | tail = *(volatile u64 *)pd->port_rcvhdrtail_kvaddr; |
1387 | 1403 | ||
1388 | if (test_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag)) { | 1404 | if (head != tail) |
1389 | pollflag |= POLLERR; | ||
1390 | clear_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag); | ||
1391 | } | ||
1392 | |||
1393 | if (tail != head || | ||
1394 | test_bit(IPATH_PORT_WAITING_RCV, &pd->int_flag)) { | ||
1395 | pollflag |= POLLIN | POLLRDNORM; | 1405 | pollflag |= POLLIN | POLLRDNORM; |
1396 | clear_bit(IPATH_PORT_WAITING_RCV, &pd->int_flag); | 1406 | else { |
1397 | } | 1407 | /* this saves a spin_lock/unlock in interrupt handler */ |
1398 | |||
1399 | if (!pollflag) { | ||
1400 | set_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag); | 1408 | set_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag); |
1401 | if (pd->poll_type & IPATH_POLL_TYPE_OVERFLOW) | 1409 | /* flush waiting flag so we don't miss an event */ |
1402 | set_bit(IPATH_PORT_WAITING_OVERFLOW, | 1410 | wmb(); |
1403 | &pd->port_flag); | ||
1404 | 1411 | ||
1405 | set_bit(pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT, | 1412 | set_bit(pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT, |
1406 | &dd->ipath_rcvctrl); | 1413 | &dd->ipath_rcvctrl); |
@@ -1917,6 +1924,12 @@ static int ipath_do_user_init(struct file *fp, | |||
1917 | ipath_cdbg(VERBOSE, "Wrote port%d egrhead %x from tail regs\n", | 1924 | ipath_cdbg(VERBOSE, "Wrote port%d egrhead %x from tail regs\n", |
1918 | pd->port_port, head32); | 1925 | pd->port_port, head32); |
1919 | pd->port_tidcursor = 0; /* start at beginning after open */ | 1926 | pd->port_tidcursor = 0; /* start at beginning after open */ |
1927 | |||
1928 | /* initialize poll variables... */ | ||
1929 | pd->port_urgent = 0; | ||
1930 | pd->port_urgent_poll = 0; | ||
1931 | pd->port_hdrqfull_poll = pd->port_hdrqfull; | ||
1932 | |||
1920 | /* | 1933 | /* |
1921 | * now enable the port; the tail registers will be written to memory | 1934 | * now enable the port; the tail registers will be written to memory |
1922 | * by the chip as soon as it sees the write to | 1935 | * 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 | 2052 | ||
2040 | if (dd->ipath_kregbase) { | 2053 | if (dd->ipath_kregbase) { |
2041 | int i; | 2054 | int i; |
2042 | /* atomically clear receive enable port. */ | 2055 | /* atomically clear receive enable port and intr avail. */ |
2043 | clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + port, | 2056 | clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + port, |
2044 | &dd->ipath_rcvctrl); | 2057 | &dd->ipath_rcvctrl); |
2058 | clear_bit(pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT, | ||
2059 | &dd->ipath_rcvctrl); | ||
2045 | ipath_write_kreg( dd, dd->ipath_kregs->kr_rcvctrl, | 2060 | ipath_write_kreg( dd, dd->ipath_kregs->kr_rcvctrl, |
2046 | dd->ipath_rcvctrl); | 2061 | dd->ipath_rcvctrl); |
2047 | /* and read back from chip to be sure that nothing | 2062 | /* 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 11b361408ae..61eac8cc0d9 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) | |||
688 | chkerrpkts = 1; | 688 | chkerrpkts = 1; |
689 | dd->ipath_lastrcvhdrqtails[i] = tl; | 689 | dd->ipath_lastrcvhdrqtails[i] = tl; |
690 | pd->port_hdrqfull++; | 690 | pd->port_hdrqfull++; |
691 | if (test_bit(IPATH_PORT_WAITING_OVERFLOW, | 691 | /* flush hdrqfull so that poll() sees it */ |
692 | &pd->port_flag)) { | 692 | wmb(); |
693 | clear_bit( | 693 | wake_up_interruptible(&pd->port_wait); |
694 | IPATH_PORT_WAITING_OVERFLOW, | ||
695 | &pd->port_flag); | ||
696 | set_bit( | ||
697 | IPATH_PORT_WAITING_OVERFLOW, | ||
698 | &pd->int_flag); | ||
699 | wake_up_interruptible( | ||
700 | &pd->port_wait); | ||
701 | } | ||
702 | } | 694 | } |
703 | } | 695 | } |
704 | } | 696 | } |
@@ -960,6 +952,8 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat) | |||
960 | int i; | 952 | int i; |
961 | int rcvdint = 0; | 953 | int rcvdint = 0; |
962 | 954 | ||
955 | /* test_bit below needs this... */ | ||
956 | rmb(); | ||
963 | portr = ((istat >> INFINIPATH_I_RCVAVAIL_SHIFT) & | 957 | portr = ((istat >> INFINIPATH_I_RCVAVAIL_SHIFT) & |
964 | dd->ipath_i_rcvavail_mask) | 958 | dd->ipath_i_rcvavail_mask) |
965 | | ((istat >> INFINIPATH_I_RCVURG_SHIFT) & | 959 | | ((istat >> INFINIPATH_I_RCVURG_SHIFT) & |
@@ -967,22 +961,15 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat) | |||
967 | for (i = 1; i < dd->ipath_cfgports; i++) { | 961 | for (i = 1; i < dd->ipath_cfgports; i++) { |
968 | struct ipath_portdata *pd = dd->ipath_pd[i]; | 962 | struct ipath_portdata *pd = dd->ipath_pd[i]; |
969 | if (portr & (1 << i) && pd && pd->port_cnt) { | 963 | if (portr & (1 << i) && pd && pd->port_cnt) { |
970 | if (test_bit(IPATH_PORT_WAITING_RCV, | 964 | if (test_and_clear_bit(IPATH_PORT_WAITING_RCV, |
971 | &pd->port_flag)) { | 965 | &pd->port_flag)) { |
972 | clear_bit(IPATH_PORT_WAITING_RCV, | ||
973 | &pd->port_flag); | ||
974 | set_bit(IPATH_PORT_WAITING_RCV, | ||
975 | &pd->int_flag); | ||
976 | clear_bit(i + INFINIPATH_R_INTRAVAIL_SHIFT, | 966 | clear_bit(i + INFINIPATH_R_INTRAVAIL_SHIFT, |
977 | &dd->ipath_rcvctrl); | 967 | &dd->ipath_rcvctrl); |
978 | wake_up_interruptible(&pd->port_wait); | 968 | wake_up_interruptible(&pd->port_wait); |
979 | rcvdint = 1; | 969 | rcvdint = 1; |
980 | } else if (test_bit(IPATH_PORT_WAITING_URG, | 970 | } else if (test_and_clear_bit(IPATH_PORT_WAITING_URG, |
981 | &pd->port_flag)) { | 971 | &pd->port_flag)) { |
982 | clear_bit(IPATH_PORT_WAITING_URG, | 972 | pd->port_urgent++; |
983 | &pd->port_flag); | ||
984 | set_bit(IPATH_PORT_WAITING_URG, | ||
985 | &pd->int_flag); | ||
986 | wake_up_interruptible(&pd->port_wait); | 973 | wake_up_interruptible(&pd->port_wait); |
987 | } | 974 | } |
988 | } | 975 | } |
diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h index d983f92b9bc..872fb36703d 100644 --- a/drivers/infiniband/hw/ipath/ipath_kernel.h +++ b/drivers/infiniband/hw/ipath/ipath_kernel.h | |||
@@ -139,6 +139,12 @@ struct ipath_portdata { | |||
139 | u32 port_pionowait; | 139 | u32 port_pionowait; |
140 | /* total number of rcvhdrqfull errors */ | 140 | /* total number of rcvhdrqfull errors */ |
141 | u32 port_hdrqfull; | 141 | u32 port_hdrqfull; |
142 | /* saved total number of rcvhdrqfull errors for poll edge trigger */ | ||
143 | u32 port_hdrqfull_poll; | ||
144 | /* total number of polled urgent packets */ | ||
145 | u32 port_urgent; | ||
146 | /* saved total number of polled urgent packets for poll edge trigger */ | ||
147 | u32 port_urgent_poll; | ||
142 | /* pid of process using this port */ | 148 | /* pid of process using this port */ |
143 | pid_t port_pid; | 149 | pid_t port_pid; |
144 | /* same size as task_struct .comm[] */ | 150 | /* 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 | #define IPATH_PORT_MASTER_UNINIT 4 | 763 | #define IPATH_PORT_MASTER_UNINIT 4 |
758 | /* waiting for an urgent packet to arrive */ | 764 | /* waiting for an urgent packet to arrive */ |
759 | #define IPATH_PORT_WAITING_URG 5 | 765 | #define IPATH_PORT_WAITING_URG 5 |
760 | /* waiting for a header overflow */ | ||
761 | #define IPATH_PORT_WAITING_OVERFLOW 6 | ||
762 | 766 | ||
763 | /* free up any allocated data at closes */ | 767 | /* free up any allocated data at closes */ |
764 | void ipath_free_data(struct ipath_portdata *dd); | 768 | void ipath_free_data(struct ipath_portdata *dd); |