]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: periodically relog deferred intent items
authorDarrick J. Wong <darrick.wong@oracle.com>
Thu, 12 Nov 2020 22:21:44 +0000 (17:21 -0500)
committerEric Sandeen <sandeen@sandeen.net>
Thu, 12 Nov 2020 22:21:44 +0000 (17:21 -0500)
Source kernel commit: 4e919af7827a6adfc28e82cd6c4ffcfcc3dd6118

There's a subtle design flaw in the deferred log item code that can lead
to pinning the log tail.  Taking up the defer ops chain examples from
the previous commit, we can get trapped in sequences like this:

Caller hands us a transaction t0 with D0-D3 attached.  The defer ops
chain will look like the following if the transaction rolls succeed:

t1: D0(t0), D1(t0), D2(t0), D3(t0)
t2: d4(t1), d5(t1), D1(t0), D2(t0), D3(t0)
t3: d5(t1), D1(t0), D2(t0), D3(t0)
...
t9: d9(t7), D3(t0)
t10: D3(t0)
t11: d10(t10), d11(t10)
t12: d11(t10)

In transaction 9, we finish d9 and try to roll to t10 while holding onto
an intent item for D3 that we logged in t0.

The previous commit changed the order in which we place new defer ops in
the defer ops processing chain to reduce the maximum chain length.  Now
make xfs_defer_finish_noroll capable of relogging the entire chain
periodically so that we can always move the log tail forward.  Most
chains will never get relogged, except for operations that generate very
long chains (large extents containing many blocks with different sharing
levels) or are on filesystems with small logs and a lot of ongoing
metadata updates.

Callers are now required to ensure that the transaction reservation is
large enough to handle logging done items and new intent items for the
maximum possible chain length.  Most callers are careful to keep the
chain lengths low, so the overhead should be minimal.

The decision to relog an intent item is made based on whether the intent
was logged in a previous checkpoint, since there's no point in relogging
an intent into the same checkpoint.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
include/xfs_trace.h
include/xfs_trans.h
libxfs/xfs_defer.c

index 91f2b98b6c30ca6ee6c5f277365691138d0e9f76..a1002638f39a2354f3e372f843b47546e68a6608 100644 (file)
@@ -63,6 +63,8 @@
 #define trace_xfs_iext_insert(a,b,c,d)         ((void) 0)
 #define trace_xfs_iext_remove(a,b,c,d)         ((void) 0)
 
+#define trace_xfs_defer_relog_intent(a,b)      ((void) 0)
+
 #define trace_xfs_dir2_grow_inode(a,b)         ((void) 0)
 #define trace_xfs_dir2_shrink_inode(a,b)       ((void) 0)
 
index 6daf35cfcf7671d2826db928c74419cb058eb59c..d3683441894de76397c4973e2c566e266e3ccb1c 100644 (file)
@@ -148,4 +148,7 @@ libxfs_trans_read_buf(
        return libxfs_trans_read_buf_map(mp, tp, btp, &map, 1, flags, bpp, ops);
 }
 
+#define xfs_log_item_in_current_chkpt(lip)     (false)
+#define xfs_trans_item_relog(lip, tp)          (NULL)
+
 #endif /* __XFS_TRANS_H__ */
index 58b229e4054e2272702db22d584de8c3b0ed99b4..abee6d4260e262d418302126c586b54d6027f1f0 100644 (file)
@@ -342,6 +342,42 @@ xfs_defer_cancel_list(
        }
 }
 
+/*
+ * Prevent a log intent item from pinning the tail of the log by logging a
+ * done item to release the intent item; and then log a new intent item.
+ * The caller should provide a fresh transaction and roll it after we're done.
+ */
+static int
+xfs_defer_relog(
+       struct xfs_trans                **tpp,
+       struct list_head                *dfops)
+{
+       struct xfs_defer_pending        *dfp;
+
+       ASSERT((*tpp)->t_flags & XFS_TRANS_PERM_LOG_RES);
+
+       list_for_each_entry(dfp, dfops, dfp_list) {
+               /*
+                * If the log intent item for this deferred op is not a part of
+                * the current log checkpoint, relog the intent item to keep
+                * the log tail moving forward.  We're ok with this being racy
+                * because an incorrect decision means we'll be a little slower
+                * at pushing the tail.
+                */
+               if (dfp->dfp_intent == NULL ||
+                   xfs_log_item_in_current_chkpt(dfp->dfp_intent))
+                       continue;
+
+               trace_xfs_defer_relog_intent((*tpp)->t_mountp, dfp);
+               XFS_STATS_INC((*tpp)->t_mountp, defer_relog);
+               dfp->dfp_intent = xfs_trans_item_relog(dfp->dfp_intent, *tpp);
+       }
+
+       if ((*tpp)->t_flags & XFS_TRANS_DIRTY)
+               return xfs_defer_trans_roll(tpp);
+       return 0;
+}
+
 /*
  * Log an intent-done item for the first pending intent, and finish the work
  * items.
@@ -428,6 +464,11 @@ xfs_defer_finish_noroll(
                if (error)
                        goto out_shutdown;
 
+               /* Possibly relog intent items to keep the log moving. */
+               error = xfs_defer_relog(tp, &dop_pending);
+               if (error)
+                       goto out_shutdown;
+
                dfp = list_first_entry(&dop_pending, struct xfs_defer_pending,
                                       dfp_list);
                error = xfs_defer_finish_one(*tp, dfp);