--- /dev/null
+RDMA/cxgb4: 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.
+
+diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
+--- a/drivers/infiniband/hw/cxgb4/cm.c 2012-11-19 15:09:38.780091885 +0530
++++ b/drivers/infiniband/hw/cxgb4/cm.c 2012-11-20 17:50:36.002975441 +0530
+@@ -159,10 +159,12 @@ static void start_ep_timer(struct c4iw_e
+ {
+ 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,15 +173,10 @@ static void start_ep_timer(struct c4iw_e
+
+ static void stop_ep_timer(struct c4iw_ep *ep)
+ {
+- PDBG("%s ep %p\n", __func__, ep);
+- if (!timer_pending(&ep->timer)) {
+- printk(KERN_ERR "%s timer stopped when its not running! "
+- "ep %p state %u\n", __func__, ep, ep->com.state);
+- WARN_ON(1);
+- 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,
+@@ -3259,11 +3256,16 @@ static DECLARE_WORK(skb_work, process_wo
+ 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);
+ }
+
+ /*
+diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
+--- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h 2012-11-19 15:09:38.779091885 +0530
++++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h 2012-11-20 17:56:52.231994435 +0530
+@@ -720,6 +720,7 @@ enum c4iw_ep_flags {
+ ABORT_REQ_IN_PROGRESS = 1,
+ RELEASE_RESOURCES = 2,
+ CLOSE_SENT = 3,
++ TIMEOUT = 4,
+ QP_REFERENCED = 5,
+ };
+
--- /dev/null
+RDMA/cxgb4: don't reconnect on abort for mpa_rev 1
+
+only reconnect if the endpoint wasn't freed.
+
+peer_abort() should only attempt to reconnect if the endpoint wasn't freed.
+Also remove hwtid from the debugfs idr.
+
+Add missing check for peer2peer in MPAv2 code
+
+use correct mpa version on reject.
+
+diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
+--- a/drivers/infiniband/hw/cxgb4/cm.c 2012-11-19 15:09:38.780091885 +0530
++++ b/drivers/infiniband/hw/cxgb4/cm.c 2012-11-19 19:04:44.528809027 +0530
+@@ -287,10 +287,10 @@ void _c4iw_free_ep(struct kref *kref)
+ if (test_bit(QP_REFERENCED, &ep->com.flags))
+ deref_qp(ep);
+ if (test_bit(RELEASE_RESOURCES, &ep->com.flags)) {
++ remove_handle(ep->com.dev, &ep->com.dev->hwtid_idr, ep->hwtid);
+ cxgb4_remove_tid(ep->com.dev->rdev.lldi.tids, 0, ep->hwtid);
+ dst_release(ep->dst);
+ cxgb4_l2t_release(ep->l2t);
+- remove_handle(ep->com.dev, &ep->com.dev->hwtid_idr, ep->hwtid);
+ }
+ kfree(ep);
+ }
+@@ -723,7 +723,7 @@ static int send_mpa_reject(struct c4iw_e
+ memset(mpa, 0, sizeof(*mpa));
+ memcpy(mpa->key, MPA_KEY_REP, sizeof(mpa->key));
+ mpa->flags = MPA_REJECT;
+- mpa->revision = mpa_rev;
++ mpa->revision = ep->mpa_attr.version;
+ mpa->private_data_size = htons(plen);
+
+ if (ep->mpa_attr.version == 2 && ep->mpa_attr.enhanced_rdma_conn) {
+@@ -2226,7 +2226,7 @@ static int peer_abort(struct c4iw_dev *d
+ break;
+ case MPA_REQ_SENT:
+ stop_ep_timer(ep);
+- if (mpa_rev == 2 && ep->tried_with_mpa_v1)
++ if (mpa_rev == 1 || (mpa_rev == 2 && ep->tried_with_mpa_v1))
+ connect_reply_upcall(ep, -ECONNRESET);
+ else {
+ /*
+@@ -2298,9 +2298,8 @@ static int peer_abort(struct c4iw_dev *d
+ out:
+ if (release)
+ release_ep_resources(ep);
+-
+- /* retry with mpa-v1 */
+- if (ep && ep->retry_with_mpa_v1) {
++ else if (ep->retry_with_mpa_v1) {
++ remove_handle(ep->com.dev, &ep->com.dev->hwtid_idr, ep->hwtid);
+ cxgb4_remove_tid(ep->com.dev->rdev.lldi.tids, 0, ep->hwtid);
+ dst_release(ep->dst);
+ cxgb4_l2t_release(ep->l2t);
--- /dev/null
+RDMA/cxgb4: Don't wakeup threads for MPAv2
+
+Don't wakeup threads blocked in rdma_init/rdma_fini if we are on
+MPAv2, and want to retry connection with MPAv1.
+
+Stop ep-timer on getting MPA version mismatch, before doing the
+abort_connection - in process_mpa_request.
+
+Take care to stop ep-timer in error paths for process_mpa_request.
+
+diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
+--- a/drivers/infiniband/hw/cxgb4/cm.c 2012-11-19 15:09:38.780091885 +0530
++++ b/drivers/infiniband/hw/cxgb4/cm.c 2012-11-19 17:24:22.842503571 +0530
+@@ -1324,11 +1324,13 @@ static void process_mpa_request(struct c
+ if (mpa->revision > mpa_rev) {
+ printk(KERN_ERR MOD "%s MPA version mismatch. Local = %d,"
+ " Received = %d\n", __func__, mpa_rev, mpa->revision);
++ stop_ep_timer(ep);
+ abort_connection(ep, skb, GFP_KERNEL);
+ return;
+ }
+
+ if (memcmp(mpa->key, MPA_KEY_REQ, sizeof(mpa->key))) {
++ stop_ep_timer(ep);
+ abort_connection(ep, skb, GFP_KERNEL);
+ return;
+ }
+@@ -1339,6 +1341,7 @@ static void process_mpa_request(struct c
+ * Fail if there's too much private data.
+ */
+ if (plen > MPA_MAX_PRIVATE_DATA) {
++ stop_ep_timer(ep);
+ abort_connection(ep, skb, GFP_KERNEL);
+ return;
+ }
+@@ -1347,6 +1350,7 @@ static void process_mpa_request(struct c
+ * If plen does not account for pkt size
+ */
+ if (ep->mpa_pkt_len > (sizeof(*mpa) + plen)) {
++ stop_ep_timer(ep);
+ abort_connection(ep, skb, GFP_KERNEL);
+ return;
+ }
+@@ -3352,8 +3356,14 @@ static int peer_abort_intr(struct c4iw_d
+
+ /*
+ * Wake up any threads in rdma_init() or rdma_fini().
++ * However, if we are on MPAv2 and want to retry with MPAv1
++ * then, don't wake up yet.
+ */
+- c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET);
++ if (mpa_rev == 2 && !ep->tried_with_mpa_v1) {
++ if (ep->com.state != MPA_REQ_SENT)
++ c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET);
++ } else
++ c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET);
+ sched(dev, skb);
+ return 0;
+ }