diff options
author | KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> | 2009-04-02 19:57:45 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-02 22:04:56 -0400 |
commit | a3b2d692690aef228e493b1beaafe5364cab3237 (patch) | |
tree | d3ad99c5370462861a1b918b4136e7bd7ad78e52 /mm/memcontrol.c | |
parent | 3c776e64660028236313f0e54f3a9945764422df (diff) |
cgroups: use css id in swap cgroup for saving memory v5
Try to use CSS ID for records in swap_cgroup. By this, on 64bit machine,
size of swap_cgroup goes down to 2 bytes from 8bytes.
This means, when 2GB of swap is equipped, (assume the page size is 4096bytes)
From size of swap_cgroup = 2G/4k * 8 = 4Mbytes.
To size of swap_cgroup = 2G/4k * 2 = 1Mbytes.
Reduction is large. Of course, there are trade-offs. This CSS ID will
add overhead to swap-in/swap-out/swap-free.
But in general,
- swap is a resource which the user tend to avoid use.
- If swap is never used, swap_cgroup area is not used.
- Reading traditional manuals, size of swap should be proportional to
size of memory. Memory size of machine is increasing now.
I think reducing size of swap_cgroup makes sense.
Note:
- ID->CSS lookup routine has no locks, it's under RCU-Read-Side.
- memcg can be obsolete at rmdir() but not freed while refcnt from
swap_cgroup is available.
Changelog v4->v5:
- reworked on to memcg-charge-swapcache-to-proper-memcg.patch
Changlog ->v4:
- fixed not configured case.
- deleted unnecessary comments.
- fixed NULL pointer bug.
- fixed message in dmesg.
[nishimura@mxp.nes.nec.co.jp: css_tryget can be called twice in !PageCgroupUsed case]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Cc: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
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 | 74 |
1 files changed, 62 insertions, 12 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 81b0ae8183d0..55dea5968464 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -991,10 +991,31 @@ nomem: | |||
991 | return -ENOMEM; | 991 | return -ENOMEM; |
992 | } | 992 | } |
993 | 993 | ||
994 | |||
995 | /* | ||
996 | * A helper function to get mem_cgroup from ID. must be called under | ||
997 | * rcu_read_lock(). The caller must check css_is_removed() or some if | ||
998 | * it's concern. (dropping refcnt from swap can be called against removed | ||
999 | * memcg.) | ||
1000 | */ | ||
1001 | static struct mem_cgroup *mem_cgroup_lookup(unsigned short id) | ||
1002 | { | ||
1003 | struct cgroup_subsys_state *css; | ||
1004 | |||
1005 | /* ID 0 is unused ID */ | ||
1006 | if (!id) | ||
1007 | return NULL; | ||
1008 | css = css_lookup(&mem_cgroup_subsys, id); | ||
1009 | if (!css) | ||
1010 | return NULL; | ||
1011 | return container_of(css, struct mem_cgroup, css); | ||
1012 | } | ||
1013 | |||
994 | static struct mem_cgroup *try_get_mem_cgroup_from_swapcache(struct page *page) | 1014 | static struct mem_cgroup *try_get_mem_cgroup_from_swapcache(struct page *page) |
995 | { | 1015 | { |
996 | struct mem_cgroup *mem; | 1016 | struct mem_cgroup *mem; |
997 | struct page_cgroup *pc; | 1017 | struct page_cgroup *pc; |
1018 | unsigned short id; | ||
998 | swp_entry_t ent; | 1019 | swp_entry_t ent; |
999 | 1020 | ||
1000 | VM_BUG_ON(!PageLocked(page)); | 1021 | VM_BUG_ON(!PageLocked(page)); |
@@ -1006,16 +1027,19 @@ static struct mem_cgroup *try_get_mem_cgroup_from_swapcache(struct page *page) | |||
1006 | /* | 1027 | /* |
1007 | * Used bit of swapcache is solid under page lock. | 1028 | * Used bit of swapcache is solid under page lock. |
1008 | */ | 1029 | */ |
1009 | if (PageCgroupUsed(pc)) | 1030 | if (PageCgroupUsed(pc)) { |
1010 | mem = pc->mem_cgroup; | 1031 | mem = pc->mem_cgroup; |
1011 | else { | 1032 | if (mem && !css_tryget(&mem->css)) |
1033 | mem = NULL; | ||
1034 | } else { | ||
1012 | ent.val = page_private(page); | 1035 | ent.val = page_private(page); |
1013 | mem = lookup_swap_cgroup(ent); | 1036 | id = lookup_swap_cgroup(ent); |
1037 | rcu_read_lock(); | ||
1038 | mem = mem_cgroup_lookup(id); | ||
1039 | if (mem && !css_tryget(&mem->css)) | ||
1040 | mem = NULL; | ||
1041 | rcu_read_unlock(); | ||
1014 | } | 1042 | } |
1015 | if (!mem) | ||
1016 | return NULL; | ||
1017 | if (!css_tryget(&mem->css)) | ||
1018 | return NULL; | ||
1019 | return mem; | 1043 | return mem; |
1020 | } | 1044 | } |
1021 | 1045 | ||
@@ -1276,12 +1300,22 @@ int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, | |||
1276 | 1300 | ||
1277 | if (do_swap_account && !ret && PageSwapCache(page)) { | 1301 | if (do_swap_account && !ret && PageSwapCache(page)) { |
1278 | swp_entry_t ent = {.val = page_private(page)}; | 1302 | swp_entry_t ent = {.val = page_private(page)}; |
1303 | unsigned short id; | ||
1279 | /* avoid double counting */ | 1304 | /* avoid double counting */ |
1280 | mem = swap_cgroup_record(ent, NULL); | 1305 | id = swap_cgroup_record(ent, 0); |
1306 | rcu_read_lock(); | ||
1307 | mem = mem_cgroup_lookup(id); | ||
1281 | if (mem) { | 1308 | if (mem) { |
1309 | /* | ||
1310 | * We did swap-in. Then, this entry is doubly counted | ||
1311 | * both in mem and memsw. We uncharge it, here. | ||
1312 | * Recorded ID can be obsolete. We avoid calling | ||
1313 | * css_tryget() | ||
1314 | */ | ||
1282 | res_counter_uncharge(&mem->memsw, PAGE_SIZE); | 1315 | res_counter_uncharge(&mem->memsw, PAGE_SIZE); |
1283 | mem_cgroup_put(mem); | 1316 | mem_cgroup_put(mem); |
1284 | } | 1317 | } |
1318 | rcu_read_unlock(); | ||
1285 | } | 1319 | } |
1286 | return ret; | 1320 | return ret; |
1287 | } | 1321 | } |
@@ -1346,13 +1380,21 @@ void mem_cgroup_commit_charge_swapin(struct page *page, struct mem_cgroup *ptr) | |||
1346 | */ | 1380 | */ |
1347 | if (do_swap_account && PageSwapCache(page)) { | 1381 | if (do_swap_account && PageSwapCache(page)) { |
1348 | swp_entry_t ent = {.val = page_private(page)}; | 1382 | swp_entry_t ent = {.val = page_private(page)}; |
1383 | unsigned short id; | ||
1349 | struct mem_cgroup *memcg; | 1384 | struct mem_cgroup *memcg; |
1350 | memcg = swap_cgroup_record(ent, NULL); | 1385 | |
1386 | id = swap_cgroup_record(ent, 0); | ||
1387 | rcu_read_lock(); | ||
1388 | memcg = mem_cgroup_lookup(id); | ||
1351 | if (memcg) { | 1389 | if (memcg) { |
1390 | /* | ||
1391 | * This recorded memcg can be obsolete one. So, avoid | ||
1392 | * calling css_tryget | ||
1393 | */ | ||
1352 | res_counter_uncharge(&memcg->memsw, PAGE_SIZE); | 1394 | res_counter_uncharge(&memcg->memsw, PAGE_SIZE); |
1353 | mem_cgroup_put(memcg); | 1395 | mem_cgroup_put(memcg); |
1354 | } | 1396 | } |
1355 | 1397 | rcu_read_unlock(); | |
1356 | } | 1398 | } |
1357 | /* add this page(page_cgroup) to the LRU we want. */ | 1399 | /* add this page(page_cgroup) to the LRU we want. */ |
1358 | 1400 | ||
@@ -1473,7 +1515,7 @@ void mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent) | |||
1473 | MEM_CGROUP_CHARGE_TYPE_SWAPOUT); | 1515 | MEM_CGROUP_CHARGE_TYPE_SWAPOUT); |
1474 | /* record memcg information */ | 1516 | /* record memcg information */ |
1475 | if (do_swap_account && memcg) { | 1517 | if (do_swap_account && memcg) { |
1476 | swap_cgroup_record(ent, memcg); | 1518 | swap_cgroup_record(ent, css_id(&memcg->css)); |
1477 | mem_cgroup_get(memcg); | 1519 | mem_cgroup_get(memcg); |
1478 | } | 1520 | } |
1479 | if (memcg) | 1521 | if (memcg) |
@@ -1488,15 +1530,23 @@ void mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent) | |||
1488 | void mem_cgroup_uncharge_swap(swp_entry_t ent) | 1530 | void mem_cgroup_uncharge_swap(swp_entry_t ent) |
1489 | { | 1531 | { |
1490 | struct mem_cgroup *memcg; | 1532 | struct mem_cgroup *memcg; |
1533 | unsigned short id; | ||
1491 | 1534 | ||
1492 | if (!do_swap_account) | 1535 | if (!do_swap_account) |
1493 | return; | 1536 | return; |
1494 | 1537 | ||
1495 | memcg = swap_cgroup_record(ent, NULL); | 1538 | id = swap_cgroup_record(ent, 0); |
1539 | rcu_read_lock(); | ||
1540 | memcg = mem_cgroup_lookup(id); | ||
1496 | if (memcg) { | 1541 | if (memcg) { |
1542 | /* | ||
1543 | * We uncharge this because swap is freed. | ||
1544 | * This memcg can be obsolete one. We avoid calling css_tryget | ||
1545 | */ | ||
1497 | res_counter_uncharge(&memcg->memsw, PAGE_SIZE); | 1546 | res_counter_uncharge(&memcg->memsw, PAGE_SIZE); |
1498 | mem_cgroup_put(memcg); | 1547 | mem_cgroup_put(memcg); |
1499 | } | 1548 | } |
1549 | rcu_read_unlock(); | ||
1500 | } | 1550 | } |
1501 | #endif | 1551 | #endif |
1502 | 1552 | ||