summaryrefslogtreecommitdiffstats
path: root/mm/compaction.c
diff options
context:
space:
mode:
authorMel Gorman <mgorman@techsingularity.net>2019-03-05 18:45:38 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2019-03-06 00:07:17 -0500
commite332f741a8dd1ec9a6dc8aa997296ecbfe64323e (patch)
treeb336a96e0c0073222608d70f4e029d259bfd8691 /mm/compaction.c
parent4fca9730c51d51f643f2a3f8f10ebd718349c80f (diff)
mm, compaction: be selective about what pageblocks to clear skip hints
Pageblock hints are cleared when compaction restarts or kswapd makes enough progress that it can sleep but it's over-eager in that the bit is cleared for migration sources with no LRU pages and migration targets with no free pages. As pageblock skip hint flushes are relatively rare and out-of-band with respect to kswapd, this patch makes a few more expensive checks to see if it's appropriate to even clear the bit. Every pageblock that is not cleared will avoid 512 pages being scanned unnecessarily on x86-64. The impact is variable with different workloads showing small differences in latency, success rates and scan rates. This is expected as clearing the hints is not that common but doing a small amount of work out-of-band to avoid a large amount of work in-band later is generally a good thing. Link: http://lkml.kernel.org/r/20190118175136.31341-22-mgorman@techsingularity.net Signed-off-by: Mel Gorman <mgorman@techsingularity.net> Signed-off-by: Qian Cai <cai@lca.pw> Acked-by: Vlastimil Babka <vbabka@suse.cz> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Dan Carpenter <dan.carpenter@oracle.com> Cc: David Rientjes <rientjes@google.com> Cc: YueHaibing <yuehaibing@huawei.com> [cai@lca.pw: no stuck in __reset_isolation_pfn()] Link: http://lkml.kernel.org/r/20190206034732.75687-1-cai@lca.pw Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/compaction.c')
-rw-r--r--mm/compaction.c124
1 files changed, 106 insertions, 18 deletions
diff --git a/mm/compaction.c b/mm/compaction.c
index b83cdb42f249..3084cee77fda 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -237,6 +237,70 @@ static bool pageblock_skip_persistent(struct page *page)
237 return false; 237 return false;
238} 238}
239 239
240static bool
241__reset_isolation_pfn(struct zone *zone, unsigned long pfn, bool check_source,
242 bool check_target)
243{
244 struct page *page = pfn_to_online_page(pfn);
245 struct page *end_page;
246 unsigned long block_pfn;
247
248 if (!page)
249 return false;
250 if (zone != page_zone(page))
251 return false;
252 if (pageblock_skip_persistent(page))
253 return false;
254
255 /*
256 * If skip is already cleared do no further checking once the
257 * restart points have been set.
258 */
259 if (check_source && check_target && !get_pageblock_skip(page))
260 return true;
261
262 /*
263 * If clearing skip for the target scanner, do not select a
264 * non-movable pageblock as the starting point.
265 */
266 if (!check_source && check_target &&
267 get_pageblock_migratetype(page) != MIGRATE_MOVABLE)
268 return false;
269
270 /*
271 * Only clear the hint if a sample indicates there is either a
272 * free page or an LRU page in the block. One or other condition
273 * is necessary for the block to be a migration source/target.
274 */
275 block_pfn = pageblock_start_pfn(pfn);
276 pfn = max(block_pfn, zone->zone_start_pfn);
277 page = pfn_to_page(pfn);
278 if (zone != page_zone(page))
279 return false;
280 pfn = block_pfn + pageblock_nr_pages;
281 pfn = min(pfn, zone_end_pfn(zone));
282 end_page = pfn_to_page(pfn);
283
284 do {
285 if (pfn_valid_within(pfn)) {
286 if (check_source && PageLRU(page)) {
287 clear_pageblock_skip(page);
288 return true;
289 }
290
291 if (check_target && PageBuddy(page)) {
292 clear_pageblock_skip(page);
293 return true;
294 }
295 }
296
297 page += (1 << PAGE_ALLOC_COSTLY_ORDER);
298 pfn += (1 << PAGE_ALLOC_COSTLY_ORDER);
299 } while (page < end_page);
300
301 return false;
302}
303
240/* 304/*
241 * This function is called to clear all cached information on pageblocks that 305 * This function is called to clear all cached information on pageblocks that
242 * should be skipped for page isolation when the migrate and free page scanner 306 * should be skipped for page isolation when the migrate and free page scanner
@@ -244,30 +308,54 @@ static bool pageblock_skip_persistent(struct page *page)
244 */ 308 */
245static void __reset_isolation_suitable(struct zone *zone) 309static void __reset_isolation_suitable(struct zone *zone)
246{ 310{
247 unsigned long start_pfn = zone->zone_start_pfn; 311 unsigned long migrate_pfn = zone->zone_start_pfn;
248 unsigned long end_pfn = zone_end_pfn(zone); 312 unsigned long free_pfn = zone_end_pfn(zone);
249 unsigned long pfn; 313 unsigned long reset_migrate = free_pfn;
314 unsigned long reset_free = migrate_pfn;
315 bool source_set = false;
316 bool free_set = false;
250 317
251 zone->compact_blockskip_flush = false; 318 if (!zone->compact_blockskip_flush)
319 return;
252 320
253 /* Walk the zone and mark every pageblock as suitable for isolation */ 321 zone->compact_blockskip_flush = false;
254 for (pfn = start_pfn; pfn < end_pfn; pfn += pageblock_nr_pages) {
255 struct page *page;
256 322
323 /*
324 * Walk the zone and update pageblock skip information. Source looks
325 * for PageLRU while target looks for PageBuddy. When the scanner
326 * is found, both PageBuddy and PageLRU are checked as the pageblock
327 * is suitable as both source and target.
328 */
329 for (; migrate_pfn < free_pfn; migrate_pfn += pageblock_nr_pages,
330 free_pfn -= pageblock_nr_pages) {
257 cond_resched(); 331 cond_resched();
258 332
259 page = pfn_to_online_page(pfn); 333 /* Update the migrate PFN */
260 if (!page) 334 if (__reset_isolation_pfn(zone, migrate_pfn, true, source_set) &&
261 continue; 335 migrate_pfn < reset_migrate) {
262 if (zone != page_zone(page)) 336 source_set = true;
263 continue; 337 reset_migrate = migrate_pfn;
264 if (pageblock_skip_persistent(page)) 338 zone->compact_init_migrate_pfn = reset_migrate;
265 continue; 339 zone->compact_cached_migrate_pfn[0] = reset_migrate;
340 zone->compact_cached_migrate_pfn[1] = reset_migrate;
341 }
266 342
267 clear_pageblock_skip(page); 343 /* Update the free PFN */
344 if (__reset_isolation_pfn(zone, free_pfn, free_set, true) &&
345 free_pfn > reset_free) {
346 free_set = true;
347 reset_free = free_pfn;
348 zone->compact_init_free_pfn = reset_free;
349 zone->compact_cached_free_pfn = reset_free;
350 }
268 } 351 }
269 352
270 reset_cached_positions(zone); 353 /* Leave no distance if no suitable block was reset */
354 if (reset_migrate >= reset_free) {
355 zone->compact_cached_migrate_pfn[0] = migrate_pfn;
356 zone->compact_cached_migrate_pfn[1] = migrate_pfn;
357 zone->compact_cached_free_pfn = free_pfn;
358 }
271} 359}
272 360
273void reset_isolation_suitable(pg_data_t *pgdat) 361void reset_isolation_suitable(pg_data_t *pgdat)
@@ -1190,7 +1278,7 @@ fast_isolate_freepages(struct compact_control *cc)
1190 * If starting the scan, use a deeper search and use the highest 1278 * If starting the scan, use a deeper search and use the highest
1191 * PFN found if a suitable one is not found. 1279 * PFN found if a suitable one is not found.
1192 */ 1280 */
1193 if (cc->free_pfn == pageblock_start_pfn(zone_end_pfn(cc->zone) - 1)) { 1281 if (cc->free_pfn >= cc->zone->compact_init_free_pfn) {
1194 limit = pageblock_nr_pages >> 1; 1282 limit = pageblock_nr_pages >> 1;
1195 scan_start = true; 1283 scan_start = true;
1196 } 1284 }
@@ -2017,7 +2105,7 @@ static enum compact_result compact_zone(struct compact_control *cc)
2017 cc->zone->compact_cached_migrate_pfn[1] = cc->migrate_pfn; 2105 cc->zone->compact_cached_migrate_pfn[1] = cc->migrate_pfn;
2018 } 2106 }
2019 2107
2020 if (cc->migrate_pfn == start_pfn) 2108 if (cc->migrate_pfn <= cc->zone->compact_init_migrate_pfn)
2021 cc->whole_zone = true; 2109 cc->whole_zone = true;
2022 } 2110 }
2023 2111