at v2.6.26 635 lines 15 kB view raw
1/* 2 * Copyright (c) 2004 Topspin Communications. All rights reserved. 3 * Copyright (c) 2005 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/init.h> 36#include <linux/errno.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 */ 46struct mlx4_mpt_entry { 47 __be32 flags; 48 __be32 qpn; 49 __be32 key; 50 __be32 pd; 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} __attribute__((packed)); 62 63#define MLX4_MPT_FLAG_SW_OWNS (0xfUL << 28) 64#define MLX4_MPT_FLAG_MIO (1 << 17) 65#define MLX4_MPT_FLAG_BIND_ENABLE (1 << 15) 66#define MLX4_MPT_FLAG_PHYSICAL (1 << 9) 67#define MLX4_MPT_FLAG_REGION (1 << 8) 68 69#define MLX4_MTT_FLAG_PRESENT 1 70 71#define MLX4_MPT_STATUS_SW 0xF0 72#define MLX4_MPT_STATUS_HW 0x00 73 74static u32 mlx4_buddy_alloc(struct mlx4_buddy *buddy, int order) 75{ 76 int o; 77 int m; 78 u32 seg; 79 80 spin_lock(&buddy->lock); 81 82 for (o = order; o <= buddy->max_order; ++o) { 83 m = 1 << (buddy->max_order - o); 84 seg = find_first_bit(buddy->bits[o], m); 85 if (seg < m) 86 goto found; 87 } 88 89 spin_unlock(&buddy->lock); 90 return -1; 91 92 found: 93 clear_bit(seg, buddy->bits[o]); 94 95 while (o > order) { 96 --o; 97 seg <<= 1; 98 set_bit(seg ^ 1, buddy->bits[o]); 99 } 100 101 spin_unlock(&buddy->lock); 102 103 seg <<= order; 104 105 return seg; 106} 107 108static void mlx4_buddy_free(struct mlx4_buddy *buddy, u32 seg, int order) 109{ 110 seg >>= order; 111 112 spin_lock(&buddy->lock); 113 114 while (test_bit(seg ^ 1, buddy->bits[order])) { 115 clear_bit(seg ^ 1, buddy->bits[order]); 116 seg >>= 1; 117 ++order; 118 } 119 120 set_bit(seg, buddy->bits[order]); 121 122 spin_unlock(&buddy->lock); 123} 124 125static int mlx4_buddy_init(struct mlx4_buddy *buddy, int max_order) 126{ 127 int i, s; 128 129 buddy->max_order = max_order; 130 spin_lock_init(&buddy->lock); 131 132 buddy->bits = kzalloc((buddy->max_order + 1) * sizeof (long *), 133 GFP_KERNEL); 134 if (!buddy->bits) 135 goto err_out; 136 137 for (i = 0; i <= buddy->max_order; ++i) { 138 s = BITS_TO_LONGS(1 << (buddy->max_order - i)); 139 buddy->bits[i] = kmalloc(s * sizeof (long), GFP_KERNEL); 140 if (!buddy->bits[i]) 141 goto err_out_free; 142 bitmap_zero(buddy->bits[i], 1 << (buddy->max_order - i)); 143 } 144 145 set_bit(0, buddy->bits[buddy->max_order]); 146 147 return 0; 148 149err_out_free: 150 for (i = 0; i <= buddy->max_order; ++i) 151 kfree(buddy->bits[i]); 152 153 kfree(buddy->bits); 154 155err_out: 156 return -ENOMEM; 157} 158 159static void mlx4_buddy_cleanup(struct mlx4_buddy *buddy) 160{ 161 int i; 162 163 for (i = 0; i <= buddy->max_order; ++i) 164 kfree(buddy->bits[i]); 165 166 kfree(buddy->bits); 167} 168 169static u32 mlx4_alloc_mtt_range(struct mlx4_dev *dev, int order) 170{ 171 struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; 172 u32 seg; 173 174 seg = mlx4_buddy_alloc(&mr_table->mtt_buddy, order); 175 if (seg == -1) 176 return -1; 177 178 if (mlx4_table_get_range(dev, &mr_table->mtt_table, seg, 179 seg + (1 << order) - 1)) { 180 mlx4_buddy_free(&mr_table->mtt_buddy, seg, order); 181 return -1; 182 } 183 184 return seg; 185} 186 187int mlx4_mtt_init(struct mlx4_dev *dev, int npages, int page_shift, 188 struct mlx4_mtt *mtt) 189{ 190 int i; 191 192 if (!npages) { 193 mtt->order = -1; 194 mtt->page_shift = MLX4_ICM_PAGE_SHIFT; 195 return 0; 196 } else 197 mtt->page_shift = page_shift; 198 199 for (mtt->order = 0, i = MLX4_MTT_ENTRY_PER_SEG; i < npages; i <<= 1) 200 ++mtt->order; 201 202 mtt->first_seg = mlx4_alloc_mtt_range(dev, mtt->order); 203 if (mtt->first_seg == -1) 204 return -ENOMEM; 205 206 return 0; 207} 208EXPORT_SYMBOL_GPL(mlx4_mtt_init); 209 210void mlx4_mtt_cleanup(struct mlx4_dev *dev, struct mlx4_mtt *mtt) 211{ 212 struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; 213 214 if (mtt->order < 0) 215 return; 216 217 mlx4_buddy_free(&mr_table->mtt_buddy, mtt->first_seg, mtt->order); 218 mlx4_table_put_range(dev, &mr_table->mtt_table, mtt->first_seg, 219 mtt->first_seg + (1 << mtt->order) - 1); 220} 221EXPORT_SYMBOL_GPL(mlx4_mtt_cleanup); 222 223u64 mlx4_mtt_addr(struct mlx4_dev *dev, struct mlx4_mtt *mtt) 224{ 225 return (u64) mtt->first_seg * dev->caps.mtt_entry_sz; 226} 227EXPORT_SYMBOL_GPL(mlx4_mtt_addr); 228 229static u32 hw_index_to_key(u32 ind) 230{ 231 return (ind >> 24) | (ind << 8); 232} 233 234static u32 key_to_hw_index(u32 key) 235{ 236 return (key << 24) | (key >> 8); 237} 238 239static int mlx4_SW2HW_MPT(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, 240 int mpt_index) 241{ 242 return mlx4_cmd(dev, mailbox->dma, mpt_index, 0, MLX4_CMD_SW2HW_MPT, 243 MLX4_CMD_TIME_CLASS_B); 244} 245 246static int mlx4_HW2SW_MPT(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, 247 int mpt_index) 248{ 249 return mlx4_cmd_box(dev, 0, mailbox ? mailbox->dma : 0, mpt_index, 250 !mailbox, MLX4_CMD_HW2SW_MPT, MLX4_CMD_TIME_CLASS_B); 251} 252 253int mlx4_mr_alloc(struct mlx4_dev *dev, u32 pd, u64 iova, u64 size, u32 access, 254 int npages, int page_shift, struct mlx4_mr *mr) 255{ 256 struct mlx4_priv *priv = mlx4_priv(dev); 257 u32 index; 258 int err; 259 260 index = mlx4_bitmap_alloc(&priv->mr_table.mpt_bitmap); 261 if (index == -1) 262 return -ENOMEM; 263 264 mr->iova = iova; 265 mr->size = size; 266 mr->pd = pd; 267 mr->access = access; 268 mr->enabled = 0; 269 mr->key = hw_index_to_key(index); 270 271 err = mlx4_mtt_init(dev, npages, page_shift, &mr->mtt); 272 if (err) 273 mlx4_bitmap_free(&priv->mr_table.mpt_bitmap, index); 274 275 return err; 276} 277EXPORT_SYMBOL_GPL(mlx4_mr_alloc); 278 279void mlx4_mr_free(struct mlx4_dev *dev, struct mlx4_mr *mr) 280{ 281 struct mlx4_priv *priv = mlx4_priv(dev); 282 int err; 283 284 if (mr->enabled) { 285 err = mlx4_HW2SW_MPT(dev, NULL, 286 key_to_hw_index(mr->key) & 287 (dev->caps.num_mpts - 1)); 288 if (err) 289 mlx4_warn(dev, "HW2SW_MPT failed (%d)\n", err); 290 } 291 292 mlx4_mtt_cleanup(dev, &mr->mtt); 293 mlx4_bitmap_free(&priv->mr_table.mpt_bitmap, key_to_hw_index(mr->key)); 294} 295EXPORT_SYMBOL_GPL(mlx4_mr_free); 296 297int mlx4_mr_enable(struct mlx4_dev *dev, struct mlx4_mr *mr) 298{ 299 struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; 300 struct mlx4_cmd_mailbox *mailbox; 301 struct mlx4_mpt_entry *mpt_entry; 302 int err; 303 304 err = mlx4_table_get(dev, &mr_table->dmpt_table, key_to_hw_index(mr->key)); 305 if (err) 306 return err; 307 308 mailbox = mlx4_alloc_cmd_mailbox(dev); 309 if (IS_ERR(mailbox)) { 310 err = PTR_ERR(mailbox); 311 goto err_table; 312 } 313 mpt_entry = mailbox->buf; 314 315 memset(mpt_entry, 0, sizeof *mpt_entry); 316 317 mpt_entry->flags = cpu_to_be32(MLX4_MPT_FLAG_SW_OWNS | 318 MLX4_MPT_FLAG_MIO | 319 MLX4_MPT_FLAG_REGION | 320 mr->access); 321 322 mpt_entry->key = cpu_to_be32(key_to_hw_index(mr->key)); 323 mpt_entry->pd = cpu_to_be32(mr->pd); 324 mpt_entry->start = cpu_to_be64(mr->iova); 325 mpt_entry->length = cpu_to_be64(mr->size); 326 mpt_entry->entity_size = cpu_to_be32(mr->mtt.page_shift); 327 if (mr->mtt.order < 0) { 328 mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_PHYSICAL); 329 mpt_entry->mtt_seg = 0; 330 } else 331 mpt_entry->mtt_seg = cpu_to_be64(mlx4_mtt_addr(dev, &mr->mtt)); 332 333 err = mlx4_SW2HW_MPT(dev, mailbox, 334 key_to_hw_index(mr->key) & (dev->caps.num_mpts - 1)); 335 if (err) { 336 mlx4_warn(dev, "SW2HW_MPT failed (%d)\n", err); 337 goto err_cmd; 338 } 339 340 mr->enabled = 1; 341 342 mlx4_free_cmd_mailbox(dev, mailbox); 343 344 return 0; 345 346err_cmd: 347 mlx4_free_cmd_mailbox(dev, mailbox); 348 349err_table: 350 mlx4_table_put(dev, &mr_table->dmpt_table, key_to_hw_index(mr->key)); 351 return err; 352} 353EXPORT_SYMBOL_GPL(mlx4_mr_enable); 354 355static int mlx4_write_mtt_chunk(struct mlx4_dev *dev, struct mlx4_mtt *mtt, 356 int start_index, int npages, u64 *page_list) 357{ 358 struct mlx4_priv *priv = mlx4_priv(dev); 359 __be64 *mtts; 360 dma_addr_t dma_handle; 361 int i; 362 int s = start_index * sizeof (u64); 363 364 /* All MTTs must fit in the same page */ 365 if (start_index / (PAGE_SIZE / sizeof (u64)) != 366 (start_index + npages - 1) / (PAGE_SIZE / sizeof (u64))) 367 return -EINVAL; 368 369 if (start_index & (MLX4_MTT_ENTRY_PER_SEG - 1)) 370 return -EINVAL; 371 372 mtts = mlx4_table_find(&priv->mr_table.mtt_table, mtt->first_seg + 373 s / dev->caps.mtt_entry_sz, &dma_handle); 374 if (!mtts) 375 return -ENOMEM; 376 377 for (i = 0; i < npages; ++i) 378 mtts[i] = cpu_to_be64(page_list[i] | MLX4_MTT_FLAG_PRESENT); 379 380 dma_sync_single(&dev->pdev->dev, dma_handle, npages * sizeof (u64), DMA_TO_DEVICE); 381 382 return 0; 383} 384 385int mlx4_write_mtt(struct mlx4_dev *dev, struct mlx4_mtt *mtt, 386 int start_index, int npages, u64 *page_list) 387{ 388 int chunk; 389 int err; 390 391 if (mtt->order < 0) 392 return -EINVAL; 393 394 while (npages > 0) { 395 chunk = min_t(int, PAGE_SIZE / sizeof(u64), npages); 396 err = mlx4_write_mtt_chunk(dev, mtt, start_index, chunk, page_list); 397 if (err) 398 return err; 399 400 npages -= chunk; 401 start_index += chunk; 402 page_list += chunk; 403 } 404 405 return 0; 406} 407EXPORT_SYMBOL_GPL(mlx4_write_mtt); 408 409int mlx4_buf_write_mtt(struct mlx4_dev *dev, struct mlx4_mtt *mtt, 410 struct mlx4_buf *buf) 411{ 412 u64 *page_list; 413 int err; 414 int i; 415 416 page_list = kmalloc(buf->npages * sizeof *page_list, GFP_KERNEL); 417 if (!page_list) 418 return -ENOMEM; 419 420 for (i = 0; i < buf->npages; ++i) 421 if (buf->nbufs == 1) 422 page_list[i] = buf->direct.map + (i << buf->page_shift); 423 else 424 page_list[i] = buf->page_list[i].map; 425 426 err = mlx4_write_mtt(dev, mtt, 0, buf->npages, page_list); 427 428 kfree(page_list); 429 return err; 430} 431EXPORT_SYMBOL_GPL(mlx4_buf_write_mtt); 432 433int mlx4_init_mr_table(struct mlx4_dev *dev) 434{ 435 struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; 436 int err; 437 438 err = mlx4_bitmap_init(&mr_table->mpt_bitmap, dev->caps.num_mpts, 439 ~0, dev->caps.reserved_mrws); 440 if (err) 441 return err; 442 443 err = mlx4_buddy_init(&mr_table->mtt_buddy, 444 ilog2(dev->caps.num_mtt_segs)); 445 if (err) 446 goto err_buddy; 447 448 if (dev->caps.reserved_mtts) { 449 if (mlx4_alloc_mtt_range(dev, fls(dev->caps.reserved_mtts - 1)) == -1) { 450 mlx4_warn(dev, "MTT table of order %d is too small.\n", 451 mr_table->mtt_buddy.max_order); 452 err = -ENOMEM; 453 goto err_reserve_mtts; 454 } 455 } 456 457 return 0; 458 459err_reserve_mtts: 460 mlx4_buddy_cleanup(&mr_table->mtt_buddy); 461 462err_buddy: 463 mlx4_bitmap_cleanup(&mr_table->mpt_bitmap); 464 465 return err; 466} 467 468void mlx4_cleanup_mr_table(struct mlx4_dev *dev) 469{ 470 struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; 471 472 mlx4_buddy_cleanup(&mr_table->mtt_buddy); 473 mlx4_bitmap_cleanup(&mr_table->mpt_bitmap); 474} 475 476static inline int mlx4_check_fmr(struct mlx4_fmr *fmr, u64 *page_list, 477 int npages, u64 iova) 478{ 479 int i, page_mask; 480 481 if (npages > fmr->max_pages) 482 return -EINVAL; 483 484 page_mask = (1 << fmr->page_shift) - 1; 485 486 /* We are getting page lists, so va must be page aligned. */ 487 if (iova & page_mask) 488 return -EINVAL; 489 490 /* Trust the user not to pass misaligned data in page_list */ 491 if (0) 492 for (i = 0; i < npages; ++i) { 493 if (page_list[i] & ~page_mask) 494 return -EINVAL; 495 } 496 497 if (fmr->maps >= fmr->max_maps) 498 return -EINVAL; 499 500 return 0; 501} 502 503int mlx4_map_phys_fmr(struct mlx4_dev *dev, struct mlx4_fmr *fmr, u64 *page_list, 504 int npages, u64 iova, u32 *lkey, u32 *rkey) 505{ 506 u32 key; 507 int i, err; 508 509 err = mlx4_check_fmr(fmr, page_list, npages, iova); 510 if (err) 511 return err; 512 513 ++fmr->maps; 514 515 key = key_to_hw_index(fmr->mr.key); 516 key += dev->caps.num_mpts; 517 *lkey = *rkey = fmr->mr.key = hw_index_to_key(key); 518 519 *(u8 *) fmr->mpt = MLX4_MPT_STATUS_SW; 520 521 /* Make sure MPT status is visible before writing MTT entries */ 522 wmb(); 523 524 for (i = 0; i < npages; ++i) 525 fmr->mtts[i] = cpu_to_be64(page_list[i] | MLX4_MTT_FLAG_PRESENT); 526 527 dma_sync_single(&dev->pdev->dev, fmr->dma_handle, 528 npages * sizeof(u64), DMA_TO_DEVICE); 529 530 fmr->mpt->key = cpu_to_be32(key); 531 fmr->mpt->lkey = cpu_to_be32(key); 532 fmr->mpt->length = cpu_to_be64(npages * (1ull << fmr->page_shift)); 533 fmr->mpt->start = cpu_to_be64(iova); 534 535 /* Make MTT entries are visible before setting MPT status */ 536 wmb(); 537 538 *(u8 *) fmr->mpt = MLX4_MPT_STATUS_HW; 539 540 /* Make sure MPT status is visible before consumer can use FMR */ 541 wmb(); 542 543 return 0; 544} 545EXPORT_SYMBOL_GPL(mlx4_map_phys_fmr); 546 547int mlx4_fmr_alloc(struct mlx4_dev *dev, u32 pd, u32 access, int max_pages, 548 int max_maps, u8 page_shift, struct mlx4_fmr *fmr) 549{ 550 struct mlx4_priv *priv = mlx4_priv(dev); 551 u64 mtt_seg; 552 int err = -ENOMEM; 553 554 if (page_shift < (ffs(dev->caps.page_size_cap) - 1) || page_shift >= 32) 555 return -EINVAL; 556 557 /* All MTTs must fit in the same page */ 558 if (max_pages * sizeof *fmr->mtts > PAGE_SIZE) 559 return -EINVAL; 560 561 fmr->page_shift = page_shift; 562 fmr->max_pages = max_pages; 563 fmr->max_maps = max_maps; 564 fmr->maps = 0; 565 566 err = mlx4_mr_alloc(dev, pd, 0, 0, access, max_pages, 567 page_shift, &fmr->mr); 568 if (err) 569 return err; 570 571 mtt_seg = fmr->mr.mtt.first_seg * dev->caps.mtt_entry_sz; 572 573 fmr->mtts = mlx4_table_find(&priv->mr_table.mtt_table, 574 fmr->mr.mtt.first_seg, 575 &fmr->dma_handle); 576 if (!fmr->mtts) { 577 err = -ENOMEM; 578 goto err_free; 579 } 580 581 return 0; 582 583err_free: 584 mlx4_mr_free(dev, &fmr->mr); 585 return err; 586} 587EXPORT_SYMBOL_GPL(mlx4_fmr_alloc); 588 589int mlx4_fmr_enable(struct mlx4_dev *dev, struct mlx4_fmr *fmr) 590{ 591 struct mlx4_priv *priv = mlx4_priv(dev); 592 int err; 593 594 err = mlx4_mr_enable(dev, &fmr->mr); 595 if (err) 596 return err; 597 598 fmr->mpt = mlx4_table_find(&priv->mr_table.dmpt_table, 599 key_to_hw_index(fmr->mr.key), NULL); 600 if (!fmr->mpt) 601 return -ENOMEM; 602 603 return 0; 604} 605EXPORT_SYMBOL_GPL(mlx4_fmr_enable); 606 607void mlx4_fmr_unmap(struct mlx4_dev *dev, struct mlx4_fmr *fmr, 608 u32 *lkey, u32 *rkey) 609{ 610 if (!fmr->maps) 611 return; 612 613 fmr->maps = 0; 614 615 *(u8 *) fmr->mpt = MLX4_MPT_STATUS_SW; 616} 617EXPORT_SYMBOL_GPL(mlx4_fmr_unmap); 618 619int mlx4_fmr_free(struct mlx4_dev *dev, struct mlx4_fmr *fmr) 620{ 621 if (fmr->maps) 622 return -EBUSY; 623 624 fmr->mr.enabled = 0; 625 mlx4_mr_free(dev, &fmr->mr); 626 627 return 0; 628} 629EXPORT_SYMBOL_GPL(mlx4_fmr_free); 630 631int mlx4_SYNC_TPT(struct mlx4_dev *dev) 632{ 633 return mlx4_cmd(dev, 0, 0, 0, MLX4_CMD_SYNC_TPT, 1000); 634} 635EXPORT_SYMBOL_GPL(mlx4_SYNC_TPT);