mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-28 22:54:05 +08:00
xfs: pull up iolock from xfs_free_eofblocks()
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: Brian Foster <bfoster@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
This commit is contained in:
parent
64f61ab604
commit
a36b926180
@ -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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
}
|
}
|
||||||
|
|
||||||
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(
|
|||||||
* 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,
|
|||||||
|
|
||||||
/* 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);
|
||||||
|
@ -1322,7 +1322,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;
|
||||||
@ -1358,19 +1358,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;
|
||||||
}
|
}
|
||||||
|
@ -1692,32 +1692,34 @@ xfs_release(
|
|||||||
if (xfs_can_free_eofblocks(ip, false)) {
|
if (xfs_can_free_eofblocks(ip, false)) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we can't get the iolock just skip truncating the blocks
|
* Check if the inode is being opened, written and closed
|
||||||
* past EOF because we could deadlock with the mmap_sem
|
* frequently and we have delayed allocation blocks outstanding
|
||||||
* otherwise. We'll get another chance to drop them once the
|
* (e.g. streaming writes from the NFS server), truncating the
|
||||||
* last reference to the inode is dropped, so we'll never leak
|
* blocks past EOF will cause fragmentation to occur.
|
||||||
* blocks permanently.
|
|
||||||
*
|
*
|
||||||
* Further, check if the inode is being opened, written and
|
* In this case don't do the truncation, but we have to be
|
||||||
* closed frequently and we have delayed allocation blocks
|
* careful how we detect this case. Blocks beyond EOF show up as
|
||||||
* outstanding (e.g. streaming writes from the NFS server),
|
* i_delayed_blks even when the inode is clean, so we need to
|
||||||
* truncating the blocks past EOF will cause fragmentation to
|
* truncate them away first before checking for a dirty release.
|
||||||
* occur.
|
* Hence on the first dirty close we will still remove the
|
||||||
*
|
* speculative allocation, but after that we will leave it in
|
||||||
* In this case don't do the truncation, either, but we have to
|
* place.
|
||||||
* 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_iflags_test(ip, XFS_IDIRTY_RELEASE))
|
||||||
return 0;
|
return 0;
|
||||||
|
/*
|
||||||
error = xfs_free_eofblocks(mp, ip, true);
|
* If we can't get the iolock just skip truncating the blocks
|
||||||
if (error && error != -EAGAIN)
|
* past EOF because we could deadlock with the mmap_sem
|
||||||
return error;
|
* otherwise. We'll get another chance to drop them once the
|
||||||
|
* last reference to the inode is dropped, so we'll never leak
|
||||||
|
* blocks permanently.
|
||||||
|
*/
|
||||||
|
if (xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL)) {
|
||||||
|
error = xfs_free_eofblocks(ip);
|
||||||
|
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
|
||||||
|
if (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)
|
||||||
@ -1904,8 +1906,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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user