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