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 flush 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 initialized 18 // with the associated disk block contents. 19 // * B_DIRTY: the buffer data has been modified 20 // and needs to be written to disk. 21 22 #include "types.h" 23 #include "defs.h" 24 #include "param.h" 25 #include "spinlock.h" 26 #include "buf.h" 27 28 struct { 29 struct spinlock lock; 30 struct buf buf[NBUF]; 31 32 // Linked list of all buffers, through prev/next. 33 // head.next is most recently used. 34 struct buf head; 35 } bcache; 36 37 void 38 binit(void) 39 { 40 struct buf *b; 41 42 initlock(&bcache.lock, "bcache"); 43 44 //PAGEBREAK! 45 // Create linked list of buffers 46 bcache.head.prev = &bcache.head; 47 bcache.head.next = &bcache.head; 48 for(b = bcache.buf; b < bcache.buf+NBUF; b++){ 49 b->next = bcache.head.next; 50 b->prev = &bcache.head; 51 b->dev = -1; 52 initlock(&b->lock, "buf"); 53 initsleeplock(&b->sleeplock); 54 bcache.head.next->prev = b; 55 bcache.head.next = b; 56 } 57 } 58 59 // Look through buffer cache for sector on device dev. 60 // If not found, allocate fresh block. 61 // In either case, return sleep-locked buffer. 62 static struct buf* 63 bget(uint dev, uint sector) 64 { 65 struct buf *b; 66 67 acquire(&bcache.lock); 68 // Try for cached block. 69 for(b = bcache.head.next; b != &bcache.head; b = b->next){ 70 acquire(&b->lock); 71 if(b->dev == dev && b->sector == sector){ 72 release(&bcache.lock); 73 acquire_sleeplock(&b->sleeplock, &b->lock); 74 release(&b->lock); 75 return b; 76 } 77 release(&b->lock); 78 } 79 80 // Allocate fresh block. 81 for(b = bcache.head.prev; b != &bcache.head; b = b->prev){ 82 acquire(&b->lock); 83 if (!acquired_sleeplock(&b->sleeplock)) { 84 release(&bcache.lock); 85 b->dev = dev; 86 b->sector = sector; 87 b->flags = 0; 88 acquire_sleeplock(&b->sleeplock, &b->lock); 89 release(&b->lock); 90 return b; 91 } 92 release(&b->lock); 93 } 94 panic("bget: no buffers"); 95 } 96 97 // Return a locked buf with the contents of the indicated disk sector. 98 struct buf* 99 bread(uint dev, uint sector) 100 { 101 struct buf *b; 102 103 b = bget(dev, sector); 104 if(!(b->flags & B_VALID)) 105 iderw(b); 106 return b; 107 } 108 109 // Write b's contents to disk. Must be locked. 110 void 111 bwrite(struct buf *b) 112 { 113 if(!acquired_sleeplock(&b->sleeplock)) 114 panic("bwrite"); 115 b->flags |= B_DIRTY; 116 iderw(b); 117 } 118 119 // Release the buffer b. 120 void 121 brelse(struct buf *b) 122 { 123 if(!acquired_sleeplock(&b->sleeplock)) 124 panic("brelse"); 125 126 acquire(&bcache.lock); 127 acquire(&b->lock); 128 b->next->prev = b->prev; 129 b->prev->next = b->next; 130 b->next = bcache.head.next; 131 b->prev = &bcache.head; 132 bcache.head.next->prev = b; 133 bcache.head.next = b; 134 135 release_sleeplock(&b->sleeplock); 136 release(&b->lock); 137 138 release(&bcache.lock); 139 } 140 141