summaryrefslogtreecommitdiffstats
path: root/mm/memcontrol.c
diff options
context:
space:
mode:
authorMichal Hocko <mhocko@suse.cz>2014-01-24 00:53:37 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2014-01-24 01:36:53 +0100
commit0eef615665ede1e0d603ea9ecca88c1da6f02234 (patch)
tree01bc010860b4738a4ab261435dcdc2d2d4a20f53 /mm/memcontrol.c
parentmemcg: fix endless loop caused by mem_cgroup_iter (diff)
downloadlinux-0eef615665ede1e0d603ea9ecca88c1da6f02234.tar.xz
linux-0eef615665ede1e0d603ea9ecca88c1da6f02234.zip
memcg: fix css reference leak and endless loop in mem_cgroup_iter
Commit 19f39402864e ("memcg: simplify mem_cgroup_iter") has reorganized mem_cgroup_iter code in order to simplify it. A part of that change was dropping an optimization which didn't call css_tryget on the root of the walked tree. The patch however didn't change the css_put part in mem_cgroup_iter which excludes root. This wasn't an issue at the time because __mem_cgroup_iter_next bailed out for root early without taking a reference as cgroup iterators (css_next_descendant_pre) didn't visit root themselves. Nevertheless cgroup iterators have been reworked to visit root by commit bd8815a6d802 ("cgroup: make css_for_each_descendant() and friends include the origin css in the iteration") when the root bypass have been dropped in __mem_cgroup_iter_next. This means that css_put is not called for root and so css along with mem_cgroup and other cgroup internal object tied by css lifetime are never freed. Fix the issue by reintroducing root check in __mem_cgroup_iter_next and do not take css reference for it. This reference counting magic protects us also from another issue, an endless loop reported by Hugh Dickins when reclaim races with root removal and css_tryget called by iterator internally would fail. There would be no other nodes to visit so __mem_cgroup_iter_next would return NULL and mem_cgroup_iter would interpret it as "start looping from root again" and so mem_cgroup_iter would loop forever internally. 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.12+] Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/memcontrol.c')
-rw-r--r--mm/memcontrol.c18
1 files changed, 13 insertions, 5 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index da07784dde87..98f80beeac7f 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1117,14 +1117,22 @@ skip_node:
* skipped and we should continue the tree walk.
* last_visited css is safe to use because it is
* protected by css_get and the tree walk is rcu safe.
+ *
+ * We do not take a reference on the root of the tree walk
+ * because we might race with the root removal when it would
+ * be the only node in the iterated hierarchy and mem_cgroup_iter
+ * would end up in an endless loop because it expects that at
+ * least one valid node will be returned. Root cannot disappear
+ * because caller of the iterator should hold it already so
+ * skipping css reference should be safe.
*/
if (next_css) {
- if ((next_css->flags & CSS_ONLINE) && css_tryget(next_css))
+ if ((next_css->flags & CSS_ONLINE) &&
+ (next_css == &root->css || css_tryget(next_css)))
return mem_cgroup_from_css(next_css);
- else {
- prev_css = next_css;
- goto skip_node;
- }
+
+ prev_css = next_css;
+ goto skip_node;
}
return NULL;