diff options
Diffstat (limited to 'mm')
-rw-r--r-- | mm/memcontrol.c | 105 | ||||
-rw-r--r-- | mm/page-writeback.c | 22 | ||||
-rw-r--r-- | mm/rmap.c | 20 |
3 files changed, 79 insertions, 68 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 23976fd885fd..d6ac0e33e150 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -1536,12 +1536,8 @@ int mem_cgroup_swappiness(struct mem_cgroup *memcg) | |||
1536 | * start move here. | 1536 | * start move here. |
1537 | */ | 1537 | */ |
1538 | 1538 | ||
1539 | /* for quick checking without looking up memcg */ | ||
1540 | atomic_t memcg_moving __read_mostly; | ||
1541 | |||
1542 | static void mem_cgroup_start_move(struct mem_cgroup *memcg) | 1539 | static void mem_cgroup_start_move(struct mem_cgroup *memcg) |
1543 | { | 1540 | { |
1544 | atomic_inc(&memcg_moving); | ||
1545 | atomic_inc(&memcg->moving_account); | 1541 | atomic_inc(&memcg->moving_account); |
1546 | synchronize_rcu(); | 1542 | synchronize_rcu(); |
1547 | } | 1543 | } |
@@ -1552,10 +1548,8 @@ static void mem_cgroup_end_move(struct mem_cgroup *memcg) | |||
1552 | * Now, mem_cgroup_clear_mc() may call this function with NULL. | 1548 | * Now, mem_cgroup_clear_mc() may call this function with NULL. |
1553 | * We check NULL in callee rather than caller. | 1549 | * We check NULL in callee rather than caller. |
1554 | */ | 1550 | */ |
1555 | if (memcg) { | 1551 | if (memcg) |
1556 | atomic_dec(&memcg_moving); | ||
1557 | atomic_dec(&memcg->moving_account); | 1552 | atomic_dec(&memcg->moving_account); |
1558 | } | ||
1559 | } | 1553 | } |
1560 | 1554 | ||
1561 | /* | 1555 | /* |
@@ -2204,41 +2198,52 @@ cleanup: | |||
2204 | return true; | 2198 | return true; |
2205 | } | 2199 | } |
2206 | 2200 | ||
2207 | /* | 2201 | /** |
2208 | * Used to update mapped file or writeback or other statistics. | 2202 | * mem_cgroup_begin_page_stat - begin a page state statistics transaction |
2203 | * @page: page that is going to change accounted state | ||
2204 | * @locked: &memcg->move_lock slowpath was taken | ||
2205 | * @flags: IRQ-state flags for &memcg->move_lock | ||
2209 | * | 2206 | * |
2210 | * Notes: Race condition | 2207 | * This function must mark the beginning of an accounted page state |
2208 | * change to prevent double accounting when the page is concurrently | ||
2209 | * being moved to another memcg: | ||
2211 | * | 2210 | * |
2212 | * Charging occurs during page instantiation, while the page is | 2211 | * memcg = mem_cgroup_begin_page_stat(page, &locked, &flags); |
2213 | * unmapped and locked in page migration, or while the page table is | 2212 | * if (TestClearPageState(page)) |
2214 | * locked in THP migration. No race is possible. | 2213 | * mem_cgroup_update_page_stat(memcg, state, -1); |
2214 | * mem_cgroup_end_page_stat(memcg, locked, flags); | ||
2215 | * | 2215 | * |
2216 | * Uncharge happens to pages with zero references, no race possible. | 2216 | * The RCU lock is held throughout the transaction. The fast path can |
2217 | * get away without acquiring the memcg->move_lock (@locked is false) | ||
2218 | * because page moving starts with an RCU grace period. | ||
2217 | * | 2219 | * |
2218 | * Charge moving between groups is protected by checking mm->moving | 2220 | * The RCU lock also protects the memcg from being freed when the page |
2219 | * account and taking the move_lock in the slowpath. | 2221 | * state that is going to change is the only thing preventing the page |
2222 | * from being uncharged. E.g. end-writeback clearing PageWriteback(), | ||
2223 | * which allows migration to go ahead and uncharge the page before the | ||
2224 | * account transaction might be complete. | ||
2220 | */ | 2225 | */ |
2221 | 2226 | struct mem_cgroup *mem_cgroup_begin_page_stat(struct page *page, | |
2222 | void __mem_cgroup_begin_update_page_stat(struct page *page, | 2227 | bool *locked, |
2223 | bool *locked, unsigned long *flags) | 2228 | unsigned long *flags) |
2224 | { | 2229 | { |
2225 | struct mem_cgroup *memcg; | 2230 | struct mem_cgroup *memcg; |
2226 | struct page_cgroup *pc; | 2231 | struct page_cgroup *pc; |
2227 | 2232 | ||
2233 | rcu_read_lock(); | ||
2234 | |||
2235 | if (mem_cgroup_disabled()) | ||
2236 | return NULL; | ||
2237 | |||
2228 | pc = lookup_page_cgroup(page); | 2238 | pc = lookup_page_cgroup(page); |
2229 | again: | 2239 | again: |
2230 | memcg = pc->mem_cgroup; | 2240 | memcg = pc->mem_cgroup; |
2231 | if (unlikely(!memcg || !PageCgroupUsed(pc))) | 2241 | if (unlikely(!memcg || !PageCgroupUsed(pc))) |
2232 | return; | 2242 | return NULL; |
2233 | /* | 2243 | |
2234 | * If this memory cgroup is not under account moving, we don't | 2244 | *locked = false; |
2235 | * need to take move_lock_mem_cgroup(). Because we already hold | ||
2236 | * rcu_read_lock(), any calls to move_account will be delayed until | ||
2237 | * rcu_read_unlock(). | ||
2238 | */ | ||
2239 | VM_BUG_ON(!rcu_read_lock_held()); | ||
2240 | if (atomic_read(&memcg->moving_account) <= 0) | 2245 | if (atomic_read(&memcg->moving_account) <= 0) |
2241 | return; | 2246 | return memcg; |
2242 | 2247 | ||
2243 | move_lock_mem_cgroup(memcg, flags); | 2248 | move_lock_mem_cgroup(memcg, flags); |
2244 | if (memcg != pc->mem_cgroup || !PageCgroupUsed(pc)) { | 2249 | if (memcg != pc->mem_cgroup || !PageCgroupUsed(pc)) { |
@@ -2246,36 +2251,40 @@ again: | |||
2246 | goto again; | 2251 | goto again; |
2247 | } | 2252 | } |
2248 | *locked = true; | 2253 | *locked = true; |
2254 | |||
2255 | return memcg; | ||
2249 | } | 2256 | } |
2250 | 2257 | ||
2251 | void __mem_cgroup_end_update_page_stat(struct page *page, unsigned long *flags) | 2258 | /** |
2259 | * mem_cgroup_end_page_stat - finish a page state statistics transaction | ||
2260 | * @memcg: the memcg that was accounted against | ||
2261 | * @locked: value received from mem_cgroup_begin_page_stat() | ||
2262 | * @flags: value received from mem_cgroup_begin_page_stat() | ||
2263 | */ | ||
2264 | void mem_cgroup_end_page_stat(struct mem_cgroup *memcg, bool locked, | ||
2265 | unsigned long flags) | ||
2252 | { | 2266 | { |
2253 | struct page_cgroup *pc = lookup_page_cgroup(page); | 2267 | if (memcg && locked) |
2268 | move_unlock_mem_cgroup(memcg, &flags); | ||
2254 | 2269 | ||
2255 | /* | 2270 | rcu_read_unlock(); |
2256 | * It's guaranteed that pc->mem_cgroup never changes while | ||
2257 | * lock is held because a routine modifies pc->mem_cgroup | ||
2258 | * should take move_lock_mem_cgroup(). | ||
2259 | */ | ||
2260 | move_unlock_mem_cgroup(pc->mem_cgroup, flags); | ||
2261 | } | 2271 | } |
2262 | 2272 | ||
2263 | void mem_cgroup_update_page_stat(struct page *page, | 2273 | /** |
2274 | * mem_cgroup_update_page_stat - update page state statistics | ||
2275 | * @memcg: memcg to account against | ||
2276 | * @idx: page state item to account | ||
2277 | * @val: number of pages (positive or negative) | ||
2278 | * | ||
2279 | * See mem_cgroup_begin_page_stat() for locking requirements. | ||
2280 | */ | ||
2281 | void mem_cgroup_update_page_stat(struct mem_cgroup *memcg, | ||
2264 | enum mem_cgroup_stat_index idx, int val) | 2282 | enum mem_cgroup_stat_index idx, int val) |
2265 | { | 2283 | { |
2266 | struct mem_cgroup *memcg; | ||
2267 | struct page_cgroup *pc = lookup_page_cgroup(page); | ||
2268 | unsigned long uninitialized_var(flags); | ||
2269 | |||
2270 | if (mem_cgroup_disabled()) | ||
2271 | return; | ||
2272 | |||
2273 | VM_BUG_ON(!rcu_read_lock_held()); | 2284 | VM_BUG_ON(!rcu_read_lock_held()); |
2274 | memcg = pc->mem_cgroup; | ||
2275 | if (unlikely(!memcg || !PageCgroupUsed(pc))) | ||
2276 | return; | ||
2277 | 2285 | ||
2278 | this_cpu_add(memcg->stat->count[idx], val); | 2286 | if (memcg) |
2287 | this_cpu_add(memcg->stat->count[idx], val); | ||
2279 | } | 2288 | } |
2280 | 2289 | ||
2281 | /* | 2290 | /* |
diff --git a/mm/page-writeback.c b/mm/page-writeback.c index ff6a5b07211e..19ceae87522d 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c | |||
@@ -2327,11 +2327,12 @@ EXPORT_SYMBOL(clear_page_dirty_for_io); | |||
2327 | int test_clear_page_writeback(struct page *page) | 2327 | int test_clear_page_writeback(struct page *page) |
2328 | { | 2328 | { |
2329 | struct address_space *mapping = page_mapping(page); | 2329 | struct address_space *mapping = page_mapping(page); |
2330 | int ret; | ||
2331 | bool locked; | ||
2332 | unsigned long memcg_flags; | 2330 | unsigned long memcg_flags; |
2331 | struct mem_cgroup *memcg; | ||
2332 | bool locked; | ||
2333 | int ret; | ||
2333 | 2334 | ||
2334 | mem_cgroup_begin_update_page_stat(page, &locked, &memcg_flags); | 2335 | memcg = mem_cgroup_begin_page_stat(page, &locked, &memcg_flags); |
2335 | if (mapping) { | 2336 | if (mapping) { |
2336 | struct backing_dev_info *bdi = mapping->backing_dev_info; | 2337 | struct backing_dev_info *bdi = mapping->backing_dev_info; |
2337 | unsigned long flags; | 2338 | unsigned long flags; |
@@ -2352,22 +2353,23 @@ int test_clear_page_writeback(struct page *page) | |||
2352 | ret = TestClearPageWriteback(page); | 2353 | ret = TestClearPageWriteback(page); |
2353 | } | 2354 | } |
2354 | if (ret) { | 2355 | if (ret) { |
2355 | mem_cgroup_dec_page_stat(page, MEM_CGROUP_STAT_WRITEBACK); | 2356 | mem_cgroup_dec_page_stat(memcg, MEM_CGROUP_STAT_WRITEBACK); |
2356 | dec_zone_page_state(page, NR_WRITEBACK); | 2357 | dec_zone_page_state(page, NR_WRITEBACK); |
2357 | inc_zone_page_state(page, NR_WRITTEN); | 2358 | inc_zone_page_state(page, NR_WRITTEN); |
2358 | } | 2359 | } |
2359 | mem_cgroup_end_update_page_stat(page, &locked, &memcg_flags); | 2360 | mem_cgroup_end_page_stat(memcg, locked, memcg_flags); |
2360 | return ret; | 2361 | return ret; |
2361 | } | 2362 | } |
2362 | 2363 | ||
2363 | int __test_set_page_writeback(struct page *page, bool keep_write) | 2364 | int __test_set_page_writeback(struct page *page, bool keep_write) |
2364 | { | 2365 | { |
2365 | struct address_space *mapping = page_mapping(page); | 2366 | struct address_space *mapping = page_mapping(page); |
2366 | int ret; | ||
2367 | bool locked; | ||
2368 | unsigned long memcg_flags; | 2367 | unsigned long memcg_flags; |
2368 | struct mem_cgroup *memcg; | ||
2369 | bool locked; | ||
2370 | int ret; | ||
2369 | 2371 | ||
2370 | mem_cgroup_begin_update_page_stat(page, &locked, &memcg_flags); | 2372 | memcg = mem_cgroup_begin_page_stat(page, &locked, &memcg_flags); |
2371 | if (mapping) { | 2373 | if (mapping) { |
2372 | struct backing_dev_info *bdi = mapping->backing_dev_info; | 2374 | struct backing_dev_info *bdi = mapping->backing_dev_info; |
2373 | unsigned long flags; | 2375 | unsigned long flags; |
@@ -2394,10 +2396,10 @@ int __test_set_page_writeback(struct page *page, bool keep_write) | |||
2394 | ret = TestSetPageWriteback(page); | 2396 | ret = TestSetPageWriteback(page); |
2395 | } | 2397 | } |
2396 | if (!ret) { | 2398 | if (!ret) { |
2397 | mem_cgroup_inc_page_stat(page, MEM_CGROUP_STAT_WRITEBACK); | 2399 | mem_cgroup_inc_page_stat(memcg, MEM_CGROUP_STAT_WRITEBACK); |
2398 | inc_zone_page_state(page, NR_WRITEBACK); | 2400 | inc_zone_page_state(page, NR_WRITEBACK); |
2399 | } | 2401 | } |
2400 | mem_cgroup_end_update_page_stat(page, &locked, &memcg_flags); | 2402 | mem_cgroup_end_page_stat(memcg, locked, memcg_flags); |
2401 | return ret; | 2403 | return ret; |
2402 | 2404 | ||
2403 | } | 2405 | } |
@@ -1042,15 +1042,16 @@ void page_add_new_anon_rmap(struct page *page, | |||
1042 | */ | 1042 | */ |
1043 | void page_add_file_rmap(struct page *page) | 1043 | void page_add_file_rmap(struct page *page) |
1044 | { | 1044 | { |
1045 | bool locked; | 1045 | struct mem_cgroup *memcg; |
1046 | unsigned long flags; | 1046 | unsigned long flags; |
1047 | bool locked; | ||
1047 | 1048 | ||
1048 | mem_cgroup_begin_update_page_stat(page, &locked, &flags); | 1049 | memcg = mem_cgroup_begin_page_stat(page, &locked, &flags); |
1049 | if (atomic_inc_and_test(&page->_mapcount)) { | 1050 | if (atomic_inc_and_test(&page->_mapcount)) { |
1050 | __inc_zone_page_state(page, NR_FILE_MAPPED); | 1051 | __inc_zone_page_state(page, NR_FILE_MAPPED); |
1051 | mem_cgroup_inc_page_stat(page, MEM_CGROUP_STAT_FILE_MAPPED); | 1052 | mem_cgroup_inc_page_stat(memcg, MEM_CGROUP_STAT_FILE_MAPPED); |
1052 | } | 1053 | } |
1053 | mem_cgroup_end_update_page_stat(page, &locked, &flags); | 1054 | mem_cgroup_end_page_stat(memcg, locked, flags); |
1054 | } | 1055 | } |
1055 | 1056 | ||
1056 | /** | 1057 | /** |
@@ -1061,9 +1062,10 @@ void page_add_file_rmap(struct page *page) | |||
1061 | */ | 1062 | */ |
1062 | void page_remove_rmap(struct page *page) | 1063 | void page_remove_rmap(struct page *page) |
1063 | { | 1064 | { |
1065 | struct mem_cgroup *uninitialized_var(memcg); | ||
1064 | bool anon = PageAnon(page); | 1066 | bool anon = PageAnon(page); |
1065 | bool locked; | ||
1066 | unsigned long flags; | 1067 | unsigned long flags; |
1068 | bool locked; | ||
1067 | 1069 | ||
1068 | /* | 1070 | /* |
1069 | * The anon case has no mem_cgroup page_stat to update; but may | 1071 | * The anon case has no mem_cgroup page_stat to update; but may |
@@ -1071,7 +1073,7 @@ void page_remove_rmap(struct page *page) | |||
1071 | * we hold the lock against page_stat move: so avoid it on anon. | 1073 | * we hold the lock against page_stat move: so avoid it on anon. |
1072 | */ | 1074 | */ |
1073 | if (!anon) | 1075 | if (!anon) |
1074 | mem_cgroup_begin_update_page_stat(page, &locked, &flags); | 1076 | memcg = mem_cgroup_begin_page_stat(page, &locked, &flags); |
1075 | 1077 | ||
1076 | /* page still mapped by someone else? */ | 1078 | /* page still mapped by someone else? */ |
1077 | if (!atomic_add_negative(-1, &page->_mapcount)) | 1079 | if (!atomic_add_negative(-1, &page->_mapcount)) |
@@ -1096,8 +1098,7 @@ void page_remove_rmap(struct page *page) | |||
1096 | -hpage_nr_pages(page)); | 1098 | -hpage_nr_pages(page)); |
1097 | } else { | 1099 | } else { |
1098 | __dec_zone_page_state(page, NR_FILE_MAPPED); | 1100 | __dec_zone_page_state(page, NR_FILE_MAPPED); |
1099 | mem_cgroup_dec_page_stat(page, MEM_CGROUP_STAT_FILE_MAPPED); | 1101 | mem_cgroup_dec_page_stat(memcg, MEM_CGROUP_STAT_FILE_MAPPED); |
1100 | mem_cgroup_end_update_page_stat(page, &locked, &flags); | ||
1101 | } | 1102 | } |
1102 | if (unlikely(PageMlocked(page))) | 1103 | if (unlikely(PageMlocked(page))) |
1103 | clear_page_mlock(page); | 1104 | clear_page_mlock(page); |
@@ -1110,10 +1111,9 @@ void page_remove_rmap(struct page *page) | |||
1110 | * Leaving it set also helps swapoff to reinstate ptes | 1111 | * Leaving it set also helps swapoff to reinstate ptes |
1111 | * faster for those pages still in swapcache. | 1112 | * faster for those pages still in swapcache. |
1112 | */ | 1113 | */ |
1113 | return; | ||
1114 | out: | 1114 | out: |
1115 | if (!anon) | 1115 | if (!anon) |
1116 | mem_cgroup_end_update_page_stat(page, &locked, &flags); | 1116 | mem_cgroup_end_page_stat(memcg, locked, flags); |
1117 | } | 1117 | } |
1118 | 1118 | ||
1119 | /* | 1119 | /* |