Commit d69b042f authored by Balbir Singh's avatar Balbir Singh Committed by Linus Torvalds

memcg: add file-based RSS accounting

Add file RSS tracking per memory cgroup

We currently don't track file RSS, the RSS we report is actually anon RSS.
 All the file mapped pages, come in through the page cache and get
accounted there.  This patch adds support for accounting file RSS pages.
It should

1. Help improve the metrics reported by the memory resource controller
2. Will form the basis for a future shared memory accounting heuristic
   that has been proposed by Kamezawa.

Unfortunately, we cannot rename the existing "rss" keyword used in
memory.stat to "anon_rss".  We however, add "mapped_file" data and hope to
educate the end user through documentation.

[hugh.dickins@tiscali.co.uk: fix mem_cgroup_update_mapped_file_stat oops]
Signed-off-by: default avatarBalbir Singh <balbir@linux.vnet.ibm.com>
Acked-by: default avatarKAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.cn>
Cc: Paul Menage <menage@google.com>
Cc: Dhaval Giani <dhaval@linux.vnet.ibm.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: default avatarHugh Dickins <hugh.dickins@tiscali.co.uk>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent cd500819
...@@ -117,7 +117,7 @@ static inline bool mem_cgroup_disabled(void) ...@@ -117,7 +117,7 @@ static inline bool mem_cgroup_disabled(void)
} }
extern bool mem_cgroup_oom_called(struct task_struct *task); extern bool mem_cgroup_oom_called(struct task_struct *task);
void mem_cgroup_update_mapped_file_stat(struct page *page, int val);
#else /* CONFIG_CGROUP_MEM_RES_CTLR */ #else /* CONFIG_CGROUP_MEM_RES_CTLR */
struct mem_cgroup; struct mem_cgroup;
...@@ -271,6 +271,11 @@ mem_cgroup_print_oom_info(struct mem_cgroup *memcg, struct task_struct *p) ...@@ -271,6 +271,11 @@ mem_cgroup_print_oom_info(struct mem_cgroup *memcg, struct task_struct *p)
{ {
} }
static inline void mem_cgroup_update_mapped_file_stat(struct page *page,
int val)
{
}
#endif /* CONFIG_CGROUP_MEM_CONT */ #endif /* CONFIG_CGROUP_MEM_CONT */
#endif /* _LINUX_MEMCONTROL_H */ #endif /* _LINUX_MEMCONTROL_H */
......
...@@ -62,7 +62,8 @@ enum mem_cgroup_stat_index { ...@@ -62,7 +62,8 @@ enum mem_cgroup_stat_index {
* For MEM_CONTAINER_TYPE_ALL, usage = pagecache + rss. * For MEM_CONTAINER_TYPE_ALL, usage = pagecache + rss.
*/ */
MEM_CGROUP_STAT_CACHE, /* # of pages charged as cache */ MEM_CGROUP_STAT_CACHE, /* # of pages charged as cache */
MEM_CGROUP_STAT_RSS, /* # of pages charged as rss */ MEM_CGROUP_STAT_RSS, /* # of pages charged as anon rss */
MEM_CGROUP_STAT_MAPPED_FILE, /* # of pages charged as file rss */
MEM_CGROUP_STAT_PGPGIN_COUNT, /* # of pages paged in */ MEM_CGROUP_STAT_PGPGIN_COUNT, /* # of pages paged in */
MEM_CGROUP_STAT_PGPGOUT_COUNT, /* # of pages paged out */ MEM_CGROUP_STAT_PGPGOUT_COUNT, /* # of pages paged out */
...@@ -900,6 +901,44 @@ static void record_last_oom(struct mem_cgroup *mem) ...@@ -900,6 +901,44 @@ static void record_last_oom(struct mem_cgroup *mem)
mem_cgroup_walk_tree(mem, NULL, record_last_oom_cb); mem_cgroup_walk_tree(mem, NULL, record_last_oom_cb);
} }
/*
* Currently used to update mapped file statistics, but the routine can be
* generalized to update other statistics as well.
*/
void mem_cgroup_update_mapped_file_stat(struct page *page, int val)
{
struct mem_cgroup *mem;
struct mem_cgroup_stat *stat;
struct mem_cgroup_stat_cpu *cpustat;
int cpu;
struct page_cgroup *pc;
if (!page_is_file_cache(page))
return;
pc = lookup_page_cgroup(page);
if (unlikely(!pc))
return;
lock_page_cgroup(pc);
mem = pc->mem_cgroup;
if (!mem)
goto done;
if (!PageCgroupUsed(pc))
goto done;
/*
* Preemption is already disabled, we don't need get_cpu()
*/
cpu = smp_processor_id();
stat = &mem->stat;
cpustat = &stat->cpustat[cpu];
__mem_cgroup_stat_add_safe(cpustat, MEM_CGROUP_STAT_MAPPED_FILE, val);
done:
unlock_page_cgroup(pc);
}
/* /*
* Unlike exported interface, "oom" parameter is added. if oom==true, * Unlike exported interface, "oom" parameter is added. if oom==true,
...@@ -1098,6 +1137,10 @@ static int mem_cgroup_move_account(struct page_cgroup *pc, ...@@ -1098,6 +1137,10 @@ static int mem_cgroup_move_account(struct page_cgroup *pc,
struct mem_cgroup_per_zone *from_mz, *to_mz; struct mem_cgroup_per_zone *from_mz, *to_mz;
int nid, zid; int nid, zid;
int ret = -EBUSY; int ret = -EBUSY;
struct page *page;
int cpu;
struct mem_cgroup_stat *stat;
struct mem_cgroup_stat_cpu *cpustat;
VM_BUG_ON(from == to); VM_BUG_ON(from == to);
VM_BUG_ON(PageLRU(pc->page)); VM_BUG_ON(PageLRU(pc->page));
...@@ -1118,6 +1161,23 @@ static int mem_cgroup_move_account(struct page_cgroup *pc, ...@@ -1118,6 +1161,23 @@ static int mem_cgroup_move_account(struct page_cgroup *pc,
res_counter_uncharge(&from->res, PAGE_SIZE); res_counter_uncharge(&from->res, PAGE_SIZE);
mem_cgroup_charge_statistics(from, pc, false); mem_cgroup_charge_statistics(from, pc, false);
page = pc->page;
if (page_is_file_cache(page) && page_mapped(page)) {
cpu = smp_processor_id();
/* Update mapped_file data for mem_cgroup "from" */
stat = &from->stat;
cpustat = &stat->cpustat[cpu];
__mem_cgroup_stat_add_safe(cpustat, MEM_CGROUP_STAT_MAPPED_FILE,
-1);
/* Update mapped_file data for mem_cgroup "to" */
stat = &to->stat;
cpustat = &stat->cpustat[cpu];
__mem_cgroup_stat_add_safe(cpustat, MEM_CGROUP_STAT_MAPPED_FILE,
1);
}
if (do_swap_account) if (do_swap_account)
res_counter_uncharge(&from->memsw, PAGE_SIZE); res_counter_uncharge(&from->memsw, PAGE_SIZE);
css_put(&from->css); css_put(&from->css);
...@@ -2046,6 +2106,7 @@ static int mem_cgroup_reset(struct cgroup *cont, unsigned int event) ...@@ -2046,6 +2106,7 @@ static int mem_cgroup_reset(struct cgroup *cont, unsigned int event)
enum { enum {
MCS_CACHE, MCS_CACHE,
MCS_RSS, MCS_RSS,
MCS_MAPPED_FILE,
MCS_PGPGIN, MCS_PGPGIN,
MCS_PGPGOUT, MCS_PGPGOUT,
MCS_INACTIVE_ANON, MCS_INACTIVE_ANON,
...@@ -2066,6 +2127,7 @@ struct { ...@@ -2066,6 +2127,7 @@ struct {
} memcg_stat_strings[NR_MCS_STAT] = { } memcg_stat_strings[NR_MCS_STAT] = {
{"cache", "total_cache"}, {"cache", "total_cache"},
{"rss", "total_rss"}, {"rss", "total_rss"},
{"mapped_file", "total_mapped_file"},
{"pgpgin", "total_pgpgin"}, {"pgpgin", "total_pgpgin"},
{"pgpgout", "total_pgpgout"}, {"pgpgout", "total_pgpgout"},
{"inactive_anon", "total_inactive_anon"}, {"inactive_anon", "total_inactive_anon"},
...@@ -2086,6 +2148,8 @@ static int mem_cgroup_get_local_stat(struct mem_cgroup *mem, void *data) ...@@ -2086,6 +2148,8 @@ static int mem_cgroup_get_local_stat(struct mem_cgroup *mem, void *data)
s->stat[MCS_CACHE] += val * PAGE_SIZE; s->stat[MCS_CACHE] += val * PAGE_SIZE;
val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_RSS); val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_RSS);
s->stat[MCS_RSS] += val * PAGE_SIZE; s->stat[MCS_RSS] += val * PAGE_SIZE;
val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_MAPPED_FILE);
s->stat[MCS_MAPPED_FILE] += val * PAGE_SIZE;
val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_PGPGIN_COUNT); val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_PGPGIN_COUNT);
s->stat[MCS_PGPGIN] += val; s->stat[MCS_PGPGIN] += val;
val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_PGPGOUT_COUNT); val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_PGPGOUT_COUNT);
......
...@@ -99,6 +99,8 @@ struct page_cgroup *lookup_page_cgroup(struct page *page) ...@@ -99,6 +99,8 @@ struct page_cgroup *lookup_page_cgroup(struct page *page)
unsigned long pfn = page_to_pfn(page); unsigned long pfn = page_to_pfn(page);
struct mem_section *section = __pfn_to_section(pfn); struct mem_section *section = __pfn_to_section(pfn);
if (!section->page_cgroup)
return NULL;
return section->page_cgroup + pfn; return section->page_cgroup + pfn;
} }
......
...@@ -703,8 +703,10 @@ void page_add_new_anon_rmap(struct page *page, ...@@ -703,8 +703,10 @@ void page_add_new_anon_rmap(struct page *page,
*/ */
void page_add_file_rmap(struct page *page) void page_add_file_rmap(struct page *page)
{ {
if (atomic_inc_and_test(&page->_mapcount)) if (atomic_inc_and_test(&page->_mapcount)) {
__inc_zone_page_state(page, NR_FILE_MAPPED); __inc_zone_page_state(page, NR_FILE_MAPPED);
mem_cgroup_update_mapped_file_stat(page, 1);
}
} }
#ifdef CONFIG_DEBUG_VM #ifdef CONFIG_DEBUG_VM
...@@ -753,6 +755,7 @@ void page_remove_rmap(struct page *page) ...@@ -753,6 +755,7 @@ void page_remove_rmap(struct page *page)
mem_cgroup_uncharge_page(page); mem_cgroup_uncharge_page(page);
__dec_zone_page_state(page, __dec_zone_page_state(page,
PageAnon(page) ? NR_ANON_PAGES : NR_FILE_MAPPED); PageAnon(page) ? NR_ANON_PAGES : NR_FILE_MAPPED);
mem_cgroup_update_mapped_file_stat(page, -1);
/* /*
* It would be tidy to reset the PageAnon mapping here, * It would be tidy to reset the PageAnon mapping here,
* but that might overwrite a racing page_add_anon_rmap * but that might overwrite a racing page_add_anon_rmap
......
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