aboutsummaryrefslogtreecommitdiffstats
path: root/mm/sparse.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/sparse.c')
-rw-r--r--mm/sparse.c51
1 files changed, 47 insertions, 4 deletions
diff --git a/mm/sparse.c b/mm/sparse.c
index 08f053218ee8..dff71f173ae9 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -8,6 +8,7 @@
8#include <linux/module.h> 8#include <linux/module.h>
9#include <linux/spinlock.h> 9#include <linux/spinlock.h>
10#include <linux/vmalloc.h> 10#include <linux/vmalloc.h>
11#include "internal.h"
11#include <asm/dma.h> 12#include <asm/dma.h>
12#include <asm/pgalloc.h> 13#include <asm/pgalloc.h>
13#include <asm/pgtable.h> 14#include <asm/pgtable.h>
@@ -376,6 +377,9 @@ static void __kfree_section_memmap(struct page *memmap, unsigned long nr_pages)
376{ 377{
377 return; /* XXX: Not implemented yet */ 378 return; /* XXX: Not implemented yet */
378} 379}
380static void free_map_bootmem(struct page *page, unsigned long nr_pages)
381{
382}
379#else 383#else
380static struct page *__kmalloc_section_memmap(unsigned long nr_pages) 384static struct page *__kmalloc_section_memmap(unsigned long nr_pages)
381{ 385{
@@ -413,17 +417,47 @@ static void __kfree_section_memmap(struct page *memmap, unsigned long nr_pages)
413 free_pages((unsigned long)memmap, 417 free_pages((unsigned long)memmap,
414 get_order(sizeof(struct page) * nr_pages)); 418 get_order(sizeof(struct page) * nr_pages));
415} 419}
420
421static void free_map_bootmem(struct page *page, unsigned long nr_pages)
422{
423 unsigned long maps_section_nr, removing_section_nr, i;
424 int magic;
425
426 for (i = 0; i < nr_pages; i++, page++) {
427 magic = atomic_read(&page->_mapcount);
428
429 BUG_ON(magic == NODE_INFO);
430
431 maps_section_nr = pfn_to_section_nr(page_to_pfn(page));
432 removing_section_nr = page->private;
433
434 /*
435 * When this function is called, the removing section is
436 * logical offlined state. This means all pages are isolated
437 * from page allocator. If removing section's memmap is placed
438 * on the same section, it must not be freed.
439 * If it is freed, page allocator may allocate it which will
440 * be removed physically soon.
441 */
442 if (maps_section_nr != removing_section_nr)
443 put_page_bootmem(page);
444 }
445}
416#endif /* CONFIG_SPARSEMEM_VMEMMAP */ 446#endif /* CONFIG_SPARSEMEM_VMEMMAP */
417 447
418static void free_section_usemap(struct page *memmap, unsigned long *usemap) 448static void free_section_usemap(struct page *memmap, unsigned long *usemap)
419{ 449{
450 struct page *usemap_page;
451 unsigned long nr_pages;
452
420 if (!usemap) 453 if (!usemap)
421 return; 454 return;
422 455
456 usemap_page = virt_to_page(usemap);
423 /* 457 /*
424 * Check to see if allocation came from hot-plug-add 458 * Check to see if allocation came from hot-plug-add
425 */ 459 */
426 if (PageSlab(virt_to_page(usemap))) { 460 if (PageSlab(usemap_page)) {
427 kfree(usemap); 461 kfree(usemap);
428 if (memmap) 462 if (memmap)
429 __kfree_section_memmap(memmap, PAGES_PER_SECTION); 463 __kfree_section_memmap(memmap, PAGES_PER_SECTION);
@@ -431,10 +465,19 @@ static void free_section_usemap(struct page *memmap, unsigned long *usemap)
431 } 465 }
432 466
433 /* 467 /*
434 * TODO: Allocations came from bootmem - how do I free up ? 468 * The usemap came from bootmem. This is packed with other usemaps
469 * on the section which has pgdat at boot time. Just keep it as is now.
435 */ 470 */
436 printk(KERN_WARNING "Not freeing up allocations from bootmem " 471
437 "- leaking memory\n"); 472 if (memmap) {
473 struct page *memmap_page;
474 memmap_page = virt_to_page(memmap);
475
476 nr_pages = PAGE_ALIGN(PAGES_PER_SECTION * sizeof(struct page))
477 >> PAGE_SHIFT;
478
479 free_map_bootmem(memmap_page, nr_pages);
480 }
438} 481}
439 482
440/* 483/*