diff options
Diffstat (limited to 'fs/xfs/xfs_mount.c')
-rw-r--r-- | fs/xfs/xfs_mount.c | 100 |
1 files changed, 72 insertions, 28 deletions
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index a3378252baa1..99db27d6ac8a 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -207,6 +207,9 @@ xfs_initialize_perag( if (xfs_buf_hash_init(pag)) goto out_free_pag; init_waitqueue_head(&pag->pagb_wait); + spin_lock_init(&pag->pagb_lock); + pag->pagb_count = 0; + pag->pagb_tree = RB_ROOT; if (radix_tree_preload(GFP_NOFS)) goto out_hash_destroy; @@ -606,6 +609,56 @@ xfs_default_resblks(xfs_mount_t *mp) return resblks; } +/* Ensure the summary counts are correct. */ +STATIC int +xfs_check_summary_counts( + struct xfs_mount *mp) +{ + /* + * The AG0 superblock verifier rejects in-progress filesystems, + * so we should never see the flag set this far into mounting. + */ + if (mp->m_sb.sb_inprogress) { + xfs_err(mp, "sb_inprogress set after log recovery??"); + WARN_ON(1); + return -EFSCORRUPTED; + } + + /* + * Now the log is mounted, we know if it was an unclean shutdown or + * not. If it was, with the first phase of recovery has completed, we + * have consistent AG blocks on disk. We have not recovered EFIs yet, + * but they are recovered transactionally in the second recovery phase + * later. + * + * If the log was clean when we mounted, we can check the summary + * counters. If any of them are obviously incorrect, we can recompute + * them from the AGF headers in the next step. + */ + if (XFS_LAST_UNMOUNT_WAS_CLEAN(mp) && + (mp->m_sb.sb_fdblocks > mp->m_sb.sb_dblocks || + mp->m_sb.sb_ifree > mp->m_sb.sb_icount)) + mp->m_flags |= XFS_MOUNT_BAD_SUMMARY; + + /* + * We can safely re-initialise incore superblock counters from the + * per-ag data. These may not be correct if the filesystem was not + * cleanly unmounted, so we waited for recovery to finish before doing + * this. + * + * If the filesystem was cleanly unmounted or the previous check did + * not flag anything weird, then we can trust the values in the + * superblock to be correct and we don't need to do anything here. + * Otherwise, recalculate the summary counters. + */ + if ((!xfs_sb_version_haslazysbcount(&mp->m_sb) || + XFS_LAST_UNMOUNT_WAS_CLEAN(mp)) && + !(mp->m_flags & XFS_MOUNT_BAD_SUMMARY)) + return 0; + + return xfs_initialize_perag_data(mp, mp->m_sb.sb_agcount); +} + /* * This function does the following on an initial mount of a file system: * - reads the superblock from disk and init the mount struct @@ -831,32 +884,10 @@ xfs_mountfs( goto out_fail_wait; } - /* - * Now the log is mounted, we know if it was an unclean shutdown or - * not. If it was, with the first phase of recovery has completed, we - * have consistent AG blocks on disk. We have not recovered EFIs yet, - * but they are recovered transactionally in the second recovery phase - * later. - * - * Hence we can safely re-initialise incore superblock counters from - * the per-ag data. These may not be correct if the filesystem was not - * cleanly unmounted, so we need to wait for recovery to finish before - * doing this. - * - * If the filesystem was cleanly unmounted, then we can trust the - * values in the superblock to be correct and we don't need to do - * anything here. - * - * If we are currently making the filesystem, the initialisation will - * fail as the perag data is in an undefined state. - */ - if (xfs_sb_version_haslazysbcount(&mp->m_sb) && - !XFS_LAST_UNMOUNT_WAS_CLEAN(mp) && - !mp->m_sb.sb_inprogress) { - error = xfs_initialize_perag_data(mp, sbp->sb_agcount); - if (error) - goto out_log_dealloc; - } + /* Make sure the summary counts are ok. */ + error = xfs_check_summary_counts(mp); + if (error) + goto out_log_dealloc; /* * Get and sanity-check the root inode. @@ -1011,7 +1042,7 @@ xfs_mountfs( out_rtunmount: xfs_rtunmount_inodes(mp); out_rele_rip: - IRELE(rip); + xfs_irele(rip); /* Clean out dquots that might be in memory after quotacheck. */ xfs_qm_unmount(mp); /* @@ -1067,7 +1098,7 @@ xfs_unmountfs( xfs_fs_unreserve_ag_blocks(mp); xfs_qm_unmount_quotas(mp); xfs_rtunmount_inodes(mp); - IRELE(mp->m_rootip); + xfs_irele(mp->m_rootip); /* * We can potentially deadlock here if we have an inode cluster @@ -1395,3 +1426,16 @@ xfs_dev_is_read_only( } return 0; } + +/* Force the summary counters to be recalculated at next mount. */ +void +xfs_force_summary_recalc( + struct xfs_mount *mp) +{ + if (!xfs_sb_version_haslazysbcount(&mp->m_sb)) + return; + + spin_lock(&mp->m_sb_lock); + mp->m_flags |= XFS_MOUNT_BAD_SUMMARY; + spin_unlock(&mp->m_sb_lock); +} |