comparison src/log.c @ 0:83c23a36980d

Init
author Tatsuki IHA <e125716@ie.u-ryukyu.ac.jp>
date Fri, 26 May 2017 23:11:05 +0900
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:83c23a36980d
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 initlog(void)
54 {
55 struct superblock sb;
56
57 if (sizeof(struct logheader) >= BSIZE) {
58 panic("initlog: too big logheader");
59 }
60
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 install_trans(void)
71 {
72 int tail;
73 struct buf *lbuf;
74 struct buf *dbuf;
75
76 for (tail = 0; tail < log.lh.n; tail++) {
77 lbuf = bread(log.dev, log.start+tail+1); // read log block
78 dbuf = bread(log.dev, log.lh.sector[tail]); // read dst
79
80 memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst
81
82 bwrite(dbuf); // write dst to disk
83 brelse(lbuf);
84 brelse(dbuf);
85 }
86 }
87
88 // Read the log header from disk into the in-memory log header
89 static void read_head(void)
90 {
91 struct buf *buf;
92 struct logheader *lh;
93 int i;
94
95 buf = bread(log.dev, log.start);
96 lh = (struct logheader *) (buf->data);
97 log.lh.n = lh->n;
98
99 for (i = 0; i < log.lh.n; i++) {
100 log.lh.sector[i] = lh->sector[i];
101 }
102
103 brelse(buf);
104 }
105
106 // Write in-memory log header to disk.
107 // This is the true point at which the
108 // current transaction commits.
109 static void write_head(void)
110 {
111 struct buf *buf;
112 struct logheader *hb;
113 int i;
114
115 buf = bread(log.dev, log.start);
116 hb = (struct logheader *) (buf->data);
117
118 hb->n = log.lh.n;
119
120 for (i = 0; i < log.lh.n; i++) {
121 hb->sector[i] = log.lh.sector[i];
122 }
123
124 bwrite(buf);
125 brelse(buf);
126 }
127
128 static void recover_from_log(void)
129 {
130 read_head();
131 install_trans(); // if committed, copy from log to disk
132 log.lh.n = 0;
133 write_head(); // clear the log
134 }
135
136 void begin_trans(void)
137 {
138 acquire(&log.lock);
139
140 while (log.busy) {
141 sleep(&log, &log.lock);
142 }
143
144 log.busy = 1;
145 release(&log.lock);
146 }
147
148 void commit_trans(void)
149 {
150 if (log.lh.n > 0) {
151 write_head(); // Write header to disk -- the real commit
152 install_trans(); // Now install writes to home locations
153 log.lh.n = 0;
154 write_head(); // Erase the transaction from the log
155 }
156
157 acquire(&log.lock);
158 log.busy = 0;
159 wakeup(&log);
160 release(&log.lock);
161 }
162
163 // Caller has modified b->data and is done with the buffer.
164 // Append the block to the log and record the block number,
165 // but don't write the log header (which would commit the write).
166 // log_write() replaces bwrite(); a typical use is:
167 // bp = bread(...)
168 // modify bp->data[]
169 // log_write(bp)
170 // brelse(bp)
171 void log_write(struct buf *b)
172 {
173 struct buf *lbuf;
174 int i;
175
176 if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1) {
177 panic("too big a transaction");
178 }
179
180 if (!log.busy) {
181 panic("write outside of trans");
182 }
183
184 for (i = 0; i < log.lh.n; i++) {
185 if (log.lh.sector[i] == b->sector) { // log absorbtion?
186 break;
187 }
188 }
189
190 log.lh.sector[i] = b->sector;
191 lbuf = bread(b->dev, log.start+i+1);
192
193 memmove(lbuf->data, b->data, BSIZE);
194 bwrite(lbuf);
195 brelse(lbuf);
196
197 if (i == log.lh.n) {
198 log.lh.n++;
199 }
200
201 b->flags |= B_DIRTY; // XXX prevent eviction
202 }
203
204 //PAGEBREAK!
205 // Blank page.
206