aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorMichal Hocko <mhocko@suse.cz>2014-01-23 18:53:35 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-01-23 19:36:53 -0500
commitecc736fc3c71c411a9d201d8588c9e7e049e5d8c (patch)
treebc150baf2dbb0a8374c69e7c2c2d53c1d0d36777 /mm
parentd49ad9355420c743c736bfd1dee9eaa5b1a7722a (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.c17
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,
1167static void mem_cgroup_iter_update(struct mem_cgroup_reclaim_iter *iter, 1175static 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++;