simplify ide queuing

nits in comments
This commit is contained in:
rtm 2007-08-24 19:32:36 +00:00
parent 2bc72bdd29
commit 902b13f5d6
8 changed files with 79 additions and 89 deletions

19
bio.c
View file

@ -4,15 +4,14 @@
// holding cached copies of disk block contents. // holding cached copies of disk block contents.
// Each buf has two state bits B_BUSY and B_VALID. // Each buf has two state bits B_BUSY and B_VALID.
// If B_BUSY is set, it means that some code is currently // If B_BUSY is set, it means that some code is currently
// editing buf, so other code is not allowed to look at it. // modifying buf, so other code is not allowed to look at it.
// To wait for a buffer that is B_BUSY, sleep on buf. // To wait for a buffer that is B_BUSY, sleep on buf.
// (See bget below.) // (See bget below.)
// //
// If B_VALID is set, it means that the memory contents // If B_VALID is set, it means that the sector in b->data is
// have been initialized by reading them off the disk. // the same as on the disk. If B_VALID is not set, the contents
// (Conversely, if B_VALID is not set, the memory contents
// of buf must be initialized, often by calling bread, // of buf must be initialized, often by calling bread,
// before being used.) // before being used.
// //
// After making changes to a buf's memory, call bwrite to flush // After making changes to a buf's memory, call bwrite to flush
// the changes out to disk, to keep the disk and memory copies // the changes out to disk, to keep the disk and memory copies
@ -79,7 +78,6 @@ bget(uint dev, uint sector)
goto loop; goto loop;
} }
b->flags |= B_BUSY; b->flags |= B_BUSY;
// b->flags &= ~B_VALID; // Force reread from disk
release(&buf_table_lock); release(&buf_table_lock);
return b; return b;
} }
@ -98,7 +96,8 @@ bget(uint dev, uint sector)
panic("bget: no buffers"); panic("bget: no buffers");
} }
// Read buf's contents from disk. // Return a B_BUSY buf with the contents of the indicated
// disk sector.
struct buf* struct buf*
bread(uint dev, uint sector) bread(uint dev, uint sector)
{ {
@ -108,7 +107,8 @@ bread(uint dev, uint sector)
if(b->flags & B_VALID) if(b->flags & B_VALID)
return b; return b;
ide_rw(dev & 0xff, sector, b->data, 1, 1); b->flags &= ~B_WRITE;
ide_rw(b);
b->flags |= B_VALID; b->flags |= B_VALID;
return b; return b;
@ -121,7 +121,8 @@ bwrite(struct buf *b)
{ {
if((b->flags & B_BUSY) == 0) if((b->flags & B_BUSY) == 0)
panic("bwrite"); panic("bwrite");
ide_rw(b->dev & 0xff, b->sector, b->data, 1, 0); b->flags |= B_WRITE;
ide_rw(b);
b->flags |= B_VALID; b->flags |= B_VALID;
} }

5
buf.h
View file

@ -2,9 +2,12 @@ struct buf {
int flags; int flags;
uint dev; uint dev;
uint sector; uint sector;
struct buf *prev; struct buf *prev; // LRU cache list
struct buf *next; struct buf *next;
struct buf *qnext; // disk queue
int done;
uchar data[512]; uchar data[512];
}; };
#define B_BUSY 0x1 // buffer is locked by some process #define B_BUSY 0x1 // buffer is locked by some process
#define B_VALID 0x2 // buffer contains the data of the sector #define B_VALID 0x2 // buffer contains the data of the sector
#define B_WRITE 0x4 // asking device driver to write, else read

2
defs.h
View file

@ -55,7 +55,7 @@ int writei(struct inode*, char*, uint, uint);
// ide.c // ide.c
void ide_init(void); void ide_init(void);
void ide_intr(void); void ide_intr(void);
void ide_rw(int, uint, void*, uint, int); void ide_rw(struct buf *);
// ioapic.c // ioapic.c
void ioapic_enable(int irq, int cpu); void ioapic_enable(int irq, int cpu);

20
fs.c
View file

@ -6,7 +6,7 @@
// + Directories: inode with special contents (list of other inodes!) // + Directories: inode with special contents (list of other inodes!)
// + Names: paths like /usr/rtm/xv6/fs.c for convenient naming. // + Names: paths like /usr/rtm/xv6/fs.c for convenient naming.
// //
// Disk layout is: superblock, inodes, disk bitmap, data blocks. // Disk layout is: superblock, inodes, block not-free bitmap, data blocks.
// //
// This file contains the low-level file system manipulation // This file contains the low-level file system manipulation
// routines. The (higher-level) system call implementations // routines. The (higher-level) system call implementations
@ -51,7 +51,7 @@ balloc(uint dev)
bi = b % BPB; bi = b % BPB;
m = 0x1 << (bi % 8); m = 0x1 << (bi % 8);
if((bp->data[bi/8] & m) == 0) { // is block free? if((bp->data[bi/8] & m) == 0) { // is block free?
bp->data[bi/8] |= 0x1 << (bi % 8); bp->data[bi/8] |= m;
bwrite(bp); // mark it allocated on disk bwrite(bp); // mark it allocated on disk
brelse(bp); brelse(bp);
return b; return b;
@ -81,6 +81,8 @@ bfree(int dev, uint b)
bp = bread(dev, BBLOCK(b, ninodes)); bp = bread(dev, BBLOCK(b, ninodes));
bi = b % BPB; bi = b % BPB;
m = 0x1 << (bi % 8); m = 0x1 << (bi % 8);
if((bp->data[bi/8] & m) == 0)
panic("freeing free block");
bp->data[bi/8] &= ~m; bp->data[bi/8] &= ~m;
bwrite(bp); // mark it free on disk bwrite(bp); // mark it free on disk
brelse(bp); brelse(bp);
@ -93,18 +95,17 @@ bfree(int dev, uint b)
// on-disk structures to provide a place for synchronizing access // on-disk structures to provide a place for synchronizing access
// to inodes shared between multiple processes. // to inodes shared between multiple processes.
// //
// ip->ref counts the number of references to this // ip->ref counts the number of pointer references to this cached
// inode; references are typically kept in struct file and in cp->cwd. // inode; references are typically kept in struct file and in cp->cwd.
// When ip->ref falls to zero, the inode is no longer cached. // When ip->ref falls to zero, the inode is no longer cached.
// It is an error to use an inode without holding a reference to it. // It is an error to use an inode without holding a reference to it.
// //
// Inodes can be marked busy, just like bufs, meaning // Inodes can be locked with I_BUSY (like bufs and B_BUSY).
// that some process has exclusive use of the inode.
// Processes are only allowed to read and write inode // Processes are only allowed to read and write inode
// metadata and contents when holding the inode's lock. // metadata and contents when holding the inode's lock.
// Because inodes locks are held during disk accesses, // Because inode locks are held during disk accesses,
// they are implemented using a flag, as in the buffer cache, // they are implemented using a flag rather than with
// not using spin locks. Callers are responsible for locking // spin locks. Callers are responsible for locking
// inodes before passing them to routines in this file; leaving // inodes before passing them to routines in this file; leaving
// this responsibility with the caller makes it possible for them // this responsibility with the caller makes it possible for them
// to create arbitrarily-sized atomic operations. // to create arbitrarily-sized atomic operations.
@ -112,7 +113,8 @@ bfree(int dev, uint b)
// To give maximum control over locking to the callers, // To give maximum control over locking to the callers,
// the routines in this file that return inode pointers // the routines in this file that return inode pointers
// return pointers to *unlocked* inodes. It is the callers' // return pointers to *unlocked* inodes. It is the callers'
// responsibility to lock them before using them. // responsibility to lock them before using them. A non-zero
// ip->ref keeps these unlocked inodes in the cache.
struct { struct {
struct spinlock lock; struct spinlock lock;

111
ide.c
View file

@ -8,6 +8,7 @@
#include "x86.h" #include "x86.h"
#include "traps.h" #include "traps.h"
#include "spinlock.h" #include "spinlock.h"
#include "buf.h"
#define IDE_BSY 0x80 #define IDE_BSY 0x80
#define IDE_DRDY 0x40 #define IDE_DRDY 0x40
@ -18,28 +19,17 @@
#define IDE_CMD_WRITE 0x30 #define IDE_CMD_WRITE 0x30
// IDE request queue. // IDE request queue.
// The next request will be stored in request[head], // ide_queue points to the buf now being read/written to the disk.
// and the request currently being served by the disk // ide_queue->qnext points to the next buf to be processed.
// is request[tail].
// Must hold ide_lock while manipulating queue. // Must hold ide_lock while manipulating queue.
struct ide_request {
int diskno;
uint secno;
void *addr;
uint nsecs;
uint read;
int done;
};
static struct ide_request request[NREQUEST];
static int head, tail;
static struct spinlock ide_lock; static struct spinlock ide_lock;
static struct buf *ide_queue;
static int disk_1_present; static int disk_1_present;
static int disk_queue;
static int ide_probe_disk1(void); static int ide_probe_disk1(void);
static void ide_start_request();
//PAGEBREAK: 10 //PAGEBREAK: 10
// Wait for IDE disk to become ready. // Wait for IDE disk to become ready.
@ -93,80 +83,75 @@ void
ide_intr(void) ide_intr(void)
{ {
acquire(&ide_lock); acquire(&ide_lock);
request[tail].done = 1; if(ide_queue){
wakeup(&request[tail]); //cprintf("intr %x\n", ide_queue);
if((ide_queue->flags & B_WRITE) == 0)
if(ide_wait_ready(1) >= 0)
insl(0x1F0, ide_queue->data, 512/4);
ide_queue->done = 1;
wakeup(ide_queue);
ide_queue = ide_queue->qnext;
ide_start_request();
} else {
cprintf("stray ide interrupt\n");
}
release(&ide_lock); release(&ide_lock);
} }
// Start the next request in the queue. // Start the next request in the queue.
// Caller must hold ide_lock.
static void static void
ide_start_request (void) ide_start_request (void)
{ {
struct ide_request *r; if(ide_queue){
if(head != tail) {
r = &request[tail];
ide_wait_ready(0); ide_wait_ready(0);
//cprintf("start %x\n", ide_queue);
outb(0x3f6, 0); // generate interrupt outb(0x3f6, 0); // generate interrupt
outb(0x1F2, r->nsecs); outb(0x1F2, 1); // number of sectors
outb(0x1F3, r->secno & 0xFF); outb(0x1F3, ide_queue->sector & 0xFF);
outb(0x1F4, (r->secno >> 8) & 0xFF); outb(0x1F4, (ide_queue->sector >> 8) & 0xFF);
outb(0x1F5, (r->secno >> 16) & 0xFF); outb(0x1F5, (ide_queue->sector >> 16) & 0xFF);
outb(0x1F6, 0xE0 | ((r->diskno&1)<<4) | ((r->secno>>24)&0x0F)); outb(0x1F6, 0xE0 |
if(r->read) ((ide_queue->dev & 1)<<4) |
outb(0x1F7, IDE_CMD_READ); ((ide_queue->sector>>24)&0x0F));
else { if(ide_queue->flags & B_WRITE){
outb(0x1F7, IDE_CMD_WRITE); outb(0x1F7, IDE_CMD_WRITE);
outsl(0x1F0, r->addr, 512/4); outsl(0x1F0, ide_queue->data, 512/4);
} else {
outb(0x1F7, IDE_CMD_READ);
} }
} }
} }
//PAGEBREAK: 30 //PAGEBREAK: 30
// Run an entire disk operation. // Queue up a disk operation and wait for it to finish.
// b must have B_BUSY set.
void void
ide_rw(int diskno, uint secno, void *addr, uint nsecs, int read) ide_rw(struct buf *b)
{ {
struct ide_request *r; struct buf **pp;
if(diskno && !disk_1_present) if((b->dev & 0xff) && !disk_1_present)
panic("ide disk 1 not present"); panic("ide disk 1 not present");
acquire(&ide_lock); acquire(&ide_lock);
// Add request to queue. b->done = 0;
while((head + 1) % NREQUEST == tail) b->qnext = 0;
sleep(&disk_queue, &ide_lock);
r = &request[head]; // cprintf("enqueue %x %x\n", b, ide_queue);
r->secno = secno;
r->addr = addr;
r->nsecs = nsecs;
r->diskno = diskno;
r->read = read;
r->done = 0;
head = (head + 1) % NREQUEST;
// Start request if necessary. // append b to ide_queue
ide_start_request(); pp = &ide_queue;
while(*pp)
pp = &(*pp)->qnext;
*pp = b;
// Wait for request to finish. if(ide_queue == b)
while(!r->done) ide_start_request();
sleep(r, &ide_lock);
// Finish request. while(!b->done)
if(read){ sleep(b, &ide_lock);
if(ide_wait_ready(1) >= 0)
insl(0x1F0, addr, 512/4);
}
// Remove request from queue.
if((head + 1) % NREQUEST == tail)
wakeup(&disk_queue);
tail = (tail + 1) % NREQUEST;
// Start next request in queue, if any.
ide_start_request();
release(&ide_lock); release(&ide_lock);
} }

View file

@ -95,7 +95,7 @@ kalloc(int n)
char *p; char *p;
struct run *r, **rr; struct run *r, **rr;
if(n % PAGE) if(n % PAGE || n <= 0)
panic("kalloc"); panic("kalloc");
acquire(&kalloc_lock); acquire(&kalloc_lock);

View file

@ -5,7 +5,6 @@
#define NOFILE 16 // open files per process #define NOFILE 16 // open files per process
#define NFILE 100 // open files per system #define NFILE 100 // open files per system
#define NBUF 10 // size of disk block cache #define NBUF 10 // size of disk block cache
#define NREQUEST NBUF // outstanding disk requests
#define NINODE 50 // maximum number of active i-nodes #define NINODE 50 // maximum number of active i-nodes
#define NDEV 10 // maximum major device number #define NDEV 10 // maximum major device number
#define ROOTDEV 1 // device number of file system root disk #define ROOTDEV 1 // device number of file system root disk

View file

@ -49,7 +49,7 @@ fetchstr(struct proc *p, uint addr, char **pp)
return -1; return -1;
} }
// Fetch the argno'th word-sized system call argument as an integer. // Fetch the argno'th 32-bit system call argument.
int int
argint(int argno, int *ip) argint(int argno, int *ip)
{ {