165 lines
4 KiB
C
165 lines
4 KiB
C
|
#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++;
|
||
|
}
|