Blame view

mm/memory_hotplug.c 23.9 KB
3947be196   Dave Hansen   [PATCH] memory ho...
1
2
3
4
5
  /*
   *  linux/mm/memory_hotplug.c
   *
   *  Copyright (C)
   */
3947be196   Dave Hansen   [PATCH] memory ho...
6
7
8
9
10
11
12
  #include <linux/stddef.h>
  #include <linux/mm.h>
  #include <linux/swap.h>
  #include <linux/interrupt.h>
  #include <linux/pagemap.h>
  #include <linux/bootmem.h>
  #include <linux/compiler.h>
b95f1b31b   Paul Gortmaker   mm: Map most file...
13
  #include <linux/export.h>
3947be196   Dave Hansen   [PATCH] memory ho...
14
  #include <linux/pagevec.h>
2d1d43f6a   Chandra Seetharaman   [PATCH] call mm/p...
15
  #include <linux/writeback.h>
3947be196   Dave Hansen   [PATCH] memory ho...
16
17
18
19
20
21
22
  #include <linux/slab.h>
  #include <linux/sysctl.h>
  #include <linux/cpu.h>
  #include <linux/memory.h>
  #include <linux/memory_hotplug.h>
  #include <linux/highmem.h>
  #include <linux/vmalloc.h>
0a5470390   KAMEZAWA Hiroyuki   [PATCH] register ...
23
  #include <linux/ioport.h>
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
24
25
26
  #include <linux/delay.h>
  #include <linux/migrate.h>
  #include <linux/page-isolation.h>
71088785c   Badari Pulavarty   mm: cleanup to ma...
27
  #include <linux/pfn.h>
6ad696d2c   Andi Kleen   mm: allow memory ...
28
  #include <linux/suspend.h>
6d9c285a6   KOSAKI Motohiro   mm: move inc_zone...
29
  #include <linux/mm_inline.h>
d96ae5309   akpm@linux-foundation.org   memory-hotplug: c...
30
  #include <linux/firmware-map.h>
3947be196   Dave Hansen   [PATCH] memory ho...
31
32
  
  #include <asm/tlbflush.h>
1e5ad9a3b   Adrian Bunk   mm/memory_hotplug...
33
  #include "internal.h"
9d0ad8ca4   Daniel Kiper   mm: extend memory...
34
35
36
37
38
39
40
41
42
43
  /*
   * online_page_callback contains pointer to current page onlining function.
   * Initially it is generic_online_page(). If it is required it could be
   * changed by calling set_online_page_callback() for callback registration
   * and restore_online_page_callback() for generic callback restore.
   */
  
  static void generic_online_page(struct page *page);
  
  static online_page_callback_t online_page_callback = generic_online_page;
20d6c96b5   KOSAKI Motohiro   mem-hotplug: intr...
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
  DEFINE_MUTEX(mem_hotplug_mutex);
  
  void lock_memory_hotplug(void)
  {
  	mutex_lock(&mem_hotplug_mutex);
  
  	/* for exclusive hibernation if CONFIG_HIBERNATION=y */
  	lock_system_sleep();
  }
  
  void unlock_memory_hotplug(void)
  {
  	unlock_system_sleep();
  	mutex_unlock(&mem_hotplug_mutex);
  }
45e0b78b0   Keith Mannthey   [PATCH] hot-add-m...
59
60
61
62
63
64
65
66
67
68
  /* add this memory to iomem resource */
  static struct resource *register_memory_resource(u64 start, u64 size)
  {
  	struct resource *res;
  	res = kzalloc(sizeof(struct resource), GFP_KERNEL);
  	BUG_ON(!res);
  
  	res->name = "System RAM";
  	res->start = start;
  	res->end = start + size - 1;
887c3cb18   Yasunori Goto   Add IORESOUCE_BUS...
69
  	res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
45e0b78b0   Keith Mannthey   [PATCH] hot-add-m...
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
  	if (request_resource(&iomem_resource, res) < 0) {
  		printk("System RAM resource %llx - %llx cannot be added
  ",
  		(unsigned long long)res->start, (unsigned long long)res->end);
  		kfree(res);
  		res = NULL;
  	}
  	return res;
  }
  
  static void release_memory_resource(struct resource *res)
  {
  	if (!res)
  		return;
  	release_resource(res);
  	kfree(res);
  	return;
  }
53947027a   Keith Mannthey   [PATCH] hot-add-m...
88
  #ifdef CONFIG_MEMORY_HOTPLUG_SPARSE
047532787   Yasunori Goto   memory hotplug: r...
89
  #ifndef CONFIG_SPARSEMEM_VMEMMAP
5f24ce5fd   Andrea Arcangeli   thp: remove PG_buddy
90
91
  static void get_page_bootmem(unsigned long info,  struct page *page,
  			     unsigned long type)
047532787   Yasunori Goto   memory hotplug: r...
92
  {
5f24ce5fd   Andrea Arcangeli   thp: remove PG_buddy
93
  	page->lru.next = (struct list_head *) type;
047532787   Yasunori Goto   memory hotplug: r...
94
95
96
97
  	SetPagePrivate(page);
  	set_page_private(page, info);
  	atomic_inc(&page->_count);
  }
23ce932a5   Rakib Mullick   mm: fix section m...
98
99
100
  /* reference to __meminit __free_pages_bootmem is valid
   * so use __ref to tell modpost not to generate a warning */
  void __ref put_page_bootmem(struct page *page)
047532787   Yasunori Goto   memory hotplug: r...
101
  {
5f24ce5fd   Andrea Arcangeli   thp: remove PG_buddy
102
  	unsigned long type;
047532787   Yasunori Goto   memory hotplug: r...
103

5f24ce5fd   Andrea Arcangeli   thp: remove PG_buddy
104
105
106
  	type = (unsigned long) page->lru.next;
  	BUG_ON(type < MEMORY_HOTPLUG_MIN_BOOTMEM_TYPE ||
  	       type > MEMORY_HOTPLUG_MAX_BOOTMEM_TYPE);
047532787   Yasunori Goto   memory hotplug: r...
107
108
109
110
  
  	if (atomic_dec_return(&page->_count) == 1) {
  		ClearPagePrivate(page);
  		set_page_private(page, 0);
5f24ce5fd   Andrea Arcangeli   thp: remove PG_buddy
111
  		INIT_LIST_HEAD(&page->lru);
047532787   Yasunori Goto   memory hotplug: r...
112
113
114
115
  		__free_pages_bootmem(page, 0);
  	}
  
  }
d92bc3185   Adrian Bunk   mm: make register...
116
  static void register_page_bootmem_info_section(unsigned long start_pfn)
047532787   Yasunori Goto   memory hotplug: r...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
  {
  	unsigned long *usemap, mapsize, section_nr, i;
  	struct mem_section *ms;
  	struct page *page, *memmap;
  
  	if (!pfn_valid(start_pfn))
  		return;
  
  	section_nr = pfn_to_section_nr(start_pfn);
  	ms = __nr_to_section(section_nr);
  
  	/* Get section's memmap address */
  	memmap = sparse_decode_mem_map(ms->section_mem_map, section_nr);
  
  	/*
  	 * Get page for the memmap's phys address
  	 * XXX: need more consideration for sparse_vmemmap...
  	 */
  	page = virt_to_page(memmap);
  	mapsize = sizeof(struct page) * PAGES_PER_SECTION;
  	mapsize = PAGE_ALIGN(mapsize) >> PAGE_SHIFT;
  
  	/* remember memmap's page */
  	for (i = 0; i < mapsize; i++, page++)
  		get_page_bootmem(section_nr, page, SECTION_INFO);
  
  	usemap = __nr_to_section(section_nr)->pageblock_flags;
  	page = virt_to_page(usemap);
  
  	mapsize = PAGE_ALIGN(usemap_size()) >> PAGE_SHIFT;
  
  	for (i = 0; i < mapsize; i++, page++)
af370fb8c   Yasunori Goto   memory hotplug: s...
149
  		get_page_bootmem(section_nr, page, MIX_SECTION_INFO);
047532787   Yasunori Goto   memory hotplug: r...
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
  
  }
  
  void register_page_bootmem_info_node(struct pglist_data *pgdat)
  {
  	unsigned long i, pfn, end_pfn, nr_pages;
  	int node = pgdat->node_id;
  	struct page *page;
  	struct zone *zone;
  
  	nr_pages = PAGE_ALIGN(sizeof(struct pglist_data)) >> PAGE_SHIFT;
  	page = virt_to_page(pgdat);
  
  	for (i = 0; i < nr_pages; i++, page++)
  		get_page_bootmem(node, page, NODE_INFO);
  
  	zone = &pgdat->node_zones[0];
  	for (; zone < pgdat->node_zones + MAX_NR_ZONES - 1; zone++) {
  		if (zone->wait_table) {
  			nr_pages = zone->wait_table_hash_nr_entries
  				* sizeof(wait_queue_head_t);
  			nr_pages = PAGE_ALIGN(nr_pages) >> PAGE_SHIFT;
  			page = virt_to_page(zone->wait_table);
  
  			for (i = 0; i < nr_pages; i++, page++)
  				get_page_bootmem(node, page, NODE_INFO);
  		}
  	}
  
  	pfn = pgdat->node_start_pfn;
  	end_pfn = pfn + pgdat->node_spanned_pages;
  
  	/* register_section info */
  	for (; pfn < end_pfn; pfn += PAGES_PER_SECTION)
  		register_page_bootmem_info_section(pfn);
  
  }
  #endif /* !CONFIG_SPARSEMEM_VMEMMAP */
76cdd58e5   Heiko Carstens   memory_hotplug: a...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
  static void grow_zone_span(struct zone *zone, unsigned long start_pfn,
  			   unsigned long end_pfn)
  {
  	unsigned long old_zone_end_pfn;
  
  	zone_span_writelock(zone);
  
  	old_zone_end_pfn = zone->zone_start_pfn + zone->spanned_pages;
  	if (start_pfn < zone->zone_start_pfn)
  		zone->zone_start_pfn = start_pfn;
  
  	zone->spanned_pages = max(old_zone_end_pfn, end_pfn) -
  				zone->zone_start_pfn;
  
  	zone_span_writeunlock(zone);
  }
  
  static void grow_pgdat_span(struct pglist_data *pgdat, unsigned long start_pfn,
  			    unsigned long end_pfn)
  {
  	unsigned long old_pgdat_end_pfn =
  		pgdat->node_start_pfn + pgdat->node_spanned_pages;
  
  	if (start_pfn < pgdat->node_start_pfn)
  		pgdat->node_start_pfn = start_pfn;
  
  	pgdat->node_spanned_pages = max(old_pgdat_end_pfn, end_pfn) -
  					pgdat->node_start_pfn;
  }
31168481c   Al Viro   meminit section w...
217
  static int __meminit __add_zone(struct zone *zone, unsigned long phys_start_pfn)
3947be196   Dave Hansen   [PATCH] memory ho...
218
219
220
221
222
  {
  	struct pglist_data *pgdat = zone->zone_pgdat;
  	int nr_pages = PAGES_PER_SECTION;
  	int nid = pgdat->node_id;
  	int zone_type;
76cdd58e5   Heiko Carstens   memory_hotplug: a...
223
  	unsigned long flags;
3947be196   Dave Hansen   [PATCH] memory ho...
224
225
  
  	zone_type = zone - pgdat->node_zones;
76cdd58e5   Heiko Carstens   memory_hotplug: a...
226
227
228
229
230
231
232
233
234
235
236
237
238
  	if (!zone->wait_table) {
  		int ret;
  
  		ret = init_currently_empty_zone(zone, phys_start_pfn,
  						nr_pages, MEMMAP_HOTPLUG);
  		if (ret)
  			return ret;
  	}
  	pgdat_resize_lock(zone->zone_pgdat, &flags);
  	grow_zone_span(zone, phys_start_pfn, phys_start_pfn + nr_pages);
  	grow_pgdat_span(zone->zone_pgdat, phys_start_pfn,
  			phys_start_pfn + nr_pages);
  	pgdat_resize_unlock(zone->zone_pgdat, &flags);
a2f3aa025   Dave Hansen   [PATCH] Fix spars...
239
240
  	memmap_init_zone(nr_pages, nid, zone_type,
  			 phys_start_pfn, MEMMAP_HOTPLUG);
718127cc3   Yasunori Goto   [PATCH] wait_tabl...
241
  	return 0;
3947be196   Dave Hansen   [PATCH] memory ho...
242
  }
c04fc586c   Gary Hade   mm: show node to ...
243
244
  static int __meminit __add_section(int nid, struct zone *zone,
  					unsigned long phys_start_pfn)
3947be196   Dave Hansen   [PATCH] memory ho...
245
  {
3947be196   Dave Hansen   [PATCH] memory ho...
246
  	int nr_pages = PAGES_PER_SECTION;
3947be196   Dave Hansen   [PATCH] memory ho...
247
  	int ret;
ebd15302d   KAMEZAWA Hiroyuki   [PATCH] memory ho...
248
249
  	if (pfn_valid(phys_start_pfn))
  		return -EEXIST;
0b0acbec1   Dave Hansen   [PATCH] memory ho...
250
  	ret = sparse_add_one_section(zone, phys_start_pfn, nr_pages);
3947be196   Dave Hansen   [PATCH] memory ho...
251
252
253
  
  	if (ret < 0)
  		return ret;
718127cc3   Yasunori Goto   [PATCH] wait_tabl...
254
255
256
257
  	ret = __add_zone(zone, phys_start_pfn);
  
  	if (ret < 0)
  		return ret;
c04fc586c   Gary Hade   mm: show node to ...
258
  	return register_new_memory(nid, __pfn_to_section(phys_start_pfn));
3947be196   Dave Hansen   [PATCH] memory ho...
259
  }
0c0a4a517   Yasunori Goto   memory hotplug: f...
260
261
262
263
264
265
266
267
268
269
  #ifdef CONFIG_SPARSEMEM_VMEMMAP
  static int __remove_section(struct zone *zone, struct mem_section *ms)
  {
  	/*
  	 * XXX: Freeing memmap with vmemmap is not implement yet.
  	 *      This should be removed later.
  	 */
  	return -EBUSY;
  }
  #else
ea01ea937   Badari Pulavarty   hotplug memory re...
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
  static int __remove_section(struct zone *zone, struct mem_section *ms)
  {
  	unsigned long flags;
  	struct pglist_data *pgdat = zone->zone_pgdat;
  	int ret = -EINVAL;
  
  	if (!valid_section(ms))
  		return ret;
  
  	ret = unregister_memory_section(ms);
  	if (ret)
  		return ret;
  
  	pgdat_resize_lock(pgdat, &flags);
  	sparse_remove_one_section(zone, ms);
  	pgdat_resize_unlock(pgdat, &flags);
  	return 0;
  }
0c0a4a517   Yasunori Goto   memory hotplug: f...
288
  #endif
ea01ea937   Badari Pulavarty   hotplug memory re...
289

3947be196   Dave Hansen   [PATCH] memory ho...
290
291
292
293
294
295
  /*
   * Reasonably generic function for adding memory.  It is
   * expected that archs that support memory hotplug will
   * call this function after deciding the zone to which to
   * add the new pages.
   */
c04fc586c   Gary Hade   mm: show node to ...
296
297
  int __ref __add_pages(int nid, struct zone *zone, unsigned long phys_start_pfn,
  			unsigned long nr_pages)
3947be196   Dave Hansen   [PATCH] memory ho...
298
299
300
  {
  	unsigned long i;
  	int err = 0;
6f712711d   KAMEZAWA Hiroyuki   [PATCH] memory ho...
301
302
303
304
  	int start_sec, end_sec;
  	/* during initialize mem_map, align hot-added range to section */
  	start_sec = pfn_to_section_nr(phys_start_pfn);
  	end_sec = pfn_to_section_nr(phys_start_pfn + nr_pages - 1);
3947be196   Dave Hansen   [PATCH] memory ho...
305

6f712711d   KAMEZAWA Hiroyuki   [PATCH] memory ho...
306
  	for (i = start_sec; i <= end_sec; i++) {
c04fc586c   Gary Hade   mm: show node to ...
307
  		err = __add_section(nid, zone, i << PFN_SECTION_SHIFT);
3947be196   Dave Hansen   [PATCH] memory ho...
308

6f712711d   KAMEZAWA Hiroyuki   [PATCH] memory ho...
309
  		/*
183ff22bb   Simon Arlott   spelling fixes: mm/
310
  		 * EEXIST is finally dealt with by ioresource collision
6f712711d   KAMEZAWA Hiroyuki   [PATCH] memory ho...
311
312
  		 * check. see add_memory() => register_memory_resource()
  		 * Warning will be printed if there is collision.
bed120c64   Joel H Schopp   [PATCH] spufs: fi...
313
314
  		 */
  		if (err && (err != -EEXIST))
3947be196   Dave Hansen   [PATCH] memory ho...
315
  			break;
6f712711d   KAMEZAWA Hiroyuki   [PATCH] memory ho...
316
  		err = 0;
3947be196   Dave Hansen   [PATCH] memory ho...
317
318
319
320
  	}
  
  	return err;
  }
bed120c64   Joel H Schopp   [PATCH] spufs: fi...
321
  EXPORT_SYMBOL_GPL(__add_pages);
3947be196   Dave Hansen   [PATCH] memory ho...
322

ea01ea937   Badari Pulavarty   hotplug memory re...
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
  /**
   * __remove_pages() - remove sections of pages from a zone
   * @zone: zone from which pages need to be removed
   * @phys_start_pfn: starting pageframe (must be aligned to start of a section)
   * @nr_pages: number of pages to remove (must be multiple of section size)
   *
   * Generic helper function to remove section mappings and sysfs entries
   * for the section of the memory we are removing. Caller needs to make
   * sure that pages are marked reserved and zones are adjust properly by
   * calling offline_pages().
   */
  int __remove_pages(struct zone *zone, unsigned long phys_start_pfn,
  		 unsigned long nr_pages)
  {
  	unsigned long i, ret = 0;
  	int sections_to_remove;
  
  	/*
  	 * We can only remove entire sections
  	 */
  	BUG_ON(phys_start_pfn & ~PAGE_SECTION_MASK);
  	BUG_ON(nr_pages % PAGES_PER_SECTION);
ea01ea937   Badari Pulavarty   hotplug memory re...
345
346
347
  	sections_to_remove = nr_pages / PAGES_PER_SECTION;
  	for (i = 0; i < sections_to_remove; i++) {
  		unsigned long pfn = phys_start_pfn + i*PAGES_PER_SECTION;
de7f0cba9   Nathan Fontenot   memory hotplug: r...
348
349
  		release_mem_region(pfn << PAGE_SHIFT,
  				   PAGES_PER_SECTION << PAGE_SHIFT);
ea01ea937   Badari Pulavarty   hotplug memory re...
350
351
352
353
354
355
356
  		ret = __remove_section(zone, __pfn_to_section(pfn));
  		if (ret)
  			break;
  	}
  	return ret;
  }
  EXPORT_SYMBOL_GPL(__remove_pages);
9d0ad8ca4   Daniel Kiper   mm: extend memory...
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
  int set_online_page_callback(online_page_callback_t callback)
  {
  	int rc = -EINVAL;
  
  	lock_memory_hotplug();
  
  	if (online_page_callback == generic_online_page) {
  		online_page_callback = callback;
  		rc = 0;
  	}
  
  	unlock_memory_hotplug();
  
  	return rc;
  }
  EXPORT_SYMBOL_GPL(set_online_page_callback);
  
  int restore_online_page_callback(online_page_callback_t callback)
  {
  	int rc = -EINVAL;
  
  	lock_memory_hotplug();
  
  	if (online_page_callback == callback) {
  		online_page_callback = generic_online_page;
  		rc = 0;
  	}
  
  	unlock_memory_hotplug();
  
  	return rc;
  }
  EXPORT_SYMBOL_GPL(restore_online_page_callback);
  
  void __online_page_set_limits(struct page *page)
180c06efc   Jeremy Fitzhardinge   hotplug-memory: m...
392
  {
4738e1b9c   Jan Beulich   memory hotplug: f...
393
  	unsigned long pfn = page_to_pfn(page);
4738e1b9c   Jan Beulich   memory hotplug: f...
394
395
  	if (pfn >= num_physpages)
  		num_physpages = pfn + 1;
9d0ad8ca4   Daniel Kiper   mm: extend memory...
396
397
398
399
400
401
  }
  EXPORT_SYMBOL_GPL(__online_page_set_limits);
  
  void __online_page_increment_counters(struct page *page)
  {
  	totalram_pages++;
180c06efc   Jeremy Fitzhardinge   hotplug-memory: m...
402
403
404
405
406
  
  #ifdef CONFIG_HIGHMEM
  	if (PageHighMem(page))
  		totalhigh_pages++;
  #endif
9d0ad8ca4   Daniel Kiper   mm: extend memory...
407
408
  }
  EXPORT_SYMBOL_GPL(__online_page_increment_counters);
180c06efc   Jeremy Fitzhardinge   hotplug-memory: m...
409

9d0ad8ca4   Daniel Kiper   mm: extend memory...
410
411
  void __online_page_free(struct page *page)
  {
180c06efc   Jeremy Fitzhardinge   hotplug-memory: m...
412
413
414
415
  	ClearPageReserved(page);
  	init_page_count(page);
  	__free_page(page);
  }
9d0ad8ca4   Daniel Kiper   mm: extend memory...
416
417
418
419
420
421
422
423
  EXPORT_SYMBOL_GPL(__online_page_free);
  
  static void generic_online_page(struct page *page)
  {
  	__online_page_set_limits(page);
  	__online_page_increment_counters(page);
  	__online_page_free(page);
  }
180c06efc   Jeremy Fitzhardinge   hotplug-memory: m...
424

75884fb1c   KAMEZAWA Hiroyuki   memory unplug: me...
425
426
  static int online_pages_range(unsigned long start_pfn, unsigned long nr_pages,
  			void *arg)
3947be196   Dave Hansen   [PATCH] memory ho...
427
428
  {
  	unsigned long i;
75884fb1c   KAMEZAWA Hiroyuki   memory unplug: me...
429
430
431
432
433
  	unsigned long onlined_pages = *(unsigned long *)arg;
  	struct page *page;
  	if (PageReserved(pfn_to_page(start_pfn)))
  		for (i = 0; i < nr_pages; i++) {
  			page = pfn_to_page(start_pfn + i);
9d0ad8ca4   Daniel Kiper   mm: extend memory...
434
  			(*online_page_callback)(page);
75884fb1c   KAMEZAWA Hiroyuki   memory unplug: me...
435
436
437
438
439
  			onlined_pages++;
  		}
  	*(unsigned long *)arg = onlined_pages;
  	return 0;
  }
839a4fcc8   KOSAKI Motohiro   mm, mem-hotplug: ...
440
  int __ref online_pages(unsigned long pfn, unsigned long nr_pages)
75884fb1c   KAMEZAWA Hiroyuki   memory unplug: me...
441
  {
3947be196   Dave Hansen   [PATCH] memory ho...
442
443
  	unsigned long onlined_pages = 0;
  	struct zone *zone;
6811378e7   Yasunori Goto   [PATCH] wait_tabl...
444
  	int need_zonelists_rebuild = 0;
7b78d335a   Yasunori Goto   memory hotplug: r...
445
446
447
  	int nid;
  	int ret;
  	struct memory_notify arg;
925268a06   KAMEZAWA Hiroyuki   memory hotplug: o...
448
  	lock_memory_hotplug();
7b78d335a   Yasunori Goto   memory hotplug: r...
449
450
451
452
453
454
455
  	arg.start_pfn = pfn;
  	arg.nr_pages = nr_pages;
  	arg.status_change_nid = -1;
  
  	nid = page_to_nid(pfn_to_page(pfn));
  	if (node_present_pages(nid) == 0)
  		arg.status_change_nid = nid;
3947be196   Dave Hansen   [PATCH] memory ho...
456

7b78d335a   Yasunori Goto   memory hotplug: r...
457
458
459
460
  	ret = memory_notify(MEM_GOING_ONLINE, &arg);
  	ret = notifier_to_errno(ret);
  	if (ret) {
  		memory_notify(MEM_CANCEL_ONLINE, &arg);
925268a06   KAMEZAWA Hiroyuki   memory hotplug: o...
461
  		unlock_memory_hotplug();
7b78d335a   Yasunori Goto   memory hotplug: r...
462
463
  		return ret;
  	}
3947be196   Dave Hansen   [PATCH] memory ho...
464
465
466
  	/*
  	 * This doesn't need a lock to do pfn_to_page().
  	 * The section can't be removed here because of the
da19cbcf7   Daniel Walker   driver core: memo...
467
  	 * memory_block->state_mutex.
3947be196   Dave Hansen   [PATCH] memory ho...
468
469
  	 */
  	zone = page_zone(pfn_to_page(pfn));
6811378e7   Yasunori Goto   [PATCH] wait_tabl...
470
471
472
473
474
  	/*
  	 * If this zone is not populated, then it is not in zonelist.
  	 * This means the page allocator ignores this zone.
  	 * So, zonelist must be updated after online.
  	 */
4eaf3f643   Haicheng Li   mem-hotplug: fix ...
475
  	mutex_lock(&zonelists_mutex);
6811378e7   Yasunori Goto   [PATCH] wait_tabl...
476
477
  	if (!populated_zone(zone))
  		need_zonelists_rebuild = 1;
908eedc61   KAMEZAWA Hiroyuki   walk system ram r...
478
  	ret = walk_system_ram_range(pfn, nr_pages, &onlined_pages,
75884fb1c   KAMEZAWA Hiroyuki   memory unplug: me...
479
  		online_pages_range);
fd8a4221a   Geoff Levand   memory_hotplug: c...
480
  	if (ret) {
4eaf3f643   Haicheng Li   mem-hotplug: fix ...
481
  		mutex_unlock(&zonelists_mutex);
fd8a4221a   Geoff Levand   memory_hotplug: c...
482
483
484
485
  		printk(KERN_DEBUG "online_pages %lx at %lx failed
  ",
  			nr_pages, pfn);
  		memory_notify(MEM_CANCEL_ONLINE, &arg);
925268a06   KAMEZAWA Hiroyuki   memory hotplug: o...
486
  		unlock_memory_hotplug();
fd8a4221a   Geoff Levand   memory_hotplug: c...
487
488
  		return ret;
  	}
3947be196   Dave Hansen   [PATCH] memory ho...
489
  	zone->present_pages += onlined_pages;
f2937be58   Yasunori Goto   [PATCH] memory ho...
490
  	zone->zone_pgdat->node_present_pages += onlined_pages;
1f522509c   Haicheng Li   mem-hotplug: avoi...
491
492
493
494
  	if (need_zonelists_rebuild)
  		build_all_zonelists(zone);
  	else
  		zone_pcp_update(zone);
3947be196   Dave Hansen   [PATCH] memory ho...
495

4eaf3f643   Haicheng Li   mem-hotplug: fix ...
496
  	mutex_unlock(&zonelists_mutex);
1b79acc91   KOSAKI Motohiro   mm, mem-hotplug: ...
497
498
  
  	init_per_zone_wmark_min();
7ea1530ab   Christoph Lameter   Memoryless nodes:...
499
500
501
502
  	if (onlined_pages) {
  		kswapd_run(zone_to_nid(zone));
  		node_set_state(zone_to_nid(zone), N_HIGH_MEMORY);
  	}
61b13993a   Dave Hansen   [PATCH] memory ho...
503

1f522509c   Haicheng Li   mem-hotplug: avoi...
504
  	vm_total_pages = nr_free_pagecache_pages();
2f7f24eca   Kent Liu   memory-hotplug: d...
505

2d1d43f6a   Chandra Seetharaman   [PATCH] call mm/p...
506
  	writeback_set_ratelimit();
7b78d335a   Yasunori Goto   memory hotplug: r...
507
508
509
  
  	if (onlined_pages)
  		memory_notify(MEM_ONLINE, &arg);
925268a06   KAMEZAWA Hiroyuki   memory hotplug: o...
510
  	unlock_memory_hotplug();
7b78d335a   Yasunori Goto   memory hotplug: r...
511

3947be196   Dave Hansen   [PATCH] memory ho...
512
513
  	return 0;
  }
53947027a   Keith Mannthey   [PATCH] hot-add-m...
514
  #endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
bc02af93d   Yasunori Goto   [PATCH] pgdat all...
515

e13193319   Hidetoshi Seto   mm/memory_hotplug...
516
517
  /* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG */
  static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start)
9af3c2dea   Yasunori Goto   [PATCH] pgdat all...
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
  {
  	struct pglist_data *pgdat;
  	unsigned long zones_size[MAX_NR_ZONES] = {0};
  	unsigned long zholes_size[MAX_NR_ZONES] = {0};
  	unsigned long start_pfn = start >> PAGE_SHIFT;
  
  	pgdat = arch_alloc_nodedata(nid);
  	if (!pgdat)
  		return NULL;
  
  	arch_refresh_nodedata(nid, pgdat);
  
  	/* we can use NODE_DATA(nid) from here */
  
  	/* init node's zones as empty zones, we don't have any present pages.*/
9109fb7b3   Johannes Weiner   mm: drop unneeded...
533
  	free_area_init_node(nid, zones_size, start_pfn, zholes_size);
9af3c2dea   Yasunori Goto   [PATCH] pgdat all...
534

959ecc48f   KAMEZAWA Hiroyuki   mm/memory_hotplug...
535
536
537
538
  	/*
  	 * The node we allocated has no zone fallback lists. For avoiding
  	 * to access not-initialized zonelist, build here.
  	 */
f957db4fc   David Rientjes   mm, hotplug: prot...
539
  	mutex_lock(&zonelists_mutex);
959ecc48f   KAMEZAWA Hiroyuki   mm/memory_hotplug...
540
  	build_all_zonelists(NULL);
f957db4fc   David Rientjes   mm, hotplug: prot...
541
  	mutex_unlock(&zonelists_mutex);
959ecc48f   KAMEZAWA Hiroyuki   mm/memory_hotplug...
542

9af3c2dea   Yasunori Goto   [PATCH] pgdat all...
543
544
545
546
547
548
549
550
551
  	return pgdat;
  }
  
  static void rollback_node_hotadd(int nid, pg_data_t *pgdat)
  {
  	arch_refresh_nodedata(nid, NULL);
  	arch_free_nodedata(pgdat);
  	return;
  }
0a5470390   KAMEZAWA Hiroyuki   [PATCH] register ...
552

cf23422b9   minskey guo   cpu/mem hotplug: ...
553
554
555
556
557
558
559
  /*
   * called by cpu_up() to online a node without onlined memory.
   */
  int mem_online_node(int nid)
  {
  	pg_data_t	*pgdat;
  	int	ret;
20d6c96b5   KOSAKI Motohiro   mem-hotplug: intr...
560
  	lock_memory_hotplug();
cf23422b9   minskey guo   cpu/mem hotplug: ...
561
  	pgdat = hotadd_new_pgdat(nid, 0);
7553e8f2d   David Rientjes   mm, hotplug: fix ...
562
  	if (!pgdat) {
cf23422b9   minskey guo   cpu/mem hotplug: ...
563
564
565
566
567
568
569
570
  		ret = -ENOMEM;
  		goto out;
  	}
  	node_set_online(nid);
  	ret = register_one_node(nid);
  	BUG_ON(ret);
  
  out:
20d6c96b5   KOSAKI Motohiro   mem-hotplug: intr...
571
  	unlock_memory_hotplug();
cf23422b9   minskey guo   cpu/mem hotplug: ...
572
573
  	return ret;
  }
31168481c   Al Viro   meminit section w...
574
575
  /* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG */
  int __ref add_memory(int nid, u64 start, u64 size)
bc02af93d   Yasunori Goto   [PATCH] pgdat all...
576
  {
9af3c2dea   Yasunori Goto   [PATCH] pgdat all...
577
578
  	pg_data_t *pgdat = NULL;
  	int new_pgdat = 0;
ebd15302d   KAMEZAWA Hiroyuki   [PATCH] memory ho...
579
  	struct resource *res;
bc02af93d   Yasunori Goto   [PATCH] pgdat all...
580
  	int ret;
20d6c96b5   KOSAKI Motohiro   mem-hotplug: intr...
581
  	lock_memory_hotplug();
6ad696d2c   Andi Kleen   mm: allow memory ...
582

ebd15302d   KAMEZAWA Hiroyuki   [PATCH] memory ho...
583
  	res = register_memory_resource(start, size);
6ad696d2c   Andi Kleen   mm: allow memory ...
584
  	ret = -EEXIST;
ebd15302d   KAMEZAWA Hiroyuki   [PATCH] memory ho...
585
  	if (!res)
6ad696d2c   Andi Kleen   mm: allow memory ...
586
  		goto out;
ebd15302d   KAMEZAWA Hiroyuki   [PATCH] memory ho...
587

9af3c2dea   Yasunori Goto   [PATCH] pgdat all...
588
589
  	if (!node_online(nid)) {
  		pgdat = hotadd_new_pgdat(nid, start);
6ad696d2c   Andi Kleen   mm: allow memory ...
590
  		ret = -ENOMEM;
9af3c2dea   Yasunori Goto   [PATCH] pgdat all...
591
  		if (!pgdat)
6ad696d2c   Andi Kleen   mm: allow memory ...
592
  			goto out;
9af3c2dea   Yasunori Goto   [PATCH] pgdat all...
593
  		new_pgdat = 1;
9af3c2dea   Yasunori Goto   [PATCH] pgdat all...
594
  	}
bc02af93d   Yasunori Goto   [PATCH] pgdat all...
595
596
  	/* call arch's memory hotadd */
  	ret = arch_add_memory(nid, start, size);
9af3c2dea   Yasunori Goto   [PATCH] pgdat all...
597
598
  	if (ret < 0)
  		goto error;
0fc44159b   Yasunori Goto   [PATCH] Register ...
599
  	/* we online node here. we can't roll back from here. */
9af3c2dea   Yasunori Goto   [PATCH] pgdat all...
600
  	node_set_online(nid);
0fc44159b   Yasunori Goto   [PATCH] Register ...
601
602
603
604
605
606
607
608
609
  	if (new_pgdat) {
  		ret = register_one_node(nid);
  		/*
  		 * If sysfs file of new node can't create, cpu on the node
  		 * can't be hot-added. There is no rollback way now.
  		 * So, check by BUG_ON() to catch it reluctantly..
  		 */
  		BUG_ON(ret);
  	}
d96ae5309   akpm@linux-foundation.org   memory-hotplug: c...
610
611
  	/* create new memmap entry */
  	firmware_map_add_hotplug(start, start + size, "System RAM");
6ad696d2c   Andi Kleen   mm: allow memory ...
612
  	goto out;
9af3c2dea   Yasunori Goto   [PATCH] pgdat all...
613
614
615
616
  error:
  	/* rollback pgdat allocation and others */
  	if (new_pgdat)
  		rollback_node_hotadd(nid, pgdat);
ebd15302d   KAMEZAWA Hiroyuki   [PATCH] memory ho...
617
618
  	if (res)
  		release_memory_resource(res);
9af3c2dea   Yasunori Goto   [PATCH] pgdat all...
619

6ad696d2c   Andi Kleen   mm: allow memory ...
620
  out:
20d6c96b5   KOSAKI Motohiro   mem-hotplug: intr...
621
  	unlock_memory_hotplug();
bc02af93d   Yasunori Goto   [PATCH] pgdat all...
622
623
624
  	return ret;
  }
  EXPORT_SYMBOL_GPL(add_memory);
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
625
626
627
  
  #ifdef CONFIG_MEMORY_HOTREMOVE
  /*
5c755e9fd   Badari Pulavarty   memory-hotplug: a...
628
629
630
631
632
633
634
635
636
637
638
639
640
641
   * A free page on the buddy free lists (not the per-cpu lists) has PageBuddy
   * set and the size of the free page is given by page_order(). Using this,
   * the function determines if the pageblock contains only free pages.
   * Due to buddy contraints, a free page at least the size of a pageblock will
   * be located at the start of the pageblock
   */
  static inline int pageblock_free(struct page *page)
  {
  	return PageBuddy(page) && page_order(page) >= pageblock_order;
  }
  
  /* Return the start of the next active pageblock after a given page */
  static struct page *next_active_pageblock(struct page *page)
  {
5c755e9fd   Badari Pulavarty   memory-hotplug: a...
642
643
  	/* Ensure the starting page is pageblock-aligned */
  	BUG_ON(page_to_pfn(page) & (pageblock_nr_pages - 1));
5c755e9fd   Badari Pulavarty   memory-hotplug: a...
644
  	/* If the entire pageblock is free, move to the end of free page */
0dcc48c15   KAMEZAWA Hiroyuki   memory hotplug: f...
645
646
647
648
649
650
651
  	if (pageblock_free(page)) {
  		int order;
  		/* be careful. we don't have locks, page_order can be changed.*/
  		order = page_order(page);
  		if ((order < MAX_ORDER) && (order >= pageblock_order))
  			return page + (1 << order);
  	}
5c755e9fd   Badari Pulavarty   memory-hotplug: a...
652

0dcc48c15   KAMEZAWA Hiroyuki   memory hotplug: f...
653
  	return page + pageblock_nr_pages;
5c755e9fd   Badari Pulavarty   memory-hotplug: a...
654
655
656
657
658
  }
  
  /* Checks if this range of memory is likely to be hot-removable. */
  int is_mem_section_removable(unsigned long start_pfn, unsigned long nr_pages)
  {
5c755e9fd   Badari Pulavarty   memory-hotplug: a...
659
660
661
662
663
  	struct page *page = pfn_to_page(start_pfn);
  	struct page *end_page = page + nr_pages;
  
  	/* Check the starting page of each pageblock within the range */
  	for (; page < end_page; page = next_active_pageblock(page)) {
49ac82558   KAMEZAWA Hiroyuki   memory hotplug: u...
664
  		if (!is_pageblock_removable_nolock(page))
5c755e9fd   Badari Pulavarty   memory-hotplug: a...
665
  			return 0;
49ac82558   KAMEZAWA Hiroyuki   memory hotplug: u...
666
  		cond_resched();
5c755e9fd   Badari Pulavarty   memory-hotplug: a...
667
668
669
670
671
672
673
  	}
  
  	/* All pageblocks in the memory block are likely to be hot-removable */
  	return 1;
  }
  
  /*
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
   * Confirm all pages in a range [start, end) is belongs to the same zone.
   */
  static int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)
  {
  	unsigned long pfn;
  	struct zone *zone = NULL;
  	struct page *page;
  	int i;
  	for (pfn = start_pfn;
  	     pfn < end_pfn;
  	     pfn += MAX_ORDER_NR_PAGES) {
  		i = 0;
  		/* This is just a CONFIG_HOLES_IN_ZONE check.*/
  		while ((i < MAX_ORDER_NR_PAGES) && !pfn_valid_within(pfn + i))
  			i++;
  		if (i == MAX_ORDER_NR_PAGES)
  			continue;
  		page = pfn_to_page(pfn + i);
  		if (zone && page_zone(page) != zone)
  			return 0;
  		zone = page_zone(page);
  	}
  	return 1;
  }
  
  /*
   * Scanning pfn is much easier than scanning lru list.
   * Scan pfn from start to end and Find LRU page.
   */
7bbc0905e   Andrew Morton   mm/memory_hotplug...
703
  static unsigned long scan_lru_pages(unsigned long start, unsigned long end)
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
704
705
706
707
708
709
710
711
712
713
714
715
716
717
  {
  	unsigned long pfn;
  	struct page *page;
  	for (pfn = start; pfn < end; pfn++) {
  		if (pfn_valid(pfn)) {
  			page = pfn_to_page(pfn);
  			if (PageLRU(page))
  				return pfn;
  		}
  	}
  	return 0;
  }
  
  static struct page *
3c1d43787   Hugh Dickins   mm: remove GFP_HI...
718
  hotremove_migrate_alloc(struct page *page, unsigned long private, int **x)
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
719
  {
3c1d43787   Hugh Dickins   mm: remove GFP_HI...
720
721
  	/* This should be improooooved!! */
  	return alloc_page(GFP_HIGHUSER_MOVABLE);
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
722
  }
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
  #define NR_OFFLINE_AT_ONCE_PAGES	(256)
  static int
  do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
  {
  	unsigned long pfn;
  	struct page *page;
  	int move_pages = NR_OFFLINE_AT_ONCE_PAGES;
  	int not_managed = 0;
  	int ret = 0;
  	LIST_HEAD(source);
  
  	for (pfn = start_pfn; pfn < end_pfn && move_pages > 0; pfn++) {
  		if (!pfn_valid(pfn))
  			continue;
  		page = pfn_to_page(pfn);
700c2a46e   Konstantin Khlebnikov   mem-hotplug: call...
738
  		if (!get_page_unless_zero(page))
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
739
740
741
742
743
  			continue;
  		/*
  		 * We can skip free pages. And we can only deal with pages on
  		 * LRU.
  		 */
62695a84e   Nick Piggin   vmscan: move isol...
744
  		ret = isolate_lru_page(page);
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
745
  		if (!ret) { /* Success */
700c2a46e   Konstantin Khlebnikov   mem-hotplug: call...
746
  			put_page(page);
62695a84e   Nick Piggin   vmscan: move isol...
747
  			list_add_tail(&page->lru, &source);
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
748
  			move_pages--;
6d9c285a6   KOSAKI Motohiro   mm: move inc_zone...
749
750
  			inc_zone_page_state(page, NR_ISOLATED_ANON +
  					    page_is_file_cache(page));
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
751
  		} else {
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
752
  #ifdef CONFIG_DEBUG_VM
718a38211   Wu Fengguang   mm: introduce dum...
753
754
755
756
  			printk(KERN_ALERT "removing pfn %lx from LRU failed
  ",
  			       pfn);
  			dump_page(page);
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
757
  #endif
700c2a46e   Konstantin Khlebnikov   mem-hotplug: call...
758
  			put_page(page);
25985edce   Lucas De Marchi   Fix common misspe...
759
  			/* Because we don't have big zone->lock. we should
809c44497   Bob Liu   mm: do_migrate_ra...
760
761
762
  			   check this again here. */
  			if (page_count(page)) {
  				not_managed++;
f3ab2636c   Bob Liu   mm: do_migrate_ra...
763
  				ret = -EBUSY;
809c44497   Bob Liu   mm: do_migrate_ra...
764
765
  				break;
  			}
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
766
767
  		}
  	}
f3ab2636c   Bob Liu   mm: do_migrate_ra...
768
769
770
771
772
773
  	if (!list_empty(&source)) {
  		if (not_managed) {
  			putback_lru_pages(&source);
  			goto out;
  		}
  		/* this function returns # of failed pages */
77f1fe6b0   Mel Gorman   mm: migration: al...
774
  		ret = migrate_pages(&source, hotremove_migrate_alloc, 0,
a6bc32b89   Mel Gorman   mm: compaction: i...
775
  							true, MIGRATE_SYNC);
f3ab2636c   Bob Liu   mm: do_migrate_ra...
776
  		if (ret)
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
777
  			putback_lru_pages(&source);
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
778
  	}
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
  out:
  	return ret;
  }
  
  /*
   * remove from free_area[] and mark all as Reserved.
   */
  static int
  offline_isolated_pages_cb(unsigned long start, unsigned long nr_pages,
  			void *data)
  {
  	__offline_isolated_pages(start, start + nr_pages);
  	return 0;
  }
  
  static void
  offline_isolated_pages(unsigned long start_pfn, unsigned long end_pfn)
  {
908eedc61   KAMEZAWA Hiroyuki   walk system ram r...
797
  	walk_system_ram_range(start_pfn, end_pfn - start_pfn, NULL,
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
  				offline_isolated_pages_cb);
  }
  
  /*
   * Check all pages in range, recoreded as memory resource, are isolated.
   */
  static int
  check_pages_isolated_cb(unsigned long start_pfn, unsigned long nr_pages,
  			void *data)
  {
  	int ret;
  	long offlined = *(long *)data;
  	ret = test_pages_isolated(start_pfn, start_pfn + nr_pages);
  	offlined = nr_pages;
  	if (!ret)
  		*(long *)data += offlined;
  	return ret;
  }
  
  static long
  check_pages_isolated(unsigned long start_pfn, unsigned long end_pfn)
  {
  	long offlined = 0;
  	int ret;
908eedc61   KAMEZAWA Hiroyuki   walk system ram r...
822
  	ret = walk_system_ram_range(start_pfn, end_pfn - start_pfn, &offlined,
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
823
824
825
826
827
  			check_pages_isolated_cb);
  	if (ret < 0)
  		offlined = (long)ret;
  	return offlined;
  }
839a4fcc8   KOSAKI Motohiro   mm, mem-hotplug: ...
828
  static int __ref offline_pages(unsigned long start_pfn,
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
829
830
831
832
  		  unsigned long end_pfn, unsigned long timeout)
  {
  	unsigned long pfn, nr_pages, expire;
  	long offlined_pages;
7b78d335a   Yasunori Goto   memory hotplug: r...
833
  	int ret, drain, retry_max, node;
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
834
  	struct zone *zone;
7b78d335a   Yasunori Goto   memory hotplug: r...
835
  	struct memory_notify arg;
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
836
837
838
839
840
841
842
843
844
845
846
  
  	BUG_ON(start_pfn >= end_pfn);
  	/* at least, alignment against pageblock is necessary */
  	if (!IS_ALIGNED(start_pfn, pageblock_nr_pages))
  		return -EINVAL;
  	if (!IS_ALIGNED(end_pfn, pageblock_nr_pages))
  		return -EINVAL;
  	/* This makes hotplug much easier...and readable.
  	   we assume this for now. .*/
  	if (!test_pages_in_a_zone(start_pfn, end_pfn))
  		return -EINVAL;
7b78d335a   Yasunori Goto   memory hotplug: r...
847

20d6c96b5   KOSAKI Motohiro   mem-hotplug: intr...
848
  	lock_memory_hotplug();
6ad696d2c   Andi Kleen   mm: allow memory ...
849

7b78d335a   Yasunori Goto   memory hotplug: r...
850
851
852
  	zone = page_zone(pfn_to_page(start_pfn));
  	node = zone_to_nid(zone);
  	nr_pages = end_pfn - start_pfn;
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
853
854
855
  	/* set above range as isolated */
  	ret = start_isolate_page_range(start_pfn, end_pfn);
  	if (ret)
6ad696d2c   Andi Kleen   mm: allow memory ...
856
  		goto out;
7b78d335a   Yasunori Goto   memory hotplug: r...
857
858
859
860
861
862
863
864
865
866
867
  
  	arg.start_pfn = start_pfn;
  	arg.nr_pages = nr_pages;
  	arg.status_change_nid = -1;
  	if (nr_pages >= node_present_pages(node))
  		arg.status_change_nid = node;
  
  	ret = memory_notify(MEM_GOING_OFFLINE, &arg);
  	ret = notifier_to_errno(ret);
  	if (ret)
  		goto failed_removal;
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
  	pfn = start_pfn;
  	expire = jiffies + timeout;
  	drain = 0;
  	retry_max = 5;
  repeat:
  	/* start memory hot removal */
  	ret = -EAGAIN;
  	if (time_after(jiffies, expire))
  		goto failed_removal;
  	ret = -EINTR;
  	if (signal_pending(current))
  		goto failed_removal;
  	ret = 0;
  	if (drain) {
  		lru_add_drain_all();
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
883
  		cond_resched();
9f8f21725   Christoph Lameter   Page allocator: c...
884
  		drain_all_pages();
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
  	}
  
  	pfn = scan_lru_pages(start_pfn, end_pfn);
  	if (pfn) { /* We have page on LRU */
  		ret = do_migrate_range(pfn, end_pfn);
  		if (!ret) {
  			drain = 1;
  			goto repeat;
  		} else {
  			if (ret < 0)
  				if (--retry_max == 0)
  					goto failed_removal;
  			yield();
  			drain = 1;
  			goto repeat;
  		}
  	}
  	/* drain all zone's lru pagevec, this is asyncronous... */
  	lru_add_drain_all();
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
904
905
  	yield();
  	/* drain pcp pages , this is synchrouns. */
9f8f21725   Christoph Lameter   Page allocator: c...
906
  	drain_all_pages();
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
907
908
909
910
911
912
913
914
915
916
917
  	/* check again */
  	offlined_pages = check_pages_isolated(start_pfn, end_pfn);
  	if (offlined_pages < 0) {
  		ret = -EBUSY;
  		goto failed_removal;
  	}
  	printk(KERN_INFO "Offlined Pages %ld
  ", offlined_pages);
  	/* Ok, all of our target is islaoted.
  	   We cannot do rollback at this point. */
  	offline_isolated_pages(start_pfn, end_pfn);
dbc0e4cef   KAMEZAWA Hiroyuki   memory hotremove:...
918
919
  	/* reset pagetype flags and makes migrate type to be MOVABLE */
  	undo_isolate_page_range(start_pfn, end_pfn);
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
920
  	/* removal success */
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
921
922
923
  	zone->present_pages -= offlined_pages;
  	zone->zone_pgdat->node_present_pages -= offlined_pages;
  	totalram_pages -= offlined_pages;
7b78d335a   Yasunori Goto   memory hotplug: r...
924

1b79acc91   KOSAKI Motohiro   mm, mem-hotplug: ...
925
  	init_per_zone_wmark_min();
8fe23e057   David Rientjes   mm: clear node in...
926
927
928
929
  	if (!node_present_pages(node)) {
  		node_clear_state(node, N_HIGH_MEMORY);
  		kswapd_stop(node);
  	}
bce7394a3   Minchan Kim   page-allocator: r...
930

0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
931
932
  	vm_total_pages = nr_free_pagecache_pages();
  	writeback_set_ratelimit();
7b78d335a   Yasunori Goto   memory hotplug: r...
933
934
  
  	memory_notify(MEM_OFFLINE, &arg);
20d6c96b5   KOSAKI Motohiro   mem-hotplug: intr...
935
  	unlock_memory_hotplug();
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
936
937
938
939
940
941
  	return 0;
  
  failed_removal:
  	printk(KERN_INFO "memory offlining %lx to %lx failed
  ",
  		start_pfn, end_pfn);
7b78d335a   Yasunori Goto   memory hotplug: r...
942
  	memory_notify(MEM_CANCEL_OFFLINE, &arg);
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
943
944
  	/* pushback to free area */
  	undo_isolate_page_range(start_pfn, end_pfn);
7b78d335a   Yasunori Goto   memory hotplug: r...
945

6ad696d2c   Andi Kleen   mm: allow memory ...
946
  out:
20d6c96b5   KOSAKI Motohiro   mem-hotplug: intr...
947
  	unlock_memory_hotplug();
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
948
949
  	return ret;
  }
71088785c   Badari Pulavarty   mm: cleanup to ma...
950
951
952
953
954
955
956
957
958
  
  int remove_memory(u64 start, u64 size)
  {
  	unsigned long start_pfn, end_pfn;
  
  	start_pfn = PFN_DOWN(start);
  	end_pfn = start_pfn + PFN_DOWN(size);
  	return offline_pages(start_pfn, end_pfn, 120 * HZ);
  }
48e94196a   KAMEZAWA Hiroyuki   fix memory hot re...
959
960
961
962
963
  #else
  int remove_memory(u64 start, u64 size)
  {
  	return -EINVAL;
  }
0c0e61958   KAMEZAWA Hiroyuki   memory unplug: pa...
964
  #endif /* CONFIG_MEMORY_HOTREMOVE */
71088785c   Badari Pulavarty   mm: cleanup to ma...
965
  EXPORT_SYMBOL_GPL(remove_memory);