Commit | Line | Data |
---|---|---|
cef1cce5 | 1 | /* |
87427da5 | 2 | * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved. |
cef1cce5 BS |
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 <asm/io.h> | |
35 | ||
36 | #include "ipath_verbs.h" | |
b55f4f06 | 37 | #include "ipath_kernel.h" |
cef1cce5 BS |
38 | |
39 | /** | |
40 | * ipath_alloc_lkey - allocate an lkey | |
41 | * @rkt: lkey table in which to allocate the lkey | |
42 | * @mr: memory region that this lkey protects | |
43 | * | |
44 | * Returns 1 if successful, otherwise returns 0. | |
45 | */ | |
46 | ||
47 | int ipath_alloc_lkey(struct ipath_lkey_table *rkt, struct ipath_mregion *mr) | |
48 | { | |
49 | unsigned long flags; | |
50 | u32 r; | |
51 | u32 n; | |
52 | int ret; | |
53 | ||
54 | spin_lock_irqsave(&rkt->lock, flags); | |
55 | ||
56 | /* Find the next available LKEY */ | |
57 | r = n = rkt->next; | |
58 | for (;;) { | |
59 | if (rkt->table[r] == NULL) | |
60 | break; | |
61 | r = (r + 1) & (rkt->max - 1); | |
62 | if (r == n) { | |
63 | spin_unlock_irqrestore(&rkt->lock, flags); | |
39c0d0b9 | 64 | ipath_dbg("LKEY table full\n"); |
cef1cce5 BS |
65 | ret = 0; |
66 | goto bail; | |
67 | } | |
68 | } | |
69 | rkt->next = (r + 1) & (rkt->max - 1); | |
70 | /* | |
71 | * Make sure lkey is never zero which is reserved to indicate an | |
72 | * unrestricted LKEY. | |
73 | */ | |
74 | rkt->gen++; | |
75 | mr->lkey = (r << (32 - ib_ipath_lkey_table_size)) | | |
76 | ((((1 << (24 - ib_ipath_lkey_table_size)) - 1) & rkt->gen) | |
77 | << 8); | |
78 | if (mr->lkey == 0) { | |
79 | mr->lkey |= 1 << 8; | |
80 | rkt->gen++; | |
81 | } | |
82 | rkt->table[r] = mr; | |
83 | spin_unlock_irqrestore(&rkt->lock, flags); | |
84 | ||
85 | ret = 1; | |
86 | ||
87 | bail: | |
88 | return ret; | |
89 | } | |
90 | ||
91 | /** | |
92 | * ipath_free_lkey - free an lkey | |
93 | * @rkt: table from which to free the lkey | |
94 | * @lkey: lkey id to free | |
95 | */ | |
96 | void ipath_free_lkey(struct ipath_lkey_table *rkt, u32 lkey) | |
97 | { | |
98 | unsigned long flags; | |
99 | u32 r; | |
100 | ||
101 | if (lkey == 0) | |
102 | return; | |
103 | r = lkey >> (32 - ib_ipath_lkey_table_size); | |
104 | spin_lock_irqsave(&rkt->lock, flags); | |
105 | rkt->table[r] = NULL; | |
106 | spin_unlock_irqrestore(&rkt->lock, flags); | |
107 | } | |
108 | ||
109 | /** | |
110 | * ipath_lkey_ok - check IB SGE for validity and initialize | |
111 | * @rkt: table containing lkey to check SGE against | |
112 | * @isge: outgoing internal SGE | |
113 | * @sge: SGE to check | |
114 | * @acc: access flags | |
115 | * | |
116 | * Return 1 if valid and successful, otherwise returns 0. | |
117 | * | |
118 | * Check the IB SGE for validity and initialize our internal version | |
119 | * of it. | |
120 | */ | |
6a553af2 | 121 | int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge, |
cef1cce5 BS |
122 | struct ib_sge *sge, int acc) |
123 | { | |
6a553af2 | 124 | struct ipath_lkey_table *rkt = &to_idev(qp->ibqp.device)->lk_table; |
cef1cce5 | 125 | struct ipath_mregion *mr; |
12eef41f | 126 | unsigned n, m; |
cef1cce5 BS |
127 | size_t off; |
128 | int ret; | |
129 | ||
130 | /* | |
131 | * We use LKEY == zero to mean a physical kmalloc() address. | |
132 | * This is a bit of a hack since we rely on dma_map_single() | |
133 | * being reversible by calling bus_to_virt(). | |
134 | */ | |
135 | if (sge->lkey == 0) { | |
253fb390 RW |
136 | struct ipath_pd *pd = to_ipd(qp->ibqp.pd); |
137 | ||
138 | if (pd->user) { | |
139 | ret = 0; | |
140 | goto bail; | |
141 | } | |
cef1cce5 | 142 | isge->mr = NULL; |
f2cbb660 | 143 | isge->vaddr = (void *) sge->addr; |
cef1cce5 BS |
144 | isge->length = sge->length; |
145 | isge->sge_length = sge->length; | |
146 | ret = 1; | |
147 | goto bail; | |
148 | } | |
cef1cce5 | 149 | mr = rkt->table[(sge->lkey >> (32 - ib_ipath_lkey_table_size))]; |
6a553af2 BS |
150 | if (unlikely(mr == NULL || mr->lkey != sge->lkey || |
151 | qp->ibqp.pd != mr->pd)) { | |
cef1cce5 BS |
152 | ret = 0; |
153 | goto bail; | |
154 | } | |
155 | ||
156 | off = sge->addr - mr->user_base; | |
157 | if (unlikely(sge->addr < mr->user_base || | |
158 | off + sge->length > mr->length || | |
159 | (mr->access_flags & acc) != acc)) { | |
160 | ret = 0; | |
161 | goto bail; | |
162 | } | |
163 | ||
164 | off += mr->offset; | |
12eef41f BS |
165 | m = 0; |
166 | n = 0; | |
167 | while (off >= mr->map[m]->segs[n].length) { | |
168 | off -= mr->map[m]->segs[n].length; | |
169 | n++; | |
170 | if (n >= IPATH_SEGSZ) { | |
171 | m++; | |
172 | n = 0; | |
cef1cce5 BS |
173 | } |
174 | } | |
12eef41f BS |
175 | isge->mr = mr; |
176 | isge->vaddr = mr->map[m]->segs[n].vaddr + off; | |
177 | isge->length = mr->map[m]->segs[n].length - off; | |
cef1cce5 | 178 | isge->sge_length = sge->length; |
12eef41f BS |
179 | isge->m = m; |
180 | isge->n = n; | |
cef1cce5 BS |
181 | |
182 | ret = 1; | |
183 | ||
184 | bail: | |
185 | return ret; | |
186 | } | |
187 | ||
188 | /** | |
189 | * ipath_rkey_ok - check the IB virtual address, length, and RKEY | |
190 | * @dev: infiniband device | |
191 | * @ss: SGE state | |
192 | * @len: length of data | |
193 | * @vaddr: virtual address to place data | |
194 | * @rkey: rkey to check | |
195 | * @acc: access flags | |
196 | * | |
197 | * Return 1 if successful, otherwise 0. | |
cef1cce5 | 198 | */ |
6a553af2 | 199 | int ipath_rkey_ok(struct ipath_qp *qp, struct ipath_sge_state *ss, |
cef1cce5 BS |
200 | u32 len, u64 vaddr, u32 rkey, int acc) |
201 | { | |
6a553af2 | 202 | struct ipath_ibdev *dev = to_idev(qp->ibqp.device); |
cef1cce5 BS |
203 | struct ipath_lkey_table *rkt = &dev->lk_table; |
204 | struct ipath_sge *sge = &ss->sge; | |
205 | struct ipath_mregion *mr; | |
12eef41f | 206 | unsigned n, m; |
cef1cce5 BS |
207 | size_t off; |
208 | int ret; | |
209 | ||
c9f79bdc | 210 | /* |
f2cbb660 RC |
211 | * We use RKEY == zero for kernel virtual addresses |
212 | * (see ipath_get_dma_mr and ipath_dma.c). | |
c9f79bdc RC |
213 | */ |
214 | if (rkey == 0) { | |
253fb390 RW |
215 | struct ipath_pd *pd = to_ipd(qp->ibqp.pd); |
216 | ||
217 | if (pd->user) { | |
218 | ret = 0; | |
219 | goto bail; | |
220 | } | |
c9f79bdc | 221 | sge->mr = NULL; |
f2cbb660 | 222 | sge->vaddr = (void *) vaddr; |
c9f79bdc RC |
223 | sge->length = len; |
224 | sge->sge_length = len; | |
225 | ss->sg_list = NULL; | |
226 | ss->num_sge = 1; | |
227 | ret = 1; | |
228 | goto bail; | |
229 | } | |
230 | ||
cef1cce5 | 231 | mr = rkt->table[(rkey >> (32 - ib_ipath_lkey_table_size))]; |
6a553af2 BS |
232 | if (unlikely(mr == NULL || mr->lkey != rkey || |
233 | qp->ibqp.pd != mr->pd)) { | |
cef1cce5 BS |
234 | ret = 0; |
235 | goto bail; | |
236 | } | |
237 | ||
238 | off = vaddr - mr->iova; | |
239 | if (unlikely(vaddr < mr->iova || off + len > mr->length || | |
240 | (mr->access_flags & acc) == 0)) { | |
241 | ret = 0; | |
242 | goto bail; | |
243 | } | |
244 | ||
245 | off += mr->offset; | |
12eef41f BS |
246 | m = 0; |
247 | n = 0; | |
248 | while (off >= mr->map[m]->segs[n].length) { | |
249 | off -= mr->map[m]->segs[n].length; | |
250 | n++; | |
251 | if (n >= IPATH_SEGSZ) { | |
252 | m++; | |
253 | n = 0; | |
cef1cce5 BS |
254 | } |
255 | } | |
12eef41f BS |
256 | sge->mr = mr; |
257 | sge->vaddr = mr->map[m]->segs[n].vaddr + off; | |
258 | sge->length = mr->map[m]->segs[n].length - off; | |
cef1cce5 | 259 | sge->sge_length = len; |
12eef41f BS |
260 | sge->m = m; |
261 | sge->n = n; | |
cef1cce5 BS |
262 | ss->sg_list = NULL; |
263 | ss->num_sge = 1; | |
264 | ||
265 | ret = 1; | |
266 | ||
267 | bail: | |
268 | return ret; | |
269 | } |