diff options
author | Muli Ben-Yehuda <mulix@mulix.org> | 2006-01-11 16:44:42 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-11 22:04:55 -0500 |
commit | 17a941d854a3f7b0bb916fdeee4c9ffdcc19d8d3 (patch) | |
tree | b6b3b55318336adf769bf57141a01a9defbbb202 /arch/x86_64/kernel/pci-nommu.c | |
parent | 8a6fdd3e912e0ce6f723431d66baf704bf8a1d26 (diff) |
[PATCH] x86_64: Use function pointers to call DMA mapping functions
AK: I hacked Muli's original patch a lot and there were a lot
of changes - all bugs are probably to blame on me now.
There were also some changes in the fall back behaviour
for swiotlb - in particular it doesn't try to use GFP_DMA
now anymore. Also all DMA mapping operations use the
same core dma_alloc_coherent code with proper fallbacks now.
And various other changes and cleanups.
Known problems: iommu=force swiotlb=force together breaks
needs more testing.
This patch cleans up x86_64's DMA mapping dispatching code. Right now
we have three possible IOMMU types: AGP GART, swiotlb and nommu, and
in the future we will also have Xen's x86_64 swiotlb and other HW
IOMMUs for x86_64. In order to support all of them cleanly, this
patch:
- introduces a struct dma_mapping_ops with function pointers for each
of the DMA mapping operations of gart (AMD HW IOMMU), swiotlb
(software IOMMU) and nommu (no IOMMU).
- gets rid of:
if (swiotlb)
return swiotlb_xxx();
- PCI_DMA_BUS_IS_PHYS is now checked against the dma_ops being set
This makes swiotlb faster by avoiding double copying in some cases.
Signed-Off-By: Muli Ben-Yehuda <mulix@mulix.org>
Signed-Off-By: Jon D. Mason <jdmason@us.ibm.com>
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/x86_64/kernel/pci-nommu.c')
-rw-r--r-- | arch/x86_64/kernel/pci-nommu.c | 144 |
1 files changed, 74 insertions, 70 deletions
diff --git a/arch/x86_64/kernel/pci-nommu.c b/arch/x86_64/kernel/pci-nommu.c index 5a981dca87ff..3209adac3272 100644 --- a/arch/x86_64/kernel/pci-nommu.c +++ b/arch/x86_64/kernel/pci-nommu.c | |||
@@ -6,89 +6,93 @@ | |||
6 | #include <linux/string.h> | 6 | #include <linux/string.h> |
7 | #include <asm/proto.h> | 7 | #include <asm/proto.h> |
8 | #include <asm/processor.h> | 8 | #include <asm/processor.h> |
9 | #include <asm/dma.h> | ||
9 | 10 | ||
10 | int iommu_merge = 0; | 11 | static int |
11 | EXPORT_SYMBOL(iommu_merge); | 12 | check_addr(char *name, struct device *hwdev, dma_addr_t bus, size_t size) |
12 | |||
13 | dma_addr_t bad_dma_address; | ||
14 | EXPORT_SYMBOL(bad_dma_address); | ||
15 | |||
16 | int iommu_bio_merge = 0; | ||
17 | EXPORT_SYMBOL(iommu_bio_merge); | ||
18 | |||
19 | int iommu_sac_force = 0; | ||
20 | EXPORT_SYMBOL(iommu_sac_force); | ||
21 | |||
22 | /* | ||
23 | * Dummy IO MMU functions | ||
24 | */ | ||
25 | |||
26 | void *dma_alloc_coherent(struct device *hwdev, size_t size, | ||
27 | dma_addr_t *dma_handle, gfp_t gfp) | ||
28 | { | 13 | { |
29 | void *ret; | 14 | if (hwdev && bus + size > *hwdev->dma_mask) { |
30 | u64 mask; | 15 | printk(KERN_ERR |
31 | int order = get_order(size); | 16 | "nommu_%s: overflow %Lx+%lu of device mask %Lx\n", |
32 | 17 | name, (long long)bus, size, (long long)*hwdev->dma_mask); | |
33 | if (hwdev) | 18 | return 0; |
34 | mask = hwdev->coherent_dma_mask & *hwdev->dma_mask; | ||
35 | else | ||
36 | mask = 0xffffffff; | ||
37 | for (;;) { | ||
38 | ret = (void *)__get_free_pages(gfp, order); | ||
39 | if (ret == NULL) | ||
40 | return NULL; | ||
41 | *dma_handle = virt_to_bus(ret); | ||
42 | if ((*dma_handle & ~mask) == 0) | ||
43 | break; | ||
44 | free_pages((unsigned long)ret, order); | ||
45 | if (gfp & GFP_DMA) | ||
46 | return NULL; | ||
47 | gfp |= GFP_DMA; | ||
48 | } | 19 | } |
20 | return 1; | ||
21 | } | ||
49 | 22 | ||
50 | memset(ret, 0, size); | 23 | static dma_addr_t |
51 | return ret; | 24 | nommu_map_single(struct device *hwdev, void *ptr, size_t size, |
25 | int direction) | ||
26 | { | ||
27 | dma_addr_t bus = virt_to_bus(ptr); | ||
28 | if (!check_addr("map_single", hwdev, bus, size)) | ||
29 | return bad_dma_address; | ||
30 | return bus; | ||
52 | } | 31 | } |
53 | EXPORT_SYMBOL(dma_alloc_coherent); | ||
54 | 32 | ||
55 | void dma_free_coherent(struct device *hwdev, size_t size, | 33 | void nommu_unmap_single(struct device *dev, dma_addr_t addr,size_t size, |
56 | void *vaddr, dma_addr_t dma_handle) | 34 | int direction) |
57 | { | 35 | { |
58 | free_pages((unsigned long)vaddr, get_order(size)); | ||
59 | } | 36 | } |
60 | EXPORT_SYMBOL(dma_free_coherent); | ||
61 | 37 | ||
62 | int dma_supported(struct device *hwdev, u64 mask) | 38 | /* Map a set of buffers described by scatterlist in streaming |
39 | * mode for DMA. This is the scatter-gather version of the | ||
40 | * above pci_map_single interface. Here the scatter gather list | ||
41 | * elements are each tagged with the appropriate dma address | ||
42 | * and length. They are obtained via sg_dma_{address,length}(SG). | ||
43 | * | ||
44 | * NOTE: An implementation may be able to use a smaller number of | ||
45 | * DMA address/length pairs than there are SG table elements. | ||
46 | * (for example via virtual mapping capabilities) | ||
47 | * The routine returns the number of addr/length pairs actually | ||
48 | * used, at most nents. | ||
49 | * | ||
50 | * Device ownership issues as mentioned above for pci_map_single are | ||
51 | * the same here. | ||
52 | */ | ||
53 | int nommu_map_sg(struct device *hwdev, struct scatterlist *sg, | ||
54 | int nents, int direction) | ||
63 | { | 55 | { |
64 | /* | 56 | int i; |
65 | * we fall back to GFP_DMA when the mask isn't all 1s, | ||
66 | * so we can't guarantee allocations that must be | ||
67 | * within a tighter range than GFP_DMA.. | ||
68 | * RED-PEN this won't work for pci_map_single. Caller has to | ||
69 | * use GFP_DMA in the first place. | ||
70 | */ | ||
71 | if (mask < 0x00ffffff) | ||
72 | return 0; | ||
73 | 57 | ||
74 | return 1; | 58 | BUG_ON(direction == DMA_NONE); |
75 | } | 59 | for (i = 0; i < nents; i++ ) { |
76 | EXPORT_SYMBOL(dma_supported); | 60 | struct scatterlist *s = &sg[i]; |
61 | BUG_ON(!s->page); | ||
62 | s->dma_address = virt_to_bus(page_address(s->page) +s->offset); | ||
63 | if (!check_addr("map_sg", hwdev, s->dma_address, s->length)) | ||
64 | return 0; | ||
65 | s->dma_length = s->length; | ||
66 | } | ||
67 | return nents; | ||
68 | } | ||
77 | 69 | ||
78 | int dma_get_cache_alignment(void) | 70 | /* Unmap a set of streaming mode DMA translations. |
71 | * Again, cpu read rules concerning calls here are the same as for | ||
72 | * pci_unmap_single() above. | ||
73 | */ | ||
74 | void nommu_unmap_sg(struct device *dev, struct scatterlist *sg, | ||
75 | int nents, int dir) | ||
79 | { | 76 | { |
80 | return boot_cpu_data.x86_clflush_size; | ||
81 | } | 77 | } |
82 | EXPORT_SYMBOL(dma_get_cache_alignment); | ||
83 | 78 | ||
84 | static int __init check_ram(void) | 79 | struct dma_mapping_ops nommu_dma_ops = { |
85 | { | 80 | .map_single = nommu_map_single, |
86 | if (end_pfn >= 0xffffffff>>PAGE_SHIFT) { | 81 | .unmap_single = nommu_unmap_single, |
87 | printk( | 82 | .map_sg = nommu_map_sg, |
88 | KERN_ERR "WARNING more than 4GB of memory but IOMMU not compiled in.\n" | 83 | .unmap_sg = nommu_unmap_sg, |
89 | KERN_ERR "WARNING 32bit PCI may malfunction.\n"); | 84 | .is_phys = 1, |
90 | } | 85 | }; |
91 | return 0; | ||
92 | } | ||
93 | __initcall(check_ram); | ||
94 | 86 | ||
87 | void __init no_iommu_init(void) | ||
88 | { | ||
89 | if (dma_ops) | ||
90 | return; | ||
91 | printk(KERN_INFO "PCI-DMA: Disabling IOMMU.\n"); | ||
92 | dma_ops = &nommu_dma_ops; | ||
93 | if (end_pfn > MAX_DMA32_PFN) { | ||
94 | printk(KERN_ERR | ||
95 | "WARNING more than 4GB of memory but IOMMU disabled.\n" | ||
96 | KERN_ERR "WARNING 32bit PCI may malfunction.\n"); | ||
97 | } | ||
98 | } | ||