Mercurial > hg > Members > mitsuki > xv6_rpi2_port
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 |