vfs_bio.c

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

C
871
字号
/*	$NetBSD: vfs_bio.c,v 1.43.4.1 1996/06/18 19:58:04 pk Exp $	*//*- * Copyright (c) 1994 Christopher G. Demetriou * Copyright (c) 1982, 1986, 1989, 1993 *	The Regents of the University of California.  All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. * *	@(#)vfs_bio.c	8.6 (Berkeley) 1/11/94 *//* * Some references: *	Bach: The Design of the UNIX Operating System (Prentice Hall, 1986) *	Leffler, et al.: The Design and Implementation of the 4.3BSD *		UNIX Operating System (Addison Welley, 1989) */#include <sys/param.h>#include <sys/systm.h>#include <sys/proc.h>#include <sys/buf.h>#include <sys/vnode.h>#include <sys/mount.h>#include <sys/trace.h>#include <sys/malloc.h>#include <sys/resourcevar.h>#include <sys/conf.h>#include <vm/vm.h>/* Macros to clear/set/test flags. */#define	SET(t, f)	(t) |= (f)#define	CLR(t, f)	(t) &= ~(f)#define	ISSET(t, f)	((t) & (f))/* * Definitions for the buffer hash lists. */#define	BUFHASH(dvp, lbn)	\	(&bufhashtbl[((long)(dvp) / sizeof(*(dvp)) + (int)(lbn)) & bufhash])LIST_HEAD(bufhashhdr, buf) *bufhashtbl, invalhash;u_long	bufhash;/* * Insq/Remq for the buffer hash lists. */#define	binshash(bp, dp)	LIST_INSERT_HEAD(dp, bp, b_hash)#define	bremhash(bp)		LIST_REMOVE(bp, b_hash)/* * Definitions for the buffer free lists. */#define	BQUEUES		4		/* number of free buffer queues */#define	BQ_LOCKED	0		/* super-blocks &c */#define	BQ_LRU		1		/* lru, useful buffers */#define	BQ_AGE		2		/* rubbish */#define	BQ_EMPTY	3		/* buffer headers with no memory */TAILQ_HEAD(bqueues, buf) bufqueues[BQUEUES];int needbuffer;/* * Insq/Remq for the buffer free lists. */#define	binsheadfree(bp, dp)	TAILQ_INSERT_HEAD(dp, bp, b_freelist)#define	binstailfree(bp, dp)	TAILQ_INSERT_TAIL(dp, bp, b_freelist)static __inline struct buf *bio_doread __P((struct vnode *, daddr_t, int,					    struct ucred *, int));int count_lock_queue __P((void));voidbremfree(bp)	struct buf *bp;{	struct bqueues *dp = NULL;	/*	 * We only calculate the head of the freelist when removing	 * the last element of the list as that is the only time that	 * it is needed (e.g. to reset the tail pointer).	 *	 * NB: This makes an assumption about how tailq's are implemented.	 */	if (bp->b_freelist.tqe_next == NULL) {		for (dp = bufqueues; dp < &bufqueues[BQUEUES]; dp++)			if (dp->tqh_last == &bp->b_freelist.tqe_next)				break;		if (dp == &bufqueues[BQUEUES])			panic("bremfree: lost tail");	}	TAILQ_REMOVE(dp, bp, b_freelist);}/* * Initialize buffers and hash links for buffers. */voidbufinit(){	register struct buf *bp;	struct bqueues *dp;	register int i;	int base, residual;	for (dp = bufqueues; dp < &bufqueues[BQUEUES]; dp++)		TAILQ_INIT(dp);	bufhashtbl = hashinit(nbuf, M_CACHE, &bufhash);	base = bufpages / nbuf;	residual = bufpages % nbuf;	for (i = 0; i < nbuf; i++) {		bp = &buf[i];		bzero((char *)bp, sizeof *bp);		bp->b_dev = NODEV;		bp->b_rcred = NOCRED;		bp->b_wcred = NOCRED;		bp->b_vnbufs.le_next = NOLIST;		bp->b_data = buffers + i * MAXBSIZE;		if (i < residual)			bp->b_bufsize = (base + 1) * CLBYTES;		else			bp->b_bufsize = base * CLBYTES;		bp->b_flags = B_INVAL;		dp = bp->b_bufsize ? &bufqueues[BQ_AGE] : &bufqueues[BQ_EMPTY];		binsheadfree(bp, dp);		binshash(bp, &invalhash);	}}static __inline struct buf *bio_doread(vp, blkno, size, cred, async)	struct vnode *vp;	daddr_t blkno;	int size;	struct ucred *cred;	int async;{	register struct buf *bp;	bp = getblk(vp, blkno, size, 0, 0);	/*	 * If buffer does not have data valid, start a read.	 * Note that if buffer is B_INVAL, getblk() won't return it.	 * Therefore, it's valid if it's I/O has completed or been delayed.	 */	if (!ISSET(bp->b_flags, (B_DONE | B_DELWRI))) {		/* Start I/O for the buffer (keeping credentials). */		SET(bp->b_flags, B_READ | async);		if (cred != NOCRED && bp->b_rcred == NOCRED) {			crhold(cred);			bp->b_rcred = cred;		}		VOP_STRATEGY(bp);		/* Pay for the read. */		curproc->p_stats->p_ru.ru_inblock++;		/* XXX */	} else if (async) {		brelse(bp);	}	return (bp);}/* * Read a disk block. * This algorithm described in Bach (p.54). */intbread(vp, blkno, size, cred, bpp)	struct vnode *vp;	daddr_t blkno;	int size;	struct ucred *cred;	struct buf **bpp;{	register struct buf *bp;	/* Get buffer for block. */	bp = *bpp = bio_doread(vp, blkno, size, cred, 0);	/* Wait for the read to complete, and return result. */	return (biowait(bp));}/* * Read-ahead multiple disk blocks. The first is sync, the rest async. * Trivial modification to the breada algorithm presented in Bach (p.55). */intbreadn(vp, blkno, size, rablks, rasizes, nrablks, cred, bpp)	struct vnode *vp;	daddr_t blkno; int size;	daddr_t rablks[]; int rasizes[];	int nrablks;	struct ucred *cred;	struct buf **bpp;{	register struct buf *bp;	int i;	bp = *bpp = bio_doread(vp, blkno, size, cred, 0);	/*	 * For each of the read-ahead blocks, start a read, if necessary.	 */	for (i = 0; i < nrablks; i++) {		/* If it's in the cache, just go on to next one. */		if (incore(vp, rablks[i]))			continue;		/* Get a buffer for the read-ahead block */		(void) bio_doread(vp, rablks[i], rasizes[i], cred, B_ASYNC);	}	/* Otherwise, we had to start a read for it; wait until it's valid. */	return (biowait(bp));}/* * Read with single-block read-ahead.  Defined in Bach (p.55), but * implemented as a call to breadn(). * XXX for compatibility with old file systems. */intbreada(vp, blkno, size, rablkno, rabsize, cred, bpp)	struct vnode *vp;	daddr_t blkno; int size;	daddr_t rablkno; int rabsize;	struct ucred *cred;	struct buf **bpp;{	return (breadn(vp, blkno, size, &rablkno, &rabsize, 1, cred, bpp));	}/* * Block write.  Described in Bach (p.56) */intbwrite(bp)	struct buf *bp;{	int rv, sync, wasdelayed, s;	/*	 * Remember buffer type, to switch on it later.  If the write was	 * synchronous, but the file system was mounted with MNT_ASYNC,	 * convert it to a delayed write.  	 * XXX note that this relies on delayed tape writes being converted	 * to async, not sync writes (which is safe, but ugly).	 */	sync = !ISSET(bp->b_flags, B_ASYNC);	if (sync && bp->b_vp && bp->b_vp->v_mount &&	    ISSET(bp->b_vp->v_mount->mnt_flag, MNT_ASYNC)) {		bdwrite(bp);		return (0);	}	wasdelayed = ISSET(bp->b_flags, B_DELWRI);	CLR(bp->b_flags, (B_READ | B_DONE | B_ERROR | B_DELWRI));	s = splbio();	if (!sync) {		/*		 * If not synchronous, pay for the I/O operation and make		 * sure the buf is on the correct vnode queue.  We have		 * to do this now, because if we don't, the vnode may not		 * be properly notified that its I/O has completed.		 */		if (wasdelayed)			reassignbuf(bp, bp->b_vp);		else			curproc->p_stats->p_ru.ru_oublock++;	}	/* Initiate disk write.  Make sure the appropriate party is charged. */	bp->b_vp->v_numoutput++;	splx(s);	SET(bp->b_flags, B_WRITEINPROG);	VOP_STRATEGY(bp);	if (sync) {		/*		 * If I/O was synchronous, wait for it to complete.		 */		rv = biowait(bp);		/*		 * Pay for the I/O operation, if it's not been paid for, and		 * make sure it's on the correct vnode queue. (async operatings		 * were payed for above.)		 */		s = splbio();		if (wasdelayed)			reassignbuf(bp, bp->b_vp);		else			curproc->p_stats->p_ru.ru_oublock++;		splx(s);		/* Release the buffer. */		brelse(bp);		return (rv);	} else {		return (0);	}}intvn_bwrite(v)	void *v;{	struct vop_bwrite_args *ap = v;	return (bwrite(ap->a_bp));}/* * Delayed write. * * The buffer is marked dirty, but is not queued for I/O. * This routine should be used when the buffer is expected * to be modified again soon, typically a small write that * partially fills a buffer. * * NB: magnetic tapes cannot be delayed; they must be * written in the order that the writes are requested. * * Described in Leffler, et al. (pp. 208-213). */voidbdwrite(bp)	struct buf *bp;{	int s;	/*	 * If the block hasn't been seen before:	 *	(1) Mark it as having been seen,	 *	(2) Charge for the write,	 *	(3) Make sure it's on its vnode's correct block list.	 */	if (!ISSET(bp->b_flags, B_DELWRI)) {		SET(bp->b_flags, B_DELWRI);		curproc->p_stats->p_ru.ru_oublock++;	/* XXX */		s = splbio();		reassignbuf(bp, bp->b_vp);		splx(s);	}	/* If this is a tape block, write the block now. */	if (bdevsw[major(bp->b_dev)].d_type == D_TAPE) {		bawrite(bp);		return;	}	/* Otherwise, the "write" is done, so mark and release the buffer. */	CLR(bp->b_flags, B_NEEDCOMMIT);	SET(bp->b_flags, B_DONE);	brelse(bp);}/* * Asynchronous block write; just an asynchronous bwrite(). */voidbawrite(bp)	struct buf *bp;{	SET(bp->b_flags, B_ASYNC);	VOP_BWRITE(bp);}/* * Release a buffer on to the free lists. * Described in Bach (p. 46). */voidbrelse(bp)	struct buf *bp;{	struct bqueues *bufq;	int s;	/* Wake up any processes waiting for any buffer to become free. */	if (needbuffer) {		needbuffer = 0;		wakeup(&needbuffer);	}	/* Wake up any proceeses waiting for _this_ buffer to become free. */	if (ISSET(bp->b_flags, B_WANTED)) {		CLR(bp->b_flags, B_WANTED);		wakeup(bp);	}	/* Block disk interrupts. */	s = splbio();	/*	 * Determine which queue the buffer should be on, then put it there.

⌨️ 快捷键说明

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