#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
xfs_mount_t *mp,
uint type)
{
- fs_check_frozen(XFS_MTOVFS(mp), SB_FREEZE_TRANS);
- atomic_inc(&mp->m_active_trans);
-
- return (_xfs_trans_alloc(mp, type));
-
+ vfs_wait_for_freeze(XFS_MTOVFS(mp), SB_FREEZE_TRANS);
+ return _xfs_trans_alloc(mp, type);
}
xfs_trans_t *
{
xfs_trans_t *tp;
- ASSERT(xfs_trans_zone != NULL);
- tp = kmem_zone_zalloc(xfs_trans_zone, KM_SLEEP);
+ atomic_inc(&mp->m_active_trans);
- /*
- * Initialize the transaction structure.
- */
+ tp = kmem_zone_zalloc(xfs_trans_zone, KM_SLEEP);
tp->t_magic = XFS_TRANS_MAGIC;
tp->t_type = type;
tp->t_mountp = mp;
tp->t_busy_free = XFS_LBC_NUM_SLOTS;
XFS_LIC_INIT(&(tp->t_items));
XFS_LBC_INIT(&(tp->t_busy));
-
- return (tp);
+ return tp;
}
/*
tp->t_blk_res = tp->t_blk_res_used;
ntp->t_rtx_res = tp->t_rtx_res - tp->t_rtx_res_used;
tp->t_rtx_res = tp->t_rtx_res_used;
- PFLAGS_DUP(&tp->t_pflags, &ntp->t_pflags);
+ ntp->t_pflags = tp->t_pflags;
XFS_TRANS_DUP_DQINFO(tp->t_mountp, tp, ntp);
uint logcount)
{
int log_flags;
- int error;
- int rsvd;
-
- error = 0;
- rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0;
+ int error = 0;
+ int rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0;
/* Mark this thread as being in a transaction */
- PFLAGS_SET_FSTRANS(&tp->t_pflags);
+ current_set_flags_nested(&tp->t_pflags, PF_FSTRANS);
/*
* Attempt to reserve the needed disk blocks by decrementing
*/
if (blocks > 0) {
error = xfs_mod_incore_sb(tp->t_mountp, XFS_SBS_FDBLOCKS,
- -blocks, rsvd);
+ -((int64_t)blocks), rsvd);
if (error != 0) {
- PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+ current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
return (XFS_ERROR(ENOSPC));
}
tp->t_blk_res += blocks;
*/
if (rtextents > 0) {
error = xfs_mod_incore_sb(tp->t_mountp, XFS_SBS_FREXTENTS,
- -rtextents, rsvd);
+ -((int64_t)rtextents), rsvd);
if (error) {
error = XFS_ERROR(ENOSPC);
goto undo_log;
undo_blocks:
if (blocks > 0) {
(void) xfs_mod_incore_sb(tp->t_mountp, XFS_SBS_FDBLOCKS,
- blocks, rsvd);
+ (int64_t)blocks, rsvd);
tp->t_blk_res = 0;
}
- PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+ current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
- return (error);
+ return error;
}
*
* Mark the transaction structure to indicate that the superblock
* needs to be updated before committing.
+ *
+ * Because we may not be keeping track of allocated/free inodes and
+ * used filesystem blocks in the superblock, we do not mark the
+ * superblock dirty in this transaction if we modify these fields.
+ * We still need to update the transaction deltas so that they get
+ * applied to the incore superblock, but we don't want them to
+ * cause the superblock to get locked and logged if these are the
+ * only fields in the superblock that the transaction modifies.
*/
void
xfs_trans_mod_sb(
xfs_trans_t *tp,
uint field,
- long delta)
+ int64_t delta)
{
+ uint32_t flags = (XFS_TRANS_DIRTY|XFS_TRANS_SB_DIRTY);
+ xfs_mount_t *mp = tp->t_mountp;
switch (field) {
case XFS_TRANS_SB_ICOUNT:
tp->t_icount_delta += delta;
+ if (xfs_sb_version_haslazysbcount(&mp->m_sb))
+ flags &= ~XFS_TRANS_SB_DIRTY;
break;
case XFS_TRANS_SB_IFREE:
tp->t_ifree_delta += delta;
+ if (xfs_sb_version_haslazysbcount(&mp->m_sb))
+ flags &= ~XFS_TRANS_SB_DIRTY;
break;
case XFS_TRANS_SB_FDBLOCKS:
/*
ASSERT(tp->t_blk_res_used <= tp->t_blk_res);
}
tp->t_fdblocks_delta += delta;
+ if (xfs_sb_version_haslazysbcount(&mp->m_sb))
+ flags &= ~XFS_TRANS_SB_DIRTY;
break;
case XFS_TRANS_SB_RES_FDBLOCKS:
/*
*/
ASSERT(delta < 0);
tp->t_res_fdblocks_delta += delta;
+ if (xfs_sb_version_haslazysbcount(&mp->m_sb))
+ flags &= ~XFS_TRANS_SB_DIRTY;
break;
case XFS_TRANS_SB_FREXTENTS:
/*
case XFS_TRANS_SB_RES_FREXTENTS:
/*
* The allocation has already been applied to the
- * in-core superblocks's counter. This should only
+ * in-core superblock's counter. This should only
* be applied to the on-disk superblock.
*/
ASSERT(delta < 0);
return;
}
- tp->t_flags |= (XFS_TRANS_SB_DIRTY | XFS_TRANS_DIRTY);
+ tp->t_flags |= flags;
}
/*
(tp->t_ag_freeblks_delta + tp->t_ag_flist_delta +
tp->t_ag_btree_delta));
- if (tp->t_icount_delta != 0) {
- INT_MOD(sbp->sb_icount, ARCH_CONVERT, tp->t_icount_delta);
- }
- if (tp->t_ifree_delta != 0) {
- INT_MOD(sbp->sb_ifree, ARCH_CONVERT, tp->t_ifree_delta);
- }
+ /*
+ * Only update the superblock counters if we are logging them
+ */
+ if (!xfs_sb_version_haslazysbcount(&(tp->t_mountp->m_sb))) {
+ if (tp->t_icount_delta != 0) {
+ INT_MOD(sbp->sb_icount, ARCH_CONVERT, tp->t_icount_delta);
+ }
+ if (tp->t_ifree_delta != 0) {
+ INT_MOD(sbp->sb_ifree, ARCH_CONVERT, tp->t_ifree_delta);
+ }
- if (tp->t_fdblocks_delta != 0) {
- INT_MOD(sbp->sb_fdblocks, ARCH_CONVERT, tp->t_fdblocks_delta);
- }
- if (tp->t_res_fdblocks_delta != 0) {
- INT_MOD(sbp->sb_fdblocks, ARCH_CONVERT, tp->t_res_fdblocks_delta);
+ if (tp->t_fdblocks_delta != 0) {
+ INT_MOD(sbp->sb_fdblocks, ARCH_CONVERT, tp->t_fdblocks_delta);
+ }
+ if (tp->t_res_fdblocks_delta != 0) {
+ INT_MOD(sbp->sb_fdblocks, ARCH_CONVERT, tp->t_res_fdblocks_delta);
+ }
}
if (tp->t_frextents_delta != 0) {
if (whole)
/*
- * Log the whole thing, the fields are discontiguous.
+ * Log the whole thing, the fields are noncontiguous.
*/
xfs_trans_log_buf(tp, bp, 0, sizeof(xfs_sb_t) - 1);
else
}
/*
- * xfs_trans_unreserve_and_mod_sb() is called to release unused
- * reservations and apply superblock counter changes to the in-core
- * superblock.
+ * xfs_trans_unreserve_and_mod_sb() is called to release unused reservations
+ * and apply superblock counter changes to the in-core superblock. The
+ * t_res_fdblocks_delta and t_res_frextents_delta fields are explicitly NOT
+ * applied to the in-core superblock. The idea is that that has already been
+ * done.
*
* This is done efficiently with a single call to xfs_mod_incore_sb_batch().
+ * However, we have to ensure that we only modify each superblock field only
+ * once because the application of the delta values may not be atomic. That can
+ * lead to ENOSPC races occurring if we have two separate modifcations of the
+ * free space counter to put back the entire reservation and then take away
+ * what we used.
+ *
+ * If we are not logging superblock counters, then the inode allocated/free and
+ * used block counts are not updated in the on disk superblock. In this case,
+ * XFS_TRANS_SB_DIRTY will not be set when the transaction is updated but we
+ * still need to update the incore superblock with the changes.
*/
STATIC void
xfs_trans_unreserve_and_mod_sb(
{
xfs_mod_sb_t msb[14]; /* If you add cases, add entries */
xfs_mod_sb_t *msbp;
+ xfs_mount_t *mp = tp->t_mountp;
/* REFERENCED */
int error;
int rsvd;
+ int64_t blkdelta = 0;
+ int64_t rtxdelta = 0;
msbp = msb;
rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0;
- /*
- * Release any reserved blocks. Any that were allocated
- * will be taken back again by fdblocks_delta below.
- */
- if (tp->t_blk_res > 0) {
+ /* calculate free blocks delta */
+ if (tp->t_blk_res > 0)
+ blkdelta = tp->t_blk_res;
+
+ if ((tp->t_fdblocks_delta != 0) &&
+ (xfs_sb_version_haslazysbcount(&mp->m_sb) ||
+ (tp->t_flags & XFS_TRANS_SB_DIRTY)))
+ blkdelta += tp->t_fdblocks_delta;
+
+ if (blkdelta != 0) {
msbp->msb_field = XFS_SBS_FDBLOCKS;
- msbp->msb_delta = tp->t_blk_res;
+ msbp->msb_delta = blkdelta;
msbp++;
}
- /*
- * Release any reserved real time extents . Any that were
- * allocated will be taken back again by frextents_delta below.
- */
- if (tp->t_rtx_res > 0) {
+ /* calculate free realtime extents delta */
+ if (tp->t_rtx_res > 0)
+ rtxdelta = tp->t_rtx_res;
+
+ if ((tp->t_frextents_delta != 0) &&
+ (tp->t_flags & XFS_TRANS_SB_DIRTY))
+ rtxdelta += tp->t_frextents_delta;
+
+ if (rtxdelta != 0) {
msbp->msb_field = XFS_SBS_FREXTENTS;
- msbp->msb_delta = tp->t_rtx_res;
+ msbp->msb_delta = rtxdelta;
msbp++;
}
- /*
- * Apply any superblock modifications to the in-core version.
- * The t_res_fdblocks_delta and t_res_frextents_delta fields are
- * explicity NOT applied to the in-core superblock.
- * The idea is that that has already been done.
- */
- if (tp->t_flags & XFS_TRANS_SB_DIRTY) {
+ /* apply remaining deltas */
+
+ if (xfs_sb_version_haslazysbcount(&mp->m_sb) ||
+ (tp->t_flags & XFS_TRANS_SB_DIRTY)) {
if (tp->t_icount_delta != 0) {
msbp->msb_field = XFS_SBS_ICOUNT;
- msbp->msb_delta = (int)tp->t_icount_delta;
+ msbp->msb_delta = tp->t_icount_delta;
msbp++;
}
if (tp->t_ifree_delta != 0) {
msbp->msb_field = XFS_SBS_IFREE;
- msbp->msb_delta = (int)tp->t_ifree_delta;
- msbp++;
- }
- if (tp->t_fdblocks_delta != 0) {
- msbp->msb_field = XFS_SBS_FDBLOCKS;
- msbp->msb_delta = (int)tp->t_fdblocks_delta;
- msbp++;
- }
- if (tp->t_frextents_delta != 0) {
- msbp->msb_field = XFS_SBS_FREXTENTS;
- msbp->msb_delta = (int)tp->t_frextents_delta;
+ msbp->msb_delta = tp->t_ifree_delta;
msbp++;
}
+ }
+
+ if (tp->t_flags & XFS_TRANS_SB_DIRTY) {
if (tp->t_dblocks_delta != 0) {
msbp->msb_field = XFS_SBS_DBLOCKS;
- msbp->msb_delta = (int)tp->t_dblocks_delta;
+ msbp->msb_delta = tp->t_dblocks_delta;
msbp++;
}
if (tp->t_agcount_delta != 0) {
msbp->msb_field = XFS_SBS_AGCOUNT;
- msbp->msb_delta = (int)tp->t_agcount_delta;
+ msbp->msb_delta = tp->t_agcount_delta;
msbp++;
}
if (tp->t_imaxpct_delta != 0) {
msbp->msb_field = XFS_SBS_IMAX_PCT;
- msbp->msb_delta = (int)tp->t_imaxpct_delta;
+ msbp->msb_delta = tp->t_imaxpct_delta;
msbp++;
}
if (tp->t_rextsize_delta != 0) {
msbp->msb_field = XFS_SBS_REXTSIZE;
- msbp->msb_delta = (int)tp->t_rextsize_delta;
+ msbp->msb_delta = tp->t_rextsize_delta;
msbp++;
}
if (tp->t_rbmblocks_delta != 0) {
msbp->msb_field = XFS_SBS_RBMBLOCKS;
- msbp->msb_delta = (int)tp->t_rbmblocks_delta;
+ msbp->msb_delta = tp->t_rbmblocks_delta;
msbp++;
}
if (tp->t_rblocks_delta != 0) {
msbp->msb_field = XFS_SBS_RBLOCKS;
- msbp->msb_delta = (int)tp->t_rblocks_delta;
+ msbp->msb_delta = tp->t_rblocks_delta;
msbp++;
}
if (tp->t_rextents_delta != 0) {
msbp->msb_field = XFS_SBS_REXTENTS;
- msbp->msb_delta = (int)tp->t_rextents_delta;
+ msbp->msb_delta = tp->t_rextents_delta;
msbp++;
}
if (tp->t_rextslog_delta != 0) {
msbp->msb_field = XFS_SBS_REXTSLOG;
- msbp->msb_delta = (int)tp->t_rextslog_delta;
+ msbp->msb_delta = tp->t_rextslog_delta;
msbp++;
}
}
_xfs_trans_commit(
xfs_trans_t *tp,
uint flags,
- xfs_lsn_t *commit_lsn_p,
int *log_flushed)
{
xfs_log_iovec_t *log_vector;
if (commit_lsn == -1 && !shutdown)
shutdown = XFS_ERROR(EIO);
}
- PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+ current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
xfs_trans_free_items(tp, shutdown? XFS_TRANS_ABORT : 0);
xfs_trans_free_busy(tp);
xfs_trans_free(tp);
XFS_STATS_INC(xs_trans_empty);
- if (commit_lsn_p)
- *commit_lsn_p = commit_lsn;
return (shutdown);
}
ASSERT(tp->t_ticket != NULL);
*/
nvec = xfs_trans_count_vecs(tp);
if (nvec == 0) {
- xfs_force_shutdown(mp, XFS_LOG_IO_ERROR);
+ xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR);
goto shut_us_down;
} else if (nvec <= XFS_TRANS_LOGVEC_COUNT) {
log_vector = log_vector_fast;
kmem_free(log_vector, nvec * sizeof(xfs_log_iovec_t));
}
- if (commit_lsn_p)
- *commit_lsn_p = commit_lsn;
-
/*
* If we got a log write error. Unpin the logitems that we
* had pinned, clean up, free trans structure, and return error.
*/
if (error || commit_lsn == -1) {
- PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+ current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
xfs_trans_uncommit(tp, flags|XFS_TRANS_ABORT);
return XFS_ERROR(EIO);
}
/*
* Mark this thread as no longer being in a transaction
*/
- PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+ current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
/*
* Once all the items of the transaction have been copied
*/
if ((tp->t_flags & XFS_TRANS_DIRTY) && !XFS_FORCED_SHUTDOWN(mp)) {
XFS_ERROR_REPORT("xfs_trans_cancel", XFS_ERRLEVEL_LOW, mp);
- xfs_force_shutdown(mp, XFS_CORRUPT_INCORE);
+ xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
}
#ifdef DEBUG
if (!(flags & XFS_TRANS_ABORT)) {
}
/* mark this thread as no longer being in a transaction */
- PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+ current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
xfs_trans_free_items(tp, flags);
xfs_trans_free_busy(tp);