diff options
Diffstat (limited to 'fs/xfs/xfs_ialloc.c')
| -rw-r--r-- | fs/xfs/xfs_ialloc.c | 129 | 
1 files changed, 82 insertions, 47 deletions
diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c index 0ce56d6a492..348ac30174c 100644 --- a/fs/xfs/xfs_ialloc.c +++ b/fs/xfs/xfs_ialloc.c @@ -40,6 +40,7 @@  #include "xfs_rtalloc.h"  #include "xfs_error.h"  #include "xfs_bmap.h" +#include "xfs_imap.h"  /* @@ -1196,36 +1197,28 @@ error0:  }  /* - * Return the location of the inode in bno/off, for mapping it into a buffer. + * Return the location of the inode in imap, for mapping it into a buffer.   */ -/*ARGSUSED*/  int -xfs_dilocate( -	xfs_mount_t	*mp,	/* file system mount structure */ -	xfs_trans_t	*tp,	/* transaction pointer */ +xfs_imap( +	xfs_mount_t	 *mp,	/* file system mount structure */ +	xfs_trans_t	 *tp,	/* transaction pointer */  	xfs_ino_t	ino,	/* inode to locate */ -	xfs_fsblock_t	*bno,	/* output: block containing inode */ -	int		*len,	/* output: num blocks in inode cluster */ -	int		*off,	/* output: index in block of inode */ -	uint		flags)	/* flags concerning inode lookup */ +	struct xfs_imap	*imap,	/* location map structure */ +	uint		flags)	/* flags for inode btree lookup */  {  	xfs_agblock_t	agbno;	/* block number of inode in the alloc group */ -	xfs_buf_t	*agbp;	/* agi buffer */  	xfs_agino_t	agino;	/* inode number within alloc group */  	xfs_agnumber_t	agno;	/* allocation group number */  	int		blks_per_cluster; /* num blocks per inode cluster */  	xfs_agblock_t	chunk_agbno;	/* first block in inode chunk */ -	xfs_agino_t	chunk_agino;	/* first agino in inode chunk */ -	__int32_t	chunk_cnt;	/* count of free inodes in chunk */ -	xfs_inofree_t	chunk_free;	/* mask of free inodes in chunk */  	xfs_agblock_t	cluster_agbno;	/* first block in inode cluster */ -	xfs_btree_cur_t	*cur;	/* inode btree cursor */  	int		error;	/* error code */ -	int		i;	/* temp state */  	int		offset;	/* index of inode in its buffer */  	int		offset_agbno;	/* blks from chunk start to inode */  	ASSERT(ino != NULLFSINO); +  	/*  	 * Split up the inode number into its parts.  	 */ @@ -1240,20 +1233,20 @@ xfs_dilocate(  			return XFS_ERROR(EINVAL);  		if (agno >= mp->m_sb.sb_agcount) {  			xfs_fs_cmn_err(CE_ALERT, mp, -					"xfs_dilocate: agno (%d) >= " +					"xfs_imap: agno (%d) >= "  					"mp->m_sb.sb_agcount (%d)",  					agno,  mp->m_sb.sb_agcount);  		}  		if (agbno >= mp->m_sb.sb_agblocks) {  			xfs_fs_cmn_err(CE_ALERT, mp, -					"xfs_dilocate: agbno (0x%llx) >= " +					"xfs_imap: agbno (0x%llx) >= "  					"mp->m_sb.sb_agblocks (0x%lx)",  					(unsigned long long) agbno,  					(unsigned long) mp->m_sb.sb_agblocks);  		}  		if (ino != XFS_AGINO_TO_INO(mp, agno, agino)) {  			xfs_fs_cmn_err(CE_ALERT, mp, -					"xfs_dilocate: ino (0x%llx) != " +					"xfs_imap: ino (0x%llx) != "  					"XFS_AGINO_TO_INO(mp, agno, agino) "  					"(0x%llx)",  					ino, XFS_AGINO_TO_INO(mp, agno, agino)); @@ -1262,63 +1255,89 @@ xfs_dilocate(  #endif /* DEBUG */  		return XFS_ERROR(EINVAL);  	} -	if ((mp->m_sb.sb_blocksize >= XFS_INODE_CLUSTER_SIZE(mp))) { + +	/* +	 * If the inode cluster size is the same as the blocksize or +	 * smaller we get to the buffer by simple arithmetics. +	 */ +	if (XFS_INODE_CLUSTER_SIZE(mp) <= mp->m_sb.sb_blocksize) {  		offset = XFS_INO_TO_OFFSET(mp, ino);  		ASSERT(offset < mp->m_sb.sb_inopblock); -		*bno = XFS_AGB_TO_FSB(mp, agno, agbno); -		*off = offset; -		*len = 1; + +		imap->im_blkno = XFS_AGB_TO_DADDR(mp, agno, agbno); +		imap->im_len = XFS_FSB_TO_BB(mp, 1); +		imap->im_boffset = (ushort)(offset << mp->m_sb.sb_inodelog);  		return 0;  	} +  	blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_blocklog; -	if (*bno != NULLFSBLOCK) { + +	/* +	 * If we get a block number passed from bulkstat we can use it to +	 * find the buffer easily. +	 */ +	if (imap->im_blkno) {  		offset = XFS_INO_TO_OFFSET(mp, ino);  		ASSERT(offset < mp->m_sb.sb_inopblock); -		cluster_agbno = XFS_FSB_TO_AGBNO(mp, *bno); -		*off = ((agbno - cluster_agbno) * mp->m_sb.sb_inopblock) + -			offset; -		*len = blks_per_cluster; + +		cluster_agbno = XFS_DADDR_TO_AGBNO(mp, imap->im_blkno); +		offset += (agbno - cluster_agbno) * mp->m_sb.sb_inopblock; + +		imap->im_len = XFS_FSB_TO_BB(mp, blks_per_cluster); +		imap->im_boffset = (ushort)(offset << mp->m_sb.sb_inodelog);  		return 0;  	} + +	/* +	 * If the inode chunks are aligned then use simple maths to +	 * find the location. Otherwise we have to do a btree +	 * lookup to find the location. +	 */  	if (mp->m_inoalign_mask) {  		offset_agbno = agbno & mp->m_inoalign_mask;  		chunk_agbno = agbno - offset_agbno;  	} else { +		xfs_btree_cur_t	*cur;	/* inode btree cursor */ +		xfs_agino_t	chunk_agino; /* first agino in inode chunk */ +		__int32_t	chunk_cnt; /* count of free inodes in chunk */ +		xfs_inofree_t	chunk_free; /* mask of free inodes in chunk */ +		xfs_buf_t	*agbp;	/* agi buffer */ +		int		i;	/* temp state */ +  		down_read(&mp->m_peraglock);  		error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);  		up_read(&mp->m_peraglock);  		if (error) { -#ifdef DEBUG -			xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: " +			xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "  					"xfs_ialloc_read_agi() returned "  					"error %d, agno %d",  					error, agno); -#endif /* DEBUG */  			return error;  		} +  		cur = xfs_inobt_init_cursor(mp, tp, agbp, agno); -		if ((error = xfs_inobt_lookup_le(cur, agino, 0, 0, &i))) { -#ifdef DEBUG -			xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: " +		error = xfs_inobt_lookup_le(cur, agino, 0, 0, &i); +		if (error) { +			xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "  					"xfs_inobt_lookup_le() failed"); -#endif /* DEBUG */  			goto error0;  		} -		if ((error = xfs_inobt_get_rec(cur, &chunk_agino, &chunk_cnt, -				&chunk_free, &i))) { -#ifdef DEBUG -			xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: " + +		error = xfs_inobt_get_rec(cur, &chunk_agino, &chunk_cnt, +				&chunk_free, &i); +		if (error) { +			xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "  					"xfs_inobt_get_rec() failed"); -#endif /* DEBUG */  			goto error0;  		}  		if (i == 0) {  #ifdef DEBUG -			xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: " +			xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "  					"xfs_inobt_get_rec() failed");  #endif /* DEBUG */  			error = XFS_ERROR(EINVAL);  		} + error0:  		xfs_trans_brelse(tp, agbp);  		xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);  		if (error) @@ -1326,19 +1345,35 @@ xfs_dilocate(  		chunk_agbno = XFS_AGINO_TO_AGBNO(mp, chunk_agino);  		offset_agbno = agbno - chunk_agbno;  	} +  	ASSERT(agbno >= chunk_agbno);  	cluster_agbno = chunk_agbno +  		((offset_agbno / blks_per_cluster) * blks_per_cluster);  	offset = ((agbno - cluster_agbno) * mp->m_sb.sb_inopblock) +  		XFS_INO_TO_OFFSET(mp, ino); -	*bno = XFS_AGB_TO_FSB(mp, agno, cluster_agbno); -	*off = offset; -	*len = blks_per_cluster; + +	imap->im_blkno = XFS_AGB_TO_DADDR(mp, agno, cluster_agbno); +	imap->im_len = XFS_FSB_TO_BB(mp, blks_per_cluster); +	imap->im_boffset = (ushort)(offset << mp->m_sb.sb_inodelog); + +	/* +	 * If the inode number maps to a block outside the bounds +	 * of the file system then return NULL rather than calling +	 * read_buf and panicing when we get an error from the +	 * driver. +	 */ +	if ((imap->im_blkno + imap->im_len) > +	    XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)) { +		xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: " +			"(imap->im_blkno (0x%llx) + imap->im_len (0x%llx)) > " +			" XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks) (0x%llx)", +			(unsigned long long) imap->im_blkno, +			(unsigned long long) imap->im_len, +			XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)); +		return XFS_ERROR(EINVAL); +	} +  	return 0; -error0: -	xfs_trans_brelse(tp, agbp); -	xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); -	return error;  }  /*  |