sgi-xpc: Remove NULL pointer dereference.
[safe/jmp/linux-2.6] / drivers / infiniband / core / cma.c
index f8d69b3..2a2e508 100644 (file)
@@ -4,29 +4,33 @@
  * Copyright (c) 1999-2005, Mellanox Technologies, Inc. All rights reserved.
  * Copyright (c) 2005-2006 Intel Corporation.  All rights reserved.
  *
- * This Software is licensed under one of the following licenses:
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
  *
- * 1) under the terms of the "Common Public License 1.0" a copy of which is
- *    available from the Open Source Initiative, see
- *    http://www.opensource.org/licenses/cpl.php.
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
  *
- * 2) under the terms of the "The BSD License" a copy of which is
- *    available from the Open Source Initiative, see
- *    http://www.opensource.org/licenses/bsd-license.php.
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
  *
- * 3) under the terms of the "GNU General Public License (GPL) Version 2" a
- *    copy of which is available from the Open Source Initiative, see
- *    http://www.opensource.org/licenses/gpl-license.php.
- *
- * Licensee has the right to choose one of the above licenses.
- *
- * Redistributions of source code must retain the above copyright
- * notice and one of the license notices.
- *
- * Redistributions in binary form must reproduce both the above copyright
- * notice, one of the license notices in the documentation
- * and/or other materials provided with the distribution.
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
  *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
  */
 
 #include <linux/completion.h>
@@ -38,6 +42,7 @@
 #include <linux/inetdevice.h>
 
 #include <net/tcp.h>
+#include <net/ipv6.h>
 
 #include <rdma/rdma_cm.h>
 #include <rdma/rdma_cm_ib.h>
@@ -52,6 +57,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 +83,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,17 +119,19 @@ 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;
-       atomic_t                dev_remove;
+       struct mutex            handler_mutex;
 
        int                     backlog;
        int                     timeout_ms;
@@ -139,6 +146,7 @@ struct rdma_id_private {
        u32                     qkey;
        u32                     qp_num;
        u8                      srq;
+       u8                      tos;
 };
 
 struct cma_multicast {
@@ -148,9 +156,7 @@ struct cma_multicast {
        } multicast;
        struct list_head        list;
        void                    *context;
-       struct sockaddr         addr;
-       u8                      pad[sizeof(struct sockaddr_in6) -
-                                   sizeof(struct sockaddr)];
+       struct sockaddr_storage addr;
 };
 
 struct cma_work {
@@ -161,18 +167,24 @@ struct cma_work {
        struct rdma_cm_event    event;
 };
 
+struct cma_ndev_work {
+       struct work_struct      work;
+       struct rdma_id_private  *id;
+       struct rdma_cm_event    event;
+};
+
 union cma_ip_addr {
        struct in6_addr ip6;
        struct {
-               __u32 pad[3];
-               __u32 addr;
+               __be32 pad[3];
+               __be32 addr;
        } ip4;
 };
 
 struct cma_hdr {
        u8 cma_version;
        u8 ip_version;  /* IP version: 7:4 */
-       __u16 port;
+       __be16 port;
        union cma_ip_addr src_addr;
        union cma_ip_addr dst_addr;
 };
@@ -182,8 +194,8 @@ struct sdp_hh {
        u8 sdp_version; /* Major version: 7:4 */
        u8 ip_version;  /* IP version: 7:4 */
        u8 sdp_specific1[10];
-       __u16 port;
-       __u16 sdp_specific2;
+       __be16 port;
+       __be16 sdp_specific2;
        union cma_ip_addr src_addr;
        union cma_ip_addr dst_addr;
 };
@@ -347,10 +359,20 @@ 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_callback(struct rdma_id_private *id_priv,
+                             enum cma_state state)
 {
-       if (atomic_dec_and_test(&id_priv->dev_remove))
-               wake_up(&id_priv->wait_remove);
+       mutex_lock(&id_priv->handler_mutex);
+       if (id_priv->state != state) {
+               mutex_unlock(&id_priv->handler_mutex);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+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,
@@ -367,10 +389,10 @@ 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);
-       atomic_set(&id_priv->dev_remove, 0);
+       mutex_init(&id_priv->handler_mutex);
        INIT_LIST_HEAD(&id_priv->listen_list);
        INIT_LIST_HEAD(&id_priv->mc_list);
        get_random_bytes(&id_priv->seq_num, sizeof id_priv->seq_num);
@@ -452,61 +474,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 +606,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 +626,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 +637,12 @@ 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);
+       if (addr->sa_family == AF_INET)
+               return ipv4_is_loopback(
+                       ((struct sockaddr_in *) addr)->sin_addr.s_addr);
+       else
+               return ipv6_addr_loopback(
+                       &((struct sockaddr_in6 *) addr)->sin6_addr);
 }
 
 static inline int cma_any_addr(struct sockaddr *addr)
@@ -605,7 +664,7 @@ static inline int cma_any_port(struct sockaddr *addr)
 }
 
 static int cma_get_net_info(void *hdr, enum rdma_port_space ps,
-                           u8 *ip_ver, __u16 *port,
+                           u8 *ip_ver, __be16 *port,
                            union cma_ip_addr **src, union cma_ip_addr **dst)
 {
        switch (ps) {
@@ -637,7 +696,7 @@ static int cma_get_net_info(void *hdr, enum rdma_port_space ps,
 
 static void cma_save_net_info(struct rdma_addr *addr,
                              struct rdma_addr *listen_addr,
-                             u8 ip_ver, __u16 port,
+                             u8 ip_ver, __be16 port,
                              union cma_ip_addr *src, union cma_ip_addr *dst)
 {
        struct sockaddr_in *listen4, *ip4;
@@ -695,50 +754,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);
 }
@@ -754,8 +790,8 @@ static void cma_cancel_operation(struct rdma_id_private *id_priv,
                cma_cancel_route(id_priv);
                break;
        case CMA_LISTEN:
-               if (cma_any_addr(&id_priv->id.route.addr.src_addr) &&
-                   !id_priv->cma_dev)
+               if (cma_any_addr((struct sockaddr *) &id_priv->id.route.addr.src_addr)
+                               && !id_priv->cma_dev)
                        cma_cancel_listens(id_priv);
                break;
        default:
@@ -826,6 +862,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 +874,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 +888,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 +924,11 @@ 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 ((ib_event->event != IB_CM_TIMEWAIT_EXIT &&
+               cma_disable_callback(id_priv, CMA_CONNECT)) ||
+           (ib_event->event == IB_CM_TIMEWAIT_EXIT &&
+               cma_disable_callback(id_priv, CMA_DISCONNECT)))
+               return 0;
 
        memset(&event, 0, sizeof event);
        switch (ib_event->event) {
@@ -922,18 +963,20 @@ static int cma_ib_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
                event.event = RDMA_CM_EVENT_DISCONNECTED;
                break;
        case IB_CM_TIMEWAIT_EXIT:
+               event.event = RDMA_CM_EVENT_TIMEWAIT_EXIT;
+               break;
        case IB_CM_MRA_RECEIVED:
                /* 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;
                event.param.conn.private_data_len = IB_CM_REJ_PRIVATE_DATA_SIZE;
                break;
        default:
-               printk(KERN_ERR "RDMA CMA: unexpected IB CM event: %d",
+               printk(KERN_ERR "RDMA CMA: unexpected IB CM event: %d\n",
                       ib_event->event);
                goto out;
        }
@@ -943,12 +986,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);
+               mutex_unlock(&id_priv->handler_mutex);
                rdma_destroy_id(&id_priv->id);
                return ret;
        }
 out:
-       cma_release_remove(id_priv);
+       mutex_unlock(&id_priv->handler_mutex);
        return ret;
 }
 
@@ -959,8 +1002,9 @@ static struct rdma_id_private *cma_new_conn_id(struct rdma_cm_id *listen_id,
        struct rdma_cm_id *id;
        struct rdma_route *rt;
        union cma_ip_addr *src, *dst;
-       __u16 port;
+       __be16 port;
        u8 ip_ver;
+       int ret;
 
        if (cma_get_net_info(ib_event->private_data, listen_id->ps,
                             &ip_ver, &port, &src, &dst))
@@ -985,10 +1029,11 @@ static struct rdma_id_private *cma_new_conn_id(struct rdma_cm_id *listen_id,
        if (rt->num_paths == 2)
                rt->path_rec[1] = *ib_event->param.req_rcvd.alternate_path;
 
-       ib_addr_set_sgid(&rt->addr.dev_addr, &rt->path_rec[0].sgid);
        ib_addr_set_dgid(&rt->addr.dev_addr, &rt->path_rec[0].dgid);
-       ib_addr_set_pkey(&rt->addr.dev_addr, be16_to_cpu(rt->path_rec[0].pkey));
-       rt->addr.dev_addr.dev_type = RDMA_NODE_IB_CA;
+       ret = rdma_translate_ip((struct sockaddr *) &id->route.addr.src_addr,
+                               &id->route.addr.dev_addr);
+       if (ret)
+               goto destroy_id;
 
        id_priv = container_of(id, struct rdma_id_private, id);
        id_priv->state = CMA_CONNECT;
@@ -1006,7 +1051,7 @@ static struct rdma_id_private *cma_new_udp_id(struct rdma_cm_id *listen_id,
        struct rdma_id_private *id_priv;
        struct rdma_cm_id *id;
        union cma_ip_addr *src, *dst;
-       __u16 port;
+       __be16 port;
        u8 ip_ver;
        int ret;
 
@@ -1023,7 +1068,7 @@ static struct rdma_id_private *cma_new_udp_id(struct rdma_cm_id *listen_id,
        cma_save_net_info(&id->route.addr, &listen_id->route.addr,
                          ip_ver, port, src, dst);
 
-       ret = rdma_translate_ip(&id->route.addr.src_addr,
+       ret = rdma_translate_ip((struct sockaddr *) &id->route.addr.src_addr,
                                &id->route.addr.dev_addr);
        if (ret)
                goto err;
@@ -1058,11 +1103,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_callback(listen_id, CMA_LISTEN))
+               return -ECONNABORTED;
 
        memset(&event, 0, sizeof event);
        offset = cma_user_data_offset(listen_id->id.ps);
@@ -1082,7 +1124,7 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
                goto out;
        }
 
-       atomic_inc(&conn_id->dev_remove);
+       mutex_lock_nested(&conn_id->handler_mutex, SINGLE_DEPTH_NESTING);
        mutex_lock(&lock);
        ret = cma_acquire_dev(conn_id);
        mutex_unlock(&lock);
@@ -1094,19 +1136,30 @@ 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) {
+               /*
+                * Acquire mutex to prevent user executing rdma_destroy_id()
+                * while we're accessing the cm_id.
+                */
+               mutex_lock(&lock);
+               if (cma_comp(conn_id, CMA_CONNECT) &&
+                   !cma_is_ud_ps(conn_id->id.ps))
+                       ib_send_cm_mra(cm_id, CMA_CM_MRA_SETTING, NULL, 0);
+               mutex_unlock(&lock);
+               mutex_unlock(&conn_id->handler_mutex);
                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);
+       mutex_unlock(&conn_id->handler_mutex);
        rdma_destroy_id(&conn_id->id);
 
 out:
-       cma_release_remove(listen_id);
+       mutex_unlock(&listen_id->handler_mutex);
        return ret;
 }
 
@@ -1120,7 +1173,7 @@ static void cma_set_compare_data(enum rdma_port_space ps, struct sockaddr *addr,
 {
        struct cma_hdr *cma_data, *cma_mask;
        struct sdp_hh *sdp_data, *sdp_mask;
-       __u32 ip4_addr;
+       __be32 ip4_addr;
        struct in6_addr ip6_addr;
 
        memset(compare, 0, sizeof *compare);
@@ -1136,12 +1189,12 @@ static void cma_set_compare_data(enum rdma_port_space ps, struct sockaddr *addr,
                        sdp_set_ip_ver(sdp_data, 4);
                        sdp_set_ip_ver(sdp_mask, 0xF);
                        sdp_data->dst_addr.ip4.addr = ip4_addr;
-                       sdp_mask->dst_addr.ip4.addr = ~0;
+                       sdp_mask->dst_addr.ip4.addr = htonl(~0);
                } else {
                        cma_set_ip_ver(cma_data, 4);
                        cma_set_ip_ver(cma_mask, 0xF);
                        cma_data->dst_addr.ip4.addr = ip4_addr;
-                       cma_mask->dst_addr.ip4.addr = ~0;
+                       cma_mask->dst_addr.ip4.addr = htonl(~0);
                }
                break;
        case AF_INET6:
@@ -1172,9 +1225,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_callback(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 +1269,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);
+               mutex_unlock(&id_priv->handler_mutex);
                rdma_destroy_id(&id_priv->id);
                return ret;
        }
 
-       cma_release_remove(id_priv);
+       mutex_unlock(&id_priv->handler_mutex);
        return ret;
 }
 
@@ -1233,36 +1287,34 @@ 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_callback(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,
                                   listen_id->id.context,
                                   RDMA_PS_TCP);
-       if (!new_cm_id) {
+       if (IS_ERR(new_cm_id)) {
                ret = -ENOMEM;
                goto out;
        }
        conn_id = container_of(new_cm_id, struct rdma_id_private, id);
-       atomic_inc(&conn_id->dev_remove);
+       mutex_lock_nested(&conn_id->handler_mutex, SINGLE_DEPTH_NESTING);
        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);
+               mutex_unlock(&conn_id->handler_mutex);
                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);
+               mutex_unlock(&conn_id->handler_mutex);
                rdma_destroy_id(new_cm_id);
                goto out;
        }
@@ -1271,7 +1323,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);
+               mutex_unlock(&conn_id->handler_mutex);
                rdma_destroy_id(new_cm_id);
                goto out;
        }
@@ -1285,23 +1337,35 @@ 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) {
+               mutex_unlock(&conn_id->handler_mutex);
+               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);
+               mutex_unlock(&conn_id->handler_mutex);
                rdma_destroy_id(&conn_id->id);
+               goto out;
        }
 
+       mutex_unlock(&conn_id->handler_mutex);
+
 out:
        if (dev)
                dev_put(dev);
-       cma_release_remove(listen_id);
+       mutex_unlock(&listen_id->handler_mutex);
        return ret;
 }
 
@@ -1317,7 +1381,7 @@ static int cma_ib_listen(struct rdma_id_private *id_priv)
        if (IS_ERR(id_priv->cm_id.ib))
                return PTR_ERR(id_priv->cm_id.ib);
 
-       addr = &id_priv->id.route.addr.src_addr;
+       addr = (struct sockaddr *) &id_priv->id.route.addr.src_addr;
        svc_id = cma_get_service_id(id_priv->id.ps, addr);
        if (cma_any_addr(addr))
                ret = ib_cm_listen(id_priv->cm_id.ib, svc_id, 0, NULL);
@@ -1383,18 +1447,17 @@ static void cma_listen_on_dev(struct rdma_id_private *id_priv,
 
        dev_id_priv->state = CMA_ADDR_BOUND;
        memcpy(&id->route.addr.src_addr, &id_priv->id.route.addr.src_addr,
-              ip_addr_size(&id_priv->id.route.addr.src_addr));
+              ip_addr_size((struct sockaddr *) &id_priv->id.route.addr.src_addr));
 
        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\n", ret, cma_dev->device->name);
 }
 
 static void cma_listen_on_all(struct rdma_id_private *id_priv)
@@ -1410,10 +1473,10 @@ static void cma_listen_on_all(struct rdma_id_private *id_priv)
 
 static int cma_bind_any(struct rdma_cm_id *id, sa_family_t af)
 {
-       struct sockaddr_in addr_in;
+       struct sockaddr_storage addr_in;
 
        memset(&addr_in, 0, sizeof addr_in);
-       addr_in.sin_family = af;
+       addr_in.ss_family = af;
        return rdma_bind_addr(id, (struct sockaddr *) &addr_in);
 }
 
@@ -1460,6 +1523,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,21 +1556,38 @@ 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,
+                                                       (struct sockaddr *) &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.ss_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,
-                               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;
 }
@@ -1509,7 +1598,7 @@ static void cma_work_handler(struct work_struct *_work)
        struct rdma_id_private *id_priv = work->id;
        int destroy = 0;
 
-       atomic_inc(&id_priv->dev_remove);
+       mutex_lock(&id_priv->handler_mutex);
        if (!cma_comp_exch(id_priv, work->old_state, work->new_state))
                goto out;
 
@@ -1518,7 +1607,31 @@ static void cma_work_handler(struct work_struct *_work)
                destroy = 1;
        }
 out:
-       cma_release_remove(id_priv);
+       mutex_unlock(&id_priv->handler_mutex);
+       cma_deref_id(id_priv);
+       if (destroy)
+               rdma_destroy_id(&id_priv->id);
+       kfree(work);
+}
+
+static void cma_ndev_work_handler(struct work_struct *_work)
+{
+       struct cma_ndev_work *work = container_of(_work, struct cma_ndev_work, work);
+       struct rdma_id_private *id_priv = work->id;
+       int destroy = 0;
+
+       mutex_lock(&id_priv->handler_mutex);
+       if (id_priv->state == CMA_DESTROYING ||
+           id_priv->state == CMA_DEVICE_REMOVAL)
+               goto out;
+
+       if (id_priv->id.event_handler(&id_priv->id, &work->event)) {
+               cma_exch(id_priv, CMA_DESTROYING);
+               destroy = 1;
+       }
+
+out:
+       mutex_unlock(&id_priv->handler_mutex);
        cma_deref_id(id_priv);
        if (destroy)
                rdma_destroy_id(&id_priv->id);
@@ -1681,7 +1794,7 @@ static void addr_handler(int status, struct sockaddr *src_addr,
        struct rdma_cm_event event;
 
        memset(&event, 0, sizeof event);
-       atomic_inc(&id_priv->dev_remove);
+       mutex_lock(&id_priv->handler_mutex);
 
        /*
         * Grab mutex to block rdma_destroy_id() from removing the device while
@@ -1710,13 +1823,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);
+               mutex_unlock(&id_priv->handler_mutex);
                cma_deref_id(id_priv);
                rdma_destroy_id(&id_priv->id);
                return;
        }
 out:
-       cma_release_remove(id_priv);
+       mutex_unlock(&id_priv->handler_mutex);
        cma_deref_id(id_priv);
 }
 
@@ -1740,7 +1853,7 @@ static int cma_resolve_loopback(struct rdma_id_private *id_priv)
        ib_addr_get_sgid(&id_priv->id.route.addr.dev_addr, &gid);
        ib_addr_set_dgid(&id_priv->id.route.addr.dev_addr, &gid);
 
-       if (cma_zero_addr(&id_priv->id.route.addr.src_addr)) {
+       if (cma_zero_addr((struct sockaddr *) &id_priv->id.route.addr.src_addr)) {
                src_in = (struct sockaddr_in *)&id_priv->id.route.addr.src_addr;
                dst_in = (struct sockaddr_in *)&id_priv->id.route.addr.dst_addr;
                src_in->sin_family = dst_in->sin_family;
@@ -1789,7 +1902,7 @@ int rdma_resolve_addr(struct rdma_cm_id *id, struct sockaddr *src_addr,
        if (cma_any_addr(dst_addr))
                ret = cma_resolve_loopback(id_priv);
        else
-               ret = rdma_resolve_ip(&addr_client, &id->route.addr.src_addr,
+               ret = rdma_resolve_ip(&addr_client, (struct sockaddr *) &id->route.addr.src_addr,
                                      dst_addr, &id->route.addr.dev_addr,
                                      timeout_ms, addr_handler, id_priv);
        if (ret)
@@ -1820,7 +1933,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;
 
@@ -1850,13 +1963,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));
@@ -1864,18 +1978,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;
 
@@ -1911,11 +2026,11 @@ static int cma_use_port(struct idr *ps, struct rdma_id_private *id_priv)
         * We don't support binding to any address if anyone is bound to
         * a specific address on the same port.
         */
-       if (cma_any_addr(&id_priv->id.route.addr.src_addr))
+       if (cma_any_addr((struct sockaddr *) &id_priv->id.route.addr.src_addr))
                return -EADDRNOTAVAIL;
 
        hlist_for_each_entry(cur_id, node, &bind_list->owners, node) {
-               if (cma_any_addr(&cur_id->id.route.addr.src_addr))
+               if (cma_any_addr((struct sockaddr *) &cur_id->id.route.addr.src_addr))
                        return -EADDRNOTAVAIL;
 
                cur_sin = (struct sockaddr_in *) &cur_id->id.route.addr.src_addr;
@@ -1950,7 +2065,7 @@ static int cma_get_port(struct rdma_id_private *id_priv)
        }
 
        mutex_lock(&lock);
-       if (cma_any_port(&id_priv->id.route.addr.src_addr))
+       if (cma_any_port((struct sockaddr *) &id_priv->id.route.addr.src_addr))
                ret = cma_alloc_any_port(ps, id_priv);
        else
                ret = cma_use_port(ps, id_priv);
@@ -1964,7 +2079,7 @@ int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr)
        struct rdma_id_private *id_priv;
        int ret;
 
-       if (addr->sa_family != AF_INET)
+       if (addr->sa_family != AF_INET && addr->sa_family != AF_INET6)
                return -EAFNOSUPPORT;
 
        id_priv = container_of(id, struct rdma_id_private, id);
@@ -2004,31 +2119,59 @@ EXPORT_SYMBOL(rdma_bind_addr);
 static int cma_format_hdr(void *hdr, enum rdma_port_space ps,
                          struct rdma_route *route)
 {
-       struct sockaddr_in *src4, *dst4;
        struct cma_hdr *cma_hdr;
        struct sdp_hh *sdp_hdr;
 
-       src4 = (struct sockaddr_in *) &route->addr.src_addr;
-       dst4 = (struct sockaddr_in *) &route->addr.dst_addr;
-
-       switch (ps) {
-       case RDMA_PS_SDP:
-               sdp_hdr = hdr;
-               if (sdp_get_majv(sdp_hdr->sdp_version) != SDP_MAJ_VERSION)
-                       return -EINVAL;
-               sdp_set_ip_ver(sdp_hdr, 4);
-               sdp_hdr->src_addr.ip4.addr = src4->sin_addr.s_addr;
-               sdp_hdr->dst_addr.ip4.addr = dst4->sin_addr.s_addr;
-               sdp_hdr->port = src4->sin_port;
-               break;
-       default:
-               cma_hdr = hdr;
-               cma_hdr->cma_version = CMA_VERSION;
-               cma_set_ip_ver(cma_hdr, 4);
-               cma_hdr->src_addr.ip4.addr = src4->sin_addr.s_addr;
-               cma_hdr->dst_addr.ip4.addr = dst4->sin_addr.s_addr;
-               cma_hdr->port = src4->sin_port;
-               break;
+       if (route->addr.src_addr.ss_family == AF_INET) {
+               struct sockaddr_in *src4, *dst4;
+
+               src4 = (struct sockaddr_in *) &route->addr.src_addr;
+               dst4 = (struct sockaddr_in *) &route->addr.dst_addr;
+
+               switch (ps) {
+               case RDMA_PS_SDP:
+                       sdp_hdr = hdr;
+                       if (sdp_get_majv(sdp_hdr->sdp_version) != SDP_MAJ_VERSION)
+                               return -EINVAL;
+                       sdp_set_ip_ver(sdp_hdr, 4);
+                       sdp_hdr->src_addr.ip4.addr = src4->sin_addr.s_addr;
+                       sdp_hdr->dst_addr.ip4.addr = dst4->sin_addr.s_addr;
+                       sdp_hdr->port = src4->sin_port;
+                       break;
+               default:
+                       cma_hdr = hdr;
+                       cma_hdr->cma_version = CMA_VERSION;
+                       cma_set_ip_ver(cma_hdr, 4);
+                       cma_hdr->src_addr.ip4.addr = src4->sin_addr.s_addr;
+                       cma_hdr->dst_addr.ip4.addr = dst4->sin_addr.s_addr;
+                       cma_hdr->port = src4->sin_port;
+                       break;
+               }
+       } else {
+               struct sockaddr_in6 *src6, *dst6;
+
+               src6 = (struct sockaddr_in6 *) &route->addr.src_addr;
+               dst6 = (struct sockaddr_in6 *) &route->addr.dst_addr;
+
+               switch (ps) {
+               case RDMA_PS_SDP:
+                       sdp_hdr = hdr;
+                       if (sdp_get_majv(sdp_hdr->sdp_version) != SDP_MAJ_VERSION)
+                               return -EINVAL;
+                       sdp_set_ip_ver(sdp_hdr, 6);
+                       sdp_hdr->src_addr.ip6 = src6->sin6_addr;
+                       sdp_hdr->dst_addr.ip6 = dst6->sin6_addr;
+                       sdp_hdr->port = src6->sin6_port;
+                       break;
+               default:
+                       cma_hdr = hdr;
+                       cma_hdr->cma_version = CMA_VERSION;
+                       cma_set_ip_ver(cma_hdr, 6);
+                       cma_hdr->src_addr.ip6 = src6->sin6_addr;
+                       cma_hdr->dst_addr.ip6 = dst6->sin6_addr;
+                       cma_hdr->port = src6->sin6_port;
+                       break;
+               }
        }
        return 0;
 }
@@ -2041,11 +2184,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_callback(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;
@@ -2073,7 +2215,7 @@ static int cma_sidr_rep_handler(struct ib_cm_id *cm_id,
                event.status = 0;
                break;
        default:
-               printk(KERN_ERR "RDMA CMA: unexpected IB CM event: %d",
+               printk(KERN_ERR "RDMA CMA: unexpected IB CM event: %d\n",
                       ib_event->event);
                goto out;
        }
@@ -2083,12 +2225,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);
+               mutex_unlock(&id_priv->handler_mutex);
                rdma_destroy_id(&id_priv->id);
                return ret;
        }
 out:
-       cma_release_remove(id_priv);
+       mutex_unlock(&id_priv->handler_mutex);
        return ret;
 }
 
@@ -2123,7 +2265,7 @@ static int cma_resolve_ib_udp(struct rdma_id_private *id_priv,
 
        req.path = route->path_rec;
        req.service_id = cma_get_service_id(id_priv->id.ps,
-                                           &route->addr.dst_addr);
+                                           (struct sockaddr *) &route->addr.dst_addr);
        req.timeout_ms = 1 << (CMA_CM_RESPONSE_TIMEOUT - 8);
        req.max_cm_retries = CMA_MAX_CM_RETRIES;
 
@@ -2174,7 +2316,7 @@ static int cma_connect_ib(struct rdma_id_private *id_priv,
                req.alternate_path = &route->path_rec[1];
 
        req.service_id = cma_get_service_id(id_priv->id.ps,
-                                           &route->addr.dst_addr);
+                                           (struct sockaddr *) &route->addr.dst_addr);
        req.qp_num = id_priv->qp_num;
        req.qp_type = IB_QPT_RC;
        req.starting_psn = id_priv->seq_num;
@@ -2221,7 +2363,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;
 
@@ -2284,25 +2426,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;
@@ -2311,7 +2443,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;
@@ -2328,7 +2459,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;
 
@@ -2400,7 +2531,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;
 }
@@ -2412,7 +2543,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) {
@@ -2434,7 +2565,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)) {
@@ -2465,13 +2596,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. */
@@ -2498,14 +2628,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_callback(id_priv, CMA_ADDR_BOUND) &&
+           cma_disable_callback(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;
@@ -2523,12 +2654,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);
+               mutex_unlock(&id_priv->handler_mutex);
                rdma_destroy_id(&id_priv->id);
                return 0;
        }
-out:
-       cma_release_remove(id_priv);
+
+       mutex_unlock(&id_priv->handler_mutex);
        return 0;
 }
 
@@ -2548,11 +2679,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);
        }
 }
@@ -2571,7 +2700,7 @@ static int cma_join_ib_multicast(struct rdma_id_private *id_priv,
        if (ret)
                return ret;
 
-       cma_set_mgid(id_priv, &mc->addr, &rec.mgid);
+       cma_set_mgid(id_priv, (struct sockaddr *) &mc->addr, &rec.mgid);
        if (id_priv->id.ps == RDMA_PS_UDP)
                rec.qkey = cpu_to_be32(RDMA_UDP_QKEY);
        ib_addr_get_sgid(dev_addr, &rec.port_gid);
@@ -2662,6 +2791,65 @@ void rdma_leave_multicast(struct rdma_cm_id *id, struct sockaddr *addr)
 }
 EXPORT_SYMBOL(rdma_leave_multicast);
 
+static int cma_netdev_change(struct net_device *ndev, struct rdma_id_private *id_priv)
+{
+       struct rdma_dev_addr *dev_addr;
+       struct cma_ndev_work *work;
+
+       dev_addr = &id_priv->id.route.addr.dev_addr;
+
+       if ((dev_addr->src_dev == ndev) &&
+           memcmp(dev_addr->src_dev_addr, ndev->dev_addr, ndev->addr_len)) {
+               printk(KERN_INFO "RDMA CM addr change for ndev %s used by id %p\n",
+                      ndev->name, &id_priv->id);
+               work = kzalloc(sizeof *work, GFP_KERNEL);
+               if (!work)
+                       return -ENOMEM;
+
+               INIT_WORK(&work->work, cma_ndev_work_handler);
+               work->id = id_priv;
+               work->event.event = RDMA_CM_EVENT_ADDR_CHANGE;
+               atomic_inc(&id_priv->refcount);
+               queue_work(cma_wq, &work->work);
+       }
+
+       return 0;
+}
+
+static int cma_netdev_callback(struct notifier_block *self, unsigned long event,
+                              void *ctx)
+{
+       struct net_device *ndev = (struct net_device *)ctx;
+       struct cma_device *cma_dev;
+       struct rdma_id_private *id_priv;
+       int ret = NOTIFY_DONE;
+
+       if (dev_net(ndev) != &init_net)
+               return NOTIFY_DONE;
+
+       if (event != NETDEV_BONDING_FAILOVER)
+               return NOTIFY_DONE;
+
+       if (!(ndev->flags & IFF_MASTER) || !(ndev->priv_flags & IFF_BONDING))
+               return NOTIFY_DONE;
+
+       mutex_lock(&lock);
+       list_for_each_entry(cma_dev, &dev_list, list)
+               list_for_each_entry(id_priv, &cma_dev->id_list, list) {
+                       ret = cma_netdev_change(ndev, id_priv);
+                       if (ret)
+                               goto out;
+               }
+
+out:
+       mutex_unlock(&lock);
+       return ret;
+}
+
+static struct notifier_block cma_nb = {
+       .notifier_call = cma_netdev_callback
+};
+
 static void cma_add_one(struct ib_device *device)
 {
        struct cma_device *cma_dev;
@@ -2672,7 +2860,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);
@@ -2690,6 +2877,7 @@ static int cma_remove_id_dev(struct rdma_id_private *id_priv)
 {
        struct rdma_cm_event event;
        enum cma_state state;
+       int ret = 0;
 
        /* Record that we want to remove the device */
        state = cma_exch(id_priv, CMA_DEVICE_REMOVAL);
@@ -2697,15 +2885,18 @@ static int cma_remove_id_dev(struct rdma_id_private *id_priv)
                return 0;
 
        cma_cancel_operation(id_priv, state);
-       wait_event(id_priv->wait_remove, !atomic_read(&id_priv->dev_remove));
+       mutex_lock(&id_priv->handler_mutex);
 
        /* Check for destruction from another callback. */
        if (!cma_comp(id_priv, CMA_DEVICE_REMOVAL))
-               return 0;
+               goto out;
 
        memset(&event, 0, sizeof event);
        event.event = RDMA_CM_EVENT_DEVICE_REMOVAL;
-       return id_priv->id.event_handler(&id_priv->id, &event);
+       ret = id_priv->id.event_handler(&id_priv->id, &event);
+out:
+       mutex_unlock(&id_priv->handler_mutex);
+       return ret;
 }
 
 static void cma_process_remove(struct cma_device *cma_dev)
@@ -2718,16 +2909,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);
@@ -2758,18 +2945,20 @@ 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;
 
        ib_sa_register_client(&sa_client);
        rdma_addr_register_client(&addr_client);
+       register_netdevice_notifier(&cma_nb);
 
        ret = ib_register_client(&cma_client);
        if (ret)
@@ -2777,6 +2966,7 @@ static int cma_init(void)
        return 0;
 
 err:
+       unregister_netdevice_notifier(&cma_nb);
        rdma_addr_unregister_client(&addr_client);
        ib_sa_unregister_client(&sa_client);
        destroy_workqueue(cma_wq);
@@ -2786,6 +2976,7 @@ err:
 static void cma_cleanup(void)
 {
        ib_unregister_client(&cma_client);
+       unregister_netdevice_notifier(&cma_nb);
        rdma_addr_unregister_client(&addr_client);
        ib_sa_unregister_client(&sa_client);
        destroy_workqueue(cma_wq);