diff options
| author | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2011-05-10 20:52:07 +0200 | 
|---|---|---|
| committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2011-05-10 22:50:41 +0200 | 
| commit | 020abf03cd659388f94cb328e1e1df0656e0d7ff (patch) | |
| tree | 40d05011708ad1b4a05928d167eb120420581aa6 /fs/xfs/xfs_alloc.c | |
| parent | 0ff8fbc61727c926883eec381fbd3d32d1fab504 (diff) | |
| parent | 693d92a1bbc9e42681c42ed190bd42b636ca876f (diff) | |
Merge tag 'v2.6.39-rc7'
in order to pull in changes in drivers/media/dvb/firewire/ and
sound/firewire/.
Diffstat (limited to 'fs/xfs/xfs_alloc.c')
| -rw-r--r-- | fs/xfs/xfs_alloc.c | 545 | 
1 files changed, 250 insertions, 295 deletions
diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c index 112abc439ca..27d64d752ea 100644 --- a/fs/xfs/xfs_alloc.c +++ b/fs/xfs/xfs_alloc.c @@ -41,10 +41,6 @@  #define	XFSA_FIXUP_BNO_OK	1  #define	XFSA_FIXUP_CNT_OK	2 -static int -xfs_alloc_busy_search(struct xfs_mount *mp, xfs_agnumber_t agno, -		    xfs_agblock_t bno, xfs_extlen_t len); -  /*   * Prototypes for per-ag allocation routines   */ @@ -94,7 +90,7 @@ xfs_alloc_lookup_ge(   * Lookup the first record less than or equal to [bno, len]   * in the btree given by cur.   */ -STATIC int				/* error */ +int					/* error */  xfs_alloc_lookup_le(  	struct xfs_btree_cur	*cur,	/* btree cursor */  	xfs_agblock_t		bno,	/* starting block of extent */ @@ -127,7 +123,7 @@ xfs_alloc_update(  /*   * Get the data from the pointed-to record.   */ -STATIC int				/* error */ +int					/* error */  xfs_alloc_get_rec(  	struct xfs_btree_cur	*cur,	/* btree cursor */  	xfs_agblock_t		*bno,	/* output: starting block of extent */ @@ -151,10 +147,9 @@ xfs_alloc_get_rec(   */  STATIC void  xfs_alloc_compute_aligned( +	xfs_alloc_arg_t	*args,		/* allocation argument structure */  	xfs_agblock_t	foundbno,	/* starting block in found extent */  	xfs_extlen_t	foundlen,	/* length in found extent */ -	xfs_extlen_t	alignment,	/* alignment for allocation */ -	xfs_extlen_t	minlen,		/* minimum length for allocation */  	xfs_agblock_t	*resbno,	/* result block number */  	xfs_extlen_t	*reslen)	/* result length */  { @@ -162,8 +157,8 @@ xfs_alloc_compute_aligned(  	xfs_extlen_t	diff;  	xfs_extlen_t	len; -	if (alignment > 1 && foundlen >= minlen) { -		bno = roundup(foundbno, alignment); +	if (args->alignment > 1 && foundlen >= args->minlen) { +		bno = roundup(foundbno, args->alignment);  		diff = bno - foundbno;  		len = diff >= foundlen ? 0 : foundlen - diff;  	} else { @@ -468,6 +463,27 @@ xfs_alloc_read_agfl(  	return 0;  } +STATIC int +xfs_alloc_update_counters( +	struct xfs_trans	*tp, +	struct xfs_perag	*pag, +	struct xfs_buf		*agbp, +	long			len) +{ +	struct xfs_agf		*agf = XFS_BUF_TO_AGF(agbp); + +	pag->pagf_freeblks += len; +	be32_add_cpu(&agf->agf_freeblks, len); + +	xfs_trans_agblocks_delta(tp, len); +	if (unlikely(be32_to_cpu(agf->agf_freeblks) > +		     be32_to_cpu(agf->agf_length))) +		return EFSCORRUPTED; + +	xfs_alloc_log_agf(tp, agbp, XFS_AGF_FREEBLKS); +	return 0; +} +  /*   * Allocation group level functions.   */ @@ -509,49 +525,44 @@ xfs_alloc_ag_vextent(  		ASSERT(0);  		/* NOTREACHED */  	} -	if (error) + +	if (error || args->agbno == NULLAGBLOCK)  		return error; -	/* -	 * If the allocation worked, need to change the agf structure -	 * (and log it), and the superblock. -	 */ -	if (args->agbno != NULLAGBLOCK) { -		xfs_agf_t	*agf;	/* allocation group freelist header */ -		long		slen = (long)args->len; -		ASSERT(args->len >= args->minlen && args->len <= args->maxlen); -		ASSERT(!(args->wasfromfl) || !args->isfl); -		ASSERT(args->agbno % args->alignment == 0); -		if (!(args->wasfromfl)) { - -			agf = XFS_BUF_TO_AGF(args->agbp); -			be32_add_cpu(&agf->agf_freeblks, -(args->len)); -			xfs_trans_agblocks_delta(args->tp, -						 -((long)(args->len))); -			args->pag->pagf_freeblks -= args->len; -			ASSERT(be32_to_cpu(agf->agf_freeblks) <= -				be32_to_cpu(agf->agf_length)); -			xfs_alloc_log_agf(args->tp, args->agbp, -						XFS_AGF_FREEBLKS); -			/* -			 * Search the busylist for these blocks and mark the -			 * transaction as synchronous if blocks are found. This -			 * avoids the need to block due to a synchronous log -			 * force to ensure correct ordering as the synchronous -			 * transaction will guarantee that for us. -			 */ -			if (xfs_alloc_busy_search(args->mp, args->agno, -						args->agbno, args->len)) -				xfs_trans_set_sync(args->tp); -		} -		if (!args->isfl) -			xfs_trans_mod_sb(args->tp, -				args->wasdel ? XFS_TRANS_SB_RES_FDBLOCKS : -					XFS_TRANS_SB_FDBLOCKS, -slen); -		XFS_STATS_INC(xs_allocx); -		XFS_STATS_ADD(xs_allocb, args->len); +	ASSERT(args->len >= args->minlen); +	ASSERT(args->len <= args->maxlen); +	ASSERT(!args->wasfromfl || !args->isfl); +	ASSERT(args->agbno % args->alignment == 0); + +	if (!args->wasfromfl) { +		error = xfs_alloc_update_counters(args->tp, args->pag, +						  args->agbp, +						  -((long)(args->len))); +		if (error) +			return error; + +		/* +		 * Search the busylist for these blocks and mark the +		 * transaction as synchronous if blocks are found. This +		 * avoids the need to block due to a synchronous log +		 * force to ensure correct ordering as the synchronous +		 * transaction will guarantee that for us. +		 */ +		if (xfs_alloc_busy_search(args->mp, args->agno, +					args->agbno, args->len)) +			xfs_trans_set_sync(args->tp);  	} -	return 0; + +	if (!args->isfl) { +		xfs_trans_mod_sb(args->tp, args->wasdel ? +				 XFS_TRANS_SB_RES_FDBLOCKS : +				 XFS_TRANS_SB_FDBLOCKS, +				 -((long)(args->len))); +	} + +	XFS_STATS_INC(xs_allocx); +	XFS_STATS_ADD(xs_allocb, args->len); +	return error;  }  /* @@ -577,61 +588,58 @@ xfs_alloc_ag_vextent_exact(  	xfs_extlen_t	rlen;	/* length of returned extent */  	ASSERT(args->alignment == 1); +  	/*  	 * Allocate/initialize a cursor for the by-number freespace btree.  	 */  	bno_cur = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp, -		args->agno, XFS_BTNUM_BNO); +					  args->agno, XFS_BTNUM_BNO); +  	/*  	 * Lookup bno and minlen in the btree (minlen is irrelevant, really).  	 * Look for the closest free block <= bno, it must contain bno  	 * if any free block does.  	 */ -	if ((error = xfs_alloc_lookup_le(bno_cur, args->agbno, args->minlen, &i))) +	error = xfs_alloc_lookup_le(bno_cur, args->agbno, args->minlen, &i); +	if (error)  		goto error0; -	if (!i) { -		/* -		 * Didn't find it, return null. -		 */ -		xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR); -		args->agbno = NULLAGBLOCK; -		return 0; -	} +	if (!i) +		goto not_found; +  	/*  	 * Grab the freespace record.  	 */ -	if ((error = xfs_alloc_get_rec(bno_cur, &fbno, &flen, &i))) +	error = xfs_alloc_get_rec(bno_cur, &fbno, &flen, &i); +	if (error)  		goto error0;  	XFS_WANT_CORRUPTED_GOTO(i == 1, error0);  	ASSERT(fbno <= args->agbno);  	minend = args->agbno + args->minlen;  	maxend = args->agbno + args->maxlen;  	fend = fbno + flen; +  	/*  	 * Give up if the freespace isn't long enough for the minimum request.  	 */ -	if (fend < minend) { -		xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR); -		args->agbno = NULLAGBLOCK; -		return 0; -	} +	if (fend < minend) +		goto not_found; +  	/*  	 * End of extent will be smaller of the freespace end and the  	 * maximal requested end. -	 */ -	end = XFS_AGBLOCK_MIN(fend, maxend); -	/* +	 *  	 * Fix the length according to mod and prod if given.  	 */ +	end = XFS_AGBLOCK_MIN(fend, maxend);  	args->len = end - args->agbno;  	xfs_alloc_fix_len(args); -	if (!xfs_alloc_fix_minleft(args)) { -		xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR); -		return 0; -	} +	if (!xfs_alloc_fix_minleft(args)) +		goto not_found; +  	rlen = args->len;  	ASSERT(args->agbno + rlen <= fend);  	end = args->agbno + rlen; +  	/*  	 * We are allocating agbno for rlen [agbno .. end]  	 * Allocate/initialize a cursor for the by-size btree. @@ -640,16 +648,25 @@ xfs_alloc_ag_vextent_exact(  		args->agno, XFS_BTNUM_CNT);  	ASSERT(args->agbno + args->len <=  		be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_length)); -	if ((error = xfs_alloc_fixup_trees(cnt_cur, bno_cur, fbno, flen, -			args->agbno, args->len, XFSA_FIXUP_BNO_OK))) { +	error = xfs_alloc_fixup_trees(cnt_cur, bno_cur, fbno, flen, args->agbno, +				      args->len, XFSA_FIXUP_BNO_OK); +	if (error) {  		xfs_btree_del_cursor(cnt_cur, XFS_BTREE_ERROR);  		goto error0;  	} +  	xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR);  	xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR); -	trace_xfs_alloc_exact_done(args);  	args->wasfromfl = 0; +	trace_xfs_alloc_exact_done(args); +	return 0; + +not_found: +	/* Didn't find it, return null. */ +	xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR); +	args->agbno = NULLAGBLOCK; +	trace_xfs_alloc_exact_notfound(args);  	return 0;  error0: @@ -659,6 +676,94 @@ error0:  }  /* + * Search the btree in a given direction via the search cursor and compare + * the records found against the good extent we've already found. + */ +STATIC int +xfs_alloc_find_best_extent( +	struct xfs_alloc_arg	*args,	/* allocation argument structure */ +	struct xfs_btree_cur	**gcur,	/* good cursor */ +	struct xfs_btree_cur	**scur,	/* searching cursor */ +	xfs_agblock_t		gdiff,	/* difference for search comparison */ +	xfs_agblock_t		*sbno,	/* extent found by search */ +	xfs_extlen_t		*slen, +	xfs_extlen_t		*slena,	/* aligned length */ +	int			dir)	/* 0 = search right, 1 = search left */ +{ +	xfs_agblock_t		bno; +	xfs_agblock_t		new; +	xfs_agblock_t		sdiff; +	int			error; +	int			i; + +	/* The good extent is perfect, no need to  search. */ +	if (!gdiff) +		goto out_use_good; + +	/* +	 * Look until we find a better one, run out of space or run off the end. +	 */ +	do { +		error = xfs_alloc_get_rec(*scur, sbno, slen, &i); +		if (error) +			goto error0; +		XFS_WANT_CORRUPTED_GOTO(i == 1, error0); +		xfs_alloc_compute_aligned(args, *sbno, *slen, &bno, slena); + +		/* +		 * The good extent is closer than this one. +		 */ +		if (!dir) { +			if (bno >= args->agbno + gdiff) +				goto out_use_good; +		} else { +			if (bno <= args->agbno - gdiff) +				goto out_use_good; +		} + +		/* +		 * Same distance, compare length and pick the best. +		 */ +		if (*slena >= args->minlen) { +			args->len = XFS_EXTLEN_MIN(*slena, args->maxlen); +			xfs_alloc_fix_len(args); + +			sdiff = xfs_alloc_compute_diff(args->agbno, args->len, +						       args->alignment, *sbno, +						       *slen, &new); + +			/* +			 * Choose closer size and invalidate other cursor. +			 */ +			if (sdiff < gdiff) +				goto out_use_search; +			goto out_use_good; +		} + +		if (!dir) +			error = xfs_btree_increment(*scur, 0, &i); +		else +			error = xfs_btree_decrement(*scur, 0, &i); +		if (error) +			goto error0; +	} while (i); + +out_use_good: +	xfs_btree_del_cursor(*scur, XFS_BTREE_NOERROR); +	*scur = NULL; +	return 0; + +out_use_search: +	xfs_btree_del_cursor(*gcur, XFS_BTREE_NOERROR); +	*gcur = NULL; +	return 0; + +error0: +	/* caller invalidates cursors */ +	return error; +} + +/*   * Allocate a variable extent near bno in the allocation group agno.   * Extent's length (returned in len) will be between minlen and maxlen,   * and of the form k * prod + mod unless there's nothing that large. @@ -775,8 +880,8 @@ xfs_alloc_ag_vextent_near(  			if ((error = xfs_alloc_get_rec(cnt_cur, <bno, <len, &i)))  				goto error0;  			XFS_WANT_CORRUPTED_GOTO(i == 1, error0); -			xfs_alloc_compute_aligned(ltbno, ltlen, args->alignment, -					args->minlen, <bnoa, <lena); +			xfs_alloc_compute_aligned(args, ltbno, ltlen, +						  <bnoa, <lena);  			if (ltlena < args->minlen)  				continue;  			args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen); @@ -896,8 +1001,8 @@ xfs_alloc_ag_vextent_near(  			if ((error = xfs_alloc_get_rec(bno_cur_lt, <bno, <len, &i)))  				goto error0;  			XFS_WANT_CORRUPTED_GOTO(i == 1, error0); -			xfs_alloc_compute_aligned(ltbno, ltlen, args->alignment, -					args->minlen, <bnoa, <lena); +			xfs_alloc_compute_aligned(args, ltbno, ltlen, +						  <bnoa, <lena);  			if (ltlena >= args->minlen)  				break;  			if ((error = xfs_btree_decrement(bno_cur_lt, 0, &i))) @@ -912,8 +1017,8 @@ xfs_alloc_ag_vextent_near(  			if ((error = xfs_alloc_get_rec(bno_cur_gt, >bno, >len, &i)))  				goto error0;  			XFS_WANT_CORRUPTED_GOTO(i == 1, error0); -			xfs_alloc_compute_aligned(gtbno, gtlen, args->alignment, -					args->minlen, >bnoa, >lena); +			xfs_alloc_compute_aligned(args, gtbno, gtlen, +						  >bnoa, >lena);  			if (gtlena >= args->minlen)  				break;  			if ((error = xfs_btree_increment(bno_cur_gt, 0, &i))) @@ -925,203 +1030,45 @@ xfs_alloc_ag_vextent_near(  			}  		}  	} while (bno_cur_lt || bno_cur_gt); +  	/*  	 * Got both cursors still active, need to find better entry.  	 */  	if (bno_cur_lt && bno_cur_gt) { -		/* -		 * Left side is long enough, look for a right side entry. -		 */  		if (ltlena >= args->minlen) {  			/* -			 * Fix up the length. +			 * Left side is good, look for a right side entry.  			 */  			args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen);  			xfs_alloc_fix_len(args); -			rlen = args->len; -			ltdiff = xfs_alloc_compute_diff(args->agbno, rlen, +			ltdiff = xfs_alloc_compute_diff(args->agbno, args->len,  				args->alignment, ltbno, ltlen, <new); + +			error = xfs_alloc_find_best_extent(args, +						&bno_cur_lt, &bno_cur_gt, +						ltdiff, >bno, >len, >lena, +						0 /* search right */); +		} else { +			ASSERT(gtlena >= args->minlen); +  			/* -			 * Not perfect. -			 */ -			if (ltdiff) { -				/* -				 * Look until we find a better one, run out of -				 * space, or run off the end. -				 */ -				while (bno_cur_lt && bno_cur_gt) { -					if ((error = xfs_alloc_get_rec( -							bno_cur_gt, >bno, -							>len, &i))) -						goto error0; -					XFS_WANT_CORRUPTED_GOTO(i == 1, error0); -					xfs_alloc_compute_aligned(gtbno, gtlen, -						args->alignment, args->minlen, -						>bnoa, >lena); -					/* -					 * The left one is clearly better. -					 */ -					if (gtbnoa >= args->agbno + ltdiff) { -						xfs_btree_del_cursor( -							bno_cur_gt, -							XFS_BTREE_NOERROR); -						bno_cur_gt = NULL; -						break; -					} -					/* -					 * If we reach a big enough entry, -					 * compare the two and pick the best. -					 */ -					if (gtlena >= args->minlen) { -						args->len = -							XFS_EXTLEN_MIN(gtlena, -								args->maxlen); -						xfs_alloc_fix_len(args); -						rlen = args->len; -						gtdiff = xfs_alloc_compute_diff( -							args->agbno, rlen, -							args->alignment, -							gtbno, gtlen, >new); -						/* -						 * Right side is better. -						 */ -						if (gtdiff < ltdiff) { -							xfs_btree_del_cursor( -								bno_cur_lt, -								XFS_BTREE_NOERROR); -							bno_cur_lt = NULL; -						} -						/* -						 * Left side is better. -						 */ -						else { -							xfs_btree_del_cursor( -								bno_cur_gt, -								XFS_BTREE_NOERROR); -							bno_cur_gt = NULL; -						} -						break; -					} -					/* -					 * Fell off the right end. -					 */ -					if ((error = xfs_btree_increment( -							bno_cur_gt, 0, &i))) -						goto error0; -					if (!i) { -						xfs_btree_del_cursor( -							bno_cur_gt, -							XFS_BTREE_NOERROR); -						bno_cur_gt = NULL; -						break; -					} -				} -			} -			/* -			 * The left side is perfect, trash the right side. -			 */ -			else { -				xfs_btree_del_cursor(bno_cur_gt, -						     XFS_BTREE_NOERROR); -				bno_cur_gt = NULL; -			} -		} -		/* -		 * It's the right side that was found first, look left. -		 */ -		else { -			/* -			 * Fix up the length. +			 * Right side is good, look for a left side entry.  			 */  			args->len = XFS_EXTLEN_MIN(gtlena, args->maxlen);  			xfs_alloc_fix_len(args); -			rlen = args->len; -			gtdiff = xfs_alloc_compute_diff(args->agbno, rlen, +			gtdiff = xfs_alloc_compute_diff(args->agbno, args->len,  				args->alignment, gtbno, gtlen, >new); -			/* -			 * Right side entry isn't perfect. -			 */ -			if (gtdiff) { -				/* -				 * Look until we find a better one, run out of -				 * space, or run off the end. -				 */ -				while (bno_cur_lt && bno_cur_gt) { -					if ((error = xfs_alloc_get_rec( -							bno_cur_lt, <bno, -							<len, &i))) -						goto error0; -					XFS_WANT_CORRUPTED_GOTO(i == 1, error0); -					xfs_alloc_compute_aligned(ltbno, ltlen, -						args->alignment, args->minlen, -						<bnoa, <lena); -					/* -					 * The right one is clearly better. -					 */ -					if (ltbnoa <= args->agbno - gtdiff) { -						xfs_btree_del_cursor( -							bno_cur_lt, -							XFS_BTREE_NOERROR); -						bno_cur_lt = NULL; -						break; -					} -					/* -					 * If we reach a big enough entry, -					 * compare the two and pick the best. -					 */ -					if (ltlena >= args->minlen) { -						args->len = XFS_EXTLEN_MIN( -							ltlena, args->maxlen); -						xfs_alloc_fix_len(args); -						rlen = args->len; -						ltdiff = xfs_alloc_compute_diff( -							args->agbno, rlen, -							args->alignment, -							ltbno, ltlen, <new); -						/* -						 * Left side is better. -						 */ -						if (ltdiff < gtdiff) { -							xfs_btree_del_cursor( -								bno_cur_gt, -								XFS_BTREE_NOERROR); -							bno_cur_gt = NULL; -						} -						/* -						 * Right side is better. -						 */ -						else { -							xfs_btree_del_cursor( -								bno_cur_lt, -								XFS_BTREE_NOERROR); -							bno_cur_lt = NULL; -						} -						break; -					} -					/* -					 * Fell off the left end. -					 */ -					if ((error = xfs_btree_decrement( -							bno_cur_lt, 0, &i))) -						goto error0; -					if (!i) { -						xfs_btree_del_cursor(bno_cur_lt, -							XFS_BTREE_NOERROR); -						bno_cur_lt = NULL; -						break; -					} -				} -			} -			/* -			 * The right side is perfect, trash the left side. -			 */ -			else { -				xfs_btree_del_cursor(bno_cur_lt, -					XFS_BTREE_NOERROR); -				bno_cur_lt = NULL; -			} + +			error = xfs_alloc_find_best_extent(args, +						&bno_cur_gt, &bno_cur_lt, +						gtdiff, <bno, <len, <lena, +						1 /* search left */);  		} + +		if (error) +			goto error0;  	} +  	/*  	 * If we couldn't get anything, give up.  	 */ @@ -1130,6 +1077,7 @@ xfs_alloc_ag_vextent_near(  		args->agbno = NULLAGBLOCK;  		return 0;  	} +  	/*  	 * At this point we have selected a freespace entry, either to the  	 * left or to the right.  If it's on the right, copy all the @@ -1146,6 +1094,7 @@ xfs_alloc_ag_vextent_near(  		j = 1;  	} else  		j = 0; +  	/*  	 * Fix up the length and compute the useful address.  	 */ @@ -1248,8 +1197,7 @@ xfs_alloc_ag_vextent_size(  	 * once aligned; if not, we search left for something better.  	 * This can't happen in the second case above.  	 */ -	xfs_alloc_compute_aligned(fbno, flen, args->alignment, args->minlen, -		&rbno, &rlen); +	xfs_alloc_compute_aligned(args, fbno, flen, &rbno, &rlen);  	rlen = XFS_EXTLEN_MIN(args->maxlen, rlen);  	XFS_WANT_CORRUPTED_GOTO(rlen == 0 ||  			(rlen <= flen && rbno + rlen <= fbno + flen), error0); @@ -1274,8 +1222,8 @@ xfs_alloc_ag_vextent_size(  			XFS_WANT_CORRUPTED_GOTO(i == 1, error0);  			if (flen < bestrlen)  				break; -			xfs_alloc_compute_aligned(fbno, flen, args->alignment, -				args->minlen, &rbno, &rlen); +			xfs_alloc_compute_aligned(args, fbno, flen, +						  &rbno, &rlen);  			rlen = XFS_EXTLEN_MIN(args->maxlen, rlen);  			XFS_WANT_CORRUPTED_GOTO(rlen == 0 ||  				(rlen <= flen && rbno + rlen <= fbno + flen), @@ -1453,6 +1401,7 @@ xfs_free_ag_extent(  	xfs_mount_t	*mp;		/* mount point struct for filesystem */  	xfs_agblock_t	nbno;		/* new starting block of freespace */  	xfs_extlen_t	nlen;		/* new length of freespace */ +	xfs_perag_t	*pag;		/* per allocation group data */  	mp = tp->t_mountp;  	/* @@ -1651,30 +1600,20 @@ xfs_free_ag_extent(  	XFS_WANT_CORRUPTED_GOTO(i == 1, error0);  	xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);  	cnt_cur = NULL; +  	/*  	 * Update the freespace totals in the ag and superblock.  	 */ -	{ -		xfs_agf_t	*agf; -		xfs_perag_t	*pag;		/* per allocation group data */ - -		pag = xfs_perag_get(mp, agno); -		pag->pagf_freeblks += len; -		xfs_perag_put(pag); - -		agf = XFS_BUF_TO_AGF(agbp); -		be32_add_cpu(&agf->agf_freeblks, len); -		xfs_trans_agblocks_delta(tp, len); -		XFS_WANT_CORRUPTED_GOTO( -			be32_to_cpu(agf->agf_freeblks) <= -			be32_to_cpu(agf->agf_length), -			error0); -		xfs_alloc_log_agf(tp, agbp, XFS_AGF_FREEBLKS); -		if (!isfl) -			xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, (long)len); -		XFS_STATS_INC(xs_freex); -		XFS_STATS_ADD(xs_freeb, len); -	} +	pag = xfs_perag_get(mp, agno); +	error = xfs_alloc_update_counters(tp, pag, agbp, len); +	xfs_perag_put(pag); +	if (error) +		goto error0; + +	if (!isfl) +		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, (long)len); +	XFS_STATS_INC(xs_freex); +	XFS_STATS_ADD(xs_freeb, len);  	trace_xfs_free_extent(mp, agno, bno, len, isfl, haveleft, haveright); @@ -2456,17 +2395,33 @@ xfs_free_extent(  	memset(&args, 0, sizeof(xfs_alloc_arg_t));  	args.tp = tp;  	args.mp = tp->t_mountp; + +	/* +	 * validate that the block number is legal - the enables us to detect +	 * and handle a silent filesystem corruption rather than crashing. +	 */  	args.agno = XFS_FSB_TO_AGNO(args.mp, bno); -	ASSERT(args.agno < args.mp->m_sb.sb_agcount); +	if (args.agno >= args.mp->m_sb.sb_agcount) +		return EFSCORRUPTED; +  	args.agbno = XFS_FSB_TO_AGBNO(args.mp, bno); +	if (args.agbno >= args.mp->m_sb.sb_agblocks) +		return EFSCORRUPTED; +  	args.pag = xfs_perag_get(args.mp, args.agno); -	if ((error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING))) +	ASSERT(args.pag); + +	error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING); +	if (error)  		goto error0; -#ifdef DEBUG -	ASSERT(args.agbp != NULL); -	ASSERT((args.agbno + len) <= -		be32_to_cpu(XFS_BUF_TO_AGF(args.agbp)->agf_length)); -#endif + +	/* validate the extent size is legal now we have the agf locked */ +	if (args.agbno + len > +			be32_to_cpu(XFS_BUF_TO_AGF(args.agbp)->agf_length)) { +		error = EFSCORRUPTED; +		goto error0; +	} +  	error = xfs_free_ag_extent(tp, args.agbp, args.agno, args.agbno, len, 0);  error0:  	xfs_perag_put(args.pag); @@ -2676,7 +2631,7 @@ restart:   * will require a synchronous transaction, but it can still be   * used to distinguish between a partial or exact match.   */ -static int +int  xfs_alloc_busy_search(  	struct xfs_mount	*mp,  	xfs_agnumber_t		agno,  | 
