Annotation of prex/usr/server/fs/vfs/vfs_bio.c, Revision 1.1.1.1
1.1 nbrk 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: * 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 <prex/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: BIO_UNLOCK();
169: mutex_lock(&bp->b_lock);
170: mutex_unlock(&bp->b_lock);
171: /* Scan again if it's busy */
172: goto start;
173: }
174: bio_remove(bp);
175: SET(bp->b_flags, B_BUSY);
176: } else {
177: bp = bio_remove_head();
178: if (ISSET(bp->b_flags, B_DELWRI)) {
179: BIO_UNLOCK();
180: bwrite(bp);
181: goto start;
182: }
183: bp->b_flags = B_BUSY;
184: bp->b_dev = dev;
185: bp->b_blkno = blkno;
186: }
187: mutex_lock(&bp->b_lock);
188: BIO_UNLOCK();
189: DPRINTF(VFSDB_BIO, ("getblk: done bp=%x\n", bp));
190: return bp;
191: }
192:
193: /*
194: * Release a buffer, with no I/O implied.
195: */
196: void
197: brelse(struct buf *bp)
198: {
199: ASSERT(ISSET(bp->b_flags, B_BUSY));
200: DPRINTF(VFSDB_BIO, ("brelse: bp=%x dev=%x blkno=%d\n",
201: bp, bp->b_dev, bp->b_blkno));
202:
203: BIO_LOCK();
204: CLR(bp->b_flags, B_BUSY);
205: mutex_unlock(&bp->b_lock);
206: if (ISSET(bp->b_flags, B_INVAL))
207: bio_insert_head(bp);
208: else
209: bio_insert_tail(bp);
210: BIO_UNLOCK();
211: }
212:
213: /*
214: * Block read with cache.
215: * @dev: device id to read from.
216: * @blkno: block number.
217: * @buf: buffer pointer to be returned.
218: *
219: * An actual read operation is done only when the cached
220: * buffer is dirty.
221: */
222: int
223: bread(dev_t dev, int blkno, struct buf **bpp)
224: {
225: struct buf *bp;
226: size_t size;
227: int err;
228:
229: DPRINTF(VFSDB_BIO, ("bread: dev=%x blkno=%d\n", dev, blkno));
230: bp = getblk(dev, blkno);
231:
232: if (!ISSET(bp->b_flags, (B_DONE | B_DELWRI))) {
233: size = BSIZE;
234: err = device_read((device_t)dev, bp->b_data, &size, blkno);
235: if (err) {
236: DPRINTF(VFSDB_BIO, ("bread: i/o error\n"));
237: brelse(bp);
238: return err;
239: }
240: }
241: CLR(bp->b_flags, B_INVAL);
242: SET(bp->b_flags, (B_READ | B_DONE));
243: DPRINTF(VFSDB_BIO, ("bread: done bp=%x\n\n", bp));
244: *bpp = bp;
245: return 0;
246: }
247:
248: /*
249: * Block write with cache.
250: * @buf: buffer to write.
251: *
252: * The data is copied to the buffer.
253: * Then release the buffer.
254: */
255: int
256: bwrite(struct buf *bp)
257: {
258: size_t size;
259: int err;
260:
261: ASSERT(ISSET(bp->b_flags, B_BUSY));
262: DPRINTF(VFSDB_BIO, ("bwrite: dev=%x blkno=%d\n", bp->b_dev,
263: bp->b_blkno));
264:
265: BIO_LOCK();
266: CLR(bp->b_flags, (B_READ | B_DONE | B_DELWRI));
267: BIO_UNLOCK();
268:
269: size = BSIZE;
270: err = device_write((device_t)bp->b_dev, bp->b_data, &size,
271: bp->b_blkno);
272: if (err)
273: return err;
274: BIO_LOCK();
275: SET(bp->b_flags, B_DONE);
276: BIO_UNLOCK();
277: brelse(bp);
278: return 0;
279: }
280:
281: /*
282: * Delayed write.
283: *
284: * The buffer is marked dirty, but an actual I/O is not
285: * performed. This routine should be used when the buffer
286: * is expected to be modified again soon.
287: */
288: void
289: bdwrite(struct buf *bp)
290: {
291:
292: BIO_LOCK();
293: SET(bp->b_flags, B_DELWRI);
294: CLR(bp->b_flags, B_DONE);
295: BIO_UNLOCK();
296: brelse(bp);
297: }
298:
299: /*
300: * Flush write-behind block
301: */
302: void
303: bflush(struct buf *bp)
304: {
305:
306: BIO_LOCK();
307: if (ISSET(bp->b_flags, B_DELWRI))
308: bwrite(bp);
309: BIO_UNLOCK();
310: }
311:
312: /*
313: * Invalidate buffer for specified device.
314: * This is called when unmount.
315: */
316: void
317: binval(dev_t dev)
318: {
319: struct buf *bp;
320: int i;
321:
322: BIO_LOCK();
323: for (i = 0; i < NBUFS; i++) {
324: bp = &buf_table[i];
325: if (bp->b_dev == dev) {
326: if (ISSET(bp->b_flags, B_DELWRI))
327: bwrite(bp);
328: else if (ISSET(bp->b_flags, B_BUSY))
329: brelse(bp);
330: bp->b_flags = B_INVAL;
331: }
332: }
333: BIO_UNLOCK();
334: }
335:
336: /*
337: * Invalidate all buffers.
338: * This is called when unmount.
339: */
340: void
341: bio_sync(void)
342: {
343: struct buf *bp;
344: int i;
345:
346: start:
347: BIO_LOCK();
348: for (i = 0; i < NBUFS; i++) {
349: bp = &buf_table[i];
350: if (ISSET(bp->b_flags, B_BUSY)) {
351: BIO_UNLOCK();
352: mutex_lock(&bp->b_lock);
353: mutex_unlock(&bp->b_lock);
354: goto start;
355: }
356: if (ISSET(bp->b_flags, B_DELWRI))
357: bwrite(bp);
358: }
359: BIO_UNLOCK();
360: }
361:
362: /*
363: * Initialize the buffer I/O system.
364: */
365: void
366: bio_init(void)
367: {
368: struct buf *bp;
369: int i;
370:
371: for (i = 0; i < NBUFS; i++) {
372: bp = &buf_table[i];
373: bp->b_flags = B_INVAL;
374: bp->b_data = buffers[i];
375: mutex_init(&bp->b_lock);
376: list_insert(&free_list, &bp->b_link);
377: }
378: sem_init(&free_sem, NBUFS);
379: DPRINTF(VFSDB_BIO, ("bio: Buffer cache size %dK bytes\n",
380: BSIZE * NBUFS / 1024));
381: }
CVSweb