diff options
| author | David S. Miller <davem@huronp11.davemloft.net> | 2008-02-06 06:50:26 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2008-02-06 07:12:25 -0500 |
| commit | 38192d52f159bc06b7f523800c10b583cdd661d5 (patch) | |
| tree | 4cf695d583c0a657133642c0299cbfa536e25663 | |
| parent | b3ff81dd8ae29ec431f6cc91aff601a51ef6fb8c (diff) | |
[SPARC64]: Temporarily remove IOMMU merging code.
Changeset fde6a3c82d67f592eb587be4d12222b0ae6d4321 ("iommu sg merging:
sparc64: make iommu respect the segment size limits") broke sparc64
because whilst it added the segment limiting code to the first pass of
SG mapping (in prepare_sg()) it did not add matching code to the
second pass handling (in fill_sg())
As a result the two passes disagree where the segment boundaries
should be, resulting in OOPSes, DMA corruption, and corrupted
superblocks.
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | arch/sparc64/kernel/Makefile | 2 | ||||
| -rw-r--r-- | arch/sparc64/kernel/iommu.c | 142 | ||||
| -rw-r--r-- | arch/sparc64/kernel/iommu_common.c | 248 | ||||
| -rw-r--r-- | arch/sparc64/kernel/iommu_common.h | 26 | ||||
| -rw-r--r-- | arch/sparc64/kernel/pci_sun4v.c | 170 | ||||
| -rw-r--r-- | include/asm-sparc64/io.h | 2 |
6 files changed, 93 insertions, 497 deletions
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index ef50d217432f..4b78b24ef413 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile | |||
| @@ -11,7 +11,7 @@ obj-y := process.o setup.o cpu.o idprom.o \ | |||
| 11 | traps.o auxio.o una_asm.o sysfs.o iommu.o \ | 11 | traps.o auxio.o una_asm.o sysfs.o iommu.o \ |
| 12 | irq.o ptrace.o time.o sys_sparc.o signal.o \ | 12 | irq.o ptrace.o time.o sys_sparc.o signal.o \ |
| 13 | unaligned.o central.o pci.o starfire.o semaphore.o \ | 13 | unaligned.o central.o pci.o starfire.o semaphore.o \ |
| 14 | power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \ | 14 | power.o sbus.o sparc64_ksyms.o chmc.o \ |
| 15 | visemul.o prom.o of_device.o hvapi.o sstate.o mdesc.o | 15 | visemul.o prom.o of_device.o hvapi.o sstate.o mdesc.o |
| 16 | 16 | ||
| 17 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | 17 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
diff --git a/arch/sparc64/kernel/iommu.c b/arch/sparc64/kernel/iommu.c index 4b9115a4d92e..5623a4d59dff 100644 --- a/arch/sparc64/kernel/iommu.c +++ b/arch/sparc64/kernel/iommu.c | |||
| @@ -472,94 +472,15 @@ static void dma_4u_unmap_single(struct device *dev, dma_addr_t bus_addr, | |||
| 472 | spin_unlock_irqrestore(&iommu->lock, flags); | 472 | spin_unlock_irqrestore(&iommu->lock, flags); |
| 473 | } | 473 | } |
| 474 | 474 | ||
| 475 | #define SG_ENT_PHYS_ADDRESS(SG) (__pa(sg_virt((SG)))) | ||
| 476 | |||
| 477 | static void fill_sg(iopte_t *iopte, struct scatterlist *sg, | ||
| 478 | int nused, int nelems, | ||
| 479 | unsigned long iopte_protection) | ||
| 480 | { | ||
| 481 | struct scatterlist *dma_sg = sg; | ||
| 482 | int i; | ||
| 483 | |||
| 484 | for (i = 0; i < nused; i++) { | ||
| 485 | unsigned long pteval = ~0UL; | ||
| 486 | u32 dma_npages; | ||
| 487 | |||
| 488 | dma_npages = ((dma_sg->dma_address & (IO_PAGE_SIZE - 1UL)) + | ||
| 489 | dma_sg->dma_length + | ||
| 490 | ((IO_PAGE_SIZE - 1UL))) >> IO_PAGE_SHIFT; | ||
| 491 | do { | ||
| 492 | unsigned long offset; | ||
| 493 | signed int len; | ||
| 494 | |||
| 495 | /* If we are here, we know we have at least one | ||
| 496 | * more page to map. So walk forward until we | ||
| 497 | * hit a page crossing, and begin creating new | ||
| 498 | * mappings from that spot. | ||
| 499 | */ | ||
| 500 | for (;;) { | ||
| 501 | unsigned long tmp; | ||
| 502 | |||
| 503 | tmp = SG_ENT_PHYS_ADDRESS(sg); | ||
| 504 | len = sg->length; | ||
| 505 | if (((tmp ^ pteval) >> IO_PAGE_SHIFT) != 0UL) { | ||
| 506 | pteval = tmp & IO_PAGE_MASK; | ||
| 507 | offset = tmp & (IO_PAGE_SIZE - 1UL); | ||
| 508 | break; | ||
| 509 | } | ||
| 510 | if (((tmp ^ (tmp + len - 1UL)) >> IO_PAGE_SHIFT) != 0UL) { | ||
| 511 | pteval = (tmp + IO_PAGE_SIZE) & IO_PAGE_MASK; | ||
| 512 | offset = 0UL; | ||
| 513 | len -= (IO_PAGE_SIZE - (tmp & (IO_PAGE_SIZE - 1UL))); | ||
| 514 | break; | ||
| 515 | } | ||
| 516 | sg = sg_next(sg); | ||
| 517 | nelems--; | ||
| 518 | } | ||
| 519 | |||
| 520 | pteval = iopte_protection | (pteval & IOPTE_PAGE); | ||
| 521 | while (len > 0) { | ||
| 522 | *iopte++ = __iopte(pteval); | ||
| 523 | pteval += IO_PAGE_SIZE; | ||
| 524 | len -= (IO_PAGE_SIZE - offset); | ||
| 525 | offset = 0; | ||
| 526 | dma_npages--; | ||
| 527 | } | ||
| 528 | |||
| 529 | pteval = (pteval & IOPTE_PAGE) + len; | ||
| 530 | sg = sg_next(sg); | ||
| 531 | nelems--; | ||
| 532 | |||
| 533 | /* Skip over any tail mappings we've fully mapped, | ||
| 534 | * adjusting pteval along the way. Stop when we | ||
| 535 | * detect a page crossing event. | ||
| 536 | */ | ||
| 537 | while (nelems && | ||
| 538 | (pteval << (64 - IO_PAGE_SHIFT)) != 0UL && | ||
| 539 | (pteval == SG_ENT_PHYS_ADDRESS(sg)) && | ||
| 540 | ((pteval ^ | ||
| 541 | (SG_ENT_PHYS_ADDRESS(sg) + sg->length - 1UL)) >> IO_PAGE_SHIFT) == 0UL) { | ||
| 542 | pteval += sg->length; | ||
| 543 | sg = sg_next(sg); | ||
| 544 | nelems--; | ||
| 545 | } | ||
| 546 | if ((pteval << (64 - IO_PAGE_SHIFT)) == 0UL) | ||
| 547 | pteval = ~0UL; | ||
| 548 | } while (dma_npages != 0); | ||
| 549 | dma_sg = sg_next(dma_sg); | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | static int dma_4u_map_sg(struct device *dev, struct scatterlist *sglist, | 475 | static int dma_4u_map_sg(struct device *dev, struct scatterlist *sglist, |
| 554 | int nelems, enum dma_data_direction direction) | 476 | int nelems, enum dma_data_direction direction) |
| 555 | { | 477 | { |
| 556 | struct iommu *iommu; | 478 | unsigned long flags, ctx, i, npages, iopte_protection; |
| 479 | struct scatterlist *sg; | ||
| 557 | struct strbuf *strbuf; | 480 | struct strbuf *strbuf; |
| 558 | unsigned long flags, ctx, npages, iopte_protection; | 481 | struct iommu *iommu; |
| 559 | iopte_t *base; | 482 | iopte_t *base; |
| 560 | u32 dma_base; | 483 | u32 dma_base; |
| 561 | struct scatterlist *sgtmp; | ||
| 562 | int used; | ||
| 563 | 484 | ||
| 564 | /* Fast path single entry scatterlists. */ | 485 | /* Fast path single entry scatterlists. */ |
| 565 | if (nelems == 1) { | 486 | if (nelems == 1) { |
| @@ -578,11 +499,7 @@ static int dma_4u_map_sg(struct device *dev, struct scatterlist *sglist, | |||
| 578 | if (unlikely(direction == DMA_NONE)) | 499 | if (unlikely(direction == DMA_NONE)) |
| 579 | goto bad_no_ctx; | 500 | goto bad_no_ctx; |
| 580 | 501 | ||
| 581 | /* Step 1: Prepare scatter list. */ | 502 | npages = calc_npages(sglist, nelems); |
| 582 | |||
| 583 | npages = prepare_sg(dev, sglist, nelems); | ||
| 584 | |||
| 585 | /* Step 2: Allocate a cluster and context, if necessary. */ | ||
| 586 | 503 | ||
| 587 | spin_lock_irqsave(&iommu->lock, flags); | 504 | spin_lock_irqsave(&iommu->lock, flags); |
| 588 | 505 | ||
| @@ -599,18 +516,6 @@ static int dma_4u_map_sg(struct device *dev, struct scatterlist *sglist, | |||
| 599 | dma_base = iommu->page_table_map_base + | 516 | dma_base = iommu->page_table_map_base + |
| 600 | ((base - iommu->page_table) << IO_PAGE_SHIFT); | 517 | ((base - iommu->page_table) << IO_PAGE_SHIFT); |
| 601 | 518 | ||
| 602 | /* Step 3: Normalize DMA addresses. */ | ||
| 603 | used = nelems; | ||
| 604 | |||
| 605 | sgtmp = sglist; | ||
| 606 | while (used && sgtmp->dma_length) { | ||
| 607 | sgtmp->dma_address += dma_base; | ||
| 608 | sgtmp = sg_next(sgtmp); | ||
| 609 | used--; | ||
| 610 | } | ||
| 611 | used = nelems - used; | ||
| 612 | |||
| 613 | /* Step 4: Create the mappings. */ | ||
| 614 | if (strbuf->strbuf_enabled) | 519 | if (strbuf->strbuf_enabled) |
| 615 | iopte_protection = IOPTE_STREAMING(ctx); | 520 | iopte_protection = IOPTE_STREAMING(ctx); |
| 616 | else | 521 | else |
| @@ -618,13 +523,27 @@ static int dma_4u_map_sg(struct device *dev, struct scatterlist *sglist, | |||
| 618 | if (direction != DMA_TO_DEVICE) | 523 | if (direction != DMA_TO_DEVICE) |
| 619 | iopte_protection |= IOPTE_WRITE; | 524 | iopte_protection |= IOPTE_WRITE; |
| 620 | 525 | ||
| 621 | fill_sg(base, sglist, used, nelems, iopte_protection); | 526 | for_each_sg(sglist, sg, nelems, i) { |
| 527 | unsigned long paddr = SG_ENT_PHYS_ADDRESS(sg); | ||
| 528 | unsigned long slen = sg->length; | ||
| 529 | unsigned long this_npages; | ||
| 622 | 530 | ||
| 623 | #ifdef VERIFY_SG | 531 | this_npages = iommu_num_pages(paddr, slen); |
| 624 | verify_sglist(sglist, nelems, base, npages); | ||
| 625 | #endif | ||
| 626 | 532 | ||
| 627 | return used; | 533 | sg->dma_address = dma_base | (paddr & ~IO_PAGE_MASK); |
| 534 | sg->dma_length = slen; | ||
| 535 | |||
| 536 | paddr &= IO_PAGE_MASK; | ||
| 537 | while (this_npages--) { | ||
| 538 | iopte_val(*base) = iopte_protection | paddr; | ||
| 539 | |||
| 540 | base++; | ||
| 541 | paddr += IO_PAGE_SIZE; | ||
| 542 | dma_base += IO_PAGE_SIZE; | ||
| 543 | } | ||
| 544 | } | ||
| 545 | |||
| 546 | return nelems; | ||
| 628 | 547 | ||
| 629 | bad: | 548 | bad: |
| 630 | iommu_free_ctx(iommu, ctx); | 549 | iommu_free_ctx(iommu, ctx); |
| @@ -637,11 +556,10 @@ bad_no_ctx: | |||
| 637 | static void dma_4u_unmap_sg(struct device *dev, struct scatterlist *sglist, | 556 | static void dma_4u_unmap_sg(struct device *dev, struct scatterlist *sglist, |
| 638 | int nelems, enum dma_data_direction direction) | 557 | int nelems, enum dma_data_direction direction) |
| 639 | { | 558 | { |
| 640 | struct iommu *iommu; | 559 | unsigned long flags, ctx, i, npages; |
| 641 | struct strbuf *strbuf; | 560 | struct strbuf *strbuf; |
| 561 | struct iommu *iommu; | ||
| 642 | iopte_t *base; | 562 | iopte_t *base; |
| 643 | unsigned long flags, ctx, i, npages; | ||
| 644 | struct scatterlist *sg, *sgprv; | ||
| 645 | u32 bus_addr; | 563 | u32 bus_addr; |
| 646 | 564 | ||
| 647 | if (unlikely(direction == DMA_NONE)) { | 565 | if (unlikely(direction == DMA_NONE)) { |
| @@ -654,15 +572,7 @@ static void dma_4u_unmap_sg(struct device *dev, struct scatterlist *sglist, | |||
| 654 | 572 | ||
| 655 | bus_addr = sglist->dma_address & IO_PAGE_MASK; | 573 | bus_addr = sglist->dma_address & IO_PAGE_MASK; |
| 656 | 574 | ||
| 657 | sgprv = NULL; | 575 | npages = calc_npages(sglist, nelems); |
| 658 | for_each_sg(sglist, sg, nelems, i) { | ||
| 659 | if (sg->dma_length == 0) | ||
| 660 | break; | ||
| 661 | sgprv = sg; | ||
| 662 | } | ||
| 663 | |||
| 664 | npages = (IO_PAGE_ALIGN(sgprv->dma_address + sgprv->dma_length) - | ||
| 665 | bus_addr) >> IO_PAGE_SHIFT; | ||
| 666 | 576 | ||
| 667 | base = iommu->page_table + | 577 | base = iommu->page_table + |
| 668 | ((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT); | 578 | ((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT); |
diff --git a/arch/sparc64/kernel/iommu_common.c b/arch/sparc64/kernel/iommu_common.c deleted file mode 100644 index 72a4acfe8c7b..000000000000 --- a/arch/sparc64/kernel/iommu_common.c +++ /dev/null | |||
| @@ -1,248 +0,0 @@ | |||
| 1 | /* $Id: iommu_common.c,v 1.9 2001/12/17 07:05:09 davem Exp $ | ||
| 2 | * iommu_common.c: UltraSparc SBUS/PCI common iommu code. | ||
| 3 | * | ||
| 4 | * Copyright (C) 1999 David S. Miller (davem@redhat.com) | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/dma-mapping.h> | ||
| 8 | #include "iommu_common.h" | ||
| 9 | |||
| 10 | /* You are _strongly_ advised to enable the following debugging code | ||
| 11 | * any time you make changes to the sg code below, run it for a while | ||
| 12 | * with filesystems mounted read-only before buying the farm... -DaveM | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifdef VERIFY_SG | ||
| 16 | static int verify_lengths(struct scatterlist *sglist, int nents, int npages) | ||
| 17 | { | ||
| 18 | int sg_len, dma_len; | ||
| 19 | int i, pgcount; | ||
| 20 | struct scatterlist *sg; | ||
| 21 | |||
| 22 | sg_len = 0; | ||
| 23 | for_each_sg(sglist, sg, nents, i) | ||
| 24 | sg_len += sg->length; | ||
| 25 | |||
| 26 | dma_len = 0; | ||
| 27 | for_each_sg(sglist, sg, nents, i) { | ||
| 28 | if (!sg->dma_length) | ||
| 29 | break; | ||
| 30 | dma_len += sg->dma_length; | ||
| 31 | } | ||
| 32 | |||
| 33 | if (sg_len != dma_len) { | ||
| 34 | printk("verify_lengths: Error, different, sg[%d] dma[%d]\n", | ||
| 35 | sg_len, dma_len); | ||
| 36 | return -1; | ||
| 37 | } | ||
| 38 | |||
| 39 | pgcount = 0; | ||
| 40 | for_each_sg(sglist, sg, nents, i) { | ||
| 41 | unsigned long start, end; | ||
| 42 | |||
| 43 | if (!sg->dma_length) | ||
| 44 | break; | ||
| 45 | |||
| 46 | start = sg->dma_address; | ||
| 47 | start = start & IO_PAGE_MASK; | ||
| 48 | |||
| 49 | end = sg->dma_address + sg->dma_length; | ||
| 50 | end = (end + (IO_PAGE_SIZE - 1)) & IO_PAGE_MASK; | ||
| 51 | |||
| 52 | pgcount += ((end - start) >> IO_PAGE_SHIFT); | ||
| 53 | } | ||
| 54 | |||
| 55 | if (pgcount != npages) { | ||
| 56 | printk("verify_lengths: Error, page count wrong, " | ||
| 57 | "npages[%d] pgcount[%d]\n", | ||
| 58 | npages, pgcount); | ||
| 59 | return -1; | ||
| 60 | } | ||
| 61 | |||
| 62 | /* This test passes... */ | ||
| 63 | return 0; | ||
| 64 | } | ||
| 65 | |||
| 66 | static int verify_one_map(struct scatterlist *dma_sg, struct scatterlist **__sg, int nents, iopte_t **__iopte) | ||
| 67 | { | ||
| 68 | struct scatterlist *sg = *__sg; | ||
| 69 | iopte_t *iopte = *__iopte; | ||
| 70 | u32 dlen = dma_sg->dma_length; | ||
| 71 | u32 daddr; | ||
| 72 | unsigned int sglen; | ||
| 73 | unsigned long sgaddr; | ||
| 74 | |||
| 75 | daddr = dma_sg->dma_address; | ||
| 76 | sglen = sg->length; | ||
| 77 | sgaddr = (unsigned long) sg_virt(sg); | ||
| 78 | while (dlen > 0) { | ||
| 79 | unsigned long paddr; | ||
| 80 | |||
| 81 | /* SG and DMA_SG must begin at the same sub-page boundary. */ | ||
| 82 | if ((sgaddr & ~IO_PAGE_MASK) != (daddr & ~IO_PAGE_MASK)) { | ||
| 83 | printk("verify_one_map: Wrong start offset " | ||
| 84 | "sg[%08lx] dma[%08x]\n", | ||
| 85 | sgaddr, daddr); | ||
| 86 | nents = -1; | ||
| 87 | goto out; | ||
| 88 | } | ||
| 89 | |||
| 90 | /* Verify the IOPTE points to the right page. */ | ||
| 91 | paddr = iopte_val(*iopte) & IOPTE_PAGE; | ||
| 92 | if ((paddr + PAGE_OFFSET) != (sgaddr & IO_PAGE_MASK)) { | ||
| 93 | printk("verify_one_map: IOPTE[%08lx] maps the " | ||
| 94 | "wrong page, should be [%08lx]\n", | ||
| 95 | iopte_val(*iopte), (sgaddr & IO_PAGE_MASK) - PAGE_OFFSET); | ||
| 96 | nents = -1; | ||
| 97 | goto out; | ||
| 98 | } | ||
| 99 | |||
| 100 | /* If this SG crosses a page, adjust to that next page | ||
| 101 | * boundary and loop. | ||
| 102 | */ | ||
| 103 | if ((sgaddr & IO_PAGE_MASK) ^ ((sgaddr + sglen - 1) & IO_PAGE_MASK)) { | ||
| 104 | unsigned long next_page, diff; | ||
| 105 | |||
| 106 | next_page = (sgaddr + IO_PAGE_SIZE) & IO_PAGE_MASK; | ||
| 107 | diff = next_page - sgaddr; | ||
| 108 | sgaddr += diff; | ||
| 109 | daddr += diff; | ||
| 110 | sglen -= diff; | ||
| 111 | dlen -= diff; | ||
| 112 | if (dlen > 0) | ||
| 113 | iopte++; | ||
| 114 | continue; | ||
| 115 | } | ||
| 116 | |||
| 117 | /* SG wholly consumed within this page. */ | ||
| 118 | daddr += sglen; | ||
| 119 | dlen -= sglen; | ||
| 120 | |||
| 121 | if (dlen > 0 && ((daddr & ~IO_PAGE_MASK) == 0)) | ||
| 122 | iopte++; | ||
| 123 | |||
| 124 | sg = sg_next(sg); | ||
| 125 | if (--nents <= 0) | ||
| 126 | break; | ||
| 127 | sgaddr = (unsigned long) sg_virt(sg); | ||
| 128 | sglen = sg->length; | ||
| 129 | } | ||
| 130 | if (dlen < 0) { | ||
| 131 | /* Transfer overrun, big problems. */ | ||
| 132 | printk("verify_one_map: Transfer overrun by %d bytes.\n", | ||
| 133 | -dlen); | ||
| 134 | nents = -1; | ||
| 135 | } else { | ||
| 136 | /* Advance to next dma_sg implies that the next iopte will | ||
| 137 | * begin it. | ||
| 138 | */ | ||
| 139 | iopte++; | ||
| 140 | } | ||
| 141 | |||
| 142 | out: | ||
| 143 | *__sg = sg; | ||
| 144 | *__iopte = iopte; | ||
| 145 | return nents; | ||
| 146 | } | ||
| 147 | |||
| 148 | static int verify_maps(struct scatterlist *sg, int nents, iopte_t *iopte) | ||
| 149 | { | ||
| 150 | struct scatterlist *dma_sg = sg; | ||
| 151 | struct scatterlist *orig_dma_sg = dma_sg; | ||
| 152 | int orig_nents = nents; | ||
| 153 | |||
| 154 | for (;;) { | ||
| 155 | nents = verify_one_map(dma_sg, &sg, nents, &iopte); | ||
| 156 | if (nents <= 0) | ||
| 157 | break; | ||
| 158 | dma_sg = sg_next(dma_sg); | ||
| 159 | if (dma_sg->dma_length == 0) | ||
| 160 | break; | ||
| 161 | } | ||
| 162 | |||
| 163 | if (nents > 0) { | ||
| 164 | printk("verify_maps: dma maps consumed by some sgs remain (%d)\n", | ||
| 165 | nents); | ||
| 166 | return -1; | ||
| 167 | } | ||
| 168 | |||
| 169 | if (nents < 0) { | ||
| 170 | printk("verify_maps: Error, messed up mappings, " | ||
| 171 | "at sg %d dma_sg %d\n", | ||
| 172 | (int) (orig_nents + nents), (int) (dma_sg - orig_dma_sg)); | ||
| 173 | return -1; | ||
| 174 | } | ||
| 175 | |||
| 176 | /* This test passes... */ | ||
| 177 | return 0; | ||
| 178 | } | ||
| 179 | |||
| 180 | void verify_sglist(struct scatterlist *sglist, int nents, iopte_t *iopte, int npages) | ||
| 181 | { | ||
| 182 | struct scatterlist *sg; | ||
| 183 | |||
| 184 | if (verify_lengths(sglist, nents, npages) < 0 || | ||
| 185 | verify_maps(sglist, nents, iopte) < 0) { | ||
| 186 | int i; | ||
| 187 | |||
| 188 | printk("verify_sglist: Crap, messed up mappings, dumping, iodma at "); | ||
| 189 | printk("%016lx.\n", sglist->dma_address & IO_PAGE_MASK); | ||
| 190 | |||
| 191 | for_each_sg(sglist, sg, nents, i) { | ||
| 192 | printk("sg(%d): page_addr(%p) off(%x) length(%x) " | ||
| 193 | "dma_address[%016x] dma_length[%016x]\n", | ||
| 194 | i, | ||
| 195 | page_address(sg_page(sg)), sg->offset, | ||
| 196 | sg->length, | ||
| 197 | sg->dma_address, sg->dma_length); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | /* Seems to be ok */ | ||
| 202 | } | ||
| 203 | #endif | ||
| 204 | |||
| 205 | unsigned long prepare_sg(struct device *dev, struct scatterlist *sg, int nents) | ||
| 206 | { | ||
| 207 | struct scatterlist *dma_sg = sg; | ||
| 208 | unsigned long prev; | ||
| 209 | u32 dent_addr, dent_len; | ||
| 210 | unsigned int max_seg_size; | ||
| 211 | |||
| 212 | prev = (unsigned long) sg_virt(sg); | ||
| 213 | prev += (unsigned long) (dent_len = sg->length); | ||
| 214 | dent_addr = (u32) ((unsigned long)(sg_virt(sg)) & (IO_PAGE_SIZE - 1UL)); | ||
| 215 | max_seg_size = dma_get_max_seg_size(dev); | ||
| 216 | while (--nents) { | ||
| 217 | unsigned long addr; | ||
| 218 | |||
| 219 | sg = sg_next(sg); | ||
| 220 | addr = (unsigned long) sg_virt(sg); | ||
| 221 | if (! VCONTIG(prev, addr) || | ||
| 222 | dent_len + sg->length > max_seg_size) { | ||
| 223 | dma_sg->dma_address = dent_addr; | ||
| 224 | dma_sg->dma_length = dent_len; | ||
| 225 | dma_sg = sg_next(dma_sg); | ||
| 226 | |||
| 227 | dent_addr = ((dent_addr + | ||
| 228 | dent_len + | ||
| 229 | (IO_PAGE_SIZE - 1UL)) >> IO_PAGE_SHIFT); | ||
| 230 | dent_addr <<= IO_PAGE_SHIFT; | ||
| 231 | dent_addr += addr & (IO_PAGE_SIZE - 1UL); | ||
| 232 | dent_len = 0; | ||
| 233 | } | ||
| 234 | dent_len += sg->length; | ||
| 235 | prev = addr + sg->length; | ||
| 236 | } | ||
| 237 | dma_sg->dma_address = dent_addr; | ||
| 238 | dma_sg->dma_length = dent_len; | ||
| 239 | |||
| 240 | if (dma_sg != sg) { | ||
| 241 | dma_sg = sg_next(dma_sg); | ||
| 242 | dma_sg->dma_length = 0; | ||
| 243 | } | ||
| 244 | |||
| 245 | return ((unsigned long) dent_addr + | ||
| 246 | (unsigned long) dent_len + | ||
| 247 | (IO_PAGE_SIZE - 1UL)) >> IO_PAGE_SHIFT; | ||
| 248 | } | ||
diff --git a/arch/sparc64/kernel/iommu_common.h b/arch/sparc64/kernel/iommu_common.h index a90d046e8024..4b5cafa2877a 100644 --- a/arch/sparc64/kernel/iommu_common.h +++ b/arch/sparc64/kernel/iommu_common.h | |||
| @@ -30,6 +30,32 @@ | |||
| 30 | */ | 30 | */ |
| 31 | #define IOMMU_PAGE_SHIFT 13 | 31 | #define IOMMU_PAGE_SHIFT 13 |
| 32 | 32 | ||
| 33 | #define SG_ENT_PHYS_ADDRESS(SG) (__pa(sg_virt((SG)))) | ||
| 34 | |||
| 35 | static inline unsigned long iommu_num_pages(unsigned long vaddr, | ||
| 36 | unsigned long slen) | ||
| 37 | { | ||
| 38 | unsigned long npages; | ||
| 39 | |||
| 40 | npages = IO_PAGE_ALIGN(vaddr + slen) - (vaddr & IO_PAGE_MASK); | ||
| 41 | npages >>= IO_PAGE_SHIFT; | ||
| 42 | |||
| 43 | return npages; | ||
| 44 | } | ||
| 45 | |||
| 46 | static inline unsigned long calc_npages(struct scatterlist *sglist, int nelems) | ||
| 47 | { | ||
| 48 | unsigned long i, npages = 0; | ||
| 49 | struct scatterlist *sg; | ||
| 50 | |||
| 51 | for_each_sg(sglist, sg, nelems, i) { | ||
| 52 | unsigned long paddr = SG_ENT_PHYS_ADDRESS(sg); | ||
| 53 | npages += iommu_num_pages(paddr, sg->length); | ||
| 54 | } | ||
| 55 | |||
| 56 | return npages; | ||
| 57 | } | ||
| 58 | |||
| 33 | /* You are _strongly_ advised to enable the following debugging code | 59 | /* You are _strongly_ advised to enable the following debugging code |
| 34 | * any time you make changes to the sg code below, run it for a while | 60 | * any time you make changes to the sg code below, run it for a while |
| 35 | * with filesystems mounted read-only before buying the farm... -DaveM | 61 | * with filesystems mounted read-only before buying the farm... -DaveM |
diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c index 5ea2eab1ccda..61baf8dc095e 100644 --- a/arch/sparc64/kernel/pci_sun4v.c +++ b/arch/sparc64/kernel/pci_sun4v.c | |||
| @@ -365,113 +365,14 @@ static void dma_4v_unmap_single(struct device *dev, dma_addr_t bus_addr, | |||
| 365 | spin_unlock_irqrestore(&iommu->lock, flags); | 365 | spin_unlock_irqrestore(&iommu->lock, flags); |
| 366 | } | 366 | } |
| 367 | 367 | ||
| 368 | #define SG_ENT_PHYS_ADDRESS(SG) (__pa(sg_virt((SG)))) | ||
| 369 | |||
| 370 | static long fill_sg(long entry, struct device *dev, | ||
| 371 | struct scatterlist *sg, | ||
| 372 | int nused, int nelems, unsigned long prot) | ||
| 373 | { | ||
| 374 | struct scatterlist *dma_sg = sg; | ||
| 375 | unsigned long flags; | ||
| 376 | int i; | ||
| 377 | |||
| 378 | local_irq_save(flags); | ||
| 379 | |||
| 380 | iommu_batch_start(dev, prot, entry); | ||
| 381 | |||
| 382 | for (i = 0; i < nused; i++) { | ||
| 383 | unsigned long pteval = ~0UL; | ||
| 384 | u32 dma_npages; | ||
| 385 | |||
| 386 | dma_npages = ((dma_sg->dma_address & (IO_PAGE_SIZE - 1UL)) + | ||
| 387 | dma_sg->dma_length + | ||
| 388 | ((IO_PAGE_SIZE - 1UL))) >> IO_PAGE_SHIFT; | ||
| 389 | do { | ||
| 390 | unsigned long offset; | ||
| 391 | signed int len; | ||
| 392 | |||
| 393 | /* If we are here, we know we have at least one | ||
| 394 | * more page to map. So walk forward until we | ||
| 395 | * hit a page crossing, and begin creating new | ||
| 396 | * mappings from that spot. | ||
| 397 | */ | ||
| 398 | for (;;) { | ||
| 399 | unsigned long tmp; | ||
| 400 | |||
| 401 | tmp = SG_ENT_PHYS_ADDRESS(sg); | ||
| 402 | len = sg->length; | ||
| 403 | if (((tmp ^ pteval) >> IO_PAGE_SHIFT) != 0UL) { | ||
| 404 | pteval = tmp & IO_PAGE_MASK; | ||
| 405 | offset = tmp & (IO_PAGE_SIZE - 1UL); | ||
| 406 | break; | ||
| 407 | } | ||
| 408 | if (((tmp ^ (tmp + len - 1UL)) >> IO_PAGE_SHIFT) != 0UL) { | ||
| 409 | pteval = (tmp + IO_PAGE_SIZE) & IO_PAGE_MASK; | ||
| 410 | offset = 0UL; | ||
| 411 | len -= (IO_PAGE_SIZE - (tmp & (IO_PAGE_SIZE - 1UL))); | ||
| 412 | break; | ||
| 413 | } | ||
| 414 | sg = sg_next(sg); | ||
| 415 | nelems--; | ||
| 416 | } | ||
| 417 | |||
| 418 | pteval = (pteval & IOPTE_PAGE); | ||
| 419 | while (len > 0) { | ||
| 420 | long err; | ||
| 421 | |||
| 422 | err = iommu_batch_add(pteval); | ||
| 423 | if (unlikely(err < 0L)) | ||
| 424 | goto iommu_map_failed; | ||
| 425 | |||
| 426 | pteval += IO_PAGE_SIZE; | ||
| 427 | len -= (IO_PAGE_SIZE - offset); | ||
| 428 | offset = 0; | ||
| 429 | dma_npages--; | ||
| 430 | } | ||
| 431 | |||
| 432 | pteval = (pteval & IOPTE_PAGE) + len; | ||
| 433 | sg = sg_next(sg); | ||
| 434 | nelems--; | ||
| 435 | |||
| 436 | /* Skip over any tail mappings we've fully mapped, | ||
| 437 | * adjusting pteval along the way. Stop when we | ||
| 438 | * detect a page crossing event. | ||
| 439 | */ | ||
| 440 | while (nelems && | ||
| 441 | (pteval << (64 - IO_PAGE_SHIFT)) != 0UL && | ||
| 442 | (pteval == SG_ENT_PHYS_ADDRESS(sg)) && | ||
| 443 | ((pteval ^ | ||
| 444 | (SG_ENT_PHYS_ADDRESS(sg) + sg->length - 1UL)) >> IO_PAGE_SHIFT) == 0UL) { | ||
| 445 | pteval += sg->length; | ||
| 446 | sg = sg_next(sg); | ||
| 447 | nelems--; | ||
| 448 | } | ||
| 449 | if ((pteval << (64 - IO_PAGE_SHIFT)) == 0UL) | ||
| 450 | pteval = ~0UL; | ||
| 451 | } while (dma_npages != 0); | ||
| 452 | dma_sg = sg_next(dma_sg); | ||
| 453 | } | ||
| 454 | |||
| 455 | if (unlikely(iommu_batch_end() < 0L)) | ||
| 456 | goto iommu_map_failed; | ||
| 457 | |||
| 458 | local_irq_restore(flags); | ||
| 459 | return 0; | ||
| 460 | |||
| 461 | iommu_map_failed: | ||
| 462 | local_irq_restore(flags); | ||
| 463 | return -1L; | ||
| 464 | } | ||
| 465 | |||
| 466 | static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist, | 368 | static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist, |
| 467 | int nelems, enum dma_data_direction direction) | 369 | int nelems, enum dma_data_direction direction) |
| 468 | { | 370 | { |
| 371 | unsigned long flags, npages, i, prot; | ||
| 372 | struct scatterlist *sg; | ||
| 469 | struct iommu *iommu; | 373 | struct iommu *iommu; |
| 470 | unsigned long flags, npages, prot; | ||
| 471 | u32 dma_base; | ||
| 472 | struct scatterlist *sgtmp; | ||
| 473 | long entry, err; | 374 | long entry, err; |
| 474 | int used; | 375 | u32 dma_base; |
| 475 | 376 | ||
| 476 | /* Fast path single entry scatterlists. */ | 377 | /* Fast path single entry scatterlists. */ |
| 477 | if (nelems == 1) { | 378 | if (nelems == 1) { |
| @@ -489,10 +390,8 @@ static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist, | |||
| 489 | if (unlikely(direction == DMA_NONE)) | 390 | if (unlikely(direction == DMA_NONE)) |
| 490 | goto bad; | 391 | goto bad; |
| 491 | 392 | ||
| 492 | /* Step 1: Prepare scatter list. */ | 393 | npages = calc_npages(sglist, nelems); |
| 493 | npages = prepare_sg(dev, sglist, nelems); | ||
| 494 | 394 | ||
| 495 | /* Step 2: Allocate a cluster and context, if necessary. */ | ||
| 496 | spin_lock_irqsave(&iommu->lock, flags); | 395 | spin_lock_irqsave(&iommu->lock, flags); |
| 497 | entry = arena_alloc(&iommu->arena, npages); | 396 | entry = arena_alloc(&iommu->arena, npages); |
| 498 | spin_unlock_irqrestore(&iommu->lock, flags); | 397 | spin_unlock_irqrestore(&iommu->lock, flags); |
| @@ -503,27 +402,45 @@ static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist, | |||
| 503 | dma_base = iommu->page_table_map_base + | 402 | dma_base = iommu->page_table_map_base + |
| 504 | (entry << IO_PAGE_SHIFT); | 403 | (entry << IO_PAGE_SHIFT); |
| 505 | 404 | ||
| 506 | /* Step 3: Normalize DMA addresses. */ | ||
| 507 | used = nelems; | ||
| 508 | |||
| 509 | sgtmp = sglist; | ||
| 510 | while (used && sgtmp->dma_length) { | ||
| 511 | sgtmp->dma_address += dma_base; | ||
| 512 | sgtmp = sg_next(sgtmp); | ||
| 513 | used--; | ||
| 514 | } | ||
| 515 | used = nelems - used; | ||
| 516 | |||
| 517 | /* Step 4: Create the mappings. */ | ||
| 518 | prot = HV_PCI_MAP_ATTR_READ; | 405 | prot = HV_PCI_MAP_ATTR_READ; |
| 519 | if (direction != DMA_TO_DEVICE) | 406 | if (direction != DMA_TO_DEVICE) |
| 520 | prot |= HV_PCI_MAP_ATTR_WRITE; | 407 | prot |= HV_PCI_MAP_ATTR_WRITE; |
| 521 | 408 | ||
| 522 | err = fill_sg(entry, dev, sglist, used, nelems, prot); | 409 | local_irq_save(flags); |
| 410 | |||
| 411 | iommu_batch_start(dev, prot, entry); | ||
| 412 | |||
| 413 | for_each_sg(sglist, sg, nelems, i) { | ||
| 414 | unsigned long paddr = SG_ENT_PHYS_ADDRESS(sg); | ||
| 415 | unsigned long slen = sg->length; | ||
| 416 | unsigned long this_npages; | ||
| 417 | |||
| 418 | this_npages = iommu_num_pages(paddr, slen); | ||
| 419 | |||
| 420 | sg->dma_address = dma_base | (paddr & ~IO_PAGE_MASK); | ||
| 421 | sg->dma_length = slen; | ||
| 422 | |||
| 423 | paddr &= IO_PAGE_MASK; | ||
| 424 | while (this_npages--) { | ||
| 425 | err = iommu_batch_add(paddr); | ||
| 426 | if (unlikely(err < 0L)) { | ||
| 427 | local_irq_restore(flags); | ||
| 428 | goto iommu_map_failed; | ||
| 429 | } | ||
| 430 | |||
| 431 | paddr += IO_PAGE_SIZE; | ||
| 432 | dma_base += IO_PAGE_SIZE; | ||
| 433 | } | ||
| 434 | } | ||
| 435 | |||
| 436 | err = iommu_batch_end(); | ||
| 437 | |||
| 438 | local_irq_restore(flags); | ||
| 439 | |||
| 523 | if (unlikely(err < 0L)) | 440 | if (unlikely(err < 0L)) |
| 524 | goto iommu_map_failed; | 441 | goto iommu_map_failed; |
| 525 | 442 | ||
| 526 | return used; | 443 | return nelems; |
| 527 | 444 | ||
| 528 | bad: | 445 | bad: |
| 529 | if (printk_ratelimit()) | 446 | if (printk_ratelimit()) |
| @@ -541,12 +458,11 @@ iommu_map_failed: | |||
| 541 | static void dma_4v_unmap_sg(struct device *dev, struct scatterlist *sglist, | 458 | static void dma_4v_unmap_sg(struct device *dev, struct scatterlist *sglist, |
| 542 | int nelems, enum dma_data_direction direction) | 459 | int nelems, enum dma_data_direction direction) |
| 543 | { | 460 | { |
| 461 | unsigned long flags, npages; | ||
| 544 | struct pci_pbm_info *pbm; | 462 | struct pci_pbm_info *pbm; |
| 463 | u32 devhandle, bus_addr; | ||
| 545 | struct iommu *iommu; | 464 | struct iommu *iommu; |
| 546 | unsigned long flags, i, npages; | ||
| 547 | struct scatterlist *sg, *sgprv; | ||
| 548 | long entry; | 465 | long entry; |
| 549 | u32 devhandle, bus_addr; | ||
| 550 | 466 | ||
| 551 | if (unlikely(direction == DMA_NONE)) { | 467 | if (unlikely(direction == DMA_NONE)) { |
| 552 | if (printk_ratelimit()) | 468 | if (printk_ratelimit()) |
| @@ -558,16 +474,8 @@ static void dma_4v_unmap_sg(struct device *dev, struct scatterlist *sglist, | |||
| 558 | devhandle = pbm->devhandle; | 474 | devhandle = pbm->devhandle; |
| 559 | 475 | ||
| 560 | bus_addr = sglist->dma_address & IO_PAGE_MASK; | 476 | bus_addr = sglist->dma_address & IO_PAGE_MASK; |
| 561 | sgprv = NULL; | ||
| 562 | for_each_sg(sglist, sg, nelems, i) { | ||
| 563 | if (sg->dma_length == 0) | ||
| 564 | break; | ||
| 565 | |||
| 566 | sgprv = sg; | ||
| 567 | } | ||
| 568 | 477 | ||
| 569 | npages = (IO_PAGE_ALIGN(sgprv->dma_address + sgprv->dma_length) - | 478 | npages = calc_npages(sglist, nelems); |
| 570 | bus_addr) >> IO_PAGE_SHIFT; | ||
| 571 | 479 | ||
| 572 | entry = ((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT); | 480 | entry = ((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT); |
| 573 | 481 | ||
diff --git a/include/asm-sparc64/io.h b/include/asm-sparc64/io.h index c299b853b5ba..b6ece223562d 100644 --- a/include/asm-sparc64/io.h +++ b/include/asm-sparc64/io.h | |||
| @@ -16,7 +16,7 @@ | |||
| 16 | /* BIO layer definitions. */ | 16 | /* BIO layer definitions. */ |
| 17 | extern unsigned long kern_base, kern_size; | 17 | extern unsigned long kern_base, kern_size; |
| 18 | #define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) | 18 | #define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) |
| 19 | #define BIO_VMERGE_BOUNDARY 8192 | 19 | #define BIO_VMERGE_BOUNDARY 0 |
| 20 | 20 | ||
| 21 | static inline u8 _inb(unsigned long addr) | 21 | static inline u8 _inb(unsigned long addr) |
| 22 | { | 22 | { |
