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