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 | ||
