diff options
author | Becky Bruce <beckyb@kernel.crashing.org> | 2008-12-22 13:26:09 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-12-28 04:06:47 -0500 |
commit | fb05a37929e0cd99016b4f5e5a5ef077fb10a947 (patch) | |
tree | 154b9d0d3b984a03480f54d00f82f56fb3407d2f | |
parent | bc40ac66988a7721f2a244b6df65f8c13d16479c (diff) |
swiotlb: add support for systems with highmem
Impact: extend code for highmem - existing users unaffected
On highmem systems, the original dma buffer might not
have a virtual mapping - we need to kmap it in to perform
the bounce. Extract the code that does the actual
copy into a function that does the kmap if highmem
is enabled, and default to the normal swiotlb memcpy
if not.
[ ported by Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com> ]
Signed-off-by: Becky Bruce <beckyb@kernel.crashing.org>
Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | lib/swiotlb.c | 68 |
1 files changed, 51 insertions, 17 deletions
diff --git a/lib/swiotlb.c b/lib/swiotlb.c index 98a7a4450e02..785046e4c3ab 100644 --- a/lib/swiotlb.c +++ b/lib/swiotlb.c | |||
@@ -14,6 +14,7 @@ | |||
14 | * 04/07/.. ak Better overflow handling. Assorted fixes. | 14 | * 04/07/.. ak Better overflow handling. Assorted fixes. |
15 | * 05/09/10 linville Add support for syncing ranges, support syncing for | 15 | * 05/09/10 linville Add support for syncing ranges, support syncing for |
16 | * DMA_BIDIRECTIONAL mappings, miscellaneous cleanup. | 16 | * DMA_BIDIRECTIONAL mappings, miscellaneous cleanup. |
17 | * 08/12/11 beckyb Add highmem support | ||
17 | */ | 18 | */ |
18 | 19 | ||
19 | #include <linux/cache.h> | 20 | #include <linux/cache.h> |
@@ -24,6 +25,7 @@ | |||
24 | #include <linux/swiotlb.h> | 25 | #include <linux/swiotlb.h> |
25 | #include <linux/string.h> | 26 | #include <linux/string.h> |
26 | #include <linux/swiotlb.h> | 27 | #include <linux/swiotlb.h> |
28 | #include <linux/pfn.h> | ||
27 | #include <linux/types.h> | 29 | #include <linux/types.h> |
28 | #include <linux/ctype.h> | 30 | #include <linux/ctype.h> |
29 | #include <linux/highmem.h> | 31 | #include <linux/highmem.h> |
@@ -149,11 +151,6 @@ int __weak swiotlb_arch_range_needs_mapping(void *ptr, size_t size) | |||
149 | return 0; | 151 | return 0; |
150 | } | 152 | } |
151 | 153 | ||
152 | static dma_addr_t swiotlb_sg_to_bus(struct device *hwdev, struct scatterlist *sg) | ||
153 | { | ||
154 | return swiotlb_phys_to_bus(hwdev, page_to_phys(sg_page(sg)) + sg->offset); | ||
155 | } | ||
156 | |||
157 | static void swiotlb_print_info(unsigned long bytes) | 154 | static void swiotlb_print_info(unsigned long bytes) |
158 | { | 155 | { |
159 | phys_addr_t pstart, pend; | 156 | phys_addr_t pstart, pend; |
@@ -330,6 +327,47 @@ static int is_swiotlb_buffer(char *addr) | |||
330 | } | 327 | } |
331 | 328 | ||
332 | /* | 329 | /* |
330 | * Bounce: copy the swiotlb buffer back to the original dma location | ||
331 | */ | ||
332 | static void swiotlb_bounce(phys_addr_t phys, char *dma_addr, size_t size, | ||
333 | enum dma_data_direction dir) | ||
334 | { | ||
335 | unsigned long pfn = PFN_DOWN(phys); | ||
336 | |||
337 | if (PageHighMem(pfn_to_page(pfn))) { | ||
338 | /* The buffer does not have a mapping. Map it in and copy */ | ||
339 | unsigned int offset = phys & ~PAGE_MASK; | ||
340 | char *buffer; | ||
341 | unsigned int sz = 0; | ||
342 | unsigned long flags; | ||
343 | |||
344 | while (size) { | ||
345 | sz = min(PAGE_SIZE - offset, size); | ||
346 | |||
347 | local_irq_save(flags); | ||
348 | buffer = kmap_atomic(pfn_to_page(pfn), | ||
349 | KM_BOUNCE_READ); | ||
350 | if (dir == DMA_TO_DEVICE) | ||
351 | memcpy(dma_addr, buffer + offset, sz); | ||
352 | else | ||
353 | memcpy(buffer + offset, dma_addr, sz); | ||
354 | kunmap_atomic(buffer, KM_BOUNCE_READ); | ||
355 | local_irq_restore(flags); | ||
356 | |||
357 | size -= sz; | ||
358 | pfn++; | ||
359 | dma_addr += sz; | ||
360 | offset = 0; | ||
361 | } | ||
362 | } else { | ||
363 | if (dir == DMA_TO_DEVICE) | ||
364 | memcpy(dma_addr, phys_to_virt(phys), size); | ||
365 | else | ||
366 | memcpy(phys_to_virt(phys), dma_addr, size); | ||
367 | } | ||
368 | } | ||
369 | |||
370 | /* | ||
333 | * Allocates bounce buffer and returns its kernel virtual address. | 371 | * Allocates bounce buffer and returns its kernel virtual address. |
334 | */ | 372 | */ |
335 | static void * | 373 | static void * |
@@ -430,7 +468,7 @@ found: | |||
430 | for (i = 0; i < nslots; i++) | 468 | for (i = 0; i < nslots; i++) |
431 | io_tlb_orig_addr[index+i] = phys + (i << IO_TLB_SHIFT); | 469 | io_tlb_orig_addr[index+i] = phys + (i << IO_TLB_SHIFT); |
432 | if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) | 470 | if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) |
433 | memcpy(dma_addr, phys_to_virt(phys), size); | 471 | swiotlb_bounce(phys, dma_addr, size, DMA_TO_DEVICE); |
434 | 472 | ||
435 | return dma_addr; | 473 | return dma_addr; |
436 | } | 474 | } |
@@ -450,11 +488,7 @@ unmap_single(struct device *hwdev, char *dma_addr, size_t size, int dir) | |||
450 | * First, sync the memory before unmapping the entry | 488 | * First, sync the memory before unmapping the entry |
451 | */ | 489 | */ |
452 | if (phys && ((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL))) | 490 | if (phys && ((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL))) |
453 | /* | 491 | swiotlb_bounce(phys, dma_addr, size, DMA_FROM_DEVICE); |
454 | * bounce... copy the data back into the original buffer * and | ||
455 | * delete the bounce buffer. | ||
456 | */ | ||
457 | memcpy(phys_to_virt(phys), dma_addr, size); | ||
458 | 492 | ||
459 | /* | 493 | /* |
460 | * Return the buffer to the free list by setting the corresponding | 494 | * Return the buffer to the free list by setting the corresponding |
@@ -494,13 +528,13 @@ sync_single(struct device *hwdev, char *dma_addr, size_t size, | |||
494 | switch (target) { | 528 | switch (target) { |
495 | case SYNC_FOR_CPU: | 529 | case SYNC_FOR_CPU: |
496 | if (likely(dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL)) | 530 | if (likely(dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL)) |
497 | memcpy(phys_to_virt(phys), dma_addr, size); | 531 | swiotlb_bounce(phys, dma_addr, size, DMA_FROM_DEVICE); |
498 | else | 532 | else |
499 | BUG_ON(dir != DMA_TO_DEVICE); | 533 | BUG_ON(dir != DMA_TO_DEVICE); |
500 | break; | 534 | break; |
501 | case SYNC_FOR_DEVICE: | 535 | case SYNC_FOR_DEVICE: |
502 | if (likely(dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)) | 536 | if (likely(dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)) |
503 | memcpy(dma_addr, phys_to_virt(phys), size); | 537 | swiotlb_bounce(phys, dma_addr, size, DMA_TO_DEVICE); |
504 | else | 538 | else |
505 | BUG_ON(dir != DMA_FROM_DEVICE); | 539 | BUG_ON(dir != DMA_FROM_DEVICE); |
506 | break; | 540 | break; |
@@ -817,11 +851,11 @@ swiotlb_unmap_sg_attrs(struct device *hwdev, struct scatterlist *sgl, | |||
817 | BUG_ON(dir == DMA_NONE); | 851 | BUG_ON(dir == DMA_NONE); |
818 | 852 | ||
819 | for_each_sg(sgl, sg, nelems, i) { | 853 | for_each_sg(sgl, sg, nelems, i) { |
820 | if (sg->dma_address != swiotlb_sg_to_bus(hwdev, sg)) | 854 | if (sg->dma_address != swiotlb_virt_to_bus(hwdev, sg_virt(sg))) |
821 | unmap_single(hwdev, swiotlb_bus_to_virt(sg->dma_address), | 855 | unmap_single(hwdev, swiotlb_bus_to_virt(sg->dma_address), |
822 | sg->dma_length, dir); | 856 | sg->dma_length, dir); |
823 | else if (dir == DMA_FROM_DEVICE) | 857 | else if (dir == DMA_FROM_DEVICE) |
824 | dma_mark_clean(swiotlb_bus_to_virt(sg->dma_address), sg->dma_length); | 858 | dma_mark_clean(sg_virt(sg), sg->dma_length); |
825 | } | 859 | } |
826 | } | 860 | } |
827 | EXPORT_SYMBOL(swiotlb_unmap_sg_attrs); | 861 | EXPORT_SYMBOL(swiotlb_unmap_sg_attrs); |
@@ -850,11 +884,11 @@ swiotlb_sync_sg(struct device *hwdev, struct scatterlist *sgl, | |||
850 | BUG_ON(dir == DMA_NONE); | 884 | BUG_ON(dir == DMA_NONE); |
851 | 885 | ||
852 | for_each_sg(sgl, sg, nelems, i) { | 886 | for_each_sg(sgl, sg, nelems, i) { |
853 | if (sg->dma_address != swiotlb_sg_to_bus(hwdev, sg)) | 887 | if (sg->dma_address != swiotlb_virt_to_bus(hwdev, sg_virt(sg))) |
854 | sync_single(hwdev, swiotlb_bus_to_virt(sg->dma_address), | 888 | sync_single(hwdev, swiotlb_bus_to_virt(sg->dma_address), |
855 | sg->dma_length, dir, target); | 889 | sg->dma_length, dir, target); |
856 | else if (dir == DMA_FROM_DEVICE) | 890 | else if (dir == DMA_FROM_DEVICE) |
857 | dma_mark_clean(swiotlb_bus_to_virt(sg->dma_address), sg->dma_length); | 891 | dma_mark_clean(sg_virt(sg), sg->dma_length); |
858 | } | 892 | } |
859 | } | 893 | } |
860 | 894 | ||