shift percpu_counter_destroy() into destroy_super_work()
authorOleg Nesterov <oleg@redhat.com>
Wed, 22 Jul 2015 18:21:13 +0000 (20:21 +0200)
committerOleg Nesterov <oleg@redhat.com>
Sat, 15 Aug 2015 11:52:11 +0000 (13:52 +0200)
Of course, this patch is ugly as hell. It will be (partially)
reverted later. We add it to ensure that other WIP changes in
percpu_rw_semaphore won't break fs/super.c.

We do not even need this change right now, percpu_free_rwsem()
is fine in atomic context. But we are going to change this, it
will be might_sleep() after we merge the rcu_sync() patches.

And even after that we do not really need destroy_super_work(),
we will kill it in any case. Instead, destroy_super_rcu() should
just check that rss->cb_state == CB_IDLE and do call_rcu() again
in the (very unlikely) case this is not true.

So this is just the temporary kludge which helps us to avoid the
conflicts with the changes which will be (hopefully) routed via
rcu tree.

Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Reviewed-by: Jan Kara <jack@suse.com>
fs/super.c
include/linux/fs.h

index 8aa3cbc571d18aabc4cfb8c8cd153d58a96fa995..c937bd7b4d33ba57da6156821b0b4413963f5d99 100644 (file)
@@ -135,6 +135,24 @@ static unsigned long super_cache_count(struct shrinker *shrink,
        return total_objects;
 }
 
+static void destroy_super_work(struct work_struct *work)
+{
+       struct super_block *s = container_of(work, struct super_block,
+                                                       destroy_work);
+       int i;
+
+       for (i = 0; i < SB_FREEZE_LEVELS; i++)
+               percpu_counter_destroy(&s->s_writers.counter[i]);
+       kfree(s);
+}
+
+static void destroy_super_rcu(struct rcu_head *head)
+{
+       struct super_block *s = container_of(head, struct super_block, rcu);
+       INIT_WORK(&s->destroy_work, destroy_super_work);
+       schedule_work(&s->destroy_work);
+}
+
 /**
  *     destroy_super   -       frees a superblock
  *     @s: superblock to free
@@ -143,16 +161,13 @@ static unsigned long super_cache_count(struct shrinker *shrink,
  */
 static void destroy_super(struct super_block *s)
 {
-       int i;
        list_lru_destroy(&s->s_dentry_lru);
        list_lru_destroy(&s->s_inode_lru);
-       for (i = 0; i < SB_FREEZE_LEVELS; i++)
-               percpu_counter_destroy(&s->s_writers.counter[i]);
        security_sb_free(s);
        WARN_ON(!list_empty(&s->s_mounts));
        kfree(s->s_subtype);
        kfree(s->s_options);
-       kfree_rcu(s, rcu);
+       call_rcu(&s->rcu, destroy_super_rcu);
 }
 
 /**
index acb7cad84edd2550cbfad31c860b5cb2a72c2377..4bed78966c6bbc75d4a5879c1c21703be55e739b 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/lockdep.h>
 #include <linux/percpu-rwsem.h>
 #include <linux/blk_types.h>
+#include <linux/workqueue.h>
 
 #include <asm/byteorder.h>
 #include <uapi/linux/fs.h>
@@ -1375,7 +1376,7 @@ struct super_block {
        struct list_lru         s_dentry_lru ____cacheline_aligned_in_smp;
        struct list_lru         s_inode_lru ____cacheline_aligned_in_smp;
        struct rcu_head         rcu;
-
+       struct work_struct      destroy_work;
        /*
         * Indicates how deep in a filesystem stack this SB is
         */