aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_log.c
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2012-10-08 06:56:12 -0400
committerBen Myers <bpm@sgi.com>2012-10-17 14:43:35 -0400
commitd35e88faa3b0fc2cea35c3b2dca358b5cd09b45f (patch)
tree8aa8c87b9c31b0180fa08c905a6dca84223adca2 /fs/xfs/xfs_log.c
parent33479e0542df066fb0b47df18780e93bfe6e0dc5 (diff)
xfs: only update the last_sync_lsn when a transaction completes
The log write code stamps each iclog with the current tail LSN in the iclog header so that recovery knows where to find the tail of thelog once it has found the head. Normally this is taken from the first item on the AIL - the log item that corresponds to the oldest active item in the log. The problem is that when the AIL is empty, the tail lsn is dervied from the the l_last_sync_lsn, which is the LSN of the last iclog to be written to the log. In most cases this doesn't happen, because the AIL is rarely empty on an active filesystem. However, when it does, it opens up an interesting case when the transaction being committed to the iclog spans multiple iclogs. That is, the first iclog is stamped with the l_last_sync_lsn, and IO is issued. Then the next iclog is setup, the changes copied into the iclog (takes some time), and then the l_last_sync_lsn is stamped into the header and IO is issued. This is still the same transaction, so the tail lsn of both iclogs must be the same for log recovery to find the entire transaction to be able to replay it. The problem arises in that the iclog buffer IO completion updates the l_last_sync_lsn with it's own LSN. Therefore, If the first iclog completes it's IO before the second iclog is filled and has the tail lsn stamped in it, it will stamp the LSN of the first iclog into it's tail lsn field. If the system fails at this point, log recovery will not see a complete transaction, so the transaction will no be replayed. The fix is simple - the l_last_sync_lsn is updated when a iclog buffer IO completes, and this is incorrect. The l_last_sync_lsn shoul dbe updated when a transaction is completed by a iclog buffer IO. That is, only iclog buffers that have transaction commit callbacks attached to them should update the l_last_sync_lsn. This means that the last_sync_lsn will only move forward when a commit record it written, not in the middle of a large transaction that is rolling through multiple iclog buffers. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Mark Tinguely <tinguely@sgi.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Ben Myers <bpm@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_log.c')
-rw-r--r--fs/xfs/xfs_log.c19
1 files changed, 16 insertions, 3 deletions
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index d2d59692739f..46b6986e39b0 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -2461,14 +2461,27 @@ xlog_state_do_callback(
2461 2461
2462 2462
2463 /* 2463 /*
2464 * update the last_sync_lsn before we drop the 2464 * Completion of a iclog IO does not imply that
2465 * a transaction has completed, as transactions
2466 * can be large enough to span many iclogs. We
2467 * cannot change the tail of the log half way
2468 * through a transaction as this may be the only
2469 * transaction in the log and moving th etail to
2470 * point to the middle of it will prevent
2471 * recovery from finding the start of the
2472 * transaction. Hence we should only update the
2473 * last_sync_lsn if this iclog contains
2474 * transaction completion callbacks on it.
2475 *
2476 * We have to do this before we drop the
2465 * icloglock to ensure we are the only one that 2477 * icloglock to ensure we are the only one that
2466 * can update it. 2478 * can update it.
2467 */ 2479 */
2468 ASSERT(XFS_LSN_CMP(atomic64_read(&log->l_last_sync_lsn), 2480 ASSERT(XFS_LSN_CMP(atomic64_read(&log->l_last_sync_lsn),
2469 be64_to_cpu(iclog->ic_header.h_lsn)) <= 0); 2481 be64_to_cpu(iclog->ic_header.h_lsn)) <= 0);
2470 atomic64_set(&log->l_last_sync_lsn, 2482 if (iclog->ic_callback)
2471 be64_to_cpu(iclog->ic_header.h_lsn)); 2483 atomic64_set(&log->l_last_sync_lsn,
2484 be64_to_cpu(iclog->ic_header.h_lsn));
2472 2485
2473 } else 2486 } else
2474 ioerrors++; 2487 ioerrors++;