xfs_ialloc.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,403 行 · 第 1/3 页

C
1,403
字号
	int		error;	/* error return value */	int		i;	/* result code */	int		ilen;	/* inodes in an inode cluster */	xfs_mount_t	*mp;	/* mount structure for filesystem */	int		off;	/* offset of inode in inode chunk */	xfs_inobt_rec_t	rec;	/* btree record */	mp = tp->t_mountp;	/*	 * Break up inode number into its components.	 */	agno = XFS_INO_TO_AGNO(mp, inode);	if (agno >= mp->m_sb.sb_agcount)  {		cmn_err(CE_WARN,			"xfs_difree: agno >= mp->m_sb.sb_agcount (%d >= %d) on %s.  Returning EINVAL.",			agno, mp->m_sb.sb_agcount, mp->m_fsname);		ASSERT(0);		return XFS_ERROR(EINVAL);	}	agino = XFS_INO_TO_AGINO(mp, inode);	if (inode != XFS_AGINO_TO_INO(mp, agno, agino))  {		cmn_err(CE_WARN,			"xfs_difree: inode != XFS_AGINO_TO_INO() (%d != %d) on %s.  Returning EINVAL.",			inode, XFS_AGINO_TO_INO(mp, agno, agino), mp->m_fsname);		ASSERT(0);		return XFS_ERROR(EINVAL);	}	agbno = XFS_AGINO_TO_AGBNO(mp, agino);	if (agbno >= mp->m_sb.sb_agblocks)  {		cmn_err(CE_WARN,			"xfs_difree: agbno >= mp->m_sb.sb_agblocks (%d >= %d) on %s.  Returning EINVAL.",			agbno, mp->m_sb.sb_agblocks, mp->m_fsname);		ASSERT(0);		return XFS_ERROR(EINVAL);	}	/*	 * Get the allocation group header.	 */	down_read(&mp->m_peraglock);	error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);	up_read(&mp->m_peraglock);	if (error) {		cmn_err(CE_WARN,			"xfs_difree: xfs_ialloc_read_agi() returned an error %d on %s.  Returning error.",			error, mp->m_fsname);		return error;	}	agi = XFS_BUF_TO_AGI(agbp);	ASSERT(INT_GET(agi->agi_magicnum, ARCH_CONVERT) == XFS_AGI_MAGIC);	ASSERT(agbno < INT_GET(agi->agi_length, ARCH_CONVERT));	/*	 * Initialize the cursor.	 */	cur = xfs_btree_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_INO,		(xfs_inode_t *)0, 0);#ifdef DEBUG	if (cur->bc_nlevels == 1) {		int freecount = 0;		if ((error = xfs_inobt_lookup_ge(cur, 0, 0, 0, &i)))			goto error0;		do {			if ((error = xfs_inobt_get_rec(cur, &rec.ir_startino,					&rec.ir_freecount, &rec.ir_free, &i, ARCH_NOCONVERT)))				goto error0;			if (i) {				freecount += rec.ir_freecount;				if ((error = xfs_inobt_increment(cur, 0, &i)))					goto error0;			}		} while (i == 1);		ASSERT(freecount == INT_GET(agi->agi_freecount, ARCH_CONVERT) ||		       XFS_FORCED_SHUTDOWN(mp));	}#endif	/*	 * Look for the entry describing this inode.	 */	if ((error = xfs_inobt_lookup_le(cur, agino, 0, 0, &i))) {		cmn_err(CE_WARN,			"xfs_difree: xfs_inobt_lookup_le returned()  an error %d on %s.  Returning error.",			error, mp->m_fsname);		goto error0;	}	XFS_WANT_CORRUPTED_GOTO(i == 1, error0);	if ((error = xfs_inobt_get_rec(cur, &rec.ir_startino, &rec.ir_freecount,			&rec.ir_free, &i, ARCH_NOCONVERT))) {		cmn_err(CE_WARN,			"xfs_difree: xfs_inobt_get_rec()  returned an error %d on %s.  Returning error.",			error, mp->m_fsname);		goto error0;	}	XFS_WANT_CORRUPTED_GOTO(i == 1, error0);	/*	 * Get the offset in the inode chunk.	 */	off = agino - rec.ir_startino;	ASSERT(off >= 0 && off < XFS_INODES_PER_CHUNK);	ASSERT(!XFS_INOBT_IS_FREE(&rec, off, ARCH_NOCONVERT));	/*	 * Mark the inode free & increment the count.	 */	XFS_INOBT_SET_FREE(&rec, off, ARCH_NOCONVERT);	rec.ir_freecount++;	/*	 * When an inode cluster is free, it becomes elgible for removal	 */	if ((mp->m_flags & XFS_MOUNT_IDELETE) &&	    (rec.ir_freecount == XFS_IALLOC_INODES(mp))) {		*delete = 1;		*first_ino = XFS_AGINO_TO_INO(mp, agno, rec.ir_startino);		/*		 * Remove the inode cluster from the AGI B+Tree, adjust the		 * AGI and Superblock inode counts, and mark the disk space		 * to be freed when the transaction is committed.		 */		ilen = XFS_IALLOC_INODES(mp);		INT_MOD(agi->agi_count, ARCH_CONVERT, -ilen);		INT_MOD(agi->agi_freecount, ARCH_CONVERT, -(ilen - 1));		xfs_ialloc_log_agi(tp, agbp, XFS_AGI_COUNT | XFS_AGI_FREECOUNT);		down_read(&mp->m_peraglock);		mp->m_perag[agno].pagi_freecount -= ilen - 1;		up_read(&mp->m_peraglock);		xfs_trans_mod_sb(tp, XFS_TRANS_SB_ICOUNT, -ilen);		xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, -(ilen - 1));		if ((error = xfs_inobt_delete(cur, &i))) {			cmn_err(CE_WARN, "xfs_difree: xfs_inobt_delete returned an error %d on %s.\n",				error, mp->m_fsname);			goto error0;		}		xfs_bmap_add_free(XFS_AGB_TO_FSB(mp,				agno, XFS_INO_TO_AGBNO(mp,rec.ir_startino)),				XFS_IALLOC_BLOCKS(mp), flist, mp);	} else {		*delete = 0;		if ((error = xfs_inobt_update(cur, rec.ir_startino, rec.ir_freecount, rec.ir_free))) {			cmn_err(CE_WARN,				"xfs_difree: xfs_inobt_update()  returned an error %d on %s.  Returning error.",				error, mp->m_fsname);			goto error0;		}		/* 		 * Change the inode free counts and log the ag/sb changes.		 */		INT_MOD(agi->agi_freecount, ARCH_CONVERT, 1);		xfs_ialloc_log_agi(tp, agbp, XFS_AGI_FREECOUNT);		down_read(&mp->m_peraglock);		mp->m_perag[agno].pagi_freecount++;		up_read(&mp->m_peraglock);		xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, 1);	}#ifdef DEBUG	if (cur->bc_nlevels == 1) {		int freecount = 0;		if ((error = xfs_inobt_lookup_ge(cur, 0, 0, 0, &i)))			goto error0;		do {			if ((error = xfs_inobt_get_rec(cur,					&rec.ir_startino,					&rec.ir_freecount,					&rec.ir_free, &i,					ARCH_NOCONVERT)))				goto error0;			if (i) {				freecount += rec.ir_freecount;				if ((error = xfs_inobt_increment(cur, 0, &i)))					goto error0;			}		} while (i == 1);		ASSERT(freecount == INT_GET(agi->agi_freecount, ARCH_CONVERT) ||		       XFS_FORCED_SHUTDOWN(mp));	}#endif	xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);	return 0;error0:	xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);	return error;}/* * Return the location of the inode in bno/off, for mapping it into a buffer. *//*ARGSUSED*/intxfs_dilocate(	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 */{	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.	 */	agno = XFS_INO_TO_AGNO(mp, ino);	agino = XFS_INO_TO_AGINO(mp, ino);	agbno = XFS_AGINO_TO_AGBNO(mp, agino);	if (agno >= mp->m_sb.sb_agcount || agbno >= mp->m_sb.sb_agblocks ||	    ino != XFS_AGINO_TO_INO(mp, agno, agino)) {#ifdef DEBUG		if (agno >= mp->m_sb.sb_agcount) {			xfs_fs_cmn_err(CE_ALERT, mp,					"xfs_dilocate: 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) >= "					"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_AGINO_TO_INO(mp, agno, agino) "					"(0x%llx)",					ino, XFS_AGINO_TO_INO(mp, agno, agino));		}#endif /* DEBUG */		return XFS_ERROR(EINVAL);	}	if ((mp->m_sb.sb_blocksize >= XFS_INODE_CLUSTER_SIZE(mp)) ||	    !(flags & XFS_IMAP_LOOKUP)) {		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;		return 0;	}	blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_blocklog;	if (*bno != NULLFSBLOCK) {		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;		return 0;	}	if (mp->m_inoalign_mask) {		offset_agbno = agbno & mp->m_inoalign_mask;		chunk_agbno = agbno - offset_agbno;	} else {		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_ialloc_read_agi() returned "					"error %d, agno %d",					error, agno);#endif /* DEBUG */			return error;		}		cur = xfs_btree_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_INO,			(xfs_inode_t *)0, 0);		if ((error = xfs_inobt_lookup_le(cur, agino, 0, 0, &i))) {#ifdef DEBUG			xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: "					"xfs_inobt_lookup_le() failed");#endif /* DEBUG */			goto error0;		}		if ((error = xfs_inobt_get_rec(cur, &chunk_agino, &chunk_cnt,				&chunk_free, &i, ARCH_NOCONVERT))) {#ifdef DEBUG			xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: "					"xfs_inobt_get_rec() failed");#endif /* DEBUG */			goto error0;		}		if (i == 0) {#ifdef DEBUG			xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: "					"xfs_inobt_get_rec() failed");#endif /* DEBUG */			error = XFS_ERROR(EINVAL);		}		xfs_trans_brelse(tp, agbp);		xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);		if (error)			return error;		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;	return 0;error0:	xfs_trans_brelse(tp, agbp);	xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);	return error;}/* * Compute and fill in value of m_in_maxlevels. */voidxfs_ialloc_compute_maxlevels(	xfs_mount_t	*mp)		/* file system mount structure */{	int		level;	uint		maxblocks;	uint		maxleafents;	int		minleafrecs;	int		minnoderecs;	maxleafents = (1LL << XFS_INO_AGINO_BITS(mp)) >>		XFS_INODES_PER_CHUNK_LOG;	minleafrecs = mp->m_alloc_mnr[0];	minnoderecs = mp->m_alloc_mnr[1];	maxblocks = (maxleafents + minleafrecs - 1) / minleafrecs;	for (level = 1; maxblocks > 1; level++)		maxblocks = (maxblocks + minnoderecs - 1) / minnoderecs;	mp->m_in_maxlevels = level;}/* * Log specified fields for the ag hdr (inode section) */voidxfs_ialloc_log_agi(	xfs_trans_t	*tp,		/* transaction pointer */	xfs_buf_t	*bp,		/* allocation group header buffer */	int		fields)		/* bitmask of fields to log */{	int			first;		/* first byte number */	int			last;		/* last byte number */	static const short	offsets[] = {	/* field starting offsets */					/* keep in sync with bit definitions */		offsetof(xfs_agi_t, agi_magicnum),		offsetof(xfs_agi_t, agi_versionnum),		offsetof(xfs_agi_t, agi_seqno),		offsetof(xfs_agi_t, agi_length),		offsetof(xfs_agi_t, agi_count),		offsetof(xfs_agi_t, agi_root),		offsetof(xfs_agi_t, agi_level),		offsetof(xfs_agi_t, agi_freecount),		offsetof(xfs_agi_t, agi_newino),		offsetof(xfs_agi_t, agi_dirino),		offsetof(xfs_agi_t, agi_unlinked),		sizeof(xfs_agi_t)	};#ifdef DEBUG	xfs_agi_t		*agi;	/* allocation group header */	agi = XFS_BUF_TO_AGI(bp);	ASSERT(INT_GET(agi->agi_magicnum, ARCH_CONVERT) == XFS_AGI_MAGIC);#endif	/*	 * Compute byte offsets for the first and last fields.	 */	xfs_btree_offsets(fields, offsets, XFS_AGI_NUM_BITS, &first, &last);	/*	 * Log the allocation group inode header buffer.	 */	xfs_trans_log_buf(tp, bp, first, last);}/* * Read in the allocation group header (inode allocation section) */intxfs_ialloc_read_agi(	xfs_mount_t	*mp,		/* file system mount structure */	xfs_trans_t	*tp,		/* transaction pointer */	xfs_agnumber_t	agno,		/* allocation group number */	xfs_buf_t	**bpp)		/* allocation group hdr buf */{	xfs_agi_t	*agi;		/* allocation group header */	int		agi_ok;		/* agi is consistent */	xfs_buf_t	*bp;		/* allocation group hdr buf */	xfs_perag_t	*pag;		/* per allocation group data */	int		error;	ASSERT(agno != NULLAGNUMBER);	error = xfs_trans_read_buf(			mp, tp, mp->m_ddev_targp,			XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),			XFS_FSS_TO_BB(mp, 1), 0, &bp);	if (error)		return error;	ASSERT(bp && !XFS_BUF_GETERROR(bp));	/*	 * Validate the magic number of the agi block.	 */	agi = XFS_BUF_TO_AGI(bp);	agi_ok =		INT_GET(agi->agi_magicnum, ARCH_CONVERT) == XFS_AGI_MAGIC &&		XFS_AGI_GOOD_VERSION(			INT_GET(agi->agi_versionnum, ARCH_CONVERT));	if (unlikely(XFS_TEST_ERROR(!agi_ok, mp, XFS_ERRTAG_IALLOC_READ_AGI,			XFS_RANDOM_IALLOC_READ_AGI))) {		XFS_CORRUPTION_ERROR("xfs_ialloc_read_agi", XFS_ERRLEVEL_LOW,				     mp, agi);		xfs_trans_brelse(tp, bp);		return XFS_ERROR(EFSCORRUPTED);	}	pag = &mp->m_perag[agno];	if (!pag->pagi_init) {		pag->pagi_freecount = INT_GET(agi->agi_freecount, ARCH_CONVERT);		pag->pagi_init = 1;	} else {		/*		 * It's possible for these to be out of sync if		 * we are in the middle of a forced shutdown.		 */		ASSERT(pag->pagi_freecount ==				INT_GET(agi->agi_freecount, ARCH_CONVERT)			|| XFS_FORCED_SHUTDOWN(mp));	}#ifdef DEBUG	{		int	i;		for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++)			ASSERT(!INT_ISZERO(agi->agi_unlinked[i], ARCH_CONVERT));	}#endif	XFS_BUF_SET_VTYPE_REF(bp, B_FS_AGI, XFS_AGI_REF);	*bpp = bp;	return 0;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?