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[LOGBLOCKS];
  38 };
  39 
  40 struct 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 };
  48 struct log log;
  49 
  50 static void recover_from_log(void);
  51 static void commit();
  52 
  53 void
  54 initlog(int dev, struct superblock *sb)
  55 {
  56   if (sizeof(struct logheader) >= BSIZE)
  57     panic("initlog: too big logheader");
  58 
  59   initlock(&log.lock, "log");
  60   log.start = sb->logstart;
  61   log.dev = dev;
  62   recover_from_log();
  63 }
  64 
  65 // Copy committed blocks from log to their home location
  66 static void
  67 install_trans(int recovering)
  68 {
  69   int tail;
  70 
  71   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 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     if(recovering == 0)
  80       bunpin(dbuf);
  81     brelse(lbuf);
  82     brelse(dbuf);
  83   }
  84 }
  85 
  86 // Read the log header from disk into the in-memory log header
  87 static void
  88 read_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 }
  99 
 100 // Write in-memory log header to disk.
 101 // This is the true point at which the
 102 // current transaction commits.
 103 static void
 104 write_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 }
 116 
 117 static void
 118 recover_from_log(void)
 119 {
 120   read_head();
 121   install_trans(1); // if committed, copy from log to disk
 122   log.lh.n = 0;
 123   write_head(); // clear the log
 124 }
 125 
 126 // called at the start of each FS system call.
 127 void
 128 begin_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 }
 144 
 145 // called at the end of each FS system call.
 146 // commits if this was the last outstanding operation.
 147 void
 148 end_op(void)
 149 {
 150   int do_commit = 0;
 151 
 152   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 decreased
 162     // the amount of reserved space.
 163     wakeup(&log);
 164   }
 165   release(&log.lock);
 166 
 167   if(do_commit){
 168     // call commit w/o holding locks, since not allowed
 169     // to sleep with locks.
 170     commit();
 171     acquire(&log.lock);
 172     log.committing = 0;
 173     wakeup(&log);
 174     release(&log.lock);
 175   }
 176 }
 177 
 178 // Copy modified blocks from cache to log.
 179 static void
 180 write_log(void)
 181 {
 182   int tail;
 183 
 184   for (tail = 0; tail < log.lh.n; tail++) {
 185     struct buf *to = bread(log.dev, log.start+tail+1); // log block
 186     struct buf *from = bread(log.dev, log.lh.block[tail]); // cache block
 187     memmove(to->data, from->data, BSIZE);
 188     bwrite(to);  // write the log
 189     brelse(from);
 190     brelse(to);
 191   }
 192 }
 193 
 194 static void
 195 commit()
 196 {
 197   if (log.lh.n > 0) {
 198     write_log();     // Write modified blocks from cache to log
 199     write_head();    // Write header to disk -- the real commit
 200     install_trans(0); // Now install writes to home locations
 201     log.lh.n = 0;
 202     write_head();    // Erase the transaction from the log
 203   }
 204 }
 205 
 206 // 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)
 215 void
 216 log_write(struct buf *b)
 217 {
 218   int i;
 219 
 220   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");
 225 
 226   for (i = 0; i < log.lh.n; i++) {
 227     if (log.lh.block[i] == b->blockno)   // log absorption
 228       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 }
 237 

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