]>
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 | ||
fab97220 HS |
43 | #include "ehca_classes.h" |
44 | #include "ehca_iverbs.h" | |
45 | #include "ehca_mrmw.h" | |
46 | #include "ehca_tools.h" | |
47 | #include "hcp_if.h" | |
48 | ||
49 | struct ib_ucontext *ehca_alloc_ucontext(struct ib_device *device, | |
50 | struct ib_udata *udata) | |
51 | { | |
52 | struct ehca_ucontext *my_context; | |
53 | ||
54 | my_context = kzalloc(sizeof *my_context, GFP_KERNEL); | |
55 | if (!my_context) { | |
56 | ehca_err(device, "Out of memory device=%p", device); | |
57 | return ERR_PTR(-ENOMEM); | |
58 | } | |
59 | ||
60 | return &my_context->ib_ucontext; | |
61 | } | |
62 | ||
63 | int ehca_dealloc_ucontext(struct ib_ucontext *context) | |
64 | { | |
65 | kfree(container_of(context, struct ehca_ucontext, ib_ucontext)); | |
66 | return 0; | |
67 | } | |
68 | ||
4c34bdf5 | 69 | static void ehca_mm_open(struct vm_area_struct *vma) |
fab97220 | 70 | { |
2b94397a | 71 | u32 *count = (u32 *)vma->vm_private_data; |
4c34bdf5 HNN |
72 | if (!count) { |
73 | ehca_gen_err("Invalid vma struct vm_start=%lx vm_end=%lx", | |
74 | vma->vm_start, vma->vm_end); | |
75 | return; | |
76 | } | |
77 | (*count)++; | |
78 | if (!(*count)) | |
79 | ehca_gen_err("Use count overflow vm_start=%lx vm_end=%lx", | |
80 | vma->vm_start, vma->vm_end); | |
81 | ehca_gen_dbg("vm_start=%lx vm_end=%lx count=%x", | |
82 | vma->vm_start, vma->vm_end, *count); | |
83 | } | |
fab97220 | 84 | |
4c34bdf5 HNN |
85 | static void ehca_mm_close(struct vm_area_struct *vma) |
86 | { | |
2b94397a | 87 | u32 *count = (u32 *)vma->vm_private_data; |
4c34bdf5 HNN |
88 | if (!count) { |
89 | ehca_gen_err("Invalid vma struct vm_start=%lx vm_end=%lx", | |
90 | vma->vm_start, vma->vm_end); | |
91 | return; | |
92 | } | |
93 | (*count)--; | |
94 | ehca_gen_dbg("vm_start=%lx vm_end=%lx count=%x", | |
95 | vma->vm_start, vma->vm_end, *count); | |
96 | } | |
fab97220 | 97 | |
f0f37e2f | 98 | static const struct vm_operations_struct vm_ops = { |
4c34bdf5 HNN |
99 | .open = ehca_mm_open, |
100 | .close = ehca_mm_close, | |
101 | }; | |
102 | ||
103 | static int ehca_mmap_fw(struct vm_area_struct *vma, struct h_galpas *galpas, | |
104 | u32 *mm_count) | |
105 | { | |
106 | int ret; | |
107 | u64 vsize, physical; | |
108 | ||
109 | vsize = vma->vm_end - vma->vm_start; | |
e390d3b5 | 110 | if (vsize < EHCA_PAGESIZE) { |
4c34bdf5 HNN |
111 | ehca_gen_err("invalid vsize=%lx", vma->vm_end - vma->vm_start); |
112 | return -EINVAL; | |
113 | } | |
114 | ||
115 | physical = galpas->user.fw_handle; | |
116 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | |
3750f605 | 117 | ehca_gen_dbg("vsize=%llx physical=%llx", vsize, physical); |
4c34bdf5 | 118 | /* VM_IO | VM_RESERVED are set by remap_pfn_range() */ |
e390d3b5 HNN |
119 | ret = remap_4k_pfn(vma, vma->vm_start, physical >> EHCA_PAGESHIFT, |
120 | vma->vm_page_prot); | |
4c34bdf5 | 121 | if (unlikely(ret)) { |
e3722192 | 122 | ehca_gen_err("remap_pfn_range() failed ret=%i", ret); |
4c34bdf5 HNN |
123 | return -ENOMEM; |
124 | } | |
125 | ||
126 | vma->vm_private_data = mm_count; | |
127 | (*mm_count)++; | |
128 | vma->vm_ops = &vm_ops; | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
133 | static int ehca_mmap_queue(struct vm_area_struct *vma, struct ipz_queue *queue, | |
134 | u32 *mm_count) | |
135 | { | |
136 | int ret; | |
137 | u64 start, ofs; | |
138 | struct page *page; | |
139 | ||
140 | vma->vm_flags |= VM_RESERVED; | |
141 | start = vma->vm_start; | |
142 | for (ofs = 0; ofs < queue->queue_length; ofs += PAGE_SIZE) { | |
143 | u64 virt_addr = (u64)ipz_qeit_calc(queue, ofs); | |
144 | page = virt_to_page(virt_addr); | |
145 | ret = vm_insert_page(vma, start, page); | |
146 | if (unlikely(ret)) { | |
e3722192 | 147 | ehca_gen_err("vm_insert_page() failed rc=%i", ret); |
4c34bdf5 | 148 | return ret; |
fab97220 | 149 | } |
e2f81daf | 150 | start += PAGE_SIZE; |
4c34bdf5 HNN |
151 | } |
152 | vma->vm_private_data = mm_count; | |
153 | (*mm_count)++; | |
154 | vma->vm_ops = &vm_ops; | |
fab97220 | 155 | |
4c34bdf5 HNN |
156 | return 0; |
157 | } | |
158 | ||
159 | static int ehca_mmap_cq(struct vm_area_struct *vma, struct ehca_cq *cq, | |
160 | u32 rsrc_type) | |
161 | { | |
162 | int ret; | |
163 | ||
164 | switch (rsrc_type) { | |
5281a4b8 | 165 | case 0: /* galpa fw handle */ |
4c34bdf5 HNN |
166 | ehca_dbg(cq->ib_cq.device, "cq_num=%x fw", cq->cq_number); |
167 | ret = ehca_mmap_fw(vma, &cq->galpas, &cq->mm_count_galpa); | |
168 | if (unlikely(ret)) { | |
fab97220 | 169 | ehca_err(cq->ib_cq.device, |
e3722192 | 170 | "ehca_mmap_fw() failed rc=%i cq_num=%x", |
4c34bdf5 HNN |
171 | ret, cq->cq_number); |
172 | return ret; | |
fab97220 | 173 | } |
4c34bdf5 | 174 | break; |
fab97220 | 175 | |
5281a4b8 | 176 | case 1: /* cq queue_addr */ |
4c34bdf5 HNN |
177 | ehca_dbg(cq->ib_cq.device, "cq_num=%x queue", cq->cq_number); |
178 | ret = ehca_mmap_queue(vma, &cq->ipz_queue, &cq->mm_count_queue); | |
179 | if (unlikely(ret)) { | |
180 | ehca_err(cq->ib_cq.device, | |
e3722192 | 181 | "ehca_mmap_queue() failed rc=%i cq_num=%x", |
4c34bdf5 HNN |
182 | ret, cq->cq_number); |
183 | return ret; | |
fab97220 HS |
184 | } |
185 | break; | |
186 | ||
4c34bdf5 HNN |
187 | default: |
188 | ehca_err(cq->ib_cq.device, "bad resource type=%x cq_num=%x", | |
189 | rsrc_type, cq->cq_number); | |
190 | return -EINVAL; | |
191 | } | |
fab97220 | 192 | |
4c34bdf5 HNN |
193 | return 0; |
194 | } | |
195 | ||
196 | static int ehca_mmap_qp(struct vm_area_struct *vma, struct ehca_qp *qp, | |
197 | u32 rsrc_type) | |
198 | { | |
199 | int ret; | |
200 | ||
201 | switch (rsrc_type) { | |
5281a4b8 | 202 | case 0: /* galpa fw handle */ |
4c34bdf5 HNN |
203 | ehca_dbg(qp->ib_qp.device, "qp_num=%x fw", qp->ib_qp.qp_num); |
204 | ret = ehca_mmap_fw(vma, &qp->galpas, &qp->mm_count_galpa); | |
205 | if (unlikely(ret)) { | |
206 | ehca_err(qp->ib_qp.device, | |
e3722192 | 207 | "remap_pfn_range() failed ret=%i qp_num=%x", |
4c34bdf5 HNN |
208 | ret, qp->ib_qp.qp_num); |
209 | return -ENOMEM; | |
fab97220 | 210 | } |
4c34bdf5 | 211 | break; |
fab97220 | 212 | |
5281a4b8 | 213 | case 1: /* qp rqueue_addr */ |
4da27d6d | 214 | ehca_dbg(qp->ib_qp.device, "qp_num=%x rq", qp->ib_qp.qp_num); |
2b94397a HNN |
215 | ret = ehca_mmap_queue(vma, &qp->ipz_rqueue, |
216 | &qp->mm_count_rqueue); | |
4c34bdf5 | 217 | if (unlikely(ret)) { |
fab97220 | 218 | ehca_err(qp->ib_qp.device, |
e3722192 | 219 | "ehca_mmap_queue(rq) failed rc=%i qp_num=%x", |
4c34bdf5 HNN |
220 | ret, qp->ib_qp.qp_num); |
221 | return ret; | |
fab97220 | 222 | } |
4c34bdf5 | 223 | break; |
fab97220 | 224 | |
5281a4b8 | 225 | case 2: /* qp squeue_addr */ |
4da27d6d | 226 | ehca_dbg(qp->ib_qp.device, "qp_num=%x sq", qp->ib_qp.qp_num); |
2b94397a HNN |
227 | ret = ehca_mmap_queue(vma, &qp->ipz_squeue, |
228 | &qp->mm_count_squeue); | |
4c34bdf5 HNN |
229 | if (unlikely(ret)) { |
230 | ehca_err(qp->ib_qp.device, | |
e3722192 | 231 | "ehca_mmap_queue(sq) failed rc=%i qp_num=%x", |
4c34bdf5 HNN |
232 | ret, qp->ib_qp.qp_num); |
233 | return ret; | |
fab97220 HS |
234 | } |
235 | break; | |
236 | ||
237 | default: | |
4c34bdf5 HNN |
238 | ehca_err(qp->ib_qp.device, "bad resource type=%x qp=num=%x", |
239 | rsrc_type, qp->ib_qp.qp_num); | |
240 | return -EINVAL; | |
fab97220 | 241 | } |
fab97220 | 242 | |
4c34bdf5 | 243 | return 0; |
fab97220 HS |
244 | } |
245 | ||
fab97220 HS |
246 | int ehca_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) |
247 | { | |
5281a4b8 SR |
248 | u64 fileoffset = vma->vm_pgoff; |
249 | u32 idr_handle = fileoffset & 0x1FFFFFF; | |
250 | u32 q_type = (fileoffset >> 27) & 0x1; /* CQ, QP,... */ | |
251 | u32 rsrc_type = (fileoffset >> 25) & 0x3; /* sq,rq,cmnd_window */ | |
fab97220 | 252 | u32 ret; |
fab97220 HS |
253 | struct ehca_cq *cq; |
254 | struct ehca_qp *qp; | |
a6a12947 | 255 | struct ib_uobject *uobject; |
fab97220 HS |
256 | |
257 | switch (q_type) { | |
5281a4b8 | 258 | case 0: /* CQ */ |
26ed687f | 259 | read_lock(&ehca_cq_idr_lock); |
fab97220 | 260 | cq = idr_find(&ehca_cq_idr, idr_handle); |
26ed687f | 261 | read_unlock(&ehca_cq_idr_lock); |
fab97220 HS |
262 | |
263 | /* make sure this mmap really belongs to the authorized user */ | |
264 | if (!cq) | |
265 | return -EINVAL; | |
266 | ||
fab97220 HS |
267 | if (!cq->ib_cq.uobject || cq->ib_cq.uobject->context != context) |
268 | return -EINVAL; | |
269 | ||
4c34bdf5 HNN |
270 | ret = ehca_mmap_cq(vma, cq, rsrc_type); |
271 | if (unlikely(ret)) { | |
272 | ehca_err(cq->ib_cq.device, | |
e3722192 | 273 | "ehca_mmap_cq() failed rc=%i cq_num=%x", |
4c34bdf5 HNN |
274 | ret, cq->cq_number); |
275 | return ret; | |
fab97220 HS |
276 | } |
277 | break; | |
278 | ||
5281a4b8 | 279 | case 1: /* QP */ |
26ed687f | 280 | read_lock(&ehca_qp_idr_lock); |
fab97220 | 281 | qp = idr_find(&ehca_qp_idr, idr_handle); |
26ed687f | 282 | read_unlock(&ehca_qp_idr_lock); |
fab97220 HS |
283 | |
284 | /* make sure this mmap really belongs to the authorized user */ | |
285 | if (!qp) | |
286 | return -EINVAL; | |
287 | ||
a6a12947 JF |
288 | uobject = IS_SRQ(qp) ? qp->ib_srq.uobject : qp->ib_qp.uobject; |
289 | if (!uobject || uobject->context != context) | |
fab97220 HS |
290 | return -EINVAL; |
291 | ||
4c34bdf5 HNN |
292 | ret = ehca_mmap_qp(vma, qp, rsrc_type); |
293 | if (unlikely(ret)) { | |
294 | ehca_err(qp->ib_qp.device, | |
e3722192 | 295 | "ehca_mmap_qp() failed rc=%i qp_num=%x", |
4c34bdf5 HNN |
296 | ret, qp->ib_qp.qp_num); |
297 | return ret; | |
fab97220 HS |
298 | } |
299 | break; | |
300 | ||
301 | default: | |
302 | ehca_gen_err("bad queue type %x", q_type); | |
303 | return -EINVAL; | |
304 | } | |
305 | ||
306 | return 0; | |
307 | } |