diff options
Diffstat (limited to 'mm/memory-failure.c')
-rw-r--r-- | mm/memory-failure.c | 45 |
1 files changed, 37 insertions, 8 deletions
diff --git a/mm/memory-failure.c b/mm/memory-failure.c index dc47415a5511..9a285f8cdbe1 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c | |||
@@ -350,6 +350,30 @@ static const char *action_name[] = { | |||
350 | }; | 350 | }; |
351 | 351 | ||
352 | /* | 352 | /* |
353 | * XXX: It is possible that a page is isolated from LRU cache, | ||
354 | * and then kept in swap cache or failed to remove from page cache. | ||
355 | * The page count will stop it from being freed by unpoison. | ||
356 | * Stress tests should be aware of this memory leak problem. | ||
357 | */ | ||
358 | static int delete_from_lru_cache(struct page *p) | ||
359 | { | ||
360 | if (!isolate_lru_page(p)) { | ||
361 | /* | ||
362 | * Clear sensible page flags, so that the buddy system won't | ||
363 | * complain when the page is unpoison-and-freed. | ||
364 | */ | ||
365 | ClearPageActive(p); | ||
366 | ClearPageUnevictable(p); | ||
367 | /* | ||
368 | * drop the page count elevated by isolate_lru_page() | ||
369 | */ | ||
370 | page_cache_release(p); | ||
371 | return 0; | ||
372 | } | ||
373 | return -EIO; | ||
374 | } | ||
375 | |||
376 | /* | ||
353 | * Error hit kernel page. | 377 | * Error hit kernel page. |
354 | * Do nothing, try to be lucky and not touch this instead. For a few cases we | 378 | * Do nothing, try to be lucky and not touch this instead. For a few cases we |
355 | * could be more sophisticated. | 379 | * could be more sophisticated. |
@@ -393,6 +417,8 @@ static int me_pagecache_clean(struct page *p, unsigned long pfn) | |||
393 | int ret = FAILED; | 417 | int ret = FAILED; |
394 | struct address_space *mapping; | 418 | struct address_space *mapping; |
395 | 419 | ||
420 | delete_from_lru_cache(p); | ||
421 | |||
396 | /* | 422 | /* |
397 | * For anonymous pages we're done the only reference left | 423 | * For anonymous pages we're done the only reference left |
398 | * should be the one m_f() holds. | 424 | * should be the one m_f() holds. |
@@ -522,14 +548,20 @@ static int me_swapcache_dirty(struct page *p, unsigned long pfn) | |||
522 | /* Trigger EIO in shmem: */ | 548 | /* Trigger EIO in shmem: */ |
523 | ClearPageUptodate(p); | 549 | ClearPageUptodate(p); |
524 | 550 | ||
525 | return DELAYED; | 551 | if (!delete_from_lru_cache(p)) |
552 | return DELAYED; | ||
553 | else | ||
554 | return FAILED; | ||
526 | } | 555 | } |
527 | 556 | ||
528 | static int me_swapcache_clean(struct page *p, unsigned long pfn) | 557 | static int me_swapcache_clean(struct page *p, unsigned long pfn) |
529 | { | 558 | { |
530 | delete_from_swap_cache(p); | 559 | delete_from_swap_cache(p); |
531 | 560 | ||
532 | return RECOVERED; | 561 | if (!delete_from_lru_cache(p)) |
562 | return RECOVERED; | ||
563 | else | ||
564 | return FAILED; | ||
533 | } | 565 | } |
534 | 566 | ||
535 | /* | 567 | /* |
@@ -746,7 +778,6 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn, | |||
746 | 778 | ||
747 | int __memory_failure(unsigned long pfn, int trapno, int flags) | 779 | int __memory_failure(unsigned long pfn, int trapno, int flags) |
748 | { | 780 | { |
749 | unsigned long lru_flag; | ||
750 | struct page_state *ps; | 781 | struct page_state *ps; |
751 | struct page *p; | 782 | struct page *p; |
752 | int res; | 783 | int res; |
@@ -796,13 +827,11 @@ int __memory_failure(unsigned long pfn, int trapno, int flags) | |||
796 | */ | 827 | */ |
797 | if (!PageLRU(p)) | 828 | if (!PageLRU(p)) |
798 | lru_add_drain_all(); | 829 | lru_add_drain_all(); |
799 | lru_flag = p->flags & lru; | 830 | if (!PageLRU(p)) { |
800 | if (isolate_lru_page(p)) { | ||
801 | action_result(pfn, "non LRU", IGNORED); | 831 | action_result(pfn, "non LRU", IGNORED); |
802 | put_page(p); | 832 | put_page(p); |
803 | return -EBUSY; | 833 | return -EBUSY; |
804 | } | 834 | } |
805 | page_cache_release(p); | ||
806 | 835 | ||
807 | /* | 836 | /* |
808 | * Lock the page and wait for writeback to finish. | 837 | * Lock the page and wait for writeback to finish. |
@@ -825,7 +854,7 @@ int __memory_failure(unsigned long pfn, int trapno, int flags) | |||
825 | /* | 854 | /* |
826 | * Torn down by someone else? | 855 | * Torn down by someone else? |
827 | */ | 856 | */ |
828 | if ((lru_flag & lru) && !PageSwapCache(p) && p->mapping == NULL) { | 857 | if (PageLRU(p) && !PageSwapCache(p) && p->mapping == NULL) { |
829 | action_result(pfn, "already truncated LRU", IGNORED); | 858 | action_result(pfn, "already truncated LRU", IGNORED); |
830 | res = 0; | 859 | res = 0; |
831 | goto out; | 860 | goto out; |
@@ -833,7 +862,7 @@ int __memory_failure(unsigned long pfn, int trapno, int flags) | |||
833 | 862 | ||
834 | res = -EBUSY; | 863 | res = -EBUSY; |
835 | for (ps = error_states;; ps++) { | 864 | for (ps = error_states;; ps++) { |
836 | if (((p->flags | lru_flag)& ps->mask) == ps->res) { | 865 | if ((p->flags & ps->mask) == ps->res) { |
837 | res = page_action(ps, p, pfn); | 866 | res = page_action(ps, p, pfn); |
838 | break; | 867 | break; |
839 | } | 868 | } |