diff options
| -rw-r--r-- | include/linux/memory_hotplug.h | 27 | ||||
| -rw-r--r-- | include/linux/mmzone.h | 1 | ||||
| -rw-r--r-- | mm/bootmem.c | 1 | ||||
| -rw-r--r-- | mm/memory_hotplug.c | 99 | ||||
| -rw-r--r-- | mm/sparse.c | 3 |
5 files changed, 128 insertions, 3 deletions
diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index aca9c65f8d08..73e358612eaf 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h | |||
| @@ -11,6 +11,15 @@ struct pglist_data; | |||
| 11 | struct mem_section; | 11 | struct mem_section; |
| 12 | 12 | ||
| 13 | #ifdef CONFIG_MEMORY_HOTPLUG | 13 | #ifdef CONFIG_MEMORY_HOTPLUG |
| 14 | |||
| 15 | /* | ||
| 16 | * Magic number for free bootmem. | ||
| 17 | * The normal smallest mapcount is -1. Here is smaller value than it. | ||
| 18 | */ | ||
| 19 | #define SECTION_INFO 0xfffffffe | ||
| 20 | #define MIX_INFO 0xfffffffd | ||
| 21 | #define NODE_INFO 0xfffffffc | ||
| 22 | |||
| 14 | /* | 23 | /* |
| 15 | * pgdat resizing functions | 24 | * pgdat resizing functions |
| 16 | */ | 25 | */ |
| @@ -145,6 +154,18 @@ static inline void arch_refresh_nodedata(int nid, pg_data_t *pgdat) | |||
| 145 | #endif /* CONFIG_NUMA */ | 154 | #endif /* CONFIG_NUMA */ |
| 146 | #endif /* CONFIG_HAVE_ARCH_NODEDATA_EXTENSION */ | 155 | #endif /* CONFIG_HAVE_ARCH_NODEDATA_EXTENSION */ |
| 147 | 156 | ||
| 157 | #ifdef CONFIG_SPARSEMEM_VMEMMAP | ||
| 158 | static inline void register_page_bootmem_info_node(struct pglist_data *pgdat) | ||
| 159 | { | ||
| 160 | } | ||
| 161 | static inline void put_page_bootmem(struct page *page) | ||
| 162 | { | ||
| 163 | } | ||
| 164 | #else | ||
| 165 | extern void register_page_bootmem_info_node(struct pglist_data *pgdat); | ||
| 166 | extern void put_page_bootmem(struct page *page); | ||
| 167 | #endif | ||
| 168 | |||
| 148 | #else /* ! CONFIG_MEMORY_HOTPLUG */ | 169 | #else /* ! CONFIG_MEMORY_HOTPLUG */ |
| 149 | /* | 170 | /* |
| 150 | * Stub functions for when hotplug is off | 171 | * Stub functions for when hotplug is off |
| @@ -172,6 +193,10 @@ static inline int mhp_notimplemented(const char *func) | |||
| 172 | return -ENOSYS; | 193 | return -ENOSYS; |
| 173 | } | 194 | } |
| 174 | 195 | ||
| 196 | static inline void register_page_bootmem_info_node(struct pglist_data *pgdat) | ||
| 197 | { | ||
| 198 | } | ||
| 199 | |||
| 175 | #endif /* ! CONFIG_MEMORY_HOTPLUG */ | 200 | #endif /* ! CONFIG_MEMORY_HOTPLUG */ |
| 176 | 201 | ||
| 177 | extern int add_memory(int nid, u64 start, u64 size); | 202 | extern int add_memory(int nid, u64 start, u64 size); |
| @@ -180,5 +205,7 @@ extern int remove_memory(u64 start, u64 size); | |||
| 180 | extern int sparse_add_one_section(struct zone *zone, unsigned long start_pfn, | 205 | extern int sparse_add_one_section(struct zone *zone, unsigned long start_pfn, |
| 181 | int nr_pages); | 206 | int nr_pages); |
| 182 | extern void sparse_remove_one_section(struct zone *zone, struct mem_section *ms); | 207 | extern void sparse_remove_one_section(struct zone *zone, struct mem_section *ms); |
| 208 | extern struct page *sparse_decode_mem_map(unsigned long coded_mem_map, | ||
| 209 | unsigned long pnum); | ||
| 183 | 210 | ||
| 184 | #endif /* __LINUX_MEMORY_HOTPLUG_H */ | 211 | #endif /* __LINUX_MEMORY_HOTPLUG_H */ |
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index c3828497f41d..aad98003176f 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h | |||
| @@ -896,6 +896,7 @@ static inline struct mem_section *__nr_to_section(unsigned long nr) | |||
| 896 | return &mem_section[SECTION_NR_TO_ROOT(nr)][nr & SECTION_ROOT_MASK]; | 896 | return &mem_section[SECTION_NR_TO_ROOT(nr)][nr & SECTION_ROOT_MASK]; |
| 897 | } | 897 | } |
| 898 | extern int __section_nr(struct mem_section* ms); | 898 | extern int __section_nr(struct mem_section* ms); |
| 899 | extern unsigned long usemap_size(void); | ||
| 899 | 900 | ||
| 900 | /* | 901 | /* |
| 901 | * We use the lower bits of the mem_map pointer to store | 902 | * We use the lower bits of the mem_map pointer to store |
diff --git a/mm/bootmem.c b/mm/bootmem.c index b6791646143e..369624d2789c 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c | |||
| @@ -461,6 +461,7 @@ void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, | |||
| 461 | 461 | ||
| 462 | unsigned long __init free_all_bootmem_node(pg_data_t *pgdat) | 462 | unsigned long __init free_all_bootmem_node(pg_data_t *pgdat) |
| 463 | { | 463 | { |
| 464 | register_page_bootmem_info_node(pgdat); | ||
| 464 | return free_all_bootmem_core(pgdat); | 465 | return free_all_bootmem_core(pgdat); |
| 465 | } | 466 | } |
| 466 | 467 | ||
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index c8b3ca79de2d..cba36ef0d506 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c | |||
| @@ -58,8 +58,105 @@ static void release_memory_resource(struct resource *res) | |||
| 58 | return; | 58 | return; |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | |||
| 62 | #ifdef CONFIG_MEMORY_HOTPLUG_SPARSE | 61 | #ifdef CONFIG_MEMORY_HOTPLUG_SPARSE |
| 62 | #ifndef CONFIG_SPARSEMEM_VMEMMAP | ||
| 63 | static void get_page_bootmem(unsigned long info, struct page *page, int magic) | ||
| 64 | { | ||
| 65 | atomic_set(&page->_mapcount, magic); | ||
| 66 | SetPagePrivate(page); | ||
| 67 | set_page_private(page, info); | ||
| 68 | atomic_inc(&page->_count); | ||
| 69 | } | ||
| 70 | |||
| 71 | void put_page_bootmem(struct page *page) | ||
| 72 | { | ||
| 73 | int magic; | ||
| 74 | |||
| 75 | magic = atomic_read(&page->_mapcount); | ||
| 76 | BUG_ON(magic >= -1); | ||
| 77 | |||
| 78 | if (atomic_dec_return(&page->_count) == 1) { | ||
| 79 | ClearPagePrivate(page); | ||
| 80 | set_page_private(page, 0); | ||
| 81 | reset_page_mapcount(page); | ||
| 82 | __free_pages_bootmem(page, 0); | ||
| 83 | } | ||
| 84 | |||
| 85 | } | ||
| 86 | |||
| 87 | void register_page_bootmem_info_section(unsigned long start_pfn) | ||
| 88 | { | ||
| 89 | unsigned long *usemap, mapsize, section_nr, i; | ||
| 90 | struct mem_section *ms; | ||
| 91 | struct page *page, *memmap; | ||
| 92 | |||
| 93 | if (!pfn_valid(start_pfn)) | ||
| 94 | return; | ||
| 95 | |||
| 96 | section_nr = pfn_to_section_nr(start_pfn); | ||
| 97 | ms = __nr_to_section(section_nr); | ||
| 98 | |||
| 99 | /* Get section's memmap address */ | ||
| 100 | memmap = sparse_decode_mem_map(ms->section_mem_map, section_nr); | ||
| 101 | |||
| 102 | /* | ||
| 103 | * Get page for the memmap's phys address | ||
| 104 | * XXX: need more consideration for sparse_vmemmap... | ||
| 105 | */ | ||
| 106 | page = virt_to_page(memmap); | ||
| 107 | mapsize = sizeof(struct page) * PAGES_PER_SECTION; | ||
| 108 | mapsize = PAGE_ALIGN(mapsize) >> PAGE_SHIFT; | ||
| 109 | |||
| 110 | /* remember memmap's page */ | ||
| 111 | for (i = 0; i < mapsize; i++, page++) | ||
| 112 | get_page_bootmem(section_nr, page, SECTION_INFO); | ||
| 113 | |||
| 114 | usemap = __nr_to_section(section_nr)->pageblock_flags; | ||
| 115 | page = virt_to_page(usemap); | ||
| 116 | |||
| 117 | mapsize = PAGE_ALIGN(usemap_size()) >> PAGE_SHIFT; | ||
| 118 | |||
| 119 | for (i = 0; i < mapsize; i++, page++) | ||
| 120 | get_page_bootmem(section_nr, page, MIX_INFO); | ||
| 121 | |||
| 122 | } | ||
| 123 | |||
| 124 | void register_page_bootmem_info_node(struct pglist_data *pgdat) | ||
| 125 | { | ||
| 126 | unsigned long i, pfn, end_pfn, nr_pages; | ||
| 127 | int node = pgdat->node_id; | ||
| 128 | struct page *page; | ||
| 129 | struct zone *zone; | ||
| 130 | |||
| 131 | nr_pages = PAGE_ALIGN(sizeof(struct pglist_data)) >> PAGE_SHIFT; | ||
| 132 | page = virt_to_page(pgdat); | ||
| 133 | |||
| 134 | for (i = 0; i < nr_pages; i++, page++) | ||
| 135 | get_page_bootmem(node, page, NODE_INFO); | ||
| 136 | |||
| 137 | zone = &pgdat->node_zones[0]; | ||
| 138 | for (; zone < pgdat->node_zones + MAX_NR_ZONES - 1; zone++) { | ||
| 139 | if (zone->wait_table) { | ||
| 140 | nr_pages = zone->wait_table_hash_nr_entries | ||
| 141 | * sizeof(wait_queue_head_t); | ||
| 142 | nr_pages = PAGE_ALIGN(nr_pages) >> PAGE_SHIFT; | ||
| 143 | page = virt_to_page(zone->wait_table); | ||
| 144 | |||
| 145 | for (i = 0; i < nr_pages; i++, page++) | ||
| 146 | get_page_bootmem(node, page, NODE_INFO); | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | pfn = pgdat->node_start_pfn; | ||
| 151 | end_pfn = pfn + pgdat->node_spanned_pages; | ||
| 152 | |||
| 153 | /* register_section info */ | ||
| 154 | for (; pfn < end_pfn; pfn += PAGES_PER_SECTION) | ||
| 155 | register_page_bootmem_info_section(pfn); | ||
| 156 | |||
| 157 | } | ||
| 158 | #endif /* !CONFIG_SPARSEMEM_VMEMMAP */ | ||
| 159 | |||
| 63 | static int __add_zone(struct zone *zone, unsigned long phys_start_pfn) | 160 | static int __add_zone(struct zone *zone, unsigned long phys_start_pfn) |
| 64 | { | 161 | { |
| 65 | struct pglist_data *pgdat = zone->zone_pgdat; | 162 | struct pglist_data *pgdat = zone->zone_pgdat; |
diff --git a/mm/sparse.c b/mm/sparse.c index 186a85bf7912..8903c484389a 100644 --- a/mm/sparse.c +++ b/mm/sparse.c | |||
| @@ -210,7 +210,6 @@ static unsigned long sparse_encode_mem_map(struct page *mem_map, unsigned long p | |||
| 210 | /* | 210 | /* |
| 211 | * Decode mem_map from the coded memmap | 211 | * Decode mem_map from the coded memmap |
| 212 | */ | 212 | */ |
| 213 | static | ||
| 214 | struct page *sparse_decode_mem_map(unsigned long coded_mem_map, unsigned long pnum) | 213 | struct page *sparse_decode_mem_map(unsigned long coded_mem_map, unsigned long pnum) |
| 215 | { | 214 | { |
| 216 | /* mask off the extra low bits of information */ | 215 | /* mask off the extra low bits of information */ |
| @@ -233,7 +232,7 @@ static int __meminit sparse_init_one_section(struct mem_section *ms, | |||
| 233 | return 1; | 232 | return 1; |
| 234 | } | 233 | } |
| 235 | 234 | ||
| 236 | static unsigned long usemap_size(void) | 235 | unsigned long usemap_size(void) |
| 237 | { | 236 | { |
| 238 | unsigned long size_bytes; | 237 | unsigned long size_bytes; |
| 239 | size_bytes = roundup(SECTION_BLOCKFLAGS_BITS, 8) / 8; | 238 | size_bytes = roundup(SECTION_BLOCKFLAGS_BITS, 8) / 8; |
