summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2020-10-25 17:14:52 -0700
committerDarrick J. Wong <darrick.wong@oracle.com>2020-10-26 18:32:20 -0700
commit0acc119063cb28804d86f5c0b21f28c080ab12a4 (patch)
tree1e4e1bb9f6e51e3f277b5fa2667a55a9f06b9a55
parentd483a176342d5a1c0babd6332c120ca106a5894c (diff)
xfs: create a polled function to force inode inactivationdeferred-inactivation_2020-10-26
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 <darrick.wong@oracle.com>
-rw-r--r--fs/xfs/xfs_icache.c47
-rw-r--r--fs/xfs/xfs_icache.h2
-rw-r--r--fs/xfs/xfs_mount.c2
-rw-r--r--fs/xfs/xfs_mount.h5
-rw-r--r--fs/xfs/xfs_super.c3
5 files changed, 56 insertions, 3 deletions
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index 426e63c7718a..a2096ca82504 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -26,6 +26,7 @@
#include "xfs_health.h"
#include <linux/iversion.h>
+#include <linux/nmi.h>
STATIC int xfs_inode_free_eofblocks(struct xfs_inode *ip, void *args);
STATIC int xfs_inode_free_cowblocks(struct xfs_inode *ip, void *args);
@@ -2114,8 +2115,12 @@ xfs_inactive_inodes(
struct xfs_mount *mp,
struct xfs_eofblocks *eofb)
{
- return __xfs_inode_walk(mp, XFS_INODE_WALK_INACTIVE,
+ int error;
+
+ error = __xfs_inode_walk(mp, XFS_INODE_WALK_INACTIVE,
xfs_inactive_inode, eofb, XFS_ICI_INACTIVE_TAG);
+ wake_up(&mp->m_inactive_wait);
+ return error;
}
/* Try to get inode inactivation moving. */
@@ -2145,6 +2150,7 @@ xfs_inactive_worker(
if (error && error != -EAGAIN)
xfs_err(mp, "inode inactivation failed, error %d", error);
+ wake_up(&mp->m_inactive_wait);
sb_end_write(mp->m_super);
xfs_inactive_work_queue(pag);
}
@@ -2237,3 +2243,42 @@ xfs_inactive_schedule_now(
spin_unlock(&pag->pag_ici_lock);
}
}
+
+/* Return true if there are inodes still being inactivated. */
+static bool
+xfs_inactive_pending(
+ struct xfs_mount *mp)
+{
+ struct xfs_perag *pag;
+ xfs_agnumber_t agno = 0;
+ bool ret = false;
+
+ while (!ret &&
+ (pag = xfs_perag_get_tag(mp, agno, XFS_ICI_INACTIVE_TAG))) {
+ agno = pag->pag_agno + 1;
+ spin_lock(&pag->pag_ici_lock);
+ if (pag->pag_ici_inactive)
+ ret = true;
+ spin_unlock(&pag->pag_ici_lock);
+ xfs_perag_put(pag);
+ }
+
+ return ret;
+}
+
+/*
+ * Flush all pending inactivation work and poll until finished. This function
+ * is for callers that must flush with vfs locks held, such as unmount,
+ * remount, and iunlinks processing during mount.
+ */
+void
+xfs_inactive_force_poll(
+ struct xfs_mount *mp)
+{
+ xfs_inactive_schedule_now(mp);
+
+ while (!wait_event_timeout(mp->m_inactive_wait,
+ xfs_inactive_pending(mp) == false, HZ)) {
+ touch_softlockup_watchdog();
+ }
+}
diff --git a/fs/xfs/xfs_icache.h b/fs/xfs/xfs_icache.h
index b633b358685a..0988e0c30317 100644
--- a/fs/xfs/xfs_icache.h
+++ b/fs/xfs/xfs_icache.h
@@ -82,4 +82,6 @@ void xfs_inactive_shutdown(struct xfs_mount *mp);
void xfs_inactive_cancel_work(struct xfs_mount *mp);
void xfs_inactive_schedule_now(struct xfs_mount *mp);
+void xfs_inactive_force_poll(struct xfs_mount *mp);
+
#endif
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index 01a05ec822a5..24cc8c6ab217 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -1062,7 +1062,7 @@ xfs_unmountfs(
* Since this can involve finobt updates, do it now before we lose the
* per-AG space reservations.
*/
- xfs_inactive_force(mp);
+ xfs_inactive_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 409d4d438244..698adc0d8025 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -221,6 +221,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 42fc4e8b0a33..113efe2b8316 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -1797,7 +1797,7 @@ xfs_remount_ro(
* Since this can involve finobt updates, do it now before we lose the
* per-AG space reservations.
*/
- xfs_inactive_force(mp);
+ xfs_inactive_force_poll(mp);
/* Free the per-AG metadata reservation pool. */
error = xfs_fs_unreserve_ag_blocks(mp);
@@ -1923,6 +1923,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