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

📄 ffs_alloc.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Copyright (c) 1982, 1986, 1989, 1993 *	The Regents of the University of California.  All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software *    must display the following acknowledgement: *	This product includes software developed by the University of *	California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * *	@(#)ffs_alloc.c	8.8 (Berkeley) 2/21/94 */#include <sys/param.h>#include <sys/systm.h>#include <sys/buf.h>#include <sys/proc.h>#include <sys/vnode.h>#include <sys/mount.h>#include <sys/kernel.h>#include <sys/syslog.h>#include <vm/vm.h>#include <ufs/ufs/quota.h>#include <ufs/ufs/inode.h>#include <ufs/ffs/fs.h>#include <ufs/ffs/ffs_extern.h>extern u_long nextgennumber;static daddr_t	ffs_alloccg __P((struct inode *, int, daddr_t, int));static daddr_t	ffs_alloccgblk __P((struct fs *, struct cg *, daddr_t));static daddr_t	ffs_clusteralloc __P((struct inode *, int, daddr_t, int));static ino_t	ffs_dirpref __P((struct fs *));static daddr_t	ffs_fragextend __P((struct inode *, int, long, int, int));static void	ffs_fserr __P((struct fs *, u_int, char *));static u_long	ffs_hashalloc		    __P((struct inode *, int, long, int, u_long (*)()));static ino_t	ffs_nodealloccg __P((struct inode *, int, daddr_t, int));static daddr_t	ffs_mapsearch __P((struct fs *, struct cg *, daddr_t, int));/* * Allocate a block in the file system. *  * The size of the requested block is given, which must be some * multiple of fs_fsize and <= fs_bsize. * A preference may be optionally specified. If a preference is given * the following hierarchy is used to allocate a block: *   1) allocate the requested block. *   2) allocate a rotationally optimal block in the same cylinder. *   3) allocate a block in the same cylinder group. *   4) quadradically rehash into other cylinder groups, until an *      available block is located. * If no block preference is given the following heirarchy is used * to allocate a block: *   1) allocate a block in the cylinder group that contains the *      inode for the file. *   2) quadradically rehash into other cylinder groups, until an *      available block is located. */ffs_alloc(ip, lbn, bpref, size, cred, bnp)	register struct inode *ip;	daddr_t lbn, bpref;	int size;	struct ucred *cred;	daddr_t *bnp;{	register struct fs *fs;	daddr_t bno;	int cg, error;		*bnp = 0;	fs = ip->i_fs;#ifdef DIAGNOSTIC	if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0) {		printf("dev = 0x%x, bsize = %d, size = %d, fs = %s\n",		    ip->i_dev, fs->fs_bsize, size, fs->fs_fsmnt);		panic("ffs_alloc: bad size");	}	if (cred == NOCRED)		panic("ffs_alloc: missing credential\n");#endif /* DIAGNOSTIC */	if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0)		goto nospace;	if (cred->cr_uid != 0 && freespace(fs, fs->fs_minfree) <= 0)		goto nospace;#ifdef QUOTA	if (error = chkdq(ip, (long)btodb(size), cred, 0))		return (error);#endif	if (bpref >= fs->fs_size)		bpref = 0;	if (bpref == 0)		cg = ino_to_cg(fs, ip->i_number);	else		cg = dtog(fs, bpref);	bno = (daddr_t)ffs_hashalloc(ip, cg, (long)bpref, size,	    (u_long (*)())ffs_alloccg);	if (bno > 0) {		ip->i_blocks += btodb(size);		ip->i_flag |= IN_CHANGE | IN_UPDATE;		*bnp = bno;		return (0);	}#ifdef QUOTA	/*	 * Restore user's disk quota because allocation failed.	 */	(void) chkdq(ip, (long)-btodb(size), cred, FORCE);#endifnospace:	ffs_fserr(fs, cred->cr_uid, "file system full");	uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt);	return (ENOSPC);}/* * Reallocate a fragment to a bigger size * * The number and size of the old block is given, and a preference * and new size is also specified. The allocator attempts to extend * the original block. Failing that, the regular block allocator is * invoked to get an appropriate block. */ffs_realloccg(ip, lbprev, bpref, osize, nsize, cred, bpp)	register struct inode *ip;	daddr_t lbprev;	daddr_t bpref;	int osize, nsize;	struct ucred *cred;	struct buf **bpp;{	register struct fs *fs;	struct buf *bp;	int cg, request, error;	daddr_t bprev, bno;		*bpp = 0;	fs = ip->i_fs;#ifdef DIAGNOSTIC	if ((u_int)osize > fs->fs_bsize || fragoff(fs, osize) != 0 ||	    (u_int)nsize > fs->fs_bsize || fragoff(fs, nsize) != 0) {		printf(		    "dev = 0x%x, bsize = %d, osize = %d, nsize = %d, fs = %s\n",		    ip->i_dev, fs->fs_bsize, osize, nsize, fs->fs_fsmnt);		panic("ffs_realloccg: bad size");	}	if (cred == NOCRED)		panic("ffs_realloccg: missing credential\n");#endif /* DIAGNOSTIC */	if (cred->cr_uid != 0 && freespace(fs, fs->fs_minfree) <= 0)		goto nospace;	if ((bprev = ip->i_db[lbprev]) == 0) {		printf("dev = 0x%x, bsize = %d, bprev = %d, fs = %s\n",		    ip->i_dev, fs->fs_bsize, bprev, fs->fs_fsmnt);		panic("ffs_realloccg: bad bprev");	}	/*	 * Allocate the extra space in the buffer.	 */	if (error = bread(ITOV(ip), lbprev, osize, NOCRED, &bp)) {		brelse(bp);		return (error);	}#ifdef QUOTA	if (error = chkdq(ip, (long)btodb(nsize - osize), cred, 0)) {		brelse(bp);		return (error);	}#endif	/*	 * Check for extension in the existing location.	 */	cg = dtog(fs, bprev);	if (bno = ffs_fragextend(ip, cg, (long)bprev, osize, nsize)) {		if (bp->b_blkno != fsbtodb(fs, bno))			panic("bad blockno");		ip->i_blocks += btodb(nsize - osize);		ip->i_flag |= IN_CHANGE | IN_UPDATE;		allocbuf(bp, nsize);		bp->b_flags |= B_DONE;		bzero((char *)bp->b_data + osize, (u_int)nsize - osize);		*bpp = bp;		return (0);	}	/*	 * Allocate a new disk location.	 */	if (bpref >= fs->fs_size)		bpref = 0;	switch ((int)fs->fs_optim) {	case FS_OPTSPACE:		/*		 * Allocate an exact sized fragment. Although this makes 		 * best use of space, we will waste time relocating it if 		 * the file continues to grow. If the fragmentation is		 * less than half of the minimum free reserve, we choose		 * to begin optimizing for time.		 */		request = nsize;		if (fs->fs_minfree < 5 ||		    fs->fs_cstotal.cs_nffree >		    fs->fs_dsize * fs->fs_minfree / (2 * 100))			break;		log(LOG_NOTICE, "%s: optimization changed from SPACE to TIME\n",			fs->fs_fsmnt);		fs->fs_optim = FS_OPTTIME;		break;	case FS_OPTTIME:		/*		 * At this point we have discovered a file that is trying to		 * grow a small fragment to a larger fragment. To save time,		 * we allocate a full sized block, then free the unused portion.		 * If the file continues to grow, the `ffs_fragextend' call		 * above will be able to grow it in place without further		 * copying. If aberrant programs cause disk fragmentation to		 * grow within 2% of the free reserve, we choose to begin		 * optimizing for space.		 */		request = fs->fs_bsize;		if (fs->fs_cstotal.cs_nffree <		    fs->fs_dsize * (fs->fs_minfree - 2) / 100)			break;		log(LOG_NOTICE, "%s: optimization changed from TIME to SPACE\n",			fs->fs_fsmnt);		fs->fs_optim = FS_OPTSPACE;		break;	default:		printf("dev = 0x%x, optim = %d, fs = %s\n",		    ip->i_dev, fs->fs_optim, fs->fs_fsmnt);		panic("ffs_realloccg: bad optim");		/* NOTREACHED */	}	bno = (daddr_t)ffs_hashalloc(ip, cg, (long)bpref, request,	    (u_long (*)())ffs_alloccg);	if (bno > 0) {		bp->b_blkno = fsbtodb(fs, bno);		(void) vnode_pager_uncache(ITOV(ip));		ffs_blkfree(ip, bprev, (long)osize);		if (nsize < request)			ffs_blkfree(ip, bno + numfrags(fs, nsize),			    (long)(request - nsize));		ip->i_blocks += btodb(nsize - osize);		ip->i_flag |= IN_CHANGE | IN_UPDATE;		allocbuf(bp, nsize);		bp->b_flags |= B_DONE;		bzero((char *)bp->b_data + osize, (u_int)nsize - osize);		*bpp = bp;		return (0);	}#ifdef QUOTA	/*	 * Restore user's disk quota because allocation failed.	 */	(void) chkdq(ip, (long)-btodb(nsize - osize), cred, FORCE);#endif	brelse(bp);nospace:	/*	 * no space available	 */	ffs_fserr(fs, cred->cr_uid, "file system full");	uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt);	return (ENOSPC);}/* * Reallocate a sequence of blocks into a contiguous sequence of blocks. * * The vnode and an array of buffer pointers for a range of sequential * logical blocks to be made contiguous is given. The allocator attempts * to find a range of sequential blocks starting as close as possible to * an fs_rotdelay offset from the end of the allocation for the logical * block immediately preceeding the current range. If successful, the * physical block numbers in the buffer pointers and in the inode are * changed to reflect the new allocation. If unsuccessful, the allocation * is left unchanged. The success in doing the reallocation is returned. * Note that the error return is not reflected back to the user. Rather * the previous block allocation will be used. */#include <sys/sysctl.h>int doasyncfree = 1;struct ctldebug debug14 = { "doasyncfree", &doasyncfree };intffs_reallocblks(ap)	struct vop_reallocblks_args /* {		struct vnode *a_vp;		struct cluster_save *a_buflist;	} */ *ap;{	struct fs *fs;	struct inode *ip;	struct vnode *vp;	struct buf *sbp, *ebp;	daddr_t *bap, *sbap, *ebap;	struct cluster_save *buflist;	daddr_t start_lbn, end_lbn, soff, eoff, newblk, blkno;	struct indir start_ap[NIADDR + 1], end_ap[NIADDR + 1], *idp;	int i, len, start_lvl, end_lvl, pref, ssize;	vp = ap->a_vp;	ip = VTOI(vp);	fs = ip->i_fs;	if (fs->fs_contigsumsize <= 0)		return (ENOSPC);	buflist = ap->a_buflist;	len = buflist->bs_nchildren;	start_lbn = buflist->bs_children[0]->b_lblkno;	end_lbn = start_lbn + len - 1;#ifdef DIAGNOSTIC	for (i = 1; i < len; i++)		if (buflist->bs_children[i]->b_lblkno != start_lbn + i)			panic("ffs_reallocblks: non-cluster");#endif	/*	 * If the latest allocation is in a new cylinder group, assume that	 * the filesystem has decided to move and do not force it back to	 * the previous cylinder group.	 */	if (dtog(fs, dbtofsb(fs, buflist->bs_children[0]->b_blkno)) !=	    dtog(fs, dbtofsb(fs, buflist->bs_children[len - 1]->b_blkno)))		return (ENOSPC);	if (ufs_getlbns(vp, start_lbn, start_ap, &start_lvl) ||	    ufs_getlbns(vp, end_lbn, end_ap, &end_lvl))		return (ENOSPC);	/*	 * Get the starting offset and block map for the first block.	 */	if (start_lvl == 0) {		sbap = &ip->i_db[0];		soff = start_lbn;	} else {		idp = &start_ap[start_lvl - 1];		if (bread(vp, idp->in_lbn, (int)fs->fs_bsize, NOCRED, &sbp)) {			brelse(sbp);			return (ENOSPC);		}		sbap = (daddr_t *)sbp->b_data;		soff = idp->in_off;	}	/*	 * Find the preferred location for the cluster.	 */	pref = ffs_blkpref(ip, start_lbn, soff, sbap);	/*	 * If the block range spans two block maps, get the second map.	 */	if (end_lvl == 0 || (idp = &end_ap[end_lvl - 1])->in_off + 1 >= len) {		ssize = len;	} else {#ifdef DIAGNOSTIC		if (start_ap[start_lvl-1].in_lbn == idp->in_lbn)			panic("ffs_reallocblk: start == end");#endif		ssize = len - (idp->in_off + 1);		if (bread(vp, idp->in_lbn, (int)fs->fs_bsize, NOCRED, &ebp))			goto fail;		ebap = (daddr_t *)ebp->b_data;	}	/*	 * Search the block map looking for an allocation of the desired size.	 */	if ((newblk = (daddr_t)ffs_hashalloc(ip, dtog(fs, pref), (long)pref,	    len, (u_long (*)())ffs_clusteralloc)) == 0)		goto fail;	/*	 * We have found a new contiguous block.	 *	 * First we have to replace the old block pointers with the new	 * block pointers in the inode and indirect blocks associated	 * with the file.	 */	blkno = newblk;	for (bap = &sbap[soff], i = 0; i < len; i++, blkno += fs->fs_frag) {		if (i == ssize)			bap = ebap;#ifdef DIAGNOSTIC		if (buflist->bs_children[i]->b_blkno != fsbtodb(fs, *bap))			panic("ffs_reallocblks: alloc mismatch");#endif		*bap++ = blkno;	}	/*	 * Next we must write out the modified inode and indirect blocks.	 * For strict correctness, the writes should be synchronous since	 * the old block values may have been written to disk. In practise	 * they are almost never written, but if we are concerned about 	 * strict correctness, the `doasyncfree' flag should be set to zero.	 *	 * The test on `doasyncfree' should be changed to test a flag	 * that shows whether the associated buffers and inodes have	 * been written. The flag should be set when the cluster is	 * started and cleared whenever the buffer or inode is flushed.	 * We can then check below to see if it is set, and do the	 * synchronous write only when it has been cleared.	 */	if (sbap != &ip->i_db[0]) {		if (doasyncfree)			bdwrite(sbp);		else			bwrite(sbp);	} else {		ip->i_flag |= IN_CHANGE | IN_UPDATE;		if (!doasyncfree)			VOP_UPDATE(vp, &time, &time, MNT_WAIT);	}	if (ssize < len)		if (doasyncfree)			bdwrite(ebp);		else			bwrite(ebp);	/*	 * Last, free the old blocks and assign the new blocks to the buffers.	 */	for (blkno = newblk, i = 0; i < len; i++, blkno += fs->fs_frag) {		ffs_blkfree(ip, dbtofsb(fs, buflist->bs_children[i]->b_blkno),		    fs->fs_bsize);		buflist->bs_children[i]->b_blkno = fsbtodb(fs, blkno);	}	return (0);fail:	if (ssize < len)		brelse(ebp);	if (sbap != &ip->i_db[0])		brelse(sbp);	return (ENOSPC);}/* * Allocate an inode in the file system. *  * If allocating a directory, use ffs_dirpref to select the inode. * If allocating in a directory, the following hierarchy is followed: *   1) allocate the preferred inode. *   2) allocate an inode in the same cylinder group. *   3) quadradically rehash into other cylinder groups, until an *      available inode is located. * If no inode preference is given the following heirarchy is used * to allocate an inode: *   1) allocate an inode in cylinder group 0. *   2) quadradically rehash into other cylinder groups, until an *      available inode is located. */ffs_valloc(ap)	struct vop_valloc_args /* {		struct vnode *a_pvp;		int a_mode;		struct ucred *a_cred;		struct vnode **a_vpp;	} */ *ap;{	register struct vnode *pvp = ap->a_pvp;	register struct inode *pip;	register struct fs *fs;	register struct inode *ip;	mode_t mode = ap->a_mode;	ino_t ino, ipref;	int cg, error;		*ap->a_vpp = NULL;	pip = VTOI(pvp);	fs = pip->i_fs;	if (fs->fs_cstotal.cs_nifree == 0)		goto noinodes;	if ((mode & IFMT) == IFDIR)		ipref = ffs_dirpref(fs);

⌨️ 快捷键说明

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