]>
Commit | Line | Data |
---|---|---|
fab97220 HS |
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 | ||
5a0e3ad6 TH |
43 | #include <linux/slab.h> |
44 | ||
fab97220 HS |
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 | ||
51 | struct 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 | ||
65 | int ehca_dealloc_ucontext(struct ib_ucontext *context) | |
66 | { | |
67 | kfree(container_of(context, struct ehca_ucontext, ib_ucontext)); | |
68 | return 0; | |
69 | } | |
70 | ||
4c34bdf5 | 71 | static void ehca_mm_open(struct vm_area_struct *vma) |
fab97220 | 72 | { |
2b94397a | 73 | u32 *count = (u32 *)vma->vm_private_data; |
4c34bdf5 HNN |
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 | } | |
fab97220 | 86 | |
4c34bdf5 HNN |
87 | static void ehca_mm_close(struct vm_area_struct *vma) |
88 | { | |
2b94397a | 89 | u32 *count = (u32 *)vma->vm_private_data; |
4c34bdf5 HNN |
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 | } | |
fab97220 | 99 | |
f0f37e2f | 100 | static const struct vm_operations_struct vm_ops = { |
4c34bdf5 HNN |
101 | .open = ehca_mm_open, |
102 | .close = ehca_mm_close, | |
103 | }; | |
104 | ||
105 | static 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; | |
e390d3b5 | 112 | if (vsize < EHCA_PAGESIZE) { |
4c34bdf5 HNN |
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); | |
3750f605 | 119 | ehca_gen_dbg("vsize=%llx physical=%llx", vsize, physical); |
314e51b9 | 120 | /* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */ |
e390d3b5 HNN |
121 | ret = remap_4k_pfn(vma, vma->vm_start, physical >> EHCA_PAGESHIFT, |
122 | vma->vm_page_prot); | |
4c34bdf5 | 123 | if (unlikely(ret)) { |
e3722192 | 124 | ehca_gen_err("remap_pfn_range() failed ret=%i", ret); |
4c34bdf5 HNN |
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 | ||
135 | static 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 | ||
314e51b9 | 142 | vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; |
4c34bdf5 HNN |
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)) { | |
e3722192 | 149 | ehca_gen_err("vm_insert_page() failed rc=%i", ret); |
4c34bdf5 | 150 | return ret; |
fab97220 | 151 | } |
e2f81daf | 152 | start += PAGE_SIZE; |
4c34bdf5 HNN |
153 | } |
154 | vma->vm_private_data = mm_count; | |
155 | (*mm_count)++; | |
156 | vma->vm_ops = &vm_ops; | |
fab97220 | 157 | |
4c34bdf5 HNN |
158 | return 0; |
159 | } | |
160 | ||
161 | static 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) { | |
5281a4b8 | 167 | case 0: /* galpa fw handle */ |
4c34bdf5 HNN |
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)) { | |
fab97220 | 171 | ehca_err(cq->ib_cq.device, |
e3722192 | 172 | "ehca_mmap_fw() failed rc=%i cq_num=%x", |
4c34bdf5 HNN |
173 | ret, cq->cq_number); |
174 | return ret; | |
fab97220 | 175 | } |
4c34bdf5 | 176 | break; |
fab97220 | 177 | |
5281a4b8 | 178 | case 1: /* cq queue_addr */ |
4c34bdf5 HNN |
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, | |
e3722192 | 183 | "ehca_mmap_queue() failed rc=%i cq_num=%x", |
4c34bdf5 HNN |
184 | ret, cq->cq_number); |
185 | return ret; | |
fab97220 HS |
186 | } |
187 | break; | |
188 | ||
4c34bdf5 HNN |
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 | } | |
fab97220 | 194 | |
4c34bdf5 HNN |
195 | return 0; |
196 | } | |
197 | ||
198 | static 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) { | |
5281a4b8 | 204 | case 0: /* galpa fw handle */ |
4c34bdf5 HNN |
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, | |
e3722192 | 209 | "remap_pfn_range() failed ret=%i qp_num=%x", |
4c34bdf5 HNN |
210 | ret, qp->ib_qp.qp_num); |
211 | return -ENOMEM; | |
fab97220 | 212 | } |
4c34bdf5 | 213 | break; |
fab97220 | 214 | |
5281a4b8 | 215 | case 1: /* qp rqueue_addr */ |
4da27d6d | 216 | ehca_dbg(qp->ib_qp.device, "qp_num=%x rq", qp->ib_qp.qp_num); |
2b94397a HNN |
217 | ret = ehca_mmap_queue(vma, &qp->ipz_rqueue, |
218 | &qp->mm_count_rqueue); | |
4c34bdf5 | 219 | if (unlikely(ret)) { |
fab97220 | 220 | ehca_err(qp->ib_qp.device, |
e3722192 | 221 | "ehca_mmap_queue(rq) failed rc=%i qp_num=%x", |
4c34bdf5 HNN |
222 | ret, qp->ib_qp.qp_num); |
223 | return ret; | |
fab97220 | 224 | } |
4c34bdf5 | 225 | break; |
fab97220 | 226 | |
5281a4b8 | 227 | case 2: /* qp squeue_addr */ |
4da27d6d | 228 | ehca_dbg(qp->ib_qp.device, "qp_num=%x sq", qp->ib_qp.qp_num); |
2b94397a HNN |
229 | ret = ehca_mmap_queue(vma, &qp->ipz_squeue, |
230 | &qp->mm_count_squeue); | |
4c34bdf5 HNN |
231 | if (unlikely(ret)) { |
232 | ehca_err(qp->ib_qp.device, | |
e3722192 | 233 | "ehca_mmap_queue(sq) failed rc=%i qp_num=%x", |
4c34bdf5 HNN |
234 | ret, qp->ib_qp.qp_num); |
235 | return ret; | |
fab97220 HS |
236 | } |
237 | break; | |
238 | ||
239 | default: | |
4c34bdf5 HNN |
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; | |
fab97220 | 243 | } |
fab97220 | 244 | |
4c34bdf5 | 245 | return 0; |
fab97220 HS |
246 | } |
247 | ||
fab97220 HS |
248 | int ehca_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) |
249 | { | |
5281a4b8 SR |
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 */ | |
fab97220 | 254 | u32 ret; |
fab97220 HS |
255 | struct ehca_cq *cq; |
256 | struct ehca_qp *qp; | |
a6a12947 | 257 | struct ib_uobject *uobject; |
fab97220 HS |
258 | |
259 | switch (q_type) { | |
5281a4b8 | 260 | case 0: /* CQ */ |
26ed687f | 261 | read_lock(&ehca_cq_idr_lock); |
fab97220 | 262 | cq = idr_find(&ehca_cq_idr, idr_handle); |
26ed687f | 263 | read_unlock(&ehca_cq_idr_lock); |
fab97220 HS |
264 | |
265 | /* make sure this mmap really belongs to the authorized user */ | |
266 | if (!cq) | |
267 | return -EINVAL; | |
268 | ||
fab97220 HS |
269 | if (!cq->ib_cq.uobject || cq->ib_cq.uobject->context != context) |
270 | return -EINVAL; | |
271 | ||
4c34bdf5 HNN |
272 | ret = ehca_mmap_cq(vma, cq, rsrc_type); |
273 | if (unlikely(ret)) { | |
274 | ehca_err(cq->ib_cq.device, | |
e3722192 | 275 | "ehca_mmap_cq() failed rc=%i cq_num=%x", |
4c34bdf5 HNN |
276 | ret, cq->cq_number); |
277 | return ret; | |
fab97220 HS |
278 | } |
279 | break; | |
280 | ||
5281a4b8 | 281 | case 1: /* QP */ |
26ed687f | 282 | read_lock(&ehca_qp_idr_lock); |
fab97220 | 283 | qp = idr_find(&ehca_qp_idr, idr_handle); |
26ed687f | 284 | read_unlock(&ehca_qp_idr_lock); |
fab97220 HS |
285 | |
286 | /* make sure this mmap really belongs to the authorized user */ | |
287 | if (!qp) | |
288 | return -EINVAL; | |
289 | ||
a6a12947 JF |
290 | uobject = IS_SRQ(qp) ? qp->ib_srq.uobject : qp->ib_qp.uobject; |
291 | if (!uobject || uobject->context != context) | |
fab97220 HS |
292 | return -EINVAL; |
293 | ||
4c34bdf5 HNN |
294 | ret = ehca_mmap_qp(vma, qp, rsrc_type); |
295 | if (unlikely(ret)) { | |
296 | ehca_err(qp->ib_qp.device, | |
e3722192 | 297 | "ehca_mmap_qp() failed rc=%i qp_num=%x", |
4c34bdf5 HNN |
298 | ret, qp->ib_qp.qp_num); |
299 | return ret; | |
fab97220 HS |
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 | } |