On Mon, 10 Apr 2023 18:15:57 -0700 "Darrick J. Wong" <
djw...@kernel.org>
Take a look at the diff below.
To close the race, a quick fix is put drain_workqueue() under
mp->m_inodegc_stop_mutex on one hand. On the other, queue work
after mutex_trylock().
+++ b/fs/xfs/xfs_icache.c
@@ -1917,10 +1917,14 @@ xfs_inodegc_stop(
if (!xfs_clear_inodegc_enabled(mp))
return;
+ mutex_lock(&mp->m_inodegc_stop_mutex);
+
xfs_inodegc_queue_all(mp);
drain_workqueue(mp->m_inodegc_wq);
trace_xfs_inodegc_stop(mp, __return_address);
+
+ mutex_unlock(&mp->m_inodegc_stop_mutex);
}
/*
@@ -2042,7 +2046,6 @@ xfs_inodegc_queue(
struct xfs_inodegc *gc;
int items;
unsigned int shrinker_hits;
- unsigned long queue_delay = 1;
trace_xfs_inode_set_need_inactive(ip);
spin_lock(&ip->i_flags_lock);
@@ -2060,16 +2063,18 @@ xfs_inodegc_queue(
* is scheduled to run on this CPU.
*/
if (!xfs_is_inodegc_enabled(mp)) {
+out:
put_cpu_ptr(gc);
return;
}
- if (xfs_inodegc_want_queue_work(ip, items))
- queue_delay = 0;
+ if (!mutex_trylock(&mp->m_inodegc_stop_mutex))
+ goto out;
trace_xfs_inodegc_queue(mp, __return_address);
- mod_delayed_work(mp->m_inodegc_wq, &gc->work, queue_delay);
+ mod_delayed_work(mp->m_inodegc_wq, &gc->work, 0);
put_cpu_ptr(gc);
+ mutex_unlock(&mp->m_inodegc_stop_mutex);
if (xfs_inodegc_want_flush_work(ip, items, shrinker_hits)) {
trace_xfs_inodegc_throttle(mp, __return_address);
@@ -2201,6 +2206,9 @@ xfs_inodegc_shrinker_scan(
if (!xfs_is_inodegc_enabled(mp))
return SHRINK_STOP;
+ if (!mutex_trylock(&mp->m_inodegc_stop_mutex))
+ return SHRINK_STOP;
+
trace_xfs_inodegc_shrinker_scan(mp, sc, __return_address);
for_each_online_cpu(cpu) {
@@ -2213,6 +2221,7 @@ xfs_inodegc_shrinker_scan(
no_items = false;
}
}
+ mutex_unlock(&mp->m_inodegc_stop_mutex);
/*
* If there are no inodes to inactivate, we don't want the shrinker
--