2011-07-28 00:35:46 +00:00
|
|
|
#include "types.h"
|
2019-05-31 15:45:42 +00:00
|
|
|
#include "riscv.h"
|
2011-07-28 00:35:46 +00:00
|
|
|
#include "defs.h"
|
|
|
|
#include "param.h"
|
|
|
|
#include "spinlock.h"
|
2016-09-11 21:24:04 +00:00
|
|
|
#include "sleeplock.h"
|
2011-07-28 00:35:46 +00:00
|
|
|
#include "fs.h"
|
|
|
|
#include "buf.h"
|
|
|
|
|
2014-08-27 21:15:30 +00:00
|
|
|
// Simple logging that allows concurrent FS system calls.
|
2011-08-12 13:25:39 +00:00
|
|
|
//
|
2014-08-28 10:27:01 +00:00
|
|
|
// A log transaction contains the updates of multiple FS system
|
|
|
|
// calls. The logging system only commits when there are
|
2014-08-27 21:15:30 +00:00
|
|
|
// no FS system calls active. Thus there is never
|
|
|
|
// any reasoning required about whether a commit might
|
|
|
|
// write an uncommitted system call's updates to disk.
|
|
|
|
//
|
|
|
|
// A system call should call begin_op()/end_op() to mark
|
|
|
|
// its start and end. Usually begin_op() just increments
|
|
|
|
// the count of in-progress FS system calls and returns.
|
|
|
|
// But if it thinks the log is close to running out, it
|
2014-08-28 10:27:01 +00:00
|
|
|
// sleeps until the last outstanding end_op() commits.
|
2011-08-12 13:25:39 +00:00
|
|
|
//
|
|
|
|
// The log is a physical re-do log containing disk blocks.
|
|
|
|
// The on-disk log format:
|
2015-04-03 12:22:02 +00:00
|
|
|
// header block, containing block #s for block A, B, C, ...
|
2011-08-12 13:25:39 +00:00
|
|
|
// block A
|
|
|
|
// block B
|
|
|
|
// block C
|
|
|
|
// ...
|
|
|
|
// Log appends are synchronous.
|
|
|
|
|
|
|
|
// Contents of the header block, used for both the on-disk header block
|
2015-04-03 12:22:02 +00:00
|
|
|
// and to keep track in memory of logged block# before commit.
|
2011-07-28 00:35:46 +00:00
|
|
|
struct logheader {
|
2016-08-25 13:13:00 +00:00
|
|
|
int n;
|
2015-04-03 12:22:02 +00:00
|
|
|
int block[LOGSIZE];
|
2011-07-28 00:35:46 +00:00
|
|
|
};
|
|
|
|
|
2011-09-02 19:08:35 +00:00
|
|
|
struct log {
|
2011-07-28 00:35:46 +00:00
|
|
|
struct spinlock lock;
|
|
|
|
int start;
|
|
|
|
int size;
|
2014-08-27 21:15:30 +00:00
|
|
|
int outstanding; // how many FS sys calls are executing.
|
|
|
|
int committing; // in commit(), please wait.
|
2011-07-28 00:35:46 +00:00
|
|
|
int dev;
|
|
|
|
struct logheader lh;
|
2011-09-02 19:08:35 +00:00
|
|
|
};
|
|
|
|
struct log log;
|
2011-07-28 00:35:46 +00:00
|
|
|
|
|
|
|
static void recover_from_log(void);
|
2014-08-27 21:15:30 +00:00
|
|
|
static void commit();
|
2011-07-28 00:35:46 +00:00
|
|
|
|
|
|
|
void
|
2015-06-27 16:39:13 +00:00
|
|
|
initlog(int dev)
|
2011-07-28 00:35:46 +00:00
|
|
|
{
|
|
|
|
if (sizeof(struct logheader) >= BSIZE)
|
|
|
|
panic("initlog: too big logheader");
|
|
|
|
|
|
|
|
struct superblock sb;
|
|
|
|
initlock(&log.lock, "log");
|
2015-06-27 16:39:13 +00:00
|
|
|
readsb(dev, &sb);
|
|
|
|
log.start = sb.logstart;
|
2011-07-28 00:35:46 +00:00
|
|
|
log.size = sb.nlog;
|
2015-06-27 16:39:13 +00:00
|
|
|
log.dev = dev;
|
2011-07-28 00:35:46 +00:00
|
|
|
recover_from_log();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy committed blocks from log to their home location
|
2016-08-25 13:13:00 +00:00
|
|
|
static void
|
2011-07-28 00:35:46 +00:00
|
|
|
install_trans(void)
|
|
|
|
{
|
|
|
|
int tail;
|
|
|
|
|
2011-08-12 13:25:39 +00:00
|
|
|
for (tail = 0; tail < log.lh.n; tail++) {
|
2011-09-01 14:25:20 +00:00
|
|
|
struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block
|
2015-04-03 12:22:02 +00:00
|
|
|
struct buf *dbuf = bread(log.dev, log.lh.block[tail]); // read dst
|
2011-09-01 14:25:20 +00:00
|
|
|
memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst
|
2011-10-11 10:41:37 +00:00
|
|
|
bwrite(dbuf); // write dst to disk
|
2019-07-30 12:54:43 +00:00
|
|
|
bunpin(dbuf);
|
2016-08-25 13:13:00 +00:00
|
|
|
brelse(lbuf);
|
2011-07-28 00:35:46 +00:00
|
|
|
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;
|
2011-08-12 13:25:39 +00:00
|
|
|
log.lh.n = lh->n;
|
|
|
|
for (i = 0; i < log.lh.n; i++) {
|
2015-04-03 12:22:02 +00:00
|
|
|
log.lh.block[i] = lh->block[i];
|
2011-07-28 00:35:46 +00:00
|
|
|
}
|
|
|
|
brelse(buf);
|
|
|
|
}
|
|
|
|
|
2011-10-11 10:41:37 +00:00
|
|
|
// Write in-memory log header to disk.
|
|
|
|
// This is the true point at which the
|
|
|
|
// current transaction commits.
|
2011-07-28 00:35:46 +00:00
|
|
|
static void
|
|
|
|
write_head(void)
|
|
|
|
{
|
|
|
|
struct buf *buf = bread(log.dev, log.start);
|
|
|
|
struct logheader *hb = (struct logheader *) (buf->data);
|
|
|
|
int i;
|
2011-08-12 13:25:39 +00:00
|
|
|
hb->n = log.lh.n;
|
|
|
|
for (i = 0; i < log.lh.n; i++) {
|
2015-04-03 12:22:02 +00:00
|
|
|
hb->block[i] = log.lh.block[i];
|
2011-07-28 00:35:46 +00:00
|
|
|
}
|
|
|
|
bwrite(buf);
|
|
|
|
brelse(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
recover_from_log(void)
|
|
|
|
{
|
2016-08-25 13:13:00 +00:00
|
|
|
read_head();
|
2011-08-15 16:44:20 +00:00
|
|
|
install_trans(); // if committed, copy from log to disk
|
2011-08-12 13:25:39 +00:00
|
|
|
log.lh.n = 0;
|
2011-08-15 16:44:20 +00:00
|
|
|
write_head(); // clear the log
|
2011-07-28 00:35:46 +00:00
|
|
|
}
|
|
|
|
|
2014-08-28 10:27:01 +00:00
|
|
|
// called at the start of each FS system call.
|
2011-07-28 00:35:46 +00:00
|
|
|
void
|
2014-08-27 21:15:30 +00:00
|
|
|
begin_op(void)
|
2011-07-28 00:35:46 +00:00
|
|
|
{
|
|
|
|
acquire(&log.lock);
|
2014-08-27 21:15:30 +00:00
|
|
|
while(1){
|
|
|
|
if(log.committing){
|
|
|
|
sleep(&log, &log.lock);
|
2014-08-28 09:57:47 +00:00
|
|
|
} else if(log.lh.n + (log.outstanding+1)*MAXOPBLOCKS > LOGSIZE){
|
|
|
|
// this op might exhaust log space; wait for commit.
|
|
|
|
sleep(&log, &log.lock);
|
2014-08-27 21:15:30 +00:00
|
|
|
} else {
|
|
|
|
log.outstanding += 1;
|
|
|
|
release(&log.lock);
|
|
|
|
break;
|
|
|
|
}
|
2011-08-29 21:18:40 +00:00
|
|
|
}
|
2011-07-28 00:35:46 +00:00
|
|
|
}
|
|
|
|
|
2014-08-28 10:27:01 +00:00
|
|
|
// called at the end of each FS system call.
|
|
|
|
// commits if this was the last outstanding operation.
|
2011-07-28 00:35:46 +00:00
|
|
|
void
|
2014-08-27 21:15:30 +00:00
|
|
|
end_op(void)
|
|
|
|
{
|
|
|
|
int do_commit = 0;
|
|
|
|
|
|
|
|
acquire(&log.lock);
|
|
|
|
log.outstanding -= 1;
|
|
|
|
if(log.committing)
|
|
|
|
panic("log.committing");
|
|
|
|
if(log.outstanding == 0){
|
|
|
|
do_commit = 1;
|
|
|
|
log.committing = 1;
|
2014-08-28 09:57:47 +00:00
|
|
|
} else {
|
2017-08-09 18:16:55 +00:00
|
|
|
// begin_op() may be waiting for log space,
|
|
|
|
// and decrementing log.outstanding has decreased
|
|
|
|
// the amount of reserved space.
|
2014-08-28 09:57:47 +00:00
|
|
|
wakeup(&log);
|
2014-08-27 21:15:30 +00:00
|
|
|
}
|
|
|
|
release(&log.lock);
|
|
|
|
|
|
|
|
if(do_commit){
|
2014-08-28 10:27:01 +00:00
|
|
|
// call commit w/o holding locks, since not allowed
|
|
|
|
// to sleep with locks.
|
2014-08-27 21:15:30 +00:00
|
|
|
commit();
|
|
|
|
acquire(&log.lock);
|
|
|
|
log.committing = 0;
|
|
|
|
wakeup(&log);
|
|
|
|
release(&log.lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-28 20:06:12 +00:00
|
|
|
// Copy modified blocks from cache to log.
|
2016-08-25 13:13:00 +00:00
|
|
|
static void
|
2014-08-28 20:06:12 +00:00
|
|
|
write_log(void)
|
|
|
|
{
|
|
|
|
int tail;
|
|
|
|
|
|
|
|
for (tail = 0; tail < log.lh.n; tail++) {
|
|
|
|
struct buf *to = bread(log.dev, log.start+tail+1); // log block
|
2015-04-03 12:22:02 +00:00
|
|
|
struct buf *from = bread(log.dev, log.lh.block[tail]); // cache block
|
2014-08-28 20:06:12 +00:00
|
|
|
memmove(to->data, from->data, BSIZE);
|
|
|
|
bwrite(to); // write the log
|
2016-08-25 13:13:00 +00:00
|
|
|
brelse(from);
|
2014-08-28 20:06:12 +00:00
|
|
|
brelse(to);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-27 21:15:30 +00:00
|
|
|
static void
|
|
|
|
commit()
|
2011-07-28 00:35:46 +00:00
|
|
|
{
|
2011-08-15 18:11:22 +00:00
|
|
|
if (log.lh.n > 0) {
|
2014-08-28 20:06:12 +00:00
|
|
|
write_log(); // Write modified blocks from cache to log
|
2011-10-11 10:41:37 +00:00
|
|
|
write_head(); // Write header to disk -- the real commit
|
|
|
|
install_trans(); // Now install writes to home locations
|
2016-08-25 13:13:00 +00:00
|
|
|
log.lh.n = 0;
|
2011-10-11 10:41:37 +00:00
|
|
|
write_head(); // Erase the transaction from the log
|
2011-08-15 18:11:22 +00:00
|
|
|
}
|
2011-07-28 00:35:46 +00:00
|
|
|
}
|
|
|
|
|
2011-08-12 13:25:39 +00:00
|
|
|
// Caller has modified b->data and is done with the buffer.
|
2019-07-29 21:33:16 +00:00
|
|
|
// Record the block number and pin in the cache by increasing refcnt.
|
2014-08-28 20:06:12 +00:00
|
|
|
// commit()/write_log() will do the disk write.
|
|
|
|
//
|
2011-08-12 13:25:39 +00:00
|
|
|
// log_write() replaces bwrite(); a typical use is:
|
|
|
|
// bp = bread(...)
|
|
|
|
// modify bp->data[]
|
|
|
|
// log_write(bp)
|
|
|
|
// brelse(bp)
|
2011-07-28 00:35:46 +00:00
|
|
|
void
|
|
|
|
log_write(struct buf *b)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2011-08-12 13:25:39 +00:00
|
|
|
if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1)
|
2011-07-28 00:35:46 +00:00
|
|
|
panic("too big a transaction");
|
2014-08-27 21:15:30 +00:00
|
|
|
if (log.outstanding < 1)
|
2014-08-28 20:06:12 +00:00
|
|
|
panic("log_write outside of trans");
|
2011-07-28 00:35:46 +00:00
|
|
|
|
2015-02-19 16:51:47 +00:00
|
|
|
acquire(&log.lock);
|
2011-08-12 13:25:39 +00:00
|
|
|
for (i = 0; i < log.lh.n; i++) {
|
2015-04-03 12:22:02 +00:00
|
|
|
if (log.lh.block[i] == b->blockno) // log absorbtion
|
2011-07-28 00:35:46 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-04-03 12:22:02 +00:00
|
|
|
log.lh.block[i] = b->blockno;
|
2019-07-29 21:33:16 +00:00
|
|
|
if (i == log.lh.n) { // Add new block to log?
|
2019-07-30 12:54:43 +00:00
|
|
|
bpin(b);
|
2011-08-12 13:25:39 +00:00
|
|
|
log.lh.n++;
|
2019-07-29 21:33:16 +00:00
|
|
|
}
|
2015-02-19 16:51:47 +00:00
|
|
|
release(&log.lock);
|
2011-07-28 00:35:46 +00:00
|
|
|
}
|
2011-09-02 19:14:06 +00:00
|
|
|
|