xfs: split out iclog writing from xfs_trans_commit()

Split the the part of xfs_trans_commit() that deals with writing the
transaction into the iclog into a separate function. This isolates the
physical commit process from the logical commit operation and makes
it easier to insert different transaction commit paths without affecting
the existing algorithm adversely.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
Dave Chinner 2010-03-08 11:28:28 +11:00 committed by Alex Elder
parent 713bf88bba
commit 0924378a68
1 changed files with 201 additions and 190 deletions

View File

@ -47,8 +47,6 @@
STATIC void xfs_trans_apply_sb_deltas(xfs_trans_t *);
STATIC uint xfs_trans_count_vecs(xfs_trans_t *);
STATIC void xfs_trans_fill_vecs(xfs_trans_t *, xfs_log_iovec_t *);
STATIC void xfs_trans_uncommit(xfs_trans_t *, uint);
STATIC void xfs_trans_committed(xfs_trans_t *, int);
STATIC void xfs_trans_chunk_committed(xfs_log_item_chunk_t *, xfs_lsn_t, int);
@ -764,94 +762,126 @@ xfs_trans_unreserve_and_mod_sb(
}
}
/*
* Total up the number of log iovecs needed to commit this
* transaction. The transaction itself needs one for the
* transaction header. Ask each dirty item in turn how many
* it needs to get the total.
*/
static uint
xfs_trans_count_vecs(
xfs_trans_t *tp)
{
int nvecs;
xfs_log_item_desc_t *lidp;
nvecs = 1;
lidp = xfs_trans_first_item(tp);
ASSERT(lidp != NULL);
/* In the non-debug case we need to start bailing out if we
* didn't find a log_item here, return zero and let trans_commit
* deal with it.
*/
if (lidp == NULL)
return 0;
while (lidp != NULL) {
/*
* Skip items which aren't dirty in this transaction.
*/
if (!(lidp->lid_flags & XFS_LID_DIRTY)) {
lidp = xfs_trans_next_item(tp, lidp);
continue;
}
lidp->lid_size = IOP_SIZE(lidp->lid_item);
nvecs += lidp->lid_size;
lidp = xfs_trans_next_item(tp, lidp);
}
return nvecs;
}
/*
* xfs_trans_commit
* Fill in the vector with pointers to data to be logged
* by this transaction. The transaction header takes
* the first vector, and then each dirty item takes the
* number of vectors it indicated it needed in xfs_trans_count_vecs().
*
* Commit the given transaction to the log a/synchronously.
*
* XFS disk error handling mechanism is not based on a typical
* transaction abort mechanism. Logically after the filesystem
* gets marked 'SHUTDOWN', we can't let any new transactions
* be durable - ie. committed to disk - because some metadata might
* be inconsistent. In such cases, this returns an error, and the
* caller may assume that all locked objects joined to the transaction
* have already been unlocked as if the commit had succeeded.
* Do not reference the transaction structure after this call.
* As each item fills in the entries it needs, also pin the item
* so that it cannot be flushed out until the log write completes.
*/
/*ARGSUSED*/
int
_xfs_trans_commit(
xfs_trans_t *tp,
uint flags,
int *log_flushed)
static void
xfs_trans_fill_vecs(
struct xfs_trans *tp,
struct xfs_log_iovec *log_vector)
{
xfs_log_iovec_t *log_vector;
int nvec;
xfs_mount_t *mp;
xfs_lsn_t commit_lsn;
/* REFERENCED */
int error;
int log_flags;
int sync;
#define XFS_TRANS_LOGVEC_COUNT 16
xfs_log_iovec_t log_vector_fast[XFS_TRANS_LOGVEC_COUNT];
struct xlog_in_core *commit_iclog;
int shutdown;
commit_lsn = -1;
xfs_log_item_desc_t *lidp;
struct xfs_log_iovec *vecp;
uint nitems;
/*
* Determine whether this commit is releasing a permanent
* log reservation or not.
* Skip over the entry for the transaction header, we'll
* fill that in at the end.
*/
if (flags & XFS_TRANS_RELEASE_LOG_RES) {
ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
log_flags = XFS_LOG_REL_PERM_RESERV;
} else {
log_flags = 0;
}
mp = tp->t_mountp;
vecp = log_vector + 1;
/*
* If there is nothing to be logged by the transaction,
* then unlock all of the items associated with the
* transaction and free the transaction structure.
* Also make sure to return any reserved blocks to
* the free pool.
*/
shut_us_down:
shutdown = XFS_FORCED_SHUTDOWN(mp) ? EIO : 0;
if (!(tp->t_flags & XFS_TRANS_DIRTY) || shutdown) {
xfs_trans_unreserve_and_mod_sb(tp);
/*
* It is indeed possible for the transaction to be
* not dirty but the dqinfo portion to be. All that
* means is that we have some (non-persistent) quota
* reservations that need to be unreserved.
*/
xfs_trans_unreserve_and_mod_dquots(tp);
if (tp->t_ticket) {
commit_lsn = xfs_log_done(mp, tp->t_ticket,
NULL, log_flags);
if (commit_lsn == -1 && !shutdown)
shutdown = XFS_ERROR(EIO);
nitems = 0;
lidp = xfs_trans_first_item(tp);
ASSERT(lidp);
while (lidp) {
/* Skip items which aren't dirty in this transaction. */
if (!(lidp->lid_flags & XFS_LID_DIRTY)) {
lidp = xfs_trans_next_item(tp, lidp);
continue;
}
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);
return (shutdown);
/*
* The item may be marked dirty but not log anything. This can
* be used to get called when a transaction is committed.
*/
if (lidp->lid_size)
nitems++;
IOP_FORMAT(lidp->lid_item, vecp);
vecp += lidp->lid_size;
IOP_PIN(lidp->lid_item);
lidp = xfs_trans_next_item(tp, lidp);
}
ASSERT(tp->t_ticket != NULL);
/*
* If we need to update the superblock, then do it now.
* Now that we've counted the number of items in this transaction, fill
* in the transaction header. Note that the transaction header does not
* have a log item.
*/
if (tp->t_flags & XFS_TRANS_SB_DIRTY)
xfs_trans_apply_sb_deltas(tp);
xfs_trans_apply_dquot_deltas(tp);
tp->t_header.th_magic = XFS_TRANS_HEADER_MAGIC;
tp->t_header.th_type = tp->t_type;
tp->t_header.th_num_items = nitems;
log_vector->i_addr = (xfs_caddr_t)&tp->t_header;
log_vector->i_len = sizeof(xfs_trans_header_t);
log_vector->i_type = XLOG_REG_TYPE_TRANSHDR;
}
/*
* Format the transaction direct to the iclog. This isolates the physical
* transaction commit operation from the logical operation and hence allows
* other methods to be introduced without affecting the existing commit path.
*/
static int
xfs_trans_commit_iclog(
struct xfs_mount *mp,
struct xfs_trans *tp,
xfs_lsn_t *commit_lsn,
int flags)
{
int shutdown;
int error;
int log_flags = 0;
struct xlog_in_core *commit_iclog;
#define XFS_TRANS_LOGVEC_COUNT 16
struct xfs_log_iovec log_vector_fast[XFS_TRANS_LOGVEC_COUNT];
struct xfs_log_iovec *log_vector;
uint nvec;
/*
* Ask each log item how many log_vector entries it will
@ -861,8 +891,7 @@ shut_us_down:
*/
nvec = xfs_trans_count_vecs(tp);
if (nvec == 0) {
xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR);
goto shut_us_down;
return ENOMEM; /* triggers a shutdown! */
} else if (nvec <= XFS_TRANS_LOGVEC_COUNT) {
log_vector = log_vector_fast;
} else {
@ -877,6 +906,9 @@ shut_us_down:
*/
xfs_trans_fill_vecs(tp, log_vector);
if (flags & XFS_TRANS_RELEASE_LOG_RES)
log_flags = XFS_LOG_REL_PERM_RESERV;
error = xfs_log_write(mp, log_vector, nvec, tp->t_ticket, &(tp->t_lsn));
/*
@ -884,18 +916,17 @@ shut_us_down:
* at any time after this call. However, all the items associated
* with the transaction are still locked and pinned in memory.
*/
commit_lsn = xfs_log_done(mp, tp->t_ticket, &commit_iclog, log_flags);
*commit_lsn = xfs_log_done(mp, tp->t_ticket, &commit_iclog, log_flags);
tp->t_commit_lsn = commit_lsn;
if (nvec > XFS_TRANS_LOGVEC_COUNT) {
tp->t_commit_lsn = *commit_lsn;
if (nvec > XFS_TRANS_LOGVEC_COUNT)
kmem_free(log_vector);
}
/*
* 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) {
if (error || *commit_lsn == -1) {
current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
xfs_trans_uncommit(tp, flags|XFS_TRANS_ABORT);
return XFS_ERROR(EIO);
@ -909,8 +940,6 @@ shut_us_down:
*/
xfs_trans_unreserve_and_mod_sb(tp);
sync = tp->t_flags & XFS_TRANS_SYNC;
/*
* Tell the LM to call the transaction completion routine
* when the log write with LSN commit_lsn completes (e.g.
@ -953,7 +982,7 @@ shut_us_down:
* the commit lsn of this transaction for dependency tracking
* purposes.
*/
xfs_trans_unlock_items(tp, commit_lsn);
xfs_trans_unlock_items(tp, *commit_lsn);
/*
* If we detected a log error earlier, finish committing
@ -973,7 +1002,92 @@ shut_us_down:
* and the items are released we can finally allow the iclog to
* go to disk.
*/
error = xfs_log_release_iclog(mp, commit_iclog);
return xfs_log_release_iclog(mp, commit_iclog);
}
/*
* xfs_trans_commit
*
* Commit the given transaction to the log a/synchronously.
*
* XFS disk error handling mechanism is not based on a typical
* transaction abort mechanism. Logically after the filesystem
* gets marked 'SHUTDOWN', we can't let any new transactions
* be durable - ie. committed to disk - because some metadata might
* be inconsistent. In such cases, this returns an error, and the
* caller may assume that all locked objects joined to the transaction
* have already been unlocked as if the commit had succeeded.
* Do not reference the transaction structure after this call.
*/
/*ARGSUSED*/
int
_xfs_trans_commit(
xfs_trans_t *tp,
uint flags,
int *log_flushed)
{
xfs_mount_t *mp = tp->t_mountp;
xfs_lsn_t commit_lsn = -1;
int error;
int log_flags = 0;
int sync = tp->t_flags & XFS_TRANS_SYNC;
int shutdown;
/*
* Determine whether this commit is releasing a permanent
* log reservation or not.
*/
if (flags & XFS_TRANS_RELEASE_LOG_RES) {
ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
log_flags = XFS_LOG_REL_PERM_RESERV;
}
/*
* If there is nothing to be logged by the transaction,
* then unlock all of the items associated with the
* transaction and free the transaction structure.
* Also make sure to return any reserved blocks to
* the free pool.
*/
shut_us_down:
shutdown = XFS_FORCED_SHUTDOWN(mp) ? EIO : 0;
if (!(tp->t_flags & XFS_TRANS_DIRTY) || shutdown) {
xfs_trans_unreserve_and_mod_sb(tp);
/*
* It is indeed possible for the transaction to be
* not dirty but the dqinfo portion to be. All that
* means is that we have some (non-persistent) quota
* reservations that need to be unreserved.
*/
xfs_trans_unreserve_and_mod_dquots(tp);
if (tp->t_ticket) {
commit_lsn = xfs_log_done(mp, tp->t_ticket,
NULL, log_flags);
if (commit_lsn == -1 && !shutdown)
shutdown = XFS_ERROR(EIO);
}
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);
return (shutdown);
}
ASSERT(tp->t_ticket != NULL);
/*
* If we need to update the superblock, then do it now.
*/
if (tp->t_flags & XFS_TRANS_SB_DIRTY)
xfs_trans_apply_sb_deltas(tp);
xfs_trans_apply_dquot_deltas(tp);
error = xfs_trans_commit_iclog(mp, tp, &commit_lsn, flags);
if (error == ENOMEM) {
xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR);
goto shut_us_down;
}
/*
* If the transaction needs to be synchronous, then force the
@ -992,47 +1106,6 @@ shut_us_down:
return (error);
}
/*
* Total up the number of log iovecs needed to commit this
* transaction. The transaction itself needs one for the
* transaction header. Ask each dirty item in turn how many
* it needs to get the total.
*/
STATIC uint
xfs_trans_count_vecs(
xfs_trans_t *tp)
{
int nvecs;
xfs_log_item_desc_t *lidp;
nvecs = 1;
lidp = xfs_trans_first_item(tp);
ASSERT(lidp != NULL);
/* In the non-debug case we need to start bailing out if we
* didn't find a log_item here, return zero and let trans_commit
* deal with it.
*/
if (lidp == NULL)
return 0;
while (lidp != NULL) {
/*
* Skip items which aren't dirty in this transaction.
*/
if (!(lidp->lid_flags & XFS_LID_DIRTY)) {
lidp = xfs_trans_next_item(tp, lidp);
continue;
}
lidp->lid_size = IOP_SIZE(lidp->lid_item);
nvecs += lidp->lid_size;
lidp = xfs_trans_next_item(tp, lidp);
}
return nvecs;
}
/*
* Called from the trans_commit code when we notice that
* the filesystem is in the middle of a forced shutdown.
@ -1062,68 +1135,6 @@ xfs_trans_uncommit(
xfs_trans_free(tp);
}
/*
* Fill in the vector with pointers to data to be logged
* by this transaction. The transaction header takes
* the first vector, and then each dirty item takes the
* number of vectors it indicated it needed in xfs_trans_count_vecs().
*
* As each item fills in the entries it needs, also pin the item
* so that it cannot be flushed out until the log write completes.
*/
STATIC void
xfs_trans_fill_vecs(
xfs_trans_t *tp,
xfs_log_iovec_t *log_vector)
{
xfs_log_item_desc_t *lidp;
xfs_log_iovec_t *vecp;
uint nitems;
/*
* Skip over the entry for the transaction header, we'll
* fill that in at the end.
*/
vecp = log_vector + 1; /* pointer arithmetic */
nitems = 0;
lidp = xfs_trans_first_item(tp);
ASSERT(lidp != NULL);
while (lidp != NULL) {
/*
* Skip items which aren't dirty in this transaction.
*/
if (!(lidp->lid_flags & XFS_LID_DIRTY)) {
lidp = xfs_trans_next_item(tp, lidp);
continue;
}
/*
* The item may be marked dirty but not log anything.
* This can be used to get called when a transaction
* is committed.
*/
if (lidp->lid_size) {
nitems++;
}
IOP_FORMAT(lidp->lid_item, vecp);
vecp += lidp->lid_size; /* pointer arithmetic */
IOP_PIN(lidp->lid_item);
lidp = xfs_trans_next_item(tp, lidp);
}
/*
* Now that we've counted the number of items in this
* transaction, fill in the transaction header.
*/
tp->t_header.th_magic = XFS_TRANS_HEADER_MAGIC;
tp->t_header.th_type = tp->t_type;
tp->t_header.th_num_items = nitems;
log_vector->i_addr = (xfs_caddr_t)&tp->t_header;
log_vector->i_len = sizeof(xfs_trans_header_t);
log_vector->i_type = XLOG_REG_TYPE_TRANSHDR;
}
/*
* Unlock all of the transaction's items and free the transaction.
* The transaction must not have modified any of its items, because