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