]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: hook live rmap operations during a repair operation
authorDarrick J. Wong <djwong@kernel.org>
Mon, 22 Apr 2024 17:01:12 +0000 (10:01 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Mon, 3 Jun 2024 18:37:40 +0000 (11:37 -0700)
Source kernel commit: 7e1b84b24d257700e417bc9cd724c1efdff653d7

Hook the regular rmap code when an rmapbt 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>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
libxfs/xfs_ag.c
libxfs/xfs_ag.h
libxfs/xfs_rmap.c
libxfs/xfs_rmap.h

index 06a88128568218f7bd940ef905638ee6f0c2e445..e2fc3e88244faceb7f3ebb32a06b4d6de28ccdf9 100644 (file)
@@ -415,6 +415,7 @@ xfs_initialize_perag(
                init_waitqueue_head(&pag->pag_active_wq);
                pag->pagb_count = 0;
                pag->pagb_tree = RB_ROOT;
+               xfs_hooks_init(&pag->pag_rmap_update_hooks);
 #endif /* __KERNEL__ */
 
                error = xfs_buf_cache_init(&pag->pag_bcache);
index e019b79dbbe3d5b1c0e90d5cad5fe12ed2040e94..35de09a2516c704b87af86bd3e33ad8ff1768196 100644 (file)
@@ -120,6 +120,9 @@ struct xfs_perag {
         * inconsistencies.
         */
        struct xfs_defer_drain  pag_intents_drain;
+
+       /* Hook to feed rmapbt updates to an active online repair. */
+       struct xfs_hooks        pag_rmap_update_hooks;
 #endif /* __KERNEL__ */
 };
 
index a7be2aa92c0acc3814fa39b510c1dd5d44478174..c3195e53236601c5b1f3ec89701f78608dca284f 100644 (file)
@@ -820,6 +820,86 @@ out_error:
        return error;
 }
 
+#ifdef CONFIG_XFS_LIVE_HOOKS
+/*
+ * Use a static key here to reduce the overhead of rmapbt live updates.  If
+ * the compiler supports jump labels, the static branch will be replaced by a
+ * nop sled when there are no hook users.  Online fsck is currently the only
+ * caller, so this is a reasonable tradeoff.
+ *
+ * Note: Patching the kernel code requires taking the cpu hotplug lock.  Other
+ * parts of the kernel allocate memory with that lock held, which means that
+ * XFS callers cannot hold any locks that might be used by memory reclaim or
+ * writeback when calling the static_branch_{inc,dec} functions.
+ */
+DEFINE_STATIC_XFS_HOOK_SWITCH(xfs_rmap_hooks_switch);
+
+void
+xfs_rmap_hook_disable(void)
+{
+       xfs_hooks_switch_off(&xfs_rmap_hooks_switch);
+}
+
+void
+xfs_rmap_hook_enable(void)
+{
+       xfs_hooks_switch_on(&xfs_rmap_hooks_switch);
+}
+
+/* Call downstream hooks for a reverse mapping update. */
+static inline void
+xfs_rmap_update_hook(
+       struct xfs_trans                *tp,
+       struct xfs_perag                *pag,
+       enum xfs_rmap_intent_type       op,
+       xfs_agblock_t                   startblock,
+       xfs_extlen_t                    blockcount,
+       bool                            unwritten,
+       const struct xfs_owner_info     *oinfo)
+{
+       if (xfs_hooks_switched_on(&xfs_rmap_hooks_switch)) {
+               struct xfs_rmap_update_params   p = {
+                       .startblock     = startblock,
+                       .blockcount     = blockcount,
+                       .unwritten      = unwritten,
+                       .oinfo          = *oinfo, /* struct copy */
+               };
+
+               if (pag)
+                       xfs_hooks_call(&pag->pag_rmap_update_hooks, op, &p);
+       }
+}
+
+/* Call the specified function during a reverse mapping update. */
+int
+xfs_rmap_hook_add(
+       struct xfs_perag        *pag,
+       struct xfs_rmap_hook    *hook)
+{
+       return xfs_hooks_add(&pag->pag_rmap_update_hooks, &hook->rmap_hook);
+}
+
+/* Stop calling the specified function during a reverse mapping update. */
+void
+xfs_rmap_hook_del(
+       struct xfs_perag        *pag,
+       struct xfs_rmap_hook    *hook)
+{
+       xfs_hooks_del(&pag->pag_rmap_update_hooks, &hook->rmap_hook);
+}
+
+/* Configure rmap update hook functions. */
+void
+xfs_rmap_hook_setup(
+       struct xfs_rmap_hook    *hook,
+       notifier_fn_t           mod_fn)
+{
+       xfs_hook_setup(&hook->rmap_hook, mod_fn);
+}
+#else
+# define xfs_rmap_update_hook(t, p, o, s, b, u, oi)    do { } while (0)
+#endif /* CONFIG_XFS_LIVE_HOOKS */
+
 /*
  * Remove a reference to an extent in the rmap btree.
  */
@@ -840,7 +920,7 @@ xfs_rmap_free(
                return 0;
 
        cur = xfs_rmapbt_init_cursor(mp, tp, agbp, pag);
-
+       xfs_rmap_update_hook(tp, pag, XFS_RMAP_UNMAP, bno, len, false, oinfo);
        error = xfs_rmap_unmap(cur, bno, len, false, oinfo);
 
        xfs_btree_del_cursor(cur, error);
@@ -1092,6 +1172,7 @@ xfs_rmap_alloc(
                return 0;
 
        cur = xfs_rmapbt_init_cursor(mp, tp, agbp, pag);
+       xfs_rmap_update_hook(tp, pag, XFS_RMAP_MAP, bno, len, false, oinfo);
        error = xfs_rmap_map(cur, bno, len, false, oinfo);
 
        xfs_btree_del_cursor(cur, error);
@@ -2507,6 +2588,38 @@ xfs_rmap_finish_one_cleanup(
                xfs_trans_brelse(tp, agbp);
 }
 
+/* Commit an rmap operation into the ondisk tree. */
+int
+__xfs_rmap_finish_intent(
+       struct xfs_btree_cur            *rcur,
+       enum xfs_rmap_intent_type       op,
+       xfs_agblock_t                   bno,
+       xfs_extlen_t                    len,
+       const struct xfs_owner_info     *oinfo,
+       bool                            unwritten)
+{
+       switch (op) {
+       case XFS_RMAP_ALLOC:
+       case XFS_RMAP_MAP:
+               return xfs_rmap_map(rcur, bno, len, unwritten, oinfo);
+       case XFS_RMAP_MAP_SHARED:
+               return xfs_rmap_map_shared(rcur, bno, len, unwritten, oinfo);
+       case XFS_RMAP_FREE:
+       case XFS_RMAP_UNMAP:
+               return xfs_rmap_unmap(rcur, bno, len, unwritten, oinfo);
+       case XFS_RMAP_UNMAP_SHARED:
+               return xfs_rmap_unmap_shared(rcur, bno, len, unwritten, oinfo);
+       case XFS_RMAP_CONVERT:
+               return xfs_rmap_convert(rcur, bno, len, !unwritten, oinfo);
+       case XFS_RMAP_CONVERT_SHARED:
+               return xfs_rmap_convert_shared(rcur, bno, len, !unwritten,
+                               oinfo);
+       default:
+               ASSERT(0);
+               return -EFSCORRUPTED;
+       }
+}
+
 /*
  * Process one of the deferred rmap operations.  We pass back the
  * btree cursor to maintain our lock on the rmapbt between calls.
@@ -2573,39 +2686,14 @@ xfs_rmap_finish_one(
        unwritten = ri->ri_bmap.br_state == XFS_EXT_UNWRITTEN;
        bno = XFS_FSB_TO_AGBNO(rcur->bc_mp, ri->ri_bmap.br_startblock);
 
-       switch (ri->ri_type) {
-       case XFS_RMAP_ALLOC:
-       case XFS_RMAP_MAP:
-               error = xfs_rmap_map(rcur, bno, ri->ri_bmap.br_blockcount,
-                               unwritten, &oinfo);
-               break;
-       case XFS_RMAP_MAP_SHARED:
-               error = xfs_rmap_map_shared(rcur, bno,
-                               ri->ri_bmap.br_blockcount, unwritten, &oinfo);
-               break;
-       case XFS_RMAP_FREE:
-       case XFS_RMAP_UNMAP:
-               error = xfs_rmap_unmap(rcur, bno, ri->ri_bmap.br_blockcount,
-                               unwritten, &oinfo);
-               break;
-       case XFS_RMAP_UNMAP_SHARED:
-               error = xfs_rmap_unmap_shared(rcur, bno,
-                               ri->ri_bmap.br_blockcount, unwritten, &oinfo);
-               break;
-       case XFS_RMAP_CONVERT:
-               error = xfs_rmap_convert(rcur, bno, ri->ri_bmap.br_blockcount,
-                               !unwritten, &oinfo);
-               break;
-       case XFS_RMAP_CONVERT_SHARED:
-               error = xfs_rmap_convert_shared(rcur, bno,
-                               ri->ri_bmap.br_blockcount, !unwritten, &oinfo);
-               break;
-       default:
-               ASSERT(0);
-               error = -EFSCORRUPTED;
-       }
+       error = __xfs_rmap_finish_intent(rcur, ri->ri_type, bno,
+                       ri->ri_bmap.br_blockcount, &oinfo, unwritten);
+       if (error)
+               return error;
 
-       return error;
+       xfs_rmap_update_hook(tp, ri->ri_pag, ri->ri_type, bno,
+                       ri->ri_bmap.br_blockcount, unwritten, &oinfo);
+       return 0;
 }
 
 /*
index 58c67896d12cb3ffafb9a9147c2efa6ab6f557c3..9d01fe689497bd9a219aef87d088176e2f39f368 100644 (file)
@@ -186,6 +186,10 @@ void xfs_rmap_finish_one_cleanup(struct xfs_trans *tp,
                struct xfs_btree_cur *rcur, int error);
 int xfs_rmap_finish_one(struct xfs_trans *tp, struct xfs_rmap_intent *ri,
                struct xfs_btree_cur **pcur);
+int __xfs_rmap_finish_intent(struct xfs_btree_cur *rcur,
+               enum xfs_rmap_intent_type op, xfs_agblock_t bno,
+               xfs_extlen_t len, const struct xfs_owner_info *oinfo,
+               bool unwritten);
 
 int xfs_rmap_lookup_le_range(struct xfs_btree_cur *cur, xfs_agblock_t bno,
                uint64_t owner, uint64_t offset, unsigned int flags,
@@ -235,4 +239,29 @@ extern struct kmem_cache   *xfs_rmap_intent_cache;
 int __init xfs_rmap_intent_init_cache(void);
 void xfs_rmap_intent_destroy_cache(void);
 
+/*
+ * Parameters for tracking reverse mapping changes.  The hook function arg
+ * parameter is enum xfs_rmap_intent_type, and the rest is below.
+ */
+struct xfs_rmap_update_params {
+       xfs_agblock_t                   startblock;
+       xfs_extlen_t                    blockcount;
+       struct xfs_owner_info           oinfo;
+       bool                            unwritten;
+};
+
+#ifdef CONFIG_XFS_LIVE_HOOKS
+
+struct xfs_rmap_hook {
+       struct xfs_hook                 rmap_hook;
+};
+
+void xfs_rmap_hook_disable(void);
+void xfs_rmap_hook_enable(void);
+
+int xfs_rmap_hook_add(struct xfs_perag *pag, struct xfs_rmap_hook *hook);
+void xfs_rmap_hook_del(struct xfs_perag *pag, struct xfs_rmap_hook *hook);
+void xfs_rmap_hook_setup(struct xfs_rmap_hook *hook, notifier_fn_t mod_fn);
+#endif
+
 #endif /* __XFS_RMAP_H__ */