diff options
author | Andi Kleen <ak@suse.de> | 2005-11-05 11:25:53 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-11-14 22:55:13 -0500 |
commit | a2f1b424900715ed9d1699c3bb88a434a2b42bc0 (patch) | |
tree | 8ef440f840656365166ff2d71aa445c224c53546 /arch | |
parent | 56720367cd89ef5265f39da2d674c5b92cd4cd87 (diff) |
[PATCH] x86_64: Add 4GB DMA32 zone
Add a new 4GB GFP_DMA32 zone between the GFP_DMA and GFP_NORMAL zones.
As a bit of historical background: when the x86-64 port
was originally designed we had some discussion if we should
use a 16MB DMA zone like i386 or a 4GB DMA zone like IA64 or
both. Both was ruled out at this point because it was in early
2.4 when VM is still quite shakey and had bad troubles even
dealing with one DMA zone. We settled on the 16MB DMA zone mainly
because we worried about older soundcards and the floppy.
But this has always caused problems since then because
device drivers had trouble getting enough DMA able memory. These days
the VM works much better and the wide use of NUMA has proven
it can deal with many zones successfully.
So this patch adds both zones.
This helps drivers who need a lot of memory below 4GB because
their hardware is not accessing more (graphic drivers - proprietary
and free ones, video frame buffer drivers, sound drivers etc.).
Previously they could only use IOMMU+16MB GFP_DMA, which
was not enough memory.
Another common problem is that hardware who has full memory
addressing for >4GB misses it for some control structures in memory
(like transmit rings or other metadata). They tended to allocate memory
in the 16MB GFP_DMA or the IOMMU/swiotlb then using pci_alloc_consistent,
but that can tie up a lot of precious 16MB GFPDMA/IOMMU/swiotlb memory
(even on AMD systems the IOMMU tends to be quite small) especially if you have
many devices. With the new zone pci_alloc_consistent can just put
this stuff into memory below 4GB which works better.
One argument was still if the zone should be 4GB or 2GB. The main
motivation for 2GB would be an unnamed not so unpopular hardware
raid controller (mostly found in older machines from a particular four letter
company) who has a strange 2GB restriction in firmware. But
that one works ok with swiotlb/IOMMU anyways, so it doesn't really
need GFP_DMA32. I chose 4GB to be compatible with IA64 and because
it seems to be the most common restriction.
The new zone is so far added only for x86-64.
For other architectures who don't set up this
new zone nothing changes. Architectures can set a compatibility
define in Kconfig CONFIG_DMA_IS_DMA32 that will define GFP_DMA32
as GFP_DMA. Otherwise it's a nop because on 32bit architectures
it's normally not needed because GFP_NORMAL (=0) is DMA able
enough.
One problem is still that GFP_DMA means different things on different
architectures. e.g. some drivers used to have #ifdef ia64 use GFP_DMA
(trusting it to be 4GB) #elif __x86_64__ (use other hacks like
the swiotlb because 16MB is not enough) ... . This was quite
ugly and is now obsolete.
These should be now converted to use GFP_DMA32 unconditionally. I haven't done
this yet. Or best only use pci_alloc_consistent/dma_alloc_coherent
which will use GFP_DMA32 transparently.
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86_64/mm/init.c | 65 | ||||
-rw-r--r-- | arch/x86_64/mm/numa.c | 25 |
2 files changed, 47 insertions, 43 deletions
diff --git a/arch/x86_64/mm/init.c b/arch/x86_64/mm/init.c index e60a1a848de8..a1ad4cc423a7 100644 --- a/arch/x86_64/mm/init.c +++ b/arch/x86_64/mm/init.c | |||
@@ -318,32 +318,51 @@ void zap_low_mappings(void) | |||
318 | flush_tlb_all(); | 318 | flush_tlb_all(); |
319 | } | 319 | } |
320 | 320 | ||
321 | /* Compute zone sizes for the DMA and DMA32 zones in a node. */ | ||
322 | __init void | ||
323 | size_zones(unsigned long *z, unsigned long *h, | ||
324 | unsigned long start_pfn, unsigned long end_pfn) | ||
325 | { | ||
326 | int i; | ||
327 | unsigned long w; | ||
328 | |||
329 | for (i = 0; i < MAX_NR_ZONES; i++) | ||
330 | z[i] = 0; | ||
331 | |||
332 | if (start_pfn < MAX_DMA_PFN) | ||
333 | z[ZONE_DMA] = MAX_DMA_PFN - start_pfn; | ||
334 | if (start_pfn < MAX_DMA32_PFN) { | ||
335 | unsigned long dma32_pfn = MAX_DMA32_PFN; | ||
336 | if (dma32_pfn > end_pfn) | ||
337 | dma32_pfn = end_pfn; | ||
338 | z[ZONE_DMA32] = dma32_pfn - start_pfn; | ||
339 | } | ||
340 | z[ZONE_NORMAL] = end_pfn - start_pfn; | ||
341 | |||
342 | /* Remove lower zones from higher ones. */ | ||
343 | w = 0; | ||
344 | for (i = 0; i < MAX_NR_ZONES; i++) { | ||
345 | if (z[i]) | ||
346 | z[i] -= w; | ||
347 | w += z[i]; | ||
348 | } | ||
349 | |||
350 | /* Compute holes */ | ||
351 | w = 0; | ||
352 | for (i = 0; i < MAX_NR_ZONES; i++) { | ||
353 | unsigned long s = w; | ||
354 | w += z[i]; | ||
355 | h[i] = e820_hole_size(s, w); | ||
356 | } | ||
357 | } | ||
358 | |||
321 | #ifndef CONFIG_NUMA | 359 | #ifndef CONFIG_NUMA |
322 | void __init paging_init(void) | 360 | void __init paging_init(void) |
323 | { | 361 | { |
324 | { | 362 | unsigned long zones[MAX_NR_ZONES], holes[MAX_NR_ZONES]; |
325 | unsigned long zones_size[MAX_NR_ZONES]; | 363 | size_zones(zones, holes, 0, end_pfn); |
326 | unsigned long holes[MAX_NR_ZONES]; | 364 | free_area_init_node(0, NODE_DATA(0), zones, |
327 | unsigned int max_dma; | 365 | __pa(PAGE_OFFSET) >> PAGE_SHIFT, holes); |
328 | |||
329 | memset(zones_size, 0, sizeof(zones_size)); | ||
330 | memset(holes, 0, sizeof(holes)); | ||
331 | |||
332 | max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; | ||
333 | |||
334 | if (end_pfn < max_dma) { | ||
335 | zones_size[ZONE_DMA] = end_pfn; | ||
336 | holes[ZONE_DMA] = e820_hole_size(0, end_pfn); | ||
337 | } else { | ||
338 | zones_size[ZONE_DMA] = max_dma; | ||
339 | holes[ZONE_DMA] = e820_hole_size(0, max_dma); | ||
340 | zones_size[ZONE_NORMAL] = end_pfn - max_dma; | ||
341 | holes[ZONE_NORMAL] = e820_hole_size(max_dma, end_pfn); | ||
342 | } | ||
343 | free_area_init_node(0, NODE_DATA(0), zones_size, | ||
344 | __pa(PAGE_OFFSET) >> PAGE_SHIFT, holes); | ||
345 | } | ||
346 | return; | ||
347 | } | 366 | } |
348 | #endif | 367 | #endif |
349 | 368 | ||
diff --git a/arch/x86_64/mm/numa.c b/arch/x86_64/mm/numa.c index 214803821001..18e86e2eac2d 100644 --- a/arch/x86_64/mm/numa.c +++ b/arch/x86_64/mm/numa.c | |||
@@ -132,29 +132,14 @@ void __init setup_node_zones(int nodeid) | |||
132 | unsigned long start_pfn, end_pfn; | 132 | unsigned long start_pfn, end_pfn; |
133 | unsigned long zones[MAX_NR_ZONES]; | 133 | unsigned long zones[MAX_NR_ZONES]; |
134 | unsigned long holes[MAX_NR_ZONES]; | 134 | unsigned long holes[MAX_NR_ZONES]; |
135 | unsigned long dma_end_pfn; | ||
136 | 135 | ||
137 | memset(zones, 0, sizeof(unsigned long) * MAX_NR_ZONES); | 136 | start_pfn = node_start_pfn(nodeid); |
138 | memset(holes, 0, sizeof(unsigned long) * MAX_NR_ZONES); | 137 | end_pfn = node_end_pfn(nodeid); |
139 | 138 | ||
140 | start_pfn = node_start_pfn(nodeid); | 139 | Dprintk(KERN_INFO "setting up node %d %lx-%lx\n", |
141 | end_pfn = node_end_pfn(nodeid); | 140 | nodeid, start_pfn, end_pfn); |
142 | 141 | ||
143 | Dprintk(KERN_INFO "setting up node %d %lx-%lx\n", nodeid, start_pfn, end_pfn); | 142 | size_zones(zones, holes, start_pfn, end_pfn); |
144 | |||
145 | /* All nodes > 0 have a zero length zone DMA */ | ||
146 | dma_end_pfn = __pa(MAX_DMA_ADDRESS) >> PAGE_SHIFT; | ||
147 | if (start_pfn < dma_end_pfn) { | ||
148 | zones[ZONE_DMA] = dma_end_pfn - start_pfn; | ||
149 | holes[ZONE_DMA] = e820_hole_size(start_pfn, dma_end_pfn); | ||
150 | zones[ZONE_NORMAL] = end_pfn - dma_end_pfn; | ||
151 | holes[ZONE_NORMAL] = e820_hole_size(dma_end_pfn, end_pfn); | ||
152 | |||
153 | } else { | ||
154 | zones[ZONE_NORMAL] = end_pfn - start_pfn; | ||
155 | holes[ZONE_NORMAL] = e820_hole_size(start_pfn, end_pfn); | ||
156 | } | ||
157 | |||
158 | free_area_init_node(nodeid, NODE_DATA(nodeid), zones, | 143 | free_area_init_node(nodeid, NODE_DATA(nodeid), zones, |
159 | start_pfn, holes); | 144 | start_pfn, holes); |
160 | } | 145 | } |