source: mainline/uspace/lib/block/libblock.c@ bf61d3a

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bf61d3a was 7e752b2, checked in by Martin Decky <martin@…>, 15 years ago
  • correct printf() formatting strings and corresponding arguments
  • minor cstyle changes and other small fixes
  • Property mode set to 100644
File size: 21.6 KB
RevLine 
[fc840d9]1/*
[ed903174]2 * Copyright (c) 2008 Jakub Jermar
3 * Copyright (c) 2008 Martin Decky
[fc840d9]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
[97c9da8]30/** @addtogroup libblock
[fc840d9]31 * @{
[97c9da8]32 */
[fc840d9]33/**
34 * @file
35 * @brief
36 */
37
[97c9da8]38#include "libblock.h"
[fc840d9]39#include "../../srv/vfs/vfs.h"
[7858bc5f]40#include <ipc/devmap.h>
[c5747fe]41#include <ipc/bd.h>
[7858bc5f]42#include <ipc/services.h>
[fc840d9]43#include <errno.h>
[7858bc5f]44#include <sys/mman.h>
[fc840d9]45#include <async.h>
46#include <ipc/ipc.h>
47#include <as.h>
48#include <assert.h>
[1e4cada]49#include <fibril_synch.h>
[d9c8c81]50#include <adt/list.h>
51#include <adt/hash_table.h>
[1ee00b7]52#include <macros.h>
[d00ae4c]53#include <mem.h>
[16fc3c9]54#include <sys/typefmt.h>
55#include <stacktrace.h>
[fc840d9]56
[916bf1a]57/** Lock protecting the device connection list */
[4e1b57d]58static FIBRIL_MUTEX_INITIALIZE(dcl_lock);
[916bf1a]59/** Device connection list head. */
60static LIST_INITIALIZE(dcl_head);
61
[f1ba5d6]62#define CACHE_BUCKETS_LOG2 10
63#define CACHE_BUCKETS (1 << CACHE_BUCKETS_LOG2)
64
65typedef struct {
[4e1b57d]66 fibril_mutex_t lock;
[1ee00b7]67 size_t lblock_size; /**< Logical block size. */
[f1ba5d6]68 unsigned block_count; /**< Total number of blocks. */
[d68e4d5]69 unsigned blocks_cached; /**< Number of cached blocks. */
[f1ba5d6]70 hash_table_t block_hash;
71 link_t free_head;
[1fbe064b]72 enum cache_mode mode;
[f1ba5d6]73} cache_t;
74
[916bf1a]75typedef struct {
76 link_t link;
[991f645]77 devmap_handle_t devmap_handle;
[916bf1a]78 int dev_phone;
[a830611]79 fibril_mutex_t comm_area_lock;
80 void *comm_area;
81 size_t comm_size;
[916bf1a]82 void *bb_buf;
[ed903174]83 aoff64_t bb_addr;
[1ee00b7]84 size_t pblock_size; /**< Physical block size. */
[f1ba5d6]85 cache_t *cache;
[916bf1a]86} devcon_t;
87
[ed903174]88static int read_blocks(devcon_t *devcon, aoff64_t ba, size_t cnt);
89static int write_blocks(devcon_t *devcon, aoff64_t ba, size_t cnt);
[00b1d20e]90static int get_block_size(int dev_phone, size_t *bsize);
[ed903174]91static int get_num_blocks(int dev_phone, aoff64_t *nblocks);
[1fbe064b]92
[991f645]93static devcon_t *devcon_search(devmap_handle_t devmap_handle)
[916bf1a]94{
95 link_t *cur;
96
[4e1b57d]97 fibril_mutex_lock(&dcl_lock);
[916bf1a]98 for (cur = dcl_head.next; cur != &dcl_head; cur = cur->next) {
99 devcon_t *devcon = list_get_instance(cur, devcon_t, link);
[991f645]100 if (devcon->devmap_handle == devmap_handle) {
[4e1b57d]101 fibril_mutex_unlock(&dcl_lock);
[916bf1a]102 return devcon;
103 }
104 }
[4e1b57d]105 fibril_mutex_unlock(&dcl_lock);
[916bf1a]106 return NULL;
107}
108
[991f645]109static int devcon_add(devmap_handle_t devmap_handle, int dev_phone, size_t bsize,
[a830611]110 void *comm_area, size_t comm_size)
[916bf1a]111{
112 link_t *cur;
113 devcon_t *devcon;
114
[a830611]115 if (comm_size < bsize)
[1ee00b7]116 return EINVAL;
117
[916bf1a]118 devcon = malloc(sizeof(devcon_t));
119 if (!devcon)
120 return ENOMEM;
121
122 link_initialize(&devcon->link);
[991f645]123 devcon->devmap_handle = devmap_handle;
[916bf1a]124 devcon->dev_phone = dev_phone;
[a830611]125 fibril_mutex_initialize(&devcon->comm_area_lock);
126 devcon->comm_area = comm_area;
127 devcon->comm_size = comm_size;
[6284978]128 devcon->bb_buf = NULL;
[1ee00b7]129 devcon->bb_addr = 0;
130 devcon->pblock_size = bsize;
[f1ba5d6]131 devcon->cache = NULL;
[916bf1a]132
[4e1b57d]133 fibril_mutex_lock(&dcl_lock);
[916bf1a]134 for (cur = dcl_head.next; cur != &dcl_head; cur = cur->next) {
135 devcon_t *d = list_get_instance(cur, devcon_t, link);
[991f645]136 if (d->devmap_handle == devmap_handle) {
[4e1b57d]137 fibril_mutex_unlock(&dcl_lock);
[916bf1a]138 free(devcon);
139 return EEXIST;
140 }
141 }
142 list_append(&devcon->link, &dcl_head);
[4e1b57d]143 fibril_mutex_unlock(&dcl_lock);
[916bf1a]144 return EOK;
145}
146
147static void devcon_remove(devcon_t *devcon)
148{
[4e1b57d]149 fibril_mutex_lock(&dcl_lock);
[916bf1a]150 list_remove(&devcon->link);
[4e1b57d]151 fibril_mutex_unlock(&dcl_lock);
[916bf1a]152}
[7858bc5f]153
[991f645]154int block_init(devmap_handle_t devmap_handle, size_t comm_size)
[7858bc5f]155{
156 int rc;
[916bf1a]157 int dev_phone;
[a830611]158 void *comm_area;
[1ee00b7]159 size_t bsize;
160
[a830611]161 comm_area = mmap(NULL, comm_size, PROTO_READ | PROTO_WRITE,
[7858bc5f]162 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
[a830611]163 if (!comm_area) {
[7858bc5f]164 return ENOMEM;
165 }
166
[991f645]167 dev_phone = devmap_device_connect(devmap_handle, IPC_FLAG_BLOCKING);
[7858bc5f]168 if (dev_phone < 0) {
[a830611]169 munmap(comm_area, comm_size);
[7858bc5f]170 return dev_phone;
171 }
172
[0da4e41]173 rc = async_share_out_start(dev_phone, comm_area,
[7858bc5f]174 AS_AREA_READ | AS_AREA_WRITE);
175 if (rc != EOK) {
[a830611]176 munmap(comm_area, comm_size);
[7858bc5f]177 ipc_hangup(dev_phone);
178 return rc;
179 }
[1ee00b7]180
181 if (get_block_size(dev_phone, &bsize) != EOK) {
[a830611]182 munmap(comm_area, comm_size);
[1ee00b7]183 ipc_hangup(dev_phone);
184 return rc;
185 }
[916bf1a]186
[991f645]187 rc = devcon_add(devmap_handle, dev_phone, bsize, comm_area, comm_size);
[916bf1a]188 if (rc != EOK) {
[a830611]189 munmap(comm_area, comm_size);
[916bf1a]190 ipc_hangup(dev_phone);
191 return rc;
192 }
193
[7858bc5f]194 return EOK;
195}
196
[991f645]197void block_fini(devmap_handle_t devmap_handle)
[7858bc5f]198{
[991f645]199 devcon_t *devcon = devcon_search(devmap_handle);
[916bf1a]200 assert(devcon);
201
[64bc4b6]202 if (devcon->cache)
[991f645]203 (void) block_cache_fini(devmap_handle);
[64bc4b6]204
[916bf1a]205 devcon_remove(devcon);
206
[6284978]207 if (devcon->bb_buf)
208 free(devcon->bb_buf);
[f1ba5d6]209
[a830611]210 munmap(devcon->comm_area, devcon->comm_size);
[916bf1a]211 ipc_hangup(devcon->dev_phone);
212
213 free(devcon);
[7858bc5f]214}
215
[991f645]216int block_bb_read(devmap_handle_t devmap_handle, aoff64_t ba)
[6284978]217{
218 void *bb_buf;
[0c243b4]219 int rc;
[6284978]220
[991f645]221 devcon_t *devcon = devcon_search(devmap_handle);
[6284978]222 if (!devcon)
223 return ENOENT;
224 if (devcon->bb_buf)
225 return EEXIST;
[1ee00b7]226 bb_buf = malloc(devcon->pblock_size);
[6284978]227 if (!bb_buf)
228 return ENOMEM;
[1ee00b7]229
[a830611]230 fibril_mutex_lock(&devcon->comm_area_lock);
[1ee00b7]231 rc = read_blocks(devcon, 0, 1);
[0c243b4]232 if (rc != EOK) {
[a830611]233 fibril_mutex_unlock(&devcon->comm_area_lock);
[6284978]234 free(bb_buf);
[0c243b4]235 return rc;
[6284978]236 }
[a830611]237 memcpy(bb_buf, devcon->comm_area, devcon->pblock_size);
238 fibril_mutex_unlock(&devcon->comm_area_lock);
[6408be3]239
[6284978]240 devcon->bb_buf = bb_buf;
[1ee00b7]241 devcon->bb_addr = ba;
[6284978]242
243 return EOK;
244}
245
[991f645]246void *block_bb_get(devmap_handle_t devmap_handle)
[7858bc5f]247{
[991f645]248 devcon_t *devcon = devcon_search(devmap_handle);
[916bf1a]249 assert(devcon);
250 return devcon->bb_buf;
[7858bc5f]251}
252
[f1ba5d6]253static hash_index_t cache_hash(unsigned long *key)
254{
255 return *key & (CACHE_BUCKETS - 1);
256}
257
258static int cache_compare(unsigned long *key, hash_count_t keys, link_t *item)
259{
260 block_t *b = hash_table_get_instance(item, block_t, hash_link);
261 return b->boff == *key;
262}
263
264static void cache_remove_callback(link_t *item)
265{
266}
267
268static hash_table_operations_t cache_ops = {
269 .hash = cache_hash,
270 .compare = cache_compare,
271 .remove_callback = cache_remove_callback
272};
273
[991f645]274int block_cache_init(devmap_handle_t devmap_handle, size_t size, unsigned blocks,
[1fbe064b]275 enum cache_mode mode)
[f1ba5d6]276{
[991f645]277 devcon_t *devcon = devcon_search(devmap_handle);
[f1ba5d6]278 cache_t *cache;
279 if (!devcon)
280 return ENOENT;
281 if (devcon->cache)
282 return EEXIST;
283 cache = malloc(sizeof(cache_t));
284 if (!cache)
285 return ENOMEM;
286
[4e1b57d]287 fibril_mutex_initialize(&cache->lock);
[f1ba5d6]288 list_initialize(&cache->free_head);
[1ee00b7]289 cache->lblock_size = size;
[f1ba5d6]290 cache->block_count = blocks;
[d68e4d5]291 cache->blocks_cached = 0;
[1fbe064b]292 cache->mode = mode;
[f1ba5d6]293
[1ee00b7]294 /* No block size translation a.t.m. */
295 assert(cache->lblock_size == devcon->pblock_size);
296
[f1ba5d6]297 if (!hash_table_create(&cache->block_hash, CACHE_BUCKETS, 1,
298 &cache_ops)) {
299 free(cache);
300 return ENOMEM;
301 }
302
303 devcon->cache = cache;
304 return EOK;
305}
306
[991f645]307int block_cache_fini(devmap_handle_t devmap_handle)
[64bc4b6]308{
[991f645]309 devcon_t *devcon = devcon_search(devmap_handle);
[64bc4b6]310 cache_t *cache;
311 int rc;
312
313 if (!devcon)
314 return ENOENT;
315 if (!devcon->cache)
316 return EOK;
317 cache = devcon->cache;
318
319 /*
320 * We are expecting to find all blocks for this device handle on the
321 * free list, i.e. the block reference count should be zero. Do not
322 * bother with the cache and block locks because we are single-threaded.
323 */
324 while (!list_empty(&cache->free_head)) {
325 block_t *b = list_get_instance(cache->free_head.next,
326 block_t, free_link);
327
328 list_remove(&b->free_link);
329 if (b->dirty) {
330 memcpy(devcon->comm_area, b->data, b->size);
331 rc = write_blocks(devcon, b->boff, 1);
332 if (rc != EOK)
333 return rc;
334 }
335
[ed903174]336 unsigned long key = b->boff;
[64bc4b6]337 hash_table_remove(&cache->block_hash, &key, 1);
338
339 free(b->data);
340 free(b);
341 }
342
343 hash_table_destroy(&cache->block_hash);
344 devcon->cache = NULL;
345 free(cache);
346
347 return EOK;
348}
349
[d68e4d5]350#define CACHE_LO_WATERMARK 10
351#define CACHE_HI_WATERMARK 20
[e1c88d5]352static bool cache_can_grow(cache_t *cache)
[fc840d9]353{
[d68e4d5]354 if (cache->blocks_cached < CACHE_LO_WATERMARK)
355 return true;
356 if (!list_empty(&cache->free_head))
357 return false;
[e1c88d5]358 return true;
359}
360
361static void block_initialize(block_t *b)
362{
[4e1b57d]363 fibril_mutex_initialize(&b->lock);
[e1c88d5]364 b->refcnt = 1;
365 b->dirty = false;
[cd688d9]366 b->toxic = false;
[4e1b57d]367 fibril_rwlock_initialize(&b->contents_lock);
[e1c88d5]368 link_initialize(&b->free_link);
369 link_initialize(&b->hash_link);
370}
371
372/** Instantiate a block in memory and get a reference to it.
373 *
[c91f2d1b]374 * @param block Pointer to where the function will store the
375 * block pointer on success.
[991f645]376 * @param devmap_handle Device handle of the block device.
[e1c88d5]377 * @param boff Block offset.
[1d8cdb1]378 * @param flags If BLOCK_FLAGS_NOREAD is specified, block_get()
379 * will not read the contents of the block from the
380 * device.
[e1c88d5]381 *
[c91f2d1b]382 * @return EOK on success or a negative error code.
[e1c88d5]383 */
[991f645]384int block_get(block_t **block, devmap_handle_t devmap_handle, aoff64_t boff, int flags)
[e1c88d5]385{
386 devcon_t *devcon;
387 cache_t *cache;
[fc840d9]388 block_t *b;
[e1c88d5]389 link_t *l;
390 unsigned long key = boff;
[b7b3fda]391 int rc;
[e1c88d5]392
[991f645]393 devcon = devcon_search(devmap_handle);
[fc840d9]394
[e1c88d5]395 assert(devcon);
396 assert(devcon->cache);
[fc840d9]397
[e1c88d5]398 cache = devcon->cache;
[02ee6bf5]399
400retry:
[b7b3fda]401 rc = EOK;
[4f690cd]402 b = NULL;
[b7b3fda]403
[4e1b57d]404 fibril_mutex_lock(&cache->lock);
[e1c88d5]405 l = hash_table_find(&cache->block_hash, &key);
406 if (l) {
407 /*
408 * We found the block in the cache.
409 */
410 b = hash_table_get_instance(l, block_t, hash_link);
[4e1b57d]411 fibril_mutex_lock(&b->lock);
[e1c88d5]412 if (b->refcnt++ == 0)
413 list_remove(&b->free_link);
[402a18f]414 if (b->toxic)
415 rc = EIO;
[4e1b57d]416 fibril_mutex_unlock(&b->lock);
417 fibril_mutex_unlock(&cache->lock);
[e1c88d5]418 } else {
419 /*
420 * The block was not found in the cache.
421 */
422 if (cache_can_grow(cache)) {
423 /*
424 * We can grow the cache by allocating new blocks.
425 * Should the allocation fail, we fail over and try to
426 * recycle a block from the cache.
427 */
428 b = malloc(sizeof(block_t));
429 if (!b)
430 goto recycle;
[1ee00b7]431 b->data = malloc(cache->lblock_size);
[e1c88d5]432 if (!b->data) {
433 free(b);
434 goto recycle;
435 }
[d68e4d5]436 cache->blocks_cached++;
[e1c88d5]437 } else {
438 /*
439 * Try to recycle a block from the free list.
440 */
441 unsigned long temp_key;
442recycle:
[7a56b1ed]443 if (list_empty(&cache->free_head)) {
444 fibril_mutex_unlock(&cache->lock);
445 rc = ENOMEM;
446 goto out;
447 }
[e1c88d5]448 l = cache->free_head.next;
[d68e4d5]449 b = list_get_instance(l, block_t, free_link);
[02ee6bf5]450
451 fibril_mutex_lock(&b->lock);
452 if (b->dirty) {
453 /*
454 * The block needs to be written back to the
455 * device before it changes identity. Do this
456 * while not holding the cache lock so that
457 * concurrency is not impeded. Also move the
458 * block to the end of the free list so that we
459 * do not slow down other instances of
460 * block_get() draining the free list.
461 */
462 list_remove(&b->free_link);
463 list_append(&b->free_link, &cache->free_head);
464 fibril_mutex_unlock(&cache->lock);
[a830611]465 fibril_mutex_lock(&devcon->comm_area_lock);
466 memcpy(devcon->comm_area, b->data, b->size);
[1ee00b7]467 rc = write_blocks(devcon, b->boff, 1);
[a830611]468 fibril_mutex_unlock(&devcon->comm_area_lock);
[402a18f]469 if (rc != EOK) {
470 /*
471 * We did not manage to write the block
472 * to the device. Keep it around for
473 * another try. Hopefully, we will grab
474 * another block next time.
475 */
476 fibril_mutex_unlock(&b->lock);
477 goto retry;
478 }
[02ee6bf5]479 b->dirty = false;
480 if (!fibril_mutex_trylock(&cache->lock)) {
481 /*
482 * Somebody is probably racing with us.
483 * Unlock the block and retry.
484 */
485 fibril_mutex_unlock(&b->lock);
486 goto retry;
487 }
488
489 }
490 fibril_mutex_unlock(&b->lock);
491
492 /*
493 * Unlink the block from the free list and the hash
494 * table.
495 */
496 list_remove(&b->free_link);
[e1c88d5]497 temp_key = b->boff;
498 hash_table_remove(&cache->block_hash, &temp_key, 1);
499 }
[fc840d9]500
[e1c88d5]501 block_initialize(b);
[991f645]502 b->devmap_handle = devmap_handle;
[1ee00b7]503 b->size = cache->lblock_size;
[e1c88d5]504 b->boff = boff;
[a6d97fb9]505 hash_table_insert(&cache->block_hash, &key, &b->hash_link);
506
507 /*
508 * Lock the block before releasing the cache lock. Thus we don't
[5ac8918]509 * kill concurrent operations on the cache while doing I/O on
510 * the block.
[a6d97fb9]511 */
[4e1b57d]512 fibril_mutex_lock(&b->lock);
513 fibril_mutex_unlock(&cache->lock);
[a6d97fb9]514
[1d8cdb1]515 if (!(flags & BLOCK_FLAGS_NOREAD)) {
516 /*
517 * The block contains old or no data. We need to read
518 * the new contents from the device.
519 */
[a830611]520 fibril_mutex_lock(&devcon->comm_area_lock);
[1ee00b7]521 rc = read_blocks(devcon, b->boff, 1);
[a830611]522 memcpy(b->data, devcon->comm_area, cache->lblock_size);
523 fibril_mutex_unlock(&devcon->comm_area_lock);
[402a18f]524 if (rc != EOK)
525 b->toxic = true;
526 } else
527 rc = EOK;
[fc840d9]528
[4e1b57d]529 fibril_mutex_unlock(&b->lock);
[a6d97fb9]530 }
[7a56b1ed]531out:
[4f690cd]532 if ((rc != EOK) && b) {
533 assert(b->toxic);
534 (void) block_put(b);
535 b = NULL;
536 }
[c91f2d1b]537 *block = b;
[402a18f]538 return rc;
[fc840d9]539}
540
[d5a720cf]541/** Release a reference to a block.
542 *
[a6d97fb9]543 * If the last reference is dropped, the block is put on the free list.
[d5a720cf]544 *
545 * @param block Block of which a reference is to be released.
[c91f2d1b]546 *
547 * @return EOK on success or a negative error code.
[d5a720cf]548 */
[c91f2d1b]549int block_put(block_t *block)
[fc840d9]550{
[991f645]551 devcon_t *devcon = devcon_search(block->devmap_handle);
[d5a720cf]552 cache_t *cache;
[ddfc39a3]553 unsigned blocks_cached;
554 enum cache_mode mode;
[402a18f]555 int rc = EOK;
[d5a720cf]556
557 assert(devcon);
558 assert(devcon->cache);
559
560 cache = devcon->cache;
[ddfc39a3]561
562retry:
563 fibril_mutex_lock(&cache->lock);
564 blocks_cached = cache->blocks_cached;
565 mode = cache->mode;
566 fibril_mutex_unlock(&cache->lock);
567
568 /*
569 * Determine whether to sync the block. Syncing the block is best done
570 * when not holding the cache lock as it does not impede concurrency.
571 * Since the situation may have changed when we unlocked the cache, the
572 * blocks_cached and mode variables are mere hints. We will recheck the
573 * conditions later when the cache lock is held again.
574 */
575 fibril_mutex_lock(&block->lock);
[402a18f]576 if (block->toxic)
577 block->dirty = false; /* will not write back toxic block */
[ddfc39a3]578 if (block->dirty && (block->refcnt == 1) &&
579 (blocks_cached > CACHE_HI_WATERMARK || mode != CACHE_MODE_WB)) {
[a830611]580 fibril_mutex_lock(&devcon->comm_area_lock);
581 memcpy(devcon->comm_area, block->data, block->size);
[1ee00b7]582 rc = write_blocks(devcon, block->boff, 1);
[a830611]583 fibril_mutex_unlock(&devcon->comm_area_lock);
[ddfc39a3]584 block->dirty = false;
585 }
586 fibril_mutex_unlock(&block->lock);
587
[4e1b57d]588 fibril_mutex_lock(&cache->lock);
589 fibril_mutex_lock(&block->lock);
[d5a720cf]590 if (!--block->refcnt) {
591 /*
[d68e4d5]592 * Last reference to the block was dropped. Either free the
[402a18f]593 * block or put it on the free list. In case of an I/O error,
594 * free the block.
[d68e4d5]595 */
[402a18f]596 if ((cache->blocks_cached > CACHE_HI_WATERMARK) ||
597 (rc != EOK)) {
[d68e4d5]598 /*
[402a18f]599 * Currently there are too many cached blocks or there
600 * was an I/O error when writing the block back to the
601 * device.
[d68e4d5]602 */
603 if (block->dirty) {
[ddfc39a3]604 /*
605 * We cannot sync the block while holding the
606 * cache lock. Release everything and retry.
607 */
608 block->refcnt++;
609 fibril_mutex_unlock(&block->lock);
610 fibril_mutex_unlock(&cache->lock);
611 goto retry;
[d68e4d5]612 }
613 /*
614 * Take the block out of the cache and free it.
615 */
616 unsigned long key = block->boff;
617 hash_table_remove(&cache->block_hash, &key, 1);
618 free(block);
619 free(block->data);
620 cache->blocks_cached--;
621 fibril_mutex_unlock(&cache->lock);
[402a18f]622 return rc;
[d68e4d5]623 }
624 /*
625 * Put the block on the free list.
[d5a720cf]626 */
[1fbe064b]627 if (cache->mode != CACHE_MODE_WB && block->dirty) {
[ddfc39a3]628 /*
629 * We cannot sync the block while holding the cache
630 * lock. Release everything and retry.
631 */
632 block->refcnt++;
633 fibril_mutex_unlock(&block->lock);
634 fibril_mutex_unlock(&cache->lock);
635 goto retry;
[1fbe064b]636 }
[ddfc39a3]637 list_append(&block->free_link, &cache->free_head);
[d5a720cf]638 }
[4e1b57d]639 fibril_mutex_unlock(&block->lock);
640 fibril_mutex_unlock(&cache->lock);
[c91f2d1b]641
[402a18f]642 return rc;
[d5a720cf]643}
644
[6408be3]645/** Read sequential data from a block device.
[d5a720cf]646 *
[991f645]647 * @param devmap_handle Device handle of the block device.
[d5a720cf]648 * @param bufpos Pointer to the first unread valid offset within the
649 * communication buffer.
650 * @param buflen Pointer to the number of unread bytes that are ready in
651 * the communication buffer.
652 * @param pos Device position to be read.
653 * @param dst Destination buffer.
654 * @param size Size of the destination buffer.
655 * @param block_size Block size to be used for the transfer.
656 *
657 * @return EOK on success or a negative return code on failure.
658 */
[991f645]659int block_seqread(devmap_handle_t devmap_handle, size_t *bufpos, size_t *buflen,
[ed903174]660 aoff64_t *pos, void *dst, size_t size)
[d5a720cf]661{
[ed903174]662 size_t offset = 0;
[d5a720cf]663 size_t left = size;
[1ee00b7]664 size_t block_size;
665 devcon_t *devcon;
666
[991f645]667 devcon = devcon_search(devmap_handle);
[d5a720cf]668 assert(devcon);
[1ee00b7]669 block_size = devcon->pblock_size;
[e1c88d5]670
[a830611]671 fibril_mutex_lock(&devcon->comm_area_lock);
[d5a720cf]672 while (left > 0) {
673 size_t rd;
674
675 if (*bufpos + left < *buflen)
676 rd = left;
677 else
678 rd = *buflen - *bufpos;
679
680 if (rd > 0) {
681 /*
682 * Copy the contents of the communication buffer to the
683 * destination buffer.
684 */
[a830611]685 memcpy(dst + offset, devcon->comm_area + *bufpos, rd);
[d5a720cf]686 offset += rd;
687 *bufpos += rd;
688 *pos += rd;
689 left -= rd;
690 }
691
[ed903174]692 if (*bufpos == *buflen) {
[d5a720cf]693 /* Refill the communication buffer with a new block. */
[6408be3]694 int rc;
695
[1ee00b7]696 rc = read_blocks(devcon, *pos / block_size, 1);
[d68e4d5]697 if (rc != EOK) {
[a830611]698 fibril_mutex_unlock(&devcon->comm_area_lock);
[6408be3]699 return rc;
[d68e4d5]700 }
[d5a720cf]701
702 *bufpos = 0;
703 *buflen = block_size;
704 }
705 }
[a830611]706 fibril_mutex_unlock(&devcon->comm_area_lock);
[d5a720cf]707
708 return EOK;
[fc840d9]709}
710
[00b1d20e]711/** Read blocks directly from device (bypass cache).
712 *
[991f645]713 * @param devmap_handle Device handle of the block device.
[00b1d20e]714 * @param ba Address of first block.
715 * @param cnt Number of blocks.
716 * @param src Buffer for storing the data.
717 *
718 * @return EOK on success or negative error code on failure.
719 */
[991f645]720int block_read_direct(devmap_handle_t devmap_handle, aoff64_t ba, size_t cnt, void *buf)
[00b1d20e]721{
722 devcon_t *devcon;
723 int rc;
724
[991f645]725 devcon = devcon_search(devmap_handle);
[00b1d20e]726 assert(devcon);
727
728 fibril_mutex_lock(&devcon->comm_area_lock);
729
730 rc = read_blocks(devcon, ba, cnt);
731 if (rc == EOK)
732 memcpy(buf, devcon->comm_area, devcon->pblock_size * cnt);
733
734 fibril_mutex_unlock(&devcon->comm_area_lock);
735
736 return rc;
737}
738
739/** Write blocks directly to device (bypass cache).
740 *
[991f645]741 * @param devmap_handle Device handle of the block device.
[00b1d20e]742 * @param ba Address of first block.
743 * @param cnt Number of blocks.
744 * @param src The data to be written.
745 *
746 * @return EOK on success or negative error code on failure.
747 */
[991f645]748int block_write_direct(devmap_handle_t devmap_handle, aoff64_t ba, size_t cnt,
[00b1d20e]749 const void *data)
750{
751 devcon_t *devcon;
752 int rc;
753
[991f645]754 devcon = devcon_search(devmap_handle);
[00b1d20e]755 assert(devcon);
756
757 fibril_mutex_lock(&devcon->comm_area_lock);
758
759 memcpy(devcon->comm_area, data, devcon->pblock_size * cnt);
[dccf721]760 rc = write_blocks(devcon, ba, cnt);
[00b1d20e]761
762 fibril_mutex_unlock(&devcon->comm_area_lock);
763
764 return rc;
765}
766
767/** Get device block size.
768 *
[991f645]769 * @param devmap_handle Device handle of the block device.
[00b1d20e]770 * @param bsize Output block size.
771 *
772 * @return EOK on success or negative error code on failure.
773 */
[991f645]774int block_get_bsize(devmap_handle_t devmap_handle, size_t *bsize)
[00b1d20e]775{
776 devcon_t *devcon;
777
[991f645]778 devcon = devcon_search(devmap_handle);
[00b1d20e]779 assert(devcon);
780
781 return get_block_size(devcon->dev_phone, bsize);
782}
783
[08232ee]784/** Get number of blocks on device.
785 *
[991f645]786 * @param devmap_handle Device handle of the block device.
[08232ee]787 * @param nblocks Output number of blocks.
788 *
789 * @return EOK on success or negative error code on failure.
790 */
[991f645]791int block_get_nblocks(devmap_handle_t devmap_handle, aoff64_t *nblocks)
[08232ee]792{
793 devcon_t *devcon;
794
[991f645]795 devcon = devcon_search(devmap_handle);
[08232ee]796 assert(devcon);
797
798 return get_num_blocks(devcon->dev_phone, nblocks);
799}
800
[1ee00b7]801/** Read blocks from block device.
[6408be3]802 *
803 * @param devcon Device connection.
[1ee00b7]804 * @param ba Address of first block.
805 * @param cnt Number of blocks.
[6408be3]806 * @param src Buffer for storing the data.
807 *
808 * @return EOK on success or negative error code on failure.
809 */
[ed903174]810static int read_blocks(devcon_t *devcon, aoff64_t ba, size_t cnt)
[6408be3]811{
812 int rc;
813
814 assert(devcon);
[1ee00b7]815 rc = async_req_3_0(devcon->dev_phone, BD_READ_BLOCKS, LOWER32(ba),
816 UPPER32(ba), cnt);
[16fc3c9]817 if (rc != EOK) {
[7e752b2]818 printf("Error %d reading %zu blocks starting at block %" PRIuOFF64
819 " from device handle %" PRIun "\n", rc, cnt, ba,
[991f645]820 devcon->devmap_handle);
[16fc3c9]821#ifndef NDEBUG
822 stacktrace_print();
823#endif
824 }
[1ee00b7]825 return rc;
[6408be3]826}
827
[1fbe064b]828/** Write block to block device.
829 *
830 * @param devcon Device connection.
[1ee00b7]831 * @param ba Address of first block.
832 * @param cnt Number of blocks.
[1fbe064b]833 * @param src Buffer containing the data to write.
834 *
835 * @return EOK on success or negative error code on failure.
836 */
[ed903174]837static int write_blocks(devcon_t *devcon, aoff64_t ba, size_t cnt)
[1fbe064b]838{
839 int rc;
840
841 assert(devcon);
[1ee00b7]842 rc = async_req_3_0(devcon->dev_phone, BD_WRITE_BLOCKS, LOWER32(ba),
843 UPPER32(ba), cnt);
[16fc3c9]844 if (rc != EOK) {
[7e752b2]845 printf("Error %d writing %zu blocks starting at block %" PRIuOFF64
846 " to device handle %" PRIun "\n", rc, cnt, ba, devcon->devmap_handle);
[16fc3c9]847#ifndef NDEBUG
848 stacktrace_print();
849#endif
850 }
[1ee00b7]851 return rc;
852}
[1fbe064b]853
[1ee00b7]854/** Get block size used by the device. */
[00b1d20e]855static int get_block_size(int dev_phone, size_t *bsize)
[1ee00b7]856{
857 ipcarg_t bs;
858 int rc;
859
860 rc = async_req_0_1(dev_phone, BD_GET_BLOCK_SIZE, &bs);
861 if (rc == EOK)
862 *bsize = (size_t) bs;
863
864 return rc;
[1fbe064b]865}
866
[08232ee]867/** Get total number of blocks on block device. */
[ed903174]868static int get_num_blocks(int dev_phone, aoff64_t *nblocks)
[08232ee]869{
870 ipcarg_t nb_l, nb_h;
871 int rc;
872
873 rc = async_req_0_2(dev_phone, BD_GET_NUM_BLOCKS, &nb_l, &nb_h);
874 if (rc == EOK) {
[ed903174]875 *nblocks = (aoff64_t) MERGE_LOUP32(nb_l, nb_h);
[08232ee]876 }
877
878 return rc;
879}
880
[fc840d9]881/** @}
882 */
Note: See TracBrowser for help on using the repository browser.