root/kernel/log.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. initlog
  2. install_trans
  3. read_head
  4. write_head
  5. recover_from_log
  6. begin_op
  7. end_op
  8. write_log
  9. commit
  10. log_write

   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"
   9 
  10 // Simple logging that allows concurrent FS system calls.
  11 //
  12 // A log transaction contains the updates of multiple FS system
  13 // calls. The logging system only commits when there are
  14 // no FS system calls active. Thus there is never
  15 // any reasoning required about whether a commit might
  16 // write an uncommitted system call's updates to disk.
  17 //
  18 // A system call should call begin_op()/end_op() to mark
  19 // its start and end. Usually begin_op() just increments
  20 // the count of in-progress FS system calls and returns.
  21 // But if it thinks the log is close to running out, it
  22 // 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 A
  28 //   block B
  29 //   block C
  30 //   ...
  31 // Log appends are synchronous.
  32 
  33 // Contents of the header block, used for both the on-disk header block
  34 // and to keep track in memory of logged block# before commit.
  35 struct logheader {
  36   int n;
  37   int block[LOGSIZE];
  38 };
  39 
  40 struct log {
  41   struct spinlock lock;
  42   int start;
  43   int size;
  44   int outstanding; // how many FS sys calls are executing.
  45   int committing;  // in commit(), please wait.
  46   int dev;
  47   struct logheader lh;
  48 };
  49 struct log log;
  50 
  51 static void recover_from_log(void);
  52 static void commit();
  53 
  54 void
  55 initlog(int dev, struct superblock *sb)
  56 {
  57   if (sizeof(struct logheader) >= BSIZE)
  58     panic("initlog: too big logheader");
  59 
  60   initlock(&log.lock, "log");
  61   log.start = sb->logstart;
  62   log.size = sb->nlog;
  63   log.dev = dev;
  64   recover_from_log();
  65 }
  66 
  67 // Copy committed blocks from log to their home location
  68 static void
  69 install_trans(int recovering)
  70 {
  71   int tail;
  72 
  73   for (tail = 0; tail < log.lh.n; tail++) {
  74     struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block
  75     struct buf *dbuf = bread(log.dev, log.lh.block[tail]); // read dst
  76     memmove(dbuf->data, lbuf->data, BSIZE);  // copy block to dst
  77     bwrite(dbuf);  // write dst to disk
  78     if(recovering == 0)
  79       bunpin(dbuf);
  80     brelse(lbuf);
  81     brelse(dbuf);
  82   }
  83 }
  84 
  85 // Read the log header from disk into the in-memory log header
  86 static void
  87 read_head(void)
  88 {
  89   struct buf *buf = bread(log.dev, log.start);
  90   struct logheader *lh = (struct logheader *) (buf->data);
  91   int i;
  92   log.lh.n = lh->n;
  93   for (i = 0; i < log.lh.n; i++) {
  94     log.lh.block[i] = lh->block[i];
  95   }
  96   brelse(buf);
  97 }
  98 
  99 // Write in-memory log header to disk.
 100 // This is the true point at which the
 101 // current transaction commits.
 102 static void
 103 write_head(void)
 104 {
 105   struct buf *buf = bread(log.dev, log.start);
 106   struct logheader *hb = (struct logheader *) (buf->data);
 107   int i;
 108   hb->n = log.lh.n;
 109   for (i = 0; i < log.lh.n; i++) {
 110     hb->block[i] = log.lh.block[i];
 111   }
 112   bwrite(buf);
 113   brelse(buf);
 114 }
 115 
 116 static void
 117 recover_from_log(void)
 118 {
 119   read_head();
 120   install_trans(1); // if committed, copy from log to disk
 121   log.lh.n = 0;
 122   write_head(); // clear the log
 123 }
 124 
 125 // called at the start of each FS system call.
 126 void
 127 begin_op(void)
 128 {
 129   acquire(&log.lock);
 130   while(1){
 131     if(log.committing){
 132       sleep(&log, &log.lock);
 133     } else if(log.lh.n + (log.outstanding+1)*MAXOPBLOCKS > LOGSIZE){
 134       // this op might exhaust log space; wait for commit.
 135       sleep(&log, &log.lock);
 136     } else {
 137       log.outstanding += 1;
 138       release(&log.lock);
 139       break;
 140     }
 141   }
 142 }
 143 
 144 // called at the end of each FS system call.
 145 // commits if this was the last outstanding operation.
 146 void
 147 end_op(void)
 148 {
 149   int do_commit = 0;
 150 
 151   acquire(&log.lock);
 152   log.outstanding -= 1;
 153   if(log.committing)
 154     panic("log.committing");
 155   if(log.outstanding == 0){
 156     do_commit = 1;
 157     log.committing = 1;
 158   } else {
 159     // begin_op() may be waiting for log space,
 160     // and decrementing log.outstanding has decreased
 161     // the amount of reserved space.
 162     wakeup(&log);
 163   }
 164   release(&log.lock);
 165 
 166   if(do_commit){
 167     // call commit w/o holding locks, since not allowed
 168     // to sleep with locks.
 169     commit();
 170     acquire(&log.lock);
 171     log.committing = 0;
 172     wakeup(&log);
 173     release(&log.lock);
 174   }
 175 }
 176 
 177 // Copy modified blocks from cache to log.
 178 static void
 179 write_log(void)
 180 {
 181   int tail;
 182 
 183   for (tail = 0; tail < log.lh.n; tail++) {
 184     struct buf *to = bread(log.dev, log.start+tail+1); // log block
 185     struct buf *from = bread(log.dev, log.lh.block[tail]); // cache block
 186     memmove(to->data, from->data, BSIZE);
 187     bwrite(to);  // write the log
 188     brelse(from);
 189     brelse(to);
 190   }
 191 }
 192 
 193 static void
 194 commit()
 195 {
 196   if (log.lh.n > 0) {
 197     write_log();     // Write modified blocks from cache to log
 198     write_head();    // Write header to disk -- the real commit
 199     install_trans(0); // Now install writes to home locations
 200     log.lh.n = 0;
 201     write_head();    // Erase the transaction from the log
 202   }
 203 }
 204 
 205 // Caller has modified b->data and is done with the buffer.
 206 // Record the block number and pin in the cache by increasing refcnt.
 207 // commit()/write_log() will do the disk write.
 208 //
 209 // log_write() replaces bwrite(); a typical use is:
 210 //   bp = bread(...)
 211 //   modify bp->data[]
 212 //   log_write(bp)
 213 //   brelse(bp)
 214 void
 215 log_write(struct buf *b)
 216 {
 217   int i;
 218 
 219   acquire(&log.lock);
 220   if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1)
 221     panic("too big a transaction");
 222   if (log.outstanding < 1)
 223     panic("log_write outside of trans");
 224 
 225   for (i = 0; i < log.lh.n; i++) {
 226     if (log.lh.block[i] == b->blockno)   // log absorption
 227       break;
 228   }
 229   log.lh.block[i] = b->blockno;
 230   if (i == log.lh.n) {  // Add new block to log?
 231     bpin(b);
 232     log.lh.n++;
 233   }
 234   release(&log.lock);
 235 }
 236 

/* [<][>][^][v][top][bottom][index][help] */