summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2019-08-30 15:45:09 -0700
committerDarrick J. Wong <darrick.wong@oracle.com>2019-10-09 09:39:24 -0700
commit310f9e7a3ee2d06f41921ca1a0c05000932e0f9e (patch)
treefe170abea95d5340660cc087630fc115259ed140 /fs
parent13511738b007eb913615be24fcc700270d8ae143 (diff)
xfs: deferred inode inactivation
Instead of calling xfs_inactive directly from xfs_fs_destroy_inode, defer the inactivation phase to a separate workqueue. With this we avoid blocking memory reclaim on filesystem metadata updates that are necessary to free an in-core inode, such as post-eof block freeing, COW staging extent freeing, and truncating and freeing unlinked inodes. Now that work is deferred to a workqueue where we can do the freeing in batches. We introduce two new inode flags -- NEEDS_INACTIVE and INACTIVATING. The first flag helps our worker find inodes needing inactivation, and the second flag marks inodes that are in the process of being inactivated. A concurrent xfs_iget on the inode can still resurrect the inode by clearing NEEDS_INACTIVE (or bailing if INACTIVATING is set). Unfortunately, deferring the inactivation has one huge downside -- eventual consistency. Since all the freeing is deferred to a worker thread, one can rm a file but the space doesn't come back immediately. This can cause some odd side effects with quota accounting and statfs, so we also force inactivation scans in order to maintain the existing behaviors, at least outwardly. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/xfs/xfs_icache.c327
-rw-r--r--fs/xfs/xfs_icache.h7
-rw-r--r--fs/xfs/xfs_inode.c76
-rw-r--r--fs/xfs/xfs_inode.h15
-rw-r--r--fs/xfs/xfs_iomap.c1
-rw-r--r--fs/xfs/xfs_log_recover.c7
-rw-r--r--fs/xfs/xfs_mount.c15
-rw-r--r--fs/xfs/xfs_mount.h5
-rw-r--r--fs/xfs/xfs_qm_syscalls.c6
-rw-r--r--fs/xfs/xfs_super.c53
-rw-r--r--fs/xfs/xfs_trace.h11
11 files changed, 498 insertions, 25 deletions
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index b5b2c63eb30b..296cf2f2c417 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -25,6 +25,9 @@
#include <linux/iversion.h>
+static void xfs_perag_set_inactive_tag(struct xfs_perag *pag);
+static void xfs_perag_clear_inactive_tag(struct xfs_perag *pag);
+
/*
* Allocate and initialise an xfs_inode.
*/
@@ -205,7 +208,7 @@ xfs_perag_clear_reclaim_tag(
struct xfs_mount *mp = pag->pag_mount;
lockdep_assert_held(&pag->pag_ici_lock);
- if (--pag->pag_ici_reclaimable)
+ if (--pag->pag_ici_reclaimable || pag->pag_ici_inactive > 0)
return;
/* clear the reclaim tag from the perag radix tree */
@@ -224,10 +227,12 @@ xfs_perag_clear_reclaim_tag(
*/
void
xfs_inode_set_reclaim_tag(
- struct xfs_inode *ip)
+ struct xfs_inode *ip,
+ bool need_inactive)
{
struct xfs_mount *mp = ip->i_mount;
struct xfs_perag *pag;
+ unsigned long iflags = need_inactive ? XFS_NEED_INACTIVE : 0;
pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino));
spin_lock(&pag->pag_ici_lock);
@@ -235,8 +240,11 @@ xfs_inode_set_reclaim_tag(
radix_tree_tag_set(&pag->pag_ici_root, XFS_INO_TO_AGINO(mp, ip->i_ino),
XFS_ICI_RECLAIM_TAG);
- xfs_perag_set_reclaim_tag(pag);
- __xfs_iflags_set(ip, XFS_IRECLAIMABLE);
+ if (need_inactive)
+ xfs_perag_set_inactive_tag(pag);
+ else
+ xfs_perag_set_reclaim_tag(pag);
+ __xfs_iflags_set(ip, XFS_IRECLAIMABLE | iflags);
spin_unlock(&ip->i_flags_lock);
spin_unlock(&pag->pag_ici_lock);
@@ -315,6 +323,13 @@ xfs_iget_check_free_state(
struct xfs_inode *ip,
int flags)
{
+ /*
+ * Unlinked inodes awaiting inactivation must not be reused until we
+ * have a chance to clear the on-disk metadata.
+ */
+ if (VFS_I(ip)->i_nlink == 0 && (ip->i_flags & XFS_NEED_INACTIVE))
+ return -ENOENT;
+
if (flags & XFS_IGET_CREATE) {
/* should be a free inode */
if (VFS_I(ip)->i_mode != 0) {
@@ -374,14 +389,14 @@ xfs_iget_cache_hit(
/*
* If we are racing with another cache hit that is currently
* instantiating this inode or currently recycling it out of
- * reclaimabe state, wait for the initialisation to complete
+ * reclaimable state, wait for the initialisation to complete
* before continuing.
*
* XXX(hch): eventually we should do something equivalent to
* wait_on_inode to wait for these flags to be cleared
* instead of polling for it.
*/
- if (ip->i_flags & (XFS_INEW|XFS_IRECLAIM)) {
+ if (ip->i_flags & (XFS_INEW | XFS_IRECLAIM | XFS_INACTIVATING)) {
trace_xfs_iget_skip(ip);
XFS_STATS_INC(mp, xs_ig_frecycle);
error = -EAGAIN;
@@ -401,6 +416,8 @@ xfs_iget_cache_hit(
* Need to carefully get it back into useable state.
*/
if (ip->i_flags & XFS_IRECLAIMABLE) {
+ bool needed_inactive;
+
trace_xfs_iget_reclaim(ip);
if (flags & XFS_IGET_INCORE) {
@@ -409,16 +426,33 @@ xfs_iget_cache_hit(
}
/*
+ * If we played inactivation accounting tricks with this inode
+ * we have to undo them prior to resurrecting this inode.
+ */
+ needed_inactive = (ip->i_flags & XFS_NEED_INACTIVE);
+
+ /*
* We need to set XFS_IRECLAIM to prevent xfs_reclaim_inode
* from stomping over us while we recycle the inode. We can't
* clear the radix tree reclaimable tag yet as it requires
* pag_ici_lock to be held exclusive.
+ *
+ * Clear NEED_INACTIVE so that the inactive worker won't
+ * touch this inode now that we're trying to resurrect it.
*/
ip->i_flags |= XFS_IRECLAIM;
+ ip->i_flags &= ~XFS_NEED_INACTIVE;
spin_unlock(&ip->i_flags_lock);
rcu_read_unlock();
+ if (needed_inactive) {
+ xfs_inode_inactivation_cleanup(ip);
+ spin_lock(&pag->pag_ici_lock);
+ xfs_perag_clear_inactive_tag(pag);
+ spin_unlock(&pag->pag_ici_lock);
+ }
+
error = xfs_reinit_inode(mp, inode);
if (error) {
bool wake;
@@ -1060,7 +1094,8 @@ xfs_reclaim_inode_grab(
*/
spin_lock(&ip->i_flags_lock);
if (!__xfs_iflags_test(ip, XFS_IRECLAIMABLE) ||
- __xfs_iflags_test(ip, XFS_IRECLAIM)) {
+ __xfs_iflags_test(ip, XFS_IRECLAIM) ||
+ __xfs_iflags_test(ip, XFS_NEED_INACTIVE)) {
/* not a reclaim candidate. */
spin_unlock(&ip->i_flags_lock);
return 1;
@@ -1119,6 +1154,8 @@ xfs_reclaim_inode(
xfs_ino_t ino = ip->i_ino; /* for radix_tree_delete */
int error;
+ trace_xfs_inode_reclaiming(ip);
+
restart:
error = 0;
xfs_ilock(ip, XFS_ILOCK_EXCL);
@@ -1551,6 +1588,10 @@ xfs_inode_free_scan(
struct xfs_mount *mp,
struct xfs_eofblocks *eofb)
{
+ if (eofb->eof_flags & XFS_EOF_FLAGS_SYNC)
+ xfs_inactive_inodes(mp, eofb);
+ else
+ xfs_inactive_force(mp);
xfs_icache_free_eofblocks(mp, eofb);
xfs_icache_free_cowblocks(mp, eofb);
}
@@ -1881,3 +1922,275 @@ xfs_start_block_reaping(
xfs_queue_eofblocks(mp);
xfs_queue_cowblocks(mp);
}
+
+/*
+ * Deferred Inode Inactivation
+ * ===========================
+ *
+ * Sometimes, inodes need to have work done on them once the last program has
+ * closed the file. Typically this means cleaning out any leftover post-eof or
+ * CoW staging blocks for linked files. For inodes that have been totally
+ * unlinked, this means unmapping data/attr/cow blocks, removing the inode
+ * from the unlinked buckets, and marking it free in the inobt and inode table.
+ *
+ * This process can generate many metadata updates, which shows up as close()
+ * and unlink() calls that take a long time. We defer all that work to a
+ * per-AG workqueue which means that we can batch a lot of work and do it in
+ * inode order for better performance. Furthermore, we can control the
+ * workqueue, which means that we can avoid doing inactivation work at a bad
+ * time, such as when the fs is frozen.
+ *
+ * Deferred inactivation introduces new inode flag states (NEED_INACTIVE and
+ * INACTIVATING) and reuses the RECLAIM radix tree tag for fast access because
+ * we the radix tree only grants us three tags. We maintain separate perag
+ * counters for both types, and move counts as inodes wander the state machine,
+ * which now works as follows:
+ *
+ * When an inode hits zero refcount, we:
+ * - Set the RECLAIMABLE inode flag
+ * - Set the RECLAIM tag in the per-AG inode tree
+ * - Set the RECLAIM tag in the per-fs AG tree
+ *
+ * If the inode needs inactivation, we:
+ * - Set the NEED_INACTIVE inode flag
+ * - Increment the per-AG inactive count
+ * - Schedule background inode inactivation
+ *
+ * If the inode did not need inactivation, we:
+ * - Increment the per-AG reclaim count
+ * - Schedule background inode reclamation
+ *
+ * When it is time for background inode inactivation, we:
+ * - Set the INACTIVATING inode flag
+ * - Make all the on-disk updates
+ * - Clear both INACTIVATING and NEED_INACTIVE inode flags
+ * - Decrement the per-AG inactive count
+ * - Increment the per-AG reclaim count
+ * - Schedule background inode reclamation
+ *
+ * When it is time for background inode reclamation, we:
+ * - Set the IRECLAIM inode flag
+ * - Detach all the resources and remove the inode from the per-AG inode tree
+ * - Clear both IRECLAIM and RECLAIMABLE inode flags
+ * - Decrement the per-AG reclaim count
+ * - Clear the RECLAIM tag from the per-AG inode tree
+ * - Clear the RECLAIM tag from the per-fs AG tree if there are no more
+ * inodes waiting for reclamation or inactivation
+ */
+
+/* Queue a new inode inactivation pass if there are reclaimable inodes. */
+static void
+xfs_inactive_work_queue(
+ struct xfs_mount *mp)
+{
+ rcu_read_lock();
+ if (radix_tree_tagged(&mp->m_perag_tree, XFS_ICI_RECLAIM_TAG))
+ queue_delayed_work(mp->m_inactive_workqueue,
+ &mp->m_inactive_work,
+ msecs_to_jiffies(xfs_syncd_centisecs / 6 * 10));
+ rcu_read_unlock();
+}
+
+/* Remember that an AG has one more inode to inactivate. */
+static void
+xfs_perag_set_inactive_tag(
+ struct xfs_perag *pag)
+{
+ struct xfs_mount *mp = pag->pag_mount;
+
+ lockdep_assert_held(&pag->pag_ici_lock);
+ if (pag->pag_ici_inactive++ == 0) {
+ /* propagate the reclaim tag up into the perag radix tree */
+ spin_lock(&mp->m_perag_lock);
+ radix_tree_tag_set(&mp->m_perag_tree, pag->pag_agno,
+ XFS_ICI_RECLAIM_TAG);
+ spin_unlock(&mp->m_perag_lock);
+ }
+
+ /*
+ * Schedule periodic background inode inactivation. Inactivation can
+ * take a while, so we allow the deferral of an already-scheduled
+ * inactivation on the grounds that we prefer batching.
+ */
+ xfs_inactive_work_queue(mp);
+
+ trace_xfs_perag_set_reclaim(mp, pag->pag_agno, -1, _RET_IP_);
+}
+
+/* Remember that an AG has one less inode to inactivate. */
+static void
+xfs_perag_clear_inactive_tag(
+ struct xfs_perag *pag)
+{
+ lockdep_assert_held(&pag->pag_ici_lock);
+ pag->pag_ici_inactive--;
+ pag->pag_ici_reclaimable++;
+}
+
+/*
+ * Grab the inode for inactivation exclusively.
+ * Return true if we grabbed it.
+ */
+STATIC bool
+xfs_inactive_grab(
+ struct xfs_inode *ip,
+ int flags)
+{
+ ASSERT(rcu_read_lock_held());
+
+ /* quick check for stale RCU freed inode */
+ if (!ip->i_ino)
+ return false;
+
+ /*
+ * The radix tree lock here protects a thread in xfs_iget from racing
+ * with us starting reclaim on the inode.
+ *
+ * Due to RCU lookup, we may find inodes that have been freed and only
+ * have XFS_IRECLAIM set. Indeed, we may see reallocated inodes that
+ * aren't candidates for reclaim at all, so we must check the
+ * XFS_IRECLAIMABLE is set first before proceeding to reclaim.
+ * Obviously if XFS_NEED_INACTIVE isn't set then we ignore this inode.
+ */
+ spin_lock(&ip->i_flags_lock);
+ if (!(ip->i_flags & XFS_IRECLAIMABLE) ||
+ !(ip->i_flags & XFS_NEED_INACTIVE) ||
+ (ip->i_flags & XFS_INACTIVATING)) {
+ /* not a inactivation candidate. */
+ spin_unlock(&ip->i_flags_lock);
+ return false;
+ }
+
+ ip->i_flags |= XFS_INACTIVATING;
+ spin_unlock(&ip->i_flags_lock);
+ return true;
+}
+
+struct xfs_inactive_ctx {
+ struct xfs_eofblocks *eofb;
+ bool kick_reclaim;
+};
+
+#define DEFINE_INACTIVE_CTX(name, e) \
+ struct xfs_inactive_ctx name = { .eofb = (e), .kick_reclaim = false }
+
+/* Inactivate this inode. */
+STATIC int
+xfs_inactive_inode(
+ struct xfs_inode *ip,
+ struct xfs_perag *pag,
+ void *args)
+{
+ struct xfs_inactive_ctx *inctx = args;
+ struct xfs_eofblocks *eofb = inctx->eofb;
+
+ ASSERT(ip->i_mount->m_super->s_writers.frozen < SB_FREEZE_FS);
+
+ /*
+ * Not a match for our passed in scan filter? Put it back on the shelf
+ * and move on.
+ */
+ spin_lock(&ip->i_flags_lock);
+ if (!xfs_inode_matches_eofb(ip, eofb)) {
+ ip->i_flags &= ~XFS_INACTIVATING;
+ spin_unlock(&ip->i_flags_lock);
+ return 0;
+ }
+ spin_unlock(&ip->i_flags_lock);
+
+ trace_xfs_inode_inactivating(ip);
+
+ /* Update metadata prior to freeing inode. */
+ xfs_inode_inactivation_cleanup(ip);
+ xfs_inactive(ip);
+ ASSERT(XFS_FORCED_SHUTDOWN(ip->i_mount) || ip->i_delayed_blks == 0);
+ spin_lock(&pag->pag_ici_lock);
+ xfs_perag_clear_inactive_tag(pag);
+ spin_unlock(&pag->pag_ici_lock);
+
+ /*
+ * Clear the inactive state flags and schedule a reclaim run once
+ * we're done with the inactivations.
+ */
+ spin_lock(&ip->i_flags_lock);
+ ip->i_flags &= ~(XFS_NEED_INACTIVE | XFS_INACTIVATING);
+ ASSERT(ip->i_flags & XFS_IRECLAIMABLE);
+ inctx->kick_reclaim = true;
+ spin_unlock(&ip->i_flags_lock);
+ return 0;
+}
+
+/*
+ * Walk the AGs and reclaim the inodes in them. Even if the filesystem is
+ * corrupted, we still need to clear the INACTIVE iflag so that we can move
+ * on to reclaiming the inode.
+ */
+int
+xfs_inactive_inodes(
+ struct xfs_mount *mp,
+ struct xfs_eofblocks *eofb)
+{
+ DEFINE_INACTIVE_CTX(inctx, eofb);
+ int error;
+
+ error = xfs_ici_walk_fns(mp, 0, xfs_inactive_grab, xfs_inactive_inode,
+ NULL, &inctx, XFS_ICI_RECLAIM_TAG);
+
+ /* If we inactivated any inodes at all, we need to kick reclaim. */
+ if (inctx.kick_reclaim)
+ xfs_reclaim_work_queue(mp);
+
+ return error;
+}
+
+/* Try to get inode inactivation moving. */
+void
+xfs_inactive_worker(
+ struct work_struct *work)
+{
+ struct xfs_mount *mp = container_of(to_delayed_work(work),
+ struct xfs_mount, m_inactive_work);
+ int error;
+
+ /*
+ * We want to skip inode inactivation while the filesystem is frozen
+ * because we don't want the inactivation thread to block while taking
+ * sb_intwrite. Therefore, we try to take sb_write for the duration
+ * of the inactive scan -- a freeze attempt will block until we're
+ * done here, and if the fs is past stage 1 freeze we'll bounce out
+ * until things unfreeze. If the fs goes down while frozen we'll
+ * still have log recovery to clean up after us.
+ */
+ if (!sb_start_write_trylock(mp->m_super))
+ return;
+
+ error = xfs_inactive_inodes(mp, NULL);
+ if (error && error != -EAGAIN)
+ xfs_err(mp, "inode inactivation failed, error %d", error);
+
+ sb_end_write(mp->m_super);
+ xfs_inactive_work_queue(mp);
+}
+
+/* Flush all inode inactivation work that might be queued. */
+void
+xfs_inactive_force(
+ struct xfs_mount *mp)
+{
+ queue_delayed_work(mp->m_inactive_workqueue, &mp->m_inactive_work, 0);
+ flush_delayed_work(&mp->m_inactive_work);
+}
+
+/*
+ * Flush all inode inactivation work that might be queued, make sure the
+ * delayed work item is not queued, and then make sure there aren't any more
+ * inodes waiting to be inactivated.
+ */
+void
+xfs_inactive_shutdown(
+ struct xfs_mount *mp)
+{
+ cancel_delayed_work_sync(&mp->m_inactive_work);
+ flush_workqueue(mp->m_inactive_workqueue);
+ xfs_inactive_inodes(mp, NULL);
+}
diff --git a/fs/xfs/xfs_icache.h b/fs/xfs/xfs_icache.h
index 603fd3391634..eaddfba46f3e 100644
--- a/fs/xfs/xfs_icache.h
+++ b/fs/xfs/xfs_icache.h
@@ -55,7 +55,7 @@ int xfs_reclaim_inodes(struct xfs_mount *mp, int mode);
int xfs_reclaim_inodes_count(struct xfs_mount *mp);
long xfs_reclaim_inodes_nr(struct xfs_mount *mp, int nr_to_scan);
-void xfs_inode_set_reclaim_tag(struct xfs_inode *ip);
+void xfs_inode_set_reclaim_tag(struct xfs_inode *ip, bool need_inactive);
bool xfs_inode_free_quota_blocks(struct xfs_inode *ip, bool sync);
void xfs_inode_free_blocks(struct xfs_mount *mp, bool sync);
@@ -82,4 +82,9 @@ int xfs_icache_inode_is_allocated(struct xfs_mount *mp, struct xfs_trans *tp,
void xfs_stop_block_reaping(struct xfs_mount *mp);
void xfs_start_block_reaping(struct xfs_mount *mp);
+void xfs_inactive_worker(struct work_struct *work);
+int xfs_inactive_inodes(struct xfs_mount *mp, struct xfs_eofblocks *eofb);
+void xfs_inactive_force(struct xfs_mount *mp);
+void xfs_inactive_shutdown(struct xfs_mount *mp);
+
#endif
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 8587bfb5a51b..1299a6efed23 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1855,6 +1855,68 @@ xfs_inode_iadjust(
xfs_qm_iadjust(ip, direction, inodes, dblocks, rblocks);
}
+/* Clean up inode inactivation. */
+void
+xfs_inode_inactivation_cleanup(
+ struct xfs_inode *ip)
+{
+ int ret;
+
+ if (XFS_FORCED_SHUTDOWN(ip->i_mount))
+ return;
+
+ /*
+ * Undo the pending-inactivation counter updates since we're bringing
+ * this inode back to life.
+ */
+ ret = xfs_qm_dqattach(ip);
+ if (ret)
+ xfs_err(ip->i_mount, "error %d reactivating inode quota", ret);
+
+ xfs_inode_iadjust(ip, -1);
+}
+
+/* Prepare inode for inactivation. */
+void
+xfs_inode_inactivation_prep(
+ struct xfs_inode *ip)
+{
+ int ret;
+
+ if (XFS_FORCED_SHUTDOWN(ip->i_mount))
+ return;
+
+ /*
+ * If this inode is unlinked (and now unreferenced) we need to dispose
+ * of it in the on disk metadata.
+ *
+ * Bump generation so that the inode can't be opened by handle now that
+ * the last external references has dropped. Bulkstat won't return
+ * inodes with zero nlink so nobody will ever find this inode again.
+ * Then add this inode & blocks to the counts of things that will be
+ * freed during the next inactivation run.
+ */
+ if (VFS_I(ip)->i_nlink == 0)
+ VFS_I(ip)->i_generation++;
+
+ /*
+ * Increase the pending-inactivation counters so that the fs looks like
+ * it's free.
+ */
+ ret = xfs_qm_dqattach(ip);
+ if (ret)
+ xfs_err(ip->i_mount, "error %d inactivating inode quota", ret);
+
+ xfs_inode_iadjust(ip, 1);
+
+ /*
+ * Detach dquots just in case someone tries a quotaoff while
+ * the inode is waiting on the inactive list. We'll reattach
+ * them (if needed) when inactivating the inode.
+ */
+ xfs_qm_dqdetach(ip);
+}
+
/*
* Returns true if we need to update the on-disk metadata before we can free
* the memory used by this inode. Updates include freeing post-eof
@@ -1946,6 +2008,16 @@ xfs_inactive(
if (mp->m_flags & XFS_MOUNT_RDONLY)
return;
+ /*
+ * Re-attach dquots prior to freeing EOF blocks or CoW staging extents.
+ * We dropped the dquot prior to inactivation (because quotaoff can't
+ * resurrect inactive inodes to force-drop the dquot) so we /must/
+ * do this before touching any block mappings.
+ */
+ error = xfs_qm_dqattach(ip);
+ if (error)
+ return;
+
/* Try to clean out the cow blocks if there are any. */
if (xfs_inode_has_cow_data(ip))
xfs_reflink_cancel_cow_range(ip, 0, NULLFILEOFF, true);
@@ -1971,10 +2043,6 @@ xfs_inactive(
ip->i_d.di_nextents > 0 || ip->i_delayed_blks > 0))
truncate = 1;
- error = xfs_qm_dqattach(ip);
- if (error)
- return;
-
if (S_ISLNK(VFS_I(ip)->i_mode))
error = xfs_inactive_symlink(ip);
else if (truncate)
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index fcfafb2ad101..e814985f6839 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -227,6 +227,7 @@ static inline bool xfs_inode_has_cow_data(struct xfs_inode *ip)
#define XFS_IRECLAIMABLE (1 << 2) /* inode can be reclaimed */
#define __XFS_INEW_BIT 3 /* inode has just been allocated */
#define XFS_INEW (1 << __XFS_INEW_BIT)
+#define XFS_NEED_INACTIVE (1 << 4) /* see XFS_INACTIVATING below */
#define XFS_ITRUNCATED (1 << 5) /* truncated down so flush-on-close */
#define XFS_IDIRTY_RELEASE (1 << 6) /* dirty release already seen */
#define __XFS_IFLOCK_BIT 7 /* inode is being flushed right now */
@@ -244,13 +245,23 @@ static inline bool xfs_inode_has_cow_data(struct xfs_inode *ip)
#define XFS_ICOWBLOCKS (1 << 12)/* has the cowblocks tag set */
/*
+ * If we need to update on-disk metadata before this IRECLAIMABLE inode can be
+ * freed, then NEED_INACTIVE will be set. Once we start the updates, the
+ * INACTIVATING bit will be set to keep iget away from this inode. After the
+ * inactivation completes, both flags will be cleared and the inode is a
+ * plain old IRECLAIMABLE inode.
+ */
+#define XFS_INACTIVATING (1 << 13)
+
+/*
* Per-lifetime flags need to be reset when re-using a reclaimable inode during
* inode lookup. This prevents unintended behaviour on the new inode from
* ocurring.
*/
#define XFS_IRECLAIM_RESET_FLAGS \
(XFS_IRECLAIMABLE | XFS_IRECLAIM | \
- XFS_IDIRTY_RELEASE | XFS_ITRUNCATED)
+ XFS_IDIRTY_RELEASE | XFS_ITRUNCATED | XFS_NEED_INACTIVE | \
+ XFS_INACTIVATING)
/*
* Synchronize processes attempting to flush the in-core inode back to disk.
@@ -514,6 +525,8 @@ extern struct kmem_zone *xfs_inode_zone;
bool xfs_inode_verify_forks(struct xfs_inode *ip);
int xfs_has_eofblocks(struct xfs_inode *ip, bool *has);
bool xfs_inode_needs_inactivation(struct xfs_inode *ip);
+void xfs_inode_inactivation_prep(struct xfs_inode *ip);
+void xfs_inode_inactivation_cleanup(struct xfs_inode *ip);
int xfs_iunlink_init(struct xfs_perag *pag);
void xfs_iunlink_destroy(struct xfs_perag *pag);
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 61048aa27728..7fd65b43b0b2 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -490,6 +490,7 @@ xfs_iomap_prealloc_size(
alloc_blocks);
freesp = percpu_counter_read_positive(&mp->m_fdblocks);
+ freesp += percpu_counter_read_positive(&mp->m_dinactive);
if (freesp < mp->m_low_space[XFS_LOWSP_5_PCNT]) {
shift = 2;
if (freesp < mp->m_low_space[XFS_LOWSP_4_PCNT])
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 508319039dce..4fbb9da60872 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -5096,6 +5096,13 @@ xlog_recover_process_iunlinks(
}
xfs_buf_rele(agibp);
}
+
+ /*
+ * Now that we've put all the iunlink inodes on the lru, let's make
+ * sure that we perform all the on-disk metadata updates to actually
+ * free those inodes.
+ */
+ xfs_inactive_force(mp);
}
STATIC void
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index ba5b6f3b2b88..3b8669f6bd24 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -988,6 +988,7 @@ xfs_mountfs(
* qm_unmount_quotas and therefore rely on qm_unmount to release the
* quota inodes.
*/
+ xfs_inactive_shutdown(mp);
cancel_delayed_work_sync(&mp->m_reclaim_work);
xfs_reclaim_inodes(mp, SYNC_WAIT);
xfs_health_unmount(mp);
@@ -1027,6 +1028,13 @@ xfs_unmountfs(
uint64_t resblks;
int error;
+ /*
+ * Perform all on-disk metadata updates required to inactivate inodes.
+ * Since this can involve finobt updates, do it now before we lose the
+ * per-AG space reservations.
+ */
+ xfs_inactive_force(mp);
+
xfs_stop_block_reaping(mp);
xfs_fs_unreserve_ag_blocks(mp);
xfs_qm_unmount_quotas(mp);
@@ -1078,6 +1086,13 @@ xfs_unmountfs(
xfs_qm_unmount(mp);
/*
+ * Kick off inode inactivation again to push the metadata inodes past
+ * INACTIVE into RECLAIM. We also have to deactivate the inactivation
+ * worker.
+ */
+ xfs_inactive_shutdown(mp);
+
+ /*
* Unreserve any blocks we have so that when we unmount we don't account
* the reserved free space as used. This is really only necessary for
* lazy superblock counting because it trusts the incore superblock
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index bdfc986a4af0..2d675dd37e5b 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -177,6 +177,7 @@ typedef struct xfs_mount {
trimming */
struct delayed_work m_cowblocks_work; /* background cow blocks
trimming */
+ struct delayed_work m_inactive_work; /* background inode inactive */
bool m_update_sb; /* sb needs update in mount */
int64_t m_low_space[XFS_LOWSP_MAX];
/* low free space thresholds */
@@ -190,6 +191,7 @@ typedef struct xfs_mount {
struct workqueue_struct *m_unwritten_workqueue;
struct workqueue_struct *m_cil_workqueue;
struct workqueue_struct *m_reclaim_workqueue;
+ struct workqueue_struct *m_inactive_workqueue;
struct workqueue_struct *m_eofblocks_workqueue;
struct workqueue_struct *m_sync_workqueue;
@@ -396,7 +398,8 @@ typedef struct xfs_perag {
spinlock_t pag_ici_lock; /* incore inode cache lock */
struct radix_tree_root pag_ici_root; /* incore inode cache root */
- int pag_ici_reclaimable; /* reclaimable inodes */
+ unsigned int pag_ici_reclaimable; /* reclaimable inodes */
+ unsigned int pag_ici_inactive; /* inactive inodes */
struct mutex pag_ici_reclaim_lock; /* serialisation point */
unsigned long pag_ici_reclaim_cursor; /* reclaim restart point */
diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c
index 7a6345be4ae9..48e44ff50d4f 100644
--- a/fs/xfs/xfs_qm_syscalls.c
+++ b/fs/xfs/xfs_qm_syscalls.c
@@ -43,6 +43,12 @@ xfs_qm_scall_quotaoff(
xfs_qoff_logitem_t *qoffstart;
/*
+ * Clean up the inactive list before we turn quota off, to reduce the
+ * amount of quotaoff work we have to do with the mutex held.
+ */
+ xfs_inactive_force(mp);
+
+ /*
* No file system can have quotas enabled on disk but not in core.
* Note that quota utilities (like quotaoff) _expect_
* errno == -EEXIST here.
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 06c12887d6b1..6ecf4686e8a8 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -838,8 +838,15 @@ xfs_init_mount_workqueues(
if (!mp->m_sync_workqueue)
goto out_destroy_eofb;
+ mp->m_inactive_workqueue = alloc_workqueue("xfs-inactive/%s",
+ WQ_MEM_RECLAIM|WQ_FREEZABLE, 0, mp->m_fsname);
+ if (!mp->m_inactive_workqueue)
+ goto out_destroy_sync;
+
return 0;
+out_destroy_sync:
+ destroy_workqueue(mp->m_inactive_workqueue);
out_destroy_eofb:
destroy_workqueue(mp->m_eofblocks_workqueue);
out_destroy_reclaim:
@@ -858,6 +865,7 @@ STATIC void
xfs_destroy_mount_workqueues(
struct xfs_mount *mp)
{
+ destroy_workqueue(mp->m_inactive_workqueue);
destroy_workqueue(mp->m_sync_workqueue);
destroy_workqueue(mp->m_eofblocks_workqueue);
destroy_workqueue(mp->m_reclaim_workqueue);
@@ -945,28 +953,34 @@ xfs_fs_destroy_inode(
struct inode *inode)
{
struct xfs_inode *ip = XFS_I(inode);
+ struct xfs_mount *mp = ip->i_mount;
+ bool need_inactive;
trace_xfs_destroy_inode(ip);
ASSERT(!rwsem_is_locked(&inode->i_rwsem));
- XFS_STATS_INC(ip->i_mount, vn_rele);
- XFS_STATS_INC(ip->i_mount, vn_remove);
-
- xfs_inactive(ip);
-
- if (!XFS_FORCED_SHUTDOWN(ip->i_mount) && ip->i_delayed_blks) {
+ XFS_STATS_INC(mp, vn_rele);
+ XFS_STATS_INC(mp, vn_remove);
+
+ need_inactive = xfs_inode_needs_inactivation(ip);
+ if (need_inactive) {
+ trace_xfs_inode_set_need_inactive(ip);
+ xfs_inode_inactivation_prep(ip);
+ } else if (!XFS_FORCED_SHUTDOWN(ip->i_mount) && ip->i_delayed_blks) {
xfs_check_delalloc(ip, XFS_DATA_FORK);
xfs_check_delalloc(ip, XFS_COW_FORK);
ASSERT(0);
}
-
- XFS_STATS_INC(ip->i_mount, vn_reclaim);
+ XFS_STATS_INC(mp, vn_reclaim);
+ trace_xfs_inode_set_reclaimable(ip);
/*
* We should never get here with one of the reclaim flags already set.
*/
ASSERT_ALWAYS(!xfs_iflags_test(ip, XFS_IRECLAIMABLE));
ASSERT_ALWAYS(!xfs_iflags_test(ip, XFS_IRECLAIM));
+ ASSERT_ALWAYS(!xfs_iflags_test(ip, XFS_NEED_INACTIVE));
+ ASSERT_ALWAYS(!xfs_iflags_test(ip, XFS_INACTIVATING));
/*
* We always use background reclaim here because even if the
@@ -975,7 +989,7 @@ xfs_fs_destroy_inode(
* this more efficiently than we can here, so simply let background
* reclaim tear down all inodes.
*/
- xfs_inode_set_reclaim_tag(ip);
+ xfs_inode_set_reclaim_tag(ip, need_inactive);
}
static void
@@ -1382,6 +1396,13 @@ xfs_fs_remount(
return error;
}
+ /*
+ * Perform all on-disk metadata updates required to inactivate
+ * inodes. Since this can involve finobt updates, do it now
+ * before we lose the per-AG space reservations.
+ */
+ xfs_inactive_force(mp);
+
/* Free the per-AG metadata reservation pool. */
error = xfs_fs_unreserve_ag_blocks(mp);
if (error) {
@@ -1435,6 +1456,18 @@ xfs_fs_unfreeze(
return 0;
}
+/*
+ * Before we get to stage 1 of a freeze, force all the inactivation work so
+ * that there's less work to do if we crash during the freeze.
+ */
+STATIC int
+xfs_fs_freeze_super(
+ struct super_block *sb)
+{
+ xfs_inactive_force(XFS_M(sb));
+ return freeze_super(sb);
+}
+
STATIC int
xfs_fs_show_options(
struct seq_file *m,
@@ -1608,6 +1641,7 @@ xfs_mount_alloc(
INIT_DELAYED_WORK(&mp->m_reclaim_work, xfs_reclaim_worker);
INIT_DELAYED_WORK(&mp->m_eofblocks_work, xfs_eofblocks_worker);
INIT_DELAYED_WORK(&mp->m_cowblocks_work, xfs_cowblocks_worker);
+ INIT_DELAYED_WORK(&mp->m_inactive_work, xfs_inactive_worker);
mp->m_kobj.kobject.kset = xfs_kset;
/*
* We don't create the finobt per-ag space reservation until after log
@@ -1886,6 +1920,7 @@ static const struct super_operations xfs_super_operations = {
.show_options = xfs_fs_show_options,
.nr_cached_objects = xfs_fs_nr_cached_objects,
.free_cached_objects = xfs_fs_free_cached_objects,
+ .freeze_super = xfs_fs_freeze_super,
};
static struct file_system_type xfs_fs_type = {
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 45ca7cbfe0c1..56ff3fcc0b09 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -595,14 +595,17 @@ DECLARE_EVENT_CLASS(xfs_inode_class,
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
+ __field(unsigned long, iflags)
),
TP_fast_assign(
__entry->dev = VFS_I(ip)->i_sb->s_dev;
__entry->ino = ip->i_ino;
+ __entry->iflags = ip->i_flags;
),
- TP_printk("dev %d:%d ino 0x%llx",
+ TP_printk("dev %d:%d ino 0x%llx iflags 0x%lx",
MAJOR(__entry->dev), MINOR(__entry->dev),
- __entry->ino)
+ __entry->ino,
+ __entry->iflags)
)
#define DEFINE_INODE_EVENT(name) \
@@ -646,6 +649,10 @@ DEFINE_INODE_EVENT(xfs_inode_free_eofblocks_invalid);
DEFINE_INODE_EVENT(xfs_inode_set_cowblocks_tag);
DEFINE_INODE_EVENT(xfs_inode_clear_cowblocks_tag);
DEFINE_INODE_EVENT(xfs_inode_free_cowblocks_invalid);
+DEFINE_INODE_EVENT(xfs_inode_set_reclaimable);
+DEFINE_INODE_EVENT(xfs_inode_reclaiming);
+DEFINE_INODE_EVENT(xfs_inode_set_need_inactive);
+DEFINE_INODE_EVENT(xfs_inode_inactivating);
/*
* ftrace's __print_symbolic requires that all enum values be wrapped in the