]> err.no Git - linux-2.6/commitdiff
[PATCH] mm: never ClearPageLRU released pages
authorNick Piggin <npiggin@suse.de>
Wed, 22 Mar 2006 08:07:58 +0000 (00:07 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Wed, 22 Mar 2006 15:53:56 +0000 (07:53 -0800)
If vmscan finds a zero refcount page on the lru list, never ClearPageLRU
it.  This means the release code need not hold ->lru_lock to stabilise
PageLRU, so that lock may be skipped entirely when releasing !PageLRU pages
(because we know PageLRU won't have been temporarily cleared by vmscan,
which was previously guaranteed by holding the lock to synchronise against
vmscan).

Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
mm/swap.c
mm/vmscan.c

index b524ea90bddb93554b7ace67af24af59fc657d17..3045a0f4c4519ac5052cbc052450dd0723056e2e 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -209,19 +209,18 @@ int lru_add_drain_all(void)
  */
 void fastcall __page_cache_release(struct page *page)
 {
-       unsigned long flags;
-       struct zone *zone = page_zone(page);
+       if (PageLRU(page)) {
+               unsigned long flags;
+               struct zone *zone = page_zone(page);
 
-       spin_lock_irqsave(&zone->lru_lock, flags);
-       if (TestClearPageLRU(page))
+               spin_lock_irqsave(&zone->lru_lock, flags);
+               if (!TestClearPageLRU(page))
+                       BUG();
                del_page_from_lru(zone, page);
-       if (page_count(page) != 0)
-               page = NULL;
-       spin_unlock_irqrestore(&zone->lru_lock, flags);
-       if (page)
-               free_hot_page(page);
+               spin_unlock_irqrestore(&zone->lru_lock, flags);
+       }
+       free_hot_page(page);
 }
-
 EXPORT_SYMBOL(__page_cache_release);
 
 /*
@@ -245,7 +244,6 @@ void release_pages(struct page **pages, int nr, int cold)
        pagevec_init(&pages_to_free, cold);
        for (i = 0; i < nr; i++) {
                struct page *page = pages[i];
-               struct zone *pagezone;
 
                if (unlikely(PageCompound(page))) {
                        if (zone) {
@@ -259,23 +257,27 @@ void release_pages(struct page **pages, int nr, int cold)
                if (!put_page_testzero(page))
                        continue;
 
-               pagezone = page_zone(page);
-               if (pagezone != zone) {
-                       if (zone)
-                               spin_unlock_irq(&zone->lru_lock);
-                       zone = pagezone;
-                       spin_lock_irq(&zone->lru_lock);
-               }
-               if (TestClearPageLRU(page))
+               if (PageLRU(page)) {
+                       struct zone *pagezone = page_zone(page);
+                       if (pagezone != zone) {
+                               if (zone)
+                                       spin_unlock_irq(&zone->lru_lock);
+                               zone = pagezone;
+                               spin_lock_irq(&zone->lru_lock);
+                       }
+                       if (!TestClearPageLRU(page))
+                               BUG();
                        del_page_from_lru(zone, page);
-               if (page_count(page) == 0) {
-                       if (!pagevec_add(&pages_to_free, page)) {
+               }
+
+               if (!pagevec_add(&pages_to_free, page)) {
+                       if (zone) {
                                spin_unlock_irq(&zone->lru_lock);
-                               __pagevec_free(&pages_to_free);
-                               pagevec_reinit(&pages_to_free);
-                               zone = NULL;    /* No lock is held */
+                               zone = NULL;
                        }
-               }
+                       __pagevec_free(&pages_to_free);
+                       pagevec_reinit(&pages_to_free);
+               }
        }
        if (zone)
                spin_unlock_irq(&zone->lru_lock);
index 4fe7e3aa02e2f38773105620d2640d779f6381ca..acb7611cd5257e5efba27e4a70526130bc436620 100644 (file)
@@ -1085,21 +1085,25 @@ static int isolate_lru_pages(int nr_to_scan, struct list_head *src,
                page = lru_to_page(src);
                prefetchw_prev_lru_page(page, src, flags);
 
-               if (!TestClearPageLRU(page))
-                       BUG();
                list_del(&page->lru);
-               if (get_page_testone(page)) {
+               if (unlikely(get_page_testone(page))) {
                        /*
                         * It is being freed elsewhere
                         */
                        __put_page(page);
-                       SetPageLRU(page);
                        list_add(&page->lru, src);
                        continue;
-               } else {
-                       list_add(&page->lru, dst);
-                       nr_taken++;
                }
+
+               /*
+                * Be careful not to clear PageLRU until after we're sure
+                * the page is not being freed elsewhere -- the page release
+                * code relies on it.
+                */
+               if (!TestClearPageLRU(page))
+                       BUG();
+               list_add(&page->lru, dst);
+               nr_taken++;
        }
 
        *scanned = scan;