in hw/vmw_pvrdma/pvrdma_qp.c [191:428]
int pvrdma_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr,
struct ib_udata *udata)
{
struct pvrdma_qp *qp = to_vqp(ibqp);
struct pvrdma_dev *dev = to_vdev(ibqp->device);
union pvrdma_cmd_req req;
union pvrdma_cmd_resp rsp;
struct pvrdma_cmd_create_qp *cmd = &req.create_qp;
struct pvrdma_cmd_create_qp_resp *resp = &rsp.create_qp_resp;
struct pvrdma_cmd_create_qp_resp_v2 *resp_v2 = &rsp.create_qp_resp_v2;
struct pvrdma_create_qp ucmd;
struct pvrdma_create_qp_resp qp_resp = {};
unsigned long flags;
int ret;
bool is_srq = !!init_attr->srq;
if (init_attr->create_flags) {
dev_warn(&dev->pdev->dev,
"invalid create queuepair flags %#x\n",
init_attr->create_flags);
return -EOPNOTSUPP;
}
if (init_attr->qp_type != IB_QPT_RC &&
init_attr->qp_type != IB_QPT_UD &&
init_attr->qp_type != IB_QPT_GSI) {
dev_warn(&dev->pdev->dev, "queuepair type %d not supported\n",
init_attr->qp_type);
return -EOPNOTSUPP;
}
if (is_srq && !dev->dsr->caps.max_srq) {
dev_warn(&dev->pdev->dev,
"SRQs not supported by device\n");
return -EINVAL;
}
if (!atomic_add_unless(&dev->num_qps, 1, dev->dsr->caps.max_qp))
return -ENOMEM;
switch (init_attr->qp_type) {
case IB_QPT_GSI:
if (init_attr->port_num == 0 ||
init_attr->port_num > ibqp->device->phys_port_cnt) {
dev_warn(&dev->pdev->dev, "invalid queuepair attrs\n");
ret = -EINVAL;
goto err_qp;
}
fallthrough;
case IB_QPT_RC:
case IB_QPT_UD:
spin_lock_init(&qp->sq.lock);
spin_lock_init(&qp->rq.lock);
mutex_init(&qp->mutex);
refcount_set(&qp->refcnt, 1);
init_completion(&qp->free);
qp->state = IB_QPS_RESET;
qp->is_kernel = !udata;
if (!qp->is_kernel) {
dev_dbg(&dev->pdev->dev,
"create queuepair from user space\n");
if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd))) {
ret = -EFAULT;
goto err_qp;
}
/* Userspace supports qpn and qp handles? */
if (dev->dsr_version >= PVRDMA_QPHANDLE_VERSION &&
udata->outlen < sizeof(qp_resp)) {
dev_warn(&dev->pdev->dev,
"create queuepair not supported\n");
ret = -EOPNOTSUPP;
goto err_qp;
}
if (!is_srq) {
/* set qp->sq.wqe_cnt, shift, buf_size.. */
qp->rumem = ib_umem_get(ibqp->device,
ucmd.rbuf_addr,
ucmd.rbuf_size, 0);
if (IS_ERR(qp->rumem)) {
ret = PTR_ERR(qp->rumem);
goto err_qp;
}
qp->srq = NULL;
} else {
qp->rumem = NULL;
qp->srq = to_vsrq(init_attr->srq);
}
qp->sumem = ib_umem_get(ibqp->device, ucmd.sbuf_addr,
ucmd.sbuf_size, 0);
if (IS_ERR(qp->sumem)) {
if (!is_srq)
ib_umem_release(qp->rumem);
ret = PTR_ERR(qp->sumem);
goto err_qp;
}
qp->npages_send =
ib_umem_num_dma_blocks(qp->sumem, PAGE_SIZE);
if (!is_srq)
qp->npages_recv = ib_umem_num_dma_blocks(
qp->rumem, PAGE_SIZE);
else
qp->npages_recv = 0;
qp->npages = qp->npages_send + qp->npages_recv;
} else {
ret = pvrdma_set_sq_size(to_vdev(ibqp->device),
&init_attr->cap, qp);
if (ret)
goto err_qp;
ret = pvrdma_set_rq_size(to_vdev(ibqp->device),
&init_attr->cap, qp);
if (ret)
goto err_qp;
qp->npages = qp->npages_send + qp->npages_recv;
/* Skip header page. */
qp->sq.offset = PVRDMA_QP_NUM_HEADER_PAGES * PAGE_SIZE;
/* Recv queue pages are after send pages. */
qp->rq.offset = qp->npages_send * PAGE_SIZE;
}
if (qp->npages < 0 || qp->npages > PVRDMA_PAGE_DIR_MAX_PAGES) {
dev_warn(&dev->pdev->dev,
"overflow pages in queuepair\n");
ret = -EINVAL;
goto err_umem;
}
ret = pvrdma_page_dir_init(dev, &qp->pdir, qp->npages,
qp->is_kernel);
if (ret) {
dev_warn(&dev->pdev->dev,
"could not allocate page directory\n");
goto err_umem;
}
if (!qp->is_kernel) {
pvrdma_page_dir_insert_umem(&qp->pdir, qp->sumem, 0);
if (!is_srq)
pvrdma_page_dir_insert_umem(&qp->pdir,
qp->rumem,
qp->npages_send);
} else {
/* Ring state is always the first page. */
qp->sq.ring = qp->pdir.pages[0];
qp->rq.ring = is_srq ? NULL : &qp->sq.ring[1];
}
break;
default:
ret = -EINVAL;
goto err_qp;
}
/* Not supported */
init_attr->cap.max_inline_data = 0;
memset(cmd, 0, sizeof(*cmd));
cmd->hdr.cmd = PVRDMA_CMD_CREATE_QP;
cmd->pd_handle = to_vpd(ibqp->pd)->pd_handle;
cmd->send_cq_handle = to_vcq(init_attr->send_cq)->cq_handle;
cmd->recv_cq_handle = to_vcq(init_attr->recv_cq)->cq_handle;
if (is_srq)
cmd->srq_handle = to_vsrq(init_attr->srq)->srq_handle;
else
cmd->srq_handle = 0;
cmd->max_send_wr = init_attr->cap.max_send_wr;
cmd->max_recv_wr = init_attr->cap.max_recv_wr;
cmd->max_send_sge = init_attr->cap.max_send_sge;
cmd->max_recv_sge = init_attr->cap.max_recv_sge;
cmd->max_inline_data = init_attr->cap.max_inline_data;
cmd->sq_sig_all = (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR) ? 1 : 0;
cmd->qp_type = ib_qp_type_to_pvrdma(init_attr->qp_type);
cmd->is_srq = is_srq;
cmd->lkey = 0;
cmd->access_flags = IB_ACCESS_LOCAL_WRITE;
cmd->total_chunks = qp->npages;
cmd->send_chunks = qp->npages_send - PVRDMA_QP_NUM_HEADER_PAGES;
cmd->pdir_dma = qp->pdir.dir_dma;
dev_dbg(&dev->pdev->dev, "create queuepair with %d, %d, %d, %d\n",
cmd->max_send_wr, cmd->max_recv_wr, cmd->max_send_sge,
cmd->max_recv_sge);
ret = pvrdma_cmd_post(dev, &req, &rsp, PVRDMA_CMD_CREATE_QP_RESP);
if (ret < 0) {
dev_warn(&dev->pdev->dev,
"could not create queuepair, error: %d\n", ret);
goto err_pdir;
}
/* max_send_wr/_recv_wr/_send_sge/_recv_sge/_inline_data */
qp->port = init_attr->port_num;
if (dev->dsr_version >= PVRDMA_QPHANDLE_VERSION) {
qp->ibqp.qp_num = resp_v2->qpn;
qp->qp_handle = resp_v2->qp_handle;
} else {
qp->ibqp.qp_num = resp->qpn;
qp->qp_handle = resp->qpn;
}
spin_lock_irqsave(&dev->qp_tbl_lock, flags);
dev->qp_tbl[qp->qp_handle % dev->dsr->caps.max_qp] = qp;
spin_unlock_irqrestore(&dev->qp_tbl_lock, flags);
if (udata) {
qp_resp.qpn = qp->ibqp.qp_num;
qp_resp.qp_handle = qp->qp_handle;
if (ib_copy_to_udata(udata, &qp_resp,
min(udata->outlen, sizeof(qp_resp)))) {
dev_warn(&dev->pdev->dev,
"failed to copy back udata\n");
__pvrdma_destroy_qp(dev, qp);
return -EINVAL;
}
}
return 0;
err_pdir:
pvrdma_page_dir_cleanup(dev, &qp->pdir);
err_umem:
ib_umem_release(qp->rumem);
ib_umem_release(qp->sumem);
err_qp:
atomic_dec(&dev->num_qps);
return ret;
}