Commit 798b1dc5 authored by Brian Foster's avatar Brian Foster Committed by Greg Kroah-Hartman

xfs: pull up iolock from xfs_free_eofblocks()

commit a36b9261 upstream.

xfs_free_eofblocks() requires the IOLOCK_EXCL lock, but is called from
different contexts where the lock may or may not be held. The
need_iolock parameter exists for this reason, to indicate whether
xfs_free_eofblocks() must acquire the iolock itself before it can
proceed.

This is ugly and confusing. Simplify the semantics of
xfs_free_eofblocks() to require the caller to acquire the iolock
appropriately and kill the need_iolock parameter. While here, the mp
param can be removed as well as the xfs_mount is accessible from the
xfs_inode structure. This patch does not change behavior.
Signed-off-by: default avatarBrian Foster <bfoster@redhat.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 08a2a268
...@@ -917,17 +917,18 @@ xfs_can_free_eofblocks(struct xfs_inode *ip, bool force) ...@@ -917,17 +917,18 @@ xfs_can_free_eofblocks(struct xfs_inode *ip, bool force)
*/ */
int int
xfs_free_eofblocks( xfs_free_eofblocks(
xfs_mount_t *mp, struct xfs_inode *ip)
xfs_inode_t *ip,
bool need_iolock)
{ {
xfs_trans_t *tp; struct xfs_trans *tp;
int error; int error;
xfs_fileoff_t end_fsb; xfs_fileoff_t end_fsb;
xfs_fileoff_t last_fsb; xfs_fileoff_t last_fsb;
xfs_filblks_t map_len; xfs_filblks_t map_len;
int nimaps; int nimaps;
xfs_bmbt_irec_t imap; struct xfs_bmbt_irec imap;
struct xfs_mount *mp = ip->i_mount;
ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
/* /*
* Figure out if there are any blocks beyond the end * Figure out if there are any blocks beyond the end
...@@ -944,6 +945,10 @@ xfs_free_eofblocks( ...@@ -944,6 +945,10 @@ xfs_free_eofblocks(
error = xfs_bmapi_read(ip, end_fsb, map_len, &imap, &nimaps, 0); error = xfs_bmapi_read(ip, end_fsb, map_len, &imap, &nimaps, 0);
xfs_iunlock(ip, XFS_ILOCK_SHARED); xfs_iunlock(ip, XFS_ILOCK_SHARED);
/*
* If there are blocks after the end of file, truncate the file to its
* current size to free them up.
*/
if (!error && (nimaps != 0) && if (!error && (nimaps != 0) &&
(imap.br_startblock != HOLESTARTBLOCK || (imap.br_startblock != HOLESTARTBLOCK ||
ip->i_delayed_blks)) { ip->i_delayed_blks)) {
...@@ -954,22 +959,10 @@ xfs_free_eofblocks( ...@@ -954,22 +959,10 @@ xfs_free_eofblocks(
if (error) if (error)
return error; return error;
/*
* There are blocks after the end of file.
* Free them up now by truncating the file to
* its current size.
*/
if (need_iolock) {
if (!xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL))
return -EAGAIN;
}
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0,
&tp); &tp);
if (error) { if (error) {
ASSERT(XFS_FORCED_SHUTDOWN(mp)); ASSERT(XFS_FORCED_SHUTDOWN(mp));
if (need_iolock)
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
return error; return error;
} }
...@@ -997,8 +990,6 @@ xfs_free_eofblocks( ...@@ -997,8 +990,6 @@ xfs_free_eofblocks(
} }
xfs_iunlock(ip, XFS_ILOCK_EXCL); xfs_iunlock(ip, XFS_ILOCK_EXCL);
if (need_iolock)
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
} }
return error; return error;
} }
...@@ -1415,7 +1406,7 @@ xfs_shift_file_space( ...@@ -1415,7 +1406,7 @@ xfs_shift_file_space(
* into the accessible region of the file. * into the accessible region of the file.
*/ */
if (xfs_can_free_eofblocks(ip, true)) { if (xfs_can_free_eofblocks(ip, true)) {
error = xfs_free_eofblocks(mp, ip, false); error = xfs_free_eofblocks(ip);
if (error) if (error)
return error; return error;
} }
......
...@@ -63,8 +63,7 @@ int xfs_insert_file_space(struct xfs_inode *, xfs_off_t offset, ...@@ -63,8 +63,7 @@ int xfs_insert_file_space(struct xfs_inode *, xfs_off_t offset,
/* EOF block manipulation functions */ /* EOF block manipulation functions */
bool xfs_can_free_eofblocks(struct xfs_inode *ip, bool force); bool xfs_can_free_eofblocks(struct xfs_inode *ip, bool force);
int xfs_free_eofblocks(struct xfs_mount *mp, struct xfs_inode *ip, int xfs_free_eofblocks(struct xfs_inode *ip);
bool need_iolock);
int xfs_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip, int xfs_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip,
struct xfs_swapext *sx); struct xfs_swapext *sx);
......
...@@ -1324,7 +1324,7 @@ xfs_inode_free_eofblocks( ...@@ -1324,7 +1324,7 @@ xfs_inode_free_eofblocks(
int flags, int flags,
void *args) void *args)
{ {
int ret; int ret = 0;
struct xfs_eofblocks *eofb = args; struct xfs_eofblocks *eofb = args;
bool need_iolock = true; bool need_iolock = true;
int match; int match;
...@@ -1360,19 +1360,25 @@ xfs_inode_free_eofblocks( ...@@ -1360,19 +1360,25 @@ xfs_inode_free_eofblocks(
return 0; return 0;
/* /*
* A scan owner implies we already hold the iolock. Skip it in * A scan owner implies we already hold the iolock. Skip it here
* xfs_free_eofblocks() to avoid deadlock. This also eliminates * to avoid deadlock.
* the possibility of EAGAIN being returned.
*/ */
if (eofb->eof_scan_owner == ip->i_ino) if (eofb->eof_scan_owner == ip->i_ino)
need_iolock = false; need_iolock = false;
} }
ret = xfs_free_eofblocks(ip->i_mount, ip, need_iolock); /*
* If the caller is waiting, return -EAGAIN to keep the background
/* don't revisit the inode if we're not waiting */ * scanner moving and revisit the inode in a subsequent pass.
if (ret == -EAGAIN && !(flags & SYNC_WAIT)) */
ret = 0; if (need_iolock && !xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL)) {
if (flags & SYNC_WAIT)
ret = -EAGAIN;
return ret;
}
ret = xfs_free_eofblocks(ip);
if (need_iolock)
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
return ret; return ret;
} }
......
...@@ -1700,33 +1700,35 @@ xfs_release( ...@@ -1700,33 +1700,35 @@ xfs_release(
if (xfs_can_free_eofblocks(ip, false)) { if (xfs_can_free_eofblocks(ip, false)) {
/*
* Check if the inode is being opened, written and closed
* frequently and we have delayed allocation blocks outstanding
* (e.g. streaming writes from the NFS server), truncating the
* blocks past EOF will cause fragmentation to occur.
*
* In this case don't do the truncation, but we have to be
* careful how we detect this case. Blocks beyond EOF show up as
* i_delayed_blks even when the inode is clean, so we need to
* truncate them away first before checking for a dirty release.
* Hence on the first dirty close we will still remove the
* speculative allocation, but after that we will leave it in
* place.
*/
if (xfs_iflags_test(ip, XFS_IDIRTY_RELEASE))
return 0;
/* /*
* If we can't get the iolock just skip truncating the blocks * If we can't get the iolock just skip truncating the blocks
* past EOF because we could deadlock with the mmap_sem * past EOF because we could deadlock with the mmap_sem
* otherwise. We'll get another chance to drop them once the * otherwise. We'll get another chance to drop them once the
* last reference to the inode is dropped, so we'll never leak * last reference to the inode is dropped, so we'll never leak
* blocks permanently. * blocks permanently.
*
* Further, check if the inode is being opened, written and
* closed frequently and we have delayed allocation blocks
* outstanding (e.g. streaming writes from the NFS server),
* truncating the blocks past EOF will cause fragmentation to
* occur.
*
* In this case don't do the truncation, either, but we have to
* be careful how we detect this case. Blocks beyond EOF show
* up as i_delayed_blks even when the inode is clean, so we
* need to truncate them away first before checking for a dirty
* release. Hence on the first dirty close we will still remove
* the speculative allocation, but after that we will leave it
* in place.
*/ */
if (xfs_iflags_test(ip, XFS_IDIRTY_RELEASE)) if (xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL)) {
return 0; error = xfs_free_eofblocks(ip);
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
error = xfs_free_eofblocks(mp, ip, true); if (error)
if (error && error != -EAGAIN)
return error; return error;
}
/* delalloc blocks after truncation means it really is dirty */ /* delalloc blocks after truncation means it really is dirty */
if (ip->i_delayed_blks) if (ip->i_delayed_blks)
...@@ -1913,8 +1915,11 @@ xfs_inactive( ...@@ -1913,8 +1915,11 @@ xfs_inactive(
* cache. Post-eof blocks must be freed, lest we end up with * cache. Post-eof blocks must be freed, lest we end up with
* broken free space accounting. * broken free space accounting.
*/ */
if (xfs_can_free_eofblocks(ip, true)) if (xfs_can_free_eofblocks(ip, true)) {
xfs_free_eofblocks(mp, ip, false); xfs_ilock(ip, XFS_IOLOCK_EXCL);
xfs_free_eofblocks(ip);
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
}
return; return;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment