// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2020-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong */ #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 = mp->m_rsumblocks; blocks += xfs_bmbt_calc_size(mp, blocks) * 2; if (blocks > UINT_MAX) return -EOPNOTSUPP; rts->resblks += blocks; return 0; } 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; int error; /* We require the rmapbt to rebuild anything. */ if (!xfs_has_rmapbt(mp)) return -EOPNOTSUPP; /* We require atomic file exchange range to rebuild anything. */ if (!xfs_has_exchange_range(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. */ xfs_trans_ijoin(sc->tp, sc->ip, 0); xfs_trans_ijoin(sc->tp, sc->tempip, 0); error = xrep_tempfile_prealloc(sc, 0, rts->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, rts->rsumblocks, xrep_rtsummary_prep_buf, rts); if (error) return error; error = xrep_tempfile_set_isize(sc, XFS_FSB_TO_B(mp, rts->rsumblocks)); 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_rsumblocks = rts->rsumblocks; /* Free the old rtsummary blocks if they're not in use. */ return xrep_reap_ifork(sc, sc->tempip, XFS_DATA_FORK); }