diff options
| -rw-r--r-- | mm/memcontrol.c | 37 |
1 files changed, 26 insertions, 11 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 7a94ef6b35e2..5b562b375cbd 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
| @@ -2197,8 +2197,11 @@ void mem_cgroup_split_huge_fixup(struct page *head, struct page *tail) | |||
| 2197 | */ | 2197 | */ |
| 2198 | 2198 | ||
| 2199 | static void __mem_cgroup_move_account(struct page_cgroup *pc, | 2199 | static void __mem_cgroup_move_account(struct page_cgroup *pc, |
| 2200 | struct mem_cgroup *from, struct mem_cgroup *to, bool uncharge) | 2200 | struct mem_cgroup *from, struct mem_cgroup *to, bool uncharge, |
| 2201 | int charge_size) | ||
| 2201 | { | 2202 | { |
| 2203 | int nr_pages = charge_size >> PAGE_SHIFT; | ||
| 2204 | |||
| 2202 | VM_BUG_ON(from == to); | 2205 | VM_BUG_ON(from == to); |
| 2203 | VM_BUG_ON(PageLRU(pc->page)); | 2206 | VM_BUG_ON(PageLRU(pc->page)); |
| 2204 | VM_BUG_ON(!page_is_cgroup_locked(pc)); | 2207 | VM_BUG_ON(!page_is_cgroup_locked(pc)); |
| @@ -2212,14 +2215,14 @@ static void __mem_cgroup_move_account(struct page_cgroup *pc, | |||
| 2212 | __this_cpu_inc(to->stat->count[MEM_CGROUP_STAT_FILE_MAPPED]); | 2215 | __this_cpu_inc(to->stat->count[MEM_CGROUP_STAT_FILE_MAPPED]); |
| 2213 | preempt_enable(); | 2216 | preempt_enable(); |
| 2214 | } | 2217 | } |
| 2215 | mem_cgroup_charge_statistics(from, PageCgroupCache(pc), -1); | 2218 | mem_cgroup_charge_statistics(from, PageCgroupCache(pc), -nr_pages); |
| 2216 | if (uncharge) | 2219 | if (uncharge) |
| 2217 | /* This is not "cancel", but cancel_charge does all we need. */ | 2220 | /* This is not "cancel", but cancel_charge does all we need. */ |
| 2218 | mem_cgroup_cancel_charge(from, PAGE_SIZE); | 2221 | mem_cgroup_cancel_charge(from, charge_size); |
| 2219 | 2222 | ||
| 2220 | /* caller should have done css_get */ | 2223 | /* caller should have done css_get */ |
| 2221 | pc->mem_cgroup = to; | 2224 | pc->mem_cgroup = to; |
| 2222 | mem_cgroup_charge_statistics(to, PageCgroupCache(pc), 1); | 2225 | mem_cgroup_charge_statistics(to, PageCgroupCache(pc), nr_pages); |
| 2223 | /* | 2226 | /* |
| 2224 | * We charges against "to" which may not have any tasks. Then, "to" | 2227 | * We charges against "to" which may not have any tasks. Then, "to" |
| 2225 | * can be under rmdir(). But in current implementation, caller of | 2228 | * can be under rmdir(). But in current implementation, caller of |
| @@ -2234,15 +2237,19 @@ static void __mem_cgroup_move_account(struct page_cgroup *pc, | |||
| 2234 | * __mem_cgroup_move_account() | 2237 | * __mem_cgroup_move_account() |
| 2235 | */ | 2238 | */ |
| 2236 | static int mem_cgroup_move_account(struct page_cgroup *pc, | 2239 | static int mem_cgroup_move_account(struct page_cgroup *pc, |
| 2237 | struct mem_cgroup *from, struct mem_cgroup *to, bool uncharge) | 2240 | struct mem_cgroup *from, struct mem_cgroup *to, |
| 2241 | bool uncharge, int charge_size) | ||
| 2238 | { | 2242 | { |
| 2239 | int ret = -EINVAL; | 2243 | int ret = -EINVAL; |
| 2240 | unsigned long flags; | 2244 | unsigned long flags; |
| 2241 | 2245 | ||
| 2246 | if ((charge_size > PAGE_SIZE) && !PageTransHuge(pc->page)) | ||
| 2247 | return -EBUSY; | ||
| 2248 | |||
| 2242 | lock_page_cgroup(pc); | 2249 | lock_page_cgroup(pc); |
| 2243 | if (PageCgroupUsed(pc) && pc->mem_cgroup == from) { | 2250 | if (PageCgroupUsed(pc) && pc->mem_cgroup == from) { |
| 2244 | move_lock_page_cgroup(pc, &flags); | 2251 | move_lock_page_cgroup(pc, &flags); |
| 2245 | __mem_cgroup_move_account(pc, from, to, uncharge); | 2252 | __mem_cgroup_move_account(pc, from, to, uncharge, charge_size); |
| 2246 | move_unlock_page_cgroup(pc, &flags); | 2253 | move_unlock_page_cgroup(pc, &flags); |
| 2247 | ret = 0; | 2254 | ret = 0; |
| 2248 | } | 2255 | } |
| @@ -2267,6 +2274,8 @@ static int mem_cgroup_move_parent(struct page_cgroup *pc, | |||
| 2267 | struct cgroup *cg = child->css.cgroup; | 2274 | struct cgroup *cg = child->css.cgroup; |
| 2268 | struct cgroup *pcg = cg->parent; | 2275 | struct cgroup *pcg = cg->parent; |
| 2269 | struct mem_cgroup *parent; | 2276 | struct mem_cgroup *parent; |
| 2277 | int charge = PAGE_SIZE; | ||
| 2278 | unsigned long flags; | ||
| 2270 | int ret; | 2279 | int ret; |
| 2271 | 2280 | ||
| 2272 | /* Is ROOT ? */ | 2281 | /* Is ROOT ? */ |
| @@ -2278,17 +2287,23 @@ static int mem_cgroup_move_parent(struct page_cgroup *pc, | |||
| 2278 | goto out; | 2287 | goto out; |
| 2279 | if (isolate_lru_page(page)) | 2288 | if (isolate_lru_page(page)) |
| 2280 | goto put; | 2289 | goto put; |
| 2290 | /* The page is isolated from LRU and we have no race with splitting */ | ||
| 2291 | charge = PAGE_SIZE << compound_order(page); | ||
| 2281 | 2292 | ||
| 2282 | parent = mem_cgroup_from_cont(pcg); | 2293 | parent = mem_cgroup_from_cont(pcg); |
| 2283 | ret = __mem_cgroup_try_charge(NULL, gfp_mask, &parent, false, | 2294 | ret = __mem_cgroup_try_charge(NULL, gfp_mask, &parent, false, charge); |
| 2284 | PAGE_SIZE); | ||
| 2285 | if (ret || !parent) | 2295 | if (ret || !parent) |
| 2286 | goto put_back; | 2296 | goto put_back; |
| 2287 | 2297 | ||
| 2288 | ret = mem_cgroup_move_account(pc, child, parent, true); | 2298 | if (charge > PAGE_SIZE) |
| 2299 | flags = compound_lock_irqsave(page); | ||
| 2300 | |||
| 2301 | ret = mem_cgroup_move_account(pc, child, parent, true, charge); | ||
| 2289 | if (ret) | 2302 | if (ret) |
| 2290 | mem_cgroup_cancel_charge(parent, PAGE_SIZE); | 2303 | mem_cgroup_cancel_charge(parent, charge); |
| 2291 | put_back: | 2304 | put_back: |
| 2305 | if (charge > PAGE_SIZE) | ||
| 2306 | compound_unlock_irqrestore(page, flags); | ||
| 2292 | putback_lru_page(page); | 2307 | putback_lru_page(page); |
| 2293 | put: | 2308 | put: |
| 2294 | put_page(page); | 2309 | put_page(page); |
| @@ -4868,7 +4883,7 @@ retry: | |||
| 4868 | goto put; | 4883 | goto put; |
| 4869 | pc = lookup_page_cgroup(page); | 4884 | pc = lookup_page_cgroup(page); |
| 4870 | if (!mem_cgroup_move_account(pc, | 4885 | if (!mem_cgroup_move_account(pc, |
| 4871 | mc.from, mc.to, false)) { | 4886 | mc.from, mc.to, false, PAGE_SIZE)) { |
| 4872 | mc.precharge--; | 4887 | mc.precharge--; |
| 4873 | /* we uncharge from mc.from later. */ | 4888 | /* we uncharge from mc.from later. */ |
| 4874 | mc.moved_charge++; | 4889 | mc.moved_charge++; |
