diff options
Diffstat (limited to 'fs/xfs/xfs_vnodeops.c')
| -rw-r--r-- | fs/xfs/xfs_vnodeops.c | 168 | 
1 files changed, 121 insertions, 47 deletions
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 2a5c637344b..d95f565a390 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -47,6 +47,7 @@  #include "xfs_filestream.h"  #include "xfs_vnodeops.h"  #include "xfs_trace.h" +#include "xfs_icache.h"  /*   * The maximum pathlen is 1024 bytes. Since the minimum file system @@ -79,7 +80,7 @@ xfs_readlink_bmap(  		d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock);  		byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount); -		bp = xfs_buf_read(mp->m_ddev_targp, d, BTOBB(byte_cnt), 0); +		bp = xfs_buf_read(mp->m_ddev_targp, d, BTOBB(byte_cnt), 0, NULL);  		if (!bp)  			return XFS_ERROR(ENOMEM);  		error = bp->b_error; @@ -150,7 +151,7 @@ xfs_readlink(   * when the link count isn't zero and by xfs_dm_punch_hole() when   * punching a hole to EOF.   */ -STATIC int +int  xfs_free_eofblocks(  	xfs_mount_t	*mp,  	xfs_inode_t	*ip, @@ -199,7 +200,7 @@ xfs_free_eofblocks(  		if (need_iolock) {  			if (!xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL)) {  				xfs_trans_cancel(tp, 0); -				return 0; +				return EAGAIN;  			}  		} @@ -237,6 +238,8 @@ xfs_free_eofblocks(  		} else {  			error = xfs_trans_commit(tp,  						XFS_TRANS_RELEASE_LOG_RES); +			if (!error) +				xfs_inode_clear_eofblocks_tag(ip);  		}  		xfs_iunlock(ip, XFS_ILOCK_EXCL); @@ -425,19 +428,18 @@ xfs_release(  		truncated = xfs_iflags_test_and_clear(ip, XFS_ITRUNCATED);  		if (truncated) {  			xfs_iflags_clear(ip, XFS_IDIRTY_RELEASE); -			if (VN_DIRTY(VFS_I(ip)) && ip->i_delayed_blks > 0) -				xfs_flush_pages(ip, 0, -1, XBF_ASYNC, FI_NONE); +			if (VN_DIRTY(VFS_I(ip)) && ip->i_delayed_blks > 0) { +				error = -filemap_flush(VFS_I(ip)->i_mapping); +				if (error) +					return error; +			}  		}  	}  	if (ip->i_d.di_nlink == 0)  		return 0; -	if ((S_ISREG(ip->i_d.di_mode) && -	     (VFS_I(ip)->i_size > 0 || -	      (VN_CACHED(VFS_I(ip)) > 0 || ip->i_delayed_blks > 0)) && -	     (ip->i_df.if_flags & XFS_IFEXTENTS))  && -	    (!(ip->i_d.di_flags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)))) { +	if (xfs_can_free_eofblocks(ip, false)) {  		/*  		 * If we can't get the iolock just skip truncating the blocks @@ -464,7 +466,7 @@ xfs_release(  			return 0;  		error = xfs_free_eofblocks(mp, ip, true); -		if (error) +		if (error && error != EAGAIN)  			return error;  		/* delalloc blocks after truncation means it really is dirty */ @@ -513,13 +515,12 @@ xfs_inactive(  		goto out;  	if (ip->i_d.di_nlink != 0) { -		if ((S_ISREG(ip->i_d.di_mode) && -		    (VFS_I(ip)->i_size > 0 || -		     (VN_CACHED(VFS_I(ip)) > 0 || ip->i_delayed_blks > 0)) && -		    (ip->i_df.if_flags & XFS_IFEXTENTS) && -		    (!(ip->i_d.di_flags & -				(XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) || -		     ip->i_delayed_blks != 0))) { +		/* +		 * force is true because we are evicting an inode from the +		 * cache. Post-eof blocks must be freed, lest we end up with +		 * broken free space accounting. +		 */ +		if (xfs_can_free_eofblocks(ip, true)) {  			error = xfs_free_eofblocks(mp, ip, false);  			if (error)  				return VN_INACTIVE_CACHE; @@ -777,7 +778,7 @@ xfs_create(  			XFS_TRANS_PERM_LOG_RES, log_count);  	if (error == ENOSPC) {  		/* flush outstanding delalloc blocks and retry */ -		xfs_flush_inodes(dp); +		xfs_flush_inodes(mp);  		error = xfs_trans_reserve(tp, resblks, log_res, 0,  				XFS_TRANS_PERM_LOG_RES, log_count);  	} @@ -1957,12 +1958,11 @@ xfs_free_file_space(  	rounding = max_t(uint, 1 << mp->m_sb.sb_blocklog, PAGE_CACHE_SIZE);  	ioffset = offset & ~(rounding - 1); - -	if (VN_CACHED(VFS_I(ip)) != 0) { -		error = xfs_flushinval_pages(ip, ioffset, -1, FI_REMAPF_LOCKED); -		if (error) -			goto out_unlock_iolock; -	} +	error = -filemap_write_and_wait_range(VFS_I(ip)->i_mapping, +					      ioffset, -1); +	if (error) +		goto out_unlock_iolock; +	truncate_pagecache_range(VFS_I(ip), ioffset, -1);  	/*  	 * Need to zero the stuff we're not freeing, on disk. @@ -2095,6 +2095,73 @@ xfs_free_file_space(  	return error;  } + +STATIC int +xfs_zero_file_space( +	struct xfs_inode	*ip, +	xfs_off_t		offset, +	xfs_off_t		len, +	int			attr_flags) +{ +	struct xfs_mount	*mp = ip->i_mount; +	uint			granularity; +	xfs_off_t		start_boundary; +	xfs_off_t		end_boundary; +	int			error; + +	granularity = max_t(uint, 1 << mp->m_sb.sb_blocklog, PAGE_CACHE_SIZE); + +	/* +	 * Round the range of extents we are going to convert inwards.  If the +	 * offset is aligned, then it doesn't get changed so we zero from the +	 * start of the block offset points to. +	 */ +	start_boundary = round_up(offset, granularity); +	end_boundary = round_down(offset + len, granularity); + +	ASSERT(start_boundary >= offset); +	ASSERT(end_boundary <= offset + len); + +	if (!(attr_flags & XFS_ATTR_NOLOCK)) +		xfs_ilock(ip, XFS_IOLOCK_EXCL); + +	if (start_boundary < end_boundary - 1) { +		/* punch out the page cache over the conversion range */ +		truncate_pagecache_range(VFS_I(ip), start_boundary, +					 end_boundary - 1); +		/* convert the blocks */ +		error = xfs_alloc_file_space(ip, start_boundary, +					end_boundary - start_boundary - 1, +					XFS_BMAPI_PREALLOC | XFS_BMAPI_CONVERT, +					attr_flags); +		if (error) +			goto out_unlock; + +		/* We've handled the interior of the range, now for the edges */ +		if (start_boundary != offset) +			error = xfs_iozero(ip, offset, start_boundary - offset); +		if (error) +			goto out_unlock; + +		if (end_boundary != offset + len) +			error = xfs_iozero(ip, end_boundary, +					   offset + len - end_boundary); + +	} else { +		/* +		 * It's either a sub-granularity range or the range spanned lies +		 * partially across two adjacent blocks. +		 */ +		error = xfs_iozero(ip, offset, len); +	} + +out_unlock: +	if (!(attr_flags & XFS_ATTR_NOLOCK)) +		xfs_iunlock(ip, XFS_IOLOCK_EXCL); +	return error; + +} +  /*   * xfs_change_file_space()   *      This routine allocates or frees disk space for the given file. @@ -2120,10 +2187,8 @@ xfs_change_file_space(  	xfs_fsize_t	fsize;  	int		setprealloc;  	xfs_off_t	startoffset; -	xfs_off_t	llen;  	xfs_trans_t	*tp;  	struct iattr	iattr; -	int		prealloc_type;  	if (!S_ISREG(ip->i_d.di_mode))  		return XFS_ERROR(EINVAL); @@ -2141,12 +2206,30 @@ xfs_change_file_space(  		return XFS_ERROR(EINVAL);  	} -	llen = bf->l_len > 0 ? bf->l_len - 1 : bf->l_len; +	/* +	 * length of <= 0 for resv/unresv/zero is invalid.  length for +	 * alloc/free is ignored completely and we have no idea what userspace +	 * might have set it to, so set it to zero to allow range +	 * checks to pass. +	 */ +	switch (cmd) { +	case XFS_IOC_ZERO_RANGE: +	case XFS_IOC_RESVSP: +	case XFS_IOC_RESVSP64: +	case XFS_IOC_UNRESVSP: +	case XFS_IOC_UNRESVSP64: +		if (bf->l_len <= 0) +			return XFS_ERROR(EINVAL); +		break; +	default: +		bf->l_len = 0; +		break; +	}  	if (bf->l_start < 0 ||  	    bf->l_start > mp->m_super->s_maxbytes || -	    bf->l_start + llen < 0 || -	    bf->l_start + llen > mp->m_super->s_maxbytes) +	    bf->l_start + bf->l_len < 0 || +	    bf->l_start + bf->l_len >= mp->m_super->s_maxbytes)  		return XFS_ERROR(EINVAL);  	bf->l_whence = 0; @@ -2154,29 +2237,20 @@ xfs_change_file_space(  	startoffset = bf->l_start;  	fsize = XFS_ISIZE(ip); -	/* -	 * XFS_IOC_RESVSP and XFS_IOC_UNRESVSP will reserve or unreserve -	 * file space. -	 * These calls do NOT zero the data space allocated to the file, -	 * nor do they change the file size. -	 * -	 * XFS_IOC_ALLOCSP and XFS_IOC_FREESP will allocate and free file -	 * space. -	 * These calls cause the new file data to be zeroed and the file -	 * size to be changed. -	 */  	setprealloc = clrprealloc = 0; -	prealloc_type = XFS_BMAPI_PREALLOC; -  	switch (cmd) {  	case XFS_IOC_ZERO_RANGE: -		prealloc_type |= XFS_BMAPI_CONVERT; -		xfs_tosspages(ip, startoffset, startoffset + bf->l_len, 0); -		/* FALLTHRU */ +		error = xfs_zero_file_space(ip, startoffset, bf->l_len, +						attr_flags); +		if (error) +			return error; +		setprealloc = 1; +		break; +  	case XFS_IOC_RESVSP:  	case XFS_IOC_RESVSP64:  		error = xfs_alloc_file_space(ip, startoffset, bf->l_len, -						prealloc_type, attr_flags); +						XFS_BMAPI_PREALLOC, attr_flags);  		if (error)  			return error;  		setprealloc = 1;  |