source: mainline/uspace/lib/block/block.c@ 1e4a937

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1e4a937 was 5e801dc, checked in by GitHub <noreply@…>, 6 years ago

Indicate and enforce constness of hash table key in certain functions (#158)

The assumption here is that modifying key in the hash/equal functions in something completely unexpected, and not something you would ever want to do intentionally, so it makes sense to disallow it entirely to get that extra level of checking.

  • Property mode set to 100644
File size: 23.5 KB
RevLine 
[fc840d9]1/*
[ed903174]2 * Copyright (c) 2008 Jakub Jermar
3 * Copyright (c) 2008 Martin Decky
[e272949]4 * Copyright (c) 2011 Martin Sucha
[fc840d9]5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
[97c9da8]31/** @addtogroup libblock
[fc840d9]32 * @{
[97c9da8]33 */
[fc840d9]34/**
35 * @file
36 * @brief
37 */
38
[15f3c3f]39#include <ipc/loc.h>
[7858bc5f]40#include <ipc/services.h>
[fc840d9]41#include <errno.h>
42#include <async.h>
43#include <as.h>
44#include <assert.h>
[4802dd7]45#include <bd.h>
[1e4cada]46#include <fibril_synch.h>
[d9c8c81]47#include <adt/list.h>
48#include <adt/hash_table.h>
[1ee00b7]49#include <macros.h>
[d00ae4c]50#include <mem.h>
[38d150e]51#include <stdlib.h>
[c7bbf029]52#include <stdio.h>
[16fc3c9]53#include <stacktrace.h>
[c1694b6b]54#include <str_error.h>
[7354b5e]55#include <offset.h>
56#include <inttypes.h>
[f73b291]57#include "block.h"
[fc840d9]58
[3d35386]59#define MAX_WRITE_RETRIES 10
60
[916bf1a]61/** Lock protecting the device connection list */
[4e1b57d]62static FIBRIL_MUTEX_INITIALIZE(dcl_lock);
[916bf1a]63/** Device connection list head. */
[b72efe8]64static LIST_INITIALIZE(dcl);
[916bf1a]65
[f1ba5d6]66typedef struct {
[4e1b57d]67 fibril_mutex_t lock;
[79ae36dd]68 size_t lblock_size; /**< Logical block size. */
69 unsigned blocks_cluster; /**< Physical blocks per block_t */
70 unsigned block_count; /**< Total number of blocks. */
71 unsigned blocks_cached; /**< Number of cached blocks. */
[f1ba5d6]72 hash_table_t block_hash;
[b72efe8]73 list_t free_list;
[1fbe064b]74 enum cache_mode mode;
[f1ba5d6]75} cache_t;
76
[916bf1a]77typedef struct {
78 link_t link;
[15f3c3f]79 service_id_t service_id;
[79ae36dd]80 async_sess_t *sess;
[4802dd7]81 bd_t *bd;
[916bf1a]82 void *bb_buf;
[ed903174]83 aoff64_t bb_addr;
[3d35386]84 aoff64_t pblocks; /**< Number of physical blocks */
[79ae36dd]85 size_t pblock_size; /**< Physical block size. */
[f1ba5d6]86 cache_t *cache;
[916bf1a]87} devcon_t;
88
[b7fd2a0]89static errno_t read_blocks(devcon_t *, aoff64_t, size_t, void *, size_t);
90static errno_t write_blocks(devcon_t *, aoff64_t, size_t, void *, size_t);
[79ae36dd]91static aoff64_t ba_ltop(devcon_t *, aoff64_t);
[1fbe064b]92
[15f3c3f]93static devcon_t *devcon_search(service_id_t service_id)
[916bf1a]94{
[4e1b57d]95 fibril_mutex_lock(&dcl_lock);
[a35b458]96
[feeac0d]97 list_foreach(dcl, link, devcon_t, devcon) {
[15f3c3f]98 if (devcon->service_id == service_id) {
[4e1b57d]99 fibril_mutex_unlock(&dcl_lock);
[916bf1a]100 return devcon;
101 }
102 }
[a35b458]103
[4e1b57d]104 fibril_mutex_unlock(&dcl_lock);
[916bf1a]105 return NULL;
106}
107
[b7fd2a0]108static errno_t devcon_add(service_id_t service_id, async_sess_t *sess,
[3d35386]109 size_t bsize, aoff64_t dev_size, bd_t *bd)
[916bf1a]110{
111 devcon_t *devcon;
[a35b458]112
[916bf1a]113 devcon = malloc(sizeof(devcon_t));
114 if (!devcon)
115 return ENOMEM;
[a35b458]116
[916bf1a]117 link_initialize(&devcon->link);
[15f3c3f]118 devcon->service_id = service_id;
[79ae36dd]119 devcon->sess = sess;
[4802dd7]120 devcon->bd = bd;
[6284978]121 devcon->bb_buf = NULL;
[1ee00b7]122 devcon->bb_addr = 0;
123 devcon->pblock_size = bsize;
[3d35386]124 devcon->pblocks = dev_size;
[f1ba5d6]125 devcon->cache = NULL;
[a35b458]126
[4e1b57d]127 fibril_mutex_lock(&dcl_lock);
[feeac0d]128 list_foreach(dcl, link, devcon_t, d) {
[15f3c3f]129 if (d->service_id == service_id) {
[4e1b57d]130 fibril_mutex_unlock(&dcl_lock);
[916bf1a]131 free(devcon);
132 return EEXIST;
133 }
134 }
[b72efe8]135 list_append(&devcon->link, &dcl);
[4e1b57d]136 fibril_mutex_unlock(&dcl_lock);
[916bf1a]137 return EOK;
138}
139
140static void devcon_remove(devcon_t *devcon)
141{
[4e1b57d]142 fibril_mutex_lock(&dcl_lock);
[916bf1a]143 list_remove(&devcon->link);
[4e1b57d]144 fibril_mutex_unlock(&dcl_lock);
[916bf1a]145}
[7858bc5f]146
[b7fd2a0]147errno_t block_init(service_id_t service_id, size_t comm_size)
[7858bc5f]148{
[4802dd7]149 bd_t *bd;
150
[f9b2cb4c]151 async_sess_t *sess = loc_service_connect(service_id, INTERFACE_BLOCK,
[79ae36dd]152 IPC_FLAG_BLOCKING);
153 if (!sess) {
154 return ENOENT;
[7858bc5f]155 }
[a35b458]156
[b7fd2a0]157 errno_t rc = bd_open(sess, &bd);
[7858bc5f]158 if (rc != EOK) {
[79ae36dd]159 async_hangup(sess);
[7858bc5f]160 return rc;
161 }
[a35b458]162
[79ae36dd]163 size_t bsize;
[4802dd7]164 rc = bd_get_block_size(bd, &bsize);
[79ae36dd]165 if (rc != EOK) {
[4802dd7]166 bd_close(bd);
[79ae36dd]167 async_hangup(sess);
[1ee00b7]168 return rc;
169 }
[3d35386]170
171 aoff64_t dev_size;
172 rc = bd_get_num_blocks(bd, &dev_size);
173 if (rc != EOK) {
174 bd_close(bd);
175 async_hangup(sess);
176 return rc;
177 }
[a35b458]178
[3d35386]179 rc = devcon_add(service_id, sess, bsize, dev_size, bd);
[916bf1a]180 if (rc != EOK) {
[4802dd7]181 bd_close(bd);
[79ae36dd]182 async_hangup(sess);
[916bf1a]183 return rc;
184 }
[a35b458]185
[7858bc5f]186 return EOK;
187}
188
[15f3c3f]189void block_fini(service_id_t service_id)
[7858bc5f]190{
[15f3c3f]191 devcon_t *devcon = devcon_search(service_id);
[916bf1a]192 assert(devcon);
[a35b458]193
[64bc4b6]194 if (devcon->cache)
[15f3c3f]195 (void) block_cache_fini(service_id);
[a35b458]196
[dd8b6a8]197 (void)bd_sync_cache(devcon->bd, 0, 0);
[a35b458]198
[916bf1a]199 devcon_remove(devcon);
[a35b458]200
[6284978]201 if (devcon->bb_buf)
202 free(devcon->bb_buf);
[a35b458]203
[4802dd7]204 bd_close(devcon->bd);
[79ae36dd]205 async_hangup(devcon->sess);
[a35b458]206
[79ae36dd]207 free(devcon);
[7858bc5f]208}
209
[b7fd2a0]210errno_t block_bb_read(service_id_t service_id, aoff64_t ba)
[6284978]211{
212 void *bb_buf;
[b7fd2a0]213 errno_t rc;
[6284978]214
[15f3c3f]215 devcon_t *devcon = devcon_search(service_id);
[6284978]216 if (!devcon)
217 return ENOENT;
218 if (devcon->bb_buf)
219 return EEXIST;
[1ee00b7]220 bb_buf = malloc(devcon->pblock_size);
[6284978]221 if (!bb_buf)
222 return ENOMEM;
[1ee00b7]223
[4802dd7]224 rc = read_blocks(devcon, 0, 1, bb_buf, devcon->pblock_size);
[0c243b4]225 if (rc != EOK) {
[1433ecda]226 free(bb_buf);
[0c243b4]227 return rc;
[6284978]228 }
[6408be3]229
[6284978]230 devcon->bb_buf = bb_buf;
[1ee00b7]231 devcon->bb_addr = ba;
[6284978]232
233 return EOK;
234}
235
[15f3c3f]236void *block_bb_get(service_id_t service_id)
[7858bc5f]237{
[15f3c3f]238 devcon_t *devcon = devcon_search(service_id);
[916bf1a]239 assert(devcon);
240 return devcon->bb_buf;
[7858bc5f]241}
242
[5e801dc]243static size_t cache_key_hash(const void *key)
[f1ba5d6]244{
[5e801dc]245 const aoff64_t *lba = key;
[062d900]246 return *lba;
[f1ba5d6]247}
248
[062d900]249static size_t cache_hash(const ht_link_t *item)
[f1ba5d6]250{
[062d900]251 block_t *b = hash_table_get_inst(item, block_t, hash_link);
252 return b->lba;
[f1ba5d6]253}
254
[5e801dc]255static bool cache_key_equal(const void *key, const ht_link_t *item)
[f1ba5d6]256{
[5e801dc]257 const aoff64_t *lba = key;
[062d900]258 block_t *b = hash_table_get_inst(item, block_t, hash_link);
259 return b->lba == *lba;
[f1ba5d6]260}
261
[062d900]262static hash_table_ops_t cache_ops = {
[f1ba5d6]263 .hash = cache_hash,
[062d900]264 .key_hash = cache_key_hash,
265 .key_equal = cache_key_equal,
[4e00f87]266 .equal = NULL,
267 .remove_callback = NULL
[f1ba5d6]268};
269
[b7fd2a0]270errno_t block_cache_init(service_id_t service_id, size_t size, unsigned blocks,
[1fbe064b]271 enum cache_mode mode)
[f1ba5d6]272{
[15f3c3f]273 devcon_t *devcon = devcon_search(service_id);
[f1ba5d6]274 cache_t *cache;
275 if (!devcon)
276 return ENOENT;
277 if (devcon->cache)
278 return EEXIST;
279 cache = malloc(sizeof(cache_t));
280 if (!cache)
281 return ENOMEM;
[a35b458]282
[4e1b57d]283 fibril_mutex_initialize(&cache->lock);
[b72efe8]284 list_initialize(&cache->free_list);
[1ee00b7]285 cache->lblock_size = size;
[f1ba5d6]286 cache->block_count = blocks;
[d68e4d5]287 cache->blocks_cached = 0;
[1fbe064b]288 cache->mode = mode;
[f1ba5d6]289
[f092718]290 /* Allow 1:1 or small-to-large block size translation */
[37cf3792]291 if (cache->lblock_size % devcon->pblock_size != 0) {
292 free(cache);
[f092718]293 return ENOTSUP;
[37cf3792]294 }
[f092718]295
296 cache->blocks_cluster = cache->lblock_size / devcon->pblock_size;
[1ee00b7]297
[062d900]298 if (!hash_table_create(&cache->block_hash, 0, 0, &cache_ops)) {
[f1ba5d6]299 free(cache);
300 return ENOMEM;
301 }
302
303 devcon->cache = cache;
304 return EOK;
305}
306
[b7fd2a0]307errno_t block_cache_fini(service_id_t service_id)
[64bc4b6]308{
[15f3c3f]309 devcon_t *devcon = devcon_search(service_id);
[64bc4b6]310 cache_t *cache;
[b7fd2a0]311 errno_t rc;
[64bc4b6]312
313 if (!devcon)
314 return ENOENT;
315 if (!devcon->cache)
316 return EOK;
317 cache = devcon->cache;
[a35b458]318
[64bc4b6]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 */
[b72efe8]324 while (!list_empty(&cache->free_list)) {
325 block_t *b = list_get_instance(list_first(&cache->free_list),
[64bc4b6]326 block_t, free_link);
327
328 list_remove(&b->free_link);
329 if (b->dirty) {
[4802dd7]330 rc = write_blocks(devcon, b->pba, cache->blocks_cluster,
331 b->data, b->size);
[64bc4b6]332 if (rc != EOK)
333 return rc;
334 }
335
[062d900]336 hash_table_remove_item(&cache->block_hash, &b->hash_link);
[a35b458]337
[64bc4b6]338 free(b->data);
339 free(b);
340 }
341
342 hash_table_destroy(&cache->block_hash);
343 devcon->cache = NULL;
344 free(cache);
345
346 return EOK;
347}
348
[1b20da0]349#define CACHE_LO_WATERMARK 10
350#define CACHE_HI_WATERMARK 20
[e1c88d5]351static bool cache_can_grow(cache_t *cache)
[fc840d9]352{
[d68e4d5]353 if (cache->blocks_cached < CACHE_LO_WATERMARK)
354 return true;
[b72efe8]355 if (!list_empty(&cache->free_list))
[d68e4d5]356 return false;
[e1c88d5]357 return true;
358}
359
360static void block_initialize(block_t *b)
361{
[4e1b57d]362 fibril_mutex_initialize(&b->lock);
[e1c88d5]363 b->refcnt = 1;
[3d35386]364 b->write_failures = 0;
[e1c88d5]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}
370
371/** Instantiate a block in memory and get a reference to it.
372 *
[c91f2d1b]373 * @param block Pointer to where the function will store the
374 * block pointer on success.
[15f3c3f]375 * @param service_id Service ID of the block device.
[a6ba0c9]376 * @param ba Block address (logical).
[1d8cdb1]377 * @param flags If BLOCK_FLAGS_NOREAD is specified, block_get()
378 * will not read the contents of the block from the
379 * device.
[e1c88d5]380 *
[cde999a]381 * @return EOK on success or an error code.
[e1c88d5]382 */
[b7fd2a0]383errno_t block_get(block_t **block, service_id_t service_id, aoff64_t ba, int flags)
[e1c88d5]384{
385 devcon_t *devcon;
386 cache_t *cache;
[fc840d9]387 block_t *b;
[062d900]388 link_t *link;
[3d35386]389 aoff64_t p_ba;
[b7fd2a0]390 errno_t rc;
[a35b458]391
[15f3c3f]392 devcon = devcon_search(service_id);
[fc840d9]393
[e1c88d5]394 assert(devcon);
395 assert(devcon->cache);
[a35b458]396
[e1c88d5]397 cache = devcon->cache;
[02ee6bf5]398
[7c3fb9b]399 /*
400 * Check whether the logical block (or part of it) is beyond
[3d35386]401 * the end of the device or not.
402 */
403 p_ba = ba_ltop(devcon, ba);
404 p_ba += cache->blocks_cluster;
405 if (p_ba >= devcon->pblocks) {
406 /* This request cannot be satisfied */
407 return EIO;
408 }
409
[02ee6bf5]410retry:
[b7b3fda]411 rc = EOK;
[4f690cd]412 b = NULL;
[b7b3fda]413
[4e1b57d]414 fibril_mutex_lock(&cache->lock);
[062d900]415 ht_link_t *hlink = hash_table_find(&cache->block_hash, &ba);
416 if (hlink) {
[1433ecda]417 found:
[e1c88d5]418 /*
419 * We found the block in the cache.
420 */
[062d900]421 b = hash_table_get_inst(hlink, block_t, hash_link);
[4e1b57d]422 fibril_mutex_lock(&b->lock);
[e1c88d5]423 if (b->refcnt++ == 0)
424 list_remove(&b->free_link);
[402a18f]425 if (b->toxic)
426 rc = EIO;
[4e1b57d]427 fibril_mutex_unlock(&b->lock);
428 fibril_mutex_unlock(&cache->lock);
[e1c88d5]429 } else {
430 /*
431 * The block was not found in the cache.
432 */
433 if (cache_can_grow(cache)) {
434 /*
435 * We can grow the cache by allocating new blocks.
436 * Should the allocation fail, we fail over and try to
437 * recycle a block from the cache.
438 */
439 b = malloc(sizeof(block_t));
440 if (!b)
441 goto recycle;
[1ee00b7]442 b->data = malloc(cache->lblock_size);
[e1c88d5]443 if (!b->data) {
444 free(b);
[0dfaa099]445 b = NULL;
[e1c88d5]446 goto recycle;
447 }
[d68e4d5]448 cache->blocks_cached++;
[e1c88d5]449 } else {
450 /*
451 * Try to recycle a block from the free list.
452 */
[1433ecda]453 recycle:
[b72efe8]454 if (list_empty(&cache->free_list)) {
[7a56b1ed]455 fibril_mutex_unlock(&cache->lock);
456 rc = ENOMEM;
457 goto out;
458 }
[062d900]459 link = list_first(&cache->free_list);
460 b = list_get_instance(link, block_t, free_link);
[02ee6bf5]461
462 fibril_mutex_lock(&b->lock);
463 if (b->dirty) {
464 /*
465 * The block needs to be written back to the
466 * device before it changes identity. Do this
467 * while not holding the cache lock so that
468 * concurrency is not impeded. Also move the
469 * block to the end of the free list so that we
470 * do not slow down other instances of
471 * block_get() draining the free list.
472 */
473 list_remove(&b->free_link);
[b72efe8]474 list_append(&b->free_link, &cache->free_list);
[02ee6bf5]475 fibril_mutex_unlock(&cache->lock);
[f092718]476 rc = write_blocks(devcon, b->pba,
[4802dd7]477 cache->blocks_cluster, b->data, b->size);
[402a18f]478 if (rc != EOK) {
479 /*
480 * We did not manage to write the block
481 * to the device. Keep it around for
482 * another try. Hopefully, we will grab
483 * another block next time.
484 */
[3d35386]485 if (b->write_failures < MAX_WRITE_RETRIES) {
486 b->write_failures++;
487 fibril_mutex_unlock(&b->lock);
488 goto retry;
489 } else {
490 printf("Too many errors writing block %"
[1433ecda]491 PRIuOFF64 "from device handle %" PRIun "\n"
[3d35386]492 "SEVERE DATA LOSS POSSIBLE\n",
[1433ecda]493 b->lba, devcon->service_id);
[3d35386]494 }
495 } else
496 b->write_failures = 0;
497
[02ee6bf5]498 b->dirty = false;
499 if (!fibril_mutex_trylock(&cache->lock)) {
500 /*
501 * Somebody is probably racing with us.
502 * Unlock the block and retry.
503 */
504 fibril_mutex_unlock(&b->lock);
505 goto retry;
506 }
[062d900]507 hlink = hash_table_find(&cache->block_hash, &ba);
508 if (hlink) {
[5716e9a]509 /*
510 * Someone else must have already
511 * instantiated the block while we were
512 * not holding the cache lock.
513 * Leave the recycled block on the
514 * freelist and continue as if we
515 * found the block of interest during
516 * the first try.
517 */
518 fibril_mutex_unlock(&b->lock);
519 goto found;
520 }
[02ee6bf5]521
522 }
523 fibril_mutex_unlock(&b->lock);
524
525 /*
526 * Unlink the block from the free list and the hash
527 * table.
528 */
529 list_remove(&b->free_link);
[062d900]530 hash_table_remove_item(&cache->block_hash, &b->hash_link);
[e1c88d5]531 }
[fc840d9]532
[e1c88d5]533 block_initialize(b);
[15f3c3f]534 b->service_id = service_id;
[1ee00b7]535 b->size = cache->lblock_size;
[a6ba0c9]536 b->lba = ba;
537 b->pba = ba_ltop(devcon, b->lba);
[062d900]538 hash_table_insert(&cache->block_hash, &b->hash_link);
[a6d97fb9]539
540 /*
541 * Lock the block before releasing the cache lock. Thus we don't
[5ac8918]542 * kill concurrent operations on the cache while doing I/O on
543 * the block.
[a6d97fb9]544 */
[4e1b57d]545 fibril_mutex_lock(&b->lock);
546 fibril_mutex_unlock(&cache->lock);
[a6d97fb9]547
[1d8cdb1]548 if (!(flags & BLOCK_FLAGS_NOREAD)) {
549 /*
550 * The block contains old or no data. We need to read
551 * the new contents from the device.
552 */
[4802dd7]553 rc = read_blocks(devcon, b->pba, cache->blocks_cluster,
554 b->data, cache->lblock_size);
[1b20da0]555 if (rc != EOK)
[402a18f]556 b->toxic = true;
557 } else
558 rc = EOK;
[fc840d9]559
[4e1b57d]560 fibril_mutex_unlock(&b->lock);
[a6d97fb9]561 }
[7a56b1ed]562out:
[4f690cd]563 if ((rc != EOK) && b) {
564 assert(b->toxic);
565 (void) block_put(b);
566 b = NULL;
567 }
[c91f2d1b]568 *block = b;
[402a18f]569 return rc;
[fc840d9]570}
571
[d5a720cf]572/** Release a reference to a block.
573 *
[a6d97fb9]574 * If the last reference is dropped, the block is put on the free list.
[d5a720cf]575 *
576 * @param block Block of which a reference is to be released.
[c91f2d1b]577 *
[cde999a]578 * @return EOK on success or an error code.
[d5a720cf]579 */
[b7fd2a0]580errno_t block_put(block_t *block)
[fc840d9]581{
[15f3c3f]582 devcon_t *devcon = devcon_search(block->service_id);
[d5a720cf]583 cache_t *cache;
[ddfc39a3]584 unsigned blocks_cached;
585 enum cache_mode mode;
[b7fd2a0]586 errno_t rc = EOK;
[d5a720cf]587
588 assert(devcon);
589 assert(devcon->cache);
[0f1cf7a]590 assert(block->refcnt >= 1);
[d5a720cf]591
592 cache = devcon->cache;
[ddfc39a3]593
594retry:
595 fibril_mutex_lock(&cache->lock);
596 blocks_cached = cache->blocks_cached;
597 mode = cache->mode;
598 fibril_mutex_unlock(&cache->lock);
599
600 /*
601 * Determine whether to sync the block. Syncing the block is best done
602 * when not holding the cache lock as it does not impede concurrency.
603 * Since the situation may have changed when we unlocked the cache, the
604 * blocks_cached and mode variables are mere hints. We will recheck the
605 * conditions later when the cache lock is held again.
606 */
607 fibril_mutex_lock(&block->lock);
[402a18f]608 if (block->toxic)
609 block->dirty = false; /* will not write back toxic block */
[ddfc39a3]610 if (block->dirty && (block->refcnt == 1) &&
611 (blocks_cached > CACHE_HI_WATERMARK || mode != CACHE_MODE_WB)) {
[4802dd7]612 rc = write_blocks(devcon, block->pba, cache->blocks_cluster,
613 block->data, block->size);
[3d35386]614 if (rc == EOK)
615 block->write_failures = 0;
[ddfc39a3]616 block->dirty = false;
617 }
618 fibril_mutex_unlock(&block->lock);
619
[4e1b57d]620 fibril_mutex_lock(&cache->lock);
621 fibril_mutex_lock(&block->lock);
[d5a720cf]622 if (!--block->refcnt) {
623 /*
[d68e4d5]624 * Last reference to the block was dropped. Either free the
[402a18f]625 * block or put it on the free list. In case of an I/O error,
626 * free the block.
[d68e4d5]627 */
[402a18f]628 if ((cache->blocks_cached > CACHE_HI_WATERMARK) ||
629 (rc != EOK)) {
[d68e4d5]630 /*
[402a18f]631 * Currently there are too many cached blocks or there
632 * was an I/O error when writing the block back to the
633 * device.
[d68e4d5]634 */
635 if (block->dirty) {
[ddfc39a3]636 /*
637 * We cannot sync the block while holding the
638 * cache lock. Release everything and retry.
639 */
640 block->refcnt++;
[3d35386]641
642 if (block->write_failures < MAX_WRITE_RETRIES) {
643 block->write_failures++;
644 fibril_mutex_unlock(&block->lock);
[c1f26834]645 fibril_mutex_unlock(&cache->lock);
[3d35386]646 goto retry;
647 } else {
648 printf("Too many errors writing block %"
[1433ecda]649 PRIuOFF64 "from device handle %" PRIun "\n"
[3d35386]650 "SEVERE DATA LOSS POSSIBLE\n",
[1433ecda]651 block->lba, devcon->service_id);
[3d35386]652 }
[d68e4d5]653 }
654 /*
655 * Take the block out of the cache and free it.
656 */
[062d900]657 hash_table_remove_item(&cache->block_hash, &block->hash_link);
[956d4df8]658 fibril_mutex_unlock(&block->lock);
[d68e4d5]659 free(block->data);
[b9e6205]660 free(block);
[d68e4d5]661 cache->blocks_cached--;
662 fibril_mutex_unlock(&cache->lock);
[402a18f]663 return rc;
[d68e4d5]664 }
665 /*
666 * Put the block on the free list.
[d5a720cf]667 */
[1fbe064b]668 if (cache->mode != CACHE_MODE_WB && block->dirty) {
[ddfc39a3]669 /*
670 * We cannot sync the block while holding the cache
671 * lock. Release everything and retry.
672 */
673 block->refcnt++;
674 fibril_mutex_unlock(&block->lock);
675 fibril_mutex_unlock(&cache->lock);
676 goto retry;
[1fbe064b]677 }
[b72efe8]678 list_append(&block->free_link, &cache->free_list);
[d5a720cf]679 }
[4e1b57d]680 fibril_mutex_unlock(&block->lock);
681 fibril_mutex_unlock(&cache->lock);
[c91f2d1b]682
[402a18f]683 return rc;
[d5a720cf]684}
685
[6408be3]686/** Read sequential data from a block device.
[d5a720cf]687 *
[15f3c3f]688 * @param service_id Service ID of the block device.
[4802dd7]689 * @param buf Buffer for holding one block
[d5a720cf]690 * @param bufpos Pointer to the first unread valid offset within the
691 * communication buffer.
692 * @param buflen Pointer to the number of unread bytes that are ready in
693 * the communication buffer.
694 * @param pos Device position to be read.
695 * @param dst Destination buffer.
696 * @param size Size of the destination buffer.
697 * @param block_size Block size to be used for the transfer.
698 *
[cde999a]699 * @return EOK on success or an error code on failure.
[d5a720cf]700 */
[b7fd2a0]701errno_t block_seqread(service_id_t service_id, void *buf, size_t *bufpos,
[4802dd7]702 size_t *buflen, aoff64_t *pos, void *dst, size_t size)
[d5a720cf]703{
[ed903174]704 size_t offset = 0;
[d5a720cf]705 size_t left = size;
[1ee00b7]706 size_t block_size;
707 devcon_t *devcon;
708
[15f3c3f]709 devcon = devcon_search(service_id);
[d5a720cf]710 assert(devcon);
[1ee00b7]711 block_size = devcon->pblock_size;
[a35b458]712
[d5a720cf]713 while (left > 0) {
714 size_t rd;
[a35b458]715
[d5a720cf]716 if (*bufpos + left < *buflen)
717 rd = left;
718 else
719 rd = *buflen - *bufpos;
[a35b458]720
[d5a720cf]721 if (rd > 0) {
722 /*
723 * Copy the contents of the communication buffer to the
724 * destination buffer.
725 */
[4802dd7]726 memcpy(dst + offset, buf + *bufpos, rd);
[d5a720cf]727 offset += rd;
728 *bufpos += rd;
729 *pos += rd;
730 left -= rd;
731 }
[a35b458]732
[ed903174]733 if (*bufpos == *buflen) {
[d5a720cf]734 /* Refill the communication buffer with a new block. */
[b7fd2a0]735 errno_t rc;
[6408be3]736
[4802dd7]737 rc = read_blocks(devcon, *pos / block_size, 1, buf,
738 devcon->pblock_size);
[d68e4d5]739 if (rc != EOK) {
[6408be3]740 return rc;
[d68e4d5]741 }
[a35b458]742
[d5a720cf]743 *bufpos = 0;
744 *buflen = block_size;
745 }
746 }
[a35b458]747
[d5a720cf]748 return EOK;
[fc840d9]749}
750
[00b1d20e]751/** Read blocks directly from device (bypass cache).
752 *
[15f3c3f]753 * @param service_id Service ID of the block device.
[a6ba0c9]754 * @param ba Address of first block (physical).
[00b1d20e]755 * @param cnt Number of blocks.
756 * @param src Buffer for storing the data.
757 *
[cde999a]758 * @return EOK on success or an error code on failure.
[00b1d20e]759 */
[b7fd2a0]760errno_t block_read_direct(service_id_t service_id, aoff64_t ba, size_t cnt, void *buf)
[00b1d20e]761{
762 devcon_t *devcon;
763
[15f3c3f]764 devcon = devcon_search(service_id);
[00b1d20e]765 assert(devcon);
766
[4802dd7]767 return read_blocks(devcon, ba, cnt, buf, devcon->pblock_size * cnt);
[00b1d20e]768}
769
770/** Write blocks directly to device (bypass cache).
771 *
[15f3c3f]772 * @param service_id Service ID of the block device.
[a6ba0c9]773 * @param ba Address of first block (physical).
[00b1d20e]774 * @param cnt Number of blocks.
775 * @param src The data to be written.
776 *
[cde999a]777 * @return EOK on success or an error code on failure.
[00b1d20e]778 */
[b7fd2a0]779errno_t block_write_direct(service_id_t service_id, aoff64_t ba, size_t cnt,
[00b1d20e]780 const void *data)
781{
782 devcon_t *devcon;
783
[15f3c3f]784 devcon = devcon_search(service_id);
[00b1d20e]785 assert(devcon);
786
[4802dd7]787 return write_blocks(devcon, ba, cnt, (void *)data, devcon->pblock_size * cnt);
[00b1d20e]788}
789
[78d50bd]790/** Synchronize blocks to persistent storage.
791 *
792 * @param service_id Service ID of the block device.
793 * @param ba Address of first block (physical).
794 * @param cnt Number of blocks.
795 *
[cde999a]796 * @return EOK on success or an error code on failure.
[78d50bd]797 */
[b7fd2a0]798errno_t block_sync_cache(service_id_t service_id, aoff64_t ba, size_t cnt)
[78d50bd]799{
800 devcon_t *devcon;
801
802 devcon = devcon_search(service_id);
803 assert(devcon);
804
805 return bd_sync_cache(devcon->bd, ba, cnt);
806}
807
[00b1d20e]808/** Get device block size.
809 *
[15f3c3f]810 * @param service_id Service ID of the block device.
[00b1d20e]811 * @param bsize Output block size.
812 *
[cde999a]813 * @return EOK on success or an error code on failure.
[00b1d20e]814 */
[b7fd2a0]815errno_t block_get_bsize(service_id_t service_id, size_t *bsize)
[00b1d20e]816{
817 devcon_t *devcon;
818
[15f3c3f]819 devcon = devcon_search(service_id);
[00b1d20e]820 assert(devcon);
[4802dd7]821
822 return bd_get_block_size(devcon->bd, bsize);
[00b1d20e]823}
824
[08232ee]825/** Get number of blocks on device.
826 *
[15f3c3f]827 * @param service_id Service ID of the block device.
[08232ee]828 * @param nblocks Output number of blocks.
829 *
[cde999a]830 * @return EOK on success or an error code on failure.
[08232ee]831 */
[b7fd2a0]832errno_t block_get_nblocks(service_id_t service_id, aoff64_t *nblocks)
[08232ee]833{
[15f3c3f]834 devcon_t *devcon = devcon_search(service_id);
[08232ee]835 assert(devcon);
[3d35386]836
[4802dd7]837 return bd_get_num_blocks(devcon->bd, nblocks);
[08232ee]838}
839
[e272949]840/** Read bytes directly from the device (bypass cache)
[1b20da0]841 *
[15f3c3f]842 * @param service_id Service ID of the block device.
[e272949]843 * @param abs_offset Absolute offset in bytes where to start reading
844 * @param bytes Number of bytes to read
845 * @param data Buffer that receives the data
[1b20da0]846 *
[cde999a]847 * @return EOK on success or an error code on failure.
[e272949]848 */
[b7fd2a0]849errno_t block_read_bytes_direct(service_id_t service_id, aoff64_t abs_offset,
[e272949]850 size_t bytes, void *data)
851{
[b7fd2a0]852 errno_t rc;
[e272949]853 size_t phys_block_size;
854 size_t buf_size;
855 void *buffer;
856 aoff64_t first_block;
857 aoff64_t last_block;
858 size_t blocks;
859 size_t offset;
[a35b458]860
[15f3c3f]861 rc = block_get_bsize(service_id, &phys_block_size);
[e272949]862 if (rc != EOK) {
863 return rc;
864 }
[a35b458]865
[c4aa9cf]866 /* calculate data position and required space */
[e272949]867 first_block = abs_offset / phys_block_size;
868 offset = abs_offset % phys_block_size;
869 last_block = (abs_offset + bytes - 1) / phys_block_size;
870 blocks = last_block - first_block + 1;
871 buf_size = blocks * phys_block_size;
[a35b458]872
[c4aa9cf]873 /* read the data into memory */
[e272949]874 buffer = malloc(buf_size);
875 if (buffer == NULL) {
876 return ENOMEM;
877 }
[a35b458]878
[15f3c3f]879 rc = block_read_direct(service_id, first_block, blocks, buffer);
[e272949]880 if (rc != EOK) {
881 free(buffer);
882 return rc;
883 }
[a35b458]884
[c4aa9cf]885 /* copy the data from the buffer */
[e272949]886 memcpy(data, buffer + offset, bytes);
887 free(buffer);
[a35b458]888
[e272949]889 return EOK;
890}
891
[4046b2f4]892/** Get TOC from device.
893 *
894 * @param service_id Service ID of the block device.
895 * @param session Starting session.
896 *
[08cba4b]897 * @return Allocated TOC structure.
[cde999a]898 * @return EOK on success or an error code.
[4046b2f4]899 *
900 */
[b7fd2a0]901errno_t block_read_toc(service_id_t service_id, uint8_t session, void *buf,
[3abf70c7]902 size_t bufsize)
[4046b2f4]903{
904 devcon_t *devcon = devcon_search(service_id);
[a35b458]905
[4802dd7]906 assert(devcon);
[3abf70c7]907 return bd_read_toc(devcon->bd, session, buf, bufsize);
[4046b2f4]908}
909
[1ee00b7]910/** Read blocks from block device.
[6408be3]911 *
912 * @param devcon Device connection.
[1ee00b7]913 * @param ba Address of first block.
914 * @param cnt Number of blocks.
[6408be3]915 * @param src Buffer for storing the data.
916 *
[cde999a]917 * @return EOK on success or an error code on failure.
[6408be3]918 */
[b7fd2a0]919static errno_t read_blocks(devcon_t *devcon, aoff64_t ba, size_t cnt, void *buf,
[4802dd7]920 size_t size)
[6408be3]921{
922 assert(devcon);
[a35b458]923
[b7fd2a0]924 errno_t rc = bd_read_blocks(devcon->bd, ba, cnt, buf, size);
[16fc3c9]925 if (rc != EOK) {
[c1694b6b]926 printf("Error %s reading %zu blocks starting at block %" PRIuOFF64
927 " from device handle %" PRIun "\n", str_error_name(rc), cnt, ba,
[15f3c3f]928 devcon->service_id);
[16fc3c9]929#ifndef NDEBUG
930 stacktrace_print();
931#endif
932 }
[a35b458]933
[1ee00b7]934 return rc;
[6408be3]935}
936
[1fbe064b]937/** Write block to block device.
938 *
939 * @param devcon Device connection.
[1ee00b7]940 * @param ba Address of first block.
941 * @param cnt Number of blocks.
[1fbe064b]942 * @param src Buffer containing the data to write.
943 *
[cde999a]944 * @return EOK on success or an error code on failure.
[1fbe064b]945 */
[b7fd2a0]946static errno_t write_blocks(devcon_t *devcon, aoff64_t ba, size_t cnt, void *data,
[4802dd7]947 size_t size)
[1fbe064b]948{
949 assert(devcon);
[a35b458]950
[b7fd2a0]951 errno_t rc = bd_write_blocks(devcon->bd, ba, cnt, data, size);
[16fc3c9]952 if (rc != EOK) {
[c1694b6b]953 printf("Error %s writing %zu blocks starting at block %" PRIuOFF64
954 " to device handle %" PRIun "\n", str_error_name(rc), cnt, ba, devcon->service_id);
[16fc3c9]955#ifndef NDEBUG
956 stacktrace_print();
957#endif
958 }
[a35b458]959
[1ee00b7]960 return rc;
961}
[1fbe064b]962
[f092718]963/** Convert logical block address to physical block address. */
964static aoff64_t ba_ltop(devcon_t *devcon, aoff64_t lba)
965{
966 assert(devcon->cache != NULL);
967 return lba * devcon->cache->blocks_cluster;
968}
969
[fc840d9]970/** @}
971 */
Note: See TracBrowser for help on using the repository browser.