vfs_bio.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 993 行 · 第 1/2 页

C
993
字号
 */voidbrelse(bp)	struct buf *bp;{	struct bqueues *bufq;	int s;	KASSERT(ISSET(bp->b_flags, B_BUSY));	/* Wake up any processes waiting for any buffer to become free. */	if (needbuffer) {		needbuffer = 0;		wakeup(&needbuffer);	}	/* Block disk interrupts. */	s = splbio();	/* Wake up any proceeses waiting for _this_ buffer to become free. */	if (ISSET(bp->b_flags, B_WANTED)) {		CLR(bp->b_flags, B_WANTED|B_AGE);		wakeup(bp);	}	/*	 * Determine which queue the buffer should be on, then put it there.	 */	/* If it's locked, don't report an error; try again later. */	if (ISSET(bp->b_flags, (B_LOCKED|B_ERROR)) == (B_LOCKED|B_ERROR))		CLR(bp->b_flags, B_ERROR);	/* If it's not cacheable, or an error, mark it invalid. */	if (ISSET(bp->b_flags, (B_NOCACHE|B_ERROR)))		SET(bp->b_flags, B_INVAL);	if (ISSET(bp->b_flags, B_VFLUSH)) {		/*		 * This is a delayed write buffer that was just flushed to		 * disk.  It is still on the LRU queue.  If it's become		 * invalid, then we need to move it to a different queue;		 * otherwise leave it in its current position.		 */		CLR(bp->b_flags, B_VFLUSH);		if (!ISSET(bp->b_flags, B_ERROR|B_INVAL|B_LOCKED|B_AGE))			goto already_queued;		else			bremfree(bp);	}	if ((bp->b_bufsize <= 0) || ISSET(bp->b_flags, B_INVAL)) {		/*		 * If it's invalid or empty, dissociate it from its vnode		 * and put on the head of the appropriate queue.		 */		if (LIST_FIRST(&bp->b_dep) != NULL && bioops.io_deallocate)			(*bioops.io_deallocate)(bp);		CLR(bp->b_flags, B_DONE|B_DELWRI);		if (bp->b_vp) {			reassignbuf(bp, bp->b_vp);			brelvp(bp);		}		if (bp->b_bufsize <= 0)			/* no data */			bufq = &bufqueues[BQ_EMPTY];		else			/* invalid data */			bufq = &bufqueues[BQ_AGE];		binsheadfree(bp, bufq);	} else {		/*		 * It has valid data.  Put it on the end of the appropriate		 * queue, so that it'll stick around for as long as possible.		 * If buf is AGE, but has dependencies, must put it on last		 * bufqueue to be scanned, ie LRU. This protects against the		 * livelock where BQ_AGE only has buffers with dependencies,		 * and we thus never get to the dependent buffers in BQ_LRU.		 */		if (ISSET(bp->b_flags, B_LOCKED))			/* locked in core */			bufq = &bufqueues[BQ_LOCKED];		else if (!ISSET(bp->b_flags, B_AGE))			/* valid data */			bufq = &bufqueues[BQ_LRU];		else {			/* stale but valid data */			int has_deps;			if (LIST_FIRST(&bp->b_dep) != NULL &&			    bioops.io_countdeps)				has_deps = (*bioops.io_countdeps)(bp, 0);			else				has_deps = 0;			bufq = has_deps ? &bufqueues[BQ_LRU] :			    &bufqueues[BQ_AGE];		}		binstailfree(bp, bufq);	}already_queued:	/* Unlock the buffer. */	CLR(bp->b_flags, B_AGE|B_ASYNC|B_BUSY|B_NOCACHE|B_ORDERED);	SET(bp->b_flags, B_CACHE);	/* Allow disk interrupts. */	splx(s);}/* * Determine if a block is in the cache. * Just look on what would be its hash chain.  If it's there, return * a pointer to it, unless it's marked invalid.  If it's marked invalid, * we normally don't return the buffer, unless the caller explicitly * wants us to. */struct buf *incore(vp, blkno)	struct vnode *vp;	daddr_t blkno;{	struct buf *bp;	bp = BUFHASH(vp, blkno)->lh_first;	/* Search hash chain */	for (; bp != NULL; bp = bp->b_hash.le_next) {		if (bp->b_lblkno == blkno && bp->b_vp == vp &&		    !ISSET(bp->b_flags, B_INVAL))		return (bp);	}	return (NULL);}/* * Get a block of requested size that is associated with * a given vnode and block offset. If it is found in the * block cache, mark it as having been found, make it busy * and return it. Otherwise, return an empty block of the * correct size. It is up to the caller to insure that the * cached blocks be of the correct size. */struct buf *getblk(vp, blkno, size, slpflag, slptimeo)	struct vnode *vp;	daddr_t blkno;	int size, slpflag, slptimeo;{	struct buf *bp;	int s, err;start:	bp = incore(vp, blkno);	if (bp != NULL) {		s = splbio();		if (ISSET(bp->b_flags, B_BUSY)) {			if (curproc == uvm.pagedaemon_proc) {				splx(s);				return NULL;			}			SET(bp->b_flags, B_WANTED);			err = tsleep(bp, slpflag | (PRIBIO + 1), "getblk",				     slptimeo);			splx(s);			if (err)				return (NULL);			goto start;		}#ifdef DIAGNOSTIC		if (ISSET(bp->b_flags, B_DONE|B_DELWRI) && bp->b_bcount < size)			panic("getblk: block size invariant failed");#endif		SET(bp->b_flags, B_BUSY);		bremfree(bp);		splx(s);	} else {		if ((bp = getnewbuf(slpflag, slptimeo)) == NULL)			goto start;		binshash(bp, BUFHASH(vp, blkno));		bp->b_blkno = bp->b_lblkno = bp->b_rawblkno = blkno;		s = splbio();		bgetvp(vp, bp);		splx(s);	}	allocbuf(bp, size);	return (bp);}/* * Get an empty, disassociated buffer of given size. */struct buf *geteblk(size)	int size;{	struct buf *bp; 	while ((bp = getnewbuf(0, 0)) == 0)		;	SET(bp->b_flags, B_INVAL);	binshash(bp, &invalhash);	allocbuf(bp, size);	return (bp);}/* * Expand or contract the actual memory allocated to a buffer. * * If the buffer shrinks, data is lost, so it's up to the * caller to have written it out *first*; this routine will not * start a write.  If the buffer grows, it's the callers * responsibility to fill out the buffer's additional contents. */voidallocbuf(bp, size)	struct buf *bp;	int size;{	struct buf *nbp;	vsize_t desired_size;	int s;	desired_size = round_page((vsize_t)size);	if (desired_size > MAXBSIZE)		panic("allocbuf: buffer larger than MAXBSIZE requested");	if (bp->b_bufsize == desired_size)		goto out;	/*	 * If the buffer is smaller than the desired size, we need to snarf	 * it from other buffers.  Get buffers (via getnewbuf()), and	 * steal their pages.	 */	while (bp->b_bufsize < desired_size) {		int amt;		/* find a buffer */		while ((nbp = getnewbuf(0, 0)) == NULL)			;		SET(nbp->b_flags, B_INVAL);		binshash(nbp, &invalhash);		/* and steal its pages, up to the amount we need */		amt = min(nbp->b_bufsize, (desired_size - bp->b_bufsize));		pagemove((nbp->b_data + nbp->b_bufsize - amt),			 bp->b_data + bp->b_bufsize, amt);		bp->b_bufsize += amt;		nbp->b_bufsize -= amt;		/* reduce transfer count if we stole some data */		if (nbp->b_bcount > nbp->b_bufsize)			nbp->b_bcount = nbp->b_bufsize;#ifdef DIAGNOSTIC		if (nbp->b_bufsize < 0)			panic("allocbuf: negative bufsize");#endif		brelse(nbp);	}	/*	 * If we want a buffer smaller than the current size,	 * shrink this buffer.  Grab a buf head from the EMPTY queue,	 * move a page onto it, and put it on front of the AGE queue.	 * If there are no free buffer headers, leave the buffer alone.	 */	if (bp->b_bufsize > desired_size) {		s = splbio();		if ((nbp = bufqueues[BQ_EMPTY].tqh_first) == NULL) {			/* No free buffer head */			splx(s);			goto out;		}		bremfree(nbp);		SET(nbp->b_flags, B_BUSY);		splx(s);		/* move the page to it and note this change */		pagemove(bp->b_data + desired_size,		    nbp->b_data, bp->b_bufsize - desired_size);		nbp->b_bufsize = bp->b_bufsize - desired_size;		bp->b_bufsize = desired_size;		nbp->b_bcount = 0;		SET(nbp->b_flags, B_INVAL);		/* release the newly-filled buffer and leave */		brelse(nbp);	}out:	bp->b_bcount = size;}/* * Find a buffer which is available for use. * Select something from a free list. * Preference is to AGE list, then LRU list.     */struct buf *getnewbuf(slpflag, slptimeo)	int slpflag, slptimeo;{	struct buf *bp;	int s;start:	s = splbio();	if ((bp = bufqueues[BQ_AGE].tqh_first) != NULL ||	    (bp = bufqueues[BQ_LRU].tqh_first) != NULL) {		bremfree(bp);	} else {		/* wait for a free buffer of any kind */		needbuffer = 1;		tsleep(&needbuffer, slpflag|(PRIBIO+1), "getnewbuf", slptimeo);		splx(s);		return (NULL);	}	if (ISSET(bp->b_flags, B_VFLUSH)) {		/*		 * This is a delayed write buffer being flushed to disk.  Make		 * sure it gets aged out of the queue when it's finished, and		 * leave it off the LRU queue.		 */		CLR(bp->b_flags, B_VFLUSH);		SET(bp->b_flags, B_AGE);		splx(s);		goto start;	}	/* Buffer is no longer on free lists. */	SET(bp->b_flags, B_BUSY);	/* If buffer was a delayed write, start it, and go back to the top. */	if (ISSET(bp->b_flags, B_DELWRI)) {		splx(s);		/*		 * This buffer has gone through the LRU, so make sure it gets		 * reused ASAP.		 */		SET(bp->b_flags, B_AGE);		bawrite(bp);		goto start;	}	/* disassociate us from our vnode, if we had one... */	if (bp->b_vp)		brelvp(bp);	splx(s);	if (LIST_FIRST(&bp->b_dep) != NULL && bioops.io_deallocate)		(*bioops.io_deallocate)(bp);	/* clear out various other fields */	bp->b_flags = B_BUSY;	bp->b_dev = NODEV;	bp->b_blkno = bp->b_lblkno = bp->b_rawblkno = 0;	bp->b_iodone = 0;	bp->b_error = 0;	bp->b_resid = 0;	bp->b_bcount = 0;		bremhash(bp);	return (bp); }#endif /*OSKIT*//* * Wait for operations on the buffer to complete. * When they do, extract and return the I/O's error value. */intbiowait(bp)	struct buf *bp;{	int s;		s = splbio();	while (!ISSET(bp->b_flags, B_DONE))		tsleep(bp, PRIBIO + 1, "biowait", 0);	splx(s);	/* check for interruption of I/O (e.g. via NFS), then errors. */	if (ISSET(bp->b_flags, B_EINTR)) {		CLR(bp->b_flags, B_EINTR);		return (EINTR);	} else if (ISSET(bp->b_flags, B_ERROR))		return (bp->b_error ? bp->b_error : EIO);	else		return (0);}/* * Mark I/O complete on a buffer. * * If a callback has been requested, e.g. the pageout * daemon, do so. Otherwise, awaken waiting processes. * * [ Leffler, et al., says on p.247: *	"This routine wakes up the blocked process, frees the buffer *	for an asynchronous write, or, for a request by the pagedaemon *	process, invokes a procedure specified in the buffer structure" ] * * In real life, the pagedaemon (or other system processes) wants * to do async stuff to, and doesn't want the buffer brelse()'d. * (for swap pager, that puts swap buffers on the free lists (!!!), * for the vn device, that puts malloc'd buffers on the free lists!) */voidbiodone(bp)	struct buf *bp;{	int s = splbio();	if (ISSET(bp->b_flags, B_DONE))		panic("biodone already");	SET(bp->b_flags, B_DONE);		/* note that it's done */	if (LIST_FIRST(&bp->b_dep) != NULL && bioops.io_complete)		(*bioops.io_complete)(bp);	if (!ISSET(bp->b_flags, B_READ))	/* wake up reader */		vwakeup(bp);	if (ISSET(bp->b_flags, B_CALL)) {	/* if necessary, call out */		CLR(bp->b_flags, B_CALL);	/* but note callout done */		(*bp->b_iodone)(bp);	} else {		if (ISSET(bp->b_flags, B_ASYNC))	/* if async, release */			brelse(bp);		else {				/* or just wakeup the buffer */			CLR(bp->b_flags, B_WANTED);			wakeup(bp);		}	}	splx(s);}#ifndef OSKIT/* * Return a count of buffers on the "locked" queue. */intcount_lock_queue(){	struct buf *bp;	int n = 0;	for (bp = bufqueues[BQ_LOCKED].tqh_first; bp;	    bp = bp->b_freelist.tqe_next)		n++;	return (n);}#ifdef DEBUG/* * Print out statistics on the current allocation of the buffer pool. * Can be enabled to print out on every ``sync'' by setting "syncprt" * in vfs_syscalls.c using sysctl. */voidvfs_bufstats(){	int s, i, j, count;	struct buf *bp;	struct bqueues *dp;	int counts[(MAXBSIZE / PAGE_SIZE) + 1];	static char *bname[BQUEUES] = { "LOCKED", "LRU", "AGE", "EMPTY" };	for (dp = bufqueues, i = 0; dp < &bufqueues[BQUEUES]; dp++, i++) {		count = 0;		for (j = 0; j <= MAXBSIZE/PAGE_SIZE; j++)			counts[j] = 0;		s = splbio();		for (bp = dp->tqh_first; bp; bp = bp->b_freelist.tqe_next) {			counts[bp->b_bufsize/PAGE_SIZE]++;			count++;		}		splx(s);		printf("%s: total-%d", bname[i], count);		for (j = 0; j <= MAXBSIZE/PAGE_SIZE; j++)			if (counts[j] != 0)				printf(", %d-%d", j * PAGE_SIZE, counts[j]);		printf("\n");	}}#endif /* DEBUG */#endif /*OSKIT*/

⌨️ 快捷键说明

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