Commit 5bb4ad95 authored by Chandan Babu R's avatar Chandan Babu R
Browse files

Merge tag 'repair-rtbitmap-6.8_2023-12-15' of...

Merge tag 'repair-rtbitmap-6.8_2023-12-15' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux

 into xfs-6.8-mergeB

xfs: online repair of rt bitmap file

Add in the necessary infrastructure to check the inode and data forks of
metadata files, then apply that to the realtime bitmap file.  We won't
be able to reconstruct the contents of the rtbitmap file until rmapbt is
added for realtime volumes, but we can at least get the basics started.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Signed-off-by: default avatarChandan Babu R <chandanbabu@kernel.org>

* tag 'repair-rtbitmap-6.8_2023-12-15' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux:
  xfs: online repair of realtime bitmaps
  xfs: create a new inode fork block unmap helper
  xfs: repair the inode core and forks of a metadata inode
  xfs: always check the rtbitmap and rtsummary files
  xfs: check rt summary file geometry more thoroughly
  xfs: check rt bitmap file geometry more thoroughly
parents 98e63b91 ffd37b22
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -192,5 +192,9 @@ xfs-y += $(addprefix scrub/, \
				   refcount_repair.o \
				   repair.o \
				   )

xfs-$(CONFIG_XFS_RT)		+= $(addprefix scrub/, \
				   rtbitmap_repair.o \
				   )
endif
endif
+40 −1
Original line number Diff line number Diff line
@@ -5239,7 +5239,7 @@ xfs_bmap_del_extent_real(
 * that value.  If not all extents in the block range can be removed then
 * *done is set.
 */
int						/* error */
static int
__xfs_bunmapi(
	struct xfs_trans	*tp,		/* transaction pointer */
	struct xfs_inode	*ip,		/* incore inode */
@@ -6220,3 +6220,42 @@ xfs_bmap_validate_extent(
	return xfs_bmap_validate_extent_raw(ip->i_mount,
			XFS_IS_REALTIME_INODE(ip), whichfork, irec);
}

/*
 * Used in xfs_itruncate_extents().  This is the maximum number of extents
 * freed from a file in a single transaction.
 */
#define	XFS_ITRUNC_MAX_EXTENTS	2

/*
 * Unmap every extent in part of an inode's fork.  We don't do any higher level
 * invalidation work at all.
 */
int
xfs_bunmapi_range(
	struct xfs_trans	**tpp,
	struct xfs_inode	*ip,
	uint32_t		flags,
	xfs_fileoff_t		startoff,
	xfs_fileoff_t		endoff)
{
	xfs_filblks_t		unmap_len = endoff - startoff + 1;
	int			error = 0;

	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));

	while (unmap_len > 0) {
		ASSERT((*tpp)->t_highest_agno == NULLAGNUMBER);
		error = __xfs_bunmapi(*tpp, ip, startoff, &unmap_len, flags,
				XFS_ITRUNC_MAX_EXTENTS);
		if (error)
			goto out;

		/* free the just unmapped extents */
		error = xfs_defer_finish(tpp);
		if (error)
			goto out;
	}
out:
	return error;
}
+2 −3
Original line number Diff line number Diff line
@@ -190,9 +190,6 @@ int xfs_bmapi_read(struct xfs_inode *ip, xfs_fileoff_t bno,
int	xfs_bmapi_write(struct xfs_trans *tp, struct xfs_inode *ip,
		xfs_fileoff_t bno, xfs_filblks_t len, uint32_t flags,
		xfs_extlen_t total, struct xfs_bmbt_irec *mval, int *nmap);
int	__xfs_bunmapi(struct xfs_trans *tp, struct xfs_inode *ip,
		xfs_fileoff_t bno, xfs_filblks_t *rlen, uint32_t flags,
		xfs_extnum_t nexts);
int	xfs_bunmapi(struct xfs_trans *tp, struct xfs_inode *ip,
		xfs_fileoff_t bno, xfs_filblks_t len, uint32_t flags,
		xfs_extnum_t nexts, int *done);
@@ -273,6 +270,8 @@ int xfs_bmap_complain_bad_rec(struct xfs_inode *ip, int whichfork,
int	xfs_bmapi_remap(struct xfs_trans *tp, struct xfs_inode *ip,
		xfs_fileoff_t bno, xfs_filblks_t len, xfs_fsblock_t startblock,
		uint32_t flags);
int	xfs_bunmapi_range(struct xfs_trans **tpp, struct xfs_inode *ip,
		uint32_t flags, xfs_fileoff_t startoff, xfs_fileoff_t endoff);

extern struct kmem_cache	*xfs_bmap_intent_cache;

+13 −4
Original line number Diff line number Diff line
@@ -86,6 +86,9 @@ struct xrep_bmap {

	/* What d the REFLINK flag be set when the repair is over? */
	enum reflink_scan_state	reflink_scan;

	/* Do we allow unwritten extents? */
	bool			allow_unwritten;
};

/* Is this space extent shared?  Flag the inode if it is. */
@@ -262,6 +265,10 @@ xrep_bmap_walk_rmap(
	    !(rec->rm_flags & XFS_RMAP_ATTR_FORK))
		return 0;

	/* Reject unwritten extents if we don't allow those. */
	if ((rec->rm_flags & XFS_RMAP_UNWRITTEN) && !rb->allow_unwritten)
		return -EFSCORRUPTED;

	fsbno = XFS_AGB_TO_FSB(mp, cur->bc_ag.pag->pag_agno,
			rec->rm_startblock);

@@ -780,10 +787,11 @@ xrep_bmap_init_reflink_scan(
}

/* Repair an inode fork. */
STATIC int
int
xrep_bmap(
	struct xfs_scrub	*sc,
	int			whichfork)
	int			whichfork,
	bool			allow_unwritten)
{
	struct xrep_bmap	*rb;
	char			*descr;
@@ -803,6 +811,7 @@ xrep_bmap(
	rb->sc = sc;
	rb->whichfork = whichfork;
	rb->reflink_scan = xrep_bmap_init_reflink_scan(sc, whichfork);
	rb->allow_unwritten = allow_unwritten;

	/* Set up enough storage to handle the max records for this fork. */
	large_extcount = xfs_has_large_extent_counts(sc->mp);
@@ -846,7 +855,7 @@ int
xrep_bmap_data(
	struct xfs_scrub	*sc)
{
	return xrep_bmap(sc, XFS_DATA_FORK);
	return xrep_bmap(sc, XFS_DATA_FORK, true);
}

/* Repair an inode's attr fork. */
@@ -854,5 +863,5 @@ int
xrep_bmap_attr(
	struct xfs_scrub	*sc)
{
	return xrep_bmap(sc, XFS_ATTR_FORK);
	return xrep_bmap(sc, XFS_ATTR_FORK, false);
}
+153 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include "xfs_defer.h"
#include "xfs_errortag.h"
#include "xfs_error.h"
#include "xfs_reflink.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/trace.h"
@@ -962,3 +963,155 @@ xrep_will_attempt(

	return false;
}

/* Try to fix some part of a metadata inode by calling another scrubber. */
STATIC int
xrep_metadata_inode_subtype(
	struct xfs_scrub	*sc,
	unsigned int		scrub_type)
{
	__u32			smtype = sc->sm->sm_type;
	__u32			smflags = sc->sm->sm_flags;
	unsigned int		sick_mask = sc->sick_mask;
	int			error;

	/*
	 * Let's see if the inode needs repair.  We're going to open-code calls
	 * to the scrub and repair functions so that we can hang on to the
	 * resources that we already acquired instead of using the standard
	 * setup/teardown routines.
	 */
	sc->sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;
	sc->sm->sm_type = scrub_type;

	switch (scrub_type) {
	case XFS_SCRUB_TYPE_INODE:
		error = xchk_inode(sc);
		break;
	case XFS_SCRUB_TYPE_BMBTD:
		error = xchk_bmap_data(sc);
		break;
	case XFS_SCRUB_TYPE_BMBTA:
		error = xchk_bmap_attr(sc);
		break;
	default:
		ASSERT(0);
		error = -EFSCORRUPTED;
	}
	if (error)
		goto out;

	if (!xrep_will_attempt(sc))
		goto out;

	/*
	 * Repair some part of the inode.  This will potentially join the inode
	 * to the transaction.
	 */
	switch (scrub_type) {
	case XFS_SCRUB_TYPE_INODE:
		error = xrep_inode(sc);
		break;
	case XFS_SCRUB_TYPE_BMBTD:
		error = xrep_bmap(sc, XFS_DATA_FORK, false);
		break;
	case XFS_SCRUB_TYPE_BMBTA:
		error = xrep_bmap(sc, XFS_ATTR_FORK, false);
		break;
	}
	if (error)
		goto out;

	/*
	 * Finish all deferred intent items and then roll the transaction so
	 * that the inode will not be joined to the transaction when we exit
	 * the function.
	 */
	error = xfs_defer_finish(&sc->tp);
	if (error)
		goto out;
	error = xfs_trans_roll(&sc->tp);
	if (error)
		goto out;

	/*
	 * Clear the corruption flags and re-check the metadata that we just
	 * repaired.
	 */
	sc->sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;

	switch (scrub_type) {
	case XFS_SCRUB_TYPE_INODE:
		error = xchk_inode(sc);
		break;
	case XFS_SCRUB_TYPE_BMBTD:
		error = xchk_bmap_data(sc);
		break;
	case XFS_SCRUB_TYPE_BMBTA:
		error = xchk_bmap_attr(sc);
		break;
	}
	if (error)
		goto out;

	/* If corruption persists, the repair has failed. */
	if (xchk_needs_repair(sc->sm)) {
		error = -EFSCORRUPTED;
		goto out;
	}
out:
	sc->sick_mask = sick_mask;
	sc->sm->sm_type = smtype;
	sc->sm->sm_flags = smflags;
	return error;
}

/*
 * Repair the ondisk forks of a metadata inode.  The caller must ensure that
 * sc->ip points to the metadata inode and the ILOCK is held on that inode.
 * The inode must not be joined to the transaction before the call, and will
 * not be afterwards.
 */
int
xrep_metadata_inode_forks(
	struct xfs_scrub	*sc)
{
	bool			dirty = false;
	int			error;

	/* Repair the inode record and the data fork. */
	error = xrep_metadata_inode_subtype(sc, XFS_SCRUB_TYPE_INODE);
	if (error)
		return error;

	error = xrep_metadata_inode_subtype(sc, XFS_SCRUB_TYPE_BMBTD);
	if (error)
		return error;

	/* Make sure the attr fork looks ok before we delete it. */
	error = xrep_metadata_inode_subtype(sc, XFS_SCRUB_TYPE_BMBTA);
	if (error)
		return error;

	/* Clear the reflink flag since metadata never shares. */
	if (xfs_is_reflink_inode(sc->ip)) {
		dirty = true;
		xfs_trans_ijoin(sc->tp, sc->ip, 0);
		error = xfs_reflink_clear_inode_flag(sc->ip, &sc->tp);
		if (error)
			return error;
	}

	/*
	 * If we modified the inode, roll the transaction but don't rejoin the
	 * inode to the new transaction because xrep_bmap_data can do that.
	 */
	if (dirty) {
		error = xfs_trans_roll(&sc->tp);
		if (error)
			return error;
		dirty = false;
	}

	return 0;
}
Loading