⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xfs_alloc_btree.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write the Free Software Foundation, * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */#include "xfs.h"#include "xfs_fs.h"#include "xfs_types.h"#include "xfs_bit.h"#include "xfs_log.h"#include "xfs_inum.h"#include "xfs_trans.h"#include "xfs_sb.h"#include "xfs_ag.h"#include "xfs_dir2.h"#include "xfs_dmapi.h"#include "xfs_mount.h"#include "xfs_bmap_btree.h"#include "xfs_alloc_btree.h"#include "xfs_ialloc_btree.h"#include "xfs_dir2_sf.h"#include "xfs_attr_sf.h"#include "xfs_dinode.h"#include "xfs_inode.h"#include "xfs_btree.h"#include "xfs_ialloc.h"#include "xfs_alloc.h"#include "xfs_error.h"/* * Prototypes for internal functions. */STATIC void xfs_alloc_log_block(xfs_trans_t *, xfs_buf_t *, int);STATIC void xfs_alloc_log_keys(xfs_btree_cur_t *, xfs_buf_t *, int, int);STATIC void xfs_alloc_log_ptrs(xfs_btree_cur_t *, xfs_buf_t *, int, int);STATIC void xfs_alloc_log_recs(xfs_btree_cur_t *, xfs_buf_t *, int, int);STATIC int xfs_alloc_lshift(xfs_btree_cur_t *, int, int *);STATIC int xfs_alloc_newroot(xfs_btree_cur_t *, int *);STATIC int xfs_alloc_rshift(xfs_btree_cur_t *, int, int *);STATIC int xfs_alloc_split(xfs_btree_cur_t *, int, xfs_agblock_t *,		xfs_alloc_key_t *, xfs_btree_cur_t **, int *);STATIC int xfs_alloc_updkey(xfs_btree_cur_t *, xfs_alloc_key_t *, int);/* * Internal functions. *//* * Single level of the xfs_alloc_delete record deletion routine. * Delete record pointed to by cur/level. * Remove the record from its block then rebalance the tree. * Return 0 for error, 1 for done, 2 to go on to the next level. */STATIC int				/* error */xfs_alloc_delrec(	xfs_btree_cur_t		*cur,	/* btree cursor */	int			level,	/* level removing record from */	int			*stat)	/* fail/done/go-on */{	xfs_agf_t		*agf;	/* allocation group freelist header */	xfs_alloc_block_t	*block;	/* btree block record/key lives in */	xfs_agblock_t		bno;	/* btree block number */	xfs_buf_t		*bp;	/* buffer for block */	int			error;	/* error return value */	int			i;	/* loop index */	xfs_alloc_key_t		key;	/* kp points here if block is level 0 */	xfs_agblock_t		lbno;	/* left block's block number */	xfs_buf_t		*lbp;	/* left block's buffer pointer */	xfs_alloc_block_t	*left;	/* left btree block */	xfs_alloc_key_t		*lkp=NULL;	/* left block key pointer */	xfs_alloc_ptr_t		*lpp=NULL;	/* left block address pointer */	int			lrecs=0;	/* number of records in left block */	xfs_alloc_rec_t		*lrp;	/* left block record pointer */	xfs_mount_t		*mp;	/* mount structure */	int			ptr;	/* index in btree block for this rec */	xfs_agblock_t		rbno;	/* right block's block number */	xfs_buf_t		*rbp;	/* right block's buffer pointer */	xfs_alloc_block_t	*right;	/* right btree block */	xfs_alloc_key_t		*rkp;	/* right block key pointer */	xfs_alloc_ptr_t		*rpp;	/* right block address pointer */	int			rrecs=0;	/* number of records in right block */	int			numrecs;	xfs_alloc_rec_t		*rrp;	/* right block record pointer */	xfs_btree_cur_t		*tcur;	/* temporary btree cursor */	/*	 * Get the index of the entry being deleted, check for nothing there.	 */	ptr = cur->bc_ptrs[level];	if (ptr == 0) {		*stat = 0;		return 0;	}	/*	 * Get the buffer & block containing the record or key/ptr.	 */	bp = cur->bc_bufs[level];	block = XFS_BUF_TO_ALLOC_BLOCK(bp);#ifdef DEBUG	if ((error = xfs_btree_check_sblock(cur, block, level, bp)))		return error;#endif	/*	 * Fail if we're off the end of the block.	 */	numrecs = be16_to_cpu(block->bb_numrecs);	if (ptr > numrecs) {		*stat = 0;		return 0;	}	XFS_STATS_INC(xs_abt_delrec);	/*	 * It's a nonleaf.  Excise the key and ptr being deleted, by	 * sliding the entries past them down one.	 * Log the changed areas of the block.	 */	if (level > 0) {		lkp = XFS_ALLOC_KEY_ADDR(block, 1, cur);		lpp = XFS_ALLOC_PTR_ADDR(block, 1, cur);#ifdef DEBUG		for (i = ptr; i < numrecs; i++) {			if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(lpp[i]), level)))				return error;		}#endif		if (ptr < numrecs) {			memmove(&lkp[ptr - 1], &lkp[ptr],				(numrecs - ptr) * sizeof(*lkp));			memmove(&lpp[ptr - 1], &lpp[ptr],				(numrecs - ptr) * sizeof(*lpp));			xfs_alloc_log_ptrs(cur, bp, ptr, numrecs - 1);			xfs_alloc_log_keys(cur, bp, ptr, numrecs - 1);		}	}	/*	 * It's a leaf.  Excise the record being deleted, by sliding the	 * entries past it down one.  Log the changed areas of the block.	 */	else {		lrp = XFS_ALLOC_REC_ADDR(block, 1, cur);		if (ptr < numrecs) {			memmove(&lrp[ptr - 1], &lrp[ptr],				(numrecs - ptr) * sizeof(*lrp));			xfs_alloc_log_recs(cur, bp, ptr, numrecs - 1);		}		/*		 * If it's the first record in the block, we'll need a key		 * structure to pass up to the next level (updkey).		 */		if (ptr == 1) {			key.ar_startblock = lrp->ar_startblock;			key.ar_blockcount = lrp->ar_blockcount;			lkp = &key;		}	}	/*	 * Decrement and log the number of entries in the block.	 */	numrecs--;	block->bb_numrecs = cpu_to_be16(numrecs);	xfs_alloc_log_block(cur->bc_tp, bp, XFS_BB_NUMRECS);	/*	 * See if the longest free extent in the allocation group was	 * changed by this operation.  True if it's the by-size btree, and	 * this is the leaf level, and there is no right sibling block,	 * and this was the last record.	 */	agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);	mp = cur->bc_mp;	if (level == 0 &&	    cur->bc_btnum == XFS_BTNUM_CNT &&	    be32_to_cpu(block->bb_rightsib) == NULLAGBLOCK &&	    ptr > numrecs) {		ASSERT(ptr == numrecs + 1);		/*		 * There are still records in the block.  Grab the size		 * from the last one.		 */		if (numrecs) {			rrp = XFS_ALLOC_REC_ADDR(block, numrecs, cur);			agf->agf_longest = rrp->ar_blockcount;		}		/*		 * No free extents left.		 */		else			agf->agf_longest = 0;		mp->m_perag[be32_to_cpu(agf->agf_seqno)].pagf_longest =			be32_to_cpu(agf->agf_longest);		xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp,			XFS_AGF_LONGEST);	}	/*	 * Is this the root level?  If so, we're almost done.	 */	if (level == cur->bc_nlevels - 1) {		/*		 * If this is the root level,		 * and there's only one entry left,		 * and it's NOT the leaf level,		 * then we can get rid of this level.		 */		if (numrecs == 1 && level > 0) {			/*			 * lpp is still set to the first pointer in the block.			 * Make it the new root of the btree.			 */			bno = be32_to_cpu(agf->agf_roots[cur->bc_btnum]);			agf->agf_roots[cur->bc_btnum] = *lpp;			be32_add(&agf->agf_levels[cur->bc_btnum], -1);			mp->m_perag[be32_to_cpu(agf->agf_seqno)].pagf_levels[cur->bc_btnum]--;			/*			 * Put this buffer/block on the ag's freelist.			 */			error = xfs_alloc_put_freelist(cur->bc_tp,					cur->bc_private.a.agbp, NULL, bno, 1);			if (error)				return error;			/*			 * Since blocks move to the free list without the			 * coordination used in xfs_bmap_finish, we can't allow			 * block to be available for reallocation and			 * non-transaction writing (user data) until we know			 * that the transaction that moved it to the free list			 * is permanently on disk. We track the blocks by			 * declaring these blocks as "busy"; the busy list is			 * maintained on a per-ag basis and each transaction			 * records which entries should be removed when the			 * iclog commits to disk. If a busy block is			 * allocated, the iclog is pushed up to the LSN			 * that freed the block.			 */			xfs_alloc_mark_busy(cur->bc_tp,				be32_to_cpu(agf->agf_seqno), bno, 1);			xfs_trans_agbtree_delta(cur->bc_tp, -1);			xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp,				XFS_AGF_ROOTS | XFS_AGF_LEVELS);			/*			 * Update the cursor so there's one fewer level.			 */			xfs_btree_setbuf(cur, level, NULL);			cur->bc_nlevels--;		} else if (level > 0 &&			   (error = xfs_alloc_decrement(cur, level, &i)))			return error;		*stat = 1;		return 0;	}	/*	 * If we deleted the leftmost entry in the block, update the	 * key values above us in the tree.	 */	if (ptr == 1 && (error = xfs_alloc_updkey(cur, lkp, level + 1)))		return error;	/*	 * If the number of records remaining in the block is at least	 * the minimum, we're done.	 */	if (numrecs >= XFS_ALLOC_BLOCK_MINRECS(level, cur)) {		if (level > 0 && (error = xfs_alloc_decrement(cur, level, &i)))			return error;		*stat = 1;		return 0;	}	/*	 * Otherwise, we have to move some records around to keep the	 * tree balanced.  Look at the left and right sibling blocks to	 * see if we can re-balance by moving only one record.	 */	rbno = be32_to_cpu(block->bb_rightsib);	lbno = be32_to_cpu(block->bb_leftsib);	bno = NULLAGBLOCK;	ASSERT(rbno != NULLAGBLOCK || lbno != NULLAGBLOCK);	/*	 * Duplicate the cursor so our btree manipulations here won't	 * disrupt the next level up.	 */	if ((error = xfs_btree_dup_cursor(cur, &tcur)))		return error;	/*	 * If there's a right sibling, see if it's ok to shift an entry	 * out of it.	 */	if (rbno != NULLAGBLOCK) {		/*		 * Move the temp cursor to the last entry in the next block.		 * Actually any entry but the first would suffice.		 */		i = xfs_btree_lastrec(tcur, level);		XFS_WANT_CORRUPTED_GOTO(i == 1, error0);		if ((error = xfs_alloc_increment(tcur, level, &i)))			goto error0;		XFS_WANT_CORRUPTED_GOTO(i == 1, error0);		i = xfs_btree_lastrec(tcur, level);		XFS_WANT_CORRUPTED_GOTO(i == 1, error0);		/*		 * Grab a pointer to the block.		 */		rbp = tcur->bc_bufs[level];		right = XFS_BUF_TO_ALLOC_BLOCK(rbp);#ifdef DEBUG		if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))			goto error0;#endif		/*		 * Grab the current block number, for future use.		 */		bno = be32_to_cpu(right->bb_leftsib);		/*		 * If right block is full enough so that removing one entry		 * won't make it too empty, and left-shifting an entry out		 * of right to us works, we're done.		 */		if (be16_to_cpu(right->bb_numrecs) - 1 >=		     XFS_ALLOC_BLOCK_MINRECS(level, cur)) {			if ((error = xfs_alloc_lshift(tcur, level, &i)))				goto error0;			if (i) {				ASSERT(be16_to_cpu(block->bb_numrecs) >=				       XFS_ALLOC_BLOCK_MINRECS(level, cur));				xfs_btree_del_cursor(tcur,						     XFS_BTREE_NOERROR);				if (level > 0 &&				    (error = xfs_alloc_decrement(cur, level,					    &i)))					return error;				*stat = 1;				return 0;			}		}		/*		 * Otherwise, grab the number of records in right for		 * future reference, and fix up the temp cursor to point		 * to our block again (last record).		 */		rrecs = be16_to_cpu(right->bb_numrecs);		if (lbno != NULLAGBLOCK) {			i = xfs_btree_firstrec(tcur, level);			XFS_WANT_CORRUPTED_GOTO(i == 1, error0);			if ((error = xfs_alloc_decrement(tcur, level, &i)))				goto error0;			XFS_WANT_CORRUPTED_GOTO(i == 1, error0);		}	}	/*	 * If there's a left sibling, see if it's ok to shift an entry	 * out of it.	 */	if (lbno != NULLAGBLOCK) {		/*		 * Move the temp cursor to the first entry in the		 * previous block.		 */		i = xfs_btree_firstrec(tcur, level);		XFS_WANT_CORRUPTED_GOTO(i == 1, error0);		if ((error = xfs_alloc_decrement(tcur, level, &i)))			goto error0;		XFS_WANT_CORRUPTED_GOTO(i == 1, error0);		xfs_btree_firstrec(tcur, level);		/*		 * Grab a pointer to the block.		 */		lbp = tcur->bc_bufs[level];		left = XFS_BUF_TO_ALLOC_BLOCK(lbp);#ifdef DEBUG		if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))			goto error0;#endif		/*		 * Grab the current block number, for future use.		 */		bno = be32_to_cpu(left->bb_rightsib);		/*		 * If left block is full enough so that removing one entry		 * won't make it too empty, and right-shifting an entry out		 * of left to us works, we're done.		 */		if (be16_to_cpu(left->bb_numrecs) - 1 >=		     XFS_ALLOC_BLOCK_MINRECS(level, cur)) {			if ((error = xfs_alloc_rshift(tcur, level, &i)))				goto error0;			if (i) {				ASSERT(be16_to_cpu(block->bb_numrecs) >=				       XFS_ALLOC_BLOCK_MINRECS(level, cur));				xfs_btree_del_cursor(tcur,						     XFS_BTREE_NOERROR);				if (level == 0)					cur->bc_ptrs[0]++;				*stat = 1;				return 0;			}		}		/*		 * Otherwise, grab the number of records in right for		 * future reference.		 */		lrecs = be16_to_cpu(left->bb_numrecs);	}	/*	 * Delete the temp cursor, we're done with it.	 */	xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);	/*	 * If here, we need to do a join to keep the tree balanced.	 */	ASSERT(bno != NULLAGBLOCK);	/*	 * See if we can join with the left neighbor block.	 */	if (lbno != NULLAGBLOCK &&	    lrecs + numrecs <= XFS_ALLOC_BLOCK_MAXRECS(level, cur)) {		/*		 * Set "right" to be the starting block,		 * "left" to be the left neighbor.		 */		rbno = bno;		right = block;		rrecs = be16_to_cpu(right->bb_numrecs);		rbp = bp;		if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,				cur->bc_private.a.agno, lbno, 0, &lbp,				XFS_ALLOC_BTREE_REF)))			return error;		left = XFS_BUF_TO_ALLOC_BLOCK(lbp);		lrecs = be16_to_cpu(left->bb_numrecs);		if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))			return error;

⌨️ 快捷键说明

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