diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2020-03-17 14:05:24 -0700 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2020-06-01 21:16:46 -0700 |
commit | 967c77d10b3e5b19d5750ae2b4952064e82965a5 (patch) | |
tree | 7db21a56bdd1ff7c6152686acba4f1109ced90dd | |
parent | 91d1184e1106e9f2e4ec13bec61db9c8f5928bf0 (diff) |
xfs: online repair of realtime bitmaps
Rebuild the realtime bitmap from the realtime rmap btree.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-rw-r--r-- | fs/xfs/Makefile | 1 | ||||
-rw-r--r-- | fs/xfs/scrub/repair.h | 3 | ||||
-rw-r--r-- | fs/xfs/scrub/rtbitmap.c | 35 | ||||
-rw-r--r-- | fs/xfs/scrub/rtbitmap_repair.c | 235 | ||||
-rw-r--r-- | fs/xfs/scrub/scrub.c | 2 | ||||
-rw-r--r-- | fs/xfs/scrub/trace.h | 52 |
6 files changed, 326 insertions, 2 deletions
diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile index 97f6f9de102b..22762789ae01 100644 --- a/fs/xfs/Makefile +++ b/fs/xfs/Makefile @@ -191,6 +191,7 @@ xfs-y += $(addprefix scrub/, \ xfs-$(CONFIG_XFS_QUOTA) += scrub/quota_repair.o xfs-$(CONFIG_XFS_RT) += $(addprefix scrub/, \ + rtbitmap_repair.o \ rtsummary_repair.o \ ) endif diff --git a/fs/xfs/scrub/repair.h b/fs/xfs/scrub/repair.h index b135c655ccbd..845630096e27 100644 --- a/fs/xfs/scrub/repair.h +++ b/fs/xfs/scrub/repair.h @@ -100,8 +100,10 @@ int xrep_quota(struct xfs_scrub *sc); #endif /* CONFIG_XFS_QUOTA */ #ifdef CONFIG_XFS_RT int xrep_rtsummary(struct xfs_scrub *sc); +int xrep_rtbitmap(struct xfs_scrub *sc); #else # define xrep_rtsummary xrep_notsupported +# define xrep_rtbitmap xrep_notsupported #endif /* CONFIG_XFS_RT */ struct xrep_newbt_resv { @@ -223,6 +225,7 @@ xrep_rmapbt_setup( #define xrep_fscounters xrep_notsupported #define xrep_quota xrep_notsupported #define xrep_rtsummary xrep_notsupported +#define xrep_rtbitmap xrep_notsupported #endif /* CONFIG_XFS_ONLINE_REPAIR */ diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c index 7557271afe0b..cd1bbcdda477 100644 --- a/fs/xfs/scrub/rtbitmap.c +++ b/fs/xfs/scrub/rtbitmap.c @@ -20,6 +20,7 @@ #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/btree.h" +#include "scrub/xfile.h" /* Set us up with the realtime metadata locked. */ int @@ -27,12 +28,44 @@ xchk_setup_rtbitmap( struct xfs_scrub *sc, struct xfs_inode *ip) { + unsigned long long resblks = 0; int error; - error = xchk_trans_alloc(sc, 0); + /* + * If we're doing a repair, we reserve 2x the bitmap blocks: once for + * the new bitmap contents and again for the bmbt blocks and the + * remapping operation. + */ + if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) { + loff_t bmp_bytes; + + /* Create an xfile to hold our reconstructed bitmap. */ + bmp_bytes = XFS_FSB_TO_B(sc->mp, sc->mp->m_sb.sb_rbmblocks); + sc->xfile = xfile_create("rtbitmap", bmp_bytes); + if (IS_ERR(sc->xfile)) + return PTR_ERR(sc->xfile); + + resblks = sc->mp->m_sb.sb_rbmblocks * 2; + if (resblks > UINT_MAX) + return -EOPNOTSUPP; + } + error = xchk_trans_alloc(sc, resblks); if (error) return error; +#ifdef CONFIG_XFS_ONLINE_REPAIR + if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) { + /* + * Allocate a memory buffer for faster creation of the new + * bitmap. + */ + sc->buf = kmem_alloc_large(sc->mp->m_sb.sb_blocksize, + KM_MAYFAIL); + if (!sc->buf) + return -ENOMEM; + } +#endif + sc->ilock_flags = XFS_ILOCK_EXCL | XFS_ILOCK_RTBITMAP; sc->ip = sc->mp->m_rbmip; xfs_ilock(sc->ip, sc->ilock_flags); diff --git a/fs/xfs/scrub/rtbitmap_repair.c b/fs/xfs/scrub/rtbitmap_repair.c new file mode 100644 index 000000000000..30d212d20ed9 --- /dev/null +++ b/fs/xfs/scrub/rtbitmap_repair.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 Oracle. All Rights Reserved. + * Author: Darrick J. Wong <darrick.wong@oracle.com> + */ +#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_rmap.h" +#include "xfs_rtrmap_btree.h" +#include "scrub/scrub.h" +#include "scrub/common.h" +#include "scrub/trace.h" +#include "scrub/repair.h" +#include "scrub/xfile.h" + +struct xrep_rtbmp { + struct xfs_scrub *sc; + + /* The next rt block we expect to see during our rtrmapbt walk. */ + xfs_rtblock_t next_rtbno; +}; + +/* + * Compute the byte offset of the xfs_rtword_t corresponding to the given rt + * extent's location in the bitmap. + */ +static inline loff_t +rtword_off( + xfs_rtblock_t rt_ext) +{ + return (rt_ext >> XFS_NBWORDLOG) * sizeof(xfs_rtword_t); +} + +/* Perform a logical OR operation on an rtword in the incore bitmap. */ +static int +xrep_rtbitmap_or( + struct xrep_rtbmp *rb, + xfs_rtblock_t rt_ext, + xfs_rtword_t mask) +{ + loff_t pos; + xfs_rtword_t word; + int error; + + pos = rtword_off(rt_ext); + error = xfile_io(rb->sc->xfile, XFILE_IO_READ, &pos, &word, + sizeof(word)); + if (error) + return error; + + trace_xrep_rtbitmap_or(rb->sc->mp, rt_ext, rtword_off(rt_ext), mask, + word); + + word |= mask; + pos = rtword_off(rt_ext); + return xfile_io(rb->sc->xfile, XFILE_IO_WRITE, &pos, &word, + sizeof(word)); +} + +/* Mark a range free in the incore rtbitmap. */ +STATIC int +xrep_rtbitmap_mark_free( + struct xrep_rtbmp *rb, + xfs_rtblock_t next) +{ + struct xfs_mount *mp = rb->sc->mp; + xfs_rtblock_t start_ext; + xfs_rtblock_t next_ext; + loff_t pos, endpos; + unsigned int bit; + unsigned int mod; + xfs_rtword_t mask; + int error; + + /* Convert rt blocks to rt extents. */ + start_ext = div_u64_rem(rb->next_rtbno, mp->m_sb.sb_rextsize, &mod); + if (mod) + return -EFSCORRUPTED; + next_ext = div_u64_rem(next, mp->m_sb.sb_rextsize, &mod); + if (mod) + return -EFSCORRUPTED; + + trace_xrep_rtbitmap_record_free(mp, start_ext, next_ext - 1); + + /* Set bits as needed to round start_ext up to the nearest word. */ + bit = start_ext & (XFS_NBWORD - 1); + if (bit) { + xfs_rtblock_t len = next_ext - start_ext; + unsigned int lastbit; + + lastbit = XFS_RTMIN(bit + len, XFS_NBWORD); + mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit; + + error = xrep_rtbitmap_or(rb, start_ext, mask); + if (error || lastbit - bit == len) + return error; + start_ext += XFS_NBWORD - bit; + } + + /* Set bits as needed to round next_ext down to the nearest word. */ + bit = next_ext & (XFS_NBWORD - 1); + if (bit) { + mask = ((xfs_rtword_t)1 << bit) - 1; + + error = xrep_rtbitmap_or(rb, next_ext, mask); + if (error || start_ext + bit == next_ext) + return error; + next_ext -= bit; + } + + trace_xrep_rtbitmap_record_free_bulk(mp, start_ext, next_ext - 1); + + /* Set all the bytes in between, up to a whole fs block at once. */ + pos = start_ext >> XFS_NBBYLOG; + endpos = next_ext >> XFS_NBBYLOG; + + while (pos < endpos) { + loff_t rem; + size_t count = min_t(loff_t, endpos - pos, + mp->m_sb.sb_blocksize); + + /* Try to get us aligned to an even blocksize. */ + rem = pos & (mp->m_sb.sb_blocksize - 1); + if (rem) + count = min_t(loff_t, count, + mp->m_sb.sb_blocksize - rem); + + error = xfile_io(rb->sc->xfile, XFILE_IO_WRITE, &pos, + rb->sc->buf, count); + if (error) + return error; + } + + return 0; +} + +/* Set free space in the rtbitmap based on rtrmapbt records. */ +STATIC int +xrep_rtbitmap_walk_rtrmap( + struct xfs_btree_cur *cur, + struct xfs_rmap_irec *rec, + void *priv) +{ + struct xrep_rtbmp *rb = priv; + int error = 0; + + if (xchk_should_terminate(rb->sc, &error)) + return error; + + if (rb->next_rtbno < rec->rm_startblock) { + error = xrep_rtbitmap_mark_free(rb, rec->rm_startblock); + if (error) + return error; + } + + rb->next_rtbno = max(rb->next_rtbno, + rec->rm_startblock + rec->rm_blockcount); + return 0; +} + +/* + * Walk the rtrmapbt to find all the gaps between records, and mark the gaps + * in the realtime bitmap that we're computing. + */ +STATIC int +xrep_rtbitmap_find_freespace( + struct xrep_rtbmp *rb) +{ + struct xfs_scrub *sc = rb->sc; + struct xfs_btree_cur *cur; + int error; + + /* Prepare a buffer of ones so that we can accelerate bulk setting. */ + memset(sc->buf, 0xFF, sc->mp->m_sb.sb_blocksize); + + xfs_ilock(sc->mp->m_rrmapip, XFS_ILOCK_SHARED); + cur = xfs_rtrmapbt_init_cursor(sc->mp, sc->tp, sc->mp->m_rrmapip); + + error = xfs_rmap_query_all(cur, xrep_rtbitmap_walk_rtrmap, rb); + + xfs_btree_del_cursor(cur, error); + xfs_iunlock(sc->mp->m_rrmapip, XFS_ILOCK_SHARED); + + if (error || sc->mp->m_sb.sb_rblocks == rb->next_rtbno) + return error; + + /* Mark all space to the end of the rt device free. */ + return xrep_rtbitmap_mark_free(rb, sc->mp->m_sb.sb_rblocks); +} + +/* Repair the realtime bitmap. */ +int +xrep_rtbitmap( + struct xfs_scrub *sc) +{ + struct xrep_rtbmp rb = { + .sc = sc, + }; + int error; + + /* We require the realtime rmapbt to rebuild anything. */ + if (!xfs_sb_version_hasrtrmapbt(&sc->mp->m_sb)) + return -EOPNOTSUPP; + + /* Generate the new rtbitmap data. */ + error = xrep_rtbitmap_find_freespace(&rb); + if (error) + return error; + + /* Make sure any problems with the fork are fixed. */ + error = xrep_metadata_inode_forks(sc); + if (error) + return error; + + /* Make sure we have space allocated for the entire bitmap file. */ + xfs_trans_ijoin(sc->tp, sc->ip, 0); + error = xrep_fallocate(sc, 0, sc->mp->m_sb.sb_rbmblocks); + if (error) + return error; + + /* Copy the bitmap file that we generated. */ + return xrep_set_file_contents(sc, &xfs_rtbuf_ops, XFS_BLFT_RTBITMAP_BUF, + XFS_FSB_TO_B(sc->mp, sc->mp->m_sb.sb_rbmblocks)); +} diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c index 9525409840d8..5825e76534d9 100644 --- a/fs/xfs/scrub/scrub.c +++ b/fs/xfs/scrub/scrub.c @@ -326,7 +326,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = { .setup = xchk_setup_rtbitmap, .scrub = xchk_rtbitmap, .has = xfs_sb_version_hasrealtime, - .repair = xrep_notsupported, + .repair = xrep_rtbitmap, }, [XFS_SCRUB_TYPE_RTSUM] = { /* realtime summary */ .type = ST_FS, diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index 10c4e594079b..09aeaed01d6b 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -1268,6 +1268,58 @@ TRACE_EVENT(xrep_dir_insert_rec, __print_symbolic(__entry->ftype, XFS_DIR3_FTYPE_STR)) ); +DECLARE_EVENT_CLASS(xrep_rtbitmap_class, + TP_PROTO(struct xfs_mount *mp, xfs_rtblock_t start, xfs_rtblock_t end), + TP_ARGS(mp, start, end), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_rtblock_t, start) + __field(xfs_rtblock_t, end) + ), + TP_fast_assign( + __entry->dev = mp->m_super->s_dev; + __entry->start = start; + __entry->end = end; + ), + TP_printk("dev %d:%d start %llu end %llu", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->start, + __entry->end) +); +#define DEFINE_REPAIR_RTBITMAP_EVENT(name) \ +DEFINE_EVENT(xrep_rtbitmap_class, name, \ + TP_PROTO(struct xfs_mount *mp, xfs_rtblock_t start, \ + xfs_rtblock_t end), \ + TP_ARGS(mp, start, end)) +DEFINE_REPAIR_RTBITMAP_EVENT(xrep_rtbitmap_record_free); +DEFINE_REPAIR_RTBITMAP_EVENT(xrep_rtbitmap_record_free_bulk); + +TRACE_EVENT(xrep_rtbitmap_or, + TP_PROTO(struct xfs_mount *mp, xfs_rtblock_t start, loff_t pos, + xfs_rtword_t mask, xfs_rtword_t word), + TP_ARGS(mp, start, pos, mask, word), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_rtblock_t, start) + __field(loff_t, pos) + __field(unsigned int, mask) + __field(unsigned int, word) + ), + TP_fast_assign( + __entry->dev = mp->m_super->s_dev; + __entry->start = start; + __entry->pos = pos; + __entry->mask = mask; + __entry->word = word; + ), + TP_printk("dev %d:%d start %llu pos %lld mask 0x%x word 0x%x", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->start, + __entry->pos, + __entry->mask, + __entry->word) +) + #endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */ #endif /* _TRACE_XFS_SCRUB_TRACE_H */ |