xv6-65oo2/kernel/bio.c

154 lines
3.2 KiB
C
Raw Permalink Normal View History

2006-09-07 14:28:12 +00:00
// Buffer cache.
//
// The buffer cache is a linked list of buf structures holding
// cached copies of disk block contents. Caching disk blocks
// in memory reduces the number of disk reads and also provides
// a synchronization point for disk blocks used by multiple processes.
//
// Interface:
// * To get a buffer for a particular disk block, call bread.
2011-10-11 10:41:37 +00:00
// * After changing buffer data, call bwrite to write it to disk.
// * When done with the buffer, call brelse.
// * Do not use the buffer after calling brelse.
// * Only one process at a time can use a buffer,
// so do not keep them longer than necessary.
2006-09-07 14:28:12 +00:00
#include "types.h"
2007-08-27 23:26:33 +00:00
#include "param.h"
#include "spinlock.h"
#include "sleeplock.h"
2019-05-31 15:45:42 +00:00
#include "riscv.h"
#include "defs.h"
#include "fs.h"
#include "buf.h"
2009-05-31 01:29:17 +00:00
struct {
struct spinlock lock;
struct buf buf[NBUF];
2009-05-31 01:29:17 +00:00
// Linked list of all buffers, through prev/next.
// Sorted by how recently the buffer was used.
// head.next is most recent, head.prev is least.
2009-05-31 01:29:17 +00:00
struct buf head;
} bcache;
2006-08-12 22:34:13 +00:00
void
binit(void)
{
2006-08-12 22:34:13 +00:00
struct buf *b;
initlock(&bcache.lock, "bcache");
2006-08-12 22:34:13 +00:00
2006-09-07 14:28:12 +00:00
// Create linked list of buffers
2009-05-31 01:29:17 +00:00
bcache.head.prev = &bcache.head;
bcache.head.next = &bcache.head;
for(b = bcache.buf; b < bcache.buf+NBUF; b++){
b->next = bcache.head.next;
b->prev = &bcache.head;
initsleeplock(&b->lock, "buffer");
2009-05-31 01:29:17 +00:00
bcache.head.next->prev = b;
bcache.head.next = b;
2006-08-12 22:34:13 +00:00
}
}
// Look through buffer cache for block on device dev.
2014-08-27 18:14:52 +00:00
// If not found, allocate a buffer.
// In either case, return locked buffer.
2006-09-07 14:28:12 +00:00
static struct buf*
bget(uint dev, uint blockno)
{
struct buf *b;
2009-05-31 01:29:17 +00:00
acquire(&bcache.lock);
// Is the block already cached?
2009-05-31 01:29:17 +00:00
for(b = bcache.head.next; b != &bcache.head; b = b->next){
if(b->dev == dev && b->blockno == blockno){
b->refcnt++;
release(&bcache.lock);
acquiresleep(&b->lock);
return b;
}
}
// Not cached.
// Recycle the least recently used (LRU) unused buffer.
2009-05-31 01:29:17 +00:00
for(b = bcache.head.prev; b != &bcache.head; b = b->prev){
if(b->refcnt == 0) {
b->dev = dev;
b->blockno = blockno;
b->valid = 0;
b->refcnt = 1;
release(&bcache.lock);
acquiresleep(&b->lock);
return b;
}
}
panic("bget: no buffers");
}
// Return a locked buf with the contents of the indicated block.
2006-09-06 17:27:19 +00:00
struct buf*
bread(uint dev, uint blockno)
{
struct buf *b;
b = bget(dev, blockno);
if(!b->valid) {
virtio_disk_rw(b, 0);
b->valid = 1;
}
return b;
}
// Write b's contents to disk. Must be locked.
2006-08-07 01:38:46 +00:00
void
bwrite(struct buf *b)
2006-08-07 01:38:46 +00:00
{
if(!holdingsleep(&b->lock))
2006-09-07 15:29:54 +00:00
panic("bwrite");
virtio_disk_rw(b, 1);
2006-08-07 01:38:46 +00:00
}
// Release a locked buffer.
// Move to the head of the most-recently-used list.
void
brelse(struct buf *b)
{
if(!holdingsleep(&b->lock))
panic("brelse");
2006-09-06 17:27:19 +00:00
releasesleep(&b->lock);
acquire(&bcache.lock);
b->refcnt--;
if (b->refcnt == 0) {
// no one is waiting for it.
b->next->prev = b->prev;
b->prev->next = b->next;
b->next = bcache.head.next;
b->prev = &bcache.head;
bcache.head.next->prev = b;
bcache.head.next = b;
}
2009-05-31 01:29:17 +00:00
release(&bcache.lock);
}
2019-07-30 12:54:43 +00:00
void
bpin(struct buf *b) {
acquire(&bcache.lock);
b->refcnt++;
release(&bcache.lock);
}
void
bunpin(struct buf *b) {
acquire(&bcache.lock);
b->refcnt--;
release(&bcache.lock);
}