Permalink
Browse files

Dirt simple logging

Passes usertests and stressfs
Seems to recover correctly in a number of simple cases
  • Loading branch information...
1 parent 97657d7 commit 13a96baefc0ff5d8262c4bc8c797bee4b157443c Frans Kaashoek committed Jul 28, 2011
Showing with 244 additions and 47 deletions.
  1. +1 −0 Makefile
  2. +8 −0 defs.h
  3. +6 −6 fs.c
  4. +1 −0 fs.h
  5. +3 −0 initcode.S
  6. +164 −0 log.c
  7. +1 −1 main.c
  8. +8 −5 mkfs.c
  9. +2 −0 param.h
  10. +31 −18 syscall.c
  11. +19 −17 syscall.h
View
@@ -26,6 +26,7 @@ OBJS = \
uart.o\
vectors.o\
vm.o\
+ log.o\
# Cross-compiling (e.g., on Mac OS X)
#TOOLPREFIX = i386-jos-elf-
View
8 defs.h
@@ -6,6 +6,7 @@ struct pipe;
struct proc;
struct spinlock;
struct stat;
+struct superblock;
// bio.c
void binit(void);
@@ -32,6 +33,7 @@ int filestat(struct file*, struct stat*);
int filewrite(struct file*, char*, int n);
// fs.c
+void readsb(int dev, struct superblock *sb);
int dirlink(struct inode*, char*, uint);
struct inode* dirlookup(struct inode*, char*, uint*);
struct inode* ialloc(uint, short);
@@ -75,6 +77,12 @@ void lapicinit(int);
void lapicstartap(uchar, uint);
void microdelay(int);
+// log.c
+void initlog(void);
+void log_write(struct buf*);
+void begin_trans();
+void commit_trans();
+
// mp.c
extern int ismp;
int mpbcpu(void);
View
12 fs.c
@@ -25,7 +25,7 @@
static void itrunc(struct inode*);
// Read the super block.
-static void
+void
readsb(int dev, struct superblock *sb)
{
struct buf *bp;
@@ -65,7 +65,7 @@ balloc(uint dev)
m = 1 << (bi % 8);
if((bp->data[bi/8] & m) == 0){ // Is block free?
bp->data[bi/8] |= m; // Mark block in use on disk.
- bwrite(bp);
+ log_write(bp);
brelse(bp);
return b + bi;
}
@@ -92,7 +92,7 @@ bfree(int dev, uint b)
if((bp->data[bi/8] & m) == 0)
panic("freeing free block");
bp->data[bi/8] &= ~m; // Mark block free on disk.
- bwrite(bp);
+ log_write(bp);
brelse(bp);
}
@@ -159,7 +159,7 @@ ialloc(uint dev, short type)
if(dip->type == 0){ // a free inode
memset(dip, 0, sizeof(*dip));
dip->type = type;
- bwrite(bp); // mark it allocated on the disk
+ log_write(bp); // mark it allocated on the disk
brelse(bp);
return iget(dev, inum);
}
@@ -183,7 +183,7 @@ iupdate(struct inode *ip)
dip->nlink = ip->nlink;
dip->size = ip->size;
memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));
- bwrite(bp);
+ log_write(bp);
brelse(bp);
}
@@ -339,7 +339,7 @@ bmap(struct inode *ip, uint bn)
a = (uint*)bp->data;
if((addr = a[bn]) == 0){
a[bn] = addr = balloc(ip->dev);
- bwrite(bp);
+ log_write(bp);
}
brelse(bp);
return addr;
View
1 fs.h
@@ -13,6 +13,7 @@ struct superblock {
uint size; // Size of file system image (blocks)
uint nblocks; // Number of data blocks
uint ninodes; // Number of inodes.
+ uint nlog; // Number of log blocks
};
#define NDIRECT 12
View
@@ -3,9 +3,12 @@
#include "syscall.h"
#include "traps.h"
+
# exec(init, argv)
.globl start
start:
+ movl $SYS_init, %eax
+ int $T_SYSCALL
pushl $argv
pushl $init
pushl $0 // where caller pc would be
View
164 log.c
@@ -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++;
+}
View
2 main.c
@@ -20,7 +20,7 @@ main(void)
lapicinit(mpbcpu());
seginit(); // set up segments
kinit(); // initialize memory allocator
- jmpkstack(); // call mainc() on a properly-allocated stack
+ jmpkstack(); // call mainc() on a properly-allocated stack
}
void
View
13 mkfs.c
@@ -9,8 +9,10 @@
#include "types.h"
#include "fs.h"
#include "stat.h"
+#include "param.h"
-int nblocks = 995;
+int nblocks = 985;
+int nlog = LOGSIZE;
int ninodes = 200;
int size = 1024;
@@ -79,17 +81,18 @@ main(int argc, char *argv[])
sb.size = xint(size);
sb.nblocks = xint(nblocks); // so whole disk is size sectors
sb.ninodes = xint(ninodes);
+ sb.nlog = xint(nlog);
bitblocks = size/(512*8) + 1;
usedblocks = ninodes / IPB + 3 + bitblocks;
freeblock = usedblocks;
- printf("used %d (bit %d ninode %zu) free %u total %d\n", usedblocks,
- bitblocks, ninodes/IPB + 1, freeblock, nblocks+usedblocks);
+ printf("used %d (bit %d ninode %zu) free %u log %u total %d\n", 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);
memset(buf, 0, sizeof(buf));
View
@@ -10,3 +10,5 @@
#define USERTOP 0xA0000 // end of user address space
#define PHYSTOP 0x1000000 // use phys mem up to here as free pool
#define MAXARG 32 // max exec arguments
+#define LOGSIZE 10 // size of log
+
View
@@ -98,39 +98,52 @@ extern int sys_wait(void);
extern int sys_write(void);
extern int sys_uptime(void);
+int
+sys_init(void)
+{
+ initlog();
+ return 0;
+}
+
static int (*syscalls[])(void) = {
-[SYS_chdir] sys_chdir,
-[SYS_close] sys_close,
-[SYS_dup] sys_dup,
-[SYS_exec] sys_exec,
-[SYS_exit] sys_exit,
+[SYS_init] sys_init,
[SYS_fork] sys_fork,
-[SYS_fstat] sys_fstat,
-[SYS_getpid] sys_getpid,
-[SYS_kill] sys_kill,
-[SYS_link] sys_link,
-[SYS_mkdir] sys_mkdir,
-[SYS_mknod] sys_mknod,
-[SYS_open] sys_open,
+[SYS_exit] sys_exit,
+[SYS_wait] sys_wait,
[SYS_pipe] sys_pipe,
[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_sleep] sys_sleep,
-[SYS_unlink] sys_unlink,
-[SYS_wait] sys_wait,
-[SYS_write] sys_write,
[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
syscall(void)
{
int num;
-
+
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]();
+ } else if (num >= SYS_open && num < NELEM(syscalls) && syscalls[num]) {
+ begin_trans();
proc->tf->eax = syscalls[num]();
- else {
+ commit_trans();
+ } else {
cprintf("%d %s: unknown sys call %d\n",
proc->pid, proc->name, num);
proc->tf->eax = -1;
Oops, something went wrong.

0 comments on commit 13a96ba

Please sign in to comment.