forked from TencentOS/TencentOS-kernel
pagecachelimit: limit the pagecache ratio of totalram
limit the pagecache ratio of totalram Signed-off-by: Zhiping Du <zhipingdu@tencent.com> Signed-off-by: Chunguang Xu <brookxu@tencent.com>
This commit is contained in:
parent
36ac6e3ba3
commit
6711b34671
|
|
@ -0,0 +1,49 @@
|
|||
Functionality:
|
||||
-------------
|
||||
The patch introduces three new tunables in the proc filesystem:
|
||||
|
||||
/proc/sys/vm/pagecache_limit_ratio
|
||||
|
||||
This tunable sets a limit ratio of totalram to the unmapped pages in the pagecache.
|
||||
If zero, it wil disable pagecache limit function. and it will work if non-zero.
|
||||
|
||||
Examples:
|
||||
echo 50 >/proc/sys/vm/pagecache_limit_ratio
|
||||
|
||||
This sets a baseline limits for the page cache (not the buffer cache!) of 50% totalram.
|
||||
As we only consider pagecache pages that are unmapped, currently mapped pages (files that are mmap'ed such as e.g. binaries and libraries as well as SysV shared memory) are not limited by this.
|
||||
|
||||
/proc/sys/vm/pagecache_limit_reclaim_ratio
|
||||
|
||||
This sets the real reclaim ratio of totalram, it defaults 2%(ADDITIONAL_RECLAIM_RATIO) larger than pagecache_limit_ratio. pagecache_limit_ratio is the check ratio of pagecache, and we will reclaim some more than this, in case of reclaim pagecache frequently.
|
||||
|
||||
/proc/sys/vm/pagecache_limit_ignore_dirty
|
||||
|
||||
The default for this setting is 1; this means that we don't consider dirty memory to be part of the limited pagecache, as we can not easily free up dirty memory (we'd need to do writes for this). By setting this to 0, we actually consider dirty (unampped) memory to be freeable and do a third pass in shrink_page_cache() where we schedule the pages for writeout. Values larger than 1 are also possible and result in a fraction of the dirty pages to be considered non-freeable.
|
||||
|
||||
|
||||
|
||||
|
||||
How it works:
|
||||
------------
|
||||
The heart of this patch is a new function called shrink_page_cache(). It is called from balance_pgdat (which is the worker for kswapd) or add_to_page_cache if the pagecache is above the limit.
|
||||
The balance_pgdat() is also called in __alloc_pages_slowpath.
|
||||
|
||||
shrink_page_cache() calculates the nr of pages the cache is over its limit. It reduces this number then shrinks the pagecache (using the Kernel LRUs).
|
||||
|
||||
shrink_page_cache does several passes:
|
||||
- Just reclaiming from inactive pagecache memory.
|
||||
This is fast -- but it might not find enough free pages; if that happens,
|
||||
the second pass will happen
|
||||
- In the second pass, pages from active list will also be considered.
|
||||
- The third pass will only happen if pagecacahe_limig_ignore-dirty is not 1.
|
||||
In that case, the third pass is a repetition of the second pass, but this
|
||||
time we allow pages to be written out.
|
||||
|
||||
In all passes, only unmapped pages will be considered.
|
||||
|
||||
|
||||
Foreground vs. background shrinking:
|
||||
-----------------------------------
|
||||
|
||||
Usually, the Linux kernel reclaims its memory using the kernel thread kswapd. It reclaims memory in the background. If it can't reclaim memory fast enough, it retries with higher priority and if this still doesn't succeed it uses a direct reclaim path.
|
||||
|
|
@ -375,6 +375,14 @@ struct zone {
|
|||
*/
|
||||
long lowmem_reserve[MAX_NR_ZONES];
|
||||
|
||||
/*
|
||||
* This atomic counter is set when there is pagecache limit
|
||||
* reclaim going on on this particular zone. Other potential
|
||||
* reclaiers should back off to prevent from heavy lru_lock
|
||||
* bouncing.
|
||||
*/
|
||||
atomic_t pagecache_reclaim;
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
int node;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -609,6 +609,8 @@ static inline int fault_in_pages_readable(const char __user *uaddr, int size)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int add_to_page_cache(struct page *page, struct address_space *mapping,
|
||||
pgoff_t index, gfp_t gfp_mask);
|
||||
int add_to_page_cache_locked(struct page *page, struct address_space *mapping,
|
||||
pgoff_t index, gfp_t gfp_mask);
|
||||
int add_to_page_cache_lru(struct page *page, struct address_space *mapping,
|
||||
|
|
@ -617,22 +619,6 @@ extern void delete_from_page_cache(struct page *page);
|
|||
extern void __delete_from_page_cache(struct page *page, void *shadow);
|
||||
int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask);
|
||||
|
||||
/*
|
||||
* Like add_to_page_cache_locked, but used to add newly allocated pages:
|
||||
* the page is new, so we can just run __SetPageLocked() against it.
|
||||
*/
|
||||
static inline int add_to_page_cache(struct page *page,
|
||||
struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask)
|
||||
{
|
||||
int error;
|
||||
|
||||
__SetPageLocked(page);
|
||||
error = add_to_page_cache_locked(page, mapping, offset, gfp_mask);
|
||||
if (unlikely(error))
|
||||
__ClearPageLocked(page);
|
||||
return error;
|
||||
}
|
||||
|
||||
static inline unsigned long dir_pages(struct inode *inode)
|
||||
{
|
||||
return (unsigned long)(inode->i_size + PAGE_SIZE - 1) >>
|
||||
|
|
|
|||
|
|
@ -356,6 +356,14 @@ extern unsigned long mem_cgroup_shrink_node(struct mem_cgroup *mem,
|
|||
unsigned long *nr_scanned);
|
||||
extern unsigned long shrink_all_memory(unsigned long nr_pages);
|
||||
extern int vm_swappiness;
|
||||
#define ADDITIONAL_RECLAIM_RATIO 2
|
||||
extern unsigned long pagecache_over_limit(void);
|
||||
extern void shrink_page_cache(gfp_t mask, struct page *page);
|
||||
extern unsigned long vm_pagecache_limit_pages;
|
||||
extern unsigned long vm_pagecache_limit_reclaim_pages;
|
||||
extern int vm_pagecache_limit_ratio;
|
||||
extern int vm_pagecache_limit_reclaim_ratio;
|
||||
extern unsigned int vm_pagecache_ignore_dirty;
|
||||
extern int remove_mapping(struct address_space *mapping, struct page *page);
|
||||
extern unsigned long vm_total_pages;
|
||||
|
||||
|
|
|
|||
|
|
@ -1280,6 +1280,12 @@ static struct ctl_table kern_table[] = {
|
|||
{ }
|
||||
};
|
||||
|
||||
int pc_limit_proc_dointvec(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos);
|
||||
|
||||
int pc_reclaim_limit_proc_dointvec(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos);
|
||||
|
||||
static struct ctl_table vm_table[] = {
|
||||
{
|
||||
.procname = "overcommit_memory",
|
||||
|
|
@ -1406,6 +1412,31 @@ static struct ctl_table vm_table[] = {
|
|||
.extra1 = &zero,
|
||||
.extra2 = &one_hundred,
|
||||
},
|
||||
{
|
||||
.procname = "pagecache_limit_ratio",
|
||||
.data = &vm_pagecache_limit_ratio,
|
||||
.maxlen = sizeof(vm_pagecache_limit_ratio),
|
||||
.mode = 0644,
|
||||
.proc_handler = &pc_limit_proc_dointvec,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &one_hundred,
|
||||
},
|
||||
{
|
||||
.procname = "pagecache_limit_reclaim_ratio",
|
||||
.data = &vm_pagecache_limit_reclaim_ratio,
|
||||
.maxlen = sizeof(vm_pagecache_limit_reclaim_ratio),
|
||||
.mode = 0644,
|
||||
.proc_handler = &pc_reclaim_limit_proc_dointvec,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &one_hundred,
|
||||
},
|
||||
{
|
||||
.procname = "pagecache_limit_ignore_dirty",
|
||||
.data = &vm_pagecache_ignore_dirty,
|
||||
.maxlen = sizeof(vm_pagecache_ignore_dirty),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
#ifdef CONFIG_HUGETLB_PAGE
|
||||
{
|
||||
.procname = "nr_hugepages",
|
||||
|
|
@ -2355,6 +2386,48 @@ static int do_proc_dointvec(struct ctl_table *table, int write,
|
|||
buffer, lenp, ppos, conv, data);
|
||||
}
|
||||
|
||||
int setup_pagecache_limit(void)
|
||||
{
|
||||
/* reclaim $ADDITIONAL_RECLAIM_PAGES more than limit. */
|
||||
vm_pagecache_limit_reclaim_ratio = vm_pagecache_limit_ratio + ADDITIONAL_RECLAIM_RATIO;
|
||||
|
||||
if (vm_pagecache_limit_reclaim_ratio > 100)
|
||||
vm_pagecache_limit_reclaim_ratio = 100;
|
||||
if (vm_pagecache_limit_ratio == 0)
|
||||
vm_pagecache_limit_reclaim_ratio = 0;
|
||||
|
||||
vm_pagecache_limit_pages = vm_pagecache_limit_ratio * totalram_pages / 100;
|
||||
vm_pagecache_limit_reclaim_pages = vm_pagecache_limit_reclaim_ratio * totalram_pages / 100;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pc_limit_proc_dointvec(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
|
||||
if (write && !ret)
|
||||
ret = setup_pagecache_limit();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pc_reclaim_limit_proc_dointvec(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
if (write && vm_pagecache_limit_ratio == 0)
|
||||
return -EINVAL;
|
||||
|
||||
int pre_reclaim_ratio = vm_pagecache_limit_reclaim_ratio;
|
||||
int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
|
||||
if (write && !ret) {
|
||||
if (vm_pagecache_limit_reclaim_ratio - vm_pagecache_limit_ratio < ADDITIONAL_RECLAIM_RATIO) {
|
||||
vm_pagecache_limit_reclaim_ratio = pre_reclaim_ratio;
|
||||
return -EINVAL;
|
||||
}
|
||||
vm_pagecache_limit_reclaim_pages = vm_pagecache_limit_reclaim_ratio * totalram_pages / 100;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_proc_douintvec_w(unsigned int *tbl_data,
|
||||
struct ctl_table *table,
|
||||
void __user *buffer,
|
||||
|
|
|
|||
23
mm/filemap.c
23
mm/filemap.c
|
|
@ -797,12 +797,35 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping,
|
|||
}
|
||||
EXPORT_SYMBOL(add_to_page_cache_locked);
|
||||
|
||||
/*
|
||||
* Like add_to_page_cache_locked, but used to add newly allocated pages:
|
||||
* the page is new, so we can just run __set_page_locked() against it.
|
||||
*/
|
||||
int add_to_page_cache(struct page *page,
|
||||
struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (unlikely(vm_pagecache_limit_pages) && pagecache_over_limit() > 0)
|
||||
shrink_page_cache(gfp_mask, page);
|
||||
|
||||
__SetPageLocked(page);
|
||||
error = add_to_page_cache_locked(page, mapping, offset, gfp_mask);
|
||||
if (unlikely(error))
|
||||
__ClearPageLocked(page);
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(add_to_page_cache);
|
||||
|
||||
int add_to_page_cache_lru(struct page *page, struct address_space *mapping,
|
||||
pgoff_t offset, gfp_t gfp_mask)
|
||||
{
|
||||
void *shadow = NULL;
|
||||
int ret;
|
||||
|
||||
if (unlikely(vm_pagecache_limit_pages) && pagecache_over_limit() > 0)
|
||||
shrink_page_cache(gfp_mask, page);
|
||||
|
||||
__SetPageLocked(page);
|
||||
ret = __add_to_page_cache_locked(page, mapping, offset,
|
||||
gfp_mask, &shadow);
|
||||
|
|
|
|||
|
|
@ -7692,6 +7692,49 @@ void zone_pcp_reset(struct zone *zone)
|
|||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/* Returns a number that's positive if the pagecache is above
|
||||
* the set limit*/
|
||||
unsigned long pagecache_over_limit()
|
||||
{
|
||||
unsigned long should_reclaim_pages = 0;
|
||||
unsigned long overlimit_pages = 0;
|
||||
unsigned long delta_pages = 0;
|
||||
/* We only want to limit unmapped and non-shmem page cache pages;
|
||||
* normally all shmem pages are mapped as well*/
|
||||
unsigned long pgcache_pages = global_node_page_state(NR_FILE_PAGES)
|
||||
- max_t(unsigned long,
|
||||
global_node_page_state(NR_FILE_MAPPED),
|
||||
global_node_page_state(NR_SHMEM));
|
||||
/* We certainly can't free more than what's on the LRU lists
|
||||
* minus the dirty ones*/
|
||||
unsigned long pgcache_lru_pages = global_node_page_state(NR_ACTIVE_FILE)
|
||||
+ global_node_page_state(NR_INACTIVE_FILE);
|
||||
|
||||
if (vm_pagecache_ignore_dirty != 0)
|
||||
pgcache_lru_pages -= global_node_page_state(NR_FILE_DIRTY)
|
||||
/vm_pagecache_ignore_dirty;
|
||||
/* Paranoia */
|
||||
if (unlikely(pgcache_lru_pages > LONG_MAX))
|
||||
return 0;
|
||||
|
||||
/* Limit it to 94% of LRU (not all there might be unmapped) */
|
||||
pgcache_lru_pages -= pgcache_lru_pages/16;
|
||||
pgcache_pages = min_t(unsigned long, pgcache_pages, pgcache_lru_pages);
|
||||
|
||||
/*
|
||||
*delta_pages: we should reclaim at least 2% more pages than overlimit_page, values get from
|
||||
* /proc/vm/pagecache_limit_reclaim_pages
|
||||
*should_reclaim_pages: the real pages we will reclaim, but it should less than pgcache_pages;
|
||||
*/
|
||||
if (pgcache_pages > vm_pagecache_limit_pages) {
|
||||
overlimit_pages = pgcache_pages - vm_pagecache_limit_pages;
|
||||
delta_pages = vm_pagecache_limit_reclaim_pages - vm_pagecache_limit_pages;
|
||||
should_reclaim_pages = min_t(unsigned long, delta_pages, vm_pagecache_limit_pages) + overlimit_pages;
|
||||
return should_reclaim_pages;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MEMORY_HOTREMOVE
|
||||
/*
|
||||
* All pages in the range must be in a single zone and isolated
|
||||
|
|
|
|||
16
mm/shmem.c
16
mm/shmem.c
|
|
@ -1227,6 +1227,17 @@ int shmem_unuse(swp_entry_t swap, struct page *page)
|
|||
false);
|
||||
if (error)
|
||||
goto out;
|
||||
/*
|
||||
* try to shrink the page cache proactively even though
|
||||
* we might already have the page in so the shrinking is
|
||||
* not necessary but this is much easier than dropping
|
||||
* the lock in shmem_unuse_inode before add_to_page_cache_lru.
|
||||
* GFP_NOWAIT makes sure that we do not shrink when adding
|
||||
* to page cache
|
||||
*/
|
||||
if (unlikely(vm_pagecache_limit_pages) && pagecache_over_limit() > 0)
|
||||
shrink_page_cache(GFP_KERNEL, NULL);
|
||||
|
||||
/* No radix_tree_preload: swap entry keeps a place for page in tree */
|
||||
error = -EAGAIN;
|
||||
|
||||
|
|
@ -1248,8 +1259,11 @@ int shmem_unuse(swp_entry_t swap, struct page *page)
|
|||
if (error != -ENOMEM)
|
||||
error = 0;
|
||||
mem_cgroup_cancel_charge(page, memcg, false);
|
||||
} else
|
||||
} else {
|
||||
mem_cgroup_commit_charge(page, memcg, true, false);
|
||||
if (unlikely(vm_pagecache_limit_pages) && pagecache_over_limit() > 0)
|
||||
shrink_page_cache(GFP_KERNEL, page);
|
||||
}
|
||||
out:
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
|
|
|
|||
319
mm/vmscan.c
319
mm/vmscan.c
|
|
@ -150,6 +150,11 @@ struct scan_control {
|
|||
* From 0 .. 100. Higher means more swappy.
|
||||
*/
|
||||
int vm_swappiness = 60;
|
||||
unsigned long vm_pagecache_limit_pages __read_mostly = 0;
|
||||
unsigned long vm_pagecache_limit_reclaim_pages = 0;
|
||||
int vm_pagecache_limit_ratio __read_mostly = 0;
|
||||
int vm_pagecache_limit_reclaim_ratio __read_mostly = 0;
|
||||
unsigned int vm_pagecache_ignore_dirty __read_mostly = 1;
|
||||
/*
|
||||
* The total number of pages which are beyond the high watermark within all
|
||||
* zones.
|
||||
|
|
@ -3310,6 +3315,8 @@ static bool kswapd_shrink_node(pg_data_t *pgdat,
|
|||
return sc->nr_scanned >= sc->nr_to_reclaim;
|
||||
}
|
||||
|
||||
static void __shrink_page_cache(gfp_t mask);
|
||||
|
||||
/*
|
||||
* For kswapd, balance_pgdat() will reclaim pages across a node from zones
|
||||
* that are eligible for use by the caller until at least one zone is
|
||||
|
|
@ -3339,6 +3346,10 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int classzone_idx)
|
|||
};
|
||||
count_vm_event(PAGEOUTRUN);
|
||||
|
||||
/* this reclaims from all zones so don't count to sc.nr_reclaimed */
|
||||
if (unlikely(vm_pagecache_limit_pages) && pagecache_over_limit() > 0)
|
||||
__shrink_page_cache(GFP_KERNEL);
|
||||
|
||||
do {
|
||||
unsigned long nr_reclaimed = sc.nr_reclaimed;
|
||||
bool raise_priority = true;
|
||||
|
|
@ -3505,6 +3516,12 @@ static void kswapd_try_to_sleep(pg_data_t *pgdat, int alloc_order, int reclaim_o
|
|||
prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);
|
||||
}
|
||||
|
||||
/* We do not need to loop_again if we have not achieved our
|
||||
* pagecache target (i.e. && pagecache_over_limit(0) > 0) because
|
||||
* the limit will be checked next time a page is added to the page
|
||||
* cache. This might cause a short stall but we should rather not
|
||||
* keep kswapd awake.
|
||||
*/
|
||||
/*
|
||||
* After a short sleep, check if it was a premature sleep. If not, then
|
||||
* go fully to sleep until explicitly woken up.
|
||||
|
|
@ -3662,6 +3679,27 @@ void wakeup_kswapd(struct zone *zone, int order, enum zone_type classzone_idx)
|
|||
wake_up_interruptible(&pgdat->kswapd_wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* * The reclaimable count would be mostly accurate.
|
||||
* * The less reclaimable pages may be
|
||||
* * - mlocked pages, which will be moved to unevictable list when encountered
|
||||
* * - mapped pages, which may require several travels to be reclaimed
|
||||
* * - dirty pages, which is not "instantly" reclaimable
|
||||
* * */
|
||||
|
||||
static unsigned long global_reclaimable_pages(void)
|
||||
{
|
||||
int nr;
|
||||
|
||||
nr = global_node_page_state(NR_ACTIVE_FILE) +
|
||||
global_node_page_state(NR_INACTIVE_FILE);
|
||||
|
||||
if (get_nr_swap_pages() > 0)
|
||||
nr += global_node_page_state(NR_ACTIVE_ANON) +
|
||||
global_node_page_state(NR_INACTIVE_ANON);
|
||||
return nr;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HIBERNATION
|
||||
/*
|
||||
* Try to free `nr_to_reclaim' of memory, system-wide, and return the number of
|
||||
|
|
@ -3704,6 +3742,287 @@ unsigned long shrink_all_memory(unsigned long nr_to_reclaim)
|
|||
}
|
||||
#endif /* CONFIG_HIBERNATION */
|
||||
|
||||
/*
|
||||
* Returns non-zero if the lock has been acquired, false if somebody
|
||||
* else is holding the lock.
|
||||
*/
|
||||
static int pagecache_reclaim_lock_zone(struct zone *zone)
|
||||
{
|
||||
return atomic_add_unless(&zone->pagecache_reclaim, 1, 1);
|
||||
}
|
||||
|
||||
static void pagecache_reclaim_unlock_zone(struct zone *zone)
|
||||
{
|
||||
BUG_ON(atomic_dec_return(&zone->pagecache_reclaim));
|
||||
}
|
||||
|
||||
/*
|
||||
* Potential page cache reclaimers who are not able to take
|
||||
* reclaim lock on any zone are sleeping on this waitqueue.
|
||||
* So this is basically a congestion wait queue for them.
|
||||
*/
|
||||
DECLARE_WAIT_QUEUE_HEAD(pagecache_reclaim_wq);
|
||||
|
||||
/*
|
||||
* Similar to shrink_zone but it has a different consumer - pagecache limit
|
||||
* so we cannot reuse the original function - and we do not want to clobber
|
||||
* that code path so we have to live with this code duplication.
|
||||
*
|
||||
* In short this simply scans through the given lru for all cgroups for the
|
||||
* give zone.
|
||||
*
|
||||
* returns true if we managed to cumulatively reclaim (via nr_reclaimed)
|
||||
* the given nr_to_reclaim pages, false otherwise. The caller knows that
|
||||
* it doesn't have to touch other zones if the target was hit already.
|
||||
*
|
||||
* DO NOT USE OUTSIDE of shrink_all_zones unless you have a really really
|
||||
* really good reason.
|
||||
*/
|
||||
static bool shrink_zone_per_memcg(struct zone *zone, enum lru_list lru,
|
||||
unsigned long nr_to_scan, unsigned long nr_to_reclaim,
|
||||
unsigned long *nr_reclaimed, struct scan_control *sc)
|
||||
{
|
||||
struct mem_cgroup *root = sc->target_mem_cgroup;
|
||||
struct mem_cgroup *memcg;
|
||||
struct mem_cgroup_reclaim_cookie reclaim = {
|
||||
.pgdat = zone->zone_pgdat,
|
||||
.priority = sc->priority,
|
||||
};
|
||||
|
||||
memcg = mem_cgroup_iter(root, NULL, &reclaim);
|
||||
do {
|
||||
struct lruvec *lruvec;
|
||||
|
||||
lruvec = mem_cgroup_lruvec(zone->zone_pgdat, memcg);
|
||||
*nr_reclaimed += shrink_list(lru, nr_to_scan, lruvec, memcg, sc);
|
||||
if (*nr_reclaimed >= nr_to_reclaim) {
|
||||
mem_cgroup_iter_break(root, memcg);
|
||||
return true;
|
||||
}
|
||||
memcg = mem_cgroup_iter(root, memcg, &reclaim);
|
||||
} while (memcg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tries to reclaim 'nr_pages' pages from LRU lists system-wide, for given
|
||||
* pass.
|
||||
*
|
||||
* For pass > 3 we also try to shrink the LRU lists that contain a few pages
|
||||
*
|
||||
* Returns the number of scanned zones.
|
||||
*/
|
||||
static int shrink_all_zones(unsigned long nr_pages, int pass,
|
||||
struct scan_control *sc)
|
||||
{
|
||||
struct zone *zone;
|
||||
unsigned long nr_reclaimed = 0;
|
||||
unsigned int nr_locked_zones = 0;
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
prepare_to_wait(&pagecache_reclaim_wq, &wait, TASK_INTERRUPTIBLE);
|
||||
|
||||
for_each_populated_zone(zone) {
|
||||
enum lru_list lru;
|
||||
|
||||
/*
|
||||
* Back off if somebody is already reclaiming this zone
|
||||
* for the pagecache reclaim.
|
||||
*/
|
||||
if (!pagecache_reclaim_lock_zone(zone))
|
||||
continue;
|
||||
|
||||
|
||||
/*
|
||||
* This reclaimer might scan a zone so it will never
|
||||
* sleep on pagecache_reclaim_wq
|
||||
*/
|
||||
finish_wait(&pagecache_reclaim_wq, &wait);
|
||||
nr_locked_zones++;
|
||||
|
||||
for_each_evictable_lru(lru) {
|
||||
enum zone_stat_item ls = NR_ZONE_LRU_BASE + lru;
|
||||
unsigned long lru_pages = zone_page_state(zone, ls);
|
||||
|
||||
/* For pass = 0, we don't shrink the active list */
|
||||
if (pass == 0 && (lru == LRU_ACTIVE_ANON ||
|
||||
lru == LRU_ACTIVE_FILE))
|
||||
continue;
|
||||
|
||||
/* Original code relied on nr_saved_scan which is no
|
||||
* longer present so we are just considering LRU pages.
|
||||
* This means that the zone has to have quite large
|
||||
* LRU list for default priority and minimum nr_pages
|
||||
* size (8*SWAP_CLUSTER_MAX). In the end we will tend
|
||||
* to reclaim more from large zones wrt. small.
|
||||
* This should be OK because shrink_page_cache is called
|
||||
* when we are getting to short memory condition so
|
||||
* LRUs tend to be large.
|
||||
*/
|
||||
if (((lru_pages >> sc->priority) + 1) >= nr_pages || pass >= 3) {
|
||||
unsigned long nr_to_scan;
|
||||
|
||||
nr_to_scan = min(nr_pages, lru_pages);
|
||||
|
||||
/*
|
||||
* A bit of a hack but the code has always been
|
||||
* updating sc->nr_reclaimed once per shrink_all_zones
|
||||
* rather than accumulating it for all calls to shrink
|
||||
* lru. This costs us an additional argument to
|
||||
* shrink_zone_per_memcg but well...
|
||||
*
|
||||
* Let's stick with this for bug-to-bug compatibility
|
||||
*/
|
||||
while (nr_to_scan > 0) {
|
||||
/* shrink_list takes lru_lock with IRQ off so we
|
||||
* should be careful about really huge nr_to_scan
|
||||
*/
|
||||
unsigned long batch = min_t(unsigned long, nr_to_scan, SWAP_CLUSTER_MAX);
|
||||
|
||||
if (shrink_zone_per_memcg(zone, lru,
|
||||
batch, nr_pages, &nr_reclaimed, sc)) {
|
||||
pagecache_reclaim_unlock_zone(zone);
|
||||
goto out_wakeup;
|
||||
}
|
||||
nr_to_scan -= batch;
|
||||
}
|
||||
}
|
||||
}
|
||||
pagecache_reclaim_unlock_zone(zone);
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to go to sleep because all the zones are already reclaimed.
|
||||
* One of the reclaimer will wake us up or __shrink_page_cache will
|
||||
* do it if there is nothing to be done.
|
||||
*/
|
||||
if (!nr_locked_zones) {
|
||||
schedule();
|
||||
finish_wait(&pagecache_reclaim_wq, &wait);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out_wakeup:
|
||||
wake_up_interruptible(&pagecache_reclaim_wq);
|
||||
sc->nr_reclaimed += nr_reclaimed;
|
||||
out:
|
||||
return nr_locked_zones;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to shrink the page cache
|
||||
*
|
||||
* This function calculates the number of pages (nr_pages) the page
|
||||
* cache is over its limit and shrinks the page cache accordingly.
|
||||
*
|
||||
* The maximum number of pages, the page cache shrinks in one call of
|
||||
* this function is limited to SWAP_CLUSTER_MAX pages. Therefore it may
|
||||
* require a number of calls to actually reach the vm_pagecache_limit_kb.
|
||||
*
|
||||
* This function is similar to shrink_all_memory, except that it may never
|
||||
* swap out mapped pages and only does four passes.
|
||||
*/
|
||||
static void __shrink_page_cache(gfp_t mask)
|
||||
{
|
||||
unsigned long ret = 0;
|
||||
int pass = 0;
|
||||
struct reclaim_state reclaim_state;
|
||||
struct scan_control sc = {
|
||||
.gfp_mask = mask,
|
||||
.may_swap = 0,
|
||||
.may_unmap = 0,
|
||||
.may_writepage = 0,
|
||||
.target_mem_cgroup = NULL,
|
||||
.reclaim_idx = MAX_NR_ZONES,
|
||||
};
|
||||
struct reclaim_state *old_rs = current->reclaim_state;
|
||||
long nr_pages;
|
||||
|
||||
/* We might sleep during direct reclaim so make atomic context
|
||||
* is certainly a bug.
|
||||
*/
|
||||
BUG_ON(!(mask & __GFP_RECLAIM));
|
||||
|
||||
retry:
|
||||
/* How many pages are we over the limit?*/
|
||||
nr_pages = pagecache_over_limit();
|
||||
|
||||
/*
|
||||
* Return early if there's no work to do.
|
||||
* Wake up reclaimers that couldn't scan any zone due to congestion.
|
||||
* There is apparently nothing to do so they do not have to sleep.
|
||||
* This makes sure that no sleeping reclaimer will stay behind.
|
||||
* Allow breaching the limit if the task is on the way out.
|
||||
*/
|
||||
if (nr_pages <= 0 || fatal_signal_pending(current)) {
|
||||
wake_up_interruptible(&pagecache_reclaim_wq);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* But do a few at least */
|
||||
nr_pages = max_t(unsigned long, nr_pages, 8*SWAP_CLUSTER_MAX);
|
||||
|
||||
current->reclaim_state = &reclaim_state;
|
||||
|
||||
/*
|
||||
* Shrink the LRU in 4 passes:
|
||||
* 0 = Reclaim from inactive_list only (fast)
|
||||
* 1 = Reclaim from active list but don't reclaim mapped and dirtied (not that fast)
|
||||
* 2 = Reclaim from active list but don't reclaim mapped (2nd pass)
|
||||
* it may reclaim dirtied if vm_pagecache_ignore_dirty = 0
|
||||
* 3 = same as pass 2, but it will reclaim some few pages , detail in shrink_all_zones
|
||||
*/
|
||||
for (; pass <= 3; pass++) {
|
||||
for (sc.priority = DEF_PRIORITY; sc.priority >= 0; sc.priority--) {
|
||||
unsigned long nr_to_scan = nr_pages - ret;
|
||||
struct mem_cgroup *memcg = NULL;
|
||||
int nid;
|
||||
|
||||
sc.nr_scanned = 0;
|
||||
|
||||
/*
|
||||
* No zone reclaimed because of too many reclaimers. Retry whether
|
||||
* there is still something to do
|
||||
*/
|
||||
if (!shrink_all_zones(nr_to_scan, pass, &sc))
|
||||
goto retry;
|
||||
|
||||
ret += sc.nr_reclaimed;
|
||||
if (ret >= nr_pages)
|
||||
goto out;
|
||||
|
||||
reclaim_state.reclaimed_slab = 0;
|
||||
for_each_online_node(nid) {
|
||||
do {
|
||||
shrink_slab(mask, nid, memcg, sc.nr_scanned,
|
||||
global_reclaimable_pages());
|
||||
} while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)) != NULL);
|
||||
}
|
||||
ret += reclaim_state.reclaimed_slab;
|
||||
|
||||
if (ret >= nr_pages)
|
||||
goto out;
|
||||
|
||||
}
|
||||
if (pass == 1) {
|
||||
if (vm_pagecache_ignore_dirty == 1 ||
|
||||
(mask & (__GFP_IO | __GFP_FS)) != (__GFP_IO | __GFP_FS) )
|
||||
break;
|
||||
else
|
||||
sc.may_writepage = 1;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
current->reclaim_state = old_rs;
|
||||
}
|
||||
|
||||
void shrink_page_cache(gfp_t mask, struct page *page)
|
||||
{
|
||||
__shrink_page_cache(mask);
|
||||
}
|
||||
|
||||
/* It's optimal to keep kswapds on the same CPUs as their memory, but
|
||||
not required for correctness. So if the last cpu in a node goes
|
||||
away, we get changed to run anywhere: as the first one comes back,
|
||||
|
|
|
|||
Loading…
Reference in New Issue