simplify ide queuing
nits in comments
This commit is contained in:
parent
2bc72bdd29
commit
902b13f5d6
8 changed files with 79 additions and 89 deletions
19
bio.c
19
bio.c
|
@ -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
5
buf.h
|
@ -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
2
defs.h
|
@ -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
20
fs.c
|
@ -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
111
ide.c
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
2
kalloc.c
2
kalloc.c
|
@ -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);
|
||||||
|
|
1
param.h
1
param.h
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue