xv6

port of xv6 to x86-64
git clone http://frotz.net/git/xv6.git
Log | Files | Refs | README | LICENSE

log.c (4545B)


      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. Each system call that might write the file system
      9 // should be surrounded with begin_trans() and commit_trans() calls.
     10 //
     11 // The log holds at most one transaction at a time. Commit forces
     12 // the log (with commit record) to disk, then installs the affected
     13 // blocks to disk, then erases the log. begin_trans() ensures that
     14 // only one system call can be in a transaction; others must wait.
     15 // 
     16 // Allowing only one transaction at a time means that the file
     17 // system code doesn't have to worry about the possibility of
     18 // one transaction reading a block that another one has modified,
     19 // for example an i-node block.
     20 //
     21 // Read-only system calls don't need to use transactions, though
     22 // this means that they may observe uncommitted data. I-node and
     23 // buffer locks prevent read-only calls from seeing inconsistent data.
     24 //
     25 // The log is a physical re-do log containing disk blocks.
     26 // The on-disk log format:
     27 //   header block, containing sector #s for block A, B, C, ...
     28 //   block A
     29 //   block B
     30 //   block C
     31 //   ...
     32 // Log appends are synchronous.
     33 
     34 // Contents of the header block, used for both the on-disk header block
     35 // and to keep track in memory of logged sector #s before commit.
     36 struct logheader {
     37   int n;   
     38   int sector[LOGSIZE];
     39 };
     40 
     41 struct log {
     42   struct spinlock lock;
     43   int start;
     44   int size;
     45   int busy; // a transaction is active
     46   int dev;
     47   struct logheader lh;
     48 };
     49 struct log log;
     50 
     51 static void recover_from_log(void);
     52 
     53 void
     54 initlog(void)
     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(ROOTDEV, &sb);
     62   log.start = sb.size - sb.nlog;
     63   log.size = sb.nlog;
     64   log.dev = ROOTDEV;
     65   recover_from_log();
     66 }
     67 
     68 // Copy committed blocks from log to their home location
     69 static void 
     70 install_trans(void)
     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.sector[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)
     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.sector[i] = lh->sector[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)
    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->sector[i] = log.lh.sector[i];
    110   }
    111   bwrite(buf);
    112   brelse(buf);
    113 }
    114 
    115 static void
    116 recover_from_log(void)
    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 void
    125 begin_trans(void)
    126 {
    127   acquire(&log.lock);
    128   while (log.busy) {
    129     sleep(&log, &log.lock);
    130   }
    131   log.busy = 1;
    132   release(&log.lock);
    133 }
    134 
    135 void
    136 commit_trans(void)
    137 {
    138   if (log.lh.n > 0) {
    139     write_head();    // Write header to disk -- the real commit
    140     install_trans(); // Now install writes to home locations
    141     log.lh.n = 0; 
    142     write_head();    // Erase the transaction from the log
    143   }
    144   
    145   acquire(&log.lock);
    146   log.busy = 0;
    147   wakeup(&log);
    148   release(&log.lock);
    149 }
    150 
    151 // Caller has modified b->data and is done with the buffer.
    152 // Append the block to the log and record the block number, 
    153 // but don't write the log header (which would commit the write).
    154 // log_write() replaces bwrite(); a typical use is:
    155 //   bp = bread(...)
    156 //   modify bp->data[]
    157 //   log_write(bp)
    158 //   brelse(bp)
    159 void
    160 log_write(struct buf *b)
    161 {
    162   int i;
    163 
    164   if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1)
    165     panic("too big a transaction");
    166   if (!log.busy)
    167     panic("write outside of trans");
    168 
    169   for (i = 0; i < log.lh.n; i++) {
    170     if (log.lh.sector[i] == b->sector)   // log absorbtion?
    171       break;
    172   }
    173   log.lh.sector[i] = b->sector;
    174   struct buf *lbuf = bread(b->dev, log.start+i+1);
    175   memmove(lbuf->data, b->data, BSIZE);
    176   bwrite(lbuf);
    177   brelse(lbuf);
    178   if (i == log.lh.n)
    179     log.lh.n++;
    180   b->flags |= B_DIRTY; // XXX prevent eviction
    181 }
    182 
    183 //PAGEBREAK!
    184 // Blank page.
    185