diff options
Diffstat (limited to 'mm/memcontrol.c')
| -rw-r--r-- | mm/memcontrol.c | 57 |
1 files changed, 26 insertions, 31 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 34d3ca9572d6..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,8 +3782,7 @@ 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); | 3785 | __this_cpu_sub(from->stat->count[idx], nr_pages); |
| 3777 | __this_cpu_add(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(); |
| 3780 | } | 3788 | } |
| @@ -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; |
