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_DEFAULT & ~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_DEFAULT & ~QCOW2_OL_ACTIVE_L2, c->entries[i].offset, s->cluster_size); } else { ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, 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, c->entries[i].table, s->cluster_size); if (ret < 0) { return ret; } c->entries[i].dirty = false; return 0; }
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; trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, offset, read_from_disk); /* Check if the table is already cached */ for (i = 0; i < c->size; i++) { if (c->entries[i].offset == offset) { goto found; } } /* If not, write a table back and replace it */ i = qcow2_cache_find_entry_to_replace(c); 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, c->entries[i].table, s->cluster_size); if (ret < 0) { return ret; } } /* Give the table some hits for the start so that it won't be replaced * immediately. The number 32 is completely arbitrary. */ c->entries[i].cache_hits = 32; c->entries[i].offset = offset; /* And return the right table */ found: c->entries[i].cache_hits++; c->entries[i].ref++; *table = c->entries[i].table; trace_qcow2_cache_get_done(qemu_coroutine_self(), c == s->l2_table_cache, i); return 0; }
void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset, BlockDriverCompletionFunc *cb, void *opaque) { QEDReadL2TableCB *read_l2_table_cb; qed_unref_l2_cache_entry(request->l2_table); /* Check for cached L2 entry */ request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, offset); if (request->l2_table) { cb(opaque, 0); return; } request->l2_table = qed_alloc_l2_cache_entry(&s->l2_cache); request->l2_table->table = qed_alloc_table(s); read_l2_table_cb = gencb_alloc(sizeof(*read_l2_table_cb), cb, opaque); read_l2_table_cb->s = s; read_l2_table_cb->l2_offset = offset; read_l2_table_cb->request = request; BLKDBG_EVENT(s->bs->file, BLKDBG_L2_LOAD); qed_read_table(s, offset, request->l2_table->table, qed_read_l2_table_cb, read_l2_table_cb); }
void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n, BlockDriverCompletionFunc *cb, void *opaque) { BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE); qed_write_table(s, s->header.l1_table_offset, s->l1_table, index, n, false, cb, opaque); }
void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request, unsigned int index, unsigned int n, bool flush, BlockDriverCompletionFunc *cb, void *opaque) { BLKDBG_EVENT(s->bs->file, BLKDBG_L2_UPDATE); qed_write_table(s, request->l2_table->offset, request->l2_table->table, index, n, flush, cb, opaque); }
static int coroutine_fn raw_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int flags) { void *buf = NULL; BlockDriver *drv; QEMUIOVector local_qiov; int ret; if (bs->probed && sector_num == 0) { /* As long as these conditions are true, we can't get partial writes to * the probe buffer and can just directly check the request. */ QEMU_BUILD_BUG_ON(BLOCK_PROBE_BUF_SIZE != 512); QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != 512); if (nb_sectors == 0) { /* qemu_iovec_to_buf() would fail, but we want to return success * instead of -EINVAL in this case. */ return 0; } buf = qemu_try_blockalign(bs->file->bs, 512); if (!buf) { ret = -ENOMEM; goto fail; } ret = qemu_iovec_to_buf(qiov, 0, buf, 512); if (ret != 512) { ret = -EINVAL; goto fail; } drv = bdrv_probe_all(buf, 512, NULL); if (drv != bs->drv) { ret = -EPERM; goto fail; } /* Use the checked buffer, a malicious guest might be overwriting its * original buffer in the background. */ qemu_iovec_init(&local_qiov, qiov->niov + 1); qemu_iovec_add(&local_qiov, buf, 512); qemu_iovec_concat(&local_qiov, qiov, 512, qiov->size - 512); qiov = &local_qiov; } BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); ret = bdrv_co_do_pwritev(bs->file->bs, sector_num * BDRV_SECTOR_SIZE, nb_sectors * BDRV_SECTOR_SIZE, qiov, flags); fail: if (qiov == &local_qiov) { qemu_iovec_destroy(&local_qiov); } qemu_vfree(buf); return ret; }
static int load_refcount_block(BlockDriverState *bs, int64_t refcount_block_offset, void **refcount_block) { BDRVQcowState *s = bs->opaque; int ret; BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD); ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, refcount_block); return ret; }
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; } 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) { 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, c->entries[i].table, s->cluster_size); if (ret < 0) { return ret; } c->entries[i].dirty = false; return 0; }
static int write_refcount_block(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; size_t size = s->cluster_size; if (s->refcount_block_cache_offset == 0) { return 0; } BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE); if (bdrv_pwrite_sync(bs->file, s->refcount_block_cache_offset, s->refcount_block_cache, size) < 0) { return -EIO; } return 0; }
int qcow2_refcount_init(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; int ret, refcount_table_size2, i; refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t); s->refcount_table = g_malloc(refcount_table_size2); if (s->refcount_table_size > 0) { BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_LOAD); ret = bdrv_pread(bs->file, s->refcount_table_offset, s->refcount_table, refcount_table_size2); if (ret != refcount_table_size2) goto fail; for(i = 0; i < s->refcount_table_size; i++) be64_to_cpus(&s->refcount_table[i]); } return 0; fail: return -ENOMEM; }
static int load_refcount_block(BlockDriverState *bs, int64_t refcount_block_offset) { BDRVQcowState *s = bs->opaque; int ret; if (cache_refcount_updates) { ret = write_refcount_block(bs); if (ret < 0) { return ret; } } BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD); ret = bdrv_pread(bs->file, refcount_block_offset, s->refcount_block_cache, s->cluster_size); if (ret < 0) { return ret; } s->refcount_block_cache_offset = refcount_block_offset; return 0; }
static int coroutine_fn raw_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); return bdrv_co_readv(bs->file->bs, sector_num, nb_sectors, qiov); }
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; }