vmscan: unevictable LRU scan sysctl

This patch adds a function to scan individual or all zones' unevictable
lists and move any pages that have become evictable onto the respective
zone's inactive list, where shrink_inactive_list() will deal with them.

Adds sysctl to scan all nodes, and per node attributes to individual
nodes' zones.

Kosaki: If evictable page found in unevictable lru when write
/proc/sys/vm/scan_unevictable_pages, print filename and file offset of
these pages.

[akpm@linux-foundation.org: fix one CONFIG_MMU=n build error]
[kosaki.motohiro@jp.fujitsu.com: adapt vmscan-unevictable-lru-scan-sysctl.patch to new sysfs API]
Signed-off-by: Lee Schermerhorn <lee.schermerhorn@hp.com>
Signed-off-by: Rik van Riel <riel@redhat.com>
Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Lee Schermerhorn 2008-10-18 20:26:53 -07:00 committed by Linus Torvalds
parent 64d6519dda
commit af936a1606
6 changed files with 201 additions and 2 deletions

View file

@ -13,6 +13,7 @@
#include <linux/nodemask.h>
#include <linux/cpu.h>
#include <linux/device.h>
#include <linux/swap.h>
static struct sysdev_class node_class = {
.name = "node",
@ -191,6 +192,8 @@ int register_node(struct node *node, int num, struct node *parent)
sysdev_create_file(&node->sysdev, &attr_meminfo);
sysdev_create_file(&node->sysdev, &attr_numastat);
sysdev_create_file(&node->sysdev, &attr_distance);
scan_unevictable_register_node(node);
}
return error;
}
@ -210,6 +213,8 @@ void unregister_node(struct node *node)
sysdev_remove_file(&node->sysdev, &attr_numastat);
sysdev_remove_file(&node->sysdev, &attr_distance);
scan_unevictable_unregister_node(node);
sysdev_unregister(&node->sysdev);
}

View file

@ -75,6 +75,9 @@ void anon_vma_unlink(struct vm_area_struct *);
void anon_vma_link(struct vm_area_struct *);
void __anon_vma_link(struct vm_area_struct *);
extern struct anon_vma *page_lock_anon_vma(struct page *page);
extern void page_unlock_anon_vma(struct anon_vma *anon_vma);
/*
* rmap interfaces called when adding or removing pte of page
*/

View file

@ -7,6 +7,7 @@
#include <linux/list.h>
#include <linux/memcontrol.h>
#include <linux/sched.h>
#include <linux/node.h>
#include <asm/atomic.h>
#include <asm/page.h>
@ -235,15 +236,29 @@ static inline int zone_reclaim(struct zone *z, gfp_t mask, unsigned int order)
#ifdef CONFIG_UNEVICTABLE_LRU
extern int page_evictable(struct page *page, struct vm_area_struct *vma);
extern void scan_mapping_unevictable_pages(struct address_space *);
extern unsigned long scan_unevictable_pages;
extern int scan_unevictable_handler(struct ctl_table *, int, struct file *,
void __user *, size_t *, loff_t *);
extern int scan_unevictable_register_node(struct node *node);
extern void scan_unevictable_unregister_node(struct node *node);
#else
static inline int page_evictable(struct page *page,
struct vm_area_struct *vma)
{
return 1;
}
static inline void scan_mapping_unevictable_pages(struct address_space *mapping)
{
}
static inline int scan_unevictable_register_node(struct node *node)
{
return 0;
}
static inline void scan_unevictable_unregister_node(struct node *node) { }
#endif
extern int kswapd_run(int nid);

View file

@ -833,6 +833,16 @@ static struct ctl_table kern_table[] = {
.proc_handler = &proc_dointvec,
},
#endif
#ifdef CONFIG_UNEVICTABLE_LRU
{
.ctl_name = CTL_UNNUMBERED,
.procname = "scan_unevictable_pages",
.data = &scan_unevictable_pages,
.maxlen = sizeof(scan_unevictable_pages),
.mode = 0644,
.proc_handler = &scan_unevictable_handler,
},
#endif
/*
* NOTE: do not add new entries to this table unless you have read
* Documentation/sysctl/ctl_unnumbered.txt

View file

@ -181,7 +181,7 @@ void __init anon_vma_init(void)
* Getting a lock on a stable anon_vma from a page off the LRU is
* tricky: page_lock_anon_vma rely on RCU to guard against the races.
*/
static struct anon_vma *page_lock_anon_vma(struct page *page)
struct anon_vma *page_lock_anon_vma(struct page *page)
{
struct anon_vma *anon_vma;
unsigned long anon_mapping;
@ -201,7 +201,7 @@ out:
return NULL;
}
static void page_unlock_anon_vma(struct anon_vma *anon_vma)
void page_unlock_anon_vma(struct anon_vma *anon_vma)
{
spin_unlock(&anon_vma->lock);
rcu_read_unlock();

View file

@ -39,6 +39,7 @@
#include <linux/freezer.h>
#include <linux/memcontrol.h>
#include <linux/delayacct.h>
#include <linux/sysctl.h>
#include <asm/tlbflush.h>
#include <asm/div64.h>
@ -2363,6 +2364,39 @@ int page_evictable(struct page *page, struct vm_area_struct *vma)
return 1;
}
static void show_page_path(struct page *page)
{
char buf[256];
if (page_is_file_cache(page)) {
struct address_space *mapping = page->mapping;
struct dentry *dentry;
pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
spin_lock(&mapping->i_mmap_lock);
dentry = d_find_alias(mapping->host);
printk(KERN_INFO "rescued: %s %lu\n",
dentry_path(dentry, buf, 256), pgoff);
spin_unlock(&mapping->i_mmap_lock);
} else {
#if defined(CONFIG_MM_OWNER) && defined(CONFIG_MMU)
struct anon_vma *anon_vma;
struct vm_area_struct *vma;
anon_vma = page_lock_anon_vma(page);
if (!anon_vma)
return;
list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
printk(KERN_INFO "rescued: anon %s\n",
vma->vm_mm->owner->comm);
break;
}
page_unlock_anon_vma(anon_vma);
#endif
}
}
/**
* check_move_unevictable_page - check page for evictability and move to appropriate zone lru list
* @page: page to check evictability and move to appropriate lru list
@ -2382,6 +2416,9 @@ retry:
ClearPageUnevictable(page);
if (page_evictable(page, NULL)) {
enum lru_list l = LRU_INACTIVE_ANON + page_is_file_cache(page);
show_page_path(page);
__dec_zone_state(zone, NR_UNEVICTABLE);
list_move(&page->lru, &zone->lru[l].list);
__inc_zone_state(zone, NR_INACTIVE_ANON + l);
@ -2451,4 +2488,133 @@ void scan_mapping_unevictable_pages(struct address_space *mapping)
}
}
/**
* scan_zone_unevictable_pages - check unevictable list for evictable pages
* @zone - zone of which to scan the unevictable list
*
* Scan @zone's unevictable LRU lists to check for pages that have become
* evictable. Move those that have to @zone's inactive list where they
* become candidates for reclaim, unless shrink_inactive_zone() decides
* to reactivate them. Pages that are still unevictable are rotated
* back onto @zone's unevictable list.
*/
#define SCAN_UNEVICTABLE_BATCH_SIZE 16UL /* arbitrary lock hold batch size */
void scan_zone_unevictable_pages(struct zone *zone)
{
struct list_head *l_unevictable = &zone->lru[LRU_UNEVICTABLE].list;
unsigned long scan;
unsigned long nr_to_scan = zone_page_state(zone, NR_UNEVICTABLE);
while (nr_to_scan > 0) {
unsigned long batch_size = min(nr_to_scan,
SCAN_UNEVICTABLE_BATCH_SIZE);
spin_lock_irq(&zone->lru_lock);
for (scan = 0; scan < batch_size; scan++) {
struct page *page = lru_to_page(l_unevictable);
if (!trylock_page(page))
continue;
prefetchw_prev_lru_page(page, l_unevictable, flags);
if (likely(PageLRU(page) && PageUnevictable(page)))
check_move_unevictable_page(page, zone);
unlock_page(page);
}
spin_unlock_irq(&zone->lru_lock);
nr_to_scan -= batch_size;
}
}
/**
* scan_all_zones_unevictable_pages - scan all unevictable lists for evictable pages
*
* A really big hammer: scan all zones' unevictable LRU lists to check for
* pages that have become evictable. Move those back to the zones'
* inactive list where they become candidates for reclaim.
* This occurs when, e.g., we have unswappable pages on the unevictable lists,
* and we add swap to the system. As such, it runs in the context of a task
* that has possibly/probably made some previously unevictable pages
* evictable.
*/
void scan_all_zones_unevictable_pages(void)
{
struct zone *zone;
for_each_zone(zone) {
scan_zone_unevictable_pages(zone);
}
}
/*
* scan_unevictable_pages [vm] sysctl handler. On demand re-scan of
* all nodes' unevictable lists for evictable pages
*/
unsigned long scan_unevictable_pages;
int scan_unevictable_handler(struct ctl_table *table, int write,
struct file *file, void __user *buffer,
size_t *length, loff_t *ppos)
{
proc_doulongvec_minmax(table, write, file, buffer, length, ppos);
if (write && *(unsigned long *)table->data)
scan_all_zones_unevictable_pages();
scan_unevictable_pages = 0;
return 0;
}
/*
* per node 'scan_unevictable_pages' attribute. On demand re-scan of
* a specified node's per zone unevictable lists for evictable pages.
*/
static ssize_t read_scan_unevictable_node(struct sys_device *dev,
struct sysdev_attribute *attr,
char *buf)
{
return sprintf(buf, "0\n"); /* always zero; should fit... */
}
static ssize_t write_scan_unevictable_node(struct sys_device *dev,
struct sysdev_attribute *attr,
const char *buf, size_t count)
{
struct zone *node_zones = NODE_DATA(dev->id)->node_zones;
struct zone *zone;
unsigned long res;
unsigned long req = strict_strtoul(buf, 10, &res);
if (!req)
return 1; /* zero is no-op */
for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
if (!populated_zone(zone))
continue;
scan_zone_unevictable_pages(zone);
}
return 1;
}
static SYSDEV_ATTR(scan_unevictable_pages, S_IRUGO | S_IWUSR,
read_scan_unevictable_node,
write_scan_unevictable_node);
int scan_unevictable_register_node(struct node *node)
{
return sysdev_create_file(&node->sysdev, &attr_scan_unevictable_pages);
}
void scan_unevictable_unregister_node(struct node *node)
{
sysdev_remove_file(&node->sysdev, &attr_scan_unevictable_pages);
}
#endif