Mercurial > hg > Members > anatofuz > CbC_xv6
diff src/log.c @ 0:83c23a36980d
Init
author | Tatsuki IHA <e125716@ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 26 May 2017 23:11:05 +0900 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/log.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,206 @@ +#include "types.h" +#include "defs.h" +#include "param.h" +#include "spinlock.h" +#include "fs.h" +#include "buf.h" + +// Simple logging. Each system call that might write the file system +// should be surrounded with begin_trans() and commit_trans() calls. +// +// The log holds at most one transaction at a time. Commit forces +// the log (with commit record) to disk, then installs the affected +// blocks to disk, then erases the log. begin_trans() ensures that +// only one system call can be in a transaction; others must wait. +// +// Allowing only one transaction at a time means that the file +// system code doesn't have to worry about the possibility of +// one transaction reading a block that another one has modified, +// for example an i-node block. +// +// Read-only system calls don't need to use transactions, though +// this means that they may observe uncommitted data. I-node and +// buffer locks prevent read-only calls from seeing inconsistent data. +// +// The log is a physical re-do log containing disk blocks. +// The on-disk log format: +// header block, containing sector #s for block A, B, C, ... +// block A +// block B +// block C +// ... +// Log appends are synchronous. + +// Contents of the header block, used for both the on-disk header block +// and to keep track in memory of logged sector #s before commit. +struct logheader { + int n; + int sector[LOGSIZE]; +}; + +struct log { + struct spinlock lock; + int start; + int size; + int busy; // a transaction is active + int dev; + struct logheader lh; +}; +struct log log; + +static void recover_from_log(void); + +void initlog(void) +{ + struct superblock sb; + + if (sizeof(struct logheader) >= BSIZE) { + panic("initlog: too big logheader"); + } + + 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; + struct buf *lbuf; + struct buf *dbuf; + + for (tail = 0; tail < log.lh.n; tail++) { + lbuf = bread(log.dev, log.start+tail+1); // read log block + dbuf = bread(log.dev, log.lh.sector[tail]); // read dst + + memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst + + bwrite(dbuf); // write dst to disk + brelse(lbuf); + brelse(dbuf); + } +} + +// Read the log header from disk into the in-memory log header +static void read_head(void) +{ + struct buf *buf; + struct logheader *lh; + int i; + + buf = bread(log.dev, log.start); + lh = (struct logheader *) (buf->data); + log.lh.n = lh->n; + + for (i = 0; i < log.lh.n; i++) { + log.lh.sector[i] = lh->sector[i]; + } + + brelse(buf); +} + +// Write in-memory log header to disk. +// This is the true point at which the +// current transaction commits. +static void write_head(void) +{ + struct buf *buf; + struct logheader *hb; + int i; + + buf = bread(log.dev, log.start); + hb = (struct logheader *) (buf->data); + + hb->n = log.lh.n; + + for (i = 0; i < log.lh.n; i++) { + hb->sector[i] = log.lh.sector[i]; + } + + bwrite(buf); + brelse(buf); +} + +static void recover_from_log(void) +{ + read_head(); + install_trans(); // if committed, copy from log to disk + log.lh.n = 0; + write_head(); // clear the log +} + +void begin_trans(void) +{ + acquire(&log.lock); + + while (log.busy) { + sleep(&log, &log.lock); + } + + log.busy = 1; + release(&log.lock); +} + +void commit_trans(void) +{ + if (log.lh.n > 0) { + write_head(); // Write header to disk -- the real commit + install_trans(); // Now install writes to home locations + log.lh.n = 0; + write_head(); // Erase the transaction from the log + } + + acquire(&log.lock); + log.busy = 0; + wakeup(&log); + release(&log.lock); +} + +// Caller has modified b->data and is done with the buffer. +// Append the block to the log and record the block number, +// but don't write the log header (which would commit the write). +// log_write() replaces bwrite(); a typical use is: +// bp = bread(...) +// modify bp->data[] +// log_write(bp) +// brelse(bp) +void log_write(struct buf *b) +{ + struct buf *lbuf; + int i; + + if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1) { + panic("too big a transaction"); + } + + if (!log.busy) { + panic("write outside of trans"); + } + + for (i = 0; i < log.lh.n; i++) { + if (log.lh.sector[i] == b->sector) { // log absorbtion? + break; + } + } + + log.lh.sector[i] = b->sector; + lbuf = bread(b->dev, log.start+i+1); + + memmove(lbuf->data, b->data, BSIZE); + bwrite(lbuf); + brelse(lbuf); + + if (i == log.lh.n) { + log.lh.n++; + } + + b->flags |= B_DIRTY; // XXX prevent eviction +} + +//PAGEBREAK! +// Blank page. +