Commit c6904f67 authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

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: default avatar"Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 8defee8d
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
@@ -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,43 @@ 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));

	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 +263,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 +337,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,
+1 −0
Original line number Diff line number Diff line
@@ -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);
+41 −0
Original line number Diff line number Diff line
@@ -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
+2 −0
Original line number Diff line number Diff line
@@ -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 {