]> Pileus Git - ~andy/linux/blobdiff - fs/xfs/xfs_iget.c
[XFS] factor xfs_iget_core() into hit and miss cases
[~andy/linux] / fs / xfs / xfs_iget.c
index 58865fe47806663cd5bca451b93d534f22b306a8..b2539b17c95444a29a5bc0c03e15d77866a03663 100644 (file)
 #include "xfs_utils.h"
 
 /*
- * Look up an inode by number in the given file system.
- * The inode is looked up in the cache held in each AG.
- * If the inode is found in the cache, attach it to the provided
- * vnode.
- *
- * If it is not in core, read it in from the file system's device,
- * add it to the cache and attach the provided vnode.
- *
- * The inode is locked according to the value of the lock_flags parameter.
- * This flag parameter indicates how and if the inode's IO lock and inode lock
- * should be taken.
- *
- * mp -- the mount point structure for the current file system.  It points
- *       to the inode hash table.
- * tp -- a pointer to the current transaction if there is one.  This is
- *       simply passed through to the xfs_iread() call.
- * ino -- the number of the inode desired.  This is the unique identifier
- *        within the file system for the inode being requested.
- * lock_flags -- flags indicating how to lock the inode.  See the comment
- *              for xfs_ilock() for a list of valid values.
- * bno -- the block number starting the buffer containing the inode,
- *       if known (as by bulkstat), else 0.
+ * Check the validity of the inode we just found it the cache
  */
-STATIC int
-xfs_iget_core(
-       struct inode    *inode,
-       xfs_mount_t     *mp,
-       xfs_trans_t     *tp,
-       xfs_ino_t       ino,
-       uint            flags,
-       uint            lock_flags,
-       xfs_inode_t     **ipp,
-       xfs_daddr_t     bno)
+static int
+xfs_iget_cache_hit(
+       struct inode            *inode,
+       struct xfs_perag        *pag,
+       struct xfs_inode        *ip,
+       int                     flags,
+       int                     lock_flags) __releases(pag->pag_ici_lock)
 {
-       struct inode    *old_inode;
-       xfs_inode_t     *ip;
-       int             error;
-       unsigned long   first_index, mask;
-       xfs_perag_t     *pag;
-       xfs_agino_t     agino;
+       struct xfs_mount        *mp = ip->i_mount;
+       struct inode            *old_inode;
+       int                     error = 0;
 
-       /* the radix tree exists only in inode capable AGs */
-       if (XFS_INO_TO_AGNO(mp, ino) >= mp->m_maxagi)
-               return EINVAL;
-
-       /* get the perag structure and ensure that it's inode capable */
-       pag = xfs_get_perag(mp, ino);
-       if (!pag->pagi_inodeok)
-               return EINVAL;
-       ASSERT(pag->pag_ici_init);
-       agino = XFS_INO_TO_AGINO(mp, ino);
-
-again:
-       read_lock(&pag->pag_ici_lock);
-       ip = radix_tree_lookup(&pag->pag_ici_root, agino);
+       /*
+        * If INEW is set this inode is being set up
+        * Pause and try again.
+        */
+       if (xfs_iflags_test(ip, XFS_INEW)) {
+               error = EAGAIN;
+               XFS_STATS_INC(xs_ig_frecycle);
+               goto out_error;
+       }
 
-       if (ip != NULL) {
+       old_inode = ip->i_vnode;
+       if (old_inode == NULL) {
                /*
-                * If INEW is set this inode is being set up
+                * If IRECLAIM is set this inode is
+                * on its way out of the system,
                 * we need to pause and try again.
                 */
-               if (xfs_iflags_test(ip, XFS_INEW)) {
-                       read_unlock(&pag->pag_ici_lock);
-                       delay(1);
+               if (xfs_iflags_test(ip, XFS_IRECLAIM)) {
+                       error = EAGAIN;
                        XFS_STATS_INC(xs_ig_frecycle);
+                       goto out_error;
+               }
+               ASSERT(xfs_iflags_test(ip, XFS_IRECLAIMABLE));
 
-                       goto again;
+               /*
+                * If lookup is racing with unlink, then we
+                * should return an error immediately so we
+                * don't remove it from the reclaim list and
+                * potentially leak the inode.
+                */
+               if ((ip->i_d.di_mode == 0) &&
+                   !(flags & XFS_IGET_CREATE)) {
+                       error = ENOENT;
+                       goto out_error;
                }
+               xfs_itrace_exit_tag(ip, "xfs_iget.alloc");
 
-               old_inode = ip->i_vnode;
-               if (old_inode == NULL) {
-                       /*
-                        * If IRECLAIM is set this inode is
-                        * on its way out of the system,
-                        * we need to pause and try again.
-                        */
-                       if (xfs_iflags_test(ip, XFS_IRECLAIM)) {
-                               read_unlock(&pag->pag_ici_lock);
-                               delay(1);
-                               XFS_STATS_INC(xs_ig_frecycle);
-
-                               goto again;
-                       }
-                       ASSERT(xfs_iflags_test(ip, XFS_IRECLAIMABLE));
-
-                       /*
-                        * If lookup is racing with unlink, then we
-                        * should return an error immediately so we
-                        * don't remove it from the reclaim list and
-                        * potentially leak the inode.
-                        */
-                       if ((ip->i_d.di_mode == 0) &&
-                           !(flags & XFS_IGET_CREATE)) {
-                               read_unlock(&pag->pag_ici_lock);
-                               xfs_put_perag(mp, pag);
-                               return ENOENT;
-                       }
-
-                       xfs_itrace_exit_tag(ip, "xfs_iget.alloc");
-
-                       XFS_STATS_INC(xs_ig_found);
-                       xfs_iflags_clear(ip, XFS_IRECLAIMABLE);
-                       read_unlock(&pag->pag_ici_lock);
-
-                       XFS_MOUNT_ILOCK(mp);
-                       list_del_init(&ip->i_reclaim);
-                       XFS_MOUNT_IUNLOCK(mp);
-
-                       goto finish_inode;
-
-               } else if (inode != old_inode) {
-                       /* The inode is being torn down, pause and
-                        * try again.
-                        */
-                       if (old_inode->i_state & (I_FREEING | I_CLEAR)) {
-                               read_unlock(&pag->pag_ici_lock);
-                               delay(1);
-                               XFS_STATS_INC(xs_ig_frecycle);
-
-                               goto again;
-                       }
+               xfs_iflags_clear(ip, XFS_IRECLAIMABLE);
+               read_unlock(&pag->pag_ici_lock);
+
+               XFS_MOUNT_ILOCK(mp);
+               list_del_init(&ip->i_reclaim);
+               XFS_MOUNT_IUNLOCK(mp);
+
+       } else if (inode != old_inode) {
+               /* The inode is being torn down, pause and
+                * try again.
+                */
+               if (old_inode->i_state & (I_FREEING | I_CLEAR)) {
+                       error = EAGAIN;
+                       XFS_STATS_INC(xs_ig_frecycle);
+                       goto out_error;
+               }
 /* Chances are the other vnode (the one in the inode) is being torn
 * down right now, and we landed on top of it. Question is, what do
 * we do? Unhook the old inode and hook up the new one?
 */
-                       cmn_err(CE_PANIC,
-               "xfs_iget_core: ambiguous vns: vp/0x%p, invp/0x%p",
-                                       old_inode, inode);
-               }
-
-               /*
-                * Inode cache hit
-                */
+               cmn_err(CE_PANIC,
+       "xfs_iget_core: ambiguous vns: vp/0x%p, invp/0x%p",
+                               old_inode, inode);
+       } else {
                read_unlock(&pag->pag_ici_lock);
-               XFS_STATS_INC(xs_ig_found);
+       }
 
-finish_inode:
-               if (ip->i_d.di_mode == 0 && !(flags & XFS_IGET_CREATE)) {
-                       xfs_put_perag(mp, pag);
-                       return ENOENT;
-               }
+       if (ip->i_d.di_mode == 0 && !(flags & XFS_IGET_CREATE)) {
+               error = ENOENT;
+               goto out;
+       }
 
-               if (lock_flags != 0)
-                       xfs_ilock(ip, lock_flags);
+       if (lock_flags != 0)
+               xfs_ilock(ip, lock_flags);
 
-               xfs_iflags_clear(ip, XFS_ISTALE);
-               xfs_itrace_exit_tag(ip, "xfs_iget.found");
-               goto return_ip;
-       }
+       xfs_iflags_clear(ip, XFS_ISTALE);
+       xfs_itrace_exit_tag(ip, "xfs_iget.found");
+       XFS_STATS_INC(xs_ig_found);
+       return 0;
 
-       /*
-        * Inode cache miss
-        */
+out_error:
        read_unlock(&pag->pag_ici_lock);
-       XFS_STATS_INC(xs_ig_missed);
+out:
+       return error;
+}
+
+
+static int
+xfs_iget_cache_miss(
+       struct xfs_mount        *mp,
+       struct xfs_perag        *pag,
+       xfs_trans_t             *tp,
+       xfs_ino_t               ino,
+       struct xfs_inode        **ipp,
+       xfs_daddr_t             bno,
+       int                     flags,
+       int                     lock_flags) __releases(pag->pag_ici_lock)
+{
+       struct xfs_inode        *ip;
+       int                     error;
+       unsigned long           first_index, mask;
+       xfs_agino_t             agino = XFS_INO_TO_AGINO(mp, ino);
 
        /*
         * Read the disk inode attributes into a new inode structure and get
@@ -202,17 +160,14 @@ finish_inode:
         */
        error = xfs_iread(mp, tp, ino, &ip, bno,
                          (flags & XFS_IGET_BULKSTAT) ? XFS_IMAP_BULKSTAT : 0);
-       if (error) {
-               xfs_put_perag(mp, pag);
+       if (error)
                return error;
-       }
 
        xfs_itrace_exit_tag(ip, "xfs_iget.alloc");
 
        if ((ip->i_d.di_mode == 0) && !(flags & XFS_IGET_CREATE)) {
-               xfs_idestroy(ip);
-               xfs_put_perag(mp, pag);
-               return ENOENT;
+               error = ENOENT;
+               goto out_destroy;
        }
 
        /*
@@ -220,9 +175,8 @@ finish_inode:
         * write spinlock.
         */
        if (radix_tree_preload(GFP_KERNEL)) {
-               xfs_idestroy(ip);
-               delay(1);
-               goto again;
+               error = EAGAIN;
+               goto out_destroy;
        }
 
        if (lock_flags)
@@ -231,32 +185,104 @@ finish_inode:
        mask = ~(((XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_inodelog)) - 1);
        first_index = agino & mask;
        write_lock(&pag->pag_ici_lock);
-       /*
-        * insert the new inode
-        */
+
+       /* insert the new inode */
        error = radix_tree_insert(&pag->pag_ici_root, agino, ip);
        if (unlikely(error)) {
-               BUG_ON(error != -EEXIST);
-               write_unlock(&pag->pag_ici_lock);
-               radix_tree_preload_end();
-               if (lock_flags)
-                       xfs_iunlock(ip, lock_flags);
-               xfs_idestroy(ip);
+               WARN_ON(error != -EEXIST);
                XFS_STATS_INC(xs_ig_dup);
-               goto again;
+               error = EAGAIN;
+               goto out_unlock;
        }
 
-       /*
-        * These values _must_ be set before releasing the radix tree lock!
-        */
+       /* These values _must_ be set before releasing the radix tree lock! */
        ip->i_udquot = ip->i_gdquot = NULL;
        xfs_iflags_set(ip, XFS_INEW);
 
        write_unlock(&pag->pag_ici_lock);
        radix_tree_preload_end();
+       *ipp = ip;
+       return 0;
+
+out_unlock:
+       write_unlock(&pag->pag_ici_lock);
+       radix_tree_preload_end();
+out_destroy:
+       xfs_idestroy(ip);
+       return error;
+}
+
+/*
+ * Look up an inode by number in the given file system.
+ * The inode is looked up in the cache held in each AG.
+ * If the inode is found in the cache, attach it to the provided
+ * vnode.
+ *
+ * If it is not in core, read it in from the file system's device,
+ * add it to the cache and attach the provided vnode.
+ *
+ * The inode is locked according to the value of the lock_flags parameter.
+ * This flag parameter indicates how and if the inode's IO lock and inode lock
+ * should be taken.
+ *
+ * mp -- the mount point structure for the current file system.  It points
+ *       to the inode hash table.
+ * tp -- a pointer to the current transaction if there is one.  This is
+ *       simply passed through to the xfs_iread() call.
+ * ino -- the number of the inode desired.  This is the unique identifier
+ *        within the file system for the inode being requested.
+ * lock_flags -- flags indicating how to lock the inode.  See the comment
+ *              for xfs_ilock() for a list of valid values.
+ * bno -- the block number starting the buffer containing the inode,
+ *       if known (as by bulkstat), else 0.
+ */
+STATIC int
+xfs_iget_core(
+       struct inode    *inode,
+       xfs_mount_t     *mp,
+       xfs_trans_t     *tp,
+       xfs_ino_t       ino,
+       uint            flags,
+       uint            lock_flags,
+       xfs_inode_t     **ipp,
+       xfs_daddr_t     bno)
+{
+       xfs_inode_t     *ip;
+       int             error;
+       xfs_perag_t     *pag;
+       xfs_agino_t     agino;
+
+       /* the radix tree exists only in inode capable AGs */
+       if (XFS_INO_TO_AGNO(mp, ino) >= mp->m_maxagi)
+               return EINVAL;
+
+       /* get the perag structure and ensure that it's inode capable */
+       pag = xfs_get_perag(mp, ino);
+       if (!pag->pagi_inodeok)
+               return EINVAL;
+       ASSERT(pag->pag_ici_init);
+       agino = XFS_INO_TO_AGINO(mp, ino);
+
+again:
+       error = 0;
+       read_lock(&pag->pag_ici_lock);
+       ip = radix_tree_lookup(&pag->pag_ici_root, agino);
+
+       if (ip) {
+               error = xfs_iget_cache_hit(inode, pag, ip, flags, lock_flags);
+               if (error)
+                       goto out_error_or_again;
+       } else {
+               read_unlock(&pag->pag_ici_lock);
+               XFS_STATS_INC(xs_ig_missed);
+
+               error = xfs_iget_cache_miss(mp, pag, tp, ino, &ip, bno,
+                                                       flags, lock_flags);
+               if (error)
+                       goto out_error_or_again;
+       }
        xfs_put_perag(mp, pag);
 
- return_ip:
        ASSERT(ip->i_df.if_ext_max ==
               XFS_IFORK_DSIZE(ip) / sizeof(xfs_bmbt_rec_t));
 
@@ -276,6 +302,14 @@ finish_inode:
        if (ip->i_d.di_mode != 0)
                xfs_setup_inode(ip);
        return 0;
+
+out_error_or_again:
+       if (error == EAGAIN) {
+               delay(1);
+               goto again;
+       }
+       xfs_put_perag(mp, pag);
+       return error;
 }