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); |
