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