When destroying the inode, cancel all pending reservations in the CoW fork so that all the reserved blocks go back to the free pile. Also free the reservations whenever we truncate or punch the entire file. Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx> --- fs/xfs/xfs_bmap_util.c | 1 + fs/xfs/xfs_inode.c | 5 +++- fs/xfs/xfs_reflink.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_reflink.h | 1 + fs/xfs/xfs_super.c | 10 ++++++++ 5 files changed, 80 insertions(+), 1 deletion(-) diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index dd5a2f7..3e274f6 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c @@ -1393,6 +1393,7 @@ xfs_free_file_space( * Clear the reflink flag if we freed everything. */ if (ip->i_d.di_nblocks == 0 && xfs_is_reflink_inode(ip)) { + xfs_reflink_cancel_pending_cow(ip); ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK; xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); } diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index a323631..59964c3 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -48,6 +48,7 @@ #include "xfs_trans_priv.h" #include "xfs_log.h" #include "xfs_bmap_btree.h" +#include "xfs_reflink.h" kmem_zone_t *xfs_inode_zone; @@ -1615,8 +1616,10 @@ xfs_itruncate_extents( /* * Clear the reflink flag if we truncated everything. */ - if (ip->i_d.di_nblocks == 0 && xfs_is_reflink_inode(ip)) + if (ip->i_d.di_nblocks == 0 && xfs_is_reflink_inode(ip)) { + xfs_reflink_cancel_pending_cow(ip); ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK; + } /* * Always re-log the inode so that our permanent transaction can keep diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 8594bc4..dcc71b9 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -658,3 +658,67 @@ out: trace_xfs_reflink_end_cow_error(ip, error, _RET_IP_); return error; } + +/** + * xfs_reflink_cancel_pending_cow() -- Cancel all pending CoW fork reservations. + * @ip: The XFS inode. + */ +int +xfs_reflink_cancel_pending_cow( + struct xfs_inode *ip) +{ + struct xfs_bmbt_irec irec; + struct xfs_ifork *ifp; + xfs_extnum_t idx; + struct xfs_bmbt_rec_host *gotp; + int error; + + /* + * If this isn't a reflink inode there had better not be anything + * in the CoW fork! + */ + if (!xfs_is_reflink_inode(ip)) { + ASSERT(ip->i_cowfp == NULL || ip->i_cowfp->if_bytes == 0); + return 0; + } + + trace_xfs_reflink_cancel_pending_cow(ip); + + ASSERT(ip->i_cowfp != NULL); + ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); + + /* Go find the old extent in the CoW fork. */ + ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); + gotp = xfs_iext_bno_to_ext(ifp, 0, &idx); + while (gotp) { + xfs_bmbt_get_all(gotp, &irec); + + /* + * We should never be asked to cancel allocated CoW extents + * unless the FS is broken, because doing so prevents the + * block remapping after the write finishes. + */ + ASSERT(isnullstartblock(irec.br_startblock) || + XFS_FORCED_SHUTDOWN(ip->i_mount)); + if (!isnullstartblock(irec.br_startblock)) + goto advloop; + + trace_xfs_reflink_cancel_cow(ip, &irec); + + /* Give the blocks back. */ + xfs_mod_fdblocks(ip->i_mount, irec.br_blockcount, false); + ip->i_delayed_blks -= irec.br_blockcount; + error = xfs_bunmapi_cow(ip, &idx, &irec); + if (error) + break; + + /* Roll on... */ +advloop: + idx++; + if (idx >= ifp->if_bytes / sizeof(xfs_bmbt_rec_t)) + break; + gotp = xfs_iext_get_ext(ifp, idx); + } + + return 0; +} diff --git a/fs/xfs/xfs_reflink.h b/fs/xfs/xfs_reflink.h index 2d2832b..cf4b43b 100644 --- a/fs/xfs/xfs_reflink.h +++ b/fs/xfs/xfs_reflink.h @@ -32,6 +32,7 @@ extern int xfs_reflink_end_cow_failed(struct xfs_inode *ip, xfs_off_t offset, size_t count); extern int xfs_reflink_end_cow(struct xfs_inode *ip, xfs_off_t offset, size_t count); +extern int xfs_reflink_cancel_pending_cow(struct xfs_inode *ip); int xfs_map_cow_blocks(struct inode *inode, xfs_off_t offset, struct xfs_bmbt_irec *imap); diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 85e582d..36df40b 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -45,6 +45,7 @@ #include "xfs_filestream.h" #include "xfs_quota.h" #include "xfs_sysfs.h" +#include "xfs_reflink.h" #include <linux/namei.h> #include <linux/init.h> @@ -920,11 +921,20 @@ xfs_fs_destroy_inode( struct inode *inode) { struct xfs_inode *ip = XFS_I(inode); + int error; trace_xfs_destroy_inode(ip); XFS_STATS_INC(ip->i_mount, vn_reclaim); + /* Cancel the CoW reservations. */ + xfs_ilock(ip, XFS_ILOCK_EXCL); + error = xfs_reflink_cancel_pending_cow(ip); + xfs_iunlock(ip, XFS_ILOCK_EXCL); + if (error) + xfs_warn(ip->i_mount, "Error %d destroying inode %llu.\n", + error, ip->i_ino); + ASSERT(XFS_FORCED_SHUTDOWN(ip->i_mount) || ip->i_delayed_blks == 0); /* _______________________________________________ xfs mailing list xfs@xxxxxxxxxxx http://oss.sgi.com/mailman/listinfo/xfs