V10/lsys/fs/alloc.c

Compare this file to the similar file:
Show the results in this format:

#include "sys/param.h"
#include "sys/systm.h"
#include "sys/filsys.h"
#include "sys/fblk.h"
#include "sys/buf.h"
#include "sys/inode.h"
#include "sys/ino.h"
#include "sys/user.h"

typedef	struct fblk *FP;

/*
 * alloc will obtain the next available
 * free disk block from the free list of
 * the specified filsys.
 * The super block has up to NICFREE remembered
 * free blocks; the last of these is read to
 * obtain NICFREE more . . .
 */
static daddr_t bitfsalloc(), oldfsalloc(), bigfsalloc();

struct buf *
alloc(fip, prev)
struct inode *fip;
daddr_t prev;
{
	daddr_t bno;
	register struct filsys *fp;
	register struct buf *bp;

	fp = getfs(fip);
	while (fp->s_flock)
		sleep((caddr_t)&fp->s_flock, PINOD);
	if(BITFS(fip->i_dev))	/* unfortunately device dependent */
		if(fp->U.N.S_flag)
			bno = bigfsalloc(fp, prev);
		else
			bno = bitfsalloc(fp, prev);
	else
		bno = oldfsalloc(fp, fip->i_dev);
	if (bno == 0) {
		/*fp->s_nfree = 0;	/* but it would be wrong FIX*/
		fp->s_tfree = 0;
		fserr(fip->i_dev, fp, "file system full");
		tsleep((caddr_t)&u, PRIBIO, 5);	/* slow things down */
		u.u_error = ENOSPC;
		return (NULL);
	}
	bp = getblk(fip->i_dev, bno);
	clrbuf(bp);
	fp->s_fmod = 1;
	fp->s_tfree--;
	return (bp);
}

/*
 * bitmap fs alloc:
 * given the previous block in the file,
 * try to pick one in the same cylinder,
 * preferably in a nice rotational place
 * if we can't, pick one in the next couple of cylinders
 * if we can't, just pick any block
 */

static daddr_t
bitfsalloc(fp, prev)
register struct filsys *fp;
daddr_t prev;
{
	register daddr_t bno;
	register long i;
	daddr_t cyl, end, best;

	best = -1;
	if (fp->s_cylsize == 0) {	/* safety for the root.  ugh. */
		fp->s_cylsize = 40;
		fp->s_aspace = 4;	/* good for comets */
	}
	if (prev >= fp->s_isize) {
		bno = prev / fp->s_cylsize;
		bno *= fp->s_cylsize;
		if (bno < fp->s_isize)
			bno = fp->s_isize;
		cyl = bno + fp->s_cylsize;
		end = bno + 3 * fp->s_cylsize;
		if (end > fp->s_fsize)
			end = fp->s_fsize;
		if (cyl > fp->s_fsize)
			cyl = fp->s_fsize;
		i = bno - fp->s_isize;
		best = findbit(fp->s_bfree, i, cyl-fp->s_isize, prev-fp->s_isize,
			fp->s_aspace);	/* in same cylinder */
		if(best == -1)
			best = findbit(fp->s_bfree, cyl-fp->s_isize,
				end-fp->s_isize, 0L, 0);
	}
	if(best == -1)
		best = findbit(fp->s_bfree, 0L, fp->s_fsize-fp->s_isize, 0L, 0);
	if(best == -1)
		return(0);
	BITALLOC(fp->s_bfree, best);
	if (fp->s_valid) {
		fp->s_valid = 0;
		update();		/* ugh */
	}
	return (best+fp->s_isize);
}

/*
 * new-style, multi-segment bitmap
 * take care not to cross segment boundaries
 */

static daddr_t
bigfsalloc(fp, prev)
register struct filsys *fp;
daddr_t prev;
{
	register int fblk;
	register long bs;
	register long best, beg, cyl;
	int nblk;

	bs = fp->U.N.S_bsize;
	if(prev > 0) {
		beg = prev/fp->s_cylsize;
		beg *= fp->s_cylsize;
		if(beg < fp->s_isize)
			beg = fp->s_isize;	/* unnecessary */
		cyl = beg + fp->s_cylsize;
		if(cyl > fp->s_fsize)
			cyl = fp->s_fsize;
		fblk = beg/bs;
		if (fblk != (cyl-1)/bs)
			cyl = (fblk+1)*bs;	/* bound to this block */
		best = findbit(fp->U.N.S_blk[fblk]->b_un.b_words, beg%bs,
			cyl%bs, prev%bs, fp->s_aspace);
		if (best != -1)
			goto done;
		beg = cyl;
		cyl = beg + 2 * fp->s_cylsize;
		if (cyl >= fp->s_fsize)
			cyl = fp->s_fsize;
		fblk = beg/bs;
		if (fblk != (cyl - 1)/bs)
			cyl = (fblk+1)*bs;
		best = findbit(fp->U.N.S_blk[fblk]->b_un.b_words, beg%bs,
			cyl%bs, 0L, 0);
		if(best != -1)
			goto done;
	}
	/*
	 * find any free bit at all
	 */
	nblk = (fp->s_fsize + bs - 1) / bs;
	for (fblk = 0; fblk < nblk; fblk++) {
		best = findbit(fp->U.N.S_blk[fblk]->b_un.b_words, 0L, bs, 0L, 0);
		if(best != -1)
			goto done;
	}
	/*
	 * found nothing nowhere
	 */
	return(0);
	/*
	 * found bit best in block fblk
	 */
done:
	BITALLOC((fp->U.N.S_blk[fblk]->b_un.b_words), best);
	if(fp->s_valid) {
		fp->s_valid = 0;
		update();
	}
	return(best+fblk*bs);
}

/* find a bit between bot and top, preferably at least space from prev */
findbit(ptr, bot, top, prev, space)
long *ptr;
long bot, top, prev;
{
	register long *p;
	register int j;
	register long bno, best;

	best = -1;
	j = bot/BITCELL;
	p = ptr + j;
	bno = j*BITCELL;	/* start on long boundary */
	for(; bno < top; p++) {
		if(*p == 0) {	/* none free in whole long */
			bno += BITCELL;	/* so onward */
			continue;
		}
		for(j = 0; j < BITCELL; j++, bno++) {
			if((*p & (1<<j)) == 0)
				continue;
			if(bno < bot || bno >= top)	/* out of the loop? */
				continue;	/* first and last longs */
			if(best == -1)
				best = bno;
			if(bno - prev >= space)
				return(bno);
		}
	}
	return(best);	/* -1 or whatever we found */
}

static daddr_t
oldfsalloc(fp, dev)
register struct filsys *fp;
{
	register daddr_t bno;
	register struct buf *bp;

	do {
		if (fp->s_nfree <= 0)
			return (0);
		if (fp->s_nfree > NICFREE) {
			fserr(dev, fp, "bad free count");
			return (0);
		}
		bno = fp->s_free[--fp->s_nfree];
		if (bno == 0)
			return (0);
	} while (badblock(dev, fp, bno));
	if (fp->s_nfree <= 0) {
		fp->s_flock++;
		bp = bread(dev, bno);
		if ((bp->b_flags&B_ERROR) == 0) {
			fp->s_nfree = ((FP)(bp->b_un.b_addr))->df_nfree;
			bcopy((caddr_t)((FP)(bp->b_un.b_addr))->df_free,
			    (caddr_t)fp->s_free, sizeof(fp->s_free));
		}
		brelse(bp);
		fp->s_flock = 0;
		wakeup((caddr_t)&fp->s_flock);
		if (fp->s_nfree <= 0)
			return (0);
	}
	return (bno);
}

/*
 * place the specified disk block
 * back on the free list of the
 * specified device.
 */
free(fip, bno)
	struct inode *fip;
	daddr_t bno;
{	int bs;
	register struct filsys *fp;
	register struct buf *bp;

	fp = getfs(fip);
	fp->s_fmod = 1;
	while (fp->s_flock)
		sleep((caddr_t)&fp->s_flock, PINOD);
	if (badblock(fip->i_dev, fp, bno))
		return;
	if(BITFS(fip->i_dev)) {
	/* paranoia suggests checking it's not already free */
		if(fp->U.N.S_flag) {
			bs = fp->U.N.S_bsize;
			BITFREE((fp->U.N.S_blk[bno/bs]->b_un.b_words), bno%bs);
		}
		else {
			bno -= fp->s_isize;
			BITFREE(fp->s_bfree, bno);
		}
		if(fp->s_valid) {	/* not any more */
			fp->s_valid = 0;
			update();	/* even GROSSER */
		}
	}
	else {
		if (fp->s_nfree <= 0) {
			fp->s_nfree = 1;
			fp->s_free[0] = 0;
		}
		if (fp->s_nfree >= NICFREE) {
			fp->s_flock++;
			bp = getblk(fip->i_dev, bno);
			((FP)(bp->b_un.b_addr))->df_nfree = fp->s_nfree;
			bcopy((caddr_t)fp->s_free,
			    (caddr_t)((FP)(bp->b_un.b_addr))->df_free,
			    sizeof(fp->s_free));
			fp->s_nfree = 0;
			bwrite(bp);
			fp->s_flock = 0;
			wakeup((caddr_t)&fp->s_flock);
		}
		fp->s_free[fp->s_nfree++] = bno;
	}
	fp->s_tfree++;
	fp->s_fmod = 1;
}

/*
 * Check that a block number is in the
 * range between the I list and the size
 * of the device.
 * This is used mainly to check that a
 * garbage file system has not been mounted.
 */
badblock(dev, fp, bn)
	dev_t dev;
	register struct filsys *fp;
	daddr_t bn;
{

	if (bn < fp->s_isize || bn >= fp->s_fsize) {
		fserr(dev, fp, "bad block");
		return(1);
	}
	return(0);
}

/*
 * allocate an unused disk inode in the specified filesystem
 * up to NICINOD possible i-numbers are kept in a list
 * in s_inode; try those first.  If the list empties,
 * scan the i-list and fill it again.
 * To speed things up, pick up the scan where it last
 * left off (s_lasti) unless there are believed to be
 * many free i-nodes with lower numbers (s_nbehind).
 * s_ilock is there only to avoid having two processes
 * in the list-filling code.
 */

int dupinod;		/* debug */

struct inode *
ialloc(fip)
	struct inode *fip;
{
	register struct filsys *fp;
	register struct buf *bp;
	register struct inode *ip;
	register int i;
	struct dinode *dp;
	ino_t ino;
	int first;
	int inopb;
	daddr_t adr;

	fp = getfs(fip);
	if (fp->s_ninode > NICINOD || fp->s_ninode < 0) {
		fserr(fip->i_dev, fp, "bad inode count");
		fp->s_ninode = 0;
	}
loop:
	if (fp->s_ninode > 0) {
		ino = fp->s_inode[--fp->s_ninode];
		if (ifind(fip, ino)) {	/* already in use */
			dupinod++;	/* debug */
			goto loop;
		}
		ip = iget(fip, fip->i_dev, ino);
		if (ip == NULL)		/* probably table full */
			return(NULL);
		if (ip->i_count == 1 && fsiread(fip, ip) < 0)
			goto loop;
		if (ip->i_mode != 0) {	/* already allocated */
			iput(ip);
			goto loop;
		}
		for (i=0; i<NADDR; i++)
			ip->i_un.i_addr[i] = 0;
		fp->s_tinode--;
		fp->s_fmod = 1;
		return(ip);
	}
	/*
	 * nothing left in super-block; restock
	 */
	while (fp->s_ilock)
		sleep((caddr_t)&fp->s_ilock, PINOD);
	if (fp->s_ninode > 0)	/* someone beat us to it? */
		goto loop;
	fp->s_ilock++;
	first = 1;
	inopb = INOPB(fip->i_dev);	/* optimization */
fromtop:
	ino = fp->s_lasti;
	ino = ((ino / inopb) * inopb) + 1;	/* round down to start of block */
	adr = itod(fip->i_dev, ino);
	if (fp->s_nbehind > 4 * NICINOD
	||  adr <= SUPERB || adr >= fp->s_isize
	||  first == 0) {
		first = 0;
		ino = 1;
		adr = SUPERB + 1;
		fp->s_nbehind = 0;
	}
	for(; adr < fp->s_isize; adr++) {
		bp = bread(fip->i_dev, adr);
		if (bp->b_flags & B_ERROR) {
			brelse(bp);
			ino += inopb;
			continue;
		}
		dp = bp->b_un.b_dino;
		for (i=0; i<inopb; i++, ino++, dp++) {
			if (dp->di_mode != 0 || ifind(fip, ino))
				continue;
			fp->s_inode[fp->s_ninode++] = ino;
			if (fp->s_ninode >= NICINOD)
				break;
		}
		brelse(bp);
		if (fp->s_ninode >= NICINOD)
			break;
	}
	/*
	 * if we found nothing,
	 * try again from the beginning of the i-list
	 */
	if (fp->s_ninode <= 0 && first) {
		first = 0;
		goto fromtop;
	}
	if (fp->s_ninode == NICINOD)
		fp->s_lasti = ino;
	else {		/* hit the end, but found something */
		fp->s_lasti = 1;
		fp->s_nbehind = 0;
	}
	fp->s_ilock = 0;
	wakeup((caddr_t)&fp->s_ilock);
	if (fp->s_ninode > 0)
		goto loop;
	fserr(fip->i_dev, fp, "out of inodes");
	u.u_error = ENOSPC;
	return (NULL);
}

/*
 * Free the specified inode on the specified device.
 * The algorithm stores up to NICINOD inodes in the super
 * block and throws away any more.  It keeps track of the
 * number of inodes thrown away which preceded the current
 * search point in the file system.  This lets us rescan
 * for more inodes from the beginning only when there
 * are a reasonable number of inodes back there to reallocate.
 */

ifree(ip)
register struct inode *ip;
{
	register struct filsys *fp;

	fp = getfs(ip);
	if (fp->s_ninode > NICINOD || fp->s_ninode < 0) {
		fserr(ip->i_dev, fp, "bad inode count");
		fp->s_ninode = 0;
	}
	fp->s_tinode++;
	if (fp->s_ilock)
		return;
	if (fp->s_ninode >= NICINOD) {
		if (fp->s_lasti > ip->i_number)
			fp->s_nbehind++;
		return;
	}
	fp->s_inode[fp->s_ninode++] = ip->i_number;
	fp->s_fmod = 1;
}

/*
 * getfs maps an inode into
 * a pointer to the incore super
 * block.
 *
 * panic: no fs -- the device is not mounted.
 *	this "cannot happen"
 */
struct filsys *
getfs(fip)
register struct inode *fip;
{

	if (fip->i_fstyp != 0 || fip->i_un.i_bufp == NULL)
		panic("no fs");
	return (fip->i_un.i_bufp->b_un.b_filsys);
}

/*
 * Fserr prints the name of a file system
 * with an error diagnostic, in the form
 *	filsys: error message
 */
fserr(dev, fp, cp)
	dev_t dev;
	struct filsys *fp;
	char *cp;
{

	printf("0%o,0%o %s: %s\n", major(dev), minor(dev), fp->s_fsmnt, cp);
}

/*
 * Update is the internal name of 'sync'.  It goes through the disk
 * queues to initiate sandbagged IO; goes through the inodes to write
 * modified nodes; and it goes through the mount table to initiate modified
 * super blocks.
 */
update()
{
	register struct inode *ip;
	static int updlock;

	if (updlock)
		return;
	updlock++;
	/*
	 * Write back each (modified) inode.
	 */
	for (ip = inode; ip < inodeNINODE; ip++)
		if((ip->i_flag&ILOCK)==0 && ip->i_count) {
			ip->i_flag |= ILOCK;
			ip->i_count++;
			iupdat(ip, &time, &time, 0);
			iput(ip);
		}
	updlock = 0;
	/*
	 * Force stale buffer cache information to be flushed,
	 * for all devices.
	 */
	bflush(NODEV);
}