diff options
Diffstat (limited to 'mm/memcontrol.c')
-rw-r--r-- | mm/memcontrol.c | 72 |
1 files changed, 42 insertions, 30 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 795e525afaba..07d92b84f448 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -3739,27 +3739,21 @@ static bool mem_cgroup_force_empty_list(struct mem_cgroup *memcg, | |||
3739 | } | 3739 | } |
3740 | 3740 | ||
3741 | /* | 3741 | /* |
3742 | * make mem_cgroup's charge to be 0 if there is no task. | 3742 | * make mem_cgroup's charge to be 0 if there is no task by moving |
3743 | * all the charges and pages to the parent. | ||
3743 | * This enables deleting this mem_cgroup. | 3744 | * This enables deleting this mem_cgroup. |
3745 | * | ||
3746 | * Caller is responsible for holding css reference on the memcg. | ||
3744 | */ | 3747 | */ |
3745 | static int mem_cgroup_force_empty(struct mem_cgroup *memcg, bool free_all) | 3748 | static int mem_cgroup_reparent_charges(struct mem_cgroup *memcg) |
3746 | { | 3749 | { |
3747 | int ret; | ||
3748 | int node, zid, shrink; | ||
3749 | int nr_retries = MEM_CGROUP_RECLAIM_RETRIES; | ||
3750 | struct cgroup *cgrp = memcg->css.cgroup; | 3750 | struct cgroup *cgrp = memcg->css.cgroup; |
3751 | int node, zid; | ||
3752 | int ret; | ||
3751 | 3753 | ||
3752 | css_get(&memcg->css); | ||
3753 | |||
3754 | shrink = 0; | ||
3755 | /* should free all ? */ | ||
3756 | if (free_all) | ||
3757 | goto try_to_free; | ||
3758 | move_account: | ||
3759 | do { | 3754 | do { |
3760 | ret = -EBUSY; | ||
3761 | if (cgroup_task_count(cgrp) || !list_empty(&cgrp->children)) | 3755 | if (cgroup_task_count(cgrp) || !list_empty(&cgrp->children)) |
3762 | goto out; | 3756 | return -EBUSY; |
3763 | /* This is for making all *used* pages to be on LRU. */ | 3757 | /* This is for making all *used* pages to be on LRU. */ |
3764 | lru_add_drain_all(); | 3758 | lru_add_drain_all(); |
3765 | drain_all_stock_sync(memcg); | 3759 | drain_all_stock_sync(memcg); |
@@ -3783,27 +3777,34 @@ move_account: | |||
3783 | cond_resched(); | 3777 | cond_resched(); |
3784 | /* "ret" should also be checked to ensure all lists are empty. */ | 3778 | /* "ret" should also be checked to ensure all lists are empty. */ |
3785 | } while (res_counter_read_u64(&memcg->res, RES_USAGE) > 0 || ret); | 3779 | } while (res_counter_read_u64(&memcg->res, RES_USAGE) > 0 || ret); |
3786 | out: | 3780 | |
3787 | css_put(&memcg->css); | ||
3788 | return ret; | 3781 | return ret; |
3782 | } | ||
3783 | |||
3784 | /* | ||
3785 | * Reclaims as many pages from the given memcg as possible and moves | ||
3786 | * the rest to the parent. | ||
3787 | * | ||
3788 | * Caller is responsible for holding css reference for memcg. | ||
3789 | */ | ||
3790 | static int mem_cgroup_force_empty(struct mem_cgroup *memcg) | ||
3791 | { | ||
3792 | int nr_retries = MEM_CGROUP_RECLAIM_RETRIES; | ||
3793 | struct cgroup *cgrp = memcg->css.cgroup; | ||
3789 | 3794 | ||
3790 | try_to_free: | ||
3791 | /* returns EBUSY if there is a task or if we come here twice. */ | 3795 | /* returns EBUSY if there is a task or if we come here twice. */ |
3792 | if (cgroup_task_count(cgrp) || !list_empty(&cgrp->children) || shrink) { | 3796 | if (cgroup_task_count(cgrp) || !list_empty(&cgrp->children)) |
3793 | ret = -EBUSY; | 3797 | return -EBUSY; |
3794 | goto out; | 3798 | |
3795 | } | ||
3796 | /* we call try-to-free pages for make this cgroup empty */ | 3799 | /* we call try-to-free pages for make this cgroup empty */ |
3797 | lru_add_drain_all(); | 3800 | lru_add_drain_all(); |
3798 | /* try to free all pages in this cgroup */ | 3801 | /* try to free all pages in this cgroup */ |
3799 | shrink = 1; | ||
3800 | while (nr_retries && res_counter_read_u64(&memcg->res, RES_USAGE) > 0) { | 3802 | while (nr_retries && res_counter_read_u64(&memcg->res, RES_USAGE) > 0) { |
3801 | int progress; | 3803 | int progress; |
3802 | 3804 | ||
3803 | if (signal_pending(current)) { | 3805 | if (signal_pending(current)) |
3804 | ret = -EINTR; | 3806 | return -EINTR; |
3805 | goto out; | 3807 | |
3806 | } | ||
3807 | progress = try_to_free_mem_cgroup_pages(memcg, GFP_KERNEL, | 3808 | progress = try_to_free_mem_cgroup_pages(memcg, GFP_KERNEL, |
3808 | false); | 3809 | false); |
3809 | if (!progress) { | 3810 | if (!progress) { |
@@ -3814,13 +3815,19 @@ try_to_free: | |||
3814 | 3815 | ||
3815 | } | 3816 | } |
3816 | lru_add_drain(); | 3817 | lru_add_drain(); |
3817 | /* try move_account...there may be some *locked* pages. */ | 3818 | return mem_cgroup_reparent_charges(memcg); |
3818 | goto move_account; | ||
3819 | } | 3819 | } |
3820 | 3820 | ||
3821 | static int mem_cgroup_force_empty_write(struct cgroup *cont, unsigned int event) | 3821 | static int mem_cgroup_force_empty_write(struct cgroup *cont, unsigned int event) |
3822 | { | 3822 | { |
3823 | return mem_cgroup_force_empty(mem_cgroup_from_cont(cont), true); | 3823 | struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); |
3824 | int ret; | ||
3825 | |||
3826 | css_get(&memcg->css); | ||
3827 | ret = mem_cgroup_force_empty(memcg); | ||
3828 | css_put(&memcg->css); | ||
3829 | |||
3830 | return ret; | ||
3824 | } | 3831 | } |
3825 | 3832 | ||
3826 | 3833 | ||
@@ -5003,8 +5010,13 @@ free_out: | |||
5003 | static int mem_cgroup_pre_destroy(struct cgroup *cont) | 5010 | static int mem_cgroup_pre_destroy(struct cgroup *cont) |
5004 | { | 5011 | { |
5005 | struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); | 5012 | struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); |
5013 | int ret; | ||
5006 | 5014 | ||
5007 | return mem_cgroup_force_empty(memcg, false); | 5015 | css_get(&memcg->css); |
5016 | ret = mem_cgroup_reparent_charges(memcg); | ||
5017 | css_put(&memcg->css); | ||
5018 | |||
5019 | return ret; | ||
5008 | } | 5020 | } |
5009 | 5021 | ||
5010 | static void mem_cgroup_destroy(struct cgroup *cont) | 5022 | static void mem_cgroup_destroy(struct cgroup *cont) |