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

xfs: online repair of realtime bitmaps



Fix all the file metadata surrounding the realtime bitmap file, which
includes the rt geometry, file size, forks, and space mappings.  The
bitmap contents themselves cannot be fixed without rt rmap, so that will
come later.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent a59eb5fc
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
+7 −0
Original line number Diff line number Diff line
@@ -115,6 +115,12 @@ int xrep_bmap_data(struct xfs_scrub *sc);
int xrep_bmap_attr(struct xfs_scrub *sc);
int xrep_bmap_cow(struct xfs_scrub *sc);

#ifdef CONFIG_XFS_RT
int xrep_rtbitmap(struct xfs_scrub *sc);
#else
# define xrep_rtbitmap			xrep_notsupported
#endif /* CONFIG_XFS_RT */

int xrep_reinit_pagf(struct xfs_scrub *sc);
int xrep_reinit_pagi(struct xfs_scrub *sc);

@@ -177,6 +183,7 @@ xrep_setup_nothing(
#define xrep_bmap_data			xrep_notsupported
#define xrep_bmap_attr			xrep_notsupported
#define xrep_bmap_cow			xrep_notsupported
#define xrep_rtbitmap			xrep_notsupported

#endif /* CONFIG_XFS_ONLINE_REPAIR */

+9 −7
Original line number Diff line number Diff line
@@ -17,12 +17,8 @@
#include "xfs_bit.h"
#include "scrub/scrub.h"
#include "scrub/common.h"

struct xchk_rtbitmap {
	uint64_t		rextents;
	uint64_t		rbmblocks;
	unsigned int		rextslog;
};
#include "scrub/repair.h"
#include "scrub/rtbitmap.h"

/* Set us up with the realtime metadata locked. */
int
@@ -38,7 +34,13 @@ xchk_setup_rtbitmap(
		return -ENOMEM;
	sc->buf = rtb;

	error = xchk_trans_alloc(sc, 0);
	if (xchk_could_repair(sc)) {
		error = xrep_setup_rtbitmap(sc, rtb);
		if (error)
			return error;
	}

	error = xchk_trans_alloc(sc, rtb->resblks);
	if (error)
		return error;

+22 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (C) 2023 Oracle.  All Rights Reserved.
 * Author: Darrick J. Wong <djwong@kernel.org>
 */
#ifndef __XFS_SCRUB_RTBITMAP_H__
#define __XFS_SCRUB_RTBITMAP_H__

struct xchk_rtbitmap {
	uint64_t		rextents;
	uint64_t		rbmblocks;
	unsigned int		rextslog;
	unsigned int		resblks;
};

#ifdef CONFIG_XFS_ONLINE_REPAIR
int xrep_setup_rtbitmap(struct xfs_scrub *sc, struct xchk_rtbitmap *rtb);
#else
# define xrep_setup_rtbitmap(sc, rtb)	(0)
#endif /* CONFIG_XFS_ONLINE_REPAIR */

#endif /* __XFS_SCRUB_RTBITMAP_H__ */
+202 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (C) 2020-2023 Oracle.  All Rights Reserved.
 * Author: Darrick J. Wong <djwong@kernel.org>
 */
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_btree.h"
#include "xfs_log_format.h"
#include "xfs_trans.h"
#include "xfs_inode.h"
#include "xfs_bit.h"
#include "xfs_bmap.h"
#include "xfs_bmap_btree.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/trace.h"
#include "scrub/repair.h"
#include "scrub/xfile.h"
#include "scrub/rtbitmap.h"

/* Set up to repair the realtime bitmap file metadata. */
int
xrep_setup_rtbitmap(
	struct xfs_scrub	*sc,
	struct xchk_rtbitmap	*rtb)
{
	struct xfs_mount	*mp = sc->mp;
	unsigned long long	blocks = 0;

	/*
	 * Reserve enough blocks to write out a completely new bmbt for a
	 * maximally fragmented bitmap file.  We do not hold the rtbitmap
	 * ILOCK yet, so this is entirely speculative.
	 */
	blocks = xfs_bmbt_calc_size(mp, mp->m_sb.sb_rbmblocks);
	if (blocks > UINT_MAX)
		return -EOPNOTSUPP;

	rtb->resblks += blocks;
	return 0;
}

/*
 * Make sure that the given range of the data fork of the realtime file is
 * mapped to written blocks.  The caller must ensure that the inode is joined
 * to the transaction.
 */
STATIC int
xrep_rtbitmap_data_mappings(
	struct xfs_scrub	*sc,
	xfs_filblks_t		len)
{
	struct xfs_bmbt_irec	map;
	xfs_fileoff_t		off = 0;
	int			error;

	ASSERT(sc->ip != NULL);

	while (off < len) {
		int		nmaps = 1;

		/*
		 * If we have a real extent mapping this block then we're
		 * in ok shape.
		 */
		error = xfs_bmapi_read(sc->ip, off, len - off, &map, &nmaps,
				XFS_DATA_FORK);
		if (error)
			return error;
		if (nmaps == 0) {
			ASSERT(nmaps != 0);
			return -EFSCORRUPTED;
		}

		/*
		 * Written extents are ok.  Holes are not filled because we
		 * do not know the freespace information.
		 */
		if (xfs_bmap_is_written_extent(&map) ||
		    map.br_startblock == HOLESTARTBLOCK) {
			off = map.br_startoff + map.br_blockcount;
			continue;
		}

		/*
		 * If we find a delalloc reservation then something is very
		 * very wrong.  Bail out.
		 */
		if (map.br_startblock == DELAYSTARTBLOCK)
			return -EFSCORRUPTED;

		/* Make sure we're really converting an unwritten extent. */
		if (map.br_state != XFS_EXT_UNWRITTEN) {
			ASSERT(map.br_state == XFS_EXT_UNWRITTEN);
			return -EFSCORRUPTED;
		}

		/* Make sure this block has a real zeroed extent mapped. */
		nmaps = 1;
		error = xfs_bmapi_write(sc->tp, sc->ip, map.br_startoff,
				map.br_blockcount,
				XFS_BMAPI_CONVERT | XFS_BMAPI_ZERO,
				0, &map, &nmaps);
		if (error)
			return error;
		if (nmaps != 1)
			return -EFSCORRUPTED;

		/* Commit new extent and all deferred work. */
		error = xrep_defer_finish(sc);
		if (error)
			return error;

		off = map.br_startoff + map.br_blockcount;
	}

	return 0;
}

/* Fix broken rt volume geometry. */
STATIC int
xrep_rtbitmap_geometry(
	struct xfs_scrub	*sc,
	struct xchk_rtbitmap	*rtb)
{
	struct xfs_mount	*mp = sc->mp;
	struct xfs_trans	*tp = sc->tp;

	/* Superblock fields */
	if (mp->m_sb.sb_rextents != rtb->rextents)
		xfs_trans_mod_sb(sc->tp, XFS_TRANS_SB_REXTENTS,
				rtb->rextents - mp->m_sb.sb_rextents);

	if (mp->m_sb.sb_rbmblocks != rtb->rbmblocks)
		xfs_trans_mod_sb(tp, XFS_TRANS_SB_RBMBLOCKS,
				rtb->rbmblocks - mp->m_sb.sb_rbmblocks);

	if (mp->m_sb.sb_rextslog != rtb->rextslog)
		xfs_trans_mod_sb(tp, XFS_TRANS_SB_REXTSLOG,
				rtb->rextslog - mp->m_sb.sb_rextslog);

	/* Fix broken isize */
	sc->ip->i_disk_size = roundup_64(sc->ip->i_disk_size,
					 mp->m_sb.sb_blocksize);

	if (sc->ip->i_disk_size < XFS_FSB_TO_B(mp, rtb->rbmblocks))
		sc->ip->i_disk_size = XFS_FSB_TO_B(mp, rtb->rbmblocks);

	xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE);
	return xrep_roll_trans(sc);
}

/* Repair the realtime bitmap file metadata. */
int
xrep_rtbitmap(
	struct xfs_scrub	*sc)
{
	struct xchk_rtbitmap	*rtb = sc->buf;
	struct xfs_mount	*mp = sc->mp;
	unsigned long long	blocks = 0;
	int			error;

	/* Impossibly large rtbitmap means we can't touch the filesystem. */
	if (rtb->rbmblocks > U32_MAX)
		return 0;

	/*
	 * If the size of the rt bitmap file is larger than what we reserved,
	 * figure out if we need to adjust the block reservation in the
	 * transaction.
	 */
	blocks = xfs_bmbt_calc_size(mp, rtb->rbmblocks);
	if (blocks > UINT_MAX)
		return -EOPNOTSUPP;
	if (blocks > rtb->resblks) {
		error = xfs_trans_reserve_more(sc->tp, blocks, 0);
		if (error)
			return error;

		rtb->resblks += blocks;
	}

	/* Fix inode core and forks. */
	error = xrep_metadata_inode_forks(sc);
	if (error)
		return error;

	xfs_trans_ijoin(sc->tp, sc->ip, 0);

	/* Ensure no unwritten extents. */
	error = xrep_rtbitmap_data_mappings(sc, rtb->rbmblocks);
	if (error)
		return error;

	/* Fix inconsistent bitmap geometry */
	return xrep_rtbitmap_geometry(sc, rtb);
}
Loading