summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 5d4ad197d756..8d85dcba5d3a 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 766b9c9acc63..994fd3d59872 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -655,15 +655,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 b1e598543b6e..cc1fb0f49854 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -26,6 +26,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
*/
@@ -978,6 +981,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)
@@ -1605,9 +1623,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 ea77c67adacd..8084fb8965f2 100644
--- a/fs/xfs/xfs_icache.h
+++ b/fs/xfs/xfs_icache.h
@@ -53,6 +53,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 a22cf9f6fe01..7de6443f0901 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1160,10 +1160,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 39b0fb7f7d6a..f22f0eef907b 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 33fdd6c67c32..9a42041c159a 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);
@@ -1027,6 +1039,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 c9962df1bf55..751bd3620a6e 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -3743,6 +3743,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 */