aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVipul Pandya <vipul@chelsio.com>2013-01-07 08:11:56 -0500
committerRoland Dreier <roland@purestorage.com>2013-02-14 18:51:57 -0500
commit1ec779cc29238e6f4d315bff53cd36165819bfd5 (patch)
treeee580b96f17f19ae0f1badc761cc5ef5ee2347d6
parente8e5b9278ba0502ada73b8b94b8498cc19def743 (diff)
RDMA/cxgb4: Fix endpoint timeout race condition
The endpoint timeout logic had a race that could cause an endpoint object to be freed while it was still on the timedout list. This can happen if the timer is stopped after it had fired, but before the timedout thread processed the endpoint timeout. Signed-off-by: Vipul Pandya <vipul@chelsio.com> Signed-off-by: Roland Dreier <roland@purestorage.com>
-rw-r--r--drivers/infiniband/hw/cxgb4/cm.c29
-rw-r--r--drivers/infiniband/hw/cxgb4/iw_cxgb4.h1
2 files changed, 17 insertions, 13 deletions
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index 51ceb618beb2..ab5b4dd39dec 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -159,10 +159,12 @@ static void start_ep_timer(struct c4iw_ep *ep)
159{ 159{
160 PDBG("%s ep %p\n", __func__, ep); 160 PDBG("%s ep %p\n", __func__, ep);
161 if (timer_pending(&ep->timer)) { 161 if (timer_pending(&ep->timer)) {
162 PDBG("%s stopped / restarted timer ep %p\n", __func__, ep); 162 pr_err("%s timer already started! ep %p\n",
163 del_timer_sync(&ep->timer); 163 __func__, ep);
164 } else 164 return;
165 c4iw_get_ep(&ep->com); 165 }
166 clear_bit(TIMEOUT, &ep->com.flags);
167 c4iw_get_ep(&ep->com);
166 ep->timer.expires = jiffies + ep_timeout_secs * HZ; 168 ep->timer.expires = jiffies + ep_timeout_secs * HZ;
167 ep->timer.data = (unsigned long)ep; 169 ep->timer.data = (unsigned long)ep;
168 ep->timer.function = ep_timeout; 170 ep->timer.function = ep_timeout;
@@ -171,14 +173,10 @@ static void start_ep_timer(struct c4iw_ep *ep)
171 173
172static void stop_ep_timer(struct c4iw_ep *ep) 174static void stop_ep_timer(struct c4iw_ep *ep)
173{ 175{
174 PDBG("%s ep %p\n", __func__, ep); 176 PDBG("%s ep %p stopping\n", __func__, ep);
175 if (!timer_pending(&ep->timer)) {
176 WARN(1, "%s timer stopped when its not running! "
177 "ep %p state %u\n", __func__, ep, ep->com.state);
178 return;
179 }
180 del_timer_sync(&ep->timer); 177 del_timer_sync(&ep->timer);
181 c4iw_put_ep(&ep->com); 178 if (!test_and_set_bit(TIMEOUT, &ep->com.flags))
179 c4iw_put_ep(&ep->com);
182} 180}
183 181
184static int c4iw_l2t_send(struct c4iw_rdev *rdev, struct sk_buff *skb, 182static int c4iw_l2t_send(struct c4iw_rdev *rdev, struct sk_buff *skb,
@@ -3191,11 +3189,16 @@ static DECLARE_WORK(skb_work, process_work);
3191static void ep_timeout(unsigned long arg) 3189static void ep_timeout(unsigned long arg)
3192{ 3190{
3193 struct c4iw_ep *ep = (struct c4iw_ep *)arg; 3191 struct c4iw_ep *ep = (struct c4iw_ep *)arg;
3192 int kickit = 0;
3194 3193
3195 spin_lock(&timeout_lock); 3194 spin_lock(&timeout_lock);
3196 list_add_tail(&ep->entry, &timeout_list); 3195 if (!test_and_set_bit(TIMEOUT, &ep->com.flags)) {
3196 list_add_tail(&ep->entry, &timeout_list);
3197 kickit = 1;
3198 }
3197 spin_unlock(&timeout_lock); 3199 spin_unlock(&timeout_lock);
3198 queue_work(workq, &skb_work); 3200 if (kickit)
3201 queue_work(workq, &skb_work);
3199} 3202}
3200 3203
3201/* 3204/*
diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
index 0aaaa0e81f29..94a3b3c47a8a 100644
--- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
+++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
@@ -716,6 +716,7 @@ enum c4iw_ep_flags {
716 ABORT_REQ_IN_PROGRESS = 1, 716 ABORT_REQ_IN_PROGRESS = 1,
717 RELEASE_RESOURCES = 2, 717 RELEASE_RESOURCES = 2,
718 CLOSE_SENT = 3, 718 CLOSE_SENT = 3,
719 TIMEOUT = 4,
719 QP_REFERENCED = 5, 720 QP_REFERENCED = 5,
720}; 721};
721 722