Skip to content

Commit 6191cf3

Browse files
Brian FosterDarrick J. Wong
authored andcommitted
xfs: flush inodegc workqueue tasks before cancel
The xfs_inodegc_stop() helper performs a high level flush of pending work on the percpu queues and then runs a cancel_work_sync() on each of the percpu work tasks to ensure all work has completed before returning. While cancel_work_sync() waits for wq tasks to complete, it does not guarantee work tasks have started. This means that the _stop() helper can queue and instantly cancel a wq task without having completed the associated work. This can be observed by tracepoint inspection of a simple "rm -f <file>; fsfreeze -f <mnt>" test: xfs_destroy_inode: ... ino 0x83 ... xfs_inode_set_need_inactive: ... ino 0x83 ... xfs_inodegc_stop: ... ... xfs_inodegc_start: ... xfs_inodegc_worker: ... xfs_inode_inactivating: ... ino 0x83 ... The first few lines show that the inode is removed and need inactive state set, but the inactivation work has not completed before the inodegc mechanism stops. The inactivation doesn't actually occur until the fs is unfrozen and the gc mechanism starts back up. Note that this test requires fsfreeze to reproduce because xfs_freeze indirectly invokes xfs_fs_statfs(), which calls xfs_inodegc_flush(). When this occurs, the workqueue try_to_grab_pending() logic first tries to steal the pending bit, which does not succeed because the bit has been set by queue_work_on(). Subsequently, it checks for association of a pool workqueue from the work item under the pool lock. This association is set at the point a work item is queued and cleared when dequeued for processing. If the association exists, the work item is removed from the queue and cancel_work_sync() returns true. If the pwq association is cleared, the remove attempt assumes the task is busy and retries (eventually returning false to the caller after waiting for the work task to complete). To avoid this race, we can flush each work item explicitly before cancel. However, since the _queue_all() already schedules each underlying work item, the workqueue level helpers are sufficient to achieve the same ordering effect. E.g., the inodegc enabled flag prevents scheduling any further work in the _stop() case. Use the drain_workqueue() helper in this particular case to make the intent a bit more self explanatory. Signed-off-by: Brian Foster <bfoster@redhat.com> Reviewed-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Dave Chinner <dchinner@redhat.com>
1 parent a8e422a commit 6191cf3

File tree

1 file changed

+4
-18
lines changed

1 file changed

+4
-18
lines changed

fs/xfs/xfs_icache.c

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1852,28 +1852,20 @@ xfs_inodegc_worker(
18521852
}
18531853

18541854
/*
1855-
* Force all currently queued inode inactivation work to run immediately, and
1856-
* wait for the work to finish. Two pass - queue all the work first pass, wait
1857-
* for it in a second pass.
1855+
* Force all currently queued inode inactivation work to run immediately and
1856+
* wait for the work to finish.
18581857
*/
18591858
void
18601859
xfs_inodegc_flush(
18611860
struct xfs_mount *mp)
18621861
{
1863-
struct xfs_inodegc *gc;
1864-
int cpu;
1865-
18661862
if (!xfs_is_inodegc_enabled(mp))
18671863
return;
18681864

18691865
trace_xfs_inodegc_flush(mp, __return_address);
18701866

18711867
xfs_inodegc_queue_all(mp);
1872-
1873-
for_each_online_cpu(cpu) {
1874-
gc = per_cpu_ptr(mp->m_inodegc, cpu);
1875-
flush_work(&gc->work);
1876-
}
1868+
flush_workqueue(mp->m_inodegc_wq);
18771869
}
18781870

18791871
/*
@@ -1884,18 +1876,12 @@ void
18841876
xfs_inodegc_stop(
18851877
struct xfs_mount *mp)
18861878
{
1887-
struct xfs_inodegc *gc;
1888-
int cpu;
1889-
18901879
if (!xfs_clear_inodegc_enabled(mp))
18911880
return;
18921881

18931882
xfs_inodegc_queue_all(mp);
1883+
drain_workqueue(mp->m_inodegc_wq);
18941884

1895-
for_each_online_cpu(cpu) {
1896-
gc = per_cpu_ptr(mp->m_inodegc, cpu);
1897-
cancel_work_sync(&gc->work);
1898-
}
18991885
trace_xfs_inodegc_stop(mp, __return_address);
19001886
}
19011887

0 commit comments

Comments
 (0)