Ultrix-3.1/sys/sys/alloc.c

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


/**********************************************************************
 *   Copyright (c) Digital Equipment Corporation 1984, 1985, 1986.    *
 *   All Rights Reserved. 					      *
 *   Reference "/usr/src/COPYRIGHT" for applicable restrictions.      *
 **********************************************************************/

/*
 * SCCSID: @(#)alloc.c	3.1	7/21/87
 */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/filsys.h>
#include <sys/mount.h>
#include <sys/fblk.h>
#include <sys/conf.h>
#include <sys/buf.h>
#include <sys/inode.h>
#include <sys/ino.h>
#include <sys/dir.h>
#include <sys/user.h>
typedef	struct fblk *FBLKP;

#include <sys/seg.h>

/*
 * alloc will obtain the next available
 * free disk block from the free list of
 * the specified device.
 * The super block has up to NICFREE remembered
 * free blocks; the last of these is read to
 * obtain NICFREE more . . .
 *
 * no space on dev x/y -- when
 * the free list is exhausted.
 */
struct buf *
alloc(dev)
dev_t dev;
{
	daddr_t bno;
	register struct filsys *fp;
	register struct buf *bp;
	int	i;
	register struct fblk *fbp;
	struct filsys *fps;
	segm	mntmap;

	if ((fp = getfs(dev)) == NULL)
		goto nofs;
	saveseg5(mntmap);
	while(fp->s_flock & S_BUSY) {
		fp->s_flock |= S_WANTED;
		normalseg5();	/* can't sleep with a buffer mapped! */
		sleep((caddr_t)&fp->s_flock, PINOD);
		restorseg5(mntmap);
	}
	do {
		if(fp->s_nfree <= 0)
			goto nospace;
		if (fp->s_nfree > NICFREE) {
			prdev("Bad free count", dev);
			goto nospace;
		}
		bno = fp->s_free[--fp->s_nfree];
		if(bno == 0)
			goto nospace;
	} while (badblock(fp, bno, dev));
	if(fp->s_nfree <= 0) {
		fp->s_flock |= S_BUSY;
		normalseg5();
		bp = bread(dev, bno);
		restorseg5(mntmap);
		if ((bp->b_flags&B_ERROR) == 0)
			copyio(PADDR(bp), &fp->s_nfree, sizeof (struct fblk),
								U_RKD);
		brelse(bp);
		/*
		 * Write the superblock back, synchronously,
		 * so that the free list pointer won't point at garbage.
		 * We can still end up with dups in free if we then
		 * use some of the blocks in this freeblock, then crash
		 * without a sync.
		 */
		fp->s_fmod = 0;
		fp->s_time = time;
		normalseg5();
		bp = getblk(dev, SUPERB);
		if (!(bp->b_flags & B_MOUNT)) {	/* just a precaution... */
			prdev("SUPERB not B_MOUNT!",dev);
			panic("alloc:");
		}
		/*
		 * We've been keeping the superblock in the buffer,
		 * so all we have to do is write it out.
		 */
		bwrite(bp);
		restorseg5(mntmap);
		if (fp->s_flock & S_WANTED)
			wakeup((caddr_t)&fp->s_flock);
		fp->s_flock = 0;
		if (fp->s_nfree <=0)
			goto nospace;
	}
	normalseg5();
	bp = getblk(dev, bno);
	clrbuf(bp);
	restorseg5(mntmap);
	fp->s_fmod = 1;
	fp->s_tfree--;
	normalseg5();
	return(bp);

nospace:
	fp->s_nfree = 0;
	fp->s_tfree = 0;
	normalseg5();
	prdev("no space", dev);
	for (i = 0; i < 5; i++)
		sleep((caddr_t)&lbolt, PRIBIO);
nofs:
	u.u_error = ENOSPC;
	return(NULL);
}

/*
 * place the specified disk block
 * back on the free list of the
 * specified device.
 */
free(dev, bno)
dev_t dev;
daddr_t bno;
{
	register struct filsys *fp;
	register struct buf *bp;
	register struct fblk *fbp;
	segm	mntmap;

	if ((fp = getfs(dev)) == NULL)
		return;
	if (badblock(fp, bno, dev))
		goto ret;
	saveseg5(mntmap);
	while(fp->s_flock & S_BUSY) {
		fp->s_flock |= S_WANTED;
		normalseg5();
		sleep((caddr_t)&fp->s_flock, PINOD);
		restorseg5(mntmap);
	}
	if(fp->s_nfree <= 0) {
		fp->s_nfree = 1;
		fp->s_free[0] = 0;
	}
	if(fp->s_nfree >= NICFREE) {
		fp->s_flock |= S_BUSY;
		normalseg5();		/* normal mapping, getblk will sleep */
		bp = getblk(dev, bno);
		restorseg5(mntmap);	/* map back to SB */
		copyio(PADDR(bp), &fp->s_nfree, sizeof(struct fblk), U_WKD);
		fp->s_nfree = 0;
		normalseg5();
		bwrite(bp);
		restorseg5(mntmap);
		if (fp->s_flock & S_WANTED)
			wakeup((caddr_t)&fp->s_flock);
		fp->s_flock = 0;
	}
	fp->s_free[fp->s_nfree++] = bno;
	fp->s_tfree++;
	fp->s_fmod = 1;
ret:
	normalseg5();
}

/*
 * 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.
 *
 * bad block on dev x/y -- not in range
 */
badblock(fp, bn, dev)
register struct filsys *fp;
daddr_t bn;
dev_t dev;
{

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

/*
 * Allocate an unused I node
 * on the specified device.
 * Used with file creation.
 * The algorithm keeps up to
 * NICINOD spare I nodes in the
 * super block. When this runs out,
 * a linear search through the
 * I list is instituted to pick
 * up NICINOD more.
 */
struct inode *
ialloc(dev)
dev_t dev;
{
	register struct filsys *fp;
	register struct buf *bp;
	register struct inode *ip;
	int i;
	struct dinode *dp;
	ino_t ino;
	daddr_t adr, eadr;
	ino_t	inobas;
	int	first;
	segm	mntmap1, mntmap2;

	if ((fp = getfs(dev)) == NULL)
		goto nofs;
	saveseg5(mntmap1);
loop:
	while (fp->s_ilock & S_BUSY) {
		fp->s_ilock |= S_WANTED;
		normalseg5();
		sleep((caddr_t)&fp->s_ilock, PINOD);
		restorseg5(mntmap1);
	}
	if(fp->s_ninode > 0) {
		ino = fp->s_inode[--fp->s_ninode];
		if (ino < ROOTINO)
			goto loop;
		normalseg5();
		ip = iget(dev, ino);
		if(ip == NULL)
			return(NULL);
		if(ip->i_mode == 0) {
			for (i=0; i<NADDR; i++)
				ip->i_addr[i] = 0;
			restorseg5(mntmap1);
			fp->s_fmod = 1;
			fp->s_tinode--;
			normalseg5();
			return(ip);
		}
		/*
		 * Inode was allocated after all.
		 * Look some more.
		 */
		iput(ip);
		restorseg5(mntmap1);
		goto loop;
	}
	fp->s_ilock |= S_BUSY;
	eadr = fp->s_isize;		/* search to the end of the I-list */
	if (fp->s_nbehind < 4 * NICINOD) {
		first = 1;
		ino = fp->s_lasti;
		adr = itod(ino);
	} else {
fromtop:
		first = 0;
		ino = 1;
		adr = SUPERB+1;
		fp->s_nbehind = 0;
	}
	for (; adr < eadr; adr++)
	{
		normalseg5();
		inobas = ino;
		bp = bread(dev, adr);
		if (bp->b_flags & B_ERROR) {
			brelse(bp);
			ino += INOPB;
			restorseg5(mntmap1);
			continue;
		}
		dp = (struct dinode *) mapin(bp);
		saveseg5(mntmap2);
		for (i = 0; i < INOPB; i++) {
			if (dp->di_mode != 0)
				goto cont;
# ifdef	UCB_IHASH
			if (ifind(dev, ino))
				goto cont;
# else
			for (ip = &inode[0]; ip < &inode[ninode]; ip++)
				if(dev==ip->i_dev && ino==ip->i_number)
					goto cont;
# endif
			restorseg5(mntmap1);
			fp->s_inode[fp->s_ninode++] = ino;
			if (fp->s_ninode >= NICINOD)
				break;
			restorseg5(mntmap2);
		cont:
			ino++;
			dp++;
		}
		restorseg5(mntmap1);
		brelse(bp);
		if (fp->s_ninode >= NICINOD)
			break;
	}
	if (fp->s_ninode < NICINOD && first) {
		/*
		 * Go back and start at the beginning, searching
		 * until we get where we started from.
		 */
		eadr = itod(fp->s_lasti);
		goto fromtop;
	}
	fp->s_lasti = inobas;
	if (fp->s_ilock & S_WANTED)
		wakeup((caddr_t)&fp->s_ilock);
	fp->s_ilock = 0;
	if(fp->s_ninode > 0)
		goto loop;
	normalseg5();
	prdev("Out of inodes", dev);
nofs:
	u.u_error = ENOSPC;
	return(NULL);
}

/*
 * Free the specified I node
 * on the specified device.
 * The algorithm stores up
 * to NICINOD I nodes in the super
 * block and throws away any more.
 */
ifree(dev, ino)
dev_t dev;
ino_t ino;
{
	register struct filsys *fp;


	if ((fp = getfs(dev)) == NULL)
		return;
	fp->s_tinode++;
	if (fp->s_ilock & S_BUSY)
		goto ret;
	if (fp->s_ninode >= NICINOD) {
		if (fp->s_lasti > ino)
			fp->s_nbehind++;
	} else {
		fp->s_inode[fp->s_ninode++] = ino;
		fp->s_fmod = 1;
	}
ret:
	normalseg5();
}

/*
 * getfs maps a device number into
 * a pointer to the incore super
 * block.
 * The algorithm is a linear
 * search through the mount table.
 * A consistency check of the
 * in core free-block and i-node
 * counts.
 *
 * bad count on dev x/y -- the count
 *	check failed. At this point, all
 *	the counts are zeroed which will
 *	almost certainly lead to "no space"
 *	diagnostic
 * panic: no fs -- the device is not mounted.
 *	this "cannot happen"
 *	Or so we thought, if the pipedev isn't mounted
 *	we'll get the appropriate message, so we just do
 *	a warning and return NULL.
 */
struct filsys *
getfs(dev)
dev_t dev;
{
	register struct mount *mp;
	register struct filsys *fp;

	for(mp = &mount[0]; mp < &mount[nmount]; mp++)
	if (mp->m_inodp != NULL && mp->m_dev == dev) {
		fp = mapin(mp->m_bufp);
		if(fp->s_nfree > NICFREE || fp->s_ninode > NICINOD) {
			prdev("bad count", dev);
			fp->s_nfree = 0;
			fp->s_ninode = 0;
		}
		return(fp);
	}
	prdev("no fs", dev);
	return((struct filsys *) NULL);
}

/*
 * update is the internal name of
 * 'sync'. It goes through the disk
 * queues to initiate sandbagged IO;
 * goes through the I nodes to write
 * modified nodes; and it goes through
 * the mount table to initiate modified
 * super blocks.
 */
update()
{
	register struct inode *ip;
	register struct mount *mp;
	register struct buf *bp;
	struct filsys *fp;
	struct filsys *fpdst;
	segm	mntmap;

	if(updlock)
		return;
	updlock++;
	for(mp = &mount[0]; mp < &mount[nmount]; mp++)
		if(mp->m_inodp != NULL)
		{
			fp = (struct filsys *)mapin(mp->m_bufp);
			if(fp->s_fmod==0 || (fp->s_ilock & S_BUSY) ||
			   fp->s_flock!=0 || fp->s_ronly!=0) {
				normalseg5();
				continue;
			}
			saveseg5(mntmap);
			normalseg5();
			bp = getblk(mp->m_dev, SUPERB);
			if (bp != mp->m_bufp) {
				prdev("sblock pointer changed ",mp->m_dev);
				panic("update");
			}
			if (bp->b_flags & B_ERROR)
				continue;
			restorseg5(mntmap);
			fp->s_fmod = 0;
			fp->s_time = time;
			normalseg5();
			bwrite(bp);
		}
	for(ip = &inode[0]; ip < &inode[ninode]; 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;
	bflush(BF_FLUSH, NODEV);
}