1 // Buffer cache. 2 // 3 // The buffer cache is a linked list of buf structures holding 4 // cached copies of disk block contents. Caching disk blocks 5 // in memory reduces the number of disk reads and also provides 6 // a synchronization point for disk blocks used by multiple processes. 7 // 8 // Interface: 9 // * To get a buffer for a particular disk block, call bread. 10 // * After changing buffer data, call bwrite to write it to disk. 11 // * When done with the buffer, call brelse. 12 // * Do not use the buffer after calling brelse. 13 // * Only one process at a time can use a buffer, 14 // so do not keep them longer than necessary. 15 // 16 // The implementation uses two state flags internally: 17 // * B_VALID: the buffer data has been read from the disk. 18 // * B_DIRTY: the buffer data has been modified 19 // and needs to be written to disk. 20 21 #include "types.h" 22 #include "defs.h" 23 #include "param.h" 24 #include "spinlock.h" 25 #include "sleeplock.h" 26 #include "fs.h" 27 #include "buf.h" 28 29 struct { 30 struct spinlock lock; 31 struct buf buf[NBUF]; 32 33 // Linked list of all buffers, through prev/next. 34 // head.next is most recently used. 35 struct buf head; 36 } bcache; 37 38 void 39 binit(void) 40 { 41 struct buf *b; 42 43 initlock(&bcache.lock, "bcache"); 44 45 //PAGEBREAK! 46 // Create linked list of buffers 47 bcache.head.prev = &bcache.head; 48 bcache.head.next = &bcache.head; 49 for(b = bcache.buf; b < bcache.buf+NBUF; b++){ 50 b->next = bcache.head.next; 51 b->prev = &bcache.head; 52 b->dev = -1; 53 initsleeplock(&b->lock, "buffer"); 54 bcache.head.next->prev = b; 55 bcache.head.next = b; 56 } 57 } 58 59 // Look through buffer cache for block on device dev. 60 // If not found, allocate a buffer. 61 // In either case, return locked buffer. 62 static struct buf* 63 bget(uint dev, uint blockno) 64 { 65 struct buf *b; 66 67 acquire(&bcache.lock); 68 69 //cprintf("bget %d\n", blockno); 70 loop: 71 // Is the block already cached? 72 for(b = bcache.head.next; b != &bcache.head; b = b->next){ 73 if(b->dev == dev && b->blockno == blockno){ 74 if(!holdingsleep(&b->lock)) { 75 acquiresleep(&b->lock); 76 //cprintf("return buffer %p for blk %d\n", b - bcache.buf, blockno); 77 release(&bcache.lock); 78 return b; 79 } 80 sleep(b, &bcache.lock); 81 goto loop; 82 } 83 } 84 85 // Not cached; recycle some non-locked and clean buffer. 86 // "clean" because B_DIRTY and not locked means log.c 87 // hasn't yet committed the changes to the buffer. 88 for(b = bcache.head.prev; b != &bcache.head; b = b->prev){ 89 if(!holdingsleep(&b->lock) && (b->flags & B_DIRTY) == 0){ 90 b->dev = dev; 91 b->blockno = blockno; 92 b->flags = 0; // XXX 93 acquiresleep(&b->lock); 94 //cprintf("return buffer %p for blk %d\n", b - bcache.buf, blockno); 95 release(&bcache.lock); 96 return b; 97 } 98 } 99 panic("bget: no buffers"); 100 } 101 102 // Return a locked buf with the contents of the indicated block. 103 struct buf* 104 bread(uint dev, uint blockno) 105 { 106 struct buf *b; 107 108 b = bget(dev, blockno); 109 if(!(b->flags & B_VALID)) { 110 iderw(b); 111 } 112 return b; 113 } 114 115 // Write b's contents to disk. Must be locked. 116 void 117 bwrite(struct buf *b) 118 { 119 if(b->lock.locked == 0) 120 panic("bwrite"); 121 b->flags |= B_DIRTY; 122 iderw(b); 123 } 124 125 // Release a locked buffer. 126 // Move to the head of the MRU list. 127 void 128 brelse(struct buf *b) 129 { 130 if(b->lock.locked == 0) 131 panic("brelse"); 132 133 acquire(&bcache.lock); 134 135 b->next->prev = b->prev; 136 b->prev->next = b->next; 137 b->next = bcache.head.next; 138 b->prev = &bcache.head; 139 bcache.head.next->prev = b; 140 bcache.head.next = b; 141 releasesleep(&b->lock); 142 wakeup(b); 143 144 release(&bcache.lock); 145 } 146 //PAGEBREAK! 147 // Blank page. 148 149