diff options
Diffstat (limited to 'fs/xfs/xfs_icache.c')
| -rw-r--r-- | fs/xfs/xfs_icache.c | 70 | 
1 files changed, 49 insertions, 21 deletions
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 3bcb8fd2a826..d53a316162d6 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -296,6 +296,7 @@ xfs_reinit_inode(  	uint32_t	generation = inode->i_generation;  	uint64_t	version = inode_peek_iversion(inode);  	umode_t		mode = inode->i_mode; +	dev_t		dev = inode->i_rdev;  	error = inode_init_always(mp->m_super, inode); @@ -303,6 +304,7 @@ xfs_reinit_inode(  	inode->i_generation = generation;  	inode_set_iversion_queried(inode, version);  	inode->i_mode = mode; +	inode->i_rdev = dev;  	return error;  } @@ -474,6 +476,11 @@ xfs_iget_cache_miss(  	if (error)  		goto out_destroy; +	if (!xfs_inode_verify_forks(ip)) { +		error = -EFSCORRUPTED; +		goto out_destroy; +	} +  	trace_xfs_iget_miss(ip);  	if ((VFS_I(ip)->i_mode == 0) && !(flags & XFS_IGET_CREATE)) { @@ -1651,28 +1658,15 @@ xfs_inode_clear_eofblocks_tag(  }  /* - * Automatic CoW Reservation Freeing - * - * These functions automatically garbage collect leftover CoW reservations - * that were made on behalf of a cowextsize hint when we start to run out - * of quota or when the reservations sit around for too long.  If the file - * has dirty pages or is undergoing writeback, its CoW reservations will - * be retained. - * - * The actual garbage collection piggybacks off the same code that runs - * the speculative EOF preallocation garbage collector. + * Set ourselves up to free CoW blocks from this file.  If it's already clean + * then we can bail out quickly, but otherwise we must back off if the file + * is undergoing some kind of write.   */ -STATIC int -xfs_inode_free_cowblocks( +static bool +xfs_prep_free_cowblocks(  	struct xfs_inode	*ip, -	int			flags, -	void			*args) +	struct xfs_ifork	*ifp)  { -	int ret; -	struct xfs_eofblocks *eofb = args; -	int match; -	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); -  	/*  	 * Just clear the tag if we have an empty cow fork or none at all. It's  	 * possible the inode was fully unshared since it was originally tagged. @@ -1680,7 +1674,7 @@ xfs_inode_free_cowblocks(  	if (!xfs_is_reflink_inode(ip) || !ifp->if_bytes) {  		trace_xfs_inode_free_cowblocks_invalid(ip);  		xfs_inode_clear_cowblocks_tag(ip); -		return 0; +		return false;  	}  	/* @@ -1691,6 +1685,35 @@ xfs_inode_free_cowblocks(  	    mapping_tagged(VFS_I(ip)->i_mapping, PAGECACHE_TAG_DIRTY) ||  	    mapping_tagged(VFS_I(ip)->i_mapping, PAGECACHE_TAG_WRITEBACK) ||  	    atomic_read(&VFS_I(ip)->i_dio_count)) +		return false; + +	return true; +} + +/* + * Automatic CoW Reservation Freeing + * + * These functions automatically garbage collect leftover CoW reservations + * that were made on behalf of a cowextsize hint when we start to run out + * of quota or when the reservations sit around for too long.  If the file + * has dirty pages or is undergoing writeback, its CoW reservations will + * be retained. + * + * The actual garbage collection piggybacks off the same code that runs + * the speculative EOF preallocation garbage collector. + */ +STATIC int +xfs_inode_free_cowblocks( +	struct xfs_inode	*ip, +	int			flags, +	void			*args) +{ +	struct xfs_eofblocks	*eofb = args; +	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); +	int			match; +	int			ret = 0; + +	if (!xfs_prep_free_cowblocks(ip, ifp))  		return 0;  	if (eofb) { @@ -1711,7 +1734,12 @@ xfs_inode_free_cowblocks(  	xfs_ilock(ip, XFS_IOLOCK_EXCL);  	xfs_ilock(ip, XFS_MMAPLOCK_EXCL); -	ret = xfs_reflink_cancel_cow_range(ip, 0, NULLFILEOFF, false); +	/* +	 * Check again, nobody else should be able to dirty blocks or change +	 * the reflink iflag now that we have the first two locks held. +	 */ +	if (xfs_prep_free_cowblocks(ip, ifp)) +		ret = xfs_reflink_cancel_cow_range(ip, 0, NULLFILEOFF, false);  	xfs_iunlock(ip, XFS_MMAPLOCK_EXCL);  	xfs_iunlock(ip, XFS_IOLOCK_EXCL);  |