From: Darrick J. Wong <djwong@xxxxxxxxxx> Create a polled version of xfs_inactive_force so that we can force inactivation while holding a lock (usually the umount lock) without tripping over the softlockup timer. This is for callers that hold vfs locks while calling inactivation, which is currently unmount, iunlink processing during mount, and rw->ro remount. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- fs/xfs/xfs_icache.c | 40 +++++++++++++++++++++++++++++++++++++++- fs/xfs/xfs_icache.h | 1 + fs/xfs/xfs_mount.c | 2 +- fs/xfs/xfs_mount.h | 5 +++++ fs/xfs/xfs_super.c | 3 ++- 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 29d99e5edbdf..53104c463d0b 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -25,6 +25,7 @@ #include "xfs_ialloc.h" #include <linux/iversion.h> +#include <linux/nmi.h> /* Forward declarations to reduce indirect calls in xfs_inode_walk_ag */ static int xfs_blockgc_scan_inode(struct xfs_inode *ip, void *args); @@ -2064,9 +2065,13 @@ xfs_inodegc_free_space( struct xfs_mount *mp, struct xfs_eofblocks *eofb) { + int error; + trace_xfs_inodegc_free_space(mp, eofb, _RET_IP_); - return xfs_inode_walk(mp, xfs_inodegc_inactivate, eofb); + error = xfs_inode_walk(mp, xfs_inodegc_inactivate, eofb); + wake_up(&mp->m_inactive_wait); + return error; } /* Background inode inactivation worker. */ @@ -2136,6 +2141,39 @@ xfs_inodegc_force( flush_workqueue(mp->m_gc_workqueue); } +/* + * Force all inode inactivation work to run immediately, and poll until the + * work is complete. Callers should only use this function if they must + * inactivate inodes while holding VFS locks, and must ensure that no new + * inodes will be queued for inactivation. + */ +void +xfs_inodegc_force_poll( + struct xfs_mount *mp) +{ + struct xfs_perag *pag; + xfs_agnumber_t agno; + bool queued = false; + + for_each_perag_tag(mp, agno, pag, XFS_ICI_INODEGC_TAG) { + xfs_inodegc_force_pag(pag); + queued = true; + } + if (!queued) + return; + + /* + * Touch the softlockup watchdog every 1/10th of a second while there + * are still inactivation-tagged inodes in the filesystem. + */ + while (!wait_event_timeout(mp->m_inactive_wait, + !radix_tree_tagged(&mp->m_perag_tree, + XFS_ICI_INODEGC_TAG), + HZ / 10)) { + touch_softlockup_watchdog(); + } +} + /* Stop all queued inactivation work. */ void xfs_inodegc_stop( diff --git a/fs/xfs/xfs_icache.h b/fs/xfs/xfs_icache.h index d4171998deef..371453028dc8 100644 --- a/fs/xfs/xfs_icache.h +++ b/fs/xfs/xfs_icache.h @@ -78,6 +78,7 @@ void xfs_blockgc_start(struct xfs_mount *mp); void xfs_inodegc_worker(struct work_struct *work); void xfs_inodegc_force(struct xfs_mount *mp); +void xfs_inodegc_force_poll(struct xfs_mount *mp); void xfs_inodegc_stop(struct xfs_mount *mp); void xfs_inodegc_start(struct xfs_mount *mp); int xfs_inodegc_free_space(struct xfs_mount *mp, struct xfs_eofblocks *eofb); diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index a5963061485c..1012b1b361ba 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -1109,7 +1109,7 @@ xfs_unmountfs( * Since this can involve finobt updates, do it now before we lose the * per-AG space reservations. */ - xfs_inodegc_force(mp); + xfs_inodegc_force_poll(mp); xfs_blockgc_stop(mp); xfs_fs_unreserve_ag_blocks(mp); diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index ff765c73a542..61dad1ba4dcf 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -213,6 +213,11 @@ typedef struct xfs_mount { unsigned int *m_errortag; struct xfs_kobj m_errortag_kobj; #endif + /* + * Use this to wait for the inode inactivation workqueue to finish + * inactivating all the inodes. + */ + struct wait_queue_head m_inactive_wait; } xfs_mount_t; #define M_IGEO(mp) (&(mp)->m_ino_geo) diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 566e5657c1b0..8329a3efced7 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -1754,7 +1754,7 @@ xfs_remount_ro( * Since this can involve finobt updates, do it now before we lose the * per-AG space reservations. */ - xfs_inodegc_force(mp); + xfs_inodegc_force_poll(mp); /* Free the per-AG metadata reservation pool. */ error = xfs_fs_unreserve_ag_blocks(mp); @@ -1880,6 +1880,7 @@ static int xfs_init_fs_context( INIT_WORK(&mp->m_flush_inodes_work, xfs_flush_inodes_worker); INIT_DELAYED_WORK(&mp->m_reclaim_work, xfs_reclaim_worker); mp->m_kobj.kobject.kset = xfs_kset; + init_waitqueue_head(&mp->m_inactive_wait); /* * We don't create the finobt per-ag space reservation until after log * recovery, so we must set this to true so that an ifree transaction