xref: /xv6-public/log.c (revision 843eecfc)
113a96baeSFrans Kaashoek #include "types.h"
213a96baeSFrans Kaashoek #include "defs.h"
313a96baeSFrans Kaashoek #include "param.h"
413a96baeSFrans Kaashoek #include "mmu.h"
513a96baeSFrans Kaashoek #include "proc.h"
613a96baeSFrans Kaashoek #include "x86.h"
713a96baeSFrans Kaashoek #include "spinlock.h"
813a96baeSFrans Kaashoek #include "fs.h"
913a96baeSFrans Kaashoek #include "buf.h"
1013a96baeSFrans Kaashoek 
112e590463SRobert Morris // Simple logging. Each system call that might write the file system
122e590463SRobert Morris // should be surrounded with begin_trans() and commit_trans() calls.
132e590463SRobert Morris //
142e590463SRobert Morris // The log holds at most one transaction at a time. Commit forces
152e590463SRobert Morris // the log (with commit record) to disk, then installs the affected
162e590463SRobert Morris // blocks to disk, then erases the log. begin_trans() ensures that
172e590463SRobert Morris // only one system call can be in a transaction; others must wait.
182e590463SRobert Morris //
192e590463SRobert Morris // Allowing only one transaction at a time means that the file
202e590463SRobert Morris // system code doesn't have to worry about the possibility of
212e590463SRobert Morris // one transaction reading a block that another one has modified,
222e590463SRobert Morris // for example an i-node block.
232e590463SRobert Morris //
242e590463SRobert Morris // Read-only system calls don't need to use transactions, though
252e590463SRobert Morris // this means that they may observe uncommitted data. I-node
262e590463SRobert Morris // and buffer locks prevent read-only calls from seeing inconsistent data.
272e590463SRobert Morris //
282e590463SRobert Morris // The log is a physical re-do log containing disk blocks.
292e590463SRobert Morris // The on-disk log format:
302e590463SRobert Morris //   header block, containing sector #s for block A, B, C, ...
312e590463SRobert Morris //   block A
322e590463SRobert Morris //   block B
332e590463SRobert Morris //   block C
342e590463SRobert Morris //   ...
352e590463SRobert Morris // Log appends are synchronous.
3613a96baeSFrans Kaashoek 
372e590463SRobert Morris // Contents of the header block, used for both the on-disk header block
382e590463SRobert Morris // and to keep track in memory of logged sector #s before commit.
3913a96baeSFrans Kaashoek struct logheader {
402e590463SRobert Morris   int n;
4113a96baeSFrans Kaashoek   int sector[LOGSIZE];
4213a96baeSFrans Kaashoek };
4313a96baeSFrans Kaashoek 
4413a96baeSFrans Kaashoek struct {
4513a96baeSFrans Kaashoek   struct spinlock lock;
4613a96baeSFrans Kaashoek   int start;
4713a96baeSFrans Kaashoek   int size;
4813a96baeSFrans Kaashoek   int intrans;
4913a96baeSFrans Kaashoek   int dev;
5013a96baeSFrans Kaashoek   struct logheader lh;
5113a96baeSFrans Kaashoek } log;
5213a96baeSFrans Kaashoek 
5313a96baeSFrans Kaashoek static void recover_from_log(void);
5413a96baeSFrans Kaashoek 
5513a96baeSFrans Kaashoek void
5613a96baeSFrans Kaashoek initlog(void)
5713a96baeSFrans Kaashoek {
5813a96baeSFrans Kaashoek   if (sizeof(struct logheader) >= BSIZE)
5913a96baeSFrans Kaashoek     panic("initlog: too big logheader");
6013a96baeSFrans Kaashoek 
6113a96baeSFrans Kaashoek   struct superblock sb;
6213a96baeSFrans Kaashoek   initlock(&log.lock, "log");
6313a96baeSFrans Kaashoek   readsb(ROOTDEV, &sb);
6413a96baeSFrans Kaashoek   log.start = sb.size - sb.nlog;
6513a96baeSFrans Kaashoek   log.size = sb.nlog;
6613a96baeSFrans Kaashoek   log.dev = ROOTDEV;
6713a96baeSFrans Kaashoek   recover_from_log();
6813a96baeSFrans Kaashoek }
6913a96baeSFrans Kaashoek 
7013a96baeSFrans Kaashoek // Copy committed blocks from log to their home location
7113a96baeSFrans Kaashoek static void
7213a96baeSFrans Kaashoek install_trans(void)
7313a96baeSFrans Kaashoek {
7413a96baeSFrans Kaashoek   int tail;
7513a96baeSFrans Kaashoek 
762e590463SRobert Morris   //if (log.lh.n > 0)
772e590463SRobert Morris   //  cprintf("install_trans %d\n", log.lh.n);
782e590463SRobert Morris   for (tail = 0; tail < log.lh.n; tail++) {
792e590463SRobert Morris     // cprintf("put entry %d to disk block %d\n", tail, log.lh.sector[tail]);
8013a96baeSFrans Kaashoek     struct buf *lbuf = bread(log.dev, log.start+tail+1);   // read i'th block from log
8113a96baeSFrans Kaashoek     struct buf *dbuf = bread(log.dev, log.lh.sector[tail]);  // read dst block
8213a96baeSFrans Kaashoek     memmove(dbuf->data, lbuf->data, BSIZE);
8313a96baeSFrans Kaashoek     bwrite(dbuf);
8413a96baeSFrans Kaashoek     brelse(lbuf);
8513a96baeSFrans Kaashoek     brelse(dbuf);
8613a96baeSFrans Kaashoek   }
8713a96baeSFrans Kaashoek }
8813a96baeSFrans Kaashoek 
8913a96baeSFrans Kaashoek // Read the log header from disk into the in-memory log header
9013a96baeSFrans Kaashoek static void
9113a96baeSFrans Kaashoek read_head(void)
9213a96baeSFrans Kaashoek {
9313a96baeSFrans Kaashoek   struct buf *buf = bread(log.dev, log.start);
9413a96baeSFrans Kaashoek   struct logheader *lh = (struct logheader *) (buf->data);
9513a96baeSFrans Kaashoek   int i;
962e590463SRobert Morris   log.lh.n = lh->n;
972e590463SRobert Morris   for (i = 0; i < log.lh.n; i++) {
9813a96baeSFrans Kaashoek     log.lh.sector[i] = lh->sector[i];
9913a96baeSFrans Kaashoek   }
10013a96baeSFrans Kaashoek   brelse(buf);
1012e590463SRobert Morris   //if (log.lh.n > 0)
1022e590463SRobert Morris   //  cprintf("read_head: %d\n", log.lh.n);
10313a96baeSFrans Kaashoek }
10413a96baeSFrans Kaashoek 
10513a96baeSFrans Kaashoek // Write the in-memory log header to disk, committing log entries till head
10613a96baeSFrans Kaashoek static void
10713a96baeSFrans Kaashoek write_head(void)
10813a96baeSFrans Kaashoek {
1092e590463SRobert Morris   // if (log.lh.n > 0)
1102e590463SRobert Morris   //   cprintf("write_head: %d\n", log.lh.n);
11113a96baeSFrans Kaashoek 
11213a96baeSFrans Kaashoek   struct buf *buf = bread(log.dev, log.start);
11313a96baeSFrans Kaashoek   struct logheader *hb = (struct logheader *) (buf->data);
11413a96baeSFrans Kaashoek   int i;
1152e590463SRobert Morris   hb->n = log.lh.n;
1162e590463SRobert Morris   for (i = 0; i < log.lh.n; i++) {
11713a96baeSFrans Kaashoek     hb->sector[i] = log.lh.sector[i];
11813a96baeSFrans Kaashoek   }
11913a96baeSFrans Kaashoek   bwrite(buf);
12013a96baeSFrans Kaashoek   brelse(buf);
12113a96baeSFrans Kaashoek }
12213a96baeSFrans Kaashoek 
12313a96baeSFrans Kaashoek static void
12413a96baeSFrans Kaashoek recover_from_log(void)
12513a96baeSFrans Kaashoek {
12613a96baeSFrans Kaashoek   read_head();
1275053dd6aSRobert Morris   install_trans(); // if committed, copy from log to disk
1282e590463SRobert Morris   log.lh.n = 0;
1295053dd6aSRobert Morris   write_head(); // clear the log
13013a96baeSFrans Kaashoek }
13113a96baeSFrans Kaashoek 
13213a96baeSFrans Kaashoek void
13313a96baeSFrans Kaashoek begin_trans(void)
13413a96baeSFrans Kaashoek {
13513a96baeSFrans Kaashoek   acquire(&log.lock);
13613a96baeSFrans Kaashoek   while (log.intrans) {
13713a96baeSFrans Kaashoek     sleep(&log, &log.lock);
13813a96baeSFrans Kaashoek   }
13913a96baeSFrans Kaashoek   log.intrans = 1;
14013a96baeSFrans Kaashoek   release(&log.lock);
14113a96baeSFrans Kaashoek }
14213a96baeSFrans Kaashoek 
14313a96baeSFrans Kaashoek void
14413a96baeSFrans Kaashoek commit_trans(void)
14513a96baeSFrans Kaashoek {
146*843eecfcSAustin Clements   if (log.lh.n > 0) {
14713a96baeSFrans Kaashoek     write_head();        // This causes all blocks till log.head to be commited
14813a96baeSFrans Kaashoek     install_trans();     // Install all the transactions till head
1492e590463SRobert Morris     log.lh.n = 0;
15013a96baeSFrans Kaashoek     write_head();        // Reclaim log
151*843eecfcSAustin Clements   }
15213a96baeSFrans Kaashoek 
15313a96baeSFrans Kaashoek   acquire(&log.lock);
15413a96baeSFrans Kaashoek   log.intrans = 0;
15513a96baeSFrans Kaashoek   wakeup(&log);
15613a96baeSFrans Kaashoek   release(&log.lock);
15713a96baeSFrans Kaashoek }
15813a96baeSFrans Kaashoek 
1592e590463SRobert Morris // Caller has modified b->data and is done with the buffer.
1602e590463SRobert Morris // Append the block to the log and record the block number,
1612e590463SRobert Morris // but don't write the log header (which would commit the write).
1622e590463SRobert Morris // log_write() replaces bwrite(); a typical use is:
1632e590463SRobert Morris //   bp = bread(...)
1642e590463SRobert Morris //   modify bp->data[]
1652e590463SRobert Morris //   log_write(bp)
1662e590463SRobert Morris //   brelse(bp)
16713a96baeSFrans Kaashoek void
16813a96baeSFrans Kaashoek log_write(struct buf *b)
16913a96baeSFrans Kaashoek {
17013a96baeSFrans Kaashoek   int i;
17113a96baeSFrans Kaashoek 
1722e590463SRobert Morris   if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1)
17313a96baeSFrans Kaashoek     panic("too big a transaction");
17413a96baeSFrans Kaashoek   if (!log.intrans)
17513a96baeSFrans Kaashoek     panic("write outside of trans");
17613a96baeSFrans Kaashoek 
1772e590463SRobert Morris   // cprintf("log_write: %d %d\n", b->sector, log.lh.n);
17813a96baeSFrans Kaashoek 
1792e590463SRobert Morris   for (i = 0; i < log.lh.n; i++) {
18013a96baeSFrans Kaashoek     if (log.lh.sector[i] == b->sector)   // log absorbtion?
18113a96baeSFrans Kaashoek       break;
18213a96baeSFrans Kaashoek   }
18313a96baeSFrans Kaashoek   log.lh.sector[i] = b->sector;
18413a96baeSFrans Kaashoek   struct buf *lbuf = bread(b->dev, log.start+i+1);
18513a96baeSFrans Kaashoek   memmove(lbuf->data, b->data, BSIZE);
18613a96baeSFrans Kaashoek   bwrite(lbuf);
18713a96baeSFrans Kaashoek   brelse(lbuf);
1882e590463SRobert Morris   if (i == log.lh.n)
1892e590463SRobert Morris     log.lh.n++;
19013a96baeSFrans Kaashoek }
191