]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: support embedded dfops in transaction
authorBrian Foster <bfoster@redhat.com>
Fri, 5 Oct 2018 02:36:10 +0000 (21:36 -0500)
committerEric Sandeen <sandeen@redhat.com>
Fri, 5 Oct 2018 02:36:10 +0000 (21:36 -0500)
Source kernel commit: e021a2e5fc520d930f949f303e7307038e258645

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>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
include/xfs_trans.h
libxfs/trans.c
libxfs/xfs_defer.c
libxfs/xfs_defer.h

index 1ad4e958d4a09fa7142999c6f9f2abb1888870c5..4c722b59e9937e03cbcfb639c306a12c60c47cb5 100644 (file)
@@ -60,6 +60,20 @@ typedef struct xfs_qoff_logitem {
        xfs_qoff_logformat_t    qql_format;     /* logged structure */
 } xfs_qoff_logitem_t;
 
+#define XFS_DEFER_OPS_NR_INODES        2       /* join up to two inodes */
+#define XFS_DEFER_OPS_NR_BUFS  2       /* join up to two buffers */
+
+struct xfs_defer_ops {
+       struct list_head                dop_intake;     /* unlogged pending work */
+       struct list_head                dop_pending;    /* logged pending work */
+
+       /* relog these with each roll */
+       struct xfs_inode                *dop_inodes[XFS_DEFER_OPS_NR_INODES];
+       struct xfs_buf          *dop_bufs[XFS_DEFER_OPS_NR_BUFS];
+
+       bool                    dop_low;        /* alloc in low mode */
+};
+
 typedef struct xfs_trans {
        unsigned int    t_type;                 /* transaction type */
        unsigned int    t_log_res;              /* amt of log space resvd */
@@ -75,6 +89,7 @@ typedef struct xfs_trans {
        long            t_frextents_delta;      /* superblock freextents chg */
        struct list_head        t_items;        /* first log item desc chunk */
        struct xfs_defer_ops    *t_dfops;       /* deferred operations */
+       struct xfs_defer_ops    t_dfops_internal;
 
 } xfs_trans_t;
 
index bee4a761c60a8a604a51da2e4f89c8909cd60ba8..ec509667cc71f16eec0b8affea14f2fe1b50f089 100644 (file)
@@ -17,6 +17,7 @@
 #include "xfs_inode.h"
 #include "xfs_trans.h"
 #include "xfs_sb.h"
+#include "xfs_defer.h"
 
 static void xfs_trans_free_items(struct xfs_trans *tp);
 STATIC struct xfs_trans *xfs_trans_dup(struct xfs_trans *tp);
@@ -159,7 +160,12 @@ xfs_trans_dup(
        ntp->t_blk_res = tp->t_blk_res - tp->t_blk_res_used;
        tp->t_blk_res = tp->t_blk_res_used;
 
-       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);
 
        return ntp;
 }
@@ -260,6 +266,14 @@ libxfs_trans_alloc(
        INIT_LIST_HEAD(&tp->t_items);
        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) {
                xfs_trans_cancel(tp);
@@ -319,6 +333,9 @@ libxfs_trans_cancel(
        if (tp == NULL)
                goto out;
 
+       if (tp->t_dfops)
+               xfs_defer_cancel(tp->t_dfops);
+
        xfs_trans_free_items(tp);
        xfs_trans_free(tp);
 
@@ -977,8 +994,14 @@ __xfs_trans_commit(
        if (tp == NULL)
                return 0;
 
-       ASSERT(!tp->t_dfops ||
-              !xfs_defer_has_unfinished_work(tp->t_dfops) || regrant);
+       /* 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 (!(tp->t_flags & XFS_TRANS_DIRTY)) {
 #ifdef XACT_DEBUG
index 17ee0e0af11c368c37ac4acd29bf142793d18206..d8b940efb69bf4e01bcf99bc8808d5521ae203aa 100644 (file)
@@ -555,3 +555,25 @@ xfs_defer_init(
        }
        trace_xfs_defer_init(mp, dop, _RET_IP_);
 }
+
+/*
+ * Move state from one xfs_defer_ops to another and reset the source to initial
+ * state. This is primarily used to carry state forward across transaction rolls
+ * with internal dfops.
+ */
+void
+xfs_defer_move(
+       struct xfs_defer_ops    *dst,
+       struct xfs_defer_ops    *src)
+{
+       ASSERT(dst != src);
+
+       list_splice_init(&src->dop_intake, &dst->dop_intake);
+       list_splice_init(&src->dop_pending, &dst->dop_pending);
+
+       memcpy(dst->dop_inodes, src->dop_inodes, sizeof(dst->dop_inodes));
+       memcpy(dst->dop_bufs, src->dop_bufs, sizeof(dst->dop_bufs));
+       dst->dop_low = src->dop_low;
+
+       xfs_defer_reset(src);
+}
index 8f58f217fdff668000bb668d80922361dc5f3339..35507ca9a148456d57fe2f814198de351a56ff38 100644 (file)
@@ -7,6 +7,7 @@
 #define        __XFS_DEFER_H__
 
 struct xfs_defer_op_type;
+struct xfs_defer_ops;
 
 /*
  * Save a log intent item and a list of extents, so that we can replay
@@ -45,20 +46,6 @@ enum xfs_defer_ops_type {
        XFS_DEFER_OPS_TYPE_MAX,
 };
 
-#define XFS_DEFER_OPS_NR_INODES        2       /* join up to two inodes */
-#define XFS_DEFER_OPS_NR_BUFS  2       /* join up to two buffers */
-
-struct xfs_defer_ops {
-       struct list_head        dop_intake;     /* unlogged pending work */
-       struct list_head        dop_pending;    /* logged pending work */
-
-       /* relog these with each roll */
-       struct xfs_inode        *dop_inodes[XFS_DEFER_OPS_NR_INODES];
-       struct xfs_buf          *dop_bufs[XFS_DEFER_OPS_NR_BUFS];
-
-       bool                    dop_low;        /* alloc in low mode */
-};
-
 void xfs_defer_add(struct xfs_defer_ops *dop, enum xfs_defer_ops_type type,
                struct list_head *h);
 int xfs_defer_finish(struct xfs_trans **tp, struct xfs_defer_ops *dop);
@@ -67,6 +54,7 @@ void xfs_defer_init(struct xfs_trans *tp, struct xfs_defer_ops *dop);
 bool xfs_defer_has_unfinished_work(struct xfs_defer_ops *dop);
 int xfs_defer_ijoin(struct xfs_defer_ops *dop, struct xfs_inode *ip);
 int xfs_defer_bjoin(struct xfs_defer_ops *dop, struct xfs_buf *bp);
+void xfs_defer_move(struct xfs_defer_ops *dst, struct xfs_defer_ops *src);
 
 /* Description of a deferred type. */
 struct xfs_defer_op_type {