]> Pileus Git - ~andy/linux/blobdiff - drivers/infiniband/hw/cxgb4/cm.c
RDMA/cxgb4: Fix endpoint timeout race condition
[~andy/linux] / drivers / infiniband / hw / cxgb4 / cm.c
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);
 }
 
 /*