diff options
author | Nick Piggin <npiggin@suse.de> | 2006-01-18 20:42:27 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-18 22:20:17 -0500 |
commit | 053837fce7aa79025ed57656855df09f80175527 (patch) | |
tree | 05d7615894131a368fc4943f641b11acdd2ae694 /mm/vmscan.c | |
parent | e236a166b2bc437769a9b8b5d19186a3761bde48 (diff) |
[PATCH] mm: migration page refcounting fix
Migration code currently does not take a reference to target page
properly, so between unlocking the pte and trying to take a new
reference to the page with isolate_lru_page, anything could happen to
it.
Fix this by holding the pte lock until we get a chance to elevate the
refcount.
Other small cleanups while we're here.
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Christoph Lameter <clameter@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'mm/vmscan.c')
-rw-r--r-- | mm/vmscan.c | 71 |
1 files changed, 30 insertions, 41 deletions
diff --git a/mm/vmscan.c b/mm/vmscan.c index bf903b2d198f..827bf674577a 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c | |||
@@ -586,7 +586,7 @@ static inline void move_to_lru(struct page *page) | |||
586 | } | 586 | } |
587 | 587 | ||
588 | /* | 588 | /* |
589 | * Add isolated pages on the list back to the LRU | 589 | * Add isolated pages on the list back to the LRU. |
590 | * | 590 | * |
591 | * returns the number of pages put back. | 591 | * returns the number of pages put back. |
592 | */ | 592 | */ |
@@ -760,46 +760,33 @@ next: | |||
760 | return nr_failed + retry; | 760 | return nr_failed + retry; |
761 | } | 761 | } |
762 | 762 | ||
763 | static void lru_add_drain_per_cpu(void *dummy) | ||
764 | { | ||
765 | lru_add_drain(); | ||
766 | } | ||
767 | |||
768 | /* | 763 | /* |
769 | * Isolate one page from the LRU lists and put it on the | 764 | * Isolate one page from the LRU lists and put it on the |
770 | * indicated list. Do necessary cache draining if the | 765 | * indicated list with elevated refcount. |
771 | * page is not on the LRU lists yet. | ||
772 | * | 766 | * |
773 | * Result: | 767 | * Result: |
774 | * 0 = page not on LRU list | 768 | * 0 = page not on LRU list |
775 | * 1 = page removed from LRU list and added to the specified list. | 769 | * 1 = page removed from LRU list and added to the specified list. |
776 | * -ENOENT = page is being freed elsewhere. | ||
777 | */ | 770 | */ |
778 | int isolate_lru_page(struct page *page) | 771 | int isolate_lru_page(struct page *page) |
779 | { | 772 | { |
780 | int rc = 0; | 773 | int ret = 0; |
781 | struct zone *zone = page_zone(page); | ||
782 | 774 | ||
783 | redo: | 775 | if (PageLRU(page)) { |
784 | spin_lock_irq(&zone->lru_lock); | 776 | struct zone *zone = page_zone(page); |
785 | rc = __isolate_lru_page(page); | 777 | spin_lock_irq(&zone->lru_lock); |
786 | if (rc == 1) { | 778 | if (TestClearPageLRU(page)) { |
787 | if (PageActive(page)) | 779 | ret = 1; |
788 | del_page_from_active_list(zone, page); | 780 | get_page(page); |
789 | else | 781 | if (PageActive(page)) |
790 | del_page_from_inactive_list(zone, page); | 782 | del_page_from_active_list(zone, page); |
791 | } | 783 | else |
792 | spin_unlock_irq(&zone->lru_lock); | 784 | del_page_from_inactive_list(zone, page); |
793 | if (rc == 0) { | 785 | } |
794 | /* | 786 | spin_unlock_irq(&zone->lru_lock); |
795 | * Maybe this page is still waiting for a cpu to drain it | ||
796 | * from one of the lru lists? | ||
797 | */ | ||
798 | rc = schedule_on_each_cpu(lru_add_drain_per_cpu, NULL); | ||
799 | if (rc == 0 && PageLRU(page)) | ||
800 | goto redo; | ||
801 | } | 787 | } |
802 | return rc; | 788 | |
789 | return ret; | ||
803 | } | 790 | } |
804 | #endif | 791 | #endif |
805 | 792 | ||
@@ -831,18 +818,20 @@ static int isolate_lru_pages(int nr_to_scan, struct list_head *src, | |||
831 | page = lru_to_page(src); | 818 | page = lru_to_page(src); |
832 | prefetchw_prev_lru_page(page, src, flags); | 819 | prefetchw_prev_lru_page(page, src, flags); |
833 | 820 | ||
834 | switch (__isolate_lru_page(page)) { | 821 | if (!TestClearPageLRU(page)) |
835 | case 1: | ||
836 | /* Succeeded to isolate page */ | ||
837 | list_move(&page->lru, dst); | ||
838 | nr_taken++; | ||
839 | break; | ||
840 | case -ENOENT: | ||
841 | /* Not possible to isolate */ | ||
842 | list_move(&page->lru, src); | ||
843 | break; | ||
844 | default: | ||
845 | BUG(); | 822 | BUG(); |
823 | list_del(&page->lru); | ||
824 | if (get_page_testone(page)) { | ||
825 | /* | ||
826 | * It is being freed elsewhere | ||
827 | */ | ||
828 | __put_page(page); | ||
829 | SetPageLRU(page); | ||
830 | list_add(&page->lru, src); | ||
831 | continue; | ||
832 | } else { | ||
833 | list_add(&page->lru, dst); | ||
834 | nr_taken++; | ||
846 | } | 835 | } |
847 | } | 836 | } |
848 | 837 | ||