Commit bdb0d04f authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Dave Chinner

xfs: split xfs_free_file_space in manageable pieces

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
Signed-off-by: default avatarDave Chinner <david@fromorbit.com>
parent 570b6211
...@@ -1184,69 +1184,72 @@ xfs_zero_remaining_bytes( ...@@ -1184,69 +1184,72 @@ xfs_zero_remaining_bytes(
return error; return error;
} }
int static int
xfs_free_file_space( xfs_unmap_extent(
struct xfs_inode *ip, struct xfs_inode *ip,
xfs_off_t offset, xfs_fileoff_t startoffset_fsb,
xfs_off_t len) xfs_filblks_t len_fsb,
int *done)
{ {
int done; struct xfs_mount *mp = ip->i_mount;
xfs_fileoff_t endoffset_fsb; struct xfs_trans *tp;
int error; struct xfs_bmap_free free_list;
xfs_fsblock_t firstfsb; xfs_fsblock_t firstfsb;
xfs_bmap_free_t free_list; uint resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0);
xfs_bmbt_irec_t imap; int error;
xfs_off_t ioffset;
xfs_off_t iendoffset;
xfs_extlen_t mod=0;
xfs_mount_t *mp;
int nimap;
uint resblks;
xfs_off_t rounding;
int rt;
xfs_fileoff_t startoffset_fsb;
xfs_trans_t *tp;
mp = ip->i_mount; error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);
if (error) {
ASSERT(error == -ENOSPC || XFS_FORCED_SHUTDOWN(mp));
return error;
}
trace_xfs_free_file_space(ip); xfs_ilock(ip, XFS_ILOCK_EXCL);
error = xfs_trans_reserve_quota(tp, mp, ip->i_udquot, ip->i_gdquot,
ip->i_pdquot, resblks, 0, XFS_QMOPT_RES_REGBLKS);
if (error)
goto out_trans_cancel;
error = xfs_qm_dqattach(ip, 0); xfs_trans_ijoin(tp, ip, 0);
xfs_bmap_init(&free_list, &firstfsb);
error = xfs_bunmapi(tp, ip, startoffset_fsb, len_fsb, 0, 2, &firstfsb,
&free_list, done);
if (error) if (error)
return error; goto out_bmap_cancel;
error = 0; error = xfs_bmap_finish(&tp, &free_list, NULL);
if (len <= 0) /* if nothing being freed */ if (error)
goto out_bmap_cancel;
error = xfs_trans_commit(tp);
out_unlock:
xfs_iunlock(ip, XFS_ILOCK_EXCL);
return error; return error;
rt = XFS_IS_REALTIME_INODE(ip);
startoffset_fsb = XFS_B_TO_FSB(mp, offset);
endoffset_fsb = XFS_B_TO_FSBT(mp, offset + len);
/* wait for the completion of any pending DIOs */ out_bmap_cancel:
inode_dio_wait(VFS_I(ip)); xfs_bmap_cancel(&free_list);
out_trans_cancel:
xfs_trans_cancel(tp);
goto out_unlock;
}
rounding = max_t(xfs_off_t, 1 << mp->m_sb.sb_blocklog, PAGE_SIZE); static int
ioffset = round_down(offset, rounding); xfs_adjust_extent_unmap_boundaries(
iendoffset = round_up(offset + len, rounding) - 1; struct xfs_inode *ip,
error = filemap_write_and_wait_range(VFS_I(ip)->i_mapping, ioffset, xfs_fileoff_t *startoffset_fsb,
iendoffset); xfs_fileoff_t *endoffset_fsb)
if (error) {
goto out; struct xfs_mount *mp = ip->i_mount;
truncate_pagecache_range(VFS_I(ip), ioffset, iendoffset); struct xfs_bmbt_irec imap;
int nimap, error;
xfs_extlen_t mod = 0;
/*
* Need to zero the stuff we're not freeing, on disk.
* If it's a realtime file & can't use unwritten extents then we
* actually need to zero the extent edges. Otherwise xfs_bunmapi
* will take care of it for us.
*/
if (rt && !xfs_sb_version_hasextflgbit(&mp->m_sb)) {
nimap = 1; nimap = 1;
error = xfs_bmapi_read(ip, startoffset_fsb, 1, error = xfs_bmapi_read(ip, *startoffset_fsb, 1, &imap, &nimap, 0);
&imap, &nimap, 0);
if (error) if (error)
goto out; return error;
ASSERT(nimap == 0 || nimap == 1);
if (nimap && imap.br_startblock != HOLESTARTBLOCK) { if (nimap && imap.br_startblock != HOLESTARTBLOCK) {
xfs_daddr_t block; xfs_daddr_t block;
...@@ -1254,21 +1257,90 @@ xfs_free_file_space( ...@@ -1254,21 +1257,90 @@ xfs_free_file_space(
block = imap.br_startblock; block = imap.br_startblock;
mod = do_div(block, mp->m_sb.sb_rextsize); mod = do_div(block, mp->m_sb.sb_rextsize);
if (mod) if (mod)
startoffset_fsb += mp->m_sb.sb_rextsize - mod; *startoffset_fsb += mp->m_sb.sb_rextsize - mod;
} }
nimap = 1; nimap = 1;
error = xfs_bmapi_read(ip, endoffset_fsb - 1, 1, error = xfs_bmapi_read(ip, *endoffset_fsb - 1, 1, &imap, &nimap, 0);
&imap, &nimap, 0);
if (error) if (error)
goto out; return error;
ASSERT(nimap == 0 || nimap == 1);
if (nimap && imap.br_startblock != HOLESTARTBLOCK) { if (nimap && imap.br_startblock != HOLESTARTBLOCK) {
ASSERT(imap.br_startblock != DELAYSTARTBLOCK); ASSERT(imap.br_startblock != DELAYSTARTBLOCK);
mod++; mod++;
if (mod && (mod != mp->m_sb.sb_rextsize)) if (mod && mod != mp->m_sb.sb_rextsize)
endoffset_fsb -= mod; *endoffset_fsb -= mod;
} }
return 0;
}
static int
xfs_flush_unmap_range(
struct xfs_inode *ip,
xfs_off_t offset,
xfs_off_t len)
{
struct xfs_mount *mp = ip->i_mount;
struct inode *inode = VFS_I(ip);
xfs_off_t rounding, start, end;
int error;
/* wait for the completion of any pending DIOs */
inode_dio_wait(inode);
rounding = max_t(xfs_off_t, 1 << mp->m_sb.sb_blocklog, PAGE_SIZE);
start = round_down(offset, rounding);
end = round_up(offset + len, rounding) - 1;
error = filemap_write_and_wait_range(inode->i_mapping, start, end);
if (error)
return error;
truncate_pagecache_range(inode, start, end);
return 0;
}
int
xfs_free_file_space(
struct xfs_inode *ip,
xfs_off_t offset,
xfs_off_t len)
{
struct xfs_mount *mp = ip->i_mount;
xfs_fileoff_t startoffset_fsb;
xfs_fileoff_t endoffset_fsb;
int done, error;
trace_xfs_free_file_space(ip);
error = xfs_qm_dqattach(ip, 0);
if (error)
return error;
if (len <= 0) /* if nothing being freed */
return 0;
error = xfs_flush_unmap_range(ip, offset, len);
if (error)
return error;
startoffset_fsb = XFS_B_TO_FSB(mp, offset);
endoffset_fsb = XFS_B_TO_FSBT(mp, offset + len);
/*
* Need to zero the stuff we're not freeing, on disk. If it's a RT file
* and we can't use unwritten extents then we actually need to ensure
* to zero the whole extent, otherwise we just need to take of block
* boundaries, and xfs_bunmapi will handle the rest.
*/
if (XFS_IS_REALTIME_INODE(ip) &&
!xfs_sb_version_hasextflgbit(&mp->m_sb)) {
error = xfs_adjust_extent_unmap_boundaries(ip, &startoffset_fsb,
&endoffset_fsb);
if (error)
return error;
} }
if ((done = (endoffset_fsb <= startoffset_fsb))) if ((done = (endoffset_fsb <= startoffset_fsb)))
/* /*
* One contiguous piece to clear * One contiguous piece to clear
...@@ -1288,62 +1360,12 @@ xfs_free_file_space( ...@@ -1288,62 +1360,12 @@ xfs_free_file_space(
offset + len - 1); offset + len - 1);
} }
/*
* free file space until done or until there is an error
*/
resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0);
while (!error && !done) { while (!error && !done) {
error = xfs_unmap_extent(ip, startoffset_fsb,
/* endoffset_fsb - startoffset_fsb, &done);
* allocate and setup the transaction. Allow this
* transaction to dip into the reserve blocks to ensure
* the freeing of the space succeeds at ENOSPC.
*/
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0,
&tp);
if (error) {
ASSERT(error == -ENOSPC || XFS_FORCED_SHUTDOWN(mp));
break;
}
xfs_ilock(ip, XFS_ILOCK_EXCL);
error = xfs_trans_reserve_quota(tp, mp,
ip->i_udquot, ip->i_gdquot, ip->i_pdquot,
resblks, 0, XFS_QMOPT_RES_REGBLKS);
if (error)
goto error1;
xfs_trans_ijoin(tp, ip, 0);
/*
* issue the bunmapi() call to free the blocks
*/
xfs_bmap_init(&free_list, &firstfsb);
error = xfs_bunmapi(tp, ip, startoffset_fsb,
endoffset_fsb - startoffset_fsb,
0, 2, &firstfsb, &free_list, &done);
if (error)
goto error0;
/*
* complete the transaction
*/
error = xfs_bmap_finish(&tp, &free_list, NULL);
if (error)
goto error0;
error = xfs_trans_commit(tp);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
} }
out:
return error; return error;
error0:
xfs_bmap_cancel(&free_list);
error1:
xfs_trans_cancel(tp);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
goto out;
} }
/* /*
......
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