diff options
| author | Steve French <sfrench@us.ibm.com> | 2008-05-06 13:55:32 -0400 |
|---|---|---|
| committer | Steve French <sfrench@us.ibm.com> | 2008-05-06 13:55:32 -0400 |
| commit | a815752ac0ffdb910e92958d41d28f4fb28e5296 (patch) | |
| tree | a3aa16a282354da0debe8e3a3a7ed8aac6e54001 /lib/swiotlb.c | |
| parent | 5ade9deaaa3e1f7291467d97b238648e43eae15e (diff) | |
| parent | a15306365a16380f3bafee9e181ba01231d4acd7 (diff) | |
Merge branch 'master' of /pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'lib/swiotlb.c')
| -rw-r--r-- | lib/swiotlb.c | 149 |
1 files changed, 86 insertions, 63 deletions
diff --git a/lib/swiotlb.c b/lib/swiotlb.c index 025922807e6e..d568894df8cc 100644 --- a/lib/swiotlb.c +++ b/lib/swiotlb.c | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | 31 | ||
| 32 | #include <linux/init.h> | 32 | #include <linux/init.h> |
| 33 | #include <linux/bootmem.h> | 33 | #include <linux/bootmem.h> |
| 34 | #include <linux/iommu-helper.h> | ||
| 34 | 35 | ||
| 35 | #define OFFSET(val,align) ((unsigned long) \ | 36 | #define OFFSET(val,align) ((unsigned long) \ |
| 36 | ( (val) & ( (align) - 1))) | 37 | ( (val) & ( (align) - 1))) |
| @@ -282,15 +283,6 @@ address_needs_mapping(struct device *hwdev, dma_addr_t addr) | |||
| 282 | return (addr & ~mask) != 0; | 283 | return (addr & ~mask) != 0; |
| 283 | } | 284 | } |
| 284 | 285 | ||
| 285 | static inline unsigned int is_span_boundary(unsigned int index, | ||
| 286 | unsigned int nslots, | ||
| 287 | unsigned long offset_slots, | ||
| 288 | unsigned long max_slots) | ||
| 289 | { | ||
| 290 | unsigned long offset = (offset_slots + index) & (max_slots - 1); | ||
| 291 | return offset + nslots > max_slots; | ||
| 292 | } | ||
| 293 | |||
| 294 | /* | 286 | /* |
| 295 | * Allocates bounce buffer and returns its kernel virtual address. | 287 | * Allocates bounce buffer and returns its kernel virtual address. |
| 296 | */ | 288 | */ |
| @@ -331,56 +323,53 @@ map_single(struct device *hwdev, char *buffer, size_t size, int dir) | |||
| 331 | * request and allocate a buffer from that IO TLB pool. | 323 | * request and allocate a buffer from that IO TLB pool. |
| 332 | */ | 324 | */ |
| 333 | spin_lock_irqsave(&io_tlb_lock, flags); | 325 | spin_lock_irqsave(&io_tlb_lock, flags); |
| 334 | { | 326 | index = ALIGN(io_tlb_index, stride); |
| 335 | index = ALIGN(io_tlb_index, stride); | 327 | if (index >= io_tlb_nslabs) |
| 336 | if (index >= io_tlb_nslabs) | 328 | index = 0; |
| 337 | index = 0; | 329 | wrap = index; |
| 338 | wrap = index; | 330 | |
| 339 | 331 | do { | |
| 340 | do { | 332 | while (iommu_is_span_boundary(index, nslots, offset_slots, |
| 341 | while (is_span_boundary(index, nslots, offset_slots, | 333 | max_slots)) { |
| 342 | max_slots)) { | ||
| 343 | index += stride; | ||
| 344 | if (index >= io_tlb_nslabs) | ||
| 345 | index = 0; | ||
| 346 | if (index == wrap) | ||
| 347 | goto not_found; | ||
| 348 | } | ||
| 349 | |||
| 350 | /* | ||
| 351 | * If we find a slot that indicates we have 'nslots' | ||
| 352 | * number of contiguous buffers, we allocate the | ||
| 353 | * buffers from that slot and mark the entries as '0' | ||
| 354 | * indicating unavailable. | ||
| 355 | */ | ||
| 356 | if (io_tlb_list[index] >= nslots) { | ||
| 357 | int count = 0; | ||
| 358 | |||
| 359 | for (i = index; i < (int) (index + nslots); i++) | ||
| 360 | io_tlb_list[i] = 0; | ||
| 361 | for (i = index - 1; (OFFSET(i, IO_TLB_SEGSIZE) != IO_TLB_SEGSIZE -1) && io_tlb_list[i]; i--) | ||
| 362 | io_tlb_list[i] = ++count; | ||
| 363 | dma_addr = io_tlb_start + (index << IO_TLB_SHIFT); | ||
| 364 | |||
| 365 | /* | ||
| 366 | * Update the indices to avoid searching in | ||
| 367 | * the next round. | ||
| 368 | */ | ||
| 369 | io_tlb_index = ((index + nslots) < io_tlb_nslabs | ||
| 370 | ? (index + nslots) : 0); | ||
| 371 | |||
| 372 | goto found; | ||
| 373 | } | ||
| 374 | index += stride; | 334 | index += stride; |
| 375 | if (index >= io_tlb_nslabs) | 335 | if (index >= io_tlb_nslabs) |
| 376 | index = 0; | 336 | index = 0; |
| 377 | } while (index != wrap); | 337 | if (index == wrap) |
| 338 | goto not_found; | ||
| 339 | } | ||
| 378 | 340 | ||
| 379 | not_found: | 341 | /* |
| 380 | spin_unlock_irqrestore(&io_tlb_lock, flags); | 342 | * If we find a slot that indicates we have 'nslots' number of |
| 381 | return NULL; | 343 | * contiguous buffers, we allocate the buffers from that slot |
| 382 | } | 344 | * and mark the entries as '0' indicating unavailable. |
| 383 | found: | 345 | */ |
| 346 | if (io_tlb_list[index] >= nslots) { | ||
| 347 | int count = 0; | ||
| 348 | |||
| 349 | for (i = index; i < (int) (index + nslots); i++) | ||
| 350 | io_tlb_list[i] = 0; | ||
| 351 | for (i = index - 1; (OFFSET(i, IO_TLB_SEGSIZE) != IO_TLB_SEGSIZE - 1) && io_tlb_list[i]; i--) | ||
| 352 | io_tlb_list[i] = ++count; | ||
| 353 | dma_addr = io_tlb_start + (index << IO_TLB_SHIFT); | ||
| 354 | |||
| 355 | /* | ||
| 356 | * Update the indices to avoid searching in the next | ||
| 357 | * round. | ||
| 358 | */ | ||
| 359 | io_tlb_index = ((index + nslots) < io_tlb_nslabs | ||
| 360 | ? (index + nslots) : 0); | ||
| 361 | |||
| 362 | goto found; | ||
| 363 | } | ||
| 364 | index += stride; | ||
| 365 | if (index >= io_tlb_nslabs) | ||
| 366 | index = 0; | ||
| 367 | } while (index != wrap); | ||
| 368 | |||
| 369 | not_found: | ||
| 370 | spin_unlock_irqrestore(&io_tlb_lock, flags); | ||
| 371 | return NULL; | ||
| 372 | found: | ||
| 384 | spin_unlock_irqrestore(&io_tlb_lock, flags); | 373 | spin_unlock_irqrestore(&io_tlb_lock, flags); |
| 385 | 374 | ||
| 386 | /* | 375 | /* |
| @@ -566,7 +555,8 @@ swiotlb_full(struct device *dev, size_t size, int dir, int do_panic) | |||
| 566 | * either swiotlb_unmap_single or swiotlb_dma_sync_single is performed. | 555 | * either swiotlb_unmap_single or swiotlb_dma_sync_single is performed. |
| 567 | */ | 556 | */ |
| 568 | dma_addr_t | 557 | dma_addr_t |
| 569 | swiotlb_map_single(struct device *hwdev, void *ptr, size_t size, int dir) | 558 | swiotlb_map_single_attrs(struct device *hwdev, void *ptr, size_t size, |
| 559 | int dir, struct dma_attrs *attrs) | ||
| 570 | { | 560 | { |
| 571 | dma_addr_t dev_addr = virt_to_bus(ptr); | 561 | dma_addr_t dev_addr = virt_to_bus(ptr); |
| 572 | void *map; | 562 | void *map; |
| @@ -599,6 +589,13 @@ swiotlb_map_single(struct device *hwdev, void *ptr, size_t size, int dir) | |||
| 599 | 589 | ||
| 600 | return dev_addr; | 590 | return dev_addr; |
| 601 | } | 591 | } |
| 592 | EXPORT_SYMBOL(swiotlb_map_single_attrs); | ||
| 593 | |||
| 594 | dma_addr_t | ||
| 595 | swiotlb_map_single(struct device *hwdev, void *ptr, size_t size, int dir) | ||
| 596 | { | ||
| 597 | return swiotlb_map_single_attrs(hwdev, ptr, size, dir, NULL); | ||
| 598 | } | ||
| 602 | 599 | ||
| 603 | /* | 600 | /* |
| 604 | * Unmap a single streaming mode DMA translation. The dma_addr and size must | 601 | * Unmap a single streaming mode DMA translation. The dma_addr and size must |
| @@ -609,8 +606,8 @@ swiotlb_map_single(struct device *hwdev, void *ptr, size_t size, int dir) | |||
| 609 | * whatever the device wrote there. | 606 | * whatever the device wrote there. |
| 610 | */ | 607 | */ |
| 611 | void | 608 | void |
| 612 | swiotlb_unmap_single(struct device *hwdev, dma_addr_t dev_addr, size_t size, | 609 | swiotlb_unmap_single_attrs(struct device *hwdev, dma_addr_t dev_addr, |
| 613 | int dir) | 610 | size_t size, int dir, struct dma_attrs *attrs) |
| 614 | { | 611 | { |
| 615 | char *dma_addr = bus_to_virt(dev_addr); | 612 | char *dma_addr = bus_to_virt(dev_addr); |
| 616 | 613 | ||
| @@ -620,7 +617,14 @@ swiotlb_unmap_single(struct device *hwdev, dma_addr_t dev_addr, size_t size, | |||
| 620 | else if (dir == DMA_FROM_DEVICE) | 617 | else if (dir == DMA_FROM_DEVICE) |
| 621 | dma_mark_clean(dma_addr, size); | 618 | dma_mark_clean(dma_addr, size); |
| 622 | } | 619 | } |
| 620 | EXPORT_SYMBOL(swiotlb_unmap_single_attrs); | ||
| 623 | 621 | ||
| 622 | void | ||
| 623 | swiotlb_unmap_single(struct device *hwdev, dma_addr_t dev_addr, size_t size, | ||
| 624 | int dir) | ||
| 625 | { | ||
| 626 | return swiotlb_unmap_single_attrs(hwdev, dev_addr, size, dir, NULL); | ||
| 627 | } | ||
| 624 | /* | 628 | /* |
| 625 | * Make physical memory consistent for a single streaming mode DMA translation | 629 | * Make physical memory consistent for a single streaming mode DMA translation |
| 626 | * after a transfer. | 630 | * after a transfer. |
| @@ -691,6 +695,8 @@ swiotlb_sync_single_range_for_device(struct device *hwdev, dma_addr_t dev_addr, | |||
| 691 | SYNC_FOR_DEVICE); | 695 | SYNC_FOR_DEVICE); |
| 692 | } | 696 | } |
| 693 | 697 | ||
| 698 | void swiotlb_unmap_sg_attrs(struct device *, struct scatterlist *, int, int, | ||
| 699 | struct dma_attrs *); | ||
| 694 | /* | 700 | /* |
| 695 | * Map a set of buffers described by scatterlist in streaming mode for DMA. | 701 | * Map a set of buffers described by scatterlist in streaming mode for DMA. |
| 696 | * This is the scatter-gather version of the above swiotlb_map_single | 702 | * This is the scatter-gather version of the above swiotlb_map_single |
| @@ -708,8 +714,8 @@ swiotlb_sync_single_range_for_device(struct device *hwdev, dma_addr_t dev_addr, | |||
| 708 | * same here. | 714 | * same here. |
| 709 | */ | 715 | */ |
| 710 | int | 716 | int |
| 711 | swiotlb_map_sg(struct device *hwdev, struct scatterlist *sgl, int nelems, | 717 | swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, int nelems, |
| 712 | int dir) | 718 | int dir, struct dma_attrs *attrs) |
| 713 | { | 719 | { |
| 714 | struct scatterlist *sg; | 720 | struct scatterlist *sg; |
| 715 | void *addr; | 721 | void *addr; |
| @@ -727,7 +733,8 @@ swiotlb_map_sg(struct device *hwdev, struct scatterlist *sgl, int nelems, | |||
| 727 | /* Don't panic here, we expect map_sg users | 733 | /* Don't panic here, we expect map_sg users |
| 728 | to do proper error handling. */ | 734 | to do proper error handling. */ |
| 729 | swiotlb_full(hwdev, sg->length, dir, 0); | 735 | swiotlb_full(hwdev, sg->length, dir, 0); |
| 730 | swiotlb_unmap_sg(hwdev, sgl, i, dir); | 736 | swiotlb_unmap_sg_attrs(hwdev, sgl, i, dir, |
| 737 | attrs); | ||
| 731 | sgl[0].dma_length = 0; | 738 | sgl[0].dma_length = 0; |
| 732 | return 0; | 739 | return 0; |
| 733 | } | 740 | } |
| @@ -738,14 +745,22 @@ swiotlb_map_sg(struct device *hwdev, struct scatterlist *sgl, int nelems, | |||
| 738 | } | 745 | } |
| 739 | return nelems; | 746 | return nelems; |
| 740 | } | 747 | } |
| 748 | EXPORT_SYMBOL(swiotlb_map_sg_attrs); | ||
| 749 | |||
| 750 | int | ||
| 751 | swiotlb_map_sg(struct device *hwdev, struct scatterlist *sgl, int nelems, | ||
| 752 | int dir) | ||
| 753 | { | ||
| 754 | return swiotlb_map_sg_attrs(hwdev, sgl, nelems, dir, NULL); | ||
| 755 | } | ||
| 741 | 756 | ||
| 742 | /* | 757 | /* |
| 743 | * Unmap a set of streaming mode DMA translations. Again, cpu read rules | 758 | * Unmap a set of streaming mode DMA translations. Again, cpu read rules |
| 744 | * concerning calls here are the same as for swiotlb_unmap_single() above. | 759 | * concerning calls here are the same as for swiotlb_unmap_single() above. |
| 745 | */ | 760 | */ |
| 746 | void | 761 | void |
| 747 | swiotlb_unmap_sg(struct device *hwdev, struct scatterlist *sgl, int nelems, | 762 | swiotlb_unmap_sg_attrs(struct device *hwdev, struct scatterlist *sgl, |
| 748 | int dir) | 763 | int nelems, int dir, struct dma_attrs *attrs) |
| 749 | { | 764 | { |
| 750 | struct scatterlist *sg; | 765 | struct scatterlist *sg; |
| 751 | int i; | 766 | int i; |
| @@ -760,6 +775,14 @@ swiotlb_unmap_sg(struct device *hwdev, struct scatterlist *sgl, int nelems, | |||
| 760 | dma_mark_clean(SG_ENT_VIRT_ADDRESS(sg), sg->dma_length); | 775 | dma_mark_clean(SG_ENT_VIRT_ADDRESS(sg), sg->dma_length); |
| 761 | } | 776 | } |
| 762 | } | 777 | } |
| 778 | EXPORT_SYMBOL(swiotlb_unmap_sg_attrs); | ||
| 779 | |||
| 780 | void | ||
| 781 | swiotlb_unmap_sg(struct device *hwdev, struct scatterlist *sgl, int nelems, | ||
| 782 | int dir) | ||
| 783 | { | ||
| 784 | return swiotlb_unmap_sg_attrs(hwdev, sgl, nelems, dir, NULL); | ||
| 785 | } | ||
| 763 | 786 | ||
| 764 | /* | 787 | /* |
| 765 | * Make physical memory consistent for a set of streaming mode DMA translations | 788 | * Make physical memory consistent for a set of streaming mode DMA translations |
