Xv6 with picoc & Linkage editor  v1.0
The project delineate mutual cohesion between c library, linkage editor ( linker), interpreter and operating system by porting the same on xv6 kernel
log.c
00001 #include "types.h"
00002 #include "defs.h"
00003 #include "param.h"
00004 #include "spinlock.h"
00005 #include "fs.h"
00006 #include "buf.h"
00007 
00008 // Simple logging. Each system call that might write the file system
00009 // should be surrounded with begin_trans() and commit_trans() calls.
00010 //
00011 // The log holds at most one transaction at a time. Commit forces
00012 // the log (with commit record) to disk, then installs the affected
00013 // blocks to disk, then erases the log. begin_trans() ensures that
00014 // only one system call can be in a transaction; others must wait.
00015 // 
00016 // Allowing only one transaction at a time means that the file
00017 // system code doesn't have to worry about the possibility of
00018 // one transaction reading a block that another one has modified,
00019 // for example an i-node block.
00020 //
00021 // Read-only system calls don't need to use transactions, though
00022 // this means that they may observe uncommitted data. I-node and
00023 // buffer locks prevent read-only calls from seeing inconsistent data.
00024 //
00025 // The log is a physical re-do log containing disk blocks.
00026 // The on-disk log format:
00027 //   header block, containing sector #s for block A, B, C, ...
00028 //   block A
00029 //   block B
00030 //   block C
00031 //   ...
00032 // Log appends are synchronous.
00033 
00034 // Contents of the header block, used for both the on-disk header block
00035 // and to keep track in memory of logged sector #s before commit.
00036 struct logheader {
00037   int n;   
00038   int sector[LOGSIZE];
00039 };
00040 
00041 struct log {
00042   struct spinlock lock;
00043   int start;
00044   int size;
00045   int busy; // a transaction is active
00046   int dev;
00047   struct logheader lh;
00048 };
00049 struct log log;
00050 
00051 static void recover_from_log(void);
00052 
00053 void
00054 initlog(void)
00055 {
00056   if (sizeof(struct logheader) >= BSIZE)
00057     panic("initlog: too big logheader");
00058 
00059   struct superblock sb;
00060   initlock(&log.lock, "log");
00061   readsb(ROOTDEV, &sb);
00062   log.start = sb.size - sb.nlog;
00063   log.size = sb.nlog;
00064   log.dev = ROOTDEV;
00065   recover_from_log();
00066 }
00067 
00068 // Copy committed blocks from log to their home location
00069 static void 
00070 install_trans(void)
00071 {
00072   int tail;
00073 
00074   for (tail = 0; tail < log.lh.n; tail++) {
00075     struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block
00076     struct buf *dbuf = bread(log.dev, log.lh.sector[tail]); // read dst
00077     memmove(dbuf->data, lbuf->data, BSIZE);  // copy block to dst
00078     bwrite(dbuf);  // write dst to disk
00079     brelse(lbuf); 
00080     brelse(dbuf);
00081   }
00082 }
00083 
00084 // Read the log header from disk into the in-memory log header
00085 static void
00086 read_head(void)
00087 {
00088   struct buf *buf = bread(log.dev, log.start);
00089   struct logheader *lh = (struct logheader *) (buf->data);
00090   int i;
00091   log.lh.n = lh->n;
00092   for (i = 0; i < log.lh.n; i++) {
00093     log.lh.sector[i] = lh->sector[i];
00094   }
00095   brelse(buf);
00096 }
00097 
00098 // Write in-memory log header to disk.
00099 // This is the true point at which the
00100 // current transaction commits.
00101 static void
00102 write_head(void)
00103 {
00104   struct buf *buf = bread(log.dev, log.start);
00105   struct logheader *hb = (struct logheader *) (buf->data);
00106   int i;
00107   hb->n = log.lh.n;
00108   for (i = 0; i < log.lh.n; i++) {
00109     hb->sector[i] = log.lh.sector[i];
00110   }
00111   bwrite(buf);
00112   brelse(buf);
00113 }
00114 
00115 static void
00116 recover_from_log(void)
00117 {
00118   read_head();      
00119   install_trans(); // if committed, copy from log to disk
00120   log.lh.n = 0;
00121   write_head(); // clear the log
00122 }
00123 
00124 void
00125 begin_trans(void)
00126 {
00127   acquire(&log.lock);
00128   while (log.busy) {
00129     sleep(&log, &log.lock);
00130   }
00131   log.busy = 1;
00132   release(&log.lock);
00133 }
00134 
00135 void
00136 commit_trans(void)
00137 {
00138   if (log.lh.n > 0) {
00139     write_head();    // Write header to disk -- the real commit
00140     install_trans(); // Now install writes to home locations
00141     log.lh.n = 0; 
00142     write_head();    // Erase the transaction from the log
00143   }
00144   
00145   acquire(&log.lock);
00146   log.busy = 0;
00147   wakeup(&log);
00148   release(&log.lock);
00149 }
00150 
00151 // Caller has modified b->data and is done with the buffer.
00152 // Append the block to the log and record the block number, 
00153 // but don't write the log header (which would commit the write).
00154 // log_write() replaces bwrite(); a typical use is:
00155 //   bp = bread(...)
00156 //   modify bp->data[]
00157 //   log_write(bp)
00158 //   brelse(bp)
00159 void
00160 log_write(struct buf *b)
00161 {
00162   int i;
00163 
00164   if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1)
00165     panic("too big a transaction");
00166   if (!log.busy)
00167     panic("write outside of trans");
00168 
00169   for (i = 0; i < log.lh.n; i++) {
00170     if (log.lh.sector[i] == b->sector)   // log absorbtion?
00171       break;
00172   }
00173   log.lh.sector[i] = b->sector;
00174   struct buf *lbuf = bread(b->dev, log.start+i+1);
00175   memmove(lbuf->data, b->data, BSIZE);
00176   bwrite(lbuf);
00177   brelse(lbuf);
00178   if (i == log.lh.n)
00179     log.lh.n++;
00180   b->flags |= B_DIRTY; // XXX prevent eviction
00181 }
00182 
00183 //PAGEBREAK!
00184 // Blank page.
00185 
 All Data Structures