diff options
-rw-r--r-- | include/linux/memcontrol.h | 35 | ||||
-rw-r--r-- | mm/memcontrol.c | 62 | ||||
-rw-r--r-- | mm/rmap.c | 28 |
3 files changed, 101 insertions, 24 deletions
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index c54e5dfa1962..bf7ae01fc93b 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h | |||
@@ -141,6 +141,31 @@ static inline bool mem_cgroup_disabled(void) | |||
141 | return false; | 141 | return false; |
142 | } | 142 | } |
143 | 143 | ||
144 | void __mem_cgroup_begin_update_page_stat(struct page *page, bool *locked, | ||
145 | unsigned long *flags); | ||
146 | |||
147 | static inline void mem_cgroup_begin_update_page_stat(struct page *page, | ||
148 | bool *locked, unsigned long *flags) | ||
149 | { | ||
150 | if (mem_cgroup_disabled()) | ||
151 | return; | ||
152 | rcu_read_lock(); | ||
153 | *locked = false; | ||
154 | return __mem_cgroup_begin_update_page_stat(page, locked, flags); | ||
155 | } | ||
156 | |||
157 | void __mem_cgroup_end_update_page_stat(struct page *page, | ||
158 | unsigned long *flags); | ||
159 | static inline void mem_cgroup_end_update_page_stat(struct page *page, | ||
160 | bool *locked, unsigned long *flags) | ||
161 | { | ||
162 | if (mem_cgroup_disabled()) | ||
163 | return; | ||
164 | if (*locked) | ||
165 | __mem_cgroup_end_update_page_stat(page, flags); | ||
166 | rcu_read_unlock(); | ||
167 | } | ||
168 | |||
144 | void mem_cgroup_update_page_stat(struct page *page, | 169 | void mem_cgroup_update_page_stat(struct page *page, |
145 | enum mem_cgroup_page_stat_item idx, | 170 | enum mem_cgroup_page_stat_item idx, |
146 | int val); | 171 | int val); |
@@ -341,6 +366,16 @@ mem_cgroup_print_oom_info(struct mem_cgroup *memcg, struct task_struct *p) | |||
341 | { | 366 | { |
342 | } | 367 | } |
343 | 368 | ||
369 | static inline void mem_cgroup_begin_update_page_stat(struct page *page, | ||
370 | bool *locked, unsigned long *flags) | ||
371 | { | ||
372 | } | ||
373 | |||
374 | static inline void mem_cgroup_end_update_page_stat(struct page *page, | ||
375 | bool *locked, unsigned long *flags) | ||
376 | { | ||
377 | } | ||
378 | |||
344 | static inline void mem_cgroup_inc_page_stat(struct page *page, | 379 | static inline void mem_cgroup_inc_page_stat(struct page *page, |
345 | enum mem_cgroup_page_stat_item idx) | 380 | enum mem_cgroup_page_stat_item idx) |
346 | { | 381 | { |
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 8afed2819b8f..df1e180f6c30 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -1910,32 +1910,59 @@ bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, int order) | |||
1910 | * If there is, we take a lock. | 1910 | * If there is, we take a lock. |
1911 | */ | 1911 | */ |
1912 | 1912 | ||
1913 | void __mem_cgroup_begin_update_page_stat(struct page *page, | ||
1914 | bool *locked, unsigned long *flags) | ||
1915 | { | ||
1916 | struct mem_cgroup *memcg; | ||
1917 | struct page_cgroup *pc; | ||
1918 | |||
1919 | pc = lookup_page_cgroup(page); | ||
1920 | again: | ||
1921 | memcg = pc->mem_cgroup; | ||
1922 | if (unlikely(!memcg || !PageCgroupUsed(pc))) | ||
1923 | return; | ||
1924 | /* | ||
1925 | * If this memory cgroup is not under account moving, we don't | ||
1926 | * need to take move_lock_page_cgroup(). Because we already hold | ||
1927 | * rcu_read_lock(), any calls to move_account will be delayed until | ||
1928 | * rcu_read_unlock() if mem_cgroup_stealed() == true. | ||
1929 | */ | ||
1930 | if (!mem_cgroup_stealed(memcg)) | ||
1931 | return; | ||
1932 | |||
1933 | move_lock_mem_cgroup(memcg, flags); | ||
1934 | if (memcg != pc->mem_cgroup || !PageCgroupUsed(pc)) { | ||
1935 | move_unlock_mem_cgroup(memcg, flags); | ||
1936 | goto again; | ||
1937 | } | ||
1938 | *locked = true; | ||
1939 | } | ||
1940 | |||
1941 | void __mem_cgroup_end_update_page_stat(struct page *page, unsigned long *flags) | ||
1942 | { | ||
1943 | struct page_cgroup *pc = lookup_page_cgroup(page); | ||
1944 | |||
1945 | /* | ||
1946 | * It's guaranteed that pc->mem_cgroup never changes while | ||
1947 | * lock is held because a routine modifies pc->mem_cgroup | ||
1948 | * should take move_lock_page_cgroup(). | ||
1949 | */ | ||
1950 | move_unlock_mem_cgroup(pc->mem_cgroup, flags); | ||
1951 | } | ||
1952 | |||
1913 | void mem_cgroup_update_page_stat(struct page *page, | 1953 | void mem_cgroup_update_page_stat(struct page *page, |
1914 | enum mem_cgroup_page_stat_item idx, int val) | 1954 | enum mem_cgroup_page_stat_item idx, int val) |
1915 | { | 1955 | { |
1916 | struct mem_cgroup *memcg; | 1956 | struct mem_cgroup *memcg; |
1917 | struct page_cgroup *pc = lookup_page_cgroup(page); | 1957 | struct page_cgroup *pc = lookup_page_cgroup(page); |
1918 | bool need_unlock = false; | ||
1919 | unsigned long uninitialized_var(flags); | 1958 | unsigned long uninitialized_var(flags); |
1920 | 1959 | ||
1921 | if (mem_cgroup_disabled()) | 1960 | if (mem_cgroup_disabled()) |
1922 | return; | 1961 | return; |
1923 | again: | 1962 | |
1924 | rcu_read_lock(); | ||
1925 | memcg = pc->mem_cgroup; | 1963 | memcg = pc->mem_cgroup; |
1926 | if (unlikely(!memcg || !PageCgroupUsed(pc))) | 1964 | if (unlikely(!memcg || !PageCgroupUsed(pc))) |
1927 | goto out; | 1965 | return; |
1928 | /* pc->mem_cgroup is unstable ? */ | ||
1929 | if (unlikely(mem_cgroup_stealed(memcg))) { | ||
1930 | /* take a lock against to access pc->mem_cgroup */ | ||
1931 | move_lock_mem_cgroup(memcg, &flags); | ||
1932 | if (memcg != pc->mem_cgroup || !PageCgroupUsed(pc)) { | ||
1933 | move_unlock_mem_cgroup(memcg, &flags); | ||
1934 | rcu_read_unlock(); | ||
1935 | goto again; | ||
1936 | } | ||
1937 | need_unlock = true; | ||
1938 | } | ||
1939 | 1966 | ||
1940 | switch (idx) { | 1967 | switch (idx) { |
1941 | case MEMCG_NR_FILE_MAPPED: | 1968 | case MEMCG_NR_FILE_MAPPED: |
@@ -1950,11 +1977,6 @@ again: | |||
1950 | } | 1977 | } |
1951 | 1978 | ||
1952 | this_cpu_add(memcg->stat->count[idx], val); | 1979 | this_cpu_add(memcg->stat->count[idx], val); |
1953 | |||
1954 | out: | ||
1955 | if (unlikely(need_unlock)) | ||
1956 | move_unlock_mem_cgroup(memcg, &flags); | ||
1957 | rcu_read_unlock(); | ||
1958 | } | 1980 | } |
1959 | 1981 | ||
1960 | /* | 1982 | /* |
@@ -1148,10 +1148,15 @@ void page_add_new_anon_rmap(struct page *page, | |||
1148 | */ | 1148 | */ |
1149 | void page_add_file_rmap(struct page *page) | 1149 | void page_add_file_rmap(struct page *page) |
1150 | { | 1150 | { |
1151 | bool locked; | ||
1152 | unsigned long flags; | ||
1153 | |||
1154 | mem_cgroup_begin_update_page_stat(page, &locked, &flags); | ||
1151 | if (atomic_inc_and_test(&page->_mapcount)) { | 1155 | if (atomic_inc_and_test(&page->_mapcount)) { |
1152 | __inc_zone_page_state(page, NR_FILE_MAPPED); | 1156 | __inc_zone_page_state(page, NR_FILE_MAPPED); |
1153 | mem_cgroup_inc_page_stat(page, MEMCG_NR_FILE_MAPPED); | 1157 | mem_cgroup_inc_page_stat(page, MEMCG_NR_FILE_MAPPED); |
1154 | } | 1158 | } |
1159 | mem_cgroup_end_update_page_stat(page, &locked, &flags); | ||
1155 | } | 1160 | } |
1156 | 1161 | ||
1157 | /** | 1162 | /** |
@@ -1162,9 +1167,21 @@ void page_add_file_rmap(struct page *page) | |||
1162 | */ | 1167 | */ |
1163 | void page_remove_rmap(struct page *page) | 1168 | void page_remove_rmap(struct page *page) |
1164 | { | 1169 | { |
1170 | bool anon = PageAnon(page); | ||
1171 | bool locked; | ||
1172 | unsigned long flags; | ||
1173 | |||
1174 | /* | ||
1175 | * The anon case has no mem_cgroup page_stat to update; but may | ||
1176 | * uncharge_page() below, where the lock ordering can deadlock if | ||
1177 | * we hold the lock against page_stat move: so avoid it on anon. | ||
1178 | */ | ||
1179 | if (!anon) | ||
1180 | mem_cgroup_begin_update_page_stat(page, &locked, &flags); | ||
1181 | |||
1165 | /* page still mapped by someone else? */ | 1182 | /* page still mapped by someone else? */ |
1166 | if (!atomic_add_negative(-1, &page->_mapcount)) | 1183 | if (!atomic_add_negative(-1, &page->_mapcount)) |
1167 | return; | 1184 | goto out; |
1168 | 1185 | ||
1169 | /* | 1186 | /* |
1170 | * Now that the last pte has gone, s390 must transfer dirty | 1187 | * Now that the last pte has gone, s390 must transfer dirty |
@@ -1173,7 +1190,7 @@ void page_remove_rmap(struct page *page) | |||
1173 | * not if it's in swapcache - there might be another pte slot | 1190 | * not if it's in swapcache - there might be another pte slot |
1174 | * containing the swap entry, but page not yet written to swap. | 1191 | * containing the swap entry, but page not yet written to swap. |
1175 | */ | 1192 | */ |
1176 | if ((!PageAnon(page) || PageSwapCache(page)) && | 1193 | if ((!anon || PageSwapCache(page)) && |
1177 | page_test_and_clear_dirty(page_to_pfn(page), 1)) | 1194 | page_test_and_clear_dirty(page_to_pfn(page), 1)) |
1178 | set_page_dirty(page); | 1195 | set_page_dirty(page); |
1179 | /* | 1196 | /* |
@@ -1181,8 +1198,8 @@ void page_remove_rmap(struct page *page) | |||
1181 | * and not charged by memcg for now. | 1198 | * and not charged by memcg for now. |
1182 | */ | 1199 | */ |
1183 | if (unlikely(PageHuge(page))) | 1200 | if (unlikely(PageHuge(page))) |
1184 | return; | 1201 | goto out; |
1185 | if (PageAnon(page)) { | 1202 | if (anon) { |
1186 | mem_cgroup_uncharge_page(page); | 1203 | mem_cgroup_uncharge_page(page); |
1187 | if (!PageTransHuge(page)) | 1204 | if (!PageTransHuge(page)) |
1188 | __dec_zone_page_state(page, NR_ANON_PAGES); | 1205 | __dec_zone_page_state(page, NR_ANON_PAGES); |
@@ -1202,6 +1219,9 @@ void page_remove_rmap(struct page *page) | |||
1202 | * Leaving it set also helps swapoff to reinstate ptes | 1219 | * Leaving it set also helps swapoff to reinstate ptes |
1203 | * faster for those pages still in swapcache. | 1220 | * faster for those pages still in swapcache. |
1204 | */ | 1221 | */ |
1222 | out: | ||
1223 | if (!anon) | ||
1224 | mem_cgroup_end_update_page_stat(page, &locked, &flags); | ||
1205 | } | 1225 | } |
1206 | 1226 | ||
1207 | /* | 1227 | /* |