diff options
Diffstat (limited to 'drivers/net/mlx4/mr.c')
-rw-r--r-- | drivers/net/mlx4/mr.c | 667 |
1 files changed, 667 insertions, 0 deletions
diff --git a/drivers/net/mlx4/mr.c b/drivers/net/mlx4/mr.c new file mode 100644 index 00000000000..9c188bdd7f4 --- /dev/null +++ b/drivers/net/mlx4/mr.c | |||
@@ -0,0 +1,667 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2004 Topspin Communications. All rights reserved. | ||
3 | * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved. | ||
4 | * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. | ||
5 | * | ||
6 | * This software is available to you under a choice of one of two | ||
7 | * licenses. You may choose to be licensed under the terms of the GNU | ||
8 | * General Public License (GPL) Version 2, available from the file | ||
9 | * COPYING in the main directory of this source tree, or the | ||
10 | * OpenIB.org BSD license below: | ||
11 | * | ||
12 | * Redistribution and use in source and binary forms, with or | ||
13 | * without modification, are permitted provided that the following | ||
14 | * conditions are met: | ||
15 | * | ||
16 | * - Redistributions of source code must retain the above | ||
17 | * copyright notice, this list of conditions and the following | ||
18 | * disclaimer. | ||
19 | * | ||
20 | * - Redistributions in binary form must reproduce the above | ||
21 | * copyright notice, this list of conditions and the following | ||
22 | * disclaimer in the documentation and/or other materials | ||
23 | * provided with the distribution. | ||
24 | * | ||
25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
26 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
27 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
28 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
29 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
30 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
31 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
32 | * SOFTWARE. | ||
33 | */ | ||
34 | |||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | |||
38 | #include <linux/mlx4/cmd.h> | ||
39 | |||
40 | #include "mlx4.h" | ||
41 | #include "icm.h" | ||
42 | |||
43 | /* | ||
44 | * Must be packed because mtt_seg is 64 bits but only aligned to 32 bits. | ||
45 | */ | ||
46 | struct mlx4_mpt_entry { | ||
47 | __be32 flags; | ||
48 | __be32 qpn; | ||
49 | __be32 key; | ||
50 | __be32 pd_flags; | ||
51 | __be64 start; | ||
52 | __be64 length; | ||
53 | __be32 lkey; | ||
54 | __be32 win_cnt; | ||
55 | u8 reserved1[3]; | ||
56 | u8 mtt_rep; | ||
57 | __be64 mtt_seg; | ||
58 | __be32 mtt_sz; | ||
59 | __be32 entity_size; | ||
60 | __be32 first_byte_offset; | ||
61 | } __packed; | ||
62 | |||
63 | #define MLX4_MPT_FLAG_SW_OWNS (0xfUL << 28) | ||
64 | #define MLX4_MPT_FLAG_FREE (0x3UL << 28) | ||
65 | #define MLX4_MPT_FLAG_MIO (1 << 17) | ||
66 | #define MLX4_MPT_FLAG_BIND_ENABLE (1 << 15) | ||
67 | #define MLX4_MPT_FLAG_PHYSICAL (1 << 9) | ||
68 | #define MLX4_MPT_FLAG_REGION (1 << 8) | ||
69 | |||
70 | #define MLX4_MPT_PD_FLAG_FAST_REG (1 << 27) | ||
71 | #define MLX4_MPT_PD_FLAG_RAE (1 << 28) | ||
72 | #define MLX4_MPT_PD_FLAG_EN_INV (3 << 24) | ||
73 | |||
74 | #define MLX4_MPT_STATUS_SW 0xF0 | ||
75 | #define MLX4_MPT_STATUS_HW 0x00 | ||
76 | |||
77 | static u32 mlx4_buddy_alloc(struct mlx4_buddy *buddy, int order) | ||
78 | { | ||
79 | int o; | ||
80 | int m; | ||
81 | u32 seg; | ||
82 | |||
83 | spin_lock(&buddy->lock); | ||
84 | |||
85 | for (o = order; o <= buddy->max_order; ++o) | ||
86 | if (buddy->num_free[o]) { | ||
87 | m = 1 << (buddy->max_order - o); | ||
88 | seg = find_first_bit(buddy->bits[o], m); | ||
89 | if (seg < m) | ||
90 | goto found; | ||
91 | } | ||
92 | |||
93 | spin_unlock(&buddy->lock); | ||
94 | return -1; | ||
95 | |||
96 | found: | ||
97 | clear_bit(seg, buddy->bits[o]); | ||
98 | --buddy->num_free[o]; | ||
99 | |||
100 | while (o > order) { | ||
101 | --o; | ||
102 | seg <<= 1; | ||
103 | set_bit(seg ^ 1, buddy->bits[o]); | ||
104 | ++buddy->num_free[o]; | ||
105 | } | ||
106 | |||
107 | spin_unlock(&buddy->lock); | ||
108 | |||
109 | seg <<= order; | ||
110 | |||
111 | return seg; | ||
112 | } | ||
113 | |||
114 | static void mlx4_buddy_free(struct mlx4_buddy *buddy, u32 seg, int order) | ||
115 | { | ||
116 | seg >>= order; | ||
117 | |||
118 | spin_lock(&buddy->lock); | ||
119 | |||
120 | while (test_bit(seg ^ 1, buddy->bits[order])) { | ||
121 | clear_bit(seg ^ 1, buddy->bits[order]); | ||
122 | --buddy->num_free[order]; | ||
123 | seg >>= 1; | ||
124 | ++order; | ||
125 | } | ||
126 | |||
127 | set_bit(seg, buddy->bits[order]); | ||
128 | ++buddy->num_free[order]; | ||
129 | |||
130 | spin_unlock(&buddy->lock); | ||
131 | } | ||
132 | |||
133 | static int mlx4_buddy_init(struct mlx4_buddy *buddy, int max_order) | ||
134 | { | ||
135 | int i, s; | ||
136 | |||
137 | buddy->max_order = max_order; | ||
138 | spin_lock_init(&buddy->lock); | ||
139 | |||
140 | buddy->bits = kzalloc((buddy->max_order + 1) * sizeof (long *), | ||
141 | GFP_KERNEL); | ||
142 | buddy->num_free = kzalloc((buddy->max_order + 1) * sizeof (int *), | ||
143 | GFP_KERNEL); | ||
144 | if (!buddy->bits || !buddy->num_free) | ||
145 | goto err_out; | ||
146 | |||
147 | for (i = 0; i <= buddy->max_order; ++i) { | ||
148 | s = BITS_TO_LONGS(1 << (buddy->max_order - i)); | ||
149 | buddy->bits[i] = kmalloc(s * sizeof (long), GFP_KERNEL); | ||
150 | if (!buddy->bits[i]) | ||
151 | goto err_out_free; | ||
152 | bitmap_zero(buddy->bits[i], 1 << (buddy->max_order - i)); | ||
153 | } | ||
154 | |||
155 | set_bit(0, buddy->bits[buddy->max_order]); | ||
156 | buddy->num_free[buddy->max_order] = 1; | ||
157 | |||
158 | return 0; | ||
159 | |||
160 | err_out_free: | ||
161 | for (i = 0; i <= buddy->max_order; ++i) | ||
162 | kfree(buddy->bits[i]); | ||
163 | |||
164 | err_out: | ||
165 | kfree(buddy->bits); | ||
166 | kfree(buddy->num_free); | ||
167 | |||
168 | return -ENOMEM; | ||
169 | } | ||
170 | |||
171 | static void mlx4_buddy_cleanup(struct mlx4_buddy *buddy) | ||
172 | { | ||
173 | int i; | ||
174 | |||
175 | for (i = 0; i <= buddy->max_order; ++i) | ||
176 | kfree(buddy->bits[i]); | ||
177 | |||
178 | kfree(buddy->bits); | ||
179 | kfree(buddy->num_free); | ||
180 | } | ||
181 | |||
182 | static u32 mlx4_alloc_mtt_range(struct mlx4_dev *dev, int order) | ||
183 | { | ||
184 | struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; | ||
185 | u32 seg; | ||
186 | |||
187 | seg = mlx4_buddy_alloc(&mr_table->mtt_buddy, order); | ||
188 | if (seg == -1) | ||
189 | return -1; | ||
190 | |||
191 | if (mlx4_table_get_range(dev, &mr_table->mtt_table, seg, | ||
192 | seg + (1 << order) - 1)) { | ||
193 | mlx4_buddy_free(&mr_table->mtt_buddy, seg, order); | ||
194 | return -1; | ||
195 | } | ||
196 | |||
197 | return seg; | ||
198 | } | ||
199 | |||
200 | int mlx4_mtt_init(struct mlx4_dev *dev, int npages, int page_shift, | ||
201 | struct mlx4_mtt *mtt) | ||
202 | { | ||
203 | int i; | ||
204 | |||
205 | if (!npages) { | ||
206 | mtt->order = -1; | ||
207 | mtt->page_shift = MLX4_ICM_PAGE_SHIFT; | ||
208 | return 0; | ||
209 | } else | ||
210 | mtt->page_shift = page_shift; | ||
211 | |||
212 | for (mtt->order = 0, i = dev->caps.mtts_per_seg; i < npages; i <<= 1) | ||
213 | ++mtt->order; | ||
214 | |||
215 | mtt->first_seg = mlx4_alloc_mtt_range(dev, mtt->order); | ||
216 | if (mtt->first_seg == -1) | ||
217 | return -ENOMEM; | ||
218 | |||
219 | return 0; | ||
220 | } | ||
221 | EXPORT_SYMBOL_GPL(mlx4_mtt_init); | ||
222 | |||
223 | void mlx4_mtt_cleanup(struct mlx4_dev *dev, struct mlx4_mtt *mtt) | ||
224 | { | ||
225 | struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; | ||
226 | |||
227 | if (mtt->order < 0) | ||
228 | return; | ||
229 | |||
230 | mlx4_buddy_free(&mr_table->mtt_buddy, mtt->first_seg, mtt->order); | ||
231 | mlx4_table_put_range(dev, &mr_table->mtt_table, mtt->first_seg, | ||
232 | mtt->first_seg + (1 << mtt->order) - 1); | ||
233 | } | ||
234 | EXPORT_SYMBOL_GPL(mlx4_mtt_cleanup); | ||
235 | |||
236 | u64 mlx4_mtt_addr(struct mlx4_dev *dev, struct mlx4_mtt *mtt) | ||
237 | { | ||
238 | return (u64) mtt->first_seg * dev->caps.mtt_entry_sz; | ||
239 | } | ||
240 | EXPORT_SYMBOL_GPL(mlx4_mtt_addr); | ||
241 | |||
242 | static u32 hw_index_to_key(u32 ind) | ||
243 | { | ||
244 | return (ind >> 24) | (ind << 8); | ||
245 | } | ||
246 | |||
247 | static u32 key_to_hw_index(u32 key) | ||
248 | { | ||
249 | return (key << 24) | (key >> 8); | ||
250 | } | ||
251 | |||
252 | static int mlx4_SW2HW_MPT(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, | ||
253 | int mpt_index) | ||
254 | { | ||
255 | return mlx4_cmd(dev, mailbox->dma, mpt_index, 0, MLX4_CMD_SW2HW_MPT, | ||
256 | MLX4_CMD_TIME_CLASS_B); | ||
257 | } | ||
258 | |||
259 | static int mlx4_HW2SW_MPT(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, | ||
260 | int mpt_index) | ||
261 | { | ||
262 | return mlx4_cmd_box(dev, 0, mailbox ? mailbox->dma : 0, mpt_index, | ||
263 | !mailbox, MLX4_CMD_HW2SW_MPT, MLX4_CMD_TIME_CLASS_B); | ||
264 | } | ||
265 | |||
266 | int mlx4_mr_alloc(struct mlx4_dev *dev, u32 pd, u64 iova, u64 size, u32 access, | ||
267 | int npages, int page_shift, struct mlx4_mr *mr) | ||
268 | { | ||
269 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
270 | u32 index; | ||
271 | int err; | ||
272 | |||
273 | index = mlx4_bitmap_alloc(&priv->mr_table.mpt_bitmap); | ||
274 | if (index == -1) | ||
275 | return -ENOMEM; | ||
276 | |||
277 | mr->iova = iova; | ||
278 | mr->size = size; | ||
279 | mr->pd = pd; | ||
280 | mr->access = access; | ||
281 | mr->enabled = 0; | ||
282 | mr->key = hw_index_to_key(index); | ||
283 | |||
284 | err = mlx4_mtt_init(dev, npages, page_shift, &mr->mtt); | ||
285 | if (err) | ||
286 | mlx4_bitmap_free(&priv->mr_table.mpt_bitmap, index); | ||
287 | |||
288 | return err; | ||
289 | } | ||
290 | EXPORT_SYMBOL_GPL(mlx4_mr_alloc); | ||
291 | |||
292 | void mlx4_mr_free(struct mlx4_dev *dev, struct mlx4_mr *mr) | ||
293 | { | ||
294 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
295 | int err; | ||
296 | |||
297 | if (mr->enabled) { | ||
298 | err = mlx4_HW2SW_MPT(dev, NULL, | ||
299 | key_to_hw_index(mr->key) & | ||
300 | (dev->caps.num_mpts - 1)); | ||
301 | if (err) | ||
302 | mlx4_warn(dev, "HW2SW_MPT failed (%d)\n", err); | ||
303 | } | ||
304 | |||
305 | mlx4_mtt_cleanup(dev, &mr->mtt); | ||
306 | mlx4_bitmap_free(&priv->mr_table.mpt_bitmap, key_to_hw_index(mr->key)); | ||
307 | } | ||
308 | EXPORT_SYMBOL_GPL(mlx4_mr_free); | ||
309 | |||
310 | int mlx4_mr_enable(struct mlx4_dev *dev, struct mlx4_mr *mr) | ||
311 | { | ||
312 | struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; | ||
313 | struct mlx4_cmd_mailbox *mailbox; | ||
314 | struct mlx4_mpt_entry *mpt_entry; | ||
315 | int err; | ||
316 | |||
317 | err = mlx4_table_get(dev, &mr_table->dmpt_table, key_to_hw_index(mr->key)); | ||
318 | if (err) | ||
319 | return err; | ||
320 | |||
321 | mailbox = mlx4_alloc_cmd_mailbox(dev); | ||
322 | if (IS_ERR(mailbox)) { | ||
323 | err = PTR_ERR(mailbox); | ||
324 | goto err_table; | ||
325 | } | ||
326 | mpt_entry = mailbox->buf; | ||
327 | |||
328 | memset(mpt_entry, 0, sizeof *mpt_entry); | ||
329 | |||
330 | mpt_entry->flags = cpu_to_be32(MLX4_MPT_FLAG_MIO | | ||
331 | MLX4_MPT_FLAG_REGION | | ||
332 | mr->access); | ||
333 | |||
334 | mpt_entry->key = cpu_to_be32(key_to_hw_index(mr->key)); | ||
335 | mpt_entry->pd_flags = cpu_to_be32(mr->pd | MLX4_MPT_PD_FLAG_EN_INV); | ||
336 | mpt_entry->start = cpu_to_be64(mr->iova); | ||
337 | mpt_entry->length = cpu_to_be64(mr->size); | ||
338 | mpt_entry->entity_size = cpu_to_be32(mr->mtt.page_shift); | ||
339 | |||
340 | if (mr->mtt.order < 0) { | ||
341 | mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_PHYSICAL); | ||
342 | mpt_entry->mtt_seg = 0; | ||
343 | } else { | ||
344 | mpt_entry->mtt_seg = cpu_to_be64(mlx4_mtt_addr(dev, &mr->mtt)); | ||
345 | } | ||
346 | |||
347 | if (mr->mtt.order >= 0 && mr->mtt.page_shift == 0) { | ||
348 | /* fast register MR in free state */ | ||
349 | mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_FREE); | ||
350 | mpt_entry->pd_flags |= cpu_to_be32(MLX4_MPT_PD_FLAG_FAST_REG | | ||
351 | MLX4_MPT_PD_FLAG_RAE); | ||
352 | mpt_entry->mtt_sz = cpu_to_be32((1 << mr->mtt.order) * | ||
353 | dev->caps.mtts_per_seg); | ||
354 | } else { | ||
355 | mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_SW_OWNS); | ||
356 | } | ||
357 | |||
358 | err = mlx4_SW2HW_MPT(dev, mailbox, | ||
359 | key_to_hw_index(mr->key) & (dev->caps.num_mpts - 1)); | ||
360 | if (err) { | ||
361 | mlx4_warn(dev, "SW2HW_MPT failed (%d)\n", err); | ||
362 | goto err_cmd; | ||
363 | } | ||
364 | |||
365 | mr->enabled = 1; | ||
366 | |||
367 | mlx4_free_cmd_mailbox(dev, mailbox); | ||
368 | |||
369 | return 0; | ||
370 | |||
371 | err_cmd: | ||
372 | mlx4_free_cmd_mailbox(dev, mailbox); | ||
373 | |||
374 | err_table: | ||
375 | mlx4_table_put(dev, &mr_table->dmpt_table, key_to_hw_index(mr->key)); | ||
376 | return err; | ||
377 | } | ||
378 | EXPORT_SYMBOL_GPL(mlx4_mr_enable); | ||
379 | |||
380 | static int mlx4_write_mtt_chunk(struct mlx4_dev *dev, struct mlx4_mtt *mtt, | ||
381 | int start_index, int npages, u64 *page_list) | ||
382 | { | ||
383 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
384 | __be64 *mtts; | ||
385 | dma_addr_t dma_handle; | ||
386 | int i; | ||
387 | int s = start_index * sizeof (u64); | ||
388 | |||
389 | /* All MTTs must fit in the same page */ | ||
390 | if (start_index / (PAGE_SIZE / sizeof (u64)) != | ||
391 | (start_index + npages - 1) / (PAGE_SIZE / sizeof (u64))) | ||
392 | return -EINVAL; | ||
393 | |||
394 | if (start_index & (dev->caps.mtts_per_seg - 1)) | ||
395 | return -EINVAL; | ||
396 | |||
397 | mtts = mlx4_table_find(&priv->mr_table.mtt_table, mtt->first_seg + | ||
398 | s / dev->caps.mtt_entry_sz, &dma_handle); | ||
399 | if (!mtts) | ||
400 | return -ENOMEM; | ||
401 | |||
402 | dma_sync_single_for_cpu(&dev->pdev->dev, dma_handle, | ||
403 | npages * sizeof (u64), DMA_TO_DEVICE); | ||
404 | |||
405 | for (i = 0; i < npages; ++i) | ||
406 | mtts[i] = cpu_to_be64(page_list[i] | MLX4_MTT_FLAG_PRESENT); | ||
407 | |||
408 | dma_sync_single_for_device(&dev->pdev->dev, dma_handle, | ||
409 | npages * sizeof (u64), DMA_TO_DEVICE); | ||
410 | |||
411 | return 0; | ||
412 | } | ||
413 | |||
414 | int mlx4_write_mtt(struct mlx4_dev *dev, struct mlx4_mtt *mtt, | ||
415 | int start_index, int npages, u64 *page_list) | ||
416 | { | ||
417 | int chunk; | ||
418 | int err; | ||
419 | |||
420 | if (mtt->order < 0) | ||
421 | return -EINVAL; | ||
422 | |||
423 | while (npages > 0) { | ||
424 | chunk = min_t(int, PAGE_SIZE / sizeof(u64), npages); | ||
425 | err = mlx4_write_mtt_chunk(dev, mtt, start_index, chunk, page_list); | ||
426 | if (err) | ||
427 | return err; | ||
428 | |||
429 | npages -= chunk; | ||
430 | start_index += chunk; | ||
431 | page_list += chunk; | ||
432 | } | ||
433 | |||
434 | return 0; | ||
435 | } | ||
436 | EXPORT_SYMBOL_GPL(mlx4_write_mtt); | ||
437 | |||
438 | int mlx4_buf_write_mtt(struct mlx4_dev *dev, struct mlx4_mtt *mtt, | ||
439 | struct mlx4_buf *buf) | ||
440 | { | ||
441 | u64 *page_list; | ||
442 | int err; | ||
443 | int i; | ||
444 | |||
445 | page_list = kmalloc(buf->npages * sizeof *page_list, GFP_KERNEL); | ||
446 | if (!page_list) | ||
447 | return -ENOMEM; | ||
448 | |||
449 | for (i = 0; i < buf->npages; ++i) | ||
450 | if (buf->nbufs == 1) | ||
451 | page_list[i] = buf->direct.map + (i << buf->page_shift); | ||
452 | else | ||
453 | page_list[i] = buf->page_list[i].map; | ||
454 | |||
455 | err = mlx4_write_mtt(dev, mtt, 0, buf->npages, page_list); | ||
456 | |||
457 | kfree(page_list); | ||
458 | return err; | ||
459 | } | ||
460 | EXPORT_SYMBOL_GPL(mlx4_buf_write_mtt); | ||
461 | |||
462 | int mlx4_init_mr_table(struct mlx4_dev *dev) | ||
463 | { | ||
464 | struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; | ||
465 | int err; | ||
466 | |||
467 | err = mlx4_bitmap_init(&mr_table->mpt_bitmap, dev->caps.num_mpts, | ||
468 | ~0, dev->caps.reserved_mrws, 0); | ||
469 | if (err) | ||
470 | return err; | ||
471 | |||
472 | err = mlx4_buddy_init(&mr_table->mtt_buddy, | ||
473 | ilog2(dev->caps.num_mtt_segs)); | ||
474 | if (err) | ||
475 | goto err_buddy; | ||
476 | |||
477 | if (dev->caps.reserved_mtts) { | ||
478 | if (mlx4_alloc_mtt_range(dev, fls(dev->caps.reserved_mtts - 1)) == -1) { | ||
479 | mlx4_warn(dev, "MTT table of order %d is too small.\n", | ||
480 | mr_table->mtt_buddy.max_order); | ||
481 | err = -ENOMEM; | ||
482 | goto err_reserve_mtts; | ||
483 | } | ||
484 | } | ||
485 | |||
486 | return 0; | ||
487 | |||
488 | err_reserve_mtts: | ||
489 | mlx4_buddy_cleanup(&mr_table->mtt_buddy); | ||
490 | |||
491 | err_buddy: | ||
492 | mlx4_bitmap_cleanup(&mr_table->mpt_bitmap); | ||
493 | |||
494 | return err; | ||
495 | } | ||
496 | |||
497 | void mlx4_cleanup_mr_table(struct mlx4_dev *dev) | ||
498 | { | ||
499 | struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; | ||
500 | |||
501 | mlx4_buddy_cleanup(&mr_table->mtt_buddy); | ||
502 | mlx4_bitmap_cleanup(&mr_table->mpt_bitmap); | ||
503 | } | ||
504 | |||
505 | static inline int mlx4_check_fmr(struct mlx4_fmr *fmr, u64 *page_list, | ||
506 | int npages, u64 iova) | ||
507 | { | ||
508 | int i, page_mask; | ||
509 | |||
510 | if (npages > fmr->max_pages) | ||
511 | return -EINVAL; | ||
512 | |||
513 | page_mask = (1 << fmr->page_shift) - 1; | ||
514 | |||
515 | /* We are getting page lists, so va must be page aligned. */ | ||
516 | if (iova & page_mask) | ||
517 | return -EINVAL; | ||
518 | |||
519 | /* Trust the user not to pass misaligned data in page_list */ | ||
520 | if (0) | ||
521 | for (i = 0; i < npages; ++i) { | ||
522 | if (page_list[i] & ~page_mask) | ||
523 | return -EINVAL; | ||
524 | } | ||
525 | |||
526 | if (fmr->maps >= fmr->max_maps) | ||
527 | return -EINVAL; | ||
528 | |||
529 | return 0; | ||
530 | } | ||
531 | |||
532 | int mlx4_map_phys_fmr(struct mlx4_dev *dev, struct mlx4_fmr *fmr, u64 *page_list, | ||
533 | int npages, u64 iova, u32 *lkey, u32 *rkey) | ||
534 | { | ||
535 | u32 key; | ||
536 | int i, err; | ||
537 | |||
538 | err = mlx4_check_fmr(fmr, page_list, npages, iova); | ||
539 | if (err) | ||
540 | return err; | ||
541 | |||
542 | ++fmr->maps; | ||
543 | |||
544 | key = key_to_hw_index(fmr->mr.key); | ||
545 | key += dev->caps.num_mpts; | ||
546 | *lkey = *rkey = fmr->mr.key = hw_index_to_key(key); | ||
547 | |||
548 | *(u8 *) fmr->mpt = MLX4_MPT_STATUS_SW; | ||
549 | |||
550 | /* Make sure MPT status is visible before writing MTT entries */ | ||
551 | wmb(); | ||
552 | |||
553 | dma_sync_single_for_cpu(&dev->pdev->dev, fmr->dma_handle, | ||
554 | npages * sizeof(u64), DMA_TO_DEVICE); | ||
555 | |||
556 | for (i = 0; i < npages; ++i) | ||
557 | fmr->mtts[i] = cpu_to_be64(page_list[i] | MLX4_MTT_FLAG_PRESENT); | ||
558 | |||
559 | dma_sync_single_for_device(&dev->pdev->dev, fmr->dma_handle, | ||
560 | npages * sizeof(u64), DMA_TO_DEVICE); | ||
561 | |||
562 | fmr->mpt->key = cpu_to_be32(key); | ||
563 | fmr->mpt->lkey = cpu_to_be32(key); | ||
564 | fmr->mpt->length = cpu_to_be64(npages * (1ull << fmr->page_shift)); | ||
565 | fmr->mpt->start = cpu_to_be64(iova); | ||
566 | |||
567 | /* Make MTT entries are visible before setting MPT status */ | ||
568 | wmb(); | ||
569 | |||
570 | *(u8 *) fmr->mpt = MLX4_MPT_STATUS_HW; | ||
571 | |||
572 | /* Make sure MPT status is visible before consumer can use FMR */ | ||
573 | wmb(); | ||
574 | |||
575 | return 0; | ||
576 | } | ||
577 | EXPORT_SYMBOL_GPL(mlx4_map_phys_fmr); | ||
578 | |||
579 | int mlx4_fmr_alloc(struct mlx4_dev *dev, u32 pd, u32 access, int max_pages, | ||
580 | int max_maps, u8 page_shift, struct mlx4_fmr *fmr) | ||
581 | { | ||
582 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
583 | u64 mtt_seg; | ||
584 | int err = -ENOMEM; | ||
585 | |||
586 | if (page_shift < (ffs(dev->caps.page_size_cap) - 1) || page_shift >= 32) | ||
587 | return -EINVAL; | ||
588 | |||
589 | /* All MTTs must fit in the same page */ | ||
590 | if (max_pages * sizeof *fmr->mtts > PAGE_SIZE) | ||
591 | return -EINVAL; | ||
592 | |||
593 | fmr->page_shift = page_shift; | ||
594 | fmr->max_pages = max_pages; | ||
595 | fmr->max_maps = max_maps; | ||
596 | fmr->maps = 0; | ||
597 | |||
598 | err = mlx4_mr_alloc(dev, pd, 0, 0, access, max_pages, | ||
599 | page_shift, &fmr->mr); | ||
600 | if (err) | ||
601 | return err; | ||
602 | |||
603 | mtt_seg = fmr->mr.mtt.first_seg * dev->caps.mtt_entry_sz; | ||
604 | |||
605 | fmr->mtts = mlx4_table_find(&priv->mr_table.mtt_table, | ||
606 | fmr->mr.mtt.first_seg, | ||
607 | &fmr->dma_handle); | ||
608 | if (!fmr->mtts) { | ||
609 | err = -ENOMEM; | ||
610 | goto err_free; | ||
611 | } | ||
612 | |||
613 | return 0; | ||
614 | |||
615 | err_free: | ||
616 | mlx4_mr_free(dev, &fmr->mr); | ||
617 | return err; | ||
618 | } | ||
619 | EXPORT_SYMBOL_GPL(mlx4_fmr_alloc); | ||
620 | |||
621 | int mlx4_fmr_enable(struct mlx4_dev *dev, struct mlx4_fmr *fmr) | ||
622 | { | ||
623 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
624 | int err; | ||
625 | |||
626 | err = mlx4_mr_enable(dev, &fmr->mr); | ||
627 | if (err) | ||
628 | return err; | ||
629 | |||
630 | fmr->mpt = mlx4_table_find(&priv->mr_table.dmpt_table, | ||
631 | key_to_hw_index(fmr->mr.key), NULL); | ||
632 | if (!fmr->mpt) | ||
633 | return -ENOMEM; | ||
634 | |||
635 | return 0; | ||
636 | } | ||
637 | EXPORT_SYMBOL_GPL(mlx4_fmr_enable); | ||
638 | |||
639 | void mlx4_fmr_unmap(struct mlx4_dev *dev, struct mlx4_fmr *fmr, | ||
640 | u32 *lkey, u32 *rkey) | ||
641 | { | ||
642 | if (!fmr->maps) | ||
643 | return; | ||
644 | |||
645 | fmr->maps = 0; | ||
646 | |||
647 | *(u8 *) fmr->mpt = MLX4_MPT_STATUS_SW; | ||
648 | } | ||
649 | EXPORT_SYMBOL_GPL(mlx4_fmr_unmap); | ||
650 | |||
651 | int mlx4_fmr_free(struct mlx4_dev *dev, struct mlx4_fmr *fmr) | ||
652 | { | ||
653 | if (fmr->maps) | ||
654 | return -EBUSY; | ||
655 | |||
656 | fmr->mr.enabled = 0; | ||
657 | mlx4_mr_free(dev, &fmr->mr); | ||
658 | |||
659 | return 0; | ||
660 | } | ||
661 | EXPORT_SYMBOL_GPL(mlx4_fmr_free); | ||
662 | |||
663 | int mlx4_SYNC_TPT(struct mlx4_dev *dev) | ||
664 | { | ||
665 | return mlx4_cmd(dev, 0, 0, 0, MLX4_CMD_SYNC_TPT, 1000); | ||
666 | } | ||
667 | EXPORT_SYMBOL_GPL(mlx4_SYNC_TPT); | ||