xfs: convert to SPDX license tags
[linux-2.6-block.git] / fs / xfs / libxfs / xfs_sb.c
index d9b94bd5f6899f705441d007364c67c391b724c6..a63788661a4ca4af2e176e286e7e9b34a0c3ff0c 100644 (file)
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2000-2005 Silicon Graphics, Inc.
  * All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #include "xfs.h"
 #include "xfs_fs.h"
@@ -278,6 +266,22 @@ xfs_mount_validate_sb(
                return -EFSCORRUPTED;
        }
 
+       if (sbp->sb_unit) {
+               if (!xfs_sb_version_hasdalign(sbp) ||
+                   sbp->sb_unit > sbp->sb_width ||
+                   (sbp->sb_width % sbp->sb_unit) != 0) {
+                       xfs_notice(mp, "SB stripe unit sanity check failed");
+                       return -EFSCORRUPTED;
+               }
+       } else if (xfs_sb_version_hasdalign(sbp)) {
+               xfs_notice(mp, "SB stripe alignment sanity check failed");
+               return -EFSCORRUPTED;
+       } else if (sbp->sb_width) {
+               xfs_notice(mp, "SB stripe width sanity check failed");
+               return -EFSCORRUPTED;
+       }
+
+
        if (xfs_sb_version_hascrc(&mp->m_sb) &&
            sbp->sb_blocksize < XFS_MIN_CRC_BLOCKSIZE) {
                xfs_notice(mp, "v5 SB sanity check failed");
@@ -888,6 +892,111 @@ xfs_sync_sb(
        return xfs_trans_commit(tp);
 }
 
+/*
+ * Update all the secondary superblocks to match the new state of the primary.
+ * Because we are completely overwriting all the existing fields in the
+ * secondary superblock buffers, there is no need to read them in from disk.
+ * Just get a new buffer, stamp it and write it.
+ *
+ * The sb buffers need to be cached here so that we serialise against other
+ * operations that access the secondary superblocks, but we don't want to keep
+ * them in memory once it is written so we mark it as a one-shot buffer.
+ */
+int
+xfs_update_secondary_sbs(
+       struct xfs_mount        *mp)
+{
+       xfs_agnumber_t          agno;
+       int                     saved_error = 0;
+       int                     error = 0;
+       LIST_HEAD               (buffer_list);
+
+       /* update secondary superblocks. */
+       for (agno = 1; agno < mp->m_sb.sb_agcount; agno++) {
+               struct xfs_buf          *bp;
+
+               bp = xfs_buf_get(mp->m_ddev_targp,
+                                XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
+                                XFS_FSS_TO_BB(mp, 1), 0);
+               /*
+                * If we get an error reading or writing alternate superblocks,
+                * continue.  xfs_repair chooses the "best" superblock based
+                * on most matches; if we break early, we'll leave more
+                * superblocks un-updated than updated, and xfs_repair may
+                * pick them over the properly-updated primary.
+                */
+               if (!bp) {
+                       xfs_warn(mp,
+               "error allocating secondary superblock for ag %d",
+                               agno);
+                       if (!saved_error)
+                               saved_error = -ENOMEM;
+                       continue;
+               }
+
+               bp->b_ops = &xfs_sb_buf_ops;
+               xfs_buf_oneshot(bp);
+               xfs_buf_zero(bp, 0, BBTOB(bp->b_length));
+               xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb);
+               xfs_buf_delwri_queue(bp, &buffer_list);
+               xfs_buf_relse(bp);
+
+               /* don't hold too many buffers at once */
+               if (agno % 16)
+                       continue;
+
+               error = xfs_buf_delwri_submit(&buffer_list);
+               if (error) {
+                       xfs_warn(mp,
+               "write error %d updating a secondary superblock near ag %d",
+                               error, agno);
+                       if (!saved_error)
+                               saved_error = error;
+                       continue;
+               }
+       }
+       error = xfs_buf_delwri_submit(&buffer_list);
+       if (error) {
+               xfs_warn(mp,
+               "write error %d updating a secondary superblock near ag %d",
+                       error, agno);
+       }
+
+       return saved_error ? saved_error : error;
+}
+
+/*
+ * Same behavior as xfs_sync_sb, except that it is always synchronous and it
+ * also writes the superblock buffer to disk sector 0 immediately.
+ */
+int
+xfs_sync_sb_buf(
+       struct xfs_mount        *mp)
+{
+       struct xfs_trans        *tp;
+       struct xfs_buf          *bp;
+       int                     error;
+
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_sb, 0, 0, 0, &tp);
+       if (error)
+               return error;
+
+       bp = xfs_trans_getsb(tp, mp, 0);
+       xfs_log_sb(tp);
+       xfs_trans_bhold(tp, bp);
+       xfs_trans_set_sync(tp);
+       error = xfs_trans_commit(tp);
+       if (error)
+               goto out;
+       /*
+        * write out the sb buffer to get the changes to disk
+        */
+       error = xfs_bwrite(bp);
+out:
+       xfs_buf_relse(bp);
+       return error;
+}
+
 int
 xfs_fs_geometry(
        struct xfs_sb           *sbp,
@@ -972,3 +1081,47 @@ xfs_fs_geometry(
 
        return 0;
 }
+
+/* Read a secondary superblock. */
+int
+xfs_sb_read_secondary(
+       struct xfs_mount        *mp,
+       struct xfs_trans        *tp,
+       xfs_agnumber_t          agno,
+       struct xfs_buf          **bpp)
+{
+       struct xfs_buf          *bp;
+       int                     error;
+
+       ASSERT(agno != 0 && agno != NULLAGNUMBER);
+       error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
+                       XFS_AG_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
+                       XFS_FSS_TO_BB(mp, 1), 0, &bp, &xfs_sb_buf_ops);
+       if (error)
+               return error;
+       xfs_buf_set_ref(bp, XFS_SSB_REF);
+       *bpp = bp;
+       return 0;
+}
+
+/* Get an uninitialised secondary superblock buffer. */
+int
+xfs_sb_get_secondary(
+       struct xfs_mount        *mp,
+       struct xfs_trans        *tp,
+       xfs_agnumber_t          agno,
+       struct xfs_buf          **bpp)
+{
+       struct xfs_buf          *bp;
+
+       ASSERT(agno != 0 && agno != NULLAGNUMBER);
+       bp = xfs_trans_get_buf(tp, mp->m_ddev_targp,
+                       XFS_AG_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
+                       XFS_FSS_TO_BB(mp, 1), 0);
+       if (!bp)
+               return -ENOMEM;
+       bp->b_ops = &xfs_sb_buf_ops;
+       xfs_buf_oneshot(bp);
+       *bpp = bp;
+       return 0;
+}