|
@@ -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(
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/*
|
|
|
- * 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.
|
|
|
+ * 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.
|
|
|
*/
|
|
|
- /*ARGSUSED*/
|
|
|
-int
|
|
|
-_xfs_trans_commit(
|
|
|
- xfs_trans_t *tp,
|
|
|
- uint flags,
|
|
|
- int *log_flushed)
|
|
|
+static uint
|
|
|
+xfs_trans_count_vecs(
|
|
|
+ xfs_trans_t *tp)
|
|
|
{
|
|
|
- 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;
|
|
|
+ int nvecs;
|
|
|
+ xfs_log_item_desc_t *lidp;
|
|
|
|
|
|
- commit_lsn = -1;
|
|
|
+ nvecs = 1;
|
|
|
+ lidp = xfs_trans_first_item(tp);
|
|
|
+ ASSERT(lidp != NULL);
|
|
|
|
|
|
- /*
|
|
|
- * Determine whether this commit is releasing a permanent
|
|
|
- * log reservation or not.
|
|
|
+ /* 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 (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;
|
|
|
+ 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);
|
|
|
}
|
|
|
- mp = tp->t_mountp;
|
|
|
+
|
|
|
+ return nvecs;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * 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(
|
|
|
+ struct xfs_trans *tp,
|
|
|
+ struct xfs_log_iovec *log_vector)
|
|
|
+{
|
|
|
+ xfs_log_item_desc_t *lidp;
|
|
|
+ struct xfs_log_iovec *vecp;
|
|
|
+ uint nitems;
|
|
|
|
|
|
/*
|
|
|
- * 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.
|
|
|
+ * Skip over the entry for the transaction header, we'll
|
|
|
+ * fill that in at the end.
|
|
|
*/
|
|
|
-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);
|
|
|
+ vecp = log_vector + 1;
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
- * 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.
|
|
|
+ * The item may be marked dirty but not log anything. This can
|
|
|
+ * be used to get called when a transaction is committed.
|
|
|
*/
|
|
|
- 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);
|
|
|
+ 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
|