Design a log system for crash recovery
Last updated
// Contents of the header block, used for both the on-disk header block
// and to keep track in memory of logged block# before commit.
struct logheader {
int n; //xiaying: number of current added block.
int block[LOGSIZE];
};
struct log {
struct spinlock lock;
int start;
int size; //xiaying: total allowed capacity.
int outstanding; // how many FS sys calls are executing.
int committing; // in commit(), please wait.
int dev;
struct logheader lh;
};
struct log log[NDISK];
static void recover_from_log(int);
static void commit(int);begin_op();
ilock(f->ip);
r = writei(f->ip, …);
iunlock(f->ip);
end_op();// called at the start of each FS system call.
void
begin_op(int dev)
{
acquire(&log[dev].lock);
while(1){
if(log[dev].committing){
sleep(&log, &log[dev].lock);
} else if(log[dev].lh.n + (log[dev].outstanding+1)*MAXOPBLOCKS > LOGSIZE){
// this op might exhaust log space; wait for commit.
sleep(&log, &log[dev].lock);
} else {
log[dev].outstanding += 1;
release(&log[dev].lock);
break;
}
}
}void
ilock(struct inode *ip)
{
struct buf *bp;
struct dinode *dip;
if(ip == 0 || ip->ref < 1)
panic("ilock");
acquiresleep(&ip->lock);
if(ip->valid == 0){
bp = bread(ip->dev, IBLOCK(ip->inum, sb));
dip = (struct dinode*)bp->data + ip->inum%IPB;
ip->type = dip->type;
ip->major = dip->major;
ip->minor = dip->minor;
ip->nlink = dip->nlink;
ip->size = dip->size;
memmove(ip->addrs, dip->addrs, sizeof(ip->addrs));
brelse(bp);
ip->valid = 1;
if(ip->type == 0)
panic("ilock: no type");
}
}void
iunlock(struct inode *ip)
{
if(ip == 0 || !holdingsleep(&ip->lock) || ip->ref < 1)
panic("iunlock");
releasesleep(&ip->lock);
}void
end_op(int dev)
{
int do_commit = 0;
acquire(&log[dev].lock);
log[dev].outstanding -= 1;
if(log[dev].committing)
panic(“log[dev].committing”);
if(log[dev].outstanding == 0){
do_commit = 1;
log[dev].committing = 1;
} else {
// begin_op() may be waiting for log space,
// and decrementing log[dev].outstanding has decreased
// the amount of reserved space.
wakeup(&log);
}
release(&log[dev].lock);
if(do_commit){
// call commit w/o holding locks, since not allowed
// to sleep with locks.
commit(dev);
acquire(&log[dev].lock);
log[dev].committing = 0;
wakeup(&log);
release(&log[dev].lock);
}
}