added base src
[xv6-db.git] / bio.c
blob15aa3ba022e5a595fb04c32d9f8d094addf110ef
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.
24 #include "types.h"
25 #include "defs.h"
26 #include "param.h"
27 #include "spinlock.h"
28 #include "buf.h"
30 struct {
31 struct spinlock lock;
32 struct buf buf[NBUF];
34 // Linked list of all buffers, through prev/next.
35 // head.next is most recently used.
36 struct buf head;
37 } bcache;
39 void
40 binit(void)
42 struct buf *b;
44 initlock(&bcache.lock, "bcache");
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 bcache.head.next->prev = b;
54 bcache.head.next = b;
58 // Look through buffer cache for sector on device dev.
59 // If not found, allocate fresh block.
60 // In either case, return locked buffer.
61 static struct buf*
62 bget(uint dev, uint sector)
64 struct buf *b;
66 acquire(&bcache.lock);
68 loop:
69 // Try for cached block.
70 for(b = bcache.head.next; b != &bcache.head; b = b->next){
71 if(b->dev == dev && b->sector == sector){
72 if(!(b->flags & B_BUSY)){
73 b->flags |= B_BUSY;
74 release(&bcache.lock);
75 return b;
77 sleep(b, &bcache.lock);
78 goto loop;
82 // Allocate fresh block.
83 for(b = bcache.head.prev; b != &bcache.head; b = b->prev){
84 if((b->flags & B_BUSY) == 0){
85 b->dev = dev;
86 b->sector = sector;
87 b->flags = B_BUSY;
88 release(&bcache.lock);
89 return b;
92 panic("bget: no buffers");
95 // Return a B_BUSY buf with the contents of the indicated disk sector.
96 struct buf*
97 bread(uint dev, uint sector)
99 struct buf *b;
101 b = bget(dev, sector);
102 if(!(b->flags & B_VALID))
103 iderw(b);
104 return b;
107 // Write b's contents to disk. Must be locked.
108 void
109 bwrite(struct buf *b)
111 if((b->flags & B_BUSY) == 0)
112 panic("bwrite");
113 b->flags |= B_DIRTY;
114 iderw(b);
117 // Release the buffer b.
118 void
119 brelse(struct buf *b)
121 if((b->flags & B_BUSY) == 0)
122 panic("brelse");
124 acquire(&bcache.lock);
126 b->next->prev = b->prev;
127 b->prev->next = b->next;
128 b->next = bcache.head.next;
129 b->prev = &bcache.head;
130 bcache.head.next->prev = b;
131 bcache.head.next = b;
133 b->flags &= ~B_BUSY;
134 wakeup(b);
136 release(&bcache.lock);