vfs: count unlinked inodes
authorMiklos Szeredi <mszeredi@suse.cz>
Mon, 21 Nov 2011 11:11:32 +0000 (12:11 +0100)
committerAl Viro <viro@zeniv.linux.org.uk>
Sat, 7 Jan 2012 04:20:12 +0000 (23:20 -0500)
Add a new counter to the superblock that keeps track of unlinked but
not yet deleted inodes.

Do not WARN_ON if set_nlink is called with zero count, just do a
ratelimited printk.  This happens on xfs and probably other
filesystems after an unclean shutdown when the filesystem reads inodes
which already have zero i_nlink.  Reported by Christoph Hellwig.

Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Tested-by: Toshiyuki Okajima <toshi.okajima@jp.fujitsu.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/inode.c
include/linux/fs.h

index 961355d00e38105908d65705418c66e646753496..87535753ab04a70b4556b58d4f1443774305da7a 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/ima.h>
 #include <linux/cred.h>
 #include <linux/buffer_head.h> /* for inode_has_buffers */
+#include <linux/ratelimit.h>
 #include "internal.h"
 
 /*
@@ -242,6 +243,11 @@ void __destroy_inode(struct inode *inode)
        BUG_ON(inode_has_buffers(inode));
        security_inode_free(inode);
        fsnotify_inode_delete(inode);
+       if (!inode->i_nlink) {
+               WARN_ON(atomic_long_read(&inode->i_sb->s_remove_count) == 0);
+               atomic_long_dec(&inode->i_sb->s_remove_count);
+       }
+
 #ifdef CONFIG_FS_POSIX_ACL
        if (inode->i_acl && inode->i_acl != ACL_NOT_CACHED)
                posix_acl_release(inode->i_acl);
@@ -268,6 +274,85 @@ static void destroy_inode(struct inode *inode)
                call_rcu(&inode->i_rcu, i_callback);
 }
 
+/**
+ * drop_nlink - directly drop an inode's link count
+ * @inode: inode
+ *
+ * This is a low-level filesystem helper to replace any
+ * direct filesystem manipulation of i_nlink.  In cases
+ * where we are attempting to track writes to the
+ * filesystem, a decrement to zero means an imminent
+ * write when the file is truncated and actually unlinked
+ * on the filesystem.
+ */
+void drop_nlink(struct inode *inode)
+{
+       WARN_ON(inode->i_nlink == 0);
+       inode->__i_nlink--;
+       if (!inode->i_nlink)
+               atomic_long_inc(&inode->i_sb->s_remove_count);
+}
+EXPORT_SYMBOL(drop_nlink);
+
+/**
+ * clear_nlink - directly zero an inode's link count
+ * @inode: inode
+ *
+ * This is a low-level filesystem helper to replace any
+ * direct filesystem manipulation of i_nlink.  See
+ * drop_nlink() for why we care about i_nlink hitting zero.
+ */
+void clear_nlink(struct inode *inode)
+{
+       if (inode->i_nlink) {
+               inode->__i_nlink = 0;
+               atomic_long_inc(&inode->i_sb->s_remove_count);
+       }
+}
+EXPORT_SYMBOL(clear_nlink);
+
+/**
+ * set_nlink - directly set an inode's link count
+ * @inode: inode
+ * @nlink: new nlink (should be non-zero)
+ *
+ * This is a low-level filesystem helper to replace any
+ * direct filesystem manipulation of i_nlink.
+ */
+void set_nlink(struct inode *inode, unsigned int nlink)
+{
+       if (!nlink) {
+               printk_ratelimited(KERN_INFO
+                       "set_nlink() clearing i_nlink on %s inode %li\n",
+                       inode->i_sb->s_type->name, inode->i_ino);
+               clear_nlink(inode);
+       } else {
+               /* Yes, some filesystems do change nlink from zero to one */
+               if (inode->i_nlink == 0)
+                       atomic_long_dec(&inode->i_sb->s_remove_count);
+
+               inode->__i_nlink = nlink;
+       }
+}
+EXPORT_SYMBOL(set_nlink);
+
+/**
+ * inc_nlink - directly increment an inode's link count
+ * @inode: inode
+ *
+ * This is a low-level filesystem helper to replace any
+ * direct filesystem manipulation of i_nlink.  Currently,
+ * it is only here for parity with dec_nlink().
+ */
+void inc_nlink(struct inode *inode)
+{
+       if (WARN_ON(inode->i_nlink == 0))
+               atomic_long_dec(&inode->i_sb->s_remove_count);
+
+       inode->__i_nlink++;
+}
+EXPORT_SYMBOL(inc_nlink);
+
 void address_space_init_once(struct address_space *mapping)
 {
        memset(mapping, 0, sizeof(*mapping));
index 7b8a681b1ef423e0057cd29516ea3b339ea96912..8ac40921f5acd6586af6deab47bd08bbc5f1a56a 100644 (file)
@@ -1483,6 +1483,9 @@ struct super_block {
 
        struct shrinker s_shrink;       /* per-sb shrinker handle */
 
+       /* Number of inodes with nlink == 0 but still referenced */
+       atomic_long_t s_remove_count;
+
        /* Being remounted read-only */
        int s_readonly_remount;
 };
@@ -1768,31 +1771,10 @@ static inline void mark_inode_dirty_sync(struct inode *inode)
        __mark_inode_dirty(inode, I_DIRTY_SYNC);
 }
 
-/**
- * set_nlink - directly set an inode's link count
- * @inode: inode
- * @nlink: new nlink (should be non-zero)
- *
- * This is a low-level filesystem helper to replace any
- * direct filesystem manipulation of i_nlink.
- */
-static inline void set_nlink(struct inode *inode, unsigned int nlink)
-{
-       inode->__i_nlink = nlink;
-}
-
-/**
- * inc_nlink - directly increment an inode's link count
- * @inode: inode
- *
- * This is a low-level filesystem helper to replace any
- * direct filesystem manipulation of i_nlink.  Currently,
- * it is only here for parity with dec_nlink().
- */
-static inline void inc_nlink(struct inode *inode)
-{
-       inode->__i_nlink++;
-}
+extern void inc_nlink(struct inode *inode);
+extern void drop_nlink(struct inode *inode);
+extern void clear_nlink(struct inode *inode);
+extern void set_nlink(struct inode *inode, unsigned int nlink);
 
 static inline void inode_inc_link_count(struct inode *inode)
 {
@@ -1800,35 +1782,6 @@ static inline void inode_inc_link_count(struct inode *inode)
        mark_inode_dirty(inode);
 }
 
-/**
- * drop_nlink - directly drop an inode's link count
- * @inode: inode
- *
- * This is a low-level filesystem helper to replace any
- * direct filesystem manipulation of i_nlink.  In cases
- * where we are attempting to track writes to the
- * filesystem, a decrement to zero means an imminent
- * write when the file is truncated and actually unlinked
- * on the filesystem.
- */
-static inline void drop_nlink(struct inode *inode)
-{
-       inode->__i_nlink--;
-}
-
-/**
- * clear_nlink - directly zero an inode's link count
- * @inode: inode
- *
- * This is a low-level filesystem helper to replace any
- * direct filesystem manipulation of i_nlink.  See
- * drop_nlink() for why we care about i_nlink hitting zero.
- */
-static inline void clear_nlink(struct inode *inode)
-{
-       inode->__i_nlink = 0;
-}
-
 static inline void inode_dec_link_count(struct inode *inode)
 {
        drop_nlink(inode);