aboutsummaryrefslogtreecommitdiffstats
path: root/mm/vmscan.c
diff options
context:
space:
mode:
authorNick Piggin <npiggin@suse.de>2006-01-18 20:42:27 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-18 22:20:17 -0500
commit053837fce7aa79025ed57656855df09f80175527 (patch)
tree05d7615894131a368fc4943f641b11acdd2ae694 /mm/vmscan.c
parente236a166b2bc437769a9b8b5d19186a3761bde48 (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.c71
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
763static 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 */
778int isolate_lru_page(struct page *page) 771int 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
783redo: 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