xref: /xv6-public/bio.c (revision 558ab49f)
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