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 | { |