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 11*2e590463SRobert Morris // Simple logging. Each system call that might write the file system 12*2e590463SRobert Morris // should be surrounded with begin_trans() and commit_trans() calls. 13*2e590463SRobert Morris // 14*2e590463SRobert Morris // The log holds at most one transaction at a time. Commit forces 15*2e590463SRobert Morris // the log (with commit record) to disk, then installs the affected 16*2e590463SRobert Morris // blocks to disk, then erases the log. begin_trans() ensures that 17*2e590463SRobert Morris // only one system call can be in a transaction; others must wait. 18*2e590463SRobert Morris // 19*2e590463SRobert Morris // Allowing only one transaction at a time means that the file 20*2e590463SRobert Morris // system code doesn't have to worry about the possibility of 21*2e590463SRobert Morris // one transaction reading a block that another one has modified, 22*2e590463SRobert Morris // for example an i-node block. 23*2e590463SRobert Morris // 24*2e590463SRobert Morris // Read-only system calls don't need to use transactions, though 25*2e590463SRobert Morris // this means that they may observe uncommitted data. I-node 26*2e590463SRobert Morris // and buffer locks prevent read-only calls from seeing inconsistent data. 27*2e590463SRobert Morris // 28*2e590463SRobert Morris // The log is a physical re-do log containing disk blocks. 29*2e590463SRobert Morris // The on-disk log format: 30*2e590463SRobert Morris // header block, containing sector #s for block A, B, C, ... 31*2e590463SRobert Morris // block A 32*2e590463SRobert Morris // block B 33*2e590463SRobert Morris // block C 34*2e590463SRobert Morris // ... 35*2e590463SRobert Morris // Log appends are synchronous. 3613a96baeSFrans Kaashoek 37*2e590463SRobert Morris // Contents of the header block, used for both the on-disk header block 38*2e590463SRobert Morris // and to keep track in memory of logged sector #s before commit. 3913a96baeSFrans Kaashoek struct logheader { 40*2e590463SRobert 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 76*2e590463SRobert Morris //if (log.lh.n > 0) 77*2e590463SRobert Morris // cprintf("install_trans %d\n", log.lh.n); 78*2e590463SRobert Morris for (tail = 0; tail < log.lh.n; tail++) { 79*2e590463SRobert 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; 96*2e590463SRobert Morris log.lh.n = lh->n; 97*2e590463SRobert Morris for (i = 0; i < log.lh.n; i++) { 9813a96baeSFrans Kaashoek log.lh.sector[i] = lh->sector[i]; 9913a96baeSFrans Kaashoek } 10013a96baeSFrans Kaashoek brelse(buf); 101*2e590463SRobert Morris //if (log.lh.n > 0) 102*2e590463SRobert 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 { 109*2e590463SRobert Morris // if (log.lh.n > 0) 110*2e590463SRobert 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; 115*2e590463SRobert Morris hb->n = log.lh.n; 116*2e590463SRobert 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(); 12713a96baeSFrans Kaashoek install_trans(); // Install all transactions till head 128*2e590463SRobert Morris log.lh.n = 0; 12913a96baeSFrans Kaashoek write_head(); // Reclaim 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 { 14613a96baeSFrans Kaashoek write_head(); // This causes all blocks till log.head to be commited 14713a96baeSFrans Kaashoek install_trans(); // Install all the transactions till head 148*2e590463SRobert Morris log.lh.n = 0; 14913a96baeSFrans Kaashoek write_head(); // Reclaim log 15013a96baeSFrans Kaashoek 15113a96baeSFrans Kaashoek acquire(&log.lock); 15213a96baeSFrans Kaashoek log.intrans = 0; 15313a96baeSFrans Kaashoek wakeup(&log); 15413a96baeSFrans Kaashoek release(&log.lock); 15513a96baeSFrans Kaashoek } 15613a96baeSFrans Kaashoek 157*2e590463SRobert Morris // Caller has modified b->data and is done with the buffer. 158*2e590463SRobert Morris // Append the block to the log and record the block number, 159*2e590463SRobert Morris // but don't write the log header (which would commit the write). 160*2e590463SRobert Morris // log_write() replaces bwrite(); a typical use is: 161*2e590463SRobert Morris // bp = bread(...) 162*2e590463SRobert Morris // modify bp->data[] 163*2e590463SRobert Morris // log_write(bp) 164*2e590463SRobert Morris // brelse(bp) 16513a96baeSFrans Kaashoek void 16613a96baeSFrans Kaashoek log_write(struct buf *b) 16713a96baeSFrans Kaashoek { 16813a96baeSFrans Kaashoek int i; 16913a96baeSFrans Kaashoek 170*2e590463SRobert Morris if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1) 17113a96baeSFrans Kaashoek panic("too big a transaction"); 17213a96baeSFrans Kaashoek if (!log.intrans) 17313a96baeSFrans Kaashoek panic("write outside of trans"); 17413a96baeSFrans Kaashoek 175*2e590463SRobert Morris // cprintf("log_write: %d %d\n", b->sector, log.lh.n); 17613a96baeSFrans Kaashoek 177*2e590463SRobert Morris for (i = 0; i < log.lh.n; i++) { 17813a96baeSFrans Kaashoek if (log.lh.sector[i] == b->sector) // log absorbtion? 17913a96baeSFrans Kaashoek break; 18013a96baeSFrans Kaashoek } 18113a96baeSFrans Kaashoek log.lh.sector[i] = b->sector; 18213a96baeSFrans Kaashoek struct buf *lbuf = bread(b->dev, log.start+i+1); 18313a96baeSFrans Kaashoek memmove(lbuf->data, b->data, BSIZE); 18413a96baeSFrans Kaashoek bwrite(lbuf); 18513a96baeSFrans Kaashoek brelse(lbuf); 186*2e590463SRobert Morris if (i == log.lh.n) 187*2e590463SRobert Morris log.lh.n++; 18813a96baeSFrans Kaashoek } 189