xfs: fix reloading entire unlinked bucket lists
[linux-2.6-microblaze.git] / fs / xfs / xfs_inode.c
index f94f7b3..4d55f58 100644 (file)
@@ -1743,6 +1743,14 @@ xfs_inactive(
                truncate = 1;
 
        if (xfs_iflags_test(ip, XFS_IQUOTAUNCHECKED)) {
+               /*
+                * If this inode is being inactivated during a quotacheck and
+                * has not yet been scanned by quotacheck, we /must/ remove
+                * the dquots from the inode before inactivation changes the
+                * block and inode counts.  Most probably this is a result of
+                * reloading the incore iunlinked list to purge unrecovered
+                * unlinked inodes.
+                */
                xfs_qm_dqdetach(ip);
        } else {
                error = xfs_qm_dqattach(ip);
@@ -3641,6 +3649,16 @@ xfs_inode_reload_unlinked_bucket(
        if (error)
                return error;
 
+       /*
+        * We've taken ILOCK_SHARED and the AGI buffer lock to stabilize the
+        * incore unlinked list pointers for this inode.  Check once more to
+        * see if we raced with anyone else to reload the unlinked list.
+        */
+       if (!xfs_inode_unlinked_incomplete(ip)) {
+               foundit = true;
+               goto out_agibp;
+       }
+
        bucket = agino % XFS_AGI_UNLINKED_BUCKETS;
        agi = agibp->b_addr;
 
@@ -3655,25 +3673,27 @@ xfs_inode_reload_unlinked_bucket(
        while (next_agino != NULLAGINO) {
                struct xfs_inode        *next_ip = NULL;
 
+               /* Found this caller's inode, set its backlink. */
                if (next_agino == agino) {
-                       /* Found this inode, set its backlink. */
                        next_ip = ip;
                        next_ip->i_prev_unlinked = prev_agino;
                        foundit = true;
+                       goto next_inode;
                }
-               if (!next_ip) {
-                       /* Inode already in memory. */
-                       next_ip = xfs_iunlink_lookup(pag, next_agino);
-               }
-               if (!next_ip) {
-                       /* Inode not in memory, reload. */
-                       error = xfs_iunlink_reload_next(tp, agibp, prev_agino,
-                                       next_agino);
-                       if (error)
-                               break;
 
-                       next_ip = xfs_iunlink_lookup(pag, next_agino);
-               }
+               /* Try in-memory lookup first. */
+               next_ip = xfs_iunlink_lookup(pag, next_agino);
+               if (next_ip)
+                       goto next_inode;
+
+               /* Inode not in memory, try reloading it. */
+               error = xfs_iunlink_reload_next(tp, agibp, prev_agino,
+                               next_agino);
+               if (error)
+                       break;
+
+               /* Grab the reloaded inode. */
+               next_ip = xfs_iunlink_lookup(pag, next_agino);
                if (!next_ip) {
                        /* No incore inode at all?  We reloaded it... */
                        ASSERT(next_ip != NULL);
@@ -3681,10 +3701,12 @@ xfs_inode_reload_unlinked_bucket(
                        break;
                }
 
+next_inode:
                prev_agino = next_agino;
                next_agino = next_ip->i_next_unlinked;
        }
 
+out_agibp:
        xfs_trans_brelse(tp, agibp);
        /* Should have found this inode somewhere in the iunlinked bucket. */
        if (!error && !foundit)