Annotation of prex/usr/server/fs/vfs/vfs_bio.c, Revision 1.1
1.1 ! nbrk 1: /*
! 2: * Copyright (c) 2005-2007, Kohsuke Ohtani
! 3: * All rights reserved.
! 4: *
! 5: * Redistribution and use in source and binary forms, with or without
! 6: * modification, are permitted provided that the following conditions
! 7: * are met:
! 8: * 1. Redistributions of source code must retain the above copyright
! 9: * notice, this list of conditions and the following disclaimer.
! 10: * 2. Redistributions in binary form must reproduce the above copyright
! 11: * notice, this list of conditions and the following disclaimer in the
! 12: * documentation and/or other materials provided with the distribution.
! 13: * 3. Neither the name of the author nor the names of any co-contributors
! 14: * may be used to endorse or promote products derived from this software
! 15: * without specific prior written permission.
! 16: *
! 17: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
! 18: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 19: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 20: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
! 21: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 22: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 23: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 24: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 25: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 26: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 27: * SUCH DAMAGE.
! 28: */
! 29:
! 30: /*
! 31: * bio.c - buffered I/O operations
! 32: */
! 33:
! 34: /*
! 35: * References:
! 36: * Bach: The Design of the UNIX Operating System (Prentice Hall, 1986)
! 37: */
! 38:
! 39: #include <prex/prex.h>
! 40: #include <sys/list.h>
! 41: #include <sys/param.h>
! 42: #include <sys/buf.h>
! 43:
! 44: #include <limits.h>
! 45: #include <unistd.h>
! 46: #include <stdlib.h>
! 47: #include <string.h>
! 48:
! 49: #include "vfs.h"
! 50:
! 51: /* number of buffer cache */
! 52: #define NBUFS CONFIG_BUF_CACHE
! 53:
! 54: /* macros to clear/set/test flags. */
! 55: #define SET(t, f) (t) |= (f)
! 56: #define CLR(t, f) (t) &= ~(f)
! 57: #define ISSET(t, f) ((t) & (f))
! 58:
! 59: /*
! 60: * Global lock to access all buffer headers and lists.
! 61: */
! 62: #if CONFIG_FS_THREADS > 1
! 63: static mutex_t bio_lock = MUTEX_INITIALIZER;
! 64: #define BIO_LOCK() mutex_lock(&bio_lock)
! 65: #define BIO_UNLOCK() mutex_unlock(&bio_lock)
! 66: #else
! 67: #define BIO_LOCK()
! 68: #define BIO_UNLOCK()
! 69: #endif
! 70:
! 71:
! 72: /* set of buffers */
! 73: static char buffers[NBUFS][BSIZE];
! 74:
! 75: static struct buf buf_table[NBUFS];
! 76: static struct list free_list = LIST_INIT(free_list);
! 77:
! 78: static sem_t free_sem;
! 79:
! 80:
! 81: /*
! 82: * Insert buffer to the head of free list
! 83: */
! 84: static void
! 85: bio_insert_head(struct buf *bp)
! 86: {
! 87:
! 88: list_insert(&free_list, &bp->b_link);
! 89: sem_post(&free_sem);
! 90: }
! 91:
! 92: /*
! 93: * Insert buffer to the tail of free list
! 94: */
! 95: static void
! 96: bio_insert_tail(struct buf *bp)
! 97: {
! 98:
! 99: list_insert(list_prev(&free_list), &bp->b_link);
! 100: sem_post(&free_sem);
! 101: }
! 102:
! 103: /*
! 104: * Remove buffer from free list
! 105: */
! 106: static void
! 107: bio_remove(struct buf *bp)
! 108: {
! 109:
! 110: sem_wait(&free_sem, 0);
! 111: ASSERT(!list_empty(&free_list));
! 112: list_remove(&bp->b_link);
! 113: }
! 114:
! 115: /*
! 116: * Remove buffer from the head of free list
! 117: */
! 118: static struct buf *
! 119: bio_remove_head(void)
! 120: {
! 121: struct buf *bp;
! 122:
! 123: sem_wait(&free_sem, 0);
! 124: ASSERT(!list_empty(&free_list));
! 125: bp = list_entry(list_first(&free_list), struct buf, b_link);
! 126: list_remove(&bp->b_link);
! 127: return bp;
! 128: }
! 129:
! 130: /*
! 131: * Determine if a block is in the cache.
! 132: */
! 133: static struct buf *
! 134: incore(dev_t dev, int blkno)
! 135: {
! 136: struct buf *bp;
! 137: int i;
! 138:
! 139: for (i = 0; i < NBUFS; i++) {
! 140: bp = &buf_table[i];
! 141: if (bp->b_blkno == blkno && bp->b_dev == dev &&
! 142: !ISSET(bp->b_flags, B_INVAL))
! 143: return bp;
! 144: }
! 145: return NULL;
! 146: }
! 147:
! 148: /*
! 149: * Assign a buffer for the given block.
! 150: *
! 151: * The block is selected from the buffer list with LRU
! 152: * algorithm. If the appropriate block already exists in the
! 153: * block list, return it. Otherwise, the least recently used
! 154: * block is used.
! 155: */
! 156: struct buf *
! 157: getblk(dev_t dev, int blkno)
! 158: {
! 159: struct buf *bp;
! 160:
! 161: DPRINTF(VFSDB_BIO, ("getblk: dev=%x blkno=%d\n", dev, blkno));
! 162: start:
! 163: BIO_LOCK();
! 164: bp = incore(dev, blkno);
! 165: if (bp != NULL) {
! 166: /* Block found in cache. */
! 167: if (ISSET(bp->b_flags, B_BUSY)) {
! 168: BIO_UNLOCK();
! 169: mutex_lock(&bp->b_lock);
! 170: mutex_unlock(&bp->b_lock);
! 171: /* Scan again if it's busy */
! 172: goto start;
! 173: }
! 174: bio_remove(bp);
! 175: SET(bp->b_flags, B_BUSY);
! 176: } else {
! 177: bp = bio_remove_head();
! 178: if (ISSET(bp->b_flags, B_DELWRI)) {
! 179: BIO_UNLOCK();
! 180: bwrite(bp);
! 181: goto start;
! 182: }
! 183: bp->b_flags = B_BUSY;
! 184: bp->b_dev = dev;
! 185: bp->b_blkno = blkno;
! 186: }
! 187: mutex_lock(&bp->b_lock);
! 188: BIO_UNLOCK();
! 189: DPRINTF(VFSDB_BIO, ("getblk: done bp=%x\n", bp));
! 190: return bp;
! 191: }
! 192:
! 193: /*
! 194: * Release a buffer, with no I/O implied.
! 195: */
! 196: void
! 197: brelse(struct buf *bp)
! 198: {
! 199: ASSERT(ISSET(bp->b_flags, B_BUSY));
! 200: DPRINTF(VFSDB_BIO, ("brelse: bp=%x dev=%x blkno=%d\n",
! 201: bp, bp->b_dev, bp->b_blkno));
! 202:
! 203: BIO_LOCK();
! 204: CLR(bp->b_flags, B_BUSY);
! 205: mutex_unlock(&bp->b_lock);
! 206: if (ISSET(bp->b_flags, B_INVAL))
! 207: bio_insert_head(bp);
! 208: else
! 209: bio_insert_tail(bp);
! 210: BIO_UNLOCK();
! 211: }
! 212:
! 213: /*
! 214: * Block read with cache.
! 215: * @dev: device id to read from.
! 216: * @blkno: block number.
! 217: * @buf: buffer pointer to be returned.
! 218: *
! 219: * An actual read operation is done only when the cached
! 220: * buffer is dirty.
! 221: */
! 222: int
! 223: bread(dev_t dev, int blkno, struct buf **bpp)
! 224: {
! 225: struct buf *bp;
! 226: size_t size;
! 227: int err;
! 228:
! 229: DPRINTF(VFSDB_BIO, ("bread: dev=%x blkno=%d\n", dev, blkno));
! 230: bp = getblk(dev, blkno);
! 231:
! 232: if (!ISSET(bp->b_flags, (B_DONE | B_DELWRI))) {
! 233: size = BSIZE;
! 234: err = device_read((device_t)dev, bp->b_data, &size, blkno);
! 235: if (err) {
! 236: DPRINTF(VFSDB_BIO, ("bread: i/o error\n"));
! 237: brelse(bp);
! 238: return err;
! 239: }
! 240: }
! 241: CLR(bp->b_flags, B_INVAL);
! 242: SET(bp->b_flags, (B_READ | B_DONE));
! 243: DPRINTF(VFSDB_BIO, ("bread: done bp=%x\n\n", bp));
! 244: *bpp = bp;
! 245: return 0;
! 246: }
! 247:
! 248: /*
! 249: * Block write with cache.
! 250: * @buf: buffer to write.
! 251: *
! 252: * The data is copied to the buffer.
! 253: * Then release the buffer.
! 254: */
! 255: int
! 256: bwrite(struct buf *bp)
! 257: {
! 258: size_t size;
! 259: int err;
! 260:
! 261: ASSERT(ISSET(bp->b_flags, B_BUSY));
! 262: DPRINTF(VFSDB_BIO, ("bwrite: dev=%x blkno=%d\n", bp->b_dev,
! 263: bp->b_blkno));
! 264:
! 265: BIO_LOCK();
! 266: CLR(bp->b_flags, (B_READ | B_DONE | B_DELWRI));
! 267: BIO_UNLOCK();
! 268:
! 269: size = BSIZE;
! 270: err = device_write((device_t)bp->b_dev, bp->b_data, &size,
! 271: bp->b_blkno);
! 272: if (err)
! 273: return err;
! 274: BIO_LOCK();
! 275: SET(bp->b_flags, B_DONE);
! 276: BIO_UNLOCK();
! 277: brelse(bp);
! 278: return 0;
! 279: }
! 280:
! 281: /*
! 282: * Delayed write.
! 283: *
! 284: * The buffer is marked dirty, but an actual I/O is not
! 285: * performed. This routine should be used when the buffer
! 286: * is expected to be modified again soon.
! 287: */
! 288: void
! 289: bdwrite(struct buf *bp)
! 290: {
! 291:
! 292: BIO_LOCK();
! 293: SET(bp->b_flags, B_DELWRI);
! 294: CLR(bp->b_flags, B_DONE);
! 295: BIO_UNLOCK();
! 296: brelse(bp);
! 297: }
! 298:
! 299: /*
! 300: * Flush write-behind block
! 301: */
! 302: void
! 303: bflush(struct buf *bp)
! 304: {
! 305:
! 306: BIO_LOCK();
! 307: if (ISSET(bp->b_flags, B_DELWRI))
! 308: bwrite(bp);
! 309: BIO_UNLOCK();
! 310: }
! 311:
! 312: /*
! 313: * Invalidate buffer for specified device.
! 314: * This is called when unmount.
! 315: */
! 316: void
! 317: binval(dev_t dev)
! 318: {
! 319: struct buf *bp;
! 320: int i;
! 321:
! 322: BIO_LOCK();
! 323: for (i = 0; i < NBUFS; i++) {
! 324: bp = &buf_table[i];
! 325: if (bp->b_dev == dev) {
! 326: if (ISSET(bp->b_flags, B_DELWRI))
! 327: bwrite(bp);
! 328: else if (ISSET(bp->b_flags, B_BUSY))
! 329: brelse(bp);
! 330: bp->b_flags = B_INVAL;
! 331: }
! 332: }
! 333: BIO_UNLOCK();
! 334: }
! 335:
! 336: /*
! 337: * Invalidate all buffers.
! 338: * This is called when unmount.
! 339: */
! 340: void
! 341: bio_sync(void)
! 342: {
! 343: struct buf *bp;
! 344: int i;
! 345:
! 346: start:
! 347: BIO_LOCK();
! 348: for (i = 0; i < NBUFS; i++) {
! 349: bp = &buf_table[i];
! 350: if (ISSET(bp->b_flags, B_BUSY)) {
! 351: BIO_UNLOCK();
! 352: mutex_lock(&bp->b_lock);
! 353: mutex_unlock(&bp->b_lock);
! 354: goto start;
! 355: }
! 356: if (ISSET(bp->b_flags, B_DELWRI))
! 357: bwrite(bp);
! 358: }
! 359: BIO_UNLOCK();
! 360: }
! 361:
! 362: /*
! 363: * Initialize the buffer I/O system.
! 364: */
! 365: void
! 366: bio_init(void)
! 367: {
! 368: struct buf *bp;
! 369: int i;
! 370:
! 371: for (i = 0; i < NBUFS; i++) {
! 372: bp = &buf_table[i];
! 373: bp->b_flags = B_INVAL;
! 374: bp->b_data = buffers[i];
! 375: mutex_init(&bp->b_lock);
! 376: list_insert(&free_list, &bp->b_link);
! 377: }
! 378: sem_init(&free_sem, NBUFS);
! 379: DPRINTF(VFSDB_BIO, ("bio: Buffer cache size %dK bytes\n",
! 380: BSIZE * NBUFS / 1024));
! 381: }
CVSweb