]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
libxfs: port kernel transaction code
authorDarrick J. Wong <darrick.wong@oracle.com>
Thu, 4 Oct 2018 19:16:01 +0000 (14:16 -0500)
committerEric Sandeen <sandeen@redhat.com>
Thu, 4 Oct 2018 19:16:01 +0000 (14:16 -0500)
Restructure the userspace transaction code to resemble the kernel code
more closely.  This will make deferred operations behave the same (with
respect to transaction lifetimes) as the kernel.

[sandeen: tweaks to backport before libxfs updates]
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
libxfs/trans.c

index 2bb0d3b8e2d183771725e1cf3be1d7a2e54bd672..e827f0df12de7c88f6a3f95c2550cc3f10c2a7bc 100644 (file)
 #include "xfs_sb.h"
 
 static void xfs_trans_free_items(struct xfs_trans *tp);
+STATIC struct xfs_trans *xfs_trans_dup(struct xfs_trans *tp);
+static int xfs_trans_reserve(struct xfs_trans *tp, struct xfs_trans_res *resp,
+               uint blocks, uint rtextents);
+static int __xfs_trans_commit(struct xfs_trans *tp, bool regrant);
 
 /*
  * Simple transaction interface
@@ -75,19 +79,17 @@ int
 libxfs_trans_roll(
        struct xfs_trans        **tpp)
 {
-       struct xfs_mount        *mp;
        struct xfs_trans        *trans = *tpp;
        struct xfs_trans_res    tres;
-       unsigned int            old_blk_res;
        int                     error;
 
        /*
         * Copy the critical parameters from one trans to the next.
         */
-       mp = trans->t_mountp;
        tres.tr_logres = trans->t_log_res;
        tres.tr_logcount = trans->t_log_count;
-       old_blk_res = trans->t_blk_res;
+
+       *tpp = xfs_trans_dup(trans);
 
        /*
         * Commit the current transaction.
@@ -96,7 +98,7 @@ libxfs_trans_roll(
         * is in progress. The caller takes the responsibility to cancel
         * the duplicate transaction that gets returned.
         */
-       error = xfs_trans_commit(trans);
+       error = __xfs_trans_commit(trans, true);
        if (error)
                return error;
 
@@ -109,11 +111,7 @@ libxfs_trans_roll(
         * the prior and the next transactions.
         */
        tres.tr_logflags = XFS_TRANS_PERM_LOG_RES;
-       error = libxfs_trans_alloc(mp, &tres, 0, 0, 0, tpp);
-       trans = *tpp;
-       trans->t_blk_res = old_blk_res;
-
-       return 0;
+       return xfs_trans_reserve(*tpp, &tres, 0, 0);
 }
 
 /*
@@ -127,38 +125,145 @@ xfs_trans_free(
        kmem_zone_free(xfs_trans_zone, tp);
 }
 
-int
-libxfs_trans_alloc(
-       struct xfs_mount        *mp,
-       struct xfs_trans_res    *resp,
-       unsigned int            blocks,
-       unsigned int            rtextents,
-       unsigned int            flags,
-       struct xfs_trans        **tpp)
+/*
+ * This is called to create a new transaction which will share the
+ * permanent log reservation of the given transaction.  The remaining
+ * unused block and rt extent reservations are also inherited.  This
+ * implies that the original transaction is no longer allowed to allocate
+ * blocks.  Locks and log items, however, are no inherited.  They must
+ * be added to the new transaction explicitly.
+ */
+STATIC struct xfs_trans *
+xfs_trans_dup(
+       struct xfs_trans        *tp)
+{
+       struct xfs_trans        *ntp;
+
+       ntp = kmem_zone_zalloc(xfs_trans_zone, KM_SLEEP);
+
+       /*
+        * Initialize the new transaction structure.
+        */
+       ntp->t_mountp = tp->t_mountp;
+       INIT_LIST_HEAD(&ntp->t_items);
+
+       ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
+
+       ntp->t_flags = XFS_TRANS_PERM_LOG_RES |
+                      (tp->t_flags & XFS_TRANS_RESERVE) |
+                      (tp->t_flags & XFS_TRANS_NO_WRITECOUNT);
+       /* We gave our writer reference to the new transaction */
+       tp->t_flags |= XFS_TRANS_NO_WRITECOUNT;
 
+       ntp->t_agfl_dfops = tp->t_agfl_dfops;
+
+       return ntp;
+}
+
+/*
+ * This is called to reserve free disk blocks and log space for the
+ * given transaction.  This must be done before allocating any resources
+ * within the transaction.
+ *
+ * This will return ENOSPC if there are not enough blocks available.
+ * It will sleep waiting for available log space.
+ * The only valid value for the flags parameter is XFS_RES_LOG_PERM, which
+ * is used by long running transactions.  If any one of the reservations
+ * fails then they will all be backed out.
+ *
+ * This does not do quota reservations. That typically is done by the
+ * caller afterwards.
+ */
+static int
+xfs_trans_reserve(
+       struct xfs_trans        *tp,
+       struct xfs_trans_res    *resp,
+       uint                    blocks,
+       uint                    rtextents)
 {
-       struct xfs_sb   *sb = &mp->m_sb;
-       struct xfs_trans *ptr;
+       int                     error = 0;
 
        /*
         * Attempt to reserve the needed disk blocks by decrementing
-        * the number needed from the number available.  This will
+        * the number needed from the number available.  This will
         * fail if the count would go below zero.
         */
        if (blocks > 0) {
-               if (sb->sb_fdblocks < blocks)
+               if (tp->t_mountp->m_sb.sb_fdblocks < blocks)
                        return -ENOSPC;
+               tp->t_blk_res += blocks;
        }
 
-       ptr = kmem_zone_zalloc(xfs_trans_zone,
+       /*
+        * Reserve the log space needed for this transaction.
+        */
+       if (resp->tr_logres > 0) {
+               ASSERT(tp->t_log_res == 0 ||
+                      tp->t_log_res == resp->tr_logres);
+               ASSERT(tp->t_log_count == 0 ||
+                      tp->t_log_count == resp->tr_logcount);
+
+               if (resp->tr_logflags & XFS_TRANS_PERM_LOG_RES)
+                       tp->t_flags |= XFS_TRANS_PERM_LOG_RES;
+               else
+                       ASSERT(!(tp->t_flags & XFS_TRANS_PERM_LOG_RES));
+
+               tp->t_log_res = resp->tr_logres;
+               tp->t_log_count = resp->tr_logcount;
+       }
+
+       /*
+        * Attempt to reserve the needed realtime extents by decrementing
+        * the number needed from the number available.  This will
+        * fail if the count would go below zero.
+        */
+       if (rtextents > 0) {
+               if (tp->t_mountp->m_sb.sb_rextents < rtextents) {
+                       error = -ENOSPC;
+                       goto undo_blocks;
+               }
+       }
+
+       return 0;
+
+       /*
+        * Error cases jump to one of these labels to undo any
+        * reservations which have already been performed.
+        */
+undo_blocks:
+       if (blocks > 0)
+               tp->t_blk_res = 0;
+
+       return error;
+}
+
+int
+libxfs_trans_alloc(
+       struct xfs_mount        *mp,
+       struct xfs_trans_res    *resp,
+       unsigned int            blocks,
+       unsigned int            rtextents,
+       unsigned int            flags,
+       struct xfs_trans        **tpp)
+
+{
+       struct xfs_trans        *tp;
+       int                     error;
+
+       tp = kmem_zone_zalloc(xfs_trans_zone,
                (flags & XFS_TRANS_NOFS) ? KM_NOFS : KM_SLEEP);
-       ptr->t_mountp = mp;
-       ptr->t_blk_res = blocks;
-       INIT_LIST_HEAD(&ptr->t_items);
+       tp->t_mountp = mp;
+       INIT_LIST_HEAD(&tp->t_items);
+
+       error = xfs_trans_reserve(tp, resp, blocks, rtextents);
+       if (error) {
+               xfs_trans_cancel(tp);
+               return error;
+       }
 #ifdef XACT_DEBUG
-       fprintf(stderr, "allocated new transaction %p\n", ptr);
+       fprintf(stderr, "allocated new transaction %p\n", tp);
 #endif
-       *tpp = ptr;
+       *tpp = tp;
        return 0;
 }
 
@@ -186,18 +291,22 @@ libxfs_trans_alloc_empty(
 
 void
 libxfs_trans_cancel(
-       xfs_trans_t     *tp)
+       struct xfs_trans        *tp)
 {
 #ifdef XACT_DEBUG
-       xfs_trans_t     *otp = tp;
+       struct xfs_trans        *otp = tp;
 #endif
-       if (tp != NULL) {
-               xfs_trans_free_items(tp);
-               xfs_trans_free(tp);
-       }
+       if (tp == NULL)
+               goto out;
+
+       xfs_trans_free_items(tp);
+       xfs_trans_free(tp);
+
+out:
 #ifdef XACT_DEBUG
        fprintf(stderr, "## cancelled transaction %p\n", otp);
 #endif
+       return;
 }
 
 int
@@ -828,22 +937,25 @@ xfs_trans_free_items(
 /*
  * Commit the changes represented by this transaction
  */
-int
-libxfs_trans_commit(
-       xfs_trans_t     *tp)
+static int
+__xfs_trans_commit(
+       struct xfs_trans        *tp,
+       bool                    regrant)
 {
-       xfs_sb_t        *sbp;
+       struct xfs_sb           *sbp;
+       int                     error = 0;
 
        if (tp == NULL)
                return 0;
 
+       ASSERT(!tp->t_agfl_dfops ||
+              !xfs_defer_has_unfinished_work(tp->t_agfl_dfops) || regrant);
+
        if (!(tp->t_flags & XFS_TRANS_DIRTY)) {
 #ifdef XACT_DEBUG
                fprintf(stderr, "committed clean transaction %p\n", tp);
 #endif
-               xfs_trans_free_items(tp);
-               xfs_trans_free(tp);
-               return 0;
+               goto out_unreserve;
        }
 
        if (tp->t_flags & XFS_TRANS_SB_DIRTY) {
@@ -867,4 +979,16 @@ libxfs_trans_commit(
        /* That's it for the transaction structure.  Free it. */
        xfs_trans_free(tp);
        return 0;
+
+out_unreserve:
+       xfs_trans_free_items(tp);
+       xfs_trans_free(tp);
+       return error;
+}
+
+int
+libxfs_trans_commit(
+       struct xfs_trans        *tp)
+{
+       return __xfs_trans_commit(tp, false);
 }