Blame view

mm/page_isolation.c 8.02 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
2
3
4
  /*
   * linux/mm/page_isolation.c
   */
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
5
6
7
  #include <linux/mm.h>
  #include <linux/page-isolation.h>
  #include <linux/pageblock-flags.h>
ee6f509c3   Minchan Kim   mm: factor out me...
8
  #include <linux/memory.h>
c8721bbbd   Naoya Horiguchi   mm: memory-hotplu...
9
  #include <linux/hugetlb.h>
83358ece2   Joonsoo Kim   mm/page_owner: in...
10
  #include <linux/page_owner.h>
8b9132388   Michal Hocko   mm: unify new_nod...
11
  #include <linux/migrate.h>
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
12
  #include "internal.h"
0f0848e51   Joonsoo Kim   mm/page_isolation...
13
14
  #define CREATE_TRACE_POINTS
  #include <trace/events/page_isolation.h>
c5b4e1b02   Naoya Horiguchi   mm, page_isolatio...
15
16
  static int set_migratetype_isolate(struct page *page,
  				bool skip_hwpoisoned_pages)
ee6f509c3   Minchan Kim   mm: factor out me...
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
46
47
48
49
50
51
  {
  	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...
52
53
  	if (!has_unmovable_pages(zone, page, arg.pages_found,
  				 skip_hwpoisoned_pages))
ee6f509c3   Minchan Kim   mm: factor out me...
54
55
56
  		ret = 0;
  
  	/*
ac34dcd26   Yisheng Xie   mm/page_isolation...
57
  	 * immobile means "not-on-lru" pages. If immobile is larger than
ee6f509c3   Minchan Kim   mm: factor out me...
58
59
60
61
62
  	 * removable-by-driver pages reported by notifier, we'll fail.
  	 */
  
  out:
  	if (!ret) {
2139cbe62   Bartlomiej Zolnierkiewicz   cma: fix counting...
63
  		unsigned long nr_pages;
d1ce749a0   Bartlomiej Zolnierkiewicz   cma: count free C...
64
  		int migratetype = get_pageblock_migratetype(page);
2139cbe62   Bartlomiej Zolnierkiewicz   cma: fix counting...
65

a458431e1   Bartlomiej Zolnierkiewicz   mm: fix zone_wate...
66
  		set_pageblock_migratetype(page, MIGRATE_ISOLATE);
ad53f92eb   Joonsoo Kim   mm/page_alloc: fi...
67
  		zone->nr_isolate_pageblock++;
02aa0cdd7   Vlastimil Babka   mm, page_alloc: c...
68
69
  		nr_pages = move_freepages_block(zone, page, MIGRATE_ISOLATE,
  									NULL);
2139cbe62   Bartlomiej Zolnierkiewicz   cma: fix counting...
70

d1ce749a0   Bartlomiej Zolnierkiewicz   cma: count free C...
71
  		__mod_zone_freepage_state(zone, -nr_pages, migratetype);
ee6f509c3   Minchan Kim   mm: factor out me...
72
73
74
75
  	}
  
  	spin_unlock_irqrestore(&zone->lock, flags);
  	if (!ret)
ec25af84b   Vlastimil Babka   mm, page_isolatio...
76
  		drain_all_pages(zone);
ee6f509c3   Minchan Kim   mm: factor out me...
77
78
  	return ret;
  }
c5b4e1b02   Naoya Horiguchi   mm, page_isolatio...
79
  static void unset_migratetype_isolate(struct page *page, unsigned migratetype)
ee6f509c3   Minchan Kim   mm: factor out me...
80
81
  {
  	struct zone *zone;
2139cbe62   Bartlomiej Zolnierkiewicz   cma: fix counting...
82
  	unsigned long flags, nr_pages;
e3a2713c3   Joonsoo Kim   mm/page_isolation...
83
  	bool isolated_page = false;
3c605096d   Joonsoo Kim   mm/page_alloc: re...
84
  	unsigned int order;
76741e776   Vlastimil Babka   mm, page_alloc: d...
85
  	unsigned long pfn, buddy_pfn;
3c605096d   Joonsoo Kim   mm/page_alloc: re...
86
  	struct page *buddy;
2139cbe62   Bartlomiej Zolnierkiewicz   cma: fix counting...
87

ee6f509c3   Minchan Kim   mm: factor out me...
88
89
  	zone = page_zone(page);
  	spin_lock_irqsave(&zone->lock, flags);
bbf9ce971   Xishi Qiu   mm: use is_migrat...
90
  	if (!is_migrate_isolate_page(page))
ee6f509c3   Minchan Kim   mm: factor out me...
91
  		goto out;
3c605096d   Joonsoo Kim   mm/page_alloc: re...
92
93
94
95
96
97
98
99
100
101
102
103
  
  	/*
  	 * Because freepage with more than pageblock_order on isolated
  	 * pageblock is restricted to merge due to freepage counting problem,
  	 * it is possible that there is free buddy page.
  	 * move_freepages_block() doesn't care of merge so we need other
  	 * approach in order to merge them. Isolation and free will make
  	 * these pages to be merged.
  	 */
  	if (PageBuddy(page)) {
  		order = page_order(page);
  		if (order >= pageblock_order) {
76741e776   Vlastimil Babka   mm, page_alloc: d...
104
105
106
  			pfn = page_to_pfn(page);
  			buddy_pfn = __find_buddy_pfn(pfn, order);
  			buddy = page + (buddy_pfn - pfn);
3c605096d   Joonsoo Kim   mm/page_alloc: re...
107

13ad59df6   Vlastimil Babka   mm, page_alloc: a...
108
  			if (pfn_valid_within(buddy_pfn) &&
1ae7013df   Hui Zhu   CMA: page_isolati...
109
  			    !is_migrate_isolate_page(buddy)) {
3c605096d   Joonsoo Kim   mm/page_alloc: re...
110
  				__isolate_free_page(page, order);
e3a2713c3   Joonsoo Kim   mm/page_isolation...
111
  				isolated_page = true;
3c605096d   Joonsoo Kim   mm/page_alloc: re...
112
113
114
115
116
117
118
119
120
121
  			}
  		}
  	}
  
  	/*
  	 * If we isolate freepage with more than pageblock_order, there
  	 * should be no freepage in the range, so we could avoid costly
  	 * pageblock scanning for freepage moving.
  	 */
  	if (!isolated_page) {
02aa0cdd7   Vlastimil Babka   mm, page_alloc: c...
122
  		nr_pages = move_freepages_block(zone, page, migratetype, NULL);
3c605096d   Joonsoo Kim   mm/page_alloc: re...
123
124
  		__mod_zone_freepage_state(zone, nr_pages, migratetype);
  	}
a458431e1   Bartlomiej Zolnierkiewicz   mm: fix zone_wate...
125
  	set_pageblock_migratetype(page, migratetype);
ad53f92eb   Joonsoo Kim   mm/page_alloc: fi...
126
  	zone->nr_isolate_pageblock--;
ee6f509c3   Minchan Kim   mm: factor out me...
127
128
  out:
  	spin_unlock_irqrestore(&zone->lock, flags);
83358ece2   Joonsoo Kim   mm/page_owner: in...
129
  	if (isolated_page) {
46f24fd85   Joonsoo Kim   mm/page_alloc: in...
130
  		post_alloc_hook(page, order, __GFP_MOVABLE);
e3a2713c3   Joonsoo Kim   mm/page_isolation...
131
  		__free_pages(page, order);
83358ece2   Joonsoo Kim   mm/page_owner: in...
132
  	}
ee6f509c3   Minchan Kim   mm: factor out me...
133
  }
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
134
135
136
137
  static inline struct page *
  __first_valid_page(unsigned long pfn, unsigned long nr_pages)
  {
  	int i;
2ce13640b   Michal Hocko   mm: __first_valid...
138
139
140
141
142
143
144
145
146
147
148
149
  
  	for (i = 0; i < nr_pages; i++) {
  		struct page *page;
  
  		if (!pfn_valid_within(pfn + i))
  			continue;
  		page = pfn_to_online_page(pfn + i);
  		if (!page)
  			continue;
  		return page;
  	}
  	return NULL;
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
150
151
152
153
154
155
156
  }
  
  /*
   * 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...
157
   * @migratetype: migrate type to set in error recovery.
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
158
159
160
161
162
163
164
165
   *
   * 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...
166
  int start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
b023f4681   Wen Congyang   memory-hotplug: s...
167
  			     unsigned migratetype, bool skip_hwpoisoned_pages)
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
168
169
170
171
  {
  	unsigned long pfn;
  	unsigned long undo_pfn;
  	struct page *page;
fec174d66   Naoya Horiguchi   mm/page_isolation...
172
173
  	BUG_ON(!IS_ALIGNED(start_pfn, pageblock_nr_pages));
  	BUG_ON(!IS_ALIGNED(end_pfn, pageblock_nr_pages));
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
174
175
176
177
178
  
  	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...
179
180
  		if (page &&
  		    set_migratetype_isolate(page, skip_hwpoisoned_pages)) {
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
181
182
183
184
185
186
187
  			undo_pfn = pfn;
  			goto undo;
  		}
  	}
  	return 0;
  undo:
  	for (pfn = start_pfn;
dbc0e4cef   KAMEZAWA Hiroyuki   memory hotremove:...
188
  	     pfn < undo_pfn;
2ce13640b   Michal Hocko   mm: __first_valid...
189
190
191
192
193
194
  	     pfn += pageblock_nr_pages) {
  		struct page *page = pfn_to_online_page(pfn);
  		if (!page)
  			continue;
  		unset_migratetype_isolate(page, migratetype);
  	}
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
195
196
197
198
199
200
201
  
  	return -EBUSY;
  }
  
  /*
   * Make isolated pages available again.
   */
0815f3d81   Michal Nazarewicz   mm: page_isolatio...
202
203
  int undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
  			    unsigned migratetype)
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
204
205
206
  {
  	unsigned long pfn;
  	struct page *page;
6f8d2b8a2   Wang Xiaoqiang   mm/page_isolation...
207
208
209
  
  	BUG_ON(!IS_ALIGNED(start_pfn, pageblock_nr_pages));
  	BUG_ON(!IS_ALIGNED(end_pfn, pageblock_nr_pages));
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
210
211
212
213
  	for (pfn = start_pfn;
  	     pfn < end_pfn;
  	     pfn += pageblock_nr_pages) {
  		page = __first_valid_page(pfn, pageblock_nr_pages);
bbf9ce971   Xishi Qiu   mm: use is_migrat...
214
  		if (!page || !is_migrate_isolate_page(page))
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
215
  			continue;
0815f3d81   Michal Nazarewicz   mm: page_isolatio...
216
  		unset_migratetype_isolate(page, migratetype);
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
217
218
219
220
221
222
223
224
  	}
  	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.
   *
ec3b68825   Neil Zhang   mm/page_isolation...
225
   * Returns the last tested pfn.
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
226
   */
fea85cff1   Joonsoo Kim   mm/page_isolation...
227
  static unsigned long
b023f4681   Wen Congyang   memory-hotplug: s...
228
229
  __test_page_isolated_in_pageblock(unsigned long pfn, unsigned long end_pfn,
  				  bool skip_hwpoisoned_pages)
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
230
231
232
233
234
235
236
237
238
  {
  	struct page *page;
  
  	while (pfn < end_pfn) {
  		if (!pfn_valid_within(pfn)) {
  			pfn++;
  			continue;
  		}
  		page = pfn_to_page(pfn);
aa016d145   Vlastimil Babka   mm, page_isolatio...
239
  		if (PageBuddy(page))
435b405c0   Minchan Kim   memory-hotplug: f...
240
  			/*
aa016d145   Vlastimil Babka   mm, page_isolatio...
241
242
243
  			 * If the page is on a free list, it has to be on
  			 * the correct MIGRATE_ISOLATE freelist. There is no
  			 * simple way to verify that as VM_BUG_ON(), though.
435b405c0   Minchan Kim   memory-hotplug: f...
244
  			 */
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
245
  			pfn += 1 << page_order(page);
aa016d145   Vlastimil Babka   mm, page_isolatio...
246
247
  		else if (skip_hwpoisoned_pages && PageHWPoison(page))
  			/* A HWPoisoned page cannot be also PageBuddy */
b023f4681   Wen Congyang   memory-hotplug: s...
248
  			pfn++;
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
249
250
251
  		else
  			break;
  	}
fea85cff1   Joonsoo Kim   mm/page_isolation...
252
253
  
  	return pfn;
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
254
  }
b9eb63191   Joonsoo Kim   mm/memory_hotplug...
255
  /* Caller should ensure that requested range is in a single zone */
b023f4681   Wen Congyang   memory-hotplug: s...
256
257
  int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn,
  			bool skip_hwpoisoned_pages)
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
258
  {
6c1b7f680   Gerald Schaefer   memory hotplug: m...
259
  	unsigned long pfn, flags;
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
260
  	struct page *page;
6c1b7f680   Gerald Schaefer   memory hotplug: m...
261
  	struct zone *zone;
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
262

a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
263
  	/*
85dbe7060   Tang Chen   page_isolation: F...
264
265
266
  	 * 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...
267
268
269
  	 */
  	for (pfn = start_pfn; pfn < end_pfn; pfn += pageblock_nr_pages) {
  		page = __first_valid_page(pfn, pageblock_nr_pages);
bbf9ce971   Xishi Qiu   mm: use is_migrat...
270
  		if (page && !is_migrate_isolate_page(page))
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
271
272
  			break;
  	}
a70dcb969   Gerald Schaefer   memory hotplug: f...
273
274
  	page = __first_valid_page(start_pfn, end_pfn - start_pfn);
  	if ((pfn < end_pfn) || !page)
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
275
  		return -EBUSY;
85dbe7060   Tang Chen   page_isolation: F...
276
  	/* Check all pages are free or marked as ISOLATED */
a70dcb969   Gerald Schaefer   memory hotplug: f...
277
  	zone = page_zone(page);
6c1b7f680   Gerald Schaefer   memory hotplug: m...
278
  	spin_lock_irqsave(&zone->lock, flags);
fea85cff1   Joonsoo Kim   mm/page_isolation...
279
  	pfn = __test_page_isolated_in_pageblock(start_pfn, end_pfn,
b023f4681   Wen Congyang   memory-hotplug: s...
280
  						skip_hwpoisoned_pages);
6c1b7f680   Gerald Schaefer   memory hotplug: m...
281
  	spin_unlock_irqrestore(&zone->lock, flags);
fea85cff1   Joonsoo Kim   mm/page_isolation...
282

0f0848e51   Joonsoo Kim   mm/page_isolation...
283
  	trace_test_pages_isolated(start_pfn, end_pfn, pfn);
fea85cff1   Joonsoo Kim   mm/page_isolation...
284
  	return pfn < end_pfn ? -EBUSY : 0;
a5d76b54a   KAMEZAWA Hiroyuki   memory unplug: pa...
285
  }
723a0644a   Minchan Kim   mm/page_alloc: re...
286
287
288
289
  
  struct page *alloc_migrate_target(struct page *page, unsigned long private,
  				  int **resultp)
  {
8b9132388   Michal Hocko   mm: unify new_nod...
290
  	return new_page_nodemask(page, numa_node_id(), &node_states[N_MEMORY]);
723a0644a   Minchan Kim   mm/page_alloc: re...
291
  }