Enscript Output

extractedLnx/linux-2.5.66/fs/xfs/xfs_bmap.c_xfs_bmap_alloc.c

STATIC int				/* error */
xfs_bmap_alloc(
	xfs_bmalloca_t	*ap)		/* bmap alloc argument struct */
{
	xfs_fsblock_t	adjust;		/* adjustment to block numbers */
	xfs_alloctype_t atype=0;	/* type for allocation routines */
	int		error;		/* error return value */
	xfs_agnumber_t	fb_agno;	/* ag number of ap->firstblock */
	xfs_mount_t	*mp;		/* mount point structure */
	int		nullfb;		/* true if ap->firstblock isn't set */
	int		rt;		/* true if inode is realtime */
#ifdef __KERNEL__
	xfs_extlen_t	prod=0;		/* product factor for allocators */
	xfs_extlen_t	ralen=0;	/* realtime allocation length */
#endif

#define ISLEGAL(x,y)	\
	(rt ? \
		(x) < mp->m_sb.sb_rblocks : \
		XFS_FSB_TO_AGNO(mp, x) == XFS_FSB_TO_AGNO(mp, y) && \
		XFS_FSB_TO_AGNO(mp, x) < mp->m_sb.sb_agcount && \
		XFS_FSB_TO_AGBNO(mp, x) < mp->m_sb.sb_agblocks)

	/*
	 * Set up variables.
	 */
	mp = ap->ip->i_mount;
	nullfb = ap->firstblock == NULLFSBLOCK;
	rt = (ap->ip->i_d.di_flags & XFS_DIFLAG_REALTIME) && ap->userdata;
	fb_agno = nullfb ? NULLAGNUMBER : XFS_FSB_TO_AGNO(mp, ap->firstblock);
#ifdef __KERNEL__
	if (rt) {
		xfs_extlen_t	extsz;		/* file extent size for rt */
		xfs_fileoff_t	nexto;		/* next file offset */
		xfs_extlen_t	orig_alen;	/* original ap->alen */
		xfs_fileoff_t	orig_end;	/* original off+len */
		xfs_fileoff_t	orig_off;	/* original ap->off */
		xfs_extlen_t	mod_off;	/* modulus calculations */
		xfs_fileoff_t	prevo;		/* previous file offset */
		xfs_rtblock_t	rtx;		/* realtime extent number */
		xfs_extlen_t	temp;		/* temp for rt calculations */

		/*
		 * Set prod to match the realtime extent size.
		 */
		if (!(extsz = ap->ip->i_d.di_extsize))
			extsz = mp->m_sb.sb_rextsize;
		prod = extsz / mp->m_sb.sb_rextsize;
		orig_off = ap->off;
		orig_alen = ap->alen;
		orig_end = orig_off + orig_alen;
		/*
		 * If the file offset is unaligned vs. the extent size
		 * we need to align it.	 This will be possible unless
		 * the file was previously written with a kernel that didn't
		 * perform this alignment.
		 */
		mod_off = do_mod(orig_off, extsz);
		if (mod_off) {
			ap->alen += mod_off;
			ap->off -= mod_off;
		}
		/*
		 * Same adjustment for the end of the requested area.
		 */
		if ((temp = (ap->alen % extsz)))
			ap->alen += extsz - temp;
		/*
		 * If the previous block overlaps with this proposed allocation
		 * then move the start forward without adjusting the length.
		 */
		prevo =
			ap->prevp->br_startoff == NULLFILEOFF ?
				0 :
				(ap->prevp->br_startoff +
				 ap->prevp->br_blockcount);
		if (ap->off != orig_off && ap->off < prevo)
			ap->off = prevo;
		/*
		 * If the next block overlaps with this proposed allocation
		 * then move the start back without adjusting the length,
		 * but not before offset 0.
		 * This may of course make the start overlap previous block,
		 * and if we hit the offset 0 limit then the next block
		 * can still overlap too.
		 */
		nexto = (ap->eof || ap->gotp->br_startoff == NULLFILEOFF) ?
			NULLFILEOFF : ap->gotp->br_startoff;
		if (!ap->eof &&
		    ap->off + ap->alen != orig_end &&
		    ap->off + ap->alen > nexto)
			ap->off = nexto > ap->alen ? nexto - ap->alen : 0;
		/*
		 * If we're now overlapping the next or previous extent that
		 * means we can't fit an extsz piece in this hole.  Just move
		 * the start forward to the first legal spot and set
		 * the length so we hit the end.
		 */
		if ((ap->off != orig_off && ap->off < prevo) ||
		    (ap->off + ap->alen != orig_end &&
		     ap->off + ap->alen > nexto)) {
			ap->off = prevo;
			ap->alen = nexto - prevo;
		}
		/*
		 * If the result isn't a multiple of rtextents we need to
		 * remove blocks until it is.
		 */
		if ((temp = (ap->alen % mp->m_sb.sb_rextsize))) {
			/*
			 * We're not covering the original request, or
			 * we won't be able to once we fix the length.
			 */
			if (orig_off < ap->off ||
			    orig_end > ap->off + ap->alen ||
			    ap->alen - temp < orig_alen)
				return XFS_ERROR(EINVAL);
			/*
			 * Try to fix it by moving the start up.
			 */
			if (ap->off + temp <= orig_off) {
				ap->alen -= temp;
				ap->off += temp;
			}
			/*
			 * Try to fix it by moving the end in.
			 */
			else if (ap->off + ap->alen - temp >= orig_end)
				ap->alen -= temp;
			/*
			 * Set the start to the minimum then trim the length.
			 */
			else {
				ap->alen -= orig_off - ap->off;
				ap->off = orig_off;
				ap->alen -= ap->alen % mp->m_sb.sb_rextsize;
			}
			/*
			 * Result doesn't cover the request, fail it.
			 */
			if (orig_off < ap->off || orig_end > ap->off + ap->alen)
				return XFS_ERROR(EINVAL);
		}
		ASSERT(ap->alen % mp->m_sb.sb_rextsize == 0);
		/*
		 * If the offset & length are not perfectly aligned
		 * then kill prod, it will just get us in trouble.
		 */
		if (do_mod(ap->off, extsz) || ap->alen % extsz)
			prod = 1;
		/*
		 * Set ralen to be the actual requested length in rtextents.
		 */
		ralen = ap->alen / mp->m_sb.sb_rextsize;
		/*
		 * If the old value was close enough to MAXEXTLEN that
		 * we rounded up to it, cut it back so it's legal again.
		 * Note that if it's a really large request (bigger than
		 * MAXEXTLEN), we don't hear about that number, and can't
		 * adjust the starting point to match it.
		 */
		if (ralen * mp->m_sb.sb_rextsize >= MAXEXTLEN)
			ralen = MAXEXTLEN / mp->m_sb.sb_rextsize;
		/*
		 * If it's an allocation to an empty file at offset 0,
		 * pick an extent that will space things out in the rt area.
		 */
		if (ap->eof && ap->off == 0) {
			error = xfs_rtpick_extent(mp, ap->tp, ralen, &rtx);
			if (error)
				return error;
			ap->rval = rtx * mp->m_sb.sb_rextsize;
		} else
			ap->rval = 0;
	}
#else
	if (rt)
		ap->rval = 0;
#endif	/* __KERNEL__ */
	else if (nullfb)
		ap->rval = XFS_INO_TO_FSB(mp, ap->ip->i_ino);
	else
		ap->rval = ap->firstblock;
	/*
	 * If allocating at eof, and there's a previous real block,
	 * try to use it's last block as our starting point.
	 */
	if (ap->eof && ap->prevp->br_startoff != NULLFILEOFF &&
	    !ISNULLSTARTBLOCK(ap->prevp->br_startblock) &&
	    ISLEGAL(ap->prevp->br_startblock + ap->prevp->br_blockcount,
		    ap->prevp->br_startblock)) {
		ap->rval = ap->prevp->br_startblock + ap->prevp->br_blockcount;
		/*
		 * Adjust for the gap between prevp and us.
		 */
		adjust = ap->off -
			(ap->prevp->br_startoff + ap->prevp->br_blockcount);
		if (adjust &&
		    ISLEGAL(ap->rval + adjust, ap->prevp->br_startblock))
			ap->rval += adjust;
	}
	/*
	 * If not at eof, then compare the two neighbor blocks.
	 * Figure out whether either one gives us a good starting point,
	 * and pick the better one.
	 */
	else if (!ap->eof) {
		xfs_fsblock_t	gotbno;		/* right side block number */
		xfs_fsblock_t	gotdiff=0;	/* right side difference */
		xfs_fsblock_t	prevbno;	/* left side block number */
		xfs_fsblock_t	prevdiff=0;	/* left side difference */

		/*
		 * If there's a previous (left) block, select a requested
		 * start block based on it.
		 */
		if (ap->prevp->br_startoff != NULLFILEOFF &&
		    !ISNULLSTARTBLOCK(ap->prevp->br_startblock) &&
		    (prevbno = ap->prevp->br_startblock +
			       ap->prevp->br_blockcount) &&
		    ISLEGAL(prevbno, ap->prevp->br_startblock)) {
			/*
			 * Calculate gap to end of previous block.
			 */
			adjust = prevdiff = ap->off -
				(ap->prevp->br_startoff +
				 ap->prevp->br_blockcount);
			/*
			 * Figure the startblock based on the previous block's
			 * end and the gap size.
			 * Heuristic!
			 * If the gap is large relative to the piece we're
			 * allocating, or using it gives us an illegal block
			 * number, then just use the end of the previous block.
			 */
			if (prevdiff <= XFS_ALLOC_GAP_UNITS * ap->alen &&
			    ISLEGAL(prevbno + prevdiff,
				    ap->prevp->br_startblock))
				prevbno += adjust;
			else
				prevdiff += adjust;
			/*
			 * If the firstblock forbids it, can't use it,
			 * must use default.
			 */
			if (!rt && !nullfb &&
			    XFS_FSB_TO_AGNO(mp, prevbno) != fb_agno)
				prevbno = NULLFSBLOCK;
		}
		/*
		 * No previous block or can't follow it, just default.
		 */
		else
			prevbno = NULLFSBLOCK;
		/*
		 * If there's a following (right) block, select a requested
		 * start block based on it.
		 */
		if (!ISNULLSTARTBLOCK(ap->gotp->br_startblock)) {
			/*
			 * Calculate gap to start of next block.
			 */
			adjust = gotdiff = ap->gotp->br_startoff - ap->off;
			/*
			 * Figure the startblock based on the next block's
			 * start and the gap size.
			 */
			gotbno = ap->gotp->br_startblock;
			/*
			 * Heuristic!
			 * If the gap is large relative to the piece we're
			 * allocating, or using it gives us an illegal block
			 * number, then just use the start of the next block
			 * offset by our length.
			 */
			if (gotdiff <= XFS_ALLOC_GAP_UNITS * ap->alen &&
			    ISLEGAL(gotbno - gotdiff, gotbno))
				gotbno -= adjust;
			else if (ISLEGAL(gotbno - ap->alen, gotbno)) {
				gotbno -= ap->alen;
				gotdiff += adjust - ap->alen;
			} else
				gotdiff += adjust;
			/*
			 * If the firstblock forbids it, can't use it,
			 * must use default.
			 */
			if (!rt && !nullfb &&
			    XFS_FSB_TO_AGNO(mp, gotbno) != fb_agno)
				gotbno = NULLFSBLOCK;
		}
		/*
		 * No next block, just default.
		 */
		else
			gotbno = NULLFSBLOCK;
		/*
		 * If both valid, pick the better one, else the only good
		 * one, else ap->rval is already set (to 0 or the inode block).
		 */
		if (prevbno != NULLFSBLOCK && gotbno != NULLFSBLOCK)
			ap->rval = prevdiff <= gotdiff ? prevbno : gotbno;
		else if (prevbno != NULLFSBLOCK)
			ap->rval = prevbno;
		else if (gotbno != NULLFSBLOCK)
			ap->rval = gotbno;
	}
	/*
	 * If allowed, use ap->rval; otherwise must use firstblock since
	 * it's in the right allocation group.
	 */
	if (nullfb || rt || XFS_FSB_TO_AGNO(mp, ap->rval) == fb_agno)
		;
	else
		ap->rval = ap->firstblock;
	/*
	 * Realtime allocation, done through xfs_rtallocate_extent.
	 */
	if (rt) {
#ifndef __KERNEL__
		ASSERT(0);
#else
		xfs_rtblock_t	rtb;

		atype = ap->rval == 0 ?
			XFS_ALLOCTYPE_ANY_AG : XFS_ALLOCTYPE_NEAR_BNO;
		do_div(ap->rval, mp->m_sb.sb_rextsize);
		rtb = ap->rval;
		ap->alen = ralen;
		if ((error = xfs_rtallocate_extent(ap->tp, ap->rval, 1, ap->alen,
				&ralen, atype, ap->wasdel, prod, &rtb)))
			return error;
		if (rtb == NULLFSBLOCK && prod > 1 &&
		    (error = xfs_rtallocate_extent(ap->tp, ap->rval, 1,
						   ap->alen, &ralen, atype,
						   ap->wasdel, 1, &rtb)))
			return error;
		ap->rval = rtb;
		if (ap->rval != NULLFSBLOCK) {
			ap->rval *= mp->m_sb.sb_rextsize;
			ralen *= mp->m_sb.sb_rextsize;
			ap->alen = ralen;
			ap->ip->i_d.di_nblocks += ralen;
			xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE);
			if (ap->wasdel)
				ap->ip->i_delayed_blks -= ralen;
			/*
			 * Adjust the disk quota also. This was reserved
			 * earlier.
			 */
			if (XFS_IS_QUOTA_ON(mp) &&
			    ap->ip->i_ino != mp->m_sb.sb_uquotino &&
			    ap->ip->i_ino != mp->m_sb.sb_gquotino)
				xfs_trans_mod_dquot_byino(ap->tp, ap->ip,
					ap->wasdel ?
						XFS_TRANS_DQ_DELRTBCOUNT :
						XFS_TRANS_DQ_RTBCOUNT,
					(long)ralen);
		} else
			ap->alen = 0;
#endif	/* __KERNEL__ */
	}
	/*
	 * Normal allocation, done through xfs_alloc_vextent.
	 */
	else {
		xfs_agnumber_t	ag;
		xfs_alloc_arg_t args;
		xfs_extlen_t	blen;
		xfs_extlen_t	delta;
		int		isaligned;
		xfs_extlen_t	longest;
		xfs_extlen_t	need;
		xfs_extlen_t	nextminlen=0;
		int		notinit;
		xfs_perag_t	*pag;
		xfs_agnumber_t	startag;
		int		tryagain;

		tryagain = isaligned = 0;
		args.tp = ap->tp;
		args.mp = mp;
		args.fsbno = ap->rval;
		args.maxlen = MIN(ap->alen, mp->m_sb.sb_agblocks);
		blen = 0;
		if (nullfb) {
			args.type = XFS_ALLOCTYPE_START_BNO;
			args.total = ap->total;
			/*
			 * Find the longest available space.
			 * We're going to try for the whole allocation at once.
			 */
			startag = ag = XFS_FSB_TO_AGNO(mp, args.fsbno);
			notinit = 0;
			down_read(&mp->m_peraglock);
			while (blen < ap->alen) {
				pag = &mp->m_perag[ag];
				if (!pag->pagf_init &&
				    (error = xfs_alloc_pagf_init(mp, args.tp,
					    ag, XFS_ALLOC_FLAG_TRYLOCK))) {
					up_read(&mp->m_peraglock);
					return error;
				}
				/*
				 * See xfs_alloc_fix_freelist...
				 */
				if (pag->pagf_init) {
					need = XFS_MIN_FREELIST_PAG(pag, mp);
					delta = need > pag->pagf_flcount ?
						need - pag->pagf_flcount : 0;
					longest = (pag->pagf_longest > delta) ?
						(pag->pagf_longest - delta) :
						(pag->pagf_flcount > 0 ||
						 pag->pagf_longest > 0);
					if (blen < longest)
						blen = longest;
				} else
					notinit = 1;
				if (++ag == mp->m_sb.sb_agcount)
					ag = 0;
				if (ag == startag)
					break;
			}
			up_read(&mp->m_peraglock);
			/*
			 * Since the above loop did a BUF_TRYLOCK, it is
			 * possible that there is space for this request.
			 */
			if (notinit || blen < ap->minlen)
				args.minlen = ap->minlen;
			/*
			 * If the best seen length is less than the request
			 * length, use the best as the minimum.
			 */
			else if (blen < ap->alen)
				args.minlen = blen;
			/*
			 * Otherwise we've seen an extent as big as alen,
			 * use that as the minimum.
			 */
			else
				args.minlen = ap->alen;
		} else if (ap->low) {
			args.type = XFS_ALLOCTYPE_FIRST_AG;
			args.total = args.minlen = ap->minlen;
		} else {
			args.type = XFS_ALLOCTYPE_NEAR_BNO;
			args.total = ap->total;
			args.minlen = ap->minlen;
		}
		if (ap->ip->i_d.di_extsize) {
			args.prod = ap->ip->i_d.di_extsize;
			if ((args.mod = (xfs_extlen_t)do_mod(ap->off, args.prod)))
				args.mod = (xfs_extlen_t)(args.prod - args.mod);
		} else if (mp->m_sb.sb_blocksize >= NBPP) {
			args.prod = 1;
			args.mod = 0;
		} else {
			args.prod = NBPP >> mp->m_sb.sb_blocklog;
			if ((args.mod = (xfs_extlen_t)(do_mod(ap->off, args.prod))))
				args.mod = (xfs_extlen_t)(args.prod - args.mod);
		}
		/*
		 * If we are not low on available data blocks, and the
		 * underlying logical volume manager is a stripe, and
		 * the file offset is zero then try to allocate data
		 * blocks on stripe unit boundary.
		 * NOTE: ap->aeof is only set if the allocation length
		 * is >= the stripe unit and the allocation offset is
		 * at the end of file.
		 */
		if (!ap->low && ap->aeof) {
			if (!ap->off) {
				args.alignment = mp->m_dalign;
				atype = args.type;
				isaligned = 1;
				/*
				 * Adjust for alignment
				 */
				if (blen > args.alignment && blen <= ap->alen)
					args.minlen = blen - args.alignment;
				args.minalignslop = 0;
			} else {
				/*
				 * First try an exact bno allocation.
				 * If it fails then do a near or start bno
				 * allocation with alignment turned on.
				 */
				atype = args.type;
				tryagain = 1;
				args.type = XFS_ALLOCTYPE_THIS_BNO;
				args.alignment = 1;
				/*
				 * Compute the minlen+alignment for the
				 * next case.  Set slop so that the value
				 * of minlen+alignment+slop doesn't go up
				 * between the calls.
				 */
				if (blen > mp->m_dalign && blen <= ap->alen)
					nextminlen = blen - mp->m_dalign;
				else
					nextminlen = args.minlen;
				if (nextminlen + mp->m_dalign > args.minlen + 1)
					args.minalignslop =
						nextminlen + mp->m_dalign -
						args.minlen - 1;
				else
					args.minalignslop = 0;
			}
		} else {
			args.alignment = 1;
			args.minalignslop = 0;
		}
		args.minleft = ap->minleft;
		args.wasdel = ap->wasdel;
		args.isfl = 0;
		args.userdata = ap->userdata;
		if ((error = xfs_alloc_vextent(&args)))
			return error;
		if (tryagain && args.fsbno == NULLFSBLOCK) {
			/*
			 * Exact allocation failed. Now try with alignment
			 * turned on.
			 */
			args.type = atype;
			args.fsbno = ap->rval;
			args.alignment = mp->m_dalign;
			args.minlen = nextminlen;
			args.minalignslop = 0;
			isaligned = 1;
			if ((error = xfs_alloc_vextent(&args)))
				return error;
		}
		if (isaligned && args.fsbno == NULLFSBLOCK) {
			/*
			 * allocation failed, so turn off alignment and
			 * try again.
			 */
			args.type = atype;
			args.fsbno = ap->rval;
			args.alignment = 0;
			if ((error = xfs_alloc_vextent(&args)))
				return error;
		}
		if (args.fsbno == NULLFSBLOCK && nullfb &&
		    args.minlen > ap->minlen) {
			args.minlen = ap->minlen;
			args.type = XFS_ALLOCTYPE_START_BNO;
			args.fsbno = ap->rval;
			if ((error = xfs_alloc_vextent(&args)))
				return error;
		}
		if (args.fsbno == NULLFSBLOCK && nullfb) {
			args.fsbno = 0;
			args.type = XFS_ALLOCTYPE_FIRST_AG;
			args.total = ap->minlen;
			args.minleft = 0;
			if ((error = xfs_alloc_vextent(&args)))
				return error;
			ap->low = 1;
		}
		if (args.fsbno != NULLFSBLOCK) {
			ap->firstblock = ap->rval = args.fsbno;
			ASSERT(nullfb || fb_agno == args.agno ||
			       (ap->low && fb_agno < args.agno));
			ap->alen = args.len;
			ap->ip->i_d.di_nblocks += args.len;
			xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE);
			if (ap->wasdel)
				ap->ip->i_delayed_blks -= args.len;
			/*
			 * Adjust the disk quota also. This was reserved
			 * earlier.
			 */
			if (XFS_IS_QUOTA_ON(mp) &&
			    ap->ip->i_ino != mp->m_sb.sb_uquotino &&
			    ap->ip->i_ino != mp->m_sb.sb_gquotino)
				xfs_trans_mod_dquot_byino(ap->tp, ap->ip,
					ap->wasdel ?
						XFS_TRANS_DQ_DELBCOUNT :
						XFS_TRANS_DQ_BCOUNT,
					(long)args.len);
		} else {
			ap->rval = NULLFSBLOCK;
			ap->alen = 0;
		}
	}
	return 0;
#undef	ISLEGAL
}

Generated by GNU enscript 1.6.4.