From: Leon Romanovsky <[email protected]> The uverbs CQ creation UAPI allows users to supply their own umem for a CQ. Update vmw_pvrdma to support this workflow while preserving support for creating umem through the legacy interface.
Signed-off-by: Leon Romanovsky <[email protected]> --- drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c | 171 ++++++++++++++++-------- drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c | 1 + drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h | 3 + 3 files changed, 121 insertions(+), 54 deletions(-) diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c index b3df6eb9b8ef..c43c363565c1 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c @@ -90,16 +90,9 @@ int pvrdma_req_notify_cq(struct ib_cq *ibcq, return has_data; } -/** - * pvrdma_create_cq - create completion queue - * @ibcq: Allocated CQ - * @attr: completion queue attributes - * @attrs: bundle - * - * @return: 0 on success - */ -int pvrdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, - struct uverbs_attr_bundle *attrs) +int pvrdma_create_user_cq(struct ib_cq *ibcq, + const struct ib_cq_init_attr *attr, + struct uverbs_attr_bundle *attrs) { struct ib_udata *udata = &attrs->driver_udata; struct ib_device *ibdev = ibcq->device; @@ -123,58 +116,48 @@ int pvrdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, if (attr->flags) return -EOPNOTSUPP; - entries = roundup_pow_of_two(entries); - if (entries < 1 || entries > dev->dsr->caps.max_cqe) + if (attr->cqe > dev->dsr->caps.max_cqe) return -EINVAL; + entries = roundup_pow_of_two(entries); + if (!atomic_add_unless(&dev->num_cqs, 1, dev->dsr->caps.max_cq)) return -ENOMEM; cq->ibcq.cqe = entries; - cq->is_kernel = !udata; - - if (!cq->is_kernel) { - if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd))) { - ret = -EFAULT; - goto err_cq; - } - - cq->umem = ib_umem_get(ibdev, ucmd.buf_addr, ucmd.buf_size, - IB_ACCESS_LOCAL_WRITE); - if (IS_ERR(cq->umem)) { - ret = PTR_ERR(cq->umem); - goto err_cq; - } + cq->is_kernel = false; - npages = ib_umem_num_dma_blocks(cq->umem, PAGE_SIZE); - } else { - /* One extra page for shared ring state */ - npages = 1 + (entries * sizeof(struct pvrdma_cqe) + - PAGE_SIZE - 1) / PAGE_SIZE; + if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd))) { + ret = -EFAULT; + goto err_cq; + } - /* Skip header page. */ - cq->offset = PAGE_SIZE; + if (!ibcq->umem) + ibcq->umem = ib_umem_get(ibdev, ucmd.buf_addr, ucmd.buf_size, + IB_ACCESS_LOCAL_WRITE); + if (IS_ERR(ibcq->umem)) { + ret = PTR_ERR(ibcq->umem); + goto err_cq; } + npages = ib_umem_num_dma_blocks(cq->umem, PAGE_SIZE); + if (npages < 0 || npages > PVRDMA_PAGE_DIR_MAX_PAGES) { dev_warn(&dev->pdev->dev, "overflow pages in completion queue\n"); ret = -EINVAL; - goto err_umem; + goto err_cq; } - ret = pvrdma_page_dir_init(dev, &cq->pdir, npages, cq->is_kernel); + ret = pvrdma_page_dir_init(dev, &cq->pdir, npages, false); if (ret) { dev_warn(&dev->pdev->dev, "could not allocate page directory\n"); - goto err_umem; + goto err_cq; } /* Ring state is always the first page. Set in library for user cq. */ - if (cq->is_kernel) - cq->ring_state = cq->pdir.pages[0]; - else - pvrdma_page_dir_insert_umem(&cq->pdir, cq->umem, 0); + pvrdma_page_dir_insert_umem(&cq->pdir, cq->umem, 0); refcount_set(&cq->refcnt, 1); init_completion(&cq->free); @@ -183,7 +166,7 @@ int pvrdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, memset(cmd, 0, sizeof(*cmd)); cmd->hdr.cmd = PVRDMA_CMD_CREATE_CQ; cmd->nchunks = npages; - cmd->ctx_handle = context ? context->ctx_handle : 0; + cmd->ctx_handle = context->ctx_handle; cmd->cqe = entries; cmd->pdir_dma = cq->pdir.dir_dma; ret = pvrdma_cmd_post(dev, &req, &rsp, PVRDMA_CMD_CREATE_CQ_RESP); @@ -200,24 +183,106 @@ int pvrdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, dev->cq_tbl[cq->cq_handle % dev->dsr->caps.max_cq] = cq; spin_unlock_irqrestore(&dev->cq_tbl_lock, flags); - if (!cq->is_kernel) { - cq->uar = &context->uar; + cq->uar = &context->uar; - /* Copy udata back. */ - if (ib_copy_to_udata(udata, &cq_resp, sizeof(cq_resp))) { - dev_warn(&dev->pdev->dev, - "failed to copy back udata\n"); - pvrdma_destroy_cq(&cq->ibcq, udata); - return -EINVAL; - } + /* Copy udata back. */ + if (ib_copy_to_udata(udata, &cq_resp, sizeof(cq_resp))) { + dev_warn(&dev->pdev->dev, + "failed to copy back udata\n"); + pvrdma_destroy_cq(&cq->ibcq, udata); + return -EINVAL; } return 0; err_page_dir: pvrdma_page_dir_cleanup(dev, &cq->pdir); -err_umem: - ib_umem_release(cq->umem); +err_cq: + atomic_dec(&dev->num_cqs); + return ret; +} + +int pvrdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, + struct uverbs_attr_bundle *attrs) +{ + struct ib_device *ibdev = ibcq->device; + int entries = attr->cqe; + struct pvrdma_dev *dev = to_vdev(ibdev); + struct pvrdma_cq *cq = to_vcq(ibcq); + int ret; + int npages; + unsigned long flags; + union pvrdma_cmd_req req; + union pvrdma_cmd_resp rsp; + struct pvrdma_cmd_create_cq *cmd = &req.create_cq; + struct pvrdma_cmd_create_cq_resp *resp = &rsp.create_cq_resp; + + BUILD_BUG_ON(sizeof(struct pvrdma_cqe) != 64); + + if (attr->flags) + return -EOPNOTSUPP; + + if (attr->cqe > dev->dsr->caps.max_cqe) + return -EINVAL; + entries = roundup_pow_of_two(entries); + + if (!atomic_add_unless(&dev->num_cqs, 1, dev->dsr->caps.max_cq)) + return -ENOMEM; + + cq->ibcq.cqe = entries; + cq->is_kernel = true; + + /* One extra page for shared ring state */ + npages = 1 + (entries * sizeof(struct pvrdma_cqe) + + PAGE_SIZE - 1) / PAGE_SIZE; + + /* Skip header page. */ + cq->offset = PAGE_SIZE; + + if (npages < 0 || npages > PVRDMA_PAGE_DIR_MAX_PAGES) { + dev_warn(&dev->pdev->dev, + "overflow pages in completion queue\n"); + ret = -EINVAL; + goto err_cq; + } + + ret = pvrdma_page_dir_init(dev, &cq->pdir, npages, true); + if (ret) { + dev_warn(&dev->pdev->dev, + "could not allocate page directory\n"); + goto err_cq; + } + + /* Ring state is always the first page. Set in library for user cq. */ + cq->ring_state = cq->pdir.pages[0]; + + refcount_set(&cq->refcnt, 1); + init_completion(&cq->free); + spin_lock_init(&cq->cq_lock); + + memset(cmd, 0, sizeof(*cmd)); + cmd->hdr.cmd = PVRDMA_CMD_CREATE_CQ; + cmd->nchunks = npages; + cmd->ctx_handle = 0; + cmd->cqe = entries; + cmd->pdir_dma = cq->pdir.dir_dma; + ret = pvrdma_cmd_post(dev, &req, &rsp, PVRDMA_CMD_CREATE_CQ_RESP); + if (ret < 0) { + dev_warn(&dev->pdev->dev, + "could not create completion queue, error: %d\n", ret); + goto err_page_dir; + } + + cq->ibcq.cqe = resp->cqe; + cq->cq_handle = resp->cq_handle; + spin_lock_irqsave(&dev->cq_tbl_lock, flags); + dev->cq_tbl[cq->cq_handle % dev->dsr->caps.max_cq] = cq; + spin_unlock_irqrestore(&dev->cq_tbl_lock, flags); + + return 0; + +err_page_dir: + pvrdma_page_dir_cleanup(dev, &cq->pdir); err_cq: atomic_dec(&dev->num_cqs); return ret; @@ -229,8 +294,6 @@ static void pvrdma_free_cq(struct pvrdma_dev *dev, struct pvrdma_cq *cq) complete(&cq->free); wait_for_completion(&cq->free); - ib_umem_release(cq->umem); - pvrdma_page_dir_cleanup(dev, &cq->pdir); } diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c index 1664d1d7d969..3f5b94a1e517 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c @@ -194,6 +194,7 @@ static const struct ib_device_ops pvrdma_dev_ops = { .alloc_ucontext = pvrdma_alloc_ucontext, .create_ah = pvrdma_create_ah, .create_cq = pvrdma_create_cq, + .create_user_cq = pvrdma_create_user_cq, .create_qp = pvrdma_create_qp, .dealloc_pd = pvrdma_dealloc_pd, .dealloc_ucontext = pvrdma_dealloc_ucontext, diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h index 603e5a9311eb..18910d336744 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h @@ -375,6 +375,9 @@ int pvrdma_map_mr_sg(struct ib_mr *ibmr, struct scatterlist *sg, int sg_nents, unsigned int *sg_offset); int pvrdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, struct uverbs_attr_bundle *attrs); +int pvrdma_create_user_cq(struct ib_cq *ibcq, + const struct ib_cq_init_attr *attr, + struct uverbs_attr_bundle *attrs); int pvrdma_destroy_cq(struct ib_cq *cq, struct ib_udata *udata); int pvrdma_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc); int pvrdma_req_notify_cq(struct ib_cq *cq, enum ib_cq_notify_flags flags); -- 2.52.0
