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