Commit 4e5f01c2 authored by KAMEZAWA Hiroyuki's avatar KAMEZAWA Hiroyuki Committed by Linus Torvalds

memcg: clear pc->mem_cgroup if necessary.

This is a preparation before removing a flag PCG_ACCT_LRU in page_cgroup
and reducing atomic ops/complexity in memcg LRU handling.

In some cases, pages are added to lru before charge to memcg and pages
are not classfied to memory cgroup at lru addtion.  Now, the lru where
the page should be added is determined a bit in page_cgroup->flags and
pc->mem_cgroup.  I'd like to remove the check of flag.

To handle the case pc->mem_cgroup may contain stale pointers if pages
are added to LRU before classification.  This patch resets
pc->mem_cgroup to root_mem_cgroup before lru additions.

[akpm@linux-foundation.org: fix CONFIG_CGROUP_MEM_CONT=n build]
[hughd@google.com: fix CONFIG_CGROUP_MEM_RES_CTLR=y CONFIG_CGROUP_MEM_RES_CTLR_SWAP=n build]
[akpm@linux-foundation.org: ksm.c needs memcontrol.h, per Michal]
[hughd@google.com: stop oops in mem_cgroup_reset_owner()]
[hughd@google.com: fix page migration to reset_owner]
Signed-off-by: default avatarKAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Miklos Szeredi <mszeredi@suse.cz>
Acked-by: default avatarMichal Hocko <mhocko@suse.cz>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: default avatarHugh Dickins <hughd@google.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 36b62ad5
...@@ -129,6 +129,7 @@ extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, ...@@ -129,6 +129,7 @@ extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg,
extern void mem_cgroup_replace_page_cache(struct page *oldpage, extern void mem_cgroup_replace_page_cache(struct page *oldpage,
struct page *newpage); struct page *newpage);
extern void mem_cgroup_reset_owner(struct page *page);
#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
extern int do_swap_account; extern int do_swap_account;
#endif #endif
...@@ -391,6 +392,10 @@ static inline void mem_cgroup_replace_page_cache(struct page *oldpage, ...@@ -391,6 +392,10 @@ static inline void mem_cgroup_replace_page_cache(struct page *oldpage,
struct page *newpage) struct page *newpage)
{ {
} }
static inline void mem_cgroup_reset_owner(struct page *page)
{
}
#endif /* CONFIG_CGROUP_MEM_CONT */ #endif /* CONFIG_CGROUP_MEM_CONT */
#if !defined(CONFIG_CGROUP_MEM_RES_CTLR) || !defined(CONFIG_DEBUG_VM) #if !defined(CONFIG_CGROUP_MEM_RES_CTLR) || !defined(CONFIG_DEBUG_VM)
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/memcontrol.h>
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <linux/memory.h> #include <linux/memory.h>
#include <linux/mmu_notifier.h> #include <linux/mmu_notifier.h>
...@@ -1571,6 +1572,16 @@ struct page *ksm_does_need_to_copy(struct page *page, ...@@ -1571,6 +1572,16 @@ struct page *ksm_does_need_to_copy(struct page *page,
new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address); new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address);
if (new_page) { if (new_page) {
/*
* The memcg-specific accounting when moving
* pages around the LRU lists relies on the
* page's owner (memcg) to be valid. Usually,
* pages are assigned to a new owner before
* being put on the LRU list, but since this
* is not the case here, the stale owner from
* a previous allocation cycle must be reset.
*/
mem_cgroup_reset_owner(new_page);
copy_user_highpage(new_page, page, address, vma); copy_user_highpage(new_page, page, address, vma);
SetPageDirty(new_page); SetPageDirty(new_page);
......
...@@ -3050,6 +3050,23 @@ void mem_cgroup_uncharge_end(void) ...@@ -3050,6 +3050,23 @@ void mem_cgroup_uncharge_end(void)
batch->memcg = NULL; batch->memcg = NULL;
} }
/*
* A function for resetting pc->mem_cgroup for newly allocated pages.
* This function should be called if the newpage will be added to LRU
* before start accounting.
*/
void mem_cgroup_reset_owner(struct page *newpage)
{
struct page_cgroup *pc;
if (mem_cgroup_disabled())
return;
pc = lookup_page_cgroup(newpage);
VM_BUG_ON(PageCgroupUsed(pc));
pc->mem_cgroup = root_mem_cgroup;
}
#ifdef CONFIG_SWAP #ifdef CONFIG_SWAP
/* /*
* called after __delete_from_swap_cache() and drop "page" account. * called after __delete_from_swap_cache() and drop "page" account.
......
...@@ -777,6 +777,8 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, ...@@ -777,6 +777,8 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
if (!newpage) if (!newpage)
return -ENOMEM; return -ENOMEM;
mem_cgroup_reset_owner(newpage);
if (page_count(page) == 1) { if (page_count(page) == 1) {
/* page was freed from under us. So we are done. */ /* page was freed from under us. So we are done. */
goto out; goto out;
......
...@@ -300,6 +300,16 @@ struct page *read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask, ...@@ -300,6 +300,16 @@ struct page *read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,
new_page = alloc_page_vma(gfp_mask, vma, addr); new_page = alloc_page_vma(gfp_mask, vma, addr);
if (!new_page) if (!new_page)
break; /* Out of memory */ break; /* Out of memory */
/*
* The memcg-specific accounting when moving
* pages around the LRU lists relies on the
* page's owner (memcg) to be valid. Usually,
* pages are assigned to a new owner before
* being put on the LRU list, but since this
* is not the case here, the stale owner from
* a previous allocation cycle must be reset.
*/
mem_cgroup_reset_owner(new_page);
} }
/* /*
......
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