diff options
Diffstat (limited to 'fs/xfs/xfs_trans_resv.c')
| -rw-r--r-- | fs/xfs/xfs_trans_resv.c | 163 | 
1 files changed, 127 insertions, 36 deletions
diff --git a/fs/xfs/xfs_trans_resv.c b/fs/xfs/xfs_trans_resv.c index a65a3cc4061..f2bda7c76b8 100644 --- a/fs/xfs/xfs_trans_resv.c +++ b/fs/xfs/xfs_trans_resv.c @@ -18,27 +18,20 @@   */  #include "xfs.h"  #include "xfs_fs.h" +#include "xfs_shared.h"  #include "xfs_format.h" -#include "xfs_log.h" +#include "xfs_log_format.h"  #include "xfs_trans_resv.h" -#include "xfs_trans.h"  #include "xfs_sb.h"  #include "xfs_ag.h"  #include "xfs_mount.h" -#include "xfs_error.h" +#include "xfs_da_format.h"  #include "xfs_da_btree.h" -#include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_dinode.h"  #include "xfs_inode.h" -#include "xfs_btree.h" +#include "xfs_bmap_btree.h"  #include "xfs_ialloc.h" -#include "xfs_alloc.h" -#include "xfs_extent_busy.h" -#include "xfs_bmap.h" -#include "xfs_bmap_util.h"  #include "xfs_quota.h" +#include "xfs_trans.h"  #include "xfs_qm.h"  #include "xfs_trans_space.h"  #include "xfs_trace.h" @@ -89,20 +82,69 @@ xfs_calc_buf_res(   * on disk. Hence we need an inode reservation function that calculates all this   * correctly. So, we log:   * - * - log op headers for object + * - 4 log op headers for object + *	- for the ilf, the inode core and 2 forks   * - inode log format object - * - the entire inode contents (core + 2 forks) - * - two bmap btree block headers + * - the inode core + * - two inode forks containing bmap btree root blocks. + *	- the btree data contained by both forks will fit into the inode size, + *	  hence when combined with the inode core above, we have a total of the + *	  actual inode size. + *	- the BMBT headers need to be accounted separately, as they are + *	  additional to the records and pointers that fit inside the inode + *	  forks.   */  STATIC uint  xfs_calc_inode_res(  	struct xfs_mount	*mp,  	uint			ninodes)  { -	return ninodes * (sizeof(struct xlog_op_header) + -			  sizeof(struct xfs_inode_log_format) + -			  mp->m_sb.sb_inodesize + -			  2 * XFS_BMBT_BLOCK_LEN(mp)); +	return ninodes * +		(4 * sizeof(struct xlog_op_header) + +		 sizeof(struct xfs_inode_log_format) + +		 mp->m_sb.sb_inodesize + +		 2 * XFS_BMBT_BLOCK_LEN(mp)); +} + +/* + * The free inode btree is a conditional feature and the log reservation + * requirements differ slightly from that of the traditional inode allocation + * btree. The finobt tracks records for inode chunks with at least one free + * inode. A record can be removed from the tree for an inode allocation + * or free and thus the finobt reservation is unconditional across: + * + * 	- inode allocation + * 	- inode free + * 	- inode chunk allocation + * + * The 'modify' param indicates to include the record modification scenario. The + * 'alloc' param indicates to include the reservation for free space btree + * modifications on behalf of finobt modifications. This is required only for + * transactions that do not already account for free space btree modifications. + * + * the free inode btree: max depth * block size + * the allocation btrees: 2 trees * (max depth - 1) * block size + * the free inode btree entry: block size + */ +STATIC uint +xfs_calc_finobt_res( +	struct xfs_mount 	*mp, +	int			alloc, +	int			modify) +{ +	uint res; + +	if (!xfs_sb_version_hasfinobt(&mp->m_sb)) +		return 0; + +	res = xfs_calc_buf_res(mp->m_in_maxlevels, XFS_FSB_TO_B(mp, 1)); +	if (alloc) +		res += xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),  +					XFS_FSB_TO_B(mp, 1)); +	if (modify) +		res += (uint)XFS_FSB_TO_B(mp, 1); + +	return res;  }  /* @@ -182,7 +224,7 @@ xfs_calc_itruncate_reservation(  		    xfs_calc_buf_res(5, 0) +  		    xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),  				     XFS_FSB_TO_B(mp, 1)) + -		    xfs_calc_buf_res(2 + XFS_IALLOC_BLOCKS(mp) + +		    xfs_calc_buf_res(2 + mp->m_ialloc_blks +  				     mp->m_in_maxlevels, 0)));  } @@ -212,6 +254,19 @@ xfs_calc_rename_reservation(  }  /* + * For removing an inode from unlinked list at first, we can modify: + *    the agi hash list and counters: sector size + *    the on disk inode before ours in the agi hash list: inode cluster size + */ +STATIC uint +xfs_calc_iunlink_remove_reservation( +	struct xfs_mount        *mp) +{ +	return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) + +	       max_t(uint, XFS_FSB_TO_B(mp, 1), mp->m_inode_cluster_size); +} + +/*   * For creating a link to an inode:   *    the parent directory inode: inode size   *    the linked inode: inode size @@ -228,6 +283,7 @@ xfs_calc_link_reservation(  	struct xfs_mount	*mp)  {  	return XFS_DQUOT_LOGRES(mp) + +		xfs_calc_iunlink_remove_reservation(mp) +  		MAX((xfs_calc_inode_res(mp, 2) +  		     xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),  				      XFS_FSB_TO_B(mp, 1))), @@ -237,6 +293,18 @@ xfs_calc_link_reservation(  }  /* + * For adding an inode to unlinked list we can modify: + *    the agi hash list: sector size + *    the unlinked inode: inode size + */ +STATIC uint +xfs_calc_iunlink_add_reservation(xfs_mount_t *mp) +{ +	return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) + +		xfs_calc_inode_res(mp, 1); +} + +/*   * For removing a directory entry we can modify:   *    the parent directory inode: inode size   *    the removed inode: inode size @@ -253,10 +321,11 @@ xfs_calc_remove_reservation(  	struct xfs_mount	*mp)  {  	return XFS_DQUOT_LOGRES(mp) + -		MAX((xfs_calc_inode_res(mp, 2) + +		xfs_calc_iunlink_add_reservation(mp) + +		MAX((xfs_calc_inode_res(mp, 1) +  		     xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),  				      XFS_FSB_TO_B(mp, 1))), -		    (xfs_calc_buf_res(5, mp->m_sb.sb_sectsize) + +		    (xfs_calc_buf_res(4, mp->m_sb.sb_sectsize) +  		     xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 2),  				      XFS_FSB_TO_B(mp, 1))));  } @@ -275,6 +344,7 @@ xfs_calc_remove_reservation(   *    the superblock for the nlink flag: sector size   *    the directory btree: (max depth + v2) * dir block size   *    the directory inode's bmap btree: (max depth + v2) * block size + *    the finobt (record modification and allocation btrees)   */  STATIC uint  xfs_calc_create_resv_modify( @@ -283,14 +353,15 @@ xfs_calc_create_resv_modify(  	return xfs_calc_inode_res(mp, 2) +  		xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +  		(uint)XFS_FSB_TO_B(mp, 1) + -		xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1)); +		xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1)) + +		xfs_calc_finobt_res(mp, 1, 1);  }  /*   * For create we can allocate some inodes giving:   *    the agi and agf of the ag getting the new inodes: 2 * sectorsize   *    the superblock for the nlink flag: sector size - *    the inode blocks allocated: XFS_IALLOC_BLOCKS * blocksize + *    the inode blocks allocated: mp->m_ialloc_blks * blocksize   *    the inode btree: max depth * blocksize   *    the allocation btrees: 2 trees * (max depth - 1) * block size   */ @@ -300,7 +371,7 @@ xfs_calc_create_resv_alloc(  {  	return xfs_calc_buf_res(2, mp->m_sb.sb_sectsize) +  		mp->m_sb.sb_sectsize + -		xfs_calc_buf_res(XFS_IALLOC_BLOCKS(mp), XFS_FSB_TO_B(mp, 1)) + +		xfs_calc_buf_res(mp->m_ialloc_blks, XFS_FSB_TO_B(mp, 1)) +  		xfs_calc_buf_res(mp->m_in_maxlevels, XFS_FSB_TO_B(mp, 1)) +  		xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),  				 XFS_FSB_TO_B(mp, 1)); @@ -321,6 +392,7 @@ __xfs_calc_create_reservation(   *    the superblock for the nlink flag: sector size   *    the inode btree: max depth * blocksize   *    the allocation btrees: 2 trees * (max depth - 1) * block size + *    the finobt (record insertion)   */  STATIC uint  xfs_calc_icreate_resv_alloc( @@ -330,7 +402,8 @@ xfs_calc_icreate_resv_alloc(  		mp->m_sb.sb_sectsize +  		xfs_calc_buf_res(mp->m_in_maxlevels, XFS_FSB_TO_B(mp, 1)) +  		xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1), -				 XFS_FSB_TO_B(mp, 1)); +				 XFS_FSB_TO_B(mp, 1)) + +		xfs_calc_finobt_res(mp, 0, 0);  }  STATIC uint @@ -351,6 +424,20 @@ xfs_calc_create_reservation(  } +STATIC uint +xfs_calc_create_tmpfile_reservation( +	struct xfs_mount        *mp) +{ +	uint	res = XFS_DQUOT_LOGRES(mp); + +	if (xfs_sb_version_hascrc(&mp->m_sb)) +		res += xfs_calc_icreate_resv_alloc(mp); +	else +		res += xfs_calc_create_resv_alloc(mp); + +	return res + xfs_calc_iunlink_add_reservation(mp); +} +  /*   * Making a new directory is the same as creating a new file.   */ @@ -384,6 +471,7 @@ xfs_calc_symlink_reservation(   *    the on disk inode before ours in the agi hash list: inode cluster size   *    the inode btree: max depth * blocksize   *    the allocation btrees: 2 trees * (max depth - 1) * block size + *    the finobt (record insertion, removal or modification)   */  STATIC uint  xfs_calc_ifree_reservation( @@ -391,15 +479,15 @@ xfs_calc_ifree_reservation(  {  	return XFS_DQUOT_LOGRES(mp) +  		xfs_calc_inode_res(mp, 1) + -		xfs_calc_buf_res(2, mp->m_sb.sb_sectsize) + +		xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +  		xfs_calc_buf_res(1, XFS_FSB_TO_B(mp, 1)) + -		MAX((__uint16_t)XFS_FSB_TO_B(mp, 1), -		    XFS_INODE_CLUSTER_SIZE(mp)) + +		xfs_calc_iunlink_remove_reservation(mp) +  		xfs_calc_buf_res(1, 0) + -		xfs_calc_buf_res(2 + XFS_IALLOC_BLOCKS(mp) + +		xfs_calc_buf_res(2 + mp->m_ialloc_blks +  				 mp->m_in_maxlevels, 0) +  		xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1), -				 XFS_FSB_TO_B(mp, 1)); +				 XFS_FSB_TO_B(mp, 1)) + +		xfs_calc_finobt_res(mp, 0, 1);  }  /* @@ -522,7 +610,7 @@ xfs_calc_addafork_reservation(  	return XFS_DQUOT_LOGRES(mp) +  		xfs_calc_inode_res(mp, 1) +  		xfs_calc_buf_res(2, mp->m_sb.sb_sectsize) + -		xfs_calc_buf_res(1, mp->m_dirblksize) + +		xfs_calc_buf_res(1, mp->m_dir_geo->blksize) +  		xfs_calc_buf_res(XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1,  				 XFS_FSB_TO_B(mp, 1)) +  		xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1), @@ -653,15 +741,14 @@ xfs_calc_qm_setqlim_reservation(  /*   * Allocating quota on disk if needed. - *	the write transaction log space: M_RES(mp)->tr_write.tr_logres + *	the write transaction log space for quota file extent allocation   *	the unit of quota allocation: one system block size   */  STATIC uint  xfs_calc_qm_dqalloc_reservation(  	struct xfs_mount	*mp)  { -	ASSERT(M_RES(mp)->tr_write.tr_logres); -	return M_RES(mp)->tr_write.tr_logres + +	return xfs_calc_write_reservation(mp) +  		xfs_calc_buf_res(1,  			XFS_FSB_TO_B(mp, XFS_DQUOT_CLUSTER_SIZE_FSB) - 1);  } @@ -738,6 +825,11 @@ xfs_trans_resv_calc(  	resp->tr_create.tr_logcount = XFS_CREATE_LOG_COUNT;  	resp->tr_create.tr_logflags |= XFS_TRANS_PERM_LOG_RES; +	resp->tr_create_tmpfile.tr_logres = +			xfs_calc_create_tmpfile_reservation(mp); +	resp->tr_create_tmpfile.tr_logcount = XFS_CREATE_TMPFILE_LOG_COUNT; +	resp->tr_create_tmpfile.tr_logflags |= XFS_TRANS_PERM_LOG_RES; +  	resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);  	resp->tr_mkdir.tr_logcount = XFS_MKDIR_LOG_COUNT;  	resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES; @@ -793,7 +885,6 @@ xfs_trans_resv_calc(  	/* The following transaction are logged in logical format */  	resp->tr_ichange.tr_logres = xfs_calc_ichange_reservation(mp);  	resp->tr_growdata.tr_logres = xfs_calc_growdata_reservation(mp); -	resp->tr_swrite.tr_logres = xfs_calc_swrite_reservation(mp);  	resp->tr_fsyncts.tr_logres = xfs_calc_swrite_reservation(mp);  	resp->tr_writeid.tr_logres = xfs_calc_writeid_reservation(mp);  	resp->tr_attrsetrt.tr_logres = xfs_calc_attrsetrt_reservation(mp);  | 
