aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
authorScott Wood <scottwood@freescale.com>2014-08-08 19:40:42 -0400
committerScott Wood <scottwood@freescale.com>2014-09-03 18:58:21 -0400
commit1c98025c6c95bc057a25e2c6596de23288c68160 (patch)
tree37475259871168c9835b78ecd3d233f870945fa1 /arch/powerpc
parent78eb9094ca08a40b8f9d3e113a2b88e0b7dbad1d (diff)
powerpc: Dynamic DMA zone limits
Platform code can call limit_zone_pfn() to set appropriate limits for ZONE_DMA and ZONE_DMA32, and dma_direct_alloc_coherent() will select a suitable zone based on a device's mask and the pfn limits that platform code has configured. Signed-off-by: Scott Wood <scottwood@freescale.com> Cc: Shaohui Xie <Shaohui.Xie@freescale.com>
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/Kconfig4
-rw-r--r--arch/powerpc/include/asm/pgtable.h3
-rw-r--r--arch/powerpc/kernel/dma.c20
-rw-r--r--arch/powerpc/mm/mem.c61
4 files changed, 83 insertions, 5 deletions
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index bd6dd6ed3a9f..d003409b5f98 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -285,6 +285,10 @@ config PPC_EMULATE_SSTEP
285 bool 285 bool
286 default y if KPROBES || UPROBES || XMON || HAVE_HW_BREAKPOINT 286 default y if KPROBES || UPROBES || XMON || HAVE_HW_BREAKPOINT
287 287
288config ZONE_DMA32
289 bool
290 default y if PPC64
291
288source "init/Kconfig" 292source "init/Kconfig"
289 293
290source "kernel/Kconfig.freezer" 294source "kernel/Kconfig.freezer"
diff --git a/arch/powerpc/include/asm/pgtable.h b/arch/powerpc/include/asm/pgtable.h
index d98c1ecc3266..6d74167bb6bf 100644
--- a/arch/powerpc/include/asm/pgtable.h
+++ b/arch/powerpc/include/asm/pgtable.h
@@ -4,6 +4,7 @@
4 4
5#ifndef __ASSEMBLY__ 5#ifndef __ASSEMBLY__
6#include <linux/mmdebug.h> 6#include <linux/mmdebug.h>
7#include <linux/mmzone.h>
7#include <asm/processor.h> /* For TASK_SIZE */ 8#include <asm/processor.h> /* For TASK_SIZE */
8#include <asm/mmu.h> 9#include <asm/mmu.h>
9#include <asm/page.h> 10#include <asm/page.h>
@@ -281,6 +282,8 @@ extern unsigned long empty_zero_page[];
281 282
282extern pgd_t swapper_pg_dir[]; 283extern pgd_t swapper_pg_dir[];
283 284
285void limit_zone_pfn(enum zone_type zone, unsigned long max_pfn);
286int dma_pfn_limit_to_zone(u64 pfn_limit);
284extern void paging_init(void); 287extern void paging_init(void);
285 288
286/* 289/*
diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c
index ee78f6e49d64..dfd99ef8c63d 100644
--- a/arch/powerpc/kernel/dma.c
+++ b/arch/powerpc/kernel/dma.c
@@ -40,6 +40,26 @@ void *dma_direct_alloc_coherent(struct device *dev, size_t size,
40#else 40#else
41 struct page *page; 41 struct page *page;
42 int node = dev_to_node(dev); 42 int node = dev_to_node(dev);
43 u64 pfn = (dev->coherent_dma_mask >> PAGE_SHIFT) + 1;
44 int zone;
45
46 zone = dma_pfn_limit_to_zone(pfn);
47 if (zone < 0) {
48 dev_err(dev, "%s: No suitable zone for pfn %#llx\n",
49 __func__, pfn);
50 return NULL;
51 }
52
53 switch (zone) {
54 case ZONE_DMA:
55 flag |= GFP_DMA;
56 break;
57#ifdef CONFIG_ZONE_DMA32
58 case ZONE_DMA32:
59 flag |= GFP_DMA32;
60 break;
61#endif
62 };
43 63
44 /* ignore region specifiers */ 64 /* ignore region specifiers */
45 flag &= ~(__GFP_HIGHMEM); 65 flag &= ~(__GFP_HIGHMEM);
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index 2c8e90f5789e..687e7f7f7751 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -260,6 +260,54 @@ static int __init mark_nonram_nosave(void)
260 return 0; 260 return 0;
261} 261}
262 262
263static bool zone_limits_final;
264
265static unsigned long max_zone_pfns[MAX_NR_ZONES] = {
266 [0 ... MAX_NR_ZONES - 1] = ~0UL
267};
268
269/*
270 * Restrict the specified zone and all more restrictive zones
271 * to be below the specified pfn. May not be called after
272 * paging_init().
273 */
274void __init limit_zone_pfn(enum zone_type zone, unsigned long pfn_limit)
275{
276 int i;
277
278 if (WARN_ON(zone_limits_final))
279 return;
280
281 for (i = zone; i >= 0; i--) {
282 if (max_zone_pfns[i] > pfn_limit)
283 max_zone_pfns[i] = pfn_limit;
284 }
285}
286
287/*
288 * Find the least restrictive zone that is entirely below the
289 * specified pfn limit. Returns < 0 if no suitable zone is found.
290 *
291 * pfn_limit must be u64 because it can exceed 32 bits even on 32-bit
292 * systems -- the DMA limit can be higher than any possible real pfn.
293 */
294int dma_pfn_limit_to_zone(u64 pfn_limit)
295{
296 enum zone_type top_zone = ZONE_NORMAL;
297 int i;
298
299#ifdef CONFIG_HIGHMEM
300 top_zone = ZONE_HIGHMEM;
301#endif
302
303 for (i = top_zone; i >= 0; i--) {
304 if (max_zone_pfns[i] <= pfn_limit)
305 return i;
306 }
307
308 return -EPERM;
309}
310
263/* 311/*
264 * paging_init() sets up the page tables - in fact we've already done this. 312 * paging_init() sets up the page tables - in fact we've already done this.
265 */ 313 */
@@ -267,7 +315,7 @@ void __init paging_init(void)
267{ 315{
268 unsigned long long total_ram = memblock_phys_mem_size(); 316 unsigned long long total_ram = memblock_phys_mem_size();
269 phys_addr_t top_of_ram = memblock_end_of_DRAM(); 317 phys_addr_t top_of_ram = memblock_end_of_DRAM();
270 unsigned long max_zone_pfns[MAX_NR_ZONES]; 318 enum zone_type top_zone;
271 319
272#ifdef CONFIG_PPC32 320#ifdef CONFIG_PPC32
273 unsigned long v = __fix_to_virt(__end_of_fixed_addresses - 1); 321 unsigned long v = __fix_to_virt(__end_of_fixed_addresses - 1);
@@ -289,13 +337,16 @@ void __init paging_init(void)
289 (unsigned long long)top_of_ram, total_ram); 337 (unsigned long long)top_of_ram, total_ram);
290 printk(KERN_DEBUG "Memory hole size: %ldMB\n", 338 printk(KERN_DEBUG "Memory hole size: %ldMB\n",
291 (long int)((top_of_ram - total_ram) >> 20)); 339 (long int)((top_of_ram - total_ram) >> 20));
292 memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); 340
293#ifdef CONFIG_HIGHMEM 341#ifdef CONFIG_HIGHMEM
294 max_zone_pfns[ZONE_DMA] = lowmem_end_addr >> PAGE_SHIFT; 342 top_zone = ZONE_HIGHMEM;
295 max_zone_pfns[ZONE_HIGHMEM] = top_of_ram >> PAGE_SHIFT; 343 limit_zone_pfn(ZONE_NORMAL, lowmem_end_addr >> PAGE_SHIFT);
296#else 344#else
297 max_zone_pfns[ZONE_DMA] = top_of_ram >> PAGE_SHIFT; 345 top_zone = ZONE_NORMAL;
298#endif 346#endif
347
348 limit_zone_pfn(top_zone, top_of_ram >> PAGE_SHIFT);
349 zone_limits_final = true;
299 free_area_init_nodes(max_zone_pfns); 350 free_area_init_nodes(max_zone_pfns);
300 351
301 mark_nonram_nosave(); 352 mark_nonram_nosave();