xfs: online repair of realtime bitmaps
authorDarrick J. Wong <djwong@kernel.org>
Fri, 15 Dec 2023 18:03:43 +0000 (10:03 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Fri, 15 Dec 2023 18:03:43 +0000 (10:03 -0800)
Fix all the file metadata surrounding the realtime bitmap file, which
includes the rt geometry, file size, forks, and space mappings.  The
bitmap contents themselves cannot be fixed without rt rmap, so that will
come later.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/Makefile
fs/xfs/scrub/repair.h
fs/xfs/scrub/rtbitmap.c
fs/xfs/scrub/rtbitmap.h [new file with mode: 0644]
fs/xfs/scrub/rtbitmap_repair.c [new file with mode: 0644]
fs/xfs/scrub/scrub.c

index a7830df42c4eb1af5f2f6470a4c98aa45aa7494c..e7727451d1c87f3e0132a801943fb6a8ab9ed6ca 100644 (file)
@@ -192,5 +192,9 @@ xfs-y                               += $(addprefix scrub/, \
                                   refcount_repair.o \
                                   repair.o \
                                   )
+
+xfs-$(CONFIG_XFS_RT)           += $(addprefix scrub/, \
+                                  rtbitmap_repair.o \
+                                  )
 endif
 endif
index f0f9c5194e8d93fb09c43f031206657adcd098ba..86cf86037fe0affcb18ea342308f0c4aef61ea26 100644 (file)
@@ -115,6 +115,12 @@ int xrep_bmap_data(struct xfs_scrub *sc);
 int xrep_bmap_attr(struct xfs_scrub *sc);
 int xrep_bmap_cow(struct xfs_scrub *sc);
 
+#ifdef CONFIG_XFS_RT
+int xrep_rtbitmap(struct xfs_scrub *sc);
+#else
+# define xrep_rtbitmap                 xrep_notsupported
+#endif /* CONFIG_XFS_RT */
+
 int xrep_reinit_pagf(struct xfs_scrub *sc);
 int xrep_reinit_pagi(struct xfs_scrub *sc);
 
@@ -177,6 +183,7 @@ xrep_setup_nothing(
 #define xrep_bmap_data                 xrep_notsupported
 #define xrep_bmap_attr                 xrep_notsupported
 #define xrep_bmap_cow                  xrep_notsupported
+#define xrep_rtbitmap                  xrep_notsupported
 
 #endif /* CONFIG_XFS_ONLINE_REPAIR */
 
index 578b935ca93fb296cf85d5d1a0db7c9baeb48732..441ca99776527453a19b2e709f2482a758fe5af0 100644 (file)
 #include "xfs_bit.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
-
-struct xchk_rtbitmap {
-       uint64_t                rextents;
-       uint64_t                rbmblocks;
-       unsigned int            rextslog;
-};
+#include "scrub/repair.h"
+#include "scrub/rtbitmap.h"
 
 /* Set us up with the realtime metadata locked. */
 int
@@ -38,7 +34,13 @@ xchk_setup_rtbitmap(
                return -ENOMEM;
        sc->buf = rtb;
 
-       error = xchk_trans_alloc(sc, 0);
+       if (xchk_could_repair(sc)) {
+               error = xrep_setup_rtbitmap(sc, rtb);
+               if (error)
+                       return error;
+       }
+
+       error = xchk_trans_alloc(sc, rtb->resblks);
        if (error)
                return error;
 
diff --git a/fs/xfs/scrub/rtbitmap.h b/fs/xfs/scrub/rtbitmap.h
new file mode 100644 (file)
index 0000000..85304ff
--- /dev/null
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2023 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __XFS_SCRUB_RTBITMAP_H__
+#define __XFS_SCRUB_RTBITMAP_H__
+
+struct xchk_rtbitmap {
+       uint64_t                rextents;
+       uint64_t                rbmblocks;
+       unsigned int            rextslog;
+       unsigned int            resblks;
+};
+
+#ifdef CONFIG_XFS_ONLINE_REPAIR
+int xrep_setup_rtbitmap(struct xfs_scrub *sc, struct xchk_rtbitmap *rtb);
+#else
+# define xrep_setup_rtbitmap(sc, rtb)  (0)
+#endif /* CONFIG_XFS_ONLINE_REPAIR */
+
+#endif /* __XFS_SCRUB_RTBITMAP_H__ */
diff --git a/fs/xfs/scrub/rtbitmap_repair.c b/fs/xfs/scrub/rtbitmap_repair.c
new file mode 100644 (file)
index 0000000..46f5d5f
--- /dev/null
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2023 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_btree.h"
+#include "xfs_log_format.h"
+#include "xfs_trans.h"
+#include "xfs_inode.h"
+#include "xfs_bit.h"
+#include "xfs_bmap.h"
+#include "xfs_bmap_btree.h"
+#include "scrub/scrub.h"
+#include "scrub/common.h"
+#include "scrub/trace.h"
+#include "scrub/repair.h"
+#include "scrub/xfile.h"
+#include "scrub/rtbitmap.h"
+
+/* Set up to repair the realtime bitmap file metadata. */
+int
+xrep_setup_rtbitmap(
+       struct xfs_scrub        *sc,
+       struct xchk_rtbitmap    *rtb)
+{
+       struct xfs_mount        *mp = sc->mp;
+       unsigned long long      blocks = 0;
+
+       /*
+        * Reserve enough blocks to write out a completely new bmbt for a
+        * maximally fragmented bitmap file.  We do not hold the rtbitmap
+        * ILOCK yet, so this is entirely speculative.
+        */
+       blocks = xfs_bmbt_calc_size(mp, mp->m_sb.sb_rbmblocks);
+       if (blocks > UINT_MAX)
+               return -EOPNOTSUPP;
+
+       rtb->resblks += blocks;
+       return 0;
+}
+
+/*
+ * Make sure that the given range of the data fork of the realtime file is
+ * mapped to written blocks.  The caller must ensure that the inode is joined
+ * to the transaction.
+ */
+STATIC int
+xrep_rtbitmap_data_mappings(
+       struct xfs_scrub        *sc,
+       xfs_filblks_t           len)
+{
+       struct xfs_bmbt_irec    map;
+       xfs_fileoff_t           off = 0;
+       int                     error;
+
+       ASSERT(sc->ip != NULL);
+
+       while (off < len) {
+               int             nmaps = 1;
+
+               /*
+                * If we have a real extent mapping this block then we're
+                * in ok shape.
+                */
+               error = xfs_bmapi_read(sc->ip, off, len - off, &map, &nmaps,
+                               XFS_DATA_FORK);
+               if (error)
+                       return error;
+               if (nmaps == 0) {
+                       ASSERT(nmaps != 0);
+                       return -EFSCORRUPTED;
+               }
+
+               /*
+                * Written extents are ok.  Holes are not filled because we
+                * do not know the freespace information.
+                */
+               if (xfs_bmap_is_written_extent(&map) ||
+                   map.br_startblock == HOLESTARTBLOCK) {
+                       off = map.br_startoff + map.br_blockcount;
+                       continue;
+               }
+
+               /*
+                * If we find a delalloc reservation then something is very
+                * very wrong.  Bail out.
+                */
+               if (map.br_startblock == DELAYSTARTBLOCK)
+                       return -EFSCORRUPTED;
+
+               /* Make sure we're really converting an unwritten extent. */
+               if (map.br_state != XFS_EXT_UNWRITTEN) {
+                       ASSERT(map.br_state == XFS_EXT_UNWRITTEN);
+                       return -EFSCORRUPTED;
+               }
+
+               /* Make sure this block has a real zeroed extent mapped. */
+               nmaps = 1;
+               error = xfs_bmapi_write(sc->tp, sc->ip, map.br_startoff,
+                               map.br_blockcount,
+                               XFS_BMAPI_CONVERT | XFS_BMAPI_ZERO,
+                               0, &map, &nmaps);
+               if (error)
+                       return error;
+               if (nmaps != 1)
+                       return -EFSCORRUPTED;
+
+               /* Commit new extent and all deferred work. */
+               error = xrep_defer_finish(sc);
+               if (error)
+                       return error;
+
+               off = map.br_startoff + map.br_blockcount;
+       }
+
+       return 0;
+}
+
+/* Fix broken rt volume geometry. */
+STATIC int
+xrep_rtbitmap_geometry(
+       struct xfs_scrub        *sc,
+       struct xchk_rtbitmap    *rtb)
+{
+       struct xfs_mount        *mp = sc->mp;
+       struct xfs_trans        *tp = sc->tp;
+
+       /* Superblock fields */
+       if (mp->m_sb.sb_rextents != rtb->rextents)
+               xfs_trans_mod_sb(sc->tp, XFS_TRANS_SB_REXTENTS,
+                               rtb->rextents - mp->m_sb.sb_rextents);
+
+       if (mp->m_sb.sb_rbmblocks != rtb->rbmblocks)
+               xfs_trans_mod_sb(tp, XFS_TRANS_SB_RBMBLOCKS,
+                               rtb->rbmblocks - mp->m_sb.sb_rbmblocks);
+
+       if (mp->m_sb.sb_rextslog != rtb->rextslog)
+               xfs_trans_mod_sb(tp, XFS_TRANS_SB_REXTSLOG,
+                               rtb->rextslog - mp->m_sb.sb_rextslog);
+
+       /* Fix broken isize */
+       sc->ip->i_disk_size = roundup_64(sc->ip->i_disk_size,
+                                        mp->m_sb.sb_blocksize);
+
+       if (sc->ip->i_disk_size < XFS_FSB_TO_B(mp, rtb->rbmblocks))
+               sc->ip->i_disk_size = XFS_FSB_TO_B(mp, rtb->rbmblocks);
+
+       xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE);
+       return xrep_roll_trans(sc);
+}
+
+/* Repair the realtime bitmap file metadata. */
+int
+xrep_rtbitmap(
+       struct xfs_scrub        *sc)
+{
+       struct xchk_rtbitmap    *rtb = sc->buf;
+       struct xfs_mount        *mp = sc->mp;
+       unsigned long long      blocks = 0;
+       int                     error;
+
+       /* Impossibly large rtbitmap means we can't touch the filesystem. */
+       if (rtb->rbmblocks > U32_MAX)
+               return 0;
+
+       /*
+        * If the size of the rt bitmap file is larger than what we reserved,
+        * figure out if we need to adjust the block reservation in the
+        * transaction.
+        */
+       blocks = xfs_bmbt_calc_size(mp, rtb->rbmblocks);
+       if (blocks > UINT_MAX)
+               return -EOPNOTSUPP;
+       if (blocks > rtb->resblks) {
+               error = xfs_trans_reserve_more(sc->tp, blocks, 0);
+               if (error)
+                       return error;
+
+               rtb->resblks += blocks;
+       }
+
+       /* Fix inode core and forks. */
+       error = xrep_metadata_inode_forks(sc);
+       if (error)
+               return error;
+
+       xfs_trans_ijoin(sc->tp, sc->ip, 0);
+
+       /* Ensure no unwritten extents. */
+       error = xrep_rtbitmap_data_mappings(sc, rtb->rbmblocks);
+       if (error)
+               return error;
+
+       /* Fix inconsistent bitmap geometry */
+       return xrep_rtbitmap_geometry(sc, rtb);
+}
index a7019c9bba0c54cce12402798d1085c929180d0e..c33480894229e38ed23c6e222a912fa35eb8e5e3 100644 (file)
@@ -328,7 +328,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
                .type   = ST_FS,
                .setup  = xchk_setup_rtbitmap,
                .scrub  = xchk_rtbitmap,
-               .repair = xrep_notsupported,
+               .repair = xrep_rtbitmap,
        },
        [XFS_SCRUB_TYPE_RTSUM] = {      /* realtime summary */
                .type   = ST_FS,