vmscan: free swap space on swap-in/activation
authorRik van Riel <riel@redhat.com>
Sun, 19 Oct 2008 03:26:23 +0000 (20:26 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 20 Oct 2008 15:50:25 +0000 (08:50 -0700)
If vm_swap_full() (swap space more than 50% full), the system will free
swap space at swapin time.  With this patch, the system will also free the
swap space in the pageout code, when we decide that the page is not a
candidate for swapout (and just wasting swap space).

Signed-off-by: Rik van Riel <riel@redhat.com>
Signed-off-by: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
Signed-off-by: MinChan Kim <minchan.kim@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
include/linux/pagevec.h
include/linux/swap.h
mm/swap.c
mm/swapfile.c
mm/vmscan.c

index 6b8f11bcc9485bc3811ae430ae6ace6b29940eb7..fea3a982ee55d58a2a5626d5e1756779b8a9d0a5 100644 (file)
@@ -25,6 +25,7 @@ void __pagevec_release_nonlru(struct pagevec *pvec);
 void __pagevec_free(struct pagevec *pvec);
 void ____pagevec_lru_add(struct pagevec *pvec, enum lru_list lru);
 void pagevec_strip(struct pagevec *pvec);
+void pagevec_swap_free(struct pagevec *pvec);
 unsigned pagevec_lookup(struct pagevec *pvec, struct address_space *mapping,
                pgoff_t start, unsigned nr_pages);
 unsigned pagevec_lookup_tag(struct pagevec *pvec,
index fcc169610d093c1752746c2b645073437c5f888f..833be56ad835e9c070579042548e3f5f5cce69e7 100644 (file)
@@ -265,6 +265,7 @@ extern sector_t swapdev_block(int, pgoff_t);
 extern struct swap_info_struct *get_swap_info_struct(unsigned);
 extern int can_share_swap_page(struct page *);
 extern int remove_exclusive_swap_page(struct page *);
+extern int remove_exclusive_swap_page_ref(struct page *);
 struct backing_dev_info;
 
 /* linux/mm/thrash.c */
@@ -353,6 +354,11 @@ static inline int remove_exclusive_swap_page(struct page *p)
        return 0;
 }
 
+static inline int remove_exclusive_swap_page_ref(struct page *page)
+{
+       return 0;
+}
+
 static inline swp_entry_t get_swap_page(void)
 {
        swp_entry_t entry;
index e3045040dc3ec26eea087d065855a18aea429838..88a39487267769d83f281664d83e87c4e83859f1 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -427,6 +427,30 @@ void pagevec_strip(struct pagevec *pvec)
        }
 }
 
+/**
+ * pagevec_swap_free - try to free swap space from the pages in a pagevec
+ * @pvec: pagevec with swapcache pages to free the swap space of
+ *
+ * The caller needs to hold an extra reference to each page and
+ * not hold the page lock on the pages.  This function uses a
+ * trylock on the page lock so it may not always free the swap
+ * space associated with a page.
+ */
+void pagevec_swap_free(struct pagevec *pvec)
+{
+       int i;
+
+       for (i = 0; i < pagevec_count(pvec); i++) {
+               struct page *page = pvec->pages[i];
+
+               if (PageSwapCache(page) && trylock_page(page)) {
+                       if (PageSwapCache(page))
+                               remove_exclusive_swap_page_ref(page);
+                       unlock_page(page);
+               }
+       }
+}
+
 /**
  * pagevec_lookup - gang pagecache lookup
  * @pvec:      Where the resulting pages are placed
index 1e330f2998fa259d2733f73f4c50ce0d649c5dc0..2a97fafa3d89684c8fb6770650d91f905faa7a64 100644 (file)
@@ -344,7 +344,7 @@ int can_share_swap_page(struct page *page)
  * Work out if there are any other processes sharing this
  * swap cache page. Free it if you can. Return success.
  */
-int remove_exclusive_swap_page(struct page *page)
+static int remove_exclusive_swap_page_count(struct page *page, int count)
 {
        int retval;
        struct swap_info_struct * p;
@@ -357,7 +357,7 @@ int remove_exclusive_swap_page(struct page *page)
                return 0;
        if (PageWriteback(page))
                return 0;
-       if (page_count(page) != 2) /* 2: us + cache */
+       if (page_count(page) != count) /* us + cache + ptes */
                return 0;
 
        entry.val = page_private(page);
@@ -370,7 +370,7 @@ int remove_exclusive_swap_page(struct page *page)
        if (p->swap_map[swp_offset(entry)] == 1) {
                /* Recheck the page count with the swapcache lock held.. */
                spin_lock_irq(&swapper_space.tree_lock);
-               if ((page_count(page) == 2) && !PageWriteback(page)) {
+               if ((page_count(page) == count) && !PageWriteback(page)) {
                        __delete_from_swap_cache(page);
                        SetPageDirty(page);
                        retval = 1;
@@ -387,6 +387,25 @@ int remove_exclusive_swap_page(struct page *page)
        return retval;
 }
 
+/*
+ * Most of the time the page should have two references: one for the
+ * process and one for the swap cache.
+ */
+int remove_exclusive_swap_page(struct page *page)
+{
+       return remove_exclusive_swap_page_count(page, 2);
+}
+
+/*
+ * The pageout code holds an extra reference to the page.  That raises
+ * the reference count to test for to 2 for a page that is only in the
+ * swap cache plus 1 for each process that maps the page.
+ */
+int remove_exclusive_swap_page_ref(struct page *page)
+{
+       return remove_exclusive_swap_page_count(page, 2 + page_mapcount(page));
+}
+
 /*
  * Free the swap entry like above, but also try to
  * free the page cache entry if it is the last user.
index 46fdaa546b8d0493acb74b2b428693d46e0b14b3..e656035d34065d0bb0c857c770940c9acec73b71 100644 (file)
@@ -647,6 +647,9 @@ free_it:
                continue;
 
 activate_locked:
+               /* Not a candidate for swapping, so reclaim swap space. */
+               if (PageSwapCache(page) && vm_swap_full())
+                       remove_exclusive_swap_page_ref(page);
                SetPageActive(page);
                pgactivate++;
 keep_locked:
@@ -1228,6 +1231,8 @@ static void shrink_active_list(unsigned long nr_pages, struct zone *zone,
                        __mod_zone_page_state(zone, NR_ACTIVE, pgmoved);
                        pgmoved = 0;
                        spin_unlock_irq(&zone->lru_lock);
+                       if (vm_swap_full())
+                               pagevec_swap_free(&pvec);
                        __pagevec_release(&pvec);
                        spin_lock_irq(&zone->lru_lock);
                }
@@ -1237,6 +1242,8 @@ static void shrink_active_list(unsigned long nr_pages, struct zone *zone,
        __count_zone_vm_events(PGREFILL, zone, pgscanned);
        __count_vm_events(PGDEACTIVATE, pgdeactivate);
        spin_unlock_irq(&zone->lru_lock);
+       if (vm_swap_full())
+               pagevec_swap_free(&pvec);
 
        pagevec_release(&pvec);
 }