]> Pileus Git - ~andy/linux/blobdiff - mm/memcontrol.c
Merge tag 'upstream-3.14-rc1' of git://git.infradead.org/linux-ubifs
[~andy/linux] / mm / memcontrol.c
index c8336e8f8df064c9a1ecdbb8055a785b3481d4f2..53385cd4e6f02cfae85f99a2629f735094130c78 100644 (file)
@@ -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;
@@ -1158,7 +1166,15 @@ mem_cgroup_iter_load(struct mem_cgroup_reclaim_iter *iter,
        if (iter->last_dead_count == *sequence) {
                smp_rmb();
                position = iter->last_visited;
-               if (position && !css_tryget(&position->css))
+
+               /*
+                * We cannot take a reference to root because we might race
+                * with root removal and returning NULL would end up in
+                * an endless loop on the iterator user level when root
+                * would be returned all the time.
+                */
+               if (position && position != root &&
+                               !css_tryget(&position->css))
                        position = NULL;
        }
        return position;
@@ -1167,9 +1183,11 @@ mem_cgroup_iter_load(struct mem_cgroup_reclaim_iter *iter,
 static void mem_cgroup_iter_update(struct mem_cgroup_reclaim_iter *iter,
                                   struct mem_cgroup *last_visited,
                                   struct mem_cgroup *new_position,
+                                  struct mem_cgroup *root,
                                   int sequence)
 {
-       if (last_visited)
+       /* root reference counting symmetric to mem_cgroup_iter_load */
+       if (last_visited && last_visited != root)
                css_put(&last_visited->css);
        /*
         * We store the sequence count from the time @last_visited was
@@ -1244,7 +1262,8 @@ struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root,
                memcg = __mem_cgroup_iter_next(root, last_visited);
 
                if (reclaim) {
-                       mem_cgroup_iter_update(iter, last_visited, memcg, seq);
+                       mem_cgroup_iter_update(iter, last_visited, memcg, root,
+                                       seq);
 
                        if (!memcg)
                                iter->generation++;
@@ -3340,11 +3359,9 @@ static void kmem_cache_destroy_work_func(struct work_struct *w)
         * So if we aren't down to zero, we'll just schedule a worker and try
         * again
         */
-       if (atomic_read(&cachep->memcg_params->nr_pages) != 0) {
+       if (atomic_read(&cachep->memcg_params->nr_pages) != 0)
                kmem_cache_shrink(cachep);
-               if (atomic_read(&cachep->memcg_params->nr_pages) == 0)
-                       return;
-       } else
+       else
                kmem_cache_destroy(cachep);
 }
 
@@ -3383,7 +3400,7 @@ void mem_cgroup_destroy_cache(struct kmem_cache *cachep)
 static struct kmem_cache *memcg_create_kmem_cache(struct mem_cgroup *memcg,
                                                  struct kmem_cache *s)
 {
-       struct kmem_cache *new;
+       struct kmem_cache *new = NULL;
        static char *tmp_name = NULL;
        static DEFINE_MUTEX(mutex);     /* protects tmp_name */
 
@@ -3399,7 +3416,7 @@ static struct kmem_cache *memcg_create_kmem_cache(struct mem_cgroup *memcg,
        if (!tmp_name) {
                tmp_name = kmalloc(PATH_MAX, GFP_KERNEL);
                if (!tmp_name)
-                       return NULL;
+                       goto out;
        }
 
        rcu_read_lock();
@@ -3409,12 +3426,11 @@ static struct kmem_cache *memcg_create_kmem_cache(struct mem_cgroup *memcg,
 
        new = kmem_cache_create_memcg(memcg, tmp_name, s->object_size, s->align,
                                      (s->flags & ~SLAB_PANIC), s->ctor, s);
-
        if (new)
                new->allocflags |= __GFP_KMEMCG;
        else
                new = s;
-
+out:
        mutex_unlock(&mutex);
        return new;
 }