diff options
Diffstat (limited to 'mm/memory_hotplug.c')
-rw-r--r-- | mm/memory_hotplug.c | 126 |
1 files changed, 122 insertions, 4 deletions
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 841a077d5aeb..ea4038838b0a 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/memory_hotplug.h> | 21 | #include <linux/memory_hotplug.h> |
22 | #include <linux/highmem.h> | 22 | #include <linux/highmem.h> |
23 | #include <linux/vmalloc.h> | 23 | #include <linux/vmalloc.h> |
24 | #include <linux/ioport.h> | ||
24 | 25 | ||
25 | #include <asm/tlbflush.h> | 26 | #include <asm/tlbflush.h> |
26 | 27 | ||
@@ -126,6 +127,9 @@ int online_pages(unsigned long pfn, unsigned long nr_pages) | |||
126 | unsigned long i; | 127 | unsigned long i; |
127 | unsigned long flags; | 128 | unsigned long flags; |
128 | unsigned long onlined_pages = 0; | 129 | unsigned long onlined_pages = 0; |
130 | struct resource res; | ||
131 | u64 section_end; | ||
132 | unsigned long start_pfn; | ||
129 | struct zone *zone; | 133 | struct zone *zone; |
130 | int need_zonelists_rebuild = 0; | 134 | int need_zonelists_rebuild = 0; |
131 | 135 | ||
@@ -148,10 +152,27 @@ int online_pages(unsigned long pfn, unsigned long nr_pages) | |||
148 | if (!populated_zone(zone)) | 152 | if (!populated_zone(zone)) |
149 | need_zonelists_rebuild = 1; | 153 | need_zonelists_rebuild = 1; |
150 | 154 | ||
151 | for (i = 0; i < nr_pages; i++) { | 155 | res.start = (u64)pfn << PAGE_SHIFT; |
152 | struct page *page = pfn_to_page(pfn + i); | 156 | res.end = res.start + ((u64)nr_pages << PAGE_SHIFT) - 1; |
153 | online_page(page); | 157 | res.flags = IORESOURCE_MEM; /* we just need system ram */ |
154 | onlined_pages++; | 158 | section_end = res.end; |
159 | |||
160 | while (find_next_system_ram(&res) >= 0) { | ||
161 | start_pfn = (unsigned long)(res.start >> PAGE_SHIFT); | ||
162 | nr_pages = (unsigned long) | ||
163 | ((res.end + 1 - res.start) >> PAGE_SHIFT); | ||
164 | |||
165 | if (PageReserved(pfn_to_page(start_pfn))) { | ||
166 | /* this region's page is not onlined now */ | ||
167 | for (i = 0; i < nr_pages; i++) { | ||
168 | struct page *page = pfn_to_page(start_pfn + i); | ||
169 | online_page(page); | ||
170 | onlined_pages++; | ||
171 | } | ||
172 | } | ||
173 | |||
174 | res.start = res.end + 1; | ||
175 | res.end = section_end; | ||
155 | } | 176 | } |
156 | zone->present_pages += onlined_pages; | 177 | zone->present_pages += onlined_pages; |
157 | zone->zone_pgdat->node_present_pages += onlined_pages; | 178 | zone->zone_pgdat->node_present_pages += onlined_pages; |
@@ -163,3 +184,100 @@ int online_pages(unsigned long pfn, unsigned long nr_pages) | |||
163 | vm_total_pages = nr_free_pagecache_pages(); | 184 | vm_total_pages = nr_free_pagecache_pages(); |
164 | return 0; | 185 | return 0; |
165 | } | 186 | } |
187 | |||
188 | static pg_data_t *hotadd_new_pgdat(int nid, u64 start) | ||
189 | { | ||
190 | struct pglist_data *pgdat; | ||
191 | unsigned long zones_size[MAX_NR_ZONES] = {0}; | ||
192 | unsigned long zholes_size[MAX_NR_ZONES] = {0}; | ||
193 | unsigned long start_pfn = start >> PAGE_SHIFT; | ||
194 | |||
195 | pgdat = arch_alloc_nodedata(nid); | ||
196 | if (!pgdat) | ||
197 | return NULL; | ||
198 | |||
199 | arch_refresh_nodedata(nid, pgdat); | ||
200 | |||
201 | /* we can use NODE_DATA(nid) from here */ | ||
202 | |||
203 | /* init node's zones as empty zones, we don't have any present pages.*/ | ||
204 | free_area_init_node(nid, pgdat, zones_size, start_pfn, zholes_size); | ||
205 | |||
206 | return pgdat; | ||
207 | } | ||
208 | |||
209 | static void rollback_node_hotadd(int nid, pg_data_t *pgdat) | ||
210 | { | ||
211 | arch_refresh_nodedata(nid, NULL); | ||
212 | arch_free_nodedata(pgdat); | ||
213 | return; | ||
214 | } | ||
215 | |||
216 | /* add this memory to iomem resource */ | ||
217 | static void register_memory_resource(u64 start, u64 size) | ||
218 | { | ||
219 | struct resource *res; | ||
220 | |||
221 | res = kzalloc(sizeof(struct resource), GFP_KERNEL); | ||
222 | BUG_ON(!res); | ||
223 | |||
224 | res->name = "System RAM"; | ||
225 | res->start = start; | ||
226 | res->end = start + size - 1; | ||
227 | res->flags = IORESOURCE_MEM; | ||
228 | if (request_resource(&iomem_resource, res) < 0) { | ||
229 | printk("System RAM resource %llx - %llx cannot be added\n", | ||
230 | (unsigned long long)res->start, (unsigned long long)res->end); | ||
231 | kfree(res); | ||
232 | } | ||
233 | } | ||
234 | |||
235 | |||
236 | |||
237 | int add_memory(int nid, u64 start, u64 size) | ||
238 | { | ||
239 | pg_data_t *pgdat = NULL; | ||
240 | int new_pgdat = 0; | ||
241 | int ret; | ||
242 | |||
243 | if (!node_online(nid)) { | ||
244 | pgdat = hotadd_new_pgdat(nid, start); | ||
245 | if (!pgdat) | ||
246 | return -ENOMEM; | ||
247 | new_pgdat = 1; | ||
248 | ret = kswapd_run(nid); | ||
249 | if (ret) | ||
250 | goto error; | ||
251 | } | ||
252 | |||
253 | /* call arch's memory hotadd */ | ||
254 | ret = arch_add_memory(nid, start, size); | ||
255 | |||
256 | if (ret < 0) | ||
257 | goto error; | ||
258 | |||
259 | /* we online node here. we can't roll back from here. */ | ||
260 | node_set_online(nid); | ||
261 | |||
262 | if (new_pgdat) { | ||
263 | ret = register_one_node(nid); | ||
264 | /* | ||
265 | * If sysfs file of new node can't create, cpu on the node | ||
266 | * can't be hot-added. There is no rollback way now. | ||
267 | * So, check by BUG_ON() to catch it reluctantly.. | ||
268 | */ | ||
269 | BUG_ON(ret); | ||
270 | } | ||
271 | |||
272 | /* register this memory as resource */ | ||
273 | register_memory_resource(start, size); | ||
274 | |||
275 | return ret; | ||
276 | error: | ||
277 | /* rollback pgdat allocation and others */ | ||
278 | if (new_pgdat) | ||
279 | rollback_node_hotadd(nid, pgdat); | ||
280 | |||
281 | return ret; | ||
282 | } | ||
283 | EXPORT_SYMBOL_GPL(add_memory); | ||