aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve Wise <swise@opengridcomputing.com>2011-05-20 12:25:05 -0400
committerRoland Dreier <roland@purestorage.com>2011-05-24 12:47:38 -0400
commitc337374bf23b88620bcc66a7a09f141cc640f548 (patch)
tree5867078d9b9f7e8eb44df4e94b08e460aede1616
parent257313b2a87795e07a0bdf58d0fffbdba8b31051 (diff)
RDMA/cxgb4: Use completion objects for event blocking
There exists a race condition when using wait_queue_head_t objects that are declared on the stack. This was being done in a few places where we are sending work requests to the FW and awaiting replies, but we don't have an endpoint structure with an embedded c4iw_wr_wait struct. So the code was allocating it locally on the stack. Bad design. The race is: 1) thread on cpuX declares the wait_queue_head_t on the stack, then posts a firmware WR with that wait object ptr as the cookie to be returned in the WR reply. This thread will proceed to block in wait_event_timeout() but before it does: 2) An interrupt runs on cpuY with the WR reply. fw6_msg() handles this and calls c4iw_wake_up(). c4iw_wake_up() sets the condition variable in the c4iw_wr_wait object to TRUE and will call wake_up(), but before it calls wake_up(): 3) The thread on cpuX calls c4iw_wait_for_reply(), which calls wait_event_timeout(). The wait_event_timeout() macro checks the condition variable and returns immediately since it is TRUE. So this thread never blocks/sleeps. The function then returns effectively deallocating the c4iw_wr_wait object that was on the stack. 4) So at this point cpuY has a pointer to the c4iw_wr_wait object that is no longer valid. Further its pointing to a stack frame that might now be in use by some other context/thread. So cpuY continues execution and calls wake_up() on a ptr to a wait object that as been effectively deallocated. This race, when it hits, can cause a crash in wake_up(), which I've seen under heavy stress. It can also corrupt the referenced stack which can cause any number of failures. The fix: Use struct completion, which supports on-stack declarations. Completions use a spinlock around setting the condition to true and the wake up so that steps 2 and 4 above are atomic and step 3 can never happen in-between. Signed-off-by: Steve Wise <swise@opengridcomputing.com>
-rw-r--r--drivers/infiniband/hw/cxgb4/iw_cxgb4.h18
1 files changed, 5 insertions, 13 deletions
diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
index 35d2a5dd9bb4..4f045375c8e2 100644
--- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
+++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
@@ -35,7 +35,7 @@
35#include <linux/list.h> 35#include <linux/list.h>
36#include <linux/spinlock.h> 36#include <linux/spinlock.h>
37#include <linux/idr.h> 37#include <linux/idr.h>
38#include <linux/workqueue.h> 38#include <linux/completion.h>
39#include <linux/netdevice.h> 39#include <linux/netdevice.h>
40#include <linux/sched.h> 40#include <linux/sched.h>
41#include <linux/pci.h> 41#include <linux/pci.h>
@@ -131,28 +131,21 @@ static inline int c4iw_num_stags(struct c4iw_rdev *rdev)
131 131
132#define C4IW_WR_TO (10*HZ) 132#define C4IW_WR_TO (10*HZ)
133 133
134enum {
135 REPLY_READY = 0,
136};
137
138struct c4iw_wr_wait { 134struct c4iw_wr_wait {
139 wait_queue_head_t wait; 135 struct completion completion;
140 unsigned long status;
141 int ret; 136 int ret;
142}; 137};
143 138
144static inline void c4iw_init_wr_wait(struct c4iw_wr_wait *wr_waitp) 139static inline void c4iw_init_wr_wait(struct c4iw_wr_wait *wr_waitp)
145{ 140{
146 wr_waitp->ret = 0; 141 wr_waitp->ret = 0;
147 wr_waitp->status = 0; 142 init_completion(&wr_waitp->completion);
148 init_waitqueue_head(&wr_waitp->wait);
149} 143}
150 144
151static inline void c4iw_wake_up(struct c4iw_wr_wait *wr_waitp, int ret) 145static inline void c4iw_wake_up(struct c4iw_wr_wait *wr_waitp, int ret)
152{ 146{
153 wr_waitp->ret = ret; 147 wr_waitp->ret = ret;
154 set_bit(REPLY_READY, &wr_waitp->status); 148 complete(&wr_waitp->completion);
155 wake_up(&wr_waitp->wait);
156} 149}
157 150
158static inline int c4iw_wait_for_reply(struct c4iw_rdev *rdev, 151static inline int c4iw_wait_for_reply(struct c4iw_rdev *rdev,
@@ -164,8 +157,7 @@ static inline int c4iw_wait_for_reply(struct c4iw_rdev *rdev,
164 int ret; 157 int ret;
165 158
166 do { 159 do {
167 ret = wait_event_timeout(wr_waitp->wait, 160 ret = wait_for_completion_timeout(&wr_waitp->completion, to);
168 test_and_clear_bit(REPLY_READY, &wr_waitp->status), to);
169 if (!ret) { 161 if (!ret) {
170 printk(KERN_ERR MOD "%s - Device %s not responding - " 162 printk(KERN_ERR MOD "%s - Device %s not responding - "
171 "tid %u qpid %u\n", func, 163 "tid %u qpid %u\n", func,