diff options
author | KAMEZAWA Hiroyuki <kamzawa.hiroyu@jp.fujitsu.com> | 2009-04-02 19:57:35 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-02 22:04:55 -0400 |
commit | 14067bb3e24b96d92e22d19c18c0119edf5575e5 (patch) | |
tree | 54af97e899bf4253b170938f6711097824b7364f /mm/memcontrol.c | |
parent | 04046e1a0a34286382e913f8fc461440c21d88e8 (diff) |
memcg: hierarchical stat
Clean up memory.stat file routine and show "total" hierarchical stat.
This patch does
- renamed get_all_zonestat to be get_local_zonestat.
- remove old mem_cgroup_stat_desc, which is only for per-cpu stat.
- add mcs_stat to cover both of per-cpu/per-lru stat.
- add "total" stat of hierarchy (*)
- add a callback system to scan all memcg under a root.
== "total" is added.
[kamezawa@localhost ~]$ cat /opt/cgroup/xxx/memory.stat
cache 0
rss 0
pgpgin 0
pgpgout 0
inactive_anon 0
active_anon 0
inactive_file 0
active_file 0
unevictable 0
hierarchical_memory_limit 50331648
hierarchical_memsw_limit 9223372036854775807
total_cache 65536
total_rss 192512
total_pgpgin 218
total_pgpgout 155
total_inactive_anon 0
total_active_anon 135168
total_inactive_file 61440
total_active_file 4096
total_unevictable 0
==
(*) maybe the user can do calc hierarchical stat by his own program
in userland but if it can be written in clean way, it's worth to be
shown, I think.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Paul Menage <menage@google.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: David Rientjes <rientjes@google.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 | 160 |
1 files changed, 119 insertions, 41 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 61fd9590c135..33fc0302e29e 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -256,7 +256,7 @@ page_cgroup_zoneinfo(struct page_cgroup *pc) | |||
256 | return mem_cgroup_zoneinfo(mem, nid, zid); | 256 | return mem_cgroup_zoneinfo(mem, nid, zid); |
257 | } | 257 | } |
258 | 258 | ||
259 | static unsigned long mem_cgroup_get_all_zonestat(struct mem_cgroup *mem, | 259 | static unsigned long mem_cgroup_get_local_zonestat(struct mem_cgroup *mem, |
260 | enum lru_list idx) | 260 | enum lru_list idx) |
261 | { | 261 | { |
262 | int nid, zid; | 262 | int nid, zid; |
@@ -317,6 +317,42 @@ static bool mem_cgroup_is_obsolete(struct mem_cgroup *mem) | |||
317 | return css_is_removed(&mem->css); | 317 | return css_is_removed(&mem->css); |
318 | } | 318 | } |
319 | 319 | ||
320 | |||
321 | /* | ||
322 | * Call callback function against all cgroup under hierarchy tree. | ||
323 | */ | ||
324 | static int mem_cgroup_walk_tree(struct mem_cgroup *root, void *data, | ||
325 | int (*func)(struct mem_cgroup *, void *)) | ||
326 | { | ||
327 | int found, ret, nextid; | ||
328 | struct cgroup_subsys_state *css; | ||
329 | struct mem_cgroup *mem; | ||
330 | |||
331 | if (!root->use_hierarchy) | ||
332 | return (*func)(root, data); | ||
333 | |||
334 | nextid = 1; | ||
335 | do { | ||
336 | ret = 0; | ||
337 | mem = NULL; | ||
338 | |||
339 | rcu_read_lock(); | ||
340 | css = css_get_next(&mem_cgroup_subsys, nextid, &root->css, | ||
341 | &found); | ||
342 | if (css && css_tryget(css)) | ||
343 | mem = container_of(css, struct mem_cgroup, css); | ||
344 | rcu_read_unlock(); | ||
345 | |||
346 | if (mem) { | ||
347 | ret = (*func)(mem, data); | ||
348 | css_put(&mem->css); | ||
349 | } | ||
350 | nextid = found + 1; | ||
351 | } while (!ret && css); | ||
352 | |||
353 | return ret; | ||
354 | } | ||
355 | |||
320 | /* | 356 | /* |
321 | * Following LRU functions are allowed to be used without PCG_LOCK. | 357 | * Following LRU functions are allowed to be used without PCG_LOCK. |
322 | * Operations are called by routine of global LRU independently from memcg. | 358 | * Operations are called by routine of global LRU independently from memcg. |
@@ -510,8 +546,8 @@ static int calc_inactive_ratio(struct mem_cgroup *memcg, unsigned long *present_ | |||
510 | unsigned long gb; | 546 | unsigned long gb; |
511 | unsigned long inactive_ratio; | 547 | unsigned long inactive_ratio; |
512 | 548 | ||
513 | inactive = mem_cgroup_get_all_zonestat(memcg, LRU_INACTIVE_ANON); | 549 | inactive = mem_cgroup_get_local_zonestat(memcg, LRU_INACTIVE_ANON); |
514 | active = mem_cgroup_get_all_zonestat(memcg, LRU_ACTIVE_ANON); | 550 | active = mem_cgroup_get_local_zonestat(memcg, LRU_ACTIVE_ANON); |
515 | 551 | ||
516 | gb = (inactive + active) >> (30 - PAGE_SHIFT); | 552 | gb = (inactive + active) >> (30 - PAGE_SHIFT); |
517 | if (gb) | 553 | if (gb) |
@@ -1838,54 +1874,90 @@ static int mem_cgroup_reset(struct cgroup *cont, unsigned int event) | |||
1838 | return 0; | 1874 | return 0; |
1839 | } | 1875 | } |
1840 | 1876 | ||
1841 | static const struct mem_cgroup_stat_desc { | 1877 | |
1842 | const char *msg; | 1878 | /* For read statistics */ |
1843 | u64 unit; | 1879 | enum { |
1844 | } mem_cgroup_stat_desc[] = { | 1880 | MCS_CACHE, |
1845 | [MEM_CGROUP_STAT_CACHE] = { "cache", PAGE_SIZE, }, | 1881 | MCS_RSS, |
1846 | [MEM_CGROUP_STAT_RSS] = { "rss", PAGE_SIZE, }, | 1882 | MCS_PGPGIN, |
1847 | [MEM_CGROUP_STAT_PGPGIN_COUNT] = {"pgpgin", 1, }, | 1883 | MCS_PGPGOUT, |
1848 | [MEM_CGROUP_STAT_PGPGOUT_COUNT] = {"pgpgout", 1, }, | 1884 | MCS_INACTIVE_ANON, |
1885 | MCS_ACTIVE_ANON, | ||
1886 | MCS_INACTIVE_FILE, | ||
1887 | MCS_ACTIVE_FILE, | ||
1888 | MCS_UNEVICTABLE, | ||
1889 | NR_MCS_STAT, | ||
1890 | }; | ||
1891 | |||
1892 | struct mcs_total_stat { | ||
1893 | s64 stat[NR_MCS_STAT]; | ||
1849 | }; | 1894 | }; |
1850 | 1895 | ||
1896 | struct { | ||
1897 | char *local_name; | ||
1898 | char *total_name; | ||
1899 | } memcg_stat_strings[NR_MCS_STAT] = { | ||
1900 | {"cache", "total_cache"}, | ||
1901 | {"rss", "total_rss"}, | ||
1902 | {"pgpgin", "total_pgpgin"}, | ||
1903 | {"pgpgout", "total_pgpgout"}, | ||
1904 | {"inactive_anon", "total_inactive_anon"}, | ||
1905 | {"active_anon", "total_active_anon"}, | ||
1906 | {"inactive_file", "total_inactive_file"}, | ||
1907 | {"active_file", "total_active_file"}, | ||
1908 | {"unevictable", "total_unevictable"} | ||
1909 | }; | ||
1910 | |||
1911 | |||
1912 | static int mem_cgroup_get_local_stat(struct mem_cgroup *mem, void *data) | ||
1913 | { | ||
1914 | struct mcs_total_stat *s = data; | ||
1915 | s64 val; | ||
1916 | |||
1917 | /* per cpu stat */ | ||
1918 | val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_CACHE); | ||
1919 | s->stat[MCS_CACHE] += val * PAGE_SIZE; | ||
1920 | val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_RSS); | ||
1921 | s->stat[MCS_RSS] += val * PAGE_SIZE; | ||
1922 | val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_PGPGIN_COUNT); | ||
1923 | s->stat[MCS_PGPGIN] += val; | ||
1924 | val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_PGPGOUT_COUNT); | ||
1925 | s->stat[MCS_PGPGOUT] += val; | ||
1926 | |||
1927 | /* per zone stat */ | ||
1928 | val = mem_cgroup_get_local_zonestat(mem, LRU_INACTIVE_ANON); | ||
1929 | s->stat[MCS_INACTIVE_ANON] += val * PAGE_SIZE; | ||
1930 | val = mem_cgroup_get_local_zonestat(mem, LRU_ACTIVE_ANON); | ||
1931 | s->stat[MCS_ACTIVE_ANON] += val * PAGE_SIZE; | ||
1932 | val = mem_cgroup_get_local_zonestat(mem, LRU_INACTIVE_FILE); | ||
1933 | s->stat[MCS_INACTIVE_FILE] += val * PAGE_SIZE; | ||
1934 | val = mem_cgroup_get_local_zonestat(mem, LRU_ACTIVE_FILE); | ||
1935 | s->stat[MCS_ACTIVE_FILE] += val * PAGE_SIZE; | ||
1936 | val = mem_cgroup_get_local_zonestat(mem, LRU_UNEVICTABLE); | ||
1937 | s->stat[MCS_UNEVICTABLE] += val * PAGE_SIZE; | ||
1938 | return 0; | ||
1939 | } | ||
1940 | |||
1941 | static void | ||
1942 | mem_cgroup_get_total_stat(struct mem_cgroup *mem, struct mcs_total_stat *s) | ||
1943 | { | ||
1944 | mem_cgroup_walk_tree(mem, s, mem_cgroup_get_local_stat); | ||
1945 | } | ||
1946 | |||
1851 | static int mem_control_stat_show(struct cgroup *cont, struct cftype *cft, | 1947 | static int mem_control_stat_show(struct cgroup *cont, struct cftype *cft, |
1852 | struct cgroup_map_cb *cb) | 1948 | struct cgroup_map_cb *cb) |
1853 | { | 1949 | { |
1854 | struct mem_cgroup *mem_cont = mem_cgroup_from_cont(cont); | 1950 | struct mem_cgroup *mem_cont = mem_cgroup_from_cont(cont); |
1855 | struct mem_cgroup_stat *stat = &mem_cont->stat; | 1951 | struct mcs_total_stat mystat; |
1856 | int i; | 1952 | int i; |
1857 | 1953 | ||
1858 | for (i = 0; i < ARRAY_SIZE(stat->cpustat[0].count); i++) { | 1954 | memset(&mystat, 0, sizeof(mystat)); |
1859 | s64 val; | 1955 | mem_cgroup_get_local_stat(mem_cont, &mystat); |
1860 | 1956 | ||
1861 | val = mem_cgroup_read_stat(stat, i); | 1957 | for (i = 0; i < NR_MCS_STAT; i++) |
1862 | val *= mem_cgroup_stat_desc[i].unit; | 1958 | cb->fill(cb, memcg_stat_strings[i].local_name, mystat.stat[i]); |
1863 | cb->fill(cb, mem_cgroup_stat_desc[i].msg, val); | ||
1864 | } | ||
1865 | /* showing # of active pages */ | ||
1866 | { | ||
1867 | unsigned long active_anon, inactive_anon; | ||
1868 | unsigned long active_file, inactive_file; | ||
1869 | unsigned long unevictable; | ||
1870 | |||
1871 | inactive_anon = mem_cgroup_get_all_zonestat(mem_cont, | ||
1872 | LRU_INACTIVE_ANON); | ||
1873 | active_anon = mem_cgroup_get_all_zonestat(mem_cont, | ||
1874 | LRU_ACTIVE_ANON); | ||
1875 | inactive_file = mem_cgroup_get_all_zonestat(mem_cont, | ||
1876 | LRU_INACTIVE_FILE); | ||
1877 | active_file = mem_cgroup_get_all_zonestat(mem_cont, | ||
1878 | LRU_ACTIVE_FILE); | ||
1879 | unevictable = mem_cgroup_get_all_zonestat(mem_cont, | ||
1880 | LRU_UNEVICTABLE); | ||
1881 | |||
1882 | cb->fill(cb, "active_anon", (active_anon) * PAGE_SIZE); | ||
1883 | cb->fill(cb, "inactive_anon", (inactive_anon) * PAGE_SIZE); | ||
1884 | cb->fill(cb, "active_file", (active_file) * PAGE_SIZE); | ||
1885 | cb->fill(cb, "inactive_file", (inactive_file) * PAGE_SIZE); | ||
1886 | cb->fill(cb, "unevictable", unevictable * PAGE_SIZE); | ||
1887 | 1959 | ||
1888 | } | 1960 | /* Hierarchical information */ |
1889 | { | 1961 | { |
1890 | unsigned long long limit, memsw_limit; | 1962 | unsigned long long limit, memsw_limit; |
1891 | memcg_get_hierarchical_limit(mem_cont, &limit, &memsw_limit); | 1963 | memcg_get_hierarchical_limit(mem_cont, &limit, &memsw_limit); |
@@ -1894,6 +1966,12 @@ static int mem_control_stat_show(struct cgroup *cont, struct cftype *cft, | |||
1894 | cb->fill(cb, "hierarchical_memsw_limit", memsw_limit); | 1966 | cb->fill(cb, "hierarchical_memsw_limit", memsw_limit); |
1895 | } | 1967 | } |
1896 | 1968 | ||
1969 | memset(&mystat, 0, sizeof(mystat)); | ||
1970 | mem_cgroup_get_total_stat(mem_cont, &mystat); | ||
1971 | for (i = 0; i < NR_MCS_STAT; i++) | ||
1972 | cb->fill(cb, memcg_stat_strings[i].total_name, mystat.stat[i]); | ||
1973 | |||
1974 | |||
1897 | #ifdef CONFIG_DEBUG_VM | 1975 | #ifdef CONFIG_DEBUG_VM |
1898 | cb->fill(cb, "inactive_ratio", calc_inactive_ratio(mem_cont, NULL)); | 1976 | cb->fill(cb, "inactive_ratio", calc_inactive_ratio(mem_cont, NULL)); |
1899 | 1977 | ||