Blame view

mm/page_isolation.c 7.48 KB
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
1
2
3
  /*
   * linux/mm/page_isolation.c
   */
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
4
5
6
  #include <linux/mm.h>
  #include <linux/page-isolation.h>
  #include <linux/pageblock-flags.h>
ee6f509c3   Minchan Kim   mm: factor out me...
7
  #include <linux/memory.h>
c8721bbbd   Naoya Horiguchi   mm: memory-hotplu...
8
  #include <linux/hugetlb.h>
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
9
  #include "internal.h"
b023f4681   Wen Congyang   memory-hotplug: s...
10
  int set_migratetype_isolate(struct page *page, bool skip_hwpoisoned_pages)
ee6f509c3   Minchan Kim   mm: factor out me...
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  {
  	struct zone *zone;
  	unsigned long flags, pfn;
  	struct memory_isolate_notify arg;
  	int notifier_ret;
  	int ret = -EBUSY;
  
  	zone = page_zone(page);
  
  	spin_lock_irqsave(&zone->lock, flags);
  
  	pfn = page_to_pfn(page);
  	arg.start_pfn = pfn;
  	arg.nr_pages = pageblock_nr_pages;
  	arg.pages_found = 0;
  
  	/*
  	 * It may be possible to isolate a pageblock even if the
  	 * migratetype is not MIGRATE_MOVABLE. The memory isolation
  	 * notifier chain is used by balloon drivers to return the
  	 * number of pages in a range that are held by the balloon
  	 * driver to shrink memory. If all the pages are accounted for
  	 * by balloons, are free, or on the LRU, isolation can continue.
  	 * Later, for example, when memory hotplug notifier runs, these
  	 * pages reported as "can be isolated" should be isolated(freed)
  	 * by the balloon driver through the memory notifier chain.
  	 */
  	notifier_ret = memory_isolate_notify(MEM_ISOLATE_COUNT, &arg);
  	notifier_ret = notifier_to_errno(notifier_ret);
  	if (notifier_ret)
  		goto out;
  	/*
  	 * FIXME: Now, memory hotplug doesn't call shrink_slab() by itself.
  	 * We just check MOVABLE pages.
  	 */
b023f4681   Wen Congyang   memory-hotplug: s...
46
47
  	if (!has_unmovable_pages(zone, page, arg.pages_found,
  				 skip_hwpoisoned_pages))
ee6f509c3   Minchan Kim   mm: factor out me...
48
49
50
51
52
53
54
55
56
  		ret = 0;
  
  	/*
  	 * immobile means "not-on-lru" paes. If immobile is larger than
  	 * removable-by-driver pages reported by notifier, we'll fail.
  	 */
  
  out:
  	if (!ret) {
2139cbe62   Bartlomiej Zolnierkiewicz   cma: fix counting...
57
  		unsigned long nr_pages;
d1ce749a0   Bartlomiej Zolnierkiewicz   cma: count free C...
58
  		int migratetype = get_pageblock_migratetype(page);
2139cbe62   Bartlomiej Zolnierkiewicz   cma: fix counting...
59

a458431e1   Bartlomiej Zolnierkiewicz   mm: fix zone_wate...
60
  		set_pageblock_migratetype(page, MIGRATE_ISOLATE);
2139cbe62   Bartlomiej Zolnierkiewicz   cma: fix counting...
61
  		nr_pages = move_freepages_block(zone, page, MIGRATE_ISOLATE);
d1ce749a0   Bartlomiej Zolnierkiewicz   cma: count free C...
62
  		__mod_zone_freepage_state(zone, -nr_pages, migratetype);
ee6f509c3   Minchan Kim   mm: factor out me...
63
64
65
66
67
68
69
70
71
72
73
  	}
  
  	spin_unlock_irqrestore(&zone->lock, flags);
  	if (!ret)
  		drain_all_pages();
  	return ret;
  }
  
  void unset_migratetype_isolate(struct page *page, unsigned migratetype)
  {
  	struct zone *zone;
2139cbe62   Bartlomiej Zolnierkiewicz   cma: fix counting...
74
  	unsigned long flags, nr_pages;
ee6f509c3   Minchan Kim   mm: factor out me...
75
76
77
78
  	zone = page_zone(page);
  	spin_lock_irqsave(&zone->lock, flags);
  	if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE)
  		goto out;
2139cbe62   Bartlomiej Zolnierkiewicz   cma: fix counting...
79
  	nr_pages = move_freepages_block(zone, page, migratetype);
d1ce749a0   Bartlomiej Zolnierkiewicz   cma: count free C...
80
  	__mod_zone_freepage_state(zone, nr_pages, migratetype);
a458431e1   Bartlomiej Zolnierkiewicz   mm: fix zone_wate...
81
  	set_pageblock_migratetype(page, migratetype);
ee6f509c3   Minchan Kim   mm: factor out me...
82
83
84
  out:
  	spin_unlock_irqrestore(&zone->lock, flags);
  }
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
  static inline struct page *
  __first_valid_page(unsigned long pfn, unsigned long nr_pages)
  {
  	int i;
  	for (i = 0; i < nr_pages; i++)
  		if (pfn_valid_within(pfn + i))
  			break;
  	if (unlikely(i == nr_pages))
  		return NULL;
  	return pfn_to_page(pfn + i);
  }
  
  /*
   * start_isolate_page_range() -- make page-allocation-type of range of pages
   * to be MIGRATE_ISOLATE.
   * @start_pfn: The lower PFN of the range to be isolated.
   * @end_pfn: The upper PFN of the range to be isolated.
0815f3d81   Michal Nazarewicz   mm: page_isolatio...
102
   * @migratetype: migrate type to set in error recovery.
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
103
104
105
106
107
108
109
110
   *
   * Making page-allocation-type to be MIGRATE_ISOLATE means free pages in
   * the range will never be allocated. Any free pages and pages freed in the
   * future will not be allocated again.
   *
   * start_pfn/end_pfn must be aligned to pageblock_order.
   * Returns 0 on success and -EBUSY if any part of range cannot be isolated.
   */
0815f3d81   Michal Nazarewicz   mm: page_isolatio...
111
  int start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
b023f4681   Wen Congyang   memory-hotplug: s...
112
  			     unsigned migratetype, bool skip_hwpoisoned_pages)
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
113
114
115
116
117
118
119
120
121
122
123
124
  {
  	unsigned long pfn;
  	unsigned long undo_pfn;
  	struct page *page;
  
  	BUG_ON((start_pfn) & (pageblock_nr_pages - 1));
  	BUG_ON((end_pfn) & (pageblock_nr_pages - 1));
  
  	for (pfn = start_pfn;
  	     pfn < end_pfn;
  	     pfn += pageblock_nr_pages) {
  		page = __first_valid_page(pfn, pageblock_nr_pages);
b023f4681   Wen Congyang   memory-hotplug: s...
125
126
  		if (page &&
  		    set_migratetype_isolate(page, skip_hwpoisoned_pages)) {
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
127
128
129
130
131
132
133
  			undo_pfn = pfn;
  			goto undo;
  		}
  	}
  	return 0;
  undo:
  	for (pfn = start_pfn;
dbc0e4cef   KAMEZAWA Hiroyuki   memory hotremove:...
134
  	     pfn < undo_pfn;
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
135
  	     pfn += pageblock_nr_pages)
0815f3d81   Michal Nazarewicz   mm: page_isolatio...
136
  		unset_migratetype_isolate(pfn_to_page(pfn), migratetype);
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
137
138
139
140
141
142
143
  
  	return -EBUSY;
  }
  
  /*
   * Make isolated pages available again.
   */
0815f3d81   Michal Nazarewicz   mm: page_isolatio...
144
145
  int undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
  			    unsigned migratetype)
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
146
147
148
149
150
151
152
153
154
  {
  	unsigned long pfn;
  	struct page *page;
  	BUG_ON((start_pfn) & (pageblock_nr_pages - 1));
  	BUG_ON((end_pfn) & (pageblock_nr_pages - 1));
  	for (pfn = start_pfn;
  	     pfn < end_pfn;
  	     pfn += pageblock_nr_pages) {
  		page = __first_valid_page(pfn, pageblock_nr_pages);
dbc0e4cef   KAMEZAWA Hiroyuki   memory hotremove:...
155
  		if (!page || get_pageblock_migratetype(page) != MIGRATE_ISOLATE)
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
156
  			continue;
0815f3d81   Michal Nazarewicz   mm: page_isolatio...
157
  		unset_migratetype_isolate(page, migratetype);
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
158
159
160
161
162
163
164
165
  	}
  	return 0;
  }
  /*
   * Test all pages in the range is free(means isolated) or not.
   * all pages in [start_pfn...end_pfn) must be in the same zone.
   * zone->lock must be held before call this.
   *
0815f3d81   Michal Nazarewicz   mm: page_isolatio...
166
   * Returns 1 if all pages in the range are isolated.
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
167
168
   */
  static int
b023f4681   Wen Congyang   memory-hotplug: s...
169
170
  __test_page_isolated_in_pageblock(unsigned long pfn, unsigned long end_pfn,
  				  bool skip_hwpoisoned_pages)
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
171
172
173
174
175
176
177
178
179
  {
  	struct page *page;
  
  	while (pfn < end_pfn) {
  		if (!pfn_valid_within(pfn)) {
  			pfn++;
  			continue;
  		}
  		page = pfn_to_page(pfn);
41d575ad4   Minchan Kim   memory-hotplug: b...
180
  		if (PageBuddy(page)) {
435b405c0   Minchan Kim   memory-hotplug: f...
181
182
183
184
185
186
187
188
189
190
191
192
193
194
  			/*
  			 * If race between isolatation and allocation happens,
  			 * some free pages could be in MIGRATE_MOVABLE list
  			 * although pageblock's migratation type of the page
  			 * is MIGRATE_ISOLATE. Catch it and move the page into
  			 * MIGRATE_ISOLATE list.
  			 */
  			if (get_freepage_migratetype(page) != MIGRATE_ISOLATE) {
  				struct page *end_page;
  
  				end_page = page + (1 << page_order(page)) - 1;
  				move_freepages(page_zone(page), page, end_page,
  						MIGRATE_ISOLATE);
  			}
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
195
  			pfn += 1 << page_order(page);
41d575ad4   Minchan Kim   memory-hotplug: b...
196
  		}
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
197
  		else if (page_count(page) == 0 &&
b12c4ad14   Minchan Kim   mm: page_alloc: u...
198
  			get_freepage_migratetype(page) == MIGRATE_ISOLATE)
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
199
  			pfn += 1;
b023f4681   Wen Congyang   memory-hotplug: s...
200
201
202
203
204
205
206
207
  		else if (skip_hwpoisoned_pages && PageHWPoison(page)) {
  			/*
  			 * The HWPoisoned page may be not in buddy
  			 * system, and page_count() is not 0.
  			 */
  			pfn++;
  			continue;
  		}
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
208
209
210
211
212
213
214
  		else
  			break;
  	}
  	if (pfn < end_pfn)
  		return 0;
  	return 1;
  }
b023f4681   Wen Congyang   memory-hotplug: s...
215
216
  int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn,
  			bool skip_hwpoisoned_pages)
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
217
  {
6c1b7f680   Gerald Schaefer   memory hotplug: m...
218
  	unsigned long pfn, flags;
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
219
  	struct page *page;
6c1b7f680   Gerald Schaefer   memory hotplug: m...
220
221
  	struct zone *zone;
  	int ret;
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
222

a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
223
  	/*
85dbe7060   Tang Chen   page_isolation: F...
224
225
226
  	 * Note: pageblock_nr_pages != MAX_ORDER. Then, chunks of free pages
  	 * are not aligned to pageblock_nr_pages.
  	 * Then we just check migratetype first.
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
227
228
229
  	 */
  	for (pfn = start_pfn; pfn < end_pfn; pfn += pageblock_nr_pages) {
  		page = __first_valid_page(pfn, pageblock_nr_pages);
dbc0e4cef   KAMEZAWA Hiroyuki   memory hotremove:...
230
  		if (page && get_pageblock_migratetype(page) != MIGRATE_ISOLATE)
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
231
232
  			break;
  	}
a70dcb969   Gerald Schaefer   memory hotplug: f...
233
234
  	page = __first_valid_page(start_pfn, end_pfn - start_pfn);
  	if ((pfn < end_pfn) || !page)
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
235
  		return -EBUSY;
85dbe7060   Tang Chen   page_isolation: F...
236
  	/* Check all pages are free or marked as ISOLATED */
a70dcb969   Gerald Schaefer   memory hotplug: f...
237
  	zone = page_zone(page);
6c1b7f680   Gerald Schaefer   memory hotplug: m...
238
  	spin_lock_irqsave(&zone->lock, flags);
b023f4681   Wen Congyang   memory-hotplug: s...
239
240
  	ret = __test_page_isolated_in_pageblock(start_pfn, end_pfn,
  						skip_hwpoisoned_pages);
6c1b7f680   Gerald Schaefer   memory hotplug: m...
241
242
  	spin_unlock_irqrestore(&zone->lock, flags);
  	return ret ? 0 : -EBUSY;
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
243
  }
723a0644a   Minchan Kim   mm/page_alloc: re...
244
245
246
247
248
  
  struct page *alloc_migrate_target(struct page *page, unsigned long private,
  				  int **resultp)
  {
  	gfp_t gfp_mask = GFP_USER | __GFP_MOVABLE;
c8721bbbd   Naoya Horiguchi   mm: memory-hotplu...
249
250
251
252
253
254
255
256
257
258
259
260
  	/*
  	 * TODO: allocate a destination hugepage from a nearest neighbor node,
  	 * accordance with memory policy of the user process if possible. For
  	 * now as a simple work-around, we use the next node for destination.
  	 */
  	if (PageHuge(page)) {
  		nodemask_t src = nodemask_of_node(page_to_nid(page));
  		nodemask_t dst;
  		nodes_complement(dst, src);
  		return alloc_huge_page_node(page_hstate(compound_head(page)),
  					    next_node(page_to_nid(page), dst));
  	}
723a0644a   Minchan Kim   mm/page_alloc: re...
261
262
263
264
265
  	if (PageHighMem(page))
  		gfp_mask |= __GFP_HIGHMEM;
  
  	return alloc_page(gfp_mask);
  }