vfs_bio.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 993 行 · 第 1/2 页
C
993 行
/* $NetBSD: vfs_bio.c,v 1.73 2000/11/27 08:39:43 chs 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 <uvm/uvm.h>#include <miscfs/specfs/specdev.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) >> 8) + (int)(lbn)) & bufhash])LIST_HEAD(bufhashhdr, buf) *bufhashtbl, invalhash;u_long bufhash;struct bio_ops bioops; /* I/O operation notification *//* * 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;/* * Buffer pool for I/O buffers. */struct pool bufpool;/* * 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)#ifndef OSKITstatic __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;{ int s = splbio(); 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); splx(s);}/* * Initialize buffers and hash links for buffers. */voidbufinit(){ struct buf *bp; struct bqueues *dp; int i; int base, residual; /* * Initialize the buffer pool. This pool is used for buffers * which are strictly I/O control blocks, not buffer cache * buffers. */ pool_init(&bufpool, sizeof(struct buf), 0, 0, 0, "bufpl", 0, NULL, NULL, M_DEVBUF); for (dp = bufqueues; dp < &bufqueues[BQUEUES]; dp++) TAILQ_INIT(dp); bufhashtbl = hashinit(nbuf, HASH_LIST, M_CACHE, M_WAITOK, &bufhash); base = bufpages / nbuf; residual = bufpages % nbuf; for (i = 0; i < nbuf; i++) { bp = &buf[i]; memset((char *)bp, 0, sizeof(*bp)); bp->b_dev = NODEV; bp->b_vnbufs.le_next = NOLIST; LIST_INIT(&bp->b_dep); bp->b_data = buffers + i * MAXBSIZE; if (i < residual) bp->b_bufsize = (base + 1) * PAGE_SIZE; else bp->b_bufsize = base * PAGE_SIZE; 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;{ struct buf *bp; struct proc *p = (curproc != NULL ? curproc : &proc0); /* XXX */ 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. */ SET(bp->b_flags, B_READ | async); VOP_STRATEGY(bp); /* Pay for the read. */ p->p_stats->p_ru.ru_inblock++; } 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;{ struct buf *bp; /* Get buffer for block. */ bp = *bpp = bio_doread(vp, blkno, size, cred, 0); /* * Delayed write buffers are found in the cache and have * valid contents. Also, B_ERROR is not set, otherwise * getblk() would not have returned them. */ if (ISSET(bp->b_flags, B_DONE|B_DELWRI)) return (0); /* * Otherwise, we had to start a read for it; wait until * it's valid and return the 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;{ 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); } /* * Delayed write buffers are found in the cache and have * valid contents. Also, B_ERROR is not set, otherwise * getblk() would not have returned them. */ if (ISSET(bp->b_flags, B_DONE|B_DELWRI)) return (0); /* * Otherwise, we had to start a read for it; wait until * it's valid and return the result. */ 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; struct proc *p = (curproc != NULL ? curproc : &proc0); /* XXX */ struct vnode *vp; struct mount *mp; /* * 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); } /* * Collect statistics on synchronous and asynchronous writes. * Writes to block devices are charged to their associated * filesystem (if any). */ if ((vp = bp->b_vp) != NULL) { if (vp->v_type == VBLK) mp = vp->v_specmountpoint; else mp = vp->v_mount; if (mp != NULL) { if (sync) mp->mnt_stat.f_syncwrites++; else mp->mnt_stat.f_asyncwrites++; } } wasdelayed = ISSET(bp->b_flags, B_DELWRI); s = splbio(); CLR(bp->b_flags, (B_READ | B_DONE | B_ERROR | B_DELWRI)); /* * Pay for the I/O operation and make sure the buf is on the correct * vnode queue. */ if (wasdelayed) reassignbuf(bp, bp->b_vp); else p->p_stats->p_ru.ru_oublock++; /* Initiate disk write. Make sure the appropriate party is charged. */ bp->b_vp->v_numoutput++; splx(s); VOP_STRATEGY(bp); if (sync) { /* If I/O was synchronous, wait for it to complete. */ rv = biowait(bp); /* 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;{ struct proc *p = (curproc != NULL ? curproc : &proc0); /* XXX */ int s; /* If this is a tape block, write the block now. */ /* XXX NOTE: the memory filesystem usurpes major device */ /* XXX number 255, which is a bad idea. */ if (bp->b_dev != NODEV && major(bp->b_dev) != 255 && /* XXX - MFS buffers! */ bdevsw[major(bp->b_dev)].d_type == D_TAPE) { bawrite(bp); return; } /* * 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. */ s = splbio(); if (!ISSET(bp->b_flags, B_DELWRI)) { SET(bp->b_flags, B_DELWRI); p->p_stats->p_ru.ru_oublock++; reassignbuf(bp, bp->b_vp); } /* Otherwise, the "write" is done, so mark and release the buffer. */ CLR(bp->b_flags, B_NEEDCOMMIT|B_DONE); splx(s); brelse(bp);}/* * Asynchronous block write; just an asynchronous bwrite(). */voidbawrite(bp) struct buf *bp;{ SET(bp->b_flags, B_ASYNC); VOP_BWRITE(bp);}/* * Ordered block write; asynchronous, but I/O will occur in order queued. */voidbowrite(bp) struct buf *bp;{ SET(bp->b_flags, B_ASYNC | B_ORDERED); VOP_BWRITE(bp);}/* * Same as first half of bdwrite, mark buffer dirty, but do not release it. */voidbdirty(bp) struct buf *bp;{ struct proc *p = (curproc != NULL ? curproc : &proc0); /* XXX */ int s; s = splbio(); CLR(bp->b_flags, B_AGE); if (!ISSET(bp->b_flags, B_DELWRI)) { SET(bp->b_flags, B_DELWRI); p->p_stats->p_ru.ru_oublock++; reassignbuf(bp, bp->b_vp); } splx(s);}/* * Release a buffer on to the free lists. * Described in Bach (p. 46).
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?