Mercurial > hg > CbC > CbC_xv6
diff src/bio.c @ 0:83c23a36980d
Init
author | Tatsuki IHA <e125716@ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 26 May 2017 23:11:05 +0900 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/bio.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,143 @@ +// Buffer cache. +// +// The buffer cache is a linked list of buf structures holding +// cached copies of disk block contents. Caching disk blocks +// in memory reduces the number of disk reads and also provides +// a synchronization point for disk blocks used by multiple processes. +// +// Interface: +// * To get a buffer for a particular disk block, call bread. +// * After changing buffer data, call bwrite to write it to disk. +// * When done with the buffer, call brelse. +// * Do not use the buffer after calling brelse. +// * Only one process at a time can use a buffer, +// so do not keep them longer than necessary. +// +// The implementation uses three state flags internally: +// * B_BUSY: the block has been returned from bread +// and has not been passed back to brelse. +// * B_VALID: the buffer data has been read from the disk. +// * B_DIRTY: the buffer data has been modified +// and needs to be written to disk. + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "spinlock.h" +#include "buf.h" + +struct { + struct spinlock lock; + struct buf buf[NBUF]; + + // Linked list of all buffers, through prev/next. + // head.next is most recently used. + struct buf head; +} bcache; + +void binit (void) +{ + struct buf *b; + + initlock(&bcache.lock, "bcache"); + + //PAGEBREAK! + // Create linked list of buffers + bcache.head.prev = &bcache.head; + bcache.head.next = &bcache.head; + + for (b = bcache.buf; b < bcache.buf + NBUF; b++) { + b->next = bcache.head.next; + b->prev = &bcache.head; + b->dev = -1; + bcache.head.next->prev = b; + bcache.head.next = b; + } +} + +// Look through buffer cache for sector on device dev. +// If not found, allocate fresh block. +// In either case, return B_BUSY buffer. +static struct buf* bget (uint dev, uint sector) +{ + struct buf *b; + + acquire(&bcache.lock); + + loop: + // Is the sector already cached? + for (b = bcache.head.next; b != &bcache.head; b = b->next) { + if (b->dev == dev && b->sector == sector) { + if (!(b->flags & B_BUSY)) { + b->flags |= B_BUSY; + release(&bcache.lock); + return b; + } + + sleep(b, &bcache.lock); + goto loop; + } + } + + // Not cached; recycle some non-busy and clean buffer. + for (b = bcache.head.prev; b != &bcache.head; b = b->prev) { + if ((b->flags & B_BUSY) == 0 && (b->flags & B_DIRTY) == 0) { + b->dev = dev; + b->sector = sector; + b->flags = B_BUSY; + release(&bcache.lock); + return b; + } + } + + panic("bget: no buffers"); +} + +// Return a B_BUSY buf with the contents of the indicated disk sector. +struct buf* bread (uint dev, uint sector) +{ + struct buf *b; + + b = bget(dev, sector); + + if (!(b->flags & B_VALID)) { + iderw(b); + } + + return b; +} + +// Write b's contents to disk. Must be B_BUSY. +void bwrite (struct buf *b) +{ + if ((b->flags & B_BUSY) == 0) { + panic("bwrite"); + } + + b->flags |= B_DIRTY; + iderw(b); +} + +// Release a B_BUSY buffer. +// Move to the head of the MRU list. +void brelse (struct buf *b) +{ + if ((b->flags & B_BUSY) == 0) { + panic("brelse"); + } + + acquire(&bcache.lock); + + b->next->prev = b->prev; + b->prev->next = b->next; + b->next = bcache.head.next; + b->prev = &bcache.head; + bcache.head.next->prev = b; + bcache.head.next = b; + + b->flags &= ~B_BUSY; + wakeup(b); + + release(&bcache.lock); +} +