diff options
author | Darrick J. Wong <djwong@kernel.org> | 2021-09-01 11:17:59 -0700 |
---|---|---|
committer | Darrick J. Wong <djwong@kernel.org> | 2021-12-15 17:29:17 -0800 |
commit | e74c55a50a140b6388185cc62bdcbe115c993cd0 (patch) | |
tree | 4c01a3402e5571cf58311298732a2304de3f2fd3 /fs/xfs/scrub | |
parent | 09b3c0d5935229e067369692c20a8a57e2581ed4 (diff) |
xfs: online repair of realtime file bmaps
Repair the block mappings of realtime files.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Diffstat (limited to 'fs/xfs/scrub')
-rw-r--r-- | fs/xfs/scrub/bmap_repair.c | 101 | ||||
-rw-r--r-- | fs/xfs/scrub/repair.c | 68 | ||||
-rw-r--r-- | fs/xfs/scrub/repair.h | 9 |
3 files changed, 174 insertions, 4 deletions
diff --git a/fs/xfs/scrub/bmap_repair.c b/fs/xfs/scrub/bmap_repair.c index 188370660efc..d3ed1c884353 100644 --- a/fs/xfs/scrub/bmap_repair.c +++ b/fs/xfs/scrub/bmap_repair.c @@ -24,6 +24,7 @@ #include "xfs_bmap_btree.h" #include "xfs_rmap.h" #include "xfs_rmap_btree.h" +#include "xfs_rtrmap_btree.h" #include "xfs_refcount.h" #include "xfs_quota.h" #include "xfs_ialloc.h" @@ -256,6 +257,96 @@ xrep_bmap_scan_ag( return error; } +/* Check for any obvious errors or conflicts in the file mapping. */ +STATIC int +xrep_bmap_check_rtfork_rmap( + struct xfs_scrub *sc, + const struct xfs_rmap_irec *rec) +{ + /* xattr extents are never stored on realtime devices */ + if (rec->rm_flags & XFS_RMAP_ATTR_FORK) + return -EFSCORRUPTED; + + /* bmbt blocks are never stored on realtime devices */ + if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK) + return -EFSCORRUPTED; + + /* Data extents for non-rt files are never stored on the rt device. */ + if (!XFS_IS_REALTIME_INODE(sc->ip)) + return -EFSCORRUPTED; + + /* Check the file offsets and physical extents. */ + if (!xfs_verify_fileext(sc->mp, rec->rm_offset, rec->rm_blockcount)) + return -EFSCORRUPTED; + + /* Check that this fits in the rt volume. */ + if (!xfs_verify_rtext(sc->mp, rec->rm_startblock, rec->rm_blockcount)) + return -EFSCORRUPTED; + + /* Make sure this isn't free space. */ + return xrep_require_rtext_inuse(sc, rec->rm_startblock, + rec->rm_blockcount); +} + +/* Record realtime extents that belong to this inode's fork. */ +STATIC int +xrep_bmap_walk_rtrmap( + struct xfs_btree_cur *cur, + const struct xfs_rmap_irec *rec, + void *priv) +{ + struct xrep_bmap *rb = priv; + int error = 0; + + if (xchk_should_terminate(rb->sc, &error)) + return error; + + /* Skip extents which are not owned by this inode and fork. */ + if (rec->rm_owner != rb->sc->ip->i_ino) + return 0; + + error = xrep_bmap_check_rtfork_rmap(rb->sc, rec); + if (error) + return error; + + /* + * Record all blocks allocated to this file even if the extent isn't + * for the fork we're rebuilding so that we can reset di_nblocks later. + */ + rb->nblocks += rec->rm_blockcount; + + /* If this rmap isn't for the fork we want, we're done. */ + if (rb->whichfork == XFS_DATA_FORK && + (rec->rm_flags & XFS_RMAP_ATTR_FORK)) + return 0; + if (rb->whichfork == XFS_ATTR_FORK && + !(rec->rm_flags & XFS_RMAP_ATTR_FORK)) + return 0; + + return xrep_bmap_from_rmap(rb, rec->rm_offset, rec->rm_startblock, + rec->rm_blockcount, + rec->rm_flags & XFS_RMAP_UNWRITTEN); +} + +/* Scan the realtime reverse mappings to build the new extent map. */ +STATIC int +xrep_bmap_scan_rt( + struct xrep_bmap *rb) +{ + struct xfs_scrub *sc = rb->sc; + int error; + + if (xrep_is_rtmeta_ino(sc, sc->ip->i_ino)) + return 0; + + xchk_rt_lock(sc, &sc->sr); + xrep_rt_btcur_init(sc, &sc->sr); + error = xfs_rmap_query_all(sc->sr.rmap_cur, xrep_bmap_walk_rtrmap, rb); + xchk_rt_btcur_free(&sc->sr); + xchk_rt_unlock(sc, &sc->sr); + return error; +} + /* * Collect block mappings for this fork of this inode and decide if we have * enough space to rebuild. Caller is responsible for cleaning up the list if @@ -270,6 +361,12 @@ xrep_bmap_find_mappings( xfs_agnumber_t agno; int error = 0; + if (xfs_has_realtime(sc->mp)) { + error = xrep_bmap_scan_rt(rb); + if (error) + return error; + } + /* Iterate the rmaps for extents. */ for_each_perag(sc->mp, agno, pag) { error = xrep_bmap_scan_ag(rb, pag); @@ -605,10 +702,6 @@ xrep_bmap_check_inputs( return -EINVAL; } - /* Don't know how to rebuild realtime data forks. */ - if (XFS_IS_REALTIME_INODE(sc->ip)) - return -EOPNOTSUPP; - return 0; } diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c index e3e7f75778d6..1b9649407fa1 100644 --- a/fs/xfs/scrub/repair.c +++ b/fs/xfs/scrub/repair.c @@ -38,6 +38,8 @@ #include "xfs_extfree_item.h" #include "xfs_reflink.h" #include "xfs_health.h" +#include "xfs_rtrmap_btree.h" +#include "xfs_rtalloc.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/trace.h" @@ -2047,6 +2049,20 @@ xrep_ag_init( return 0; } +/* Initialize all the btree cursors for a RT repair. */ +void +xrep_rt_btcur_init( + struct xfs_scrub *sc, + struct xchk_rt *sr) +{ + struct xfs_mount *mp = sc->mp; + + if (sc->sm->sm_type != XFS_SCRUB_TYPE_RTRMAPBT && + xfs_has_rtrmapbt(mp)) + sr->rmap_cur = xfs_rtrmapbt_init_cursor(mp, sc->tp, + mp->m_rrmapip); +} + /* Reinitialize the per-AG block reservation for the AG we just fixed. */ int xrep_reset_perag_resv( @@ -2221,3 +2237,55 @@ xrep_dotdot_lookup( return ino; } + +#ifdef CONFIG_XFS_RT +/* Ensure that all rt blocks in the given range are not marked free. */ +int +xrep_require_rtext_inuse( + struct xfs_scrub *sc, + xfs_rtblock_t rtbno, + xfs_filblks_t len) +{ + struct xfs_mount *mp = sc->mp; + xfs_rtblock_t startext; + xfs_rtblock_t endext; + uint32_t mod; + bool is_free = false; + int error; + + /* Round the starting rt extent down and the end rt extent up. */ + startext = div_u64_rem(rtbno, mp->m_sb.sb_rextsize, &mod); + endext = div_u64_rem(rtbno + len - 1, mp->m_sb.sb_rextsize, &mod); + + error = xfs_rtalloc_extent_is_free(mp, sc->tp, startext, + endext - startext + 1, &is_free); + if (error) + return error; + if (is_free) + return -EFSCORRUPTED; + + return 0; +} +#endif + +/* Are we looking at a realtime metadata inode? */ +bool +xrep_is_rtmeta_ino( + struct xfs_scrub *sc, + xfs_ino_t ino) +{ + /* + * All filesystems have rt bitmap and summary inodes, even if they + * don't have an rt section. + */ + if (ino == sc->mp->m_rbmip->i_ino) + return true; + if (ino == sc->mp->m_rsumip->i_ino) + return true; + + /* Newer rt metadata files are not guaranteed to exist */ + if (sc->mp->m_rrmapip && ino == sc->mp->m_rrmapip->i_ino) + return true; + + return false; +} diff --git a/fs/xfs/scrub/repair.h b/fs/xfs/scrub/repair.h index c198f7cd3f99..e9ded3aa25d8 100644 --- a/fs/xfs/scrub/repair.h +++ b/fs/xfs/scrub/repair.h @@ -89,6 +89,15 @@ int xrep_setup_ag_allocbt(struct xfs_scrub *sc); void xrep_ag_btcur_init(struct xfs_scrub *sc, struct xchk_ag *sa); int xrep_ag_init(struct xfs_scrub *sc, struct xfs_perag *pag, struct xchk_ag *sa); +void xrep_rt_btcur_init(struct xfs_scrub *sc, struct xchk_rt *sr); + +#ifdef CONFIG_XFS_RT +int xrep_require_rtext_inuse(struct xfs_scrub *sc, xfs_rtblock_t rtbno, + xfs_filblks_t len); +#else +# define xrep_require_rtext_inuse(sc, rtbno, len) (-ENOSYS) +#endif +bool xrep_is_rtmeta_ino(struct xfs_scrub *sc, xfs_ino_t ino); /* Metadata revalidators */ |