Dirt simple logging
Passes usertests and stressfs Seems to recover correctly in a number of simple cases
This commit is contained in:
parent
97657d703f
commit
13a96baefc
11 changed files with 244 additions and 47 deletions
1
Makefile
1
Makefile
|
@ -26,6 +26,7 @@ OBJS = \
|
||||||
uart.o\
|
uart.o\
|
||||||
vectors.o\
|
vectors.o\
|
||||||
vm.o\
|
vm.o\
|
||||||
|
log.o\
|
||||||
|
|
||||||
# Cross-compiling (e.g., on Mac OS X)
|
# Cross-compiling (e.g., on Mac OS X)
|
||||||
#TOOLPREFIX = i386-jos-elf-
|
#TOOLPREFIX = i386-jos-elf-
|
||||||
|
|
8
defs.h
8
defs.h
|
@ -6,6 +6,7 @@ struct pipe;
|
||||||
struct proc;
|
struct proc;
|
||||||
struct spinlock;
|
struct spinlock;
|
||||||
struct stat;
|
struct stat;
|
||||||
|
struct superblock;
|
||||||
|
|
||||||
// bio.c
|
// bio.c
|
||||||
void binit(void);
|
void binit(void);
|
||||||
|
@ -32,6 +33,7 @@ int filestat(struct file*, struct stat*);
|
||||||
int filewrite(struct file*, char*, int n);
|
int filewrite(struct file*, char*, int n);
|
||||||
|
|
||||||
// fs.c
|
// fs.c
|
||||||
|
void readsb(int dev, struct superblock *sb);
|
||||||
int dirlink(struct inode*, char*, uint);
|
int dirlink(struct inode*, char*, uint);
|
||||||
struct inode* dirlookup(struct inode*, char*, uint*);
|
struct inode* dirlookup(struct inode*, char*, uint*);
|
||||||
struct inode* ialloc(uint, short);
|
struct inode* ialloc(uint, short);
|
||||||
|
@ -75,6 +77,12 @@ void lapicinit(int);
|
||||||
void lapicstartap(uchar, uint);
|
void lapicstartap(uchar, uint);
|
||||||
void microdelay(int);
|
void microdelay(int);
|
||||||
|
|
||||||
|
// log.c
|
||||||
|
void initlog(void);
|
||||||
|
void log_write(struct buf*);
|
||||||
|
void begin_trans();
|
||||||
|
void commit_trans();
|
||||||
|
|
||||||
// mp.c
|
// mp.c
|
||||||
extern int ismp;
|
extern int ismp;
|
||||||
int mpbcpu(void);
|
int mpbcpu(void);
|
||||||
|
|
12
fs.c
12
fs.c
|
@ -25,7 +25,7 @@
|
||||||
static void itrunc(struct inode*);
|
static void itrunc(struct inode*);
|
||||||
|
|
||||||
// Read the super block.
|
// Read the super block.
|
||||||
static void
|
void
|
||||||
readsb(int dev, struct superblock *sb)
|
readsb(int dev, struct superblock *sb)
|
||||||
{
|
{
|
||||||
struct buf *bp;
|
struct buf *bp;
|
||||||
|
@ -65,7 +65,7 @@ balloc(uint dev)
|
||||||
m = 1 << (bi % 8);
|
m = 1 << (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] |= m; // Mark block in use on disk.
|
bp->data[bi/8] |= m; // Mark block in use on disk.
|
||||||
bwrite(bp);
|
log_write(bp);
|
||||||
brelse(bp);
|
brelse(bp);
|
||||||
return b + bi;
|
return b + bi;
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ bfree(int dev, uint b)
|
||||||
if((bp->data[bi/8] & m) == 0)
|
if((bp->data[bi/8] & m) == 0)
|
||||||
panic("freeing free block");
|
panic("freeing free block");
|
||||||
bp->data[bi/8] &= ~m; // Mark block free on disk.
|
bp->data[bi/8] &= ~m; // Mark block free on disk.
|
||||||
bwrite(bp);
|
log_write(bp);
|
||||||
brelse(bp);
|
brelse(bp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ ialloc(uint dev, short type)
|
||||||
if(dip->type == 0){ // a free inode
|
if(dip->type == 0){ // a free inode
|
||||||
memset(dip, 0, sizeof(*dip));
|
memset(dip, 0, sizeof(*dip));
|
||||||
dip->type = type;
|
dip->type = type;
|
||||||
bwrite(bp); // mark it allocated on the disk
|
log_write(bp); // mark it allocated on the disk
|
||||||
brelse(bp);
|
brelse(bp);
|
||||||
return iget(dev, inum);
|
return iget(dev, inum);
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,7 @@ iupdate(struct inode *ip)
|
||||||
dip->nlink = ip->nlink;
|
dip->nlink = ip->nlink;
|
||||||
dip->size = ip->size;
|
dip->size = ip->size;
|
||||||
memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));
|
memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));
|
||||||
bwrite(bp);
|
log_write(bp);
|
||||||
brelse(bp);
|
brelse(bp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,7 +339,7 @@ bmap(struct inode *ip, uint bn)
|
||||||
a = (uint*)bp->data;
|
a = (uint*)bp->data;
|
||||||
if((addr = a[bn]) == 0){
|
if((addr = a[bn]) == 0){
|
||||||
a[bn] = addr = balloc(ip->dev);
|
a[bn] = addr = balloc(ip->dev);
|
||||||
bwrite(bp);
|
log_write(bp);
|
||||||
}
|
}
|
||||||
brelse(bp);
|
brelse(bp);
|
||||||
return addr;
|
return addr;
|
||||||
|
|
1
fs.h
1
fs.h
|
@ -13,6 +13,7 @@ struct superblock {
|
||||||
uint size; // Size of file system image (blocks)
|
uint size; // Size of file system image (blocks)
|
||||||
uint nblocks; // Number of data blocks
|
uint nblocks; // Number of data blocks
|
||||||
uint ninodes; // Number of inodes.
|
uint ninodes; // Number of inodes.
|
||||||
|
uint nlog; // Number of log blocks
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NDIRECT 12
|
#define NDIRECT 12
|
||||||
|
|
|
@ -3,9 +3,12 @@
|
||||||
#include "syscall.h"
|
#include "syscall.h"
|
||||||
#include "traps.h"
|
#include "traps.h"
|
||||||
|
|
||||||
|
|
||||||
# exec(init, argv)
|
# exec(init, argv)
|
||||||
.globl start
|
.globl start
|
||||||
start:
|
start:
|
||||||
|
movl $SYS_init, %eax
|
||||||
|
int $T_SYSCALL
|
||||||
pushl $argv
|
pushl $argv
|
||||||
pushl $init
|
pushl $init
|
||||||
pushl $0 // where caller pc would be
|
pushl $0 // where caller pc would be
|
||||||
|
|
164
log.c
Normal file
164
log.c
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
#include "types.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "mmu.h"
|
||||||
|
#include "proc.h"
|
||||||
|
#include "x86.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "buf.h"
|
||||||
|
|
||||||
|
// Dirt simple "logging" supporting only one transaction. All file system calls
|
||||||
|
// that potentially write a block should be wrapped in begin_trans and commit_trans,
|
||||||
|
// so that there is never more than one transaction. This serializes all file system
|
||||||
|
// operations that potentially write, but simplifies recovery (only the last
|
||||||
|
// one transaction to recover) and concurrency (don't have to worry about reading a modified
|
||||||
|
// block from a transaction that hasn't committed yet).
|
||||||
|
|
||||||
|
// The header of the log. If head == 0, there are no log entries. All entries till head
|
||||||
|
// are committed. sector[] records the home sector for each block in the log
|
||||||
|
// (i.e., physical logging).
|
||||||
|
struct logheader {
|
||||||
|
int head;
|
||||||
|
int sector[LOGSIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct spinlock lock;
|
||||||
|
int start;
|
||||||
|
int size;
|
||||||
|
int intrans;
|
||||||
|
int dev;
|
||||||
|
struct logheader lh;
|
||||||
|
} log;
|
||||||
|
|
||||||
|
static void recover_from_log(void);
|
||||||
|
|
||||||
|
void
|
||||||
|
initlog(void)
|
||||||
|
{
|
||||||
|
if (sizeof(struct logheader) >= BSIZE)
|
||||||
|
panic("initlog: too big logheader");
|
||||||
|
|
||||||
|
struct superblock sb;
|
||||||
|
initlock(&log.lock, "log");
|
||||||
|
readsb(ROOTDEV, &sb);
|
||||||
|
log.start = sb.size - sb.nlog;
|
||||||
|
log.size = sb.nlog;
|
||||||
|
log.dev = ROOTDEV;
|
||||||
|
recover_from_log();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy committed blocks from log to their home location
|
||||||
|
static void
|
||||||
|
install_trans(void)
|
||||||
|
{
|
||||||
|
int tail;
|
||||||
|
|
||||||
|
if (log.lh.head > 0)
|
||||||
|
cprintf("install_trans %d\n", log.lh.head);
|
||||||
|
for (tail = 0; tail < log.lh.head; tail++) {
|
||||||
|
cprintf("put entry %d to disk block %d\n", tail, log.lh.sector[tail]);
|
||||||
|
struct buf *lbuf = bread(log.dev, log.start+tail+1); // read i'th block from log
|
||||||
|
struct buf *dbuf = bread(log.dev, log.lh.sector[tail]); // read dst block
|
||||||
|
memmove(dbuf->data, lbuf->data, BSIZE);
|
||||||
|
bwrite(dbuf);
|
||||||
|
brelse(lbuf);
|
||||||
|
brelse(dbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the log header from disk into the in-memory log header
|
||||||
|
static void
|
||||||
|
read_head(void)
|
||||||
|
{
|
||||||
|
struct buf *buf = bread(log.dev, log.start);
|
||||||
|
struct logheader *lh = (struct logheader *) (buf->data);
|
||||||
|
int i;
|
||||||
|
log.lh.head = lh->head;
|
||||||
|
for (i = 0; i < log.lh.head; i++) {
|
||||||
|
log.lh.sector[i] = lh->sector[i];
|
||||||
|
}
|
||||||
|
brelse(buf);
|
||||||
|
if (log.lh.head > 0)
|
||||||
|
cprintf("read_head: %d\n", log.lh.head);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the in-memory log header to disk, committing log entries till head
|
||||||
|
static void
|
||||||
|
write_head(void)
|
||||||
|
{
|
||||||
|
if (log.lh.head > 0)
|
||||||
|
cprintf("write_head: %d\n", log.lh.head);
|
||||||
|
|
||||||
|
struct buf *buf = bread(log.dev, log.start);
|
||||||
|
struct logheader *hb = (struct logheader *) (buf->data);
|
||||||
|
int i;
|
||||||
|
hb->head = log.lh.head;
|
||||||
|
for (i = 0; i < log.lh.head; i++) {
|
||||||
|
hb->sector[i] = log.lh.sector[i];
|
||||||
|
}
|
||||||
|
bwrite(buf);
|
||||||
|
brelse(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
recover_from_log(void)
|
||||||
|
{
|
||||||
|
read_head();
|
||||||
|
install_trans(); // Install all transactions till head
|
||||||
|
log.lh.head = 0;
|
||||||
|
write_head(); // Reclaim log
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
begin_trans(void)
|
||||||
|
{
|
||||||
|
acquire(&log.lock);
|
||||||
|
while (log.intrans) {
|
||||||
|
sleep(&log, &log.lock);
|
||||||
|
}
|
||||||
|
log.intrans = 1;
|
||||||
|
release(&log.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
commit_trans(void)
|
||||||
|
{
|
||||||
|
write_head(); // This causes all blocks till log.head to be commited
|
||||||
|
install_trans(); // Install all the transactions till head
|
||||||
|
log.lh.head = 0;
|
||||||
|
write_head(); // Reclaim log
|
||||||
|
|
||||||
|
acquire(&log.lock);
|
||||||
|
log.intrans = 0;
|
||||||
|
wakeup(&log);
|
||||||
|
release(&log.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write buffer into the log at log.head and record the block number log.lh.entry, but
|
||||||
|
// don't write the log header (which would commit the write).
|
||||||
|
void
|
||||||
|
log_write(struct buf *b)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (log.lh.head >= LOGSIZE)
|
||||||
|
panic("too big a transaction");
|
||||||
|
if (!log.intrans)
|
||||||
|
panic("write outside of trans");
|
||||||
|
|
||||||
|
cprintf("log_write: %d %d\n", b->sector, log.lh.head);
|
||||||
|
|
||||||
|
for (i = 0; i < log.lh.head; i++) {
|
||||||
|
if (log.lh.sector[i] == b->sector) // log absorbtion?
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
log.lh.sector[i] = b->sector;
|
||||||
|
struct buf *lbuf = bread(b->dev, log.start+i+1);
|
||||||
|
memmove(lbuf->data, b->data, BSIZE);
|
||||||
|
bwrite(lbuf);
|
||||||
|
brelse(lbuf);
|
||||||
|
if (i == log.lh.head)
|
||||||
|
log.lh.head++;
|
||||||
|
}
|
2
main.c
2
main.c
|
@ -20,7 +20,7 @@ main(void)
|
||||||
lapicinit(mpbcpu());
|
lapicinit(mpbcpu());
|
||||||
seginit(); // set up segments
|
seginit(); // set up segments
|
||||||
kinit(); // initialize memory allocator
|
kinit(); // initialize memory allocator
|
||||||
jmpkstack(); // call mainc() on a properly-allocated stack
|
jmpkstack(); // call mainc() on a properly-allocated stack
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
13
mkfs.c
13
mkfs.c
|
@ -9,8 +9,10 @@
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
#include "stat.h"
|
#include "stat.h"
|
||||||
|
#include "param.h"
|
||||||
|
|
||||||
int nblocks = 995;
|
int nblocks = 985;
|
||||||
|
int nlog = LOGSIZE;
|
||||||
int ninodes = 200;
|
int ninodes = 200;
|
||||||
int size = 1024;
|
int size = 1024;
|
||||||
|
|
||||||
|
@ -79,17 +81,18 @@ main(int argc, char *argv[])
|
||||||
sb.size = xint(size);
|
sb.size = xint(size);
|
||||||
sb.nblocks = xint(nblocks); // so whole disk is size sectors
|
sb.nblocks = xint(nblocks); // so whole disk is size sectors
|
||||||
sb.ninodes = xint(ninodes);
|
sb.ninodes = xint(ninodes);
|
||||||
|
sb.nlog = xint(nlog);
|
||||||
|
|
||||||
bitblocks = size/(512*8) + 1;
|
bitblocks = size/(512*8) + 1;
|
||||||
usedblocks = ninodes / IPB + 3 + bitblocks;
|
usedblocks = ninodes / IPB + 3 + bitblocks;
|
||||||
freeblock = usedblocks;
|
freeblock = usedblocks;
|
||||||
|
|
||||||
printf("used %d (bit %d ninode %zu) free %u total %d\n", usedblocks,
|
printf("used %d (bit %d ninode %zu) free %u log %u total %d\n", usedblocks,
|
||||||
bitblocks, ninodes/IPB + 1, freeblock, nblocks+usedblocks);
|
bitblocks, ninodes/IPB + 1, freeblock, nlog, nblocks+usedblocks+nlog);
|
||||||
|
|
||||||
assert(nblocks + usedblocks == size);
|
assert(nblocks + usedblocks + nlog == size);
|
||||||
|
|
||||||
for(i = 0; i < nblocks + usedblocks; i++)
|
for(i = 0; i < nblocks + usedblocks + nlog; i++)
|
||||||
wsect(i, zeroes);
|
wsect(i, zeroes);
|
||||||
|
|
||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
|
|
2
param.h
2
param.h
|
@ -10,3 +10,5 @@
|
||||||
#define USERTOP 0xA0000 // end of user address space
|
#define USERTOP 0xA0000 // end of user address space
|
||||||
#define PHYSTOP 0x1000000 // use phys mem up to here as free pool
|
#define PHYSTOP 0x1000000 // use phys mem up to here as free pool
|
||||||
#define MAXARG 32 // max exec arguments
|
#define MAXARG 32 // max exec arguments
|
||||||
|
#define LOGSIZE 10 // size of log
|
||||||
|
|
||||||
|
|
49
syscall.c
49
syscall.c
|
@ -98,39 +98,52 @@ extern int sys_wait(void);
|
||||||
extern int sys_write(void);
|
extern int sys_write(void);
|
||||||
extern int sys_uptime(void);
|
extern int sys_uptime(void);
|
||||||
|
|
||||||
|
int
|
||||||
|
sys_init(void)
|
||||||
|
{
|
||||||
|
initlog();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int (*syscalls[])(void) = {
|
static int (*syscalls[])(void) = {
|
||||||
[SYS_chdir] sys_chdir,
|
[SYS_init] sys_init,
|
||||||
[SYS_close] sys_close,
|
|
||||||
[SYS_dup] sys_dup,
|
|
||||||
[SYS_exec] sys_exec,
|
|
||||||
[SYS_exit] sys_exit,
|
|
||||||
[SYS_fork] sys_fork,
|
[SYS_fork] sys_fork,
|
||||||
[SYS_fstat] sys_fstat,
|
[SYS_exit] sys_exit,
|
||||||
[SYS_getpid] sys_getpid,
|
[SYS_wait] sys_wait,
|
||||||
[SYS_kill] sys_kill,
|
|
||||||
[SYS_link] sys_link,
|
|
||||||
[SYS_mkdir] sys_mkdir,
|
|
||||||
[SYS_mknod] sys_mknod,
|
|
||||||
[SYS_open] sys_open,
|
|
||||||
[SYS_pipe] sys_pipe,
|
[SYS_pipe] sys_pipe,
|
||||||
[SYS_read] sys_read,
|
[SYS_read] sys_read,
|
||||||
|
[SYS_kill] sys_kill,
|
||||||
|
[SYS_exec] sys_exec,
|
||||||
|
[SYS_fstat] sys_fstat,
|
||||||
|
[SYS_chdir] sys_chdir,
|
||||||
|
[SYS_dup] sys_dup,
|
||||||
|
[SYS_getpid] sys_getpid,
|
||||||
[SYS_sbrk] sys_sbrk,
|
[SYS_sbrk] sys_sbrk,
|
||||||
[SYS_sleep] sys_sleep,
|
[SYS_sleep] sys_sleep,
|
||||||
[SYS_unlink] sys_unlink,
|
|
||||||
[SYS_wait] sys_wait,
|
|
||||||
[SYS_write] sys_write,
|
|
||||||
[SYS_uptime] sys_uptime,
|
[SYS_uptime] sys_uptime,
|
||||||
|
// File system calls that are run in a transaction:
|
||||||
|
[SYS_open] sys_open,
|
||||||
|
[SYS_write] sys_write,
|
||||||
|
[SYS_mknod] sys_mknod,
|
||||||
|
[SYS_unlink] sys_unlink,
|
||||||
|
[SYS_link] sys_link,
|
||||||
|
[SYS_mkdir] sys_mkdir,
|
||||||
|
[SYS_close] sys_close,
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
syscall(void)
|
syscall(void)
|
||||||
{
|
{
|
||||||
int num;
|
int num;
|
||||||
|
|
||||||
num = proc->tf->eax;
|
num = proc->tf->eax;
|
||||||
if(num >= 0 && num < NELEM(syscalls) && syscalls[num])
|
if(num >= 0 && num < SYS_open && syscalls[num]) {
|
||||||
proc->tf->eax = syscalls[num]();
|
proc->tf->eax = syscalls[num]();
|
||||||
else {
|
} else if (num >= SYS_open && num < NELEM(syscalls) && syscalls[num]) {
|
||||||
|
begin_trans();
|
||||||
|
proc->tf->eax = syscalls[num]();
|
||||||
|
commit_trans();
|
||||||
|
} else {
|
||||||
cprintf("%d %s: unknown sys call %d\n",
|
cprintf("%d %s: unknown sys call %d\n",
|
||||||
proc->pid, proc->name, num);
|
proc->pid, proc->name, num);
|
||||||
proc->tf->eax = -1;
|
proc->tf->eax = -1;
|
||||||
|
|
36
syscall.h
36
syscall.h
|
@ -1,22 +1,24 @@
|
||||||
// System call numbers
|
// System call numbers
|
||||||
|
#define SYS_init 0
|
||||||
#define SYS_fork 1
|
#define SYS_fork 1
|
||||||
#define SYS_exit 2
|
#define SYS_exit 2
|
||||||
#define SYS_wait 3
|
#define SYS_wait 3
|
||||||
#define SYS_pipe 4
|
#define SYS_pipe 4
|
||||||
#define SYS_write 5
|
#define SYS_read 5
|
||||||
#define SYS_read 6
|
#define SYS_kill 6
|
||||||
#define SYS_close 7
|
#define SYS_exec 7
|
||||||
#define SYS_kill 8
|
#define SYS_fstat 8
|
||||||
#define SYS_exec 9
|
#define SYS_chdir 9
|
||||||
#define SYS_open 10
|
#define SYS_dup 10
|
||||||
#define SYS_mknod 11
|
#define SYS_getpid 11
|
||||||
#define SYS_unlink 12
|
#define SYS_sbrk 12
|
||||||
#define SYS_fstat 13
|
#define SYS_sleep 13
|
||||||
#define SYS_link 14
|
#define SYS_uptime 14
|
||||||
#define SYS_mkdir 15
|
|
||||||
#define SYS_chdir 16
|
#define SYS_open 15
|
||||||
#define SYS_dup 17
|
#define SYS_write 16
|
||||||
#define SYS_getpid 18
|
#define SYS_mknod 17
|
||||||
#define SYS_sbrk 19
|
#define SYS_unlink 18
|
||||||
#define SYS_sleep 20
|
#define SYS_link 19
|
||||||
#define SYS_uptime 21
|
#define SYS_mkdir 20
|
||||||
|
#define SYS_close 21
|
||||||
|
|
Loading…
Reference in a new issue