#include "ext4_config.h"
#include "ext4_bcache.h"
+#include "ext4_blockdev.h"
#include "ext4_debug.h"
#include "ext4_errno.h"
return EOK;
}
+void ext4_bcache_cleanup(struct ext4_bcache *bc)
+{
+ struct ext4_buf *buf, *tmp;
+ RB_FOREACH_SAFE(buf, ext4_buf_lba, &bc->lba_root, tmp) {
+ ext4_block_flush_buf(bc->bdev, buf);
+ ext4_bcache_drop_buf(bc, buf);
+ }
+}
+
int ext4_bcache_fini_dynamic(struct ext4_bcache *bc)
{
memset(bc, 0, sizeof(struct ext4_bcache));
void ext4_bcache_drop_buf(struct ext4_bcache *bc, struct ext4_buf *buf)
{
- /*Cannot drop any referenced buffers.*/
- ext4_assert(!buf->refctr);
+ /* Warn on dropping any referenced buffers.*/
+ if (buf->refctr) {
+ ext4_dbg(DEBUG_BCACHE, DBG_WARN "Buffer is still referenced. "
+ "lba: %" PRIu64 ", refctr: %" PRIu32 "\n",
+ buf->lba, buf->refctr);
+ } else
+ RB_REMOVE(ext4_buf_lru, &bc->lru_root, buf);
RB_REMOVE(ext4_buf_lba, &bc->lba_root, buf);
- RB_REMOVE(ext4_buf_lru, &bc->lru_root, buf);
/*Forcibly drop dirty buffer.*/
if (ext4_bcache_test_flag(buf, BC_DIRTY))
- SLIST_REMOVE(&bc->dirty_list,
- buf,
- ext4_buf,
- dirty_node);
+ ext4_bcache_remove_dirty_node(bc, buf);
ext4_buf_free(buf);
bc->ref_blocks--;
}
-int ext4_bcache_alloc(struct ext4_bcache *bc, struct ext4_block *b,
- bool *is_new)
+void ext4_bcache_invalidate_lba(struct ext4_bcache *bc,
+ uint64_t from,
+ uint32_t cnt)
{
- /* Try to search the buffer with exaxt LBA. */
- struct ext4_buf *buf = ext4_buf_lookup(bc, b->lb_id);
+ uint64_t end = from + cnt - 1;
+ struct ext4_buf *tmp = ext4_buf_lookup(bc, from), *buf;
+ RB_FOREACH_FROM(buf, ext4_buf_lba, tmp) {
+ if (buf->lba > end)
+ break;
+
+ /* Clear both dirty and up-to-date flags. */
+ if (ext4_bcache_test_flag(buf, BC_DIRTY))
+ ext4_bcache_remove_dirty_node(bc, buf);
+
+ ext4_bcache_clear_dirty(buf);
+ }
+}
+
+struct ext4_buf *
+ext4_bcache_find_get(struct ext4_bcache *bc, struct ext4_block *b,
+ uint64_t lba)
+{
+ struct ext4_buf *buf = ext4_buf_lookup(bc, lba);
if (buf) {
/* If buffer is not referenced. */
if (!buf->refctr) {
buf->lru_id = ++bc->lru_ctr;
RB_REMOVE(ext4_buf_lru, &bc->lru_root, buf);
if (ext4_bcache_test_flag(buf, BC_DIRTY))
- SLIST_REMOVE(&bc->dirty_list,
- buf,
- ext4_buf,
- dirty_node);
+ ext4_bcache_remove_dirty_node(bc, buf);
}
- buf->refctr++;
+ ext4_bcache_inc_ref(buf);
- b->uptodate = ext4_bcache_test_flag(buf, BC_UPTODATE);
- /* Right now we don't propagate the dirty flag from ext4_buf to
- * ext4_block. */
- b->dirty = false;
b->buf = buf;
b->data = buf->data;
+ }
+ return buf;
+}
+int ext4_bcache_alloc(struct ext4_bcache *bc, struct ext4_block *b,
+ bool *is_new)
+{
+ /* Try to search the buffer with exaxt LBA. */
+ struct ext4_buf *buf = ext4_bcache_find_get(bc, b, b->lb_id);
+ if (buf) {
*is_new = false;
return EOK;
}
/* One more buffer in bcache now. :-) */
bc->ref_blocks++;
- buf->refctr = 1;
+ ext4_bcache_inc_ref(buf);
/* Assign new value to LRU id and increment LRU counter
* by 1*/
buf->lru_id = ++bc->lru_ctr;
- b->uptodate = false;
- b->dirty = false;
b->buf = buf;
b->data = buf->data;
ext4_assert(buf->refctr);
/*Just decrease reference counter*/
- buf->refctr--;
-
- /* If buffer is modified, buf will be mark up-to-date and dirty. */
- if (b->dirty) {
- ext4_bcache_set_flag(buf, BC_DIRTY);
- ext4_bcache_set_flag(buf, BC_UPTODATE);
- b->uptodate = true;
- }
- /* Someone might want to drop this buffer from bcache. */
- if (!b->uptodate)
- ext4_bcache_clear_flag(buf, BC_UPTODATE);
+ ext4_bcache_dec_ref(buf);
/* We are the last one touching this buffer, do the cleanups. */
if (!buf->refctr) {
RB_INSERT(ext4_buf_lru, &bc->lru_root, buf);
/* This buffer is ready to be flushed. */
- if (ext4_bcache_test_flag(buf, BC_DIRTY))
- SLIST_INSERT_HEAD(&bc->dirty_list, buf, dirty_node);
+ if (ext4_bcache_test_flag(buf, BC_DIRTY) &&
+ ext4_bcache_test_flag(buf, BC_UPTODATE)) {
+ if (bc->bdev->cache_write_back &&
+ !ext4_bcache_test_flag(buf, BC_FLUSH))
+ ext4_bcache_insert_dirty_node(bc, buf);
+ else {
+ ext4_block_flush_buf(bc->bdev, buf);
+ ext4_bcache_clear_flag(buf, BC_FLUSH);
+ }
+ }
/* The buffer is invalidated...drop it. */
if (!ext4_bcache_test_flag(buf, BC_UPTODATE))
b->lb_id = 0;
b->data = 0;
- b->uptodate = false;
- b->dirty = false;
return EOK;
}
return (bc->cnt <= bc->ref_blocks);
}
+
/**
* @}
*/