aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiang Liu <liuj97@gmail.com>2013-07-03 18:03:11 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-07-03 19:07:33 -0400
commit7b4b2a0d6c8500350784beb83a6a55e60ea3bea3 (patch)
tree9d36d3bc9b94d613ed6b556a5f534bb9eec6f876
parent4f9f47745e948eca18bb97c82dbb4d53f2380086 (diff)
mm: accurately calculate zone->managed_pages for highmem zones
Commit "mm: introduce new field 'managed_pages' to struct zone" assumes that all highmem pages will be freed into the buddy system by function mem_init(). But that's not always true, some architectures may reserve some highmem pages during boot. For example PPC may allocate highmem pages for giagant HugeTLB pages, and several architectures have code to check PageReserved flag to exclude highmem pages allocated during boot when freeing highmem pages into the buddy system. So treat highmem pages in the same way as normal pages, that is to: 1) reset zone->managed_pages to zero in mem_init(). 2) recalculate managed_pages when freeing pages into the buddy system. Signed-off-by: Jiang Liu <jiang.liu@huawei.com> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Tejun Heo <tj@kernel.org> Cc: Joonsoo Kim <js1304@gmail.com> Cc: Yinghai Lu <yinghai@kernel.org> Cc: Mel Gorman <mel@csn.ul.ie> Cc: Minchan Kim <minchan@kernel.org> Cc: Kamezawa Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Cc: Marek Szyprowski <m.szyprowski@samsung.com> Cc: "Michael S. Tsirkin" <mst@redhat.com> Cc: <sworddragon2@aol.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Chris Metcalf <cmetcalf@tilera.com> Cc: David Howells <dhowells@redhat.com> Cc: Geert Uytterhoeven <geert@linux-m68k.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Jeremy Fitzhardinge <jeremy@goop.org> Cc: Jianguo Wu <wujianguo@huawei.com> Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> Cc: Michel Lespinasse <walken@google.com> Cc: Rik van Riel <riel@redhat.com> Cc: Rusty Russell <rusty@rustcorp.com.au> Cc: Tang Chen <tangchen@cn.fujitsu.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Wen Congyang <wency@cn.fujitsu.com> Cc: Will Deacon <will.deacon@arm.com> Cc: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com> Cc: Russell King <rmk@arm.linux.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/metag/mm/init.c6
-rw-r--r--arch/x86/mm/highmem_32.c6
-rw-r--r--include/linux/bootmem.h1
-rw-r--r--mm/bootmem.c32
-rw-r--r--mm/nobootmem.c30
-rw-r--r--mm/page_alloc.c1
6 files changed, 48 insertions, 28 deletions
diff --git a/arch/metag/mm/init.c b/arch/metag/mm/init.c
index 5e2238dd72e0..d7595f58fad5 100644
--- a/arch/metag/mm/init.c
+++ b/arch/metag/mm/init.c
@@ -380,6 +380,12 @@ void __init mem_init(void)
380 380
381#ifdef CONFIG_HIGHMEM 381#ifdef CONFIG_HIGHMEM
382 unsigned long tmp; 382 unsigned long tmp;
383
384 /*
385 * Explicitly reset zone->managed_pages because highmem pages are
386 * freed before calling free_all_bootmem_node();
387 */
388 reset_all_zones_managed_pages();
383 for (tmp = highstart_pfn; tmp < highend_pfn; tmp++) 389 for (tmp = highstart_pfn; tmp < highend_pfn; tmp++)
384 free_highmem_page(pfn_to_page(tmp)); 390 free_highmem_page(pfn_to_page(tmp));
385 num_physpages += totalhigh_pages; 391 num_physpages += totalhigh_pages;
diff --git a/arch/x86/mm/highmem_32.c b/arch/x86/mm/highmem_32.c
index 252b8f5489ba..4500142bc4aa 100644
--- a/arch/x86/mm/highmem_32.c
+++ b/arch/x86/mm/highmem_32.c
@@ -1,6 +1,7 @@
1#include <linux/highmem.h> 1#include <linux/highmem.h>
2#include <linux/module.h> 2#include <linux/module.h>
3#include <linux/swap.h> /* for totalram_pages */ 3#include <linux/swap.h> /* for totalram_pages */
4#include <linux/bootmem.h>
4 5
5void *kmap(struct page *page) 6void *kmap(struct page *page)
6{ 7{
@@ -121,6 +122,11 @@ void __init set_highmem_pages_init(void)
121 struct zone *zone; 122 struct zone *zone;
122 int nid; 123 int nid;
123 124
125 /*
126 * Explicitly reset zone->managed_pages because set_highmem_pages_init()
127 * is invoked before free_all_bootmem()
128 */
129 reset_all_zones_managed_pages();
124 for_each_zone(zone) { 130 for_each_zone(zone) {
125 unsigned long zone_start_pfn, zone_end_pfn; 131 unsigned long zone_start_pfn, zone_end_pfn;
126 132
diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h
index 5f0b0e1f7c08..0e48c3221d82 100644
--- a/include/linux/bootmem.h
+++ b/include/linux/bootmem.h
@@ -46,6 +46,7 @@ extern unsigned long init_bootmem(unsigned long addr, unsigned long memend);
46 46
47extern unsigned long free_all_bootmem_node(pg_data_t *pgdat); 47extern unsigned long free_all_bootmem_node(pg_data_t *pgdat);
48extern unsigned long free_all_bootmem(void); 48extern unsigned long free_all_bootmem(void);
49extern void reset_all_zones_managed_pages(void);
49 50
50extern void free_bootmem_node(pg_data_t *pgdat, 51extern void free_bootmem_node(pg_data_t *pgdat,
51 unsigned long addr, 52 unsigned long addr,
diff --git a/mm/bootmem.c b/mm/bootmem.c
index 2b0bcb019ec2..eb792323187b 100644
--- a/mm/bootmem.c
+++ b/mm/bootmem.c
@@ -241,20 +241,26 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata)
241 return count; 241 return count;
242} 242}
243 243
244static void reset_node_lowmem_managed_pages(pg_data_t *pgdat) 244static int reset_managed_pages_done __initdata;
245
246static inline void __init reset_node_managed_pages(pg_data_t *pgdat)
245{ 247{
246 struct zone *z; 248 struct zone *z;
247 249
248 /* 250 if (reset_managed_pages_done)
249 * In free_area_init_core(), highmem zone's managed_pages is set to 251 return;
250 * present_pages, and bootmem allocator doesn't allocate from highmem 252
251 * zones. So there's no need to recalculate managed_pages because all
252 * highmem pages will be managed by the buddy system. Here highmem
253 * zone also includes highmem movable zone.
254 */
255 for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++) 253 for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++)
256 if (!is_highmem(z)) 254 z->managed_pages = 0;
257 z->managed_pages = 0; 255}
256
257void __init reset_all_zones_managed_pages(void)
258{
259 struct pglist_data *pgdat;
260
261 for_each_online_pgdat(pgdat)
262 reset_node_managed_pages(pgdat);
263 reset_managed_pages_done = 1;
258} 264}
259 265
260/** 266/**
@@ -266,7 +272,7 @@ static void reset_node_lowmem_managed_pages(pg_data_t *pgdat)
266unsigned long __init free_all_bootmem_node(pg_data_t *pgdat) 272unsigned long __init free_all_bootmem_node(pg_data_t *pgdat)
267{ 273{
268 register_page_bootmem_info_node(pgdat); 274 register_page_bootmem_info_node(pgdat);
269 reset_node_lowmem_managed_pages(pgdat); 275 reset_node_managed_pages(pgdat);
270 return free_all_bootmem_core(pgdat->bdata); 276 return free_all_bootmem_core(pgdat->bdata);
271} 277}
272 278
@@ -279,10 +285,8 @@ unsigned long __init free_all_bootmem(void)
279{ 285{
280 unsigned long total_pages = 0; 286 unsigned long total_pages = 0;
281 bootmem_data_t *bdata; 287 bootmem_data_t *bdata;
282 struct pglist_data *pgdat;
283 288
284 for_each_online_pgdat(pgdat) 289 reset_all_zones_managed_pages();
285 reset_node_lowmem_managed_pages(pgdat);
286 290
287 list_for_each_entry(bdata, &bdata_list, list) 291 list_for_each_entry(bdata, &bdata_list, list)
288 total_pages += free_all_bootmem_core(bdata); 292 total_pages += free_all_bootmem_core(bdata);
diff --git a/mm/nobootmem.c b/mm/nobootmem.c
index bdd3fa2fc73b..0ae8d91365af 100644
--- a/mm/nobootmem.c
+++ b/mm/nobootmem.c
@@ -137,20 +137,25 @@ static unsigned long __init free_low_memory_core_early(void)
137 return count; 137 return count;
138} 138}
139 139
140static void reset_node_lowmem_managed_pages(pg_data_t *pgdat) 140static int reset_managed_pages_done __initdata;
141
142static inline void __init reset_node_managed_pages(pg_data_t *pgdat)
141{ 143{
142 struct zone *z; 144 struct zone *z;
143 145
144 /* 146 if (reset_managed_pages_done)
145 * In free_area_init_core(), highmem zone's managed_pages is set to 147 return;
146 * present_pages, and bootmem allocator doesn't allocate from highmem
147 * zones. So there's no need to recalculate managed_pages because all
148 * highmem pages will be managed by the buddy system. Here highmem
149 * zone also includes highmem movable zone.
150 */
151 for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++) 148 for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++)
152 if (!is_highmem(z)) 149 z->managed_pages = 0;
153 z->managed_pages = 0; 150}
151
152void __init reset_all_zones_managed_pages(void)
153{
154 struct pglist_data *pgdat;
155
156 for_each_online_pgdat(pgdat)
157 reset_node_managed_pages(pgdat);
158 reset_managed_pages_done = 1;
154} 159}
155 160
156/** 161/**
@@ -160,10 +165,7 @@ static void reset_node_lowmem_managed_pages(pg_data_t *pgdat)
160 */ 165 */
161unsigned long __init free_all_bootmem(void) 166unsigned long __init free_all_bootmem(void)
162{ 167{
163 struct pglist_data *pgdat; 168 reset_all_zones_managed_pages();
164
165 for_each_online_pgdat(pgdat)
166 reset_node_lowmem_managed_pages(pgdat);
167 169
168 /* 170 /*
169 * We need to use MAX_NUMNODES instead of NODE_DATA(0)->node_id 171 * We need to use MAX_NUMNODES instead of NODE_DATA(0)->node_id
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index f22542f6dc12..22438eba00b6 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -5232,6 +5232,7 @@ void free_highmem_page(struct page *page)
5232{ 5232{
5233 __free_reserved_page(page); 5233 __free_reserved_page(page);
5234 totalram_pages++; 5234 totalram_pages++;
5235 page_zone(page)->managed_pages++;
5235 totalhigh_pages++; 5236 totalhigh_pages++;
5236} 5237}
5237#endif 5238#endif