split invalidate_inodes()
authorAl Viro <viro@zeniv.linux.org.uk>
Tue, 26 Oct 2010 00:49:35 +0000 (20:49 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Tue, 26 Oct 2010 01:27:18 +0000 (21:27 -0400)
Pull removal of fsnotify marks into generic_shutdown_super().
Split umount-time work into a new function - evict_inodes().
Make sure that invalidate_inodes() will be able to cope with
I_FREEING once we change locking in iput().

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/inode.c
fs/internal.h
fs/notify/inode_mark.c
fs/super.c

index f6b3b52f1278e5b757b780fb271922a908875acc..a6d60682f0fd861757df6991ff8038c8ee38b7f4 100644 (file)
@@ -477,6 +477,49 @@ static void dispose_list(struct list_head *head)
        }
 }
 
+/**
+ * evict_inodes        - evict all evictable inodes for a superblock
+ * @sb:                superblock to operate on
+ *
+ * Make sure that no inodes with zero refcount are retained.  This is
+ * called by superblock shutdown after having MS_ACTIVE flag removed,
+ * so any inode reaching zero refcount during or after that call will
+ * be immediately evicted.
+ */
+void evict_inodes(struct super_block *sb)
+{
+       struct inode *inode, *next;
+       LIST_HEAD(dispose);
+
+       down_write(&iprune_sem);
+
+       spin_lock(&inode_lock);
+       list_for_each_entry_safe(inode, next, &sb->s_inodes, i_sb_list) {
+               if (atomic_read(&inode->i_count))
+                       continue;
+
+               if (inode->i_state & (I_NEW | I_FREEING | I_WILL_FREE)) {
+                       WARN_ON(1);
+                       continue;
+               }
+
+               inode->i_state |= I_FREEING;
+
+               /*
+                * Move the inode off the IO lists and LRU once I_FREEING is
+                * set so that it won't get moved back on there if it is dirty.
+                */
+               list_move(&inode->i_lru, &dispose);
+               list_del_init(&inode->i_wb_list);
+               if (!(inode->i_state & (I_DIRTY | I_SYNC)))
+                       percpu_counter_dec(&nr_inodes_unused);
+       }
+       spin_unlock(&inode_lock);
+
+       dispose_list(&dispose);
+       up_write(&iprune_sem);
+}
+
 /**
  * invalidate_inodes   - attempt to free all inodes on a superblock
  * @sb:                superblock to operate on
@@ -493,9 +536,8 @@ int invalidate_inodes(struct super_block *sb)
        down_write(&iprune_sem);
 
        spin_lock(&inode_lock);
-       fsnotify_unmount_inodes(&sb->s_inodes);
        list_for_each_entry_safe(inode, next, &sb->s_inodes, i_sb_list) {
-               if (inode->i_state & I_NEW)
+               if (inode->i_state & (I_NEW | I_FREEING | I_WILL_FREE))
                        continue;
                if (atomic_read(&inode->i_count)) {
                        busy = 1;
index 4cc67eb6ed56653f243824dc9beb180c0b75172e..ebad3b90752dae0b37838734293ee36864af2c02 100644 (file)
@@ -106,4 +106,5 @@ extern void release_open_intent(struct nameidata *);
  * inode.c
  */
 extern int get_nr_dirty_inodes(void);
+extern int evict_inodes(struct super_block *);
 extern int invalidate_inodes(struct super_block *);
index 33297c00506050afb6bbe6227f8d47e4eb9cdd11..21ed10660b80770c34eb136a311254330568fac2 100644 (file)
@@ -240,6 +240,7 @@ void fsnotify_unmount_inodes(struct list_head *list)
 {
        struct inode *inode, *next_i, *need_iput = NULL;
 
+       spin_lock(&inode_lock);
        list_for_each_entry_safe(inode, next_i, list, i_sb_list) {
                struct inode *need_iput_tmp;
 
@@ -297,4 +298,5 @@ void fsnotify_unmount_inodes(struct list_head *list)
 
                spin_lock(&inode_lock);
        }
+       spin_unlock(&inode_lock);
 }
index 8819e3a7ff203fb521b537672d16d5e2d4cea80d..b9c9869165db359d018134e99e0a39e2738e6dba 100644 (file)
@@ -273,14 +273,14 @@ void generic_shutdown_super(struct super_block *sb)
                get_fs_excl();
                sb->s_flags &= ~MS_ACTIVE;
 
-               /* bad name - it should be evict_inodes() */
-               invalidate_inodes(sb);
+               fsnotify_unmount_inodes(&sb->s_inodes);
+
+               evict_inodes(sb);
 
                if (sop->put_super)
                        sop->put_super(sb);
 
-               /* Forget any remaining inodes */
-               if (invalidate_inodes(sb)) {
+               if (!list_empty(&sb->s_inodes)) {
                        printk("VFS: Busy inodes after unmount of %s. "
                           "Self-destruct in 5 seconds.  Have a nice day...\n",
                           sb->s_id);