Merge tag 'xfs-5.20-merge-6' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
[linux-2.6-microblaze.git] / fs / xfs / xfs_log_recover.c
index 940c810..17e923b 100644 (file)
@@ -2629,21 +2629,21 @@ xlog_recover_cancel_intents(
  */
 STATIC void
 xlog_recover_clear_agi_bucket(
-       xfs_mount_t     *mp,
-       xfs_agnumber_t  agno,
-       int             bucket)
+       struct xfs_perag        *pag,
+       int                     bucket)
 {
-       xfs_trans_t     *tp;
-       xfs_agi_t       *agi;
-       struct xfs_buf  *agibp;
-       int             offset;
-       int             error;
+       struct xfs_mount        *mp = pag->pag_mount;
+       struct xfs_trans        *tp;
+       struct xfs_agi          *agi;
+       struct xfs_buf          *agibp;
+       int                     offset;
+       int                     error;
 
        error = xfs_trans_alloc(mp, &M_RES(mp)->tr_clearagi, 0, 0, 0, &tp);
        if (error)
                goto out_error;
 
-       error = xfs_read_agi(mp, tp, agno, &agibp);
+       error = xfs_read_agi(pag, tp, &agibp);
        if (error)
                goto out_abort;
 
@@ -2662,60 +2662,62 @@ xlog_recover_clear_agi_bucket(
 out_abort:
        xfs_trans_cancel(tp);
 out_error:
-       xfs_warn(mp, "%s: failed to clear agi %d. Continuing.", __func__, agno);
+       xfs_warn(mp, "%s: failed to clear agi %d. Continuing.", __func__,
+                       pag->pag_agno);
        return;
 }
 
-STATIC xfs_agino_t
-xlog_recover_process_one_iunlink(
-       struct xfs_mount                *mp,
-       xfs_agnumber_t                  agno,
-       xfs_agino_t                     agino,
-       int                             bucket)
+static int
+xlog_recover_iunlink_bucket(
+       struct xfs_perag        *pag,
+       struct xfs_agi          *agi,
+       int                     bucket)
 {
-       struct xfs_buf                  *ibp;
-       struct xfs_dinode               *dip;
-       struct xfs_inode                *ip;
-       xfs_ino_t                       ino;
-       int                             error;
-
-       ino = XFS_AGINO_TO_INO(mp, agno, agino);
-       error = xfs_iget(mp, NULL, ino, 0, 0, &ip);
-       if (error)
-               goto fail;
+       struct xfs_mount        *mp = pag->pag_mount;
+       struct xfs_inode        *prev_ip = NULL;
+       struct xfs_inode        *ip;
+       xfs_agino_t             prev_agino, agino;
+       int                     error = 0;
 
-       /*
-        * Get the on disk inode to find the next inode in the bucket.
-        */
-       error = xfs_imap_to_bp(mp, NULL, &ip->i_imap, &ibp);
-       if (error)
-               goto fail_iput;
-       dip = xfs_buf_offset(ibp, ip->i_imap.im_boffset);
+       agino = be32_to_cpu(agi->agi_unlinked[bucket]);
+       while (agino != NULLAGINO) {
+               error = xfs_iget(mp, NULL,
+                               XFS_AGINO_TO_INO(mp, pag->pag_agno, agino),
+                               0, 0, &ip);
+               if (error)
+                       break;
 
-       xfs_iflags_clear(ip, XFS_IRECOVERY);
-       ASSERT(VFS_I(ip)->i_nlink == 0);
-       ASSERT(VFS_I(ip)->i_mode != 0);
+               ASSERT(VFS_I(ip)->i_nlink == 0);
+               ASSERT(VFS_I(ip)->i_mode != 0);
+               xfs_iflags_clear(ip, XFS_IRECOVERY);
+               agino = ip->i_next_unlinked;
 
-       /* setup for the next pass */
-       agino = be32_to_cpu(dip->di_next_unlinked);
-       xfs_buf_relse(ibp);
+               if (prev_ip) {
+                       ip->i_prev_unlinked = prev_agino;
+                       xfs_irele(prev_ip);
 
-       xfs_irele(ip);
-       return agino;
+                       /*
+                        * Ensure the inode is removed from the unlinked list
+                        * before we continue so that it won't race with
+                        * building the in-memory list here. This could be
+                        * serialised with the agibp lock, but that just
+                        * serialises via lockstepping and it's much simpler
+                        * just to flush the inodegc queue and wait for it to
+                        * complete.
+                        */
+                       xfs_inodegc_flush(mp);
+               }
 
- fail_iput:
-       xfs_irele(ip);
- fail:
-       /*
-        * We can't read in the inode this bucket points to, or this inode
-        * is messed up.  Just ditch this bucket of inodes.  We will lose
-        * some inodes and space, but at least we won't hang.
-        *
-        * Call xlog_recover_clear_agi_bucket() to perform a transaction to
-        * clear the inode pointer in the bucket.
-        */
-       xlog_recover_clear_agi_bucket(mp, agno, bucket);
-       return NULLAGINO;
+               prev_agino = agino;
+               prev_ip = ip;
+       }
+
+       if (prev_ip) {
+               ip->i_prev_unlinked = prev_agino;
+               xfs_irele(prev_ip);
+       }
+       xfs_inodegc_flush(mp);
+       return error;
 }
 
 /*
@@ -2741,59 +2743,70 @@ xlog_recover_process_one_iunlink(
  * scheduled on this CPU to ensure other scheduled work can run without undue
  * latency.
  */
-STATIC void
-xlog_recover_process_iunlinks(
-       struct xlog     *log)
+static void
+xlog_recover_iunlink_ag(
+       struct xfs_perag        *pag)
 {
-       struct xfs_mount        *mp = log->l_mp;
-       struct xfs_perag        *pag;
-       xfs_agnumber_t          agno;
        struct xfs_agi          *agi;
        struct xfs_buf          *agibp;
-       xfs_agino_t             agino;
        int                     bucket;
        int                     error;
 
-       for_each_perag(mp, agno, pag) {
-               error = xfs_read_agi(mp, NULL, pag->pag_agno, &agibp);
+       error = xfs_read_agi(pag, NULL, &agibp);
+       if (error) {
+               /*
+                * AGI is b0rked. Don't process it.
+                *
+                * We should probably mark the filesystem as corrupt after we've
+                * recovered all the ag's we can....
+                */
+               return;
+       }
+
+       /*
+        * Unlock the buffer so that it can be acquired in the normal course of
+        * the transaction to truncate and free each inode.  Because we are not
+        * racing with anyone else here for the AGI buffer, we don't even need
+        * to hold it locked to read the initial unlinked bucket entries out of
+        * the buffer. We keep buffer reference though, so that it stays pinned
+        * in memory while we need the buffer.
+        */
+       agi = agibp->b_addr;
+       xfs_buf_unlock(agibp);
+
+       for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++) {
+               error = xlog_recover_iunlink_bucket(pag, agi, bucket);
                if (error) {
                        /*
-                        * AGI is b0rked. Don't process it.
-                        *
-                        * We should probably mark the filesystem as corrupt
-                        * after we've recovered all the ag's we can....
+                        * Bucket is unrecoverable, so only a repair scan can
+                        * free the remaining unlinked inodes. Just empty the
+                        * bucket and remaining inodes on it unreferenced and
+                        * unfreeable.
                         */
-                       continue;
+                       xfs_inodegc_flush(pag->pag_mount);
+                       xlog_recover_clear_agi_bucket(pag, bucket);
                }
-               /*
-                * Unlock the buffer so that it can be acquired in the normal
-                * course of the transaction to truncate and free each inode.
-                * Because we are not racing with anyone else here for the AGI
-                * buffer, we don't even need to hold it locked to read the
-                * initial unlinked bucket entries out of the buffer. We keep
-                * buffer reference though, so that it stays pinned in memory
-                * while we need the buffer.
-                */
-               agi = agibp->b_addr;
-               xfs_buf_unlock(agibp);
-
-               for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++) {
-                       agino = be32_to_cpu(agi->agi_unlinked[bucket]);
-                       while (agino != NULLAGINO) {
-                               agino = xlog_recover_process_one_iunlink(mp,
-                                               pag->pag_agno, agino, bucket);
-                               cond_resched();
-                       }
-               }
-               xfs_buf_rele(agibp);
        }
 
+       xfs_buf_rele(agibp);
+}
+
+static void
+xlog_recover_process_iunlinks(
+       struct xlog     *log)
+{
+       struct xfs_perag        *pag;
+       xfs_agnumber_t          agno;
+
+       for_each_perag(log->l_mp, agno, pag)
+               xlog_recover_iunlink_ag(pag);
+
        /*
         * Flush the pending unlinked inodes to ensure that the inactivations
         * are fully completed on disk and the incore inodes can be reclaimed
         * before we signal that recovery is complete.
         */
-       xfs_inodegc_flush(mp);
+       xfs_inodegc_flush(log->l_mp);
 }
 
 STATIC void
@@ -3313,7 +3326,8 @@ xlog_do_recover(
        /* re-initialise in-core superblock and geometry structures */
        mp->m_features |= xfs_sb_version_to_features(sbp);
        xfs_reinit_percpu_counters(mp);
-       error = xfs_initialize_perag(mp, sbp->sb_agcount, &mp->m_maxagi);
+       error = xfs_initialize_perag(mp, sbp->sb_agcount, sbp->sb_dblocks,
+                       &mp->m_maxagi);
        if (error) {
                xfs_warn(mp, "Failed post-recovery per-ag init: %d", error);
                return error;