aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorJohannes Weiner <hannes@cmpxchg.org>2015-02-11 18:25:01 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2015-02-11 20:06:00 -0500
commit6de226191d12fce30331ebf024ca3ed24834f0ee (patch)
tree74d4910406255087f3e72c0e4d483513d8449ad4 /mm
parent93aa7d95248d04b934eb8e89717c7b8d6400bf2b (diff)
mm: memcontrol: track move_lock state internally
The complexity of memcg page stat synchronization is currently leaking into the callsites, forcing them to keep track of the move_lock state and the IRQ flags. Simplify the API by tracking it in the memcg. Signed-off-by: Johannes Weiner <hannes@cmpxchg.org> Acked-by: Michal Hocko <mhocko@suse.cz> Reviewed-by: Vladimir Davydov <vdavydov@parallels.com> Cc: Wu Fengguang <fengguang.wu@intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/memcontrol.c68
-rw-r--r--mm/page-writeback.c12
-rw-r--r--mm/rmap.c12
3 files changed, 47 insertions, 45 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index f3f8a4f52a0c..028d07c79104 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -325,9 +325,11 @@ struct mem_cgroup {
325 /* 325 /*
326 * set > 0 if pages under this cgroup are moving to other cgroup. 326 * set > 0 if pages under this cgroup are moving to other cgroup.
327 */ 327 */
328 atomic_t moving_account; 328 atomic_t moving_account;
329 /* taken only while moving_account > 0 */ 329 /* taken only while moving_account > 0 */
330 spinlock_t move_lock; 330 spinlock_t move_lock;
331 struct task_struct *move_lock_task;
332 unsigned long move_lock_flags;
331 /* 333 /*
332 * percpu counter. 334 * percpu counter.
333 */ 335 */
@@ -1977,34 +1979,33 @@ cleanup:
1977/** 1979/**
1978 * mem_cgroup_begin_page_stat - begin a page state statistics transaction 1980 * mem_cgroup_begin_page_stat - begin a page state statistics transaction
1979 * @page: page that is going to change accounted state 1981 * @page: page that is going to change accounted state
1980 * @locked: &memcg->move_lock slowpath was taken
1981 * @flags: IRQ-state flags for &memcg->move_lock
1982 * 1982 *
1983 * This function must mark the beginning of an accounted page state 1983 * This function must mark the beginning of an accounted page state
1984 * change to prevent double accounting when the page is concurrently 1984 * change to prevent double accounting when the page is concurrently
1985 * being moved to another memcg: 1985 * being moved to another memcg:
1986 * 1986 *
1987 * memcg = mem_cgroup_begin_page_stat(page, &locked, &flags); 1987 * memcg = mem_cgroup_begin_page_stat(page);
1988 * if (TestClearPageState(page)) 1988 * if (TestClearPageState(page))
1989 * mem_cgroup_update_page_stat(memcg, state, -1); 1989 * mem_cgroup_update_page_stat(memcg, state, -1);
1990 * mem_cgroup_end_page_stat(memcg, locked, flags); 1990 * mem_cgroup_end_page_stat(memcg);
1991 *
1992 * The RCU lock is held throughout the transaction. The fast path can
1993 * get away without acquiring the memcg->move_lock (@locked is false)
1994 * because page moving starts with an RCU grace period.
1995 *
1996 * The RCU lock also protects the memcg from being freed when the page
1997 * state that is going to change is the only thing preventing the page
1998 * from being uncharged. E.g. end-writeback clearing PageWriteback(),
1999 * which allows migration to go ahead and uncharge the page before the
2000 * account transaction might be complete.
2001 */ 1991 */
2002struct mem_cgroup *mem_cgroup_begin_page_stat(struct page *page, 1992struct mem_cgroup *mem_cgroup_begin_page_stat(struct page *page)
2003 bool *locked,
2004 unsigned long *flags)
2005{ 1993{
2006 struct mem_cgroup *memcg; 1994 struct mem_cgroup *memcg;
1995 unsigned long flags;
2007 1996
1997 /*
1998 * The RCU lock is held throughout the transaction. The fast
1999 * path can get away without acquiring the memcg->move_lock
2000 * because page moving starts with an RCU grace period.
2001 *
2002 * The RCU lock also protects the memcg from being freed when
2003 * the page state that is going to change is the only thing
2004 * preventing the page from being uncharged.
2005 * E.g. end-writeback clearing PageWriteback(), which allows
2006 * migration to go ahead and uncharge the page before the
2007 * account transaction might be complete.
2008 */
2008 rcu_read_lock(); 2009 rcu_read_lock();
2009 2010
2010 if (mem_cgroup_disabled()) 2011 if (mem_cgroup_disabled())
@@ -2014,16 +2015,22 @@ again:
2014 if (unlikely(!memcg)) 2015 if (unlikely(!memcg))
2015 return NULL; 2016 return NULL;
2016 2017
2017 *locked = false;
2018 if (atomic_read(&memcg->moving_account) <= 0) 2018 if (atomic_read(&memcg->moving_account) <= 0)
2019 return memcg; 2019 return memcg;
2020 2020
2021 spin_lock_irqsave(&memcg->move_lock, *flags); 2021 spin_lock_irqsave(&memcg->move_lock, flags);
2022 if (memcg != page->mem_cgroup) { 2022 if (memcg != page->mem_cgroup) {
2023 spin_unlock_irqrestore(&memcg->move_lock, *flags); 2023 spin_unlock_irqrestore(&memcg->move_lock, flags);
2024 goto again; 2024 goto again;
2025 } 2025 }
2026 *locked = true; 2026
2027 /*
2028 * When charge migration first begins, we can have locked and
2029 * unlocked page stat updates happening concurrently. Track
2030 * the task who has the lock for mem_cgroup_end_page_stat().
2031 */
2032 memcg->move_lock_task = current;
2033 memcg->move_lock_flags = flags;
2027 2034
2028 return memcg; 2035 return memcg;
2029} 2036}
@@ -2031,14 +2038,17 @@ again:
2031/** 2038/**
2032 * mem_cgroup_end_page_stat - finish a page state statistics transaction 2039 * mem_cgroup_end_page_stat - finish a page state statistics transaction
2033 * @memcg: the memcg that was accounted against 2040 * @memcg: the memcg that was accounted against
2034 * @locked: value received from mem_cgroup_begin_page_stat()
2035 * @flags: value received from mem_cgroup_begin_page_stat()
2036 */ 2041 */
2037void mem_cgroup_end_page_stat(struct mem_cgroup *memcg, bool *locked, 2042void mem_cgroup_end_page_stat(struct mem_cgroup *memcg)
2038 unsigned long *flags)
2039{ 2043{
2040 if (memcg && *locked) 2044 if (memcg && memcg->move_lock_task == current) {
2041 spin_unlock_irqrestore(&memcg->move_lock, *flags); 2045 unsigned long flags = memcg->move_lock_flags;
2046
2047 memcg->move_lock_task = NULL;
2048 memcg->move_lock_flags = 0;
2049
2050 spin_unlock_irqrestore(&memcg->move_lock, flags);
2051 }
2042 2052
2043 rcu_read_unlock(); 2053 rcu_read_unlock();
2044} 2054}
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 6f4335238e33..fb71e9deca85 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -2308,12 +2308,10 @@ EXPORT_SYMBOL(clear_page_dirty_for_io);
2308int test_clear_page_writeback(struct page *page) 2308int test_clear_page_writeback(struct page *page)
2309{ 2309{
2310 struct address_space *mapping = page_mapping(page); 2310 struct address_space *mapping = page_mapping(page);
2311 unsigned long memcg_flags;
2312 struct mem_cgroup *memcg; 2311 struct mem_cgroup *memcg;
2313 bool locked;
2314 int ret; 2312 int ret;
2315 2313
2316 memcg = mem_cgroup_begin_page_stat(page, &locked, &memcg_flags); 2314 memcg = mem_cgroup_begin_page_stat(page);
2317 if (mapping) { 2315 if (mapping) {
2318 struct backing_dev_info *bdi = mapping->backing_dev_info; 2316 struct backing_dev_info *bdi = mapping->backing_dev_info;
2319 unsigned long flags; 2317 unsigned long flags;
@@ -2338,19 +2336,17 @@ int test_clear_page_writeback(struct page *page)
2338 dec_zone_page_state(page, NR_WRITEBACK); 2336 dec_zone_page_state(page, NR_WRITEBACK);
2339 inc_zone_page_state(page, NR_WRITTEN); 2337 inc_zone_page_state(page, NR_WRITTEN);
2340 } 2338 }
2341 mem_cgroup_end_page_stat(memcg, &locked, &memcg_flags); 2339 mem_cgroup_end_page_stat(memcg);
2342 return ret; 2340 return ret;
2343} 2341}
2344 2342
2345int __test_set_page_writeback(struct page *page, bool keep_write) 2343int __test_set_page_writeback(struct page *page, bool keep_write)
2346{ 2344{
2347 struct address_space *mapping = page_mapping(page); 2345 struct address_space *mapping = page_mapping(page);
2348 unsigned long memcg_flags;
2349 struct mem_cgroup *memcg; 2346 struct mem_cgroup *memcg;
2350 bool locked;
2351 int ret; 2347 int ret;
2352 2348
2353 memcg = mem_cgroup_begin_page_stat(page, &locked, &memcg_flags); 2349 memcg = mem_cgroup_begin_page_stat(page);
2354 if (mapping) { 2350 if (mapping) {
2355 struct backing_dev_info *bdi = mapping->backing_dev_info; 2351 struct backing_dev_info *bdi = mapping->backing_dev_info;
2356 unsigned long flags; 2352 unsigned long flags;
@@ -2380,7 +2376,7 @@ int __test_set_page_writeback(struct page *page, bool keep_write)
2380 mem_cgroup_inc_page_stat(memcg, MEM_CGROUP_STAT_WRITEBACK); 2376 mem_cgroup_inc_page_stat(memcg, MEM_CGROUP_STAT_WRITEBACK);
2381 inc_zone_page_state(page, NR_WRITEBACK); 2377 inc_zone_page_state(page, NR_WRITEBACK);
2382 } 2378 }
2383 mem_cgroup_end_page_stat(memcg, &locked, &memcg_flags); 2379 mem_cgroup_end_page_stat(memcg);
2384 return ret; 2380 return ret;
2385 2381
2386} 2382}
diff --git a/mm/rmap.c b/mm/rmap.c
index 70b32498d4f2..5e3e09081164 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -1085,24 +1085,20 @@ void page_add_new_anon_rmap(struct page *page,
1085void page_add_file_rmap(struct page *page) 1085void page_add_file_rmap(struct page *page)
1086{ 1086{
1087 struct mem_cgroup *memcg; 1087 struct mem_cgroup *memcg;
1088 unsigned long flags;
1089 bool locked;
1090 1088
1091 memcg = mem_cgroup_begin_page_stat(page, &locked, &flags); 1089 memcg = mem_cgroup_begin_page_stat(page);
1092 if (atomic_inc_and_test(&page->_mapcount)) { 1090 if (atomic_inc_and_test(&page->_mapcount)) {
1093 __inc_zone_page_state(page, NR_FILE_MAPPED); 1091 __inc_zone_page_state(page, NR_FILE_MAPPED);
1094 mem_cgroup_inc_page_stat(memcg, MEM_CGROUP_STAT_FILE_MAPPED); 1092 mem_cgroup_inc_page_stat(memcg, MEM_CGROUP_STAT_FILE_MAPPED);
1095 } 1093 }
1096 mem_cgroup_end_page_stat(memcg, &locked, &flags); 1094 mem_cgroup_end_page_stat(memcg);
1097} 1095}
1098 1096
1099static void page_remove_file_rmap(struct page *page) 1097static void page_remove_file_rmap(struct page *page)
1100{ 1098{
1101 struct mem_cgroup *memcg; 1099 struct mem_cgroup *memcg;
1102 unsigned long flags;
1103 bool locked;
1104 1100
1105 memcg = mem_cgroup_begin_page_stat(page, &locked, &flags); 1101 memcg = mem_cgroup_begin_page_stat(page);
1106 1102
1107 /* page still mapped by someone else? */ 1103 /* page still mapped by someone else? */
1108 if (!atomic_add_negative(-1, &page->_mapcount)) 1104 if (!atomic_add_negative(-1, &page->_mapcount))
@@ -1123,7 +1119,7 @@ static void page_remove_file_rmap(struct page *page)
1123 if (unlikely(PageMlocked(page))) 1119 if (unlikely(PageMlocked(page)))
1124 clear_page_mlock(page); 1120 clear_page_mlock(page);
1125out: 1121out:
1126 mem_cgroup_end_page_stat(memcg, &locked, &flags); 1122 mem_cgroup_end_page_stat(memcg);
1127} 1123}
1128 1124
1129/** 1125/**