log.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. #include "types.h"
  2. #include "defs.h"
  3. #include "param.h"
  4. #include "spinlock.h"
  5. #include "fs.h"
  6. #include "buf.h"
  7. // Simple logging. Each system call that might write the file system
  8. // should be surrounded with begin_trans() and commit_trans() calls.
  9. //
  10. // The log holds at most one transaction at a time. Commit forces
  11. // the log (with commit record) to disk, then installs the affected
  12. // blocks to disk, then erases the log. begin_trans() ensures that
  13. // only one system call can be in a transaction; others must wait.
  14. //
  15. // Allowing only one transaction at a time means that the file
  16. // system code doesn't have to worry about the possibility of
  17. // one transaction reading a block that another one has modified,
  18. // for example an i-node block.
  19. //
  20. // Read-only system calls don't need to use transactions, though
  21. // this means that they may observe uncommitted data. I-node and
  22. // buffer locks prevent read-only calls from seeing inconsistent data.
  23. //
  24. // The log is a physical re-do log containing disk blocks.
  25. // The on-disk log format:
  26. // header block, containing sector #s for block A, B, C, ...
  27. // block A
  28. // block B
  29. // block C
  30. // ...
  31. // Log appends are synchronous.
  32. // Contents of the header block, used for both the on-disk header block
  33. // and to keep track in memory of logged sector #s before commit.
  34. struct logheader {
  35. int n;
  36. int sector[LOGSIZE];
  37. };
  38. struct log {
  39. struct spinlock lock;
  40. int start;
  41. int size;
  42. int busy; // a transaction is active
  43. int dev;
  44. struct logheader lh;
  45. };
  46. struct log log;
  47. static void recover_from_log(void);
  48. void
  49. initlog(void)
  50. {
  51. if (sizeof(struct logheader) >= BSIZE)
  52. panic("initlog: too big logheader");
  53. struct superblock sb;
  54. memset(&log, 0, sizeof(log));
  55. initlock(&log.lock, "log");
  56. readsb(ROOTDEV, &sb);
  57. log.start = sb.size - sb.nlog;
  58. log.size = sb.nlog;
  59. log.dev = ROOTDEV;
  60. recover_from_log();
  61. }
  62. // Copy committed blocks from log to their home location
  63. static void
  64. install_trans(void)
  65. {
  66. int tail;
  67. for (tail = 0; tail < log.lh.n; tail++) {
  68. struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block
  69. struct buf *dbuf = bread(log.dev, log.lh.sector[tail]); // read dst
  70. memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst
  71. bwrite(dbuf); // write dst to disk
  72. brelse(lbuf);
  73. brelse(dbuf);
  74. }
  75. }
  76. // Read the log header from disk into the in-memory log header
  77. static void
  78. read_head(void)
  79. {
  80. struct buf *buf = bread(log.dev, log.start);
  81. struct logheader *lh = (struct logheader *) (buf->data);
  82. int i;
  83. log.lh.n = lh->n;
  84. for (i = 0; i < log.lh.n; i++) {
  85. log.lh.sector[i] = lh->sector[i];
  86. }
  87. brelse(buf);
  88. }
  89. // Write in-memory log header to disk.
  90. // This is the true point at which the
  91. // current transaction commits.
  92. static void
  93. write_head(void)
  94. {
  95. struct buf *buf = bread(log.dev, log.start);
  96. struct logheader *hb = (struct logheader *) (buf->data);
  97. int i;
  98. hb->n = log.lh.n;
  99. for (i = 0; i < log.lh.n; i++) {
  100. hb->sector[i] = log.lh.sector[i];
  101. }
  102. bwrite(buf);
  103. brelse(buf);
  104. }
  105. static void
  106. recover_from_log(void)
  107. {
  108. read_head();
  109. install_trans(); // if committed, copy from log to disk
  110. log.lh.n = 0;
  111. write_head(); // clear the log
  112. }
  113. void
  114. begin_trans(void)
  115. {
  116. acquire(&log.lock);
  117. while (log.busy) {
  118. sleep(&log, &log.lock);
  119. }
  120. log.busy = 1;
  121. release(&log.lock);
  122. }
  123. void
  124. commit_trans(void)
  125. {
  126. if (log.lh.n > 0) {
  127. write_head(); // Write header to disk -- the real commit
  128. install_trans(); // Now install writes to home locations
  129. log.lh.n = 0;
  130. write_head(); // Erase the transaction from the log
  131. }
  132. acquire(&log.lock);
  133. log.busy = 0;
  134. wakeup(&log);
  135. release(&log.lock);
  136. }
  137. // Caller has modified b->data and is done with the buffer.
  138. // Append the block to the log and record the block number,
  139. // but don't write the log header (which would commit the write).
  140. // log_write() replaces bwrite(); a typical use is:
  141. // bp = bread(...)
  142. // modify bp->data[]
  143. // log_write(bp)
  144. // brelse(bp)
  145. void
  146. log_write(struct buf *b)
  147. {
  148. int i;
  149. if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1)
  150. panic("too big a transaction");
  151. if (!log.busy)
  152. panic("write outside of trans");
  153. for (i = 0; i < log.lh.n; i++) {
  154. if (log.lh.sector[i] == b->sector) // log absorbtion?
  155. break;
  156. }
  157. log.lh.sector[i] = b->sector;
  158. struct buf *lbuf = bread(b->dev, log.start+i+1);
  159. memmove(lbuf->data, b->data, BSIZE);
  160. bwrite(lbuf);
  161. brelse(lbuf);
  162. if (i == log.lh.n)
  163. log.lh.n++;
  164. b->flags |= B_DIRTY; // XXX prevent eviction
  165. }
  166. //PAGEBREAK!
  167. // Blank page.