root/log.c

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

/* [previous][next][first][last][top][bottom][index][help]  */