summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2020-10-25 17:14:46 -0700
committerDarrick J. Wong <darrick.wong@oracle.com>2020-10-26 18:32:18 -0700
commit6112fe66baa93f9c91726ba3d3ca1629326c8b92 (patch)
tree728b113b6b2b7e40360e2d6648a5d9472944b756
parent4ba7d46e74471b395232318be219415b428aeeba (diff)
xfs: flush speculative space allocations when we run out of spacereclaim-space-harder_2020-10-26
If a fs modification (creation, file write, reflink, etc.) is unable to reserve enough space to handle the modification, try clearing whatever space the filesystem might have been hanging onto in the hopes of speeding up the filesystem. The flushing behavior will become particularly important when we add deferred inode inactivation because that will increase the amount of space that isn't actively tied to user data. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-rw-r--r--fs/xfs/xfs_bmap_util.c13
-rw-r--r--fs/xfs/xfs_file.c11
-rw-r--r--fs/xfs/xfs_icache.c40
-rw-r--r--fs/xfs/xfs_icache.h1
-rw-r--r--fs/xfs/xfs_inode.c12
-rw-r--r--fs/xfs/xfs_iomap.c13
-rw-r--r--fs/xfs/xfs_reflink.c23
-rw-r--r--fs/xfs/xfs_trace.h1
8 files changed, 101 insertions, 13 deletions
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index 437fdc8a8fbd..602011f06fb6 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -807,7 +807,18 @@ xfs_alloc_file_space(
retry:
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks,
resrtextents, 0, &tp);
-
+ /*
+ * We weren't able to reserve enough space to handle fallocate.
+ * Flush any disk space that was being held in the hopes of
+ * speeding up the filesystem. We hold the IOLOCK so we cannot
+ * do a synchronous scan.
+ */
+ if (error == -ENOSPC && !cleared_space) {
+ cleared_space = true;
+ error = xfs_inode_free_blocks(ip->i_mount, false);
+ if (!error)
+ goto retry;
+ }
/*
* Check for running out of space
*/
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 136fb5972acd..1333c65dd2b5 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -691,15 +691,12 @@ write_retry:
goto write_retry;
iolock = 0;
} else if (ret == -ENOSPC && !cleared_space) {
- struct xfs_eofblocks eofb = {0};
-
- cleared_space = true;
xfs_flush_inodes(ip->i_mount);
-
xfs_iunlock(ip, iolock);
- eofb.eof_flags = XFS_EOF_FLAGS_SYNC;
- xfs_icache_free_eofblocks(ip->i_mount, &eofb);
- xfs_icache_free_cowblocks(ip->i_mount, &eofb);
+ cleared_space = true;
+ ret = xfs_inode_free_blocks(ip->i_mount, true);
+ if (ret)
+ return ret;
goto write_retry;
}
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index 98c09c637410..ef0b4626308a 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -27,6 +27,9 @@
#include <linux/iversion.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);
+
/*
* flags for incore inode iterator
*/
@@ -955,6 +958,21 @@ xfs_queue_eofblocks(
rcu_read_unlock();
}
+/* Scan all incore inodes for block preallocations that we can remove. */
+static inline int
+xfs_blockgc_scan(
+ struct xfs_mount *mp,
+ struct xfs_eofblocks *eofb)
+{
+ int error;
+
+ error = xfs_icache_free_eofblocks(mp, eofb);
+ if (error && error != -EAGAIN)
+ return error;
+
+ return xfs_icache_free_cowblocks(mp, eofb);
+}
+
void
xfs_eofblocks_worker(
struct work_struct *work)
@@ -1475,9 +1493,25 @@ xfs_inode_free_quota_blocks(
trace_xfs_inode_free_quota_blocks(ip->i_mount, &eofb, _RET_IP_);
- xfs_icache_free_eofblocks(ip->i_mount, &eofb);
- xfs_icache_free_cowblocks(ip->i_mount, &eofb);
- return true;
+ return xfs_blockgc_scan(ip->i_mount, &eofb) == 0;
+}
+
+/*
+ * Try to free space in the filesystem by purging eofblocks and cowblocks.
+ */
+int
+xfs_inode_free_blocks(
+ struct xfs_mount *mp,
+ bool sync)
+{
+ struct xfs_eofblocks eofb = {0};
+
+ if (sync)
+ eofb.eof_flags |= XFS_EOF_FLAGS_SYNC;
+
+ trace_xfs_inode_free_blocks(mp, &eofb, _RET_IP_);
+
+ return xfs_blockgc_scan(mp, &eofb);
}
static inline unsigned long
diff --git a/fs/xfs/xfs_icache.h b/fs/xfs/xfs_icache.h
index 88bbbc9f00f8..2f230d273a54 100644
--- a/fs/xfs/xfs_icache.h
+++ b/fs/xfs/xfs_icache.h
@@ -50,6 +50,7 @@ long xfs_reclaim_inodes_nr(struct xfs_mount *mp, int nr_to_scan);
void xfs_inode_set_reclaim_tag(struct xfs_inode *ip);
bool xfs_inode_free_quota_blocks(struct xfs_inode *ip, bool sync);
+int xfs_inode_free_blocks(struct xfs_mount *mp, bool sync);
void xfs_inode_set_eofblocks_tag(struct xfs_inode *ip);
void xfs_inode_clear_eofblocks_tag(struct xfs_inode *ip);
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index d053a5942d64..2e2f8757d031 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1163,10 +1163,18 @@ xfs_create(
*/
retry:
error = xfs_trans_alloc(mp, tres, resblks, 0, 0, &tp);
- if (error == -ENOSPC) {
+ /*
+ * We weren't able to reserve enough space to add the inode. Flush
+ * any disk space that was being held in the hopes of speeding up the
+ * filesystem.
+ */
+ if (error == -ENOSPC && !cleared_space) {
+ cleared_space = true;
/* flush outstanding delalloc blocks and retry */
xfs_flush_inodes(mp);
- error = xfs_trans_alloc(mp, tres, resblks, 0, 0, &tp);
+ error = xfs_inode_free_blocks(mp, true);
+ if (!error)
+ goto retry;
}
if (error)
goto out_release_inode;
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 497723948072..f6302d0b9071 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -246,6 +246,19 @@ xfs_iomap_write_direct(
retry:
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, resrtextents,
tflags, &tp);
+ /*
+ * We weren't able to reserve enough space for the direct write. Flush
+ * any disk space that was being held in the hopes of speeding up the
+ * filesystem. Historically, we expected callers to have preallocated
+ * all the space before a direct write, but this is not an absolute
+ * requirement. We still hold the IOLOCK so we cannot do a sync scan.
+ */
+ if (error == -ENOSPC && !cleared_space) {
+ cleared_space = true;
+ error = xfs_inode_free_blocks(ip->i_mount, false);
+ if (!error)
+ goto retry;
+ }
if (error)
return error;
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index 25b60c494b11..d47acb4bf851 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -379,6 +379,18 @@ xfs_reflink_allocate_cow(
xfs_iunlock(ip, *lockmode);
retry:
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);
+ /*
+ * We weren't able to reserve enough space to handle copy on write.
+ * Flush any disk space that was being held in the hopes of speeding up
+ * the filesystem. We potentially hold the IOLOCK so we cannot do a
+ * synchronous scan.
+ */
+ if (error == -ENOSPC && !cleared_space) {
+ cleared_space = true;
+ error = xfs_inode_free_blocks(ip->i_mount, false);
+ if (!error)
+ goto retry;
+ }
*lockmode = XFS_ILOCK_EXCL;
xfs_ilock(ip, *lockmode);
@@ -1028,6 +1040,17 @@ retry:
/* Start a rolling transaction to switch the mappings */
resblks = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK);
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);
+ /*
+ * We weren't able to reserve enough space for the remapping. Flush
+ * any disk space that was being held in the hopes of speeding up the
+ * filesystem. We still hold the IOLOCK so we cannot do a sync scan.
+ */
+ if (error == -ENOSPC && !cleared_space) {
+ cleared_space = true;
+ error = xfs_inode_free_blocks(mp, false);
+ if (!error)
+ goto retry;
+ }
if (error)
goto out;
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index e5de07eee467..bd68e25be893 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -3909,6 +3909,7 @@ DEFINE_EVENT(xfs_eofblocks_class, name, \
TP_ARGS(mp, eofb, caller_ip))
DEFINE_EOFBLOCKS_EVENT(xfs_ioc_free_eofblocks);
DEFINE_EOFBLOCKS_EVENT(xfs_inode_free_quota_blocks);
+DEFINE_EOFBLOCKS_EVENT(xfs_inode_free_blocks);
#endif /* _TRACE_XFS_H */