memcg: fix swap accounting
authorKAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Wed, 17 Jun 2009 23:27:17 +0000 (16:27 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 18 Jun 2009 20:03:47 +0000 (13:03 -0700)
This patch fixes mis-accounting of swap usage in memcg.

In the current implementation, memcg's swap account is uncharged only when
swap is completely freed.  But there are several cases where swap cannot
be freed cleanly.  For handling that, this patch changes that memcg
uncharges swap account when swap has no references other than cache.

By this, memcg's swap entry accounting can be fully synchronous with the
application's behavior.

This patch also changes memcg's hooks for swap-out.

Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Acked-by: Balbir Singh <balbir@in.ibm.com>
Cc: Hugh Dickins <hugh.dickins@tiscali.co.uk>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Dhaval Giani <dhaval@linux.vnet.ibm.com>
Cc: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
include/linux/swap.h
mm/memcontrol.c
mm/swapfile.c

index dca9b9999aebf5a1bd3f12dcfd47ea8ba2addd8e..c88b36665f79bdcef2a4c37e316bb41a14e4b831 100644 (file)
@@ -319,10 +319,11 @@ static inline void disable_swap_token(void)
 }
 
 #ifdef CONFIG_CGROUP_MEM_RES_CTLR
-extern void mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent);
+extern void
+mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout);
 #else
 static inline void
-mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent)
+mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout)
 {
 }
 #endif
index 3468c38adde69ef9415a7ee5a4317e82d1575b9b..a83e0395444b2132ecf81ca0b8e4633017efcdf0 100644 (file)
@@ -189,6 +189,7 @@ enum charge_type {
        MEM_CGROUP_CHARGE_TYPE_SHMEM,   /* used by page migration of shmem */
        MEM_CGROUP_CHARGE_TYPE_FORCE,   /* used by force_empty */
        MEM_CGROUP_CHARGE_TYPE_SWAPOUT, /* for accounting swapcache */
+       MEM_CGROUP_CHARGE_TYPE_DROP,    /* a page was unused swap cache */
        NR_CHARGE_TYPE,
 };
 
@@ -1493,6 +1494,7 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
 
        switch (ctype) {
        case MEM_CGROUP_CHARGE_TYPE_MAPPED:
+       case MEM_CGROUP_CHARGE_TYPE_DROP:
                if (page_mapped(page))
                        goto unlock_out;
                break;
@@ -1556,18 +1558,23 @@ void mem_cgroup_uncharge_cache_page(struct page *page)
  * called after __delete_from_swap_cache() and drop "page" account.
  * memcg information is recorded to swap_cgroup of "ent"
  */
-void mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent)
+void
+mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout)
 {
        struct mem_cgroup *memcg;
+       int ctype = MEM_CGROUP_CHARGE_TYPE_SWAPOUT;
+
+       if (!swapout) /* this was a swap cache but the swap is unused ! */
+               ctype = MEM_CGROUP_CHARGE_TYPE_DROP;
+
+       memcg = __mem_cgroup_uncharge_common(page, ctype);
 
-       memcg = __mem_cgroup_uncharge_common(page,
-                                       MEM_CGROUP_CHARGE_TYPE_SWAPOUT);
        /* record memcg information */
-       if (do_swap_account && memcg) {
+       if (do_swap_account && swapout && memcg) {
                swap_cgroup_record(ent, css_id(&memcg->css));
                mem_cgroup_get(memcg);
        }
-       if (memcg)
+       if (swapout && memcg)
                css_put(&memcg->css);
 }
 #endif
index 28faa01cf578bd1bc05fa20771015194fd966e4d..d1ade1a48ee7b962852c7645bc08b15c67924050 100644 (file)
@@ -583,8 +583,9 @@ static int swap_entry_free(struct swap_info_struct *p,
                        swap_list.next = p - swap_info;
                nr_swap_pages++;
                p->inuse_pages--;
-               mem_cgroup_uncharge_swap(ent);
        }
+       if (!swap_count(count))
+               mem_cgroup_uncharge_swap(ent);
        return count;
 }
 
@@ -609,12 +610,19 @@ void swap_free(swp_entry_t entry)
 void swapcache_free(swp_entry_t entry, struct page *page)
 {
        struct swap_info_struct *p;
+       int ret;
 
-       if (page)
-               mem_cgroup_uncharge_swapcache(page, entry);
        p = swap_info_get(entry);
        if (p) {
-               swap_entry_free(p, entry, SWAP_CACHE);
+               ret = swap_entry_free(p, entry, SWAP_CACHE);
+               if (page) {
+                       bool swapout;
+                       if (ret)
+                               swapout = true; /* the end of swap out */
+                       else
+                               swapout = false; /* no more swap users! */
+                       mem_cgroup_uncharge_swapcache(page, entry, swapout);
+               }
                spin_unlock(&swap_lock);
        }
        return;