summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/xfs/xfs_inode.c54
-rw-r--r--fs/xfs/xfs_mount.h7
-rw-r--r--fs/xfs/xfs_super.c31
3 files changed, 91 insertions, 1 deletions
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 19a868a1ffd0..45cfde935f73 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1786,6 +1786,60 @@ xfs_inactive_ifree(
}
/*
+ * Play some accounting tricks with deferred inactivation of unlinked inodes so
+ * that it looks like the inode got freed immediately. The superblock
+ * maintains counts of the number of inodes, data blocks, and rt blocks that
+ * would be freed if we were to force inode inactivation. These counts are
+ * added to the statfs free counters outside of the regular fdblocks/ifree
+ * counters. If userspace actually demands those "free" resources we'll force
+ * an inactivation scan to free things for real.
+ *
+ * Note that we can safely skip the block accounting trickery for complicated
+ * situations (inode with blocks on both devices, inode block counts that seem
+ * wrong) since the worst that happens is that statfs resource usage decreases
+ * more slowly.
+ *
+ * Positive @direction means we're setting up the accounting trick and
+ * negative undoes it.
+ */
+static inline void
+xfs_inode_iadjust(
+ struct xfs_inode *ip,
+ int direction)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ xfs_filblks_t iblocks;
+ int64_t inodes = 0;
+ int64_t dblocks = 0;
+ int64_t rblocks = 0;
+
+ ASSERT(direction != 0);
+
+ if (VFS_I(ip)->i_nlink == 0) {
+ inodes = 1;
+
+ iblocks = max_t(int64_t, 0, ip->i_d.di_nblocks +
+ ip->i_delayed_blks);
+ if (!XFS_IS_REALTIME_INODE(ip))
+ dblocks = iblocks;
+ else if (!XFS_IFORK_Q(ip) ||
+ XFS_IFORK_FORMAT(ip, XFS_ATTR_FORK) ==
+ XFS_DINODE_FMT_LOCAL)
+ rblocks = iblocks;
+ }
+
+ if (direction < 0) {
+ inodes = -inodes;
+ dblocks = -dblocks;
+ rblocks = -rblocks;
+ }
+
+ percpu_counter_add(&mp->m_iinactive, inodes);
+ percpu_counter_add(&mp->m_dinactive, dblocks);
+ percpu_counter_add(&mp->m_rinactive, rblocks);
+}
+
+/*
* 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
* preallocations; freeing COW staging extents; and marking the inode free in
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index c99285021f29..5707f988d262 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -85,6 +85,13 @@ typedef struct xfs_mount {
*/
struct percpu_counter m_delalloc_blks;
+ /* Count of inodes waiting for inactivation. */
+ struct percpu_counter m_iinactive;
+ /* Count of data device blocks waiting for inactivation. */
+ struct percpu_counter m_dinactive;
+ /* Coult of realtime device blocks waiting for inactivation. */
+ struct percpu_counter m_rinactive;
+
struct xfs_buf *m_sb_bp; /* buffer for superblock */
char *m_rtname; /* realtime device name */
char *m_logname; /* external log device name */
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index bd634c94b5bb..a82aea579521 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -753,6 +753,8 @@ xfs_fs_statfs(
uint64_t icount;
uint64_t ifree;
uint64_t fdblocks;
+ uint64_t iinactive;
+ uint64_t binactive;
xfs_extlen_t lsize;
int64_t ffree;
@@ -766,6 +768,7 @@ xfs_fs_statfs(
icount = percpu_counter_sum(&mp->m_icount);
ifree = percpu_counter_sum(&mp->m_ifree);
fdblocks = percpu_counter_sum(&mp->m_fdblocks);
+ iinactive = percpu_counter_sum(&mp->m_iinactive);
spin_lock(&mp->m_sb_lock);
statp->f_bsize = sbp->sb_blocksize;
@@ -789,7 +792,7 @@ xfs_fs_statfs(
sbp->sb_icount);
/* make sure statp->f_ffree does not underflow */
- ffree = statp->f_files - (icount - ifree);
+ ffree = statp->f_files - (icount - ifree) + iinactive;
statp->f_ffree = max_t(int64_t, ffree, 0);
@@ -803,7 +806,12 @@ xfs_fs_statfs(
statp->f_blocks = sbp->sb_rblocks;
statp->f_bavail = statp->f_bfree =
sbp->sb_frextents * sbp->sb_rextsize;
+ binactive = percpu_counter_sum(&mp->m_rinactive);
+ } else {
+ binactive = percpu_counter_sum(&mp->m_dinactive);
}
+ statp->f_bavail += binactive;
+ statp->f_bfree += binactive;
return 0;
}
@@ -993,8 +1001,26 @@ xfs_init_percpu_counters(
if (error)
goto free_fdblocks;
+ error = percpu_counter_init(&mp->m_iinactive, 0, GFP_KERNEL);
+ if (error)
+ goto free_delalloc;
+
+ error = percpu_counter_init(&mp->m_dinactive, 0, GFP_KERNEL);
+ if (error)
+ goto free_iinactive;
+
+ error = percpu_counter_init(&mp->m_rinactive, 0, GFP_KERNEL);
+ if (error)
+ goto free_dinactive;
+
return 0;
+free_dinactive:
+ percpu_counter_destroy(&mp->m_dinactive);
+free_iinactive:
+ percpu_counter_destroy(&mp->m_iinactive);
+free_delalloc:
+ percpu_counter_destroy(&mp->m_delalloc_blks);
free_fdblocks:
percpu_counter_destroy(&mp->m_fdblocks);
free_ifree:
@@ -1023,6 +1049,9 @@ xfs_destroy_percpu_counters(
ASSERT(XFS_FORCED_SHUTDOWN(mp) ||
percpu_counter_sum(&mp->m_delalloc_blks) == 0);
percpu_counter_destroy(&mp->m_delalloc_blks);
+ percpu_counter_destroy(&mp->m_iinactive);
+ percpu_counter_destroy(&mp->m_dinactive);
+ percpu_counter_destroy(&mp->m_rinactive);
}
static void