diff options
-rw-r--r-- | fs/xfs/xfs_bmap_util.c | 13 | ||||
-rw-r--r-- | fs/xfs/xfs_file.c | 11 | ||||
-rw-r--r-- | fs/xfs/xfs_icache.c | 40 | ||||
-rw-r--r-- | fs/xfs/xfs_icache.h | 1 | ||||
-rw-r--r-- | fs/xfs/xfs_inode.c | 12 | ||||
-rw-r--r-- | fs/xfs/xfs_iomap.c | 13 | ||||
-rw-r--r-- | fs/xfs/xfs_reflink.c | 23 | ||||
-rw-r--r-- | fs/xfs/xfs_trace.h | 1 |
8 files changed, 101 insertions, 13 deletions
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index ce463e3b61af..251f9819d84c 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 5b04d3dc200a..7b9117634a10 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -665,15 +665,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 5dffa87973b4..83f2db32bc04 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); + /* * Allocate and initialise an xfs_inode. */ @@ -970,6 +973,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) @@ -1583,9 +1601,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 835c735301b8..979e3e669be3 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); +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 7269243edfb3..6d865156f86d 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1179,10 +1179,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 20ec4622e43e..285e92dee2a9 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 85b5f8671de7..f0a418d559bd 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); @@ -1041,6 +1053,17 @@ 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; + 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 8fb42a155f54..9d8431dcd5c9 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -3779,6 +3779,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 */ |