xfs: support repairing metadata btrees rooted in metadir inodes

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>
Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
Darrick J. Wong 2024-11-20 16:20:42 -08:00
parent 8defee8dff
commit c6904f6788
4 changed files with 86 additions and 0 deletions

View File

@ -19,6 +19,8 @@
#include "xfs_rmap.h" #include "xfs_rmap.h"
#include "xfs_ag.h" #include "xfs_ag.h"
#include "xfs_defer.h" #include "xfs_defer.h"
#include "xfs_metafile.h"
#include "xfs_quota.h"
#include "scrub/scrub.h" #include "scrub/scrub.h"
#include "scrub/common.h" #include "scrub/common.h"
#include "scrub/trace.h" #include "scrub/trace.h"
@ -120,6 +122,43 @@ xrep_newbt_init_inode(
return 0; 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));
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 * Initialize accounting resources for staging a new btree. Callers are
* expected to add their own reservations (and clean them up) manually. * expected to add their own reservations (and clean them up) manually.
@ -224,6 +263,7 @@ xrep_newbt_alloc_ag_blocks(
int error = 0; int error = 0;
ASSERT(sc->sa.pag != NULL); ASSERT(sc->sa.pag != NULL);
ASSERT(xnr->resv != XFS_AG_RESV_METAFILE);
while (nr_blocks > 0) { while (nr_blocks > 0) {
struct xfs_alloc_arg args = { struct xfs_alloc_arg args = {
@ -297,6 +337,8 @@ xrep_newbt_alloc_file_blocks(
struct xfs_mount *mp = sc->mp; struct xfs_mount *mp = sc->mp;
int error = 0; int error = 0;
ASSERT(xnr->resv != XFS_AG_RESV_METAFILE);
while (nr_blocks > 0) { while (nr_blocks > 0) {
struct xfs_alloc_arg args = { struct xfs_alloc_arg args = {
.tp = sc->tp, .tp = sc->tp,

View File

@ -63,6 +63,7 @@ void xrep_newbt_init_ag(struct xrep_newbt *xnr, struct xfs_scrub *sc,
enum xfs_ag_resv_type resv); enum xfs_ag_resv_type resv);
int xrep_newbt_init_inode(struct xrep_newbt *xnr, struct xfs_scrub *sc, int xrep_newbt_init_inode(struct xrep_newbt *xnr, struct xfs_scrub *sc,
int whichfork, const struct xfs_owner_info *oinfo); 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_alloc_blocks(struct xrep_newbt *xnr, uint64_t nr_blocks);
int xrep_newbt_add_extent(struct xrep_newbt *xnr, struct xfs_perag *pag, int xrep_newbt_add_extent(struct xrep_newbt *xnr, struct xfs_perag *pag,
xfs_agblock_t agbno, xfs_extlen_t len); xfs_agblock_t agbno, xfs_extlen_t len);

View File

@ -33,6 +33,7 @@
#include "xfs_attr.h" #include "xfs_attr.h"
#include "xfs_attr_remote.h" #include "xfs_attr_remote.h"
#include "xfs_defer.h" #include "xfs_defer.h"
#include "xfs_metafile.h"
#include "scrub/scrub.h" #include "scrub/scrub.h"
#include "scrub/common.h" #include "scrub/common.h"
#include "scrub/trace.h" #include "scrub/trace.h"
@ -390,6 +391,8 @@ xreap_agextent_iter(
xfs_fsblock_t fsbno; xfs_fsblock_t fsbno;
int error = 0; int error = 0;
ASSERT(rs->resv != XFS_AG_RESV_METAFILE);
fsbno = xfs_agbno_to_fsb(sc->sa.pag, agbno); fsbno = xfs_agbno_to_fsb(sc->sa.pag, agbno);
/* /*
@ -675,6 +678,44 @@ xrep_reap_fsblocks(
return 0; 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. * Metadata files are not supposed to share blocks with anything else.
* If blocks are shared, we remove the reverse mapping (thus reducing the * If blocks are shared, we remove the reverse mapping (thus reducing the

View 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, int xrep_reap_fsblocks(struct xfs_scrub *sc, struct xfsb_bitmap *bitmap,
const struct xfs_owner_info *oinfo); const struct xfs_owner_info *oinfo);
int xrep_reap_ifork(struct xfs_scrub *sc, struct xfs_inode *ip, int whichfork); 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. */ /* Buffer cache scan context. */
struct xrep_bufscan { struct xrep_bufscan {