Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/infiniband/hw/mlx5/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 160 kB image not shown  

Quelle  qp.c   Sprache: C

 
/*
 * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */


#include <linux/etherdevice.h>
#include <rdma/ib_umem.h>
#include <rdma/ib_cache.h>
#include <rdma/ib_user_verbs.h>
#include <rdma/rdma_counter.h>
#include <linux/mlx5/fs.h>
#include "mlx5_ib.h"
#include "ib_rep.h"
#include "counters.h"
#include "cmd.h"
#include "umr.h"
#include "qp.h"
#include "wr.h"

enum {
 MLX5_IB_ACK_REQ_FREQ = 8,
};

enum {
 MLX5_IB_DEFAULT_SCHED_QUEUE = 0x83,
 MLX5_IB_DEFAULT_QP0_SCHED_QUEUE = 0x3f,
 MLX5_IB_LINK_TYPE_IB  = 0,
 MLX5_IB_LINK_TYPE_ETH  = 1
};

enum raw_qp_set_mask_map {
 MLX5_RAW_QP_MOD_SET_RQ_Q_CTR_ID  = 1UL << 0,
 MLX5_RAW_QP_RATE_LIMIT   = 1UL << 1,
};

enum {
 MLX5_QP_RM_GO_BACK_N   = 0x1,
};

struct mlx5_modify_raw_qp_param {
 u16 operation;

 u32 set_mask; /* raw_qp_set_mask_map */

 struct mlx5_rate_limit rl;

 u8 rq_q_ctr_id;
 u32 port;
};

struct mlx5_ib_qp_event_work {
 struct work_struct work;
 struct mlx5_core_qp *qp;
 int type;
};

static struct workqueue_struct *mlx5_ib_qp_event_wq;

static void get_cqs(enum ib_qp_type qp_type,
      struct ib_cq *ib_send_cq, struct ib_cq *ib_recv_cq,
      struct mlx5_ib_cq **send_cq, struct mlx5_ib_cq **recv_cq);

static int is_qp0(enum ib_qp_type qp_type)
{
 return qp_type == IB_QPT_SMI;
}

static int is_sqp(enum ib_qp_type qp_type)
{
 return is_qp0(qp_type) || is_qp1(qp_type);
}

/**
 * mlx5_ib_read_user_wqe_common() - Copy a WQE (or part of) from user WQ
 * to kernel buffer
 *
 * @umem: User space memory where the WQ is
 * @buffer: buffer to copy to
 * @buflen: buffer length
 * @wqe_index: index of WQE to copy from
 * @wq_offset: offset to start of WQ
 * @wq_wqe_cnt: number of WQEs in WQ
 * @wq_wqe_shift: log2 of WQE size
 * @bcnt: number of bytes to copy
 * @bytes_copied: number of bytes to copy (return value)
 *
 * Copies from start of WQE bcnt or less bytes.
 * Does not gurantee to copy the entire WQE.
 *
 * Return: zero on success, or an error code.
 */

static int mlx5_ib_read_user_wqe_common(struct ib_umem *umem, void *buffer,
     size_t buflen, int wqe_index,
     int wq_offset, int wq_wqe_cnt,
     int wq_wqe_shift, int bcnt,
     size_t *bytes_copied)
{
 size_t offset = wq_offset + ((wqe_index % wq_wqe_cnt) << wq_wqe_shift);
 size_t wq_end = wq_offset + (wq_wqe_cnt << wq_wqe_shift);
 size_t copy_length;
 int ret;

 /* don't copy more than requested, more than buffer length or
 * beyond WQ end
 */

 copy_length = min_t(u32, buflen, wq_end - offset);
 copy_length = min_t(u32, copy_length, bcnt);

 ret = ib_umem_copy_from(buffer, umem, offset, copy_length);
 if (ret)
  return ret;

 if (!ret && bytes_copied)
  *bytes_copied = copy_length;

 return 0;
}

static int mlx5_ib_read_kernel_wqe_sq(struct mlx5_ib_qp *qp, int wqe_index,
          void *buffer, size_t buflen, size_t *bc)
{
 struct mlx5_wqe_ctrl_seg *ctrl;
 size_t bytes_copied = 0;
 size_t wqe_length;
 void *p;
 int ds;

 wqe_index = wqe_index & qp->sq.fbc.sz_m1;

 /* read the control segment first */
 p = mlx5_frag_buf_get_wqe(&qp->sq.fbc, wqe_index);
 ctrl = p;
 ds = be32_to_cpu(ctrl->qpn_ds) & MLX5_WQE_CTRL_DS_MASK;
 wqe_length = ds * MLX5_WQE_DS_UNITS;

 /* read rest of WQE if it spreads over more than one stride */
 while (bytes_copied < wqe_length) {
  size_t copy_length =
   min_t(size_t, buflen - bytes_copied, MLX5_SEND_WQE_BB);

  if (!copy_length)
   break;

  memcpy(buffer + bytes_copied, p, copy_length);
  bytes_copied += copy_length;

  wqe_index = (wqe_index + 1) & qp->sq.fbc.sz_m1;
  p = mlx5_frag_buf_get_wqe(&qp->sq.fbc, wqe_index);
 }
 *bc = bytes_copied;
 return 0;
}

static int mlx5_ib_read_user_wqe_sq(struct mlx5_ib_qp *qp, int wqe_index,
        void *buffer, size_t buflen, size_t *bc)
{
 struct mlx5_ib_qp_base *base = &qp->trans_qp.base;
 struct ib_umem *umem = base->ubuffer.umem;
 struct mlx5_ib_wq *wq = &qp->sq;
 struct mlx5_wqe_ctrl_seg *ctrl;
 size_t bytes_copied;
 size_t bytes_copied2;
 size_t wqe_length;
 int ret;
 int ds;

 /* at first read as much as possible */
 ret = mlx5_ib_read_user_wqe_common(umem, buffer, buflen, wqe_index,
        wq->offset, wq->wqe_cnt,
        wq->wqe_shift, buflen,
        &bytes_copied);
 if (ret)
  return ret;

 /* we need at least control segment size to proceed */
 if (bytes_copied < sizeof(*ctrl))
  return -EINVAL;

 ctrl = buffer;
 ds = be32_to_cpu(ctrl->qpn_ds) & MLX5_WQE_CTRL_DS_MASK;
 wqe_length = ds * MLX5_WQE_DS_UNITS;

 /* if we copied enough then we are done */
 if (bytes_copied >= wqe_length) {
  *bc = bytes_copied;
  return 0;
 }

 /* otherwise this a wrapped around wqe
 * so read the remaining bytes starting
 * from  wqe_index 0
 */

 ret = mlx5_ib_read_user_wqe_common(umem, buffer + bytes_copied,
        buflen - bytes_copied, 0, wq->offset,
        wq->wqe_cnt, wq->wqe_shift,
        wqe_length - bytes_copied,
        &bytes_copied2);

 if (ret)
  return ret;
 *bc = bytes_copied + bytes_copied2;
 return 0;
}

int mlx5_ib_read_wqe_sq(struct mlx5_ib_qp *qp, int wqe_index, void *buffer,
   size_t buflen, size_t *bc)
{
 struct mlx5_ib_qp_base *base = &qp->trans_qp.base;
 struct ib_umem *umem = base->ubuffer.umem;

 if (buflen < sizeof(struct mlx5_wqe_ctrl_seg))
  return -EINVAL;

 if (!umem)
  return mlx5_ib_read_kernel_wqe_sq(qp, wqe_index, buffer,
        buflen, bc);

 return mlx5_ib_read_user_wqe_sq(qp, wqe_index, buffer, buflen, bc);
}

static int mlx5_ib_read_user_wqe_rq(struct mlx5_ib_qp *qp, int wqe_index,
        void *buffer, size_t buflen, size_t *bc)
{
 struct mlx5_ib_qp_base *base = &qp->trans_qp.base;
 struct ib_umem *umem = base->ubuffer.umem;
 struct mlx5_ib_wq *wq = &qp->rq;
 size_t bytes_copied;
 int ret;

 ret = mlx5_ib_read_user_wqe_common(umem, buffer, buflen, wqe_index,
        wq->offset, wq->wqe_cnt,
        wq->wqe_shift, buflen,
        &bytes_copied);

 if (ret)
  return ret;
 *bc = bytes_copied;
 return 0;
}

int mlx5_ib_read_wqe_rq(struct mlx5_ib_qp *qp, int wqe_index, void *buffer,
   size_t buflen, size_t *bc)
{
 struct mlx5_ib_qp_base *base = &qp->trans_qp.base;
 struct ib_umem *umem = base->ubuffer.umem;
 struct mlx5_ib_wq *wq = &qp->rq;
 size_t wqe_size = 1 << wq->wqe_shift;

 if (buflen < wqe_size)
  return -EINVAL;

 if (!umem)
  return -EOPNOTSUPP;

 return mlx5_ib_read_user_wqe_rq(qp, wqe_index, buffer, buflen, bc);
}

static int mlx5_ib_read_user_wqe_srq(struct mlx5_ib_srq *srq, int wqe_index,
         void *buffer, size_t buflen, size_t *bc)
{
 struct ib_umem *umem = srq->umem;
 size_t bytes_copied;
 int ret;

 ret = mlx5_ib_read_user_wqe_common(umem, buffer, buflen, wqe_index, 0,
        srq->msrq.max, srq->msrq.wqe_shift,
        buflen, &bytes_copied);

 if (ret)
  return ret;
 *bc = bytes_copied;
 return 0;
}

int mlx5_ib_read_wqe_srq(struct mlx5_ib_srq *srq, int wqe_index, void *buffer,
    size_t buflen, size_t *bc)
{
 struct ib_umem *umem = srq->umem;
 size_t wqe_size = 1 << srq->msrq.wqe_shift;

 if (buflen < wqe_size)
  return -EINVAL;

 if (!umem)
  return -EOPNOTSUPP;

 return mlx5_ib_read_user_wqe_srq(srq, wqe_index, buffer, buflen, bc);
}

static void mlx5_ib_qp_err_syndrome(struct ib_qp *ibqp)
{
 struct mlx5_ib_dev *dev = to_mdev(ibqp->device);
 int outlen = MLX5_ST_SZ_BYTES(query_qp_out);
 struct mlx5_ib_qp *qp = to_mqp(ibqp);
 void *pas_ext_union, *err_syn;
 u32 *outb;
 int err;

 if (!MLX5_CAP_GEN(dev->mdev, qpc_extension) ||
     !MLX5_CAP_GEN(dev->mdev, qp_error_syndrome))
  return;

 outb = kzalloc(outlen, GFP_KERNEL);
 if (!outb)
  return;

 err = mlx5_core_qp_query(dev, &qp->trans_qp.base.mqp, outb, outlen,
     true);
 if (err)
  goto out;

 pas_ext_union =
  MLX5_ADDR_OF(query_qp_out, outb, qp_pas_or_qpc_ext_and_pas);
 err_syn = MLX5_ADDR_OF(qpc_extension_and_pas_list_in, pas_ext_union,
          qpc_data_extension.error_syndrome);

 pr_err("%s/%d: QP %d error: %s (0x%x 0x%x 0x%x)\n",
        ibqp->device->name, ibqp->port, ibqp->qp_num,
        ib_wc_status_msg(
         MLX5_GET(cqe_error_syndrome, err_syn, syndrome)),
        MLX5_GET(cqe_error_syndrome, err_syn, vendor_error_syndrome),
        MLX5_GET(cqe_error_syndrome, err_syn, hw_syndrome_type),
        MLX5_GET(cqe_error_syndrome, err_syn, hw_error_syndrome));
out:
 kfree(outb);
}

static void mlx5_ib_handle_qp_event(struct work_struct *_work)
{
 struct mlx5_ib_qp_event_work *qpe_work =
  container_of(_work, struct mlx5_ib_qp_event_work, work);
 struct ib_qp *ibqp = &to_mibqp(qpe_work->qp)->ibqp;
 struct ib_event event = {};

 event.device = ibqp->device;
 event.element.qp = ibqp;
 switch (qpe_work->type) {
 case MLX5_EVENT_TYPE_PATH_MIG:
  event.event = IB_EVENT_PATH_MIG;
  break;
 case MLX5_EVENT_TYPE_COMM_EST:
  event.event = IB_EVENT_COMM_EST;
  break;
 case MLX5_EVENT_TYPE_SQ_DRAINED:
  event.event = IB_EVENT_SQ_DRAINED;
  break;
 case MLX5_EVENT_TYPE_SRQ_LAST_WQE:
  event.event = IB_EVENT_QP_LAST_WQE_REACHED;
  break;
 case MLX5_EVENT_TYPE_WQ_CATAS_ERROR:
  event.event = IB_EVENT_QP_FATAL;
  break;
 case MLX5_EVENT_TYPE_PATH_MIG_FAILED:
  event.event = IB_EVENT_PATH_MIG_ERR;
  break;
 case MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR:
  event.event = IB_EVENT_QP_REQ_ERR;
  break;
 case MLX5_EVENT_TYPE_WQ_ACCESS_ERROR:
  event.event = IB_EVENT_QP_ACCESS_ERR;
  break;
 default:
  pr_warn("mlx5_ib: Unexpected event type %d on QP %06x\n",
   qpe_work->type, qpe_work->qp->qpn);
  goto out;
 }

 if ((event.event == IB_EVENT_QP_FATAL) ||
     (event.event == IB_EVENT_QP_ACCESS_ERR))
  mlx5_ib_qp_err_syndrome(ibqp);

 ibqp->event_handler(&event, ibqp->qp_context);

out:
 mlx5_core_res_put(&qpe_work->qp->common);
 kfree(qpe_work);
}

static void mlx5_ib_qp_event(struct mlx5_core_qp *qp, int type)
{
 struct ib_qp *ibqp = &to_mibqp(qp)->ibqp;
 struct mlx5_ib_qp_event_work *qpe_work;

 if (type == MLX5_EVENT_TYPE_PATH_MIG) {
  /* This event is only valid for trans_qps */
  to_mibqp(qp)->port = to_mibqp(qp)->trans_qp.alt_port;
 }

 if (!ibqp->event_handler)
  goto out_no_handler;

 qpe_work = kzalloc(sizeof(*qpe_work), GFP_ATOMIC);
 if (!qpe_work)
  goto out_no_handler;

 qpe_work->qp = qp;
 qpe_work->type = type;
 INIT_WORK(&qpe_work->work, mlx5_ib_handle_qp_event);
 queue_work(mlx5_ib_qp_event_wq, &qpe_work->work);
 return;

out_no_handler:
 mlx5_core_res_put(&qp->common);
}

static int set_rq_size(struct mlx5_ib_dev *dev, struct ib_qp_cap *cap,
         int has_rq, struct mlx5_ib_qp *qp, struct mlx5_ib_create_qp *ucmd)
{
 int wqe_size;
 int wq_size;

 /* Sanity check RQ size before proceeding */
 if (cap->max_recv_wr > (1 << MLX5_CAP_GEN(dev->mdev, log_max_qp_sz)))
  return -EINVAL;

 if (!has_rq) {
  qp->rq.max_gs = 0;
  qp->rq.wqe_cnt = 0;
  qp->rq.wqe_shift = 0;
  cap->max_recv_wr = 0;
  cap->max_recv_sge = 0;
 } else {
  int wq_sig = !!(qp->flags_en & MLX5_QP_FLAG_SIGNATURE);

  if (ucmd) {
   qp->rq.wqe_cnt = ucmd->rq_wqe_count;
   if (ucmd->rq_wqe_shift > BITS_PER_BYTE * sizeof(ucmd->rq_wqe_shift))
    return -EINVAL;
   qp->rq.wqe_shift = ucmd->rq_wqe_shift;
   if ((1 << qp->rq.wqe_shift) /
        sizeof(struct mlx5_wqe_data_seg) <
       wq_sig)
    return -EINVAL;
   qp->rq.max_gs =
    (1 << qp->rq.wqe_shift) /
     sizeof(struct mlx5_wqe_data_seg) -
    wq_sig;
   qp->rq.max_post = qp->rq.wqe_cnt;
  } else {
   wqe_size =
    wq_sig ? sizeof(struct mlx5_wqe_signature_seg) :
      0;
   wqe_size += cap->max_recv_sge * sizeof(struct mlx5_wqe_data_seg);
   wqe_size = roundup_pow_of_two(wqe_size);
   wq_size = roundup_pow_of_two(cap->max_recv_wr) * wqe_size;
   wq_size = max_t(int, wq_size, MLX5_SEND_WQE_BB);
   qp->rq.wqe_cnt = wq_size / wqe_size;
   if (wqe_size > MLX5_CAP_GEN(dev->mdev, max_wqe_sz_rq)) {
    mlx5_ib_dbg(dev, "wqe_size %d, max %d\n",
         wqe_size,
         MLX5_CAP_GEN(dev->mdev,
        max_wqe_sz_rq));
    return -EINVAL;
   }
   qp->rq.wqe_shift = ilog2(wqe_size);
   qp->rq.max_gs =
    (1 << qp->rq.wqe_shift) /
     sizeof(struct mlx5_wqe_data_seg) -
    wq_sig;
   qp->rq.max_post = qp->rq.wqe_cnt;
  }
 }

 return 0;
}

static int sq_overhead(struct ib_qp_init_attr *attr)
{
 int size = 0;

 switch (attr->qp_type) {
 case IB_QPT_XRC_INI:
  size += sizeof(struct mlx5_wqe_xrc_seg);
  fallthrough;
 case IB_QPT_RC:
  size += sizeof(struct mlx5_wqe_ctrl_seg) +
   max(sizeof(struct mlx5_wqe_atomic_seg) +
       sizeof(struct mlx5_wqe_raddr_seg),
       sizeof(struct mlx5_wqe_umr_ctrl_seg) +
       sizeof(struct mlx5_mkey_seg) +
       MLX5_IB_SQ_UMR_INLINE_THRESHOLD /
       MLX5_IB_UMR_OCTOWORD);
  break;

 case IB_QPT_XRC_TGT:
  return 0;

 case IB_QPT_UC:
  size += sizeof(struct mlx5_wqe_ctrl_seg) +
   max(sizeof(struct mlx5_wqe_raddr_seg),
       sizeof(struct mlx5_wqe_umr_ctrl_seg) +
       sizeof(struct mlx5_mkey_seg));
  break;

 case IB_QPT_UD:
  if (attr->create_flags & IB_QP_CREATE_IPOIB_UD_LSO)
   size += sizeof(struct mlx5_wqe_eth_pad) +
    sizeof(struct mlx5_wqe_eth_seg);
  fallthrough;
 case IB_QPT_SMI:
 case MLX5_IB_QPT_HW_GSI:
  size += sizeof(struct mlx5_wqe_ctrl_seg) +
   sizeof(struct mlx5_wqe_datagram_seg);
  break;

 case MLX5_IB_QPT_REG_UMR:
  size += sizeof(struct mlx5_wqe_ctrl_seg) +
   sizeof(struct mlx5_wqe_umr_ctrl_seg) +
   sizeof(struct mlx5_mkey_seg);
  break;

 default:
  return -EINVAL;
 }

 return size;
}

static int calc_send_wqe(struct ib_qp_init_attr *attr)
{
 int inl_size = 0;
 int size;

 size = sq_overhead(attr);
 if (size < 0)
  return size;

 if (attr->cap.max_inline_data) {
  inl_size = size + sizeof(struct mlx5_wqe_inline_seg) +
   attr->cap.max_inline_data;
 }

 size += attr->cap.max_send_sge * sizeof(struct mlx5_wqe_data_seg);
 if (attr->create_flags & IB_QP_CREATE_INTEGRITY_EN &&
     ALIGN(max_t(int, inl_size, size), MLX5_SEND_WQE_BB) < MLX5_SIG_WQE_SIZE)
  return MLX5_SIG_WQE_SIZE;
 else
  return ALIGN(max_t(int, inl_size, size), MLX5_SEND_WQE_BB);
}

static int get_send_sge(struct ib_qp_init_attr *attr, int wqe_size)
{
 int max_sge;

 if (attr->qp_type == IB_QPT_RC)
  max_sge = (min_t(int, wqe_size, 512) -
      sizeof(struct mlx5_wqe_ctrl_seg) -
      sizeof(struct mlx5_wqe_raddr_seg)) /
   sizeof(struct mlx5_wqe_data_seg);
 else if (attr->qp_type == IB_QPT_XRC_INI)
  max_sge = (min_t(int, wqe_size, 512) -
      sizeof(struct mlx5_wqe_ctrl_seg) -
      sizeof(struct mlx5_wqe_xrc_seg) -
      sizeof(struct mlx5_wqe_raddr_seg)) /
   sizeof(struct mlx5_wqe_data_seg);
 else
  max_sge = (wqe_size - sq_overhead(attr)) /
   sizeof(struct mlx5_wqe_data_seg);

 return min_t(int, max_sge, wqe_size - sq_overhead(attr) /
       sizeof(struct mlx5_wqe_data_seg));
}

static int calc_sq_size(struct mlx5_ib_dev *dev, struct ib_qp_init_attr *attr,
   struct mlx5_ib_qp *qp)
{
 int wqe_size;
 int wq_size;

 if (!attr->cap.max_send_wr)
  return 0;

 wqe_size = calc_send_wqe(attr);
 mlx5_ib_dbg(dev, "wqe_size %d\n", wqe_size);
 if (wqe_size < 0)
  return wqe_size;

 if (wqe_size > MLX5_CAP_GEN(dev->mdev, max_wqe_sz_sq)) {
  mlx5_ib_dbg(dev, "wqe_size(%d) > max_sq_desc_sz(%d)\n",
       wqe_size, MLX5_CAP_GEN(dev->mdev, max_wqe_sz_sq));
  return -EINVAL;
 }

 qp->max_inline_data = wqe_size - sq_overhead(attr) -
         sizeof(struct mlx5_wqe_inline_seg);
 attr->cap.max_inline_data = qp->max_inline_data;

 wq_size = roundup_pow_of_two(attr->cap.max_send_wr * wqe_size);
 qp->sq.wqe_cnt = wq_size / MLX5_SEND_WQE_BB;
 if (qp->sq.wqe_cnt > (1 << MLX5_CAP_GEN(dev->mdev, log_max_qp_sz))) {
  mlx5_ib_dbg(dev, "send queue size (%d * %d / %d -> %d) exceeds limits(%d)\n",
       attr->cap.max_send_wr, wqe_size, MLX5_SEND_WQE_BB,
       qp->sq.wqe_cnt,
       1 << MLX5_CAP_GEN(dev->mdev, log_max_qp_sz));
  return -ENOMEM;
 }
 qp->sq.wqe_shift = ilog2(MLX5_SEND_WQE_BB);
 qp->sq.max_gs = get_send_sge(attr, wqe_size);
 if (qp->sq.max_gs < attr->cap.max_send_sge)
  return -ENOMEM;

 attr->cap.max_send_sge = qp->sq.max_gs;
 qp->sq.max_post = wq_size / wqe_size;
 attr->cap.max_send_wr = qp->sq.max_post;

 return wq_size;
}

static int set_user_buf_size(struct mlx5_ib_dev *dev,
       struct mlx5_ib_qp *qp,
       struct mlx5_ib_create_qp *ucmd,
       struct mlx5_ib_qp_base *base,
       struct ib_qp_init_attr *attr)
{
 int desc_sz = 1 << qp->sq.wqe_shift;

 if (desc_sz > MLX5_CAP_GEN(dev->mdev, max_wqe_sz_sq)) {
  mlx5_ib_warn(dev, "desc_sz %d, max_sq_desc_sz %d\n",
        desc_sz, MLX5_CAP_GEN(dev->mdev, max_wqe_sz_sq));
  return -EINVAL;
 }

 if (ucmd->sq_wqe_count && !is_power_of_2(ucmd->sq_wqe_count)) {
  mlx5_ib_warn(dev, "sq_wqe_count %d is not a power of two\n",
        ucmd->sq_wqe_count);
  return -EINVAL;
 }

 qp->sq.wqe_cnt = ucmd->sq_wqe_count;

 if (qp->sq.wqe_cnt > (1 << MLX5_CAP_GEN(dev->mdev, log_max_qp_sz))) {
  mlx5_ib_warn(dev, "wqe_cnt %d, max_wqes %d\n",
        qp->sq.wqe_cnt,
        1 << MLX5_CAP_GEN(dev->mdev, log_max_qp_sz));
  return -EINVAL;
 }

 if (attr->qp_type == IB_QPT_RAW_PACKET ||
     qp->flags & IB_QP_CREATE_SOURCE_QPN) {
  base->ubuffer.buf_size = qp->rq.wqe_cnt << qp->rq.wqe_shift;
  qp->raw_packet_qp.sq.ubuffer.buf_size = qp->sq.wqe_cnt << 6;
 } else {
  base->ubuffer.buf_size = (qp->rq.wqe_cnt << qp->rq.wqe_shift) +
      (qp->sq.wqe_cnt << 6);
 }

 return 0;
}

static int qp_has_rq(struct ib_qp_init_attr *attr)
{
 if (attr->qp_type == IB_QPT_XRC_INI ||
     attr->qp_type == IB_QPT_XRC_TGT || attr->srq ||
     attr->qp_type == MLX5_IB_QPT_REG_UMR ||
     !attr->cap.max_recv_wr)
  return 0;

 return 1;
}

enum {
 /* this is the first blue flame register in the array of bfregs assigned
 * to a processes. Since we do not use it for blue flame but rather
 * regular 64 bit doorbells, we do not need a lock for maintaiing
 * "odd/even" order
 */

 NUM_NON_BLUE_FLAME_BFREGS = 1,
};

static int max_bfregs(struct mlx5_ib_dev *dev, struct mlx5_bfreg_info *bfregi)
{
 return get_uars_per_sys_page(dev, bfregi->lib_uar_4k) *
        bfregi->num_static_sys_pages * MLX5_NON_FP_BFREGS_PER_UAR;
}

static int num_med_bfreg(struct mlx5_ib_dev *dev,
    struct mlx5_bfreg_info *bfregi)
{
 int n;

 n = max_bfregs(dev, bfregi) - bfregi->num_low_latency_bfregs -
     NUM_NON_BLUE_FLAME_BFREGS;

 return n >= 0 ? n : 0;
}

static int first_med_bfreg(struct mlx5_ib_dev *dev,
      struct mlx5_bfreg_info *bfregi)
{
 return num_med_bfreg(dev, bfregi) ? 1 : -ENOMEM;
}

static int first_hi_bfreg(struct mlx5_ib_dev *dev,
     struct mlx5_bfreg_info *bfregi)
{
 int med;

 med = num_med_bfreg(dev, bfregi);
 return ++med;
}

static int alloc_high_class_bfreg(struct mlx5_ib_dev *dev,
      struct mlx5_bfreg_info *bfregi)
{
 int i;

 for (i = first_hi_bfreg(dev, bfregi); i < max_bfregs(dev, bfregi); i++) {
  if (!bfregi->count[i]) {
   bfregi->count[i]++;
   return i;
  }
 }

 return -ENOMEM;
}

static int alloc_med_class_bfreg(struct mlx5_ib_dev *dev,
     struct mlx5_bfreg_info *bfregi)
{
 int minidx = first_med_bfreg(dev, bfregi);
 int i;

 if (minidx < 0)
  return minidx;

 for (i = minidx; i < first_hi_bfreg(dev, bfregi); i++) {
  if (bfregi->count[i] < bfregi->count[minidx])
   minidx = i;
  if (!bfregi->count[minidx])
   break;
 }

 bfregi->count[minidx]++;
 return minidx;
}

static int alloc_bfreg(struct mlx5_ib_dev *dev,
         struct mlx5_bfreg_info *bfregi)
{
 int bfregn = -ENOMEM;

 if (bfregi->lib_uar_dyn)
  return -EINVAL;

 mutex_lock(&bfregi->lock);
 if (bfregi->ver >= 2) {
  bfregn = alloc_high_class_bfreg(dev, bfregi);
  if (bfregn < 0)
   bfregn = alloc_med_class_bfreg(dev, bfregi);
 }

 if (bfregn < 0) {
  BUILD_BUG_ON(NUM_NON_BLUE_FLAME_BFREGS != 1);
  bfregn = 0;
  bfregi->count[bfregn]++;
 }
 mutex_unlock(&bfregi->lock);

 return bfregn;
}

void mlx5_ib_free_bfreg(struct mlx5_ib_dev *dev, struct mlx5_bfreg_info *bfregi, int bfregn)
{
 mutex_lock(&bfregi->lock);
 bfregi->count[bfregn]--;
 mutex_unlock(&bfregi->lock);
}

static enum mlx5_qp_state to_mlx5_state(enum ib_qp_state state)
{
 switch (state) {
 case IB_QPS_RESET: return MLX5_QP_STATE_RST;
 case IB_QPS_INIT: return MLX5_QP_STATE_INIT;
 case IB_QPS_RTR: return MLX5_QP_STATE_RTR;
 case IB_QPS_RTS: return MLX5_QP_STATE_RTS;
 case IB_QPS_SQD: return MLX5_QP_STATE_SQD;
 case IB_QPS_SQE: return MLX5_QP_STATE_SQER;
 case IB_QPS_ERR: return MLX5_QP_STATE_ERR;
 default:  return -1;
 }
}

static int to_mlx5_st(enum ib_qp_type type)
{
 switch (type) {
 case IB_QPT_RC:   return MLX5_QP_ST_RC;
 case IB_QPT_UC:   return MLX5_QP_ST_UC;
 case IB_QPT_UD:   return MLX5_QP_ST_UD;
 case MLX5_IB_QPT_REG_UMR: return MLX5_QP_ST_REG_UMR;
 case IB_QPT_XRC_INI:
 case IB_QPT_XRC_TGT:  return MLX5_QP_ST_XRC;
 case IB_QPT_SMI:  return MLX5_QP_ST_QP0;
 case MLX5_IB_QPT_HW_GSI: return MLX5_QP_ST_QP1;
 case MLX5_IB_QPT_DCI:  return MLX5_QP_ST_DCI;
 case IB_QPT_RAW_PACKET:  return MLX5_QP_ST_RAW_ETHERTYPE;
 default:  return -EINVAL;
 }
}

static void mlx5_ib_lock_cqs(struct mlx5_ib_cq *send_cq,
        struct mlx5_ib_cq *recv_cq);
static void mlx5_ib_unlock_cqs(struct mlx5_ib_cq *send_cq,
          struct mlx5_ib_cq *recv_cq);

int bfregn_to_uar_index(struct mlx5_ib_dev *dev,
   struct mlx5_bfreg_info *bfregi, u32 bfregn,
   bool dyn_bfreg)
{
 unsigned int bfregs_per_sys_page;
 u32 index_of_sys_page;
 u32 offset;

 if (bfregi->lib_uar_dyn)
  return -EINVAL;

 bfregs_per_sys_page = get_uars_per_sys_page(dev, bfregi->lib_uar_4k) *
    MLX5_NON_FP_BFREGS_PER_UAR;
 index_of_sys_page = bfregn / bfregs_per_sys_page;

 if (dyn_bfreg) {
  index_of_sys_page += bfregi->num_static_sys_pages;

  if (index_of_sys_page >= bfregi->num_sys_pages)
   return -EINVAL;

  if (bfregn > bfregi->num_dyn_bfregs ||
      bfregi->sys_pages[index_of_sys_page] == MLX5_IB_INVALID_UAR_INDEX) {
   mlx5_ib_dbg(dev, "Invalid dynamic uar index\n");
   return -EINVAL;
  }
 }

 offset = bfregn % bfregs_per_sys_page / MLX5_NON_FP_BFREGS_PER_UAR;
 return bfregi->sys_pages[index_of_sys_page] + offset;
}

static void destroy_user_rq(struct mlx5_ib_dev *dev, struct ib_pd *pd,
       struct mlx5_ib_rwq *rwq, struct ib_udata *udata)
{
 struct mlx5_ib_ucontext *context =
  rdma_udata_to_drv_context(
   udata,
   struct mlx5_ib_ucontext,
   ibucontext);

 if (rwq->create_flags & MLX5_IB_WQ_FLAGS_DELAY_DROP)
  atomic_dec(&dev->delay_drop.rqs_cnt);

 mlx5_ib_db_unmap_user(context, &rwq->db);
 ib_umem_release(rwq->umem);
}

static int create_user_rq(struct mlx5_ib_dev *dev, struct ib_pd *pd,
     struct ib_udata *udata, struct mlx5_ib_rwq *rwq,
     struct mlx5_ib_create_wq *ucmd)
{
 struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context(
  udata, struct mlx5_ib_ucontext, ibucontext);
 unsigned long page_size = 0;
 u32 offset = 0;
 int err;

 if (!ucmd->buf_addr)
  return -EINVAL;

 rwq->umem = ib_umem_get(&dev->ib_dev, ucmd->buf_addr, rwq->buf_size, 0);
 if (IS_ERR(rwq->umem)) {
  mlx5_ib_dbg(dev, "umem_get failed\n");
  err = PTR_ERR(rwq->umem);
  return err;
 }

 page_size = mlx5_umem_find_best_quantized_pgoff(
  rwq->umem, wq, log_wq_pg_sz, MLX5_ADAPTER_PAGE_SHIFT,
  page_offset, 64, &rwq->rq_page_offset);
 if (!page_size) {
  mlx5_ib_warn(dev, "bad offset\n");
  err = -EINVAL;
  goto err_umem;
 }

 rwq->rq_num_pas = ib_umem_num_dma_blocks(rwq->umem, page_size);
 rwq->page_shift = order_base_2(page_size);
 rwq->log_page_size =  rwq->page_shift - MLX5_ADAPTER_PAGE_SHIFT;
 rwq->wq_sig = !!(ucmd->flags & MLX5_WQ_FLAG_SIGNATURE);

 mlx5_ib_dbg(
  dev,
  "addr 0x%llx, size %zd, npages %zu, page_size %ld, ncont %d, offset %d\n",
  (unsigned long long)ucmd->buf_addr, rwq->buf_size,
  ib_umem_num_pages(rwq->umem), page_size, rwq->rq_num_pas,
  offset);

 err = mlx5_ib_db_map_user(ucontext, ucmd->db_addr, &rwq->db);
 if (err) {
  mlx5_ib_dbg(dev, "map failed\n");
  goto err_umem;
 }

 return 0;

err_umem:
 ib_umem_release(rwq->umem);
 return err;
}

static int adjust_bfregn(struct mlx5_ib_dev *dev,
    struct mlx5_bfreg_info *bfregi, int bfregn)
{
 return bfregn / MLX5_NON_FP_BFREGS_PER_UAR * MLX5_BFREGS_PER_UAR +
    bfregn % MLX5_NON_FP_BFREGS_PER_UAR;
}

static int _create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd,
      struct mlx5_ib_qp *qp, struct ib_udata *udata,
      struct ib_qp_init_attr *attr, u32 **in,
      struct mlx5_ib_create_qp_resp *resp, int *inlen,
      struct mlx5_ib_qp_base *base,
      struct mlx5_ib_create_qp *ucmd)
{
 struct mlx5_ib_ucontext *context;
 struct mlx5_ib_ubuffer *ubuffer = &base->ubuffer;
 unsigned int page_offset_quantized = 0;
 unsigned long page_size = 0;
 int uar_index = 0;
 int bfregn;
 int ncont = 0;
 __be64 *pas;
 void *qpc;
 int err;
 u16 uid;
 u32 uar_flags;

 context = rdma_udata_to_drv_context(udata, struct mlx5_ib_ucontext,
         ibucontext);
 uar_flags = qp->flags_en &
      (MLX5_QP_FLAG_UAR_PAGE_INDEX | MLX5_QP_FLAG_BFREG_INDEX);
 switch (uar_flags) {
 case MLX5_QP_FLAG_UAR_PAGE_INDEX:
  uar_index = ucmd->bfreg_index;
  bfregn = MLX5_IB_INVALID_BFREG;
  break;
 case MLX5_QP_FLAG_BFREG_INDEX:
  uar_index = bfregn_to_uar_index(dev, &context->bfregi,
      ucmd->bfreg_index, true);
  if (uar_index < 0)
   return uar_index;
  bfregn = MLX5_IB_INVALID_BFREG;
  break;
 case 0:
  if (qp->flags & IB_QP_CREATE_CROSS_CHANNEL)
   return -EINVAL;
  bfregn = alloc_bfreg(dev, &context->bfregi);
  if (bfregn < 0)
   return bfregn;
  break;
 default:
  return -EINVAL;
 }

 mlx5_ib_dbg(dev, "bfregn 0x%x, uar_index 0x%x\n", bfregn, uar_index);
 if (bfregn != MLX5_IB_INVALID_BFREG)
  uar_index = bfregn_to_uar_index(dev, &context->bfregi, bfregn,
      false);

 qp->rq.offset = 0;
 qp->sq.wqe_shift = ilog2(MLX5_SEND_WQE_BB);
 qp->sq.offset = qp->rq.wqe_cnt << qp->rq.wqe_shift;

 err = set_user_buf_size(dev, qp, ucmd, base, attr);
 if (err)
  goto err_bfreg;

 if (ucmd->buf_addr && ubuffer->buf_size) {
  ubuffer->buf_addr = ucmd->buf_addr;
  ubuffer->umem = ib_umem_get(&dev->ib_dev, ubuffer->buf_addr,
         ubuffer->buf_size, 0);
  if (IS_ERR(ubuffer->umem)) {
   err = PTR_ERR(ubuffer->umem);
   goto err_bfreg;
  }
  page_size = mlx5_umem_find_best_quantized_pgoff(
   ubuffer->umem, qpc, log_page_size,
   MLX5_ADAPTER_PAGE_SHIFT, page_offset, 64,
   &page_offset_quantized);
  if (!page_size) {
   err = -EINVAL;
   goto err_umem;
  }
  ncont = ib_umem_num_dma_blocks(ubuffer->umem, page_size);
 } else {
  ubuffer->umem = NULL;
 }

 *inlen = MLX5_ST_SZ_BYTES(create_qp_in) +
   MLX5_FLD_SZ_BYTES(create_qp_in, pas[0]) * ncont;
 *in = kvzalloc(*inlen, GFP_KERNEL);
 if (!*in) {
  err = -ENOMEM;
  goto err_umem;
 }

 uid = (attr->qp_type != IB_QPT_XRC_INI) ? to_mpd(pd)->uid : 0;
 MLX5_SET(create_qp_in, *in, uid, uid);
 qpc = MLX5_ADDR_OF(create_qp_in, *in, qpc);
 pas = (__be64 *)MLX5_ADDR_OF(create_qp_in, *in, pas);
 if (ubuffer->umem) {
  mlx5_ib_populate_pas(ubuffer->umem, page_size, pas, 0);
  MLX5_SET(qpc, qpc, log_page_size,
    order_base_2(page_size) - MLX5_ADAPTER_PAGE_SHIFT);
  MLX5_SET(qpc, qpc, page_offset, page_offset_quantized);
 }
 MLX5_SET(qpc, qpc, uar_page, uar_index);
 if (bfregn != MLX5_IB_INVALID_BFREG)
  resp->bfreg_index = adjust_bfregn(dev, &context->bfregi, bfregn);
 else
  resp->bfreg_index = MLX5_IB_INVALID_BFREG;
 qp->bfregn = bfregn;

 err = mlx5_ib_db_map_user(context, ucmd->db_addr, &qp->db);
 if (err) {
  mlx5_ib_dbg(dev, "map failed\n");
  goto err_free;
 }

 return 0;

err_free:
 kvfree(*in);

err_umem:
 ib_umem_release(ubuffer->umem);

err_bfreg:
 if (bfregn != MLX5_IB_INVALID_BFREG)
  mlx5_ib_free_bfreg(dev, &context->bfregi, bfregn);
 return err;
}

static void destroy_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
         struct mlx5_ib_qp_base *base, struct ib_udata *udata)
{
 struct mlx5_ib_ucontext *context = rdma_udata_to_drv_context(
  udata, struct mlx5_ib_ucontext, ibucontext);

 if (udata) {
  /* User QP */
  mlx5_ib_db_unmap_user(context, &qp->db);
  ib_umem_release(base->ubuffer.umem);

  /*
 * Free only the BFREGs which are handled by the kernel.
 * BFREGs of UARs allocated dynamically are handled by user.
 */

  if (qp->bfregn != MLX5_IB_INVALID_BFREG)
   mlx5_ib_free_bfreg(dev, &context->bfregi, qp->bfregn);
  return;
 }

 /* Kernel QP */
 kvfree(qp->sq.wqe_head);
 kvfree(qp->sq.w_list);
 kvfree(qp->sq.wrid);
 kvfree(qp->sq.wr_data);
 kvfree(qp->rq.wrid);
 if (qp->db.db)
  mlx5_db_free(dev->mdev, &qp->db);
 if (qp->buf.frags)
  mlx5_frag_buf_free(dev->mdev, &qp->buf);
}

static int _create_kernel_qp(struct mlx5_ib_dev *dev,
        struct ib_qp_init_attr *init_attr,
        struct mlx5_ib_qp *qp, u32 **in, int *inlen,
        struct mlx5_ib_qp_base *base)
{
 int uar_index;
 void *qpc;
 int err;

 if (init_attr->qp_type == MLX5_IB_QPT_REG_UMR)
  qp->bf.bfreg = &dev->fp_bfreg;
 else
  qp->bf.bfreg = &dev->bfreg;

 /* We need to divide by two since each register is comprised of
 * two buffers of identical size, namely odd and even
 */

 qp->bf.buf_size = (1 << MLX5_CAP_GEN(dev->mdev, log_bf_reg_size)) / 2;
 uar_index = qp->bf.bfreg->index;

 err = calc_sq_size(dev, init_attr, qp);
 if (err < 0) {
  mlx5_ib_dbg(dev, "err %d\n", err);
  return err;
 }

 qp->rq.offset = 0;
 qp->sq.offset = qp->rq.wqe_cnt << qp->rq.wqe_shift;
 base->ubuffer.buf_size = err + (qp->rq.wqe_cnt << qp->rq.wqe_shift);

 err = mlx5_frag_buf_alloc_node(dev->mdev, base->ubuffer.buf_size,
           &qp->buf, dev->mdev->priv.numa_node);
 if (err) {
  mlx5_ib_dbg(dev, "err %d\n", err);
  return err;
 }

 if (qp->rq.wqe_cnt)
  mlx5_init_fbc(qp->buf.frags, qp->rq.wqe_shift,
         ilog2(qp->rq.wqe_cnt), &qp->rq.fbc);

 if (qp->sq.wqe_cnt) {
  int sq_strides_offset = (qp->sq.offset  & (PAGE_SIZE - 1)) /
     MLX5_SEND_WQE_BB;
  mlx5_init_fbc_offset(qp->buf.frags +
         (qp->sq.offset / PAGE_SIZE),
         ilog2(MLX5_SEND_WQE_BB),
         ilog2(qp->sq.wqe_cnt),
         sq_strides_offset, &qp->sq.fbc);

  qp->sq.cur_edge = get_sq_edge(&qp->sq, 0);
 }

 *inlen = MLX5_ST_SZ_BYTES(create_qp_in) +
   MLX5_FLD_SZ_BYTES(create_qp_in, pas[0]) * qp->buf.npages;
 *in = kvzalloc(*inlen, GFP_KERNEL);
 if (!*in) {
  err = -ENOMEM;
  goto err_buf;
 }

 qpc = MLX5_ADDR_OF(create_qp_in, *in, qpc);
 MLX5_SET(qpc, qpc, uar_page, uar_index);
 MLX5_SET(qpc, qpc, ts_format, mlx5_get_qp_default_ts(dev->mdev));
 MLX5_SET(qpc, qpc, log_page_size, qp->buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT);

 /* Set "fast registration enabled" for all kernel QPs */
 MLX5_SET(qpc, qpc, fre, 1);
 MLX5_SET(qpc, qpc, rlky, 1);

 if (qp->flags & MLX5_IB_QP_CREATE_SQPN_QP1)
  MLX5_SET(qpc, qpc, deth_sqpn, 1);

 mlx5_fill_page_frag_array(&qp->buf,
      (__be64 *)MLX5_ADDR_OF(create_qp_in,
        *in, pas));

 err = mlx5_db_alloc(dev->mdev, &qp->db);
 if (err) {
  mlx5_ib_dbg(dev, "err %d\n", err);
  goto err_free;
 }

 qp->sq.wrid = kvmalloc_array(qp->sq.wqe_cnt,
         sizeof(*qp->sq.wrid), GFP_KERNEL);
 qp->sq.wr_data = kvmalloc_array(qp->sq.wqe_cnt,
     sizeof(*qp->sq.wr_data), GFP_KERNEL);
 qp->rq.wrid = kvmalloc_array(qp->rq.wqe_cnt,
         sizeof(*qp->rq.wrid), GFP_KERNEL);
 qp->sq.w_list = kvmalloc_array(qp->sq.wqe_cnt,
           sizeof(*qp->sq.w_list), GFP_KERNEL);
 qp->sq.wqe_head = kvmalloc_array(qp->sq.wqe_cnt,
      sizeof(*qp->sq.wqe_head), GFP_KERNEL);

 if (!qp->sq.wrid || !qp->sq.wr_data || !qp->rq.wrid ||
     !qp->sq.w_list || !qp->sq.wqe_head) {
  err = -ENOMEM;
  goto err_wrid;
 }

 return 0;

err_wrid:
 kvfree(qp->sq.wqe_head);
 kvfree(qp->sq.w_list);
 kvfree(qp->sq.wrid);
 kvfree(qp->sq.wr_data);
 kvfree(qp->rq.wrid);
 mlx5_db_free(dev->mdev, &qp->db);

err_free:
 kvfree(*in);

err_buf:
 mlx5_frag_buf_free(dev->mdev, &qp->buf);
 return err;
}

static u32 get_rx_type(struct mlx5_ib_qp *qp, struct ib_qp_init_attr *attr)
{
 if (attr->srq || (qp->type == IB_QPT_XRC_TGT) ||
     (qp->type == MLX5_IB_QPT_DCI) || (qp->type == IB_QPT_XRC_INI))
  return MLX5_SRQ_RQ;
 else if (!qp->has_rq)
  return MLX5_ZERO_LEN_RQ;

 return MLX5_NON_ZERO_RQ;
}

static int create_raw_packet_qp_tis(struct mlx5_ib_dev *dev,
        struct mlx5_ib_qp *qp,
        struct mlx5_ib_sq *sq, u32 tdn,
        struct ib_pd *pd)
{
 u32 in[MLX5_ST_SZ_DW(create_tis_in)] = {};
 void *tisc = MLX5_ADDR_OF(create_tis_in, in, ctx);

 MLX5_SET(create_tis_in, in, uid, to_mpd(pd)->uid);
 MLX5_SET(tisc, tisc, transport_domain, tdn);
 if (!mlx5_ib_lag_should_assign_affinity(dev) &&
     mlx5_lag_is_lacp_owner(dev->mdev))
  MLX5_SET(tisc, tisc, strict_lag_tx_port_affinity, 1);
 if (qp->flags & IB_QP_CREATE_SOURCE_QPN)
  MLX5_SET(tisc, tisc, underlay_qpn, qp->underlay_qpn);

 return mlx5_core_create_tis(dev->mdev, in, &sq->tisn);
}

static void destroy_raw_packet_qp_tis(struct mlx5_ib_dev *dev,
          struct mlx5_ib_sq *sq, struct ib_pd *pd)
{
 mlx5_cmd_destroy_tis(dev->mdev, sq->tisn, to_mpd(pd)->uid);
}

static void destroy_flow_rule_vport_sq(struct mlx5_ib_sq *sq)
{
 if (sq->flow_rule)
  mlx5_del_flow_rules(sq->flow_rule);
 sq->flow_rule = NULL;
}

static bool fr_supported(int ts_cap)
{
 return ts_cap == MLX5_TIMESTAMP_FORMAT_CAP_FREE_RUNNING ||
        ts_cap == MLX5_TIMESTAMP_FORMAT_CAP_FREE_RUNNING_AND_REAL_TIME;
}

static int get_ts_format(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq,
    bool fr_sup, bool rt_sup)
{
 if (cq->private_flags & MLX5_IB_CQ_PR_FLAGS_REAL_TIME_TS) {
  if (!rt_sup) {
   mlx5_ib_dbg(dev,
        "Real time TS format is not supported\n");
   return -EOPNOTSUPP;
  }
  return MLX5_TIMESTAMP_FORMAT_REAL_TIME;
 }
 if (cq->create_flags & IB_UVERBS_CQ_FLAGS_TIMESTAMP_COMPLETION) {
  if (!fr_sup) {
   mlx5_ib_dbg(dev,
        "Free running TS format is not supported\n");
   return -EOPNOTSUPP;
  }
  return MLX5_TIMESTAMP_FORMAT_FREE_RUNNING;
 }
 return fr_sup ? MLX5_TIMESTAMP_FORMAT_FREE_RUNNING :
   MLX5_TIMESTAMP_FORMAT_DEFAULT;
}

static int get_rq_ts_format(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *recv_cq)
{
 u8 ts_cap = MLX5_CAP_GEN(dev->mdev, rq_ts_format);

 return get_ts_format(dev, recv_cq, fr_supported(ts_cap),
        rt_supported(ts_cap));
}

static int get_sq_ts_format(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *send_cq)
{
 u8 ts_cap = MLX5_CAP_GEN(dev->mdev, sq_ts_format);

 return get_ts_format(dev, send_cq, fr_supported(ts_cap),
        rt_supported(ts_cap));
}

static int get_qp_ts_format(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *send_cq,
       struct mlx5_ib_cq *recv_cq)
{
 u8 ts_cap = MLX5_CAP_ROCE(dev->mdev, qp_ts_format);
 bool fr_sup = fr_supported(ts_cap);
 bool rt_sup = rt_supported(ts_cap);
 u8 default_ts = fr_sup ? MLX5_TIMESTAMP_FORMAT_FREE_RUNNING :
     MLX5_TIMESTAMP_FORMAT_DEFAULT;
 int send_ts_format =
  send_cq ? get_ts_format(dev, send_cq, fr_sup, rt_sup) :
     default_ts;
 int recv_ts_format =
  recv_cq ? get_ts_format(dev, recv_cq, fr_sup, rt_sup) :
     default_ts;

 if (send_ts_format < 0 || recv_ts_format < 0)
  return -EOPNOTSUPP;

 if (send_ts_format != MLX5_TIMESTAMP_FORMAT_DEFAULT &&
     recv_ts_format != MLX5_TIMESTAMP_FORMAT_DEFAULT &&
     send_ts_format != recv_ts_format) {
  mlx5_ib_dbg(
   dev,
   "The send ts_format does not match the receive ts_format\n");
  return -EOPNOTSUPP;
 }

 return send_ts_format == default_ts ? recv_ts_format : send_ts_format;
}

static int create_raw_packet_qp_sq(struct mlx5_ib_dev *dev,
       struct ib_udata *udata,
       struct mlx5_ib_sq *sq, void *qpin,
       struct ib_pd *pd, struct mlx5_ib_cq *cq)
{
 struct mlx5_ib_ubuffer *ubuffer = &sq->ubuffer;
 __be64 *pas;
 void *in;
 void *sqc;
 void *qpc = MLX5_ADDR_OF(create_qp_in, qpin, qpc);
 void *wq;
 int inlen;
 int err;
 unsigned int page_offset_quantized;
 unsigned long page_size;
 int ts_format;

 ts_format = get_sq_ts_format(dev, cq);
 if (ts_format < 0)
  return ts_format;

 sq->ubuffer.umem = ib_umem_get(&dev->ib_dev, ubuffer->buf_addr,
           ubuffer->buf_size, 0);
 if (IS_ERR(sq->ubuffer.umem))
  return PTR_ERR(sq->ubuffer.umem);
 page_size = mlx5_umem_find_best_quantized_pgoff(
  ubuffer->umem, wq, log_wq_pg_sz, MLX5_ADAPTER_PAGE_SHIFT,
  page_offset, 64, &page_offset_quantized);
 if (!page_size) {
  err = -EINVAL;
  goto err_umem;
 }

 inlen = MLX5_ST_SZ_BYTES(create_sq_in) +
  sizeof(u64) *
   ib_umem_num_dma_blocks(sq->ubuffer.umem, page_size);
 in = kvzalloc(inlen, GFP_KERNEL);
 if (!in) {
  err = -ENOMEM;
  goto err_umem;
 }

 MLX5_SET(create_sq_in, in, uid, to_mpd(pd)->uid);
 sqc = MLX5_ADDR_OF(create_sq_in, in, ctx);
 MLX5_SET(sqc, sqc, flush_in_error_en, 1);
 if (MLX5_CAP_ETH(dev->mdev, multi_pkt_send_wqe))
  MLX5_SET(sqc, sqc, allow_multi_pkt_send_wqe, 1);
 MLX5_SET(sqc, sqc, state, MLX5_SQC_STATE_RST);
 MLX5_SET(sqc, sqc, ts_format, ts_format);
 MLX5_SET(sqc, sqc, user_index, MLX5_GET(qpc, qpc, user_index));
 MLX5_SET(sqc, sqc, cqn, MLX5_GET(qpc, qpc, cqn_snd));
 MLX5_SET(sqc, sqc, tis_lst_sz, 1);
 MLX5_SET(sqc, sqc, tis_num_0, sq->tisn);
 if (MLX5_CAP_GEN(dev->mdev, eth_net_offloads) &&
     MLX5_CAP_ETH(dev->mdev, swp))
  MLX5_SET(sqc, sqc, allow_swp, 1);

 wq = MLX5_ADDR_OF(sqc, sqc, wq);
 MLX5_SET(wq, wq, wq_type, MLX5_WQ_TYPE_CYCLIC);
 MLX5_SET(wq, wq, pd, MLX5_GET(qpc, qpc, pd));
 MLX5_SET(wq, wq, uar_page, MLX5_GET(qpc, qpc, uar_page));
 MLX5_SET64(wq, wq, dbr_addr, MLX5_GET64(qpc, qpc, dbr_addr));
 MLX5_SET(wq, wq, log_wq_stride, ilog2(MLX5_SEND_WQE_BB));
 MLX5_SET(wq, wq, log_wq_sz, MLX5_GET(qpc, qpc, log_sq_size));
 MLX5_SET(wq, wq, log_wq_pg_sz,
   order_base_2(page_size) - MLX5_ADAPTER_PAGE_SHIFT);
 MLX5_SET(wq, wq, page_offset, page_offset_quantized);

 pas = (__be64 *)MLX5_ADDR_OF(wq, wq, pas);
 mlx5_ib_populate_pas(sq->ubuffer.umem, page_size, pas, 0);

 err = mlx5_core_create_sq_tracked(dev, in, inlen, &sq->base.mqp);

 kvfree(in);

 if (err)
  goto err_umem;

 return 0;

err_umem:
 ib_umem_release(sq->ubuffer.umem);
 sq->ubuffer.umem = NULL;

 return err;
}

static void destroy_raw_packet_qp_sq(struct mlx5_ib_dev *dev,
         struct mlx5_ib_sq *sq)
{
 destroy_flow_rule_vport_sq(sq);
 mlx5_core_destroy_sq_tracked(dev, &sq->base.mqp);
 ib_umem_release(sq->ubuffer.umem);
}

static int create_raw_packet_qp_rq(struct mlx5_ib_dev *dev,
       struct mlx5_ib_rq *rq, void *qpin,
       struct ib_pd *pd, struct mlx5_ib_cq *cq)
{
 struct mlx5_ib_qp *mqp = rq->base.container_mibqp;
 __be64 *pas;
 void *in;
 void *rqc;
 void *wq;
 void *qpc = MLX5_ADDR_OF(create_qp_in, qpin, qpc);
 struct ib_umem *umem = rq->base.ubuffer.umem;
 unsigned int page_offset_quantized;
 unsigned long page_size = 0;
 int ts_format;
 size_t inlen;
 int err;

 ts_format = get_rq_ts_format(dev, cq);
 if (ts_format < 0)
  return ts_format;

 page_size = mlx5_umem_find_best_quantized_pgoff(umem, wq, log_wq_pg_sz,
       MLX5_ADAPTER_PAGE_SHIFT,
       page_offset, 64,
       &page_offset_quantized);
 if (!page_size)
  return -EINVAL;

 inlen = MLX5_ST_SZ_BYTES(create_rq_in) +
  sizeof(u64) * ib_umem_num_dma_blocks(umem, page_size);
 in = kvzalloc(inlen, GFP_KERNEL);
 if (!in)
  return -ENOMEM;

 MLX5_SET(create_rq_in, in, uid, to_mpd(pd)->uid);
 rqc = MLX5_ADDR_OF(create_rq_in, in, ctx);
 if (!(rq->flags & MLX5_IB_RQ_CVLAN_STRIPPING))
  MLX5_SET(rqc, rqc, vsd, 1);
 MLX5_SET(rqc, rqc, mem_rq_type, MLX5_RQC_MEM_RQ_TYPE_MEMORY_RQ_INLINE);
 MLX5_SET(rqc, rqc, state, MLX5_RQC_STATE_RST);
 MLX5_SET(rqc, rqc, ts_format, ts_format);
 MLX5_SET(rqc, rqc, flush_in_error_en, 1);
 MLX5_SET(rqc, rqc, user_index, MLX5_GET(qpc, qpc, user_index));
 MLX5_SET(rqc, rqc, cqn, MLX5_GET(qpc, qpc, cqn_rcv));

 if (mqp->flags & IB_QP_CREATE_SCATTER_FCS)
  MLX5_SET(rqc, rqc, scatter_fcs, 1);

 wq = MLX5_ADDR_OF(rqc, rqc, wq);
 MLX5_SET(wq, wq, wq_type, MLX5_WQ_TYPE_CYCLIC);
 if (rq->flags & MLX5_IB_RQ_PCI_WRITE_END_PADDING)
  MLX5_SET(wq, wq, end_padding_mode, MLX5_WQ_END_PAD_MODE_ALIGN);
 MLX5_SET(wq, wq, page_offset, page_offset_quantized);
 MLX5_SET(wq, wq, pd, MLX5_GET(qpc, qpc, pd));
 MLX5_SET64(wq, wq, dbr_addr, MLX5_GET64(qpc, qpc, dbr_addr));
 MLX5_SET(wq, wq, log_wq_stride, MLX5_GET(qpc, qpc, log_rq_stride) + 4);
 MLX5_SET(wq, wq, log_wq_pg_sz,
   order_base_2(page_size) - MLX5_ADAPTER_PAGE_SHIFT);
 MLX5_SET(wq, wq, log_wq_sz, MLX5_GET(qpc, qpc, log_rq_size));

 pas = (__be64 *)MLX5_ADDR_OF(wq, wq, pas);
 mlx5_ib_populate_pas(umem, page_size, pas, 0);

 err = mlx5_core_create_rq_tracked(dev, in, inlen, &rq->base.mqp);

 kvfree(in);

 return err;
}

static void destroy_raw_packet_qp_rq(struct mlx5_ib_dev *dev,
         struct mlx5_ib_rq *rq)
{
 mlx5_core_destroy_rq_tracked(dev, &rq->base.mqp);
}

static void destroy_raw_packet_qp_tir(struct mlx5_ib_dev *dev,
          struct mlx5_ib_rq *rq,
          u32 qp_flags_en,
          struct ib_pd *pd)
{
 if (qp_flags_en & (MLX5_QP_FLAG_TIR_ALLOW_SELF_LB_UC |
      MLX5_QP_FLAG_TIR_ALLOW_SELF_LB_MC))
  mlx5_ib_disable_lb(dev, falsetrue);
 mlx5_cmd_destroy_tir(dev->mdev, rq->tirn, to_mpd(pd)->uid);
}

static int create_raw_packet_qp_tir(struct mlx5_ib_dev *dev,
        struct mlx5_ib_rq *rq, u32 tdn,
        u32 *qp_flags_en, struct ib_pd *pd,
        u32 *out)
{
 u8 lb_flag = 0;
 u32 *in;
 void *tirc;
 int inlen;
 int err;

 inlen = MLX5_ST_SZ_BYTES(create_tir_in);
 in = kvzalloc(inlen, GFP_KERNEL);
 if (!in)
  return -ENOMEM;

 MLX5_SET(create_tir_in, in, uid, to_mpd(pd)->uid);
 tirc = MLX5_ADDR_OF(create_tir_in, in, ctx);
 MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_DIRECT);
 MLX5_SET(tirc, tirc, inline_rqn, rq->base.mqp.qpn);
 MLX5_SET(tirc, tirc, transport_domain, tdn);
 if (*qp_flags_en & MLX5_QP_FLAG_TUNNEL_OFFLOADS)
  MLX5_SET(tirc, tirc, tunneled_offload_en, 1);

 if (*qp_flags_en & MLX5_QP_FLAG_TIR_ALLOW_SELF_LB_UC)
  lb_flag |= MLX5_TIRC_SELF_LB_BLOCK_BLOCK_UNICAST;

 if (*qp_flags_en & MLX5_QP_FLAG_TIR_ALLOW_SELF_LB_MC)
  lb_flag |= MLX5_TIRC_SELF_LB_BLOCK_BLOCK_MULTICAST;

 if (dev->is_rep) {
  lb_flag |= MLX5_TIRC_SELF_LB_BLOCK_BLOCK_UNICAST;
  *qp_flags_en |= MLX5_QP_FLAG_TIR_ALLOW_SELF_LB_UC;
 }

 MLX5_SET(tirc, tirc, self_lb_block, lb_flag);
 MLX5_SET(create_tir_in, in, opcode, MLX5_CMD_OP_CREATE_TIR);
 err = mlx5_cmd_exec_inout(dev->mdev, create_tir, in, out);
 rq->tirn = MLX5_GET(create_tir_out, out, tirn);
 if (!err && MLX5_GET(tirc, tirc, self_lb_block)) {
  err = mlx5_ib_enable_lb(dev, falsetrue);

  if (err)
   destroy_raw_packet_qp_tir(dev, rq, 0, pd);
 }
 kvfree(in);

 return err;
}

static int create_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
    u32 *in, size_t inlen, struct ib_pd *pd,
    struct ib_udata *udata,
    struct mlx5_ib_create_qp_resp *resp,
    struct ib_qp_init_attr *init_attr)
{
 struct mlx5_ib_raw_packet_qp *raw_packet_qp = &qp->raw_packet_qp;
 struct mlx5_ib_sq *sq = &raw_packet_qp->sq;
 struct mlx5_ib_rq *rq = &raw_packet_qp->rq;
 struct mlx5_ib_ucontext *mucontext = rdma_udata_to_drv_context(
  udata, struct mlx5_ib_ucontext, ibucontext);
 int err;
 u32 tdn = mucontext->tdn;
 u16 uid = to_mpd(pd)->uid;
 u32 out[MLX5_ST_SZ_DW(create_tir_out)] = {};

 if (!qp->sq.wqe_cnt && !qp->rq.wqe_cnt)
  return -EINVAL;
 if (qp->sq.wqe_cnt) {
  err = create_raw_packet_qp_tis(dev, qp, sq, tdn, pd);
  if (err)
   return err;

  err = create_raw_packet_qp_sq(dev, udata, sq, in, pd,
           to_mcq(init_attr->send_cq));
  if (err)
   goto err_destroy_tis;

  if (uid) {
   resp->tisn = sq->tisn;
   resp->comp_mask |= MLX5_IB_CREATE_QP_RESP_MASK_TISN;
   resp->sqn = sq->base.mqp.qpn;
   resp->comp_mask |= MLX5_IB_CREATE_QP_RESP_MASK_SQN;
  }

  sq->base.container_mibqp = qp;
  sq->base.mqp.event = mlx5_ib_qp_event;
 }

 if (qp->rq.wqe_cnt) {
  rq->base.container_mibqp = qp;

  if (qp->flags & IB_QP_CREATE_CVLAN_STRIPPING)
   rq->flags |= MLX5_IB_RQ_CVLAN_STRIPPING;
  if (qp->flags & IB_QP_CREATE_PCI_WRITE_END_PADDING)
   rq->flags |= MLX5_IB_RQ_PCI_WRITE_END_PADDING;
  err = create_raw_packet_qp_rq(dev, rq, in, pd,
           to_mcq(init_attr->recv_cq));
  if (err)
   goto err_destroy_sq;

  err = create_raw_packet_qp_tir(dev, rq, tdn, &qp->flags_en, pd,
            out);
  if (err)
   goto err_destroy_rq;

  if (uid) {
   resp->rqn = rq->base.mqp.qpn;
   resp->comp_mask |= MLX5_IB_CREATE_QP_RESP_MASK_RQN;
   resp->tirn = rq->tirn;
   resp->comp_mask |= MLX5_IB_CREATE_QP_RESP_MASK_TIRN;
   if (MLX5_CAP_FLOWTABLE_NIC_RX(dev->mdev, sw_owner) ||
       MLX5_CAP_FLOWTABLE_NIC_RX(dev->mdev, sw_owner_v2)) {
    resp->tir_icm_addr = MLX5_GET(
     create_tir_out, out, icm_address_31_0);
    resp->tir_icm_addr |=
     (u64)MLX5_GET(create_tir_out, out,
            icm_address_39_32)
     << 32;
    resp->tir_icm_addr |=
     (u64)MLX5_GET(create_tir_out, out,
            icm_address_63_40)
     << 40;
    resp->comp_mask |=
     MLX5_IB_CREATE_QP_RESP_MASK_TIR_ICM_ADDR;
   }
  }
 }

 qp->trans_qp.base.mqp.qpn = qp->sq.wqe_cnt ? sq->base.mqp.qpn :
           rq->base.mqp.qpn;
 return 0;

err_destroy_rq:
 destroy_raw_packet_qp_rq(dev, rq);
err_destroy_sq:
 if (!qp->sq.wqe_cnt)
  return err;
 destroy_raw_packet_qp_sq(dev, sq);
err_destroy_tis:
 destroy_raw_packet_qp_tis(dev, sq, pd);

 return err;
}

static void destroy_raw_packet_qp(struct mlx5_ib_dev *dev,
      struct mlx5_ib_qp *qp)
{
 struct mlx5_ib_raw_packet_qp *raw_packet_qp = &qp->raw_packet_qp;
 struct mlx5_ib_sq *sq = &raw_packet_qp->sq;
 struct mlx5_ib_rq *rq = &raw_packet_qp->rq;

 if (qp->rq.wqe_cnt) {
  destroy_raw_packet_qp_tir(dev, rq, qp->flags_en, qp->ibqp.pd);
  destroy_raw_packet_qp_rq(dev, rq);
 }

 if (qp->sq.wqe_cnt) {
  destroy_raw_packet_qp_sq(dev, sq);
  destroy_raw_packet_qp_tis(dev, sq, qp->ibqp.pd);
 }
}

static void raw_packet_qp_copy_info(struct mlx5_ib_qp *qp,
        struct mlx5_ib_raw_packet_qp *raw_packet_qp)
{
 struct mlx5_ib_sq *sq = &raw_packet_qp->sq;
 struct mlx5_ib_rq *rq = &raw_packet_qp->rq;

 sq->sq = &qp->sq;
 rq->rq = &qp->rq;
 sq->doorbell = &qp->db;
 rq->doorbell = &qp->db;
}

static void destroy_rss_raw_qp_tir(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp)
{
 if (qp->flags_en & (MLX5_QP_FLAG_TIR_ALLOW_SELF_LB_UC |
       MLX5_QP_FLAG_TIR_ALLOW_SELF_LB_MC))
  mlx5_ib_disable_lb(dev, falsetrue);
 mlx5_cmd_destroy_tir(dev->mdev, qp->rss_qp.tirn,
        to_mpd(qp->ibqp.pd)->uid);
}

struct mlx5_create_qp_params {
 struct ib_udata *udata;
 size_t inlen;
 size_t outlen;
 size_t ucmd_size;
 void *ucmd;
 u8 is_rss_raw : 1;
 struct ib_qp_init_attr *attr;
 u32 uidx;
 struct mlx5_ib_create_qp_resp resp;
};

static int create_rss_raw_qp_tir(struct mlx5_ib_dev *dev, struct ib_pd *pd,
     struct mlx5_ib_qp *qp,
     struct mlx5_create_qp_params *params)
{
 struct ib_qp_init_attr *init_attr = params->attr;
 struct mlx5_ib_create_qp_rss *ucmd = params->ucmd;
 struct ib_udata *udata = params->udata;
 struct mlx5_ib_ucontext *mucontext = rdma_udata_to_drv_context(
  udata, struct mlx5_ib_ucontext, ibucontext);
 int inlen;
 int outlen;
 int err;
 u32 *in;
 u32 *out;
 void *tirc;
 void *hfso;
 u32 selected_fields = 0;
 u32 outer_l4;
 u32 tdn = mucontext->tdn;
 u8 lb_flag = 0;

 if (ucmd->comp_mask) {
  mlx5_ib_dbg(dev, "invalid comp mask\n");
  return -EOPNOTSUPP;
 }

 if (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_INNER &&
     !(ucmd->flags & MLX5_QP_FLAG_TUNNEL_OFFLOADS)) {
  mlx5_ib_dbg(dev, "Tunnel offloads must be set for inner RSS\n");
  return -EOPNOTSUPP;
 }

 if (dev->is_rep)
  qp->flags_en |= MLX5_QP_FLAG_TIR_ALLOW_SELF_LB_UC;

 if (qp->flags_en & MLX5_QP_FLAG_TIR_ALLOW_SELF_LB_UC)
  lb_flag |= MLX5_TIRC_SELF_LB_BLOCK_BLOCK_UNICAST;

 if (qp->flags_en & MLX5_QP_FLAG_TIR_ALLOW_SELF_LB_MC)
  lb_flag |= MLX5_TIRC_SELF_LB_BLOCK_BLOCK_MULTICAST;

 inlen = MLX5_ST_SZ_BYTES(create_tir_in);
 outlen = MLX5_ST_SZ_BYTES(create_tir_out);
 in = kvzalloc(inlen + outlen, GFP_KERNEL);
 if (!in)
  return -ENOMEM;

 out = in + MLX5_ST_SZ_DW(create_tir_in);
 MLX5_SET(create_tir_in, in, uid, to_mpd(pd)->uid);
 tirc = MLX5_ADDR_OF(create_tir_in, in, ctx);
 MLX5_SET(tirc, tirc, disp_type,
   MLX5_TIRC_DISP_TYPE_INDIRECT);
 MLX5_SET(tirc, tirc, indirect_table,
   init_attr->rwq_ind_tbl->ind_tbl_num);
 MLX5_SET(tirc, tirc, transport_domain, tdn);

 hfso = MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_outer);

 if (ucmd->flags & MLX5_QP_FLAG_TUNNEL_OFFLOADS)
  MLX5_SET(tirc, tirc, tunneled_offload_en, 1);

 MLX5_SET(tirc, tirc, self_lb_block, lb_flag);

 if (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_INNER)
  hfso = MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_inner);
 else
  hfso = MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_outer);

 switch (ucmd->rx_hash_function) {
 case MLX5_RX_HASH_FUNC_TOEPLITZ:
 {
  void *rss_key = MLX5_ADDR_OF(tirc, tirc, rx_hash_toeplitz_key);
  size_t len = MLX5_FLD_SZ_BYTES(tirc, rx_hash_toeplitz_key);

  if (len != ucmd->rx_key_len) {
   err = -EINVAL;
   goto err;
  }

  MLX5_SET(tirc, tirc, rx_hash_fn, MLX5_RX_HASH_FN_TOEPLITZ);
  memcpy(rss_key, ucmd->rx_hash_key, len);
  break;
 }
 default:
  err = -EOPNOTSUPP;
  goto err;
 }

 if (!ucmd->rx_hash_fields_mask) {
  /* special case when this TIR serves as steering entry without hashing */
  if (!init_attr->rwq_ind_tbl->log_ind_tbl_size)
   goto create_tir;
  err = -EINVAL;
  goto err;
 }

 if (((ucmd->rx_hash_fields_mask & MLX5_RX_HASH_SRC_IPV4) ||
      (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_DST_IPV4)) &&
      ((ucmd->rx_hash_fields_mask & MLX5_RX_HASH_SRC_IPV6) ||
      (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_DST_IPV6))) {
  err = -EINVAL;
  goto err;
 }

 /* If none of IPV4 & IPV6 SRC/DST was set - this bit field is ignored */
 if ((ucmd->rx_hash_fields_mask & MLX5_RX_HASH_SRC_IPV4) ||
     (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_DST_IPV4))
  MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
    MLX5_L3_PROT_TYPE_IPV4);
 else if ((ucmd->rx_hash_fields_mask & MLX5_RX_HASH_SRC_IPV6) ||
   (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_DST_IPV6))
  MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
    MLX5_L3_PROT_TYPE_IPV6);

 outer_l4 = ((ucmd->rx_hash_fields_mask & MLX5_RX_HASH_SRC_PORT_TCP) ||
      (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_DST_PORT_TCP))
      << 0 |
     ((ucmd->rx_hash_fields_mask & MLX5_RX_HASH_SRC_PORT_UDP) ||
      (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_DST_PORT_UDP))
      << 1 |
     (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_IPSEC_SPI) << 2;

 /* Check that only one l4 protocol is set */
 if (outer_l4 & (outer_l4 - 1)) {
  err = -EINVAL;
  goto err;
 }

 /* If none of TCP & UDP SRC/DST was set - this bit field is ignored */
 if ((ucmd->rx_hash_fields_mask & MLX5_RX_HASH_SRC_PORT_TCP) ||
     (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_DST_PORT_TCP))
  MLX5_SET(rx_hash_field_select, hfso, l4_prot_type,
    MLX5_L4_PROT_TYPE_TCP);
 else if ((ucmd->rx_hash_fields_mask & MLX5_RX_HASH_SRC_PORT_UDP) ||
   (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_DST_PORT_UDP))
  MLX5_SET(rx_hash_field_select, hfso, l4_prot_type,
    MLX5_L4_PROT_TYPE_UDP);

 if ((ucmd->rx_hash_fields_mask & MLX5_RX_HASH_SRC_IPV4) ||
     (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_SRC_IPV6))
  selected_fields |= MLX5_HASH_FIELD_SEL_SRC_IP;

 if ((ucmd->rx_hash_fields_mask & MLX5_RX_HASH_DST_IPV4) ||
     (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_DST_IPV6))
  selected_fields |= MLX5_HASH_FIELD_SEL_DST_IP;

 if ((ucmd->rx_hash_fields_mask & MLX5_RX_HASH_SRC_PORT_TCP) ||
     (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_SRC_PORT_UDP))
  selected_fields |= MLX5_HASH_FIELD_SEL_L4_SPORT;

 if ((ucmd->rx_hash_fields_mask & MLX5_RX_HASH_DST_PORT_TCP) ||
     (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_DST_PORT_UDP))
  selected_fields |= MLX5_HASH_FIELD_SEL_L4_DPORT;

 if (ucmd->rx_hash_fields_mask & MLX5_RX_HASH_IPSEC_SPI)
  selected_fields |= MLX5_HASH_FIELD_SEL_IPSEC_SPI;

 MLX5_SET(rx_hash_field_select, hfso, selected_fields, selected_fields);

create_tir:
 MLX5_SET(create_tir_in, in, opcode, MLX5_CMD_OP_CREATE_TIR);
 err = mlx5_cmd_exec_inout(dev->mdev, create_tir, in, out);

 qp->rss_qp.tirn = MLX5_GET(create_tir_out, out, tirn);
 if (!err && MLX5_GET(tirc, tirc, self_lb_block)) {
  err = mlx5_ib_enable_lb(dev, falsetrue);

  if (err)
   mlx5_cmd_destroy_tir(dev->mdev, qp->rss_qp.tirn,
          to_mpd(pd)->uid);
 }

 if (err)
  goto err;

 if (mucontext->devx_uid) {
  params->resp.comp_mask |= MLX5_IB_CREATE_QP_RESP_MASK_TIRN;
  params->resp.tirn = qp->rss_qp.tirn;
  if (MLX5_CAP_FLOWTABLE_NIC_RX(dev->mdev, sw_owner) ||
      MLX5_CAP_FLOWTABLE_NIC_RX(dev->mdev, sw_owner_v2)) {
   params->resp.tir_icm_addr =
    MLX5_GET(create_tir_out, out, icm_address_31_0);
   params->resp.tir_icm_addr |=
    (u64)MLX5_GET(create_tir_out, out,
           icm_address_39_32)
    << 32;
   params->resp.tir_icm_addr |=
    (u64)MLX5_GET(create_tir_out, out,
           icm_address_63_40)
    << 40;
   params->resp.comp_mask |=
    MLX5_IB_CREATE_QP_RESP_MASK_TIR_ICM_ADDR;
  }
 }

 kvfree(in);
 /* qpn is reserved for that QP */
 qp->trans_qp.base.mqp.qpn = 0;
 qp->is_rss = true;
 return 0;

err:
 kvfree(in);
 return err;
}

static void configure_requester_scat_cqe(struct mlx5_ib_dev *dev,
      struct mlx5_ib_qp *qp,
      struct ib_qp_init_attr *init_attr,
      void *qpc)
{
 int scqe_sz;
 bool allow_scat_cqe = false;

 allow_scat_cqe = qp->flags_en & MLX5_QP_FLAG_ALLOW_SCATTER_CQE;

 if (!allow_scat_cqe && init_attr->sq_sig_type != IB_SIGNAL_ALL_WR)
  return;

 scqe_sz = mlx5_ib_get_cqe_size(init_attr->send_cq);
 if (scqe_sz == 128) {
  MLX5_SET(qpc, qpc, cs_req, MLX5_REQ_SCAT_DATA64_CQE);
  return;
 }

 if (init_attr->qp_type != MLX5_IB_QPT_DCI ||
     MLX5_CAP_GEN(dev->mdev, dc_req_scat_data_cqe))
  MLX5_SET(qpc, qpc, cs_req, MLX5_REQ_SCAT_DATA32_CQE);
}

static int atomic_size_to_mode(int size_mask)
{
 /* driver does not support atomic_size > 256B
 * and does not know how to translate bigger sizes
 */

 int supported_size_mask = size_mask & 0x1ff;
 int log_max_size;

 if (!supported_size_mask)
  return -EOPNOTSUPP;

 log_max_size = __fls(supported_size_mask);

 if (log_max_size > 3)
  return log_max_size;

 return MLX5_ATOMIC_MODE_8B;
}

static int get_atomic_mode(struct mlx5_ib_dev *dev,
      struct mlx5_ib_qp *qp)
{
 u8 atomic_operations = MLX5_CAP_ATOMIC(dev->mdev, atomic_operations);
 u8 atomic = MLX5_CAP_GEN(dev->mdev, atomic);
 int atomic_mode = -EOPNOTSUPP;
 int atomic_size_mask;

 if (!atomic)
  return -EOPNOTSUPP;

 if (qp->type == MLX5_IB_QPT_DCT)
  atomic_size_mask = MLX5_CAP_ATOMIC(dev->mdev, atomic_size_dc);
 else
  atomic_size_mask = MLX5_CAP_ATOMIC(dev->mdev, atomic_size_qp);

 if ((atomic_operations & MLX5_ATOMIC_OPS_EXTENDED_CMP_SWAP) ||
     (atomic_operations & MLX5_ATOMIC_OPS_EXTENDED_FETCH_ADD))
  atomic_mode = atomic_size_to_mode(atomic_size_mask);

 if (atomic_mode <= 0 &&
     (atomic_operations & MLX5_ATOMIC_OPS_CMP_SWAP &&
      atomic_operations & MLX5_ATOMIC_OPS_FETCH_ADD))
  atomic_mode = MLX5_ATOMIC_MODE_IB_COMP;

 /* OOO DP QPs do not support larger than 8-Bytes atomic operations */
 if (atomic_mode > MLX5_ATOMIC_MODE_8B && qp->is_ooo_rq)
  atomic_mode = MLX5_ATOMIC_MODE_8B;

 return atomic_mode;
}

static int create_xrc_tgt_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
        struct mlx5_create_qp_params *params)
{
 struct ib_qp_init_attr *attr = params->attr;
 u32 uidx = params->uidx;
 struct mlx5_ib_resources *devr = &dev->devr;
 u32 out[MLX5_ST_SZ_DW(create_qp_out)] = {};
 int inlen = MLX5_ST_SZ_BYTES(create_qp_in);
 struct mlx5_core_dev *mdev = dev->mdev;
 struct mlx5_ib_qp_base *base;
 unsigned long flags;
 void *qpc;
 u32 *in;
 int err;

 if (attr->sq_sig_type == IB_SIGNAL_ALL_WR)
  qp->sq_signal_bits = MLX5_WQE_CTRL_CQ_UPDATE;

 in = kvzalloc(inlen, GFP_KERNEL);
 if (!in)
  return -ENOMEM;

 qpc = MLX5_ADDR_OF(create_qp_in, in, qpc);

 MLX5_SET(qpc, qpc, st, MLX5_QP_ST_XRC);
 MLX5_SET(qpc, qpc, pm_state, MLX5_QP_PM_MIGRATED);
 MLX5_SET(qpc, qpc, pd, to_mpd(devr->p0)->pdn);

 if (qp->flags & IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK)
  MLX5_SET(qpc, qpc, block_lb_mc, 1);
 if (qp->flags & IB_QP_CREATE_CROSS_CHANNEL)
  MLX5_SET(qpc, qpc, cd_master, 1);
 if (qp->flags & IB_QP_CREATE_MANAGED_SEND)
  MLX5_SET(qpc, qpc, cd_slave_send, 1);
 if (qp->flags & IB_QP_CREATE_MANAGED_RECV)
  MLX5_SET(qpc, qpc, cd_slave_receive, 1);

 MLX5_SET(qpc, qpc, ts_format, mlx5_get_qp_default_ts(dev->mdev));
 MLX5_SET(qpc, qpc, rq_type, MLX5_SRQ_RQ);
 MLX5_SET(qpc, qpc, no_sq, 1);
 MLX5_SET(qpc, qpc, cqn_rcv, to_mcq(devr->c0)->mcq.cqn);
 MLX5_SET(qpc, qpc, cqn_snd, to_mcq(devr->c0)->mcq.cqn);
 MLX5_SET(qpc, qpc, srqn_rmpn_xrqn, to_msrq(devr->s0)->msrq.srqn);
 MLX5_SET(qpc, qpc, xrcd, to_mxrcd(attr->xrcd)->xrcdn);
 MLX5_SET64(qpc, qpc, dbr_addr, qp->db.dma);

 /* 0xffffff means we ask to work with cqe version 0 */
 if (MLX5_CAP_GEN(mdev, cqe_version) == MLX5_CQE_VERSION_V1)
  MLX5_SET(qpc, qpc, user_index, uidx);

 if (qp->flags & IB_QP_CREATE_PCI_WRITE_END_PADDING) {
  MLX5_SET(qpc, qpc, end_padding_mode,
    MLX5_WQ_END_PAD_MODE_ALIGN);
  /* Special case to clean flag */
  qp->flags &= ~IB_QP_CREATE_PCI_WRITE_END_PADDING;
 }

 base = &qp->trans_qp.base;
 err = mlx5_qpc_create_qp(dev, &base->mqp, in, inlen, out);
 kvfree(in);
 if (err)
  return err;

 base->container_mibqp = qp;
 base->mqp.event = mlx5_ib_qp_event;
 if (MLX5_CAP_GEN(mdev, ece_support))
  params->resp.ece_options = MLX5_GET(create_qp_out, out, ece);

 spin_lock_irqsave(&dev->reset_flow_resource_lock, flags);
 list_add_tail(&qp->qps_list, &dev->qp_list);
 spin_unlock_irqrestore(&dev->reset_flow_resource_lock, flags);

 qp->trans_qp.xrcdn = to_mxrcd(attr->xrcd)->xrcdn;
 return 0;
}

static int create_dci(struct mlx5_ib_dev *dev, struct ib_pd *pd,
        struct mlx5_ib_qp *qp,
        struct mlx5_create_qp_params *params)
{
 struct ib_qp_init_attr *init_attr = params->attr;
 struct mlx5_ib_create_qp *ucmd = params->ucmd;
 u32 out[MLX5_ST_SZ_DW(create_qp_out)] = {};
 struct ib_udata *udata = params->udata;
 u32 uidx = params->uidx;
 struct mlx5_ib_resources *devr = &dev->devr;
 int inlen = MLX5_ST_SZ_BYTES(create_qp_in);
 struct mlx5_core_dev *mdev = dev->mdev;
 struct mlx5_ib_cq *send_cq;
 struct mlx5_ib_cq *recv_cq;
 unsigned long flags;
 struct mlx5_ib_qp_base *base;
 int ts_format;
 int mlx5_st;
 void *qpc;
 u32 *in;
 int err;

 spin_lock_init(&qp->sq.lock);
 spin_lock_init(&qp->rq.lock);

 mlx5_st = to_mlx5_st(qp->type);
 if (mlx5_st < 0)
  return -EINVAL;

 if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR)
  qp->sq_signal_bits = MLX5_WQE_CTRL_CQ_UPDATE;

 base = &qp->trans_qp.base;

 qp->has_rq = qp_has_rq(init_attr);
 err = set_rq_size(dev, &init_attr->cap, qp->has_rq, qp, ucmd);
 if (err) {
  mlx5_ib_dbg(dev, "err %d\n", err);
  return err;
 }

 if (ucmd->rq_wqe_shift != qp->rq.wqe_shift ||
     ucmd->rq_wqe_count != qp->rq.wqe_cnt)
  return -EINVAL;

 if (ucmd->sq_wqe_count > (1 << MLX5_CAP_GEN(mdev, log_max_qp_sz)))
  return -EINVAL;

 ts_format = get_qp_ts_format(dev, to_mcq(init_attr->send_cq),
         to_mcq(init_attr->recv_cq));

 if (ts_format < 0)
  return ts_format;

 err = _create_user_qp(dev, pd, qp, udata, init_attr, &in, ¶ms->resp,
         &inlen, base, ucmd);
 if (err)
  return err;

 if (MLX5_CAP_GEN(mdev, ece_support))
  MLX5_SET(create_qp_in, in, ece, ucmd->ece_options);
 qpc = MLX5_ADDR_OF(create_qp_in, in, qpc);

 MLX5_SET(qpc, qpc, st, mlx5_st);
 MLX5_SET(qpc, qpc, pm_state, MLX5_QP_PM_MIGRATED);
 MLX5_SET(qpc, qpc, pd, to_mpd(pd)->pdn);

 if (qp->flags_en & MLX5_QP_FLAG_SIGNATURE)
  MLX5_SET(qpc, qpc, wq_signature, 1);

 if (qp->flags & IB_QP_CREATE_CROSS_CHANNEL)
  MLX5_SET(qpc, qpc, cd_master, 1);
 if (qp->flags & IB_QP_CREATE_MANAGED_SEND)
  MLX5_SET(qpc, qpc, cd_slave_send, 1);
 if (qp->flags_en & MLX5_QP_FLAG_SCATTER_CQE)
  configure_requester_scat_cqe(dev, qp, init_attr, qpc);

 if (qp->rq.wqe_cnt) {
  MLX5_SET(qpc, qpc, log_rq_stride, qp->rq.wqe_shift - 4);
  MLX5_SET(qpc, qpc, log_rq_size, ilog2(qp->rq.wqe_cnt));
 }

 if (qp->flags_en & MLX5_QP_FLAG_DCI_STREAM) {
  MLX5_SET(qpc, qpc, log_num_dci_stream_channels,
    ucmd->dci_streams.log_num_concurent);
  MLX5_SET(qpc, qpc, log_num_dci_errored_streams,
    ucmd->dci_streams.log_num_errored);
 }

 MLX5_SET(qpc, qpc, ts_format, ts_format);
 MLX5_SET(qpc, qpc, rq_type, get_rx_type(qp, init_attr));

 MLX5_SET(qpc, qpc, log_sq_size, ilog2(qp->sq.wqe_cnt));

 /* Set default resources */
 if (init_attr->srq) {
  MLX5_SET(qpc, qpc, xrcd, devr->xrcdn0);
  MLX5_SET(qpc, qpc, srqn_rmpn_xrqn,
    to_msrq(init_attr->srq)->msrq.srqn);
 } else {
  MLX5_SET(qpc, qpc, xrcd, devr->xrcdn1);
  MLX5_SET(qpc, qpc, srqn_rmpn_xrqn,
    to_msrq(devr->s1)->msrq.srqn);
 }

 if (init_attr->send_cq)
  MLX5_SET(qpc, qpc, cqn_snd,
    to_mcq(init_attr->send_cq)->mcq.cqn);

 if (init_attr->recv_cq)
  MLX5_SET(qpc, qpc, cqn_rcv,
    to_mcq(init_attr->recv_cq)->mcq.cqn);

 MLX5_SET64(qpc, qpc, dbr_addr, qp->db.dma);

 /* 0xffffff means we ask to work with cqe version 0 */
 if (MLX5_CAP_GEN(mdev, cqe_version) == MLX5_CQE_VERSION_V1)
  MLX5_SET(qpc, qpc, user_index, uidx);

 if (qp->flags & IB_QP_CREATE_PCI_WRITE_END_PADDING) {
  MLX5_SET(qpc, qpc, end_padding_mode,
    MLX5_WQ_END_PAD_MODE_ALIGN);
  /* Special case to clean flag */
  qp->flags &= ~IB_QP_CREATE_PCI_WRITE_END_PADDING;
 }

 err = mlx5_qpc_create_qp(dev, &base->mqp, in, inlen, out);

 kvfree(in);
 if (err)
  goto err_create;

 base->container_mibqp = qp;
 base->mqp.event = mlx5_ib_qp_event;
 if (MLX5_CAP_GEN(mdev, ece_support))
  params->resp.ece_options = MLX5_GET(create_qp_out, out, ece);

 get_cqs(qp->type, init_attr->send_cq, init_attr->recv_cq,
  &send_cq, &recv_cq);
 spin_lock_irqsave(&dev->reset_flow_resource_lock, flags);
 mlx5_ib_lock_cqs(send_cq, recv_cq);
 /* Maintain device to QPs access, needed for further handling via reset
 * flow
 */

 list_add_tail(&qp->qps_list, &dev->qp_list);
 /* Maintain CQ to QPs access, needed for further handling via reset flow
 */

 if (send_cq)
  list_add_tail(&qp->cq_send_list, &send_cq->list_send_qp);
 if (recv_cq)
  list_add_tail(&qp->cq_recv_list, &recv_cq->list_recv_qp);
 mlx5_ib_unlock_cqs(send_cq, recv_cq);
 spin_unlock_irqrestore(&dev->reset_flow_resource_lock, flags);

 return 0;

err_create:
 destroy_qp(dev, qp, base, udata);
 return err;
}

static int create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd,
     struct mlx5_ib_qp *qp,
     struct mlx5_create_qp_params *params)
{
 struct ib_qp_init_attr *init_attr = params->attr;
 struct mlx5_ib_create_qp *ucmd = params->ucmd;
 u32 out[MLX5_ST_SZ_DW(create_qp_out)] = {};
 struct ib_udata *udata = params->udata;
 u32 uidx = params->uidx;
 struct mlx5_ib_resources *devr = &dev->devr;
 int inlen = MLX5_ST_SZ_BYTES(create_qp_in);
 struct mlx5_core_dev *mdev = dev->mdev;
 struct mlx5_ib_cq *send_cq;
 struct mlx5_ib_cq *recv_cq;
 unsigned long flags;
 struct mlx5_ib_qp_base *base;
 int ts_format;
 int mlx5_st;
 void *qpc;
 u32 *in;
 int err;

 spin_lock_init(&qp->sq.lock);
 spin_lock_init(&qp->rq.lock);

 mlx5_st = to_mlx5_st(qp->type);
 if (mlx5_st < 0)
  return -EINVAL;

 if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR)
  qp->sq_signal_bits = MLX5_WQE_CTRL_CQ_UPDATE;

 if (qp->flags & IB_QP_CREATE_SOURCE_QPN)
  qp->underlay_qpn = init_attr->source_qpn;

 base = (init_attr->qp_type == IB_QPT_RAW_PACKET ||
  qp->flags & IB_QP_CREATE_SOURCE_QPN) ?
        &qp->raw_packet_qp.rq.base :
        &qp->trans_qp.base;

 qp->has_rq = qp_has_rq(init_attr);
 err = set_rq_size(dev, &init_attr->cap, qp->has_rq, qp, ucmd);
 if (err) {
  mlx5_ib_dbg(dev, "err %d\n", err);
  return err;
 }

 if (ucmd->rq_wqe_shift != qp->rq.wqe_shift ||
     ucmd->rq_wqe_count != qp->rq.wqe_cnt)
  return -EINVAL;

 if (ucmd->sq_wqe_count > (1 << MLX5_CAP_GEN(mdev, log_max_qp_sz)))
  return -EINVAL;

 if (init_attr->qp_type != IB_QPT_RAW_PACKET) {
  ts_format = get_qp_ts_format(dev, to_mcq(init_attr->send_cq),
          to_mcq(init_attr->recv_cq));
  if (ts_format < 0)
   return ts_format;
 }

 err = _create_user_qp(dev, pd, qp, udata, init_attr, &in, ¶ms->resp,
         &inlen, base, ucmd);
 if (err)
  return err;

 if (is_sqp(init_attr->qp_type))
  qp->port = init_attr->port_num;

 if (MLX5_CAP_GEN(mdev, ece_support))
  MLX5_SET(create_qp_in, in, ece, ucmd->ece_options);
 qpc = MLX5_ADDR_OF(create_qp_in, in, qpc);

 MLX5_SET(qpc, qpc, st, mlx5_st);
 MLX5_SET(qpc, qpc, pm_state, MLX5_QP_PM_MIGRATED);
 MLX5_SET(qpc, qpc, pd, to_mpd(pd)->pdn);

 if (qp->flags_en & MLX5_QP_FLAG_SIGNATURE)
  MLX5_SET(qpc, qpc, wq_signature, 1);

 if (qp->flags & IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK)
  MLX5_SET(qpc, qpc, block_lb_mc, 1);

 if (qp->flags & IB_QP_CREATE_CROSS_CHANNEL)
  MLX5_SET(qpc, qpc, cd_master, 1);
 if (qp->flags & IB_QP_CREATE_MANAGED_SEND)
  MLX5_SET(qpc, qpc, cd_slave_send, 1);
 if (qp->flags & IB_QP_CREATE_MANAGED_RECV)
  MLX5_SET(qpc, qpc, cd_slave_receive, 1);
 if (qp->flags_en & MLX5_QP_FLAG_PACKET_BASED_CREDIT_MODE)
  MLX5_SET(qpc, qpc, req_e2e_credit_mode, 1);
 if ((qp->flags_en & MLX5_QP_FLAG_SCATTER_CQE) &&
     (init_attr->qp_type == IB_QPT_RC ||
      init_attr->qp_type == IB_QPT_UC)) {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=99 H=95 G=96

¤ Dauer der Verarbeitung: 0.22 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.