diff options
-rw-r--r-- | mm/migrate.c | 141 |
1 files changed, 112 insertions, 29 deletions
diff --git a/mm/migrate.c b/mm/migrate.c index 991e8886093f..652b2c642eed 100644 --- a/mm/migrate.c +++ b/mm/migrate.c | |||
@@ -2149,7 +2149,7 @@ static int migrate_vma_collect_pmd(pmd_t *pmdp, | |||
2149 | struct migrate_vma *migrate = walk->private; | 2149 | struct migrate_vma *migrate = walk->private; |
2150 | struct vm_area_struct *vma = walk->vma; | 2150 | struct vm_area_struct *vma = walk->vma; |
2151 | struct mm_struct *mm = vma->vm_mm; | 2151 | struct mm_struct *mm = vma->vm_mm; |
2152 | unsigned long addr = start; | 2152 | unsigned long addr = start, unmapped = 0; |
2153 | spinlock_t *ptl; | 2153 | spinlock_t *ptl; |
2154 | pte_t *ptep; | 2154 | pte_t *ptep; |
2155 | 2155 | ||
@@ -2194,9 +2194,12 @@ again: | |||
2194 | return migrate_vma_collect_hole(start, end, walk); | 2194 | return migrate_vma_collect_hole(start, end, walk); |
2195 | 2195 | ||
2196 | ptep = pte_offset_map_lock(mm, pmdp, addr, &ptl); | 2196 | ptep = pte_offset_map_lock(mm, pmdp, addr, &ptl); |
2197 | arch_enter_lazy_mmu_mode(); | ||
2198 | |||
2197 | for (; addr < end; addr += PAGE_SIZE, ptep++) { | 2199 | for (; addr < end; addr += PAGE_SIZE, ptep++) { |
2198 | unsigned long mpfn, pfn; | 2200 | unsigned long mpfn, pfn; |
2199 | struct page *page; | 2201 | struct page *page; |
2202 | swp_entry_t entry; | ||
2200 | pte_t pte; | 2203 | pte_t pte; |
2201 | 2204 | ||
2202 | pte = *ptep; | 2205 | pte = *ptep; |
@@ -2228,11 +2231,44 @@ again: | |||
2228 | mpfn = migrate_pfn(pfn) | MIGRATE_PFN_MIGRATE; | 2231 | mpfn = migrate_pfn(pfn) | MIGRATE_PFN_MIGRATE; |
2229 | mpfn |= pte_write(pte) ? MIGRATE_PFN_WRITE : 0; | 2232 | mpfn |= pte_write(pte) ? MIGRATE_PFN_WRITE : 0; |
2230 | 2233 | ||
2234 | /* | ||
2235 | * Optimize for the common case where page is only mapped once | ||
2236 | * in one process. If we can lock the page, then we can safely | ||
2237 | * set up a special migration page table entry now. | ||
2238 | */ | ||
2239 | if (trylock_page(page)) { | ||
2240 | pte_t swp_pte; | ||
2241 | |||
2242 | mpfn |= MIGRATE_PFN_LOCKED; | ||
2243 | ptep_get_and_clear(mm, addr, ptep); | ||
2244 | |||
2245 | /* Setup special migration page table entry */ | ||
2246 | entry = make_migration_entry(page, pte_write(pte)); | ||
2247 | swp_pte = swp_entry_to_pte(entry); | ||
2248 | if (pte_soft_dirty(pte)) | ||
2249 | swp_pte = pte_swp_mksoft_dirty(swp_pte); | ||
2250 | set_pte_at(mm, addr, ptep, swp_pte); | ||
2251 | |||
2252 | /* | ||
2253 | * This is like regular unmap: we remove the rmap and | ||
2254 | * drop page refcount. Page won't be freed, as we took | ||
2255 | * a reference just above. | ||
2256 | */ | ||
2257 | page_remove_rmap(page, false); | ||
2258 | put_page(page); | ||
2259 | unmapped++; | ||
2260 | } | ||
2261 | |||
2231 | next: | 2262 | next: |
2232 | migrate->src[migrate->npages++] = mpfn; | 2263 | migrate->src[migrate->npages++] = mpfn; |
2233 | } | 2264 | } |
2265 | arch_leave_lazy_mmu_mode(); | ||
2234 | pte_unmap_unlock(ptep - 1, ptl); | 2266 | pte_unmap_unlock(ptep - 1, ptl); |
2235 | 2267 | ||
2268 | /* Only flush the TLB if we actually modified any entries */ | ||
2269 | if (unmapped) | ||
2270 | flush_tlb_range(walk->vma, start, end); | ||
2271 | |||
2236 | return 0; | 2272 | return 0; |
2237 | } | 2273 | } |
2238 | 2274 | ||
@@ -2257,7 +2293,13 @@ static void migrate_vma_collect(struct migrate_vma *migrate) | |||
2257 | mm_walk.mm = migrate->vma->vm_mm; | 2293 | mm_walk.mm = migrate->vma->vm_mm; |
2258 | mm_walk.private = migrate; | 2294 | mm_walk.private = migrate; |
2259 | 2295 | ||
2296 | mmu_notifier_invalidate_range_start(mm_walk.mm, | ||
2297 | migrate->start, | ||
2298 | migrate->end); | ||
2260 | walk_page_range(migrate->start, migrate->end, &mm_walk); | 2299 | walk_page_range(migrate->start, migrate->end, &mm_walk); |
2300 | mmu_notifier_invalidate_range_end(mm_walk.mm, | ||
2301 | migrate->start, | ||
2302 | migrate->end); | ||
2261 | 2303 | ||
2262 | migrate->end = migrate->start + (migrate->npages << PAGE_SHIFT); | 2304 | migrate->end = migrate->start + (migrate->npages << PAGE_SHIFT); |
2263 | } | 2305 | } |
@@ -2305,32 +2347,37 @@ static bool migrate_vma_check_page(struct page *page) | |||
2305 | static void migrate_vma_prepare(struct migrate_vma *migrate) | 2347 | static void migrate_vma_prepare(struct migrate_vma *migrate) |
2306 | { | 2348 | { |
2307 | const unsigned long npages = migrate->npages; | 2349 | const unsigned long npages = migrate->npages; |
2350 | const unsigned long start = migrate->start; | ||
2351 | unsigned long addr, i, restore = 0; | ||
2308 | bool allow_drain = true; | 2352 | bool allow_drain = true; |
2309 | unsigned long i; | ||
2310 | 2353 | ||
2311 | lru_add_drain(); | 2354 | lru_add_drain(); |
2312 | 2355 | ||
2313 | for (i = 0; (i < npages) && migrate->cpages; i++) { | 2356 | for (i = 0; (i < npages) && migrate->cpages; i++) { |
2314 | struct page *page = migrate_pfn_to_page(migrate->src[i]); | 2357 | struct page *page = migrate_pfn_to_page(migrate->src[i]); |
2358 | bool remap = true; | ||
2315 | 2359 | ||
2316 | if (!page) | 2360 | if (!page) |
2317 | continue; | 2361 | continue; |
2318 | 2362 | ||
2319 | /* | 2363 | if (!(migrate->src[i] & MIGRATE_PFN_LOCKED)) { |
2320 | * Because we are migrating several pages there can be | 2364 | /* |
2321 | * a deadlock between 2 concurrent migration where each | 2365 | * Because we are migrating several pages there can be |
2322 | * are waiting on each other page lock. | 2366 | * a deadlock between 2 concurrent migration where each |
2323 | * | 2367 | * are waiting on each other page lock. |
2324 | * Make migrate_vma() a best effort thing and backoff | 2368 | * |
2325 | * for any page we can not lock right away. | 2369 | * Make migrate_vma() a best effort thing and backoff |
2326 | */ | 2370 | * for any page we can not lock right away. |
2327 | if (!trylock_page(page)) { | 2371 | */ |
2328 | migrate->src[i] = 0; | 2372 | if (!trylock_page(page)) { |
2329 | migrate->cpages--; | 2373 | migrate->src[i] = 0; |
2330 | put_page(page); | 2374 | migrate->cpages--; |
2331 | continue; | 2375 | put_page(page); |
2376 | continue; | ||
2377 | } | ||
2378 | remap = false; | ||
2379 | migrate->src[i] |= MIGRATE_PFN_LOCKED; | ||
2332 | } | 2380 | } |
2333 | migrate->src[i] |= MIGRATE_PFN_LOCKED; | ||
2334 | 2381 | ||
2335 | if (!PageLRU(page) && allow_drain) { | 2382 | if (!PageLRU(page) && allow_drain) { |
2336 | /* Drain CPU's pagevec */ | 2383 | /* Drain CPU's pagevec */ |
@@ -2339,21 +2386,50 @@ static void migrate_vma_prepare(struct migrate_vma *migrate) | |||
2339 | } | 2386 | } |
2340 | 2387 | ||
2341 | if (isolate_lru_page(page)) { | 2388 | if (isolate_lru_page(page)) { |
2342 | migrate->src[i] = 0; | 2389 | if (remap) { |
2343 | unlock_page(page); | 2390 | migrate->src[i] &= ~MIGRATE_PFN_MIGRATE; |
2344 | migrate->cpages--; | 2391 | migrate->cpages--; |
2345 | put_page(page); | 2392 | restore++; |
2393 | } else { | ||
2394 | migrate->src[i] = 0; | ||
2395 | unlock_page(page); | ||
2396 | migrate->cpages--; | ||
2397 | put_page(page); | ||
2398 | } | ||
2346 | continue; | 2399 | continue; |
2347 | } | 2400 | } |
2348 | 2401 | ||
2349 | if (!migrate_vma_check_page(page)) { | 2402 | if (!migrate_vma_check_page(page)) { |
2350 | migrate->src[i] = 0; | 2403 | if (remap) { |
2351 | unlock_page(page); | 2404 | migrate->src[i] &= ~MIGRATE_PFN_MIGRATE; |
2352 | migrate->cpages--; | 2405 | migrate->cpages--; |
2406 | restore++; | ||
2353 | 2407 | ||
2354 | putback_lru_page(page); | 2408 | get_page(page); |
2409 | putback_lru_page(page); | ||
2410 | } else { | ||
2411 | migrate->src[i] = 0; | ||
2412 | unlock_page(page); | ||
2413 | migrate->cpages--; | ||
2414 | |||
2415 | putback_lru_page(page); | ||
2416 | } | ||
2355 | } | 2417 | } |
2356 | } | 2418 | } |
2419 | |||
2420 | for (i = 0, addr = start; i < npages && restore; i++, addr += PAGE_SIZE) { | ||
2421 | struct page *page = migrate_pfn_to_page(migrate->src[i]); | ||
2422 | |||
2423 | if (!page || (migrate->src[i] & MIGRATE_PFN_MIGRATE)) | ||
2424 | continue; | ||
2425 | |||
2426 | remove_migration_pte(page, migrate->vma, addr, page); | ||
2427 | |||
2428 | migrate->src[i] = 0; | ||
2429 | unlock_page(page); | ||
2430 | put_page(page); | ||
2431 | restore--; | ||
2432 | } | ||
2357 | } | 2433 | } |
2358 | 2434 | ||
2359 | /* | 2435 | /* |
@@ -2380,12 +2456,19 @@ static void migrate_vma_unmap(struct migrate_vma *migrate) | |||
2380 | if (!page || !(migrate->src[i] & MIGRATE_PFN_MIGRATE)) | 2456 | if (!page || !(migrate->src[i] & MIGRATE_PFN_MIGRATE)) |
2381 | continue; | 2457 | continue; |
2382 | 2458 | ||
2383 | try_to_unmap(page, flags); | 2459 | if (page_mapped(page)) { |
2384 | if (page_mapped(page) || !migrate_vma_check_page(page)) { | 2460 | try_to_unmap(page, flags); |
2385 | migrate->src[i] &= ~MIGRATE_PFN_MIGRATE; | 2461 | if (page_mapped(page)) |
2386 | migrate->cpages--; | 2462 | goto restore; |
2387 | restore++; | ||
2388 | } | 2463 | } |
2464 | |||
2465 | if (migrate_vma_check_page(page)) | ||
2466 | continue; | ||
2467 | |||
2468 | restore: | ||
2469 | migrate->src[i] &= ~MIGRATE_PFN_MIGRATE; | ||
2470 | migrate->cpages--; | ||
2471 | restore++; | ||
2389 | } | 2472 | } |
2390 | 2473 | ||
2391 | for (addr = start, i = 0; i < npages && restore; addr += PAGE_SIZE, i++) { | 2474 | for (addr = start, i = 0; i < npages && restore; addr += PAGE_SIZE, i++) { |