diff options
author | Frans Kaashoek <[email protected]> | 2011-07-27 20:35:46 -0400 |
---|---|---|
committer | Frans Kaashoek <[email protected]> | 2011-07-27 20:35:46 -0400 |
commit | 13a96baefc0ff5d8262c4bc8c797bee4b157443c (patch) | |
tree | a84aa8ed35618f99c3d7e8cdd466d22ae7bad597 /log.c | |
parent | 97657d703f7a92a088b400980c17249f34640a5e (diff) | |
download | xv6-labs-13a96baefc0ff5d8262c4bc8c797bee4b157443c.tar.gz xv6-labs-13a96baefc0ff5d8262c4bc8c797bee4b157443c.tar.bz2 xv6-labs-13a96baefc0ff5d8262c4bc8c797bee4b157443c.zip |
Dirt simple logging
Passes usertests and stressfs
Seems to recover correctly in a number of simple cases
Diffstat (limited to 'log.c')
-rw-r--r-- | log.c | 164 |
1 files changed, 164 insertions, 0 deletions
@@ -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++; +} |