]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
RDMA/cxgb4: Fix endpoint timeout race condition
authorVipul Pandya <vipul@chelsio.com>
Mon, 7 Jan 2013 13:11:56 +0000 (13:11 +0000)
committerRoland Dreier <roland@purestorage.com>
Thu, 14 Feb 2013 23:51:57 +0000 (15:51 -0800)
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>
drivers/infiniband/hw/cxgb4/cm.c
drivers/infiniband/hw/cxgb4/iw_cxgb4.h

index 51ceb618beb22e65ec269c68a6958325d647ee64..ab5b4dd39dec0eb6bc6942cb0558780e0cd27656 100644 (file)
@@ -159,10 +159,12 @@ static void start_ep_timer(struct c4iw_ep *ep)
 {
        PDBG("%s ep %p\n", __func__, ep);
        if (timer_pending(&ep->timer)) {
-               PDBG("%s stopped / restarted timer ep %p\n", __func__, ep);
-               del_timer_sync(&ep->timer);
-       } else
-               c4iw_get_ep(&ep->com);
+               pr_err("%s timer already started! ep %p\n",
+                      __func__, ep);
+               return;
+       }
+       clear_bit(TIMEOUT, &ep->com.flags);
+       c4iw_get_ep(&ep->com);
        ep->timer.expires = jiffies + ep_timeout_secs * HZ;
        ep->timer.data = (unsigned long)ep;
        ep->timer.function = ep_timeout;
@@ -171,14 +173,10 @@ static void start_ep_timer(struct c4iw_ep *ep)
 
 static void stop_ep_timer(struct c4iw_ep *ep)
 {
-       PDBG("%s ep %p\n", __func__, ep);
-       if (!timer_pending(&ep->timer)) {
-               WARN(1, "%s timer stopped when its not running! "
-                      "ep %p state %u\n", __func__, ep, ep->com.state);
-               return;
-       }
+       PDBG("%s ep %p stopping\n", __func__, ep);
        del_timer_sync(&ep->timer);
-       c4iw_put_ep(&ep->com);
+       if (!test_and_set_bit(TIMEOUT, &ep->com.flags))
+               c4iw_put_ep(&ep->com);
 }
 
 static int c4iw_l2t_send(struct c4iw_rdev *rdev, struct sk_buff *skb,
@@ -3191,11 +3189,16 @@ static DECLARE_WORK(skb_work, process_work);
 static void ep_timeout(unsigned long arg)
 {
        struct c4iw_ep *ep = (struct c4iw_ep *)arg;
+       int kickit = 0;
 
        spin_lock(&timeout_lock);
-       list_add_tail(&ep->entry, &timeout_list);
+       if (!test_and_set_bit(TIMEOUT, &ep->com.flags)) {
+               list_add_tail(&ep->entry, &timeout_list);
+               kickit = 1;
+       }
        spin_unlock(&timeout_lock);
-       queue_work(workq, &skb_work);
+       if (kickit)
+               queue_work(workq, &skb_work);
 }
 
 /*
index 0aaaa0e81f29c1e8dea36f1b370c6c55b171ff1a..94a3b3c47a8ac8e3658905b808217f7374f48cff 100644 (file)
@@ -716,6 +716,7 @@ enum c4iw_ep_flags {
        ABORT_REQ_IN_PROGRESS   = 1,
        RELEASE_RESOURCES       = 2,
        CLOSE_SENT              = 3,
+       TIMEOUT                 = 4,
        QP_REFERENCED           = 5,
 };