IB/mlx4: Implement query QP
[linux-2.6-block.git] / drivers / infiniband / hw / mlx4 / qp.c
index 95d4a9d6994c50b381b2e62f0c3cfec699210b0a..40042184ad589b0b8b9d124b6622637f11251f02 100644 (file)
@@ -192,6 +192,8 @@ static int send_wqe_overhead(enum ib_qp_type type)
        case IB_QPT_GSI:
                return sizeof (struct mlx4_wqe_ctrl_seg) +
                        ALIGN(MLX4_IB_UD_HEADER_SIZE +
+                             DIV_ROUND_UP(MLX4_IB_UD_HEADER_SIZE,
+                                          MLX4_INLINE_ALIGN) *
                              sizeof (struct mlx4_wqe_inline_seg),
                              sizeof (struct mlx4_wqe_data_seg)) +
                        ALIGN(4 +
@@ -603,24 +605,6 @@ int mlx4_ib_destroy_qp(struct ib_qp *qp)
        return 0;
 }
 
-static void init_port(struct mlx4_ib_dev *dev, int port)
-{
-       struct mlx4_init_port_param param;
-       int err;
-
-       memset(&param, 0, sizeof param);
-
-       param.port_width_cap = dev->dev->caps.port_width_cap;
-       param.vl_cap         = dev->dev->caps.vl_cap;
-       param.mtu            = ib_mtu_enum_to_int(dev->dev->caps.mtu_cap);
-       param.max_gid        = dev->dev->caps.gid_table_len;
-       param.max_pkey       = dev->dev->caps.pkey_table_len;
-
-       err = mlx4_INIT_PORT(dev->dev, &param, port);
-       if (err)
-               printk(KERN_WARNING "INIT_PORT failed, return code %d.\n", err);
-}
-
 static int to_mlx4_st(enum ib_qp_type type)
 {
        switch (type) {
@@ -694,9 +678,9 @@ static int mlx4_set_path(struct mlx4_ib_dev *dev, const struct ib_ah_attr *ah,
        path->counter_index = 0xff;
 
        if (ah->ah_flags & IB_AH_GRH) {
-               if (ah->grh.sgid_index >= dev->dev->caps.gid_table_len) {
+               if (ah->grh.sgid_index >= dev->dev->caps.gid_table_len[port]) {
                        printk(KERN_ERR "sgid_index (%u) too large. max is %d\n",
-                              ah->grh.sgid_index, dev->dev->caps.gid_table_len - 1);
+                              ah->grh.sgid_index, dev->dev->caps.gid_table_len[port] - 1);
                        return -1;
                }
 
@@ -812,13 +796,14 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
        }
 
        if (attr_mask & IB_QP_ALT_PATH) {
-               if (attr->alt_pkey_index >= dev->dev->caps.pkey_table_len)
-                       return -EINVAL;
-
                if (attr->alt_port_num == 0 ||
                    attr->alt_port_num > dev->dev->caps.num_ports)
                        return -EINVAL;
 
+               if (attr->alt_pkey_index >=
+                   dev->dev->caps.pkey_table_len[attr->alt_port_num])
+                       return -EINVAL;
+
                if (mlx4_set_path(dev, &attr->alt_ah_attr, &context->alt_path,
                                  attr->alt_port_num))
                        return -EINVAL;
@@ -949,7 +934,9 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
         */
        if (is_qp0(dev, qp)) {
                if (cur_state != IB_QPS_RTR && new_state == IB_QPS_RTR)
-                       init_port(dev, qp->port);
+                       if (mlx4_INIT_PORT(dev->dev, qp->port))
+                               printk(KERN_WARNING "INIT_PORT failed for port %d\n",
+                                      qp->port);
 
                if (cur_state != IB_QPS_RESET && cur_state != IB_QPS_ERR &&
                    (new_state == IB_QPS_RESET || new_state == IB_QPS_ERR))
@@ -1012,16 +999,17 @@ int mlx4_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
        if (!ib_modify_qp_is_ok(cur_state, new_state, ibqp->qp_type, attr_mask))
                goto out;
 
-       if ((attr_mask & IB_QP_PKEY_INDEX) &&
-            attr->pkey_index >= dev->dev->caps.pkey_table_len) {
-               goto out;
-       }
-
        if ((attr_mask & IB_QP_PORT) &&
            (attr->port_num == 0 || attr->port_num > dev->dev->caps.num_ports)) {
                goto out;
        }
 
+       if (attr_mask & IB_QP_PKEY_INDEX) {
+               int p = attr_mask & IB_QP_PORT ? attr->port_num : qp->port;
+               if (attr->pkey_index >= dev->dev->caps.pkey_table_len[p])
+                       goto out;
+       }
+
        if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC &&
            attr->max_rd_atomic > dev->dev->caps.max_qp_init_rdma) {
                goto out;
@@ -1063,6 +1051,7 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr,
        u16 pkey;
        int send_size;
        int header_size;
+       int spc;
        int i;
 
        send_size = 0;
@@ -1138,10 +1127,43 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr,
                printk("\n");
        }
 
-       inl->byte_count = cpu_to_be32(1 << 31 | header_size);
-       memcpy(inl + 1, sqp->header_buf, header_size);
+       /*
+        * Inline data segments may not cross a 64 byte boundary.  If
+        * our UD header is bigger than the space available up to the
+        * next 64 byte boundary in the WQE, use two inline data
+        * segments to hold the UD header.
+        */
+       spc = MLX4_INLINE_ALIGN -
+               ((unsigned long) (inl + 1) & (MLX4_INLINE_ALIGN - 1));
+       if (header_size <= spc) {
+               inl->byte_count = cpu_to_be32(1 << 31 | header_size);
+               memcpy(inl + 1, sqp->header_buf, header_size);
+               i = 1;
+       } else {
+               inl->byte_count = cpu_to_be32(1 << 31 | spc);
+               memcpy(inl + 1, sqp->header_buf, spc);
 
-       return ALIGN(sizeof (struct mlx4_wqe_inline_seg) + header_size, 16);
+               inl = (void *) (inl + 1) + spc;
+               memcpy(inl + 1, sqp->header_buf + spc, header_size - spc);
+               /*
+                * Need a barrier here to make sure all the data is
+                * visible before the byte_count field is set.
+                * Otherwise the HCA prefetcher could grab the 64-byte
+                * chunk with this inline segment and get a valid (!=
+                * 0xffffffff) byte count but stale data, and end up
+                * generating a packet with bad headers.
+                *
+                * The first inline segment's byte_count field doesn't
+                * need a barrier, because it comes after a
+                * control/MLX segment and therefore is at an offset
+                * of 16 mod 64.
+                */
+               wmb();
+               inl->byte_count = cpu_to_be32(1 << 31 | (header_size - spc));
+               i = 2;
+       }
+
+       return ALIGN(i * sizeof (struct mlx4_wqe_inline_seg) + header_size, 16);
 }
 
 static int mlx4_wq_overflow(struct mlx4_ib_wq *wq, int nreq, struct ib_cq *ib_cq)
@@ -1433,3 +1455,140 @@ out:
 
        return err;
 }
+
+static inline enum ib_qp_state to_ib_qp_state(enum mlx4_qp_state mlx4_state)
+{
+       switch (mlx4_state) {
+       case MLX4_QP_STATE_RST:      return IB_QPS_RESET;
+       case MLX4_QP_STATE_INIT:     return IB_QPS_INIT;
+       case MLX4_QP_STATE_RTR:      return IB_QPS_RTR;
+       case MLX4_QP_STATE_RTS:      return IB_QPS_RTS;
+       case MLX4_QP_STATE_SQ_DRAINING:
+       case MLX4_QP_STATE_SQD:      return IB_QPS_SQD;
+       case MLX4_QP_STATE_SQER:     return IB_QPS_SQE;
+       case MLX4_QP_STATE_ERR:      return IB_QPS_ERR;
+       default:                     return -1;
+       }
+}
+
+static inline enum ib_mig_state to_ib_mig_state(int mlx4_mig_state)
+{
+       switch (mlx4_mig_state) {
+       case MLX4_QP_PM_ARMED:          return IB_MIG_ARMED;
+       case MLX4_QP_PM_REARM:          return IB_MIG_REARM;
+       case MLX4_QP_PM_MIGRATED:       return IB_MIG_MIGRATED;
+       default: return -1;
+       }
+}
+
+static int to_ib_qp_access_flags(int mlx4_flags)
+{
+       int ib_flags = 0;
+
+       if (mlx4_flags & MLX4_QP_BIT_RRE)
+               ib_flags |= IB_ACCESS_REMOTE_READ;
+       if (mlx4_flags & MLX4_QP_BIT_RWE)
+               ib_flags |= IB_ACCESS_REMOTE_WRITE;
+       if (mlx4_flags & MLX4_QP_BIT_RAE)
+               ib_flags |= IB_ACCESS_REMOTE_ATOMIC;
+
+       return ib_flags;
+}
+
+static void to_ib_ah_attr(struct mlx4_dev *dev, struct ib_ah_attr *ib_ah_attr,
+                               struct mlx4_qp_path *path)
+{
+       memset(ib_ah_attr, 0, sizeof *path);
+       ib_ah_attr->port_num      = path->sched_queue & 0x40 ? 2 : 1;
+
+       if (ib_ah_attr->port_num == 0 || ib_ah_attr->port_num > dev->caps.num_ports)
+               return;
+
+       ib_ah_attr->dlid          = be16_to_cpu(path->rlid);
+       ib_ah_attr->sl            = (path->sched_queue >> 2) & 0xf;
+       ib_ah_attr->src_path_bits = path->grh_mylmc & 0x7f;
+       ib_ah_attr->static_rate   = path->static_rate ? path->static_rate - 5 : 0;
+       ib_ah_attr->ah_flags      = (path->grh_mylmc & (1 << 7)) ? IB_AH_GRH : 0;
+       if (ib_ah_attr->ah_flags) {
+               ib_ah_attr->grh.sgid_index = path->mgid_index;
+               ib_ah_attr->grh.hop_limit  = path->hop_limit;
+               ib_ah_attr->grh.traffic_class =
+                       (be32_to_cpu(path->tclass_flowlabel) >> 20) & 0xff;
+               ib_ah_attr->grh.flow_label =
+                       be32_to_cpu(path->tclass_flowlabel) & 0xffffff;
+               memcpy(ib_ah_attr->grh.dgid.raw,
+                       path->rgid, sizeof ib_ah_attr->grh.dgid.raw);
+       }
+}
+
+int mlx4_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, int qp_attr_mask,
+                    struct ib_qp_init_attr *qp_init_attr)
+{
+       struct mlx4_ib_dev *dev = to_mdev(ibqp->device);
+       struct mlx4_ib_qp *qp = to_mqp(ibqp);
+       struct mlx4_qp_context context;
+       int mlx4_state;
+       int err;
+
+       if (qp->state == IB_QPS_RESET) {
+               qp_attr->qp_state = IB_QPS_RESET;
+               goto done;
+       }
+
+       err = mlx4_qp_query(dev->dev, &qp->mqp, &context);
+       if (err)
+               return -EINVAL;
+
+       mlx4_state = be32_to_cpu(context.flags) >> 28;
+
+       qp_attr->qp_state            = to_ib_qp_state(mlx4_state);
+       qp_attr->path_mtu            = context.mtu_msgmax >> 5;
+       qp_attr->path_mig_state      =
+               to_ib_mig_state((be32_to_cpu(context.flags) >> 11) & 0x3);
+       qp_attr->qkey                = be32_to_cpu(context.qkey);
+       qp_attr->rq_psn              = be32_to_cpu(context.rnr_nextrecvpsn) & 0xffffff;
+       qp_attr->sq_psn              = be32_to_cpu(context.next_send_psn) & 0xffffff;
+       qp_attr->dest_qp_num         = be32_to_cpu(context.remote_qpn) & 0xffffff;
+       qp_attr->qp_access_flags     =
+               to_ib_qp_access_flags(be32_to_cpu(context.params2));
+
+       if (qp->ibqp.qp_type == IB_QPT_RC || qp->ibqp.qp_type == IB_QPT_UC) {
+               to_ib_ah_attr(dev->dev, &qp_attr->ah_attr, &context.pri_path);
+               to_ib_ah_attr(dev->dev, &qp_attr->alt_ah_attr, &context.alt_path);
+               qp_attr->alt_pkey_index = context.alt_path.pkey_index & 0x7f;
+               qp_attr->alt_port_num   = qp_attr->alt_ah_attr.port_num;
+       }
+
+       qp_attr->pkey_index = context.pri_path.pkey_index & 0x7f;
+       qp_attr->port_num   = context.pri_path.sched_queue & 0x40 ? 2 : 1;
+
+       /* qp_attr->en_sqd_async_notify is only applicable in modify qp */
+       qp_attr->sq_draining = mlx4_state == MLX4_QP_STATE_SQ_DRAINING;
+
+       qp_attr->max_rd_atomic = 1 << ((be32_to_cpu(context.params1) >> 21) & 0x7);
+
+       qp_attr->max_dest_rd_atomic =
+               1 << ((be32_to_cpu(context.params2) >> 21) & 0x7);
+       qp_attr->min_rnr_timer      =
+               (be32_to_cpu(context.rnr_nextrecvpsn) >> 24) & 0x1f;
+       qp_attr->timeout            = context.pri_path.ackto >> 3;
+       qp_attr->retry_cnt          = (be32_to_cpu(context.params1) >> 16) & 0x7;
+       qp_attr->rnr_retry          = (be32_to_cpu(context.params1) >> 13) & 0x7;
+       qp_attr->alt_timeout        = context.alt_path.ackto >> 3;
+
+done:
+       qp_attr->cur_qp_state        = qp_attr->qp_state;
+       if (!ibqp->uobject) {
+               qp_attr->cap.max_send_wr     = qp->sq.wqe_cnt;
+               qp_attr->cap.max_recv_wr     = qp->rq.wqe_cnt;
+               qp_attr->cap.max_send_sge    = qp->sq.max_gs;
+               qp_attr->cap.max_recv_sge    = qp->rq.max_gs;
+               qp_attr->cap.max_inline_data = (1 << qp->sq.wqe_shift) -
+                       send_wqe_overhead(qp->ibqp.qp_type) -
+                       sizeof (struct mlx4_wqe_inline_seg);
+               qp_init_attr->cap            = qp_attr->cap;
+       }
+
+       return 0;
+}
+