diff options
author | Suleiman Souhlal <ssouhlal@FreeBSD.org> | 2012-12-18 17:21:41 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-18 18:02:12 -0500 |
commit | 4c9c535968cacf507c5febe49e6ec5123eadf207 (patch) | |
tree | 726189ffffb689f1fcef87a8fb1d60a2632f88ae /mm/memcontrol.c | |
parent | a0956d54492eb7257b09230680a8812b42cdee92 (diff) |
memcg: reclaim when more than one page needed
mem_cgroup_do_charge() was written before kmem accounting, and expects
three cases: being called for 1 page, being called for a stock of 32
pages, or being called for a hugepage. If we call for 2 or 3 pages (and
both the stack and several slabs used in process creation are such, at
least with the debug options I had), it assumed it's being called for
stock and just retried without reclaiming.
Fix that by passing down a minsize argument in addition to the csize.
And what to do about that (csize == PAGE_SIZE && ret) retry? If it's
needed at all (and presumably is since it's there, perhaps to handle
races), then it should be extended to more than PAGE_SIZE, yet how far?
And should there be a retry count limit, of what? For now retry up to
COSTLY_ORDER (as page_alloc.c does) and make sure not to do it if
__GFP_NORETRY.
v4: fixed nr pages calculation pointed out by Christoph Lameter.
Signed-off-by: Suleiman Souhlal <suleiman@google.com>
Signed-off-by: Glauber Costa <glommer@parallels.com>
Acked-by: Kamezawa Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Michal Hocko <mhocko@suse.cz>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: David Rientjes <rientjes@google.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: Frederic Weisbecker <fweisbec@redhat.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: JoonSoo Kim <js1304@gmail.com>
Cc: Mel Gorman <mel@csn.ul.ie>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
Cc: Rik van Riel <riel@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/memcontrol.c')
-rw-r--r-- | mm/memcontrol.c | 16 |
1 files changed, 9 insertions, 7 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 92887b286246..20e3619870ae 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -2258,7 +2258,8 @@ enum { | |||
2258 | }; | 2258 | }; |
2259 | 2259 | ||
2260 | static int mem_cgroup_do_charge(struct mem_cgroup *memcg, gfp_t gfp_mask, | 2260 | static int mem_cgroup_do_charge(struct mem_cgroup *memcg, gfp_t gfp_mask, |
2261 | unsigned int nr_pages, bool oom_check) | 2261 | unsigned int nr_pages, unsigned int min_pages, |
2262 | bool oom_check) | ||
2262 | { | 2263 | { |
2263 | unsigned long csize = nr_pages * PAGE_SIZE; | 2264 | unsigned long csize = nr_pages * PAGE_SIZE; |
2264 | struct mem_cgroup *mem_over_limit; | 2265 | struct mem_cgroup *mem_over_limit; |
@@ -2281,18 +2282,18 @@ static int mem_cgroup_do_charge(struct mem_cgroup *memcg, gfp_t gfp_mask, | |||
2281 | } else | 2282 | } else |
2282 | mem_over_limit = mem_cgroup_from_res_counter(fail_res, res); | 2283 | mem_over_limit = mem_cgroup_from_res_counter(fail_res, res); |
2283 | /* | 2284 | /* |
2284 | * nr_pages can be either a huge page (HPAGE_PMD_NR), a batch | ||
2285 | * of regular pages (CHARGE_BATCH), or a single regular page (1). | ||
2286 | * | ||
2287 | * Never reclaim on behalf of optional batching, retry with a | 2285 | * Never reclaim on behalf of optional batching, retry with a |
2288 | * single page instead. | 2286 | * single page instead. |
2289 | */ | 2287 | */ |
2290 | if (nr_pages == CHARGE_BATCH) | 2288 | if (nr_pages > min_pages) |
2291 | return CHARGE_RETRY; | 2289 | return CHARGE_RETRY; |
2292 | 2290 | ||
2293 | if (!(gfp_mask & __GFP_WAIT)) | 2291 | if (!(gfp_mask & __GFP_WAIT)) |
2294 | return CHARGE_WOULDBLOCK; | 2292 | return CHARGE_WOULDBLOCK; |
2295 | 2293 | ||
2294 | if (gfp_mask & __GFP_NORETRY) | ||
2295 | return CHARGE_NOMEM; | ||
2296 | |||
2296 | ret = mem_cgroup_reclaim(mem_over_limit, gfp_mask, flags); | 2297 | ret = mem_cgroup_reclaim(mem_over_limit, gfp_mask, flags); |
2297 | if (mem_cgroup_margin(mem_over_limit) >= nr_pages) | 2298 | if (mem_cgroup_margin(mem_over_limit) >= nr_pages) |
2298 | return CHARGE_RETRY; | 2299 | return CHARGE_RETRY; |
@@ -2305,7 +2306,7 @@ static int mem_cgroup_do_charge(struct mem_cgroup *memcg, gfp_t gfp_mask, | |||
2305 | * unlikely to succeed so close to the limit, and we fall back | 2306 | * unlikely to succeed so close to the limit, and we fall back |
2306 | * to regular pages anyway in case of failure. | 2307 | * to regular pages anyway in case of failure. |
2307 | */ | 2308 | */ |
2308 | if (nr_pages == 1 && ret) | 2309 | if (nr_pages <= (1 << PAGE_ALLOC_COSTLY_ORDER) && ret) |
2309 | return CHARGE_RETRY; | 2310 | return CHARGE_RETRY; |
2310 | 2311 | ||
2311 | /* | 2312 | /* |
@@ -2439,7 +2440,8 @@ again: | |||
2439 | nr_oom_retries = MEM_CGROUP_RECLAIM_RETRIES; | 2440 | nr_oom_retries = MEM_CGROUP_RECLAIM_RETRIES; |
2440 | } | 2441 | } |
2441 | 2442 | ||
2442 | ret = mem_cgroup_do_charge(memcg, gfp_mask, batch, oom_check); | 2443 | ret = mem_cgroup_do_charge(memcg, gfp_mask, batch, nr_pages, |
2444 | oom_check); | ||
2443 | switch (ret) { | 2445 | switch (ret) { |
2444 | case CHARGE_OK: | 2446 | case CHARGE_OK: |
2445 | break; | 2447 | break; |