Commit 68a22394 authored by Rik van Riel's avatar Rik van Riel Committed by Linus Torvalds

vmscan: free swap space on swap-in/activation

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: default avatarRik van Riel <riel@redhat.com>
Signed-off-by: default avatarLee Schermerhorn <Lee.Schermerhorn@hp.com>
Signed-off-by: default avatarMinChan Kim <minchan.kim@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent f04e9ebb
...@@ -25,6 +25,7 @@ void __pagevec_release_nonlru(struct pagevec *pvec); ...@@ -25,6 +25,7 @@ void __pagevec_release_nonlru(struct pagevec *pvec);
void __pagevec_free(struct pagevec *pvec); void __pagevec_free(struct pagevec *pvec);
void ____pagevec_lru_add(struct pagevec *pvec, enum lru_list lru); void ____pagevec_lru_add(struct pagevec *pvec, enum lru_list lru);
void pagevec_strip(struct pagevec *pvec); void pagevec_strip(struct pagevec *pvec);
void pagevec_swap_free(struct pagevec *pvec);
unsigned pagevec_lookup(struct pagevec *pvec, struct address_space *mapping, unsigned pagevec_lookup(struct pagevec *pvec, struct address_space *mapping,
pgoff_t start, unsigned nr_pages); pgoff_t start, unsigned nr_pages);
unsigned pagevec_lookup_tag(struct pagevec *pvec, unsigned pagevec_lookup_tag(struct pagevec *pvec,
......
...@@ -265,6 +265,7 @@ extern sector_t swapdev_block(int, pgoff_t); ...@@ -265,6 +265,7 @@ extern sector_t swapdev_block(int, pgoff_t);
extern struct swap_info_struct *get_swap_info_struct(unsigned); extern struct swap_info_struct *get_swap_info_struct(unsigned);
extern int can_share_swap_page(struct page *); extern int can_share_swap_page(struct page *);
extern int remove_exclusive_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; struct backing_dev_info;
/* linux/mm/thrash.c */ /* linux/mm/thrash.c */
...@@ -353,6 +354,11 @@ static inline int remove_exclusive_swap_page(struct page *p) ...@@ -353,6 +354,11 @@ static inline int remove_exclusive_swap_page(struct page *p)
return 0; return 0;
} }
static inline int remove_exclusive_swap_page_ref(struct page *page)
{
return 0;
}
static inline swp_entry_t get_swap_page(void) static inline swp_entry_t get_swap_page(void)
{ {
swp_entry_t entry; swp_entry_t entry;
......
...@@ -427,6 +427,30 @@ void pagevec_strip(struct pagevec *pvec) ...@@ -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 * pagevec_lookup - gang pagecache lookup
* @pvec: Where the resulting pages are placed * @pvec: Where the resulting pages are placed
......
...@@ -344,7 +344,7 @@ int can_share_swap_page(struct page *page) ...@@ -344,7 +344,7 @@ int can_share_swap_page(struct page *page)
* Work out if there are any other processes sharing this * Work out if there are any other processes sharing this
* swap cache page. Free it if you can. Return success. * 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; int retval;
struct swap_info_struct * p; struct swap_info_struct * p;
...@@ -357,7 +357,7 @@ int remove_exclusive_swap_page(struct page *page) ...@@ -357,7 +357,7 @@ int remove_exclusive_swap_page(struct page *page)
return 0; return 0;
if (PageWriteback(page)) if (PageWriteback(page))
return 0; return 0;
if (page_count(page) != 2) /* 2: us + cache */ if (page_count(page) != count) /* us + cache + ptes */
return 0; return 0;
entry.val = page_private(page); entry.val = page_private(page);
...@@ -370,7 +370,7 @@ int remove_exclusive_swap_page(struct page *page) ...@@ -370,7 +370,7 @@ int remove_exclusive_swap_page(struct page *page)
if (p->swap_map[swp_offset(entry)] == 1) { if (p->swap_map[swp_offset(entry)] == 1) {
/* Recheck the page count with the swapcache lock held.. */ /* Recheck the page count with the swapcache lock held.. */
spin_lock_irq(&swapper_space.tree_lock); 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); __delete_from_swap_cache(page);
SetPageDirty(page); SetPageDirty(page);
retval = 1; retval = 1;
...@@ -387,6 +387,25 @@ int remove_exclusive_swap_page(struct page *page) ...@@ -387,6 +387,25 @@ int remove_exclusive_swap_page(struct page *page)
return retval; 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 swap entry like above, but also try to
* free the page cache entry if it is the last user. * free the page cache entry if it is the last user.
......
...@@ -647,6 +647,9 @@ static unsigned long shrink_page_list(struct list_head *page_list, ...@@ -647,6 +647,9 @@ static unsigned long shrink_page_list(struct list_head *page_list,
continue; continue;
activate_locked: 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); SetPageActive(page);
pgactivate++; pgactivate++;
keep_locked: keep_locked:
...@@ -1228,6 +1231,8 @@ static void shrink_active_list(unsigned long nr_pages, struct zone *zone, ...@@ -1228,6 +1231,8 @@ static void shrink_active_list(unsigned long nr_pages, struct zone *zone,
__mod_zone_page_state(zone, NR_ACTIVE, pgmoved); __mod_zone_page_state(zone, NR_ACTIVE, pgmoved);
pgmoved = 0; pgmoved = 0;
spin_unlock_irq(&zone->lru_lock); spin_unlock_irq(&zone->lru_lock);
if (vm_swap_full())
pagevec_swap_free(&pvec);
__pagevec_release(&pvec); __pagevec_release(&pvec);
spin_lock_irq(&zone->lru_lock); spin_lock_irq(&zone->lru_lock);
} }
...@@ -1237,6 +1242,8 @@ static void shrink_active_list(unsigned long nr_pages, struct zone *zone, ...@@ -1237,6 +1242,8 @@ static void shrink_active_list(unsigned long nr_pages, struct zone *zone,
__count_zone_vm_events(PGREFILL, zone, pgscanned); __count_zone_vm_events(PGREFILL, zone, pgscanned);
__count_vm_events(PGDEACTIVATE, pgdeactivate); __count_vm_events(PGDEACTIVATE, pgdeactivate);
spin_unlock_irq(&zone->lru_lock); spin_unlock_irq(&zone->lru_lock);
if (vm_swap_full())
pagevec_swap_free(&pvec);
pagevec_release(&pvec); pagevec_release(&pvec);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment