diff options
Diffstat (limited to 'mm')
-rw-r--r-- | mm/memcontrol.c | 55 |
1 files changed, 25 insertions, 30 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 497ec33ff22d..13b9d0f221b8 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -54,6 +54,7 @@ | |||
54 | #include <linux/page_cgroup.h> | 54 | #include <linux/page_cgroup.h> |
55 | #include <linux/cpu.h> | 55 | #include <linux/cpu.h> |
56 | #include <linux/oom.h> | 56 | #include <linux/oom.h> |
57 | #include <linux/lockdep.h> | ||
57 | #include "internal.h" | 58 | #include "internal.h" |
58 | #include <net/sock.h> | 59 | #include <net/sock.h> |
59 | #include <net/ip.h> | 60 | #include <net/ip.h> |
@@ -2046,6 +2047,12 @@ static int mem_cgroup_soft_reclaim(struct mem_cgroup *root_memcg, | |||
2046 | return total; | 2047 | return total; |
2047 | } | 2048 | } |
2048 | 2049 | ||
2050 | #ifdef CONFIG_LOCKDEP | ||
2051 | static struct lockdep_map memcg_oom_lock_dep_map = { | ||
2052 | .name = "memcg_oom_lock", | ||
2053 | }; | ||
2054 | #endif | ||
2055 | |||
2049 | static DEFINE_SPINLOCK(memcg_oom_lock); | 2056 | static DEFINE_SPINLOCK(memcg_oom_lock); |
2050 | 2057 | ||
2051 | /* | 2058 | /* |
@@ -2083,7 +2090,8 @@ static bool mem_cgroup_oom_trylock(struct mem_cgroup *memcg) | |||
2083 | } | 2090 | } |
2084 | iter->oom_lock = false; | 2091 | iter->oom_lock = false; |
2085 | } | 2092 | } |
2086 | } | 2093 | } else |
2094 | mutex_acquire(&memcg_oom_lock_dep_map, 0, 1, _RET_IP_); | ||
2087 | 2095 | ||
2088 | spin_unlock(&memcg_oom_lock); | 2096 | spin_unlock(&memcg_oom_lock); |
2089 | 2097 | ||
@@ -2095,6 +2103,7 @@ static void mem_cgroup_oom_unlock(struct mem_cgroup *memcg) | |||
2095 | struct mem_cgroup *iter; | 2103 | struct mem_cgroup *iter; |
2096 | 2104 | ||
2097 | spin_lock(&memcg_oom_lock); | 2105 | spin_lock(&memcg_oom_lock); |
2106 | mutex_release(&memcg_oom_lock_dep_map, 1, _RET_IP_); | ||
2098 | for_each_mem_cgroup_tree(iter, memcg) | 2107 | for_each_mem_cgroup_tree(iter, memcg) |
2099 | iter->oom_lock = false; | 2108 | iter->oom_lock = false; |
2100 | spin_unlock(&memcg_oom_lock); | 2109 | spin_unlock(&memcg_oom_lock); |
@@ -2765,10 +2774,10 @@ done: | |||
2765 | *ptr = memcg; | 2774 | *ptr = memcg; |
2766 | return 0; | 2775 | return 0; |
2767 | nomem: | 2776 | nomem: |
2768 | *ptr = NULL; | 2777 | if (!(gfp_mask & __GFP_NOFAIL)) { |
2769 | if (gfp_mask & __GFP_NOFAIL) | 2778 | *ptr = NULL; |
2770 | return 0; | 2779 | return -ENOMEM; |
2771 | return -ENOMEM; | 2780 | } |
2772 | bypass: | 2781 | bypass: |
2773 | *ptr = root_mem_cgroup; | 2782 | *ptr = root_mem_cgroup; |
2774 | return -EINTR; | 2783 | return -EINTR; |
@@ -3773,7 +3782,6 @@ void mem_cgroup_move_account_page_stat(struct mem_cgroup *from, | |||
3773 | { | 3782 | { |
3774 | /* Update stat data for mem_cgroup */ | 3783 | /* Update stat data for mem_cgroup */ |
3775 | preempt_disable(); | 3784 | preempt_disable(); |
3776 | WARN_ON_ONCE(from->stat->count[idx] < nr_pages); | ||
3777 | __this_cpu_sub(from->stat->count[idx], nr_pages); | 3785 | __this_cpu_sub(from->stat->count[idx], nr_pages); |
3778 | __this_cpu_add(to->stat->count[idx], nr_pages); | 3786 | __this_cpu_add(to->stat->count[idx], nr_pages); |
3779 | preempt_enable(); | 3787 | preempt_enable(); |
@@ -4950,31 +4958,18 @@ static void mem_cgroup_reparent_charges(struct mem_cgroup *memcg) | |||
4950 | } while (usage > 0); | 4958 | } while (usage > 0); |
4951 | } | 4959 | } |
4952 | 4960 | ||
4953 | /* | ||
4954 | * This mainly exists for tests during the setting of set of use_hierarchy. | ||
4955 | * Since this is the very setting we are changing, the current hierarchy value | ||
4956 | * is meaningless | ||
4957 | */ | ||
4958 | static inline bool __memcg_has_children(struct mem_cgroup *memcg) | ||
4959 | { | ||
4960 | struct cgroup_subsys_state *pos; | ||
4961 | |||
4962 | /* bounce at first found */ | ||
4963 | css_for_each_child(pos, &memcg->css) | ||
4964 | return true; | ||
4965 | return false; | ||
4966 | } | ||
4967 | |||
4968 | /* | ||
4969 | * Must be called with memcg_create_mutex held, unless the cgroup is guaranteed | ||
4970 | * to be already dead (as in mem_cgroup_force_empty, for instance). This is | ||
4971 | * from mem_cgroup_count_children(), in the sense that we don't really care how | ||
4972 | * many children we have; we only need to know if we have any. It also counts | ||
4973 | * any memcg without hierarchy as infertile. | ||
4974 | */ | ||
4975 | static inline bool memcg_has_children(struct mem_cgroup *memcg) | 4961 | static inline bool memcg_has_children(struct mem_cgroup *memcg) |
4976 | { | 4962 | { |
4977 | return memcg->use_hierarchy && __memcg_has_children(memcg); | 4963 | lockdep_assert_held(&memcg_create_mutex); |
4964 | /* | ||
4965 | * The lock does not prevent addition or deletion to the list | ||
4966 | * of children, but it prevents a new child from being | ||
4967 | * initialized based on this parent in css_online(), so it's | ||
4968 | * enough to decide whether hierarchically inherited | ||
4969 | * attributes can still be changed or not. | ||
4970 | */ | ||
4971 | return memcg->use_hierarchy && | ||
4972 | !list_empty(&memcg->css.cgroup->children); | ||
4978 | } | 4973 | } |
4979 | 4974 | ||
4980 | /* | 4975 | /* |
@@ -5054,7 +5049,7 @@ static int mem_cgroup_hierarchy_write(struct cgroup_subsys_state *css, | |||
5054 | */ | 5049 | */ |
5055 | if ((!parent_memcg || !parent_memcg->use_hierarchy) && | 5050 | if ((!parent_memcg || !parent_memcg->use_hierarchy) && |
5056 | (val == 1 || val == 0)) { | 5051 | (val == 1 || val == 0)) { |
5057 | if (!__memcg_has_children(memcg)) | 5052 | if (list_empty(&memcg->css.cgroup->children)) |
5058 | memcg->use_hierarchy = val; | 5053 | memcg->use_hierarchy = val; |
5059 | else | 5054 | else |
5060 | retval = -EBUSY; | 5055 | retval = -EBUSY; |