comparison source/log.c @ 0:ed10291ff195

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