static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) { BDRVQcowState *s = bs->opaque; int ret = 0; if (!c->entries[i].dirty || !c->entries[i].offset) { return 0; } trace_qcow2_cache_entry_flush(qemu_coroutine_self(), c == s->l2_table_cache, i); if (c->depends) { ret = qcow2_cache_flush_dependency(bs, c); } else if (c->depends_on_flush) { ret = bdrv_flush(bs->file); if (ret >= 0) { c->depends_on_flush = false; } } if (ret < 0) { return ret; } if (c == s->refcount_block_cache) { ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK, c->entries[i].offset, s->cluster_size); } else if (c == s->l2_table_cache) { ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2, c->entries[i].offset, s->cluster_size); } else { ret = qcow2_pre_write_overlap_check(bs, 0, c->entries[i].offset, s->cluster_size); } if (ret < 0) { return ret; } if (c == s->refcount_block_cache) { BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART); } else if (c == s->l2_table_cache) { BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); } ret = bdrv_pwrite(bs->file, c->entries[i].offset, qcow2_cache_get_table_addr(bs, c, i), s->cluster_size); if (ret < 0) { return ret; } c->entries[i].dirty = false; return 0; }
static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c, int i, int num_tables) { #if QEMU_MADV_DONTNEED != QEMU_MADV_INVALID BDRVQcow2State *s = bs->opaque; void *t = qcow2_cache_get_table_addr(bs, c, i); int align = getpagesize(); size_t mem_size = (size_t) s->cluster_size * num_tables; size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t; size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align); if (length > 0) { qemu_madvise((uint8_t *) t + offset, length, QEMU_MADV_DONTNEED); } #endif }
static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, void **table, bool read_from_disk) { BDRVQcowState *s = bs->opaque; int i; int ret; int lookup_index; uint64_t min_lru_counter = UINT64_MAX; int min_lru_index = -1; trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, offset, read_from_disk); /* Check if the table is already cached */ i = lookup_index = (offset / s->cluster_size * 4) % c->size; do { const Qcow2CachedTable *t = &c->entries[i]; if (t->offset == offset) { goto found; } if (t->ref == 0 && t->lru_counter < min_lru_counter) { min_lru_counter = t->lru_counter; min_lru_index = i; } if (++i == c->size) { i = 0; } } while (i != lookup_index); if (min_lru_index == -1) { /* This can't happen in current synchronous code, but leave the check * here as a reminder for whoever starts using AIO with the cache */ abort(); } /* Cache miss: write a table back and replace it */ i = min_lru_index; trace_qcow2_cache_get_replace_entry(qemu_coroutine_self(), c == s->l2_table_cache, i); if (i < 0) { return i; } ret = qcow2_cache_entry_flush(bs, c, i); if (ret < 0) { return ret; } trace_qcow2_cache_get_read(qemu_coroutine_self(), c == s->l2_table_cache, i); c->entries[i].offset = 0; if (read_from_disk) { if (c == s->l2_table_cache) { BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); } ret = bdrv_pread(bs->file, offset, qcow2_cache_get_table_addr(bs, c, i), s->cluster_size); if (ret < 0) { return ret; } } c->entries[i].offset = offset; /* And return the right table */ found: c->entries[i].ref++; *table = qcow2_cache_get_table_addr(bs, c, i); trace_qcow2_cache_get_done(qemu_coroutine_self(), c == s->l2_table_cache, i); return 0; }