IB/core: Ethernet L2 attributes in verbs/cm structures
[linux-2.6-block.git] / drivers / infiniband / core / cm.c
index f2ef7ef0f36f7c19df3a2eb53fb3e3b532716a1b..c6d543c3ef52b90a516d85d2b27f41f6dfc590a5 100644 (file)
@@ -47,6 +47,7 @@
 #include <linux/sysfs.h>
 #include <linux/workqueue.h>
 #include <linux/kdev_t.h>
+#include <linux/etherdevice.h>
 
 #include <rdma/ib_cache.h>
 #include <rdma/ib_cm.h>
@@ -177,6 +178,8 @@ struct cm_av {
        struct ib_ah_attr ah_attr;
        u16 pkey_index;
        u8 timeout;
+       u8  valid;
+       u8  smac[ETH_ALEN];
 };
 
 struct cm_work {
@@ -346,6 +349,23 @@ static void cm_init_av_for_response(struct cm_port *port, struct ib_wc *wc,
                           grh, &av->ah_attr);
 }
 
+int ib_update_cm_av(struct ib_cm_id *id, const u8 *smac, const u8 *alt_smac)
+{
+       struct cm_id_private *cm_id_priv;
+
+       cm_id_priv = container_of(id, struct cm_id_private, id);
+
+       if (smac != NULL)
+               memcpy(cm_id_priv->av.smac, smac, sizeof(cm_id_priv->av.smac));
+
+       if (alt_smac != NULL)
+               memcpy(cm_id_priv->alt_av.smac, alt_smac,
+                      sizeof(cm_id_priv->alt_av.smac));
+
+       return 0;
+}
+EXPORT_SYMBOL(ib_update_cm_av);
+
 static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av)
 {
        struct cm_device *cm_dev;
@@ -376,6 +396,9 @@ static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av)
        ib_init_ah_from_path(cm_dev->ib_device, port->port_num, path,
                             &av->ah_attr);
        av->timeout = path->packet_life_time + 1;
+       memcpy(av->smac, path->smac, sizeof(av->smac));
+
+       av->valid = 1;
        return 0;
 }
 
@@ -1554,6 +1577,9 @@ static int cm_req_handler(struct cm_work *work)
 
        cm_process_routed_req(req_msg, work->mad_recv_wc->wc);
        cm_format_paths_from_req(req_msg, &work->path[0], &work->path[1]);
+
+       memcpy(work->path[0].dmac, cm_id_priv->av.ah_attr.dmac, ETH_ALEN);
+       work->path[0].vlan_id = cm_id_priv->av.ah_attr.vlan_id;
        ret = cm_init_av_by_path(&work->path[0], &cm_id_priv->av);
        if (ret) {
                ib_get_cached_gid(work->port->cm_dev->ib_device,
@@ -3500,6 +3526,30 @@ static int cm_init_qp_rtr_attr(struct cm_id_private *cm_id_priv,
                *qp_attr_mask = IB_QP_STATE | IB_QP_AV | IB_QP_PATH_MTU |
                                IB_QP_DEST_QPN | IB_QP_RQ_PSN;
                qp_attr->ah_attr = cm_id_priv->av.ah_attr;
+               if (!cm_id_priv->av.valid)
+                       return -EINVAL;
+               if (cm_id_priv->av.ah_attr.vlan_id != 0xffff) {
+                       qp_attr->vlan_id = cm_id_priv->av.ah_attr.vlan_id;
+                       *qp_attr_mask |= IB_QP_VID;
+               }
+               if (!is_zero_ether_addr(cm_id_priv->av.smac)) {
+                       memcpy(qp_attr->smac, cm_id_priv->av.smac,
+                              sizeof(qp_attr->smac));
+                       *qp_attr_mask |= IB_QP_SMAC;
+               }
+               if (cm_id_priv->alt_av.valid) {
+                       if (cm_id_priv->alt_av.ah_attr.vlan_id != 0xffff) {
+                               qp_attr->alt_vlan_id =
+                                       cm_id_priv->alt_av.ah_attr.vlan_id;
+                               *qp_attr_mask |= IB_QP_ALT_VID;
+                       }
+                       if (!is_zero_ether_addr(cm_id_priv->alt_av.smac)) {
+                               memcpy(qp_attr->alt_smac,
+                                      cm_id_priv->alt_av.smac,
+                                      sizeof(qp_attr->alt_smac));
+                               *qp_attr_mask |= IB_QP_ALT_SMAC;
+                       }
+               }
                qp_attr->path_mtu = cm_id_priv->path_mtu;
                qp_attr->dest_qp_num = be32_to_cpu(cm_id_priv->remote_qpn);
                qp_attr->rq_psn = be32_to_cpu(cm_id_priv->rq_psn);