1/*
2 * Copyright (c) 2006, 2007, 2008, 2009 QLogic Corporation. All rights reserved.
3 * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved.
4 *
5 * This software is available to you under a choice of one of two
6 * licenses.  You may choose to be licensed under the terms of the GNU
7 * General Public License (GPL) Version 2, available from the file
8 * COPYING in the main directory of this source tree, or the
9 * OpenIB.org BSD license below:
10 *
11 *     Redistribution and use in source and binary forms, with or
12 *     without modification, are permitted provided that the following
13 *     conditions are met:
14 *
15 *      - Redistributions of source code must retain the above
16 *        copyright notice, this list of conditions and the following
17 *        disclaimer.
18 *
19 *      - Redistributions in binary form must reproduce the above
20 *        copyright notice, this list of conditions and the following
21 *        disclaimer in the documentation and/or other materials
22 *        provided with the distribution.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 * SOFTWARE.
32 */
33
34#include <linux/err.h>
35#include <linux/slab.h>
36#include <linux/vmalloc.h>
37
38#include "qib_verbs.h"
39
40/**
41 * qib_post_srq_receive - post a receive on a shared receive queue
42 * @ibsrq: the SRQ to post the receive on
43 * @wr: the list of work requests to post
44 * @bad_wr: A pointer to the first WR to cause a problem is put here
45 *
46 * This may be called from interrupt context.
47 */
48int qib_post_srq_receive(struct ib_srq *ibsrq, struct ib_recv_wr *wr,
49			 struct ib_recv_wr **bad_wr)
50{
51	struct qib_srq *srq = to_isrq(ibsrq);
52	struct qib_rwq *wq;
53	unsigned long flags;
54	int ret;
55
56	for (; wr; wr = wr->next) {
57		struct qib_rwqe *wqe;
58		u32 next;
59		int i;
60
61		if ((unsigned) wr->num_sge > srq->rq.max_sge) {
62			*bad_wr = wr;
63			ret = -EINVAL;
64			goto bail;
65		}
66
67		spin_lock_irqsave(&srq->rq.lock, flags);
68		wq = srq->rq.wq;
69		next = wq->head + 1;
70		if (next >= srq->rq.size)
71			next = 0;
72		if (next == wq->tail) {
73			spin_unlock_irqrestore(&srq->rq.lock, flags);
74			*bad_wr = wr;
75			ret = -ENOMEM;
76			goto bail;
77		}
78
79		wqe = get_rwqe_ptr(&srq->rq, wq->head);
80		wqe->wr_id = wr->wr_id;
81		wqe->num_sge = wr->num_sge;
82		for (i = 0; i < wr->num_sge; i++)
83			wqe->sg_list[i] = wr->sg_list[i];
84		/* Make sure queue entry is written before the head index. */
85		smp_wmb();
86		wq->head = next;
87		spin_unlock_irqrestore(&srq->rq.lock, flags);
88	}
89	ret = 0;
90
91bail:
92	return ret;
93}
94
95/**
96 * qib_create_srq - create a shared receive queue
97 * @ibpd: the protection domain of the SRQ to create
98 * @srq_init_attr: the attributes of the SRQ
99 * @udata: data from libibverbs when creating a user SRQ
100 */
101struct ib_srq *qib_create_srq(struct ib_pd *ibpd,
102			      struct ib_srq_init_attr *srq_init_attr,
103			      struct ib_udata *udata)
104{
105	struct qib_ibdev *dev = to_idev(ibpd->device);
106	struct qib_srq *srq;
107	u32 sz;
108	struct ib_srq *ret;
109
110	if (srq_init_attr->srq_type != IB_SRQT_BASIC) {
111		ret = ERR_PTR(-ENOSYS);
112		goto done;
113	}
114
115	if (srq_init_attr->attr.max_sge == 0 ||
116	    srq_init_attr->attr.max_sge > ib_qib_max_srq_sges ||
117	    srq_init_attr->attr.max_wr == 0 ||
118	    srq_init_attr->attr.max_wr > ib_qib_max_srq_wrs) {
119		ret = ERR_PTR(-EINVAL);
120		goto done;
121	}
122
123	srq = kmalloc(sizeof(*srq), GFP_KERNEL);
124	if (!srq) {
125		ret = ERR_PTR(-ENOMEM);
126		goto done;
127	}
128
129	/*
130	 * Need to use vmalloc() if we want to support large #s of entries.
131	 */
132	srq->rq.size = srq_init_attr->attr.max_wr + 1;
133	srq->rq.max_sge = srq_init_attr->attr.max_sge;
134	sz = sizeof(struct ib_sge) * srq->rq.max_sge +
135		sizeof(struct qib_rwqe);
136	srq->rq.wq = vmalloc_user(sizeof(struct qib_rwq) + srq->rq.size * sz);
137	if (!srq->rq.wq) {
138		ret = ERR_PTR(-ENOMEM);
139		goto bail_srq;
140	}
141
142	/*
143	 * Return the address of the RWQ as the offset to mmap.
144	 * See qib_mmap() for details.
145	 */
146	if (udata && udata->outlen >= sizeof(__u64)) {
147		int err;
148		u32 s = sizeof(struct qib_rwq) + srq->rq.size * sz;
149
150		srq->ip =
151		    qib_create_mmap_info(dev, s, ibpd->uobject->context,
152					 srq->rq.wq);
153		if (!srq->ip) {
154			ret = ERR_PTR(-ENOMEM);
155			goto bail_wq;
156		}
157
158		err = ib_copy_to_udata(udata, &srq->ip->offset,
159				       sizeof(srq->ip->offset));
160		if (err) {
161			ret = ERR_PTR(err);
162			goto bail_ip;
163		}
164	} else
165		srq->ip = NULL;
166
167	/*
168	 * ib_create_srq() will initialize srq->ibsrq.
169	 */
170	spin_lock_init(&srq->rq.lock);
171	srq->rq.wq->head = 0;
172	srq->rq.wq->tail = 0;
173	srq->limit = srq_init_attr->attr.srq_limit;
174
175	spin_lock(&dev->n_srqs_lock);
176	if (dev->n_srqs_allocated == ib_qib_max_srqs) {
177		spin_unlock(&dev->n_srqs_lock);
178		ret = ERR_PTR(-ENOMEM);
179		goto bail_ip;
180	}
181
182	dev->n_srqs_allocated++;
183	spin_unlock(&dev->n_srqs_lock);
184
185	if (srq->ip) {
186		spin_lock_irq(&dev->pending_lock);
187		list_add(&srq->ip->pending_mmaps, &dev->pending_mmaps);
188		spin_unlock_irq(&dev->pending_lock);
189	}
190
191	ret = &srq->ibsrq;
192	goto done;
193
194bail_ip:
195	kfree(srq->ip);
196bail_wq:
197	vfree(srq->rq.wq);
198bail_srq:
199	kfree(srq);
200done:
201	return ret;
202}
203
204/**
205 * qib_modify_srq - modify a shared receive queue
206 * @ibsrq: the SRQ to modify
207 * @attr: the new attributes of the SRQ
208 * @attr_mask: indicates which attributes to modify
209 * @udata: user data for libibverbs.so
210 */
211int qib_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
212		   enum ib_srq_attr_mask attr_mask,
213		   struct ib_udata *udata)
214{
215	struct qib_srq *srq = to_isrq(ibsrq);
216	struct qib_rwq *wq;
217	int ret = 0;
218
219	if (attr_mask & IB_SRQ_MAX_WR) {
220		struct qib_rwq *owq;
221		struct qib_rwqe *p;
222		u32 sz, size, n, head, tail;
223
224		/* Check that the requested sizes are below the limits. */
225		if ((attr->max_wr > ib_qib_max_srq_wrs) ||
226		    ((attr_mask & IB_SRQ_LIMIT) ?
227		     attr->srq_limit : srq->limit) > attr->max_wr) {
228			ret = -EINVAL;
229			goto bail;
230		}
231
232		sz = sizeof(struct qib_rwqe) +
233			srq->rq.max_sge * sizeof(struct ib_sge);
234		size = attr->max_wr + 1;
235		wq = vmalloc_user(sizeof(struct qib_rwq) + size * sz);
236		if (!wq) {
237			ret = -ENOMEM;
238			goto bail;
239		}
240
241		/* Check that we can write the offset to mmap. */
242		if (udata && udata->inlen >= sizeof(__u64)) {
243			__u64 offset_addr;
244			__u64 offset = 0;
245
246			ret = ib_copy_from_udata(&offset_addr, udata,
247						 sizeof(offset_addr));
248			if (ret)
249				goto bail_free;
250			udata->outbuf =
251				(void __user *) (unsigned long) offset_addr;
252			ret = ib_copy_to_udata(udata, &offset,
253					       sizeof(offset));
254			if (ret)
255				goto bail_free;
256		}
257
258		spin_lock_irq(&srq->rq.lock);
259		/*
260		 * validate head and tail pointer values and compute
261		 * the number of remaining WQEs.
262		 */
263		owq = srq->rq.wq;
264		head = owq->head;
265		tail = owq->tail;
266		if (head >= srq->rq.size || tail >= srq->rq.size) {
267			ret = -EINVAL;
268			goto bail_unlock;
269		}
270		n = head;
271		if (n < tail)
272			n += srq->rq.size - tail;
273		else
274			n -= tail;
275		if (size <= n) {
276			ret = -EINVAL;
277			goto bail_unlock;
278		}
279		n = 0;
280		p = wq->wq;
281		while (tail != head) {
282			struct qib_rwqe *wqe;
283			int i;
284
285			wqe = get_rwqe_ptr(&srq->rq, tail);
286			p->wr_id = wqe->wr_id;
287			p->num_sge = wqe->num_sge;
288			for (i = 0; i < wqe->num_sge; i++)
289				p->sg_list[i] = wqe->sg_list[i];
290			n++;
291			p = (struct qib_rwqe *)((char *) p + sz);
292			if (++tail >= srq->rq.size)
293				tail = 0;
294		}
295		srq->rq.wq = wq;
296		srq->rq.size = size;
297		wq->head = n;
298		wq->tail = 0;
299		if (attr_mask & IB_SRQ_LIMIT)
300			srq->limit = attr->srq_limit;
301		spin_unlock_irq(&srq->rq.lock);
302
303		vfree(owq);
304
305		if (srq->ip) {
306			struct qib_mmap_info *ip = srq->ip;
307			struct qib_ibdev *dev = to_idev(srq->ibsrq.device);
308			u32 s = sizeof(struct qib_rwq) + size * sz;
309
310			qib_update_mmap_info(dev, ip, s, wq);
311
312			/*
313			 * Return the offset to mmap.
314			 * See qib_mmap() for details.
315			 */
316			if (udata && udata->inlen >= sizeof(__u64)) {
317				ret = ib_copy_to_udata(udata, &ip->offset,
318						       sizeof(ip->offset));
319				if (ret)
320					goto bail;
321			}
322
323			/*
324			 * Put user mapping info onto the pending list
325			 * unless it already is on the list.
326			 */
327			spin_lock_irq(&dev->pending_lock);
328			if (list_empty(&ip->pending_mmaps))
329				list_add(&ip->pending_mmaps,
330					 &dev->pending_mmaps);
331			spin_unlock_irq(&dev->pending_lock);
332		}
333	} else if (attr_mask & IB_SRQ_LIMIT) {
334		spin_lock_irq(&srq->rq.lock);
335		if (attr->srq_limit >= srq->rq.size)
336			ret = -EINVAL;
337		else
338			srq->limit = attr->srq_limit;
339		spin_unlock_irq(&srq->rq.lock);
340	}
341	goto bail;
342
343bail_unlock:
344	spin_unlock_irq(&srq->rq.lock);
345bail_free:
346	vfree(wq);
347bail:
348	return ret;
349}
350
351int qib_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr)
352{
353	struct qib_srq *srq = to_isrq(ibsrq);
354
355	attr->max_wr = srq->rq.size - 1;
356	attr->max_sge = srq->rq.max_sge;
357	attr->srq_limit = srq->limit;
358	return 0;
359}
360
361/**
362 * qib_destroy_srq - destroy a shared receive queue
363 * @ibsrq: the SRQ to destroy
364 */
365int qib_destroy_srq(struct ib_srq *ibsrq)
366{
367	struct qib_srq *srq = to_isrq(ibsrq);
368	struct qib_ibdev *dev = to_idev(ibsrq->device);
369
370	spin_lock(&dev->n_srqs_lock);
371	dev->n_srqs_allocated--;
372	spin_unlock(&dev->n_srqs_lock);
373	if (srq->ip)
374		kref_put(&srq->ip->ref, qib_release_mmap_info);
375	else
376		vfree(srq->rq.wq);
377	kfree(srq);
378
379	return 0;
380}
381