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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c3d4bb45 was 63f8966, checked in by Martin Decky <martin@…>, 15 years ago

rename library directories (the common "lib" prefix is already in the upper directory)

  • Property mode set to 100644
File size: 21.4 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_synch.h>
50#include <adt/list.h>
51#include <adt/hash_table.h>
52#include <macros.h>
53#include <mem.h>
54#include <sys/typefmt.h>
55#include <stacktrace.h>
56
57/** Lock protecting the device connection list */
58static FIBRIL_MUTEX_INITIALIZE(dcl_lock);
59/** Device connection list head. */
60static LIST_INITIALIZE(dcl_head);
61
62#define CACHE_BUCKETS_LOG2 10
63#define CACHE_BUCKETS (1 << CACHE_BUCKETS_LOG2)
64
65typedef struct {
66 fibril_mutex_t lock;
67 size_t lblock_size; /**< Logical block size. */
68 unsigned block_count; /**< Total number of blocks. */
69 unsigned blocks_cached; /**< Number of cached blocks. */
70 hash_table_t block_hash;
71 link_t free_head;
72 enum cache_mode mode;
73} cache_t;
74
75typedef struct {
76 link_t link;
77 dev_handle_t dev_handle;
78 int dev_phone;
79 fibril_mutex_t comm_area_lock;
80 void *comm_area;
81 size_t comm_size;
82 void *bb_buf;
83 aoff64_t bb_addr;
84 size_t pblock_size; /**< Physical block size. */
85 cache_t *cache;
86} devcon_t;
87
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);
90static int get_block_size(int dev_phone, size_t *bsize);
91static int get_num_blocks(int dev_phone, aoff64_t *nblocks);
92
93static devcon_t *devcon_search(dev_handle_t dev_handle)
94{
95 link_t *cur;
96
97 fibril_mutex_lock(&dcl_lock);
98 for (cur = dcl_head.next; cur != &dcl_head; cur = cur->next) {
99 devcon_t *devcon = list_get_instance(cur, devcon_t, link);
100 if (devcon->dev_handle == dev_handle) {
101 fibril_mutex_unlock(&dcl_lock);
102 return devcon;
103 }
104 }
105 fibril_mutex_unlock(&dcl_lock);
106 return NULL;
107}
108
109static int devcon_add(dev_handle_t dev_handle, int dev_phone, size_t bsize,
110 void *comm_area, size_t comm_size)
111{
112 link_t *cur;
113 devcon_t *devcon;
114
115 if (comm_size < bsize)
116 return EINVAL;
117
118 devcon = malloc(sizeof(devcon_t));
119 if (!devcon)
120 return ENOMEM;
121
122 link_initialize(&devcon->link);
123 devcon->dev_handle = dev_handle;
124 devcon->dev_phone = dev_phone;
125 fibril_mutex_initialize(&devcon->comm_area_lock);
126 devcon->comm_area = comm_area;
127 devcon->comm_size = comm_size;
128 devcon->bb_buf = NULL;
129 devcon->bb_addr = 0;
130 devcon->pblock_size = bsize;
131 devcon->cache = NULL;
132
133 fibril_mutex_lock(&dcl_lock);
134 for (cur = dcl_head.next; cur != &dcl_head; cur = cur->next) {
135 devcon_t *d = list_get_instance(cur, devcon_t, link);
136 if (d->dev_handle == dev_handle) {
137 fibril_mutex_unlock(&dcl_lock);
138 free(devcon);
139 return EEXIST;
140 }
141 }
142 list_append(&devcon->link, &dcl_head);
143 fibril_mutex_unlock(&dcl_lock);
144 return EOK;
145}
146
147static void devcon_remove(devcon_t *devcon)
148{
149 fibril_mutex_lock(&dcl_lock);
150 list_remove(&devcon->link);
151 fibril_mutex_unlock(&dcl_lock);
152}
153
154int block_init(dev_handle_t dev_handle, size_t comm_size)
155{
156 int rc;
157 int dev_phone;
158 void *comm_area;
159 size_t bsize;
160
161 comm_area = mmap(NULL, comm_size, PROTO_READ | PROTO_WRITE,
162 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
163 if (!comm_area) {
164 return ENOMEM;
165 }
166
167 dev_phone = devmap_device_connect(dev_handle, IPC_FLAG_BLOCKING);
168 if (dev_phone < 0) {
169 munmap(comm_area, comm_size);
170 return dev_phone;
171 }
172
173 rc = async_share_out_start(dev_phone, comm_area,
174 AS_AREA_READ | AS_AREA_WRITE);
175 if (rc != EOK) {
176 munmap(comm_area, comm_size);
177 ipc_hangup(dev_phone);
178 return rc;
179 }
180
181 if (get_block_size(dev_phone, &bsize) != EOK) {
182 munmap(comm_area, comm_size);
183 ipc_hangup(dev_phone);
184 return rc;
185 }
186
187 rc = devcon_add(dev_handle, dev_phone, bsize, comm_area, comm_size);
188 if (rc != EOK) {
189 munmap(comm_area, comm_size);
190 ipc_hangup(dev_phone);
191 return rc;
192 }
193
194 return EOK;
195}
196
197void block_fini(dev_handle_t dev_handle)
198{
199 devcon_t *devcon = devcon_search(dev_handle);
200 assert(devcon);
201
202 if (devcon->cache)
203 (void) block_cache_fini(dev_handle);
204
205 devcon_remove(devcon);
206
207 if (devcon->bb_buf)
208 free(devcon->bb_buf);
209
210 munmap(devcon->comm_area, devcon->comm_size);
211 ipc_hangup(devcon->dev_phone);
212
213 free(devcon);
214}
215
216int block_bb_read(dev_handle_t dev_handle, aoff64_t ba)
217{
218 void *bb_buf;
219 int rc;
220
221 devcon_t *devcon = devcon_search(dev_handle);
222 if (!devcon)
223 return ENOENT;
224 if (devcon->bb_buf)
225 return EEXIST;
226 bb_buf = malloc(devcon->pblock_size);
227 if (!bb_buf)
228 return ENOMEM;
229
230 fibril_mutex_lock(&devcon->comm_area_lock);
231 rc = read_blocks(devcon, 0, 1);
232 if (rc != EOK) {
233 fibril_mutex_unlock(&devcon->comm_area_lock);
234 free(bb_buf);
235 return rc;
236 }
237 memcpy(bb_buf, devcon->comm_area, devcon->pblock_size);
238 fibril_mutex_unlock(&devcon->comm_area_lock);
239
240 devcon->bb_buf = bb_buf;
241 devcon->bb_addr = ba;
242
243 return EOK;
244}
245
246void *block_bb_get(dev_handle_t dev_handle)
247{
248 devcon_t *devcon = devcon_search(dev_handle);
249 assert(devcon);
250 return devcon->bb_buf;
251}
252
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
274int block_cache_init(dev_handle_t dev_handle, size_t size, unsigned blocks,
275 enum cache_mode mode)
276{
277 devcon_t *devcon = devcon_search(dev_handle);
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
287 fibril_mutex_initialize(&cache->lock);
288 list_initialize(&cache->free_head);
289 cache->lblock_size = size;
290 cache->block_count = blocks;
291 cache->blocks_cached = 0;
292 cache->mode = mode;
293
294 /* No block size translation a.t.m. */
295 assert(cache->lblock_size == devcon->pblock_size);
296
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
307int block_cache_fini(dev_handle_t dev_handle)
308{
309 devcon_t *devcon = devcon_search(dev_handle);
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
336 unsigned long key = b->boff;
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
350#define CACHE_LO_WATERMARK 10
351#define CACHE_HI_WATERMARK 20
352static bool cache_can_grow(cache_t *cache)
353{
354 if (cache->blocks_cached < CACHE_LO_WATERMARK)
355 return true;
356 if (!list_empty(&cache->free_head))
357 return false;
358 return true;
359}
360
361static void block_initialize(block_t *b)
362{
363 fibril_mutex_initialize(&b->lock);
364 b->refcnt = 1;
365 b->dirty = false;
366 b->toxic = false;
367 fibril_rwlock_initialize(&b->contents_lock);
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 *
374 * @param block Pointer to where the function will store the
375 * block pointer on success.
376 * @param dev_handle Device handle of the block device.
377 * @param boff Block offset.
378 * @param flags If BLOCK_FLAGS_NOREAD is specified, block_get()
379 * will not read the contents of the block from the
380 * device.
381 *
382 * @return EOK on success or a negative error code.
383 */
384int block_get(block_t **block, dev_handle_t dev_handle, aoff64_t boff, int flags)
385{
386 devcon_t *devcon;
387 cache_t *cache;
388 block_t *b;
389 link_t *l;
390 unsigned long key = boff;
391 int rc;
392
393 devcon = devcon_search(dev_handle);
394
395 assert(devcon);
396 assert(devcon->cache);
397
398 cache = devcon->cache;
399
400retry:
401 rc = EOK;
402 b = NULL;
403
404 fibril_mutex_lock(&cache->lock);
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);
411 fibril_mutex_lock(&b->lock);
412 if (b->refcnt++ == 0)
413 list_remove(&b->free_link);
414 if (b->toxic)
415 rc = EIO;
416 fibril_mutex_unlock(&b->lock);
417 fibril_mutex_unlock(&cache->lock);
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;
431 b->data = malloc(cache->lblock_size);
432 if (!b->data) {
433 free(b);
434 goto recycle;
435 }
436 cache->blocks_cached++;
437 } else {
438 /*
439 * Try to recycle a block from the free list.
440 */
441 unsigned long temp_key;
442recycle:
443 if (list_empty(&cache->free_head)) {
444 fibril_mutex_unlock(&cache->lock);
445 rc = ENOMEM;
446 goto out;
447 }
448 l = cache->free_head.next;
449 b = list_get_instance(l, block_t, free_link);
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);
465 fibril_mutex_lock(&devcon->comm_area_lock);
466 memcpy(devcon->comm_area, b->data, b->size);
467 rc = write_blocks(devcon, b->boff, 1);
468 fibril_mutex_unlock(&devcon->comm_area_lock);
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 }
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);
497 temp_key = b->boff;
498 hash_table_remove(&cache->block_hash, &temp_key, 1);
499 }
500
501 block_initialize(b);
502 b->dev_handle = dev_handle;
503 b->size = cache->lblock_size;
504 b->boff = boff;
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
509 * kill concurrent operations on the cache while doing I/O on
510 * the block.
511 */
512 fibril_mutex_lock(&b->lock);
513 fibril_mutex_unlock(&cache->lock);
514
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 */
520 fibril_mutex_lock(&devcon->comm_area_lock);
521 rc = read_blocks(devcon, b->boff, 1);
522 memcpy(b->data, devcon->comm_area, cache->lblock_size);
523 fibril_mutex_unlock(&devcon->comm_area_lock);
524 if (rc != EOK)
525 b->toxic = true;
526 } else
527 rc = EOK;
528
529 fibril_mutex_unlock(&b->lock);
530 }
531out:
532 if ((rc != EOK) && b) {
533 assert(b->toxic);
534 (void) block_put(b);
535 b = NULL;
536 }
537 *block = b;
538 return rc;
539}
540
541/** Release a reference to a block.
542 *
543 * If the last reference is dropped, the block is put on the free list.
544 *
545 * @param block Block of which a reference is to be released.
546 *
547 * @return EOK on success or a negative error code.
548 */
549int block_put(block_t *block)
550{
551 devcon_t *devcon = devcon_search(block->dev_handle);
552 cache_t *cache;
553 unsigned blocks_cached;
554 enum cache_mode mode;
555 int rc = EOK;
556
557 assert(devcon);
558 assert(devcon->cache);
559
560 cache = devcon->cache;
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);
576 if (block->toxic)
577 block->dirty = false; /* will not write back toxic block */
578 if (block->dirty && (block->refcnt == 1) &&
579 (blocks_cached > CACHE_HI_WATERMARK || mode != CACHE_MODE_WB)) {
580 fibril_mutex_lock(&devcon->comm_area_lock);
581 memcpy(devcon->comm_area, block->data, block->size);
582 rc = write_blocks(devcon, block->boff, 1);
583 fibril_mutex_unlock(&devcon->comm_area_lock);
584 block->dirty = false;
585 }
586 fibril_mutex_unlock(&block->lock);
587
588 fibril_mutex_lock(&cache->lock);
589 fibril_mutex_lock(&block->lock);
590 if (!--block->refcnt) {
591 /*
592 * Last reference to the block was dropped. Either free the
593 * block or put it on the free list. In case of an I/O error,
594 * free the block.
595 */
596 if ((cache->blocks_cached > CACHE_HI_WATERMARK) ||
597 (rc != EOK)) {
598 /*
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.
602 */
603 if (block->dirty) {
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;
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);
622 return rc;
623 }
624 /*
625 * Put the block on the free list.
626 */
627 if (cache->mode != CACHE_MODE_WB && block->dirty) {
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;
636 }
637 list_append(&block->free_link, &cache->free_head);
638 }
639 fibril_mutex_unlock(&block->lock);
640 fibril_mutex_unlock(&cache->lock);
641
642 return rc;
643}
644
645/** Read sequential data from a block device.
646 *
647 * @param dev_handle Device handle of the block device.
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 */
659int block_seqread(dev_handle_t dev_handle, size_t *bufpos, size_t *buflen,
660 aoff64_t *pos, void *dst, size_t size)
661{
662 size_t offset = 0;
663 size_t left = size;
664 size_t block_size;
665 devcon_t *devcon;
666
667 devcon = devcon_search(dev_handle);
668 assert(devcon);
669 block_size = devcon->pblock_size;
670
671 fibril_mutex_lock(&devcon->comm_area_lock);
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 */
685 memcpy(dst + offset, devcon->comm_area + *bufpos, rd);
686 offset += rd;
687 *bufpos += rd;
688 *pos += rd;
689 left -= rd;
690 }
691
692 if (*bufpos == *buflen) {
693 /* Refill the communication buffer with a new block. */
694 int rc;
695
696 rc = read_blocks(devcon, *pos / block_size, 1);
697 if (rc != EOK) {
698 fibril_mutex_unlock(&devcon->comm_area_lock);
699 return rc;
700 }
701
702 *bufpos = 0;
703 *buflen = block_size;
704 }
705 }
706 fibril_mutex_unlock(&devcon->comm_area_lock);
707
708 return EOK;
709}
710
711/** Read blocks directly from device (bypass cache).
712 *
713 * @param dev_handle Device handle of the block device.
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 */
720int block_read_direct(dev_handle_t dev_handle, aoff64_t ba, size_t cnt, void *buf)
721{
722 devcon_t *devcon;
723 int rc;
724
725 devcon = devcon_search(dev_handle);
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 *
741 * @param dev_handle Device handle of the block device.
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 */
748int block_write_direct(dev_handle_t dev_handle, aoff64_t ba, size_t cnt,
749 const void *data)
750{
751 devcon_t *devcon;
752 int rc;
753
754 devcon = devcon_search(dev_handle);
755 assert(devcon);
756
757 fibril_mutex_lock(&devcon->comm_area_lock);
758
759 memcpy(devcon->comm_area, data, devcon->pblock_size * cnt);
760 rc = write_blocks(devcon, ba, cnt);
761
762 fibril_mutex_unlock(&devcon->comm_area_lock);
763
764 return rc;
765}
766
767/** Get device block size.
768 *
769 * @param dev_handle Device handle of the block device.
770 * @param bsize Output block size.
771 *
772 * @return EOK on success or negative error code on failure.
773 */
774int block_get_bsize(dev_handle_t dev_handle, size_t *bsize)
775{
776 devcon_t *devcon;
777
778 devcon = devcon_search(dev_handle);
779 assert(devcon);
780
781 return get_block_size(devcon->dev_phone, bsize);
782}
783
784/** Get number of blocks on device.
785 *
786 * @param dev_handle Device handle of the block device.
787 * @param nblocks Output number of blocks.
788 *
789 * @return EOK on success or negative error code on failure.
790 */
791int block_get_nblocks(dev_handle_t dev_handle, aoff64_t *nblocks)
792{
793 devcon_t *devcon;
794
795 devcon = devcon_search(dev_handle);
796 assert(devcon);
797
798 return get_num_blocks(devcon->dev_phone, nblocks);
799}
800
801/** Read blocks from block device.
802 *
803 * @param devcon Device connection.
804 * @param ba Address of first block.
805 * @param cnt Number of blocks.
806 * @param src Buffer for storing the data.
807 *
808 * @return EOK on success or negative error code on failure.
809 */
810static int read_blocks(devcon_t *devcon, aoff64_t ba, size_t cnt)
811{
812 int rc;
813
814 assert(devcon);
815 rc = async_req_3_0(devcon->dev_phone, BD_READ_BLOCKS, LOWER32(ba),
816 UPPER32(ba), cnt);
817 if (rc != EOK) {
818 printf("Error %d reading %d blocks starting at block %" PRIuOFF64
819 " from device handle %d\n", rc, cnt, ba,
820 devcon->dev_handle);
821#ifndef NDEBUG
822 stacktrace_print();
823#endif
824 }
825 return rc;
826}
827
828/** Write block to block device.
829 *
830 * @param devcon Device connection.
831 * @param ba Address of first block.
832 * @param cnt Number of blocks.
833 * @param src Buffer containing the data to write.
834 *
835 * @return EOK on success or negative error code on failure.
836 */
837static int write_blocks(devcon_t *devcon, aoff64_t ba, size_t cnt)
838{
839 int rc;
840
841 assert(devcon);
842 rc = async_req_3_0(devcon->dev_phone, BD_WRITE_BLOCKS, LOWER32(ba),
843 UPPER32(ba), cnt);
844 if (rc != EOK) {
845 printf("Error %d writing %d blocks starting at block %" PRIuOFF64
846 " to device handle %d\n", rc, cnt, ba, devcon->dev_handle);
847#ifndef NDEBUG
848 stacktrace_print();
849#endif
850 }
851 return rc;
852}
853
854/** Get block size used by the device. */
855static int get_block_size(int dev_phone, size_t *bsize)
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;
865}
866
867/** Get total number of blocks on block device. */
868static int get_num_blocks(int dev_phone, aoff64_t *nblocks)
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) {
875 *nblocks = (aoff64_t) MERGE_LOUP32(nb_l, nb_h);
876 }
877
878 return rc;
879}
880
881/** @}
882 */
Note: See TracBrowser for help on using the repository browser.