Subversion Repositories HelenOS

Rev

Rev 4581 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 4581 Rev 4718
1
/*
1
/*
2
 * Copyright (c) 2008 Jakub Jermar
2
 * Copyright (c) 2008 Jakub Jermar
3
 * Copyright (c) 2008 Martin Decky
3
 * Copyright (c) 2008 Martin Decky
4
 * All rights reserved.
4
 * All rights reserved.
5
 *
5
 *
6
 * Redistribution and use in source and binary forms, with or without
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
7
 * modification, are permitted provided that the following conditions
8
 * are met:
8
 * are met:
9
 *
9
 *
10
 * - Redistributions of source code must retain the above copyright
10
 * - Redistributions of source code must retain the above copyright
11
 *   notice, this list of conditions and the following disclaimer.
11
 *   notice, this list of conditions and the following disclaimer.
12
 * - Redistributions in binary form must reproduce the above copyright
12
 * - Redistributions in binary form must reproduce the above copyright
13
 *   notice, this list of conditions and the following disclaimer in the
13
 *   notice, this list of conditions and the following disclaimer in the
14
 *   documentation and/or other materials provided with the distribution.
14
 *   documentation and/or other materials provided with the distribution.
15
 * - The name of the author may not be used to endorse or promote products
15
 * - The name of the author may not be used to endorse or promote products
16
 *   derived from this software without specific prior written permission.
16
 *   derived from this software without specific prior written permission.
17
 *
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
28
 */
29
 
29
 
30
/** @addtogroup libblock
30
/** @addtogroup libblock
31
 * @{
31
 * @{
32
 */
32
 */
33
/**
33
/**
34
 * @file
34
 * @file
35
 * @brief
35
 * @brief
36
 */
36
 */
37
 
37
 
38
#include "libblock.h"
38
#include "libblock.h"
39
#include "../../srv/vfs/vfs.h"
39
#include "../../srv/vfs/vfs.h"
40
#include <ipc/devmap.h>
40
#include <ipc/devmap.h>
41
#include <ipc/bd.h>
41
#include <ipc/bd.h>
42
#include <ipc/services.h>
42
#include <ipc/services.h>
43
#include <errno.h>
43
#include <errno.h>
44
#include <sys/mman.h>
44
#include <sys/mman.h>
45
#include <async.h>
45
#include <async.h>
46
#include <ipc/ipc.h>
46
#include <ipc/ipc.h>
47
#include <as.h>
47
#include <as.h>
48
#include <assert.h>
48
#include <assert.h>
49
#include <fibril_sync.h>
49
#include <fibril_sync.h>
50
#include <adt/list.h>
50
#include <adt/list.h>
51
#include <adt/hash_table.h>
51
#include <adt/hash_table.h>
52
#include <mem.h>
52
#include <mem.h>
53
 
53
 
54
/** Lock protecting the device connection list */
54
/** Lock protecting the device connection list */
55
static FIBRIL_MUTEX_INITIALIZE(dcl_lock);
55
static FIBRIL_MUTEX_INITIALIZE(dcl_lock);
56
/** Device connection list head. */
56
/** Device connection list head. */
57
static LIST_INITIALIZE(dcl_head);
57
static LIST_INITIALIZE(dcl_head);
58
 
58
 
59
#define CACHE_BUCKETS_LOG2      10
59
#define CACHE_BUCKETS_LOG2      10
60
#define CACHE_BUCKETS           (1 << CACHE_BUCKETS_LOG2)
60
#define CACHE_BUCKETS           (1 << CACHE_BUCKETS_LOG2)
61
 
61
 
62
typedef struct {
62
typedef struct {
63
    fibril_mutex_t lock;
63
    fibril_mutex_t lock;
64
    size_t block_size;      /**< Block size. */
64
    size_t block_size;      /**< Block size. */
65
    unsigned block_count;       /**< Total number of blocks. */
65
    unsigned block_count;       /**< Total number of blocks. */
-
 
66
    unsigned blocks_cached;     /**< Number of cached blocks. */
66
    hash_table_t block_hash;
67
    hash_table_t block_hash;
67
    link_t free_head;
68
    link_t free_head;
68
    enum cache_mode mode;
69
    enum cache_mode mode;
69
} cache_t;
70
} cache_t;
70
 
71
 
71
typedef struct {
72
typedef struct {
72
    link_t link;
73
    link_t link;
73
    dev_handle_t dev_handle;
74
    dev_handle_t dev_handle;
74
    int dev_phone;
75
    int dev_phone;
-
 
76
    fibril_mutex_t com_area_lock;
75
    void *com_area;
77
    void *com_area;
76
    size_t com_size;
78
    size_t com_size;
77
    void *bb_buf;
79
    void *bb_buf;
78
    off_t bb_off;
80
    off_t bb_off;
79
    size_t bb_size;
81
    size_t bb_size;
80
    cache_t *cache;
82
    cache_t *cache;
81
} devcon_t;
83
} devcon_t;
82
 
84
 
83
static int write_block(devcon_t *devcon, bn_t boff, size_t block_size,
85
static int read_block(devcon_t *devcon, bn_t boff, size_t block_size);
84
    const void *src);
86
static int write_block(devcon_t *devcon, bn_t boff, size_t block_size);
85
 
87
 
86
static devcon_t *devcon_search(dev_handle_t dev_handle)
88
static devcon_t *devcon_search(dev_handle_t dev_handle)
87
{
89
{
88
    link_t *cur;
90
    link_t *cur;
89
 
91
 
90
    fibril_mutex_lock(&dcl_lock);
92
    fibril_mutex_lock(&dcl_lock);
91
    for (cur = dcl_head.next; cur != &dcl_head; cur = cur->next) {
93
    for (cur = dcl_head.next; cur != &dcl_head; cur = cur->next) {
92
        devcon_t *devcon = list_get_instance(cur, devcon_t, link);
94
        devcon_t *devcon = list_get_instance(cur, devcon_t, link);
93
        if (devcon->dev_handle == dev_handle) {
95
        if (devcon->dev_handle == dev_handle) {
94
            fibril_mutex_unlock(&dcl_lock);
96
            fibril_mutex_unlock(&dcl_lock);
95
            return devcon;
97
            return devcon;
96
        }
98
        }
97
    }
99
    }
98
    fibril_mutex_unlock(&dcl_lock);
100
    fibril_mutex_unlock(&dcl_lock);
99
    return NULL;
101
    return NULL;
100
}
102
}
101
 
103
 
102
static int devcon_add(dev_handle_t dev_handle, int dev_phone, void *com_area,
104
static int devcon_add(dev_handle_t dev_handle, int dev_phone, void *com_area,
103
   size_t com_size)
105
   size_t com_size)
104
{
106
{
105
    link_t *cur;
107
    link_t *cur;
106
    devcon_t *devcon;
108
    devcon_t *devcon;
107
 
109
 
108
    devcon = malloc(sizeof(devcon_t));
110
    devcon = malloc(sizeof(devcon_t));
109
    if (!devcon)
111
    if (!devcon)
110
        return ENOMEM;
112
        return ENOMEM;
111
   
113
   
112
    link_initialize(&devcon->link);
114
    link_initialize(&devcon->link);
113
    devcon->dev_handle = dev_handle;
115
    devcon->dev_handle = dev_handle;
114
    devcon->dev_phone = dev_phone;
116
    devcon->dev_phone = dev_phone;
-
 
117
    fibril_mutex_initialize(&devcon->com_area_lock);
115
    devcon->com_area = com_area;
118
    devcon->com_area = com_area;
116
    devcon->com_size = com_size;
119
    devcon->com_size = com_size;
117
    devcon->bb_buf = NULL;
120
    devcon->bb_buf = NULL;
118
    devcon->bb_off = 0;
121
    devcon->bb_off = 0;
119
    devcon->bb_size = 0;
122
    devcon->bb_size = 0;
120
    devcon->cache = NULL;
123
    devcon->cache = NULL;
121
 
124
 
122
    fibril_mutex_lock(&dcl_lock);
125
    fibril_mutex_lock(&dcl_lock);
123
    for (cur = dcl_head.next; cur != &dcl_head; cur = cur->next) {
126
    for (cur = dcl_head.next; cur != &dcl_head; cur = cur->next) {
124
        devcon_t *d = list_get_instance(cur, devcon_t, link);
127
        devcon_t *d = list_get_instance(cur, devcon_t, link);
125
        if (d->dev_handle == dev_handle) {
128
        if (d->dev_handle == dev_handle) {
126
            fibril_mutex_unlock(&dcl_lock);
129
            fibril_mutex_unlock(&dcl_lock);
127
            free(devcon);
130
            free(devcon);
128
            return EEXIST;
131
            return EEXIST;
129
        }
132
        }
130
    }
133
    }
131
    list_append(&devcon->link, &dcl_head);
134
    list_append(&devcon->link, &dcl_head);
132
    fibril_mutex_unlock(&dcl_lock);
135
    fibril_mutex_unlock(&dcl_lock);
133
    return EOK;
136
    return EOK;
134
}
137
}
135
 
138
 
136
static void devcon_remove(devcon_t *devcon)
139
static void devcon_remove(devcon_t *devcon)
137
{
140
{
138
    fibril_mutex_lock(&dcl_lock);
141
    fibril_mutex_lock(&dcl_lock);
139
    list_remove(&devcon->link);
142
    list_remove(&devcon->link);
140
    fibril_mutex_unlock(&dcl_lock);
143
    fibril_mutex_unlock(&dcl_lock);
141
}
144
}
142
 
145
 
143
int block_init(dev_handle_t dev_handle, size_t com_size)
146
int block_init(dev_handle_t dev_handle, size_t com_size)
144
{
147
{
145
    int rc;
148
    int rc;
146
    int dev_phone;
149
    int dev_phone;
147
    void *com_area;
150
    void *com_area;
148
   
151
   
149
    com_area = mmap(NULL, com_size, PROTO_READ | PROTO_WRITE,
152
    com_area = mmap(NULL, com_size, PROTO_READ | PROTO_WRITE,
150
        MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
153
        MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
151
    if (!com_area) {
154
    if (!com_area) {
152
        return ENOMEM;
155
        return ENOMEM;
153
    }
156
    }
154
 
157
 
155
    dev_phone = devmap_device_connect(dev_handle, IPC_FLAG_BLOCKING);
158
    dev_phone = devmap_device_connect(dev_handle, IPC_FLAG_BLOCKING);
156
    if (dev_phone < 0) {
159
    if (dev_phone < 0) {
157
        munmap(com_area, com_size);
160
        munmap(com_area, com_size);
158
        return dev_phone;
161
        return dev_phone;
159
    }
162
    }
160
 
163
 
161
    rc = ipc_share_out_start(dev_phone, com_area,
164
    rc = ipc_share_out_start(dev_phone, com_area,
162
        AS_AREA_READ | AS_AREA_WRITE);
165
        AS_AREA_READ | AS_AREA_WRITE);
163
    if (rc != EOK) {
166
    if (rc != EOK) {
164
            munmap(com_area, com_size);
167
            munmap(com_area, com_size);
165
        ipc_hangup(dev_phone);
168
        ipc_hangup(dev_phone);
166
        return rc;
169
        return rc;
167
    }
170
    }
168
   
171
   
169
    rc = devcon_add(dev_handle, dev_phone, com_area, com_size);
172
    rc = devcon_add(dev_handle, dev_phone, com_area, com_size);
170
    if (rc != EOK) {
173
    if (rc != EOK) {
171
        munmap(com_area, com_size);
174
        munmap(com_area, com_size);
172
        ipc_hangup(dev_phone);
175
        ipc_hangup(dev_phone);
173
        return rc;
176
        return rc;
174
    }
177
    }
175
 
178
 
176
    return EOK;
179
    return EOK;
177
}
180
}
178
 
181
 
179
void block_fini(dev_handle_t dev_handle)
182
void block_fini(dev_handle_t dev_handle)
180
{
183
{
181
    devcon_t *devcon = devcon_search(dev_handle);
184
    devcon_t *devcon = devcon_search(dev_handle);
182
    assert(devcon);
185
    assert(devcon);
183
   
186
   
184
    devcon_remove(devcon);
187
    devcon_remove(devcon);
185
 
188
 
186
    if (devcon->bb_buf)
189
    if (devcon->bb_buf)
187
        free(devcon->bb_buf);
190
        free(devcon->bb_buf);
188
 
191
 
189
    if (devcon->cache) {
192
    if (devcon->cache) {
190
        hash_table_destroy(&devcon->cache->block_hash);
193
        hash_table_destroy(&devcon->cache->block_hash);
191
        free(devcon->cache);
194
        free(devcon->cache);
192
    }
195
    }
193
 
196
 
194
    munmap(devcon->com_area, devcon->com_size);
197
    munmap(devcon->com_area, devcon->com_size);
195
    ipc_hangup(devcon->dev_phone);
198
    ipc_hangup(devcon->dev_phone);
196
 
199
 
197
    free(devcon);  
200
    free(devcon);  
198
}
201
}
199
 
202
 
200
int block_bb_read(dev_handle_t dev_handle, off_t off, size_t size)
203
int block_bb_read(dev_handle_t dev_handle, off_t off, size_t size)
201
{
204
{
202
    void *bb_buf;
205
    void *bb_buf;
203
    int rc;
206
    int rc;
204
 
207
 
205
    devcon_t *devcon = devcon_search(dev_handle);
208
    devcon_t *devcon = devcon_search(dev_handle);
206
    if (!devcon)
209
    if (!devcon)
207
        return ENOENT;
210
        return ENOENT;
208
    if (devcon->bb_buf)
211
    if (devcon->bb_buf)
209
        return EEXIST;
212
        return EEXIST;
210
    bb_buf = malloc(size);
213
    bb_buf = malloc(size);
211
    if (!bb_buf)
214
    if (!bb_buf)
212
        return ENOMEM;
215
        return ENOMEM;
213
   
216
   
214
    off_t bufpos = 0;
-
 
215
    size_t buflen = 0;
-
 
216
    rc = block_read(dev_handle, &bufpos, &buflen, &off,
217
    fibril_mutex_lock(&devcon->com_area_lock);
217
        bb_buf, size, size);
218
    rc = read_block(devcon, 0, size);
218
    if (rc != EOK) {
219
    if (rc != EOK) {
-
 
220
        fibril_mutex_unlock(&devcon->com_area_lock);
219
            free(bb_buf);
221
            free(bb_buf);
220
        return rc;
222
        return rc;
221
    }
223
    }
-
 
224
    memcpy(bb_buf, devcon->com_area, size);
-
 
225
    fibril_mutex_unlock(&devcon->com_area_lock);
-
 
226
 
222
    devcon->bb_buf = bb_buf;
227
    devcon->bb_buf = bb_buf;
223
    devcon->bb_off = off;
228
    devcon->bb_off = off;
224
    devcon->bb_size = size;
229
    devcon->bb_size = size;
225
 
230
 
226
    return EOK;
231
    return EOK;
227
}
232
}
228
 
233
 
229
void *block_bb_get(dev_handle_t dev_handle)
234
void *block_bb_get(dev_handle_t dev_handle)
230
{
235
{
231
    devcon_t *devcon = devcon_search(dev_handle);
236
    devcon_t *devcon = devcon_search(dev_handle);
232
    assert(devcon);
237
    assert(devcon);
233
    return devcon->bb_buf;
238
    return devcon->bb_buf;
234
}
239
}
235
 
240
 
236
static hash_index_t cache_hash(unsigned long *key)
241
static hash_index_t cache_hash(unsigned long *key)
237
{
242
{
238
    return *key & (CACHE_BUCKETS - 1);
243
    return *key & (CACHE_BUCKETS - 1);
239
}
244
}
240
 
245
 
241
static int cache_compare(unsigned long *key, hash_count_t keys, link_t *item)
246
static int cache_compare(unsigned long *key, hash_count_t keys, link_t *item)
242
{
247
{
243
    block_t *b = hash_table_get_instance(item, block_t, hash_link);
248
    block_t *b = hash_table_get_instance(item, block_t, hash_link);
244
    return b->boff == *key;
249
    return b->boff == *key;
245
}
250
}
246
 
251
 
247
static void cache_remove_callback(link_t *item)
252
static void cache_remove_callback(link_t *item)
248
{
253
{
249
}
254
}
250
 
255
 
251
static hash_table_operations_t cache_ops = {
256
static hash_table_operations_t cache_ops = {
252
    .hash = cache_hash,
257
    .hash = cache_hash,
253
    .compare = cache_compare,
258
    .compare = cache_compare,
254
    .remove_callback = cache_remove_callback
259
    .remove_callback = cache_remove_callback
255
};
260
};
256
 
261
 
257
int block_cache_init(dev_handle_t dev_handle, size_t size, unsigned blocks,
262
int block_cache_init(dev_handle_t dev_handle, size_t size, unsigned blocks,
258
    enum cache_mode mode)
263
    enum cache_mode mode)
259
{
264
{
260
    devcon_t *devcon = devcon_search(dev_handle);
265
    devcon_t *devcon = devcon_search(dev_handle);
261
    cache_t *cache;
266
    cache_t *cache;
262
    if (!devcon)
267
    if (!devcon)
263
        return ENOENT;
268
        return ENOENT;
264
    if (devcon->cache)
269
    if (devcon->cache)
265
        return EEXIST;
270
        return EEXIST;
266
    cache = malloc(sizeof(cache_t));
271
    cache = malloc(sizeof(cache_t));
267
    if (!cache)
272
    if (!cache)
268
        return ENOMEM;
273
        return ENOMEM;
269
   
274
   
270
    fibril_mutex_initialize(&cache->lock);
275
    fibril_mutex_initialize(&cache->lock);
271
    list_initialize(&cache->free_head);
276
    list_initialize(&cache->free_head);
272
    cache->block_size = size;
277
    cache->block_size = size;
273
    cache->block_count = blocks;
278
    cache->block_count = blocks;
-
 
279
    cache->blocks_cached = 0;
274
    cache->mode = mode;
280
    cache->mode = mode;
275
 
281
 
276
    if (!hash_table_create(&cache->block_hash, CACHE_BUCKETS, 1,
282
    if (!hash_table_create(&cache->block_hash, CACHE_BUCKETS, 1,
277
        &cache_ops)) {
283
        &cache_ops)) {
278
        free(cache);
284
        free(cache);
279
        return ENOMEM;
285
        return ENOMEM;
280
    }
286
    }
281
 
287
 
282
    devcon->cache = cache;
288
    devcon->cache = cache;
283
    return EOK;
289
    return EOK;
284
}
290
}
285
 
291
 
-
 
292
#define CACHE_LO_WATERMARK  10  
-
 
293
#define CACHE_HI_WATERMARK  20  
286
static bool cache_can_grow(cache_t *cache)
294
static bool cache_can_grow(cache_t *cache)
287
{
295
{
-
 
296
    if (cache->blocks_cached < CACHE_LO_WATERMARK)
-
 
297
        return true;
-
 
298
    if (!list_empty(&cache->free_head))
-
 
299
        return false;
288
    return true;
300
    return true;
289
}
301
}
290
 
302
 
291
static void block_initialize(block_t *b)
303
static void block_initialize(block_t *b)
292
{
304
{
293
    fibril_mutex_initialize(&b->lock);
305
    fibril_mutex_initialize(&b->lock);
294
    b->refcnt = 1;
306
    b->refcnt = 1;
295
    b->dirty = false;
307
    b->dirty = false;
296
    fibril_rwlock_initialize(&b->contents_lock);
308
    fibril_rwlock_initialize(&b->contents_lock);
297
    link_initialize(&b->free_link);
309
    link_initialize(&b->free_link);
298
    link_initialize(&b->hash_link);
310
    link_initialize(&b->hash_link);
299
}
311
}
300
 
312
 
301
/** Instantiate a block in memory and get a reference to it.
313
/** Instantiate a block in memory and get a reference to it.
302
 *
314
 *
303
 * @param dev_handle        Device handle of the block device.
315
 * @param dev_handle        Device handle of the block device.
304
 * @param boff          Block offset.
316
 * @param boff          Block offset.
305
 * @param flags         If BLOCK_FLAGS_NOREAD is specified, block_get()
317
 * @param flags         If BLOCK_FLAGS_NOREAD is specified, block_get()
306
 *              will not read the contents of the block from the
318
 *              will not read the contents of the block from the
307
 *              device.
319
 *              device.
308
 *
320
 *
309
 * @return          Block structure.
321
 * @return          Block structure.
310
 */
322
 */
311
block_t *block_get(dev_handle_t dev_handle, bn_t boff, int flags)
323
block_t *block_get(dev_handle_t dev_handle, bn_t boff, int flags)
312
{
324
{
313
    devcon_t *devcon;
325
    devcon_t *devcon;
314
    cache_t *cache;
326
    cache_t *cache;
315
    block_t *b;
327
    block_t *b;
316
    link_t *l;
328
    link_t *l;
317
    unsigned long key = boff;
329
    unsigned long key = boff;
-
 
330
    bn_t oboff;
318
   
331
   
319
    devcon = devcon_search(dev_handle);
332
    devcon = devcon_search(dev_handle);
320
 
333
 
321
    assert(devcon);
334
    assert(devcon);
322
    assert(devcon->cache);
335
    assert(devcon->cache);
323
   
336
   
324
    cache = devcon->cache;
337
    cache = devcon->cache;
325
    fibril_mutex_lock(&cache->lock);
338
    fibril_mutex_lock(&cache->lock);
326
    l = hash_table_find(&cache->block_hash, &key);
339
    l = hash_table_find(&cache->block_hash, &key);
327
    if (l) {
340
    if (l) {
328
        /*
341
        /*
329
         * We found the block in the cache.
342
         * We found the block in the cache.
330
         */
343
         */
331
        b = hash_table_get_instance(l, block_t, hash_link);
344
        b = hash_table_get_instance(l, block_t, hash_link);
332
        fibril_mutex_lock(&b->lock);
345
        fibril_mutex_lock(&b->lock);
333
        if (b->refcnt++ == 0)
346
        if (b->refcnt++ == 0)
334
            list_remove(&b->free_link);
347
            list_remove(&b->free_link);
335
        fibril_mutex_unlock(&b->lock);
348
        fibril_mutex_unlock(&b->lock);
336
        fibril_mutex_unlock(&cache->lock);
349
        fibril_mutex_unlock(&cache->lock);
337
    } else {
350
    } else {
338
        /*
351
        /*
339
         * The block was not found in the cache.
352
         * The block was not found in the cache.
340
         */
353
         */
341
        int rc;
354
        int rc;
342
        off_t bufpos = 0;
-
 
343
        size_t buflen = 0;
-
 
344
        off_t pos = boff * cache->block_size;
-
 
345
        bool sync = false;
355
        bool sync = false;
346
 
356
 
347
        if (cache_can_grow(cache)) {
357
        if (cache_can_grow(cache)) {
348
            /*
358
            /*
349
             * We can grow the cache by allocating new blocks.
359
             * We can grow the cache by allocating new blocks.
350
             * Should the allocation fail, we fail over and try to
360
             * Should the allocation fail, we fail over and try to
351
             * recycle a block from the cache.
361
             * recycle a block from the cache.
352
             */
362
             */
353
            b = malloc(sizeof(block_t));
363
            b = malloc(sizeof(block_t));
354
            if (!b)
364
            if (!b)
355
                goto recycle;
365
                goto recycle;
356
            b->data = malloc(cache->block_size);
366
            b->data = malloc(cache->block_size);
357
            if (!b->data) {
367
            if (!b->data) {
358
                free(b);
368
                free(b);
359
                goto recycle;
369
                goto recycle;
360
            }
370
            }
-
 
371
            cache->blocks_cached++;
361
        } else {
372
        } else {
362
            /*
373
            /*
363
             * Try to recycle a block from the free list.
374
             * Try to recycle a block from the free list.
364
             */
375
             */
365
            unsigned long temp_key;
376
            unsigned long temp_key;
366
recycle:
377
recycle:
367
            assert(!list_empty(&cache->free_head));
378
            assert(!list_empty(&cache->free_head));
368
            l = cache->free_head.next;
379
            l = cache->free_head.next;
369
            list_remove(l);
380
            list_remove(l);
370
            b = hash_table_get_instance(l, block_t, hash_link);
381
            b = list_get_instance(l, block_t, free_link);
371
            sync = b->dirty;
382
            sync = b->dirty;
-
 
383
            oboff = b->boff;
372
            temp_key = b->boff;
384
            temp_key = b->boff;
373
            hash_table_remove(&cache->block_hash, &temp_key, 1);
385
            hash_table_remove(&cache->block_hash, &temp_key, 1);
374
        }
386
        }
375
 
387
 
376
        block_initialize(b);
388
        block_initialize(b);
377
        b->dev_handle = dev_handle;
389
        b->dev_handle = dev_handle;
378
        b->size = cache->block_size;
390
        b->size = cache->block_size;
379
        b->boff = boff;
391
        b->boff = boff;
380
        hash_table_insert(&cache->block_hash, &key, &b->hash_link);
392
        hash_table_insert(&cache->block_hash, &key, &b->hash_link);
381
 
393
 
382
        /*
394
        /*
383
         * Lock the block before releasing the cache lock. Thus we don't
395
         * Lock the block before releasing the cache lock. Thus we don't
384
         * kill concurent operations on the cache while doing I/O on the
396
         * kill concurent operations on the cache while doing I/O on the
385
         * block.
397
         * block.
386
         */
398
         */
387
        fibril_mutex_lock(&b->lock);
399
        fibril_mutex_lock(&b->lock);
388
        fibril_mutex_unlock(&cache->lock);
400
        fibril_mutex_unlock(&cache->lock);
389
 
401
 
390
        if (sync) {
402
        if (sync) {
391
            /*
403
            /*
392
             * The block is dirty and needs to be written back to
404
             * The block is dirty and needs to be written back to
393
             * the device before we can read in the new contents.
405
             * the device before we can read in the new contents.
394
             */
406
             */
395
            abort();    /* TODO: block_write() */
407
            fibril_mutex_lock(&devcon->com_area_lock);
-
 
408
            memcpy(devcon->com_area, b->data, b->size);
-
 
409
            rc = write_block(devcon, oboff, cache->block_size);
-
 
410
            assert(rc == EOK);
-
 
411
            fibril_mutex_unlock(&devcon->com_area_lock);
396
        }
412
        }
397
        if (!(flags & BLOCK_FLAGS_NOREAD)) {
413
        if (!(flags & BLOCK_FLAGS_NOREAD)) {
398
            /*
414
            /*
399
             * The block contains old or no data. We need to read
415
             * The block contains old or no data. We need to read
400
             * the new contents from the device.
416
             * the new contents from the device.
401
             */
417
             */
402
            rc = block_read(dev_handle, &bufpos, &buflen, &pos,
418
            fibril_mutex_lock(&devcon->com_area_lock);
403
                b->data, cache->block_size, cache->block_size);
419
            rc = read_block(devcon, b->boff, cache->block_size);
404
            assert(rc == EOK);
420
            assert(rc == EOK);
-
 
421
            memcpy(b->data, devcon->com_area, cache->block_size);
-
 
422
            fibril_mutex_unlock(&devcon->com_area_lock);
405
        }
423
        }
406
 
424
 
407
        fibril_mutex_unlock(&b->lock);
425
        fibril_mutex_unlock(&b->lock);
408
    }
426
    }
409
    return b;
427
    return b;
410
}
428
}
411
 
429
 
412
/** Release a reference to a block.
430
/** Release a reference to a block.
413
 *
431
 *
414
 * If the last reference is dropped, the block is put on the free list.
432
 * If the last reference is dropped, the block is put on the free list.
415
 *
433
 *
416
 * @param block     Block of which a reference is to be released.
434
 * @param block     Block of which a reference is to be released.
417
 */
435
 */
418
void block_put(block_t *block)
436
void block_put(block_t *block)
419
{
437
{
420
    devcon_t *devcon = devcon_search(block->dev_handle);
438
    devcon_t *devcon = devcon_search(block->dev_handle);
421
    cache_t *cache;
439
    cache_t *cache;
422
    int rc;
440
    int rc;
423
 
441
 
424
    assert(devcon);
442
    assert(devcon);
425
    assert(devcon->cache);
443
    assert(devcon->cache);
426
 
444
 
427
    cache = devcon->cache;
445
    cache = devcon->cache;
428
    fibril_mutex_lock(&cache->lock);
446
    fibril_mutex_lock(&cache->lock);
429
    fibril_mutex_lock(&block->lock);
447
    fibril_mutex_lock(&block->lock);
430
    if (!--block->refcnt) {
448
    if (!--block->refcnt) {
431
        /*
449
        /*
432
         * Last reference to the block was dropped, put the block on the
450
         * Last reference to the block was dropped. Either free the
-
 
451
         * block or put it on the free list.
-
 
452
         */
-
 
453
        if (cache->blocks_cached > CACHE_HI_WATERMARK) {
-
 
454
            /*
-
 
455
             * Currently there are too many cached blocks.
-
 
456
             */
-
 
457
            if (block->dirty) {
-
 
458
                fibril_mutex_lock(&devcon->com_area_lock);
-
 
459
                memcpy(devcon->com_area, block->data,
-
 
460
                    block->size);
-
 
461
                rc = write_block(devcon, block->boff,
-
 
462
                    block->size);
-
 
463
                assert(rc == EOK);
-
 
464
                fibril_mutex_unlock(&devcon->com_area_lock);
-
 
465
            }
-
 
466
            /*
-
 
467
             * Take the block out of the cache and free it.
-
 
468
             */
-
 
469
            unsigned long key = block->boff;
-
 
470
            hash_table_remove(&cache->block_hash, &key, 1);
433
         * free list.
471
            free(block);
-
 
472
            free(block->data);
-
 
473
            cache->blocks_cached--;
-
 
474
            fibril_mutex_unlock(&cache->lock);
-
 
475
            return;
-
 
476
        }
-
 
477
        /*
-
 
478
         * Put the block on the free list.
434
         */
479
         */
435
        list_append(&block->free_link, &cache->free_head);
480
        list_append(&block->free_link, &cache->free_head);
436
        if (cache->mode != CACHE_MODE_WB && block->dirty) {
481
        if (cache->mode != CACHE_MODE_WB && block->dirty) {
-
 
482
            fibril_mutex_lock(&devcon->com_area_lock);
437
            rc = write_block(devcon, block->boff, block->size,
483
            memcpy(devcon->com_area, block->data, block->size);
438
                block->data);
484
            rc = write_block(devcon, block->boff, block->size);
439
            assert(rc == EOK);
485
            assert(rc == EOK);
-
 
486
            fibril_mutex_unlock(&devcon->com_area_lock);
440
 
487
 
441
            block->dirty = false;
488
            block->dirty = false;
442
        }
489
        }
443
    }
490
    }
444
    fibril_mutex_unlock(&block->lock);
491
    fibril_mutex_unlock(&block->lock);
445
    fibril_mutex_unlock(&cache->lock);
492
    fibril_mutex_unlock(&cache->lock);
446
}
493
}
447
 
494
 
448
/** Read data from a block device.
495
/** Read sequential data from a block device.
449
 *
496
 *
450
 * @param dev_handle    Device handle of the block device.
497
 * @param dev_handle    Device handle of the block device.
451
 * @param bufpos    Pointer to the first unread valid offset within the
498
 * @param bufpos    Pointer to the first unread valid offset within the
452
 *          communication buffer.
499
 *          communication buffer.
453
 * @param buflen    Pointer to the number of unread bytes that are ready in
500
 * @param buflen    Pointer to the number of unread bytes that are ready in
454
 *          the communication buffer.
501
 *          the communication buffer.
455
 * @param pos       Device position to be read.
502
 * @param pos       Device position to be read.
456
 * @param dst       Destination buffer.
503
 * @param dst       Destination buffer.
457
 * @param size      Size of the destination buffer.
504
 * @param size      Size of the destination buffer.
458
 * @param block_size    Block size to be used for the transfer.
505
 * @param block_size    Block size to be used for the transfer.
459
 *
506
 *
460
 * @return      EOK on success or a negative return code on failure.
507
 * @return      EOK on success or a negative return code on failure.
461
 */
508
 */
462
int
-
 
463
block_read(dev_handle_t dev_handle, off_t *bufpos, size_t *buflen, off_t *pos,
509
int block_seqread(dev_handle_t dev_handle, off_t *bufpos, size_t *buflen,
464
    void *dst, size_t size, size_t block_size)
510
    off_t *pos, void *dst, size_t size, size_t block_size)
465
{
511
{
466
    off_t offset = 0;
512
    off_t offset = 0;
467
    size_t left = size;
513
    size_t left = size;
468
    devcon_t *devcon = devcon_search(dev_handle);
514
    devcon_t *devcon = devcon_search(dev_handle);
469
    assert(devcon);
515
    assert(devcon);
470
   
516
   
-
 
517
    fibril_mutex_lock(&devcon->com_area_lock);
471
    while (left > 0) {
518
    while (left > 0) {
472
        size_t rd;
519
        size_t rd;
473
       
520
       
474
        if (*bufpos + left < *buflen)
521
        if (*bufpos + left < *buflen)
475
            rd = left;
522
            rd = left;
476
        else
523
        else
477
            rd = *buflen - *bufpos;
524
            rd = *buflen - *bufpos;
478
       
525
       
479
        if (rd > 0) {
526
        if (rd > 0) {
480
            /*
527
            /*
481
             * Copy the contents of the communication buffer to the
528
             * Copy the contents of the communication buffer to the
482
             * destination buffer.
529
             * destination buffer.
483
             */
530
             */
484
            memcpy(dst + offset, devcon->com_area + *bufpos, rd);
531
            memcpy(dst + offset, devcon->com_area + *bufpos, rd);
485
            offset += rd;
532
            offset += rd;
486
            *bufpos += rd;
533
            *bufpos += rd;
487
            *pos += rd;
534
            *pos += rd;
488
            left -= rd;
535
            left -= rd;
489
        }
536
        }
490
       
537
       
491
        if (*bufpos == (off_t) *buflen) {
538
        if (*bufpos == (off_t) *buflen) {
492
            /* Refill the communication buffer with a new block. */
539
            /* Refill the communication buffer with a new block. */
493
            ipcarg_t retval;
540
            int rc;
494
            int rc = async_req_2_1(devcon->dev_phone, BD_READ_BLOCK,
-
 
-
 
541
 
495
                *pos / block_size, block_size, &retval);
542
            rc = read_block(devcon, *pos / block_size, block_size);
496
            if ((rc != EOK) || (retval != EOK))
543
            if (rc != EOK) {
497
                return (rc != EOK ? rc : (int) retval);
544
                fibril_mutex_unlock(&devcon->com_area_lock);
-
 
545
                return rc;
-
 
546
            }
498
           
547
           
499
            *bufpos = 0;
548
            *bufpos = 0;
500
            *buflen = block_size;
549
            *buflen = block_size;
501
        }
550
        }
502
    }
551
    }
-
 
552
    fibril_mutex_unlock(&devcon->com_area_lock);
503
   
553
   
504
    return EOK;
554
    return EOK;
505
}
555
}
506
 
556
 
-
 
557
/** Read block from block device.
-
 
558
 *
-
 
559
 * @param devcon    Device connection.
-
 
560
 * @param boff      Block index.
-
 
561
 * @param block_size    Block size.
-
 
562
 * @param src       Buffer for storing the data.
-
 
563
 *
-
 
564
 * @return      EOK on success or negative error code on failure.
-
 
565
 */
-
 
566
static int read_block(devcon_t *devcon, bn_t boff, size_t block_size)
-
 
567
{
-
 
568
    ipcarg_t retval;
-
 
569
    int rc;
-
 
570
 
-
 
571
    assert(devcon);
-
 
572
    rc = async_req_2_1(devcon->dev_phone, BD_READ_BLOCK, boff, block_size,
-
 
573
        &retval);
-
 
574
    if ((rc != EOK) || (retval != EOK))
-
 
575
        return (rc != EOK ? rc : (int) retval);
-
 
576
 
-
 
577
    return EOK;
-
 
578
}
-
 
579
 
507
/** Write block to block device.
580
/** Write block to block device.
508
 *
581
 *
509
 * @param devcon    Device connection.
582
 * @param devcon    Device connection.
510
 * @param boff      Block index.
583
 * @param boff      Block index.
511
 * @param block_size    Block size.
584
 * @param block_size    Block size.
512
 * @param src       Buffer containing the data to write.
585
 * @param src       Buffer containing the data to write.
513
 *
586
 *
514
 * @return      EOK on success or negative error code on failure.
587
 * @return      EOK on success or negative error code on failure.
515
 */
588
 */
516
static int write_block(devcon_t *devcon, bn_t boff, size_t block_size,
589
static int write_block(devcon_t *devcon, bn_t boff, size_t block_size)
517
    const void *src)
-
 
518
{
590
{
519
    ipcarg_t retval;
591
    ipcarg_t retval;
520
    int rc;
592
    int rc;
521
 
593
 
522
    assert(devcon);
594
    assert(devcon);
523
    memcpy(devcon->com_area, src, block_size);
-
 
524
   
-
 
525
    rc = async_req_2_1(devcon->dev_phone, BD_WRITE_BLOCK,
595
    rc = async_req_2_1(devcon->dev_phone, BD_WRITE_BLOCK, boff, block_size,
526
        boff, block_size, &retval);
596
        &retval);
527
    if ((rc != EOK) || (retval != EOK))
597
    if ((rc != EOK) || (retval != EOK))
528
        return (rc != EOK ? rc : (int) retval);
598
        return (rc != EOK ? rc : (int) retval);
529
 
599
 
530
    return EOK;
600
    return EOK;
531
}
601
}
532
 
602
 
533
/** @}
603
/** @}
534
 */
604
 */
535
 
605