123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- // 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;
- memset(&bcache, 0, sizeof(bcache));
- 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 0;
- }
- // 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);
- }
|