diff options
| author | Rik van Riel <riel@redhat.com> | 2005-11-28 16:44:07 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-11-28 17:42:25 -0500 |
| commit | f7b7fd8f3ebbb2810d6893295aa984acd0fd30db (patch) | |
| tree | 01afc1edafc50a3c65ec8576c05c60da53d8d242 | |
| parent | a93a117eaa0bec426d4671a49bfa96a6fdcd2ac9 (diff) | |
[PATCH] temporarily disable swap token on memory pressure
Some users (hi Zwane) have seen a problem when running a workload that
eats nearly all of physical memory - th system does an OOM kill, even
when there is still a lot of swap free.
The problem appears to be a very big task that is holding the swap
token, and the VM has a very hard time finding any other page in the
system that is swappable.
Instead of ignoring the swap token when sc->priority reaches 0, we could
simply take the swap token away from the memory hog and make sure we
don't give it back to the memory hog for a few seconds.
This patch resolves the problem Zwane ran into.
Signed-off-by: Rik van Riel <riel@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
| -rw-r--r-- | include/linux/rmap.h | 4 | ||||
| -rw-r--r-- | include/linux/swap.h | 6 | ||||
| -rw-r--r-- | mm/rmap.c | 26 | ||||
| -rw-r--r-- | mm/thrash.c | 10 | ||||
| -rw-r--r-- | mm/vmscan.c | 11 |
5 files changed, 34 insertions, 23 deletions
diff --git a/include/linux/rmap.h b/include/linux/rmap.h index 35b30e6c8cf8..33261f1d2239 100644 --- a/include/linux/rmap.h +++ b/include/linux/rmap.h | |||
| @@ -89,7 +89,7 @@ static inline void page_dup_rmap(struct page *page) | |||
| 89 | /* | 89 | /* |
| 90 | * Called from mm/vmscan.c to handle paging out | 90 | * Called from mm/vmscan.c to handle paging out |
| 91 | */ | 91 | */ |
| 92 | int page_referenced(struct page *, int is_locked, int ignore_token); | 92 | int page_referenced(struct page *, int is_locked); |
| 93 | int try_to_unmap(struct page *); | 93 | int try_to_unmap(struct page *); |
| 94 | 94 | ||
| 95 | /* | 95 | /* |
| @@ -109,7 +109,7 @@ unsigned long page_address_in_vma(struct page *, struct vm_area_struct *); | |||
| 109 | #define anon_vma_prepare(vma) (0) | 109 | #define anon_vma_prepare(vma) (0) |
| 110 | #define anon_vma_link(vma) do {} while (0) | 110 | #define anon_vma_link(vma) do {} while (0) |
| 111 | 111 | ||
| 112 | #define page_referenced(page,l,i) TestClearPageReferenced(page) | 112 | #define page_referenced(page,l) TestClearPageReferenced(page) |
| 113 | #define try_to_unmap(page) SWAP_FAIL | 113 | #define try_to_unmap(page) SWAP_FAIL |
| 114 | 114 | ||
| 115 | #endif /* CONFIG_MMU */ | 115 | #endif /* CONFIG_MMU */ |
diff --git a/include/linux/swap.h b/include/linux/swap.h index 20c975642cab..508668f840b6 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h | |||
| @@ -239,6 +239,11 @@ static inline void put_swap_token(struct mm_struct *mm) | |||
| 239 | __put_swap_token(mm); | 239 | __put_swap_token(mm); |
| 240 | } | 240 | } |
| 241 | 241 | ||
| 242 | static inline void disable_swap_token(void) | ||
| 243 | { | ||
| 244 | put_swap_token(swap_token_mm); | ||
| 245 | } | ||
| 246 | |||
| 242 | #else /* CONFIG_SWAP */ | 247 | #else /* CONFIG_SWAP */ |
| 243 | 248 | ||
| 244 | #define total_swap_pages 0 | 249 | #define total_swap_pages 0 |
| @@ -283,6 +288,7 @@ static inline swp_entry_t get_swap_page(void) | |||
| 283 | #define put_swap_token(x) do { } while(0) | 288 | #define put_swap_token(x) do { } while(0) |
| 284 | #define grab_swap_token() do { } while(0) | 289 | #define grab_swap_token() do { } while(0) |
| 285 | #define has_swap_token(x) 0 | 290 | #define has_swap_token(x) 0 |
| 291 | #define disable_swap_token() do { } while(0) | ||
| 286 | 292 | ||
| 287 | #endif /* CONFIG_SWAP */ | 293 | #endif /* CONFIG_SWAP */ |
| 288 | #endif /* __KERNEL__*/ | 294 | #endif /* __KERNEL__*/ |
| @@ -290,7 +290,7 @@ pte_t *page_check_address(struct page *page, struct mm_struct *mm, | |||
| 290 | * repeatedly from either page_referenced_anon or page_referenced_file. | 290 | * repeatedly from either page_referenced_anon or page_referenced_file. |
| 291 | */ | 291 | */ |
| 292 | static int page_referenced_one(struct page *page, | 292 | static int page_referenced_one(struct page *page, |
| 293 | struct vm_area_struct *vma, unsigned int *mapcount, int ignore_token) | 293 | struct vm_area_struct *vma, unsigned int *mapcount) |
| 294 | { | 294 | { |
| 295 | struct mm_struct *mm = vma->vm_mm; | 295 | struct mm_struct *mm = vma->vm_mm; |
| 296 | unsigned long address; | 296 | unsigned long address; |
| @@ -311,7 +311,7 @@ static int page_referenced_one(struct page *page, | |||
| 311 | 311 | ||
| 312 | /* Pretend the page is referenced if the task has the | 312 | /* Pretend the page is referenced if the task has the |
| 313 | swap token and is in the middle of a page fault. */ | 313 | swap token and is in the middle of a page fault. */ |
| 314 | if (mm != current->mm && !ignore_token && has_swap_token(mm) && | 314 | if (mm != current->mm && has_swap_token(mm) && |
| 315 | rwsem_is_locked(&mm->mmap_sem)) | 315 | rwsem_is_locked(&mm->mmap_sem)) |
| 316 | referenced++; | 316 | referenced++; |
| 317 | 317 | ||
| @@ -321,7 +321,7 @@ out: | |||
| 321 | return referenced; | 321 | return referenced; |
| 322 | } | 322 | } |
| 323 | 323 | ||
| 324 | static int page_referenced_anon(struct page *page, int ignore_token) | 324 | static int page_referenced_anon(struct page *page) |
| 325 | { | 325 | { |
| 326 | unsigned int mapcount; | 326 | unsigned int mapcount; |
| 327 | struct anon_vma *anon_vma; | 327 | struct anon_vma *anon_vma; |
| @@ -334,8 +334,7 @@ static int page_referenced_anon(struct page *page, int ignore_token) | |||
| 334 | 334 | ||
| 335 | mapcount = page_mapcount(page); | 335 | mapcount = page_mapcount(page); |
| 336 | list_for_each_entry(vma, &anon_vma->head, anon_vma_node) { | 336 | list_for_each_entry(vma, &anon_vma->head, anon_vma_node) { |
| 337 | referenced += page_referenced_one(page, vma, &mapcount, | 337 | referenced += page_referenced_one(page, vma, &mapcount); |
| 338 | ignore_token); | ||
| 339 | if (!mapcount) | 338 | if (!mapcount) |
| 340 | break; | 339 | break; |
| 341 | } | 340 | } |
| @@ -354,7 +353,7 @@ static int page_referenced_anon(struct page *page, int ignore_token) | |||
| 354 | * | 353 | * |
| 355 | * This function is only called from page_referenced for object-based pages. | 354 | * This function is only called from page_referenced for object-based pages. |
| 356 | */ | 355 | */ |
| 357 | static int page_referenced_file(struct page *page, int ignore_token) | 356 | static int page_referenced_file(struct page *page) |
| 358 | { | 357 | { |
| 359 | unsigned int mapcount; | 358 | unsigned int mapcount; |
| 360 | struct address_space *mapping = page->mapping; | 359 | struct address_space *mapping = page->mapping; |
| @@ -392,8 +391,7 @@ static int page_referenced_file(struct page *page, int ignore_token) | |||
| 392 | referenced++; | 391 | referenced++; |
| 393 | break; | 392 | break; |
| 394 | } | 393 | } |
| 395 | referenced += page_referenced_one(page, vma, &mapcount, | 394 | referenced += page_referenced_one(page, vma, &mapcount); |
| 396 | ignore_token); | ||
| 397 | if (!mapcount) | 395 | if (!mapcount) |
| 398 | break; | 396 | break; |
| 399 | } | 397 | } |
| @@ -410,13 +408,10 @@ static int page_referenced_file(struct page *page, int ignore_token) | |||
| 410 | * Quick test_and_clear_referenced for all mappings to a page, | 408 | * Quick test_and_clear_referenced for all mappings to a page, |
| 411 | * returns the number of ptes which referenced the page. | 409 | * returns the number of ptes which referenced the page. |
| 412 | */ | 410 | */ |
| 413 | int page_referenced(struct page *page, int is_locked, int ignore_token) | 411 | int page_referenced(struct page *page, int is_locked) |
| 414 | { | 412 | { |
| 415 | int referenced = 0; | 413 | int referenced = 0; |
| 416 | 414 | ||
| 417 | if (!swap_token_default_timeout) | ||
| 418 | ignore_token = 1; | ||
| 419 | |||
| 420 | if (page_test_and_clear_young(page)) | 415 | if (page_test_and_clear_young(page)) |
| 421 | referenced++; | 416 | referenced++; |
| 422 | 417 | ||
| @@ -425,15 +420,14 @@ int page_referenced(struct page *page, int is_locked, int ignore_token) | |||
| 425 | 420 | ||
| 426 | if (page_mapped(page) && page->mapping) { | 421 | if (page_mapped(page) && page->mapping) { |
| 427 | if (PageAnon(page)) | 422 | if (PageAnon(page)) |
| 428 | referenced += page_referenced_anon(page, ignore_token); | 423 | referenced += page_referenced_anon(page); |
| 429 | else if (is_locked) | 424 | else if (is_locked) |
| 430 | referenced += page_referenced_file(page, ignore_token); | 425 | referenced += page_referenced_file(page); |
| 431 | else if (TestSetPageLocked(page)) | 426 | else if (TestSetPageLocked(page)) |
| 432 | referenced++; | 427 | referenced++; |
| 433 | else { | 428 | else { |
| 434 | if (page->mapping) | 429 | if (page->mapping) |
| 435 | referenced += page_referenced_file(page, | 430 | referenced += page_referenced_file(page); |
| 436 | ignore_token); | ||
| 437 | unlock_page(page); | 431 | unlock_page(page); |
| 438 | } | 432 | } |
| 439 | } | 433 | } |
diff --git a/mm/thrash.c b/mm/thrash.c index eff3c18c33a1..f4c560b4a2b7 100644 --- a/mm/thrash.c +++ b/mm/thrash.c | |||
| @@ -57,14 +57,17 @@ void grab_swap_token(void) | |||
| 57 | /* We have the token. Let others know we still need it. */ | 57 | /* We have the token. Let others know we still need it. */ |
| 58 | if (has_swap_token(current->mm)) { | 58 | if (has_swap_token(current->mm)) { |
| 59 | current->mm->recent_pagein = 1; | 59 | current->mm->recent_pagein = 1; |
| 60 | if (unlikely(!swap_token_default_timeout)) | ||
| 61 | disable_swap_token(); | ||
| 60 | return; | 62 | return; |
| 61 | } | 63 | } |
| 62 | 64 | ||
| 63 | if (time_after(jiffies, swap_token_check)) { | 65 | if (time_after(jiffies, swap_token_check)) { |
| 64 | 66 | ||
| 65 | /* Can't get swapout protection if we exceed our RSS limit. */ | 67 | if (!swap_token_default_timeout) { |
| 66 | // if (current->mm->rss > current->mm->rlimit_rss) | 68 | swap_token_check = jiffies + SWAP_TOKEN_CHECK_INTERVAL; |
| 67 | // return; | 69 | return; |
| 70 | } | ||
| 68 | 71 | ||
| 69 | /* ... or if we recently held the token. */ | 72 | /* ... or if we recently held the token. */ |
| 70 | if (time_before(jiffies, current->mm->swap_token_time)) | 73 | if (time_before(jiffies, current->mm->swap_token_time)) |
| @@ -95,6 +98,7 @@ void __put_swap_token(struct mm_struct *mm) | |||
| 95 | { | 98 | { |
| 96 | spin_lock(&swap_token_lock); | 99 | spin_lock(&swap_token_lock); |
| 97 | if (likely(mm == swap_token_mm)) { | 100 | if (likely(mm == swap_token_mm)) { |
| 101 | mm->swap_token_time = jiffies + SWAP_TOKEN_CHECK_INTERVAL; | ||
| 98 | swap_token_mm = &init_mm; | 102 | swap_token_mm = &init_mm; |
| 99 | swap_token_check = jiffies; | 103 | swap_token_check = jiffies; |
| 100 | } | 104 | } |
diff --git a/mm/vmscan.c b/mm/vmscan.c index 28130541270f..078cf920208a 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c | |||
| @@ -407,7 +407,7 @@ static int shrink_list(struct list_head *page_list, struct scan_control *sc) | |||
| 407 | if (PageWriteback(page)) | 407 | if (PageWriteback(page)) |
| 408 | goto keep_locked; | 408 | goto keep_locked; |
| 409 | 409 | ||
| 410 | referenced = page_referenced(page, 1, sc->priority <= 0); | 410 | referenced = page_referenced(page, 1); |
| 411 | /* In active use or really unfreeable? Activate it. */ | 411 | /* In active use or really unfreeable? Activate it. */ |
| 412 | if (referenced && page_mapping_inuse(page)) | 412 | if (referenced && page_mapping_inuse(page)) |
| 413 | goto activate_locked; | 413 | goto activate_locked; |
| @@ -756,7 +756,7 @@ refill_inactive_zone(struct zone *zone, struct scan_control *sc) | |||
| 756 | if (page_mapped(page)) { | 756 | if (page_mapped(page)) { |
| 757 | if (!reclaim_mapped || | 757 | if (!reclaim_mapped || |
| 758 | (total_swap_pages == 0 && PageAnon(page)) || | 758 | (total_swap_pages == 0 && PageAnon(page)) || |
| 759 | page_referenced(page, 0, sc->priority <= 0)) { | 759 | page_referenced(page, 0)) { |
| 760 | list_add(&page->lru, &l_active); | 760 | list_add(&page->lru, &l_active); |
| 761 | continue; | 761 | continue; |
| 762 | } | 762 | } |
| @@ -960,6 +960,8 @@ int try_to_free_pages(struct zone **zones, gfp_t gfp_mask) | |||
| 960 | sc.nr_reclaimed = 0; | 960 | sc.nr_reclaimed = 0; |
| 961 | sc.priority = priority; | 961 | sc.priority = priority; |
| 962 | sc.swap_cluster_max = SWAP_CLUSTER_MAX; | 962 | sc.swap_cluster_max = SWAP_CLUSTER_MAX; |
| 963 | if (!priority) | ||
| 964 | disable_swap_token(); | ||
| 963 | shrink_caches(zones, &sc); | 965 | shrink_caches(zones, &sc); |
| 964 | shrink_slab(sc.nr_scanned, gfp_mask, lru_pages); | 966 | shrink_slab(sc.nr_scanned, gfp_mask, lru_pages); |
| 965 | if (reclaim_state) { | 967 | if (reclaim_state) { |
| @@ -1056,6 +1058,10 @@ loop_again: | |||
| 1056 | int end_zone = 0; /* Inclusive. 0 = ZONE_DMA */ | 1058 | int end_zone = 0; /* Inclusive. 0 = ZONE_DMA */ |
| 1057 | unsigned long lru_pages = 0; | 1059 | unsigned long lru_pages = 0; |
| 1058 | 1060 | ||
| 1061 | /* The swap token gets in the way of swapout... */ | ||
| 1062 | if (!priority) | ||
| 1063 | disable_swap_token(); | ||
| 1064 | |||
| 1059 | all_zones_ok = 1; | 1065 | all_zones_ok = 1; |
| 1060 | 1066 | ||
| 1061 | if (nr_pages == 0) { | 1067 | if (nr_pages == 0) { |
| @@ -1360,6 +1366,7 @@ int zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order) | |||
| 1360 | sc.nr_reclaimed = 0; | 1366 | sc.nr_reclaimed = 0; |
| 1361 | /* scan at the highest priority */ | 1367 | /* scan at the highest priority */ |
| 1362 | sc.priority = 0; | 1368 | sc.priority = 0; |
| 1369 | disable_swap_token(); | ||
| 1363 | 1370 | ||
| 1364 | if (nr_pages > SWAP_CLUSTER_MAX) | 1371 | if (nr_pages > SWAP_CLUSTER_MAX) |
| 1365 | sc.swap_cluster_max = nr_pages; | 1372 | sc.swap_cluster_max = nr_pages; |
