diff options
Diffstat (limited to 'mm/sparse.c')
-rw-r--r-- | mm/sparse.c | 115 |
1 files changed, 106 insertions, 9 deletions
diff --git a/mm/sparse.c b/mm/sparse.c index 36511c7b5e2c..8ffc08990008 100644 --- a/mm/sparse.c +++ b/mm/sparse.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <asm/dma.h> | 12 | #include <asm/dma.h> |
13 | #include <asm/pgalloc.h> | 13 | #include <asm/pgalloc.h> |
14 | #include <asm/pgtable.h> | 14 | #include <asm/pgtable.h> |
15 | #include "internal.h" | ||
15 | 16 | ||
16 | /* | 17 | /* |
17 | * Permanent SPARSEMEM data: | 18 | * Permanent SPARSEMEM data: |
@@ -147,22 +148,41 @@ static inline int sparse_early_nid(struct mem_section *section) | |||
147 | return (section->section_mem_map >> SECTION_NID_SHIFT); | 148 | return (section->section_mem_map >> SECTION_NID_SHIFT); |
148 | } | 149 | } |
149 | 150 | ||
150 | /* Record a memory area against a node. */ | 151 | /* Validate the physical addressing limitations of the model */ |
151 | void __init memory_present(int nid, unsigned long start, unsigned long end) | 152 | void __meminit mminit_validate_memmodel_limits(unsigned long *start_pfn, |
153 | unsigned long *end_pfn) | ||
152 | { | 154 | { |
153 | unsigned long max_arch_pfn = 1UL << (MAX_PHYSMEM_BITS-PAGE_SHIFT); | 155 | unsigned long max_sparsemem_pfn = 1UL << (MAX_PHYSMEM_BITS-PAGE_SHIFT); |
154 | unsigned long pfn; | ||
155 | 156 | ||
156 | /* | 157 | /* |
157 | * Sanity checks - do not allow an architecture to pass | 158 | * Sanity checks - do not allow an architecture to pass |
158 | * in larger pfns than the maximum scope of sparsemem: | 159 | * in larger pfns than the maximum scope of sparsemem: |
159 | */ | 160 | */ |
160 | if (start >= max_arch_pfn) | 161 | if (*start_pfn > max_sparsemem_pfn) { |
161 | return; | 162 | mminit_dprintk(MMINIT_WARNING, "pfnvalidation", |
162 | if (end >= max_arch_pfn) | 163 | "Start of range %lu -> %lu exceeds SPARSEMEM max %lu\n", |
163 | end = max_arch_pfn; | 164 | *start_pfn, *end_pfn, max_sparsemem_pfn); |
165 | WARN_ON_ONCE(1); | ||
166 | *start_pfn = max_sparsemem_pfn; | ||
167 | *end_pfn = max_sparsemem_pfn; | ||
168 | } | ||
169 | |||
170 | if (*end_pfn > max_sparsemem_pfn) { | ||
171 | mminit_dprintk(MMINIT_WARNING, "pfnvalidation", | ||
172 | "End of range %lu -> %lu exceeds SPARSEMEM max %lu\n", | ||
173 | *start_pfn, *end_pfn, max_sparsemem_pfn); | ||
174 | WARN_ON_ONCE(1); | ||
175 | *end_pfn = max_sparsemem_pfn; | ||
176 | } | ||
177 | } | ||
178 | |||
179 | /* Record a memory area against a node. */ | ||
180 | void __init memory_present(int nid, unsigned long start, unsigned long end) | ||
181 | { | ||
182 | unsigned long pfn; | ||
164 | 183 | ||
165 | start &= PAGE_SECTION_MASK; | 184 | start &= PAGE_SECTION_MASK; |
185 | mminit_validate_memmodel_limits(&start, &end); | ||
166 | for (pfn = start; pfn < end; pfn += PAGES_PER_SECTION) { | 186 | for (pfn = start; pfn < end; pfn += PAGES_PER_SECTION) { |
167 | unsigned long section = pfn_to_section_nr(pfn); | 187 | unsigned long section = pfn_to_section_nr(pfn); |
168 | struct mem_section *ms; | 188 | struct mem_section *ms; |
@@ -187,6 +207,7 @@ unsigned long __init node_memmap_size_bytes(int nid, unsigned long start_pfn, | |||
187 | unsigned long pfn; | 207 | unsigned long pfn; |
188 | unsigned long nr_pages = 0; | 208 | unsigned long nr_pages = 0; |
189 | 209 | ||
210 | mminit_validate_memmodel_limits(&start_pfn, &end_pfn); | ||
190 | for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) { | 211 | for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) { |
191 | if (nid != early_pfn_to_nid(pfn)) | 212 | if (nid != early_pfn_to_nid(pfn)) |
192 | continue; | 213 | continue; |
@@ -248,16 +269,92 @@ static unsigned long *__kmalloc_section_usemap(void) | |||
248 | } | 269 | } |
249 | #endif /* CONFIG_MEMORY_HOTPLUG */ | 270 | #endif /* CONFIG_MEMORY_HOTPLUG */ |
250 | 271 | ||
272 | #ifdef CONFIG_MEMORY_HOTREMOVE | ||
273 | static unsigned long * __init | ||
274 | sparse_early_usemap_alloc_pgdat_section(struct pglist_data *pgdat) | ||
275 | { | ||
276 | unsigned long section_nr; | ||
277 | |||
278 | /* | ||
279 | * A page may contain usemaps for other sections preventing the | ||
280 | * page being freed and making a section unremovable while | ||
281 | * other sections referencing the usemap retmain active. Similarly, | ||
282 | * a pgdat can prevent a section being removed. If section A | ||
283 | * contains a pgdat and section B contains the usemap, both | ||
284 | * sections become inter-dependent. This allocates usemaps | ||
285 | * from the same section as the pgdat where possible to avoid | ||
286 | * this problem. | ||
287 | */ | ||
288 | section_nr = pfn_to_section_nr(__pa(pgdat) >> PAGE_SHIFT); | ||
289 | return alloc_bootmem_section(usemap_size(), section_nr); | ||
290 | } | ||
291 | |||
292 | static void __init check_usemap_section_nr(int nid, unsigned long *usemap) | ||
293 | { | ||
294 | unsigned long usemap_snr, pgdat_snr; | ||
295 | static unsigned long old_usemap_snr = NR_MEM_SECTIONS; | ||
296 | static unsigned long old_pgdat_snr = NR_MEM_SECTIONS; | ||
297 | struct pglist_data *pgdat = NODE_DATA(nid); | ||
298 | int usemap_nid; | ||
299 | |||
300 | usemap_snr = pfn_to_section_nr(__pa(usemap) >> PAGE_SHIFT); | ||
301 | pgdat_snr = pfn_to_section_nr(__pa(pgdat) >> PAGE_SHIFT); | ||
302 | if (usemap_snr == pgdat_snr) | ||
303 | return; | ||
304 | |||
305 | if (old_usemap_snr == usemap_snr && old_pgdat_snr == pgdat_snr) | ||
306 | /* skip redundant message */ | ||
307 | return; | ||
308 | |||
309 | old_usemap_snr = usemap_snr; | ||
310 | old_pgdat_snr = pgdat_snr; | ||
311 | |||
312 | usemap_nid = sparse_early_nid(__nr_to_section(usemap_snr)); | ||
313 | if (usemap_nid != nid) { | ||
314 | printk(KERN_INFO | ||
315 | "node %d must be removed before remove section %ld\n", | ||
316 | nid, usemap_snr); | ||
317 | return; | ||
318 | } | ||
319 | /* | ||
320 | * There is a circular dependency. | ||
321 | * Some platforms allow un-removable section because they will just | ||
322 | * gather other removable sections for dynamic partitioning. | ||
323 | * Just notify un-removable section's number here. | ||
324 | */ | ||
325 | printk(KERN_INFO "Section %ld and %ld (node %d)", usemap_snr, | ||
326 | pgdat_snr, nid); | ||
327 | printk(KERN_CONT | ||
328 | " have a circular dependency on usemap and pgdat allocations\n"); | ||
329 | } | ||
330 | #else | ||
331 | static unsigned long * __init | ||
332 | sparse_early_usemap_alloc_pgdat_section(struct pglist_data *pgdat) | ||
333 | { | ||
334 | return NULL; | ||
335 | } | ||
336 | |||
337 | static void __init check_usemap_section_nr(int nid, unsigned long *usemap) | ||
338 | { | ||
339 | } | ||
340 | #endif /* CONFIG_MEMORY_HOTREMOVE */ | ||
341 | |||
251 | static unsigned long *__init sparse_early_usemap_alloc(unsigned long pnum) | 342 | static unsigned long *__init sparse_early_usemap_alloc(unsigned long pnum) |
252 | { | 343 | { |
253 | unsigned long *usemap; | 344 | unsigned long *usemap; |
254 | struct mem_section *ms = __nr_to_section(pnum); | 345 | struct mem_section *ms = __nr_to_section(pnum); |
255 | int nid = sparse_early_nid(ms); | 346 | int nid = sparse_early_nid(ms); |
256 | 347 | ||
257 | usemap = alloc_bootmem_node(NODE_DATA(nid), usemap_size()); | 348 | usemap = sparse_early_usemap_alloc_pgdat_section(NODE_DATA(nid)); |
258 | if (usemap) | 349 | if (usemap) |
259 | return usemap; | 350 | return usemap; |
260 | 351 | ||
352 | usemap = alloc_bootmem_node(NODE_DATA(nid), usemap_size()); | ||
353 | if (usemap) { | ||
354 | check_usemap_section_nr(nid, usemap); | ||
355 | return usemap; | ||
356 | } | ||
357 | |||
261 | /* Stupid: suppress gcc warning for SPARSEMEM && !NUMA */ | 358 | /* Stupid: suppress gcc warning for SPARSEMEM && !NUMA */ |
262 | nid = 0; | 359 | nid = 0; |
263 | 360 | ||