Pull bugzilla-5452 into release branch
[pandora-kernel.git] / drivers / infiniband / core / cm.c
index 02110e0..86fee43 100644 (file)
@@ -34,6 +34,8 @@
  *
  * $Id: cm.c 2821 2005-07-08 17:07:28Z sean.hefty $
  */
+
+#include <linux/completion.h>
 #include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/idr.h>
@@ -121,8 +123,8 @@ struct cm_id_private {
 
        struct rb_node service_node;
        struct rb_node sidr_id_node;
-       spinlock_t lock;
-       wait_queue_head_t wait;
+       spinlock_t lock;        /* Do not acquire inside cm.lock */
+       struct completion comp;
        atomic_t refcount;
 
        struct ib_mad_send_buf *msg;
@@ -159,7 +161,7 @@ static void cm_work_handler(void *data);
 static inline void cm_deref_id(struct cm_id_private *cm_id_priv)
 {
        if (atomic_dec_and_test(&cm_id_priv->refcount))
-               wake_up(&cm_id_priv->wait);
+               complete(&cm_id_priv->comp);
 }
 
 static int cm_alloc_msg(struct cm_id_private *cm_id_priv,
@@ -308,10 +310,11 @@ static int cm_alloc_id(struct cm_id_private *cm_id_priv)
 {
        unsigned long flags;
        int ret;
+       static int next_id;
 
        do {
                spin_lock_irqsave(&cm.lock, flags);
-               ret = idr_get_new_above(&cm.local_id_table, cm_id_priv, 1,
+               ret = idr_get_new_above(&cm.local_id_table, cm_id_priv, next_id++,
                                        (__force int *) &cm_id_priv->id.local_id);
                spin_unlock_irqrestore(&cm.lock, flags);
        } while( (ret == -EAGAIN) && idr_pre_get(&cm.local_id_table, GFP_KERNEL) );
@@ -558,7 +561,7 @@ struct ib_cm_id *ib_create_cm_id(struct ib_device *device,
                goto error;
 
        spin_lock_init(&cm_id_priv->lock);
-       init_waitqueue_head(&cm_id_priv->wait);
+       init_completion(&cm_id_priv->comp);
        INIT_LIST_HEAD(&cm_id_priv->work_list);
        atomic_set(&cm_id_priv->work_count, -1);
        atomic_set(&cm_id_priv->refcount, 1);
@@ -684,6 +687,13 @@ retest:
                cm_reject_sidr_req(cm_id_priv, IB_SIDR_REJECT);
                break;
        case IB_CM_REQ_SENT:
+               ib_cancel_mad(cm_id_priv->av.port->mad_agent, cm_id_priv->msg);
+               spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+               ib_send_cm_rej(cm_id, IB_CM_REJ_TIMEOUT,
+                              &cm_id_priv->av.port->cm_dev->ca_guid,
+                              sizeof cm_id_priv->av.port->cm_dev->ca_guid,
+                              NULL, 0);
+               break;
        case IB_CM_MRA_REQ_RCVD:
        case IB_CM_REP_SENT:
        case IB_CM_MRA_REP_RCVD:
@@ -694,10 +704,8 @@ retest:
        case IB_CM_REP_RCVD:
        case IB_CM_MRA_REP_SENT:
                spin_unlock_irqrestore(&cm_id_priv->lock, flags);
-               ib_send_cm_rej(cm_id, IB_CM_REJ_TIMEOUT,
-                              &cm_id_priv->av.port->cm_dev->ca_guid,
-                              sizeof cm_id_priv->av.port->cm_dev->ca_guid,
-                              NULL, 0);
+               ib_send_cm_rej(cm_id, IB_CM_REJ_CONSUMER_DEFINED,
+                              NULL, 0, NULL, 0);
                break;
        case IB_CM_ESTABLISHED:
                spin_unlock_irqrestore(&cm_id_priv->lock, flags);
@@ -718,8 +726,8 @@ retest:
        }
 
        cm_free_id(cm_id->local_id);
-       atomic_dec(&cm_id_priv->refcount);
-       wait_event(cm_id_priv->wait, !atomic_read(&cm_id_priv->refcount));
+       cm_deref_id(cm_id_priv);
+       wait_for_completion(&cm_id_priv->comp);
        while ((work = cm_dequeue_work(cm_id_priv)) != NULL)
                cm_free_work(work);
        if (cm_id_priv->private_data && cm_id_priv->private_data_len)
@@ -850,7 +858,7 @@ static void cm_format_req(struct cm_req_msg *req_msg,
                       param->private_data_len);
 }
 
-static inline int cm_validate_req_param(struct ib_cm_req_param *param)
+static int cm_validate_req_param(struct ib_cm_req_param *param)
 {
        /* peer-to-peer not supported */
        if (param->peer_to_peer)
@@ -999,7 +1007,7 @@ static inline int cm_is_active_peer(__be64 local_ca_guid, __be64 remote_ca_guid,
                 (be32_to_cpu(local_qpn) > be32_to_cpu(remote_qpn))));
 }
 
-static inline void cm_format_paths_from_req(struct cm_req_msg *req_msg,
+static void cm_format_paths_from_req(struct cm_req_msg *req_msg,
                                            struct ib_sa_path_rec *primary_path,
                                            struct ib_sa_path_rec *alt_path)
 {
@@ -1541,40 +1549,46 @@ static int cm_rep_handler(struct cm_work *work)
                return -EINVAL;
        }
 
+       cm_format_rep_event(work);
+
+       spin_lock_irqsave(&cm_id_priv->lock, flags);
+       switch (cm_id_priv->id.state) {
+       case IB_CM_REQ_SENT:
+       case IB_CM_MRA_REQ_RCVD:
+               break;
+       default:
+               spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+               ret = -EINVAL;
+               goto error;
+       }
+
        cm_id_priv->timewait_info->work.remote_id = rep_msg->local_comm_id;
        cm_id_priv->timewait_info->remote_ca_guid = rep_msg->local_ca_guid;
        cm_id_priv->timewait_info->remote_qpn = cm_rep_get_local_qpn(rep_msg);
 
-       spin_lock_irqsave(&cm.lock, flags);
+       spin_lock(&cm.lock);
        /* Check for duplicate REP. */
        if (cm_insert_remote_id(cm_id_priv->timewait_info)) {
-               spin_unlock_irqrestore(&cm.lock, flags);
+               spin_unlock(&cm.lock);
+               spin_unlock_irqrestore(&cm_id_priv->lock, flags);
                ret = -EINVAL;
                goto error;
        }
        /* Check for a stale connection. */
        if (cm_insert_remote_qpn(cm_id_priv->timewait_info)) {
-               spin_unlock_irqrestore(&cm.lock, flags);
+               rb_erase(&cm_id_priv->timewait_info->remote_id_node,
+                        &cm.remote_id_table);
+               cm_id_priv->timewait_info->inserted_remote_id = 0;
+               spin_unlock(&cm.lock);
+               spin_unlock_irqrestore(&cm_id_priv->lock, flags);
                cm_issue_rej(work->port, work->mad_recv_wc,
                             IB_CM_REJ_STALE_CONN, CM_MSG_RESPONSE_REP,
                             NULL, 0);
                ret = -EINVAL;
                goto error;
        }
-       spin_unlock_irqrestore(&cm.lock, flags);
-
-       cm_format_rep_event(work);
+       spin_unlock(&cm.lock);
 
-       spin_lock_irqsave(&cm_id_priv->lock, flags);
-       switch (cm_id_priv->id.state) {
-       case IB_CM_REQ_SENT:
-       case IB_CM_MRA_REQ_RCVD:
-               break;
-       default:
-               spin_unlock_irqrestore(&cm_id_priv->lock, flags);
-               ret = -EINVAL;
-               goto error;
-       }
        cm_id_priv->id.state = IB_CM_REP_RCVD;
        cm_id_priv->id.remote_id = rep_msg->local_comm_id;
        cm_id_priv->remote_qpn = cm_rep_get_local_qpn(rep_msg);
@@ -1597,7 +1611,7 @@ static int cm_rep_handler(struct cm_work *work)
                cm_deref_id(cm_id_priv);
        return 0;
 
-error: cm_cleanup_timewait(cm_id_priv->timewait_info);
+error:
        cm_deref_id(cm_id_priv);
        return ret;
 }
@@ -3157,22 +3171,6 @@ int ib_cm_init_qp_attr(struct ib_cm_id *cm_id,
 }
 EXPORT_SYMBOL(ib_cm_init_qp_attr);
 
-static __be64 cm_get_ca_guid(struct ib_device *device)
-{
-       struct ib_device_attr *device_attr;
-       __be64 guid;
-       int ret;
-
-       device_attr = kmalloc(sizeof *device_attr, GFP_KERNEL);
-       if (!device_attr)
-               return 0;
-
-       ret = ib_query_device(device, device_attr);
-       guid = ret ? 0 : device_attr->node_guid;
-       kfree(device_attr);
-       return guid;
-}
-
 static void cm_add_one(struct ib_device *device)
 {
        struct cm_device *cm_dev;
@@ -3194,9 +3192,7 @@ static void cm_add_one(struct ib_device *device)
                return;
 
        cm_dev->device = device;
-       cm_dev->ca_guid = cm_get_ca_guid(device);
-       if (!cm_dev->ca_guid)
-               goto error1;
+       cm_dev->ca_guid = device->node_guid;
 
        set_bit(IB_MGMT_METHOD_SEND, reg_req.method_mask);
        for (i = 1; i <= device->phys_port_cnt; i++) {
@@ -3211,11 +3207,11 @@ static void cm_add_one(struct ib_device *device)
                                                        cm_recv_handler,
                                                        port);
                if (IS_ERR(port->mad_agent))
-                       goto error2;
+                       goto error1;
 
                ret = ib_modify_port(device, i, 0, &port_modify);
                if (ret)
-                       goto error3;
+                       goto error2;
        }
        ib_set_client_data(device, &cm_client, cm_dev);
 
@@ -3224,9 +3220,9 @@ static void cm_add_one(struct ib_device *device)
        write_unlock_irqrestore(&cm.device_lock, flags);
        return;
 
-error3:
-       ib_unregister_mad_agent(port->mad_agent);
 error2:
+       ib_unregister_mad_agent(port->mad_agent);
+error1:
        port_modify.set_port_cap_mask = 0;
        port_modify.clr_port_cap_mask = IB_PORT_CM_SUP;
        while (--i) {
@@ -3234,7 +3230,6 @@ error2:
                ib_modify_port(device, port->port_num, 0, &port_modify);
                ib_unregister_mad_agent(port->mad_agent);
        }
-error1:
        kfree(cm_dev);
 }