RDMA/cxgb4: Connect_request_upcall fixes
[linux-2.6-block.git] / drivers / infiniband / hw / cxgb4 / cm.c
index 773d010a249c38639b572ca69c51e8374b960b01..6bfef31f6b1e8c536e81d1d830ccd452b8420412 100644 (file)
@@ -968,13 +968,14 @@ static int act_establish(struct c4iw_dev *dev, struct sk_buff *skb)
        return 0;
 }
 
-static void close_complete_upcall(struct c4iw_ep *ep)
+static void close_complete_upcall(struct c4iw_ep *ep, int status)
 {
        struct iw_cm_event event;
 
        PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
        memset(&event, 0, sizeof(event));
        event.event = IW_CM_EVENT_CLOSE;
+       event.status = status;
        if (ep->com.cm_id) {
                PDBG("close complete delivered ep %p cm_id %p tid %u\n",
                     ep, ep->com.cm_id, ep->hwtid);
@@ -988,7 +989,6 @@ static void close_complete_upcall(struct c4iw_ep *ep)
 static int abort_connection(struct c4iw_ep *ep, struct sk_buff *skb, gfp_t gfp)
 {
        PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
-       close_complete_upcall(ep);
        state_set(&ep->com, ABORTING);
        set_bit(ABORT_CONN, &ep->com.history);
        return send_abort(ep, skb, gfp);
@@ -1067,9 +1067,10 @@ static void connect_reply_upcall(struct c4iw_ep *ep, int status)
        }
 }
 
-static void connect_request_upcall(struct c4iw_ep *ep)
+static int connect_request_upcall(struct c4iw_ep *ep)
 {
        struct iw_cm_event event;
+       int ret;
 
        PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
        memset(&event, 0, sizeof(event));
@@ -1094,15 +1095,14 @@ static void connect_request_upcall(struct c4iw_ep *ep)
                event.private_data_len = ep->plen;
                event.private_data = ep->mpa_pkt + sizeof(struct mpa_message);
        }
-       if (state_read(&ep->parent_ep->com) != DEAD) {
-               c4iw_get_ep(&ep->com);
-               ep->parent_ep->com.cm_id->event_handler(
-                                               ep->parent_ep->com.cm_id,
-                                               &event);
-       }
+       c4iw_get_ep(&ep->com);
+       ret = ep->parent_ep->com.cm_id->event_handler(ep->parent_ep->com.cm_id,
+                                                     &event);
+       if (ret)
+               c4iw_put_ep(&ep->com);
        set_bit(CONNREQ_UPCALL, &ep->com.history);
        c4iw_put_ep(&ep->parent_ep->com);
-       ep->parent_ep = NULL;
+       return ret;
 }
 
 static void established_upcall(struct c4iw_ep *ep)
@@ -1401,7 +1401,6 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb)
                return;
 
        PDBG("%s enter (%s line %u)\n", __func__, __FILE__, __LINE__);
-       stop_ep_timer(ep);
        mpa = (struct mpa_message *) ep->mpa_pkt;
 
        /*
@@ -1494,9 +1493,17 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb)
             ep->mpa_attr.p2p_type);
 
        state_set(&ep->com, MPA_REQ_RCVD);
+       stop_ep_timer(ep);
 
        /* drive upcall */
-       connect_request_upcall(ep);
+       mutex_lock(&ep->parent_ep->com.mutex);
+       if (ep->parent_ep->com.state != DEAD) {
+               if (connect_request_upcall(ep))
+                       abort_connection(ep, skb, GFP_KERNEL);
+       } else {
+               abort_connection(ep, skb, GFP_KERNEL);
+       }
+       mutex_unlock(&ep->parent_ep->com.mutex);
        return;
 }
 
@@ -2247,7 +2254,7 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb)
                        c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
                                       C4IW_QP_ATTR_NEXT_STATE, &attrs, 1);
                }
-               close_complete_upcall(ep);
+               close_complete_upcall(ep, 0);
                __state_set(&ep->com, DEAD);
                release = 1;
                disconnect = 0;
@@ -2426,7 +2433,7 @@ static int close_con_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
                                             C4IW_QP_ATTR_NEXT_STATE,
                                             &attrs, 1);
                }
-               close_complete_upcall(ep);
+               close_complete_upcall(ep, 0);
                __state_set(&ep->com, DEAD);
                release = 1;
                break;
@@ -2981,7 +2988,7 @@ int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp)
        rdev = &ep->com.dev->rdev;
        if (c4iw_fatal_error(rdev)) {
                fatal = 1;
-               close_complete_upcall(ep);
+               close_complete_upcall(ep, -EIO);
                ep->com.state = DEAD;
        }
        switch (ep->com.state) {
@@ -3023,7 +3030,7 @@ int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp)
        if (close) {
                if (abrupt) {
                        set_bit(EP_DISC_ABORT, &ep->com.history);
-                       close_complete_upcall(ep);
+                       close_complete_upcall(ep, -ECONNRESET);
                        ret = send_abort(ep, NULL, gfp);
                } else {
                        set_bit(EP_DISC_CLOSE, &ep->com.history);
@@ -3435,6 +3442,7 @@ static void process_timeout(struct c4iw_ep *ep)
                                     &attrs, 1);
                }
                __state_set(&ep->com, ABORTING);
+               close_complete_upcall(ep, -ETIMEDOUT);
                break;
        default:
                WARN(1, "%s unexpected state ep %p tid %u state %u\n",