Skip to content

Commit 3c4cb76

Browse files
dgchinnerDarrick J. Wong
authored andcommitted
xfs: xfs_trans_commit() path must check for log shutdown
If a shut races with xfs_trans_commit() and we have shut down the filesystem but not the log, we will still cancel the transaction. This can result in aborting dirty log items instead of committing and pinning them whilst the log is still running. Hence we can end up with dirty, unlogged metadata that isn't in the AIL in memory that can be flushed to disk via writeback clustering. This was discovered from a g/388 trace where an inode log item was having IO completed on it and it wasn't in the AIL, hence tripping asserts xfs_ail_check(). Inode cluster writeback started long after the filesystem shutdown started, and long after the transaction containing the dirty inode was aborted and the log item marked XFS_LI_ABORTED. The inode was seen as dirty and unpinned, so it was flushed. IO completion tried to remove the inode from the AIL, at which point stuff went bad: XFS (pmem1): Log I/O Error (0x6) detected at xfs_fs_goingdown+0xa3/0xf0 (fs/xfs/xfs_fsops.c:500). Shutting down filesystem. XFS: Assertion failed: in_ail, file: fs/xfs/xfs_trans_ail.c, line: 67 XFS (pmem1): Please unmount the filesystem and rectify the problem(s) Workqueue: xfs-buf/pmem1 xfs_buf_ioend_work RIP: 0010:assfail+0x27/0x2d Call Trace: <TASK> xfs_ail_check+0xa8/0x180 xfs_ail_delete_one+0x3b/0xf0 xfs_buf_inode_iodone+0x329/0x3f0 xfs_buf_ioend+0x1f8/0x530 xfs_buf_ioend_work+0x15/0x20 process_one_work+0x1ac/0x390 worker_thread+0x56/0x3c0 kthread+0xf6/0x120 ret_from_fork+0x1f/0x30 </TASK> xfs_trans_commit() needs to check log state for shutdown, not mount state. It cannot abort dirty log items while the log is still running as dirty items must remained pinned in memory until they are either committed to the journal or the log has shut down and they can be safely tossed away. Hence if the log has not shut down, the xfs_trans_commit() path must allow completed transactions to commit to the CIL and pin the dirty items even if a mount shutdown has started. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Darrick J. Wong <djwong@kernel.org>
1 parent 41e6362 commit 3c4cb76

1 file changed

Lines changed: 33 additions & 15 deletions

File tree

fs/xfs/xfs_trans.c

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,7 @@ __xfs_trans_commit(
836836
bool regrant)
837837
{
838838
struct xfs_mount *mp = tp->t_mountp;
839+
struct xlog *log = mp->m_log;
839840
xfs_csn_t commit_seq = 0;
840841
int error = 0;
841842
int sync = tp->t_flags & XFS_TRANS_SYNC;
@@ -864,7 +865,13 @@ __xfs_trans_commit(
864865
if (!(tp->t_flags & XFS_TRANS_DIRTY))
865866
goto out_unreserve;
866867

867-
if (xfs_is_shutdown(mp)) {
868+
/*
869+
* We must check against log shutdown here because we cannot abort log
870+
* items and leave them dirty, inconsistent and unpinned in memory while
871+
* the log is active. This leaves them open to being written back to
872+
* disk, and that will lead to on-disk corruption.
873+
*/
874+
if (xlog_is_shutdown(log)) {
868875
error = -EIO;
869876
goto out_unreserve;
870877
}
@@ -878,7 +885,7 @@ __xfs_trans_commit(
878885
xfs_trans_apply_sb_deltas(tp);
879886
xfs_trans_apply_dquot_deltas(tp);
880887

881-
xlog_cil_commit(mp->m_log, tp, &commit_seq, regrant);
888+
xlog_cil_commit(log, tp, &commit_seq, regrant);
882889

883890
xfs_trans_free(tp);
884891

@@ -905,10 +912,10 @@ __xfs_trans_commit(
905912
*/
906913
xfs_trans_unreserve_and_mod_dquots(tp);
907914
if (tp->t_ticket) {
908-
if (regrant && !xlog_is_shutdown(mp->m_log))
909-
xfs_log_ticket_regrant(mp->m_log, tp->t_ticket);
915+
if (regrant && !xlog_is_shutdown(log))
916+
xfs_log_ticket_regrant(log, tp->t_ticket);
910917
else
911-
xfs_log_ticket_ungrant(mp->m_log, tp->t_ticket);
918+
xfs_log_ticket_ungrant(log, tp->t_ticket);
912919
tp->t_ticket = NULL;
913920
}
914921
xfs_trans_free_items(tp, !!error);
@@ -926,18 +933,27 @@ xfs_trans_commit(
926933
}
927934

928935
/*
929-
* Unlock all of the transaction's items and free the transaction.
930-
* The transaction must not have modified any of its items, because
931-
* there is no way to restore them to their previous state.
936+
* Unlock all of the transaction's items and free the transaction. If the
937+
* transaction is dirty, we must shut down the filesystem because there is no
938+
* way to restore them to their previous state.
932939
*
933-
* If the transaction has made a log reservation, make sure to release
934-
* it as well.
940+
* If the transaction has made a log reservation, make sure to release it as
941+
* well.
942+
*
943+
* This is a high level function (equivalent to xfs_trans_commit()) and so can
944+
* be called after the transaction has effectively been aborted due to the mount
945+
* being shut down. However, if the mount has not been shut down and the
946+
* transaction is dirty we will shut the mount down and, in doing so, that
947+
* guarantees that the log is shut down, too. Hence we don't need to be as
948+
* careful with shutdown state and dirty items here as we need to be in
949+
* xfs_trans_commit().
935950
*/
936951
void
937952
xfs_trans_cancel(
938953
struct xfs_trans *tp)
939954
{
940955
struct xfs_mount *mp = tp->t_mountp;
956+
struct xlog *log = mp->m_log;
941957
bool dirty = (tp->t_flags & XFS_TRANS_DIRTY);
942958

943959
trace_xfs_trans_cancel(tp, _RET_IP_);
@@ -955,16 +971,18 @@ xfs_trans_cancel(
955971
}
956972

957973
/*
958-
* See if the caller is relying on us to shut down the
959-
* filesystem. This happens in paths where we detect
960-
* corruption and decide to give up.
974+
* See if the caller is relying on us to shut down the filesystem. We
975+
* only want an error report if there isn't already a shutdown in
976+
* progress, so we only need to check against the mount shutdown state
977+
* here.
961978
*/
962979
if (dirty && !xfs_is_shutdown(mp)) {
963980
XFS_ERROR_REPORT("xfs_trans_cancel", XFS_ERRLEVEL_LOW, mp);
964981
xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
965982
}
966983
#ifdef DEBUG
967-
if (!dirty && !xfs_is_shutdown(mp)) {
984+
/* Log items need to be consistent until the log is shut down. */
985+
if (!dirty && !xlog_is_shutdown(log)) {
968986
struct xfs_log_item *lip;
969987

970988
list_for_each_entry(lip, &tp->t_items, li_trans)
@@ -975,7 +993,7 @@ xfs_trans_cancel(
975993
xfs_trans_unreserve_and_mod_dquots(tp);
976994

977995
if (tp->t_ticket) {
978-
xfs_log_ticket_ungrant(mp->m_log, tp->t_ticket);
996+
xfs_log_ticket_ungrant(log, tp->t_ticket);
979997
tp->t_ticket = NULL;
980998
}
981999

0 commit comments

Comments
 (0)