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-10-22 16:41:17 -0700
commit1bc71fc8c51d5b0ac41558cba475e013b3cf1fb8 (patch)
tree94c2a37ee4d3f08166088b661c55694778025220
parent89b052a624167d8f8d39c706669e3b0ae6107173 (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.c124
-rw-r--r--fs/xfs/scrub/trace.h33
-rw-r--r--fs/xfs/xfs_mount.h5
-rw-r--r--fs/xfs/xfs_super.c3
5 files changed, 163 insertions, 6 deletions
diff --git a/fs/xfs/libxfs/xfs_rmap.c b/fs/xfs/libxfs/xfs_rmap.c
index a6be6c7fe6e5..abd0f3fd4e28 100644
--- a/fs/xfs/libxfs/xfs_rmap.c
+++ b/fs/xfs/libxfs/xfs_rmap.c
@@ -827,6 +827,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 b8e53895dfa4..513aef875554 100644
--- a/fs/xfs/scrub/rtrmap_repair.c
+++ b/fs/xfs/scrub/rtrmap_repair.c
@@ -67,6 +67,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 */
@@ -77,6 +80,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;
@@ -128,13 +134,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 = xfs_btree_mem_head_read_buf(rr->rtrmap_btree->target, 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);
@@ -143,10 +152,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;
}
@@ -567,6 +584,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);
@@ -817,6 +841,78 @@ 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)
+{
+ /*
+ * 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 = xfs_btree_mem_head_read_buf(rr->rtrmap_btree->target, 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(
@@ -830,6 +926,7 @@ xrep_rtrmapbt(
return -ENOMEM;
rr->sc = sc;
+ mutex_init(&rr->lock);
xbitmap_init(&rr->old_rtrmapbt_blocks);
/* Set up some storage */
@@ -842,29 +939,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 8a7c31e3855b..25fa1eda4162 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -2252,6 +2252,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 05b791aa24d1..8a2e2c132e39 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -275,6 +275,11 @@ typedef struct xfs_mount {
/* online file link count check stuff */
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;
+#endif
+
#if IS_ENABLED(CONFIG_XFS_ONLINE_SCRUB) && IS_ENABLED(CONFIG_XFS_RT)
/*
* Counter of live intents. We track the number of log intent items
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 3786ee4420ac..f2f00fe7d4dd 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -2010,6 +2010,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.