summaryrefslogtreecommitdiff
path: root/fs/xfs/xfs_trans.c
diff options
context:
space:
mode:
authorBrian Foster <bfoster@redhat.com>2018-07-24 23:43:11 +0300
committerDarrick J. Wong <darrick.wong@oracle.com>2018-07-26 20:15:14 +0300
commite021a2e5fc520d930f949f303e7307038e258645 (patch)
tree68349c1264484a817a5f8da9285add82f6b7ded2 /fs/xfs/xfs_trans.c
parent44fd294681de73990da656294e3dacaa7878f577 (diff)
downloadlinux-e021a2e5fc520d930f949f303e7307038e258645.tar.xz
xfs: support embedded dfops in transaction
The dfops structure used by multi-transaction operations is typically stored on the stack and carried around by the associated transaction. The lifecycle of dfops does not quite match that of the transaction, but they are tightly related in that the former depends on the latter. The relationship of these objects is tight enough that we can avoid the cumbersome boilerplate code required in most cases to manage them separately by just embedding an xfs_defer_ops in the transaction itself. This means that a transaction allocation returns with an initialized dfops, a transaction commit finishes pending deferred items before the tx commit, a transaction cancel cancels the dfops before the transaction and a transaction dup operation transfers the current dfops state to the new transaction. The dup operation is slightly complicated by the fact that we can no longer just copy a dfops pointer from the old transaction to the new transaction. This is solved through a dfops move helper that transfers the pending items and other dfops state across the transactions. This also requires that transaction rolling code always refer to the transaction for the current dfops reference. Finally, to facilitate incremental conversion to the internal dfops and continue to support the current external dfops mode of operation, create the new ->t_dfops_internal field with a layer of indirection. On allocation, ->t_dfops points to the internal dfops. This state is overridden by callers who re-init a local dfops on the transaction. Once ->t_dfops is overridden, the external dfops reference is maintained as the transaction rolls. This patch adds the fundamental ability to support an internal dfops. All codepaths that perform deferred processing continue to override the internal dfops until they are converted over in subsequent patches. Signed-off-by: Brian Foster <bfoster@redhat.com> Reviewed-by: Bill O'Donnell <billodo@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Diffstat (limited to 'fs/xfs/xfs_trans.c')
-rw-r--r--fs/xfs/xfs_trans.c30
1 files changed, 26 insertions, 4 deletions
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
index de00f79ff698..412c8d236c71 100644
--- a/fs/xfs/xfs_trans.c
+++ b/fs/xfs/xfs_trans.c
@@ -119,7 +119,13 @@ xfs_trans_dup(
ntp->t_rtx_res = tp->t_rtx_res - tp->t_rtx_res_used;
tp->t_rtx_res = tp->t_rtx_res_used;
ntp->t_pflags = tp->t_pflags;
- ntp->t_dfops = tp->t_dfops;
+
+ /* copy the dfops pointer if it's external, otherwise move it */
+ xfs_defer_init(ntp, &ntp->t_dfops_internal);
+ if (tp->t_dfops != &tp->t_dfops_internal)
+ ntp->t_dfops = tp->t_dfops;
+ else
+ xfs_defer_move(ntp->t_dfops, tp->t_dfops);
xfs_trans_dup_dqinfo(tp, ntp);
@@ -275,6 +281,13 @@ xfs_trans_alloc(
INIT_LIST_HEAD(&tp->t_items);
INIT_LIST_HEAD(&tp->t_busy);
tp->t_firstblock = NULLFSBLOCK;
+ /*
+ * We only roll transactions with permanent log reservation. Don't init
+ * ->t_dfops to skip attempts to finish or cancel an empty dfops with a
+ * non-permanent res.
+ */
+ if (resp->tr_logflags & XFS_TRANS_PERM_LOG_RES)
+ xfs_defer_init(tp, &tp->t_dfops_internal);
error = xfs_trans_reserve(tp, resp, blocks, rtextents);
if (error) {
@@ -916,11 +929,17 @@ __xfs_trans_commit(
int error = 0;
int sync = tp->t_flags & XFS_TRANS_SYNC;
- ASSERT(!tp->t_dfops ||
- !xfs_defer_has_unfinished_work(tp->t_dfops) || regrant);
-
trace_xfs_trans_commit(tp, _RET_IP_);
+ /* finish deferred items on final commit */
+ if (!regrant && tp->t_dfops) {
+ error = xfs_defer_finish(&tp, tp->t_dfops);
+ if (error) {
+ xfs_defer_cancel(tp->t_dfops);
+ goto out_unreserve;
+ }
+ }
+
/*
* If there is nothing to be logged by the transaction,
* then unlock all of the items associated with the
@@ -1010,6 +1029,9 @@ xfs_trans_cancel(
trace_xfs_trans_cancel(tp, _RET_IP_);
+ if (tp->t_dfops)
+ xfs_defer_cancel(tp->t_dfops);
+
/*
* See if the caller is relying on us to shut down the
* filesystem. This happens in paths where we detect