diff options
Diffstat (limited to 'fs/xfs/xfs_bmap_util.c')
| -rw-r--r-- | fs/xfs/xfs_bmap_util.c | 164 | 
1 files changed, 122 insertions, 42 deletions
| diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index 93e955262d07..6503cfa44262 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c @@ -84,6 +84,7 @@ xfs_zero_extent(  		GFP_NOFS, 0);  } +#ifdef CONFIG_XFS_RT  int  xfs_bmap_rtalloc(  	struct xfs_bmalloca	*ap)	/* bmap alloc argument struct */ @@ -190,6 +191,7 @@ xfs_bmap_rtalloc(  	}  	return 0;  } +#endif /* CONFIG_XFS_RT */  /*   * Check if the endoff is outside the last extent. If so the caller will grow @@ -222,22 +224,21 @@ xfs_bmap_eof(   * Count leaf blocks given a range of extent records.  Delayed allocation   * extents are not counted towards the totals.   */ -STATIC void +xfs_extnum_t  xfs_bmap_count_leaves(  	struct xfs_ifork	*ifp, -	xfs_extnum_t		*numrecs,  	xfs_filblks_t		*count)  { -	xfs_extnum_t		i; -	xfs_extnum_t		nr_exts = xfs_iext_count(ifp); - -	for (i = 0; i < nr_exts; i++) { -		xfs_bmbt_rec_host_t *frp = xfs_iext_get_ext(ifp, i); -		if (!isnullstartblock(xfs_bmbt_get_startblock(frp))) { -			(*numrecs)++; -			*count += xfs_bmbt_get_blockcount(frp); +	struct xfs_bmbt_irec	got; +	xfs_extnum_t		numrecs = 0, i = 0; + +	while (xfs_iext_get_extent(ifp, i++, &got)) { +		if (!isnullstartblock(got.br_startblock)) { +			*count += got.br_blockcount; +			numrecs++;  		}  	} +	return numrecs;  }  /* @@ -370,7 +371,7 @@ xfs_bmap_count_blocks(  	switch (XFS_IFORK_FORMAT(ip, whichfork)) {  	case XFS_DINODE_FMT_EXTENTS: -		xfs_bmap_count_leaves(ifp, nextents, count); +		*nextents = xfs_bmap_count_leaves(ifp, count);  		return 0;  	case XFS_DINODE_FMT_BTREE:  		if (!(ifp->if_flags & XFS_IFEXTENTS)) { @@ -1136,7 +1137,7 @@ xfs_alloc_file_space(  		/*  		 * Complete the transaction  		 */ -		error = xfs_defer_finish(&tp, &dfops, NULL); +		error = xfs_defer_finish(&tp, &dfops);  		if (error)  			goto error0; @@ -1202,7 +1203,8 @@ xfs_unmap_extent(  	if (error)  		goto out_bmap_cancel; -	error = xfs_defer_finish(&tp, &dfops, ip); +	xfs_defer_ijoin(&dfops, ip); +	error = xfs_defer_finish(&tp, &dfops);  	if (error)  		goto out_bmap_cancel; @@ -1459,7 +1461,19 @@ xfs_shift_file_space(  		return error;  	/* -	 * The extent shiting code works on extent granularity. So, if +	 * Clean out anything hanging around in the cow fork now that +	 * we've flushed all the dirty data out to disk to avoid having +	 * CoW extents at the wrong offsets. +	 */ +	if (xfs_is_reflink_inode(ip)) { +		error = xfs_reflink_cancel_cow_range(ip, offset, NULLFILEOFF, +				true); +		if (error) +			return error; +	} + +	/* +	 * The extent shifting code works on extent granularity. So, if  	 * stop_fsb is not the starting block of extent, we need to split  	 * the extent at stop_fsb.  	 */ @@ -1496,7 +1510,7 @@ xfs_shift_file_space(  		if (error)  			goto out_bmap_cancel; -		error = xfs_defer_finish(&tp, &dfops, NULL); +		error = xfs_defer_finish(&tp, &dfops);  		if (error)  			goto out_bmap_cancel; @@ -1777,7 +1791,8 @@ xfs_swap_extent_rmap(  			if (error)  				goto out_defer; -			error = xfs_defer_finish(tpp, &dfops, ip); +			xfs_defer_ijoin(&dfops, ip); +			error = xfs_defer_finish(tpp, &dfops);  			if (error)  				goto out_defer; @@ -1840,29 +1855,18 @@ xfs_swap_extent_forks(  	}  	/* -	 * Before we've swapped the forks, lets set the owners of the forks -	 * appropriately. We have to do this as we are demand paging the btree -	 * buffers, and so the validation done on read will expect the owner -	 * field to be correctly set. Once we change the owners, we can swap the -	 * inode forks. +	 * Btree format (v3) inodes have the inode number stamped in the bmbt +	 * block headers. We can't start changing the bmbt blocks until the +	 * inode owner change is logged so recovery does the right thing in the +	 * event of a crash. Set the owner change log flags now and leave the +	 * bmbt scan as the last step.  	 */  	if (ip->i_d.di_version == 3 && -	    ip->i_d.di_format == XFS_DINODE_FMT_BTREE) { +	    ip->i_d.di_format == XFS_DINODE_FMT_BTREE)  		(*target_log_flags) |= XFS_ILOG_DOWNER; -		error = xfs_bmbt_change_owner(tp, ip, XFS_DATA_FORK, -					      tip->i_ino, NULL); -		if (error) -			return error; -	} -  	if (tip->i_d.di_version == 3 && -	    tip->i_d.di_format == XFS_DINODE_FMT_BTREE) { +	    tip->i_d.di_format == XFS_DINODE_FMT_BTREE)  		(*src_log_flags) |= XFS_ILOG_DOWNER; -		error = xfs_bmbt_change_owner(tp, tip, XFS_DATA_FORK, -					      ip->i_ino, NULL); -		if (error) -			return error; -	}  	/*  	 * Swap the data forks of the inodes @@ -1940,6 +1944,48 @@ xfs_swap_extent_forks(  	return 0;  } +/* + * Fix up the owners of the bmbt blocks to refer to the current inode. The + * change owner scan attempts to order all modified buffers in the current + * transaction. In the event of ordered buffer failure, the offending buffer is + * physically logged as a fallback and the scan returns -EAGAIN. We must roll + * the transaction in this case to replenish the fallback log reservation and + * restart the scan. This process repeats until the scan completes. + */ +static int +xfs_swap_change_owner( +	struct xfs_trans	**tpp, +	struct xfs_inode	*ip, +	struct xfs_inode	*tmpip) +{ +	int			error; +	struct xfs_trans	*tp = *tpp; + +	do { +		error = xfs_bmbt_change_owner(tp, ip, XFS_DATA_FORK, ip->i_ino, +					      NULL); +		/* success or fatal error */ +		if (error != -EAGAIN) +			break; + +		error = xfs_trans_roll(tpp); +		if (error) +			break; +		tp = *tpp; + +		/* +		 * Redirty both inodes so they can relog and keep the log tail +		 * moving forward. +		 */ +		xfs_trans_ijoin(tp, ip, 0); +		xfs_trans_ijoin(tp, tmpip, 0); +		xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); +		xfs_trans_log_inode(tp, tmpip, XFS_ILOG_CORE); +	} while (true); + +	return error; +} +  int  xfs_swap_extents(  	struct xfs_inode	*ip,	/* target inode */ @@ -1954,7 +2000,7 @@ xfs_swap_extents(  	int			lock_flags;  	struct xfs_ifork	*cowfp;  	uint64_t		f; -	int			resblks; +	int			resblks = 0;  	/*  	 * Lock the inodes against other IO, page faults and truncate to @@ -2002,11 +2048,8 @@ xfs_swap_extents(  			  XFS_SWAP_RMAP_SPACE_RES(mp,  				XFS_IFORK_NEXTENTS(tip, XFS_DATA_FORK),  				XFS_DATA_FORK); -		error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, -				0, 0, &tp); -	} else -		error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, -				0, 0, &tp); +	} +	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);  	if (error)  		goto out_unlock; @@ -2081,17 +2124,54 @@ xfs_swap_extents(  		ip->i_d.di_flags2 |= tip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK;  		tip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;  		tip->i_d.di_flags2 |= f & XFS_DIFLAG2_REFLINK; +	} + +	/* Swap the cow forks. */ +	if (xfs_sb_version_hasreflink(&mp->m_sb)) { +		xfs_extnum_t	extnum; + +		ASSERT(ip->i_cformat == XFS_DINODE_FMT_EXTENTS); +		ASSERT(tip->i_cformat == XFS_DINODE_FMT_EXTENTS); + +		extnum = ip->i_cnextents; +		ip->i_cnextents = tip->i_cnextents; +		tip->i_cnextents = extnum; +  		cowfp = ip->i_cowfp;  		ip->i_cowfp = tip->i_cowfp;  		tip->i_cowfp = cowfp; -		xfs_inode_set_cowblocks_tag(ip); -		xfs_inode_set_cowblocks_tag(tip); + +		if (ip->i_cowfp && ip->i_cnextents) +			xfs_inode_set_cowblocks_tag(ip); +		else +			xfs_inode_clear_cowblocks_tag(ip); +		if (tip->i_cowfp && tip->i_cnextents) +			xfs_inode_set_cowblocks_tag(tip); +		else +			xfs_inode_clear_cowblocks_tag(tip);  	}  	xfs_trans_log_inode(tp, ip,  src_log_flags);  	xfs_trans_log_inode(tp, tip, target_log_flags);  	/* +	 * The extent forks have been swapped, but crc=1,rmapbt=0 filesystems +	 * have inode number owner values in the bmbt blocks that still refer to +	 * the old inode. Scan each bmbt to fix up the owner values with the +	 * inode number of the current inode. +	 */ +	if (src_log_flags & XFS_ILOG_DOWNER) { +		error = xfs_swap_change_owner(&tp, ip, tip); +		if (error) +			goto out_trans_cancel; +	} +	if (target_log_flags & XFS_ILOG_DOWNER) { +		error = xfs_swap_change_owner(&tp, tip, ip); +		if (error) +			goto out_trans_cancel; +	} + +	/*  	 * If this is a synchronous mount, make sure that the  	 * transaction goes to disk before returning to the user.  	 */ |