Commit 8a9478ca authored by KAMEZAWA Hiroyuki's avatar KAMEZAWA Hiroyuki Committed by Linus Torvalds

memcg: fix swap accounting

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: default avatarKAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Acked-by: default avatarBalbir 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 20ebcdda
...@@ -319,10 +319,11 @@ static inline void disable_swap_token(void) ...@@ -319,10 +319,11 @@ static inline void disable_swap_token(void)
} }
#ifdef CONFIG_CGROUP_MEM_RES_CTLR #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 #else
static inline void 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 #endif
......
...@@ -189,6 +189,7 @@ enum charge_type { ...@@ -189,6 +189,7 @@ enum charge_type {
MEM_CGROUP_CHARGE_TYPE_SHMEM, /* used by page migration of shmem */ MEM_CGROUP_CHARGE_TYPE_SHMEM, /* used by page migration of shmem */
MEM_CGROUP_CHARGE_TYPE_FORCE, /* used by force_empty */ MEM_CGROUP_CHARGE_TYPE_FORCE, /* used by force_empty */
MEM_CGROUP_CHARGE_TYPE_SWAPOUT, /* for accounting swapcache */ MEM_CGROUP_CHARGE_TYPE_SWAPOUT, /* for accounting swapcache */
MEM_CGROUP_CHARGE_TYPE_DROP, /* a page was unused swap cache */
NR_CHARGE_TYPE, NR_CHARGE_TYPE,
}; };
...@@ -1493,6 +1494,7 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype) ...@@ -1493,6 +1494,7 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
switch (ctype) { switch (ctype) {
case MEM_CGROUP_CHARGE_TYPE_MAPPED: case MEM_CGROUP_CHARGE_TYPE_MAPPED:
case MEM_CGROUP_CHARGE_TYPE_DROP:
if (page_mapped(page)) if (page_mapped(page))
goto unlock_out; goto unlock_out;
break; break;
...@@ -1556,18 +1558,23 @@ void mem_cgroup_uncharge_cache_page(struct page *page) ...@@ -1556,18 +1558,23 @@ void mem_cgroup_uncharge_cache_page(struct page *page)
* called after __delete_from_swap_cache() and drop "page" account. * called after __delete_from_swap_cache() and drop "page" account.
* memcg information is recorded to swap_cgroup of "ent" * 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; 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 */ /* record memcg information */
if (do_swap_account && memcg) { if (do_swap_account && swapout && memcg) {
swap_cgroup_record(ent, css_id(&memcg->css)); swap_cgroup_record(ent, css_id(&memcg->css));
mem_cgroup_get(memcg); mem_cgroup_get(memcg);
} }
if (memcg) if (swapout && memcg)
css_put(&memcg->css); css_put(&memcg->css);
} }
#endif #endif
......
...@@ -583,8 +583,9 @@ static int swap_entry_free(struct swap_info_struct *p, ...@@ -583,8 +583,9 @@ static int swap_entry_free(struct swap_info_struct *p,
swap_list.next = p - swap_info; swap_list.next = p - swap_info;
nr_swap_pages++; nr_swap_pages++;
p->inuse_pages--; p->inuse_pages--;
mem_cgroup_uncharge_swap(ent);
} }
if (!swap_count(count))
mem_cgroup_uncharge_swap(ent);
return count; return count;
} }
...@@ -609,12 +610,19 @@ void swap_free(swp_entry_t entry) ...@@ -609,12 +610,19 @@ void swap_free(swp_entry_t entry)
void swapcache_free(swp_entry_t entry, struct page *page) void swapcache_free(swp_entry_t entry, struct page *page)
{ {
struct swap_info_struct *p; struct swap_info_struct *p;
int ret;
if (page)
mem_cgroup_uncharge_swapcache(page, entry);
p = swap_info_get(entry); p = swap_info_get(entry);
if (p) { 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); spin_unlock(&swap_lock);
} }
return; return;
......
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