diff options
author | Michal Nazarewicz <mina86@mina86.com> | 2012-01-30 07:24:03 -0500 |
---|---|---|
committer | Marek Szyprowski <m.szyprowski@samsung.com> | 2012-05-21 09:09:29 -0400 |
commit | 85aa125f001f87f96a72e9e6ee515490843b1202 (patch) | |
tree | 4191169ebd6fa2a4463f9dd04fd3cb4af41ca814 /mm/compaction.c | |
parent | 03d44192f69a45d780ba124f691e76020a44ebae (diff) |
mm: compaction: introduce isolate_freepages_range()
This commit introduces isolate_freepages_range() function which
generalises isolate_freepages_block() so that it can be used on
arbitrary PFN ranges.
isolate_freepages_block() is left with only minor changes.
Signed-off-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Acked-by: Mel Gorman <mel@csn.ul.ie>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Tested-by: Rob Clark <rob.clark@linaro.org>
Tested-by: Ohad Ben-Cohen <ohad@wizery.com>
Tested-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
Tested-by: Robert Nelson <robertcnelson@gmail.com>
Tested-by: Barry Song <Baohua.Song@csr.com>
Diffstat (limited to 'mm/compaction.c')
-rw-r--r-- | mm/compaction.c | 111 |
1 files changed, 93 insertions, 18 deletions
diff --git a/mm/compaction.c b/mm/compaction.c index d9d7b35d3933..06b198fa9abe 100644 --- a/mm/compaction.c +++ b/mm/compaction.c | |||
@@ -54,24 +54,20 @@ static unsigned long release_freepages(struct list_head *freelist) | |||
54 | return count; | 54 | return count; |
55 | } | 55 | } |
56 | 56 | ||
57 | /* Isolate free pages onto a private freelist. Must hold zone->lock */ | 57 | /* |
58 | static unsigned long isolate_freepages_block(struct zone *zone, | 58 | * Isolate free pages onto a private freelist. Caller must hold zone->lock. |
59 | unsigned long blockpfn, | 59 | * If @strict is true, will abort returning 0 on any invalid PFNs or non-free |
60 | struct list_head *freelist) | 60 | * pages inside of the pageblock (even though it may still end up isolating |
61 | * some pages). | ||
62 | */ | ||
63 | static unsigned long isolate_freepages_block(unsigned long blockpfn, | ||
64 | unsigned long end_pfn, | ||
65 | struct list_head *freelist, | ||
66 | bool strict) | ||
61 | { | 67 | { |
62 | unsigned long zone_end_pfn, end_pfn; | ||
63 | int nr_scanned = 0, total_isolated = 0; | 68 | int nr_scanned = 0, total_isolated = 0; |
64 | struct page *cursor; | 69 | struct page *cursor; |
65 | 70 | ||
66 | /* Get the last PFN we should scan for free pages at */ | ||
67 | zone_end_pfn = zone->zone_start_pfn + zone->spanned_pages; | ||
68 | end_pfn = min(blockpfn + pageblock_nr_pages, zone_end_pfn); | ||
69 | |||
70 | /* Find the first usable PFN in the block to initialse page cursor */ | ||
71 | for (; blockpfn < end_pfn; blockpfn++) { | ||
72 | if (pfn_valid_within(blockpfn)) | ||
73 | break; | ||
74 | } | ||
75 | cursor = pfn_to_page(blockpfn); | 71 | cursor = pfn_to_page(blockpfn); |
76 | 72 | ||
77 | /* Isolate free pages. This assumes the block is valid */ | 73 | /* Isolate free pages. This assumes the block is valid */ |
@@ -79,15 +75,23 @@ static unsigned long isolate_freepages_block(struct zone *zone, | |||
79 | int isolated, i; | 75 | int isolated, i; |
80 | struct page *page = cursor; | 76 | struct page *page = cursor; |
81 | 77 | ||
82 | if (!pfn_valid_within(blockpfn)) | 78 | if (!pfn_valid_within(blockpfn)) { |
79 | if (strict) | ||
80 | return 0; | ||
83 | continue; | 81 | continue; |
82 | } | ||
84 | nr_scanned++; | 83 | nr_scanned++; |
85 | 84 | ||
86 | if (!PageBuddy(page)) | 85 | if (!PageBuddy(page)) { |
86 | if (strict) | ||
87 | return 0; | ||
87 | continue; | 88 | continue; |
89 | } | ||
88 | 90 | ||
89 | /* Found a free page, break it into order-0 pages */ | 91 | /* Found a free page, break it into order-0 pages */ |
90 | isolated = split_free_page(page); | 92 | isolated = split_free_page(page); |
93 | if (!isolated && strict) | ||
94 | return 0; | ||
91 | total_isolated += isolated; | 95 | total_isolated += isolated; |
92 | for (i = 0; i < isolated; i++) { | 96 | for (i = 0; i < isolated; i++) { |
93 | list_add(&page->lru, freelist); | 97 | list_add(&page->lru, freelist); |
@@ -105,6 +109,73 @@ static unsigned long isolate_freepages_block(struct zone *zone, | |||
105 | return total_isolated; | 109 | return total_isolated; |
106 | } | 110 | } |
107 | 111 | ||
112 | /** | ||
113 | * isolate_freepages_range() - isolate free pages. | ||
114 | * @start_pfn: The first PFN to start isolating. | ||
115 | * @end_pfn: The one-past-last PFN. | ||
116 | * | ||
117 | * Non-free pages, invalid PFNs, or zone boundaries within the | ||
118 | * [start_pfn, end_pfn) range are considered errors, cause function to | ||
119 | * undo its actions and return zero. | ||
120 | * | ||
121 | * Otherwise, function returns one-past-the-last PFN of isolated page | ||
122 | * (which may be greater then end_pfn if end fell in a middle of | ||
123 | * a free page). | ||
124 | */ | ||
125 | static unsigned long | ||
126 | isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn) | ||
127 | { | ||
128 | unsigned long isolated, pfn, block_end_pfn, flags; | ||
129 | struct zone *zone = NULL; | ||
130 | LIST_HEAD(freelist); | ||
131 | |||
132 | if (pfn_valid(start_pfn)) | ||
133 | zone = page_zone(pfn_to_page(start_pfn)); | ||
134 | |||
135 | for (pfn = start_pfn; pfn < end_pfn; pfn += isolated) { | ||
136 | if (!pfn_valid(pfn) || zone != page_zone(pfn_to_page(pfn))) | ||
137 | break; | ||
138 | |||
139 | /* | ||
140 | * On subsequent iterations ALIGN() is actually not needed, | ||
141 | * but we keep it that we not to complicate the code. | ||
142 | */ | ||
143 | block_end_pfn = ALIGN(pfn + 1, pageblock_nr_pages); | ||
144 | block_end_pfn = min(block_end_pfn, end_pfn); | ||
145 | |||
146 | spin_lock_irqsave(&zone->lock, flags); | ||
147 | isolated = isolate_freepages_block(pfn, block_end_pfn, | ||
148 | &freelist, true); | ||
149 | spin_unlock_irqrestore(&zone->lock, flags); | ||
150 | |||
151 | /* | ||
152 | * In strict mode, isolate_freepages_block() returns 0 if | ||
153 | * there are any holes in the block (ie. invalid PFNs or | ||
154 | * non-free pages). | ||
155 | */ | ||
156 | if (!isolated) | ||
157 | break; | ||
158 | |||
159 | /* | ||
160 | * If we managed to isolate pages, it is always (1 << n) * | ||
161 | * pageblock_nr_pages for some non-negative n. (Max order | ||
162 | * page may span two pageblocks). | ||
163 | */ | ||
164 | } | ||
165 | |||
166 | /* split_free_page does not map the pages */ | ||
167 | map_pages(&freelist); | ||
168 | |||
169 | if (pfn < end_pfn) { | ||
170 | /* Loop terminated early, cleanup. */ | ||
171 | release_freepages(&freelist); | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | /* We don't use freelists for anything. */ | ||
176 | return pfn; | ||
177 | } | ||
178 | |||
108 | /* Returns true if the page is within a block suitable for migration to */ | 179 | /* Returns true if the page is within a block suitable for migration to */ |
109 | static bool suitable_migration_target(struct page *page) | 180 | static bool suitable_migration_target(struct page *page) |
110 | { | 181 | { |
@@ -145,7 +216,7 @@ static void isolate_freepages(struct zone *zone, | |||
145 | struct compact_control *cc) | 216 | struct compact_control *cc) |
146 | { | 217 | { |
147 | struct page *page; | 218 | struct page *page; |
148 | unsigned long high_pfn, low_pfn, pfn; | 219 | unsigned long high_pfn, low_pfn, pfn, zone_end_pfn, end_pfn; |
149 | unsigned long flags; | 220 | unsigned long flags; |
150 | int nr_freepages = cc->nr_freepages; | 221 | int nr_freepages = cc->nr_freepages; |
151 | struct list_head *freelist = &cc->freepages; | 222 | struct list_head *freelist = &cc->freepages; |
@@ -165,6 +236,8 @@ static void isolate_freepages(struct zone *zone, | |||
165 | */ | 236 | */ |
166 | high_pfn = min(low_pfn, pfn); | 237 | high_pfn = min(low_pfn, pfn); |
167 | 238 | ||
239 | zone_end_pfn = zone->zone_start_pfn + zone->spanned_pages; | ||
240 | |||
168 | /* | 241 | /* |
169 | * Isolate free pages until enough are available to migrate the | 242 | * Isolate free pages until enough are available to migrate the |
170 | * pages on cc->migratepages. We stop searching if the migrate | 243 | * pages on cc->migratepages. We stop searching if the migrate |
@@ -201,7 +274,9 @@ static void isolate_freepages(struct zone *zone, | |||
201 | isolated = 0; | 274 | isolated = 0; |
202 | spin_lock_irqsave(&zone->lock, flags); | 275 | spin_lock_irqsave(&zone->lock, flags); |
203 | if (suitable_migration_target(page)) { | 276 | if (suitable_migration_target(page)) { |
204 | isolated = isolate_freepages_block(zone, pfn, freelist); | 277 | end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn); |
278 | isolated = isolate_freepages_block(pfn, end_pfn, | ||
279 | freelist, false); | ||
205 | nr_freepages += isolated; | 280 | nr_freepages += isolated; |
206 | } | 281 | } |
207 | spin_unlock_irqrestore(&zone->lock, flags); | 282 | spin_unlock_irqrestore(&zone->lock, flags); |