/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- initlog
- install_trans
- read_head
- write_head
- recover_from_log
- begin_op
- end_op
- write_log
- commit
- log_write
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 that allows concurrent FS system calls.
9 //
10 // A log transaction contains the updates of multiple FS system
11 // calls. The logging system only commits when there are
12 // no FS system calls active. Thus there is never
13 // any reasoning required about whether a commit might
14 // write an uncommitted system call's updates to disk.
15 //
16 // A system call should call begin_op()/end_op() to mark
17 // its start and end. Usually begin_op() just increments
18 // the count of in-progress FS system calls and returns.
19 // But if it thinks the log is close to running out, it
20 // sleeps until the last outstanding end_op() commits.
21 //
22 // The log is a physical re-do log containing disk blocks.
23 // The on-disk log format:
24 // header block, containing block #s for block A, B, C, ...
25 // block A
26 // block B
27 // block C
28 // ...
29 // Log appends are synchronous.
30
31 // Contents of the header block, used for both the on-disk header block
32 // and to keep track in memory of logged block# before commit.
33 struct logheader {
34 int n;
35 int block[LOGSIZE];
36 };
37
38 struct log {
39 struct spinlock lock;
40 int start;
41 int size;
42 int outstanding; // how many FS sys calls are executing.
43 int committing; // in commit(), please wait.
44 int dev;
45 struct logheader lh;
46 };
47 struct log log;
48
49 static void recover_from_log(void);
50 static void commit();
51
52 void
53 initlog(int dev)
54 {
55 if (sizeof(struct logheader) >= BSIZE)
56 panic("initlog: too big logheader");
57
58 struct superblock sb;
59 initlock(&log.lock, "log");
60 readsb(dev, &sb);
61 log.start = sb.logstart;
62 log.size = sb.nlog;
63 log.dev = dev;
64 recover_from_log();
65 }
66
67 // Copy committed blocks from log to their home location
68 static void
69 install_trans(void)
70 {
71 int tail;
72
73 for (tail = 0; tail < log.lh.n; tail++) {
74 struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block
75 struct buf *dbuf = bread(log.dev, log.lh.block[tail]); // read dst
76 memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst
77 bwrite(dbuf); // write dst to disk
78 brelse(lbuf);
79 brelse(dbuf);
80 }
81 }
82
83 // Read the log header from disk into the in-memory log header
84 static void
85 read_head(void)
86 {
87 struct buf *buf = bread(log.dev, log.start);
88 struct logheader *lh = (struct logheader *) (buf->data);
89 int i;
90 log.lh.n = lh->n;
91 for (i = 0; i < log.lh.n; i++) {
92 log.lh.block[i] = lh->block[i];
93 }
94 brelse(buf);
95 }
96
97 // Write in-memory log header to disk.
98 // This is the true point at which the
99 // current transaction commits.
100 static void
101 write_head(void)
102 {
103 struct buf *buf = bread(log.dev, log.start);
104 struct logheader *hb = (struct logheader *) (buf->data);
105 int i;
106 hb->n = log.lh.n;
107 for (i = 0; i < log.lh.n; i++) {
108 hb->block[i] = log.lh.block[i];
109 }
110 bwrite(buf);
111 brelse(buf);
112 }
113
114 static void
115 recover_from_log(void)
116 {
117 read_head();
118 install_trans(); // if committed, copy from log to disk
119 log.lh.n = 0;
120 write_head(); // clear the log
121 }
122
123 // called at the start of each FS system call.
124 void
125 begin_op(void)
126 {
127 acquire(&log.lock);
128 while(1){
129 if(log.committing){
130 sleep(&log, &log.lock);
131 } else if(log.lh.n + (log.outstanding+1)*MAXOPBLOCKS > LOGSIZE){
132 // this op might exhaust log space; wait for commit.
133 sleep(&log, &log.lock);
134 } else {
135 log.outstanding += 1;
136 release(&log.lock);
137 break;
138 }
139 }
140 }
141
142 // called at the end of each FS system call.
143 // commits if this was the last outstanding operation.
144 void
145 end_op(void)
146 {
147 int do_commit = 0;
148
149 acquire(&log.lock);
150 log.outstanding -= 1;
151 if(log.committing)
152 panic("log.committing");
153 if(log.outstanding == 0){
154 do_commit = 1;
155 log.committing = 1;
156 } else {
157 // begin_op() may be waiting for log space.
158 wakeup(&log);
159 }
160 release(&log.lock);
161
162 if(do_commit){
163 // call commit w/o holding locks, since not allowed
164 // to sleep with locks.
165 commit();
166 acquire(&log.lock);
167 log.committing = 0;
168 wakeup(&log);
169 release(&log.lock);
170 }
171 }
172
173 // Copy modified blocks from cache to log.
174 static void
175 write_log(void)
176 {
177 int tail;
178
179 for (tail = 0; tail < log.lh.n; tail++) {
180 struct buf *to = bread(log.dev, log.start+tail+1); // log block
181 struct buf *from = bread(log.dev, log.lh.block[tail]); // cache block
182 memmove(to->data, from->data, BSIZE);
183 bwrite(to); // write the log
184 brelse(from);
185 brelse(to);
186 }
187 }
188
189 static void
190 commit()
191 {
192 if (log.lh.n > 0) {
193 write_log(); // Write modified blocks from cache to log
194 write_head(); // Write header to disk -- the real commit
195 install_trans(); // Now install writes to home locations
196 log.lh.n = 0;
197 write_head(); // Erase the transaction from the log
198 }
199 }
200
201 // Caller has modified b->data and is done with the buffer.
202 // Record the block number and pin in the cache with B_DIRTY.
203 // commit()/write_log() will do the disk write.
204 //
205 // log_write() replaces bwrite(); a typical use is:
206 // bp = bread(...)
207 // modify bp->data[]
208 // log_write(bp)
209 // brelse(bp)
210 void
211 log_write(struct buf *b)
212 {
213 int i;
214
215 if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1)
216 panic("too big a transaction");
217 if (log.outstanding < 1)
218 panic("log_write outside of trans");
219
220 acquire(&log.lock);
221 for (i = 0; i < log.lh.n; i++) {
222 if (log.lh.block[i] == b->blockno) // log absorbtion
223 break;
224 }
225 log.lh.block[i] = b->blockno;
226 if (i == log.lh.n)
227 log.lh.n++;
228 b->flags |= B_DIRTY; // prevent eviction
229 release(&log.lock);
230 }
231