sata_mv ncq Fix EDMA configuration
[safe/jmp/linux-2.6] / drivers / infiniband / core / cma.c
index bb27ce9..1eff1b2 100644 (file)
@@ -52,6 +52,7 @@ MODULE_LICENSE("Dual BSD/GPL");
 
 #define CMA_CM_RESPONSE_TIMEOUT 20
 #define CMA_MAX_CM_RETRIES 15
+#define CMA_CM_MRA_SETTING (IB_CM_MRA_FLAG_DELAY | 24)
 
 static void cma_add_one(struct ib_device *device);
 static void cma_remove_one(struct ib_device *device);
@@ -77,7 +78,6 @@ static int next_port;
 struct cma_device {
        struct list_head        list;
        struct ib_device        *device;
-       __be64                  node_guid;
        struct completion       comp;
        atomic_t                refcount;
        struct list_head        id_list;
@@ -114,13 +114,16 @@ struct rdma_id_private {
 
        struct rdma_bind_list   *bind_list;
        struct hlist_node       node;
-       struct list_head        list;
-       struct list_head        listen_list;
+       struct list_head        list; /* listen_any_list or cma_device.list */
+       struct list_head        listen_list; /* per device listens */
        struct cma_device       *cma_dev;
        struct list_head        mc_list;
 
+       int                     internal_id;
        enum cma_state          state;
        spinlock_t              lock;
+       struct mutex            qp_mutex;
+
        struct completion       comp;
        atomic_t                refcount;
        wait_queue_head_t       wait_remove;
@@ -139,6 +142,7 @@ struct rdma_id_private {
        u32                     qkey;
        u32                     qp_num;
        u8                      srq;
+       u8                      tos;
 };
 
 struct cma_multicast {
@@ -347,12 +351,33 @@ static void cma_deref_id(struct rdma_id_private *id_priv)
                complete(&id_priv->comp);
 }
 
-static void cma_release_remove(struct rdma_id_private *id_priv)
+static int cma_disable_remove(struct rdma_id_private *id_priv,
+                             enum cma_state state)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&id_priv->lock, flags);
+       if (id_priv->state == state) {
+               atomic_inc(&id_priv->dev_remove);
+               ret = 0;
+       } else
+               ret = -EINVAL;
+       spin_unlock_irqrestore(&id_priv->lock, flags);
+       return ret;
+}
+
+static void cma_enable_remove(struct rdma_id_private *id_priv)
 {
        if (atomic_dec_and_test(&id_priv->dev_remove))
                wake_up(&id_priv->wait_remove);
 }
 
+static int cma_has_cm_dev(struct rdma_id_private *id_priv)
+{
+       return (id_priv->id.device && id_priv->cm_id.ib);
+}
+
 struct rdma_cm_id *rdma_create_id(rdma_cm_event_handler event_handler,
                                  void *context, enum rdma_port_space ps)
 {
@@ -367,6 +392,7 @@ struct rdma_cm_id *rdma_create_id(rdma_cm_event_handler event_handler,
        id_priv->id.event_handler = event_handler;
        id_priv->id.ps = ps;
        spin_lock_init(&id_priv->lock);
+       mutex_init(&id_priv->qp_mutex);
        init_completion(&id_priv->comp);
        atomic_set(&id_priv->refcount, 1);
        init_waitqueue_head(&id_priv->wait_remove);
@@ -452,61 +478,92 @@ EXPORT_SYMBOL(rdma_create_qp);
 
 void rdma_destroy_qp(struct rdma_cm_id *id)
 {
-       ib_destroy_qp(id->qp);
+       struct rdma_id_private *id_priv;
+
+       id_priv = container_of(id, struct rdma_id_private, id);
+       mutex_lock(&id_priv->qp_mutex);
+       ib_destroy_qp(id_priv->id.qp);
+       id_priv->id.qp = NULL;
+       mutex_unlock(&id_priv->qp_mutex);
 }
 EXPORT_SYMBOL(rdma_destroy_qp);
 
-static int cma_modify_qp_rtr(struct rdma_cm_id *id)
+static int cma_modify_qp_rtr(struct rdma_id_private *id_priv,
+                            struct rdma_conn_param *conn_param)
 {
        struct ib_qp_attr qp_attr;
        int qp_attr_mask, ret;
 
-       if (!id->qp)
-               return 0;
+       mutex_lock(&id_priv->qp_mutex);
+       if (!id_priv->id.qp) {
+               ret = 0;
+               goto out;
+       }
 
        /* Need to update QP attributes from default values. */
        qp_attr.qp_state = IB_QPS_INIT;
-       ret = rdma_init_qp_attr(id, &qp_attr, &qp_attr_mask);
+       ret = rdma_init_qp_attr(&id_priv->id, &qp_attr, &qp_attr_mask);
        if (ret)
-               return ret;
+               goto out;
 
-       ret = ib_modify_qp(id->qp, &qp_attr, qp_attr_mask);
+       ret = ib_modify_qp(id_priv->id.qp, &qp_attr, qp_attr_mask);
        if (ret)
-               return ret;
+               goto out;
 
        qp_attr.qp_state = IB_QPS_RTR;
-       ret = rdma_init_qp_attr(id, &qp_attr, &qp_attr_mask);
+       ret = rdma_init_qp_attr(&id_priv->id, &qp_attr, &qp_attr_mask);
        if (ret)
-               return ret;
+               goto out;
 
-       return ib_modify_qp(id->qp, &qp_attr, qp_attr_mask);
+       if (conn_param)
+               qp_attr.max_dest_rd_atomic = conn_param->responder_resources;
+       ret = ib_modify_qp(id_priv->id.qp, &qp_attr, qp_attr_mask);
+out:
+       mutex_unlock(&id_priv->qp_mutex);
+       return ret;
 }
 
-static int cma_modify_qp_rts(struct rdma_cm_id *id)
+static int cma_modify_qp_rts(struct rdma_id_private *id_priv,
+                            struct rdma_conn_param *conn_param)
 {
        struct ib_qp_attr qp_attr;
        int qp_attr_mask, ret;
 
-       if (!id->qp)
-               return 0;
+       mutex_lock(&id_priv->qp_mutex);
+       if (!id_priv->id.qp) {
+               ret = 0;
+               goto out;
+       }
 
        qp_attr.qp_state = IB_QPS_RTS;
-       ret = rdma_init_qp_attr(id, &qp_attr, &qp_attr_mask);
+       ret = rdma_init_qp_attr(&id_priv->id, &qp_attr, &qp_attr_mask);
        if (ret)
-               return ret;
+               goto out;
 
-       return ib_modify_qp(id->qp, &qp_attr, qp_attr_mask);
+       if (conn_param)
+               qp_attr.max_rd_atomic = conn_param->initiator_depth;
+       ret = ib_modify_qp(id_priv->id.qp, &qp_attr, qp_attr_mask);
+out:
+       mutex_unlock(&id_priv->qp_mutex);
+       return ret;
 }
 
-static int cma_modify_qp_err(struct rdma_cm_id *id)
+static int cma_modify_qp_err(struct rdma_id_private *id_priv)
 {
        struct ib_qp_attr qp_attr;
+       int ret;
 
-       if (!id->qp)
-               return 0;
+       mutex_lock(&id_priv->qp_mutex);
+       if (!id_priv->id.qp) {
+               ret = 0;
+               goto out;
+       }
 
        qp_attr.qp_state = IB_QPS_ERR;
-       return ib_modify_qp(id->qp, &qp_attr, IB_QP_STATE);
+       ret = ib_modify_qp(id_priv->id.qp, &qp_attr, IB_QP_STATE);
+out:
+       mutex_unlock(&id_priv->qp_mutex);
+       return ret;
 }
 
 static int cma_ib_init_qp_attr(struct rdma_id_private *id_priv,
@@ -553,7 +610,7 @@ int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr,
                break;
        case RDMA_TRANSPORT_IWARP:
                if (!id_priv->cm_id.iw) {
-                       qp_attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE;
+                       qp_attr->qp_access_flags = 0;
                        *qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS;
                } else
                        ret = iw_cm_init_qp_attr(id_priv->cm_id.iw, qp_attr,
@@ -573,7 +630,8 @@ static inline int cma_zero_addr(struct sockaddr *addr)
        struct in6_addr *ip6;
 
        if (addr->sa_family == AF_INET)
-               return ZERONET(((struct sockaddr_in *) addr)->sin_addr.s_addr);
+               return ipv4_is_zeronet(
+                       ((struct sockaddr_in *)addr)->sin_addr.s_addr);
        else {
                ip6 = &((struct sockaddr_in6 *) addr)->sin6_addr;
                return (ip6->s6_addr32[0] | ip6->s6_addr32[1] |
@@ -583,7 +641,7 @@ static inline int cma_zero_addr(struct sockaddr *addr)
 
 static inline int cma_loopback_addr(struct sockaddr *addr)
 {
-       return LOOPBACK(((struct sockaddr_in *) addr)->sin_addr.s_addr);
+       return ipv4_is_loopback(((struct sockaddr_in *) addr)->sin_addr.s_addr);
 }
 
 static inline int cma_any_addr(struct sockaddr *addr)
@@ -695,50 +753,27 @@ static void cma_cancel_route(struct rdma_id_private *id_priv)
        }
 }
 
-static inline int cma_internal_listen(struct rdma_id_private *id_priv)
-{
-       return (id_priv->state == CMA_LISTEN) && id_priv->cma_dev &&
-              cma_any_addr(&id_priv->id.route.addr.src_addr);
-}
-
-static void cma_destroy_listen(struct rdma_id_private *id_priv)
-{
-       cma_exch(id_priv, CMA_DESTROYING);
-
-       if (id_priv->cma_dev) {
-               switch (rdma_node_get_transport(id_priv->id.device->node_type)) {
-               case RDMA_TRANSPORT_IB:
-                       if (id_priv->cm_id.ib && !IS_ERR(id_priv->cm_id.ib))
-                               ib_destroy_cm_id(id_priv->cm_id.ib);
-                       break;
-               case RDMA_TRANSPORT_IWARP:
-                       if (id_priv->cm_id.iw && !IS_ERR(id_priv->cm_id.iw))
-                               iw_destroy_cm_id(id_priv->cm_id.iw);
-                       break;
-               default:
-                       break;
-               }
-               cma_detach_from_dev(id_priv);
-       }
-       list_del(&id_priv->listen_list);
-
-       cma_deref_id(id_priv);
-       wait_for_completion(&id_priv->comp);
-
-       kfree(id_priv);
-}
-
 static void cma_cancel_listens(struct rdma_id_private *id_priv)
 {
        struct rdma_id_private *dev_id_priv;
 
+       /*
+        * Remove from listen_any_list to prevent added devices from spawning
+        * additional listen requests.
+        */
        mutex_lock(&lock);
        list_del(&id_priv->list);
 
        while (!list_empty(&id_priv->listen_list)) {
                dev_id_priv = list_entry(id_priv->listen_list.next,
                                         struct rdma_id_private, listen_list);
-               cma_destroy_listen(dev_id_priv);
+               /* sync with device removal to avoid duplicate destruction */
+               list_del_init(&dev_id_priv->list);
+               list_del(&dev_id_priv->listen_list);
+               mutex_unlock(&lock);
+
+               rdma_destroy_id(&dev_id_priv->id);
+               mutex_lock(&lock);
        }
        mutex_unlock(&lock);
 }
@@ -826,6 +861,9 @@ void rdma_destroy_id(struct rdma_cm_id *id)
        cma_deref_id(id_priv);
        wait_for_completion(&id_priv->comp);
 
+       if (id_priv->internal_id)
+               cma_deref_id(id_priv->id.context);
+
        kfree(id_priv->id.route.path_rec);
        kfree(id_priv);
 }
@@ -835,11 +873,11 @@ static int cma_rep_recv(struct rdma_id_private *id_priv)
 {
        int ret;
 
-       ret = cma_modify_qp_rtr(&id_priv->id);
+       ret = cma_modify_qp_rtr(id_priv, NULL);
        if (ret)
                goto reject;
 
-       ret = cma_modify_qp_rts(&id_priv->id);
+       ret = cma_modify_qp_rts(id_priv, NULL);
        if (ret)
                goto reject;
 
@@ -849,7 +887,7 @@ static int cma_rep_recv(struct rdma_id_private *id_priv)
 
        return 0;
 reject:
-       cma_modify_qp_err(&id_priv->id);
+       cma_modify_qp_err(id_priv);
        ib_send_cm_rej(id_priv->cm_id.ib, IB_CM_REJ_CONSUMER_DEFINED,
                       NULL, 0, NULL, 0);
        return ret;
@@ -885,9 +923,8 @@ static int cma_ib_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
        struct rdma_cm_event event;
        int ret = 0;
 
-       atomic_inc(&id_priv->dev_remove);
-       if (!cma_comp(id_priv, CMA_CONNECT))
-               goto out;
+       if (cma_disable_remove(id_priv, CMA_CONNECT))
+               return 0;
 
        memset(&event, 0, sizeof event);
        switch (ib_event->event) {
@@ -926,7 +963,7 @@ static int cma_ib_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
                /* ignore event */
                goto out;
        case IB_CM_REJ_RECEIVED:
-               cma_modify_qp_err(&id_priv->id);
+               cma_modify_qp_err(id_priv);
                event.status = ib_event->param.rej_rcvd.reason;
                event.event = RDMA_CM_EVENT_REJECTED;
                event.param.conn.private_data = ib_event->private_data;
@@ -943,12 +980,12 @@ static int cma_ib_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
                /* Destroy the CM ID by returning a non-zero value. */
                id_priv->cm_id.ib = NULL;
                cma_exch(id_priv, CMA_DESTROYING);
-               cma_release_remove(id_priv);
+               cma_enable_remove(id_priv);
                rdma_destroy_id(&id_priv->id);
                return ret;
        }
 out:
-       cma_release_remove(id_priv);
+       cma_enable_remove(id_priv);
        return ret;
 }
 
@@ -1058,11 +1095,8 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
        int offset, ret;
 
        listen_id = cm_id->context;
-       atomic_inc(&listen_id->dev_remove);
-       if (!cma_comp(listen_id, CMA_LISTEN)) {
-               ret = -ECONNABORTED;
-               goto out;
-       }
+       if (cma_disable_remove(listen_id, CMA_LISTEN))
+               return -ECONNABORTED;
 
        memset(&event, 0, sizeof event);
        offset = cma_user_data_offset(listen_id->id.ps);
@@ -1073,6 +1107,7 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
                event.param.ud.private_data_len =
                                IB_CM_SIDR_REQ_PRIVATE_DATA_SIZE - offset;
        } else {
+               ib_send_cm_mra(cm_id, CMA_CM_MRA_SETTING, NULL, 0);
                conn_id = cma_new_conn_id(&listen_id->id, ib_event);
                cma_set_req_event_data(&event, &ib_event->param.req_rcvd,
                                       ib_event->private_data, offset);
@@ -1094,19 +1129,21 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
        cm_id->cm_handler = cma_ib_handler;
 
        ret = conn_id->id.event_handler(&conn_id->id, &event);
-       if (!ret)
+       if (!ret) {
+               cma_enable_remove(conn_id);
                goto out;
+       }
 
        /* Destroy the CM ID by returning a non-zero value. */
        conn_id->cm_id.ib = NULL;
 
 release_conn_id:
        cma_exch(conn_id, CMA_DESTROYING);
-       cma_release_remove(conn_id);
+       cma_enable_remove(conn_id);
        rdma_destroy_id(&conn_id->id);
 
 out:
-       cma_release_remove(listen_id);
+       cma_enable_remove(listen_id);
        return ret;
 }
 
@@ -1172,9 +1209,10 @@ static int cma_iw_handler(struct iw_cm_id *iw_id, struct iw_cm_event *iw_event)
        struct sockaddr_in *sin;
        int ret = 0;
 
-       memset(&event, 0, sizeof event);
-       atomic_inc(&id_priv->dev_remove);
+       if (cma_disable_remove(id_priv, CMA_CONNECT))
+               return 0;
 
+       memset(&event, 0, sizeof event);
        switch (iw_event->event) {
        case IW_CM_EVENT_CLOSE:
                event.event = RDMA_CM_EVENT_DISCONNECTED;
@@ -1215,12 +1253,12 @@ static int cma_iw_handler(struct iw_cm_id *iw_id, struct iw_cm_event *iw_event)
                /* Destroy the CM ID by returning a non-zero value. */
                id_priv->cm_id.iw = NULL;
                cma_exch(id_priv, CMA_DESTROYING);
-               cma_release_remove(id_priv);
+               cma_enable_remove(id_priv);
                rdma_destroy_id(&id_priv->id);
                return ret;
        }
 
-       cma_release_remove(id_priv);
+       cma_enable_remove(id_priv);
        return ret;
 }
 
@@ -1233,13 +1271,11 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id,
        struct net_device *dev = NULL;
        struct rdma_cm_event event;
        int ret;
+       struct ib_device_attr attr;
 
        listen_id = cm_id->context;
-       atomic_inc(&listen_id->dev_remove);
-       if (!cma_comp(listen_id, CMA_LISTEN)) {
-               ret = -ECONNABORTED;
-               goto out;
-       }
+       if (cma_disable_remove(listen_id, CMA_LISTEN))
+               return -ECONNABORTED;
 
        /* Create a new RDMA id for the new IW CM ID */
        new_cm_id = rdma_create_id(listen_id->id.event_handler,
@@ -1253,16 +1289,16 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id,
        atomic_inc(&conn_id->dev_remove);
        conn_id->state = CMA_CONNECT;
 
-       dev = ip_dev_find(iw_event->local_addr.sin_addr.s_addr);
+       dev = ip_dev_find(&init_net, iw_event->local_addr.sin_addr.s_addr);
        if (!dev) {
                ret = -EADDRNOTAVAIL;
-               cma_release_remove(conn_id);
+               cma_enable_remove(conn_id);
                rdma_destroy_id(new_cm_id);
                goto out;
        }
        ret = rdma_copy_addr(&conn_id->id.route.addr.dev_addr, dev, NULL);
        if (ret) {
-               cma_release_remove(conn_id);
+               cma_enable_remove(conn_id);
                rdma_destroy_id(new_cm_id);
                goto out;
        }
@@ -1271,7 +1307,7 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id,
        ret = cma_acquire_dev(conn_id);
        mutex_unlock(&lock);
        if (ret) {
-               cma_release_remove(conn_id);
+               cma_enable_remove(conn_id);
                rdma_destroy_id(new_cm_id);
                goto out;
        }
@@ -1285,23 +1321,32 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id,
        sin = (struct sockaddr_in *) &new_cm_id->route.addr.dst_addr;
        *sin = iw_event->remote_addr;
 
+       ret = ib_query_device(conn_id->id.device, &attr);
+       if (ret) {
+               cma_enable_remove(conn_id);
+               rdma_destroy_id(new_cm_id);
+               goto out;
+       }
+
        memset(&event, 0, sizeof event);
        event.event = RDMA_CM_EVENT_CONNECT_REQUEST;
        event.param.conn.private_data = iw_event->private_data;
        event.param.conn.private_data_len = iw_event->private_data_len;
+       event.param.conn.initiator_depth = attr.max_qp_init_rd_atom;
+       event.param.conn.responder_resources = attr.max_qp_rd_atom;
        ret = conn_id->id.event_handler(&conn_id->id, &event);
        if (ret) {
                /* User wants to destroy the CM ID */
                conn_id->cm_id.iw = NULL;
                cma_exch(conn_id, CMA_DESTROYING);
-               cma_release_remove(conn_id);
+               cma_enable_remove(conn_id);
                rdma_destroy_id(&conn_id->id);
        }
 
 out:
        if (dev)
                dev_put(dev);
-       cma_release_remove(listen_id);
+       cma_enable_remove(listen_id);
        return ret;
 }
 
@@ -1387,14 +1432,13 @@ static void cma_listen_on_dev(struct rdma_id_private *id_priv,
 
        cma_attach_to_dev(dev_id_priv, cma_dev);
        list_add_tail(&dev_id_priv->listen_list, &id_priv->listen_list);
+       atomic_inc(&id_priv->refcount);
+       dev_id_priv->internal_id = 1;
 
        ret = rdma_listen(id, id_priv->backlog);
        if (ret)
-               goto err;
-
-       return;
-err:
-       cma_destroy_listen(dev_id_priv);
+               printk(KERN_WARNING "RDMA CMA: cma_listen_on_dev, error %d, "
+                      "listening on device %s", ret, cma_dev->device->name);
 }
 
 static void cma_listen_on_all(struct rdma_id_private *id_priv)
@@ -1460,6 +1504,15 @@ err:
 }
 EXPORT_SYMBOL(rdma_listen);
 
+void rdma_set_service_type(struct rdma_cm_id *id, int tos)
+{
+       struct rdma_id_private *id_priv;
+
+       id_priv = container_of(id, struct rdma_id_private, id);
+       id_priv->tos = (u8) tos;
+}
+EXPORT_SYMBOL(rdma_set_service_type);
+
 static void cma_query_handler(int status, struct ib_sa_path_rec *path_rec,
                              void *context)
 {
@@ -1484,23 +1537,37 @@ static void cma_query_handler(int status, struct ib_sa_path_rec *path_rec,
 static int cma_query_ib_route(struct rdma_id_private *id_priv, int timeout_ms,
                              struct cma_work *work)
 {
-       struct rdma_dev_addr *addr = &id_priv->id.route.addr.dev_addr;
+       struct rdma_addr *addr = &id_priv->id.route.addr;
        struct ib_sa_path_rec path_rec;
+       ib_sa_comp_mask comp_mask;
+       struct sockaddr_in6 *sin6;
 
        memset(&path_rec, 0, sizeof path_rec);
-       ib_addr_get_sgid(addr, &path_rec.sgid);
-       ib_addr_get_dgid(addr, &path_rec.dgid);
-       path_rec.pkey = cpu_to_be16(ib_addr_get_pkey(addr));
+       ib_addr_get_sgid(&addr->dev_addr, &path_rec.sgid);
+       ib_addr_get_dgid(&addr->dev_addr, &path_rec.dgid);
+       path_rec.pkey = cpu_to_be16(ib_addr_get_pkey(&addr->dev_addr));
        path_rec.numb_path = 1;
        path_rec.reversible = 1;
+       path_rec.service_id = cma_get_service_id(id_priv->id.ps, &addr->dst_addr);
+
+       comp_mask = IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID |
+                   IB_SA_PATH_REC_PKEY | IB_SA_PATH_REC_NUMB_PATH |
+                   IB_SA_PATH_REC_REVERSIBLE | IB_SA_PATH_REC_SERVICE_ID;
+
+       if (addr->src_addr.sa_family == AF_INET) {
+               path_rec.qos_class = cpu_to_be16((u16) id_priv->tos);
+               comp_mask |= IB_SA_PATH_REC_QOS_CLASS;
+       } else {
+               sin6 = (struct sockaddr_in6 *) &addr->src_addr;
+               path_rec.traffic_class = (u8) (be32_to_cpu(sin6->sin6_flowinfo) >> 20);
+               comp_mask |= IB_SA_PATH_REC_TRAFFIC_CLASS;
+       }
 
        id_priv->query_id = ib_sa_path_rec_get(&sa_client, id_priv->id.device,
-                               id_priv->id.port_num, &path_rec,
-                               IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID |
-                               IB_SA_PATH_REC_PKEY | IB_SA_PATH_REC_NUMB_PATH |
-                               IB_SA_PATH_REC_REVERSIBLE,
-                               timeout_ms, GFP_KERNEL,
-                               cma_query_handler, work, &id_priv->query);
+                                              id_priv->id.port_num, &path_rec,
+                                              comp_mask, timeout_ms,
+                                              GFP_KERNEL, cma_query_handler,
+                                              work, &id_priv->query);
 
        return (id_priv->query_id < 0) ? id_priv->query_id : 0;
 }
@@ -1520,7 +1587,7 @@ static void cma_work_handler(struct work_struct *_work)
                destroy = 1;
        }
 out:
-       cma_release_remove(id_priv);
+       cma_enable_remove(id_priv);
        cma_deref_id(id_priv);
        if (destroy)
                rdma_destroy_id(&id_priv->id);
@@ -1712,13 +1779,13 @@ static void addr_handler(int status, struct sockaddr *src_addr,
 
        if (id_priv->id.event_handler(&id_priv->id, &event)) {
                cma_exch(id_priv, CMA_DESTROYING);
-               cma_release_remove(id_priv);
+               cma_enable_remove(id_priv);
                cma_deref_id(id_priv);
                rdma_destroy_id(&id_priv->id);
                return;
        }
 out:
-       cma_release_remove(id_priv);
+       cma_enable_remove(id_priv);
        cma_deref_id(id_priv);
 }
 
@@ -1822,7 +1889,7 @@ static int cma_alloc_port(struct idr *ps, struct rdma_id_private *id_priv,
        struct rdma_bind_list *bind_list;
        int port, ret;
 
-       bind_list = kmalloc(sizeof *bind_list, GFP_KERNEL);
+       bind_list = kzalloc(sizeof *bind_list, GFP_KERNEL);
        if (!bind_list)
                return -ENOMEM;
 
@@ -1852,13 +1919,14 @@ err1:
 static int cma_alloc_any_port(struct idr *ps, struct rdma_id_private *id_priv)
 {
        struct rdma_bind_list *bind_list;
-       int port, ret;
+       int port, ret, low, high;
 
        bind_list = kzalloc(sizeof *bind_list, GFP_KERNEL);
        if (!bind_list)
                return -ENOMEM;
 
 retry:
+       /* FIXME: add proper port randomization per like inet_csk_get_port */
        do {
                ret = idr_get_new_above(ps, bind_list, next_port, &port);
        } while ((ret == -EAGAIN) && idr_pre_get(ps, GFP_KERNEL));
@@ -1866,18 +1934,19 @@ retry:
        if (ret)
                goto err1;
 
-       if (port > sysctl_local_port_range[1]) {
-               if (next_port != sysctl_local_port_range[0]) {
+       inet_get_local_port_range(&low, &high);
+       if (port > high) {
+               if (next_port != low) {
                        idr_remove(ps, port);
-                       next_port = sysctl_local_port_range[0];
+                       next_port = low;
                        goto retry;
                }
                ret = -EADDRNOTAVAIL;
                goto err2;
        }
 
-       if (port == sysctl_local_port_range[1])
-               next_port = sysctl_local_port_range[0];
+       if (port == high)
+               next_port = low;
        else
                next_port = port + 1;
 
@@ -2043,11 +2112,10 @@ static int cma_sidr_rep_handler(struct ib_cm_id *cm_id,
        struct ib_cm_sidr_rep_event_param *rep = &ib_event->param.sidr_rep_rcvd;
        int ret = 0;
 
-       memset(&event, 0, sizeof event);
-       atomic_inc(&id_priv->dev_remove);
-       if (!cma_comp(id_priv, CMA_CONNECT))
-               goto out;
+       if (cma_disable_remove(id_priv, CMA_CONNECT))
+               return 0;
 
+       memset(&event, 0, sizeof event);
        switch (ib_event->event) {
        case IB_CM_SIDR_REQ_ERROR:
                event.event = RDMA_CM_EVENT_UNREACHABLE;
@@ -2085,12 +2153,12 @@ static int cma_sidr_rep_handler(struct ib_cm_id *cm_id,
                /* Destroy the CM ID by returning a non-zero value. */
                id_priv->cm_id.ib = NULL;
                cma_exch(id_priv, CMA_DESTROYING);
-               cma_release_remove(id_priv);
+               cma_enable_remove(id_priv);
                rdma_destroy_id(&id_priv->id);
                return ret;
        }
 out:
-       cma_release_remove(id_priv);
+       cma_enable_remove(id_priv);
        return ret;
 }
 
@@ -2223,7 +2291,7 @@ static int cma_connect_iw(struct rdma_id_private *id_priv,
        sin = (struct sockaddr_in*) &id_priv->id.route.addr.dst_addr;
        cm_id->remote_addr = *sin;
 
-       ret = cma_modify_qp_rtr(&id_priv->id);
+       ret = cma_modify_qp_rtr(id_priv, conn_param);
        if (ret)
                goto out;
 
@@ -2286,25 +2354,15 @@ static int cma_accept_ib(struct rdma_id_private *id_priv,
                         struct rdma_conn_param *conn_param)
 {
        struct ib_cm_rep_param rep;
-       struct ib_qp_attr qp_attr;
-       int qp_attr_mask, ret;
-
-       if (id_priv->id.qp) {
-               ret = cma_modify_qp_rtr(&id_priv->id);
-               if (ret)
-                       goto out;
+       int ret;
 
-               qp_attr.qp_state = IB_QPS_RTS;
-               ret = ib_cm_init_qp_attr(id_priv->cm_id.ib, &qp_attr,
-                                        &qp_attr_mask);
-               if (ret)
-                       goto out;
+       ret = cma_modify_qp_rtr(id_priv, conn_param);
+       if (ret)
+               goto out;
 
-               qp_attr.max_rd_atomic = conn_param->initiator_depth;
-               ret = ib_modify_qp(id_priv->id.qp, &qp_attr, qp_attr_mask);
-               if (ret)
-                       goto out;
-       }
+       ret = cma_modify_qp_rts(id_priv, conn_param);
+       if (ret)
+               goto out;
 
        memset(&rep, 0, sizeof rep);
        rep.qp_num = id_priv->qp_num;
@@ -2313,7 +2371,6 @@ static int cma_accept_ib(struct rdma_id_private *id_priv,
        rep.private_data_len = conn_param->private_data_len;
        rep.responder_resources = conn_param->responder_resources;
        rep.initiator_depth = conn_param->initiator_depth;
-       rep.target_ack_delay = CMA_CM_RESPONSE_TIMEOUT;
        rep.failover_accepted = 0;
        rep.flow_control = conn_param->flow_control;
        rep.rnr_retry_count = conn_param->rnr_retry_count;
@@ -2330,7 +2387,7 @@ static int cma_accept_iw(struct rdma_id_private *id_priv,
        struct iw_cm_conn_param iw_param;
        int ret;
 
-       ret = cma_modify_qp_rtr(&id_priv->id);
+       ret = cma_modify_qp_rtr(id_priv, conn_param);
        if (ret)
                return ret;
 
@@ -2402,7 +2459,7 @@ int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param)
 
        return 0;
 reject:
-       cma_modify_qp_err(id);
+       cma_modify_qp_err(id_priv);
        rdma_reject(id, NULL, 0);
        return ret;
 }
@@ -2414,7 +2471,7 @@ int rdma_notify(struct rdma_cm_id *id, enum ib_event_type event)
        int ret;
 
        id_priv = container_of(id, struct rdma_id_private, id);
-       if (!cma_comp(id_priv, CMA_CONNECT))
+       if (!cma_has_cm_dev(id_priv))
                return -EINVAL;
 
        switch (id->device->node_type) {
@@ -2436,7 +2493,7 @@ int rdma_reject(struct rdma_cm_id *id, const void *private_data,
        int ret;
 
        id_priv = container_of(id, struct rdma_id_private, id);
-       if (!cma_comp(id_priv, CMA_CONNECT))
+       if (!cma_has_cm_dev(id_priv))
                return -EINVAL;
 
        switch (rdma_node_get_transport(id->device->node_type)) {
@@ -2467,13 +2524,12 @@ int rdma_disconnect(struct rdma_cm_id *id)
        int ret;
 
        id_priv = container_of(id, struct rdma_id_private, id);
-       if (!cma_comp(id_priv, CMA_CONNECT) &&
-           !cma_comp(id_priv, CMA_DISCONNECT))
+       if (!cma_has_cm_dev(id_priv))
                return -EINVAL;
 
        switch (rdma_node_get_transport(id->device->node_type)) {
        case RDMA_TRANSPORT_IB:
-               ret = cma_modify_qp_err(id);
+               ret = cma_modify_qp_err(id_priv);
                if (ret)
                        goto out;
                /* Initiate or respond to a disconnect. */
@@ -2500,14 +2556,15 @@ static int cma_ib_mc_handler(int status, struct ib_sa_multicast *multicast)
        int ret;
 
        id_priv = mc->id_priv;
-       atomic_inc(&id_priv->dev_remove);
-       if (!cma_comp(id_priv, CMA_ADDR_BOUND) &&
-           !cma_comp(id_priv, CMA_ADDR_RESOLVED))
-               goto out;
+       if (cma_disable_remove(id_priv, CMA_ADDR_BOUND) &&
+           cma_disable_remove(id_priv, CMA_ADDR_RESOLVED))
+               return 0;
 
+       mutex_lock(&id_priv->qp_mutex);
        if (!status && id_priv->id.qp)
                status = ib_attach_mcast(id_priv->id.qp, &multicast->rec.mgid,
                                         multicast->rec.mlid);
+       mutex_unlock(&id_priv->qp_mutex);
 
        memset(&event, 0, sizeof event);
        event.status = status;
@@ -2525,12 +2582,12 @@ static int cma_ib_mc_handler(int status, struct ib_sa_multicast *multicast)
        ret = id_priv->id.event_handler(&id_priv->id, &event);
        if (ret) {
                cma_exch(id_priv, CMA_DESTROYING);
-               cma_release_remove(id_priv);
+               cma_enable_remove(id_priv);
                rdma_destroy_id(&id_priv->id);
                return 0;
        }
-out:
-       cma_release_remove(id_priv);
+
+       cma_enable_remove(id_priv);
        return 0;
 }
 
@@ -2550,11 +2607,9 @@ static void cma_set_mgid(struct rdma_id_private *id_priv,
                /* IPv6 address is an SA assigned MGID. */
                memcpy(mgid, &sin6->sin6_addr, sizeof *mgid);
        } else {
-               ip_ib_mc_map(sin->sin_addr.s_addr, mc_map);
+               ip_ib_mc_map(sin->sin_addr.s_addr, dev_addr->broadcast, mc_map);
                if (id_priv->id.ps == RDMA_PS_UDP)
                        mc_map[7] = 0x01;       /* Use RDMA CM signature */
-               mc_map[8] = ib_addr_get_pkey(dev_addr) >> 8;
-               mc_map[9] = (unsigned char) ib_addr_get_pkey(dev_addr);
                *mgid = *(union ib_gid *) (mc_map + 4);
        }
 }
@@ -2674,7 +2729,6 @@ static void cma_add_one(struct ib_device *device)
                return;
 
        cma_dev->device = device;
-       cma_dev->node_guid = device->node_guid;
 
        init_completion(&cma_dev->comp);
        atomic_set(&cma_dev->refcount, 1);
@@ -2720,16 +2774,12 @@ static void cma_process_remove(struct cma_device *cma_dev)
                id_priv = list_entry(cma_dev->id_list.next,
                                     struct rdma_id_private, list);
 
-               if (cma_internal_listen(id_priv)) {
-                       cma_destroy_listen(id_priv);
-                       continue;
-               }
-
+               list_del(&id_priv->listen_list);
                list_del_init(&id_priv->list);
                atomic_inc(&id_priv->refcount);
                mutex_unlock(&lock);
 
-               ret = cma_remove_id_dev(id_priv);
+               ret = id_priv->internal_id ? 1 : cma_remove_id_dev(id_priv);
                cma_deref_id(id_priv);
                if (ret)
                        rdma_destroy_id(&id_priv->id);
@@ -2760,12 +2810,13 @@ static void cma_remove_one(struct ib_device *device)
 
 static int cma_init(void)
 {
-       int ret;
+       int ret, low, high, remaining;
 
        get_random_bytes(&next_port, sizeof next_port);
-       next_port = (next_port % (sysctl_local_port_range[1] -
-                                 sysctl_local_port_range[0])) +
-                   sysctl_local_port_range[0];
+       inet_get_local_port_range(&low, &high);
+       remaining = (high - low) + 1;
+       next_port = ((unsigned int) next_port % remaining) + low;
+
        cma_wq = create_singlethread_workqueue("rdma_cm");
        if (!cma_wq)
                return -ENOMEM;