[IB] uverbs: have kernel return QP capabilities
[linux-block.git] / drivers / infiniband / hw / mthca / mthca_qp.c
index 0164b84d4ec643062892122cf6ebf567d92ec5f1..7f39af44b27432de3f01b0faa803f9ed9bc8ec52 100644 (file)
@@ -36,6 +36,8 @@
  */
 
 #include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
 
 #include <rdma/ib_verbs.h>
 #include <rdma/ib_cache.h>
@@ -220,6 +222,15 @@ static void *get_send_wqe(struct mthca_qp *qp, int n)
                         (PAGE_SIZE - 1));
 }
 
+static void mthca_wq_init(struct mthca_wq *wq)
+{
+       spin_lock_init(&wq->lock);
+       wq->next_ind  = 0;
+       wq->last_comp = wq->max - 1;
+       wq->head      = 0;
+       wq->tail      = 0;
+}
+
 void mthca_qp_event(struct mthca_dev *dev, u32 qpn,
                    enum ib_event_type event_type)
 {
@@ -329,8 +340,7 @@ static const struct {
                                [UC]  = (IB_QP_AV                  |
                                         IB_QP_PATH_MTU            |
                                         IB_QP_DEST_QPN            |
-                                        IB_QP_RQ_PSN              |
-                                        IB_QP_MAX_DEST_RD_ATOMIC),
+                                        IB_QP_RQ_PSN),
                                [RC]  = (IB_QP_AV                  |
                                         IB_QP_PATH_MTU            |
                                         IB_QP_DEST_QPN            |
@@ -359,8 +369,7 @@ static const struct {
                        .trans = MTHCA_TRANS_RTR2RTS,
                        .req_param = {
                                [UD]  = IB_QP_SQ_PSN,
-                               [UC]  = (IB_QP_SQ_PSN            |
-                                        IB_QP_MAX_QP_RD_ATOMIC),
+                               [UC]  = IB_QP_SQ_PSN,
                                [RC]  = (IB_QP_TIMEOUT           |
                                         IB_QP_RETRY_CNT         |
                                         IB_QP_RNR_RETRY         |
@@ -437,8 +446,6 @@ static const struct {
                                [UD]  = (IB_QP_PKEY_INDEX            |
                                         IB_QP_QKEY),
                                [UC]  = (IB_QP_AV                    |
-                                        IB_QP_MAX_QP_RD_ATOMIC      |
-                                        IB_QP_MAX_DEST_RD_ATOMIC    |
                                         IB_QP_CUR_STATE             |
                                         IB_QP_ALT_PATH              |
                                         IB_QP_ACCESS_FLAGS          |
@@ -469,7 +476,7 @@ static const struct {
                        .opt_param = {
                                [UD]  = (IB_QP_CUR_STATE             |
                                         IB_QP_QKEY),
-                               [UC]  = (IB_QP_CUR_STATE),
+                               [UC]  = IB_QP_CUR_STATE,
                                [RC]  = (IB_QP_CUR_STATE             |
                                         IB_QP_MIN_RNR_TIMER),
                                [MLX] = (IB_QP_CUR_STATE             |
@@ -577,6 +584,13 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask)
                return -EINVAL;
        }
 
+       if ((attr_mask & IB_QP_PKEY_INDEX) && 
+            attr->pkey_index >= dev->limits.pkey_table_len) {
+               mthca_dbg(dev, "PKey index (%u) too large. max is %d\n",
+                         attr->pkey_index,dev->limits.pkey_table_len-1); 
+               return -EINVAL;
+       }
+
        mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
        if (IS_ERR(mailbox))
                return PTR_ERR(mailbox);
@@ -677,7 +691,7 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask)
        }
 
        if (attr_mask & IB_QP_TIMEOUT) {
-               qp_context->pri_path.ackto = attr->timeout;
+               qp_context->pri_path.ackto = attr->timeout << 3;
                qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_ACK_TIMEOUT);
        }
 
@@ -833,8 +847,8 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask)
                store_attrs(to_msqp(qp), attr, attr_mask);
 
        /*
-        * If we are moving QP0 to RTR, bring the IB link up; if we
-        * are moving QP0 to RESET or ERROR, bring the link back down.
+        * If we moved QP0 to RTR, bring the IB link up; if we moved
+        * QP0 to RESET or ERROR, bring the link back down.
         */
        if (is_qp0(dev, qp)) {
                if (cur_state != IB_QPS_RTR &&
@@ -848,9 +862,71 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask)
                        mthca_CLOSE_IB(dev, to_msqp(qp)->port, &status);
        }
 
+       /*
+        * If we moved a kernel QP to RESET, clean up all old CQ
+        * entries and reinitialize the QP.
+        */
+       if (!err && new_state == IB_QPS_RESET && !qp->ibqp.uobject) {
+               mthca_cq_clean(dev, to_mcq(qp->ibqp.send_cq)->cqn, qp->qpn,
+                              qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL);
+               if (qp->ibqp.send_cq != qp->ibqp.recv_cq)
+                       mthca_cq_clean(dev, to_mcq(qp->ibqp.recv_cq)->cqn, qp->qpn,
+                                      qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL);
+
+               mthca_wq_init(&qp->sq);
+               mthca_wq_init(&qp->rq);
+
+               if (mthca_is_memfree(dev)) {
+                       *qp->sq.db = 0;
+                       *qp->rq.db = 0;
+               }
+       }
+
        return err;
 }
 
+static void mthca_adjust_qp_caps(struct mthca_dev *dev,
+                                struct mthca_pd *pd,
+                                struct mthca_qp *qp)
+{
+       int max_data_size;
+
+       /*
+        * Calculate the maximum size of WQE s/g segments, excluding
+        * the next segment and other non-data segments.
+        */
+       max_data_size = min(dev->limits.max_desc_sz, 1 << qp->sq.wqe_shift) -
+               sizeof (struct mthca_next_seg);
+
+       switch (qp->transport) {
+       case MLX:
+               max_data_size -= 2 * sizeof (struct mthca_data_seg);
+               break;
+
+       case UD:
+               if (mthca_is_memfree(dev))
+                       max_data_size -= sizeof (struct mthca_arbel_ud_seg);
+               else
+                       max_data_size -= sizeof (struct mthca_tavor_ud_seg);
+               break;
+
+       default:
+               max_data_size -= sizeof (struct mthca_raddr_seg);
+               break;
+       }
+
+       /* We don't support inline data for kernel QPs (yet). */
+       if (!pd->ibpd.uobject)
+               qp->max_inline_data = 0;
+        else
+               qp->max_inline_data = max_data_size - MTHCA_INLINE_HEADER_SIZE;
+
+       qp->sq.max_gs = max_data_size / sizeof (struct mthca_data_seg);
+       qp->rq.max_gs = (min(dev->limits.max_desc_sz, 1 << qp->rq.wqe_shift) -
+                       sizeof (struct mthca_next_seg)) /
+                       sizeof (struct mthca_data_seg);
+}
+
 /*
  * Allocate and register buffer for WQEs.  qp->rq.max, sq.max,
  * rq.max_gs and sq.max_gs must all be assigned.
@@ -868,27 +944,53 @@ static int mthca_alloc_wqe_buf(struct mthca_dev *dev,
        size = sizeof (struct mthca_next_seg) +
                qp->rq.max_gs * sizeof (struct mthca_data_seg);
 
+       if (size > dev->limits.max_desc_sz)
+               return -EINVAL;
+
        for (qp->rq.wqe_shift = 6; 1 << qp->rq.wqe_shift < size;
             qp->rq.wqe_shift++)
                ; /* nothing */
 
-       size = sizeof (struct mthca_next_seg) +
-               qp->sq.max_gs * sizeof (struct mthca_data_seg);
+       size = qp->sq.max_gs * sizeof (struct mthca_data_seg);
        switch (qp->transport) {
        case MLX:
                size += 2 * sizeof (struct mthca_data_seg);
                break;
+
        case UD:
-               if (mthca_is_memfree(dev))
-                       size += sizeof (struct mthca_arbel_ud_seg);
-               else
-                       size += sizeof (struct mthca_tavor_ud_seg);
+               size += mthca_is_memfree(dev) ?
+                       sizeof (struct mthca_arbel_ud_seg) :
+                       sizeof (struct mthca_tavor_ud_seg);
+               break;
+
+       case UC:
+               size += sizeof (struct mthca_raddr_seg);
+               break;
+
+       case RC:
+               size += sizeof (struct mthca_raddr_seg);
+               /*
+                * An atomic op will require an atomic segment, a
+                * remote address segment and one scatter entry.
+                */
+               size = max_t(int, size,
+                            sizeof (struct mthca_atomic_seg) +
+                            sizeof (struct mthca_raddr_seg) +
+                            sizeof (struct mthca_data_seg));
                break;
+
        default:
-               /* bind seg is as big as atomic + raddr segs */
-               size += sizeof (struct mthca_bind_seg);
+               break;
        }
 
+       /* Make sure that we have enough space for a bind request */
+       size = max_t(int, size, sizeof (struct mthca_bind_seg));
+
+       size += sizeof (struct mthca_next_seg);
+
+       if (size > dev->limits.max_desc_sz)
+               return -EINVAL;
+
        for (qp->sq.wqe_shift = 6; 1 << qp->sq.wqe_shift < size;
             qp->sq.wqe_shift++)
                ; /* nothing */
@@ -1003,16 +1105,6 @@ static void mthca_free_memfree(struct mthca_dev *dev,
        }
 }
 
-static void mthca_wq_init(struct mthca_wq* wq)
-{
-       spin_lock_init(&wq->lock);
-       wq->next_ind  = 0;
-       wq->last_comp = wq->max - 1;
-       wq->head      = 0;
-       wq->tail      = 0;
-       wq->last      = NULL;
-}
-
 static int mthca_alloc_qp_common(struct mthca_dev *dev,
                                 struct mthca_pd *pd,
                                 struct mthca_cq *send_cq,
@@ -1024,6 +1116,7 @@ static int mthca_alloc_qp_common(struct mthca_dev *dev,
        int i;
 
        atomic_set(&qp->refcount, 1);
+       init_waitqueue_head(&qp->wait);
        qp->state        = IB_QPS_RESET;
        qp->atomic_rd_en = 0;
        qp->resp_depth   = 0;
@@ -1041,6 +1134,8 @@ static int mthca_alloc_qp_common(struct mthca_dev *dev,
                return ret;
        }
 
+       mthca_adjust_qp_caps(dev, pd, qp);
+
        /*
         * If this is a userspace QP, we're done now.  The doorbells
         * will be allocated and buffers will be initialized in
@@ -1082,6 +1177,9 @@ static int mthca_alloc_qp_common(struct mthca_dev *dev,
                }
        }
 
+       qp->sq.last = get_send_wqe(qp, qp->sq.max - 1);
+       qp->rq.last = get_recv_wqe(qp, qp->rq.max - 1);
+
        return 0;
 }
 
@@ -1089,8 +1187,10 @@ static int mthca_set_qp_size(struct mthca_dev *dev, struct ib_qp_cap *cap,
                             struct mthca_qp *qp)
 {
        /* Sanity check QP size before proceeding */
-       if (cap->max_send_wr  > 65536 || cap->max_recv_wr  > 65536 ||
-           cap->max_send_sge > 64    || cap->max_recv_sge > 64)
+       if (cap->max_send_wr  > dev->limits.max_wqes ||
+           cap->max_recv_wr  > dev->limits.max_wqes ||
+           cap->max_send_sge > dev->limits.max_sg   ||
+           cap->max_recv_sge > dev->limits.max_sg)
                return -EINVAL;
 
        if (mthca_is_memfree(dev)) {
@@ -1562,15 +1662,13 @@ int mthca_tavor_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
                        goto out;
                }
 
-               if (prev_wqe) {
-                       ((struct mthca_next_seg *) prev_wqe)->nda_op =
-                               cpu_to_be32(((ind << qp->sq.wqe_shift) +
-                                            qp->send_wqe_offset) |
-                                           mthca_opcode[wr->opcode]);
-                       wmb();
-                       ((struct mthca_next_seg *) prev_wqe)->ee_nds =
-                               cpu_to_be32((size0 ? 0 : MTHCA_NEXT_DBD) | size);
-               }
+               ((struct mthca_next_seg *) prev_wqe)->nda_op =
+                       cpu_to_be32(((ind << qp->sq.wqe_shift) +
+                                    qp->send_wqe_offset) |
+                                   mthca_opcode[wr->opcode]);
+               wmb();
+               ((struct mthca_next_seg *) prev_wqe)->ee_nds =
+                       cpu_to_be32((size0 ? 0 : MTHCA_NEXT_DBD) | size);
 
                if (!size0) {
                        size0 = size;
@@ -1667,13 +1765,11 @@ int mthca_tavor_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *wr,
 
                qp->wrid[ind] = wr->wr_id;
 
-               if (likely(prev_wqe)) {
-                       ((struct mthca_next_seg *) prev_wqe)->nda_op =
-                               cpu_to_be32((ind << qp->rq.wqe_shift) | 1);
-                       wmb();
-                       ((struct mthca_next_seg *) prev_wqe)->ee_nds =
-                               cpu_to_be32(MTHCA_NEXT_DBD | size);
-               }
+               ((struct mthca_next_seg *) prev_wqe)->nda_op =
+                       cpu_to_be32((ind << qp->rq.wqe_shift) | 1);
+               wmb();
+               ((struct mthca_next_seg *) prev_wqe)->ee_nds =
+                       cpu_to_be32(MTHCA_NEXT_DBD | size);
 
                if (!size0)
                        size0 = size;
@@ -1884,15 +1980,13 @@ int mthca_arbel_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
                        goto out;
                }
 
-               if (likely(prev_wqe)) {
-                       ((struct mthca_next_seg *) prev_wqe)->nda_op =
-                               cpu_to_be32(((ind << qp->sq.wqe_shift) +
-                                            qp->send_wqe_offset) |
-                                           mthca_opcode[wr->opcode]);
-                       wmb();
-                       ((struct mthca_next_seg *) prev_wqe)->ee_nds =
-                               cpu_to_be32(MTHCA_NEXT_DBD | size);
-               }
+               ((struct mthca_next_seg *) prev_wqe)->nda_op =
+                       cpu_to_be32(((ind << qp->sq.wqe_shift) +
+                                    qp->send_wqe_offset) |
+                                   mthca_opcode[wr->opcode]);
+               wmb();
+               ((struct mthca_next_seg *) prev_wqe)->ee_nds =
+                       cpu_to_be32(MTHCA_NEXT_DBD | size);
 
                if (!size0) {
                        size0 = size;
@@ -2106,5 +2200,6 @@ void __devexit mthca_cleanup_qp_table(struct mthca_dev *dev)
        for (i = 0; i < 2; ++i)
                mthca_CONF_SPECIAL_QP(dev, i, 0, &status);
 
+       mthca_array_cleanup(&dev->qp_table.qp, dev->limits.num_qps);
        mthca_alloc_cleanup(&dev->qp_table.alloc);
 }