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 + -
显示快捷键?