162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright(c) 2016 Intel Corporation. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/err.h> 762306a36Sopenharmony_ci#include <linux/slab.h> 862306a36Sopenharmony_ci#include <linux/vmalloc.h> 962306a36Sopenharmony_ci#include <rdma/uverbs_ioctl.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "srq.h" 1262306a36Sopenharmony_ci#include "vt.h" 1362306a36Sopenharmony_ci#include "qp.h" 1462306a36Sopenharmony_ci/** 1562306a36Sopenharmony_ci * rvt_driver_srq_init - init srq resources on a per driver basis 1662306a36Sopenharmony_ci * @rdi: rvt dev structure 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * Do any initialization needed when a driver registers with rdmavt. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_civoid rvt_driver_srq_init(struct rvt_dev_info *rdi) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci spin_lock_init(&rdi->n_srqs_lock); 2362306a36Sopenharmony_ci rdi->n_srqs_allocated = 0; 2462306a36Sopenharmony_ci} 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/** 2762306a36Sopenharmony_ci * rvt_create_srq - create a shared receive queue 2862306a36Sopenharmony_ci * @ibsrq: the protection domain of the SRQ to create 2962306a36Sopenharmony_ci * @srq_init_attr: the attributes of the SRQ 3062306a36Sopenharmony_ci * @udata: data from libibverbs when creating a user SRQ 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * Return: 0 on success 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ciint rvt_create_srq(struct ib_srq *ibsrq, struct ib_srq_init_attr *srq_init_attr, 3562306a36Sopenharmony_ci struct ib_udata *udata) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct rvt_dev_info *dev = ib_to_rvt(ibsrq->device); 3862306a36Sopenharmony_ci struct rvt_srq *srq = ibsrq_to_rvtsrq(ibsrq); 3962306a36Sopenharmony_ci u32 sz; 4062306a36Sopenharmony_ci int ret; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (srq_init_attr->srq_type != IB_SRQT_BASIC) 4362306a36Sopenharmony_ci return -EOPNOTSUPP; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (srq_init_attr->attr.max_sge == 0 || 4662306a36Sopenharmony_ci srq_init_attr->attr.max_sge > dev->dparms.props.max_srq_sge || 4762306a36Sopenharmony_ci srq_init_attr->attr.max_wr == 0 || 4862306a36Sopenharmony_ci srq_init_attr->attr.max_wr > dev->dparms.props.max_srq_wr) 4962306a36Sopenharmony_ci return -EINVAL; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* 5262306a36Sopenharmony_ci * Need to use vmalloc() if we want to support large #s of entries. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci srq->rq.size = srq_init_attr->attr.max_wr + 1; 5562306a36Sopenharmony_ci srq->rq.max_sge = srq_init_attr->attr.max_sge; 5662306a36Sopenharmony_ci sz = sizeof(struct ib_sge) * srq->rq.max_sge + 5762306a36Sopenharmony_ci sizeof(struct rvt_rwqe); 5862306a36Sopenharmony_ci if (rvt_alloc_rq(&srq->rq, srq->rq.size * sz, 5962306a36Sopenharmony_ci dev->dparms.node, udata)) { 6062306a36Sopenharmony_ci ret = -ENOMEM; 6162306a36Sopenharmony_ci goto bail_srq; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* 6562306a36Sopenharmony_ci * Return the address of the RWQ as the offset to mmap. 6662306a36Sopenharmony_ci * See rvt_mmap() for details. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci if (udata && udata->outlen >= sizeof(__u64)) { 6962306a36Sopenharmony_ci u32 s = sizeof(struct rvt_rwq) + srq->rq.size * sz; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci srq->ip = rvt_create_mmap_info(dev, s, udata, srq->rq.wq); 7262306a36Sopenharmony_ci if (IS_ERR(srq->ip)) { 7362306a36Sopenharmony_ci ret = PTR_ERR(srq->ip); 7462306a36Sopenharmony_ci goto bail_wq; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci ret = ib_copy_to_udata(udata, &srq->ip->offset, 7862306a36Sopenharmony_ci sizeof(srq->ip->offset)); 7962306a36Sopenharmony_ci if (ret) 8062306a36Sopenharmony_ci goto bail_ip; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* 8462306a36Sopenharmony_ci * ib_create_srq() will initialize srq->ibsrq. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ci spin_lock_init(&srq->rq.lock); 8762306a36Sopenharmony_ci srq->limit = srq_init_attr->attr.srq_limit; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci spin_lock(&dev->n_srqs_lock); 9062306a36Sopenharmony_ci if (dev->n_srqs_allocated == dev->dparms.props.max_srq) { 9162306a36Sopenharmony_ci spin_unlock(&dev->n_srqs_lock); 9262306a36Sopenharmony_ci ret = -ENOMEM; 9362306a36Sopenharmony_ci goto bail_ip; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci dev->n_srqs_allocated++; 9762306a36Sopenharmony_ci spin_unlock(&dev->n_srqs_lock); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (srq->ip) { 10062306a36Sopenharmony_ci spin_lock_irq(&dev->pending_lock); 10162306a36Sopenharmony_ci list_add(&srq->ip->pending_mmaps, &dev->pending_mmaps); 10262306a36Sopenharmony_ci spin_unlock_irq(&dev->pending_lock); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cibail_ip: 10862306a36Sopenharmony_ci kfree(srq->ip); 10962306a36Sopenharmony_cibail_wq: 11062306a36Sopenharmony_ci rvt_free_rq(&srq->rq); 11162306a36Sopenharmony_cibail_srq: 11262306a36Sopenharmony_ci return ret; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/** 11662306a36Sopenharmony_ci * rvt_modify_srq - modify a shared receive queue 11762306a36Sopenharmony_ci * @ibsrq: the SRQ to modify 11862306a36Sopenharmony_ci * @attr: the new attributes of the SRQ 11962306a36Sopenharmony_ci * @attr_mask: indicates which attributes to modify 12062306a36Sopenharmony_ci * @udata: user data for libibverbs.so 12162306a36Sopenharmony_ci * 12262306a36Sopenharmony_ci * Return: 0 on success 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ciint rvt_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr, 12562306a36Sopenharmony_ci enum ib_srq_attr_mask attr_mask, 12662306a36Sopenharmony_ci struct ib_udata *udata) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct rvt_srq *srq = ibsrq_to_rvtsrq(ibsrq); 12962306a36Sopenharmony_ci struct rvt_dev_info *dev = ib_to_rvt(ibsrq->device); 13062306a36Sopenharmony_ci struct rvt_rq tmp_rq = {}; 13162306a36Sopenharmony_ci int ret = 0; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (attr_mask & IB_SRQ_MAX_WR) { 13462306a36Sopenharmony_ci struct rvt_krwq *okwq = NULL; 13562306a36Sopenharmony_ci struct rvt_rwq *owq = NULL; 13662306a36Sopenharmony_ci struct rvt_rwqe *p; 13762306a36Sopenharmony_ci u32 sz, size, n, head, tail; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Check that the requested sizes are below the limits. */ 14062306a36Sopenharmony_ci if ((attr->max_wr > dev->dparms.props.max_srq_wr) || 14162306a36Sopenharmony_ci ((attr_mask & IB_SRQ_LIMIT) ? 14262306a36Sopenharmony_ci attr->srq_limit : srq->limit) > attr->max_wr) 14362306a36Sopenharmony_ci return -EINVAL; 14462306a36Sopenharmony_ci sz = sizeof(struct rvt_rwqe) + 14562306a36Sopenharmony_ci srq->rq.max_sge * sizeof(struct ib_sge); 14662306a36Sopenharmony_ci size = attr->max_wr + 1; 14762306a36Sopenharmony_ci if (rvt_alloc_rq(&tmp_rq, size * sz, dev->dparms.node, 14862306a36Sopenharmony_ci udata)) 14962306a36Sopenharmony_ci return -ENOMEM; 15062306a36Sopenharmony_ci /* Check that we can write the offset to mmap. */ 15162306a36Sopenharmony_ci if (udata && udata->inlen >= sizeof(__u64)) { 15262306a36Sopenharmony_ci __u64 offset_addr; 15362306a36Sopenharmony_ci __u64 offset = 0; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ret = ib_copy_from_udata(&offset_addr, udata, 15662306a36Sopenharmony_ci sizeof(offset_addr)); 15762306a36Sopenharmony_ci if (ret) 15862306a36Sopenharmony_ci goto bail_free; 15962306a36Sopenharmony_ci udata->outbuf = (void __user *) 16062306a36Sopenharmony_ci (unsigned long)offset_addr; 16162306a36Sopenharmony_ci ret = ib_copy_to_udata(udata, &offset, 16262306a36Sopenharmony_ci sizeof(offset)); 16362306a36Sopenharmony_ci if (ret) 16462306a36Sopenharmony_ci goto bail_free; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci spin_lock_irq(&srq->rq.kwq->c_lock); 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * validate head and tail pointer values and compute 17062306a36Sopenharmony_ci * the number of remaining WQEs. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci if (udata) { 17362306a36Sopenharmony_ci owq = srq->rq.wq; 17462306a36Sopenharmony_ci head = RDMA_READ_UAPI_ATOMIC(owq->head); 17562306a36Sopenharmony_ci tail = RDMA_READ_UAPI_ATOMIC(owq->tail); 17662306a36Sopenharmony_ci } else { 17762306a36Sopenharmony_ci okwq = srq->rq.kwq; 17862306a36Sopenharmony_ci head = okwq->head; 17962306a36Sopenharmony_ci tail = okwq->tail; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci if (head >= srq->rq.size || tail >= srq->rq.size) { 18262306a36Sopenharmony_ci ret = -EINVAL; 18362306a36Sopenharmony_ci goto bail_unlock; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci n = head; 18662306a36Sopenharmony_ci if (n < tail) 18762306a36Sopenharmony_ci n += srq->rq.size - tail; 18862306a36Sopenharmony_ci else 18962306a36Sopenharmony_ci n -= tail; 19062306a36Sopenharmony_ci if (size <= n) { 19162306a36Sopenharmony_ci ret = -EINVAL; 19262306a36Sopenharmony_ci goto bail_unlock; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci n = 0; 19562306a36Sopenharmony_ci p = tmp_rq.kwq->curr_wq; 19662306a36Sopenharmony_ci while (tail != head) { 19762306a36Sopenharmony_ci struct rvt_rwqe *wqe; 19862306a36Sopenharmony_ci int i; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci wqe = rvt_get_rwqe_ptr(&srq->rq, tail); 20162306a36Sopenharmony_ci p->wr_id = wqe->wr_id; 20262306a36Sopenharmony_ci p->num_sge = wqe->num_sge; 20362306a36Sopenharmony_ci for (i = 0; i < wqe->num_sge; i++) 20462306a36Sopenharmony_ci p->sg_list[i] = wqe->sg_list[i]; 20562306a36Sopenharmony_ci n++; 20662306a36Sopenharmony_ci p = (struct rvt_rwqe *)((char *)p + sz); 20762306a36Sopenharmony_ci if (++tail >= srq->rq.size) 20862306a36Sopenharmony_ci tail = 0; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci srq->rq.kwq = tmp_rq.kwq; 21162306a36Sopenharmony_ci if (udata) { 21262306a36Sopenharmony_ci srq->rq.wq = tmp_rq.wq; 21362306a36Sopenharmony_ci RDMA_WRITE_UAPI_ATOMIC(tmp_rq.wq->head, n); 21462306a36Sopenharmony_ci RDMA_WRITE_UAPI_ATOMIC(tmp_rq.wq->tail, 0); 21562306a36Sopenharmony_ci } else { 21662306a36Sopenharmony_ci tmp_rq.kwq->head = n; 21762306a36Sopenharmony_ci tmp_rq.kwq->tail = 0; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci srq->rq.size = size; 22062306a36Sopenharmony_ci if (attr_mask & IB_SRQ_LIMIT) 22162306a36Sopenharmony_ci srq->limit = attr->srq_limit; 22262306a36Sopenharmony_ci spin_unlock_irq(&srq->rq.kwq->c_lock); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci vfree(owq); 22562306a36Sopenharmony_ci kvfree(okwq); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (srq->ip) { 22862306a36Sopenharmony_ci struct rvt_mmap_info *ip = srq->ip; 22962306a36Sopenharmony_ci struct rvt_dev_info *dev = ib_to_rvt(srq->ibsrq.device); 23062306a36Sopenharmony_ci u32 s = sizeof(struct rvt_rwq) + size * sz; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci rvt_update_mmap_info(dev, ip, s, tmp_rq.wq); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* 23562306a36Sopenharmony_ci * Return the offset to mmap. 23662306a36Sopenharmony_ci * See rvt_mmap() for details. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci if (udata && udata->inlen >= sizeof(__u64)) { 23962306a36Sopenharmony_ci ret = ib_copy_to_udata(udata, &ip->offset, 24062306a36Sopenharmony_ci sizeof(ip->offset)); 24162306a36Sopenharmony_ci if (ret) 24262306a36Sopenharmony_ci return ret; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* 24662306a36Sopenharmony_ci * Put user mapping info onto the pending list 24762306a36Sopenharmony_ci * unless it already is on the list. 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_ci spin_lock_irq(&dev->pending_lock); 25062306a36Sopenharmony_ci if (list_empty(&ip->pending_mmaps)) 25162306a36Sopenharmony_ci list_add(&ip->pending_mmaps, 25262306a36Sopenharmony_ci &dev->pending_mmaps); 25362306a36Sopenharmony_ci spin_unlock_irq(&dev->pending_lock); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci } else if (attr_mask & IB_SRQ_LIMIT) { 25662306a36Sopenharmony_ci spin_lock_irq(&srq->rq.kwq->c_lock); 25762306a36Sopenharmony_ci if (attr->srq_limit >= srq->rq.size) 25862306a36Sopenharmony_ci ret = -EINVAL; 25962306a36Sopenharmony_ci else 26062306a36Sopenharmony_ci srq->limit = attr->srq_limit; 26162306a36Sopenharmony_ci spin_unlock_irq(&srq->rq.kwq->c_lock); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci return ret; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cibail_unlock: 26662306a36Sopenharmony_ci spin_unlock_irq(&srq->rq.kwq->c_lock); 26762306a36Sopenharmony_cibail_free: 26862306a36Sopenharmony_ci rvt_free_rq(&tmp_rq); 26962306a36Sopenharmony_ci return ret; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci/** 27362306a36Sopenharmony_ci * rvt_query_srq - query srq data 27462306a36Sopenharmony_ci * @ibsrq: srq to query 27562306a36Sopenharmony_ci * @attr: return info in attr 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * Return: always 0 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_ciint rvt_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct rvt_srq *srq = ibsrq_to_rvtsrq(ibsrq); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci attr->max_wr = srq->rq.size - 1; 28462306a36Sopenharmony_ci attr->max_sge = srq->rq.max_sge; 28562306a36Sopenharmony_ci attr->srq_limit = srq->limit; 28662306a36Sopenharmony_ci return 0; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/** 29062306a36Sopenharmony_ci * rvt_destroy_srq - destory an srq 29162306a36Sopenharmony_ci * @ibsrq: srq object to destroy 29262306a36Sopenharmony_ci * @udata: user data for libibverbs.so 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ciint rvt_destroy_srq(struct ib_srq *ibsrq, struct ib_udata *udata) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct rvt_srq *srq = ibsrq_to_rvtsrq(ibsrq); 29762306a36Sopenharmony_ci struct rvt_dev_info *dev = ib_to_rvt(ibsrq->device); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci spin_lock(&dev->n_srqs_lock); 30062306a36Sopenharmony_ci dev->n_srqs_allocated--; 30162306a36Sopenharmony_ci spin_unlock(&dev->n_srqs_lock); 30262306a36Sopenharmony_ci if (srq->ip) 30362306a36Sopenharmony_ci kref_put(&srq->ip->ref, rvt_release_mmap_info); 30462306a36Sopenharmony_ci kvfree(srq->rq.kwq); 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci} 307