aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony Liguori <aliguori@us.ibm.com>2011-01-24 15:16:56 -0600
committerAnthony Liguori <aliguori@us.ibm.com>2011-01-24 15:16:56 -0600
commitb22b7b729d2d617c49381843362bc3ef336d66ec (patch)
tree2a6ddbcee95ca474bb0228ea8d7bece6524eecd0
parent4a2ba232843ac4197c586c1f0f0adbb1eb02fe34 (diff)
parenta5c062edd272a222179c2bbf54c539c992aefc93 (diff)
downloadqemu-b22b7b729d2d617c49381843362bc3ef336d66ec.zip
qemu-b22b7b729d2d617c49381843362bc3ef336d66ec.tar.gz
qemu-b22b7b729d2d617c49381843362bc3ef336d66ec.tar.bz2
Merge remote branch 'kwolf/for-anthony' into staging
-rw-r--r--Makefile.objs2
-rw-r--r--block-migration.c29
-rw-r--r--block.c8
-rw-r--r--block/qcow2-cache.c314
-rw-r--r--block/qcow2-cluster.c210
-rw-r--r--block/qcow2-refcount.c260
-rw-r--r--block/qcow2.c48
-rw-r--r--block/qcow2.h32
-rw-r--r--block/qed.c6
-rw-r--r--blockdev.c81
-rw-r--r--cutils.c8
-rw-r--r--docs/qdev-device-use.txt13
-rw-r--r--hw/ide/core.c113
-rw-r--r--hw/ide/internal.h4
-rw-r--r--hw/ide/pci.c13
-rw-r--r--hw/pci-hotplug.c2
-rw-r--r--hw/scsi-bus.c8
-rw-r--r--hw/scsi-disk.c3
-rw-r--r--hw/scsi.h3
-rw-r--r--hw/usb-msd.c4
-rw-r--r--monitor.c2
-rw-r--r--qemu-common.h4
-rw-r--r--qemu-img.c4
-rw-r--r--qemu-img.texi41
-rw-r--r--vl.c4
25 files changed, 764 insertions, 452 deletions
diff --git a/Makefile.objs b/Makefile.objs
index fda366d..93406ff 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -19,7 +19,7 @@ block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
block-nested-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o
-block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o
+block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
block-nested-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
block-nested-y += qed-check.o
block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o
diff --git a/block-migration.c b/block-migration.c
index 1475325..c9d3e81 100644
--- a/block-migration.c
+++ b/block-migration.c
@@ -350,7 +350,12 @@ static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f)
}
}
- progress = completed_sector_sum * 100 / block_mig_state.total_sector_sum;
+ if (block_mig_state.total_sector_sum != 0) {
+ progress = completed_sector_sum * 100 /
+ block_mig_state.total_sector_sum;
+ } else {
+ progress = 100;
+ }
if (progress != block_mig_state.prev_progress) {
block_mig_state.prev_progress = progress;
qemu_put_be64(f, (progress << BDRV_SECTOR_BITS)
@@ -633,8 +638,10 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)
int len, flags;
char device_name[256];
int64_t addr;
- BlockDriverState *bs;
+ BlockDriverState *bs, *bs_prev = NULL;
uint8_t *buf;
+ int64_t total_sectors = 0;
+ int nr_sectors;
do {
addr = qemu_get_be64(f);
@@ -656,10 +663,26 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)
return -EINVAL;
}
+ if (bs != bs_prev) {
+ bs_prev = bs;
+ total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
+ if (total_sectors <= 0) {
+ error_report("Error getting length of block device %s\n",
+ device_name);
+ return -EINVAL;
+ }
+ }
+
+ if (total_sectors - addr < BDRV_SECTORS_PER_DIRTY_CHUNK) {
+ nr_sectors = total_sectors - addr;
+ } else {
+ nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
+ }
+
buf = qemu_malloc(BLOCK_SIZE);
qemu_get_buffer(f, buf, BLOCK_SIZE);
- ret = bdrv_write(bs, addr, buf, BDRV_SECTORS_PER_DIRTY_CHUNK);
+ ret = bdrv_write(bs, addr, buf, nr_sectors);
qemu_free(buf);
if (ret < 0) {
diff --git a/block.c b/block.c
index ff2795b..7ad3ddf 100644
--- a/block.c
+++ b/block.c
@@ -2778,6 +2778,7 @@ int bdrv_img_create(const char *filename, const char *fmt,
QEMUOptionParameter *backing_fmt, *backing_file;
BlockDriverState *bs = NULL;
BlockDriver *drv, *proto_drv;
+ BlockDriver *backing_drv = NULL;
int ret = 0;
/* Find driver and parse its options */
@@ -2846,7 +2847,8 @@ int bdrv_img_create(const char *filename, const char *fmt,
backing_fmt = get_option_parameter(param, BLOCK_OPT_BACKING_FMT);
if (backing_fmt && backing_fmt->value.s) {
- if (!bdrv_find_format(backing_fmt->value.s)) {
+ backing_drv = bdrv_find_format(backing_fmt->value.s);
+ if (!backing_drv) {
error_report("Unknown backing file format '%s'",
backing_fmt->value.s);
ret = -EINVAL;
@@ -2863,9 +2865,9 @@ int bdrv_img_create(const char *filename, const char *fmt,
bs = bdrv_new("");
- ret = bdrv_open(bs, backing_file->value.s, flags, drv);
+ ret = bdrv_open(bs, backing_file->value.s, flags, backing_drv);
if (ret < 0) {
- error_report("Could not open '%s'", filename);
+ error_report("Could not open '%s'", backing_file->value.s);
goto out;
}
bdrv_get_geometry(bs, &size);
diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c
new file mode 100644
index 0000000..8f2955b
--- /dev/null
+++ b/block/qcow2-cache.c
@@ -0,0 +1,314 @@
+/*
+ * L2/refcount table cache for the QCOW2 format
+ *
+ * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "block_int.h"
+#include "qemu-common.h"
+#include "qcow2.h"
+
+typedef struct Qcow2CachedTable {
+ void* table;
+ int64_t offset;
+ bool dirty;
+ int cache_hits;
+ int ref;
+} Qcow2CachedTable;
+
+struct Qcow2Cache {
+ int size;
+ Qcow2CachedTable* entries;
+ struct Qcow2Cache* depends;
+ bool depends_on_flush;
+ bool writethrough;
+};
+
+Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
+ bool writethrough)
+{
+ BDRVQcowState *s = bs->opaque;
+ Qcow2Cache *c;
+ int i;
+
+ c = qemu_mallocz(sizeof(*c));
+ c->size = num_tables;
+ c->entries = qemu_mallocz(sizeof(*c->entries) * num_tables);
+ c->writethrough = writethrough;
+
+ for (i = 0; i < c->size; i++) {
+ c->entries[i].table = qemu_blockalign(bs, s->cluster_size);
+ }
+
+ return c;
+}
+
+int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c)
+{
+ int i;
+
+ for (i = 0; i < c->size; i++) {
+ assert(c->entries[i].ref == 0);
+ qemu_vfree(c->entries[i].table);
+ }
+
+ qemu_free(c->entries);
+ qemu_free(c);
+
+ return 0;
+}
+
+static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c)
+{
+ int ret;
+
+ ret = qcow2_cache_flush(bs, c->depends);
+ if (ret < 0) {
+ return ret;
+ }
+
+ c->depends = NULL;
+ c->depends_on_flush = false;
+
+ return 0;
+}
+
+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;
+}
+
+int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c)
+{
+ int result = 0;
+ int ret;
+ int i;
+
+ for (i = 0; i < c->size; i++) {
+ ret = qcow2_cache_entry_flush(bs, c, i);
+ if (ret < 0 && result != -ENOSPC) {
+ result = ret;
+ }
+ }
+
+ if (result == 0) {
+ ret = bdrv_flush(bs->file);
+ if (ret < 0) {
+ result = ret;
+ }
+ }
+
+ return result;
+}
+
+int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c,
+ Qcow2Cache *dependency)
+{
+ int ret;
+
+ if (dependency->depends) {
+ ret = qcow2_cache_flush_dependency(bs, dependency);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ if (c->depends && (c->depends != dependency)) {
+ ret = qcow2_cache_flush_dependency(bs, c);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ c->depends = dependency;
+ return 0;
+}
+
+void qcow2_cache_depends_on_flush(Qcow2Cache *c)
+{
+ c->depends_on_flush = true;
+}
+
+static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c)
+{
+ int i;
+ int min_count = INT_MAX;
+ int min_index = -1;
+
+
+ for (i = 0; i < c->size; i++) {
+ if (c->entries[i].ref) {
+ continue;
+ }
+
+ if (c->entries[i].cache_hits < min_count) {
+ min_index = i;
+ min_count = c->entries[i].cache_hits;
+ }
+
+ /* Give newer hits priority */
+ /* TODO Check how to optimize the replacement strategy */
+ c->entries[i].cache_hits /= 2;
+ }
+
+ if (min_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();
+ }
+ return min_index;
+}
+
+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;
+
+ /* 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);
+ if (i < 0) {
+ return i;
+ }
+
+ ret = qcow2_cache_entry_flush(bs, c, i);
+ if (ret < 0) {
+ return ret;
+ }
+
+ 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;
+ return 0;
+}
+
+int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
+ void **table)
+{
+ return qcow2_cache_do_get(bs, c, offset, table, true);
+}
+
+int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
+ void **table)
+{
+ return qcow2_cache_do_get(bs, c, offset, table, false);
+}
+
+int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table)
+{
+ int i;
+
+ for (i = 0; i < c->size; i++) {
+ if (c->entries[i].table == *table) {
+ goto found;
+ }
+ }
+ return -ENOENT;
+
+found:
+ c->entries[i].ref--;
+ *table = NULL;
+
+ assert(c->entries[i].ref >= 0);
+
+ if (c->writethrough) {
+ return qcow2_cache_entry_flush(bs, c, i);
+ } else {
+ return 0;
+ }
+}
+
+void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table)
+{
+ int i;
+
+ for (i = 0; i < c->size; i++) {
+ if (c->entries[i].table == table) {
+ goto found;
+ }
+ }
+ abort();
+
+found:
+ c->entries[i].dirty = true;
+}
+
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 6928c63..1c2003a 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -67,7 +67,11 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size)
qemu_free(new_l1_table);
return new_l1_table_offset;
}
- bdrv_flush(bs->file);
+
+ ret = qcow2_cache_flush(bs, s->refcount_block_cache);
+ if (ret < 0) {
+ return ret;
+ }
BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE);
for(i = 0; i < s->l1_size; i++)
@@ -81,7 +85,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size)
/* set new table */
BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE);
cpu_to_be32w((uint32_t*)data, new_l1_size);
- cpu_to_be64w((uint64_t*)(data + 4), new_l1_table_offset);
+ cpu_to_be64wu((uint64_t*)(data + 4), new_l1_table_offset);
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), data,sizeof(data));
if (ret < 0) {
goto fail;
@@ -98,63 +102,6 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size)
return ret;
}
-void qcow2_l2_cache_reset(BlockDriverState *bs)
-{
- BDRVQcowState *s = bs->opaque;
-
- memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
- memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t));
- memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t));
-}
-
-static inline int l2_cache_new_entry(BlockDriverState *bs)
-{
- BDRVQcowState *s = bs->opaque;
- uint32_t min_count;
- int min_index, i;
-
- /* find a new entry in the least used one */
- min_index = 0;
- min_count = 0xffffffff;
- for(i = 0; i < L2_CACHE_SIZE; i++) {
- if (s->l2_cache_counts[i] < min_count) {
- min_count = s->l2_cache_counts[i];
- min_index = i;
- }
- }
- return min_index;
-}
-
-/*
- * seek_l2_table
- *
- * seek l2_offset in the l2_cache table
- * if not found, return NULL,
- * if found,
- * increments the l2 cache hit count of the entry,
- * if counter overflow, divide by two all counters
- * return the pointer to the l2 cache entry
- *
- */
-
-static uint64_t *seek_l2_table(BDRVQcowState *s, uint64_t l2_offset)
-{
- int i, j;
-
- for(i = 0; i < L2_CACHE_SIZE; i++) {
- if (l2_offset == s->l2_cache_offsets[i]) {
- /* increment the hit count */
- if (++s->l2_cache_counts[i] == 0xffffffff) {
- for(j = 0; j < L2_CACHE_SIZE; j++) {
- s->l2_cache_counts[j] >>= 1;
- }
- }
- return s->l2_cache + (i << s->l2_bits);
- }
- }
- return NULL;
-}
-
/*
* l2_load
*
@@ -169,33 +116,11 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset,
uint64_t **l2_table)
{
BDRVQcowState *s = bs->opaque;
- int min_index;
int ret;
- /* seek if the table for the given offset is in the cache */
-
- *l2_table = seek_l2_table(s, l2_offset);
- if (*l2_table != NULL) {
- return 0;
- }
-
- /* not found: load a new entry in the least used one */
-
- min_index = l2_cache_new_entry(bs);
- *l2_table = s->l2_cache + (min_index << s->l2_bits);
-
- BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD);
- ret = bdrv_pread(bs->file, l2_offset, *l2_table,
- s->l2_size * sizeof(uint64_t));
- if (ret < 0) {
- qcow2_l2_cache_reset(bs);
- return ret;
- }
+ ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) l2_table);
- s->l2_cache_offsets[min_index] = l2_offset;
- s->l2_cache_counts[min_index] = 1;
-
- return 0;
+ return ret;
}
/*
@@ -238,7 +163,6 @@ static int write_l1_entry(BlockDriverState *bs, int l1_index)
static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
{
BDRVQcowState *s = bs->opaque;
- int min_index;
uint64_t old_l2_offset;
uint64_t *l2_table;
int64_t l2_offset;
@@ -252,29 +176,48 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
if (l2_offset < 0) {
return l2_offset;
}
- bdrv_flush(bs->file);
+
+ ret = qcow2_cache_flush(bs, s->refcount_block_cache);
+ if (ret < 0) {
+ goto fail;
+ }
/* allocate a new entry in the l2 cache */
- min_index = l2_cache_new_entry(bs);
- l2_table = s->l2_cache + (min_index << s->l2_bits);
+ ret = qcow2_cache_get_empty(bs, s->l2_table_cache, l2_offset, (void**) table);
+ if (ret < 0) {
+ return ret;
+ }
+
+ l2_table = *table;
if (old_l2_offset == 0) {
/* if there was no old l2 table, clear the new table */
memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
} else {
+ uint64_t* old_table;
+
/* if there was an old l2 table, read it from the disk */
BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ);
- ret = bdrv_pread(bs->file, old_l2_offset, l2_table,
- s->l2_size * sizeof(uint64_t));
+ ret = qcow2_cache_get(bs, s->l2_table_cache, old_l2_offset,
+ (void**) &old_table);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ memcpy(l2_table, old_table, s->cluster_size);
+
+ ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &old_table);
if (ret < 0) {
goto fail;
}
}
+
/* write the l2 table to the file */
BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE);
- ret = bdrv_pwrite_sync(bs->file, l2_offset, l2_table,
- s->l2_size * sizeof(uint64_t));
+
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
+ ret = qcow2_cache_flush(bs, s->l2_table_cache);
if (ret < 0) {
goto fail;
}
@@ -286,17 +229,12 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
goto fail;
}
- /* update the l2 cache entry */
-
- s->l2_cache_offsets[min_index] = l2_offset;
- s->l2_cache_counts[min_index] = 1;
-
*table = l2_table;
return 0;
fail:
+ qcow2_cache_put(bs, s->l2_table_cache, (void**) table);
s->l1_table[l1_index] = old_l2_offset;
- qcow2_l2_cache_reset(bs);
return ret;
}
@@ -521,6 +459,8 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
&l2_table[l2_index], 0, QCOW_OFLAG_COPIED);
}
+ qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+
nb_available = (c * s->cluster_sectors);
out:
if (nb_available > nb_needed)
@@ -575,6 +515,7 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
return ret;
}
} else {
+ /* FIXME Order */
if (l2_offset)
qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t));
ret = l2_allocate(bs, l1_index, &l2_table);
@@ -632,6 +573,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
cluster_offset = qcow2_alloc_bytes(bs, compressed_size);
if (cluster_offset < 0) {
+ qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
return 0;
}
@@ -646,38 +588,14 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
/* compressed clusters never have the copied flag */
BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED);
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
l2_table[l2_index] = cpu_to_be64(cluster_offset);
- if (bdrv_pwrite_sync(bs->file,
- l2_offset + l2_index * sizeof(uint64_t),
- l2_table + l2_index,
- sizeof(uint64_t)) < 0)
- return 0;
-
- return cluster_offset;
-}
-
-/*
- * Write L2 table updates to disk, writing whole sectors to avoid a
- * read-modify-write in bdrv_pwrite
- */
-#define L2_ENTRIES_PER_SECTOR (512 / 8)
-static int write_l2_entries(BlockDriverState *bs, uint64_t *l2_table,
- uint64_t l2_offset, int l2_index, int num)
-{
- int l2_start_index = l2_index & ~(L1_ENTRIES_PER_SECTOR - 1);
- int start_offset = (8 * l2_index) & ~511;
- int end_offset = (8 * (l2_index + num) + 511) & ~511;
- size_t len = end_offset - start_offset;
- int ret;
-
- BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE);
- ret = bdrv_pwrite(bs->file, l2_offset + start_offset,
- &l2_table[l2_start_index], len);
+ ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
if (ret < 0) {
- return ret;
+ return 0;
}
- return 0;
+ return cluster_offset;
}
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
@@ -686,6 +604,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
int i, j = 0, l2_index, ret;
uint64_t *old_cluster, start_sect, l2_offset, *l2_table;
uint64_t cluster_offset = m->cluster_offset;
+ bool cow = false;
if (m->nb_clusters == 0)
return 0;
@@ -695,6 +614,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
/* copy content of unmodified sectors */
start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9;
if (m->n_start) {
+ cow = true;
ret = copy_sectors(bs, start_sect, cluster_offset, 0, m->n_start);
if (ret < 0)
goto err;
@@ -702,17 +622,30 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
if (m->nb_available & (s->cluster_sectors - 1)) {
uint64_t end = m->nb_available & ~(uint64_t)(s->cluster_sectors - 1);
+ cow = true;
ret = copy_sectors(bs, start_sect + end, cluster_offset + (end << 9),
m->nb_available - end, s->cluster_sectors);
if (ret < 0)
goto err;
}
- /* update L2 table */
+ /*
+ * Update L2 table.
+ *
+ * Before we update the L2 table to actually point to the new cluster, we
+ * need to be sure that the refcounts have been increased and COW was
+ * handled.
+ */
+ if (cow) {
+ qcow2_cache_depends_on_flush(s->l2_table_cache);
+ }
+
+ qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache);
ret = get_cluster_table(bs, m->offset, &l2_table, &l2_offset, &l2_index);
if (ret < 0) {
goto err;
}
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
for (i = 0; i < m->nb_clusters; i++) {
/* if two concurrent writes happen to the same unallocated cluster
@@ -728,16 +661,9 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
(i << s->cluster_bits)) | QCOW_OFLAG_COPIED);
}
- /*
- * Before we update the L2 table to actually point to the new cluster, we
- * need to be sure that the refcounts have been increased and COW was
- * handled.
- */
- bdrv_flush(bs->file);
- ret = write_l2_entries(bs, l2_table, l2_offset, l2_index, m->nb_clusters);
+ ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
if (ret < 0) {
- qcow2_l2_cache_reset(bs);
goto err;
}
@@ -746,7 +672,6 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
* Also flush bs->file to get the right order for L2 and refcount update.
*/
if (j != 0) {
- bdrv_flush(bs->file);
for (i = 0; i < j; i++) {
qcow2_free_any_clusters(bs,
be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1);
@@ -868,7 +793,8 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
m->depends_on = old_alloc;
m->nb_clusters = 0;
*num = 0;
- return 0;
+ ret = 0;
+ goto fail;
}
}
}
@@ -884,7 +810,8 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size);
if (cluster_offset < 0) {
QLIST_REMOVE(m, next_in_flight);
- return cluster_offset;
+ ret = cluster_offset;
+ goto fail;
}
/* save info needed for meta data update */
@@ -893,12 +820,21 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
m->nb_clusters = nb_clusters;
out:
+ ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ if (ret < 0) {
+ return ret;
+ }
+
m->nb_available = MIN(nb_clusters << (s->cluster_bits - 9), n_end);
m->cluster_offset = cluster_offset;
*num = m->nb_available - n_start;
return 0;
+
+fail:
+ qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ return ret;
}
static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index a10453c..e37e226 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -32,27 +32,6 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
int addend);
-static int cache_refcount_updates = 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;
-}
-
/*********************************************************/
/* refcount handling */
@@ -61,7 +40,6 @@ int qcow2_refcount_init(BlockDriverState *bs)
BDRVQcowState *s = bs->opaque;
int ret, refcount_table_size2, i;
- s->refcount_block_cache = qemu_malloc(s->cluster_size);
refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t);
s->refcount_table = qemu_malloc(refcount_table_size2);
if (s->refcount_table_size > 0) {
@@ -81,34 +59,22 @@ int qcow2_refcount_init(BlockDriverState *bs)
void qcow2_refcount_close(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
- qemu_free(s->refcount_block_cache);
qemu_free(s->refcount_table);
}
static int load_refcount_block(BlockDriverState *bs,
- int64_t refcount_block_offset)
+ int64_t refcount_block_offset,
+ void **refcount_block)
{
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) {
- s->refcount_block_cache_offset = 0;
- return ret;
- }
+ ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset,
+ refcount_block);
- s->refcount_block_cache_offset = refcount_block_offset;
- return 0;
+ return ret;
}
/*
@@ -122,6 +88,8 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
int refcount_table_index, block_index;
int64_t refcount_block_offset;
int ret;
+ uint16_t *refcount_block;
+ uint16_t refcount;
refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
if (refcount_table_index >= s->refcount_table_size)
@@ -129,16 +97,24 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
refcount_block_offset = s->refcount_table[refcount_table_index];
if (!refcount_block_offset)
return 0;
- if (refcount_block_offset != s->refcount_block_cache_offset) {
- /* better than nothing: return allocated if read error */
- ret = load_refcount_block(bs, refcount_block_offset);
- if (ret < 0) {
- return ret;
- }
+
+ ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset,
+ (void**) &refcount_block);
+ if (ret < 0) {
+ return ret;
}
+
block_index = cluster_index &
((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
- return be16_to_cpu(s->refcount_block_cache[block_index]);
+ refcount = be16_to_cpu(refcount_block[block_index]);
+
+ ret = qcow2_cache_put(bs, s->refcount_block_cache,
+ (void**) &refcount_block);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return refcount;
}
/*
@@ -174,9 +150,10 @@ static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a,
* Loads a refcount block. If it doesn't exist yet, it is allocated first
* (including growing the refcount table if needed).
*
- * Returns the offset of the refcount block on success or -errno in error case
+ * Returns 0 on success or -errno in error case
*/
-static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
+static int alloc_refcount_block(BlockDriverState *bs,
+ int64_t cluster_index, uint16_t **refcount_block)
{
BDRVQcowState *s = bs->opaque;
unsigned int refcount_table_index;
@@ -194,13 +171,8 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
/* If it's already there, we're done */
if (refcount_block_offset) {
- if (refcount_block_offset != s->refcount_block_cache_offset) {
- ret = load_refcount_block(bs, refcount_block_offset);
- if (ret < 0) {
- return ret;
- }
- }
- return refcount_block_offset;
+ return load_refcount_block(bs, refcount_block_offset,
+ (void**) refcount_block);
}
}
@@ -226,12 +198,10 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
* refcount block into the cache
*/
- if (cache_refcount_updates) {
- ret = write_refcount_block(bs);
- if (ret < 0) {
- return ret;
- }
- }
+ *refcount_block = NULL;
+
+ /* We write to the refcount table, so we might depend on L2 tables */
+ qcow2_cache_flush(bs, s->l2_table_cache);
/* Allocate the refcount block itself and mark it as used */
int64_t new_block = alloc_clusters_noref(bs, s->cluster_size);
@@ -247,13 +217,18 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) {
/* Zero the new refcount block before updating it */
- memset(s->refcount_block_cache, 0, s->cluster_size);
- s->refcount_block_cache_offset = new_block;
+ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block,
+ (void**) refcount_block);
+ if (ret < 0) {
+ goto fail_block;
+ }
+
+ memset(*refcount_block, 0, s->cluster_size);
/* The block describes itself, need to update the cache */
int block_index = (new_block >> s->cluster_bits) &
((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
- s->refcount_block_cache[block_index] = cpu_to_be16(1);
+ (*refcount_block)[block_index] = cpu_to_be16(1);
} else {
/* Described somewhere else. This can recurse at most twice before we
* arrive at a block that describes itself. */
@@ -266,14 +241,19 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
/* Initialize the new refcount block only after updating its refcount,
* update_refcount uses the refcount cache itself */
- memset(s->refcount_block_cache, 0, s->cluster_size);
- s->refcount_block_cache_offset = new_block;
+ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block,
+ (void**) refcount_block);
+ if (ret < 0) {
+ goto fail_block;
+ }
+
+ memset(*refcount_block, 0, s->cluster_size);
}
/* Now the new refcount block needs to be written to disk */
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE);
- ret = bdrv_pwrite_sync(bs->file, new_block, s->refcount_block_cache,
- s->cluster_size);
+ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, *refcount_block);
+ ret = qcow2_cache_flush(bs, s->refcount_block_cache);
if (ret < 0) {
goto fail_block;
}
@@ -290,7 +270,12 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
}
s->refcount_table[refcount_table_index] = new_block;
- return new_block;
+ return 0;
+ }
+
+ ret = qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block);
+ if (ret < 0) {
+ goto fail_block;
}
/*
@@ -410,9 +395,9 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t));
s->free_cluster_index = old_free_cluster_index;
- ret = load_refcount_block(bs, new_block);
+ ret = load_refcount_block(bs, new_block, (void**) refcount_block);
if (ret < 0) {
- goto fail_block;
+ return ret;
}
return new_block;
@@ -420,41 +405,10 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
fail_table:
qemu_free(new_table);
fail_block:
- s->refcount_block_cache_offset = 0;
- return ret;
-}
-
-#define REFCOUNTS_PER_SECTOR (512 >> REFCOUNT_SHIFT)
-static int write_refcount_block_entries(BlockDriverState *bs,
- int64_t refcount_block_offset, int first_index, int last_index)
-{
- BDRVQcowState *s = bs->opaque;
- size_t size;
- int ret;
-
- if (cache_refcount_updates) {
- return 0;
- }
-
- if (first_index < 0) {
- return 0;
- }
-
- first_index &= ~(REFCOUNTS_PER_SECTOR - 1);
- last_index = (last_index + REFCOUNTS_PER_SECTOR)
- & ~(REFCOUNTS_PER_SECTOR - 1);
-
- size = (last_index - first_index) << REFCOUNT_SHIFT;
-
- BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART);
- ret = bdrv_pwrite(bs->file,
- refcount_block_offset + (first_index << REFCOUNT_SHIFT),
- &s->refcount_block_cache[first_index], size);
- if (ret < 0) {
- return ret;
+ if (*refcount_block != NULL) {
+ qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block);
}
-
- return 0;
+ return ret;
}
/* XXX: cache several refcount block clusters ? */
@@ -463,9 +417,8 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
{
BDRVQcowState *s = bs->opaque;
int64_t start, last, cluster_offset;
- int64_t refcount_block_offset = 0;
- int64_t table_index = -1, old_table_index;
- int first_index = -1, last_index = -1;
+ uint16_t *refcount_block = NULL;
+ int64_t old_table_index = -1;
int ret;
#ifdef DEBUG_ALLOC2
@@ -478,6 +431,11 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
return 0;
}
+ if (addend < 0) {
+ qcow2_cache_set_dependency(bs, s->refcount_block_cache,
+ s->l2_table_cache);
+ }
+
start = offset & ~(s->cluster_size - 1);
last = (offset + length - 1) & ~(s->cluster_size - 1);
for(cluster_offset = start; cluster_offset <= last;
@@ -485,42 +443,33 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
{
int block_index, refcount;
int64_t cluster_index = cluster_offset >> s->cluster_bits;
- int64_t new_block;
+ int64_t table_index =
+ cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
- /* Only write refcount block to disk when we are done with it */
- old_table_index = table_index;
- table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
- if ((old_table_index >= 0) && (table_index != old_table_index)) {
+ /* Load the refcount block and allocate it if needed */
+ if (table_index != old_table_index) {
+ if (refcount_block) {
+ ret = qcow2_cache_put(bs, s->refcount_block_cache,
+ (void**) &refcount_block);
+ if (ret < 0) {
+ goto fail;
+ }
+ }
- ret = write_refcount_block_entries(bs, refcount_block_offset,
- first_index, last_index);
+ ret = alloc_refcount_block(bs, cluster_index, &refcount_block);
if (ret < 0) {
- return ret;
+ goto fail;
}
-
- first_index = -1;
- last_index = -1;
}
+ old_table_index = table_index;
- /* Load the refcount block and allocate it if needed */
- new_block = alloc_refcount_block(bs, cluster_index);
- if (new_block < 0) {
- ret = new_block;
- goto fail;
- }
- refcount_block_offset = new_block;
+ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refcount_block);
/* we can update the count and save it */
block_index = cluster_index &
((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
- if (first_index == -1 || block_index < first_index) {
- first_index = block_index;
- }
- if (block_index > last_index) {
- last_index = block_index;
- }
- refcount = be16_to_cpu(s->refcount_block_cache[block_index]);
+ refcount = be16_to_cpu(refcount_block[block_index]);
refcount += addend;
if (refcount < 0 || refcount > 0xffff) {
ret = -EINVAL;
@@ -529,17 +478,16 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
if (refcount == 0 && cluster_index < s->free_cluster_index) {
s->free_cluster_index = cluster_index;
}
- s->refcount_block_cache[block_index] = cpu_to_be16(refcount);
+ refcount_block[block_index] = cpu_to_be16(refcount);
}
ret = 0;
fail:
-
/* Write last changed block to disk */
- if (refcount_block_offset != 0) {
+ if (refcount_block) {
int wret;
- wret = write_refcount_block_entries(bs, refcount_block_offset,
- first_index, last_index);
+ wret = qcow2_cache_put(bs, s->refcount_block_cache,
+ (void**) &refcount_block);
if (wret < 0) {
return ret < 0 ? ret : wret;
}
@@ -758,9 +706,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, l1_allocated;
int64_t old_offset, old_l2_offset;
int l2_size, i, j, l1_modified, l2_modified, nb_csectors, refcount;
-
- qcow2_l2_cache_reset(bs);
- cache_refcount_updates = 1;
+ int ret;
l2_table = NULL;
l1_table = NULL;
@@ -784,7 +730,6 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
}
l2_size = s->l2_size * sizeof(uint64_t);
- l2_table = qemu_malloc(l2_size);
l1_modified = 0;
for(i = 0; i < l1_size; i++) {
l2_offset = l1_table[i];
@@ -792,8 +737,13 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
old_l2_offset = l2_offset;
l2_offset &= ~QCOW_OFLAG_COPIED;
l2_modified = 0;
- if (bdrv_pread(bs->file, l2_offset, l2_table, l2_size) != l2_size)
+
+ ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset,
+ (void**) &l2_table);
+ if (ret < 0) {
goto fail;
+ }
+
for(j = 0; j < s->l2_size; j++) {
offset = be64_to_cpu(l2_table[j]);
if (offset != 0) {
@@ -833,17 +783,23 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
offset |= QCOW_OFLAG_COPIED;
}
if (offset != old_offset) {
+ if (addend > 0) {
+ qcow2_cache_set_dependency(bs, s->l2_table_cache,
+ s->refcount_block_cache);
+ }
l2_table[j] = cpu_to_be64(offset);
l2_modified = 1;
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
}
}
}
- if (l2_modified) {
- if (bdrv_pwrite_sync(bs->file,
- l2_offset, l2_table, l2_size) < 0)
- goto fail;
+
+ ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ if (ret < 0) {
+ goto fail;
}
+
if (addend != 0) {
refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend);
} else {
@@ -871,16 +827,14 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
}
if (l1_allocated)
qemu_free(l1_table);
- qemu_free(l2_table);
- cache_refcount_updates = 0;
- write_refcount_block(bs);
return 0;
fail:
+ if (l2_table) {
+ qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ }
+
if (l1_allocated)
qemu_free(l1_table);
- qemu_free(l2_table);
- cache_refcount_updates = 0;
- write_refcount_block(bs);
return -EIO;
}
diff --git a/block/qcow2.c b/block/qcow2.c
index b6b094c..49bf7b9 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -143,6 +143,7 @@ static int qcow2_open(BlockDriverState *bs, int flags)
int len, i, ret = 0;
QCowHeader header;
uint64_t ext_end;
+ bool writethrough;
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
if (ret < 0) {
@@ -217,8 +218,13 @@ static int qcow2_open(BlockDriverState *bs, int flags)
be64_to_cpus(&s->l1_table[i]);
}
}
- /* alloc L2 cache */
- s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
+
+ /* alloc L2 table/refcount block cache */
+ writethrough = ((flags & BDRV_O_CACHE_MASK) == 0);
+ s->l2_table_cache = qcow2_cache_create(bs, L2_CACHE_SIZE, writethrough);
+ s->refcount_block_cache = qcow2_cache_create(bs, REFCOUNT_CACHE_SIZE,
+ writethrough);
+
s->cluster_cache = qemu_malloc(s->cluster_size);
/* one more sector for decompressed data alignment */
s->cluster_data = qemu_malloc(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size
@@ -270,7 +276,9 @@ static int qcow2_open(BlockDriverState *bs, int flags)
qcow2_free_snapshots(bs);
qcow2_refcount_close(bs);
qemu_free(s->l1_table);
- qemu_free(s->l2_cache);
+ if (s->l2_table_cache) {
+ qcow2_cache_destroy(bs, s->l2_table_cache);
+ }
qemu_free(s->cluster_cache);
qemu_free(s->cluster_data);
return ret;
@@ -719,7 +727,13 @@ static void qcow2_close(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
qemu_free(s->l1_table);
- qemu_free(s->l2_cache);
+
+ qcow2_cache_flush(bs, s->l2_table_cache);
+ qcow2_cache_flush(bs, s->refcount_block_cache);
+
+ qcow2_cache_destroy(bs, s->l2_table_cache);
+ qcow2_cache_destroy(bs, s->refcount_block_cache);
+
qemu_free(s->cluster_cache);
qemu_free(s->cluster_data);
qcow2_refcount_close(bs);
@@ -1179,6 +1193,19 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
static int qcow2_flush(BlockDriverState *bs)
{
+ BDRVQcowState *s = bs->opaque;
+ int ret;
+
+ ret = qcow2_cache_flush(bs, s->l2_table_cache);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = qcow2_cache_flush(bs, s->refcount_block_cache);
+ if (ret < 0) {
+ return ret;
+ }
+
return bdrv_flush(bs->file);
}
@@ -1186,6 +1213,19 @@ static BlockDriverAIOCB *qcow2_aio_flush(BlockDriverState *bs,
BlockDriverCompletionFunc *cb,
void *opaque)
{
+ BDRVQcowState *s = bs->opaque;
+ int ret;
+
+ ret = qcow2_cache_flush(bs, s->l2_table_cache);
+ if (ret < 0) {
+ return NULL;
+ }
+
+ ret = qcow2_cache_flush(bs, s->refcount_block_cache);
+ if (ret < 0) {
+ return NULL;
+ }
+
return bdrv_aio_flush(bs->file, cb, opaque);
}
diff --git a/block/qcow2.h b/block/qcow2.h
index 5217bea..6d80120 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -51,6 +51,9 @@
#define L2_CACHE_SIZE 16
+/* Must be at least 4 to cover all cases of refcount table growth */
+#define REFCOUNT_CACHE_SIZE 4
+
typedef struct QCowHeader {
uint32_t magic;
uint32_t version;
@@ -78,6 +81,9 @@ typedef struct QCowSnapshot {
uint64_t vm_clock_nsec;
} QCowSnapshot;
+struct Qcow2Cache;
+typedef struct Qcow2Cache Qcow2Cache;
+
typedef struct BDRVQcowState {
int cluster_bits;
int cluster_size;
@@ -91,9 +97,10 @@ typedef struct BDRVQcowState {
uint64_t cluster_offset_mask;
uint64_t l1_table_offset;
uint64_t *l1_table;
- uint64_t *l2_cache;
- uint64_t l2_cache_offsets[L2_CACHE_SIZE];
- uint32_t l2_cache_counts[L2_CACHE_SIZE];
+
+ Qcow2Cache* l2_table_cache;
+ Qcow2Cache* refcount_block_cache;
+
uint8_t *cluster_cache;
uint8_t *cluster_data;
uint64_t cluster_cache_offset;
@@ -102,8 +109,6 @@ typedef struct BDRVQcowState {
uint64_t *refcount_table;
uint64_t refcount_table_offset;
uint32_t refcount_table_size;
- uint64_t refcount_block_cache_offset;
- uint16_t *refcount_block_cache;
int64_t free_cluster_index;
int64_t free_byte_offset;
@@ -215,4 +220,21 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name);
void qcow2_free_snapshots(BlockDriverState *bs);
int qcow2_read_snapshots(BlockDriverState *bs);
+/* qcow2-cache.c functions */
+Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
+ bool writethrough);
+int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c);
+
+void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table);
+int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c);
+int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c,
+ Qcow2Cache *dependency);
+void qcow2_cache_depends_on_flush(Qcow2Cache *c);
+
+int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
+ void **table);
+int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
+ void **table);
+int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table);
+
#endif
diff --git a/block/qed.c b/block/qed.c
index 085c4f2..a46f9ef 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -469,6 +469,12 @@ static int qed_create(const char *filename, uint32_t cluster_size,
return ret;
}
+ /* File must start empty and grow, check truncate is supported */
+ ret = bdrv_truncate(bs, 0);
+ if (ret < 0) {
+ goto out;
+ }
+
if (backing_file) {
header.features |= QED_F_BACKING_FILE;
header.backing_filename_offset = sizeof(le_header);
diff --git a/blockdev.c b/blockdev.c
index d7add36..f7f591f 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -107,7 +107,7 @@ DriveInfo *drive_get_by_blockdev(BlockDriverState *bs)
static void bdrv_format_print(void *opaque, const char *name)
{
- fprintf(stderr, " %s", name);
+ error_printf(" %s", name);
}
void drive_uninit(DriveInfo *dinfo)
@@ -129,8 +129,8 @@ static int parse_block_error_action(const char *buf, int is_read)
} else if (!strcmp(buf, "report")) {
return BLOCK_ERR_REPORT;
} else {
- fprintf(stderr, "qemu: '%s' invalid %s error action\n",
- buf, is_read ? "read" : "write");
+ error_report("'%s' invalid %s error action",
+ buf, is_read ? "read" : "write");
return -1;
}
}
@@ -217,31 +217,30 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
type = IF_NONE;
max_devs = 0;
} else {
- fprintf(stderr, "qemu: unsupported bus type '%s'\n", buf);
+ error_report("unsupported bus type '%s'", buf);
return NULL;
}
}
if (cyls || heads || secs) {
if (cyls < 1 || (type == IF_IDE && cyls > 16383)) {
- fprintf(stderr, "qemu: '%s' invalid physical cyls number\n", buf);
+ error_report("invalid physical cyls number");
return NULL;
}
if (heads < 1 || (type == IF_IDE && heads > 16)) {
- fprintf(stderr, "qemu: '%s' invalid physical heads number\n", buf);
+ error_report("invalid physical heads number");
return NULL;
}
if (secs < 1 || (type == IF_IDE && secs > 63)) {
- fprintf(stderr, "qemu: '%s' invalid physical secs number\n", buf);
+ error_report("invalid physical secs number");
return NULL;
}
}
if ((buf = qemu_opt_get(opts, "trans")) != NULL) {
if (!cyls) {
- fprintf(stderr,
- "qemu: '%s' trans must be used with cyls,heads and secs\n",
- buf);
+ error_report("'%s' trans must be used with cyls,heads and secs",
+ buf);
return NULL;
}
if (!strcmp(buf, "none"))
@@ -251,7 +250,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
else if (!strcmp(buf, "auto"))
translation = BIOS_ATA_TRANSLATION_AUTO;
else {
- fprintf(stderr, "qemu: '%s' invalid translation type\n", buf);
+ error_report("'%s' invalid translation type", buf);
return NULL;
}
}
@@ -261,13 +260,12 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
media = MEDIA_DISK;
} else if (!strcmp(buf, "cdrom")) {
if (cyls || secs || heads) {
- fprintf(stderr,
- "qemu: '%s' invalid physical CHS format\n", buf);
+ error_report("'%s' invalid physical CHS format", buf);
return NULL;
}
media = MEDIA_CDROM;
} else {
- fprintf(stderr, "qemu: '%s' invalid media\n", buf);
+ error_report("'%s' invalid media", buf);
return NULL;
}
}
@@ -283,7 +281,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
} else if (!strcmp(buf, "writethrough")) {
/* this is the default */
} else {
- fprintf(stderr, "qemu: invalid cache option\n");
+ error_report("invalid cache option");
return NULL;
}
}
@@ -295,7 +293,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
} else if (!strcmp(buf, "threads")) {
/* this is the default */
} else {
- fprintf(stderr, "qemu: invalid aio option\n");
+ error_report("invalid aio option");
return NULL;
}
}
@@ -303,14 +301,14 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
if ((buf = qemu_opt_get(opts, "format")) != NULL) {
if (strcmp(buf, "?") == 0) {
- fprintf(stderr, "qemu: Supported formats:");
- bdrv_iterate_format(bdrv_format_print, NULL);
- fprintf(stderr, "\n");
- return NULL;
+ error_printf("Supported formats:");
+ bdrv_iterate_format(bdrv_format_print, NULL);
+ error_printf("\n");
+ return NULL;
}
drv = bdrv_find_whitelisted_format(buf);
if (!drv) {
- fprintf(stderr, "qemu: '%s' invalid format\n", buf);
+ error_report("'%s' invalid format", buf);
return NULL;
}
}
@@ -318,7 +316,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
on_write_error = BLOCK_ERR_STOP_ENOSPC;
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
- fprintf(stderr, "werror is not supported by this format\n");
+ error_report("werror is not supported by this bus type");
return NULL;
}
@@ -331,7 +329,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
on_read_error = BLOCK_ERR_REPORT;
if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) {
- fprintf(stderr, "rerror is not supported by this format\n");
+ error_report("rerror is not supported by this bus type");
return NULL;
}
@@ -343,7 +341,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
if ((devaddr = qemu_opt_get(opts, "addr")) != NULL) {
if (type != IF_VIRTIO) {
- fprintf(stderr, "addr is not supported\n");
+ error_report("addr is not supported by this bus type");
return NULL;
}
}
@@ -352,8 +350,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
if (index != -1) {
if (bus_id != 0 || unit_id != -1) {
- fprintf(stderr,
- "qemu: index cannot be used with bus and unit\n");
+ error_report("index cannot be used with bus and unit");
return NULL;
}
if (max_devs == 0)
@@ -384,8 +381,8 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
/* check unit id */
if (max_devs && unit_id >= max_devs) {
- fprintf(stderr, "qemu: unit %d too big (max is %d)\n",
- unit_id, max_devs - 1);
+ error_report("unit %d too big (max is %d)",
+ unit_id, max_devs - 1);
return NULL;
}
@@ -479,7 +476,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
ro = 1;
} else if (ro == 1) {
if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY && type != IF_NONE) {
- fprintf(stderr, "qemu: readonly flag not supported for drive with this interface\n");
+ error_report("readonly not supported by this bus type");
return NULL;
}
}
@@ -488,8 +485,8 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv);
if (ret < 0) {
- fprintf(stderr, "qemu: could not open disk image %s: %s\n",
- file, strerror(-ret));
+ error_report("could not open disk image %s: %s",
+ file, strerror(-ret));
return NULL;
}
@@ -526,6 +523,12 @@ int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
int ret = 0;
int flags;
+ if (!filename) {
+ qerror_report(QERR_MISSING_PARAMETER, "snapshot_file");
+ ret = -1;
+ goto out;
+ }
+
bs = bdrv_find(device);
if (!bs) {
qerror_report(QERR_DEVICE_NOT_FOUND, device);
@@ -684,13 +687,15 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
/* clean up guest state from pointing to host resource by
* finding and removing DeviceState "drive" property */
- for (prop = bs->peer->info->props; prop && prop->name; prop++) {
- if (prop->info->type == PROP_TYPE_DRIVE) {
- ptr = qdev_get_prop_ptr(bs->peer, prop);
- if ((*ptr) == bs) {
- bdrv_detach(bs, bs->peer);
- *ptr = NULL;
- break;
+ if (bs->peer) {
+ for (prop = bs->peer->info->props; prop && prop->name; prop++) {
+ if (prop->info->type == PROP_TYPE_DRIVE) {
+ ptr = qdev_get_prop_ptr(bs->peer, prop);
+ if (*ptr == bs) {
+ bdrv_detach(bs, bs->peer);
+ *ptr = NULL;
+ break;
+ }
}
}
}
diff --git a/cutils.c b/cutils.c
index 7984bc1..4d2e27c 100644
--- a/cutils.c
+++ b/cutils.c
@@ -291,9 +291,9 @@ int fcntl_setfl(int fd, int flag)
* value must be terminated by whitespace, ',' or '\0'. Return -1 on
* error.
*/
-ssize_t strtosz_suffix(const char *nptr, char **end, const char default_suffix)
+int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix)
{
- ssize_t retval = -1;
+ int64_t retval = -1;
char *endptr, c, d;
int mul_required = 0;
double val, mul, integral, fraction;
@@ -365,7 +365,7 @@ ssize_t strtosz_suffix(const char *nptr, char **end, const char default_suffix)
goto fail;
}
}
- if ((val * mul >= ~(size_t)0) || val < 0) {
+ if ((val * mul >= INT64_MAX) || val < 0) {
goto fail;
}
retval = val * mul;
@@ -378,7 +378,7 @@ fail:
return retval;
}
-ssize_t strtosz(const char *nptr, char **end)
+int64_t strtosz(const char *nptr, char **end)
{
return strtosz_suffix(nptr, end, STRTOSZ_DEFSUFFIX_MB);
}
diff --git a/docs/qdev-device-use.txt b/docs/qdev-device-use.txt
index f2f9b75..4bb2be8 100644
--- a/docs/qdev-device-use.txt
+++ b/docs/qdev-device-use.txt
@@ -80,7 +80,11 @@ The -device argument differs in detail for each kind of drive:
This SCSI controller a single SCSI bus, named ID.0. Put a disk on
it:
- -device scsi-disk,drive=DRIVE-ID,bus=ID.0,scsi-id=SCSI-ID
+ -device scsi-disk,drive=DRIVE-ID,bus=ID.0,scsi-id=SCSI-ID,removable=RMB
+
+ The (optional) removable parameter lets you override the SCSI INQUIRY
+ removable (RMB) bit for non CD-ROM devices. It is ignored for CD-ROM devices
+ which are always removable. RMB is "on" or "off".
* if=floppy
@@ -116,7 +120,12 @@ For USB devices, the old way is actually different:
Provides much less control than -drive's HOST-OPTS... The new way
fixes that:
- -device usb-storage,drive=DRIVE-ID
+ -device usb-storage,drive=DRIVE-ID,removable=RMB
+
+The removable parameter gives control over the SCSI INQUIRY removable (RMB)
+bit. USB thumbdrives usually set removable=on, while USB hard disks set
+removable=off. See the if=scsi description above for details on the removable
+parameter, which applies only to scsi-disk devices and not to scsi-generic.
=== Character Devices ===
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 9496e99..e698c13 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -321,14 +321,6 @@ static inline void ide_abort_command(IDEState *s)
s->error = ABRT_ERR;
}
-static inline void ide_dma_submit_check(IDEState *s,
- BlockDriverCompletionFunc *dma_cb)
-{
- if (s->bus->dma->aiocb)
- return;
- dma_cb(s, -1);
-}
-
/* prepare data transfer and tell what to do after */
static void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
EndTransferFunc *end_transfer_func)
@@ -487,16 +479,19 @@ static int ide_handle_rw_error(IDEState *s, int error, int op)
return 1;
}
-void ide_read_dma_cb(void *opaque, int ret)
+void ide_dma_cb(void *opaque, int ret)
{
IDEState *s = opaque;
int n;
int64_t sector_num;
+handle_rw_error:
if (ret < 0) {
- if (ide_handle_rw_error(s, -ret,
- BM_STATUS_DMA_RETRY | BM_STATUS_RETRY_READ))
- {
+ int op = BM_STATUS_DMA_RETRY;
+
+ if (s->is_read)
+ op |= BM_STATUS_RETRY_READ;
+ if (ide_handle_rw_error(s, -ret, op)) {
return;
}
}
@@ -504,7 +499,7 @@ void ide_read_dma_cb(void *opaque, int ret)
n = s->io_buffer_size >> 9;
sector_num = ide_get_sector(s);
if (n > 0) {
- dma_buf_commit(s, 1);
+ dma_buf_commit(s, s->is_read);
sector_num += n;
ide_set_sector(s, sector_num);
s->nsector -= n;
@@ -514,32 +509,47 @@ void ide_read_dma_cb(void *opaque, int ret)
if (s->nsector == 0) {
s->status = READY_STAT | SEEK_STAT;
ide_set_irq(s->bus);
- eot:
- s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT);
- ide_set_inactive(s);
- return;
+ goto eot;
}
/* launch next transfer */
n = s->nsector;
s->io_buffer_index = 0;
s->io_buffer_size = n * 512;
- if (s->bus->dma->ops->prepare_buf(s->bus->dma, 1) == 0)
+ if (s->bus->dma->ops->prepare_buf(s->bus->dma, s->is_read) == 0)
goto eot;
+
#ifdef DEBUG_AIO
- printf("aio_read: sector_num=%" PRId64 " n=%d\n", sector_num, n);
+ printf("ide_dma_cb: sector_num=%" PRId64 " n=%d, is_read=%d\n",
+ sector_num, n, s->is_read);
#endif
- s->bus->dma->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num, ide_read_dma_cb, s);
- ide_dma_submit_check(s, ide_read_dma_cb);
+
+ if (s->is_read) {
+ s->bus->dma->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num,
+ ide_dma_cb, s);
+ } else {
+ s->bus->dma->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num,
+ ide_dma_cb, s);
+ }
+
+ if (!s->bus->dma->aiocb) {
+ ret = -1;
+ goto handle_rw_error;
+ }
+ return;
+
+eot:
+ s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT);
+ ide_set_inactive(s);
}
-static void ide_sector_read_dma(IDEState *s)
+static void ide_sector_start_dma(IDEState *s, int is_read)
{
s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT;
s->io_buffer_index = 0;
s->io_buffer_size = 0;
- s->is_read = 1;
- s->bus->dma->ops->start_dma(s->bus->dma, s, ide_read_dma_cb);
+ s->is_read = is_read;
+ s->bus->dma->ops->start_dma(s->bus->dma, s, ide_dma_cb);
}
static void ide_sector_write_timer_cb(void *opaque)
@@ -594,57 +604,6 @@ void ide_sector_write(IDEState *s)
}
}
-void ide_write_dma_cb(void *opaque, int ret)
-{
- IDEState *s = opaque;
- int n;
- int64_t sector_num;
-
- if (ret < 0) {
- if (ide_handle_rw_error(s, -ret, BM_STATUS_DMA_RETRY))
- return;
- }
-
- n = s->io_buffer_size >> 9;
- sector_num = ide_get_sector(s);
- if (n > 0) {
- dma_buf_commit(s, 0);
- sector_num += n;
- ide_set_sector(s, sector_num);
- s->nsector -= n;
- }
-
- /* end of transfer ? */
- if (s->nsector == 0) {
- s->status = READY_STAT | SEEK_STAT;
- ide_set_irq(s->bus);
- eot:
- s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT);
- ide_set_inactive(s);
- return;
- }
-
- n = s->nsector;
- s->io_buffer_size = n * 512;
- /* launch next transfer */
- if (s->bus->dma->ops->prepare_buf(s->bus->dma, 0) == 0)
- goto eot;
-#ifdef DEBUG_AIO
- printf("aio_write: sector_num=%" PRId64 " n=%d\n", sector_num, n);
-#endif
- s->bus->dma->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num, ide_write_dma_cb, s);
- ide_dma_submit_check(s, ide_write_dma_cb);
-}
-
-static void ide_sector_write_dma(IDEState *s)
-{
- s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT;
- s->io_buffer_index = 0;
- s->io_buffer_size = 0;
- s->is_read = 0;
- s->bus->dma->ops->start_dma(s->bus->dma, s, ide_write_dma_cb);
-}
-
void ide_atapi_cmd_ok(IDEState *s)
{
s->error = 0;
@@ -1858,7 +1817,7 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
if (!s->bs)
goto abort_cmd;
ide_cmd_lba48_transform(s, lba48);
- ide_sector_read_dma(s);
+ ide_sector_start_dma(s, 1);
break;
case WIN_WRITEDMA_EXT:
lba48 = 1;
@@ -1867,7 +1826,7 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
if (!s->bs)
goto abort_cmd;
ide_cmd_lba48_transform(s, lba48);
- ide_sector_write_dma(s);
+ ide_sector_start_dma(s, 0);
s->media_changed = 1;
break;
case WIN_READ_NATIVE_MAX_EXT:
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index 697c3b4..d533fb6 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -439,7 +439,6 @@ struct IDEState {
uint32_t mdata_size;
uint8_t *mdata_storage;
int media_changed;
- /* for pmac */
int is_read;
/* SMART */
uint8_t smart_enabled;
@@ -560,8 +559,7 @@ void ide_init2_with_non_qdev_drives(IDEBus *bus, DriveInfo *hd0,
void ide_init_ioport(IDEBus *bus, int iobase, int iobase2);
void ide_exec_cmd(IDEBus *bus, uint32_t val);
-void ide_read_dma_cb(void *opaque, int ret);
-void ide_write_dma_cb(void *opaque, int ret);
+void ide_dma_cb(void *opaque, int ret);
void ide_sector_write(IDEState *s);
void ide_sector_read(IDEState *s);
void ide_flush_cache(IDEState *s);
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index 510b2de..35168cb 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -178,14 +178,9 @@ static void bmdma_restart_dma(BMDMAState *bm, int is_read)
s->io_buffer_index = 0;
s->io_buffer_size = 0;
s->nsector = bm->nsector;
+ s->is_read = is_read;
bm->cur_addr = bm->addr;
-
- if (is_read) {
- bm->dma_cb = ide_read_dma_cb;
- } else {
- bm->dma_cb = ide_write_dma_cb;
- }
-
+ bm->dma_cb = ide_dma_cb;
bmdma_start_dma(&bm->dma, s, bm->dma_cb);
}
@@ -272,9 +267,7 @@ static void bmdma_irq(void *opaque, int n, int level)
return;
}
- if (bm) {
- bm->status |= BM_STATUS_INT;
- }
+ bm->status |= BM_STATUS_INT;
/* trigger the real irq */
qemu_set_irq(bm->irq, level);
diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c
index 716133c..270a982 100644
--- a/hw/pci-hotplug.c
+++ b/hw/pci-hotplug.c
@@ -90,7 +90,7 @@ static int scsi_hot_add(Monitor *mon, DeviceState *adapter,
* specified).
*/
dinfo->unit = qemu_opt_get_number(dinfo->opts, "unit", -1);
- scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit);
+ scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit, false);
if (!scsidev) {
return -1;
}
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 7febb86..ceeb4ec 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -87,7 +87,8 @@ void scsi_qdev_register(SCSIDeviceInfo *info)
}
/* handle legacy '-drive if=scsi,...' cmd line args */
-SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, int unit)
+SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
+ int unit, bool removable)
{
const char *driver;
DeviceState *dev;
@@ -95,6 +96,9 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, int
driver = bdrv_is_sg(bdrv) ? "scsi-generic" : "scsi-disk";
dev = qdev_create(&bus->qbus, driver);
qdev_prop_set_uint32(dev, "scsi-id", unit);
+ if (qdev_prop_exists(dev, "removable")) {
+ qdev_prop_set_bit(dev, "removable", removable);
+ }
if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) {
qdev_free(dev);
return NULL;
@@ -117,7 +121,7 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
continue;
}
qemu_opts_loc_restore(dinfo->opts);
- if (!scsi_bus_legacy_add_drive(bus, dinfo->bdrv, unit)) {
+ if (!scsi_bus_legacy_add_drive(bus, dinfo->bdrv, unit, false)) {
res = -1;
break;
}
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 6cb317c..488eedd 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -72,6 +72,7 @@ struct SCSIDiskState
/* The qemu block layer uses a fixed 512 byte sector size.
This is the number of 512 byte blocks in a single scsi sector. */
int cluster_size;
+ uint32_t removable;
uint64_t max_lba;
QEMUBH *bh;
char *version;
@@ -552,6 +553,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
} else {
outbuf[0] = 0;
+ outbuf[1] = s->removable ? 0x80 : 0;
memcpy(&outbuf[16], "QEMU HARDDISK ", 16);
}
memcpy(&outbuf[8], "QEMU ", 8);
@@ -1295,6 +1297,7 @@ static SCSIDeviceInfo scsi_disk_info = {
DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf),
DEFINE_PROP_STRING("ver", SCSIDiskState, version),
DEFINE_PROP_STRING("serial", SCSIDiskState, serial),
+ DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
DEFINE_PROP_END_OF_LIST(),
},
};
diff --git a/hw/scsi.h b/hw/scsi.h
index bf02adf..846fbba 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -94,7 +94,8 @@ static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d)
return DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus);
}
-SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, int unit);
+SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
+ int unit, bool removable);
int scsi_bus_legacy_handle_cmdline(SCSIBus *bus);
SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun);
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 729d96c..11722c7 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -51,6 +51,7 @@ typedef struct {
SCSIBus bus;
BlockConf conf;
SCSIDevice *scsi_dev;
+ uint32_t removable;
int result;
/* For async completion. */
USBPacket *packet;
@@ -515,7 +516,7 @@ static int usb_msd_initfn(USBDevice *dev)
usb_desc_init(dev);
scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete);
- s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0);
+ s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable);
if (!s->scsi_dev) {
return -1;
}
@@ -607,6 +608,7 @@ static struct USBDeviceInfo msd_info = {
.usbdevice_init = usb_msd_init,
.qdev.props = (Property[]) {
DEFINE_BLOCK_PROPERTIES(MSDState, conf),
+ DEFINE_PROP_BIT("removable", MSDState, removable, 0, false),
DEFINE_PROP_END_OF_LIST(),
},
};
diff --git a/monitor.c b/monitor.c
index d291158..0cda3da 100644
--- a/monitor.c
+++ b/monitor.c
@@ -4162,7 +4162,7 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon,
break;
case 'o':
{
- ssize_t val;
+ int64_t val;
char *end;
while (qemu_isspace(*p)) {
diff --git a/qemu-common.h b/qemu-common.h
index c766b99..c351131 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -158,8 +158,8 @@ int fcntl_setfl(int fd, int flag);
#define STRTOSZ_DEFSUFFIX_MB 'M'
#define STRTOSZ_DEFSUFFIX_KB 'K'
#define STRTOSZ_DEFSUFFIX_B 'B'
-ssize_t strtosz(const char *nptr, char **end);
-ssize_t strtosz_suffix(const char *nptr, char **end, const char default_suffix);
+int64_t strtosz(const char *nptr, char **end);
+int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix);
/* path.c */
void init_paths(const char *prefix);
diff --git a/qemu-img.c b/qemu-img.c
index afd9ed2..4a37358 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -320,7 +320,7 @@ static int img_create(int argc, char **argv)
/* Get image size, if specified */
if (optind < argc) {
- ssize_t sval;
+ int64_t sval;
sval = strtosz_suffix(argv[optind++], NULL, STRTOSZ_DEFSUFFIX_B);
if (sval < 0) {
error_report("Invalid image size specified! You may use k, M, G or "
@@ -1068,7 +1068,7 @@ static int img_snapshot(int argc, char **argv)
int action = 0;
qemu_timeval tv;
- bdrv_oflags = BDRV_O_RDWR;
+ bdrv_oflags = BDRV_O_FLAGS | BDRV_O_RDWR;
/* Parse commandline parameters */
for(;;) {
c = getopt(argc, argv, "la:c:d:h");
diff --git a/qemu-img.texi b/qemu-img.texi
index 1b90ddb..ced64a4 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -59,6 +59,13 @@ lists all snapshots in the given image
Command description:
@table @option
+@item check [-f @var{fmt}] @var{filename}
+
+Perform a consistency check on the disk image @var{filename}.
+
+Only the formats @code{qcow2}, @code{qed} and @code{vdi} support
+consistency checks.
+
@item create [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}]
Create the new disk image @var{filename} of size @var{size} and format
@@ -107,6 +114,40 @@ they are displayed too.
List, apply, create or delete snapshots in image @var{filename}.
+@item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
+
+Changes the backing file of an image. Only the formats @code{qcow2} and
+@code{qed} support changing the backing file.
+
+The backing file is changed to @var{backing_file} and (if the image format of
+@var{filename} supports this) the backing file format is changed to
+@var{backing_fmt}.
+
+There are two different modes in which @code{rebase} can operate:
+@table @option
+@item Safe mode
+This is the default mode and performs a real rebase operation. The new backing
+file may differ from the old one and qemu-img rebase will take care of keeping
+the guest-visible content of @var{filename} unchanged.
+
+In order to achieve this, any clusters that differ between @var{backing_file}
+and the old backing file of @var{filename} are merged into @var{filename}
+before actually changing the backing file.
+
+Note that the safe mode is an expensive operation, comparable to converting
+an image. It only works if the old backing file still exists.
+
+@item Unsafe mode
+qemu-img uses the unsafe mode if @code{-u} is specified. In this mode, only the
+backing file name and format of @var{filename} is changed without any checks
+on the file contents. The user must take care of specifying the correct new
+backing file, or the guest-visible content of the image will be corrupted.
+
+This mode is useful for renaming or moving the backing file to somewhere else.
+It can be used without an accessible old backing file, i.e. you can use it to
+fix an image whose backing file has already been moved/renamed.
+@end table
+
@item resize @var{filename} [+ | -]@var{size}
Change the disk image as if it had been created with @var{size}.
diff --git a/vl.c b/vl.c
index 0292184..14255c4 100644
--- a/vl.c
+++ b/vl.c
@@ -804,7 +804,7 @@ static void numa_add(const char *optarg)
if (get_param_value(option, 128, "mem", optarg) == 0) {
node_mem[nodenr] = 0;
} else {
- ssize_t sval;
+ int64_t sval;
sval = strtosz(option, NULL);
if (sval < 0) {
fprintf(stderr, "qemu: invalid numa mem size: %s\n", optarg);
@@ -2245,7 +2245,7 @@ int main(int argc, char **argv, char **envp)
exit(0);
break;
case QEMU_OPTION_m: {
- ssize_t value;
+ int64_t value;
value = strtosz(optarg, NULL);
if (value < 0) {