aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc64/kernel/iommu.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@huronp11.davemloft.net>2008-02-08 21:05:46 -0500
committerDavid S. Miller <davem@davemloft.net>2008-02-09 06:15:04 -0500
commitd284142cbad66832d5072a0aebeca7bd9ca841b7 (patch)
treee5c5ad6271b3a61e28f1767b744e0696af0cd1a4 /arch/sparc64/kernel/iommu.c
parent19814ea24e9d80583504e336340ab4590841b0b1 (diff)
[SPARC64]: IOMMU allocations using iommu-helper layer.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc64/kernel/iommu.c')
-rw-r--r--arch/sparc64/kernel/iommu.c125
1 files changed, 81 insertions, 44 deletions
diff --git a/arch/sparc64/kernel/iommu.c b/arch/sparc64/kernel/iommu.c
index 5623a4d59dff..90a5907080a1 100644
--- a/arch/sparc64/kernel/iommu.c
+++ b/arch/sparc64/kernel/iommu.c
@@ -1,6 +1,6 @@
1/* iommu.c: Generic sparc64 IOMMU support. 1/* iommu.c: Generic sparc64 IOMMU support.
2 * 2 *
3 * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net) 3 * Copyright (C) 1999, 2007, 2008 David S. Miller (davem@davemloft.net)
4 * Copyright (C) 1999, 2000 Jakub Jelinek (jakub@redhat.com) 4 * Copyright (C) 1999, 2000 Jakub Jelinek (jakub@redhat.com)
5 */ 5 */
6 6
@@ -10,6 +10,7 @@
10#include <linux/device.h> 10#include <linux/device.h>
11#include <linux/dma-mapping.h> 11#include <linux/dma-mapping.h>
12#include <linux/errno.h> 12#include <linux/errno.h>
13#include <linux/iommu-helper.h>
13 14
14#ifdef CONFIG_PCI 15#ifdef CONFIG_PCI
15#include <linux/pci.h> 16#include <linux/pci.h>
@@ -41,7 +42,7 @@
41 "i" (ASI_PHYS_BYPASS_EC_E)) 42 "i" (ASI_PHYS_BYPASS_EC_E))
42 43
43/* Must be invoked under the IOMMU lock. */ 44/* Must be invoked under the IOMMU lock. */
44static void __iommu_flushall(struct iommu *iommu) 45static void iommu_flushall(struct iommu *iommu)
45{ 46{
46 if (iommu->iommu_flushinv) { 47 if (iommu->iommu_flushinv) {
47 iommu_write(iommu->iommu_flushinv, ~(u64)0); 48 iommu_write(iommu->iommu_flushinv, ~(u64)0);
@@ -83,54 +84,91 @@ static inline void iopte_make_dummy(struct iommu *iommu, iopte_t *iopte)
83 iopte_val(*iopte) = val; 84 iopte_val(*iopte) = val;
84} 85}
85 86
86/* Based largely upon the ppc64 iommu allocator. */ 87/* Based almost entirely upon the ppc64 iommu allocator. If you use the 'handle'
87static long arena_alloc(struct iommu *iommu, unsigned long npages) 88 * facility it must all be done in one pass while under the iommu lock.
89 *
90 * On sun4u platforms, we only flush the IOMMU once every time we've passed
91 * over the entire page table doing allocations. Therefore we only ever advance
92 * the hint and cannot backtrack it.
93 */
94unsigned long iommu_range_alloc(struct device *dev,
95 struct iommu *iommu,
96 unsigned long npages,
97 unsigned long *handle)
88{ 98{
99 unsigned long n, end, start, limit, boundary_size;
89 struct iommu_arena *arena = &iommu->arena; 100 struct iommu_arena *arena = &iommu->arena;
90 unsigned long n, i, start, end, limit; 101 int pass = 0;
91 int pass; 102
103 /* This allocator was derived from x86_64's bit string search */
104
105 /* Sanity check */
106 if (unlikely(npages == 0)) {
107 if (printk_ratelimit())
108 WARN_ON(1);
109 return DMA_ERROR_CODE;
110 }
111
112 if (handle && *handle)
113 start = *handle;
114 else
115 start = arena->hint;
92 116
93 limit = arena->limit; 117 limit = arena->limit;
94 start = arena->hint;
95 pass = 0;
96 118
97again: 119 /* The case below can happen if we have a small segment appended
98 n = find_next_zero_bit(arena->map, limit, start); 120 * to a large, or when the previous alloc was at the very end of
99 end = n + npages; 121 * the available space. If so, go back to the beginning and flush.
100 if (unlikely(end >= limit)) { 122 */
123 if (start >= limit) {
124 start = 0;
125 if (iommu->flush_all)
126 iommu->flush_all(iommu);
127 }
128
129 again:
130
131 if (dev)
132 boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1,
133 1 << IO_PAGE_SHIFT);
134 else
135 boundary_size = ALIGN(1UL << 32, 1 << IO_PAGE_SHIFT);
136
137 n = iommu_area_alloc(arena->map, limit, start, npages, 0,
138 boundary_size >> IO_PAGE_SHIFT, 0);
139 if (n == -1) {
101 if (likely(pass < 1)) { 140 if (likely(pass < 1)) {
102 limit = start; 141 /* First failure, rescan from the beginning. */
103 start = 0; 142 start = 0;
104 __iommu_flushall(iommu); 143 if (iommu->flush_all)
144 iommu->flush_all(iommu);
105 pass++; 145 pass++;
106 goto again; 146 goto again;
107 } else { 147 } else {
108 /* Scanned the whole thing, give up. */ 148 /* Second failure, give up */
109 return -1; 149 return DMA_ERROR_CODE;
110 } 150 }
111 } 151 }
112 152
113 for (i = n; i < end; i++) { 153 end = n + npages;
114 if (test_bit(i, arena->map)) {
115 start = i + 1;
116 goto again;
117 }
118 }
119
120 for (i = n; i < end; i++)
121 __set_bit(i, arena->map);
122 154
123 arena->hint = end; 155 arena->hint = end;
124 156
157 /* Update handle for SG allocations */
158 if (handle)
159 *handle = end;
160
125 return n; 161 return n;
126} 162}
127 163
128static void arena_free(struct iommu_arena *arena, unsigned long base, unsigned long npages) 164void iommu_range_free(struct iommu *iommu, dma_addr_t dma_addr, unsigned long npages)
129{ 165{
130 unsigned long i; 166 struct iommu_arena *arena = &iommu->arena;
167 unsigned long entry;
131 168
132 for (i = base; i < (base + npages); i++) 169 entry = (dma_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT;
133 __clear_bit(i, arena->map); 170
171 iommu_area_free(arena->map, entry, npages);
134} 172}
135 173
136int iommu_table_init(struct iommu *iommu, int tsbsize, 174int iommu_table_init(struct iommu *iommu, int tsbsize,
@@ -156,6 +194,9 @@ int iommu_table_init(struct iommu *iommu, int tsbsize,
156 } 194 }
157 iommu->arena.limit = num_tsb_entries; 195 iommu->arena.limit = num_tsb_entries;
158 196
197 if (tlb_type != hypervisor)
198 iommu->flush_all = iommu_flushall;
199
159 /* Allocate and initialize the dummy page which we 200 /* Allocate and initialize the dummy page which we
160 * set inactive IO PTEs to point to. 201 * set inactive IO PTEs to point to.
161 */ 202 */
@@ -192,22 +233,18 @@ out_free_map:
192 return -ENOMEM; 233 return -ENOMEM;
193} 234}
194 235
195static inline iopte_t *alloc_npages(struct iommu *iommu, unsigned long npages) 236static inline iopte_t *alloc_npages(struct device *dev, struct iommu *iommu,
237 unsigned long npages)
196{ 238{
197 long entry; 239 unsigned long entry;
198 240
199 entry = arena_alloc(iommu, npages); 241 entry = iommu_range_alloc(dev, iommu, npages, NULL);
200 if (unlikely(entry < 0)) 242 if (unlikely(entry == DMA_ERROR_CODE))
201 return NULL; 243 return NULL;
202 244
203 return iommu->page_table + entry; 245 return iommu->page_table + entry;
204} 246}
205 247
206static inline void free_npages(struct iommu *iommu, dma_addr_t base, unsigned long npages)
207{
208 arena_free(&iommu->arena, base >> IO_PAGE_SHIFT, npages);
209}
210
211static int iommu_alloc_ctx(struct iommu *iommu) 248static int iommu_alloc_ctx(struct iommu *iommu)
212{ 249{
213 int lowest = iommu->ctx_lowest_free; 250 int lowest = iommu->ctx_lowest_free;
@@ -258,7 +295,7 @@ static void *dma_4u_alloc_coherent(struct device *dev, size_t size,
258 iommu = dev->archdata.iommu; 295 iommu = dev->archdata.iommu;
259 296
260 spin_lock_irqsave(&iommu->lock, flags); 297 spin_lock_irqsave(&iommu->lock, flags);
261 iopte = alloc_npages(iommu, size >> IO_PAGE_SHIFT); 298 iopte = alloc_npages(dev, iommu, size >> IO_PAGE_SHIFT);
262 spin_unlock_irqrestore(&iommu->lock, flags); 299 spin_unlock_irqrestore(&iommu->lock, flags);
263 300
264 if (unlikely(iopte == NULL)) { 301 if (unlikely(iopte == NULL)) {
@@ -296,7 +333,7 @@ static void dma_4u_free_coherent(struct device *dev, size_t size,
296 333
297 spin_lock_irqsave(&iommu->lock, flags); 334 spin_lock_irqsave(&iommu->lock, flags);
298 335
299 free_npages(iommu, dvma - iommu->page_table_map_base, npages); 336 iommu_range_free(iommu, dvma, npages);
300 337
301 spin_unlock_irqrestore(&iommu->lock, flags); 338 spin_unlock_irqrestore(&iommu->lock, flags);
302 339
@@ -327,7 +364,7 @@ static dma_addr_t dma_4u_map_single(struct device *dev, void *ptr, size_t sz,
327 npages >>= IO_PAGE_SHIFT; 364 npages >>= IO_PAGE_SHIFT;
328 365
329 spin_lock_irqsave(&iommu->lock, flags); 366 spin_lock_irqsave(&iommu->lock, flags);
330 base = alloc_npages(iommu, npages); 367 base = alloc_npages(dev, iommu, npages);
331 ctx = 0; 368 ctx = 0;
332 if (iommu->iommu_ctxflush) 369 if (iommu->iommu_ctxflush)
333 ctx = iommu_alloc_ctx(iommu); 370 ctx = iommu_alloc_ctx(iommu);
@@ -465,7 +502,7 @@ static void dma_4u_unmap_single(struct device *dev, dma_addr_t bus_addr,
465 for (i = 0; i < npages; i++) 502 for (i = 0; i < npages; i++)
466 iopte_make_dummy(iommu, base + i); 503 iopte_make_dummy(iommu, base + i);
467 504
468 free_npages(iommu, bus_addr - iommu->page_table_map_base, npages); 505 iommu_range_free(iommu, bus_addr, npages);
469 506
470 iommu_free_ctx(iommu, ctx); 507 iommu_free_ctx(iommu, ctx);
471 508
@@ -503,7 +540,7 @@ static int dma_4u_map_sg(struct device *dev, struct scatterlist *sglist,
503 540
504 spin_lock_irqsave(&iommu->lock, flags); 541 spin_lock_irqsave(&iommu->lock, flags);
505 542
506 base = alloc_npages(iommu, npages); 543 base = alloc_npages(dev, iommu, npages);
507 ctx = 0; 544 ctx = 0;
508 if (iommu->iommu_ctxflush) 545 if (iommu->iommu_ctxflush)
509 ctx = iommu_alloc_ctx(iommu); 546 ctx = iommu_alloc_ctx(iommu);
@@ -592,7 +629,7 @@ static void dma_4u_unmap_sg(struct device *dev, struct scatterlist *sglist,
592 for (i = 0; i < npages; i++) 629 for (i = 0; i < npages; i++)
593 iopte_make_dummy(iommu, base + i); 630 iopte_make_dummy(iommu, base + i);
594 631
595 free_npages(iommu, bus_addr - iommu->page_table_map_base, npages); 632 iommu_range_free(iommu, bus_addr, npages);
596 633
597 iommu_free_ctx(iommu, ctx); 634 iommu_free_ctx(iommu, ctx);
598 635