linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / infiniband / core / cm.c
index 0de335b..2514de3 100644 (file)
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  *
- * $Id: cm.c 4311 2005-12-05 18:42:01Z sean.hefty $
+ * $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>
@@ -123,8 +121,8 @@ struct cm_id_private {
 
        struct rb_node service_node;
        struct rb_node sidr_id_node;
-       spinlock_t lock;        /* Do not acquire inside cm.lock */
-       struct completion comp;
+       spinlock_t lock;
+       wait_queue_head_t wait;
        atomic_t refcount;
 
        struct ib_mad_send_buf *msg;
@@ -132,7 +130,6 @@ struct cm_id_private {
        /* todo: use alternate port on send failure */
        struct cm_av av;
        struct cm_av alt_av;
-       struct ib_cm_compare_data *compare_data;
 
        void *private_data;
        __be64 tid;
@@ -162,7 +159,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))
-               complete(&cm_id_priv->comp);
+               wake_up(&cm_id_priv->wait);
 }
 
 static int cm_alloc_msg(struct cm_id_private *cm_id_priv,
@@ -254,13 +251,23 @@ static void cm_set_private_data(struct cm_id_private *cm_id_priv,
        cm_id_priv->private_data_len = private_data_len;
 }
 
-static void cm_init_av_for_response(struct cm_port *port, struct ib_wc *wc,
-                                   struct ib_grh *grh, struct cm_av *av)
+static void cm_set_ah_attr(struct ib_ah_attr *ah_attr, u8 port_num,
+                          u16 dlid, u8 sl, u16 src_path_bits)
+{
+       memset(ah_attr, 0, sizeof ah_attr);
+       ah_attr->dlid = dlid;
+       ah_attr->sl = sl;
+       ah_attr->src_path_bits = src_path_bits;
+       ah_attr->port_num = port_num;
+}
+
+static void cm_init_av_for_response(struct cm_port *port,
+                                   struct ib_wc *wc, struct cm_av *av)
 {
        av->port = port;
        av->pkey_index = wc->pkey_index;
-       ib_init_ah_from_wc(port->cm_dev->device, port->port_num, wc,
-                          grh, &av->ah_attr);
+       cm_set_ah_attr(&av->ah_attr, port->port_num, wc->slid,
+                      wc->sl, wc->dlid_path_bits);
 }
 
 static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av)
@@ -290,8 +297,9 @@ static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av)
                return ret;
 
        av->port = port;
-       ib_init_ah_from_path(cm_dev->device, port->port_num, path,
-                            &av->ah_attr);
+       cm_set_ah_attr(&av->ah_attr, av->port->port_num,
+                      be16_to_cpu(path->dlid), path->sl,
+                      be16_to_cpu(path->slid) & 0x7F);
        av->packet_life_time = path->packet_life_time;
        return 0;
 }
@@ -347,41 +355,6 @@ static struct cm_id_private * cm_acquire_id(__be32 local_id, __be32 remote_id)
        return cm_id_priv;
 }
 
-static void cm_mask_copy(u8 *dst, u8 *src, u8 *mask)
-{
-       int i;
-
-       for (i = 0; i < IB_CM_COMPARE_SIZE / sizeof(unsigned long); i++)
-               ((unsigned long *) dst)[i] = ((unsigned long *) src)[i] &
-                                            ((unsigned long *) mask)[i];
-}
-
-static int cm_compare_data(struct ib_cm_compare_data *src_data,
-                          struct ib_cm_compare_data *dst_data)
-{
-       u8 src[IB_CM_COMPARE_SIZE];
-       u8 dst[IB_CM_COMPARE_SIZE];
-
-       if (!src_data || !dst_data)
-               return 0;
-
-       cm_mask_copy(src, src_data->data, dst_data->mask);
-       cm_mask_copy(dst, dst_data->data, src_data->mask);
-       return memcmp(src, dst, IB_CM_COMPARE_SIZE);
-}
-
-static int cm_compare_private_data(u8 *private_data,
-                                  struct ib_cm_compare_data *dst_data)
-{
-       u8 src[IB_CM_COMPARE_SIZE];
-
-       if (!dst_data)
-               return 0;
-
-       cm_mask_copy(src, private_data, dst_data->mask);
-       return memcmp(src, dst_data->data, IB_CM_COMPARE_SIZE);
-}
-
 static struct cm_id_private * cm_insert_listen(struct cm_id_private *cm_id_priv)
 {
        struct rb_node **link = &cm.listen_service_table.rb_node;
@@ -389,18 +362,14 @@ static struct cm_id_private * cm_insert_listen(struct cm_id_private *cm_id_priv)
        struct cm_id_private *cur_cm_id_priv;
        __be64 service_id = cm_id_priv->id.service_id;
        __be64 service_mask = cm_id_priv->id.service_mask;
-       int data_cmp;
 
        while (*link) {
                parent = *link;
                cur_cm_id_priv = rb_entry(parent, struct cm_id_private,
                                          service_node);
-               data_cmp = cm_compare_data(cm_id_priv->compare_data,
-                                          cur_cm_id_priv->compare_data);
                if ((cur_cm_id_priv->id.service_mask & service_id) ==
                    (service_mask & cur_cm_id_priv->id.service_id) &&
-                   (cm_id_priv->id.device == cur_cm_id_priv->id.device) &&
-                   !data_cmp)
+                   (cm_id_priv->id.device == cur_cm_id_priv->id.device))
                        return cur_cm_id_priv;
 
                if (cm_id_priv->id.device < cur_cm_id_priv->id.device)
@@ -409,10 +378,6 @@ static struct cm_id_private * cm_insert_listen(struct cm_id_private *cm_id_priv)
                        link = &(*link)->rb_right;
                else if (service_id < cur_cm_id_priv->id.service_id)
                        link = &(*link)->rb_left;
-               else if (service_id > cur_cm_id_priv->id.service_id)
-                       link = &(*link)->rb_right;
-               else if (data_cmp < 0)
-                       link = &(*link)->rb_left;
                else
                        link = &(*link)->rb_right;
        }
@@ -422,20 +387,16 @@ static struct cm_id_private * cm_insert_listen(struct cm_id_private *cm_id_priv)
 }
 
 static struct cm_id_private * cm_find_listen(struct ib_device *device,
-                                            __be64 service_id,
-                                            u8 *private_data)
+                                            __be64 service_id)
 {
        struct rb_node *node = cm.listen_service_table.rb_node;
        struct cm_id_private *cm_id_priv;
-       int data_cmp;
 
        while (node) {
                cm_id_priv = rb_entry(node, struct cm_id_private, service_node);
-               data_cmp = cm_compare_private_data(private_data,
-                                                  cm_id_priv->compare_data);
                if ((cm_id_priv->id.service_mask & service_id) ==
                     cm_id_priv->id.service_id &&
-                   (cm_id_priv->id.device == device) && !data_cmp)
+                   (cm_id_priv->id.device == device))
                        return cm_id_priv;
 
                if (device < cm_id_priv->id.device)
@@ -444,10 +405,6 @@ static struct cm_id_private * cm_find_listen(struct ib_device *device,
                        node = node->rb_right;
                else if (service_id < cm_id_priv->id.service_id)
                        node = node->rb_left;
-               else if (service_id > cm_id_priv->id.service_id)
-                       node = node->rb_right;
-               else if (data_cmp < 0)
-                       node = node->rb_left;
                else
                        node = node->rb_right;
        }
@@ -602,7 +559,7 @@ struct ib_cm_id *ib_create_cm_id(struct ib_device *device,
                goto error;
 
        spin_lock_init(&cm_id_priv->lock);
-       init_completion(&cm_id_priv->comp);
+       init_waitqueue_head(&cm_id_priv->wait);
        INIT_LIST_HEAD(&cm_id_priv->work_list);
        atomic_set(&cm_id_priv->work_count, -1);
        atomic_set(&cm_id_priv->refcount, 1);
@@ -701,7 +658,7 @@ static void cm_reset_to_idle(struct cm_id_private *cm_id_priv)
        }
 }
 
-static void cm_destroy_id(struct ib_cm_id *cm_id, int err)
+void ib_destroy_cm_id(struct ib_cm_id *cm_id)
 {
        struct cm_id_private *cm_id_priv;
        struct cm_work *work;
@@ -735,22 +692,12 @@ retest:
                               sizeof cm_id_priv->av.port->cm_dev->ca_guid,
                               NULL, 0);
                break;
-       case IB_CM_REQ_RCVD:
-               if (err == -ENOMEM) {
-                       /* Do not reject to allow future retries. */
-                       cm_reset_to_idle(cm_id_priv);
-                       spin_unlock_irqrestore(&cm_id_priv->lock, flags);
-               } else {
-                       spin_unlock_irqrestore(&cm_id_priv->lock, flags);
-                       ib_send_cm_rej(cm_id, IB_CM_REJ_CONSUMER_DEFINED,
-                                      NULL, 0, NULL, 0);
-               }
-               break;
        case IB_CM_MRA_REQ_RCVD:
        case IB_CM_REP_SENT:
        case IB_CM_MRA_REP_RCVD:
                ib_cancel_mad(cm_id_priv->av.port->mad_agent, cm_id_priv->msg);
                /* Fall through */
+       case IB_CM_REQ_RCVD:
        case IB_CM_MRA_REQ_SENT:
        case IB_CM_REP_RCVD:
        case IB_CM_MRA_REP_SENT:
@@ -777,23 +724,19 @@ retest:
        }
 
        cm_free_id(cm_id->local_id);
-       cm_deref_id(cm_id_priv);
-       wait_for_completion(&cm_id_priv->comp);
+       atomic_dec(&cm_id_priv->refcount);
+       wait_event(cm_id_priv->wait, !atomic_read(&cm_id_priv->refcount));
        while ((work = cm_dequeue_work(cm_id_priv)) != NULL)
                cm_free_work(work);
-       kfree(cm_id_priv->compare_data);
-       kfree(cm_id_priv->private_data);
+       if (cm_id_priv->private_data && cm_id_priv->private_data_len)
+               kfree(cm_id_priv->private_data);
        kfree(cm_id_priv);
 }
-
-void ib_destroy_cm_id(struct ib_cm_id *cm_id)
-{
-       cm_destroy_id(cm_id, 0);
-}
 EXPORT_SYMBOL(ib_destroy_cm_id);
 
-int ib_cm_listen(struct ib_cm_id *cm_id, __be64 service_id, __be64 service_mask,
-                struct ib_cm_compare_data *compare_data)
+int ib_cm_listen(struct ib_cm_id *cm_id,
+                __be64 service_id,
+                __be64 service_mask)
 {
        struct cm_id_private *cm_id_priv, *cur_cm_id_priv;
        unsigned long flags;
@@ -807,19 +750,7 @@ int ib_cm_listen(struct ib_cm_id *cm_id, __be64 service_id, __be64 service_mask,
                return -EINVAL;
 
        cm_id_priv = container_of(cm_id, struct cm_id_private, id);
-       if (cm_id->state != IB_CM_IDLE)
-               return -EINVAL;
-
-       if (compare_data) {
-               cm_id_priv->compare_data = kzalloc(sizeof *compare_data,
-                                                  GFP_KERNEL);
-               if (!cm_id_priv->compare_data)
-                       return -ENOMEM;
-               cm_mask_copy(cm_id_priv->compare_data->data,
-                            compare_data->data, compare_data->mask);
-               memcpy(cm_id_priv->compare_data->mask, compare_data->mask,
-                      IB_CM_COMPARE_SIZE);
-       }
+       BUG_ON(cm_id->state != IB_CM_IDLE);
 
        cm_id->state = IB_CM_LISTEN;
 
@@ -836,8 +767,6 @@ int ib_cm_listen(struct ib_cm_id *cm_id, __be64 service_id, __be64 service_mask,
 
        if (cur_cm_id_priv) {
                cm_id->state = IB_CM_IDLE;
-               kfree(cm_id_priv->compare_data);
-               cm_id_priv->compare_data = NULL;
                ret = -EBUSY;
        }
        return ret;
@@ -975,10 +904,8 @@ int ib_send_cm_req(struct ib_cm_id *cm_id,
 
        cm_id_priv->timewait_info = cm_create_timewait_info(cm_id_priv->
                                                            id.local_id);
-       if (IS_ERR(cm_id_priv->timewait_info)) {
-               ret = PTR_ERR(cm_id_priv->timewait_info);
+       if (IS_ERR(cm_id_priv->timewait_info))
                goto out;
-       }
 
        ret = cm_init_av_by_path(param->primary_path, &cm_id_priv->av);
        if (ret)
@@ -1180,7 +1107,7 @@ static void cm_process_work(struct cm_id_private *cm_id_priv,
        }
        cm_deref_id(cm_id_priv);
        if (ret)
-               cm_destroy_id(&cm_id_priv->id, ret);
+               ib_destroy_cm_id(&cm_id_priv->id);
 }
 
 static void cm_format_mra(struct cm_mra_msg *mra_msg,
@@ -1312,8 +1239,7 @@ static struct cm_id_private * cm_match_req(struct cm_work *work,
 
        /* Find matching listen request. */
        listen_cm_id_priv = cm_find_listen(cm_id_priv->id.device,
-                                          req_msg->service_id,
-                                          req_msg->private_data);
+                                          req_msg->service_id);
        if (!listen_cm_id_priv) {
                spin_unlock_irqrestore(&cm.lock, flags);
                cm_issue_rej(work->port, work->mad_recv_wc,
@@ -1348,7 +1274,6 @@ static int cm_req_handler(struct cm_work *work)
        cm_id_priv = container_of(cm_id, struct cm_id_private, id);
        cm_id_priv->id.remote_id = req_msg->local_comm_id;
        cm_init_av_for_response(work->port, work->mad_recv_wc->wc,
-                               work->mad_recv_wc->recv_buf.grh,
                                &cm_id_priv->av);
        cm_id_priv->timewait_info = cm_create_timewait_info(cm_id_priv->
                                                            id.local_id);
@@ -1622,46 +1547,40 @@ 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(&cm.lock);
+       spin_lock_irqsave(&cm.lock, flags);
        /* Check for duplicate REP. */
        if (cm_insert_remote_id(cm_id_priv->timewait_info)) {
-               spin_unlock(&cm.lock);
-               spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+               spin_unlock_irqrestore(&cm.lock, flags);
                ret = -EINVAL;
                goto error;
        }
        /* Check for a stale connection. */
        if (cm_insert_remote_qpn(cm_id_priv->timewait_info)) {
-               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);
+               spin_unlock_irqrestore(&cm.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(&cm.lock);
+       spin_unlock_irqrestore(&cm.lock, flags);
 
+       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->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);
@@ -1684,7 +1603,7 @@ static int cm_rep_handler(struct cm_work *work)
                cm_deref_id(cm_id_priv);
        return 0;
 
-error:
+error: cm_cleanup_timewait(cm_id_priv->timewait_info);
        cm_deref_id(cm_id_priv);
        return ret;
 }
@@ -2622,7 +2541,7 @@ static void cm_format_sidr_req(struct cm_sidr_req_msg *sidr_req_msg,
        cm_format_mad_hdr(&sidr_req_msg->hdr, CM_SIDR_REQ_ATTR_ID,
                          cm_form_tid(cm_id_priv, CM_MSG_SEQUENCE_SIDR));
        sidr_req_msg->request_id = cm_id_priv->id.local_id;
-       sidr_req_msg->pkey = cpu_to_be16(param->path->pkey);
+       sidr_req_msg->pkey = cpu_to_be16(param->pkey);
        sidr_req_msg->service_id = param->service_id;
 
        if (param->private_data && param->private_data_len)
@@ -2714,7 +2633,6 @@ static int cm_sidr_req_handler(struct cm_work *work)
        cm_id_priv->av.dgid.global.subnet_prefix = cpu_to_be64(wc->slid);
        cm_id_priv->av.dgid.global.interface_id = 0;
        cm_init_av_for_response(work->port, work->mad_recv_wc->wc,
-                               work->mad_recv_wc->recv_buf.grh,
                                &cm_id_priv->av);
        cm_id_priv->id.remote_id = sidr_req_msg->request_id;
        cm_id_priv->id.state = IB_CM_SIDR_REQ_RCVD;
@@ -2728,8 +2646,7 @@ static int cm_sidr_req_handler(struct cm_work *work)
                goto out; /* Duplicate message. */
        }
        cur_cm_id_priv = cm_find_listen(cm_id->device,
-                                       sidr_req_msg->service_id,
-                                       sidr_req_msg->private_data);
+                                       sidr_req_msg->service_id);
        if (!cur_cm_id_priv) {
                rb_erase(&cm_id_priv->sidr_id_node, &cm.remote_sidr_table);
                spin_unlock_irqrestore(&cm.lock, flags);
@@ -3169,7 +3086,6 @@ static int cm_init_qp_rtr_attr(struct cm_id_private *cm_id_priv,
                }
                if (cm_id_priv->alt_av.ah_attr.dlid) {
                        *qp_attr_mask |= IB_QP_ALT_PATH;
-                       qp_attr->alt_port_num = cm_id_priv->alt_av.port->port_num;
                        qp_attr->alt_ah_attr = cm_id_priv->alt_av.ah_attr;
                }
                ret = 0;
@@ -3367,6 +3283,7 @@ error:
 
 static void __exit ib_cm_cleanup(void)
 {
+       flush_workqueue(cm.wq);
        destroy_workqueue(cm.wq);
        ib_unregister_client(&cm_client);
        idr_destroy(&cm.local_id_table);