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

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