]> www.infradead.org Git - users/hch/xfs.git/commitdiff
xfs: support repairing metadata btrees rooted in metadir inodes
authorDarrick J. Wong <djwong@kernel.org>
Tue, 15 Oct 2024 19:40:07 +0000 (12:40 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 5 Nov 2024 21:36:27 +0000 (13:36 -0800)
Adapt the repair code so that we can stage a new btree in the data fork
area of a metadir inode and reap the old blocks.  We already have nearly
all of the infrastructure; the only parts that were missing were the
metadata inode reservation handling.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
fs/xfs/scrub/newbt.c
fs/xfs/scrub/newbt.h
fs/xfs/scrub/reap.c
fs/xfs/scrub/reap.h

index 70af27d987342f8ea8d616ef24b8e763f8522f12..5e0fa3c5c67084429483c42dda3d54bc54fee6a0 100644 (file)
@@ -19,6 +19,8 @@
 #include "xfs_rmap.h"
 #include "xfs_ag.h"
 #include "xfs_defer.h"
+#include "xfs_metafile.h"
+#include "xfs_quota.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
 #include "scrub/trace.h"
@@ -120,6 +122,44 @@ xrep_newbt_init_inode(
        return 0;
 }
 
+/*
+ * Initialize accounting resources for staging a new metadata inode btree.
+ * If the metadata file has a space reservation, the caller must adjust that
+ * reservation when committing the new ondisk btree.
+ */
+int
+xrep_newbt_init_metadir_inode(
+       struct xrep_newbt               *xnr,
+       struct xfs_scrub                *sc)
+{
+       struct xfs_owner_info           oinfo;
+       struct xfs_ifork                *ifp;
+
+       ASSERT(xfs_is_metadir_inode(sc->ip));
+       ASSERT(XFS_IS_DQDETACHED(sc->ip));
+
+       xfs_rmap_ino_bmbt_owner(&oinfo, sc->ip->i_ino, XFS_DATA_FORK);
+
+       ifp = kmem_cache_zalloc(xfs_ifork_cache, XCHK_GFP_FLAGS);
+       if (!ifp)
+               return -ENOMEM;
+
+       /*
+        * Allocate new metadir btree blocks with XFS_AG_RESV_NONE because the
+        * inode metadata space reservations can only account allocated space
+        * to the i_nblocks.  We do not want to change the inode core fields
+        * until we're ready to commit the new tree, so we allocate the blocks
+        * as if they were regular file blocks.  This exposes us to a higher
+        * risk of the repair being cancelled due to ENOSPC.
+        */
+       xrep_newbt_init_ag(xnr, sc, &oinfo,
+                       XFS_INO_TO_FSB(sc->mp, sc->ip->i_ino),
+                       XFS_AG_RESV_NONE);
+       xnr->ifake.if_fork = ifp;
+       xnr->ifake.if_fork_size = xfs_inode_fork_size(sc->ip, XFS_DATA_FORK);
+       return 0;
+}
+
 /*
  * Initialize accounting resources for staging a new btree.  Callers are
  * expected to add their own reservations (and clean them up) manually.
@@ -224,6 +264,7 @@ xrep_newbt_alloc_ag_blocks(
        int                     error = 0;
 
        ASSERT(sc->sa.pag != NULL);
+       ASSERT(xnr->resv != XFS_AG_RESV_METAFILE);
 
        while (nr_blocks > 0) {
                struct xfs_alloc_arg    args = {
@@ -297,6 +338,8 @@ xrep_newbt_alloc_file_blocks(
        struct xfs_mount        *mp = sc->mp;
        int                     error = 0;
 
+       ASSERT(xnr->resv != XFS_AG_RESV_METAFILE);
+
        while (nr_blocks > 0) {
                struct xfs_alloc_arg    args = {
                        .tp             = sc->tp,
index 3d804d31af24a87296a1cf98a779b2c9a3824250..5ce785599287be17a8df00c0682a37d316ba6496 100644 (file)
@@ -63,6 +63,7 @@ void xrep_newbt_init_ag(struct xrep_newbt *xnr, struct xfs_scrub *sc,
                enum xfs_ag_resv_type resv);
 int xrep_newbt_init_inode(struct xrep_newbt *xnr, struct xfs_scrub *sc,
                int whichfork, const struct xfs_owner_info *oinfo);
+int xrep_newbt_init_metadir_inode(struct xrep_newbt *xnr, struct xfs_scrub *sc);
 int xrep_newbt_alloc_blocks(struct xrep_newbt *xnr, uint64_t nr_blocks);
 int xrep_newbt_add_extent(struct xrep_newbt *xnr, struct xfs_perag *pag,
                xfs_agblock_t agbno, xfs_extlen_t len);
index 08230952053b7d254a7c7c231d024b69ec9109b8..4d7f1b82dc559d10a5cfb913c445e658ac1ec3e6 100644 (file)
@@ -33,6 +33,7 @@
 #include "xfs_attr.h"
 #include "xfs_attr_remote.h"
 #include "xfs_defer.h"
+#include "xfs_metafile.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
 #include "scrub/trace.h"
@@ -390,6 +391,8 @@ xreap_agextent_iter(
        xfs_fsblock_t           fsbno;
        int                     error = 0;
 
+       ASSERT(rs->resv != XFS_AG_RESV_METAFILE);
+
        fsbno = xfs_agbno_to_fsb(sc->sa.pag, agbno);
 
        /*
@@ -675,6 +678,44 @@ xrep_reap_fsblocks(
        return 0;
 }
 
+/*
+ * Dispose of every block of an old metadata btree that used to be rooted in a
+ * metadata directory file.
+ */
+int
+xrep_reap_metadir_fsblocks(
+       struct xfs_scrub                *sc,
+       struct xfsb_bitmap              *bitmap)
+{
+       /*
+        * Reap old metadir btree blocks with XFS_AG_RESV_NONE because the old
+        * blocks are no longer mapped by the inode, and inode metadata space
+        * reservations can only account freed space to the i_nblocks.
+        */
+       struct xfs_owner_info           oinfo;
+       struct xreap_state              rs = {
+               .sc                     = sc,
+               .oinfo                  = &oinfo,
+               .resv                   = XFS_AG_RESV_NONE,
+       };
+       int                             error;
+
+       ASSERT(xfs_has_rmapbt(sc->mp));
+       ASSERT(sc->ip != NULL);
+       ASSERT(xfs_is_metadir_inode(sc->ip));
+
+       xfs_rmap_ino_bmbt_owner(&oinfo, sc->ip->i_ino, XFS_DATA_FORK);
+
+       error = xfsb_bitmap_walk(bitmap, xreap_fsmeta_extent, &rs);
+       if (error)
+               return error;
+
+       if (xreap_dirty(&rs))
+               return xrep_defer_finish(sc);
+
+       return 0;
+}
+
 /*
  * Metadata files are not supposed to share blocks with anything else.
  * If blocks are shared, we remove the reverse mapping (thus reducing the
index 3f2f1775e29db4e71734e786eb0dac2246cf087c..70e5e6bbb8d38d601b430ec48a3b518663e8416c 100644 (file)
@@ -14,6 +14,8 @@ int xrep_reap_agblocks(struct xfs_scrub *sc, struct xagb_bitmap *bitmap,
 int xrep_reap_fsblocks(struct xfs_scrub *sc, struct xfsb_bitmap *bitmap,
                const struct xfs_owner_info *oinfo);
 int xrep_reap_ifork(struct xfs_scrub *sc, struct xfs_inode *ip, int whichfork);
+int xrep_reap_metadir_fsblocks(struct xfs_scrub *sc,
+               struct xfsb_bitmap *bitmap);
 
 /* Buffer cache scan context. */
 struct xrep_bufscan {