summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2020-03-17 14:05:24 -0700
committerDarrick J. Wong <darrick.wong@oracle.com>2020-06-01 21:16:46 -0700
commit967c77d10b3e5b19d5750ae2b4952064e82965a5 (patch)
tree7db21a56bdd1ff7c6152686acba4f1109ced90dd
parent91d1184e1106e9f2e4ec13bec61db9c8f5928bf0 (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/Makefile1
-rw-r--r--fs/xfs/scrub/repair.h3
-rw-r--r--fs/xfs/scrub/rtbitmap.c35
-rw-r--r--fs/xfs/scrub/rtbitmap_repair.c235
-rw-r--r--fs/xfs/scrub/scrub.c2
-rw-r--r--fs/xfs/scrub/trace.h52
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 */