scoutos / prex-0.9.0 / usr / server / fs / vfs / vfs_bio.c @ 03e9c04a
History | View | Annotate | Download (7.81 KB)
1 |
/*
|
---|---|
2 |
* Copyright (c) 2005-2007, Kohsuke Ohtani
|
3 |
* All rights reserved.
|
4 |
*
|
5 |
* Redistribution and use in source and binary forms, with or without
|
6 |
* modification, are permitted provided that the following conditions
|
7 |
* are met:
|
8 |
* 1. Redistributions of source code must retain the above copyright
|
9 |
* notice, this list of conditions and the following disclaimer.
|
10 |
* 2. Redistributions in binary form must reproduce the above copyright
|
11 |
* notice, this list of conditions and the following disclaimer in the
|
12 |
* documentation and/or other materials provided with the distribution.
|
13 |
* 3. Neither the name of the author nor the names of any co-contributors
|
14 |
* may be used to endorse or promote products derived from this software
|
15 |
* without specific prior written permission.
|
16 |
*
|
17 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
18 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
21 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
23 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
24 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
26 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
27 |
* SUCH DAMAGE.
|
28 |
*/
|
29 |
|
30 |
/*
|
31 |
* vfs_bio.c - buffered I/O operations
|
32 |
*/
|
33 |
|
34 |
/*
|
35 |
* References:
|
36 |
* Bach: The Design of the UNIX Operating System (Prentice Hall, 1986)
|
37 |
*/
|
38 |
|
39 |
#include <sys/prex.h> |
40 |
#include <sys/list.h> |
41 |
#include <sys/param.h> |
42 |
#include <sys/buf.h> |
43 |
|
44 |
#include <limits.h> |
45 |
#include <unistd.h> |
46 |
#include <stdlib.h> |
47 |
#include <string.h> |
48 |
|
49 |
#include "vfs.h" |
50 |
|
51 |
/* number of buffer cache */
|
52 |
#define NBUFS CONFIG_BUF_CACHE
|
53 |
|
54 |
/* macros to clear/set/test flags. */
|
55 |
#define SET(t, f) (t) |= (f)
|
56 |
#define CLR(t, f) (t) &= ~(f)
|
57 |
#define ISSET(t, f) ((t) & (f))
|
58 |
|
59 |
/*
|
60 |
* Global lock to access all buffer headers and lists.
|
61 |
*/
|
62 |
#if CONFIG_FS_THREADS > 1 |
63 |
static mutex_t bio_lock = MUTEX_INITIALIZER;
|
64 |
#define BIO_LOCK() mutex_lock(&bio_lock)
|
65 |
#define BIO_UNLOCK() mutex_unlock(&bio_lock)
|
66 |
#else
|
67 |
#define BIO_LOCK()
|
68 |
#define BIO_UNLOCK()
|
69 |
#endif
|
70 |
|
71 |
|
72 |
/* set of buffers */
|
73 |
static char buffers[NBUFS][BSIZE]; |
74 |
|
75 |
static struct buf buf_table[NBUFS]; |
76 |
static struct list free_list = LIST_INIT(free_list); |
77 |
|
78 |
static sem_t free_sem;
|
79 |
|
80 |
|
81 |
/*
|
82 |
* Insert buffer to the head of free list
|
83 |
*/
|
84 |
static void |
85 |
bio_insert_head(struct buf *bp)
|
86 |
{ |
87 |
|
88 |
list_insert(&free_list, &bp->b_link); |
89 |
sem_post(&free_sem); |
90 |
} |
91 |
|
92 |
/*
|
93 |
* Insert buffer to the tail of free list
|
94 |
*/
|
95 |
static void |
96 |
bio_insert_tail(struct buf *bp)
|
97 |
{ |
98 |
|
99 |
list_insert(list_prev(&free_list), &bp->b_link); |
100 |
sem_post(&free_sem); |
101 |
} |
102 |
|
103 |
/*
|
104 |
* Remove buffer from free list
|
105 |
*/
|
106 |
static void |
107 |
bio_remove(struct buf *bp)
|
108 |
{ |
109 |
|
110 |
sem_wait(&free_sem, 0);
|
111 |
ASSERT(!list_empty(&free_list)); |
112 |
list_remove(&bp->b_link); |
113 |
} |
114 |
|
115 |
/*
|
116 |
* Remove buffer from the head of free list
|
117 |
*/
|
118 |
static struct buf * |
119 |
bio_remove_head(void)
|
120 |
{ |
121 |
struct buf *bp;
|
122 |
|
123 |
sem_wait(&free_sem, 0);
|
124 |
ASSERT(!list_empty(&free_list)); |
125 |
bp = list_entry(list_first(&free_list), struct buf, b_link);
|
126 |
list_remove(&bp->b_link); |
127 |
return bp;
|
128 |
} |
129 |
|
130 |
/*
|
131 |
* Determine if a block is in the cache.
|
132 |
*/
|
133 |
static struct buf * |
134 |
incore(dev_t dev, int blkno)
|
135 |
{ |
136 |
struct buf *bp;
|
137 |
int i;
|
138 |
|
139 |
for (i = 0; i < NBUFS; i++) { |
140 |
bp = &buf_table[i]; |
141 |
if (bp->b_blkno == blkno && bp->b_dev == dev &&
|
142 |
!ISSET(bp->b_flags, B_INVAL)) |
143 |
return bp;
|
144 |
} |
145 |
return NULL; |
146 |
} |
147 |
|
148 |
/*
|
149 |
* Assign a buffer for the given block.
|
150 |
*
|
151 |
* The block is selected from the buffer list with LRU
|
152 |
* algorithm. If the appropriate block already exists in the
|
153 |
* block list, return it. Otherwise, the least recently used
|
154 |
* block is used.
|
155 |
*/
|
156 |
struct buf *
|
157 |
getblk(dev_t dev, int blkno)
|
158 |
{ |
159 |
struct buf *bp;
|
160 |
|
161 |
DPRINTF(VFSDB_BIO, ("getblk: dev=%x blkno=%d\n", dev, blkno));
|
162 |
start:
|
163 |
BIO_LOCK(); |
164 |
bp = incore(dev, blkno); |
165 |
if (bp != NULL) { |
166 |
/* Block found in cache. */
|
167 |
if (ISSET(bp->b_flags, B_BUSY)) {
|
168 |
/*
|
169 |
* Wait buffer ready.
|
170 |
*/
|
171 |
BIO_UNLOCK(); |
172 |
mutex_lock(&bp->b_lock); |
173 |
mutex_unlock(&bp->b_lock); |
174 |
/* Scan again if it's busy */
|
175 |
goto start;
|
176 |
} |
177 |
bio_remove(bp); |
178 |
SET(bp->b_flags, B_BUSY); |
179 |
} else {
|
180 |
bp = bio_remove_head(); |
181 |
if (ISSET(bp->b_flags, B_DELWRI)) {
|
182 |
BIO_UNLOCK(); |
183 |
bwrite(bp); |
184 |
goto start;
|
185 |
} |
186 |
bp->b_flags = B_BUSY; |
187 |
bp->b_dev = dev; |
188 |
bp->b_blkno = blkno; |
189 |
} |
190 |
mutex_lock(&bp->b_lock); |
191 |
BIO_UNLOCK(); |
192 |
DPRINTF(VFSDB_BIO, ("getblk: done bp=%x\n", bp));
|
193 |
return bp;
|
194 |
} |
195 |
|
196 |
/*
|
197 |
* Release a buffer, with no I/O implied.
|
198 |
*/
|
199 |
void
|
200 |
brelse(struct buf *bp)
|
201 |
{ |
202 |
ASSERT(ISSET(bp->b_flags, B_BUSY)); |
203 |
DPRINTF(VFSDB_BIO, ("brelse: bp=%x dev=%x blkno=%d\n",
|
204 |
bp, bp->b_dev, bp->b_blkno)); |
205 |
|
206 |
BIO_LOCK(); |
207 |
CLR(bp->b_flags, B_BUSY); |
208 |
mutex_unlock(&bp->b_lock); |
209 |
if (ISSET(bp->b_flags, B_INVAL))
|
210 |
bio_insert_head(bp); |
211 |
else
|
212 |
bio_insert_tail(bp); |
213 |
BIO_UNLOCK(); |
214 |
} |
215 |
|
216 |
/*
|
217 |
* Block read with cache.
|
218 |
* @dev: device id to read from.
|
219 |
* @blkno: block number.
|
220 |
* @buf: buffer pointer to be returned.
|
221 |
*
|
222 |
* An actual read operation is done only when the cached
|
223 |
* buffer is dirty.
|
224 |
*/
|
225 |
int
|
226 |
bread(dev_t dev, int blkno, struct buf **bpp) |
227 |
{ |
228 |
struct buf *bp;
|
229 |
size_t size; |
230 |
int error;
|
231 |
|
232 |
DPRINTF(VFSDB_BIO, ("bread: dev=%x blkno=%d\n", dev, blkno));
|
233 |
bp = getblk(dev, blkno); |
234 |
|
235 |
if (!ISSET(bp->b_flags, (B_DONE | B_DELWRI))) {
|
236 |
size = BSIZE; |
237 |
error = device_read((device_t)dev, bp->b_data, &size, blkno); |
238 |
if (error) {
|
239 |
DPRINTF(VFSDB_BIO, ("bread: i/o error\n"));
|
240 |
brelse(bp); |
241 |
return error;
|
242 |
} |
243 |
} |
244 |
CLR(bp->b_flags, B_INVAL); |
245 |
SET(bp->b_flags, (B_READ | B_DONE)); |
246 |
DPRINTF(VFSDB_BIO, ("bread: done bp=%x\n\n", bp));
|
247 |
*bpp = bp; |
248 |
return 0; |
249 |
} |
250 |
|
251 |
/*
|
252 |
* Block write with cache.
|
253 |
* @buf: buffer to write.
|
254 |
*
|
255 |
* The data is copied to the buffer.
|
256 |
* Then release the buffer.
|
257 |
*/
|
258 |
int
|
259 |
bwrite(struct buf *bp)
|
260 |
{ |
261 |
size_t size; |
262 |
int error;
|
263 |
|
264 |
ASSERT(ISSET(bp->b_flags, B_BUSY)); |
265 |
DPRINTF(VFSDB_BIO, ("bwrite: dev=%x blkno=%d\n", bp->b_dev,
|
266 |
bp->b_blkno)); |
267 |
|
268 |
BIO_LOCK(); |
269 |
CLR(bp->b_flags, (B_READ | B_DONE | B_DELWRI)); |
270 |
BIO_UNLOCK(); |
271 |
|
272 |
size = BSIZE; |
273 |
error = device_write((device_t)bp->b_dev, bp->b_data, &size, |
274 |
bp->b_blkno); |
275 |
if (error)
|
276 |
return error;
|
277 |
BIO_LOCK(); |
278 |
SET(bp->b_flags, B_DONE); |
279 |
BIO_UNLOCK(); |
280 |
brelse(bp); |
281 |
return 0; |
282 |
} |
283 |
|
284 |
/*
|
285 |
* Delayed write.
|
286 |
*
|
287 |
* The buffer is marked dirty, but an actual I/O is not
|
288 |
* performed. This routine should be used when the buffer
|
289 |
* is expected to be modified again soon.
|
290 |
*/
|
291 |
void
|
292 |
bdwrite(struct buf *bp)
|
293 |
{ |
294 |
|
295 |
BIO_LOCK(); |
296 |
SET(bp->b_flags, B_DELWRI); |
297 |
CLR(bp->b_flags, B_DONE); |
298 |
BIO_UNLOCK(); |
299 |
brelse(bp); |
300 |
} |
301 |
|
302 |
/*
|
303 |
* Flush write-behind block
|
304 |
*/
|
305 |
void
|
306 |
bflush(struct buf *bp)
|
307 |
{ |
308 |
|
309 |
BIO_LOCK(); |
310 |
if (ISSET(bp->b_flags, B_DELWRI))
|
311 |
bwrite(bp); |
312 |
BIO_UNLOCK(); |
313 |
} |
314 |
|
315 |
/*
|
316 |
* Invalidate buffer for specified device.
|
317 |
* This is called when unmount.
|
318 |
*/
|
319 |
void
|
320 |
binval(dev_t dev) |
321 |
{ |
322 |
struct buf *bp;
|
323 |
int i;
|
324 |
|
325 |
BIO_LOCK(); |
326 |
for (i = 0; i < NBUFS; i++) { |
327 |
bp = &buf_table[i]; |
328 |
if (bp->b_dev == dev) {
|
329 |
if (ISSET(bp->b_flags, B_DELWRI))
|
330 |
bwrite(bp); |
331 |
else if (ISSET(bp->b_flags, B_BUSY)) |
332 |
brelse(bp); |
333 |
bp->b_flags = B_INVAL; |
334 |
} |
335 |
} |
336 |
BIO_UNLOCK(); |
337 |
} |
338 |
|
339 |
/*
|
340 |
* Invalidate all buffers.
|
341 |
* This is called when unmount.
|
342 |
*/
|
343 |
void
|
344 |
bio_sync(void)
|
345 |
{ |
346 |
struct buf *bp;
|
347 |
int i;
|
348 |
|
349 |
start:
|
350 |
BIO_LOCK(); |
351 |
for (i = 0; i < NBUFS; i++) { |
352 |
bp = &buf_table[i]; |
353 |
if (ISSET(bp->b_flags, B_BUSY)) {
|
354 |
BIO_UNLOCK(); |
355 |
mutex_lock(&bp->b_lock); |
356 |
mutex_unlock(&bp->b_lock); |
357 |
goto start;
|
358 |
} |
359 |
if (ISSET(bp->b_flags, B_DELWRI))
|
360 |
bwrite(bp); |
361 |
} |
362 |
BIO_UNLOCK(); |
363 |
} |
364 |
|
365 |
/*
|
366 |
* Initialize the buffer I/O system.
|
367 |
*/
|
368 |
void
|
369 |
bio_init(void)
|
370 |
{ |
371 |
struct buf *bp;
|
372 |
int i;
|
373 |
|
374 |
for (i = 0; i < NBUFS; i++) { |
375 |
bp = &buf_table[i]; |
376 |
bp->b_flags = B_INVAL; |
377 |
bp->b_data = buffers[i]; |
378 |
mutex_init(&bp->b_lock); |
379 |
list_insert(&free_list, &bp->b_link); |
380 |
} |
381 |
sem_init(&free_sem, NBUFS); |
382 |
|
383 |
DPRINTF(VFSDB_BIO, ("bio: Buffer cache size %dK bytes\n",
|
384 |
BSIZE * NBUFS / 1024));
|
385 |
} |