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

xfs: create subordinate scrub contexts for xchk_metadata_inode_subtype



When a file-based metadata structure is being scrubbed in
xchk_metadata_inode_subtype, we should create an entirely new scrub
context so that each scrubber doesn't trip over another's buffers.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 5f204051
Loading
Loading
Loading
Loading
+4 −19
Original line number Diff line number Diff line
@@ -1203,27 +1203,12 @@ xchk_metadata_inode_subtype(
	struct xfs_scrub	*sc,
	unsigned int		scrub_type)
{
	__u32			smtype = sc->sm->sm_type;
	unsigned int		sick_mask = sc->sick_mask;
	struct xfs_scrub_subord	*sub;
	int			error;

	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;
	default:
		ASSERT(0);
		error = -EFSCORRUPTED;
		break;
	}

	sc->sick_mask = sick_mask;
	sc->sm->sm_type = smtype;
	sub = xchk_scrub_create_subord(sc, scrub_type);
	error = sub->sc.ops->scrub(&sub->sc);
	xchk_scrub_free_subord(sub);
	return error;
}

+13 −54
Original line number Diff line number Diff line
@@ -1009,55 +1009,27 @@ 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;
	struct xfs_scrub_subord	*sub;
	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
	 * Let's see if the inode needs repair.  Use a subordinate scrub context
	 * to call 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;
	}
	sub = xchk_scrub_create_subord(sc, scrub_type);
	error = sub->sc.ops->scrub(&sub->sc);
	if (error)
		goto out;

	if (!xrep_will_attempt(sc))
	if (!xrep_will_attempt(&sub->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;
	}
	error = sub->sc.ops->repair(&sub->sc);
	if (error)
		goto out;

@@ -1066,10 +1038,10 @@ xrep_metadata_inode_subtype(
	 * that the inode will not be joined to the transaction when we exit
	 * the function.
	 */
	error = xfs_defer_finish(&sc->tp);
	error = xfs_defer_finish(&sub->sc.tp);
	if (error)
		goto out;
	error = xfs_trans_roll(&sc->tp);
	error = xfs_trans_roll(&sub->sc.tp);
	if (error)
		goto out;

@@ -1077,31 +1049,18 @@ xrep_metadata_inode_subtype(
	 * 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;
	}
	sub->sc.sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;
	error = sub->sc.ops->scrub(&sub->sc);
	if (error)
		goto out;

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

+63 −0
Original line number Diff line number Diff line
@@ -177,6 +177,39 @@ xchk_fsgates_disable(
}
#undef FSGATES_MASK

/* Free the resources associated with a scrub subtype. */
void
xchk_scrub_free_subord(
	struct xfs_scrub_subord	*sub)
{
	struct xfs_scrub	*sc = sub->parent_sc;

	ASSERT(sc->ip == sub->sc.ip);
	ASSERT(sc->orphanage == sub->sc.orphanage);
	ASSERT(sc->tempip == sub->sc.tempip);

	sc->sm->sm_type = sub->old_smtype;
	sc->sm->sm_flags = sub->old_smflags |
				(sc->sm->sm_flags & XFS_SCRUB_FLAGS_OUT);
	sc->tp = sub->sc.tp;

	if (sub->sc.buf) {
		if (sub->sc.buf_cleanup)
			sub->sc.buf_cleanup(sub->sc.buf);
		kvfree(sub->sc.buf);
	}
	if (sub->sc.xmbtp)
		xmbuf_free(sub->sc.xmbtp);
	if (sub->sc.xfile)
		xfile_destroy(sub->sc.xfile);

	sc->ilock_flags = sub->sc.ilock_flags;
	sc->orphanage_ilock_flags = sub->sc.orphanage_ilock_flags;
	sc->temp_ilock_flags = sub->sc.temp_ilock_flags;

	kfree(sub);
}

/* Free all the resources and finish the transactions. */
STATIC int
xchk_teardown(
@@ -505,6 +538,36 @@ static inline void xchk_postmortem(struct xfs_scrub *sc)
}
#endif /* CONFIG_XFS_ONLINE_REPAIR */

/*
 * Create a new scrub context from an existing one, but with a different scrub
 * type.
 */
struct xfs_scrub_subord *
xchk_scrub_create_subord(
	struct xfs_scrub	*sc,
	unsigned int		subtype)
{
	struct xfs_scrub_subord	*sub;

	sub = kzalloc(sizeof(*sub), XCHK_GFP_FLAGS);
	if (!sub)
		return ERR_PTR(-ENOMEM);

	sub->old_smtype = sc->sm->sm_type;
	sub->old_smflags = sc->sm->sm_flags;
	sub->parent_sc = sc;
	memcpy(&sub->sc, sc, sizeof(struct xfs_scrub));
	sub->sc.ops = &meta_scrub_ops[subtype];
	sub->sc.sm->sm_type = subtype;
	sub->sc.sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;
	sub->sc.buf = NULL;
	sub->sc.buf_cleanup = NULL;
	sub->sc.xfile = NULL;
	sub->sc.xmbtp = NULL;

	return sub;
}

/* Dispatch metadata scrubbing. */
int
xfs_scrub_metadata(
+11 −0
Original line number Diff line number Diff line
@@ -156,6 +156,17 @@ struct xfs_scrub {
 */
#define XREP_FSGATES_ALL	(XREP_FSGATES_EXCHANGE_RANGE)

struct xfs_scrub_subord {
	struct xfs_scrub	sc;
	struct xfs_scrub	*parent_sc;
	unsigned int		old_smtype;
	unsigned int		old_smflags;
};

struct xfs_scrub_subord *xchk_scrub_create_subord(struct xfs_scrub *sc,
		unsigned int subtype);
void xchk_scrub_free_subord(struct xfs_scrub_subord *sub);

/* Metadata scrubbers */
int xchk_tester(struct xfs_scrub *sc);
int xchk_superblock(struct xfs_scrub *sc);