kernel/log.c

237 lines
1#include "types.h"2#include "riscv.h"3#include "defs.h"4#include "param.h"5#include "spinlock.h"6#include "sleeplock.h"7#include "fs.h"8#include "buf.h"910// Simple logging that allows concurrent FS system calls.11//12// A log transaction contains the updates of multiple FS system13// calls. The logging system only commits when there are14// no FS system calls active. Thus there is never15// any reasoning required about whether a commit might16// write an uncommitted system call's updates to disk.17//18// A system call should call begin_op()/end_op() to mark19// its start and end. Usually begin_op() just increments20// the count of in-progress FS system calls and returns.21// But if it thinks the log is close to running out, it22// sleeps until the last outstanding end_op() commits.23//24// The log is a physical re-do log containing disk blocks.25// The on-disk log format:26//   header block, containing block #s for block A, B, C, ...27//   block A28//   block B29//   block C30//   ...31// Log appends are synchronous.3233// Contents of the header block, used for both the on-disk header block34// and to keep track in memory of logged block# before commit.35struct logheader {36  int n;37  int block[LOGBLOCKS];38};3940struct log {41  struct spinlock lock;42  int start;43  int outstanding; // how many FS sys calls are executing.44  int committing;  // in commit(), please wait.45  int dev;46  struct logheader lh;47};48struct log log;4950static void recover_from_log(void);51static void commit();5253void54initlog(int dev, struct superblock *sb)55{56  if (sizeof(struct logheader) >= BSIZE)57    panic("initlog: too big logheader");5859  initlock(&log.lock, "log");60  log.start = sb->logstart;61  log.dev = dev;62  recover_from_log();63}6465// Copy committed blocks from log to their home location66static void67install_trans(int recovering)68{69  int tail;7071  for (tail = 0; tail < log.lh.n; tail++) {72    if(recovering) {73      printf("recovering tail %d dst %d\n", tail, log.lh.block[tail]);74    }75    struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block76    struct buf *dbuf = bread(log.dev, log.lh.block[tail]); // read dst77    memmove(dbuf->data, lbuf->data, BSIZE);  // copy block to dst78    bwrite(dbuf);  // write dst to disk79    if(recovering == 0)80      bunpin(dbuf);81    brelse(lbuf);82    brelse(dbuf);83  }84}8586// Read the log header from disk into the in-memory log header87static void88read_head(void)89{90  struct buf *buf = bread(log.dev, log.start);91  struct logheader *lh = (struct logheader *) (buf->data);92  int i;93  log.lh.n = lh->n;94  for (i = 0; i < log.lh.n; i++) {95    log.lh.block[i] = lh->block[i];96  }97  brelse(buf);98}99100// Write in-memory log header to disk.101// This is the true point at which the102// current transaction commits.103static void104write_head(void)105{106  struct buf *buf = bread(log.dev, log.start);107  struct logheader *hb = (struct logheader *) (buf->data);108  int i;109  hb->n = log.lh.n;110  for (i = 0; i < log.lh.n; i++) {111    hb->block[i] = log.lh.block[i];112  }113  bwrite(buf);114  brelse(buf);115}116117static void118recover_from_log(void)119{120  read_head();121  install_trans(1); // if committed, copy from log to disk122  log.lh.n = 0;123  write_head(); // clear the log124}125126// called at the start of each FS system call.127void128begin_op(void)129{130  acquire(&log.lock);131  while(1){132    if(log.committing){133      sleep(&log, &log.lock);134    } else if(log.lh.n + (log.outstanding+1)*MAXOPBLOCKS > LOGBLOCKS){135      // this op might exhaust log space; wait for commit.136      sleep(&log, &log.lock);137    } else {138      log.outstanding += 1;139      release(&log.lock);140      break;141    }142  }143}144145// called at the end of each FS system call.146// commits if this was the last outstanding operation.147void148end_op(void)149{150  int do_commit = 0;151152  acquire(&log.lock);153  log.outstanding -= 1;154  if(log.committing)155    panic("log.committing");156  if(log.outstanding == 0){157    do_commit = 1;158    log.committing = 1;159  } else {160    // begin_op() may be waiting for log space,161    // and decrementing log.outstanding has decreased162    // the amount of reserved space.163    wakeup(&log);164  }165  release(&log.lock);166167  if(do_commit){168    // call commit w/o holding locks, since not allowed169    // to sleep with locks.170    commit();171    acquire(&log.lock);172    log.committing = 0;173    wakeup(&log);174    release(&log.lock);175  }176}177178// Copy modified blocks from cache to log.179static void180write_log(void)181{182  int tail;183184  for (tail = 0; tail < log.lh.n; tail++) {185    struct buf *to = bread(log.dev, log.start+tail+1); // log block186    struct buf *from = bread(log.dev, log.lh.block[tail]); // cache block187    memmove(to->data, from->data, BSIZE);188    bwrite(to);  // write the log189    brelse(from);190    brelse(to);191  }192}193194static void195commit()196{197  if (log.lh.n > 0) {198    write_log();     // Write modified blocks from cache to log199    write_head();    // Write header to disk -- the real commit200    install_trans(0); // Now install writes to home locations201    log.lh.n = 0;202    write_head();    // Erase the transaction from the log203  }204}205206// Caller has modified b->data and is done with the buffer.207// Record the block number and pin in the cache by increasing refcnt.208// commit()/write_log() will do the disk write.209//210// log_write() replaces bwrite(); a typical use is:211//   bp = bread(...)212//   modify bp->data[]213//   log_write(bp)214//   brelse(bp)215void216log_write(struct buf *b)217{218  int i;219220  acquire(&log.lock);221  if (log.lh.n >= LOGBLOCKS)222    panic("too big a transaction");223  if (log.outstanding < 1)224    panic("log_write outside of trans");225226  for (i = 0; i < log.lh.n; i++) {227    if (log.lh.block[i] == b->blockno)   // log absorption228      break;229  }230  log.lh.block[i] = b->blockno;231  if (i == log.lh.n) {  // Add new block to log?232    bpin(b);233    log.lh.n++;234  }235  release(&log.lock);236}237238