Skip to content

Commit

Permalink
vmscan: unevictable LRU scan sysctl
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Lee Schermerhorn authored and Linus Torvalds committed Oct 20, 2008
1 parent 64d6519 commit af936a1
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 2 deletions.
5 changes: 5 additions & 0 deletions drivers/base/node.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
}

Expand Down
3 changes: 3 additions & 0 deletions include/linux/rmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
15 changes: 15 additions & 0 deletions include/linux/swap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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);
Expand Down
10 changes: 10 additions & 0 deletions kernel/sysctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions mm/rmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -201,7 +201,7 @@ static struct anon_vma *page_lock_anon_vma(struct page *page)
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();
Expand Down
166 changes: 166 additions & 0 deletions mm/vmscan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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
Expand All @@ -2382,6 +2416,9 @@ static void check_move_unevictable_page(struct page *page, struct zone *zone)
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);
Expand Down Expand Up @@ -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

0 comments on commit af936a1

Please sign in to comment.