summaryrefslogtreecommitdiff
path: root/fs/xfs/libxfs/xfs_defer.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xfs/libxfs/xfs_defer.c')
-rw-r--r--fs/xfs/libxfs/xfs_defer.c34
1 files changed, 34 insertions, 0 deletions
diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
index 9e6412bd6845..15c84e11625d 100644
--- a/fs/xfs/libxfs/xfs_defer.c
+++ b/fs/xfs/libxfs/xfs_defer.c
@@ -361,6 +361,30 @@ 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 void
+xfs_defer_relog(
+ struct xfs_trans *tp,
+ struct list_head *dfops)
+{
+ struct xfs_defer_pending *dfp;
+
+ ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
+
+ trace_xfs_defer_relog(tp, _RET_IP_);
+
+ list_for_each_entry(dfp, dfops, dfp_list) {
+ trace_xfs_defer_relog_intent(tp->t_mountp, dfp);
+ dfp->dfp_intent = xfs_trans_item_relog(dfp->dfp_intent, tp);
+ }
+
+ trace_xfs_defer_relog_done(tp, _RET_IP_);
+}
+
+/*
* Log an intent-done item for the first pending intent, and finish the work
* items.
*/
@@ -420,6 +444,7 @@ xfs_defer_finish_noroll(
struct xfs_trans **tp)
{
struct xfs_defer_pending *dfp;
+ unsigned int nr_rolls = 0;
int error = 0;
LIST_HEAD(dop_pending);
@@ -445,6 +470,15 @@ xfs_defer_finish_noroll(
if (error)
goto out_shutdown;
+ /* Every few rolls we relog all the intent items. */
+ if (!(++nr_rolls % 7)) {
+ xfs_defer_relog(*tp, &dop_pending);
+
+ error = xfs_defer_trans_roll(tp);
+ if (error)
+ goto out_shutdown;
+ }
+
dfp = list_first_entry(&dop_pending, struct xfs_defer_pending,
dfp_list);
error = xfs_defer_finish_one(*tp, dfp);