Prex Home / Browse Source - Prex Version: 0.9.0

root/usr/server/fs/vfs/vfs_bio.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. bio_insert_head
  2. bio_insert_tail
  3. bio_remove
  4. bio_remove_head
  5. incore
  6. getblk
  7. brelse
  8. bread
  9. bwrite
  10. bdwrite
  11. bflush
  12. binval
  13. bio_sync
  14. bio_init

   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  * vfs_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 <sys/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                         /*
 169                          * Wait buffer ready.
 170                          */
 171                         BIO_UNLOCK();
 172                         mutex_lock(&bp->b_lock);
 173                         mutex_unlock(&bp->b_lock);
 174                         /* Scan again if it's busy */
 175                         goto start;
 176                 }
 177                 bio_remove(bp);
 178                 SET(bp->b_flags, B_BUSY);
 179         } else {
 180                 bp = bio_remove_head();
 181                 if (ISSET(bp->b_flags, B_DELWRI)) {
 182                         BIO_UNLOCK();
 183                         bwrite(bp);
 184                         goto start;
 185                 }
 186                 bp->b_flags = B_BUSY;
 187                 bp->b_dev = dev;
 188                 bp->b_blkno = blkno;
 189         }
 190         mutex_lock(&bp->b_lock);
 191         BIO_UNLOCK();
 192         DPRINTF(VFSDB_BIO, ("getblk: done bp=%x\n", bp));
 193         return bp;
 194 }
 195 
 196 /*
 197  * Release a buffer, with no I/O implied.
 198  */
 199 void
 200 brelse(struct buf *bp)
 201 {
 202         ASSERT(ISSET(bp->b_flags, B_BUSY));
 203         DPRINTF(VFSDB_BIO, ("brelse: bp=%x dev=%x blkno=%d\n",
 204                                 bp, bp->b_dev, bp->b_blkno));
 205 
 206         BIO_LOCK();
 207         CLR(bp->b_flags, B_BUSY);
 208         mutex_unlock(&bp->b_lock);
 209         if (ISSET(bp->b_flags, B_INVAL))
 210                 bio_insert_head(bp);
 211         else
 212                 bio_insert_tail(bp);
 213         BIO_UNLOCK();
 214 }
 215 
 216 /*
 217  * Block read with cache.
 218  * @dev:   device id to read from.
 219  * @blkno: block number.
 220  * @buf:   buffer pointer to be returned.
 221  *
 222  * An actual read operation is done only when the cached
 223  * buffer is dirty.
 224  */
 225 int
 226 bread(dev_t dev, int blkno, struct buf **bpp)
 227 {
 228         struct buf *bp;
 229         size_t size;
 230         int error;
 231 
 232         DPRINTF(VFSDB_BIO, ("bread: dev=%x blkno=%d\n", dev, blkno));
 233         bp = getblk(dev, blkno);
 234 
 235         if (!ISSET(bp->b_flags, (B_DONE | B_DELWRI))) {
 236                 size = BSIZE;
 237                 error = device_read((device_t)dev, bp->b_data, &size, blkno);
 238                 if (error) {
 239                         DPRINTF(VFSDB_BIO, ("bread: i/o error\n"));
 240                         brelse(bp);
 241                         return error;
 242                 }
 243         }
 244         CLR(bp->b_flags, B_INVAL);
 245         SET(bp->b_flags, (B_READ | B_DONE));
 246         DPRINTF(VFSDB_BIO, ("bread: done bp=%x\n\n", bp));
 247         *bpp = bp;
 248         return 0;
 249 }
 250 
 251 /*
 252  * Block write with cache.
 253  * @buf:   buffer to write.
 254  *
 255  * The data is copied to the buffer.
 256  * Then release the buffer.
 257  */
 258 int
 259 bwrite(struct buf *bp)
 260 {
 261         size_t size;
 262         int error;
 263 
 264         ASSERT(ISSET(bp->b_flags, B_BUSY));
 265         DPRINTF(VFSDB_BIO, ("bwrite: dev=%x blkno=%d\n", bp->b_dev,
 266                             bp->b_blkno));
 267 
 268         BIO_LOCK();
 269         CLR(bp->b_flags, (B_READ | B_DONE | B_DELWRI));
 270         BIO_UNLOCK();
 271 
 272         size = BSIZE;
 273         error = device_write((device_t)bp->b_dev, bp->b_data, &size,
 274                            bp->b_blkno);
 275         if (error)
 276                 return error;
 277         BIO_LOCK();
 278         SET(bp->b_flags, B_DONE);
 279         BIO_UNLOCK();
 280         brelse(bp);
 281         return 0;
 282 }
 283 
 284 /*
 285  * Delayed write.
 286  *
 287  * The buffer is marked dirty, but an actual I/O is not
 288  * performed.  This routine should be used when the buffer
 289  * is expected to be modified again soon.
 290  */
 291 void
 292 bdwrite(struct buf *bp)
 293 {
 294 
 295         BIO_LOCK();
 296         SET(bp->b_flags, B_DELWRI);
 297         CLR(bp->b_flags, B_DONE);
 298         BIO_UNLOCK();
 299         brelse(bp);
 300 }
 301 
 302 /*
 303  * Flush write-behind block
 304  */
 305 void
 306 bflush(struct buf *bp)
 307 {
 308 
 309         BIO_LOCK();
 310         if (ISSET(bp->b_flags, B_DELWRI))
 311                 bwrite(bp);
 312         BIO_UNLOCK();
 313 }
 314 
 315 /*
 316  * Invalidate buffer for specified device.
 317  * This is called when unmount.
 318  */
 319 void
 320 binval(dev_t dev)
 321 {
 322         struct buf *bp;
 323         int i;
 324 
 325         BIO_LOCK();
 326         for (i = 0; i < NBUFS; i++) {
 327                 bp = &buf_table[i];
 328                 if (bp->b_dev == dev) {
 329                         if (ISSET(bp->b_flags, B_DELWRI))
 330                                 bwrite(bp);
 331                         else if (ISSET(bp->b_flags, B_BUSY))
 332                                 brelse(bp);
 333                         bp->b_flags = B_INVAL;
 334                 }
 335         }
 336         BIO_UNLOCK();
 337 }
 338 
 339 /*
 340  * Invalidate all buffers.
 341  * This is called when unmount.
 342  */
 343 void
 344 bio_sync(void)
 345 {
 346         struct buf *bp;
 347         int i;
 348 
 349  start:
 350         BIO_LOCK();
 351         for (i = 0; i < NBUFS; i++) {
 352                 bp = &buf_table[i];
 353                 if (ISSET(bp->b_flags, B_BUSY)) {
 354                         BIO_UNLOCK();
 355                         mutex_lock(&bp->b_lock);
 356                         mutex_unlock(&bp->b_lock);
 357                         goto start;
 358                 }
 359                 if (ISSET(bp->b_flags, B_DELWRI))
 360                         bwrite(bp);
 361         }
 362         BIO_UNLOCK();
 363 }
 364 
 365 /*
 366  * Initialize the buffer I/O system.
 367  */
 368 void
 369 bio_init(void)
 370 {
 371         struct buf *bp;
 372         int i;
 373 
 374         for (i = 0; i < NBUFS; i++) {
 375                 bp = &buf_table[i];
 376                 bp->b_flags = B_INVAL;
 377                 bp->b_data = buffers[i];
 378                 mutex_init(&bp->b_lock);
 379                 list_insert(&free_list, &bp->b_link);
 380         }
 381         sem_init(&free_sem, NBUFS);
 382 
 383         DPRINTF(VFSDB_BIO, ("bio: Buffer cache size %dK bytes\n",
 384                             BSIZE * NBUFS / 1024));
 385 }

/* [<][>][^][v][top][bottom][index][help] */