Commit adea02a1 authored by Wu Fengguang's avatar Wu Fengguang Committed by Linus Torvalds

mm: count only reclaimable lru pages

global_lru_pages() / zone_lru_pages() can be used in two ways:
- to estimate max reclaimable pages in determine_dirtyable_memory()
- to calculate the slab scan ratio

When swap is full or not present, the anon lru lists are not reclaimable
and also won't be scanned.  So the anon pages shall not be counted in both
usage scenarios.  Also rename to _reclaimable_pages: now they are counting
the possibly reclaimable lru pages.

It can greatly (and correctly) increase the slab scan rate under high
memory pressure (when most file pages have been reclaimed and swap is
full/absent), thus reduce false OOM kills.
Acked-by: default avatarPeter Zijlstra <a.p.zijlstra@chello.nl>
Reviewed-by: default avatarRik van Riel <riel@redhat.com>
Reviewed-by: default avatarChristoph Lameter <cl@linux-foundation.org>
Reviewed-by: default avatarMinchan Kim <minchan.kim@gmail.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: default avatarWu Fengguang <fengguang.wu@intel.com>
Acked-by: default avatarJohannes Weiner <hannes@cmpxchg.org>
Reviewed-by: default avatarMinchan Kim <minchan.kim@gmail.com>
Reviewed-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
Cc: David Howells <dhowells@redhat.com>
Cc: "Li, Ming Chun" <macli@brc.ubc.ca>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 55c37a84
...@@ -166,15 +166,8 @@ static inline unsigned long zone_page_state(struct zone *zone, ...@@ -166,15 +166,8 @@ static inline unsigned long zone_page_state(struct zone *zone,
return x; return x;
} }
extern unsigned long global_lru_pages(void); extern unsigned long global_reclaimable_pages(void);
extern unsigned long zone_reclaimable_pages(struct zone *zone);
static inline unsigned long zone_lru_pages(struct zone *zone)
{
return (zone_page_state(zone, NR_ACTIVE_ANON)
+ zone_page_state(zone, NR_ACTIVE_FILE)
+ zone_page_state(zone, NR_INACTIVE_ANON)
+ zone_page_state(zone, NR_INACTIVE_FILE));
}
#ifdef CONFIG_NUMA #ifdef CONFIG_NUMA
/* /*
......
...@@ -380,7 +380,8 @@ static unsigned long highmem_dirtyable_memory(unsigned long total) ...@@ -380,7 +380,8 @@ static unsigned long highmem_dirtyable_memory(unsigned long total)
struct zone *z = struct zone *z =
&NODE_DATA(node)->node_zones[ZONE_HIGHMEM]; &NODE_DATA(node)->node_zones[ZONE_HIGHMEM];
x += zone_page_state(z, NR_FREE_PAGES) + zone_lru_pages(z); x += zone_page_state(z, NR_FREE_PAGES) +
zone_reclaimable_pages(z);
} }
/* /*
* Make sure that the number of highmem pages is never larger * Make sure that the number of highmem pages is never larger
...@@ -404,7 +405,7 @@ unsigned long determine_dirtyable_memory(void) ...@@ -404,7 +405,7 @@ unsigned long determine_dirtyable_memory(void)
{ {
unsigned long x; unsigned long x;
x = global_page_state(NR_FREE_PAGES) + global_lru_pages(); x = global_page_state(NR_FREE_PAGES) + global_reclaimable_pages();
if (!vm_highmem_is_dirtyable) if (!vm_highmem_is_dirtyable)
x -= highmem_dirtyable_memory(x); x -= highmem_dirtyable_memory(x);
......
...@@ -1734,7 +1734,7 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist, ...@@ -1734,7 +1734,7 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL)) if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL))
continue; continue;
lru_pages += zone_lru_pages(zone); lru_pages += zone_reclaimable_pages(zone);
} }
} }
...@@ -1951,7 +1951,7 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order) ...@@ -1951,7 +1951,7 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order)
for (i = 0; i <= end_zone; i++) { for (i = 0; i <= end_zone; i++) {
struct zone *zone = pgdat->node_zones + i; struct zone *zone = pgdat->node_zones + i;
lru_pages += zone_lru_pages(zone); lru_pages += zone_reclaimable_pages(zone);
} }
/* /*
...@@ -1995,7 +1995,7 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order) ...@@ -1995,7 +1995,7 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order)
if (zone_is_all_unreclaimable(zone)) if (zone_is_all_unreclaimable(zone))
continue; continue;
if (nr_slab == 0 && zone->pages_scanned >= if (nr_slab == 0 && zone->pages_scanned >=
(zone_lru_pages(zone) * 6)) (zone_reclaimable_pages(zone) * 6))
zone_set_flag(zone, zone_set_flag(zone,
ZONE_ALL_UNRECLAIMABLE); ZONE_ALL_UNRECLAIMABLE);
/* /*
...@@ -2162,12 +2162,39 @@ void wakeup_kswapd(struct zone *zone, int order) ...@@ -2162,12 +2162,39 @@ void wakeup_kswapd(struct zone *zone, int order)
wake_up_interruptible(&pgdat->kswapd_wait); wake_up_interruptible(&pgdat->kswapd_wait);
} }
unsigned long global_lru_pages(void) /*
* 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
*/
unsigned long global_reclaimable_pages(void)
{ {
return global_page_state(NR_ACTIVE_ANON) int nr;
+ global_page_state(NR_ACTIVE_FILE)
+ global_page_state(NR_INACTIVE_ANON) nr = global_page_state(NR_ACTIVE_FILE) +
+ global_page_state(NR_INACTIVE_FILE); global_page_state(NR_INACTIVE_FILE);
if (nr_swap_pages > 0)
nr += global_page_state(NR_ACTIVE_ANON) +
global_page_state(NR_INACTIVE_ANON);
return nr;
}
unsigned long zone_reclaimable_pages(struct zone *zone)
{
int nr;
nr = zone_page_state(zone, NR_ACTIVE_FILE) +
zone_page_state(zone, NR_INACTIVE_FILE);
if (nr_swap_pages > 0)
nr += zone_page_state(zone, NR_ACTIVE_ANON) +
zone_page_state(zone, NR_INACTIVE_ANON);
return nr;
} }
#ifdef CONFIG_HIBERNATION #ifdef CONFIG_HIBERNATION
...@@ -2239,7 +2266,7 @@ unsigned long shrink_all_memory(unsigned long nr_pages) ...@@ -2239,7 +2266,7 @@ unsigned long shrink_all_memory(unsigned long nr_pages)
current->reclaim_state = &reclaim_state; current->reclaim_state = &reclaim_state;
lru_pages = global_lru_pages(); lru_pages = global_reclaimable_pages();
nr_slab = global_page_state(NR_SLAB_RECLAIMABLE); nr_slab = global_page_state(NR_SLAB_RECLAIMABLE);
/* If slab caches are huge, it's better to hit them first */ /* If slab caches are huge, it's better to hit them first */
while (nr_slab >= lru_pages) { while (nr_slab >= lru_pages) {
...@@ -2281,7 +2308,7 @@ unsigned long shrink_all_memory(unsigned long nr_pages) ...@@ -2281,7 +2308,7 @@ unsigned long shrink_all_memory(unsigned long nr_pages)
reclaim_state.reclaimed_slab = 0; reclaim_state.reclaimed_slab = 0;
shrink_slab(sc.nr_scanned, sc.gfp_mask, shrink_slab(sc.nr_scanned, sc.gfp_mask,
global_lru_pages()); global_reclaimable_pages());
sc.nr_reclaimed += reclaim_state.reclaimed_slab; sc.nr_reclaimed += reclaim_state.reclaimed_slab;
if (sc.nr_reclaimed >= nr_pages) if (sc.nr_reclaimed >= nr_pages)
goto out; goto out;
...@@ -2298,7 +2325,8 @@ unsigned long shrink_all_memory(unsigned long nr_pages) ...@@ -2298,7 +2325,8 @@ unsigned long shrink_all_memory(unsigned long nr_pages)
if (!sc.nr_reclaimed) { if (!sc.nr_reclaimed) {
do { do {
reclaim_state.reclaimed_slab = 0; reclaim_state.reclaimed_slab = 0;
shrink_slab(nr_pages, sc.gfp_mask, global_lru_pages()); shrink_slab(nr_pages, sc.gfp_mask,
global_reclaimable_pages());
sc.nr_reclaimed += reclaim_state.reclaimed_slab; sc.nr_reclaimed += reclaim_state.reclaimed_slab;
} while (sc.nr_reclaimed < nr_pages && } while (sc.nr_reclaimed < nr_pages &&
reclaim_state.reclaimed_slab > 0); reclaim_state.reclaimed_slab > 0);
......
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