diff options
author | Michal Hocko <mhocko@suse.cz> | 2014-01-23 18:53:35 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-23 19:36:53 -0500 |
commit | ecc736fc3c71c411a9d201d8588c9e7e049e5d8c (patch) | |
tree | bc150baf2dbb0a8374c69e7c2c2d53c1d0d36777 /mm | |
parent | d49ad9355420c743c736bfd1dee9eaa5b1a7722a (diff) |
memcg: fix endless loop caused by mem_cgroup_iter
Hugh has reported an endless loop when the hardlimit reclaim sees the
same group all the time. This might happen when the reclaim races with
the memcg removal.
shrink_zone
[rmdir root]
mem_cgroup_iter(root, NULL, reclaim)
// prev = NULL
rcu_read_lock()
mem_cgroup_iter_load
last_visited = iter->last_visited // gets root || NULL
css_tryget(last_visited) // failed
last_visited = NULL [1]
memcg = root = __mem_cgroup_iter_next(root, NULL)
mem_cgroup_iter_update
iter->last_visited = root;
reclaim->generation = iter->generation
mem_cgroup_iter(root, root, reclaim)
// prev = root
rcu_read_lock
mem_cgroup_iter_load
last_visited = iter->last_visited // gets root
css_tryget(last_visited) // failed
[1]
The issue seemed to be introduced by commit 5f5781619718 ("memcg: relax
memcg iter caching") which has replaced unconditional css_get/css_put by
css_tryget/css_put for the cached iterator.
This patch fixes the issue by skipping css_tryget on the root of the
tree walk in mem_cgroup_iter_load and symmetrically doesn't release it
in mem_cgroup_iter_update.
Signed-off-by: Michal Hocko <mhocko@suse.cz>
Reported-by: Hugh Dickins <hughd@google.com>
Tested-by: Hugh Dickins <hughd@google.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Greg Thelen <gthelen@google.com>
Cc: <stable@vger.kernel.org> [3.10+]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/memcontrol.c | 17 |
1 files changed, 14 insertions, 3 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index c8336e8f8df0..da07784dde87 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -1158,7 +1158,15 @@ mem_cgroup_iter_load(struct mem_cgroup_reclaim_iter *iter, | |||
1158 | if (iter->last_dead_count == *sequence) { | 1158 | if (iter->last_dead_count == *sequence) { |
1159 | smp_rmb(); | 1159 | smp_rmb(); |
1160 | position = iter->last_visited; | 1160 | position = iter->last_visited; |
1161 | if (position && !css_tryget(&position->css)) | 1161 | |
1162 | /* | ||
1163 | * We cannot take a reference to root because we might race | ||
1164 | * with root removal and returning NULL would end up in | ||
1165 | * an endless loop on the iterator user level when root | ||
1166 | * would be returned all the time. | ||
1167 | */ | ||
1168 | if (position && position != root && | ||
1169 | !css_tryget(&position->css)) | ||
1162 | position = NULL; | 1170 | position = NULL; |
1163 | } | 1171 | } |
1164 | return position; | 1172 | return position; |
@@ -1167,9 +1175,11 @@ mem_cgroup_iter_load(struct mem_cgroup_reclaim_iter *iter, | |||
1167 | static void mem_cgroup_iter_update(struct mem_cgroup_reclaim_iter *iter, | 1175 | static void mem_cgroup_iter_update(struct mem_cgroup_reclaim_iter *iter, |
1168 | struct mem_cgroup *last_visited, | 1176 | struct mem_cgroup *last_visited, |
1169 | struct mem_cgroup *new_position, | 1177 | struct mem_cgroup *new_position, |
1178 | struct mem_cgroup *root, | ||
1170 | int sequence) | 1179 | int sequence) |
1171 | { | 1180 | { |
1172 | if (last_visited) | 1181 | /* root reference counting symmetric to mem_cgroup_iter_load */ |
1182 | if (last_visited && last_visited != root) | ||
1173 | css_put(&last_visited->css); | 1183 | css_put(&last_visited->css); |
1174 | /* | 1184 | /* |
1175 | * We store the sequence count from the time @last_visited was | 1185 | * We store the sequence count from the time @last_visited was |
@@ -1244,7 +1254,8 @@ struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root, | |||
1244 | memcg = __mem_cgroup_iter_next(root, last_visited); | 1254 | memcg = __mem_cgroup_iter_next(root, last_visited); |
1245 | 1255 | ||
1246 | if (reclaim) { | 1256 | if (reclaim) { |
1247 | mem_cgroup_iter_update(iter, last_visited, memcg, seq); | 1257 | mem_cgroup_iter_update(iter, last_visited, memcg, root, |
1258 | seq); | ||
1248 | 1259 | ||
1249 | if (!memcg) | 1260 | if (!memcg) |
1250 | iter->generation++; | 1261 | iter->generation++; |