@@ -20,6 +20,10 @@
#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);
+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
@@ -76,24 +80,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;
- xfs_fsblock_t old_firstblock;
- struct list_head old_dfops;
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;
- old_firstblock = trans->t_firstblock;
- /* structure copy */
- old_dfops = trans->t_dfops;
+
+ *tpp = xfs_trans_dup(trans);
/*
* Commit the current transaction.
@@ -102,7 +99,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;
@@ -115,14 +112,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;
- trans->t_firstblock = old_firstblock;
- /* structure copy */
- trans->t_dfops = old_dfops;
-
- return 0;
+ return xfs_trans_reserve(*tpp, &tres, 0, 0);
}
/*
@@ -136,40 +126,150 @@ 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);
+ INIT_LIST_HEAD(&ntp->t_dfops);
+ ntp->t_firstblock = NULLFSBLOCK;
+
+ 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;
+
+ /* move deferred ops over to the new tp */
+ xfs_defer_move(ntp, tp);
+
+ 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;
+ }
+
+ /*
+ * 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;
}
- ptr = kmem_zone_zalloc(xfs_trans_zone,
+ /*
+ * 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);
- INIT_LIST_HEAD(&ptr->t_dfops);
- ptr->t_firstblock = NULLFSBLOCK;
+ tp->t_mountp = mp;
+ INIT_LIST_HEAD(&tp->t_items);
+ INIT_LIST_HEAD(&tp->t_dfops);
+ tp->t_firstblock = NULLFSBLOCK;
+
+ 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;
}
@@ -197,18 +297,25 @@ 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;
+
+ if (tp->t_flags & XFS_TRANS_PERM_LOG_RES)
+ xfs_defer_cancel(tp);
+
+ xfs_trans_free_items(tp);
+ xfs_trans_free(tp);
+
+out:
#ifdef XACT_DEBUG
fprintf(stderr, "## cancelled transaction %p\n", otp);
#endif
+ return;
}
int
@@ -261,6 +368,9 @@ libxfs_trans_ijoin(
ASSERT(iip->ili_flags == 0);
ASSERT(iip->ili_inode != NULL);
+ ASSERT(iip->ili_lock_flags == 0);
+ iip->ili_lock_flags = lock_flags;
+
xfs_trans_add_item(tp, (xfs_log_item_t *)(iip));
ip->i_transp = tp;
@@ -839,22 +949,36 @@ 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;
+ /*
+ * Finish deferred items on final commit. Only permanent transactions
+ * should ever have deferred ops.
+ */
+ WARN_ON_ONCE(!list_empty(&tp->t_dfops) &&
+ !(tp->t_flags & XFS_TRANS_PERM_LOG_RES));
+ if (!regrant && (tp->t_flags & XFS_TRANS_PERM_LOG_RES)) {
+ error = xfs_defer_finish_noroll(&tp);
+ if (error) {
+ xfs_defer_cancel(tp);
+ goto out_unreserve;
+ }
+ }
+
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) {
@@ -878,4 +1002,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);
}