diff options
Diffstat (limited to 'mm/memcontrol.c')
-rw-r--r-- | mm/memcontrol.c | 82 |
1 files changed, 55 insertions, 27 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 9a99cfaf0a19..7a22b4129211 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -61,7 +61,14 @@ struct mem_cgroup *root_mem_cgroup __read_mostly; | |||
61 | #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP | 61 | #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP |
62 | /* Turned on only when memory cgroup is enabled && really_do_swap_account = 1 */ | 62 | /* Turned on only when memory cgroup is enabled && really_do_swap_account = 1 */ |
63 | int do_swap_account __read_mostly; | 63 | int do_swap_account __read_mostly; |
64 | static int really_do_swap_account __initdata = 1; /* for remember boot option*/ | 64 | |
65 | /* for remember boot option*/ | ||
66 | #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED | ||
67 | static int really_do_swap_account __initdata = 1; | ||
68 | #else | ||
69 | static int really_do_swap_account __initdata = 0; | ||
70 | #endif | ||
71 | |||
65 | #else | 72 | #else |
66 | #define do_swap_account (0) | 73 | #define do_swap_account (0) |
67 | #endif | 74 | #endif |
@@ -278,13 +285,14 @@ enum move_type { | |||
278 | 285 | ||
279 | /* "mc" and its members are protected by cgroup_mutex */ | 286 | /* "mc" and its members are protected by cgroup_mutex */ |
280 | static struct move_charge_struct { | 287 | static struct move_charge_struct { |
281 | spinlock_t lock; /* for from, to, moving_task */ | 288 | spinlock_t lock; /* for from, to */ |
282 | struct mem_cgroup *from; | 289 | struct mem_cgroup *from; |
283 | struct mem_cgroup *to; | 290 | struct mem_cgroup *to; |
284 | unsigned long precharge; | 291 | unsigned long precharge; |
285 | unsigned long moved_charge; | 292 | unsigned long moved_charge; |
286 | unsigned long moved_swap; | 293 | unsigned long moved_swap; |
287 | struct task_struct *moving_task; /* a task moving charges */ | 294 | struct task_struct *moving_task; /* a task moving charges */ |
295 | struct mm_struct *mm; | ||
288 | wait_queue_head_t waitq; /* a waitq for other context */ | 296 | wait_queue_head_t waitq; /* a waitq for other context */ |
289 | } mc = { | 297 | } mc = { |
290 | .lock = __SPIN_LOCK_UNLOCKED(mc.lock), | 298 | .lock = __SPIN_LOCK_UNLOCKED(mc.lock), |
@@ -2152,7 +2160,7 @@ static void __mem_cgroup_move_account(struct page_cgroup *pc, | |||
2152 | { | 2160 | { |
2153 | VM_BUG_ON(from == to); | 2161 | VM_BUG_ON(from == to); |
2154 | VM_BUG_ON(PageLRU(pc->page)); | 2162 | VM_BUG_ON(PageLRU(pc->page)); |
2155 | VM_BUG_ON(!PageCgroupLocked(pc)); | 2163 | VM_BUG_ON(!page_is_cgroup_locked(pc)); |
2156 | VM_BUG_ON(!PageCgroupUsed(pc)); | 2164 | VM_BUG_ON(!PageCgroupUsed(pc)); |
2157 | VM_BUG_ON(pc->mem_cgroup != from); | 2165 | VM_BUG_ON(pc->mem_cgroup != from); |
2158 | 2166 | ||
@@ -4208,15 +4216,17 @@ static struct mem_cgroup *mem_cgroup_alloc(void) | |||
4208 | 4216 | ||
4209 | memset(mem, 0, size); | 4217 | memset(mem, 0, size); |
4210 | mem->stat = alloc_percpu(struct mem_cgroup_stat_cpu); | 4218 | mem->stat = alloc_percpu(struct mem_cgroup_stat_cpu); |
4211 | if (!mem->stat) { | 4219 | if (!mem->stat) |
4212 | if (size < PAGE_SIZE) | 4220 | goto out_free; |
4213 | kfree(mem); | ||
4214 | else | ||
4215 | vfree(mem); | ||
4216 | mem = NULL; | ||
4217 | } | ||
4218 | spin_lock_init(&mem->pcp_counter_lock); | 4221 | spin_lock_init(&mem->pcp_counter_lock); |
4219 | return mem; | 4222 | return mem; |
4223 | |||
4224 | out_free: | ||
4225 | if (size < PAGE_SIZE) | ||
4226 | kfree(mem); | ||
4227 | else | ||
4228 | vfree(mem); | ||
4229 | return NULL; | ||
4220 | } | 4230 | } |
4221 | 4231 | ||
4222 | /* | 4232 | /* |
@@ -4629,7 +4639,7 @@ static unsigned long mem_cgroup_count_precharge(struct mm_struct *mm) | |||
4629 | unsigned long precharge; | 4639 | unsigned long precharge; |
4630 | struct vm_area_struct *vma; | 4640 | struct vm_area_struct *vma; |
4631 | 4641 | ||
4632 | down_read(&mm->mmap_sem); | 4642 | /* We've already held the mmap_sem */ |
4633 | for (vma = mm->mmap; vma; vma = vma->vm_next) { | 4643 | for (vma = mm->mmap; vma; vma = vma->vm_next) { |
4634 | struct mm_walk mem_cgroup_count_precharge_walk = { | 4644 | struct mm_walk mem_cgroup_count_precharge_walk = { |
4635 | .pmd_entry = mem_cgroup_count_precharge_pte_range, | 4645 | .pmd_entry = mem_cgroup_count_precharge_pte_range, |
@@ -4641,7 +4651,6 @@ static unsigned long mem_cgroup_count_precharge(struct mm_struct *mm) | |||
4641 | walk_page_range(vma->vm_start, vma->vm_end, | 4651 | walk_page_range(vma->vm_start, vma->vm_end, |
4642 | &mem_cgroup_count_precharge_walk); | 4652 | &mem_cgroup_count_precharge_walk); |
4643 | } | 4653 | } |
4644 | up_read(&mm->mmap_sem); | ||
4645 | 4654 | ||
4646 | precharge = mc.precharge; | 4655 | precharge = mc.precharge; |
4647 | mc.precharge = 0; | 4656 | mc.precharge = 0; |
@@ -4692,11 +4701,16 @@ static void mem_cgroup_clear_mc(void) | |||
4692 | 4701 | ||
4693 | mc.moved_swap = 0; | 4702 | mc.moved_swap = 0; |
4694 | } | 4703 | } |
4704 | if (mc.mm) { | ||
4705 | up_read(&mc.mm->mmap_sem); | ||
4706 | mmput(mc.mm); | ||
4707 | } | ||
4695 | spin_lock(&mc.lock); | 4708 | spin_lock(&mc.lock); |
4696 | mc.from = NULL; | 4709 | mc.from = NULL; |
4697 | mc.to = NULL; | 4710 | mc.to = NULL; |
4698 | mc.moving_task = NULL; | ||
4699 | spin_unlock(&mc.lock); | 4711 | spin_unlock(&mc.lock); |
4712 | mc.moving_task = NULL; | ||
4713 | mc.mm = NULL; | ||
4700 | mem_cgroup_end_move(from); | 4714 | mem_cgroup_end_move(from); |
4701 | memcg_oom_recover(from); | 4715 | memcg_oom_recover(from); |
4702 | memcg_oom_recover(to); | 4716 | memcg_oom_recover(to); |
@@ -4722,12 +4736,21 @@ static int mem_cgroup_can_attach(struct cgroup_subsys *ss, | |||
4722 | return 0; | 4736 | return 0; |
4723 | /* We move charges only when we move a owner of the mm */ | 4737 | /* We move charges only when we move a owner of the mm */ |
4724 | if (mm->owner == p) { | 4738 | if (mm->owner == p) { |
4739 | /* | ||
4740 | * We do all the move charge works under one mmap_sem to | ||
4741 | * avoid deadlock with down_write(&mmap_sem) | ||
4742 | * -> try_charge() -> if (mc.moving_task) -> sleep. | ||
4743 | */ | ||
4744 | down_read(&mm->mmap_sem); | ||
4745 | |||
4725 | VM_BUG_ON(mc.from); | 4746 | VM_BUG_ON(mc.from); |
4726 | VM_BUG_ON(mc.to); | 4747 | VM_BUG_ON(mc.to); |
4727 | VM_BUG_ON(mc.precharge); | 4748 | VM_BUG_ON(mc.precharge); |
4728 | VM_BUG_ON(mc.moved_charge); | 4749 | VM_BUG_ON(mc.moved_charge); |
4729 | VM_BUG_ON(mc.moved_swap); | 4750 | VM_BUG_ON(mc.moved_swap); |
4730 | VM_BUG_ON(mc.moving_task); | 4751 | VM_BUG_ON(mc.moving_task); |
4752 | VM_BUG_ON(mc.mm); | ||
4753 | |||
4731 | mem_cgroup_start_move(from); | 4754 | mem_cgroup_start_move(from); |
4732 | spin_lock(&mc.lock); | 4755 | spin_lock(&mc.lock); |
4733 | mc.from = from; | 4756 | mc.from = from; |
@@ -4735,14 +4758,16 @@ static int mem_cgroup_can_attach(struct cgroup_subsys *ss, | |||
4735 | mc.precharge = 0; | 4758 | mc.precharge = 0; |
4736 | mc.moved_charge = 0; | 4759 | mc.moved_charge = 0; |
4737 | mc.moved_swap = 0; | 4760 | mc.moved_swap = 0; |
4738 | mc.moving_task = current; | ||
4739 | spin_unlock(&mc.lock); | 4761 | spin_unlock(&mc.lock); |
4762 | mc.moving_task = current; | ||
4763 | mc.mm = mm; | ||
4740 | 4764 | ||
4741 | ret = mem_cgroup_precharge_mc(mm); | 4765 | ret = mem_cgroup_precharge_mc(mm); |
4742 | if (ret) | 4766 | if (ret) |
4743 | mem_cgroup_clear_mc(); | 4767 | mem_cgroup_clear_mc(); |
4744 | } | 4768 | /* We call up_read() and mmput() in clear_mc(). */ |
4745 | mmput(mm); | 4769 | } else |
4770 | mmput(mm); | ||
4746 | } | 4771 | } |
4747 | return ret; | 4772 | return ret; |
4748 | } | 4773 | } |
@@ -4830,7 +4855,7 @@ static void mem_cgroup_move_charge(struct mm_struct *mm) | |||
4830 | struct vm_area_struct *vma; | 4855 | struct vm_area_struct *vma; |
4831 | 4856 | ||
4832 | lru_add_drain_all(); | 4857 | lru_add_drain_all(); |
4833 | down_read(&mm->mmap_sem); | 4858 | /* We've already held the mmap_sem */ |
4834 | for (vma = mm->mmap; vma; vma = vma->vm_next) { | 4859 | for (vma = mm->mmap; vma; vma = vma->vm_next) { |
4835 | int ret; | 4860 | int ret; |
4836 | struct mm_walk mem_cgroup_move_charge_walk = { | 4861 | struct mm_walk mem_cgroup_move_charge_walk = { |
@@ -4849,7 +4874,6 @@ static void mem_cgroup_move_charge(struct mm_struct *mm) | |||
4849 | */ | 4874 | */ |
4850 | break; | 4875 | break; |
4851 | } | 4876 | } |
4852 | up_read(&mm->mmap_sem); | ||
4853 | } | 4877 | } |
4854 | 4878 | ||
4855 | static void mem_cgroup_move_task(struct cgroup_subsys *ss, | 4879 | static void mem_cgroup_move_task(struct cgroup_subsys *ss, |
@@ -4858,17 +4882,11 @@ static void mem_cgroup_move_task(struct cgroup_subsys *ss, | |||
4858 | struct task_struct *p, | 4882 | struct task_struct *p, |
4859 | bool threadgroup) | 4883 | bool threadgroup) |
4860 | { | 4884 | { |
4861 | struct mm_struct *mm; | 4885 | if (!mc.mm) |
4862 | |||
4863 | if (!mc.to) | ||
4864 | /* no need to move charge */ | 4886 | /* no need to move charge */ |
4865 | return; | 4887 | return; |
4866 | 4888 | ||
4867 | mm = get_task_mm(p); | 4889 | mem_cgroup_move_charge(mc.mm); |
4868 | if (mm) { | ||
4869 | mem_cgroup_move_charge(mm); | ||
4870 | mmput(mm); | ||
4871 | } | ||
4872 | mem_cgroup_clear_mc(); | 4890 | mem_cgroup_clear_mc(); |
4873 | } | 4891 | } |
4874 | #else /* !CONFIG_MMU */ | 4892 | #else /* !CONFIG_MMU */ |
@@ -4909,10 +4927,20 @@ struct cgroup_subsys mem_cgroup_subsys = { | |||
4909 | }; | 4927 | }; |
4910 | 4928 | ||
4911 | #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP | 4929 | #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP |
4930 | static int __init enable_swap_account(char *s) | ||
4931 | { | ||
4932 | /* consider enabled if no parameter or 1 is given */ | ||
4933 | if (!s || !strcmp(s, "1")) | ||
4934 | really_do_swap_account = 1; | ||
4935 | else if (!strcmp(s, "0")) | ||
4936 | really_do_swap_account = 0; | ||
4937 | return 1; | ||
4938 | } | ||
4939 | __setup("swapaccount", enable_swap_account); | ||
4912 | 4940 | ||
4913 | static int __init disable_swap_account(char *s) | 4941 | static int __init disable_swap_account(char *s) |
4914 | { | 4942 | { |
4915 | really_do_swap_account = 0; | 4943 | enable_swap_account("0"); |
4916 | return 1; | 4944 | return 1; |
4917 | } | 4945 | } |
4918 | __setup("noswapaccount", disable_swap_account); | 4946 | __setup("noswapaccount", disable_swap_account); |