Commit 8b309acd authored by Chandan Babu R's avatar Chandan Babu R

Merge tag 'repair-rtsummary-6.10_2024-04-15' of...

Merge tag 'repair-rtsummary-6.10_2024-04-15' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into xfs-6.10-mergeA

xfs: online repair of realtime summaries

We now have all the infrastructure we need to repair file metadata.
We'll begin with the realtime summary file, because it is the least
complex data structure.  To support this we need to add three more
pieces to the temporary file code from the previous patchset --
preallocating space in the temp file, formatting metadata into that
space and writing the blocks to disk, and swapping the fork mappings
atomically.

After that, the actual reconstruction of the realtime summary
information is pretty simple, since we can simply write the incore
copy computed by the rtsummary scrubber to the temporary file, swap the
contents, and reap the old blocks.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Signed-off-by: default avatarChandan Babu R <chandanbabu@kernel.org>

* tag 'repair-rtsummary-6.10_2024-04-15' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux:
  xfs: online repair of realtime summaries
  xfs: teach the tempfile to set up atomic file content exchanges
  xfs: support preallocating and copying content into temporary files
parents 783c5170 abf039e2
......@@ -212,6 +212,7 @@ xfs-y += $(addprefix scrub/, \
xfs-$(CONFIG_XFS_RT) += $(addprefix scrub/, \
rtbitmap_repair.o \
rtsummary_repair.o \
)
xfs-$(CONFIG_XFS_QUOTA) += $(addprefix scrub/, \
......
......@@ -31,6 +31,7 @@
#include "xfs_ag.h"
#include "xfs_error.h"
#include "xfs_quota.h"
#include "xfs_exchmaps.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/trace.h"
......
......@@ -126,8 +126,10 @@ int xrep_fscounters(struct xfs_scrub *sc);
#ifdef CONFIG_XFS_RT
int xrep_rtbitmap(struct xfs_scrub *sc);
int xrep_rtsummary(struct xfs_scrub *sc);
#else
# define xrep_rtbitmap xrep_notsupported
# define xrep_rtsummary xrep_notsupported
#endif /* CONFIG_XFS_RT */
#ifdef CONFIG_XFS_QUOTA
......@@ -212,6 +214,7 @@ xrep_setup_nothing(
#define xrep_quotacheck xrep_notsupported
#define xrep_nlinks xrep_notsupported
#define xrep_fscounters xrep_notsupported
#define xrep_rtsummary xrep_notsupported
#endif /* CONFIG_XFS_ONLINE_REPAIR */
......
......@@ -17,10 +17,14 @@
#include "xfs_bit.h"
#include "xfs_bmap.h"
#include "xfs_sb.h"
#include "xfs_exchmaps.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/trace.h"
#include "scrub/xfile.h"
#include "scrub/repair.h"
#include "scrub/tempexch.h"
#include "scrub/rtsummary.h"
/*
* Realtime Summary
......@@ -32,18 +36,6 @@
* (potentially large) amount of data in pageable memory.
*/
struct xchk_rtsummary {
struct xfs_rtalloc_args args;
uint64_t rextents;
uint64_t rbmblocks;
uint64_t rsumsize;
unsigned int rsumlevels;
/* Memory buffer for the summary comparison. */
union xfs_suminfo_raw words[];
};
/* Set us up to check the rtsummary file. */
int
xchk_setup_rtsummary(
......@@ -60,6 +52,12 @@ xchk_setup_rtsummary(
return -ENOMEM;
sc->buf = rts;
if (xchk_could_repair(sc)) {
error = xrep_setup_rtsummary(sc, rts);
if (error)
return error;
}
/*
* Create an xfile to construct a new rtsummary file. The xfile allows
* us to avoid pinning kernel memory for this purpose.
......@@ -70,7 +68,7 @@ xchk_setup_rtsummary(
if (error)
return error;
error = xchk_trans_alloc(sc, 0);
error = xchk_trans_alloc(sc, rts->resblks);
if (error)
return error;
......@@ -135,7 +133,7 @@ xfsum_store(
sumoff << XFS_WORDLOG);
}
static inline int
inline int
xfsum_copyout(
struct xfs_scrub *sc,
xfs_rtsumoff_t sumoff,
......@@ -362,7 +360,12 @@ xchk_rtsummary(
error = xchk_rtsum_compare(sc);
out_rbm:
/* Unlock the rtbitmap since we're done with it. */
/*
* Unlock the rtbitmap since we're done with it. All other writers of
* the rt free space metadata grab the bitmap and summary ILOCKs in
* that order, so we're still protected against allocation activities
* even if we continue on to the repair function.
*/
xfs_iunlock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
return error;
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2020-2024 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#ifndef __XFS_SCRUB_RTSUMMARY_H__
#define __XFS_SCRUB_RTSUMMARY_H__
struct xchk_rtsummary {
#ifdef CONFIG_XFS_ONLINE_REPAIR
struct xrep_tempexch tempexch;
#endif
struct xfs_rtalloc_args args;
uint64_t rextents;
uint64_t rbmblocks;
uint64_t rsumsize;
unsigned int rsumlevels;
unsigned int resblks;
/* suminfo position of xfile as we write buffers to disk. */
xfs_rtsumoff_t prep_wordoff;
/* Memory buffer for the summary comparison. */
union xfs_suminfo_raw words[];
};
int xfsum_copyout(struct xfs_scrub *sc, xfs_rtsumoff_t sumoff,
union xfs_suminfo_raw *rawinfo, unsigned int nr_words);
#ifdef CONFIG_XFS_ONLINE_REPAIR
int xrep_setup_rtsummary(struct xfs_scrub *sc, struct xchk_rtsummary *rts);
#else
# define xrep_setup_rtsummary(sc, rts) (0)
#endif /* CONFIG_XFS_ONLINE_REPAIR */
#endif /* __XFS_SCRUB_RTSUMMARY_H__ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2020-2024 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_rtalloc.h"
#include "xfs_inode.h"
#include "xfs_bit.h"
#include "xfs_bmap.h"
#include "xfs_bmap_btree.h"
#include "xfs_exchmaps.h"
#include "xfs_rtbitmap.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/trace.h"
#include "scrub/repair.h"
#include "scrub/tempfile.h"
#include "scrub/tempexch.h"
#include "scrub/reap.h"
#include "scrub/xfile.h"
#include "scrub/rtsummary.h"
/* Set us up to repair the rtsummary file. */
int
xrep_setup_rtsummary(
struct xfs_scrub *sc,
struct xchk_rtsummary *rts)
{
struct xfs_mount *mp = sc->mp;
unsigned long long blocks;
int error;
error = xrep_tempfile_create(sc, S_IFREG);
if (error)
return error;
/*
* If we're doing a repair, we reserve enough blocks to write out a
* completely new summary file, plus twice as many blocks as we would
* need if we can only allocate one block per data fork mapping. This
* should cover the preallocation of the temporary file and exchanging
* the extent mappings.
*
* We cannot use xfs_exchmaps_estimate because we have not yet
* constructed the replacement rtsummary and therefore do not know how
* many extents it will use. By the time we do, we will have a dirty
* transaction (which we cannot drop because we cannot drop the
* rtsummary ILOCK) and cannot ask for more reservation.
*/
blocks = XFS_B_TO_FSB(mp, mp->m_rsumsize);
blocks += xfs_bmbt_calc_size(mp, blocks) * 2;
if (blocks > UINT_MAX)
return -EOPNOTSUPP;
rts->resblks += blocks;
/*
* Grab support for atomic file content exchanges before we allocate
* any transactions or grab ILOCKs.
*/
return xrep_tempexch_enable(sc);
}
static int
xrep_rtsummary_prep_buf(
struct xfs_scrub *sc,
struct xfs_buf *bp,
void *data)
{
struct xchk_rtsummary *rts = data;
struct xfs_mount *mp = sc->mp;
union xfs_suminfo_raw *ondisk;
int error;
rts->args.mp = sc->mp;
rts->args.tp = sc->tp;
rts->args.sumbp = bp;
ondisk = xfs_rsumblock_infoptr(&rts->args, 0);
rts->args.sumbp = NULL;
bp->b_ops = &xfs_rtbuf_ops;
error = xfsum_copyout(sc, rts->prep_wordoff, ondisk, mp->m_blockwsize);
if (error)
return error;
rts->prep_wordoff += mp->m_blockwsize;
xfs_trans_buf_set_type(sc->tp, bp, XFS_BLFT_RTSUMMARY_BUF);
return 0;
}
/* Repair the realtime summary. */
int
xrep_rtsummary(
struct xfs_scrub *sc)
{
struct xchk_rtsummary *rts = sc->buf;
struct xfs_mount *mp = sc->mp;
xfs_filblks_t rsumblocks;
int error;
/* We require the rmapbt to rebuild anything. */
if (!xfs_has_rmapbt(mp))
return -EOPNOTSUPP;
/* Walk away if we disagree on the size of the rt bitmap. */
if (rts->rbmblocks != mp->m_sb.sb_rbmblocks)
return 0;
/* Make sure any problems with the fork are fixed. */
error = xrep_metadata_inode_forks(sc);
if (error)
return error;
/*
* Try to take ILOCK_EXCL of the temporary file. We had better be the
* only ones holding onto this inode, but we can't block while holding
* the rtsummary file's ILOCK_EXCL.
*/
while (!xrep_tempfile_ilock_nowait(sc)) {
if (xchk_should_terminate(sc, &error))
return error;
delay(1);
}
/* Make sure we have space allocated for the entire summary file. */
rsumblocks = XFS_B_TO_FSB(mp, rts->rsumsize);
xfs_trans_ijoin(sc->tp, sc->ip, 0);
xfs_trans_ijoin(sc->tp, sc->tempip, 0);
error = xrep_tempfile_prealloc(sc, 0, rsumblocks);
if (error)
return error;
/* Last chance to abort before we start committing fixes. */
if (xchk_should_terminate(sc, &error))
return error;
/* Copy the rtsummary file that we generated. */
error = xrep_tempfile_copyin(sc, 0, rsumblocks,
xrep_rtsummary_prep_buf, rts);
if (error)
return error;
error = xrep_tempfile_set_isize(sc, rts->rsumsize);
if (error)
return error;
/*
* Now exchange the contents. Nothing in repair uses the temporary
* buffer, so we can reuse it for the tempfile exchrange information.
*/
error = xrep_tempexch_trans_reserve(sc, XFS_DATA_FORK, &rts->tempexch);
if (error)
return error;
error = xrep_tempexch_contents(sc, &rts->tempexch);
if (error)
return error;
/* Reset incore state and blow out the summary cache. */
if (mp->m_rsum_cache)
memset(mp->m_rsum_cache, 0xFF, mp->m_sb.sb_rbmblocks);
mp->m_rsumlevels = rts->rsumlevels;
mp->m_rsumsize = rts->rsumsize;
/* Free the old rtsummary blocks if they're not in use. */
return xrep_reap_ifork(sc, sc->tempip, XFS_DATA_FORK);
}
......@@ -18,6 +18,7 @@
#include "xfs_buf_mem.h"
#include "xfs_rmap.h"
#include "xfs_exchrange.h"
#include "xfs_exchmaps.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/trace.h"
......@@ -149,14 +150,15 @@ xchk_probe(
/* Scrub setup and teardown */
#define FSGATES_MASK (XCHK_FSGATES_ALL | XREP_FSGATES_ALL)
static inline void
xchk_fsgates_disable(
struct xfs_scrub *sc)
{
if (!(sc->flags & XCHK_FSGATES_ALL))
if (!(sc->flags & FSGATES_MASK))
return;
trace_xchk_fsgates_disable(sc, sc->flags & XCHK_FSGATES_ALL);
trace_xchk_fsgates_disable(sc, sc->flags & FSGATES_MASK);
if (sc->flags & XCHK_FSGATES_DRAIN)
xfs_drain_wait_disable();
......@@ -170,8 +172,9 @@ xchk_fsgates_disable(
if (sc->flags & XCHK_FSGATES_RMAP)
xfs_rmap_hook_disable();
sc->flags &= ~XCHK_FSGATES_ALL;
sc->flags &= ~FSGATES_MASK;
}
#undef FSGATES_MASK
/* Free all the resources and finish the transactions. */
STATIC int
......@@ -352,7 +355,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
.type = ST_FS,
.setup = xchk_setup_rtsummary,
.scrub = xchk_rtsummary,
.repair = xrep_notsupported,
.repair = xrep_rtsummary,
},
[XFS_SCRUB_TYPE_UQUOTA] = { /* user quota */
.type = ST_FS,
......
......@@ -131,6 +131,7 @@ struct xfs_scrub {
#define XCHK_FSGATES_QUOTA (1U << 4) /* quota live update enabled */
#define XCHK_FSGATES_DIRENTS (1U << 5) /* directory live update enabled */
#define XCHK_FSGATES_RMAP (1U << 6) /* rmapbt live update enabled */
#define XREP_FSGATES_EXCHANGE_RANGE (1U << 29) /* uses file content exchange */
#define XREP_RESET_PERAG_RESV (1U << 30) /* must reset AG space reservation */
#define XREP_ALREADY_FIXED (1U << 31) /* checking our repair work */
......@@ -145,6 +146,12 @@ struct xfs_scrub {
XCHK_FSGATES_DIRENTS | \
XCHK_FSGATES_RMAP)
/*
* The sole XREP_FSGATES* flag reflects a log intent item that is protected
* by a log-incompat feature flag. No code patching in use here.
*/
#define XREP_FSGATES_ALL (XREP_FSGATES_EXCHANGE_RANGE)
/* Metadata scrubbers */
int xchk_tester(struct xfs_scrub *sc);
int xchk_superblock(struct xfs_scrub *sc);
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2022-2024 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#ifndef __XFS_SCRUB_TEMPEXCH_H__
#define __XFS_SCRUB_TEMPEXCH_H__
#ifdef CONFIG_XFS_ONLINE_REPAIR
struct xrep_tempexch {
struct xfs_exchmaps_req req;
};
int xrep_tempexch_enable(struct xfs_scrub *sc);
int xrep_tempexch_trans_reserve(struct xfs_scrub *sc, int whichfork,
struct xrep_tempexch *ti);
int xrep_tempexch_contents(struct xfs_scrub *sc, struct xrep_tempexch *ti);
#endif /* CONFIG_XFS_ONLINE_REPAIR */
#endif /* __XFS_SCRUB_TEMPEXCH_H__ */
This diff is collapsed.
......@@ -17,6 +17,21 @@ void xrep_tempfile_iounlock(struct xfs_scrub *sc);
void xrep_tempfile_ilock(struct xfs_scrub *sc);
bool xrep_tempfile_ilock_nowait(struct xfs_scrub *sc);
void xrep_tempfile_iunlock(struct xfs_scrub *sc);
int xrep_tempfile_prealloc(struct xfs_scrub *sc, xfs_fileoff_t off,
xfs_filblks_t len);
enum xfs_blft;
typedef int (*xrep_tempfile_copyin_fn)(struct xfs_scrub *sc,
struct xfs_buf *bp, void *data);
int xrep_tempfile_copyin(struct xfs_scrub *sc, xfs_fileoff_t off,
xfs_filblks_t len, xrep_tempfile_copyin_fn fn, void *data);
int xrep_tempfile_set_isize(struct xfs_scrub *sc, unsigned long long isize);
int xrep_tempfile_roll_trans(struct xfs_scrub *sc);
#else
static inline void xrep_tempfile_iolock_both(struct xfs_scrub *sc)
{
......
......@@ -114,6 +114,7 @@ TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_HEALTHY);
{ XCHK_FSGATES_QUOTA, "fsgates_quota" }, \
{ XCHK_FSGATES_DIRENTS, "fsgates_dirents" }, \
{ XCHK_FSGATES_RMAP, "fsgates_rmap" }, \
{ XREP_FSGATES_EXCHANGE_RANGE, "fsgates_exchrange" }, \
{ XREP_RESET_PERAG_RESV, "reset_perag_resv" }, \
{ XREP_ALREADY_FIXED, "already_fixed" }
......@@ -2314,6 +2315,45 @@ TRACE_EVENT(xrep_tempfile_create,
__entry->temp_inum)
);
DECLARE_EVENT_CLASS(xrep_tempfile_class,
TP_PROTO(struct xfs_scrub *sc, int whichfork,
struct xfs_bmbt_irec *irec),
TP_ARGS(sc, whichfork, irec),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(int, whichfork)
__field(xfs_fileoff_t, lblk)
__field(xfs_filblks_t, len)
__field(xfs_fsblock_t, pblk)
__field(int, state)
),
TP_fast_assign(
__entry->dev = sc->mp->m_super->s_dev;
__entry->ino = sc->tempip->i_ino;
__entry->whichfork = whichfork;
__entry->lblk = irec->br_startoff;
__entry->len = irec->br_blockcount;
__entry->pblk = irec->br_startblock;
__entry->state = irec->br_state;
),
TP_printk("dev %d:%d ino 0x%llx whichfork %s fileoff 0x%llx fsbcount 0x%llx startblock 0x%llx state %d",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__print_symbolic(__entry->whichfork, XFS_WHICHFORK_STRINGS),
__entry->lblk,
__entry->len,
__entry->pblk,
__entry->state)
);
#define DEFINE_XREP_TEMPFILE_EVENT(name) \
DEFINE_EVENT(xrep_tempfile_class, name, \
TP_PROTO(struct xfs_scrub *sc, int whichfork, \
struct xfs_bmbt_irec *irec), \
TP_ARGS(sc, whichfork, irec))
DEFINE_XREP_TEMPFILE_EVENT(xrep_tempfile_prealloc);
DEFINE_XREP_TEMPFILE_EVENT(xrep_tempfile_copyin);
TRACE_EVENT(xreap_ifork_extent,
TP_PROTO(struct xfs_scrub *sc, struct xfs_inode *ip, int whichfork,
const struct xfs_bmbt_irec *irec),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment