Commit fb05a37929e0cd99016b4f5e5a5ef077fb10a947

Authored by Becky Bruce
Committed by Ingo Molnar
1 parent bc40ac6698

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>

Showing 1 changed file with 51 additions and 17 deletions Side-by-side Diff

... ... @@ -14,6 +14,7 @@
14 14 * 04/07/.. ak Better overflow handling. Assorted fixes.
15 15 * 05/09/10 linville Add support for syncing ranges, support syncing for
16 16 * DMA_BIDIRECTIONAL mappings, miscellaneous cleanup.
  17 + * 08/12/11 beckyb Add highmem support
17 18 */
18 19  
19 20 #include <linux/cache.h>
... ... @@ -24,6 +25,7 @@
24 25 #include <linux/swiotlb.h>
25 26 #include <linux/string.h>
26 27 #include <linux/swiotlb.h>
  28 +#include <linux/pfn.h>
27 29 #include <linux/types.h>
28 30 #include <linux/ctype.h>
29 31 #include <linux/highmem.h>
... ... @@ -149,11 +151,6 @@
149 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 154 static void swiotlb_print_info(unsigned long bytes)
158 155 {
159 156 phys_addr_t pstart, pend;
... ... @@ -330,6 +327,47 @@
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 371 * Allocates bounce buffer and returns its kernel virtual address.
334 372 */
335 373 static void *
... ... @@ -430,7 +468,7 @@
430 468 for (i = 0; i < nslots; i++)
431 469 io_tlb_orig_addr[index+i] = phys + (i << IO_TLB_SHIFT);
432 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 473 return dma_addr;
436 474 }
... ... @@ -450,11 +488,7 @@
450 488 * First, sync the memory before unmapping the entry
451 489 */
452 490 if (phys && ((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL)))
453   - /*
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);
  491 + swiotlb_bounce(phys, dma_addr, size, DMA_FROM_DEVICE);
458 492  
459 493 /*
460 494 * Return the buffer to the free list by setting the corresponding
461 495  
... ... @@ -494,13 +528,13 @@
494 528 switch (target) {
495 529 case SYNC_FOR_CPU:
496 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 532 else
499 533 BUG_ON(dir != DMA_TO_DEVICE);
500 534 break;
501 535 case SYNC_FOR_DEVICE:
502 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 538 else
505 539 BUG_ON(dir != DMA_FROM_DEVICE);
506 540 break;
507 541  
... ... @@ -817,11 +851,11 @@
817 851 BUG_ON(dir == DMA_NONE);
818 852  
819 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 855 unmap_single(hwdev, swiotlb_bus_to_virt(sg->dma_address),
822 856 sg->dma_length, dir);
823 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 861 EXPORT_SYMBOL(swiotlb_unmap_sg_attrs);
828 862  
... ... @@ -850,11 +884,11 @@
850 884 BUG_ON(dir == DMA_NONE);
851 885  
852 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 888 sync_single(hwdev, swiotlb_bus_to_virt(sg->dma_address),
855 889 sg->dma_length, dir, target);
856 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