]> git.openfabrics.org - ~emulex/for-vlad/compat-rdma.git/commitdiff
RDMA/cxgb4: fix mpa version mismatch
authorVipul Pandya <vipul@chelsio.com>
Tue, 20 Nov 2012 15:22:40 +0000 (07:22 -0800)
committerVipul Pandya <vipul@chelsio.com>
Tue, 20 Nov 2012 15:22:40 +0000 (07:22 -0800)
This patch fixes bug 2404 in OFED bugzilla

Signed-off-by: Vipul Pandya <vipul@chelsio.com>
linux-next-pending/0027-RDMA-cxgb4-endpoint-timeout-race-condition.patch [new file with mode: 0644]
linux-next-pending/0028-RDMA-cxgb4-dont-reconnect-on-abort.patch [new file with mode: 0644]
linux-next-pending/0029-RDMA-cxgb4-fix-mpa-version-mismatch.patch [new file with mode: 0644]

diff --git a/linux-next-pending/0027-RDMA-cxgb4-endpoint-timeout-race-condition.patch b/linux-next-pending/0027-RDMA-cxgb4-endpoint-timeout-race-condition.patch
new file mode 100644 (file)
index 0000000..e484915
--- /dev/null
@@ -0,0 +1,76 @@
+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,
+ };
diff --git a/linux-next-pending/0028-RDMA-cxgb4-dont-reconnect-on-abort.patch b/linux-next-pending/0028-RDMA-cxgb4-dont-reconnect-on-abort.patch
new file mode 100644 (file)
index 0000000..fd51139
--- /dev/null
@@ -0,0 +1,56 @@
+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);
diff --git a/linux-next-pending/0029-RDMA-cxgb4-fix-mpa-version-mismatch.patch b/linux-next-pending/0029-RDMA-cxgb4-fix-mpa-version-mismatch.patch
new file mode 100644 (file)
index 0000000..b3963d6
--- /dev/null
@@ -0,0 +1,59 @@
+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;
+ }