diff options
| -rw-r--r-- | include/linux/compaction.h | 4 | ||||
| -rw-r--r-- | mm/compaction.c | 100 | ||||
| -rw-r--r-- | mm/internal.h | 1 | ||||
| -rw-r--r-- | mm/page_alloc.c | 17 |
4 files changed, 93 insertions, 29 deletions
diff --git a/include/linux/compaction.h b/include/linux/compaction.h index 133ddcf83397..ef658147e4e8 100644 --- a/include/linux/compaction.h +++ b/include/linux/compaction.h | |||
| @@ -22,7 +22,7 @@ extern int sysctl_extfrag_handler(struct ctl_table *table, int write, | |||
| 22 | extern int fragmentation_index(struct zone *zone, unsigned int order); | 22 | extern int fragmentation_index(struct zone *zone, unsigned int order); |
| 23 | extern unsigned long try_to_compact_pages(struct zonelist *zonelist, | 23 | extern unsigned long try_to_compact_pages(struct zonelist *zonelist, |
| 24 | int order, gfp_t gfp_mask, nodemask_t *mask, | 24 | int order, gfp_t gfp_mask, nodemask_t *mask, |
| 25 | bool sync); | 25 | bool sync, bool *contended); |
| 26 | extern int compact_pgdat(pg_data_t *pgdat, int order); | 26 | extern int compact_pgdat(pg_data_t *pgdat, int order); |
| 27 | extern unsigned long compaction_suitable(struct zone *zone, int order); | 27 | extern unsigned long compaction_suitable(struct zone *zone, int order); |
| 28 | 28 | ||
| @@ -64,7 +64,7 @@ static inline bool compaction_deferred(struct zone *zone, int order) | |||
| 64 | #else | 64 | #else |
| 65 | static inline unsigned long try_to_compact_pages(struct zonelist *zonelist, | 65 | static inline unsigned long try_to_compact_pages(struct zonelist *zonelist, |
| 66 | int order, gfp_t gfp_mask, nodemask_t *nodemask, | 66 | int order, gfp_t gfp_mask, nodemask_t *nodemask, |
| 67 | bool sync) | 67 | bool sync, bool *contended) |
| 68 | { | 68 | { |
| 69 | return COMPACT_CONTINUE; | 69 | return COMPACT_CONTINUE; |
| 70 | } | 70 | } |
diff --git a/mm/compaction.c b/mm/compaction.c index bcce7897e17a..7fcd3a52e68d 100644 --- a/mm/compaction.c +++ b/mm/compaction.c | |||
| @@ -51,6 +51,47 @@ static inline bool migrate_async_suitable(int migratetype) | |||
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | /* | 53 | /* |
| 54 | * Compaction requires the taking of some coarse locks that are potentially | ||
| 55 | * very heavily contended. Check if the process needs to be scheduled or | ||
| 56 | * if the lock is contended. For async compaction, back out in the event | ||
| 57 | * if contention is severe. For sync compaction, schedule. | ||
| 58 | * | ||
| 59 | * Returns true if the lock is held. | ||
| 60 | * Returns false if the lock is released and compaction should abort | ||
| 61 | */ | ||
| 62 | static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags, | ||
| 63 | bool locked, struct compact_control *cc) | ||
| 64 | { | ||
| 65 | if (need_resched() || spin_is_contended(lock)) { | ||
| 66 | if (locked) { | ||
| 67 | spin_unlock_irqrestore(lock, *flags); | ||
| 68 | locked = false; | ||
| 69 | } | ||
| 70 | |||
| 71 | /* async aborts if taking too long or contended */ | ||
| 72 | if (!cc->sync) { | ||
| 73 | if (cc->contended) | ||
| 74 | *cc->contended = true; | ||
| 75 | return false; | ||
| 76 | } | ||
| 77 | |||
| 78 | cond_resched(); | ||
| 79 | if (fatal_signal_pending(current)) | ||
| 80 | return false; | ||
| 81 | } | ||
| 82 | |||
| 83 | if (!locked) | ||
| 84 | spin_lock_irqsave(lock, *flags); | ||
| 85 | return true; | ||
| 86 | } | ||
| 87 | |||
| 88 | static inline bool compact_trylock_irqsave(spinlock_t *lock, | ||
| 89 | unsigned long *flags, struct compact_control *cc) | ||
| 90 | { | ||
| 91 | return compact_checklock_irqsave(lock, flags, false, cc); | ||
| 92 | } | ||
| 93 | |||
| 94 | /* | ||
| 54 | * Isolate free pages onto a private freelist. Caller must hold zone->lock. | 95 | * Isolate free pages onto a private freelist. Caller must hold zone->lock. |
| 55 | * If @strict is true, will abort returning 0 on any invalid PFNs or non-free | 96 | * If @strict is true, will abort returning 0 on any invalid PFNs or non-free |
| 56 | * pages inside of the pageblock (even though it may still end up isolating | 97 | * pages inside of the pageblock (even though it may still end up isolating |
| @@ -173,7 +214,7 @@ isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn) | |||
| 173 | } | 214 | } |
| 174 | 215 | ||
| 175 | /* Update the number of anon and file isolated pages in the zone */ | 216 | /* Update the number of anon and file isolated pages in the zone */ |
| 176 | static void acct_isolated(struct zone *zone, struct compact_control *cc) | 217 | static void acct_isolated(struct zone *zone, bool locked, struct compact_control *cc) |
| 177 | { | 218 | { |
| 178 | struct page *page; | 219 | struct page *page; |
| 179 | unsigned int count[2] = { 0, }; | 220 | unsigned int count[2] = { 0, }; |
| @@ -181,8 +222,14 @@ static void acct_isolated(struct zone *zone, struct compact_control *cc) | |||
| 181 | list_for_each_entry(page, &cc->migratepages, lru) | 222 | list_for_each_entry(page, &cc->migratepages, lru) |
| 182 | count[!!page_is_file_cache(page)]++; | 223 | count[!!page_is_file_cache(page)]++; |
| 183 | 224 | ||
| 184 | __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]); | 225 | /* If locked we can use the interrupt unsafe versions */ |
| 185 | __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]); | 226 | if (locked) { |
| 227 | __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]); | ||
| 228 | __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]); | ||
| 229 | } else { | ||
| 230 | mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]); | ||
| 231 | mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]); | ||
| 232 | } | ||
| 186 | } | 233 | } |
| 187 | 234 | ||
| 188 | /* Similar to reclaim, but different enough that they don't share logic */ | 235 | /* Similar to reclaim, but different enough that they don't share logic */ |
| @@ -228,6 +275,8 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc, | |||
| 228 | struct list_head *migratelist = &cc->migratepages; | 275 | struct list_head *migratelist = &cc->migratepages; |
| 229 | isolate_mode_t mode = 0; | 276 | isolate_mode_t mode = 0; |
| 230 | struct lruvec *lruvec; | 277 | struct lruvec *lruvec; |
| 278 | unsigned long flags; | ||
| 279 | bool locked; | ||
| 231 | 280 | ||
| 232 | /* | 281 | /* |
| 233 | * Ensure that there are not too many pages isolated from the LRU | 282 | * Ensure that there are not too many pages isolated from the LRU |
| @@ -247,25 +296,22 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc, | |||
| 247 | 296 | ||
| 248 | /* Time to isolate some pages for migration */ | 297 | /* Time to isolate some pages for migration */ |
| 249 | cond_resched(); | 298 | cond_resched(); |
| 250 | spin_lock_irq(&zone->lru_lock); | 299 | spin_lock_irqsave(&zone->lru_lock, flags); |
| 300 | locked = true; | ||
| 251 | for (; low_pfn < end_pfn; low_pfn++) { | 301 | for (; low_pfn < end_pfn; low_pfn++) { |
| 252 | struct page *page; | 302 | struct page *page; |
| 253 | bool locked = true; | ||
| 254 | 303 | ||
| 255 | /* give a chance to irqs before checking need_resched() */ | 304 | /* give a chance to irqs before checking need_resched() */ |
| 256 | if (!((low_pfn+1) % SWAP_CLUSTER_MAX)) { | 305 | if (!((low_pfn+1) % SWAP_CLUSTER_MAX)) { |
| 257 | spin_unlock_irq(&zone->lru_lock); | 306 | spin_unlock_irqrestore(&zone->lru_lock, flags); |
| 258 | locked = false; | 307 | locked = false; |
| 259 | } | 308 | } |
| 260 | if (need_resched() || spin_is_contended(&zone->lru_lock)) { | 309 | |
| 261 | if (locked) | 310 | /* Check if it is ok to still hold the lock */ |
| 262 | spin_unlock_irq(&zone->lru_lock); | 311 | locked = compact_checklock_irqsave(&zone->lru_lock, &flags, |
| 263 | cond_resched(); | 312 | locked, cc); |
| 264 | spin_lock_irq(&zone->lru_lock); | 313 | if (!locked) |
| 265 | if (fatal_signal_pending(current)) | 314 | break; |
| 266 | break; | ||
| 267 | } else if (!locked) | ||
| 268 | spin_lock_irq(&zone->lru_lock); | ||
| 269 | 315 | ||
| 270 | /* | 316 | /* |
| 271 | * migrate_pfn does not necessarily start aligned to a | 317 | * migrate_pfn does not necessarily start aligned to a |
| @@ -349,9 +395,10 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc, | |||
| 349 | } | 395 | } |
| 350 | } | 396 | } |
| 351 | 397 | ||
| 352 | acct_isolated(zone, cc); | 398 | acct_isolated(zone, locked, cc); |
| 353 | 399 | ||
| 354 | spin_unlock_irq(&zone->lru_lock); | 400 | if (locked) |
| 401 | spin_unlock_irqrestore(&zone->lru_lock, flags); | ||
| 355 | 402 | ||
| 356 | trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated); | 403 | trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated); |
| 357 | 404 | ||
| @@ -461,7 +508,16 @@ static void isolate_freepages(struct zone *zone, | |||
| 461 | * are disabled | 508 | * are disabled |
| 462 | */ | 509 | */ |
| 463 | isolated = 0; | 510 | isolated = 0; |
| 464 | spin_lock_irqsave(&zone->lock, flags); | 511 | |
| 512 | /* | ||
| 513 | * The zone lock must be held to isolate freepages. This | ||
| 514 | * unfortunately this is a very coarse lock and can be | ||
| 515 | * heavily contended if there are parallel allocations | ||
| 516 | * or parallel compactions. For async compaction do not | ||
| 517 | * spin on the lock | ||
| 518 | */ | ||
| 519 | if (!compact_trylock_irqsave(&zone->lock, &flags, cc)) | ||
| 520 | break; | ||
| 465 | if (suitable_migration_target(page)) { | 521 | if (suitable_migration_target(page)) { |
| 466 | end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn); | 522 | end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn); |
| 467 | isolated = isolate_freepages_block(pfn, end_pfn, | 523 | isolated = isolate_freepages_block(pfn, end_pfn, |
| @@ -773,7 +829,7 @@ out: | |||
| 773 | 829 | ||
| 774 | static unsigned long compact_zone_order(struct zone *zone, | 830 | static unsigned long compact_zone_order(struct zone *zone, |
| 775 | int order, gfp_t gfp_mask, | 831 | int order, gfp_t gfp_mask, |
| 776 | bool sync) | 832 | bool sync, bool *contended) |
| 777 | { | 833 | { |
| 778 | struct compact_control cc = { | 834 | struct compact_control cc = { |
| 779 | .nr_freepages = 0, | 835 | .nr_freepages = 0, |
| @@ -782,6 +838,7 @@ static unsigned long compact_zone_order(struct zone *zone, | |||
| 782 | .migratetype = allocflags_to_migratetype(gfp_mask), | 838 | .migratetype = allocflags_to_migratetype(gfp_mask), |
| 783 | .zone = zone, | 839 | .zone = zone, |
| 784 | .sync = sync, | 840 | .sync = sync, |
| 841 | .contended = contended, | ||
| 785 | }; | 842 | }; |
| 786 | INIT_LIST_HEAD(&cc.freepages); | 843 | INIT_LIST_HEAD(&cc.freepages); |
| 787 | INIT_LIST_HEAD(&cc.migratepages); | 844 | INIT_LIST_HEAD(&cc.migratepages); |
| @@ -803,7 +860,7 @@ int sysctl_extfrag_threshold = 500; | |||
| 803 | */ | 860 | */ |
| 804 | unsigned long try_to_compact_pages(struct zonelist *zonelist, | 861 | unsigned long try_to_compact_pages(struct zonelist *zonelist, |
| 805 | int order, gfp_t gfp_mask, nodemask_t *nodemask, | 862 | int order, gfp_t gfp_mask, nodemask_t *nodemask, |
| 806 | bool sync) | 863 | bool sync, bool *contended) |
| 807 | { | 864 | { |
| 808 | enum zone_type high_zoneidx = gfp_zone(gfp_mask); | 865 | enum zone_type high_zoneidx = gfp_zone(gfp_mask); |
| 809 | int may_enter_fs = gfp_mask & __GFP_FS; | 866 | int may_enter_fs = gfp_mask & __GFP_FS; |
| @@ -827,7 +884,8 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist, | |||
| 827 | nodemask) { | 884 | nodemask) { |
| 828 | int status; | 885 | int status; |
| 829 | 886 | ||
| 830 | status = compact_zone_order(zone, order, gfp_mask, sync); | 887 | status = compact_zone_order(zone, order, gfp_mask, sync, |
| 888 | contended); | ||
| 831 | rc = max(status, rc); | 889 | rc = max(status, rc); |
| 832 | 890 | ||
| 833 | /* If a normal allocation would succeed, stop compacting */ | 891 | /* If a normal allocation would succeed, stop compacting */ |
diff --git a/mm/internal.h b/mm/internal.h index 3314f79d775a..b8c91b342e24 100644 --- a/mm/internal.h +++ b/mm/internal.h | |||
| @@ -130,6 +130,7 @@ struct compact_control { | |||
| 130 | int order; /* order a direct compactor needs */ | 130 | int order; /* order a direct compactor needs */ |
| 131 | int migratetype; /* MOVABLE, RECLAIMABLE etc */ | 131 | int migratetype; /* MOVABLE, RECLAIMABLE etc */ |
| 132 | struct zone *zone; | 132 | struct zone *zone; |
| 133 | bool *contended; /* True if a lock was contended */ | ||
| 133 | }; | 134 | }; |
| 134 | 135 | ||
| 135 | unsigned long | 136 | unsigned long |
diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 07f19248acb5..c66fb875104a 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c | |||
| @@ -2102,7 +2102,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, | |||
| 2102 | struct zonelist *zonelist, enum zone_type high_zoneidx, | 2102 | struct zonelist *zonelist, enum zone_type high_zoneidx, |
| 2103 | nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone, | 2103 | nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone, |
| 2104 | int migratetype, bool sync_migration, | 2104 | int migratetype, bool sync_migration, |
| 2105 | bool *deferred_compaction, | 2105 | bool *contended_compaction, bool *deferred_compaction, |
| 2106 | unsigned long *did_some_progress) | 2106 | unsigned long *did_some_progress) |
| 2107 | { | 2107 | { |
| 2108 | struct page *page; | 2108 | struct page *page; |
| @@ -2117,7 +2117,8 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, | |||
| 2117 | 2117 | ||
| 2118 | current->flags |= PF_MEMALLOC; | 2118 | current->flags |= PF_MEMALLOC; |
| 2119 | *did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask, | 2119 | *did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask, |
| 2120 | nodemask, sync_migration); | 2120 | nodemask, sync_migration, |
| 2121 | contended_compaction); | ||
| 2121 | current->flags &= ~PF_MEMALLOC; | 2122 | current->flags &= ~PF_MEMALLOC; |
| 2122 | if (*did_some_progress != COMPACT_SKIPPED) { | 2123 | if (*did_some_progress != COMPACT_SKIPPED) { |
| 2123 | 2124 | ||
| @@ -2163,7 +2164,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, | |||
| 2163 | struct zonelist *zonelist, enum zone_type high_zoneidx, | 2164 | struct zonelist *zonelist, enum zone_type high_zoneidx, |
| 2164 | nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone, | 2165 | nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone, |
| 2165 | int migratetype, bool sync_migration, | 2166 | int migratetype, bool sync_migration, |
| 2166 | bool *deferred_compaction, | 2167 | bool *contended_compaction, bool *deferred_compaction, |
| 2167 | unsigned long *did_some_progress) | 2168 | unsigned long *did_some_progress) |
| 2168 | { | 2169 | { |
| 2169 | return NULL; | 2170 | return NULL; |
| @@ -2336,6 +2337,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, | |||
| 2336 | unsigned long did_some_progress; | 2337 | unsigned long did_some_progress; |
| 2337 | bool sync_migration = false; | 2338 | bool sync_migration = false; |
| 2338 | bool deferred_compaction = false; | 2339 | bool deferred_compaction = false; |
| 2340 | bool contended_compaction = false; | ||
| 2339 | 2341 | ||
| 2340 | /* | 2342 | /* |
| 2341 | * In the slowpath, we sanity check order to avoid ever trying to | 2343 | * In the slowpath, we sanity check order to avoid ever trying to |
| @@ -2425,6 +2427,7 @@ rebalance: | |||
| 2425 | nodemask, | 2427 | nodemask, |
| 2426 | alloc_flags, preferred_zone, | 2428 | alloc_flags, preferred_zone, |
| 2427 | migratetype, sync_migration, | 2429 | migratetype, sync_migration, |
| 2430 | &contended_compaction, | ||
| 2428 | &deferred_compaction, | 2431 | &deferred_compaction, |
| 2429 | &did_some_progress); | 2432 | &did_some_progress); |
| 2430 | if (page) | 2433 | if (page) |
| @@ -2434,10 +2437,11 @@ rebalance: | |||
| 2434 | /* | 2437 | /* |
| 2435 | * If compaction is deferred for high-order allocations, it is because | 2438 | * If compaction is deferred for high-order allocations, it is because |
| 2436 | * sync compaction recently failed. In this is the case and the caller | 2439 | * sync compaction recently failed. In this is the case and the caller |
| 2437 | * has requested the system not be heavily disrupted, fail the | 2440 | * requested a movable allocation that does not heavily disrupt the |
| 2438 | * allocation now instead of entering direct reclaim | 2441 | * system then fail the allocation instead of entering direct reclaim. |
| 2439 | */ | 2442 | */ |
| 2440 | if (deferred_compaction && (gfp_mask & __GFP_NO_KSWAPD)) | 2443 | if ((deferred_compaction || contended_compaction) && |
| 2444 | (gfp_mask & __GFP_NO_KSWAPD)) | ||
| 2441 | goto nopage; | 2445 | goto nopage; |
| 2442 | 2446 | ||
| 2443 | /* Try direct reclaim and then allocating */ | 2447 | /* Try direct reclaim and then allocating */ |
| @@ -2508,6 +2512,7 @@ rebalance: | |||
| 2508 | nodemask, | 2512 | nodemask, |
| 2509 | alloc_flags, preferred_zone, | 2513 | alloc_flags, preferred_zone, |
| 2510 | migratetype, sync_migration, | 2514 | migratetype, sync_migration, |
| 2515 | &contended_compaction, | ||
| 2511 | &deferred_compaction, | 2516 | &deferred_compaction, |
| 2512 | &did_some_progress); | 2517 | &did_some_progress); |
| 2513 | if (page) | 2518 | if (page) |
