summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2021-09-30 14:16:10 -0700
committerDarrick J. Wong <djwong@kernel.org>2021-12-15 17:29:31 -0800
commit18945ffd27477b82920daae333883a330c4fdbe0 (patch)
treed2469a5fae5b0f4404ad896915baf05c1024adc5
parentb7308019c38079f3def4119fa13a7f38be5a576a (diff)
xfs: hook live realtime rmap operations during a repair operation
Hook the regular realtime rmap code when an rtrmapbt repair operation is running so that we can unlock the AGF buffer to scan the filesystem and keep the in-memory btree up to date during the scan. Signed-off-by: Darrick J. Wong <djwong@kernel.org>
-rw-r--r--fs/xfs/libxfs/xfs_rmap.c4
-rw-r--r--fs/xfs/scrub/rtrmap_repair.c125
-rw-r--r--fs/xfs/scrub/trace.h33
-rw-r--r--fs/xfs/xfs_mount.h3
-rw-r--r--fs/xfs/xfs_super.c3
5 files changed, 163 insertions, 5 deletions
diff --git a/fs/xfs/libxfs/xfs_rmap.c b/fs/xfs/libxfs/xfs_rmap.c
index 70f3bdb04481..96600ec15f6b 100644
--- a/fs/xfs/libxfs/xfs_rmap.c
+++ b/fs/xfs/libxfs/xfs_rmap.c
@@ -834,6 +834,10 @@ xfs_rmap_update_hook(
if (pag)
xfs_hook_call(&pag->pag_rmap_update_hooks, op, &p);
+#ifdef CONFIG_XFS_RT
+ else
+ xfs_hook_call(&tp->t_mountp->m_rtrmap_update_hooks, op, &p);
+#endif
}
#else
# define xfs_rmap_update_hook(t, p, o, s, b, u, oi) do { } while(0)
diff --git a/fs/xfs/scrub/rtrmap_repair.c b/fs/xfs/scrub/rtrmap_repair.c
index 5cef478a6142..418bb4fc50c2 100644
--- a/fs/xfs/scrub/rtrmap_repair.c
+++ b/fs/xfs/scrub/rtrmap_repair.c
@@ -81,6 +81,9 @@ struct xrep_rtrmap {
struct xrep_newbt new_btree_info;
struct xfs_btree_bload rtrmap_bload;
+ /* lock for the xfbtree and xfile */
+ struct mutex lock;
+
/* rmap records generated from primary metadata */
struct xfbtree *rtrmap_btree;
/* in-memory btree cursor for the ->get_blocks walk */
@@ -91,6 +94,9 @@ struct xrep_rtrmap {
/* bitmap of old rtrmapbt blocks */
struct xbitmap old_rtrmapbt_blocks;
+ /* Hooks into rtrmap update code. */
+ struct notifier_block rtrmap_update_hook;
+
/* inode scan cursor */
struct xchk_iscan iscan;
@@ -142,12 +148,16 @@ xrep_rtrmap_stash(
if (xchk_should_terminate(sc, &error))
return error;
+ if (xchk_iscan_aborted(&rr->iscan))
+ return -EFSCORRUPTED;
+
trace_xrep_rtrmap_found(sc->mp, &rmap);
/* Add entry to in-memory btree. */
+ mutex_lock(&rr->lock);
error = xfbtree_head_read_buf(rr->rtrmap_btree, sc->tp, &mhead_bp);
if (error)
- return error;
+ goto out_abort;
mcur = xfs_rtrmapbt_mem_cursor(sc->mp, sc->tp, mhead_bp,
rr->rtrmap_btree);
@@ -156,10 +166,18 @@ xrep_rtrmap_stash(
if (error)
goto out_cancel;
- return xfbtree_trans_commit(rr->rtrmap_btree, sc->tp);
+ error = xfbtree_trans_commit(rr->rtrmap_btree, sc->tp);
+ if (error)
+ goto out_abort;
+
+ mutex_unlock(&rr->lock);
+ return 0;
out_cancel:
xfbtree_trans_cancel(rr->rtrmap_btree, sc->tp);
+out_abort:
+ xchk_iscan_abort(&rr->iscan);
+ mutex_unlock(&rr->lock);
return error;
}
@@ -580,6 +598,13 @@ xrep_rtrmap_find_rmaps(
if (error)
return error;
+ /*
+ * If a hook failed to update the in-memory btree, we lack the data to
+ * continue the repair.
+ */
+ if (xchk_iscan_aborted(&rr->iscan))
+ return -EFSCORRUPTED;
+
/* Scan for old rtrmap blocks. */
for_each_perag(sc->mp, agno, pag) {
error = xrep_rtrmap_scan_ag(rr, pag);
@@ -799,6 +824,80 @@ xrep_rtrmap_remove_old_tree(
return xrep_reset_imeta_reservation(rr->sc);
}
+static inline bool
+xrep_rtrmapbt_want_live_update(
+ struct xchk_iscan *iscan,
+ const struct xfs_owner_info *oi)
+{
+ if (xchk_iscan_aborted(iscan))
+ return false;
+
+ /*
+ * We scanned the CoW staging extents before we started the iscan, so
+ * we need all the updates.
+ */
+ if (XFS_RMAP_NON_INODE_OWNER(oi->oi_owner))
+ return true;
+
+ /* Ignore updates to files that the scanner hasn't visited yet. */
+ return xchk_iscan_want_live_update(iscan, oi->oi_owner);
+}
+
+/*
+ * Apply a rtrmapbt update from the regular filesystem into our shadow btree.
+ * We're running from the thread that owns the rtrmap ILOCK and is generating
+ * the update, so we must be careful about which parts of the struct
+ * xrep_rtrmap that we change.
+ */
+static int
+xrep_rtrmapbt_live_update(
+ struct notifier_block *nb,
+ unsigned long op,
+ void *data)
+{
+ struct xfs_rmap_update_params *p = data;
+ struct xrep_rtrmap *rr;
+ struct xfs_mount *mp;
+ struct xfs_btree_cur *mcur;
+ struct xfs_buf *mhead_bp;
+ int error;
+
+ rr = container_of(nb, struct xrep_rtrmap, rtrmap_update_hook);
+ mp = rr->sc->mp;
+
+ if (!xrep_rtrmapbt_want_live_update(&rr->iscan, &p->oinfo))
+ goto out_unlock;
+
+ trace_xrep_rtrmap_live_update(mp, op, p);
+
+ mutex_lock(&rr->lock);
+ error = xfbtree_head_read_buf(rr->rtrmap_btree, p->tp, &mhead_bp);
+ if (error)
+ goto out_abort;
+
+ mcur = xfs_rtrmapbt_mem_cursor(mp, p->tp, mhead_bp, rr->rtrmap_btree);
+ error = __xfs_rmap_finish_intent(mcur, op, p->startblock,
+ p->blockcount, &p->oinfo, p->unwritten);
+ xfs_btree_del_cursor(mcur, error);
+ if (error)
+ goto out_cancel;
+
+ error = xfbtree_trans_commit(rr->rtrmap_btree, p->tp);
+ if (error)
+ goto out_abort;
+
+ mutex_unlock(&rr->lock);
+ return NOTIFY_DONE;
+
+out_cancel:
+ xfbtree_trans_cancel(rr->rtrmap_btree, p->tp);
+out_abort:
+ xchk_iscan_abort(&rr->iscan);
+ mutex_unlock(&rr->lock);
+out_unlock:
+ return NOTIFY_DONE;
+}
+
/* Repair the realtime rmap btree. */
int
xrep_rtrmapbt(
@@ -812,6 +911,7 @@ xrep_rtrmapbt(
return -ENOMEM;
rr->sc = sc;
+ mutex_init(&rr->lock);
xbitmap_init(&rr->old_rtrmapbt_blocks);
/* Set up some storage */
@@ -824,29 +924,44 @@ xrep_rtrmapbt(
rr->iscan.iget_retry_delay = HZ / 10;
xchk_iscan_start(&rr->iscan);
+ /*
+ * Hook into live rtrmap operations so that we can update our in-memory
+ * btree to reflect live changes on the filesystem. Since we drop the
+ * rtrmap ILOCK to scan all the inodes, we need this piece to avoid
+ * installing a stale btree.
+ */
+ error = xfs_hook_add(&sc->mp->m_rtrmap_update_hooks,
+ &rr->rtrmap_update_hook, xrep_rtrmapbt_live_update);
+ if (error)
+ goto out_records;
+
/* Collect rmaps for realtime files. */
error = xrep_rtrmap_find_rmaps(rr);
if (error)
- goto out_records;
+ goto out_hook;
xfs_trans_ijoin(sc->tp, sc->ip, 0);
error = xrep_ino_dqattach(sc);
if (error)
- goto out_records;
+ goto out_hook;
/* Rebuild the rtrmap information. */
error = xrep_rtrmap_build_new_tree(rr);
if (error)
- goto out_records;
+ goto out_hook;
/* Kill the old tree. */
error = xrep_rtrmap_remove_old_tree(rr);
+out_hook:
+ xchk_iscan_abort(&rr->iscan);
+ xfs_hook_del(&sc->mp->m_rtrmap_update_hooks, &rr->rtrmap_update_hook);
out_records:
xchk_iscan_finish(&rr->iscan);
xfbtree_destroy(rr->rtrmap_btree);
out_bitmap:
xbitmap_destroy(&rr->old_rtrmapbt_blocks);
+ mutex_destroy(&rr->lock);
kmem_free(rr);
return error;
}
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index b0a4307a1293..72ca66587d25 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -2393,6 +2393,39 @@ TRACE_EVENT(xrep_rmap_live_update,
__entry->flags)
);
+TRACE_EVENT(xrep_rtrmap_live_update,
+ TP_PROTO(struct xfs_mount *mp, unsigned int op,
+ const struct xfs_rmap_update_params *p),
+ TP_ARGS(mp, op, p),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(unsigned int, op)
+ __field(xfs_rtblock_t, rtbno)
+ __field(xfs_filblks_t, len)
+ __field(uint64_t, owner)
+ __field(uint64_t, offset)
+ __field(unsigned int, flags)
+ ),
+ TP_fast_assign(
+ __entry->dev = mp->m_super->s_dev;
+ __entry->op = op;
+ __entry->rtbno = p->startblock;
+ __entry->len = p->blockcount;
+ xfs_owner_info_unpack(&p->oinfo, &__entry->owner,
+ &__entry->offset, &__entry->flags);
+ if (p->unwritten)
+ __entry->flags |= XFS_RMAP_UNWRITTEN;
+ ),
+ TP_printk("dev %d:%d op %s rtbno 0x%llx fsbcount 0x%llx owner 0x%llx fileoff 0x%llx flags 0x%x",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __print_symbolic(__entry->op, XFS_RMAP_INTENT_STRINGS),
+ __entry->rtbno,
+ __entry->len,
+ __entry->owner,
+ __entry->offset,
+ __entry->flags)
+);
+
#endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index 79fd3293ca36..e7da1f04d588 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -335,6 +335,9 @@ typedef struct xfs_mount {
struct xfs_hook_chain m_nlink_delta_hooks;
#ifdef CONFIG_XFS_RT
+ /* online rt rmap repair stuff */
+ struct xfs_hook_chain m_rtrmap_update_hooks;
+
/*
* We use xfs_drain to track the number of deferred log intent items
* that have been queued (but not yet processed) so that waiters (e.g.
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 8301d0932518..2d3261c1b67c 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -2002,6 +2002,9 @@ static int xfs_init_fs_context(
mp->m_allocsize_log = 16; /* 64k */
xfs_hook_init(&mp->m_nlink_delta_hooks);
+#ifdef CONFIG_XFS_RT
+ xfs_hook_init(&mp->m_rtrmap_update_hooks);
+#endif
/*
* Copy binary VFS mount flags we are interested in.