diff options
| author | Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp> | 2010-03-10 18:22:17 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-12 18:52:36 -0500 |
| commit | 024914477e15ef8b17f271ec47f1bb8a589f0806 (patch) | |
| tree | 9a6a8b4224c94fcdd1b8c3127b301ee3537f8cc2 | |
| parent | 8033b97c9b5ef063e3f4bf2efe1cd0a22093aaff (diff) | |
memcg: move charges of anonymous swap
This patch is another core part of this move-charge-at-task-migration
feature. It enables moving charges of anonymous swaps.
To move the charge of swap, we need to exchange swap_cgroup's record.
In current implementation, swap_cgroup's record is protected by:
- page lock: if the entry is on swap cache.
- swap_lock: if the entry is not on swap cache.
This works well in usual swap-in/out activity.
But this behavior make the feature of moving swap charge check many
conditions to exchange swap_cgroup's record safely.
So I changed modification of swap_cgroup's recored(swap_cgroup_record())
to use xchg, and define a new function to cmpxchg swap_cgroup's record.
This patch also enables moving charge of non pte_present but not uncharged
swap caches, which can be exist on swap-out path, by getting the target
pages via find_get_page() as do_mincore() does.
[kosaki.motohiro@jp.fujitsu.com: fix ia64 build]
[akpm@linux-foundation.org: fix typos]
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@linux.vnet.ibm.com>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Paul Menage <menage@google.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | Documentation/cgroups/memory.txt | 2 | ||||
| -rw-r--r-- | include/linux/page_cgroup.h | 2 | ||||
| -rw-r--r-- | include/linux/swap.h | 9 | ||||
| -rw-r--r-- | mm/memcontrol.c | 183 | ||||
| -rw-r--r-- | mm/page_cgroup.c | 34 | ||||
| -rw-r--r-- | mm/swapfile.c | 31 |
6 files changed, 223 insertions, 38 deletions
diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt index e726fb0df719..1f59a1a38bd9 100644 --- a/Documentation/cgroups/memory.txt +++ b/Documentation/cgroups/memory.txt | |||
| @@ -420,6 +420,8 @@ NOTE2: It is recommended to set the soft limit always below the hard limit, | |||
| 420 | 420 | ||
| 421 | Users can move charges associated with a task along with task migration, that | 421 | Users can move charges associated with a task along with task migration, that |
| 422 | is, uncharge task's pages from the old cgroup and charge them to the new cgroup. | 422 | is, uncharge task's pages from the old cgroup and charge them to the new cgroup. |
| 423 | This feature is not supported in !CONFIG_MMU environments because of lack of | ||
| 424 | page tables. | ||
| 423 | 425 | ||
| 424 | 8.1 Interface | 426 | 8.1 Interface |
| 425 | 427 | ||
diff --git a/include/linux/page_cgroup.h b/include/linux/page_cgroup.h index b0e4eb126236..30b08136fdf3 100644 --- a/include/linux/page_cgroup.h +++ b/include/linux/page_cgroup.h | |||
| @@ -118,6 +118,8 @@ static inline void __init page_cgroup_init_flatmem(void) | |||
| 118 | #include <linux/swap.h> | 118 | #include <linux/swap.h> |
| 119 | 119 | ||
| 120 | #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP | 120 | #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP |
| 121 | extern unsigned short swap_cgroup_cmpxchg(swp_entry_t ent, | ||
| 122 | unsigned short old, unsigned short new); | ||
| 121 | extern unsigned short swap_cgroup_record(swp_entry_t ent, unsigned short id); | 123 | extern unsigned short swap_cgroup_record(swp_entry_t ent, unsigned short id); |
| 122 | extern unsigned short lookup_swap_cgroup(swp_entry_t ent); | 124 | extern unsigned short lookup_swap_cgroup(swp_entry_t ent); |
| 123 | extern int swap_cgroup_swapon(int type, unsigned long max_pages); | 125 | extern int swap_cgroup_swapon(int type, unsigned long max_pages); |
diff --git a/include/linux/swap.h b/include/linux/swap.h index a2602a8207a6..1f59d9340c4d 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h | |||
| @@ -355,6 +355,7 @@ static inline void disable_swap_token(void) | |||
| 355 | #ifdef CONFIG_CGROUP_MEM_RES_CTLR | 355 | #ifdef CONFIG_CGROUP_MEM_RES_CTLR |
| 356 | extern void | 356 | extern void |
| 357 | mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout); | 357 | mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout); |
| 358 | extern int mem_cgroup_count_swap_user(swp_entry_t ent, struct page **pagep); | ||
| 358 | #else | 359 | #else |
| 359 | static inline void | 360 | static inline void |
| 360 | mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout) | 361 | mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout) |
| @@ -485,6 +486,14 @@ mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent) | |||
| 485 | { | 486 | { |
| 486 | } | 487 | } |
| 487 | 488 | ||
| 489 | #ifdef CONFIG_CGROUP_MEM_RES_CTLR | ||
| 490 | static inline int | ||
| 491 | mem_cgroup_count_swap_user(swp_entry_t ent, struct page **pagep) | ||
| 492 | { | ||
| 493 | return 0; | ||
| 494 | } | ||
| 495 | #endif | ||
| 496 | |||
| 488 | #endif /* CONFIG_SWAP */ | 497 | #endif /* CONFIG_SWAP */ |
| 489 | #endif /* __KERNEL__*/ | 498 | #endif /* __KERNEL__*/ |
| 490 | #endif /* _LINUX_SWAP_H */ | 499 | #endif /* _LINUX_SWAP_H */ |
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 589084f00b70..e883198baf81 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
| @@ -33,6 +33,7 @@ | |||
| 33 | #include <linux/rbtree.h> | 33 | #include <linux/rbtree.h> |
| 34 | #include <linux/slab.h> | 34 | #include <linux/slab.h> |
| 35 | #include <linux/swap.h> | 35 | #include <linux/swap.h> |
| 36 | #include <linux/swapops.h> | ||
| 36 | #include <linux/spinlock.h> | 37 | #include <linux/spinlock.h> |
| 37 | #include <linux/fs.h> | 38 | #include <linux/fs.h> |
| 38 | #include <linux/seq_file.h> | 39 | #include <linux/seq_file.h> |
| @@ -2270,6 +2271,54 @@ void mem_cgroup_uncharge_swap(swp_entry_t ent) | |||
| 2270 | } | 2271 | } |
| 2271 | rcu_read_unlock(); | 2272 | rcu_read_unlock(); |
| 2272 | } | 2273 | } |
| 2274 | |||
| 2275 | /** | ||
| 2276 | * mem_cgroup_move_swap_account - move swap charge and swap_cgroup's record. | ||
| 2277 | * @entry: swap entry to be moved | ||
| 2278 | * @from: mem_cgroup which the entry is moved from | ||
| 2279 | * @to: mem_cgroup which the entry is moved to | ||
| 2280 | * | ||
| 2281 | * It succeeds only when the swap_cgroup's record for this entry is the same | ||
| 2282 | * as the mem_cgroup's id of @from. | ||
| 2283 | * | ||
| 2284 | * Returns 0 on success, -EINVAL on failure. | ||
| 2285 | * | ||
| 2286 | * The caller must have charged to @to, IOW, called res_counter_charge() about | ||
| 2287 | * both res and memsw, and called css_get(). | ||
| 2288 | */ | ||
| 2289 | static int mem_cgroup_move_swap_account(swp_entry_t entry, | ||
| 2290 | struct mem_cgroup *from, struct mem_cgroup *to) | ||
| 2291 | { | ||
| 2292 | unsigned short old_id, new_id; | ||
| 2293 | |||
| 2294 | old_id = css_id(&from->css); | ||
| 2295 | new_id = css_id(&to->css); | ||
| 2296 | |||
| 2297 | if (swap_cgroup_cmpxchg(entry, old_id, new_id) == old_id) { | ||
| 2298 | if (!mem_cgroup_is_root(from)) | ||
| 2299 | res_counter_uncharge(&from->memsw, PAGE_SIZE); | ||
| 2300 | mem_cgroup_swap_statistics(from, false); | ||
| 2301 | mem_cgroup_put(from); | ||
| 2302 | /* | ||
| 2303 | * we charged both to->res and to->memsw, so we should uncharge | ||
| 2304 | * to->res. | ||
| 2305 | */ | ||
| 2306 | if (!mem_cgroup_is_root(to)) | ||
| 2307 | res_counter_uncharge(&to->res, PAGE_SIZE); | ||
| 2308 | mem_cgroup_swap_statistics(to, true); | ||
| 2309 | mem_cgroup_get(to); | ||
| 2310 | css_put(&to->css); | ||
| 2311 | |||
| 2312 | return 0; | ||
| 2313 | } | ||
| 2314 | return -EINVAL; | ||
| 2315 | } | ||
| 2316 | #else | ||
| 2317 | static inline int mem_cgroup_move_swap_account(swp_entry_t entry, | ||
| 2318 | struct mem_cgroup *from, struct mem_cgroup *to) | ||
| 2319 | { | ||
| 2320 | return -EINVAL; | ||
| 2321 | } | ||
| 2273 | #endif | 2322 | #endif |
| 2274 | 2323 | ||
| 2275 | /* | 2324 | /* |
| @@ -2949,6 +2998,7 @@ static u64 mem_cgroup_move_charge_read(struct cgroup *cgrp, | |||
| 2949 | return mem_cgroup_from_cont(cgrp)->move_charge_at_immigrate; | 2998 | return mem_cgroup_from_cont(cgrp)->move_charge_at_immigrate; |
| 2950 | } | 2999 | } |
| 2951 | 3000 | ||
| 3001 | #ifdef CONFIG_MMU | ||
| 2952 | static int mem_cgroup_move_charge_write(struct cgroup *cgrp, | 3002 | static int mem_cgroup_move_charge_write(struct cgroup *cgrp, |
| 2953 | struct cftype *cft, u64 val) | 3003 | struct cftype *cft, u64 val) |
| 2954 | { | 3004 | { |
| @@ -2967,6 +3017,13 @@ static int mem_cgroup_move_charge_write(struct cgroup *cgrp, | |||
| 2967 | 3017 | ||
| 2968 | return 0; | 3018 | return 0; |
| 2969 | } | 3019 | } |
| 3020 | #else | ||
| 3021 | static int mem_cgroup_move_charge_write(struct cgroup *cgrp, | ||
| 3022 | struct cftype *cft, u64 val) | ||
| 3023 | { | ||
| 3024 | return -ENOSYS; | ||
| 3025 | } | ||
| 3026 | #endif | ||
| 2970 | 3027 | ||
| 2971 | 3028 | ||
| 2972 | /* For read statistics */ | 3029 | /* For read statistics */ |
| @@ -3489,6 +3546,7 @@ static int mem_cgroup_populate(struct cgroup_subsys *ss, | |||
| 3489 | return ret; | 3546 | return ret; |
| 3490 | } | 3547 | } |
| 3491 | 3548 | ||
| 3549 | #ifdef CONFIG_MMU | ||
| 3492 | /* Handlers for move charge at task migration. */ | 3550 | /* Handlers for move charge at task migration. */ |
| 3493 | #define PRECHARGE_COUNT_AT_ONCE 256 | 3551 | #define PRECHARGE_COUNT_AT_ONCE 256 |
| 3494 | static int mem_cgroup_do_precharge(unsigned long count) | 3552 | static int mem_cgroup_do_precharge(unsigned long count) |
| @@ -3544,77 +3602,124 @@ one_by_one: | |||
| 3544 | } | 3602 | } |
| 3545 | return ret; | 3603 | return ret; |
| 3546 | } | 3604 | } |
| 3605 | #else /* !CONFIG_MMU */ | ||
| 3606 | static int mem_cgroup_can_attach(struct cgroup_subsys *ss, | ||
| 3607 | struct cgroup *cgroup, | ||
| 3608 | struct task_struct *p, | ||
| 3609 | bool threadgroup) | ||
| 3610 | { | ||
| 3611 | return 0; | ||
| 3612 | } | ||
| 3613 | static void mem_cgroup_cancel_attach(struct cgroup_subsys *ss, | ||
| 3614 | struct cgroup *cgroup, | ||
| 3615 | struct task_struct *p, | ||
| 3616 | bool threadgroup) | ||
| 3617 | { | ||
| 3618 | } | ||
| 3619 | static void mem_cgroup_move_task(struct cgroup_subsys *ss, | ||
| 3620 | struct cgroup *cont, | ||
| 3621 | struct cgroup *old_cont, | ||
| 3622 | struct task_struct *p, | ||
| 3623 | bool threadgroup) | ||
| 3624 | { | ||
| 3625 | } | ||
| 3626 | #endif | ||
