diff options
author | Becky Bruce <beckyb@kernel.crashing.org> | 2008-12-22 13:26:08 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-12-28 04:03:12 -0500 |
commit | bc40ac66988a7721f2a244b6df65f8c13d16479c (patch) | |
tree | 581396233451acf0b372df0ba75b3d9014e991e9 /lib | |
parent | 70a7d3cc1308a55104fbe505d76f2aca8a4cf53e (diff) |
swiotlb: store phys address in io_tlb_orig_addr array
Impact: refactor code, cleanup
When we enable swiotlb for platforms that support HIGHMEM, we
can no longer store the virtual address of the original dma
buffer, because that buffer might not have a permament mapping.
Change the swiotlb code to instead store the physical address of
the original buffer.
Signed-off-by: Becky Bruce <beckyb@kernel.crashing.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/swiotlb.c | 120 |
1 files changed, 30 insertions, 90 deletions
diff --git a/lib/swiotlb.c b/lib/swiotlb.c index 3657da8ebbc3..98a7a4450e02 100644 --- a/lib/swiotlb.c +++ b/lib/swiotlb.c | |||
@@ -89,10 +89,7 @@ static unsigned int io_tlb_index; | |||
89 | * We need to save away the original address corresponding to a mapped entry | 89 | * We need to save away the original address corresponding to a mapped entry |
90 | * for the sync operations. | 90 | * for the sync operations. |
91 | */ | 91 | */ |
92 | static struct swiotlb_phys_addr { | 92 | static phys_addr_t *io_tlb_orig_addr; |
93 | struct page *page; | ||
94 | unsigned int offset; | ||
95 | } *io_tlb_orig_addr; | ||
96 | 93 | ||
97 | /* | 94 | /* |
98 | * Protect the above data structures in the map and unmap calls | 95 | * Protect the above data structures in the map and unmap calls |
@@ -204,7 +201,7 @@ swiotlb_init_with_default_size(size_t default_size) | |||
204 | for (i = 0; i < io_tlb_nslabs; i++) | 201 | for (i = 0; i < io_tlb_nslabs; i++) |
205 | io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE); | 202 | io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE); |
206 | io_tlb_index = 0; | 203 | io_tlb_index = 0; |
207 | io_tlb_orig_addr = alloc_bootmem(io_tlb_nslabs * sizeof(struct swiotlb_phys_addr)); | 204 | io_tlb_orig_addr = alloc_bootmem(io_tlb_nslabs * sizeof(phys_addr_t)); |
208 | 205 | ||
209 | /* | 206 | /* |
210 | * Get the overflow emergency buffer | 207 | * Get the overflow emergency buffer |
@@ -278,12 +275,14 @@ swiotlb_late_init_with_default_size(size_t default_size) | |||
278 | io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE); | 275 | io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE); |
279 | io_tlb_index = 0; | 276 | io_tlb_index = 0; |
280 | 277 | ||
281 | io_tlb_orig_addr = (struct swiotlb_phys_addr *)__get_free_pages(GFP_KERNEL, | 278 | io_tlb_orig_addr = (phys_addr_t *) |
282 | get_order(io_tlb_nslabs * sizeof(struct swiotlb_phys_addr))); | 279 | __get_free_pages(GFP_KERNEL, |
280 | get_order(io_tlb_nslabs * | ||
281 | sizeof(phys_addr_t))); | ||
283 | if (!io_tlb_orig_addr) | 282 | if (!io_tlb_orig_addr) |
284 | goto cleanup3; | 283 | goto cleanup3; |
285 | 284 | ||
286 | memset(io_tlb_orig_addr, 0, io_tlb_nslabs * sizeof(struct swiotlb_phys_addr)); | 285 | memset(io_tlb_orig_addr, 0, io_tlb_nslabs * sizeof(phys_addr_t)); |
287 | 286 | ||
288 | /* | 287 | /* |
289 | * Get the overflow emergency buffer | 288 | * Get the overflow emergency buffer |
@@ -298,8 +297,8 @@ swiotlb_late_init_with_default_size(size_t default_size) | |||
298 | return 0; | 297 | return 0; |
299 | 298 | ||
300 | cleanup4: | 299 | cleanup4: |
301 | free_pages((unsigned long)io_tlb_orig_addr, get_order(io_tlb_nslabs * | 300 | free_pages((unsigned long)io_tlb_orig_addr, |
302 | sizeof(char *))); | 301 | get_order(io_tlb_nslabs * sizeof(phys_addr_t))); |
303 | io_tlb_orig_addr = NULL; | 302 | io_tlb_orig_addr = NULL; |
304 | cleanup3: | 303 | cleanup3: |
305 | free_pages((unsigned long)io_tlb_list, get_order(io_tlb_nslabs * | 304 | free_pages((unsigned long)io_tlb_list, get_order(io_tlb_nslabs * |
@@ -330,59 +329,11 @@ static int is_swiotlb_buffer(char *addr) | |||
330 | return addr >= io_tlb_start && addr < io_tlb_end; | 329 | return addr >= io_tlb_start && addr < io_tlb_end; |
331 | } | 330 | } |
332 | 331 | ||
333 | static struct swiotlb_phys_addr swiotlb_bus_to_phys_addr(char *dma_addr) | ||
334 | { | ||
335 | int index = (dma_addr - io_tlb_start) >> IO_TLB_SHIFT; | ||
336 | struct swiotlb_phys_addr buffer = io_tlb_orig_addr[index]; | ||
337 | buffer.offset += (long)dma_addr & ((1 << IO_TLB_SHIFT) - 1); | ||
338 | buffer.page += buffer.offset >> PAGE_SHIFT; | ||
339 | buffer.offset &= PAGE_SIZE - 1; | ||
340 | return buffer; | ||
341 | } | ||
342 | |||
343 | static void | ||
344 | __sync_single(struct swiotlb_phys_addr buffer, char *dma_addr, size_t size, int dir) | ||
345 | { | ||
346 | if (PageHighMem(buffer.page)) { | ||
347 | size_t len, bytes; | ||
348 | char *dev, *host, *kmp; | ||
349 | |||
350 | len = size; | ||
351 | while (len != 0) { | ||
352 | unsigned long flags; | ||
353 | |||
354 | bytes = len; | ||
355 | if ((bytes + buffer.offset) > PAGE_SIZE) | ||
356 | bytes = PAGE_SIZE - buffer.offset; | ||
357 | local_irq_save(flags); /* protects KM_BOUNCE_READ */ | ||
358 | kmp = kmap_atomic(buffer.page, KM_BOUNCE_READ); | ||
359 | dev = dma_addr + size - len; | ||
360 | host = kmp + buffer.offset; | ||
361 | if (dir == DMA_FROM_DEVICE) | ||
362 | memcpy(host, dev, bytes); | ||
363 | else | ||
364 | memcpy(dev, host, bytes); | ||
365 | kunmap_atomic(kmp, KM_BOUNCE_READ); | ||
366 | local_irq_restore(flags); | ||
367 | len -= bytes; | ||
368 | buffer.page++; | ||
369 | buffer.offset = 0; | ||
370 | } | ||
371 | } else { | ||
372 | void *v = page_address(buffer.page) + buffer.offset; | ||
373 | |||
374 | if (dir == DMA_TO_DEVICE) | ||
375 | memcpy(dma_addr, v, size); | ||
376 | else | ||
377 | memcpy(v, dma_addr, size); | ||
378 | } | ||
379 | } | ||
380 | |||
381 | /* | 332 | /* |
382 | * Allocates bounce buffer and returns its kernel virtual address. | 333 | * Allocates bounce buffer and returns its kernel virtual address. |
383 | */ | 334 | */ |
384 | static void * | 335 | static void * |
385 | map_single(struct device *hwdev, struct swiotlb_phys_addr buffer, size_t size, int dir) | 336 | map_single(struct device *hwdev, phys_addr_t phys, size_t size, int dir) |
386 | { | 337 | { |
387 | unsigned long flags; | 338 | unsigned long flags; |
388 | char *dma_addr; | 339 | char *dma_addr; |
@@ -392,7 +343,6 @@ map_single(struct device *hwdev, struct swiotlb_phys_addr buffer, size_t size, i | |||
392 | unsigned long mask; | 343 | unsigned long mask; |
393 | unsigned long offset_slots; | 344 | unsigned long offset_slots; |
394 | unsigned long max_slots; | 345 | unsigned long max_slots; |
395 | struct swiotlb_phys_addr slot_buf; | ||
396 | 346 | ||
397 | mask = dma_get_seg_boundary(hwdev); | 347 | mask = dma_get_seg_boundary(hwdev); |
398 | start_dma_addr = swiotlb_virt_to_bus(hwdev, io_tlb_start) & mask; | 348 | start_dma_addr = swiotlb_virt_to_bus(hwdev, io_tlb_start) & mask; |
@@ -477,15 +427,10 @@ found: | |||
477 | * This is needed when we sync the memory. Then we sync the buffer if | 427 | * This is needed when we sync the memory. Then we sync the buffer if |
478 | * needed. | 428 | * needed. |
479 | */ | 429 | */ |
480 | slot_buf = buffer; | 430 | for (i = 0; i < nslots; i++) |
481 | for (i = 0; i < nslots; i++) { | 431 | io_tlb_orig_addr[index+i] = phys + (i << IO_TLB_SHIFT); |
482 | slot_buf.page += slot_buf.offset >> PAGE_SHIFT; | ||
483 | slot_buf.offset &= PAGE_SIZE - 1; | ||
484 | io_tlb_orig_addr[index+i] = slot_buf; | ||
485 | slot_buf.offset += 1 << IO_TLB_SHIFT; | ||
486 | } | ||
487 | if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) | 432 | if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) |
488 | __sync_single(buffer, dma_addr, size, DMA_TO_DEVICE); | 433 | memcpy(dma_addr, phys_to_virt(phys), size); |
489 | 434 | ||
490 | return dma_addr; | 435 | return dma_addr; |
491 | } | 436 | } |
@@ -499,17 +444,17 @@ unmap_single(struct device *hwdev, char *dma_addr, size_t size, int dir) | |||
499 | unsigned long flags; | 444 | unsigned long flags; |
500 | int i, count, nslots = ALIGN(size, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT; | 445 | int i, count, nslots = ALIGN(size, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT; |
501 | int index = (dma_addr - io_tlb_start) >> IO_TLB_SHIFT; | 446 | int index = (dma_addr - io_tlb_start) >> IO_TLB_SHIFT; |
502 | struct swiotlb_phys_addr buffer = swiotlb_bus_to_phys_addr(dma_addr); | 447 | phys_addr_t phys = io_tlb_orig_addr[index]; |
503 | 448 | ||
504 | /* | 449 | /* |
505 | * First, sync the memory before unmapping the entry | 450 | * First, sync the memory before unmapping the entry |
506 | */ | 451 | */ |
507 | if ((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL)) | 452 | if (phys && ((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL))) |
508 | /* | 453 | /* |
509 | * bounce... copy the data back into the original buffer * and | 454 | * bounce... copy the data back into the original buffer * and |
510 | * delete the bounce buffer. | 455 | * delete the bounce buffer. |
511 | */ | 456 | */ |
512 | __sync_single(buffer, dma_addr, size, DMA_FROM_DEVICE); | 457 | memcpy(phys_to_virt(phys), dma_addr, size); |
513 | 458 | ||
514 | /* | 459 | /* |
515 | * Return the buffer to the free list by setting the corresponding | 460 | * Return the buffer to the free list by setting the corresponding |
@@ -541,18 +486,21 @@ static void | |||
541 | sync_single(struct device *hwdev, char *dma_addr, size_t size, | 486 | sync_single(struct device *hwdev, char *dma_addr, size_t size, |
542 | int dir, int target) | 487 | int dir, int target) |
543 | { | 488 | { |
544 | struct swiotlb_phys_addr buffer = swiotlb_bus_to_phys_addr(dma_addr); | 489 | int index = (dma_addr - io_tlb_start) >> IO_TLB_SHIFT; |
490 | phys_addr_t phys = io_tlb_orig_addr[index]; | ||
491 | |||
492 | phys += ((unsigned long)dma_addr & ((1 << IO_TLB_SHIFT) - 1)); | ||
545 | 493 | ||
546 | switch (target) { | 494 | switch (target) { |
547 | case SYNC_FOR_CPU: | 495 | case SYNC_FOR_CPU: |
548 | if (likely(dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL)) | 496 | if (likely(dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL)) |
549 | __sync_single(buffer, dma_addr, size, DMA_FROM_DEVICE); | 497 | memcpy(phys_to_virt(phys), dma_addr, size); |
550 | else | 498 | else |
551 | BUG_ON(dir != DMA_TO_DEVICE); | 499 | BUG_ON(dir != DMA_TO_DEVICE); |
552 | break; | 500 | break; |
553 | case SYNC_FOR_DEVICE: | 501 | case SYNC_FOR_DEVICE: |
554 | if (likely(dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)) | 502 | if (likely(dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)) |
555 | __sync_single(buffer, dma_addr, size, DMA_TO_DEVICE); | 503 | memcpy(dma_addr, phys_to_virt(phys), size); |
556 | else | 504 | else |
557 | BUG_ON(dir != DMA_FROM_DEVICE); | 505 | BUG_ON(dir != DMA_FROM_DEVICE); |
558 | break; | 506 | break; |
@@ -591,10 +539,7 @@ swiotlb_alloc_coherent(struct device *hwdev, size_t size, | |||
591 | * swiotlb_map_single(), which will grab memory from | 539 | * swiotlb_map_single(), which will grab memory from |
592 | * the lowest available address range. | 540 | * the lowest available address range. |
593 | */ | 541 | */ |
594 | struct swiotlb_phys_addr buffer; | 542 | ret = map_single(hwdev, 0, size, DMA_FROM_DEVICE); |
595 | buffer.page = virt_to_page(NULL); | ||
596 | buffer.offset = 0; | ||
597 | ret = map_single(hwdev, buffer, size, DMA_FROM_DEVICE); | ||
598 | if (!ret) | 543 | if (!ret) |
599 | return NULL; | 544 | return NULL; |
600 | } | 545 | } |
@@ -662,7 +607,6 @@ swiotlb_map_single_attrs(struct device *hwdev, void *ptr, size_t size, | |||
662 | { | 607 | { |
663 | dma_addr_t dev_addr = swiotlb_virt_to_bus(hwdev, ptr); | 608 | dma_addr_t dev_addr = swiotlb_virt_to_bus(hwdev, ptr); |
664 | void *map; | 609 | void *map; |
665 | struct swiotlb_phys_addr buffer; | ||
666 | 610 | ||
667 | BUG_ON(dir == DMA_NONE); | 611 | BUG_ON(dir == DMA_NONE); |
668 | /* | 612 | /* |
@@ -677,9 +621,7 @@ swiotlb_map_single_attrs(struct device *hwdev, void *ptr, size_t size, | |||
677 | /* | 621 | /* |
678 | * Oh well, have to allocate and map a bounce buffer. | 622 | * Oh well, have to allocate and map a bounce buffer. |
679 | */ | 623 | */ |
680 | buffer.page = virt_to_page(ptr); | 624 | map = map_single(hwdev, virt_to_phys(ptr), size, dir); |
681 | buffer.offset = (unsigned long)ptr & ~PAGE_MASK; | ||
682 | map = map_single(hwdev, buffer, size, dir); | ||
683 | if (!map) { | 625 | if (!map) { |
684 | swiotlb_full(hwdev, size, dir, 1); | 626 | swiotlb_full(hwdev, size, dir, 1); |
685 | map = io_tlb_overflow_buffer; | 627 | map = io_tlb_overflow_buffer; |
@@ -824,20 +766,18 @@ swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, int nelems, | |||
824 | int dir, struct dma_attrs *attrs) | 766 | int dir, struct dma_attrs *attrs) |
825 | { | 767 | { |
826 | struct scatterlist *sg; | 768 | struct scatterlist *sg; |
827 | struct swiotlb_phys_addr buffer; | ||
828 | dma_addr_t dev_addr; | ||
829 | int i; | 769 | int i; |
830 | 770 | ||
831 | BUG_ON(dir == DMA_NONE); | 771 | BUG_ON(dir == DMA_NONE); |
832 | 772 | ||
833 | for_each_sg(sgl, sg, nelems, i) { | 773 | for_each_sg(sgl, sg, nelems, i) { |
834 | dev_addr = swiotlb_sg_to_bus(hwdev, sg); | 774 | void *addr = sg_virt(sg); |
835 | if (range_needs_mapping(sg_virt(sg), sg->length) || | 775 | dma_addr_t dev_addr = swiotlb_virt_to_bus(hwdev, addr); |
776 | |||
777 | if (range_needs_mapping(addr, sg->length) || | ||
836 | address_needs_mapping(hwdev, dev_addr, sg->length)) { | 778 | address_needs_mapping(hwdev, dev_addr, sg->length)) { |
837 | void *map; | 779 | void *map = map_single(hwdev, sg_phys(sg), |
838 | buffer.page = sg_page(sg); | 780 | sg->length, dir); |
839 | buffer.offset = sg->offset; | ||
840 | map = map_single(hwdev, buffer, sg->length, dir); | ||
841 | if (!map) { | 781 | if (!map) { |
842 | /* Don't panic here, we expect map_sg users | 782 | /* Don't panic here, we expect map_sg users |
843 | to do proper error handling. */ | 783 | to do proper error handling. */ |