Skip to content

Commit 70447e0

Browse files
dgchinnerDarrick J. Wong
authored andcommitted
xfs: async CIL flushes need pending pushes to be made stable
When the AIL tries to flush the CIL, it relies on the CIL push ending up on stable storage without having to wait for and manipulate iclog state directly. However, if there is already a pending CIL push when the AIL tries to flush the CIL, it won't set the cil->xc_push_commit_stable flag and so the CIL push will not actively flush the commit record iclog. generic/530 when run on a single CPU test VM can trigger this fairly reliably. This test exercises unlinked inode recovery, and can result in inodes being pinned in memory by ongoing modifications to the inode cluster buffer to record unlinked list modifications. As a result, the first inode unlinked in a buffer can pin the tail of the log whilst the inode cluster buffer is pinned by the current checkpoint that has been pushed but isn't on stable storage because because the cil->xc_push_commit_stable was not set. This results in the log/AIL effectively deadlocking until something triggers the commit record iclog to be pushed to stable storage (i.e. the periodic log worker calling xfs_log_force()). The fix is two-fold - first we should always set the cil->xc_push_commit_stable when xlog_cil_flush() is called, regardless of whether there is already a pending push or not. Second, if the CIL is empty, we should trigger an iclog flush to ensure that the iclogs of the last checkpoint have actually been submitted to disk as that checkpoint may not have been run under stable completion constraints. Reported-and-tested-by: Matthew Wilcox <willy@infradead.org> Fixes: 0020a19 ("xfs: AIL needs asynchronous CIL forcing") 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 941fbdf commit 70447e0

1 file changed

Lines changed: 19 additions & 3 deletions

File tree

fs/xfs/xfs_log_cil.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,18 +1243,27 @@ xlog_cil_push_now(
12431243
if (!async)
12441244
flush_workqueue(cil->xc_push_wq);
12451245

1246+
spin_lock(&cil->xc_push_lock);
1247+
1248+
/*
1249+
* If this is an async flush request, we always need to set the
1250+
* xc_push_commit_stable flag even if something else has already queued
1251+
* a push. The flush caller is asking for the CIL to be on stable
1252+
* storage when the next push completes, so regardless of who has queued
1253+
* the push, the flush requires stable semantics from it.
1254+
*/
1255+
cil->xc_push_commit_stable = async;
1256+
12461257
/*
12471258
* If the CIL is empty or we've already pushed the sequence then
1248-
* there's no work we need to do.
1259+
* there's no more work that we need to do.
12491260
*/
1250-
spin_lock(&cil->xc_push_lock);
12511261
if (list_empty(&cil->xc_cil) || push_seq <= cil->xc_push_seq) {
12521262
spin_unlock(&cil->xc_push_lock);
12531263
return;
12541264
}
12551265

12561266
cil->xc_push_seq = push_seq;
1257-
cil->xc_push_commit_stable = async;
12581267
queue_work(cil->xc_push_wq, &cil->xc_ctx->push_work);
12591268
spin_unlock(&cil->xc_push_lock);
12601269
}
@@ -1352,6 +1361,13 @@ xlog_cil_flush(
13521361

13531362
trace_xfs_log_force(log->l_mp, seq, _RET_IP_);
13541363
xlog_cil_push_now(log, seq, true);
1364+
1365+
/*
1366+
* If the CIL is empty, make sure that any previous checkpoint that may
1367+
* still be in an active iclog is pushed to stable storage.
1368+
*/
1369+
if (list_empty(&log->l_cilp->xc_cil))
1370+
xfs_log_force(log->l_mp, 0);
13551371
}
13561372

13571373
/*

0 commit comments

Comments
 (0)