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.c52
1 files changed, 52 insertions, 0 deletions
diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
index 8d42b914dcd4..5e5806d0b161 100644
--- a/fs/xfs/libxfs/xfs_defer.c
+++ b/fs/xfs/libxfs/xfs_defer.c
@@ -17,6 +17,7 @@
#include "xfs_inode_item.h"
#include "xfs_trace.h"
#include "xfs_icache.h"
+#include "xfs_log.h"
/*
* Deferred Operations in XFS
@@ -346,6 +347,52 @@ 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;
+ xfs_lsn_t threshold_lsn;
+
+ ASSERT((*tpp)->t_flags & XFS_TRANS_PERM_LOG_RES);
+
+ /*
+ * Figure out where we need the tail to be in order to maintain the
+ * minimum required free space in the log.
+ */
+ threshold_lsn = xlog_grant_push_threshold((*tpp)->t_mountp->m_log, 0);
+ if (threshold_lsn == NULLCOMMITLSN)
+ return 0;
+
+ list_for_each_entry(dfp, dfops, dfp_list) {
+ /*
+ * If the log intent item for this deferred op is behind the
+ * desired log tail threshold and 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_LSN_CMP(dfp->dfp_intent->li_lsn, threshold_lsn) >= 0 ||
+ xfs_log_item_in_current_chkpt(dfp->dfp_intent))
+ continue;
+
+ trace_xfs_defer_relog_intent((*tpp)->t_mountp, dfp);
+ 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.
*/
@@ -431,6 +478,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);