1/*
2 *  IBM eServer eHCA Infiniband device driver for Linux on POWER
3 *
4 *  userspace support verbs
5 *
6 *  Authors: Christoph Raisch <raisch@de.ibm.com>
7 *           Hoang-Nam Nguyen <hnguyen@de.ibm.com>
8 *           Heiko J Schick <schickhj@de.ibm.com>
9 *
10 *  Copyright (c) 2005 IBM Corporation
11 *
12 *  All rights reserved.
13 *
14 *  This source code is distributed under a dual license of GPL v2.0 and OpenIB
15 *  BSD.
16 *
17 * OpenIB BSD License
18 *
19 * Redistribution and use in source and binary forms, with or without
20 * modification, are permitted provided that the following conditions are met:
21 *
22 * Redistributions of source code must retain the above copyright notice, this
23 * list of conditions and the following disclaimer.
24 *
25 * Redistributions in binary form must reproduce the above copyright notice,
26 * this list of conditions and the following disclaimer in the documentation
27 * and/or other materials
28 * provided with the distribution.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
31 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
34 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
37 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
38 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40 * POSSIBILITY OF SUCH DAMAGE.
41 */
42
43#include <linux/slab.h>
44
45#include "ehca_classes.h"
46#include "ehca_iverbs.h"
47#include "ehca_mrmw.h"
48#include "ehca_tools.h"
49#include "hcp_if.h"
50
51struct ib_ucontext *ehca_alloc_ucontext(struct ib_device *device,
52					struct ib_udata *udata)
53{
54	struct ehca_ucontext *my_context;
55
56	my_context = kzalloc(sizeof *my_context, GFP_KERNEL);
57	if (!my_context) {
58		ehca_err(device, "Out of memory device=%p", device);
59		return ERR_PTR(-ENOMEM);
60	}
61
62	return &my_context->ib_ucontext;
63}
64
65int ehca_dealloc_ucontext(struct ib_ucontext *context)
66{
67	kfree(container_of(context, struct ehca_ucontext, ib_ucontext));
68	return 0;
69}
70
71static void ehca_mm_open(struct vm_area_struct *vma)
72{
73	u32 *count = (u32 *)vma->vm_private_data;
74	if (!count) {
75		ehca_gen_err("Invalid vma struct vm_start=%lx vm_end=%lx",
76			     vma->vm_start, vma->vm_end);
77		return;
78	}
79	(*count)++;
80	if (!(*count))
81		ehca_gen_err("Use count overflow vm_start=%lx vm_end=%lx",
82			     vma->vm_start, vma->vm_end);
83	ehca_gen_dbg("vm_start=%lx vm_end=%lx count=%x",
84		     vma->vm_start, vma->vm_end, *count);
85}
86
87static void ehca_mm_close(struct vm_area_struct *vma)
88{
89	u32 *count = (u32 *)vma->vm_private_data;
90	if (!count) {
91		ehca_gen_err("Invalid vma struct vm_start=%lx vm_end=%lx",
92			     vma->vm_start, vma->vm_end);
93		return;
94	}
95	(*count)--;
96	ehca_gen_dbg("vm_start=%lx vm_end=%lx count=%x",
97		     vma->vm_start, vma->vm_end, *count);
98}
99
100static const struct vm_operations_struct vm_ops = {
101	.open =	ehca_mm_open,
102	.close = ehca_mm_close,
103};
104
105static int ehca_mmap_fw(struct vm_area_struct *vma, struct h_galpas *galpas,
106			u32 *mm_count)
107{
108	int ret;
109	u64 vsize, physical;
110
111	vsize = vma->vm_end - vma->vm_start;
112	if (vsize < EHCA_PAGESIZE) {
113		ehca_gen_err("invalid vsize=%lx", vma->vm_end - vma->vm_start);
114		return -EINVAL;
115	}
116
117	physical = galpas->user.fw_handle;
118	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
119	ehca_gen_dbg("vsize=%llx physical=%llx", vsize, physical);
120	/* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */
121	ret = remap_4k_pfn(vma, vma->vm_start, physical >> EHCA_PAGESHIFT,
122			   vma->vm_page_prot);
123	if (unlikely(ret)) {
124		ehca_gen_err("remap_pfn_range() failed ret=%i", ret);
125		return -ENOMEM;
126	}
127
128	vma->vm_private_data = mm_count;
129	(*mm_count)++;
130	vma->vm_ops = &vm_ops;
131
132	return 0;
133}
134
135static int ehca_mmap_queue(struct vm_area_struct *vma, struct ipz_queue *queue,
136			   u32 *mm_count)
137{
138	int ret;
139	u64 start, ofs;
140	struct page *page;
141
142	vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
143	start = vma->vm_start;
144	for (ofs = 0; ofs < queue->queue_length; ofs += PAGE_SIZE) {
145		u64 virt_addr = (u64)ipz_qeit_calc(queue, ofs);
146		page = virt_to_page(virt_addr);
147		ret = vm_insert_page(vma, start, page);
148		if (unlikely(ret)) {
149			ehca_gen_err("vm_insert_page() failed rc=%i", ret);
150			return ret;
151		}
152		start += PAGE_SIZE;
153	}
154	vma->vm_private_data = mm_count;
155	(*mm_count)++;
156	vma->vm_ops = &vm_ops;
157
158	return 0;
159}
160
161static int ehca_mmap_cq(struct vm_area_struct *vma, struct ehca_cq *cq,
162			u32 rsrc_type)
163{
164	int ret;
165
166	switch (rsrc_type) {
167	case 0: /* galpa fw handle */
168		ehca_dbg(cq->ib_cq.device, "cq_num=%x fw", cq->cq_number);
169		ret = ehca_mmap_fw(vma, &cq->galpas, &cq->mm_count_galpa);
170		if (unlikely(ret)) {
171			ehca_err(cq->ib_cq.device,
172				 "ehca_mmap_fw() failed rc=%i cq_num=%x",
173				 ret, cq->cq_number);
174			return ret;
175		}
176		break;
177
178	case 1: /* cq queue_addr */
179		ehca_dbg(cq->ib_cq.device, "cq_num=%x queue", cq->cq_number);
180		ret = ehca_mmap_queue(vma, &cq->ipz_queue, &cq->mm_count_queue);
181		if (unlikely(ret)) {
182			ehca_err(cq->ib_cq.device,
183				 "ehca_mmap_queue() failed rc=%i cq_num=%x",
184				 ret, cq->cq_number);
185			return ret;
186		}
187		break;
188
189	default:
190		ehca_err(cq->ib_cq.device, "bad resource type=%x cq_num=%x",
191			 rsrc_type, cq->cq_number);
192		return -EINVAL;
193	}
194
195	return 0;
196}
197
198static int ehca_mmap_qp(struct vm_area_struct *vma, struct ehca_qp *qp,
199			u32 rsrc_type)
200{
201	int ret;
202
203	switch (rsrc_type) {
204	case 0: /* galpa fw handle */
205		ehca_dbg(qp->ib_qp.device, "qp_num=%x fw", qp->ib_qp.qp_num);
206		ret = ehca_mmap_fw(vma, &qp->galpas, &qp->mm_count_galpa);
207		if (unlikely(ret)) {
208			ehca_err(qp->ib_qp.device,
209				 "remap_pfn_range() failed ret=%i qp_num=%x",
210				 ret, qp->ib_qp.qp_num);
211			return -ENOMEM;
212		}
213		break;
214
215	case 1: /* qp rqueue_addr */
216		ehca_dbg(qp->ib_qp.device, "qp_num=%x rq", qp->ib_qp.qp_num);
217		ret = ehca_mmap_queue(vma, &qp->ipz_rqueue,
218				      &qp->mm_count_rqueue);
219		if (unlikely(ret)) {
220			ehca_err(qp->ib_qp.device,
221				 "ehca_mmap_queue(rq) failed rc=%i qp_num=%x",
222				 ret, qp->ib_qp.qp_num);
223			return ret;
224		}
225		break;
226
227	case 2: /* qp squeue_addr */
228		ehca_dbg(qp->ib_qp.device, "qp_num=%x sq", qp->ib_qp.qp_num);
229		ret = ehca_mmap_queue(vma, &qp->ipz_squeue,
230				      &qp->mm_count_squeue);
231		if (unlikely(ret)) {
232			ehca_err(qp->ib_qp.device,
233				 "ehca_mmap_queue(sq) failed rc=%i qp_num=%x",
234				 ret, qp->ib_qp.qp_num);
235			return ret;
236		}
237		break;
238
239	default:
240		ehca_err(qp->ib_qp.device, "bad resource type=%x qp=num=%x",
241			 rsrc_type, qp->ib_qp.qp_num);
242		return -EINVAL;
243	}
244
245	return 0;
246}
247
248int ehca_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
249{
250	u64 fileoffset = vma->vm_pgoff;
251	u32 idr_handle = fileoffset & 0x1FFFFFF;
252	u32 q_type = (fileoffset >> 27) & 0x1;	  /* CQ, QP,...        */
253	u32 rsrc_type = (fileoffset >> 25) & 0x3; /* sq,rq,cmnd_window */
254	u32 ret;
255	struct ehca_cq *cq;
256	struct ehca_qp *qp;
257	struct ib_uobject *uobject;
258
259	switch (q_type) {
260	case  0: /* CQ */
261		read_lock(&ehca_cq_idr_lock);
262		cq = idr_find(&ehca_cq_idr, idr_handle);
263		read_unlock(&ehca_cq_idr_lock);
264
265		/* make sure this mmap really belongs to the authorized user */
266		if (!cq)
267			return -EINVAL;
268
269		if (!cq->ib_cq.uobject || cq->ib_cq.uobject->context != context)
270			return -EINVAL;
271
272		ret = ehca_mmap_cq(vma, cq, rsrc_type);
273		if (unlikely(ret)) {
274			ehca_err(cq->ib_cq.device,
275				 "ehca_mmap_cq() failed rc=%i cq_num=%x",
276				 ret, cq->cq_number);
277			return ret;
278		}
279		break;
280
281	case 1: /* QP */
282		read_lock(&ehca_qp_idr_lock);
283		qp = idr_find(&ehca_qp_idr, idr_handle);
284		read_unlock(&ehca_qp_idr_lock);
285
286		/* make sure this mmap really belongs to the authorized user */
287		if (!qp)
288			return -EINVAL;
289
290		uobject = IS_SRQ(qp) ? qp->ib_srq.uobject : qp->ib_qp.uobject;
291		if (!uobject || uobject->context != context)
292			return -EINVAL;
293
294		ret = ehca_mmap_qp(vma, qp, rsrc_type);
295		if (unlikely(ret)) {
296			ehca_err(qp->ib_qp.device,
297				 "ehca_mmap_qp() failed rc=%i qp_num=%x",
298				 ret, qp->ib_qp.qp_num);
299			return ret;
300		}
301		break;
302
303	default:
304		ehca_gen_err("bad queue type %x", q_type);
305		return -EINVAL;
306	}
307
308	return 0;
309}
310