1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2/*
3 * Copyright(c) 2016 Intel Corporation.
4 */
5
6#include <linux/err.h>
7#include <linux/slab.h>
8#include <linux/vmalloc.h>
9#include <rdma/uverbs_ioctl.h>
10
11#include "srq.h"
12#include "vt.h"
13#include "qp.h"
14/**
15 * rvt_driver_srq_init - init srq resources on a per driver basis
16 * @rdi: rvt dev structure
17 *
18 * Do any initialization needed when a driver registers with rdmavt.
19 */
20void rvt_driver_srq_init(struct rvt_dev_info *rdi)
21{
22	spin_lock_init(&rdi->n_srqs_lock);
23	rdi->n_srqs_allocated = 0;
24}
25
26/**
27 * rvt_create_srq - create a shared receive queue
28 * @ibsrq: the protection domain of the SRQ to create
29 * @srq_init_attr: the attributes of the SRQ
30 * @udata: data from libibverbs when creating a user SRQ
31 *
32 * Return: 0 on success
33 */
34int rvt_create_srq(struct ib_srq *ibsrq, struct ib_srq_init_attr *srq_init_attr,
35		   struct ib_udata *udata)
36{
37	struct rvt_dev_info *dev = ib_to_rvt(ibsrq->device);
38	struct rvt_srq *srq = ibsrq_to_rvtsrq(ibsrq);
39	u32 sz;
40	int ret;
41
42	if (srq_init_attr->srq_type != IB_SRQT_BASIC)
43		return -EOPNOTSUPP;
44
45	if (srq_init_attr->attr.max_sge == 0 ||
46	    srq_init_attr->attr.max_sge > dev->dparms.props.max_srq_sge ||
47	    srq_init_attr->attr.max_wr == 0 ||
48	    srq_init_attr->attr.max_wr > dev->dparms.props.max_srq_wr)
49		return -EINVAL;
50
51	/*
52	 * Need to use vmalloc() if we want to support large #s of entries.
53	 */
54	srq->rq.size = srq_init_attr->attr.max_wr + 1;
55	srq->rq.max_sge = srq_init_attr->attr.max_sge;
56	sz = sizeof(struct ib_sge) * srq->rq.max_sge +
57		sizeof(struct rvt_rwqe);
58	if (rvt_alloc_rq(&srq->rq, srq->rq.size * sz,
59			 dev->dparms.node, udata)) {
60		ret = -ENOMEM;
61		goto bail_srq;
62	}
63
64	/*
65	 * Return the address of the RWQ as the offset to mmap.
66	 * See rvt_mmap() for details.
67	 */
68	if (udata && udata->outlen >= sizeof(__u64)) {
69		u32 s = sizeof(struct rvt_rwq) + srq->rq.size * sz;
70
71		srq->ip = rvt_create_mmap_info(dev, s, udata, srq->rq.wq);
72		if (IS_ERR(srq->ip)) {
73			ret = PTR_ERR(srq->ip);
74			goto bail_wq;
75		}
76
77		ret = ib_copy_to_udata(udata, &srq->ip->offset,
78				       sizeof(srq->ip->offset));
79		if (ret)
80			goto bail_ip;
81	}
82
83	/*
84	 * ib_create_srq() will initialize srq->ibsrq.
85	 */
86	spin_lock_init(&srq->rq.lock);
87	srq->limit = srq_init_attr->attr.srq_limit;
88
89	spin_lock(&dev->n_srqs_lock);
90	if (dev->n_srqs_allocated == dev->dparms.props.max_srq) {
91		spin_unlock(&dev->n_srqs_lock);
92		ret = -ENOMEM;
93		goto bail_ip;
94	}
95
96	dev->n_srqs_allocated++;
97	spin_unlock(&dev->n_srqs_lock);
98
99	if (srq->ip) {
100		spin_lock_irq(&dev->pending_lock);
101		list_add(&srq->ip->pending_mmaps, &dev->pending_mmaps);
102		spin_unlock_irq(&dev->pending_lock);
103	}
104
105	return 0;
106
107bail_ip:
108	kfree(srq->ip);
109bail_wq:
110	rvt_free_rq(&srq->rq);
111bail_srq:
112	return ret;
113}
114
115/**
116 * rvt_modify_srq - modify a shared receive queue
117 * @ibsrq: the SRQ to modify
118 * @attr: the new attributes of the SRQ
119 * @attr_mask: indicates which attributes to modify
120 * @udata: user data for libibverbs.so
121 *
122 * Return: 0 on success
123 */
124int rvt_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
125		   enum ib_srq_attr_mask attr_mask,
126		   struct ib_udata *udata)
127{
128	struct rvt_srq *srq = ibsrq_to_rvtsrq(ibsrq);
129	struct rvt_dev_info *dev = ib_to_rvt(ibsrq->device);
130	struct rvt_rq tmp_rq = {};
131	int ret = 0;
132
133	if (attr_mask & IB_SRQ_MAX_WR) {
134		struct rvt_krwq *okwq = NULL;
135		struct rvt_rwq *owq = NULL;
136		struct rvt_rwqe *p;
137		u32 sz, size, n, head, tail;
138
139		/* Check that the requested sizes are below the limits. */
140		if ((attr->max_wr > dev->dparms.props.max_srq_wr) ||
141		    ((attr_mask & IB_SRQ_LIMIT) ?
142		     attr->srq_limit : srq->limit) > attr->max_wr)
143			return -EINVAL;
144		sz = sizeof(struct rvt_rwqe) +
145			srq->rq.max_sge * sizeof(struct ib_sge);
146		size = attr->max_wr + 1;
147		if (rvt_alloc_rq(&tmp_rq, size * sz, dev->dparms.node,
148				 udata))
149			return -ENOMEM;
150		/* Check that we can write the offset to mmap. */
151		if (udata && udata->inlen >= sizeof(__u64)) {
152			__u64 offset_addr;
153			__u64 offset = 0;
154
155			ret = ib_copy_from_udata(&offset_addr, udata,
156						 sizeof(offset_addr));
157			if (ret)
158				goto bail_free;
159			udata->outbuf = (void __user *)
160					(unsigned long)offset_addr;
161			ret = ib_copy_to_udata(udata, &offset,
162					       sizeof(offset));
163			if (ret)
164				goto bail_free;
165		}
166
167		spin_lock_irq(&srq->rq.kwq->c_lock);
168		/*
169		 * validate head and tail pointer values and compute
170		 * the number of remaining WQEs.
171		 */
172		if (udata) {
173			owq = srq->rq.wq;
174			head = RDMA_READ_UAPI_ATOMIC(owq->head);
175			tail = RDMA_READ_UAPI_ATOMIC(owq->tail);
176		} else {
177			okwq = srq->rq.kwq;
178			head = okwq->head;
179			tail = okwq->tail;
180		}
181		if (head >= srq->rq.size || tail >= srq->rq.size) {
182			ret = -EINVAL;
183			goto bail_unlock;
184		}
185		n = head;
186		if (n < tail)
187			n += srq->rq.size - tail;
188		else
189			n -= tail;
190		if (size <= n) {
191			ret = -EINVAL;
192			goto bail_unlock;
193		}
194		n = 0;
195		p = tmp_rq.kwq->curr_wq;
196		while (tail != head) {
197			struct rvt_rwqe *wqe;
198			int i;
199
200			wqe = rvt_get_rwqe_ptr(&srq->rq, tail);
201			p->wr_id = wqe->wr_id;
202			p->num_sge = wqe->num_sge;
203			for (i = 0; i < wqe->num_sge; i++)
204				p->sg_list[i] = wqe->sg_list[i];
205			n++;
206			p = (struct rvt_rwqe *)((char *)p + sz);
207			if (++tail >= srq->rq.size)
208				tail = 0;
209		}
210		srq->rq.kwq = tmp_rq.kwq;
211		if (udata) {
212			srq->rq.wq = tmp_rq.wq;
213			RDMA_WRITE_UAPI_ATOMIC(tmp_rq.wq->head, n);
214			RDMA_WRITE_UAPI_ATOMIC(tmp_rq.wq->tail, 0);
215		} else {
216			tmp_rq.kwq->head = n;
217			tmp_rq.kwq->tail = 0;
218		}
219		srq->rq.size = size;
220		if (attr_mask & IB_SRQ_LIMIT)
221			srq->limit = attr->srq_limit;
222		spin_unlock_irq(&srq->rq.kwq->c_lock);
223
224		vfree(owq);
225		kvfree(okwq);
226
227		if (srq->ip) {
228			struct rvt_mmap_info *ip = srq->ip;
229			struct rvt_dev_info *dev = ib_to_rvt(srq->ibsrq.device);
230			u32 s = sizeof(struct rvt_rwq) + size * sz;
231
232			rvt_update_mmap_info(dev, ip, s, tmp_rq.wq);
233
234			/*
235			 * Return the offset to mmap.
236			 * See rvt_mmap() for details.
237			 */
238			if (udata && udata->inlen >= sizeof(__u64)) {
239				ret = ib_copy_to_udata(udata, &ip->offset,
240						       sizeof(ip->offset));
241				if (ret)
242					return ret;
243			}
244
245			/*
246			 * Put user mapping info onto the pending list
247			 * unless it already is on the list.
248			 */
249			spin_lock_irq(&dev->pending_lock);
250			if (list_empty(&ip->pending_mmaps))
251				list_add(&ip->pending_mmaps,
252					 &dev->pending_mmaps);
253			spin_unlock_irq(&dev->pending_lock);
254		}
255	} else if (attr_mask & IB_SRQ_LIMIT) {
256		spin_lock_irq(&srq->rq.kwq->c_lock);
257		if (attr->srq_limit >= srq->rq.size)
258			ret = -EINVAL;
259		else
260			srq->limit = attr->srq_limit;
261		spin_unlock_irq(&srq->rq.kwq->c_lock);
262	}
263	return ret;
264
265bail_unlock:
266	spin_unlock_irq(&srq->rq.kwq->c_lock);
267bail_free:
268	rvt_free_rq(&tmp_rq);
269	return ret;
270}
271
272/**
273 * rvt_query_srq - query srq data
274 * @ibsrq: srq to query
275 * @attr: return info in attr
276 *
277 * Return: always 0
278 */
279int rvt_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr)
280{
281	struct rvt_srq *srq = ibsrq_to_rvtsrq(ibsrq);
282
283	attr->max_wr = srq->rq.size - 1;
284	attr->max_sge = srq->rq.max_sge;
285	attr->srq_limit = srq->limit;
286	return 0;
287}
288
289/**
290 * rvt_destroy_srq - destory an srq
291 * @ibsrq: srq object to destroy
292 * @udata: user data for libibverbs.so
293 */
294int rvt_destroy_srq(struct ib_srq *ibsrq, struct ib_udata *udata)
295{
296	struct rvt_srq *srq = ibsrq_to_rvtsrq(ibsrq);
297	struct rvt_dev_info *dev = ib_to_rvt(ibsrq->device);
298
299	spin_lock(&dev->n_srqs_lock);
300	dev->n_srqs_allocated--;
301	spin_unlock(&dev->n_srqs_lock);
302	if (srq->ip)
303		kref_put(&srq->ip->ref, rvt_release_mmap_info);
304	kvfree(srq->rq.kwq);
305	return 0;
306}
307