diff options
| author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-10-04 19:17:38 +0300 | 
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-10-04 19:17:38 +0300 | 
| commit | 1b0350c355b2f32bcbd453310bf6dfb50cbe965f (patch) | |
| tree | b64e213a661babcc5b4364d181d62ea0be8cf13f /fs/xfs/xfs_reflink.c | |
| parent | d2467adb66108f1b7fec8cadbdb8ca140e88effe (diff) | |
| parent | e55ec4ddbef9897199c307dfb23167e3801fdaf5 (diff) | |
| download | linux-1b0350c355b2f32bcbd453310bf6dfb50cbe965f.tar.xz | |
Merge tag 'xfs-fixes-for-4.19-rc6' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
Dave writes:
  "XFS fixes for 4.19-rc6
   Accumlated regression and bug fixes for 4.19-rc6, including:
   o make iomap correctly mark dirty pages for sub-page block sizes
   o fix regression in handling extent-to-btree format conversion errors
   o fix torn log wrap detection for new logs
   o various corrupt inode detection fixes
   o various delalloc state fixes
   o cleanup all the missed transaction cancel cases missed from changes merged
     in 4.19-rc1
   o fix lockdep false positive on transaction allocation
   o fix locking and reference counting on buffer log items"
* tag 'xfs-fixes-for-4.19-rc6' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  xfs: fix error handling in xfs_bmap_extents_to_btree
  iomap: set page dirty after partial delalloc on mkwrite
  xfs: remove invalid log recovery first/last cycle check
  xfs: validate inode di_forkoff
  xfs: skip delalloc COW blocks in xfs_reflink_end_cow
  xfs: don't treat unknown di_flags2 as corruption in scrub
  xfs: remove duplicated include from alloc.c
  xfs: don't bring in extents in xfs_bmap_punch_delalloc_range
  xfs: fix transaction leak in xfs_reflink_allocate_cow()
  xfs: avoid lockdep false positives in xfs_trans_alloc
  xfs: refactor xfs_buf_log_item reference count handling
  xfs: clean up xfs_trans_brelse()
  xfs: don't unlock invalidated buf on aborted tx commit
  xfs: remove last of unnecessary xfs_defer_cancel() callers
  xfs: don't crash the vfs on a garbage inline symlink
Diffstat (limited to 'fs/xfs/xfs_reflink.c')
| -rw-r--r-- | fs/xfs/xfs_reflink.c | 137 | 
1 files changed, 81 insertions, 56 deletions
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 38f405415b88..5289e22cb081 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -352,6 +352,47 @@ xfs_reflink_convert_cow(  	return error;  } +/* + * Find the extent that maps the given range in the COW fork. Even if the extent + * is not shared we might have a preallocation for it in the COW fork. If so we + * use it that rather than trigger a new allocation. + */ +static int +xfs_find_trim_cow_extent( +	struct xfs_inode	*ip, +	struct xfs_bmbt_irec	*imap, +	bool			*shared, +	bool			*found) +{ +	xfs_fileoff_t		offset_fsb = imap->br_startoff; +	xfs_filblks_t		count_fsb = imap->br_blockcount; +	struct xfs_iext_cursor	icur; +	struct xfs_bmbt_irec	got; +	bool			trimmed; + +	*found = false; + +	/* +	 * If we don't find an overlapping extent, trim the range we need to +	 * allocate to fit the hole we found. +	 */ +	if (!xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &icur, &got) || +	    got.br_startoff > offset_fsb) +		return xfs_reflink_trim_around_shared(ip, imap, shared, &trimmed); + +	*shared = true; +	if (isnullstartblock(got.br_startblock)) { +		xfs_trim_extent(imap, got.br_startoff, got.br_blockcount); +		return 0; +	} + +	/* real extent found - no need to allocate */ +	xfs_trim_extent(&got, offset_fsb, count_fsb); +	*imap = got; +	*found = true; +	return 0; +} +  /* Allocate all CoW reservations covering a range of blocks in a file. */  int  xfs_reflink_allocate_cow( @@ -363,78 +404,64 @@ xfs_reflink_allocate_cow(  	struct xfs_mount	*mp = ip->i_mount;  	xfs_fileoff_t		offset_fsb = imap->br_startoff;  	xfs_filblks_t		count_fsb = imap->br_blockcount; -	struct xfs_bmbt_irec	got; -	struct xfs_trans	*tp = NULL; +	struct xfs_trans	*tp;  	int			nimaps, error = 0; -	bool			trimmed; +	bool			found;  	xfs_filblks_t		resaligned;  	xfs_extlen_t		resblks = 0; -	struct xfs_iext_cursor	icur; -retry: -	ASSERT(xfs_is_reflink_inode(ip));  	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); +	ASSERT(xfs_is_reflink_inode(ip)); -	/* -	 * Even if the extent is not shared we might have a preallocation for -	 * it in the COW fork.  If so use it. -	 */ -	if (xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &icur, &got) && -	    got.br_startoff <= offset_fsb) { -		*shared = true; - -		/* If we have a real allocation in the COW fork we're done. */ -		if (!isnullstartblock(got.br_startblock)) { -			xfs_trim_extent(&got, offset_fsb, count_fsb); -			*imap = got; -			goto convert; -		} +	error = xfs_find_trim_cow_extent(ip, imap, shared, &found); +	if (error || !*shared) +		return error; +	if (found) +		goto convert; -		xfs_trim_extent(imap, got.br_startoff, got.br_blockcount); -	} else { -		error = xfs_reflink_trim_around_shared(ip, imap, shared, &trimmed); -		if (error || !*shared) -			goto out; -	} +	resaligned = xfs_aligned_fsb_count(imap->br_startoff, +		imap->br_blockcount, xfs_get_cowextsz_hint(ip)); +	resblks = XFS_DIOSTRAT_SPACE_RES(mp, resaligned); -	if (!tp) { -		resaligned = xfs_aligned_fsb_count(imap->br_startoff, -			imap->br_blockcount, xfs_get_cowextsz_hint(ip)); -		resblks = XFS_DIOSTRAT_SPACE_RES(mp, resaligned); +	xfs_iunlock(ip, *lockmode); +	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp); +	*lockmode = XFS_ILOCK_EXCL; +	xfs_ilock(ip, *lockmode); -		xfs_iunlock(ip, *lockmode); -		error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp); -		*lockmode = XFS_ILOCK_EXCL; -		xfs_ilock(ip, *lockmode); +	if (error) +		return error; -		if (error) -			return error; +	error = xfs_qm_dqattach_locked(ip, false); +	if (error) +		goto out_trans_cancel; -		error = xfs_qm_dqattach_locked(ip, false); -		if (error) -			goto out; -		goto retry; +	/* +	 * Check for an overlapping extent again now that we dropped the ilock. +	 */ +	error = xfs_find_trim_cow_extent(ip, imap, shared, &found); +	if (error || !*shared) +		goto out_trans_cancel; +	if (found) { +		xfs_trans_cancel(tp); +		goto convert;  	}  	error = xfs_trans_reserve_quota_nblks(tp, ip, resblks, 0,  			XFS_QMOPT_RES_REGBLKS);  	if (error) -		goto out; +		goto out_trans_cancel;  	xfs_trans_ijoin(tp, ip, 0); -	nimaps = 1; -  	/* Allocate the entire reservation as unwritten blocks. */ +	nimaps = 1;  	error = xfs_bmapi_write(tp, ip, imap->br_startoff, imap->br_blockcount,  			XFS_BMAPI_COWFORK | XFS_BMAPI_PREALLOC,  			resblks, imap, &nimaps);  	if (error) -		goto out_trans_cancel; +		goto out_unreserve;  	xfs_inode_set_cowblocks_tag(ip); - -	/* Finish up. */  	error = xfs_trans_commit(tp);  	if (error)  		return error; @@ -447,12 +474,12 @@ retry:  		return -ENOSPC;  convert:  	return xfs_reflink_convert_cow_extent(ip, imap, offset_fsb, count_fsb); -out_trans_cancel: + +out_unreserve:  	xfs_trans_unreserve_quota_nblks(tp, ip, (long)resblks, 0,  			XFS_QMOPT_RES_REGBLKS); -out: -	if (tp) -		xfs_trans_cancel(tp); +out_trans_cancel: +	xfs_trans_cancel(tp);  	return error;  } @@ -666,14 +693,12 @@ xfs_reflink_end_cow(  		if (!del.br_blockcount)  			goto prev_extent; -		ASSERT(!isnullstartblock(got.br_startblock)); -  		/* -		 * Don't remap unwritten extents; these are -		 * speculatively preallocated CoW extents that have been -		 * allocated but have not yet been involved in a write. +		 * Only remap real extent that contain data.  With AIO +		 * speculatively preallocations can leak into the range we +		 * are called upon, and we need to skip them.  		 */ -		if (got.br_state == XFS_EXT_UNWRITTEN) +		if (!xfs_bmap_is_real_extent(&got))  			goto prev_extent;  		/* Unmap the old blocks in the data fork. */  | 
