Merge tag 'for-3.8-merge' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk...
[cascardo/linux.git] / fs / xfs / xfs_mount.c
index b2bd3a0..da50846 100644 (file)
@@ -42,6 +42,7 @@
 #include "xfs_fsops.h"
 #include "xfs_utils.h"
 #include "xfs_trace.h"
+#include "xfs_icache.h"
 
 
 #ifdef HAVE_PERCPU_SB
@@ -303,9 +304,8 @@ STATIC int
 xfs_mount_validate_sb(
        xfs_mount_t     *mp,
        xfs_sb_t        *sbp,
-       int             flags)
+       bool            check_inprogress)
 {
-       int             loud = !(flags & XFS_MFSI_QUIET);
 
        /*
         * If the log device and data device have the
@@ -315,21 +315,18 @@ xfs_mount_validate_sb(
         * a volume filesystem in a non-volume manner.
         */
        if (sbp->sb_magicnum != XFS_SB_MAGIC) {
-               if (loud)
-                       xfs_warn(mp, "bad magic number");
+               xfs_warn(mp, "bad magic number");
                return XFS_ERROR(EWRONGFS);
        }
 
        if (!xfs_sb_good_version(sbp)) {
-               if (loud)
-                       xfs_warn(mp, "bad version");
+               xfs_warn(mp, "bad version");
                return XFS_ERROR(EWRONGFS);
        }
 
        if (unlikely(
            sbp->sb_logstart == 0 && mp->m_logdev_targp == mp->m_ddev_targp)) {
-               if (loud)
-                       xfs_warn(mp,
+               xfs_warn(mp,
                "filesystem is marked as having an external log; "
                "specify logdev on the mount command line.");
                return XFS_ERROR(EINVAL);
@@ -337,8 +334,7 @@ xfs_mount_validate_sb(
 
        if (unlikely(
            sbp->sb_logstart != 0 && mp->m_logdev_targp != mp->m_ddev_targp)) {
-               if (loud)
-                       xfs_warn(mp,
+               xfs_warn(mp,
                "filesystem is marked as having an internal log; "
                "do not specify logdev on the mount command line.");
                return XFS_ERROR(EINVAL);
@@ -372,8 +368,7 @@ xfs_mount_validate_sb(
            sbp->sb_dblocks == 0                                        ||
            sbp->sb_dblocks > XFS_MAX_DBLOCKS(sbp)                      ||
            sbp->sb_dblocks < XFS_MIN_DBLOCKS(sbp))) {
-               if (loud)
-                       XFS_CORRUPTION_ERROR("SB sanity check failed",
+               XFS_CORRUPTION_ERROR("SB sanity check failed",
                                XFS_ERRLEVEL_LOW, mp, sbp);
                return XFS_ERROR(EFSCORRUPTED);
        }
@@ -382,12 +377,10 @@ xfs_mount_validate_sb(
         * Until this is fixed only page-sized or smaller data blocks work.
         */
        if (unlikely(sbp->sb_blocksize > PAGE_SIZE)) {
-               if (loud) {
-                       xfs_warn(mp,
+               xfs_warn(mp,
                "File system with blocksize %d bytes. "
                "Only pagesize (%ld) or less will currently work.",
                                sbp->sb_blocksize, PAGE_SIZE);
-               }
                return XFS_ERROR(ENOSYS);
        }
 
@@ -401,23 +394,20 @@ xfs_mount_validate_sb(
        case 2048:
                break;
        default:
-               if (loud)
-                       xfs_warn(mp, "inode size of %d bytes not supported",
+               xfs_warn(mp, "inode size of %d bytes not supported",
                                sbp->sb_inodesize);
                return XFS_ERROR(ENOSYS);
        }
 
        if (xfs_sb_validate_fsb_count(sbp, sbp->sb_dblocks) ||
            xfs_sb_validate_fsb_count(sbp, sbp->sb_rblocks)) {
-               if (loud)
-                       xfs_warn(mp,
+               xfs_warn(mp,
                "file system too large to be mounted on this system.");
                return XFS_ERROR(EFBIG);
        }
 
-       if (unlikely(sbp->sb_inprogress)) {
-               if (loud)
-                       xfs_warn(mp, "file system busy");
+       if (check_inprogress && sbp->sb_inprogress) {
+               xfs_warn(mp, "Offline file system operation in progress!");
                return XFS_ERROR(EFSCORRUPTED);
        }
 
@@ -425,9 +415,7 @@ xfs_mount_validate_sb(
         * Version 1 directory format has never worked on Linux.
         */
        if (unlikely(!xfs_sb_version_hasdirv2(sbp))) {
-               if (loud)
-                       xfs_warn(mp,
-                               "file system using version 1 directory format");
+               xfs_warn(mp, "file system using version 1 directory format");
                return XFS_ERROR(ENOSYS);
        }
 
@@ -520,11 +508,9 @@ out_unwind:
 
 void
 xfs_sb_from_disk(
-       struct xfs_mount        *mp,
+       struct xfs_sb   *to,
        xfs_dsb_t       *from)
 {
-       struct xfs_sb *to = &mp->m_sb;
-
        to->sb_magicnum = be32_to_cpu(from->sb_magicnum);
        to->sb_blocksize = be32_to_cpu(from->sb_blocksize);
        to->sb_dblocks = be64_to_cpu(from->sb_dblocks);
@@ -626,6 +612,72 @@ xfs_sb_to_disk(
        }
 }
 
+static void
+xfs_sb_verify(
+       struct xfs_buf  *bp)
+{
+       struct xfs_mount *mp = bp->b_target->bt_mount;
+       struct xfs_sb   sb;
+       int             error;
+
+       xfs_sb_from_disk(&sb, XFS_BUF_TO_SBP(bp));
+
+       /*
+        * Only check the in progress field for the primary superblock as
+        * mkfs.xfs doesn't clear it from secondary superblocks.
+        */
+       error = xfs_mount_validate_sb(mp, &sb, bp->b_bn == XFS_SB_DADDR);
+       if (error)
+               xfs_buf_ioerror(bp, error);
+}
+
+static void
+xfs_sb_read_verify(
+       struct xfs_buf  *bp)
+{
+       xfs_sb_verify(bp);
+}
+
+/*
+ * We may be probed for a filesystem match, so we may not want to emit
+ * messages when the superblock buffer is not actually an XFS superblock.
+ * If we find an XFS superblock, the run a normal, noisy mount because we are
+ * really going to mount it and want to know about errors.
+ */
+static void
+xfs_sb_quiet_read_verify(
+       struct xfs_buf  *bp)
+{
+       struct xfs_sb   sb;
+
+       xfs_sb_from_disk(&sb, XFS_BUF_TO_SBP(bp));
+
+       if (sb.sb_magicnum == XFS_SB_MAGIC) {
+               /* XFS filesystem, verify noisily! */
+               xfs_sb_read_verify(bp);
+               return;
+       }
+       /* quietly fail */
+       xfs_buf_ioerror(bp, EFSCORRUPTED);
+}
+
+static void
+xfs_sb_write_verify(
+       struct xfs_buf  *bp)
+{
+       xfs_sb_verify(bp);
+}
+
+const struct xfs_buf_ops xfs_sb_buf_ops = {
+       .verify_read = xfs_sb_read_verify,
+       .verify_write = xfs_sb_write_verify,
+};
+
+static const struct xfs_buf_ops xfs_sb_quiet_buf_ops = {
+       .verify_read = xfs_sb_quiet_read_verify,
+       .verify_write = xfs_sb_write_verify,
+};
+
 /*
  * xfs_readsb
  *
@@ -651,25 +703,26 @@ xfs_readsb(xfs_mount_t *mp, int flags)
 
 reread:
        bp = xfs_buf_read_uncached(mp->m_ddev_targp, XFS_SB_DADDR,
-                                       BTOBB(sector_size), 0);
+                                  BTOBB(sector_size), 0,
+                                  loud ? &xfs_sb_buf_ops
+                                       : &xfs_sb_quiet_buf_ops);
        if (!bp) {
                if (loud)
                        xfs_warn(mp, "SB buffer read failed");
                return EIO;
        }
-
-       /*
-        * Initialize the mount structure from the superblock.
-        * But first do some basic consistency checking.
-        */
-       xfs_sb_from_disk(mp, XFS_BUF_TO_SBP(bp));
-       error = xfs_mount_validate_sb(mp, &(mp->m_sb), flags);
-       if (error) {
+       if (bp->b_error) {
+               error = bp->b_error;
                if (loud)
                        xfs_warn(mp, "SB validate failed");
                goto release_buf;
        }
 
+       /*
+        * Initialize the mount structure from the superblock.
+        */
+       xfs_sb_from_disk(&mp->m_sb, XFS_BUF_TO_SBP(bp));
+
        /*
         * We must be able to do sector-sized and sector-aligned IO.
         */
@@ -1001,7 +1054,7 @@ xfs_check_sizes(xfs_mount_t *mp)
        }
        bp = xfs_buf_read_uncached(mp->m_ddev_targp,
                                        d - XFS_FSS_TO_BB(mp, 1),
-                                       XFS_FSS_TO_BB(mp, 1), 0);
+                                       XFS_FSS_TO_BB(mp, 1), 0, NULL);
        if (!bp) {
                xfs_warn(mp, "last sector read failed");
                return EIO;
@@ -1016,7 +1069,7 @@ xfs_check_sizes(xfs_mount_t *mp)
                }
                bp = xfs_buf_read_uncached(mp->m_logdev_targp,
                                        d - XFS_FSB_TO_BB(mp, 1),
-                                       XFS_FSB_TO_BB(mp, 1), 0);
+                                       XFS_FSB_TO_BB(mp, 1), 0, NULL);
                if (!bp) {
                        xfs_warn(mp, "log device read failed");
                        return EIO;
@@ -1427,6 +1480,8 @@ xfs_unmountfs(
        __uint64_t              resblks;
        int                     error;
 
+       cancel_delayed_work_sync(&mp->m_eofblocks_work);
+
        xfs_qm_unmount_quotas(mp);
        xfs_rtunmount_inodes(mp);
        IRELE(mp->m_rootip);
@@ -1450,20 +1505,15 @@ xfs_unmountfs(
 
        /*
         * And reclaim all inodes.  At this point there should be no dirty
-        * inode, and none should be pinned or locked, but use synchronous
-        * reclaim just to be sure.
+        * inodes and none should be pinned or locked, but use synchronous
+        * reclaim just to be sure. We can stop background inode reclaim
+        * here as well if it is still running.
         */
+       cancel_delayed_work_sync(&mp->m_reclaim_work);
        xfs_reclaim_inodes(mp, SYNC_WAIT);
 
        xfs_qm_unmount(mp);
 
-       /*
-        * Flush out the log synchronously so that we know for sure
-        * that nothing is pinned.  This is important because bflush()
-        * will skip pinned buffers.
-        */
-       xfs_log_force(mp, XFS_LOG_SYNC);
-
        /*
         * Unreserve any blocks we have so that when we unmount we don't account
         * the reserved free space as used. This is really only necessary for
@@ -1489,23 +1539,6 @@ xfs_unmountfs(
                xfs_warn(mp, "Unable to update superblock counters. "
                                "Freespace may not be correct on next mount.");
 
-       /*
-        * At this point we might have modified the superblock again and thus
-        * added an item to the AIL, thus flush it again.
-        */
-       xfs_ail_push_all_sync(mp->m_ail);
-       xfs_wait_buftarg(mp->m_ddev_targp);
-
-       /*
-        * The superblock buffer is uncached and xfsaild_push() will lock and
-        * set the XBF_ASYNC flag on the buffer. We cannot do xfs_buf_iowait()
-        * here but a lock on the superblock buffer will block until iodone()
-        * has completed.
-        */
-       xfs_buf_lock(mp->m_sb_bp);
-       xfs_buf_unlock(mp->m_sb_bp);
-
-       xfs_log_unmount_write(mp);
        xfs_log_unmount(mp);
        xfs_uuid_unmount(mp);