summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/xfs/xfs_bmap_util.c12
-rw-r--r--fs/xfs/xfs_file.c9
-rw-r--r--fs/xfs/xfs_icache.c30
-rw-r--r--fs/xfs/xfs_icache.h1
-rw-r--r--fs/xfs/xfs_inode.c11
-rw-r--r--fs/xfs/xfs_iomap.c12
-rw-r--r--fs/xfs/xfs_reflink.c21
-rw-r--r--fs/xfs/xfs_trace.h1
8 files changed, 85 insertions, 12 deletions
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index f1f0b55fcd89..220c64a82303 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -944,7 +944,17 @@ 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;
+ xfs_inode_free_blocks(ip->i_mount, false);
+ goto retry;
+ }
/*
* Check for running out of space
*/
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index fd9dee4b58a3..fb42146ee8cf 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -654,15 +654,10 @@ 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;
+ xfs_inode_free_blocks(ip->i_mount, true);
goto write_retry;
}
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index 6e4c3763d008..ed009d9413d9 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -1524,6 +1524,15 @@ xfs_icache_free_eofblocks(
XFS_ICI_EOFBLOCKS_TAG);
}
+static inline void
+xfs_inode_free_scan(
+ struct xfs_mount *mp,
+ struct xfs_eofblocks *eofb)
+{
+ xfs_icache_free_eofblocks(mp, eofb);
+ xfs_icache_free_cowblocks(mp, eofb);
+}
+
/*
* Run cow/eofblocks scans on the quotas applicable to the inode. For inodes
* with multiple quotas, we don't know exactly which quota caused an allocation
@@ -1579,11 +1588,28 @@ 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);
+ xfs_inode_free_scan(ip->i_mount, &eofb);
return true;
}
+/*
+ * Try to free space in the filesystem by purging eofblocks and cowblocks.
+ */
+void
+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_);
+
+ xfs_inode_free_scan(mp, &eofb);
+}
+
static inline unsigned long
xfs_iflag_for_tag(
int tag)
diff --git a/fs/xfs/xfs_icache.h b/fs/xfs/xfs_icache.h
index a42a682cec0b..738417c3568b 100644
--- a/fs/xfs/xfs_icache.h
+++ b/fs/xfs/xfs_icache.h
@@ -58,6 +58,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);
+void 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 dff7b1cd8424..ac4d7143abc2 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1171,10 +1171,17 @@ 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);
+ xfs_inode_free_blocks(mp, true);
+ goto retry;
}
if (error)
goto out_release_inode;
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 58b3ec6b00e3..2a6a2bbb4c63 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -262,6 +262,18 @@ 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;
+ xfs_inode_free_blocks(ip->i_mount, false);
+ goto retry;
+ }
if (error)
return error;
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index 17b6a67240a4..4088b6de5e9b 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -381,6 +381,17 @@ 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;
+ xfs_inode_free_blocks(ip->i_mount, false);
+ goto retry;
+ }
*lockmode = XFS_ILOCK_EXCL;
xfs_ilock(ip, *lockmode);
@@ -1050,6 +1061,16 @@ xfs_reflink_remap_extent(
resblks = XFS_EXTENTADD_SPACE_RES(ip->i_mount, XFS_DATA_FORK);
retry:
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;
+ xfs_inode_free_blocks(mp, false);
+ goto retry;
+ }
if (error)
goto out;
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 832c21a68418..f3392c1199d1 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -3642,6 +3642,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 */