X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=lwext4%2Fext4_blockdev.c;h=23ad9ee3467368699090a6c921ad5186a3c17cab;hb=e1bbe336d1736703a6b1565cfa5565fb276643a1;hp=c83015b6b51895faf4edc1b64a4b7cde476bbe0b;hpb=d812bca336c8d67a5b1a9744b36c0ecbb5baa8b3;p=lwext4.git diff --git a/lwext4/ext4_blockdev.c b/lwext4/ext4_blockdev.c index c83015b..23ad9ee 100644 --- a/lwext4/ext4_blockdev.c +++ b/lwext4/ext4_blockdev.c @@ -34,412 +34,430 @@ * @brief Block device module. */ -#include -#include -#include -#include +#include "ext4_config.h" +#include "ext4_blockdev.h" +#include "ext4_errno.h" +#include "ext4_debug.h" #include #include +static void ext4_bdif_lock(struct ext4_blockdev *bdev) +{ + if (!bdev->bdif->lock) + return; + int r = bdev->bdif->lock(bdev); + ext4_assert(r == EOK); +} -int ext4_block_init(struct ext4_blockdev *bdev) +static void ext4_bdif_unlock(struct ext4_blockdev *bdev) { - int rc; - ext4_assert(bdev); + if (!bdev->bdif->unlock) + return; - ext4_assert(bdev->open && bdev->close && bdev->bread && bdev->bwrite); + int r = bdev->bdif->unlock(bdev); + ext4_assert(r == EOK); +} - /*Low level block init*/ - rc = bdev->open(bdev); - if(rc != EOK) - return rc; +static int ext4_bdif_bread(struct ext4_blockdev *bdev, void *buf, + uint64_t blk_id, uint32_t blk_cnt) +{ + ext4_bdif_lock(bdev); + int r = bdev->bdif->bread(bdev, buf, blk_id, blk_cnt); + bdev->bdif->bread_ctr++; + ext4_bdif_unlock(bdev); + return r; +} - bdev->flags |= EXT4_BDEV_INITIALIZED; +static int ext4_bdif_bwrite(struct ext4_blockdev *bdev, const void *buf, + uint64_t blk_id, uint32_t blk_cnt) +{ + ext4_bdif_lock(bdev); + int r = bdev->bdif->bwrite(bdev, buf, blk_id, blk_cnt); + bdev->bdif->bwrite_ctr++; + ext4_bdif_unlock(bdev); + return r; +} - return EOK; +int ext4_block_init(struct ext4_blockdev *bdev) +{ + int rc; + ext4_assert(bdev); + ext4_assert(bdev->bdif); + ext4_assert(bdev->bdif->open && + bdev->bdif->close && + bdev->bdif->bread && + bdev->bdif->bwrite); + + if (bdev->bdif->ph_refctr) { + bdev->bdif->ph_refctr++; + return EOK; + } + + /*Low level block init*/ + rc = bdev->bdif->open(bdev); + if (rc != EOK) + return rc; + + bdev->bdif->ph_refctr = 1; + return EOK; } int ext4_block_bind_bcache(struct ext4_blockdev *bdev, struct ext4_bcache *bc) { - ext4_assert(bdev && bc); - bdev->bc = bc; - return EOK; + ext4_assert(bdev && bc); + bdev->bc = bc; + bc->bdev = bdev; + return EOK; } void ext4_block_set_lb_size(struct ext4_blockdev *bdev, uint64_t lb_bsize) { - /*Logical block size has to be multiply of physical */ - ext4_assert(!(lb_bsize % bdev->ph_bsize)); - - bdev->lg_bsize = lb_bsize; - bdev->lg_bcnt = (bdev->ph_bcnt * bdev->ph_bsize) / lb_bsize; + /*Logical block size has to be multiply of physical */ + ext4_assert(!(lb_bsize % bdev->bdif->ph_bsize)); + bdev->lg_bsize = lb_bsize; + bdev->lg_bcnt = bdev->part_size / lb_bsize; } int ext4_block_fini(struct ext4_blockdev *bdev) { - ext4_assert(bdev); + ext4_assert(bdev); - bdev->flags &= ~(EXT4_BDEV_INITIALIZED); + if (!bdev->bdif->ph_refctr) + return EOK; - /*Low level block fini*/ - return bdev->close(bdev); -} + bdev->bdif->ph_refctr--; + if (bdev->bdif->ph_refctr) + return EOK; + /*Low level block fini*/ + return bdev->bdif->close(bdev); +} -int ext4_block_get(struct ext4_blockdev *bdev, struct ext4_block *b, - uint64_t lba) +int ext4_block_flush_buf(struct ext4_blockdev *bdev, struct ext4_buf *buf) { - uint64_t pba; - uint32_t pb_cnt; - uint32_t i; - bool is_new; - int r; - - ext4_assert(bdev && b); - - if(!(bdev->flags & EXT4_BDEV_INITIALIZED)) - return EIO; - - if(!(lba < bdev->lg_bcnt)) - return ERANGE; - - b->dirty = 0; - b->lb_id = lba; - - /*If cache is full we have to flush it anyway :(*/ - if(ext4_bcache_is_full(bdev->bc) && bdev->cache_write_back){ - - uint32_t free_candidate = bdev->bc->cnt; - uint32_t min_lru = 0xFFFFFFFF; - - for (i = 0; i < bdev->bc->cnt; ++i) { - /*Check if buffer free was delayed.*/ - if(!bdev->bc->free_delay[i]) - continue; - - /*Check reference counter.*/ - if(bdev->bc->refctr[i]) - continue; - - if(bdev->bc->lru_id[i] < min_lru){ - min_lru = bdev->bc->lru_id[i]; - free_candidate = i; - continue; - } - } + int r; + struct ext4_bcache *bc = bdev->bc; + + if (ext4_bcache_test_flag(buf, BC_DIRTY) && + ext4_bcache_test_flag(buf, BC_UPTODATE)) { + r = ext4_blocks_set_direct(bdev, buf->data, buf->lba, 1); + if (r) { + if (buf->end_write) { + bc->dont_shake = true; + buf->end_write(bc, buf, r, buf->end_write_arg); + bc->dont_shake = false; + } + + return r; + } + + ext4_bcache_remove_dirty_node(bc, buf); + ext4_bcache_clear_flag(buf, BC_DIRTY); + if (buf->end_write) { + bc->dont_shake = true; + buf->end_write(bc, buf, r, buf->end_write_arg); + bc->dont_shake = false; + } + } + return EOK; +} - if(free_candidate < bdev->bc->cnt){ - /*Buffer free was delayed and have no reference. Flush it.*/ - r = ext4_blocks_set_direct(bdev, - bdev->bc->data + bdev->bc->itemsize * free_candidate, - bdev->bc->lba[free_candidate], 1); - if(r != EOK) - return r; +int ext4_block_flush_lba(struct ext4_blockdev *bdev, uint64_t lba) +{ + int r = EOK; + struct ext4_buf *buf; + struct ext4_block b; + buf = ext4_bcache_find_get(bdev->bc, &b, lba); + if (buf) { + r = ext4_block_flush_buf(bdev, buf); + ext4_bcache_free(bdev->bc, &b); + } + return r; +} - /*No delayed anymore*/ - bdev->bc->free_delay[free_candidate] = 0; +int ext4_block_cache_shake(struct ext4_blockdev *bdev) +{ + int r = EOK; + struct ext4_buf *buf; + if (bdev->bc->dont_shake) + return EOK; + + while (!RB_EMPTY(&bdev->bc->lru_root) && + ext4_bcache_is_full(bdev->bc)) { + + buf = ext4_buf_lowest_lru(bdev->bc); + ext4_assert(buf); + if (ext4_bcache_test_flag(buf, BC_DIRTY)) { + r = ext4_block_flush_buf(bdev, buf); + if (r != EOK) + break; + + } + + ext4_bcache_drop_buf(bdev->bc, buf); + } + return r; +} - /*Reduce refered block count*/ - bdev->bc->ref_blocks--; - } - } +int ext4_block_get_noread(struct ext4_blockdev *bdev, struct ext4_block *b, + uint64_t lba) +{ + bool is_new; + int r; + ext4_assert(bdev && b); - r = ext4_bcache_alloc(bdev->bc, b, &is_new); - if(r != EOK) - return r; + if (!bdev->bdif->ph_refctr) + return EIO; + if (!(lba < bdev->lg_bcnt)) + return ERANGE; - if(!is_new){ - /*Block is in cache. Read from physical device is not required*/ - return EOK; - } + b->lb_id = lba; - if(!b->data) - return ENOMEM; + /*If cache is full we have to (flush and) drop it anyway :(*/ + r = ext4_block_cache_shake(bdev); + if (r != EOK) + return r; - pba = (lba * bdev->lg_bsize) / bdev->ph_bsize; - pb_cnt = bdev->lg_bsize / bdev->ph_bsize; + r = ext4_bcache_alloc(bdev->bc, b, &is_new); + if (r != EOK) + return r; - r = bdev->bread(bdev, b->data, pba, pb_cnt); + if (!b->data) + return ENOMEM; - if(r != EOK){ - ext4_bcache_free(bdev->bc, b, 0); - b->lb_id = 0; - return r; - } + return EOK; +} - bdev->bread_ctr++; - return EOK; +int ext4_block_get(struct ext4_blockdev *bdev, struct ext4_block *b, + uint64_t lba) +{ + int r = ext4_block_get_noread(bdev, b, lba); + if (r != EOK) + return r; + + if (ext4_bcache_test_flag(b->buf, BC_UPTODATE)) { + /* Data in the cache is up-to-date. + * Reading from physical device is not required */ + return EOK; + } + + r = ext4_blocks_get_direct(bdev, b->data, lba, 1); + if (r != EOK) { + ext4_bcache_free(bdev->bc, b); + b->lb_id = 0; + return r; + } + + /* Mark buffer up-to-date, since + * fresh data is read from physical device just now. */ + ext4_bcache_set_flag(b->buf, BC_UPTODATE); + return EOK; } int ext4_block_set(struct ext4_blockdev *bdev, struct ext4_block *b) { - uint64_t pba; - uint32_t pb_cnt; - int r; - - ext4_assert(bdev && b); - - if(!(bdev->flags & EXT4_BDEV_INITIALIZED)) - return EIO; - - /*Doesn,t need to write.*/ - if(!b->dirty && !bdev->bc->dirty[b->cache_id]){ - ext4_bcache_free(bdev->bc, b, 0); - return EOK; - } - - /*Free cache delay mode*/ - if(bdev->cache_write_back){ + ext4_assert(bdev && b); + ext4_assert(b->buf); - /*Free cahe block and mark as free delayed*/ - return ext4_bcache_free(bdev->bc, b, bdev->cache_write_back); - } + if (!bdev->bdif->ph_refctr) + return EIO; - if(bdev->bc->refctr[b->cache_id] > 1){ - bdev->bc->dirty[b->cache_id] = true; - return ext4_bcache_free(bdev->bc, b, 0); - } - - - pba = (b->lb_id * bdev->lg_bsize) / bdev->ph_bsize; - pb_cnt = bdev->lg_bsize / bdev->ph_bsize; - - r = bdev->bwrite(bdev, b->data, pba, pb_cnt); - bdev->bc->dirty[b->cache_id] = false; - if(r != EOK){ - b->dirty = false; - ext4_bcache_free(bdev->bc, b, 0); - return r; - } - - bdev->bwrite_ctr++; - b->dirty = false; - ext4_bcache_free(bdev->bc, b, 0); - return EOK; + return ext4_bcache_free(bdev->bc, b); } -int ext4_blocks_get_direct(struct ext4_blockdev *bdev, void *buf, - uint64_t lba, uint32_t cnt) +int ext4_blocks_get_direct(struct ext4_blockdev *bdev, void *buf, uint64_t lba, + uint32_t cnt) { - uint64_t pba; - uint32_t pb_cnt; + uint64_t pba; + uint32_t pb_cnt; - ext4_assert(bdev && buf); + ext4_assert(bdev && buf); - pba = (lba * bdev->lg_bsize) / bdev->ph_bsize; - pb_cnt = bdev->lg_bsize / bdev->ph_bsize; + pba = (lba * bdev->lg_bsize + bdev->part_offset) / bdev->bdif->ph_bsize; + pb_cnt = bdev->lg_bsize / bdev->bdif->ph_bsize; - bdev->bread_ctr++; - return bdev->bread(bdev, buf, pba, pb_cnt * cnt); + return ext4_bdif_bread(bdev, buf, pba, pb_cnt * cnt); } int ext4_blocks_set_direct(struct ext4_blockdev *bdev, const void *buf, - uint64_t lba, uint32_t cnt) + uint64_t lba, uint32_t cnt) { - uint64_t pba; - uint32_t pb_cnt; + uint64_t pba; + uint32_t pb_cnt; - ext4_assert(bdev && buf); + ext4_assert(bdev && buf); - pba = (lba * bdev->lg_bsize) / bdev->ph_bsize; - pb_cnt = bdev->lg_bsize / bdev->ph_bsize; + pba = (lba * bdev->lg_bsize + bdev->part_offset) / bdev->bdif->ph_bsize; + pb_cnt = bdev->lg_bsize / bdev->bdif->ph_bsize; - bdev->bwrite_ctr++; - - return bdev->bwrite(bdev, buf, pba, pb_cnt * cnt); + return ext4_bdif_bwrite(bdev, buf, pba, pb_cnt * cnt); } - int ext4_block_writebytes(struct ext4_blockdev *bdev, uint64_t off, - const void *buf, uint32_t len) + const void *buf, uint32_t len) { - uint64_t block_idx; - uint64_t block_end; - uint32_t blen; - uint32_t unalg; - int r = EOK; - - const uint8_t *p = (void *)buf; + uint64_t block_idx; + uint32_t blen; + uint32_t unalg; + int r = EOK; - ext4_assert(bdev && buf); + const uint8_t *p = (void *)buf; - if(!(bdev->flags & EXT4_BDEV_INITIALIZED)) - return EIO; + ext4_assert(bdev && buf); - block_idx = off / bdev->ph_bsize; - block_end = block_idx + len / bdev->ph_bsize; + if (!bdev->bdif->ph_refctr) + return EIO; - if(!(block_end < bdev->ph_bcnt)) - return EINVAL; /*Ups. Out of range operation*/ + if (off + len > bdev->part_size) + return EINVAL; /*Ups. Out of range operation*/ - /*OK lets deal with the first possible unaligned block*/ - unalg = (off & (bdev->ph_bsize - 1)); - if(unalg){ + block_idx = ((off + bdev->part_offset) / bdev->bdif->ph_bsize); - uint32_t wlen = (bdev->ph_bsize - unalg) > len ? - len : (bdev->ph_bsize - unalg); + /*OK lets deal with the first possible unaligned block*/ + unalg = (off & (bdev->bdif->ph_bsize - 1)); + if (unalg) { - r = bdev->bread(bdev, bdev->ph_bbuf, block_idx, 1); + uint32_t wlen = (bdev->bdif->ph_bsize - unalg) > len + ? len + : (bdev->bdif->ph_bsize - unalg); - if(r != EOK) - return r; + r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1); + if (r != EOK) + return r; - memcpy(bdev->ph_bbuf + unalg, p, wlen); + memcpy(bdev->bdif->ph_bbuf + unalg, p, wlen); + r = ext4_bdif_bwrite(bdev, bdev->bdif->ph_bbuf, block_idx, 1); + if (r != EOK) + return r; - r = bdev->bwrite(bdev, bdev->ph_bbuf, block_idx, 1); - if(r != EOK) - return r; + p += wlen; + len -= wlen; + block_idx++; + } - p += wlen; - len -= wlen; - block_idx++; - } + /*Aligned data*/ + blen = len / bdev->bdif->ph_bsize; + r = ext4_bdif_bwrite(bdev, p, block_idx, blen); + if (r != EOK) + return r; + p += bdev->bdif->ph_bsize * blen; + len -= bdev->bdif->ph_bsize * blen; - /*Aligned data*/ - blen = len / bdev->ph_bsize; - r = bdev->bwrite(bdev, p, block_idx, blen); + block_idx += blen; - if(r != EOK) - return r; + /*Rest of the data*/ + if (len) { + r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1); + if (r != EOK) + return r; - p += bdev->ph_bsize * blen; - len -= bdev->ph_bsize * blen; + memcpy(bdev->bdif->ph_bbuf, p, len); + r = ext4_bdif_bwrite(bdev, bdev->bdif->ph_bbuf, block_idx, 1); + if (r != EOK) + return r; + } - block_idx += blen; - - - /*Rest of the data*/ - if(len){ - r = bdev->bread(bdev, bdev->ph_bbuf, block_idx, 1); - if(r != EOK) - return r; - - memcpy(bdev->ph_bbuf, p, len); - - r = bdev->bwrite(bdev, bdev->ph_bbuf, block_idx, 1); - - if(r != EOK) - return r; - } - - return r; + return r; } - int ext4_block_readbytes(struct ext4_blockdev *bdev, uint64_t off, void *buf, - uint32_t len) + uint32_t len) { - uint64_t block_idx; - uint64_t block_end; - uint32_t blen; - uint32_t unalg; - int r = EOK; + uint64_t block_idx; + uint32_t blen; + uint32_t unalg; + int r = EOK; - uint8_t *p = (void *)buf; + uint8_t *p = (void *)buf; - ext4_assert(bdev && buf); + ext4_assert(bdev && buf); - if(!(bdev->flags & EXT4_BDEV_INITIALIZED)) - return EIO; + if (!bdev->bdif->ph_refctr) + return EIO; - block_idx = off / bdev->ph_bsize; - block_end = block_idx + len / bdev->ph_bsize; + if (off + len > bdev->part_size) + return EINVAL; /*Ups. Out of range operation*/ - if(!(block_end < bdev->ph_bcnt)) - return EINVAL; /*Ups. Out of range operation*/ + block_idx = ((off + bdev->part_offset) / bdev->bdif->ph_bsize); - /*OK lets deal with the first possible unaligned block*/ - unalg = (off & (bdev->ph_bsize - 1)); - if(unalg){ + /*OK lets deal with the first possible unaligned block*/ + unalg = (off & (bdev->bdif->ph_bsize - 1)); + if (unalg) { - uint32_t rlen = (bdev->ph_bsize - unalg) > len ? - len : (bdev->ph_bsize - unalg); + uint32_t rlen = (bdev->bdif->ph_bsize - unalg) > len + ? len + : (bdev->bdif->ph_bsize - unalg); - r = bdev->bread(bdev, bdev->ph_bbuf, block_idx, 1); - if(r != EOK) - return r; + r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1); + if (r != EOK) + return r; - memcpy(p, bdev->ph_bbuf + unalg, rlen); + memcpy(p, bdev->bdif->ph_bbuf + unalg, rlen); - p += rlen; - len -= rlen; - block_idx++; - } + p += rlen; + len -= rlen; + block_idx++; + } - /*Aligned data*/ - blen = len / bdev->ph_bsize; + /*Aligned data*/ + blen = len / bdev->bdif->ph_bsize; - r = bdev->bread(bdev, p, block_idx, blen); + r = ext4_bdif_bread(bdev, p, block_idx, blen); + if (r != EOK) + return r; - if(r != EOK) - return r; + p += bdev->bdif->ph_bsize * blen; + len -= bdev->bdif->ph_bsize * blen; - p += bdev->ph_bsize * blen; - len -= bdev->ph_bsize * blen; + block_idx += blen; - block_idx += blen; + /*Rest of the data*/ + if (len) { + r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1); + if (r != EOK) + return r; + memcpy(p, bdev->bdif->ph_bbuf, len); + } - /*Rest of the data*/ - if(len){ - r = bdev->bread(bdev, bdev->ph_bbuf, block_idx, 1); - if(r != EOK) - return r; - - memcpy(p, bdev->ph_bbuf, len); - } + return r; +} - return r; +int ext4_block_cache_flush(struct ext4_blockdev *bdev) +{ + while (!SLIST_EMPTY(&bdev->bc->dirty_list)) { + int r; + struct ext4_buf *buf = SLIST_FIRST(&bdev->bc->dirty_list); + ext4_assert(buf); + r = ext4_block_flush_buf(bdev, buf); + if (r != EOK) + return r; + + } + return EOK; } -int ext4_block_cache_write_back(struct ext4_blockdev *bdev, - uint8_t on_off) +int ext4_block_cache_write_back(struct ext4_blockdev *bdev, uint8_t on_off) { - int r; - uint32_t i; - - if(on_off) - bdev->cache_write_back++; - - if(!on_off && bdev->cache_write_back) - bdev->cache_write_back--; - - /*Flush all delayed cache blocks*/ - if(!bdev->cache_write_back){ - for (i = 0; i < bdev->bc->cnt; ++i) { - - /*Check if buffer free was delayed.*/ - if(!bdev->bc->free_delay[i]) - continue; - - /*Check reference counter.*/ - if(bdev->bc->refctr[i]) - continue; - - /*Buffer free was delayed and have no reference. Flush it.*/ - r = ext4_blocks_set_direct(bdev, - bdev->bc->data + bdev->bc->itemsize * i, - bdev->bc->lba[i], 1); - if(r != EOK) - return r; - - /*No delayed anymore*/ - bdev->bc->free_delay[i] = 0; - - /*Reduce refered block count*/ - bdev->bc->ref_blocks--; - } - } - return EOK; + if (on_off) + bdev->cache_write_back++; + + if (!on_off && bdev->cache_write_back) + bdev->cache_write_back--; + + if (bdev->cache_write_back) + return EOK; + + /*Flush data in all delayed cache blocks*/ + return ext4_block_cache_flush(bdev); } /** * @} */ -