xref: /xv6-public/log.c (revision 48aa9174)
113a96baeSFrans Kaashoek #include "types.h"
213a96baeSFrans Kaashoek #include "defs.h"
313a96baeSFrans Kaashoek #include "param.h"
413a96baeSFrans Kaashoek #include "spinlock.h"
513a96baeSFrans Kaashoek #include "fs.h"
613a96baeSFrans Kaashoek #include "buf.h"
713a96baeSFrans Kaashoek 
871453f72SRobert Morris // Simple logging that allows concurrent FS system calls.
92e590463SRobert Morris //
1071453f72SRobert Morris // A log transaction contains the updates of *multiple* FS system
1171453f72SRobert Morris // calls. The logging systems only commits when there are
1271453f72SRobert Morris // no FS system calls active. Thus there is never
1371453f72SRobert Morris // any reasoning required about whether a commit might
1471453f72SRobert Morris // write an uncommitted system call's updates to disk.
152e590463SRobert Morris //
1671453f72SRobert Morris // A system call should call begin_op()/end_op() to mark
1771453f72SRobert Morris // its start and end. Usually begin_op() just increments
1871453f72SRobert Morris // the count of in-progress FS system calls and returns.
1971453f72SRobert Morris // But if it thinks the log is close to running out, it
2071453f72SRobert Morris // blocks this system call, and causes the system to wait
2171453f72SRobert Morris // until end_op() indicates there are no executing FS
2271453f72SRobert Morris // system calls, at which point the last end_op() commits
2371453f72SRobert Morris // all the system calls' writes.
242e590463SRobert Morris //
252e590463SRobert Morris // The log is a physical re-do log containing disk blocks.
262e590463SRobert Morris // The on-disk log format:
272e590463SRobert Morris //   header block, containing sector #s for block A, B, C, ...
282e590463SRobert Morris //   block A
292e590463SRobert Morris //   block B
302e590463SRobert Morris //   block C
312e590463SRobert Morris //   ...
322e590463SRobert Morris // Log appends are synchronous.
3313a96baeSFrans Kaashoek 
342e590463SRobert Morris // Contents of the header block, used for both the on-disk header block
352e590463SRobert Morris // and to keep track in memory of logged sector #s before commit.
3613a96baeSFrans Kaashoek struct logheader {
372e590463SRobert Morris   int n;
3813a96baeSFrans Kaashoek   int sector[LOGSIZE];
3913a96baeSFrans Kaashoek };
4013a96baeSFrans Kaashoek 
41ee1b3306SAustin Clements struct log {
4213a96baeSFrans Kaashoek   struct spinlock lock;
4313a96baeSFrans Kaashoek   int start;
4413a96baeSFrans Kaashoek   int size;
4571453f72SRobert Morris   int outstanding; // how many FS sys calls are executing.
4671453f72SRobert Morris   int committing;  // in commit(), please wait.
4713a96baeSFrans Kaashoek   int dev;
4813a96baeSFrans Kaashoek   struct logheader lh;
49ee1b3306SAustin Clements };
50ee1b3306SAustin Clements struct log log;
5113a96baeSFrans Kaashoek 
5213a96baeSFrans Kaashoek static void recover_from_log(void);
5371453f72SRobert Morris static void commit();
5413a96baeSFrans Kaashoek 
55*48aa9174SRobert Morris // statistics, delete eventually XXX.
56*48aa9174SRobert Morris static int maxsize;
57*48aa9174SRobert Morris static int maxoutstanding;
58*48aa9174SRobert Morris 
5913a96baeSFrans Kaashoek void
6013a96baeSFrans Kaashoek initlog(void)
6113a96baeSFrans Kaashoek {
6213a96baeSFrans Kaashoek   if (sizeof(struct logheader) >= BSIZE)
6313a96baeSFrans Kaashoek     panic("initlog: too big logheader");
6413a96baeSFrans Kaashoek 
6513a96baeSFrans Kaashoek   struct superblock sb;
6613a96baeSFrans Kaashoek   initlock(&log.lock, "log");
6713a96baeSFrans Kaashoek   readsb(ROOTDEV, &sb);
6813a96baeSFrans Kaashoek   log.start = sb.size - sb.nlog;
6913a96baeSFrans Kaashoek   log.size = sb.nlog;
7013a96baeSFrans Kaashoek   log.dev = ROOTDEV;
7113a96baeSFrans Kaashoek   recover_from_log();
7213a96baeSFrans Kaashoek }
7313a96baeSFrans Kaashoek 
7413a96baeSFrans Kaashoek // Copy committed blocks from log to their home location
7513a96baeSFrans Kaashoek static void
7613a96baeSFrans Kaashoek install_trans(void)
7713a96baeSFrans Kaashoek {
7813a96baeSFrans Kaashoek   int tail;
7913a96baeSFrans Kaashoek 
802e590463SRobert Morris   for (tail = 0; tail < log.lh.n; tail++) {
81e25b74caSFrans Kaashoek     struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block
82e25b74caSFrans Kaashoek     struct buf *dbuf = bread(log.dev, log.lh.sector[tail]); // read dst
83e25b74caSFrans Kaashoek     memmove(dbuf->data, lbuf->data, BSIZE);  // copy block to dst
84a5fbfe41SRobert Morris     bwrite(dbuf);  // write dst to disk
8513a96baeSFrans Kaashoek     brelse(lbuf);
8613a96baeSFrans Kaashoek     brelse(dbuf);
8713a96baeSFrans Kaashoek   }
8813a96baeSFrans Kaashoek }
8913a96baeSFrans Kaashoek 
9013a96baeSFrans Kaashoek // Read the log header from disk into the in-memory log header
9113a96baeSFrans Kaashoek static void
9213a96baeSFrans Kaashoek read_head(void)
9313a96baeSFrans Kaashoek {
9413a96baeSFrans Kaashoek   struct buf *buf = bread(log.dev, log.start);
9513a96baeSFrans Kaashoek   struct logheader *lh = (struct logheader *) (buf->data);
9613a96baeSFrans Kaashoek   int i;
972e590463SRobert Morris   log.lh.n = lh->n;
982e590463SRobert Morris   for (i = 0; i < log.lh.n; i++) {
9913a96baeSFrans Kaashoek     log.lh.sector[i] = lh->sector[i];
10013a96baeSFrans Kaashoek   }
10113a96baeSFrans Kaashoek   brelse(buf);
10213a96baeSFrans Kaashoek }
10313a96baeSFrans Kaashoek 
104a5fbfe41SRobert Morris // Write in-memory log header to disk.
105a5fbfe41SRobert Morris // This is the true point at which the
106a5fbfe41SRobert Morris // current transaction commits.
10713a96baeSFrans Kaashoek static void
10813a96baeSFrans Kaashoek write_head(void)
10913a96baeSFrans Kaashoek {
11013a96baeSFrans Kaashoek   struct buf *buf = bread(log.dev, log.start);
11113a96baeSFrans Kaashoek   struct logheader *hb = (struct logheader *) (buf->data);
11213a96baeSFrans Kaashoek   int i;
1132e590463SRobert Morris   hb->n = log.lh.n;
1142e590463SRobert Morris   for (i = 0; i < log.lh.n; i++) {
11513a96baeSFrans Kaashoek     hb->sector[i] = log.lh.sector[i];
11613a96baeSFrans Kaashoek   }
11713a96baeSFrans Kaashoek   bwrite(buf);
11813a96baeSFrans Kaashoek   brelse(buf);
11913a96baeSFrans Kaashoek }
12013a96baeSFrans Kaashoek 
12113a96baeSFrans Kaashoek static void
12213a96baeSFrans Kaashoek recover_from_log(void)
12313a96baeSFrans Kaashoek {
12413a96baeSFrans Kaashoek   read_head();
1255053dd6aSRobert Morris   install_trans(); // if committed, copy from log to disk
1262e590463SRobert Morris   log.lh.n = 0;
1275053dd6aSRobert Morris   write_head(); // clear the log
12813a96baeSFrans Kaashoek }
12913a96baeSFrans Kaashoek 
13071453f72SRobert Morris // an FS system call should call begin_op() when it starts.
13113a96baeSFrans Kaashoek void
13271453f72SRobert Morris begin_op(void)
13313a96baeSFrans Kaashoek {
13413a96baeSFrans Kaashoek   acquire(&log.lock);
13571453f72SRobert Morris   while(1){
13671453f72SRobert Morris     if(log.committing){
1371ddfbbb1SFrans Kaashoek       sleep(&log, &log.lock);
138*48aa9174SRobert Morris     } else if(log.lh.n + (log.outstanding+1)*MAXOPBLOCKS > LOGSIZE){
139*48aa9174SRobert Morris       // this op might exhaust log space; wait for commit.
140*48aa9174SRobert Morris       sleep(&log, &log.lock);
14171453f72SRobert Morris     } else {
14271453f72SRobert Morris       log.outstanding += 1;
143*48aa9174SRobert Morris       if(log.outstanding > maxoutstanding){
144*48aa9174SRobert Morris         maxoutstanding = log.outstanding;
145*48aa9174SRobert Morris         cprintf("%d outstanding\n", log.outstanding);
146*48aa9174SRobert Morris       }
14713a96baeSFrans Kaashoek       release(&log.lock);
14871453f72SRobert Morris       break;
14971453f72SRobert Morris     }
15071453f72SRobert Morris   }
15113a96baeSFrans Kaashoek }
15213a96baeSFrans Kaashoek 
15371453f72SRobert Morris // an FS system call should call end_op() after it finishes.
15471453f72SRobert Morris // can't write the disk &c while holding locks, thus do_commit.
15513a96baeSFrans Kaashoek void
15671453f72SRobert Morris end_op(void)
15771453f72SRobert Morris {
15871453f72SRobert Morris   int do_commit = 0;
15971453f72SRobert Morris 
16071453f72SRobert Morris   acquire(&log.lock);
16171453f72SRobert Morris   log.outstanding -= 1;
16271453f72SRobert Morris   if(log.committing)
16371453f72SRobert Morris     panic("log.committing");
16471453f72SRobert Morris   if(log.outstanding == 0){
16571453f72SRobert Morris     do_commit = 1;
16671453f72SRobert Morris     log.committing = 1;
167*48aa9174SRobert Morris   } else {
168*48aa9174SRobert Morris     // begin_op() may be waiting for log space.
169*48aa9174SRobert Morris     wakeup(&log);
17071453f72SRobert Morris   }
17171453f72SRobert Morris   release(&log.lock);
17271453f72SRobert Morris 
17371453f72SRobert Morris   if(do_commit){
17471453f72SRobert Morris     commit();
17571453f72SRobert Morris     acquire(&log.lock);
17671453f72SRobert Morris     log.committing = 0;
17771453f72SRobert Morris     wakeup(&log);
17871453f72SRobert Morris     release(&log.lock);
17971453f72SRobert Morris   }
18071453f72SRobert Morris }
18171453f72SRobert Morris 
18271453f72SRobert Morris static void
18371453f72SRobert Morris commit()
18413a96baeSFrans Kaashoek {
185843eecfcSAustin Clements   if (log.lh.n > 0) {
186a5fbfe41SRobert Morris     write_head();    // Write header to disk -- the real commit
187a5fbfe41SRobert Morris     install_trans(); // Now install writes to home locations
1882e590463SRobert Morris     log.lh.n = 0;
189a5fbfe41SRobert Morris     write_head();    // Erase the transaction from the log
190843eecfcSAustin Clements   }
19113a96baeSFrans Kaashoek }
19213a96baeSFrans Kaashoek 
1932e590463SRobert Morris // Caller has modified b->data and is done with the buffer.
1942e590463SRobert Morris // Append the block to the log and record the block number,
1952e590463SRobert Morris // but don't write the log header (which would commit the write).
1962e590463SRobert Morris // log_write() replaces bwrite(); a typical use is:
1972e590463SRobert Morris //   bp = bread(...)
1982e590463SRobert Morris //   modify bp->data[]
1992e590463SRobert Morris //   log_write(bp)
2002e590463SRobert Morris //   brelse(bp)
20113a96baeSFrans Kaashoek void
20213a96baeSFrans Kaashoek log_write(struct buf *b)
20313a96baeSFrans Kaashoek {
20413a96baeSFrans Kaashoek   int i;
20513a96baeSFrans Kaashoek 
2062e590463SRobert Morris   if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1)
20713a96baeSFrans Kaashoek     panic("too big a transaction");
20871453f72SRobert Morris   if (log.outstanding < 1)
20913a96baeSFrans Kaashoek     panic("write outside of trans");
21013a96baeSFrans Kaashoek 
2112e590463SRobert Morris   for (i = 0; i < log.lh.n; i++) {
21213a96baeSFrans Kaashoek     if (log.lh.sector[i] == b->sector)   // log absorbtion?
21313a96baeSFrans Kaashoek       break;
21413a96baeSFrans Kaashoek   }
21513a96baeSFrans Kaashoek   log.lh.sector[i] = b->sector;
21613a96baeSFrans Kaashoek   struct buf *lbuf = bread(b->dev, log.start+i+1);
21713a96baeSFrans Kaashoek   memmove(lbuf->data, b->data, BSIZE);
21813a96baeSFrans Kaashoek   bwrite(lbuf);
21913a96baeSFrans Kaashoek   brelse(lbuf);
2202e590463SRobert Morris   if (i == log.lh.n)
2212e590463SRobert Morris     log.lh.n++;
22212abb1a5SRobert Morris   b->flags |= B_DIRTY; // XXX prevent eviction
223*48aa9174SRobert Morris 
224*48aa9174SRobert Morris   if(log.lh.n > maxsize){
225*48aa9174SRobert Morris     maxsize = log.lh.n;
226*48aa9174SRobert Morris     cprintf("log size %d/%d\n", log.lh.n, LOGSIZE);
227*48aa9174SRobert Morris   }
22813a96baeSFrans Kaashoek }
2299bb1e53dSAustin Clements 
2309bb1e53dSAustin Clements //PAGEBREAK!
2319bb1e53dSAustin Clements // Blank page.
2329bb1e53dSAustin Clements 
233