source: mainline/uspace/lib/libblock/libblock.c@ 82346c50

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 82346c50 was 6408be3, checked in by Jiri Svoboda <jirik.svoboda@…>, 16 years ago

Fix chaos in block library.

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