diff options
author | Johannes Weiner <hannes@cmpxchg.org> | 2015-02-11 18:25:01 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-11 20:06:00 -0500 |
commit | 6de226191d12fce30331ebf024ca3ed24834f0ee (patch) | |
tree | 74d4910406255087f3e72c0e4d483513d8449ad4 /mm | |
parent | 93aa7d95248d04b934eb8e89717c7b8d6400bf2b (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.c | 68 | ||||
-rw-r--r-- | mm/page-writeback.c | 12 | ||||
-rw-r--r-- | mm/rmap.c | 12 |
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 | */ |
2002 | struct mem_cgroup *mem_cgroup_begin_page_stat(struct page *page, | 1992 | struct 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 | */ |
2037 | void mem_cgroup_end_page_stat(struct mem_cgroup *memcg, bool *locked, | 2042 | void 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); | |||
2308 | int test_clear_page_writeback(struct page *page) | 2308 | int 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 | ||
2345 | int __test_set_page_writeback(struct page *page, bool keep_write) | 2343 | int __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 | } |
@@ -1085,24 +1085,20 @@ void page_add_new_anon_rmap(struct page *page, | |||
1085 | void page_add_file_rmap(struct page *page) | 1085 | void 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 | ||
1099 | static void page_remove_file_rmap(struct page *page) | 1097 | static 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); |
1125 | out: | 1121 | out: |
1126 | mem_cgroup_end_page_stat(memcg, &locked, &flags); | 1122 | mem_cgroup_end_page_stat(memcg); |
1127 | } | 1123 | } |
1128 | 1124 | ||
1129 | /** | 1125 | /** |