xref: /openbmc/linux/mm/page_isolation.c (revision dba8b469)
1 /*
2  * linux/mm/page_isolation.c
3  */
4 
5 #include <linux/mm.h>
6 #include <linux/page-isolation.h>
7 #include <linux/pageblock-flags.h>
8 #include "internal.h"
9 
10 static inline struct page *
11 __first_valid_page(unsigned long pfn, unsigned long nr_pages)
12 {
13 	int i;
14 	for (i = 0; i < nr_pages; i++)
15 		if (pfn_valid_within(pfn + i))
16 			break;
17 	if (unlikely(i == nr_pages))
18 		return NULL;
19 	return pfn_to_page(pfn + i);
20 }
21 
22 /*
23  * start_isolate_page_range() -- make page-allocation-type of range of pages
24  * to be MIGRATE_ISOLATE.
25  * @start_pfn: The lower PFN of the range to be isolated.
26  * @end_pfn: The upper PFN of the range to be isolated.
27  *
28  * Making page-allocation-type to be MIGRATE_ISOLATE means free pages in
29  * the range will never be allocated. Any free pages and pages freed in the
30  * future will not be allocated again.
31  *
32  * start_pfn/end_pfn must be aligned to pageblock_order.
33  * Returns 0 on success and -EBUSY if any part of range cannot be isolated.
34  */
35 int
36 start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn)
37 {
38 	unsigned long pfn;
39 	unsigned long undo_pfn;
40 	struct page *page;
41 
42 	BUG_ON((start_pfn) & (pageblock_nr_pages - 1));
43 	BUG_ON((end_pfn) & (pageblock_nr_pages - 1));
44 
45 	for (pfn = start_pfn;
46 	     pfn < end_pfn;
47 	     pfn += pageblock_nr_pages) {
48 		page = __first_valid_page(pfn, pageblock_nr_pages);
49 		if (page && set_migratetype_isolate(page)) {
50 			undo_pfn = pfn;
51 			goto undo;
52 		}
53 	}
54 	return 0;
55 undo:
56 	for (pfn = start_pfn;
57 	     pfn < undo_pfn;
58 	     pfn += pageblock_nr_pages)
59 		unset_migratetype_isolate(pfn_to_page(pfn));
60 
61 	return -EBUSY;
62 }
63 
64 /*
65  * Make isolated pages available again.
66  */
67 int
68 undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn)
69 {
70 	unsigned long pfn;
71 	struct page *page;
72 	BUG_ON((start_pfn) & (pageblock_nr_pages - 1));
73 	BUG_ON((end_pfn) & (pageblock_nr_pages - 1));
74 	for (pfn = start_pfn;
75 	     pfn < end_pfn;
76 	     pfn += pageblock_nr_pages) {
77 		page = __first_valid_page(pfn, pageblock_nr_pages);
78 		if (!page || get_pageblock_migratetype(page) != MIGRATE_ISOLATE)
79 			continue;
80 		unset_migratetype_isolate(page);
81 	}
82 	return 0;
83 }
84 /*
85  * Test all pages in the range is free(means isolated) or not.
86  * all pages in [start_pfn...end_pfn) must be in the same zone.
87  * zone->lock must be held before call this.
88  *
89  * Returns 1 if all pages in the range is isolated.
90  */
91 static int
92 __test_page_isolated_in_pageblock(unsigned long pfn, unsigned long end_pfn)
93 {
94 	struct page *page;
95 
96 	while (pfn < end_pfn) {
97 		if (!pfn_valid_within(pfn)) {
98 			pfn++;
99 			continue;
100 		}
101 		page = pfn_to_page(pfn);
102 		if (PageBuddy(page))
103 			pfn += 1 << page_order(page);
104 		else if (page_count(page) == 0 &&
105 				page_private(page) == MIGRATE_ISOLATE)
106 			pfn += 1;
107 		else
108 			break;
109 	}
110 	if (pfn < end_pfn)
111 		return 0;
112 	return 1;
113 }
114 
115 int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn)
116 {
117 	unsigned long pfn, flags;
118 	struct page *page;
119 	struct zone *zone;
120 	int ret;
121 
122 	/*
123 	 * Note: pageblock_nr_page != MAX_ORDER. Then, chunks of free page
124 	 * is not aligned to pageblock_nr_pages.
125 	 * Then we just check pagetype fist.
126 	 */
127 	for (pfn = start_pfn; pfn < end_pfn; pfn += pageblock_nr_pages) {
128 		page = __first_valid_page(pfn, pageblock_nr_pages);
129 		if (page && get_pageblock_migratetype(page) != MIGRATE_ISOLATE)
130 			break;
131 	}
132 	page = __first_valid_page(start_pfn, end_pfn - start_pfn);
133 	if ((pfn < end_pfn) || !page)
134 		return -EBUSY;
135 	/* Check all pages are free or Marked as ISOLATED */
136 	zone = page_zone(page);
137 	spin_lock_irqsave(&zone->lock, flags);
138 	ret = __test_page_isolated_in_pageblock(start_pfn, end_pfn);
139 	spin_unlock_irqrestore(&zone->lock, flags);
140 	return ret ? 0 : -EBUSY;
141 }
142