diff options
Diffstat (limited to 'mm/memcontrol.c')
-rw-r--r-- | mm/memcontrol.c | 96 |
1 files changed, 59 insertions, 37 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 53eb30ebdb49..e5277e8a42a8 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -266,6 +266,12 @@ static struct move_charge_struct { | |||
266 | .waitq = __WAIT_QUEUE_HEAD_INITIALIZER(mc.waitq), | 266 | .waitq = __WAIT_QUEUE_HEAD_INITIALIZER(mc.waitq), |
267 | }; | 267 | }; |
268 | 268 | ||
269 | static bool move_anon(void) | ||
270 | { | ||
271 | return test_bit(MOVE_CHARGE_TYPE_ANON, | ||
272 | &mc.to->move_charge_at_immigrate); | ||
273 | } | ||
274 | |||
269 | /* | 275 | /* |
270 | * Maximum loops in mem_cgroup_hierarchical_reclaim(), used for soft | 276 | * Maximum loops in mem_cgroup_hierarchical_reclaim(), used for soft |
271 | * limit reclaim to prevent infinite loops, if they ever occur. | 277 | * limit reclaim to prevent infinite loops, if they ever occur. |
@@ -4162,50 +4168,66 @@ enum mc_target_type { | |||
4162 | MC_TARGET_SWAP, | 4168 | MC_TARGET_SWAP, |
4163 | }; | 4169 | }; |
4164 | 4170 | ||
4165 | static int is_target_pte_for_mc(struct vm_area_struct *vma, | 4171 | static struct page *mc_handle_present_pte(struct vm_area_struct *vma, |
4166 | unsigned long addr, pte_t ptent, union mc_target *target) | 4172 | unsigned long addr, pte_t ptent) |
4167 | { | 4173 | { |
4168 | struct page *page = NULL; | 4174 | struct page *page = vm_normal_page(vma, addr, ptent); |
4169 | struct page_cgroup *pc; | ||
4170 | int ret = 0; | ||
4171 | swp_entry_t ent = { .val = 0 }; | ||
4172 | int usage_count = 0; | ||
4173 | bool move_anon = test_bit(MOVE_CHARGE_TYPE_ANON, | ||
4174 | &mc.to->move_charge_at_immigrate); | ||
4175 | 4175 | ||
4176 | if (!pte_present(ptent)) { | 4176 | if (!page || !page_mapped(page)) |
4177 | /* TODO: handle swap of shmes/tmpfs */ | 4177 | return NULL; |
4178 | if (pte_none(ptent) || pte_file(ptent)) | 4178 | if (PageAnon(page)) { |
4179 | return 0; | 4179 | /* we don't move shared anon */ |
4180 | else if (is_swap_pte(ptent)) { | 4180 | if (!move_anon() || page_mapcount(page) > 2) |
4181 | ent = pte_to_swp_entry(ptent); | 4181 | return NULL; |
4182 | if (!move_anon || non_swap_entry(ent)) | 4182 | } else |
4183 | return 0; | ||
4184 | usage_count = mem_cgroup_count_swap_user(ent, &page); | ||
4185 | } | ||
4186 | } else { | ||
4187 | page = vm_normal_page(vma, addr, ptent); | ||
4188 | if (!page || !page_mapped(page)) | ||
4189 | return 0; | ||
4190 | /* | 4183 | /* |
4191 | * TODO: We don't move charges of file(including shmem/tmpfs) | 4184 | * TODO: We don't move charges of file(including shmem/tmpfs) |
4192 | * pages for now. | 4185 | * pages for now. |
4193 | */ | 4186 | */ |
4194 | if (!move_anon || !PageAnon(page)) | 4187 | return NULL; |
4195 | return 0; | 4188 | if (!get_page_unless_zero(page)) |
4196 | if (!get_page_unless_zero(page)) | 4189 | return NULL; |
4197 | return 0; | 4190 | |
4198 | usage_count = page_mapcount(page); | 4191 | return page; |
4199 | } | 4192 | } |
4200 | if (usage_count > 1) { | 4193 | |
4201 | /* | 4194 | static struct page *mc_handle_swap_pte(struct vm_area_struct *vma, |
4202 | * TODO: We don't move charges of shared(used by multiple | 4195 | unsigned long addr, pte_t ptent, swp_entry_t *entry) |
4203 | * processes) pages for now. | 4196 | { |
4204 | */ | 4197 | int usage_count; |
4198 | struct page *page = NULL; | ||
4199 | swp_entry_t ent = pte_to_swp_entry(ptent); | ||
4200 | |||
4201 | if (!move_anon() || non_swap_entry(ent)) | ||
4202 | return NULL; | ||
4203 | usage_count = mem_cgroup_count_swap_user(ent, &page); | ||
4204 | if (usage_count > 1) { /* we don't move shared anon */ | ||
4205 | if (page) | 4205 | if (page) |
4206 | put_page(page); | 4206 | put_page(page); |
4207 | return 0; | 4207 | return NULL; |
4208 | } | 4208 | } |
4209 | if (do_swap_account) | ||
4210 | entry->val = ent.val; | ||
4211 | |||
4212 | return page; | ||
4213 | } | ||
4214 | |||
4215 | static int is_target_pte_for_mc(struct vm_area_struct *vma, | ||
4216 | unsigned long addr, pte_t ptent, union mc_target *target) | ||
4217 | { | ||
4218 | struct page *page = NULL; | ||
4219 | struct page_cgroup *pc; | ||
4220 | int ret = 0; | ||
4221 | swp_entry_t ent = { .val = 0 }; | ||
4222 | |||
4223 | if (pte_present(ptent)) | ||
4224 | page = mc_handle_present_pte(vma, addr, ptent); | ||
4225 | else if (is_swap_pte(ptent)) | ||
4226 | page = mc_handle_swap_pte(vma, addr, ptent, &ent); | ||
4227 | /* TODO: handle swap of shmes/tmpfs */ | ||
4228 | |||
4229 | if (!page && !ent.val) | ||
4230 | return 0; | ||
4209 | if (page) { | 4231 | if (page) { |
4210 | pc = lookup_page_cgroup(page); | 4232 | pc = lookup_page_cgroup(page); |
4211 | /* | 4233 | /* |
@@ -4221,8 +4243,8 @@ static int is_target_pte_for_mc(struct vm_area_struct *vma, | |||
4221 | if (!ret || !target) | 4243 | if (!ret || !target) |
4222 | put_page(page); | 4244 | put_page(page); |
4223 | } | 4245 | } |
4224 | /* throught */ | 4246 | /* There is a swap entry and a page doesn't exist or isn't charged */ |
4225 | if (ent.val && do_swap_account && !ret && | 4247 | if (ent.val && !ret && |
4226 | css_id(&mc.from->css) == lookup_swap_cgroup(ent)) { | 4248 | css_id(&mc.from->css) == lookup_swap_cgroup(ent)) { |
4227 | ret = MC_TARGET_SWAP; | 4249 | ret = MC_TARGET_SWAP; |
4228 | if (target) | 4250 | if (target) |