Unevictable LRU Infrastructure

When the system contains lots of mlocked or otherwise unevictable pages,
the pageout code (kswapd) can spend lots of time scanning over these
pages.  Worse still, the presence of lots of unevictable pages can confuse
kswapd into thinking that more aggressive pageout modes are required,
resulting in all kinds of bad behaviour.

Infrastructure to manage pages excluded from reclaim--i.e., hidden from
vmscan.  Based on a patch by Larry Woodman of Red Hat.  Reworked to
maintain "unevictable" pages on a separate per-zone LRU list, to "hide"
them from vmscan.

Kosaki Motohiro added the support for the memory controller unevictable
lru list.

Pages on the unevictable list have both PG_unevictable and PG_lru set.
Thus, PG_unevictable is analogous to and mutually exclusive with
PG_active--it specifies which LRU list the page is on.

The unevictable infrastructure is enabled by a new mm Kconfig option
[CONFIG_]UNEVICTABLE_LRU.

A new function 'page_evictable(page, vma)' in vmscan.c tests whether or
not a page may be evictable.  Subsequent patches will add the various
!evictable tests.  We'll want to keep these tests light-weight for use in
shrink_active_list() and, possibly, the fault path.

To avoid races between tasks putting pages [back] onto an LRU list and
tasks that might be moving the page from non-evictable to evictable state,
the new function 'putback_lru_page()' -- inverse to 'isolate_lru_page()'
-- tests the "evictability" of a page after placing it on the LRU, before
dropping the reference.  If the page has become unevictable,
putback_lru_page() will redo the 'putback', thus moving the page to the
unevictable list.  This way, we avoid "stranding" evictable pages on the
unevictable list.

[[email protected]: fix fallout from out-of-order merge]
[[email protected]: fix UNEVICTABLE_LRU and !PROC_PAGE_MONITOR build]
[[email protected]: remove redundant mapping check]
[[email protected]: unevictable-lru-infrastructure: putback_lru_page()/unevictable page handling rework]
[[email protected]: kill unnecessary lock_page() in vmscan.c]
[[email protected]: revert migration change of unevictable lru infrastructure]
[[email protected]: revert to unevictable-lru-infrastructure-kconfig-fix.patch]
[[email protected]: restore patch failure of vmstat-unevictable-and-mlocked-pages-vm-events.patch]
Signed-off-by: Lee Schermerhorn <[email protected]>
Signed-off-by: Rik van Riel <[email protected]>
Signed-off-by: KOSAKI Motohiro <[email protected]>
Debugged-by: Benjamin Kidwell <[email protected]>
Signed-off-by: Daisuke Nishimura <[email protected]>
Signed-off-by: KAMEZAWA Hiroyuki <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
This commit is contained in:
Lee Schermerhorn 2008-10-18 20:26:39 -07:00 • committed by Linus Torvalds
commit 894bc31041
13 changed files with 345 additions and 73 deletions

View file

@ -53,14 +53,9 @@ int migrate_prep(void)
return 0;
}
static inline void move_to_lru(struct page *page)
{
lru_cache_add_lru(page, page_lru(page));
put_page(page);
}
/*
* Add isolated pages on the list back to the LRU.
* Add isolated pages on the list back to the LRU under page lock
* to avoid leaking evictable pages back onto unevictable list.
*
* returns the number of pages put back.
*/
@ -72,7 +67,7 @@ int putback_lru_pages(struct list_head *l)
list_for_each_entry_safe(page, page2, l, lru) {
list_del(&page->lru);
move_to_lru(page);
putback_lru_page(page);
count++;
}
return count;
@ -354,8 +349,11 @@ static void migrate_page_copy(struct page *newpage, struct page *page)
SetPageReferenced(newpage);
if (PageUptodate(page))
SetPageUptodate(newpage);
if (PageActive(page))
if (TestClearPageActive(page)) {
VM_BUG_ON(PageUnevictable(page));
SetPageActive(newpage);
} else
unevictable_migrate_page(newpage, page);
if (PageChecked(page))
SetPageChecked(newpage);
if (PageMappedToDisk(page))
@ -376,7 +374,6 @@ static void migrate_page_copy(struct page *newpage, struct page *page)
#ifdef CONFIG_SWAP
ClearPageSwapCache(page);
#endif
ClearPageActive(page);
ClearPagePrivate(page);
set_page_private(page, 0);
page->mapping = NULL;
@ -555,6 +552,10 @@ static int fallback_migrate_page(struct address_space *mapping,
*
* The new page will have replaced the old page if this function
* is successful.
*
* Return value:
* < 0 - error code
* == 0 - success
*/
static int move_to_new_page(struct page *newpage, struct page *page)
{
@ -617,9 +618,10 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
if (!newpage)
return -ENOMEM;
if (page_count(page) == 1)
if (page_count(page) == 1) {
/* page was freed from under us. So we are done. */
goto move_newpage;
}
charge = mem_cgroup_prepare_migration(page, newpage);
if (charge == -ENOMEM) {
@ -693,7 +695,6 @@ rcu_unlock:
rcu_read_unlock();
unlock:
unlock_page(page);
if (rc != -EAGAIN) {
@ -704,17 +705,19 @@ unlock:
* restored.
*/
list_del(&page->lru);
move_to_lru(page);
putback_lru_page(page);
}
move_newpage:
if (!charge)
mem_cgroup_end_migration(newpage);
/*
* Move the new page to the LRU. If migration was not successful
* then this will free the page.
*/
move_to_lru(newpage);
putback_lru_page(newpage);
if (result) {
if (rc)
*result = rc;