vfs: factor out inode_insert5()
authorMiklos Szeredi <miklos@szeredi.hu>
Thu, 17 May 2018 08:53:05 +0000 (10:53 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Thu, 31 May 2018 09:06:11 +0000 (11:06 +0200)
Split out common helper for race free insertion of an already allocated
inode into the cache.  Use this from iget5_locked() and
insert_inode_locked4().  Make iget5_locked() use new_inode()/iput() instead
of alloc_inode()/destroy_inode() directly.

Also export to modules for use by filesystems which want to preallocate an
inode before file/directory creation.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/inode.c
include/linux/fs.h

index 13ceb98c3bd3b344b0fe47118af1122379c58194..1bea65d37afe1a1812d0154d114b5197fa75cf80 100644 (file)
@@ -1002,6 +1002,70 @@ void unlock_two_nondirectories(struct inode *inode1, struct inode *inode2)
 }
 EXPORT_SYMBOL(unlock_two_nondirectories);
 
+/**
+ * inode_insert5 - obtain an inode from a mounted file system
+ * @inode:     pre-allocated inode to use for insert to cache
+ * @hashval:   hash value (usually inode number) to get
+ * @test:      callback used for comparisons between inodes
+ * @set:       callback used to initialize a new struct inode
+ * @data:      opaque data pointer to pass to @test and @set
+ *
+ * Search for the inode specified by @hashval and @data in the inode cache,
+ * and if present it is return it with an increased reference count. This is
+ * a variant of iget5_locked() for callers that don't want to fail on memory
+ * allocation of inode.
+ *
+ * If the inode is not in cache, insert the pre-allocated inode to cache and
+ * return it locked, hashed, and with the I_NEW flag set. The file system gets
+ * to fill it in before unlocking it via unlock_new_inode().
+ *
+ * Note both @test and @set are called with the inode_hash_lock held, so can't
+ * sleep.
+ */
+struct inode *inode_insert5(struct inode *inode, unsigned long hashval,
+                           int (*test)(struct inode *, void *),
+                           int (*set)(struct inode *, void *), void *data)
+{
+       struct hlist_head *head = inode_hashtable + hash(inode->i_sb, hashval);
+       struct inode *old;
+
+again:
+       spin_lock(&inode_hash_lock);
+       old = find_inode(inode->i_sb, head, test, data);
+       if (unlikely(old)) {
+               /*
+                * Uhhuh, somebody else created the same inode under us.
+                * Use the old inode instead of the preallocated one.
+                */
+               spin_unlock(&inode_hash_lock);
+               wait_on_inode(old);
+               if (unlikely(inode_unhashed(old))) {
+                       iput(old);
+                       goto again;
+               }
+               return old;
+       }
+
+       if (set && unlikely(set(inode, data))) {
+               inode = NULL;
+               goto unlock;
+       }
+
+       /*
+        * Return the locked inode with I_NEW set, the
+        * caller is responsible for filling in the contents
+        */
+       spin_lock(&inode->i_lock);
+       inode->i_state |= I_NEW;
+       hlist_add_head(&inode->i_hash, head);
+       spin_unlock(&inode->i_lock);
+unlock:
+       spin_unlock(&inode_hash_lock);
+
+       return inode;
+}
+EXPORT_SYMBOL(inode_insert5);
+
 /**
  * iget5_locked - obtain an inode from a mounted file system
  * @sb:                super block of file system
@@ -1026,66 +1090,18 @@ struct inode *iget5_locked(struct super_block *sb, unsigned long hashval,
                int (*test)(struct inode *, void *),
                int (*set)(struct inode *, void *), void *data)
 {
-       struct hlist_head *head = inode_hashtable + hash(sb, hashval);
-       struct inode *inode;
-again:
-       spin_lock(&inode_hash_lock);
-       inode = find_inode(sb, head, test, data);
-       spin_unlock(&inode_hash_lock);
+       struct inode *inode = ilookup5(sb, hashval, test, data);
 
-       if (inode) {
-               wait_on_inode(inode);
-               if (unlikely(inode_unhashed(inode))) {
-                       iput(inode);
-                       goto again;
-               }
-               return inode;
-       }
+       if (!inode) {
+               struct inode *new = new_inode(sb);
 
-       inode = alloc_inode(sb);
-       if (inode) {
-               struct inode *old;
-
-               spin_lock(&inode_hash_lock);
-               /* We released the lock, so.. */
-               old = find_inode(sb, head, test, data);
-               if (!old) {
-                       if (set(inode, data))
-                               goto set_failed;
-
-                       spin_lock(&inode->i_lock);
-                       inode->i_state = I_NEW;
-                       hlist_add_head(&inode->i_hash, head);
-                       spin_unlock(&inode->i_lock);
-                       inode_sb_list_add(inode);
-                       spin_unlock(&inode_hash_lock);
-
-                       /* Return the locked inode with I_NEW set, the
-                        * caller is responsible for filling in the contents
-                        */
-                       return inode;
-               }
-
-               /*
-                * Uhhuh, somebody else created the same inode under
-                * us. Use the old inode instead of the one we just
-                * allocated.
-                */
-               spin_unlock(&inode_hash_lock);
-               destroy_inode(inode);
-               inode = old;
-               wait_on_inode(inode);
-               if (unlikely(inode_unhashed(inode))) {
-                       iput(inode);
-                       goto again;
+               if (new) {
+                       inode = inode_insert5(new, hashval, test, set, data);
+                       if (unlikely(inode != new))
+                               iput(new);
                }
        }
        return inode;
-
-set_failed:
-       spin_unlock(&inode_hash_lock);
-       destroy_inode(inode);
-       return NULL;
 }
 EXPORT_SYMBOL(iget5_locked);
 
@@ -1426,43 +1442,13 @@ EXPORT_SYMBOL(insert_inode_locked);
 int insert_inode_locked4(struct inode *inode, unsigned long hashval,
                int (*test)(struct inode *, void *), void *data)
 {
-       struct super_block *sb = inode->i_sb;
-       struct hlist_head *head = inode_hashtable + hash(sb, hashval);
+       struct inode *old = inode_insert5(inode, hashval, test, NULL, data);
 
-       while (1) {
-               struct inode *old = NULL;
-
-               spin_lock(&inode_hash_lock);
-               hlist_for_each_entry(old, head, i_hash) {
-                       if (old->i_sb != sb)
-                               continue;
-                       if (!test(old, data))
-                               continue;
-                       spin_lock(&old->i_lock);
-                       if (old->i_state & (I_FREEING|I_WILL_FREE)) {
-                               spin_unlock(&old->i_lock);
-                               continue;
-                       }
-                       break;
-               }
-               if (likely(!old)) {
-                       spin_lock(&inode->i_lock);
-                       inode->i_state |= I_NEW;
-                       hlist_add_head(&inode->i_hash, head);
-                       spin_unlock(&inode->i_lock);
-                       spin_unlock(&inode_hash_lock);
-                       return 0;
-               }
-               __iget(old);
-               spin_unlock(&old->i_lock);
-               spin_unlock(&inode_hash_lock);
-               wait_on_inode(old);
-               if (unlikely(!inode_unhashed(old))) {
-                       iput(old);
-                       return -EBUSY;
-               }
+       if (old != inode) {
                iput(old);
+               return -EBUSY;
        }
+       return 0;
 }
 EXPORT_SYMBOL(insert_inode_locked4);
 
index 760d8da1b6c7dc8b6d16703e1f65de918ec8460e..4f637a9b213d5de24cdb965bb136d4a3fcb33f66 100644 (file)
@@ -2879,6 +2879,10 @@ extern struct inode *ilookup5(struct super_block *sb, unsigned long hashval,
                int (*test)(struct inode *, void *), void *data);
 extern struct inode *ilookup(struct super_block *sb, unsigned long ino);
 
+extern struct inode *inode_insert5(struct inode *inode, unsigned long hashval,
+               int (*test)(struct inode *, void *),
+               int (*set)(struct inode *, void *),
+               void *data);
 extern struct inode * iget5_locked(struct super_block *, unsigned long, int (*test)(struct inode *, void *), int (*set)(struct inode *, void *), void *);
 extern struct inode * iget_locked(struct super_block *, unsigned long);
 extern struct inode *find_inode_nowait(struct super_block *,