Statistics
| Branch: | Revision:

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
}