aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block.c94
-rw-r--r--block/block-backend.c5
-rw-r--r--block/commit.c4
-rw-r--r--block/qapi.c20
-rw-r--r--block/throttle-groups.c28
-rw-r--r--block/vmdk.c44
-rw-r--r--block/vpc.c30
-rw-r--r--block/vvfat.c90
-rw-r--r--blockdev.c11
-rw-r--r--docs/devel/bitmaps.md505
-rw-r--r--docs/interop/bitmaps.rst555
-rw-r--r--docs/interop/live-block-operations.rst1088
-rw-r--r--docs/live-block-ops.txt72
-rw-r--r--fsdev/qemu-fsdev-throttle.c2
-rw-r--r--hmp.c11
-rw-r--r--hw/core/qdev-properties.c32
-rw-r--r--hw/ide/qdev.c3
-rw-r--r--hw/scsi/scsi-disk.c5
-rw-r--r--hw/vfio/common.c19
-rw-r--r--include/hw/qdev-properties.h3
-rw-r--r--include/migration/colo.h1
-rw-r--r--include/qemu/throttle.h2
-rw-r--r--include/sysemu/block-backend.h2
-rw-r--r--io/channel.c10
-rw-r--r--linux-headers/asm-x86/kvm_para.h1
-rw-r--r--linux-headers/linux/kvm.h4
-rw-r--r--migration/colo.c5
-rw-r--r--migration/migration.c268
-rw-r--r--migration/rdma.c137
-rw-r--r--nbd/client.c8
-rw-r--r--nbd/nbd-internal.h8
-rw-r--r--nbd/server.c18
-rw-r--r--nbd/trace-events2
-rw-r--r--qapi/block-core.json9
-rw-r--r--qemu-img-cmds.hx4
-rw-r--r--qemu-img.c16
-rw-r--r--qemu-img.texi9
-rw-r--r--target/s390x/cpu_models.c6
-rw-r--r--target/s390x/gen-features.c37
-rw-r--r--target/s390x/helper.h11
-rw-r--r--target/s390x/insn-data.def21
-rw-r--r--target/s390x/mem_helper.c585
-rw-r--r--target/s390x/translate.c99
-rwxr-xr-xtests/qemu-iotests/06713
-rw-r--r--tests/qemu-iotests/067.out40
-rwxr-xr-xtests/qemu-iotests/0824
-rw-r--r--tests/qemu-iotests/082.out4
-rwxr-xr-xtests/qemu-iotests/0852
-rw-r--r--tests/qemu-iotests/111.out1
-rw-r--r--tests/qemu-iotests/1392
-rw-r--r--tests/qemu-iotests/140.out3
-rw-r--r--tests/qemu-iotests/143.out3
-rwxr-xr-xtests/qemu-iotests/1562
-rwxr-xr-xtests/qemu-iotests/1582
-rwxr-xr-xtests/qemu-iotests/186147
-rw-r--r--tests/qemu-iotests/186.out489
-rwxr-xr-xtests/qemu-iotests/1892
-rw-r--r--tests/qemu-iotests/group1
-rw-r--r--tests/test-throttle.c4
-rw-r--r--util/throttle.c18
60 files changed, 3720 insertions, 901 deletions
diff --git a/block.c b/block.c
index 98a9209371..2dd9262cd0 100644
--- a/block.c
+++ b/block.c
@@ -4396,55 +4396,65 @@ void bdrv_img_create(const char *filename, const char *fmt,
backing_fmt = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
- // The size for the image must always be specified, with one exception:
- // If we are using a backing file, we can obtain the size from there
+ /* The size for the image must always be specified, unless we have a backing
+ * file and we have not been forbidden from opening it. */
size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0);
- if (size == -1) {
- if (backing_file) {
- BlockDriverState *bs;
- char *full_backing = g_new0(char, PATH_MAX);
- int64_t size;
- int back_flags;
- QDict *backing_options = NULL;
-
- bdrv_get_full_backing_filename_from_filename(filename, backing_file,
- full_backing, PATH_MAX,
- &local_err);
- if (local_err) {
- g_free(full_backing);
- goto out;
- }
+ if (backing_file && !(flags & BDRV_O_NO_BACKING)) {
+ BlockDriverState *bs;
+ char *full_backing = g_new0(char, PATH_MAX);
+ int back_flags;
+ QDict *backing_options = NULL;
+
+ bdrv_get_full_backing_filename_from_filename(filename, backing_file,
+ full_backing, PATH_MAX,
+ &local_err);
+ if (local_err) {
+ g_free(full_backing);
+ goto out;
+ }
- /* backing files always opened read-only */
- back_flags = flags;
- back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
+ /* backing files always opened read-only */
+ back_flags = flags;
+ back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
- if (backing_fmt) {
- backing_options = qdict_new();
- qdict_put_str(backing_options, "driver", backing_fmt);
- }
+ if (backing_fmt) {
+ backing_options = qdict_new();
+ qdict_put_str(backing_options, "driver", backing_fmt);
+ }
- bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
- &local_err);
- g_free(full_backing);
- if (!bs) {
- goto out;
- }
- size = bdrv_getlength(bs);
- if (size < 0) {
- error_setg_errno(errp, -size, "Could not get size of '%s'",
- backing_file);
- bdrv_unref(bs);
- goto out;
+ bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
+ &local_err);
+ g_free(full_backing);
+ if (!bs && size != -1) {
+ /* Couldn't open BS, but we have a size, so it's nonfatal */
+ warn_reportf_err(local_err,
+ "Could not verify backing image. "
+ "This may become an error in future versions.\n");
+ local_err = NULL;
+ } else if (!bs) {
+ /* Couldn't open bs, do not have size */
+ error_append_hint(&local_err,
+ "Could not open backing image to determine size.\n");
+ goto out;
+ } else {
+ if (size == -1) {
+ /* Opened BS, have no size */
+ size = bdrv_getlength(bs);
+ if (size < 0) {
+ error_setg_errno(errp, -size, "Could not get size of '%s'",
+ backing_file);
+ bdrv_unref(bs);
+ goto out;
+ }
+ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, size, &error_abort);
}
-
- qemu_opt_set_number(opts, BLOCK_OPT_SIZE, size, &error_abort);
-
bdrv_unref(bs);
- } else {
- error_setg(errp, "Image creation needs a size parameter");
- goto out;
}
+ } /* (backing_file && !(flags & BDRV_O_NO_BACKING)) */
+
+ if (size == -1) {
+ error_setg(errp, "Image creation needs a size parameter");
+ goto out;
}
if (!quiet) {
diff --git a/block/block-backend.c b/block/block-backend.c
index fe3542b3f8..968438c149 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -83,7 +83,6 @@ static const AIOCBInfo block_backend_aiocb_info = {
static void drive_info_del(DriveInfo *dinfo);
static BlockBackend *bdrv_first_blk(BlockDriverState *bs);
-static char *blk_get_attached_dev_id(BlockBackend *blk);
/* All BlockBackends */
static QTAILQ_HEAD(, BlockBackend) block_backends =
@@ -343,7 +342,7 @@ void blk_unref(BlockBackend *blk)
* Behaves similarly to blk_next() but iterates over all BlockBackends, even the
* ones which are hidden (i.e. are not referenced by the monitor).
*/
-static BlockBackend *blk_all_next(BlockBackend *blk)
+BlockBackend *blk_all_next(BlockBackend *blk)
{
return blk ? QTAILQ_NEXT(blk, link)
: QTAILQ_FIRST(&block_backends);
@@ -726,7 +725,7 @@ void *blk_get_attached_dev(BlockBackend *blk)
/* Return the qdev ID, or if no ID is assigned the QOM path, of the block
* device attached to the BlockBackend. */
-static char *blk_get_attached_dev_id(BlockBackend *blk)
+char *blk_get_attached_dev_id(BlockBackend *blk)
{
DeviceState *dev;
diff --git a/block/commit.c b/block/commit.c
index 13143608f8..5cc910f567 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -90,7 +90,9 @@ static void commit_complete(BlockJob *job, void *opaque)
/* Make sure overlay_bs and top stay around until bdrv_set_backing_hd() */
bdrv_ref(top);
- bdrv_ref(overlay_bs);
+ if (overlay_bs) {
+ bdrv_ref(overlay_bs);
+ }
/* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
* the normal backing chain can be restored. */
diff --git a/block/qapi.c b/block/qapi.c
index 080eb8f115..95b2e2daa5 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -322,11 +322,21 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
{
BlockInfo *info = g_malloc0(sizeof(*info));
BlockDriverState *bs = blk_bs(blk);
+ char *qdev;
+
info->device = g_strdup(blk_name(blk));
info->type = g_strdup("unknown");
info->locked = blk_dev_is_medium_locked(blk);
info->removable = blk_dev_has_removable_media(blk);
+ qdev = blk_get_attached_dev_id(blk);
+ if (qdev && *qdev) {
+ info->has_qdev = true;
+ info->qdev = qdev;
+ } else {
+ g_free(qdev);
+ }
+
if (blk_dev_has_tray(blk)) {
info->has_tray_open = true;
info->tray_open = blk_dev_is_tray_open(blk);
@@ -462,8 +472,14 @@ BlockInfoList *qmp_query_block(Error **errp)
BlockBackend *blk;
Error *local_err = NULL;
- for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
- BlockInfoList *info = g_malloc0(sizeof(*info));
+ for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) {
+ BlockInfoList *info;
+
+ if (!*blk_name(blk) && !blk_get_attached_dev(blk)) {
+ continue;
+ }
+
+ info = g_malloc0(sizeof(*info));
bdrv_query_info(blk, &info->value, &local_err);
if (local_err) {
error_propagate(errp, local_err);
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index da2b490c38..890bfded3f 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -61,6 +61,7 @@ typedef struct ThrottleGroup {
QLIST_HEAD(, BlockBackendPublic) head;
BlockBackend *tokens[2];
bool any_timer_armed[2];
+ QEMUClockType clock_type;
/* These two are protected by the global throttle_groups_lock */
unsigned refcount;
@@ -98,6 +99,12 @@ ThrottleState *throttle_group_incref(const char *name)
if (!tg) {
tg = g_new0(ThrottleGroup, 1);
tg->name = g_strdup(name);
+ tg->clock_type = QEMU_CLOCK_REALTIME;
+
+ if (qtest_enabled()) {
+ /* For testing block IO throttling only */
+ tg->clock_type = QEMU_CLOCK_VIRTUAL;
+ }
qemu_mutex_init(&tg->lock);
throttle_init(&tg->ts);
QLIST_INIT(&tg->head);
@@ -310,7 +317,7 @@ static void schedule_next_request(BlockBackend *blk, bool is_write)
token = blk;
} else {
ThrottleTimers *tt = &blk_get_public(token)->throttle_timers;
- int64_t now = qemu_clock_get_ns(tt->clock_type);
+ int64_t now = qemu_clock_get_ns(tg->clock_type);
timer_mod(tt->timers[is_write], now);
tg->any_timer_armed[is_write] = true;
}
@@ -419,18 +426,10 @@ void throttle_group_restart_blk(BlockBackend *blk)
void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg)
{
BlockBackendPublic *blkp = blk_get_public(blk);
- ThrottleTimers *tt = &blkp->throttle_timers;
ThrottleState *ts = blkp->throttle_state;
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
qemu_mutex_lock(&tg->lock);
- /* throttle_config() cancels the timers */
- if (timer_pending(tt->timers[0])) {
- tg->any_timer_armed[0] = false;
- }
- if (timer_pending(tt->timers[1])) {
- tg->any_timer_armed[1] = false;
- }
- throttle_config(ts, tt, cfg);
+ throttle_config(ts, tg->clock_type, cfg);
qemu_mutex_unlock(&tg->lock);
throttle_group_restart_blk(blk);
@@ -497,13 +496,6 @@ void throttle_group_register_blk(BlockBackend *blk, const char *groupname)
BlockBackendPublic *blkp = blk_get_public(blk);
ThrottleState *ts = throttle_group_incref(groupname);
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
- int clock_type = QEMU_CLOCK_REALTIME;
-
- if (qtest_enabled()) {
- /* For testing block IO throttling only */
- clock_type = QEMU_CLOCK_VIRTUAL;
- }
-
blkp->throttle_state = ts;
qemu_mutex_lock(&tg->lock);
@@ -518,7 +510,7 @@ void throttle_group_register_blk(BlockBackend *blk, const char *groupname)
throttle_timers_init(&blkp->throttle_timers,
blk_get_aio_context(blk),
- clock_type,
+ tg->clock_type,
read_timer_cb,
write_timer_cb,
blk);
diff --git a/block/vmdk.c b/block/vmdk.c
index 24d71b5982..0fc97391a6 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -242,10 +242,11 @@ static void vmdk_free_last_extent(BlockDriverState *bs)
s->extents = g_renew(VmdkExtent, s->extents, s->num_extents);
}
-static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
+/* Return -ve errno, or 0 on success and write CID into *pcid. */
+static int vmdk_read_cid(BlockDriverState *bs, int parent, uint32_t *pcid)
{
char *desc;
- uint32_t cid = 0xffffffff;
+ uint32_t cid;
const char *p_name, *cid_str;
size_t cid_str_size;
BDRVVmdkState *s = bs->opaque;
@@ -254,8 +255,7 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
desc = g_malloc0(DESC_SIZE);
ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE);
if (ret < 0) {
- g_free(desc);
- return 0;
+ goto out;
}
if (parent) {
@@ -268,13 +268,21 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
desc[DESC_SIZE - 1] = '\0';
p_name = strstr(desc, cid_str);
- if (p_name != NULL) {
- p_name += cid_str_size;
- sscanf(p_name, "%" SCNx32, &cid);
+ if (p_name == NULL) {
+ ret = -EINVAL;
+ goto out;
}
+ p_name += cid_str_size;
+ if (sscanf(p_name, "%" SCNx32, &cid) != 1) {
+ ret = -EINVAL;
+ goto out;
+ }
+ *pcid = cid;
+ ret = 0;
+out:
g_free(desc);
- return cid;
+ return ret;
}
static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
@@ -322,7 +330,10 @@ static int vmdk_is_cid_valid(BlockDriverState *bs)
if (!s->cid_checked && bs->backing) {
BlockDriverState *p_bs = bs->backing->bs;
- cur_pcid = vmdk_read_cid(p_bs, 0);
+ if (vmdk_read_cid(p_bs, 0, &cur_pcid) != 0) {
+ /* read failure: report as not valid */
+ return 0;
+ }
if (s->parent_cid != cur_pcid) {
/* CID not valid */
return 0;
@@ -975,8 +986,14 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
if (ret) {
goto fail;
}
- s->cid = vmdk_read_cid(bs, 0);
- s->parent_cid = vmdk_read_cid(bs, 1);
+ ret = vmdk_read_cid(bs, 0, &s->cid);
+ if (ret) {
+ goto fail;
+ }
+ ret = vmdk_read_cid(bs, 1, &s->parent_cid);
+ if (ret) {
+ goto fail;
+ }
qemu_co_mutex_init(&s->lock);
/* Disable migration when VMDK images are used */
@@ -2008,8 +2025,11 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
ret = -EINVAL;
goto exit;
}
- parent_cid = vmdk_read_cid(blk_bs(blk), 0);
+ ret = vmdk_read_cid(blk_bs(blk), 0, &parent_cid);
blk_unref(blk);
+ if (ret) {
+ goto exit;
+ }
snprintf(parent_desc_line, BUF_SIZE,
"parentFileNameHint=\"%s\"", backing_file);
}
diff --git a/block/vpc.c b/block/vpc.c
index 8057d42a23..10e6519d78 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -460,17 +460,23 @@ static int vpc_reopen_prepare(BDRVReopenState *state,
/*
* Returns the absolute byte offset of the given sector in the image file.
* If the sector is not allocated, -1 is returned instead.
+ * If an error occurred trying to write an updated block bitmap back to
+ * the file, -2 is returned, and the error value is written to *err.
+ * This can only happen for a write operation.
*
* The parameter write must be 1 if the offset will be used for a write
* operation (the block bitmaps is updated then), 0 otherwise.
+ * If write is true then err must not be NULL.
*/
static inline int64_t get_image_offset(BlockDriverState *bs, uint64_t offset,
- bool write)
+ bool write, int *err)
{
BDRVVPCState *s = bs->opaque;
uint64_t bitmap_offset, block_offset;
uint32_t pagetable_index, offset_in_block;
+ assert(!(write && err == NULL));
+
pagetable_index = offset / s->block_size;
offset_in_block = offset % s->block_size;
@@ -487,10 +493,15 @@ static inline int64_t get_image_offset(BlockDriverState *bs, uint64_t offset,
correctness. */
if (write && (s->last_bitmap_offset != bitmap_offset)) {
uint8_t bitmap[s->bitmap_size];
+ int r;
s->last_bitmap_offset = bitmap_offset;
memset(bitmap, 0xff, s->bitmap_size);
- bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size);
+ r = bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size);
+ if (r < 0) {
+ *err = r;
+ return -2;
+ }
}
return block_offset;
@@ -561,7 +572,7 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t offset)
if (ret < 0)
goto fail;
- return get_image_offset(bs, offset, false);
+ return get_image_offset(bs, offset, false, NULL);
fail:
s->free_data_block_offset -= (s->block_size + s->bitmap_size);
@@ -601,7 +612,7 @@ vpc_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
qemu_iovec_init(&local_qiov, qiov->niov);
while (bytes > 0) {
- image_offset = get_image_offset(bs, offset, false);
+ image_offset = get_image_offset(bs, offset, false, NULL);
n_bytes = MIN(bytes, s->block_size - (offset % s->block_size));
if (image_offset == -1) {
@@ -650,7 +661,11 @@ vpc_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
qemu_iovec_init(&local_qiov, qiov->niov);
while (bytes > 0) {
- image_offset = get_image_offset(bs, offset, true);
+ image_offset = get_image_offset(bs, offset, true, &ret);
+ if (image_offset == -2) {
+ /* Failed to write block bitmap: can't proceed with write */
+ goto fail;
+ }
n_bytes = MIN(bytes, s->block_size - (offset % s->block_size));
if (image_offset == -1) {
@@ -702,7 +717,7 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
qemu_co_mutex_lock(&s->lock);
- offset = get_image_offset(bs, sector_num << BDRV_SECTOR_BITS, false);
+ offset = get_image_offset(bs, sector_num << BDRV_SECTOR_BITS, false, NULL);
start = offset;
allocated = (offset != -1);
*pnum = 0;
@@ -727,7 +742,8 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
if (nb_sectors == 0) {
break;
}
- offset = get_image_offset(bs, sector_num << BDRV_SECTOR_BITS, false);
+ offset = get_image_offset(bs, sector_num << BDRV_SECTOR_BITS, false,
+ NULL);
} while (offset == -1);
qemu_co_mutex_unlock(&s->lock);
diff --git a/block/vvfat.c b/block/vvfat.c
index 4dae790203..a9e207f7f0 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -71,6 +71,17 @@ void nonono(const char* file, int line, const char* msg) {
#endif
+/* bootsector OEM name. see related compatibility problems at:
+ * https://jdebp.eu/FGA/volume-boot-block-oem-name-field.html
+ * http://seasip.info/Misc/oemid.html
+ */
+#define BOOTSECTOR_OEM_NAME "MSWIN4.1"
+
+#define DIR_DELETED 0xe5
+#define DIR_KANJI DIR_DELETED
+#define DIR_KANJI_FAKE 0x05
+#define DIR_FREE 0x00
+
/* dynamic array functions */
typedef struct array_t {
char* pointer;
@@ -104,6 +115,7 @@ static inline int array_ensure_allocated(array_t* array, int index)
array->pointer = g_realloc(array->pointer, new_size);
if (!array->pointer)
return -1;
+ memset(array->pointer + array->size, 0, new_size - array->size);
array->size = new_size;
array->next = index + 1;
}
@@ -466,7 +478,7 @@ static direntry_t *create_long_filename(BDRVVVFATState *s, const char *filename)
static char is_free(const direntry_t* direntry)
{
- return direntry->name[0]==0xe5 || direntry->name[0]==0x00;
+ return direntry->name[0] == DIR_DELETED || direntry->name[0] == DIR_FREE;
}
static char is_volume_label(const direntry_t* direntry)
@@ -487,7 +499,7 @@ static char is_short_name(const direntry_t* direntry)
static char is_directory(const direntry_t* direntry)
{
- return direntry->attributes & 0x10 && direntry->name[0] != 0xe5;
+ return direntry->attributes & 0x10 && direntry->name[0] != DIR_DELETED;
}
static inline char is_dot(const direntry_t* direntry)
@@ -537,7 +549,7 @@ static direntry_t *create_short_filename(BDRVVVFATState *s,
const gchar *p, *last_dot = NULL;
gunichar c;
bool lossy_conversion = false;
- char tail[11];
+ char tail[8];
if (!entry) {
return NULL;
@@ -589,8 +601,8 @@ static direntry_t *create_short_filename(BDRVVVFATState *s,
}
}
- if (entry->name[0] == 0xe5) {
- entry->name[0] = 0x05;
+ if (entry->name[0] == DIR_KANJI) {
+ entry->name[0] = DIR_KANJI_FAKE;
}
/* numeric-tail generation */
@@ -602,7 +614,8 @@ static direntry_t *create_short_filename(BDRVVVFATState *s,
for (i = lossy_conversion ? 1 : 0; i < 999999; i++) {
direntry_t *entry1;
if (i > 0) {
- int len = sprintf(tail, "~%d", i);
+ int len = snprintf(tail, sizeof(tail), "~%u", (unsigned)i);
+ assert(len <= 7);
memcpy(entry->name + MIN(j, 8 - len), tail, len);
}
for (entry1 = array_get(&(s->directory), directory_start);
@@ -1023,7 +1036,7 @@ static int init_directories(BDRVVVFATState* s,
bootsector->jump[0]=0xeb;
bootsector->jump[1]=0x3e;
bootsector->jump[2]=0x90;
- memcpy(bootsector->name, "MSWIN4.1", 8);
+ memcpy(bootsector->name, BOOTSECTOR_OEM_NAME, 8);
bootsector->sector_size=cpu_to_le16(0x200);
bootsector->sectors_per_cluster=s->sectors_per_cluster;
bootsector->reserved_sectors=cpu_to_le16(1);
@@ -1658,6 +1671,7 @@ typedef struct {
* filename length is 0x3f * 13 bytes.
*/
unsigned char name[0x3f * 13 + 1];
+ gunichar2 name2[0x3f * 13 + 1];
int checksum, len;
int sequence_number;
} long_file_name;
@@ -1679,16 +1693,21 @@ static int parse_long_name(long_file_name* lfn,
return 1;
if (pointer[0] & 0x40) {
+ /* first entry; do some initialization */
lfn->sequence_number = pointer[0] & 0x3f;
lfn->checksum = pointer[13];
lfn->name[0] = 0;
lfn->name[lfn->sequence_number * 13] = 0;
- } else if ((pointer[0] & 0x3f) != --lfn->sequence_number)
+ } else if ((pointer[0] & 0x3f) != --lfn->sequence_number) {
+ /* not the expected sequence number */
return -1;
- else if (pointer[13] != lfn->checksum)
+ } else if (pointer[13] != lfn->checksum) {
+ /* not the expected checksum */
return -2;
- else if (pointer[12] || pointer[26] || pointer[27])
+ } else if (pointer[12] || pointer[26] || pointer[27]) {
+ /* invalid zero fields */
return -3;
+ }
offset = 13 * (lfn->sequence_number - 1);
for (i = 0, j = 1; i < 13; i++, j+=2) {
@@ -1697,16 +1716,29 @@ static int parse_long_name(long_file_name* lfn,
else if (j == 26)
j = 28;
- if (pointer[j+1] == 0)
- lfn->name[offset + i] = pointer[j];
- else if (pointer[j+1] != 0xff || (pointer[0] & 0x40) == 0)
- return -4;
- else
- lfn->name[offset + i] = 0;
+ if (pointer[j] == 0 && pointer[j + 1] == 0) {
+ /* end of long file name */
+ break;
+ }
+ gunichar2 c = (pointer[j + 1] << 8) + pointer[j];
+ lfn->name2[offset + i] = c;
}
- if (pointer[0] & 0x40)
- lfn->len = offset + strlen((char*)lfn->name + offset);
+ if (pointer[0] & 0x40) {
+ /* first entry; set len */
+ lfn->len = offset + i;
+ }
+ if ((pointer[0] & 0x3f) == 0x01) {
+ /* last entry; finalize entry */
+ glong olen;
+ gchar *utf8 = g_utf16_to_utf8(lfn->name2, lfn->len, NULL, &olen, NULL);
+ if (!utf8) {
+ return -4;
+ }
+ lfn->len = olen;
+ memcpy(lfn->name, utf8, olen + 1);
+ g_free(utf8);
+ }
return 0;
}
@@ -1722,12 +1754,14 @@ static int parse_short_name(BDRVVVFATState* s,
for (j = 7; j >= 0 && direntry->name[j] == ' '; j--);
for (i = 0; i <= j; i++) {
- if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f)
+ uint8_t c = direntry->name[i];
+ if (c != to_valid_short_char(c)) {
return -1;
- else if (s->downcase_short_names)
+ } else if (s->downcase_short_names) {
lfn->name[i] = qemu_tolower(direntry->name[i]);
- else
+ } else {
lfn->name[i] = direntry->name[i];
+ }
}
for (j = 2; j >= 0 && direntry->name[8 + j] == ' '; j--) {
@@ -1737,7 +1771,7 @@ static int parse_short_name(BDRVVVFATState* s,
lfn->name[i + j + 1] = '\0';
for (;j >= 0; j--) {
uint8_t c = direntry->name[8 + j];
- if (c <= ' ' || c > 0x7f) {
+ if (c != to_valid_short_char(c)) {
return -2;
} else if (s->downcase_short_names) {
lfn->name[i + j] = qemu_tolower(c);
@@ -1748,8 +1782,8 @@ static int parse_short_name(BDRVVVFATState* s,
} else
lfn->name[i + j + 1] = '\0';
- if (lfn->name[0] == 0x05) {
- lfn->name[0] = 0xe5;
+ if (lfn->name[0] == DIR_KANJI_FAKE) {
+ lfn->name[0] = DIR_KANJI;
}
lfn->len = strlen((char*)lfn->name);
@@ -2955,7 +2989,6 @@ DLOG(checkpoint());
/*
* Some sanity checks:
* - do not allow writing to the boot sector
- * - do not allow to write non-ASCII filenames
*/
if (sector_num < s->offset_to_fat)
@@ -2989,13 +3022,8 @@ DLOG(checkpoint());
direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
for (k = 0; k < (end - begin) * 0x10; k++) {
- /* do not allow non-ASCII filenames */
- if (parse_long_name(&lfn, direntries + k) < 0) {
- fprintf(stderr, "Warning: non-ASCII filename\n");
- return -1;
- }
/* no access to the direntry of a read-only file */
- else if (is_short_name(direntries+k) &&
+ if (is_short_name(direntries + k) &&
(direntries[k].attributes & 1)) {
if (memcmp(direntries + k,
array_get(&(s->directory), dir_index + k),
diff --git a/blockdev.c b/blockdev.c
index 7f53cc8bb3..6469f161df 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1710,7 +1710,8 @@ static void external_snapshot_prepare(BlkActionState *common,
}
flags = state->old_bs->open_flags;
- flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ);
+ flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_COPY_ON_READ);
+ flags |= BDRV_O_NO_BACKING;
/* create new image w/backing file */
mode = s->has_mode ? s->mode : NEW_IMAGE_MODE_ABSOLUTE_PATHS;
@@ -1735,8 +1736,6 @@ static void external_snapshot_prepare(BlkActionState *common,
qdict_put_str(options, "node-name", snapshot_node_name);
}
qdict_put_str(options, "driver", format);
-
- flags |= BDRV_O_NO_BACKING;
}
state->new_bs = bdrv_open(new_image_file, snapshot_ref, options, flags,
@@ -3548,6 +3547,9 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
backing_mode = MIRROR_OPEN_BACKING_CHAIN;
}
+ /* Don't open backing image in create() */
+ flags |= BDRV_O_NO_BACKING;
+
if ((arg->sync == MIRROR_SYNC_MODE_FULL || !source)
&& arg->mode != NEW_IMAGE_MODE_EXISTING)
{
@@ -3587,8 +3589,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
/* Mirroring takes care of copy-on-write using the source's backing
* file.
*/
- target_bs = bdrv_open(arg->target, NULL, options,
- flags | BDRV_O_NO_BACKING, errp);
+ target_bs = bdrv_open(arg->target, NULL, options, flags, errp);
if (!target_bs) {
goto out;
}
diff --git a/docs/devel/bitmaps.md b/docs/devel/bitmaps.md
deleted file mode 100644
index a2e8d51163..0000000000
--- a/docs/devel/bitmaps.md
+++ /dev/null
@@ -1,505 +0,0 @@
-<!--
-Copyright 2015 John Snow <jsnow@redhat.com> and Red Hat, Inc.
-All rights reserved.
-
-This file is licensed via The FreeBSD Documentation License, the full text of
-which is included at the end of this document.
--->
-
-# Dirty Bitmaps and Incremental Backup
-
-* Dirty Bitmaps are objects that track which data needs to be backed up for the
- next incremental backup.
-
-* Dirty bitmaps can be created at any time and attached to any node
- (not just complete drives.)
-
-## Dirty Bitmap Names
-
-* A dirty bitmap's name is unique to the node, but bitmaps attached to different
- nodes can share the same name.
-
-* Dirty bitmaps created for internal use by QEMU may be anonymous and have no
- name, but any user-created bitmaps may not be. There can be any number of
- anonymous bitmaps per node.
-
-* The name of a user-created bitmap must not be empty ("").
-
-## Bitmap Modes
-
-* A Bitmap can be "frozen," which means that it is currently in-use by a backup
- operation and cannot be deleted, renamed, written to, reset,
- etc.
-
-* The normal operating mode for a bitmap is "active."
-
-## Basic QMP Usage
-
-### Supported Commands ###
-
-* block-dirty-bitmap-add
-* block-dirty-bitmap-remove
-* block-dirty-bitmap-clear
-
-### Creation
-
-* To create a new bitmap, enabled, on the drive with id=drive0:
-
-```json
-{ "execute": "block-dirty-bitmap-add",
- "arguments": {
- "node": "drive0",
- "name": "bitmap0"
- }
-}
-```
-
-* This bitmap will have a default granularity that matches the cluster size of
- its associated drive, if available, clamped to between [4KiB, 64KiB].
- The current default for qcow2 is 64KiB.
-
-* To create a new bitmap that tracks changes in 32KiB segments:
-
-```json
-{ "execute": "block-dirty-bitmap-add",
- "arguments": {
- "node": "drive0",
- "name": "bitmap0",
- "granularity": 32768
- }
-}
-```
-
-### Deletion
-
-* Bitmaps that are frozen cannot be deleted.
-
-* Deleting the bitmap does not impact any other bitmaps attached to the same
- node, nor does it affect any backups already created from this node.
-
-* Because bitmaps are only unique to the node to which they are attached,
- you must specify the node/drive name here, too.
-
-```json
-{ "execute": "block-dirty-bitmap-remove",
- "arguments": {
- "node": "drive0",
- "name": "bitmap0"
- }
-}
-```
-
-### Resetting
-
-* Resetting a bitmap will clear all information it holds.
-
-* An incremental backup created from an empty bitmap will copy no data,
- as if nothing has changed.
-
-```json
-{ "execute": "block-dirty-bitmap-clear",
- "arguments": {
- "node": "drive0",
- "name": "bitmap0"
- }
-}
-```
-
-## Transactions
-
-### Justification
-
-Bitmaps can be safely modified when the VM is paused or halted by using
-the basic QMP commands. For instance, you might perform the following actions:
-
-1. Boot the VM in a paused state.
-2. Create a full drive backup of drive0.
-3. Create a new bitmap attached to drive0.
-4. Resume execution of the VM.
-5. Incremental backups are ready to be created.
-
-At this point, the bitmap and drive backup would be correctly in sync,
-and incremental backups made from this point forward would be correctly aligned
-to the full drive backup.
-
-This is not particularly useful if we decide we want to start incremental
-backups after the VM has been running for a while, for which we will need to
-perform actions such as the following:
-
-1. Boot the VM and begin execution.
-2. Using a single transaction, perform the following operations:
- * Create bitmap0.
- * Create a full drive backup of drive0.
-3. Incremental backups are now ready to be created.
-
-### Supported Bitmap Transactions
-
-* block-dirty-bitmap-add
-* block-dirty-bitmap-clear
-
-The usages are identical to their respective QMP commands, but see below
-for examples.
-
-### Example: New Incremental Backup
-
-As outlined in the justification, perhaps we want to create a new incremental
-backup chain attached to a drive.
-
-```json
-{ "execute": "transaction",
- "arguments": {
- "actions": [
- {"type": "block-dirty-bitmap-add",
- "data": {"node": "drive0", "name": "bitmap0"} },
- {"type": "drive-backup",
- "data": {"device": "drive0", "target": "/path/to/full_backup.img",
- "sync": "full", "format": "qcow2"} }
- ]
- }
-}
-```
-
-### Example: New Incremental Backup Anchor Point
-
-Maybe we just want to create a new full backup with an existing bitmap and
-want to reset the bitmap to track the new chain.
-
-```json
-{ "execute": "transaction",
- "arguments": {
- "actions": [
- {"type": "block-dirty-bitmap-clear",
- "data": {"node": "drive0", "name": "bitmap0"} },
- {"type": "drive-backup",
- "data": {"device": "drive0", "target": "/path/to/new_full_backup.img",
- "sync": "full", "format": "qcow2"} }
- ]
- }
-}
-```
-
-## Incremental Backups
-
-The star of the show.
-
-**Nota Bene!** Only incremental backups of entire drives are supported for now.
-So despite the fact that you can attach a bitmap to any arbitrary node, they are
-only currently useful when attached to the root node. This is because
-drive-backup only supports drives/devices instead of arbitrary nodes.
-
-### Example: First Incremental Backup
-
-1. Create a full backup and sync it to the dirty bitmap, as in the transactional
-examples above; or with the VM offline, manually create a full copy and then
-create a new bitmap before the VM begins execution.
-
- * Let's assume the full backup is named 'full_backup.img'.
- * Let's assume the bitmap you created is 'bitmap0' attached to 'drive0'.
-
-2. Create a destination image for the incremental backup that utilizes the
-full backup as a backing image.
-
- * Let's assume it is named 'incremental.0.img'.
-
- ```sh
- # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
- ```
-
-3. Issue the incremental backup command:
-
- ```json
- { "execute": "drive-backup",
- "arguments": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "target": "incremental.0.img",
- "format": "qcow2",
- "sync": "incremental",
- "mode": "existing"
- }
- }
- ```
-
-### Example: Second Incremental Backup
-
-1. Create a new destination image for the incremental backup that points to the
- previous one, e.g.: 'incremental.1.img'
-
- ```sh
- # qemu-img create -f qcow2 incremental.1.img -b incremental.0.img -F qcow2
- ```
-
-2. Issue a new incremental backup command. The only difference here is that we
- have changed the target image below.
-
- ```json
- { "execute": "drive-backup",
- "arguments": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "target": "incremental.1.img",
- "format": "qcow2",
- "sync": "incremental",
- "mode": "existing"
- }
- }
- ```
-
-## Errors
-
-* In the event of an error that occurs after a backup job is successfully
- launched, either by a direct QMP command or a QMP transaction, the user
- will receive a BLOCK_JOB_COMPLETE event with a failure message, accompanied
- by a BLOCK_JOB_ERROR event.
-
-* In the case of an event being cancelled, the user will receive a
- BLOCK_JOB_CANCELLED event instead of a pair of COMPLETE and ERROR events.
-
-* In either case, the incremental backup data contained within the bitmap is
- safely rolled back, and the data within the bitmap is not lost. The image
- file created for the failed attempt can be safely deleted.
-
-* Once the underlying problem is fixed (e.g. more storage space is freed up),
- you can simply retry the incremental backup command with the same bitmap.
-
-### Example
-
-1. Create a target image:
-
- ```sh
- # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
- ```
-
-2. Attempt to create an incremental backup via QMP:
-
- ```json
- { "execute": "drive-backup",
- "arguments": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "target": "incremental.0.img",
- "format": "qcow2",
- "sync": "incremental",
- "mode": "existing"
- }
- }
- ```
-
-3. Receive an event notifying us of failure:
-
- ```json
- { "timestamp": { "seconds": 1424709442, "microseconds": 844524 },
- "data": { "speed": 0, "offset": 0, "len": 67108864,
- "error": "No space left on device",
- "device": "drive1", "type": "backup" },
- "event": "BLOCK_JOB_COMPLETED" }
- ```
-
-4. Delete the failed incremental, and re-create the image.
-
- ```sh
- # rm incremental.0.img
- # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
- ```
-
-5. Retry the command after fixing the underlying problem,
- such as freeing up space on the backup volume:
-
- ```json
- { "execute": "drive-backup",
- "arguments": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "target": "incremental.0.img",
- "format": "qcow2",
- "sync": "incremental",
- "mode": "existing"
- }
- }
- ```
-
-6. Receive confirmation that the job completed successfully:
-
- ```json
- { "timestamp": { "seconds": 1424709668, "microseconds": 526525 },
- "data": { "device": "drive1", "type": "backup",
- "speed": 0, "len": 67108864, "offset": 67108864},
- "event": "BLOCK_JOB_COMPLETED" }
- ```
-
-### Partial Transactional Failures
-
-* Sometimes, a transaction will succeed in launching and return success,
- but then later the backup jobs themselves may fail. It is possible that
- a management application may have to deal with a partial backup failure
- after a successful transaction.
-
-* If multiple backup jobs are specified in a single transaction, when one of
- them fails, it will not interact with the other backup jobs in any way.
-
-* The job(s) that succeeded will clear the dirty bitmap associated with the
- operation, but the job(s) that failed will not. It is not "safe" to delete
- any incremental backups that were created successfully in this scenario,
- even though others failed.
-
-#### Example
-
-* QMP example highlighting two backup jobs:
-
- ```json
- { "execute": "transaction",
- "arguments": {
- "actions": [
- { "type": "drive-backup",
- "data": { "device": "drive0", "bitmap": "bitmap0",
- "format": "qcow2", "mode": "existing",
- "sync": "incremental", "target": "d0-incr-1.qcow2" } },
- { "type": "drive-backup",
- "data": { "device": "drive1", "bitmap": "bitmap1",
- "format": "qcow2", "mode": "existing",
- "sync": "incremental", "target": "d1-incr-1.qcow2" } },
- ]
- }
- }
- ```
-
-* QMP example response, highlighting one success and one failure:
- * Acknowledgement that the Transaction was accepted and jobs were launched:
- ```json
- { "return": {} }
- ```
-
- * Later, QEMU sends notice that the first job was completed:
- ```json
- { "timestamp": { "seconds": 1447192343, "microseconds": 615698 },
- "data": { "device": "drive0", "type": "backup",
- "speed": 0, "len": 67108864, "offset": 67108864 },
- "event": "BLOCK_JOB_COMPLETED"
- }
- ```
-
- * Later yet, QEMU sends notice that the second job has failed:
- ```json
- { "timestamp": { "seconds": 1447192399, "microseconds": 683015 },
- "data": { "device": "drive1", "action": "report",
- "operation": "read" },
- "event": "BLOCK_JOB_ERROR" }
- ```
-
- ```json
- { "timestamp": { "seconds": 1447192399, "microseconds": 685853 },
- "data": { "speed": 0, "offset": 0, "len": 67108864,
- "error": "Input/output error",
- "device": "drive1", "type": "backup" },
- "event": "BLOCK_JOB_COMPLETED" }
-
-* In the above example, "d0-incr-1.qcow2" is valid and must be kept,
- but "d1-incr-1.qcow2" is invalid and should be deleted. If a VM-wide
- incremental backup of all drives at a point-in-time is to be made,
- new backups for both drives will need to be made, taking into account
- that a new incremental backup for drive0 needs to be based on top of
- "d0-incr-1.qcow2."
-
-### Grouped Completion Mode
-
-* While jobs launched by transactions normally complete or fail on their own,
- it is possible to instruct them to complete or fail together as a group.
-
-* QMP transactions take an optional properties structure that can affect
- the semantics of the transaction.
-
-* The "completion-mode" transaction property can be either "individual"
- which is the default, legacy behavior described above, or "grouped,"
- a new behavior detailed below.
-
-* Delayed Completion: In grouped completion mode, no jobs will report
- success until all jobs are ready to report success.
-
-* Grouped failure: If any job fails in grouped completion mode, all remaining
- jobs will be cancelled. Any incremental backups will restore their dirty
- bitmap objects as if no backup command was ever issued.
-
- * Regardless of if QEMU reports a particular incremental backup job as
- CANCELLED or as an ERROR, the in-memory bitmap will be restored.
-
-#### Example
-
-* Here's the same example scenario from above with the new property:
-
- ```json
- { "execute": "transaction",
- "arguments": {
- "actions": [
- { "type": "drive-backup",
- "data": { "device": "drive0", "bitmap": "bitmap0",
- "format": "qcow2", "mode": "existing",
- "sync": "incremental", "target": "d0-incr-1.qcow2" } },
- { "type": "drive-backup",
- "data": { "device": "drive1", "bitmap": "bitmap1",
- "format": "qcow2", "mode": "existing",
- "sync": "incremental", "target": "d1-incr-1.qcow2" } },
- ],
- "properties": {
- "completion-mode": "grouped"
- }
- }
- }
- ```
-
-* QMP example response, highlighting a failure for drive2:
- * Acknowledgement that the Transaction was accepted and jobs were launched:
- ```json
- { "return": {} }
- ```
-
- * Later, QEMU sends notice that the second job has errored out,
- but that the first job was also cancelled:
- ```json
- { "timestamp": { "seconds": 1447193702, "microseconds": 632377 },
- "data": { "device": "drive1", "action": "report",
- "operation": "read" },
- "event": "BLOCK_JOB_ERROR" }
- ```
-
- ```json
- { "timestamp": { "seconds": 1447193702, "microseconds": 640074 },
- "data": { "speed": 0, "offset": 0, "len": 67108864,
- "error": "Input/output error",
- "device": "drive1", "type": "backup" },
- "event": "BLOCK_JOB_COMPLETED" }
- ```
-
- ```json
- { "timestamp": { "seconds": 1447193702, "microseconds": 640163 },
- "data": { "device": "drive0", "type": "backup", "speed": 0,
- "len": 67108864, "offset": 16777216 },
- "event": "BLOCK_JOB_CANCELLED" }
- ```
-
-<!--
-The FreeBSD Documentation License
-
-Redistribution and use in source (Markdown) and 'compiled' forms (SGML, HTML,
-PDF, PostScript, RTF and so forth) with or without modification, are permitted
-provided that the following conditions are met:
-
-Redistributions of source code (Markdown) must retain the above copyright
-notice, this list of conditions and the following disclaimer of this file
-unmodified.
-
-Redistributions in compiled form (transformed to other DTDs, converted to PDF,
-PostScript, RTF and other formats) must reproduce the above copyright notice,
-this list of conditions and the following disclaimer in the documentation and/or
-other materials provided with the distribution.
-
-THIS DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-THIS DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--->
diff --git a/docs/interop/bitmaps.rst b/docs/interop/bitmaps.rst
new file mode 100644
index 0000000000..7bcfe7f461
--- /dev/null
+++ b/docs/interop/bitmaps.rst
@@ -0,0 +1,555 @@
+..
+ Copyright 2015 John Snow <jsnow@redhat.com> and Red Hat, Inc.
+ All rights reserved.
+
+ This file is licensed via The FreeBSD Documentation License, the full
+ text of which is included at the end of this document.
+
+====================================
+Dirty Bitmaps and Incremental Backup
+====================================
+
+- Dirty Bitmaps are objects that track which data needs to be backed up
+ for the next incremental backup.
+
+- Dirty bitmaps can be created at any time and attached to any node
+ (not just complete drives).
+
+.. contents::
+
+Dirty Bitmap Names
+------------------
+
+- A dirty bitmap's name is unique to the node, but bitmaps attached to
+ different nodes can share the same name.
+
+- Dirty bitmaps created for internal use by QEMU may be anonymous and
+ have no name, but any user-created bitmaps must have a name. There
+ can be any number of anonymous bitmaps per node.
+
+- The name of a user-created bitmap must not be empty ("").
+
+Bitmap Modes
+------------
+
+- A bitmap can be "frozen," which means that it is currently in-use by
+ a backup operation and cannot be deleted, renamed, written to, reset,
+ etc.
+
+- The normal operating mode for a bitmap is "active."
+
+Basic QMP Usage
+---------------
+
+Supported Commands
+~~~~~~~~~~~~~~~~~~
+
+- ``block-dirty-bitmap-add``
+- ``block-dirty-bitmap-remove``
+- ``block-dirty-bitmap-clear``
+
+Creation
+~~~~~~~~
+
+- To create a new bitmap, enabled, on the drive with id=drive0:
+
+.. code:: json
+
+ { "execute": "block-dirty-bitmap-add",
+ "arguments": {
+ "node": "drive0",
+ "name": "bitmap0"
+ }
+ }
+
+- This bitmap will have a default granularity that matches the cluster
+ size of its associated drive, if available, clamped to between [4KiB,
+ 64KiB]. The current default for qcow2 is 64KiB.
+
+- To create a new bitmap that tracks changes in 32KiB segments:
+
+.. code:: json
+
+ { "execute": "block-dirty-bitmap-add",
+ "arguments": {
+ "node": "drive0",
+ "name": "bitmap0",
+ "granularity": 32768
+ }
+ }
+
+Deletion
+~~~~~~~~
+
+- Bitmaps that are frozen cannot be deleted.
+
+- Deleting the bitmap does not impact any other bitmaps attached to the
+ same node, nor does it affect any backups already created from this
+ node.
+
+- Because bitmaps are only unique to the node to which they are
+ attached, you must specify the node/drive name here, too.
+
+.. code:: json
+
+ { "execute": "block-dirty-bitmap-remove",
+ "arguments": {
+ "node": "drive0",
+ "name": "bitmap0"
+ }
+ }
+
+Resetting
+~~~~~~~~~
+
+- Resetting a bitmap will clear all information it holds.
+
+- An incremental backup created from an empty bitmap will copy no data,
+ as if nothing has changed.
+
+.. code:: json
+
+ { "execute": "block-dirty-bitmap-clear",
+ "arguments": {
+ "node": "drive0",
+ "name": "bitmap0"
+ }
+ }
+
+Transactions
+------------
+
+Justification
+~~~~~~~~~~~~~
+
+Bitmaps can be safely modified when the VM is paused or halted by using
+the basic QMP commands. For instance, you might perform the following
+actions:
+
+1. Boot the VM in a paused state.
+2. Create a full drive backup of drive0.
+3. Create a new bitmap attached to drive0.
+4. Resume execution of the VM.
+5. Incremental backups are ready to be created.
+
+At this point, the bitmap and drive backup would be correctly in sync,
+and incremental backups made from this point forward would be correctly
+aligned to the full drive backup.
+
+This is not particularly useful if we decide we want to start
+incremental backups after the VM has been running for a while, for which
+we will need to perform actions such as the following:
+
+1. Boot the VM and begin execution.
+2. Using a single transaction, perform the following operations:
+
+ - Create ``bitmap0``.
+ - Create a full drive backup of ``drive0``.
+
+3. Incremental backups are now ready to be created.
+
+Supported Bitmap Transactions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- ``block-dirty-bitmap-add``
+- ``block-dirty-bitmap-clear``
+
+The usages are identical to their respective QMP commands, but see below
+for examples.
+
+Example: New Incremental Backup
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+As outlined in the justification, perhaps we want to create a new
+incremental backup chain attached to a drive.
+
+.. code:: json
+
+ { "execute": "transaction",
+ "arguments": {
+ "actions": [
+ {"type": "block-dirty-bitmap-add",
+ "data": {"node": "drive0", "name": "bitmap0"} },
+ {"type": "drive-backup",
+ "data": {"device": "drive0", "target": "/path/to/full_backup.img",
+ "sync": "full", "format": "qcow2"} }
+ ]
+ }
+ }
+
+Example: New Incremental Backup Anchor Point
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Maybe we just want to create a new full backup with an existing bitmap
+and want to reset the bitmap to track the new chain.
+
+.. code:: json
+
+ { "execute": "transaction",
+ "arguments": {
+ "actions": [
+ {"type": "block-dirty-bitmap-clear",
+ "data": {"node": "drive0", "name": "bitmap0"} },
+ {"type": "drive-backup",
+ "data": {"device": "drive0", "target": "/path/to/new_full_backup.img",
+ "sync": "full", "format": "qcow2"} }
+ ]
+ }
+ }
+
+Incremental Backups
+-------------------
+
+The star of the show.
+
+**Nota Bene!** Only incremental backups of entire drives are supported
+for now. So despite the fact that you can attach a bitmap to any
+arbitrary node, they are only currently useful when attached to the root
+node. This is because drive-backup only supports drives/devices instead
+of arbitrary nodes.
+
+Example: First Incremental Backup
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Create a full backup and sync it to the dirty bitmap, as in the
+ transactional examples above; or with the VM offline, manually create
+ a full copy and then create a new bitmap before the VM begins
+ execution.
+
+ - Let's assume the full backup is named ``full_backup.img``.
+ - Let's assume the bitmap you created is ``bitmap0`` attached to
+ ``drive0``.
+
+2. Create a destination image for the incremental backup that utilizes
+ the full backup as a backing image.
+
+ - Let's assume the new incremental image is named
+ ``incremental.0.img``.
+
+ .. code:: bash
+
+ $ qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
+
+3. Issue the incremental backup command:
+
+ .. code:: json
+
+ { "execute": "drive-backup",
+ "arguments": {
+ "device": "drive0",
+ "bitmap": "bitmap0",
+ "target": "incremental.0.img",
+ "format": "qcow2",
+ "sync": "incremental",
+ "mode": "existing"
+ }
+ }
+
+Example: Second Incremental Backup
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Create a new destination image for the incremental backup that points
+ to the previous one, e.g.: ``incremental.1.img``
+
+ .. code:: bash
+
+ $ qemu-img create -f qcow2 incremental.1.img -b incremental.0.img -F qcow2
+
+2. Issue a new incremental backup command. The only difference here is
+ that we have changed the target image below.
+
+ .. code:: json
+
+ { "execute": "drive-backup",
+ "arguments": {
+ "device": "drive0",
+ "bitmap": "bitmap0",
+ "target": "incremental.1.img",
+ "format": "qcow2",
+ "sync": "incremental",
+ "mode": "existing"
+ }
+ }
+
+Errors
+------
+
+- In the event of an error that occurs after a backup job is
+ successfully launched, either by a direct QMP command or a QMP
+ transaction, the user will receive a ``BLOCK_JOB_COMPLETE`` event with
+ a failure message, accompanied by a ``BLOCK_JOB_ERROR`` event.
+
+- In the case of an event being cancelled, the user will receive a
+ ``BLOCK_JOB_CANCELLED`` event instead of a pair of COMPLETE and ERROR
+ events.
+
+- In either case, the incremental backup data contained within the
+ bitmap is safely rolled back, and the data within the bitmap is not
+ lost. The image file created for the failed attempt can be safely
+ deleted.
+
+- Once the underlying problem is fixed (e.g. more storage space is
+ freed up), you can simply retry the incremental backup command with
+ the same bitmap.
+
+Example
+~~~~~~~
+
+1. Create a target image:
+
+ .. code:: bash
+
+ $ qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
+
+2. Attempt to create an incremental backup via QMP:
+
+ .. code:: json
+
+ { "execute": "drive-backup",
+ "arguments": {
+ "device": "drive0",
+ "bitmap": "bitmap0",
+ "target": "incremental.0.img",
+ "format": "qcow2",
+ "sync": "incremental",
+ "mode": "existing"
+ }
+ }
+
+3. Receive an event notifying us of failure:
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1424709442, "microseconds": 844524 },
+ "data": { "speed": 0, "offset": 0, "len": 67108864,
+ "error": "No space left on device",
+ "device": "drive1", "type": "backup" },
+ "event": "BLOCK_JOB_COMPLETED" }
+
+4. Delete the failed incremental, and re-create the image.
+
+ .. code:: bash
+
+ $ rm incremental.0.img
+ $ qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
+
+5. Retry the command after fixing the underlying problem, such as
+ freeing up space on the backup volume:
+
+ .. code:: json
+
+ { "execute": "drive-backup",
+ "arguments": {
+ "device": "drive0",
+ "bitmap": "bitmap0",
+ "target": "incremental.0.img",
+ "format": "qcow2",
+ "sync": "incremental",
+ "mode": "existing"
+ }
+ }
+
+6. Receive confirmation that the job completed successfully:
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1424709668, "microseconds": 526525 },
+ "data": { "device": "drive1", "type": "backup",
+ "speed": 0, "len": 67108864, "offset": 67108864},
+ "event": "BLOCK_JOB_COMPLETED" }
+
+Partial Transactional Failures
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Sometimes, a transaction will succeed in launching and return
+ success, but then later the backup jobs themselves may fail. It is
+ possible that a management application may have to deal with a
+ partial backup failure after a successful transaction.
+
+- If multiple backup jobs are specified in a single transaction, when
+ one of them fails, it will not interact with the other backup jobs in
+ any way.
+
+- The job(s) that succeeded will clear the dirty bitmap associated with
+ the operation, but the job(s) that failed will not. It is not "safe"
+ to delete any incremental backups that were created successfully in
+ this scenario, even though others failed.
+
+Example
+^^^^^^^
+
+- QMP example highlighting two backup jobs:
+
+ .. code:: json
+
+ { "execute": "transaction",
+ "arguments": {
+ "actions": [
+ { "type": "drive-backup",
+ "data": { "device": "drive0", "bitmap": "bitmap0",
+ "format": "qcow2", "mode": "existing",
+ "sync": "incremental", "target": "d0-incr-1.qcow2" } },
+ { "type": "drive-backup",
+ "data": { "device": "drive1", "bitmap": "bitmap1",
+ "format": "qcow2", "mode": "existing",
+ "sync": "incremental", "target": "d1-incr-1.qcow2" } },
+ ]
+ }
+ }
+
+- QMP example response, highlighting one success and one failure:
+
+ - Acknowledgement that the Transaction was accepted and jobs were
+ launched:
+
+ .. code:: json
+
+ { "return": {} }
+
+ - Later, QEMU sends notice that the first job was completed:
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1447192343, "microseconds": 615698 },
+ "data": { "device": "drive0", "type": "backup",
+ "speed": 0, "len": 67108864, "offset": 67108864 },
+ "event": "BLOCK_JOB_COMPLETED"
+ }
+
+ - Later yet, QEMU sends notice that the second job has failed:
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1447192399, "microseconds": 683015 },
+ "data": { "device": "drive1", "action": "report",
+ "operation": "read" },
+ "event": "BLOCK_JOB_ERROR" }
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1447192399, "microseconds":
+ 685853 }, "data": { "speed": 0, "offset": 0, "len": 67108864,
+ "error": "Input/output error", "device": "drive1", "type":
+ "backup" }, "event": "BLOCK_JOB_COMPLETED" }
+
+- In the above example, ``d0-incr-1.qcow2`` is valid and must be kept,
+ but ``d1-incr-1.qcow2`` is invalid and should be deleted. If a VM-wide
+ incremental backup of all drives at a point-in-time is to be made,
+ new backups for both drives will need to be made, taking into account
+ that a new incremental backup for drive0 needs to be based on top of
+ ``d0-incr-1.qcow2``.
+
+Grouped Completion Mode
+~~~~~~~~~~~~~~~~~~~~~~~
+
+- While jobs launched by transactions normally complete or fail on
+ their own, it is possible to instruct them to complete or fail
+ together as a group.
+
+- QMP transactions take an optional properties structure that can
+ affect the semantics of the transaction.
+
+- The "completion-mode" transaction property can be either "individual"
+ which is the default, legacy behavior described above, or "grouped,"
+ a new behavior detailed below.
+
+- Delayed Completion: In grouped completion mode, no jobs will report
+ success until all jobs are ready to report success.
+
+- Grouped failure: If any job fails in grouped completion mode, all
+ remaining jobs will be cancelled. Any incremental backups will
+ restore their dirty bitmap objects as if no backup command was ever
+ issued.
+
+ - Regardless of if QEMU reports a particular incremental backup job
+ as CANCELLED or as an ERROR, the in-memory bitmap will be
+ restored.
+
+Example
+^^^^^^^
+
+- Here's the same example scenario from above with the new property:
+
+ .. code:: json
+
+ { "execute": "transaction",
+ "arguments": {
+ "actions": [
+ { "type": "drive-backup",
+ "data": { "device": "drive0", "bitmap": "bitmap0",
+ "format": "qcow2", "mode": "existing",
+ "sync": "incremental", "target": "d0-incr-1.qcow2" } },
+ { "type": "drive-backup",
+ "data": { "device": "drive1", "bitmap": "bitmap1",
+ "format": "qcow2", "mode": "existing",
+ "sync": "incremental", "target": "d1-incr-1.qcow2" } },
+ ],
+ "properties": {
+ "completion-mode": "grouped"
+ }
+ }
+ }
+
+- QMP example response, highlighting a failure for ``drive2``:
+
+ - Acknowledgement that the Transaction was accepted and jobs were
+ launched:
+
+ .. code:: json
+
+ { "return": {} }
+
+ - Later, QEMU sends notice that the second job has errored out, but
+ that the first job was also cancelled:
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1447193702, "microseconds": 632377 },
+ "data": { "device": "drive1", "action": "report",
+ "operation": "read" },
+ "event": "BLOCK_JOB_ERROR" }
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1447193702, "microseconds": 640074 },
+ "data": { "speed": 0, "offset": 0, "len": 67108864,
+ "error": "Input/output error",
+ "device": "drive1", "type": "backup" },
+ "event": "BLOCK_JOB_COMPLETED" }
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1447193702, "microseconds": 640163 },
+ "data": { "device": "drive0", "type": "backup", "speed": 0,
+ "len": 67108864, "offset": 16777216 },
+ "event": "BLOCK_JOB_CANCELLED" }
+
+.. raw:: html
+
+ <!--
+ The FreeBSD Documentation License
+
+ Redistribution and use in source (Markdown) and 'compiled' forms (SGML, HTML,
+ PDF, PostScript, RTF and so forth) with or without modification, are permitted
+ provided that the following conditions are met:
+
+ Redistributions of source code (Markdown) must retain the above copyright
+ notice, this list of conditions and the following disclaimer of this file
+ unmodified.
+
+ Redistributions in compiled form (transformed to other DTDs, converted to PDF,
+ PostScript, RTF and other formats) must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation and/or
+ other materials provided with the distribution.
+
+ THIS DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
diff --git a/docs/interop/live-block-operations.rst b/docs/interop/live-block-operations.rst
new file mode 100644
index 0000000000..5f0179749f
--- /dev/null
+++ b/docs/interop/live-block-operations.rst
@@ -0,0 +1,1088 @@
+..
+ Copyright (C) 2017 Red Hat Inc.
+
+ This work is licensed under the terms of the GNU GPL, version 2 or
+ later. See the COPYING file in the top-level directory.
+
+============================
+Live Block Device Operations
+============================
+
+QEMU Block Layer currently (as of QEMU 2.9) supports four major kinds of
+live block device jobs -- stream, commit, mirror, and backup. These can
+be used to manipulate disk image chains to accomplish certain tasks,
+namely: live copy data from backing files into overlays; shorten long
+disk image chains by merging data from overlays into backing files; live
+synchronize data from a disk image chain (including current active disk)
+to another target image; and point-in-time (and incremental) backups of
+a block device. Below is a description of the said block (QMP)
+primitives, and some (non-exhaustive list of) examples to illustrate
+their use.
+
+.. note::
+ The file ``qapi/block-core.json`` in the QEMU source tree has the
+ canonical QEMU API (QAPI) schema documentation for the QMP
+ primitives discussed here.
+
+.. todo (kashyapc):: Remove the ".. contents::" directive when Sphinx is
+ integrated.
+
+.. contents::
+
+Disk image backing chain notation
+---------------------------------
+
+A simple disk image chain. (This can be created live using QMP
+``blockdev-snapshot-sync``, or offline via ``qemu-img``)::
+
+ (Live QEMU)
+ |
+ .
+ V
+
+ [A] <----- [B]
+
+ (backing file) (overlay)
+
+The arrow can be read as: Image [A] is the backing file of disk image
+[B]. And live QEMU is currently writing to image [B], consequently, it
+is also referred to as the "active layer".
+
+There are two kinds of terminology that are common when referring to
+files in a disk image backing chain:
+
+(1) Directional: 'base' and 'top'. Given the simple disk image chain
+ above, image [A] can be referred to as 'base', and image [B] as
+ 'top'. (This terminology can be seen in in QAPI schema file,
+ block-core.json.)
+
+(2) Relational: 'backing file' and 'overlay'. Again, taking the same
+ simple disk image chain from the above, disk image [A] is referred
+ to as the backing file, and image [B] as overlay.
+
+ Throughout this document, we will use the relational terminology.
+
+.. important::
+ The overlay files can generally be any format that supports a
+ backing file, although QCOW2 is the preferred format and the one
+ used in this document.
+
+
+Brief overview of live block QMP primitives
+-------------------------------------------
+
+The following are the four different kinds of live block operations that
+QEMU block layer supports.
+
+(1) ``block-stream``: Live copy of data from backing files into overlay
+ files.
+
+ .. note:: Once the 'stream' operation has finished, three things to
+ note:
+
+ (a) QEMU rewrites the backing chain to remove
+ reference to the now-streamed and redundant backing
+ file;
+
+ (b) the streamed file *itself* won't be removed by QEMU,
+ and must be explicitly discarded by the user;
+
+ (c) the streamed file remains valid -- i.e. further
+ overlays can be created based on it. Refer the
+ ``block-stream`` section further below for more
+ details.
+
+(2) ``block-commit``: Live merge of data from overlay files into backing
+ files (with the optional goal of removing the overlay file from the
+ chain). Since QEMU 2.0, this includes "active ``block-commit``"
+ (i.e. merge the current active layer into the base image).
+
+ .. note:: Once the 'commit' operation has finished, there are three
+ things to note here as well:
+
+ (a) QEMU rewrites the backing chain to remove reference
+ to now-redundant overlay images that have been
+ committed into a backing file;
+
+ (b) the committed file *itself* won't be removed by QEMU
+ -- it ought to be manually removed;
+
+ (c) however, unlike in the case of ``block-stream``, the
+ intermediate images will be rendered invalid -- i.e.
+ no more further overlays can be created based on
+ them. Refer the ``block-commit`` section further
+ below for more details.
+
+(3) ``drive-mirror`` (and ``blockdev-mirror``): Synchronize a running
+ disk to another image.
+
+(4) ``drive-backup`` (and ``blockdev-backup``): Point-in-time (live) copy
+ of a block device to a destination.
+
+
+.. _`Interacting with a QEMU instance`:
+
+Interacting with a QEMU instance
+--------------------------------
+
+To show some example invocations of command-line, we will use the
+following invocation of QEMU, with a QMP server running over UNIX
+socket::
+
+ $ ./x86_64-softmmu/qemu-system-x86_64 -display none -nodefconfig \
+ -M q35 -nodefaults -m 512 \
+ -blockdev node-name=node-A,driver=qcow2,file.driver=file,file.node-name=file,file.filename=./a.qcow2 \
+ -device virtio-blk,drive=node-A,id=virtio0 \
+ -monitor stdio -qmp unix:/tmp/qmp-sock,server,nowait
+
+The ``-blockdev`` command-line option, used above, is available from
+QEMU 2.9 onwards. In the above invocation, notice the ``node-name``
+parameter that is used to refer to the disk image a.qcow2 ('node-A') --
+this is a cleaner way to refer to a disk image (as opposed to referring
+to it by spelling out file paths). So, we will continue to designate a
+``node-name`` to each further disk image created (either via
+``blockdev-snapshot-sync``, or ``blockdev-add``) as part of the disk
+image chain, and continue to refer to the disks using their
+``node-name`` (where possible, because ``block-commit`` does not yet, as
+of QEMU 2.9, accept ``node-name`` parameter) when performing various
+block operations.
+
+To interact with the QEMU instance launched above, we will use the
+``qmp-shell`` utility (located at: ``qemu/scripts/qmp``, as part of the
+QEMU source directory), which takes key-value pairs for QMP commands.
+Invoke it as below (which will also print out the complete raw JSON
+syntax for reference -- examples in the following sections)::
+
+ $ ./qmp-shell -v -p /tmp/qmp-sock
+ (QEMU)
+
+.. note::
+ In the event we have to repeat a certain QMP command, we will: for
+ the first occurrence of it, show the ``qmp-shell`` invocation, *and*
+ the corresponding raw JSON QMP syntax; but for subsequent
+ invocations, present just the ``qmp-shell`` syntax, and omit the
+ equivalent JSON output.
+
+
+Example disk image chain
+------------------------
+
+We will use the below disk image chain (and occasionally spelling it
+out where appropriate) when discussing various primitives::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+Where [A] is the original base image; [B] and [C] are intermediate
+overlay images; image [D] is the active layer -- i.e. live QEMU is
+writing to it. (The rule of thumb is: live QEMU will always be pointing
+to the rightmost image in a disk image chain.)
+
+The above image chain can be created by invoking
+``blockdev-snapshot-sync`` commands as following (which shows the
+creation of overlay image [B]) using the ``qmp-shell`` (our invocation
+also prints the raw JSON invocation of it)::
+
+ (QEMU) blockdev-snapshot-sync node-name=node-A snapshot-file=b.qcow2 snapshot-node-name=node-B format=qcow2
+ {
+ "execute": "blockdev-snapshot-sync",
+ "arguments": {
+ "node-name": "node-A",
+ "snapshot-file": "b.qcow2",
+ "format": "qcow2",
+ "snapshot-node-name": "node-B"
+ }
+ }
+
+Here, "node-A" is the name QEMU internally uses to refer to the base
+image [A] -- it is the backing file, based on which the overlay image,
+[B], is created.
+
+To create the rest of the overlay images, [C], and [D] (omitting the raw
+JSON output for brevity)::
+
+ (QEMU) blockdev-snapshot-sync node-name=node-B snapshot-file=c.qcow2 snapshot-node-name=node-C format=qcow2
+ (QEMU) blockdev-snapshot-sync node-name=node-C snapshot-file=d.qcow2 snapshot-node-name=node-D format=qcow2
+
+
+A note on points-in-time vs file names
+--------------------------------------
+
+In our disk image chain::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+We have *three* points in time and an active layer:
+
+- Point 1: Guest state when [B] was created is contained in file [A]
+- Point 2: Guest state when [C] was created is contained in [A] + [B]
+- Point 3: Guest state when [D] was created is contained in
+ [A] + [B] + [C]
+- Active layer: Current guest state is contained in [A] + [B] + [C] +
+ [D]
+
+Therefore, be aware with naming choices:
+
+- Naming a file after the time it is created is misleading -- the
+ guest data for that point in time is *not* contained in that file
+ (as explained earlier)
+- Rather, think of files as a *delta* from the backing file
+
+
+Live block streaming --- ``block-stream``
+-----------------------------------------
+
+The ``block-stream`` command allows you to do live copy data from backing
+files into overlay images.
+
+Given our original example disk image chain from earlier::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+The disk image chain can be shortened in one of the following different
+ways (not an exhaustive list).
+
+.. _`Case-1`:
+
+(1) Merge everything into the active layer: I.e. copy all contents from
+ the base image, [A], and overlay images, [B] and [C], into [D],
+ *while* the guest is running. The resulting chain will be a
+ standalone image, [D] -- with contents from [A], [B] and [C] merged
+ into it (where live QEMU writes go to)::
+
+ [D]
+
+.. _`Case-2`:
+
+(2) Taking the same example disk image chain mentioned earlier, merge
+ only images [B] and [C] into [D], the active layer. The result will
+ be contents of images [B] and [C] will be copied into [D], and the
+ backing file pointer of image [D] will be adjusted to point to image
+ [A]. The resulting chain will be::
+
+ [A] <-- [D]
+
+.. _`Case-3`:
+
+(3) Intermediate streaming (available since QEMU 2.8): Starting afresh
+ with the original example disk image chain, with a total of four
+ images, it is possible to copy contents from image [B] into image
+ [C]. Once the copy is finished, image [B] can now be (optionally)
+ discarded; and the backing file pointer of image [C] will be
+ adjusted to point to [A]. I.e. after performing "intermediate
+ streaming" of [B] into [C], the resulting image chain will be (where
+ live QEMU is writing to [D])::
+
+ [A] <-- [C] <-- [D]
+
+
+QMP invocation for ``block-stream``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For `Case-1`_, to merge contents of all the backing files into the
+active layer, where 'node-D' is the current active image (by default
+``block-stream`` will flatten the entire chain); ``qmp-shell`` (and its
+corresponding JSON output)::
+
+ (QEMU) block-stream device=node-D job-id=job0
+ {
+ "execute": "block-stream",
+ "arguments": {
+ "device": "node-D",
+ "job-id": "job0"
+ }
+ }
+
+For `Case-2`_, merge contents of the images [B] and [C] into [D], where
+image [D] ends up referring to image [A] as its backing file::
+
+ (QEMU) block-stream device=node-D base-node=node-A job-id=job0
+
+And for `Case-3`_, of "intermediate" streaming", merge contents of
+images [B] into [C], where [C] ends up referring to [A] as its backing
+image::
+
+ (QEMU) block-stream device=node-C base-node=node-A job-id=job0
+
+Progress of a ``block-stream`` operation can be monitored via the QMP
+command::
+
+ (QEMU) query-block-jobs
+ {
+ "execute": "query-block-jobs",
+ "arguments": {}
+ }
+
+
+Once the ``block-stream`` operation has completed, QEMU will emit an
+event, ``BLOCK_JOB_COMPLETED``. The intermediate overlays remain valid,
+and can now be (optionally) discarded, or retained to create further
+overlays based on them. Finally, the ``block-stream`` jobs can be
+restarted at anytime.
+
+
+Live block commit --- ``block-commit``
+--------------------------------------
+
+The ``block-commit`` command lets you merge live data from overlay
+images into backing file(s). Since QEMU 2.0, this includes "live active
+commit" (i.e. it is possible to merge the "active layer", the right-most
+image in a disk image chain where live QEMU will be writing to, into the
+base image). This is analogous to ``block-stream``, but in the opposite
+direction.
+
+Again, starting afresh with our example disk image chain, where live
+QEMU is writing to the right-most image in the chain, [D]::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+The disk image chain can be shortened in one of the following ways:
+
+.. _`block-commit_Case-1`:
+
+(1) Commit content from only image [B] into image [A]. The resulting
+ chain is the following, where image [C] is adjusted to point at [A]
+ as its new backing file::
+
+ [A] <-- [C] <-- [D]
+
+(2) Commit content from images [B] and [C] into image [A]. The
+ resulting chain, where image [D] is adjusted to point to image [A]
+ as its new backing file::
+
+ [A] <-- [D]
+
+.. _`block-commit_Case-3`:
+
+(3) Commit content from images [B], [C], and the active layer [D] into
+ image [A]. The resulting chain (in this case, a consolidated single
+ image)::
+
+ [A]
+
+(4) Commit content from image only image [C] into image [B]. The
+ resulting chain::
+
+ [A] <-- [B] <-- [D]
+
+(5) Commit content from image [C] and the active layer [D] into image
+ [B]. The resulting chain::
+
+ [A] <-- [B]
+
+
+QMP invocation for ``block-commit``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For :ref:`Case-1 <block-commit_Case-1>`, to merge contents only from
+image [B] into image [A], the invocation is as follows::
+
+ (QEMU) block-commit device=node-D base=a.qcow2 top=b.qcow2 job-id=job0
+ {
+ "execute": "block-commit",
+ "arguments": {
+ "device": "node-D",
+ "job-id": "job0",
+ "top": "b.qcow2",
+ "base": "a.qcow2"
+ }
+ }
+
+Once the above ``block-commit`` operation has completed, a
+``BLOCK_JOB_COMPLETED`` event will be issued, and no further action is
+required. As the end result, the backing file of image [C] is adjusted
+to point to image [A], and the original 4-image chain will end up being
+transformed to::
+
+ [A] <-- [C] <-- [D]
+
+.. note::
+ The intermediate image [B] is invalid (as in: no more further
+ overlays based on it can be created).
+
+ Reasoning: An intermediate image after a 'stream' operation still
+ represents that old point-in-time, and may be valid in that context.
+ However, an intermediate image after a 'commit' operation no longer
+ represents any point-in-time, and is invalid in any context.
+
+
+However, :ref:`Case-3 <block-commit_Case-3>` (also called: "active
+``block-commit``") is a *two-phase* operation: In the first phase, the
+content from the active overlay, along with the intermediate overlays,
+is copied into the backing file (also called the base image). In the
+second phase, adjust the said backing file as the current active image
+-- possible via issuing the command ``block-job-complete``. Optionally,
+the ``block-commit`` operation can be cancelled by issuing the command
+``block-job-cancel``, but be careful when doing this.
+
+Once the ``block-commit`` operation has completed, the event
+``BLOCK_JOB_READY`` will be emitted, signalling that the synchronization
+has finished. Now the job can be gracefully completed by issuing the
+command ``block-job-complete`` -- until such a command is issued, the
+'commit' operation remains active.
+
+The following is the flow for :ref:`Case-3 <block-commit_Case-3>` to
+convert a disk image chain such as this::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+Into::
+
+ [A]
+
+Where content from all the subsequent overlays, [B], and [C], including
+the active layer, [D], is committed back to [A] -- which is where live
+QEMU is performing all its current writes).
+
+Start the "active ``block-commit``" operation::
+
+ (QEMU) block-commit device=node-D base=a.qcow2 top=d.qcow2 job-id=job0
+ {
+ "execute": "block-commit",
+ "arguments": {
+ "device": "node-D",
+ "job-id": "job0",
+ "top": "d.qcow2",
+ "base": "a.qcow2"
+ }
+ }
+
+
+Once the synchronization has completed, the event ``BLOCK_JOB_READY`` will
+be emitted.
+
+Then, optionally query for the status of the active block operations.
+We can see the 'commit' job is now ready to be completed, as indicated
+by the line *"ready": true*::
+
+ (QEMU) query-block-jobs
+ {
+ "execute": "query-block-jobs",
+ "arguments": {}
+ }
+ {
+ "return": [
+ {
+ "busy": false,
+ "type": "commit",
+ "len": 1376256,
+ "paused": false,
+ "ready": true,
+ "io-status": "ok",
+ "offset": 1376256,
+ "device": "job0",
+ "speed": 0
+ }
+ ]
+ }
+
+Gracefully complete the 'commit' block device job::
+
+ (QEMU) block-job-complete device=job0
+ {
+ "execute": "block-job-complete",
+ "arguments": {
+ "device": "job0"
+ }
+ }
+ {
+ "return": {}
+ }
+
+Finally, once the above job is completed, an event
+``BLOCK_JOB_COMPLETED`` will be emitted.
+
+.. note::
+ The invocation for rest of the cases (2, 4, and 5), discussed in the
+ previous section, is omitted for brevity.
+
+
+Live disk synchronization --- ``drive-mirror`` and ``blockdev-mirror``
+----------------------------------------------------------------------
+
+Synchronize a running disk image chain (all or part of it) to a target
+image.
+
+Again, given our familiar disk image chain::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+The ``drive-mirror`` (and its newer equivalent ``blockdev-mirror``) allows
+you to copy data from the entire chain into a single target image (which
+can be located on a different host).
+
+Once a 'mirror' job has started, there are two possible actions while a
+``drive-mirror`` job is active:
+
+(1) Issuing the command ``block-job-cancel`` after it emits the event
+ ``BLOCK_JOB_CANCELLED``: will (after completing synchronization of
+ the content from the disk image chain to the target image, [E])
+ create a point-in-time (which is at the time of *triggering* the
+ cancel command) copy, contained in image [E], of the the entire disk
+ image chain (or only the top-most image, depending on the ``sync``
+ mode).
+
+(2) Issuing the command ``block-job-complete`` after it emits the event
+ ``BLOCK_JOB_COMPLETED``: will, after completing synchronization of
+ the content, adjust the guest device (i.e. live QEMU) to point to
+ the target image, and, causing all the new writes from this point on
+ to happen there. One use case for this is live storage migration.
+
+About synchronization modes: The synchronization mode determines
+*which* part of the disk image chain will be copied to the target.
+Currently, there are four different kinds:
+
+(1) ``full`` -- Synchronize the content of entire disk image chain to
+ the target
+
+(2) ``top`` -- Synchronize only the contents of the top-most disk image
+ in the chain to the target
+
+(3) ``none`` -- Synchronize only the new writes from this point on.
+
+ .. note:: In the case of ``drive-backup`` (or ``blockdev-backup``),
+ the behavior of ``none`` synchronization mode is different.
+ Normally, a ``backup`` job consists of two parts: Anything
+ that is overwritten by the guest is first copied out to
+ the backup, and in the background the whole image is
+ copied from start to end. With ``sync=none``, it's only
+ the first part.
+
+(4) ``incremental`` -- Synchronize content that is described by the
+ dirty bitmap
+
+.. note::
+ Refer to the :doc:`bitmaps` document in the QEMU source
+ tree to learn about the detailed workings of the ``incremental``
+ synchronization mode.
+
+
+QMP invocation for ``drive-mirror``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To copy the contents of the entire disk image chain, from [A] all the
+way to [D], to a new target (``drive-mirror`` will create the destination
+file, if it doesn't already exist), call it [E]::
+
+ (QEMU) drive-mirror device=node-D target=e.qcow2 sync=full job-id=job0
+ {
+ "execute": "drive-mirror",
+ "arguments": {
+ "device": "node-D",
+ "job-id": "job0",
+ "target": "e.qcow2",
+ "sync": "full"
+ }
+ }
+
+The ``"sync": "full"``, from the above, means: copy the *entire* chain
+to the destination.
+
+Following the above, querying for active block jobs will show that a
+'mirror' job is "ready" to be completed (and QEMU will also emit an
+event, ``BLOCK_JOB_READY``)::
+
+ (QEMU) query-block-jobs
+ {
+ "execute": "query-block-jobs",
+ "arguments": {}
+ }
+ {
+ "return": [
+ {
+ "busy": false,
+ "type": "mirror",
+ "len": 21757952,
+ "paused": false,
+ "ready": true,
+ "io-status": "ok",
+ "offset": 21757952,
+ "device": "job0",
+ "speed": 0
+ }
+ ]
+ }
+
+And, as noted in the previous section, there are two possible actions
+at this point:
+
+(a) Create a point-in-time snapshot by ending the synchronization. The
+ point-in-time is at the time of *ending* the sync. (The result of
+ the following being: the target image, [E], will be populated with
+ content from the entire chain, [A] to [D])::
+
+ (QEMU) block-job-cancel device=job0
+ {
+ "execute": "block-job-cancel",
+ "arguments": {
+ "device": "job0"
+ }
+ }
+
+(b) Or, complete the operation and pivot the live QEMU to the target
+ copy::
+
+ (QEMU) block-job-complete device=job0
+
+In either of the above cases, if you once again run the
+`query-block-jobs` command, there should not be any active block
+operation.
+
+Comparing 'commit' and 'mirror': In both then cases, the overlay images
+can be discarded. However, with 'commit', the *existing* base image
+will be modified (by updating it with contents from overlays); while in
+the case of 'mirror', a *new* target image is populated with the data
+from the disk image chain.
+
+
+QMP invocation for live storage migration with ``drive-mirror`` + NBD
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Live storage migration (without shared storage setup) is one of the most
+common use-cases that takes advantage of the ``drive-mirror`` primitive
+and QEMU's built-in Network Block Device (NBD) server. Here's a quick
+walk-through of this setup.
+
+Given the disk image chain::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+Instead of copying content from the entire chain, synchronize *only* the
+contents of the *top*-most disk image (i.e. the active layer), [D], to a
+target, say, [TargetDisk].
+
+.. important::
+ The destination host must already have the contents of the backing
+ chain, involving images [A], [B], and [C], visible via other means
+ -- whether by ``cp``, ``rsync``, or by some storage array-specific
+ command.)
+
+Sometimes, this is also referred to as "shallow copy" -- because only
+the "active layer", and not the rest of the image chain, is copied to
+the destination.
+
+.. note::
+ In this example, for the sake of simplicity, we'll be using the same
+ ``localhost`` as both source and destination.
+
+As noted earlier, on the destination host the contents of the backing
+chain -- from images [A] to [C] -- are already expected to exist in some
+form (e.g. in a file called, ``Contents-of-A-B-C.qcow2``). Now, on the
+destination host, let's create a target overlay image (with the image
+``Contents-of-A-B-C.qcow2`` as its backing file), to which the contents
+of image [D] (from the source QEMU) will be mirrored to::
+
+ $ qemu-img create -f qcow2 -b ./Contents-of-A-B-C.qcow2 \
+ -F qcow2 ./target-disk.qcow2
+
+And start the destination QEMU (we already have the source QEMU running
+-- discussed in the section: `Interacting with a QEMU instance`_)
+instance, with the following invocation. (As noted earlier, for
+simplicity's sake, the destination QEMU is started on the same host, but
+it could be located elsewhere)::
+
+ $ ./x86_64-softmmu/qemu-system-x86_64 -display none -nodefconfig \
+ -M q35 -nodefaults -m 512 \
+ -blockdev node-name=node-TargetDisk,driver=qcow2,file.driver=file,file.node-name=file,file.filename=./target-disk.qcow2 \
+ -device virtio-blk,drive=node-TargetDisk,id=virtio0 \
+ -S -monitor stdio -qmp unix:./qmp-sock2,server,nowait \
+ -incoming tcp:localhost:6666
+
+Given the disk image chain on source QEMU::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+On the destination host, it is expected that the contents of the chain
+``[A] <-- [B] <-- [C]`` are *already* present, and therefore copy *only*
+the content of image [D].
+
+(1) [On *destination* QEMU] As part of the first step, start the
+ built-in NBD server on a given host (local host, represented by
+ ``::``)and port::
+
+ (QEMU) nbd-server-start addr={"type":"inet","data":{"host":"::","port":"49153"}}
+ {
+ "execute": "nbd-server-start",
+ "arguments": {
+ "addr": {
+ "data": {
+ "host": "::",
+ "port": "49153"
+ },
+ "type": "inet"
+ }
+ }
+ }
+
+(2) [On *destination* QEMU] And export the destination disk image using
+ QEMU's built-in NBD server::
+
+ (QEMU) nbd-server-add device=node-TargetDisk writable=true
+ {
+ "execute": "nbd-server-add",
+ "arguments": {
+ "device": "node-TargetDisk"
+ }
+ }
+
+(3) [On *source* QEMU] Then, invoke ``drive-mirror`` (NB: since we're
+ running ``drive-mirror`` with ``mode=existing`` (meaning:
+ synchronize to a pre-created file, therefore 'existing', file on the
+ target host), with the synchronization mode as 'top' (``"sync:
+ "top"``)::
+
+ (QEMU) drive-mirror device=node-D target=nbd:localhost:49153:exportname=node-TargetDisk sync=top mode=existing job-id=job0
+ {
+ "execute": "drive-mirror",
+ "arguments": {
+ "device": "node-D",
+ "mode": "existing",
+ "job-id": "job0",
+ "target": "nbd:localhost:49153:exportname=node-TargetDisk",
+ "sync": "top"
+ }
+ }
+
+(4) [On *source* QEMU] Once ``drive-mirror`` copies the entire data, and the
+ event ``BLOCK_JOB_READY`` is emitted, issue ``block-job-cancel`` to
+ gracefully end the synchronization, from source QEMU::
+
+ (QEMU) block-job-cancel device=job0
+ {
+ "execute": "block-job-cancel",
+ "arguments": {
+ "device": "job0"
+ }
+ }
+
+(5) [On *destination* QEMU] Then, stop the NBD server::
+
+ (QEMU) nbd-server-stop
+ {
+ "execute": "nbd-server-stop",
+ "arguments": {}
+ }
+
+(6) [On *destination* QEMU] Finally, resume the guest vCPUs by issuing the
+ QMP command `cont`::
+
+ (QEMU) cont
+ {
+ "execute": "cont",
+ "arguments": {}
+ }
+
+.. note::
+ Higher-level libraries (e.g. libvirt) automate the entire above
+ process (although note that libvirt does not allow same-host
+ migrations to localhost for other reasons).
+
+
+Notes on ``blockdev-mirror``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``blockdev-mirror`` command is equivalent in core functionality to
+``drive-mirror``, except that it operates at node-level in a BDS graph.
+
+Also: for ``blockdev-mirror``, the 'target' image needs to be explicitly
+created (using ``qemu-img``) and attach it to live QEMU via
+``blockdev-add``, which assigns a name to the to-be created target node.
+
+E.g. the sequence of actions to create a point-in-time backup of an
+entire disk image chain, to a target, using ``blockdev-mirror`` would be:
+
+(0) Create the QCOW2 overlays, to arrive at a backing chain of desired
+ depth
+
+(1) Create the target image (using ``qemu-img``), say, ``e.qcow2``
+
+(2) Attach the above created file (``e.qcow2``), run-time, using
+ ``blockdev-add`` to QEMU
+
+(3) Perform ``blockdev-mirror`` (use ``"sync": "full"`` to copy the
+ entire chain to the target). And notice the event
+ ``BLOCK_JOB_READY``
+
+(4) Optionally, query for active block jobs, there should be a 'mirror'
+ job ready to be completed
+
+(5) Gracefully complete the 'mirror' block device job, and notice the
+ the event ``BLOCK_JOB_COMPLETED``
+
+(6) Shutdown the guest by issuing the QMP ``quit`` command so that
+ caches are flushed
+
+(7) Then, finally, compare the contents of the disk image chain, and
+ the target copy with ``qemu-img compare``. You should notice:
+ "Images are identical"
+
+
+QMP invocation for ``blockdev-mirror``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Given the disk image chain::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+To copy the contents of the entire disk image chain, from [A] all the
+way to [D], to a new target, call it [E]. The following is the flow.
+
+Create the overlay images, [B], [C], and [D]::
+
+ (QEMU) blockdev-snapshot-sync node-name=node-A snapshot-file=b.qcow2 snapshot-node-name=node-B format=qcow2
+ (QEMU) blockdev-snapshot-sync node-name=node-B snapshot-file=c.qcow2 snapshot-node-name=node-C format=qcow2
+ (QEMU) blockdev-snapshot-sync node-name=node-C snapshot-file=d.qcow2 snapshot-node-name=node-D format=qcow2
+
+Create the target image, [E]::
+
+ $ qemu-img create -f qcow2 e.qcow2 39M
+
+Add the above created target image to QEMU, via ``blockdev-add``::
+
+ (QEMU) blockdev-add driver=qcow2 node-name=node-E file={"driver":"file","filename":"e.qcow2"}
+ {
+ "execute": "blockdev-add",
+ "arguments": {
+ "node-name": "node-E",
+ "driver": "qcow2",
+ "file": {
+ "driver": "file",
+ "filename": "e.qcow2"
+ }
+ }
+ }
+
+Perform ``blockdev-mirror``, and notice the event ``BLOCK_JOB_READY``::
+
+ (QEMU) blockdev-mirror device=node-B target=node-E sync=full job-id=job0
+ {
+ "execute": "blockdev-mirror",
+ "arguments": {
+ "device": "node-D",
+ "job-id": "job0",
+ "target": "node-E",
+ "sync": "full"
+ }
+ }
+
+Query for active block jobs, there should be a 'mirror' job ready::
+
+ (QEMU) query-block-jobs
+ {
+ "execute": "query-block-jobs",
+ "arguments": {}
+ }
+ {
+ "return": [
+ {
+ "busy": false,
+ "type": "mirror",
+ "len": 21561344,
+ "paused": false,
+ "ready": true,
+ "io-status": "ok",
+ "offset": 21561344,
+ "device": "job0",
+ "speed": 0
+ }
+ ]
+ }
+
+Gracefully complete the block device job operation, and notice the
+event ``BLOCK_JOB_COMPLETED``::
+
+ (QEMU) block-job-complete device=job0
+ {
+ "execute": "block-job-complete",
+ "arguments": {
+ "device": "job0"
+ }
+ }
+ {
+ "return": {}
+ }
+
+Shutdown the guest, by issuing the ``quit`` QMP command::
+
+ (QEMU) quit
+ {
+ "execute": "quit",
+ "arguments": {}
+ }
+
+
+Live disk backup --- ``drive-backup`` and ``blockdev-backup``
+-------------------------------------------------------------
+
+The ``drive-backup`` (and its newer equivalent ``blockdev-backup``) allows
+you to create a point-in-time snapshot.
+
+In this case, the point-in-time is when you *start* the ``drive-backup``
+(or its newer equivalent ``blockdev-backup``) command.
+
+
+QMP invocation for ``drive-backup``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Yet again, starting afresh with our example disk image chain::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+To create a target image [E], with content populated from image [A] to
+[D], from the above chain, the following is the syntax. (If the target
+image does not exist, ``drive-backup`` will create it)::
+
+ (QEMU) drive-backup device=node-D sync=full target=e.qcow2 job-id=job0
+ {
+ "execute": "drive-backup",
+ "arguments": {
+ "device": "node-D",
+ "job-id": "job0",
+ "sync": "full",
+ "target": "e.qcow2"
+ }
+ }
+
+Once the above ``drive-backup`` has completed, a ``BLOCK_JOB_COMPLETED`` event
+will be issued, indicating the live block device job operation has
+completed, and no further action is required.
+
+
+Notes on ``blockdev-backup``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``blockdev-backup`` command is equivalent in functionality to
+``drive-backup``, except that it operates at node-level in a Block Driver
+State (BDS) graph.
+
+E.g. the sequence of actions to create a point-in-time backup
+of an entire disk image chain, to a target, using ``blockdev-backup``
+would be:
+
+(0) Create the QCOW2 overlays, to arrive at a backing chain of desired
+ depth
+
+(1) Create the target image (using ``qemu-img``), say, ``e.qcow2``
+
+(2) Attach the above created file (``e.qcow2``), run-time, using
+ ``blockdev-add`` to QEMU
+
+(3) Perform ``blockdev-backup`` (use ``"sync": "full"`` to copy the
+ entire chain to the target). And notice the event
+ ``BLOCK_JOB_COMPLETED``
+
+(4) Shutdown the guest, by issuing the QMP ``quit`` command, so that
+ caches are flushed
+
+(5) Then, finally, compare the contents of the disk image chain, and
+ the target copy with ``qemu-img compare``. You should notice:
+ "Images are identical"
+
+The following section shows an example QMP invocation for
+``blockdev-backup``.
+
+QMP invocation for ``blockdev-backup``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Given a disk image chain of depth 1 where image [B] is the active
+overlay (live QEMU is writing to it)::
+
+ [A] <-- [B]
+
+The following is the procedure to copy the content from the entire chain
+to a target image (say, [E]), which has the full content from [A] and
+[B].
+
+Create the overlay [B]::
+
+ (QEMU) blockdev-snapshot-sync node-name=node-A snapshot-file=b.qcow2 snapshot-node-name=node-B format=qcow2
+ {
+ "execute": "blockdev-snapshot-sync",
+ "arguments": {
+ "node-name": "node-A",
+ "snapshot-file": "b.qcow2",
+ "format": "qcow2",
+ "snapshot-node-name": "node-B"
+ }
+ }
+
+
+Create a target image that will contain the copy::
+
+ $ qemu-img create -f qcow2 e.qcow2 39M
+
+Then add it to QEMU via ``blockdev-add``::
+
+ (QEMU) blockdev-add driver=qcow2 node-name=node-E file={"driver":"file","filename":"e.qcow2"}
+ {
+ "execute": "blockdev-add",
+ "arguments": {
+ "node-name": "node-E",
+ "driver": "qcow2",
+ "file": {
+ "driver": "file",
+ "filename": "e.qcow2"
+ }
+ }
+ }
+
+Then invoke ``blockdev-backup`` to copy the contents from the entire
+image chain, consisting of images [A] and [B] to the target image
+'e.qcow2'::
+
+ (QEMU) blockdev-backup device=node-B target=node-E sync=full job-id=job0
+ {
+ "execute": "blockdev-backup",
+ "arguments": {
+ "device": "node-B",
+ "job-id": "job0",
+ "target": "node-E",
+ "sync": "full"
+ }
+ }
+
+Once the above 'backup' operation has completed, the event,
+``BLOCK_JOB_COMPLETED`` will be emitted, signalling successful
+completion.
+
+Next, query for any active block device jobs (there should be none)::
+
+ (QEMU) query-block-jobs
+ {
+ "execute": "query-block-jobs",
+ "arguments": {}
+ }
+
+Shutdown the guest::
+
+ (QEMU) quit
+ {
+ "execute": "quit",
+ "arguments": {}
+ }
+ "return": {}
+ }
+
+.. note::
+ The above step is really important; if forgotten, an error, "Failed
+ to get shared "write" lock on e.qcow2", will be thrown when you do
+ ``qemu-img compare`` to verify the integrity of the disk image
+ with the backup content.
+
+
+The end result will be the image 'e.qcow2' containing a
+point-in-time backup of the disk image chain -- i.e. contents from
+images [A] and [B] at the time the ``blockdev-backup`` command was
+initiated.
+
+One way to confirm the backup disk image contains the identical content
+with the disk image chain is to compare the backup and the contents of
+the chain, you should see "Images are identical". (NB: this is assuming
+QEMU was launched with ``-S`` option, which will not start the CPUs at
+guest boot up)::
+
+ $ qemu-img compare b.qcow2 e.qcow2
+ Warning: Image size mismatch!
+ Images are identical.
+
+NOTE: The "Warning: Image size mismatch!" is expected, as we created the
+target image (e.qcow2) with 39M size.
diff --git a/docs/live-block-ops.txt b/docs/live-block-ops.txt
deleted file mode 100644
index 2211d14428..0000000000
--- a/docs/live-block-ops.txt
+++ /dev/null
@@ -1,72 +0,0 @@
-LIVE BLOCK OPERATIONS
-=====================
-
-High level description of live block operations. Note these are not
-supported for use with the raw format at the moment.
-
-Note also that this document is incomplete and it currently only
-covers the 'stream' operation. Other operations supported by QEMU such
-as 'commit', 'mirror' and 'backup' are not described here yet. Please
-refer to the qapi/block-core.json file for an overview of those.
-
-Snapshot live merge
-===================
-
-Given a snapshot chain, described in this document in the following
-format:
-
-[A] <- [B] <- [C] <- [D] <- [E]
-
-Where the rightmost object ([E] in the example) described is the current
-image which the guest OS has write access to. To the left of it is its base
-image, and so on accordingly until the leftmost image, which has no
-base.
-
-The snapshot live merge operation transforms such a chain into a
-smaller one with fewer elements, such as this transformation relative
-to the first example:
-
-[A] <- [E]
-
-Data is copied in the right direction with destination being the
-rightmost image, but any other intermediate image can be specified
-instead. In this example data is copied from [C] into [D], so [D] can
-be backed by [B]:
-
-[A] <- [B] <- [D] <- [E]
-
-The operation is implemented in QEMU through image streaming facilities.
-
-The basic idea is to execute 'block_stream virtio0' while the guest is
-running. Progress can be monitored using 'info block-jobs'. When the
-streaming operation completes it raises a QMP event. 'block_stream'
-copies data from the backing file(s) into the active image. When finished,
-it adjusts the backing file pointer.
-
-The 'base' parameter specifies an image which data need not be
-streamed from. This image will be used as the backing file for the
-destination image when the operation is finished.
-
-In the first example above, the command would be:
-
-(qemu) block_stream virtio0 file-A.img
-
-In order to specify a destination image different from the active
-(rightmost) one we can use its node name instead.
-
-In the second example above, the command would be:
-
-(qemu) block_stream node-D file-B.img
-
-Live block copy
-===============
-
-To copy an in use image to another destination in the filesystem, one
-should create a live snapshot in the desired destination, then stream
-into that image. Example:
-
-(qemu) snapshot_blkdev ide0-hd0 /new-path/disk.img qcow2
-
-(qemu) block_stream ide0-hd0
-
-
diff --git a/fsdev/qemu-fsdev-throttle.c b/fsdev/qemu-fsdev-throttle.c
index 7ae4e86646..49eebb5412 100644
--- a/fsdev/qemu-fsdev-throttle.c
+++ b/fsdev/qemu-fsdev-throttle.c
@@ -86,7 +86,7 @@ void fsdev_throttle_init(FsThrottle *fst)
fsdev_throttle_read_timer_cb,
fsdev_throttle_write_timer_cb,
fst);
- throttle_config(&fst->ts, &fst->tt, &fst->cfg);
+ throttle_config(&fst->ts, QEMU_CLOCK_REALTIME, &fst->cfg);
qemu_co_queue_init(&fst->throttled_reqs[0]);
qemu_co_queue_init(&fst->throttled_reqs[1]);
}
diff --git a/hmp.c b/hmp.c
index b42ae59a29..bf1de747d5 100644
--- a/hmp.c
+++ b/hmp.c
@@ -401,16 +401,16 @@ static void print_block_info(Monitor *mon, BlockInfo *info,
assert(!info || !info->has_inserted || info->inserted == inserted);
- if (info) {
+ if (info && *info->device) {
monitor_printf(mon, "%s", info->device);
if (inserted && inserted->has_node_name) {
monitor_printf(mon, " (%s)", inserted->node_name);
}
} else {
- assert(inserted);
+ assert(info || inserted);
monitor_printf(mon, "%s",
- inserted->has_node_name
- ? inserted->node_name
+ inserted && inserted->has_node_name ? inserted->node_name
+ : info && info->has_qdev ? info->qdev
: "<anonymous>");
}
@@ -425,6 +425,9 @@ static void print_block_info(Monitor *mon, BlockInfo *info,
}
if (info) {
+ if (info->has_qdev) {
+ monitor_printf(mon, " Attached to: %s\n", info->qdev);
+ }
if (info->has_io_status && info->io_status != BLOCK_DEVICE_IO_STATUS_OK) {
monitor_printf(mon, " I/O status: %s\n",
BlockDeviceIoStatus_lookup[info->io_status]);
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index 58a8f92d92..078fc5d239 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -404,6 +404,31 @@ static void set_uint64(Object *obj, Visitor *v, const char *name,
visit_type_uint64(v, name, ptr, errp);
}
+static void get_int64(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ int64_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+ visit_type_int64(v, name, ptr, errp);
+}
+
+static void set_int64(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ int64_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_int64(v, name, ptr, errp);
+}
+
const PropertyInfo qdev_prop_uint64 = {
.name = "uint64",
.get = get_uint64,
@@ -411,6 +436,13 @@ const PropertyInfo qdev_prop_uint64 = {
.set_default_value = set_default_value_uint,
};
+const PropertyInfo qdev_prop_int64 = {
+ .name = "int64",
+ .get = get_int64,
+ .set = set_int64,
+ .set_default_value = set_default_value_int,
+};
+
/* --- string --- */
static void release_string(Object *obj, const char *name, void *opaque)
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index 299e592fa2..cc2f5bd280 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -164,6 +164,7 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus);
IDEState *s = bus->ifs + dev->unit;
Error *err = NULL;
+ int ret;
if (!dev->conf.blk) {
if (kind != IDE_CD) {
@@ -172,6 +173,8 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
} else {
/* Anonymous BlockBackend for an empty drive */
dev->conf.blk = blk_new(0, BLK_PERM_ALL);
+ ret = blk_attach_dev(dev->conf.blk, &dev->qdev);
+ assert(ret == 0);
}
}
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index a53f058621..5f1e5e8070 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -2384,9 +2384,14 @@ static void scsi_hd_realize(SCSIDevice *dev, Error **errp)
static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+ int ret;
if (!dev->conf.blk) {
+ /* Anonymous BlockBackend for an empty drive. As we put it into
+ * dev->conf, qdev takes care of detaching on unplug. */
dev->conf.blk = blk_new(0, BLK_PERM_ALL);
+ ret = blk_attach_dev(dev->conf.blk, &dev->qdev);
+ assert(ret == 0);
}
s->qdev.blocksize = 2048;
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index c1bb6d429a..7b2924c0ef 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -1109,6 +1109,14 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
goto free_container_exit;
}
+ vfio_kvm_device_add_group(group);
+
+ QLIST_INIT(&container->group_list);
+ QLIST_INSERT_HEAD(&space->containers, container, next);
+
+ group->container = container;
+ QLIST_INSERT_HEAD(&container->group_list, group, container_next);
+
container->listener = vfio_memory_listener;
memory_listener_register(&container->listener, container->space->as);
@@ -1122,14 +1130,11 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
container->initialized = true;
- QLIST_INIT(&container->group_list);
- QLIST_INSERT_HEAD(&space->containers, container, next);
-
- group->container = container;
- QLIST_INSERT_HEAD(&container->group_list, group, container_next);
-
return 0;
listener_release_exit:
+ QLIST_REMOVE(group, container_next);
+ QLIST_REMOVE(container, next);
+ vfio_kvm_device_del_group(group);
vfio_listener_release(container);
free_container_exit:
@@ -1234,8 +1239,6 @@ VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp)
QLIST_INSERT_HEAD(&vfio_group_list, group, next);
- vfio_kvm_device_add_group(group);
-
return group;
close_fd_exit:
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
index 39297961f3..e2321f1cc1 100644
--- a/include/hw/qdev-properties.h
+++ b/include/hw/qdev-properties.h
@@ -13,6 +13,7 @@ extern const PropertyInfo qdev_prop_uint16;
extern const PropertyInfo qdev_prop_uint32;
extern const PropertyInfo qdev_prop_int32;
extern const PropertyInfo qdev_prop_uint64;
+extern const PropertyInfo qdev_prop_int64;
extern const PropertyInfo qdev_prop_size;
extern const PropertyInfo qdev_prop_string;
extern const PropertyInfo qdev_prop_chr;
@@ -157,6 +158,8 @@ extern const PropertyInfo qdev_prop_link;
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_int32, int32_t)
#define DEFINE_PROP_UINT64(_n, _s, _f, _d) \
DEFINE_PROP_UNSIGNED(_n, _s, _f, _d, qdev_prop_uint64, uint64_t)
+#define DEFINE_PROP_INT64(_n, _s, _f, _d) \
+ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_int64, int64_t)
#define DEFINE_PROP_SIZE(_n, _s, _f, _d) \
DEFINE_PROP_UNSIGNED(_n, _s, _f, _d, qdev_prop_size, uint64_t)
#define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \
diff --git a/include/migration/colo.h b/include/migration/colo.h
index be6beba301..ff9874ea16 100644
--- a/include/migration/colo.h
+++ b/include/migration/colo.h
@@ -15,7 +15,6 @@
#include "qemu-common.h"
-bool colo_supported(void);
void colo_info_init(void);
void migrate_start_colo_process(MigrationState *s);
diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
index 9109657609..d056008c18 100644
--- a/include/qemu/throttle.h
+++ b/include/qemu/throttle.h
@@ -139,7 +139,7 @@ bool throttle_enabled(ThrottleConfig *cfg);
bool throttle_is_valid(ThrottleConfig *cfg, Error **errp);
void throttle_config(ThrottleState *ts,
- ThrottleTimers *tt,
+ QEMUClockType clock_type,
ThrottleConfig *cfg);
void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg);
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index d9ea0cdb0f..4a3730596b 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -100,6 +100,7 @@ void blk_remove_all_bs(void);
const char *blk_name(const BlockBackend *blk);
BlockBackend *blk_by_name(const char *name);
BlockBackend *blk_next(BlockBackend *blk);
+BlockBackend *blk_all_next(BlockBackend *blk);
bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp);
void monitor_remove_blk(BlockBackend *blk);
@@ -126,6 +127,7 @@ int blk_attach_dev(BlockBackend *blk, DeviceState *dev);
void blk_attach_dev_legacy(BlockBackend *blk, void *dev);
void blk_detach_dev(BlockBackend *blk, void *dev);
void *blk_get_attached_dev(BlockBackend *blk);
+char *blk_get_attached_dev_id(BlockBackend *blk);
BlockBackend *blk_by_dev(void *dev);
BlockBackend *blk_by_qdev_id(const char *id, Error **errp);
void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, void *opaque);
diff --git a/io/channel.c b/io/channel.c
index cdf74540c1..1cfb8b33a2 100644
--- a/io/channel.c
+++ b/io/channel.c
@@ -279,15 +279,9 @@ static void qio_channel_set_aio_fd_handlers(QIOChannel *ioc)
void qio_channel_attach_aio_context(QIOChannel *ioc,
AioContext *ctx)
{
- AioContext *old_ctx;
- if (ioc->ctx == ctx) {
- return;
- }
-
- old_ctx = ioc->ctx ? ioc->ctx : iohandler_get_aio_context();
- qio_channel_set_aio_fd_handler(ioc, old_ctx, NULL, NULL, NULL);
+ assert(!ioc->read_coroutine);
+ assert(!ioc->write_coroutine);
ioc->ctx = ctx;
- qio_channel_set_aio_fd_handlers(ioc);
}
void qio_channel_detach_aio_context(QIOChannel *ioc)
diff --git a/linux-headers/asm-x86/kvm_para.h b/linux-headers/asm-x86/kvm_para.h
index 3a5397988e..cefa127d84 100644
--- a/linux-headers/asm-x86/kvm_para.h
+++ b/linux-headers/asm-x86/kvm_para.h
@@ -67,6 +67,7 @@ struct kvm_clock_pairing {
#define KVM_ASYNC_PF_ENABLED (1 << 0)
#define KVM_ASYNC_PF_SEND_ALWAYS (1 << 1)
+#define KVM_ASYNC_PF_DELIVERY_AS_PF_VMEXIT (1 << 2)
/* Operations for KVM_HC_MMU_OP */
#define KVM_MMU_OP_WRITE_PTE 1
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 43e2d82be1..7971a4f8b5 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -927,6 +927,8 @@ struct kvm_ppc_resize_hpt {
#define KVM_CAP_S390_CMMA_MIGRATION 145
#define KVM_CAP_PPC_FWNMI 146
#define KVM_CAP_PPC_SMT_POSSIBLE 147
+#define KVM_CAP_HYPERV_SYNIC2 148
+#define KVM_CAP_HYPERV_VP_INDEX 149
#ifdef KVM_CAP_IRQ_ROUTING
@@ -1351,7 +1353,7 @@ struct kvm_s390_ucas_mapping {
/* Available with KVM_CAP_X86_SMM */
#define KVM_SMI _IO(KVMIO, 0xb7)
/* Available with KVM_CAP_S390_CMMA_MIGRATION */
-#define KVM_S390_GET_CMMA_BITS _IOW(KVMIO, 0xb8, struct kvm_s390_cmma_log)
+#define KVM_S390_GET_CMMA_BITS _IOWR(KVMIO, 0xb8, struct kvm_s390_cmma_log)
#define KVM_S390_SET_CMMA_BITS _IOW(KVMIO, 0xb9, struct kvm_s390_cmma_log)
#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
diff --git a/migration/colo.c b/migration/colo.c
index ef35f00c9a..a4255432ac 100644
--- a/migration/colo.c
+++ b/migration/colo.c
@@ -29,11 +29,6 @@ static bool vmstate_loading;
#define COLO_BUFFER_BASE_SIZE (4 * 1024 * 1024)
-bool colo_supported(void)
-{
- return true;
-}
-
bool migration_in_colo_state(void)
{
MigrationState *s = migrate_get_current();
diff --git a/migration/migration.c b/migration/migration.c
index a0db40d364..76153914d1 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -102,14 +102,22 @@ enum mig_rp_message_type {
static MigrationState *current_migration;
+static bool migration_object_check(MigrationState *ms, Error **errp);
+
void migration_object_init(void)
{
MachineState *ms = MACHINE(qdev_get_machine());
+ Error *err = NULL;
/* This can only be called once. */
assert(!current_migration);
current_migration = MIGRATION_OBJ(object_new(TYPE_MIGRATION));
+ if (!migration_object_check(current_migration, &err)) {
+ error_report_err(err);
+ exit(1);
+ }
+
/*
* We cannot really do this in migration_instance_init() since at
* that time global properties are not yet applied, then this
@@ -348,6 +356,7 @@ static void process_incoming_migration_co(void *opaque)
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
MIGRATION_STATUS_FAILED);
error_report("load of migration failed: %s", strerror(-ret));
+ qemu_fclose(mis->from_src_file);
exit(EXIT_FAILURE);
}
mis->bh = qemu_bh_new(process_incoming_migration_bh, mis);
@@ -403,9 +412,6 @@ MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp)
continue;
}
#endif
- if (i == MIGRATION_CAPABILITY_X_COLO && !colo_supported()) {
- continue;
- }
if (head == NULL) {
head = g_malloc0(sizeof(*caps));
caps = head;
@@ -582,51 +588,49 @@ MigrationInfo *qmp_query_migrate(Error **errp)
return info;
}
-void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params,
- Error **errp)
+/**
+ * @migration_caps_check - check capability validity
+ *
+ * @cap_list: old capability list, array of bool
+ * @params: new capabilities to be applied soon
+ * @errp: set *errp if the check failed, with reason
+ *
+ * Returns true if check passed, otherwise false.
+ */
+static bool migrate_caps_check(bool *cap_list,
+ MigrationCapabilityStatusList *params,
+ Error **errp)
{
- MigrationState *s = migrate_get_current();
MigrationCapabilityStatusList *cap;
- bool old_postcopy_cap = migrate_postcopy_ram();
+ bool old_postcopy_cap;
- if (migration_is_setup_or_active(s->state)) {
- error_setg(errp, QERR_MIGRATION_ACTIVE);
- return;
- }
+ old_postcopy_cap = cap_list[MIGRATION_CAPABILITY_POSTCOPY_RAM];
for (cap = params; cap; cap = cap->next) {
+ cap_list[cap->value->capability] = cap->value->state;
+ }
+
#ifndef CONFIG_LIVE_BLOCK_MIGRATION
- if (cap->value->capability == MIGRATION_CAPABILITY_BLOCK
- && cap->value->state) {
- error_setg(errp, "QEMU compiled without old-style (blk/-b, inc/-i) "
- "block migration");
- error_append_hint(errp, "Use drive_mirror+NBD instead.\n");
- continue;
- }
-#endif
- if (cap->value->capability == MIGRATION_CAPABILITY_X_COLO) {
- if (!colo_supported()) {
- error_setg(errp, "COLO is not currently supported, please"
- " configure with --enable-colo option in order to"
- " support COLO feature");
- continue;
- }
- }
- s->enabled_capabilities[cap->value->capability] = cap->value->state;
+ if (cap_list[MIGRATION_CAPABILITY_BLOCK]) {
+ error_setg(errp, "QEMU compiled without old-style (blk/-b, inc/-i) "
+ "block migration");
+ error_append_hint(errp, "Use drive_mirror+NBD instead.\n");
+ return false;
}
+#endif
- if (migrate_postcopy_ram()) {
- if (migrate_use_compression()) {
+ if (cap_list[MIGRATION_CAPABILITY_POSTCOPY_RAM]) {
+ if (cap_list[MIGRATION_CAPABILITY_COMPRESS]) {
/* The decompression threads asynchronously write into RAM
* rather than use the atomic copies needed to avoid
* userfaulting. It should be possible to fix the decompression
* threads for compatibility in future.
*/
- error_report("Postcopy is not currently compatible with "
- "compression");
- s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_RAM] =
- false;
+ error_setg(errp, "Postcopy is not currently compatible "
+ "with compression");
+ return false;
}
+
/* This check is reasonably expensive, so only when it's being
* set the first time, also it's only the destination that needs
* special support.
@@ -636,96 +640,141 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params,
/* postcopy_ram_supported_by_host will have emitted a more
* detailed message
*/
- error_report("Postcopy is not supported");
- s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_RAM] =
- false;
+ error_setg(errp, "Postcopy is not supported");
+ return false;
}
}
+
+ return true;
}
-void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
+void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params,
+ Error **errp)
{
MigrationState *s = migrate_get_current();
+ MigrationCapabilityStatusList *cap;
+ if (migration_is_setup_or_active(s->state)) {
+ error_setg(errp, QERR_MIGRATION_ACTIVE);
+ return;
+ }
+
+ if (!migrate_caps_check(s->enabled_capabilities, params, errp)) {
+ return;
+ }
+
+ for (cap = params; cap; cap = cap->next) {
+ s->enabled_capabilities[cap->value->capability] = cap->value->state;
+ }
+}
+
+/*
+ * Check whether the parameters are valid. Error will be put into errp
+ * (if provided). Return true if valid, otherwise false.
+ */
+static bool migrate_params_check(MigrationParameters *params, Error **errp)
+{
if (params->has_compress_level &&
(params->compress_level < 0 || params->compress_level > 9)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_level",
"is invalid, it should be in the range of 0 to 9");
- return;
+ return false;
}
+
if (params->has_compress_threads &&
(params->compress_threads < 1 || params->compress_threads > 255)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
"compress_threads",
"is invalid, it should be in the range of 1 to 255");
- return;
+ return false;
}
+
if (params->has_decompress_threads &&
(params->decompress_threads < 1 || params->decompress_threads > 255)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
"decompress_threads",
"is invalid, it should be in the range of 1 to 255");
- return;
+ return false;
}
+
if (params->has_cpu_throttle_initial &&
(params->cpu_throttle_initial < 1 ||
params->cpu_throttle_initial > 99)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
"cpu_throttle_initial",
"an integer in the range of 1 to 99");
- return;
+ return false;
}
+
if (params->has_cpu_throttle_increment &&
(params->cpu_throttle_increment < 1 ||
params->cpu_throttle_increment > 99)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
"cpu_throttle_increment",
"an integer in the range of 1 to 99");
- return;
+ return false;
}
+
if (params->has_max_bandwidth &&
(params->max_bandwidth < 0 || params->max_bandwidth > SIZE_MAX)) {
error_setg(errp, "Parameter 'max_bandwidth' expects an integer in the"
" range of 0 to %zu bytes/second", SIZE_MAX);
- return;
+ return false;
}
+
if (params->has_downtime_limit &&
(params->downtime_limit < 0 ||
params->downtime_limit > MAX_MIGRATE_DOWNTIME)) {
error_setg(errp, "Parameter 'downtime_limit' expects an integer in "
"the range of 0 to %d milliseconds",
MAX_MIGRATE_DOWNTIME);
- return;
+ return false;
}
+
if (params->has_x_checkpoint_delay && (params->x_checkpoint_delay < 0)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
"x_checkpoint_delay",
"is invalid, it should be positive");
+ return false;
}
+ return true;
+}
+
+static void migrate_params_apply(MigrationParameters *params)
+{
+ MigrationState *s = migrate_get_current();
+
if (params->has_compress_level) {
s->parameters.compress_level = params->compress_level;
}
+
if (params->has_compress_threads) {
s->parameters.compress_threads = params->compress_threads;
}
+
if (params->has_decompress_threads) {
s->parameters.decompress_threads = params->decompress_threads;
}
+
if (params->has_cpu_throttle_initial) {
s->parameters.cpu_throttle_initial = params->cpu_throttle_initial;
}
+
if (params->has_cpu_throttle_increment) {
s->parameters.cpu_throttle_increment = params->cpu_throttle_increment;
}
+
if (params->has_tls_creds) {
g_free(s->parameters.tls_creds);
s->parameters.tls_creds = g_strdup(params->tls_creds);
}
+
if (params->has_tls_hostname) {
g_free(s->parameters.tls_hostname);
s->parameters.tls_hostname = g_strdup(params->tls_hostname);
}
+
if (params->has_max_bandwidth) {
s->parameters.max_bandwidth = params->max_bandwidth;
if (s->to_dst_file) {
@@ -733,6 +782,7 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
s->parameters.max_bandwidth / XFER_LIMIT_RATIO);
}
}
+
if (params->has_downtime_limit) {
s->parameters.downtime_limit = params->downtime_limit;
}
@@ -743,11 +793,22 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
colo_checkpoint_notify(s);
}
}
+
if (params->has_block_incremental) {
s->parameters.block_incremental = params->block_incremental;
}
}
+void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
+{
+ if (!migrate_params_check(params, errp)) {
+ /* Invalid parameter */
+ return;
+ }
+
+ migrate_params_apply(params);
+}
+
void qmp_migrate_start_postcopy(Error **errp)
{
@@ -781,14 +842,27 @@ void migrate_set_state(int *state, int old_state, int new_state)
}
}
-void migrate_set_block_enabled(bool value, Error **errp)
+static MigrationCapabilityStatusList *migrate_cap_add(
+ MigrationCapabilityStatusList *list,
+ MigrationCapability index,
+ bool state)
{
MigrationCapabilityStatusList *cap;
cap = g_new0(MigrationCapabilityStatusList, 1);
cap->value = g_new0(MigrationCapabilityStatus, 1);
- cap->value->capability = MIGRATION_CAPABILITY_BLOCK;
- cap->value->state = value;
+ cap->value->capability = index;
+ cap->value->state = state;
+ cap->next = list;
+
+ return cap;
+}
+
+void migrate_set_block_enabled(bool value, Error **errp)
+{
+ MigrationCapabilityStatusList *cap;
+
+ cap = migrate_cap_add(NULL, MIGRATION_CAPABILITY_BLOCK, value);
qmp_migrate_set_capabilities(cap, errp);
qapi_free_MigrationCapabilityStatusList(cap);
}
@@ -2001,6 +2075,9 @@ void migration_global_dump(Monitor *mon)
ms->send_configuration, ms->send_section_footer);
}
+#define DEFINE_PROP_MIG_CAP(name, x) \
+ DEFINE_PROP_BOOL(name, MigrationState, enabled_capabilities[x], false)
+
static Property migration_properties[] = {
DEFINE_PROP_BOOL("store-global-state", MigrationState,
store_global_state, true),
@@ -2009,6 +2086,45 @@ static Property migration_properties[] = {
send_configuration, true),
DEFINE_PROP_BOOL("send-section-footer", MigrationState,
send_section_footer, true),
+
+ /* Migration parameters */
+ DEFINE_PROP_INT64("x-compress-level", MigrationState,
+ parameters.compress_level,
+ DEFAULT_MIGRATE_COMPRESS_LEVEL),
+ DEFINE_PROP_INT64("x-compress-threads", MigrationState,
+ parameters.compress_threads,
+ DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT),
+ DEFINE_PROP_INT64("x-decompress-threads", MigrationState,
+ parameters.decompress_threads,
+ DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT),
+ DEFINE_PROP_INT64("x-cpu-throttle-initial", MigrationState,
+ parameters.cpu_throttle_initial,
+ DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL),
+ DEFINE_PROP_INT64("x-cpu-throttle-increment", MigrationState,
+ parameters.cpu_throttle_increment,
+ DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT),
+ DEFINE_PROP_INT64("x-max-bandwidth", MigrationState,
+ parameters.max_bandwidth, MAX_THROTTLE),
+ DEFINE_PROP_INT64("x-downtime-limit", MigrationState,
+ parameters.downtime_limit,
+ DEFAULT_MIGRATE_SET_DOWNTIME),
+ DEFINE_PROP_INT64("x-checkpoint-delay", MigrationState,
+ parameters.x_checkpoint_delay,
+ DEFAULT_MIGRATE_X_CHECKPOINT_DELAY),
+
+ /* Migration capabilities */
+ DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE),
+ DEFINE_PROP_MIG_CAP("x-rdma-pin-all", MIGRATION_CAPABILITY_RDMA_PIN_ALL),
+ DEFINE_PROP_MIG_CAP("x-auto-converge", MIGRATION_CAPABILITY_AUTO_CONVERGE),
+ DEFINE_PROP_MIG_CAP("x-zero-blocks", MIGRATION_CAPABILITY_ZERO_BLOCKS),
+ DEFINE_PROP_MIG_CAP("x-compress", MIGRATION_CAPABILITY_COMPRESS),
+ DEFINE_PROP_MIG_CAP("x-events", MIGRATION_CAPABILITY_EVENTS),
+ DEFINE_PROP_MIG_CAP("x-postcopy-ram", MIGRATION_CAPABILITY_POSTCOPY_RAM),
+ DEFINE_PROP_MIG_CAP("x-colo", MIGRATION_CAPABILITY_X_COLO),
+ DEFINE_PROP_MIG_CAP("x-release-ram", MIGRATION_CAPABILITY_RELEASE_RAM),
+ DEFINE_PROP_MIG_CAP("x-block", MIGRATION_CAPABILITY_BLOCK),
+ DEFINE_PROP_MIG_CAP("x-return-path", MIGRATION_CAPABILITY_RETURN_PATH),
+
DEFINE_PROP_END_OF_LIST(),
};
@@ -2023,22 +2139,54 @@ static void migration_class_init(ObjectClass *klass, void *data)
static void migration_instance_init(Object *obj)
{
MigrationState *ms = MIGRATION_OBJ(obj);
+ MigrationParameters *params = &ms->parameters;
ms->state = MIGRATION_STATUS_NONE;
ms->xbzrle_cache_size = DEFAULT_MIGRATE_CACHE_SIZE;
ms->mbps = -1;
- ms->parameters = (MigrationParameters) {
- .compress_level = DEFAULT_MIGRATE_COMPRESS_LEVEL,
- .compress_threads = DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT,
- .decompress_threads = DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT,
- .cpu_throttle_initial = DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL,
- .cpu_throttle_increment = DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT,
- .max_bandwidth = MAX_THROTTLE,
- .downtime_limit = DEFAULT_MIGRATE_SET_DOWNTIME,
- .x_checkpoint_delay = DEFAULT_MIGRATE_X_CHECKPOINT_DELAY,
- };
- ms->parameters.tls_creds = g_strdup("");
- ms->parameters.tls_hostname = g_strdup("");
+
+ params->tls_hostname = g_strdup("");
+ params->tls_creds = g_strdup("");
+
+ /* Set has_* up only for parameter checks */
+ params->has_compress_level = true;
+ params->has_compress_threads = true;
+ params->has_decompress_threads = true;
+ params->has_cpu_throttle_initial = true;
+ params->has_cpu_throttle_increment = true;
+ params->has_max_bandwidth = true;
+ params->has_downtime_limit = true;
+ params->has_x_checkpoint_delay = true;
+ params->has_block_incremental = true;
+}
+
+/*
+ * Return true if check pass, false otherwise. Error will be put
+ * inside errp if provided.
+ */
+static bool migration_object_check(MigrationState *ms, Error **errp)
+{
+ MigrationCapabilityStatusList *head = NULL;
+ /* Assuming all off */
+ bool cap_list[MIGRATION_CAPABILITY__MAX] = { 0 }, ret;
+ int i;
+
+ if (!migrate_params_check(&ms->parameters, errp)) {
+ return false;
+ }
+
+ for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
+ if (ms->enabled_capabilities[i]) {
+ head = migrate_cap_add(head, i, true);
+ }
+ }
+
+ ret = migrate_caps_check(cap_list, head, errp);
+
+ /* It works with head == NULL */
+ qapi_free_MigrationCapabilityStatusList(head);
+
+ return ret;
}
static const TypeInfo migration_type = {
diff --git a/migration/rdma.c b/migration/rdma.c
index c6bc607a03..ca56594328 100644
--- a/migration/rdma.c
+++ b/migration/rdma.c
@@ -165,20 +165,6 @@ enum {
RDMA_CONTROL_UNREGISTER_FINISHED, /* unpinning finished */
};
-static const char *control_desc[] = {
- [RDMA_CONTROL_NONE] = "NONE",
- [RDMA_CONTROL_ERROR] = "ERROR",
- [RDMA_CONTROL_READY] = "READY",
- [RDMA_CONTROL_QEMU_FILE] = "QEMU FILE",
- [RDMA_CONTROL_RAM_BLOCKS_REQUEST] = "RAM BLOCKS REQUEST",
- [RDMA_CONTROL_RAM_BLOCKS_RESULT] = "RAM BLOCKS RESULT",
- [RDMA_CONTROL_COMPRESS] = "COMPRESS",
- [RDMA_CONTROL_REGISTER_REQUEST] = "REGISTER REQUEST",
- [RDMA_CONTROL_REGISTER_RESULT] = "REGISTER RESULT",
- [RDMA_CONTROL_REGISTER_FINISHED] = "REGISTER FINISHED",
- [RDMA_CONTROL_UNREGISTER_REQUEST] = "UNREGISTER REQUEST",
- [RDMA_CONTROL_UNREGISTER_FINISHED] = "UNREGISTER FINISHED",
-};
/*
* Memory and MR structures used to represent an IB Send/Recv work request.
@@ -251,6 +237,30 @@ typedef struct QEMU_PACKED RDMADestBlock {
uint32_t padding;
} RDMADestBlock;
+static const char *control_desc(unsigned int rdma_control)
+{
+ static const char *strs[] = {
+ [RDMA_CONTROL_NONE] = "NONE",
+ [RDMA_CONTROL_ERROR] = "ERROR",
+ [RDMA_CONTROL_READY] = "READY",
+ [RDMA_CONTROL_QEMU_FILE] = "QEMU FILE",
+ [RDMA_CONTROL_RAM_BLOCKS_REQUEST] = "RAM BLOCKS REQUEST",
+ [RDMA_CONTROL_RAM_BLOCKS_RESULT] = "RAM BLOCKS RESULT",
+ [RDMA_CONTROL_COMPRESS] = "COMPRESS",
+ [RDMA_CONTROL_REGISTER_REQUEST] = "REGISTER REQUEST",
+ [RDMA_CONTROL_REGISTER_RESULT] = "REGISTER RESULT",
+ [RDMA_CONTROL_REGISTER_FINISHED] = "REGISTER FINISHED",
+ [RDMA_CONTROL_UNREGISTER_REQUEST] = "UNREGISTER REQUEST",
+ [RDMA_CONTROL_UNREGISTER_FINISHED] = "UNREGISTER FINISHED",
+ };
+
+ if (rdma_control > RDMA_CONTROL_UNREGISTER_FINISHED) {
+ return "??BAD CONTROL VALUE??";
+ }
+
+ return strs[rdma_control];
+}
+
static uint64_t htonll(uint64_t v)
{
union { uint32_t lv[2]; uint64_t llv; } u;
@@ -1466,6 +1476,56 @@ static uint64_t qemu_rdma_poll(RDMAContext *rdma, uint64_t *wr_id_out,
return 0;
}
+/* Wait for activity on the completion channel.
+ * Returns 0 on success, none-0 on error.
+ */
+static int qemu_rdma_wait_comp_channel(RDMAContext *rdma)
+{
+ /*
+ * Coroutine doesn't start until migration_fd_process_incoming()
+ * so don't yield unless we know we're running inside of a coroutine.
+ */
+ if (rdma->migration_started_on_destination) {
+ yield_until_fd_readable(rdma->comp_channel->fd);
+ } else {
+ /* This is the source side, we're in a separate thread
+ * or destination prior to migration_fd_process_incoming()
+ * we can't yield; so we have to poll the fd.
+ * But we need to be able to handle 'cancel' or an error
+ * without hanging forever.
+ */
+ while (!rdma->error_state && !rdma->received_error) {
+ GPollFD pfds[1];
+ pfds[0].fd = rdma->comp_channel->fd;
+ pfds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR;
+ /* 0.1s timeout, should be fine for a 'cancel' */
+ switch (qemu_poll_ns(pfds, 1, 100 * 1000 * 1000)) {
+ case 1: /* fd active */
+ return 0;
+
+ case 0: /* Timeout, go around again */
+ break;
+
+ default: /* Error of some type -
+ * I don't trust errno from qemu_poll_ns
+ */
+ error_report("%s: poll failed", __func__);
+ return -EPIPE;
+ }
+
+ if (migrate_get_current()->state == MIGRATION_STATUS_CANCELLING) {
+ /* Bail out and let the cancellation happen */
+ return -EPIPE;
+ }
+ }
+ }
+
+ if (rdma->received_error) {
+ return -EPIPE;
+ }
+ return rdma->error_state;
+}
+
/*
* Block until the next work request has completed.
*
@@ -1513,22 +1573,21 @@ static int qemu_rdma_block_for_wrid(RDMAContext *rdma, int wrid_requested,
}
while (1) {
- /*
- * Coroutine doesn't start until migration_fd_process_incoming()
- * so don't yield unless we know we're running inside of a coroutine.
- */
- if (rdma->migration_started_on_destination) {
- yield_until_fd_readable(rdma->comp_channel->fd);
+ ret = qemu_rdma_wait_comp_channel(rdma);
+ if (ret) {
+ goto err_block_for_wrid;
}
- if (ibv_get_cq_event(rdma->comp_channel, &cq, &cq_ctx)) {
+ ret = ibv_get_cq_event(rdma->comp_channel, &cq, &cq_ctx);
+ if (ret) {
perror("ibv_get_cq_event");
goto err_block_for_wrid;
}
num_cq_events++;
- if (ibv_req_notify_cq(cq, 0)) {
+ ret = -ibv_req_notify_cq(cq, 0);
+ if (ret) {
goto err_block_for_wrid;
}
@@ -1564,6 +1623,8 @@ err_block_for_wrid:
if (num_cq_events) {
ibv_ack_cq_events(cq, num_cq_events);
}
+
+ rdma->error_state = ret;
return ret;
}
@@ -1590,7 +1651,7 @@ static int qemu_rdma_post_send_control(RDMAContext *rdma, uint8_t *buf,
.num_sge = 1,
};
- trace_qemu_rdma_post_send_control(control_desc[head->type]);
+ trace_qemu_rdma_post_send_control(control_desc(head->type));
/*
* We don't actually need to do a memcpy() in here if we used
@@ -1669,16 +1730,16 @@ static int qemu_rdma_exchange_get_response(RDMAContext *rdma,
network_to_control((void *) rdma->wr_data[idx].control);
memcpy(head, rdma->wr_data[idx].control, sizeof(RDMAControlHeader));
- trace_qemu_rdma_exchange_get_response_start(control_desc[expecting]);
+ trace_qemu_rdma_exchange_get_response_start(control_desc(expecting));
if (expecting == RDMA_CONTROL_NONE) {
- trace_qemu_rdma_exchange_get_response_none(control_desc[head->type],
+ trace_qemu_rdma_exchange_get_response_none(control_desc(head->type),
head->type);
} else if (head->type != expecting || head->type == RDMA_CONTROL_ERROR) {
error_report("Was expecting a %s (%d) control message"
", but got: %s (%d), length: %d",
- control_desc[expecting], expecting,
- control_desc[head->type], head->type, head->len);
+ control_desc(expecting), expecting,
+ control_desc(head->type), head->type, head->len);
if (head->type == RDMA_CONTROL_ERROR) {
rdma->received_error = true;
}
@@ -1788,7 +1849,7 @@ static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head,
}
}
- trace_qemu_rdma_exchange_send_waiting(control_desc[resp->type]);
+ trace_qemu_rdma_exchange_send_waiting(control_desc(resp->type));
ret = qemu_rdma_exchange_get_response(rdma, resp,
resp->type, RDMA_WRID_DATA);
@@ -1800,7 +1861,7 @@ static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head,
if (resp_idx) {
*resp_idx = RDMA_WRID_DATA;
}
- trace_qemu_rdma_exchange_send_received(control_desc[resp->type]);
+ trace_qemu_rdma_exchange_send_received(control_desc(resp->type));
}
rdma->control_ready_expected = 1;
@@ -2208,7 +2269,9 @@ static void qemu_rdma_cleanup(RDMAContext *rdma)
int ret, idx;
if (rdma->cm_id && rdma->connected) {
- if (rdma->error_state && !rdma->received_error) {
+ if ((rdma->error_state ||
+ migrate_get_current()->state == MIGRATION_STATUS_CANCELLING) &&
+ !rdma->received_error) {
RDMAControlHeader head = { .len = 0,
.type = RDMA_CONTROL_ERROR,
.repeat = 1,
@@ -2365,6 +2428,12 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp)
caps_to_network(&cap);
+ ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY);
+ if (ret) {
+ ERROR(errp, "posting second control recv");
+ goto err_rdma_source_connect;
+ }
+
ret = rdma_connect(rdma->cm_id, &conn_param);
if (ret) {
perror("rdma_connect");
@@ -2405,12 +2474,6 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp)
rdma_ack_cm_event(cm_event);
- ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY);
- if (ret) {
- ERROR(errp, "posting second control recv!");
- goto err_rdma_source_connect;
- }
-
rdma->control_ready_expected = 1;
rdma->nb_sent = 0;
return 0;
@@ -3350,7 +3413,7 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque)
ret = -EIO;
goto out;
default:
- error_report("Unknown control message %s", control_desc[head.type]);
+ error_report("Unknown control message %s", control_desc(head.type));
ret = -EIO;
goto out;
}
diff --git a/nbd/client.c b/nbd/client.c
index c3ee9f36b1..509ed5e4ba 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -232,8 +232,7 @@ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply,
break;
case NBD_REP_ERR_UNKNOWN:
- error_setg(errp, "Requested export not available for option %" PRIx32
- " (%s)", reply->option, nbd_opt_lookup(reply->option));
+ error_setg(errp, "Requested export not available");
break;
case NBD_REP_ERR_SHUTDOWN:
@@ -253,7 +252,7 @@ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply,
}
if (msg) {
- error_append_hint(errp, "%s\n", msg);
+ error_append_hint(errp, "server reported: %s\n", msg);
}
cleanup:
@@ -902,7 +901,8 @@ ssize_t nbd_send_request(QIOChannel *ioc, NBDRequest *request)
uint8_t buf[NBD_REQUEST_SIZE];
trace_nbd_send_request(request->from, request->len, request->handle,
- request->flags, request->type);
+ request->flags, request->type,
+ nbd_cmd_lookup(request->type));
stl_be_p(buf, NBD_REQUEST_MAGIC);
stw_be_p(buf + 4, request->flags);
diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
index 4065bc68ac..396ddb4d3e 100644
--- a/nbd/nbd-internal.h
+++ b/nbd/nbd-internal.h
@@ -38,9 +38,13 @@
*/
/* Size of all NBD_OPT_*, without payload */
-#define NBD_REQUEST_SIZE (4 + 2 + 2 + 8 + 8 + 4)
+#define NBD_REQUEST_SIZE (4 + 2 + 2 + 8 + 8 + 4)
/* Size of all NBD_REP_* sent in answer to most NBD_OPT_*, without payload */
-#define NBD_REPLY_SIZE (4 + 4 + 8)
+#define NBD_REPLY_SIZE (4 + 4 + 8)
+/* Size of reply to NBD_OPT_EXPORT_NAME */
+#define NBD_REPLY_EXPORT_NAME_SIZE (8 + 2 + 124)
+/* Size of oldstyle negotiation */
+#define NBD_OLDSTYLE_NEGOTIATE_SIZE (8 + 8 + 8 + 4 + 124)
#define NBD_REQUEST_MAGIC 0x25609513
#define NBD_REPLY_MAGIC 0x67446698
diff --git a/nbd/server.c b/nbd/server.c
index 49ed57455c..82a78bf439 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -283,12 +283,16 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length,
Error **errp)
{
char name[NBD_MAX_NAME_SIZE + 1];
- char buf[8 + 4 + 124] = "";
+ char buf[NBD_REPLY_EXPORT_NAME_SIZE] = "";
size_t len;
int ret;
/* Client sends:
[20 .. xx] export name (length bytes)
+ Server replies:
+ [ 0 .. 7] size
+ [ 8 .. 9] export flags
+ [10 .. 133] reserved (0) [unless no_zeroes]
*/
trace_nbd_negotiate_handle_export_name();
if (length >= sizeof(name)) {
@@ -800,22 +804,21 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
*/
static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp)
{
- char buf[8 + 8 + 8 + 128];
+ char buf[NBD_OLDSTYLE_NEGOTIATE_SIZE] = "";
int ret;
const uint16_t myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM |
NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA |
NBD_FLAG_SEND_WRITE_ZEROES);
bool oldStyle;
- /* Old style negotiation header without options
+ /* Old style negotiation header, no room for options
[ 0 .. 7] passwd ("NBDMAGIC")
[ 8 .. 15] magic (NBD_CLIENT_MAGIC)
[16 .. 23] size
- [24 .. 25] server flags (0)
- [26 .. 27] export flags
+ [24 .. 27] export flags (zero-extended)
[28 .. 151] reserved (0)
- New style negotiation header with options
+ New style negotiation header, client can send options
[ 0 .. 7] passwd ("NBDMAGIC")
[ 8 .. 15] magic (NBD_OPTS_MAGIC)
[16 .. 17] server flags (0)
@@ -825,7 +828,6 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp)
qio_channel_set_blocking(client->ioc, false, NULL);
trace_nbd_negotiate_begin();
- memset(buf, 0, sizeof(buf));
memcpy(buf, "NBDMAGIC", 8);
oldStyle = client->exp != NULL && !client->tlscreds;
@@ -834,7 +836,7 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp)
client->exp->nbdflags | myflags);
stq_be_p(buf + 8, NBD_CLIENT_MAGIC);
stq_be_p(buf + 16, client->exp->size);
- stw_be_p(buf + 26, client->exp->nbdflags | myflags);
+ stl_be_p(buf + 24, client->exp->nbdflags | myflags);
if (nbd_write(client->ioc, buf, sizeof(buf), errp) < 0) {
error_prepend(errp, "write failed: ");
diff --git a/nbd/trace-events b/nbd/trace-events
index f5024d85a1..d7c7746ea8 100644
--- a/nbd/trace-events
+++ b/nbd/trace-events
@@ -28,7 +28,7 @@ nbd_client_loop(void) "Doing NBD loop"
nbd_client_loop_ret(int ret, const char *error) "NBD loop returned %d: %s"
nbd_client_clear_queue(void) "Clearing NBD queue"
nbd_client_clear_socket(void) "Clearing NBD socket"
-nbd_send_request(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type) "Sending request to server: { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = %" PRIx16 ", .type = %" PRIu16 " }"
+nbd_send_request(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name) "Sending request to server: { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = %" PRIx16 ", .type = %" PRIu16 " (%s) }"
nbd_receive_reply(uint32_t magic, int32_t error, uint64_t handle) "Got reply: { magic = 0x%" PRIx32 ", .error = % " PRId32 ", handle = %" PRIu64" }"
# nbd/server.c
diff --git a/qapi/block-core.json b/qapi/block-core.json
index c437aa50ef..ff8e2ba0cb 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -457,6 +457,9 @@
#
# @device: The device name associated with the virtual device.
#
+# @qdev: The qdev ID, or if no ID is assigned, the QOM path of the block
+# device. (since 2.10)
+#
# @type: This field is returned only for compatibility reasons, it should
# not be used (always returns 'unknown')
#
@@ -482,7 +485,7 @@
# Since: 0.14.0
##
{ 'struct': 'BlockInfo',
- 'data': {'device': 'str', 'type': 'str', 'removable': 'bool',
+ 'data': {'device': 'str', '*qdev': 'str', 'type': 'str', 'removable': 'bool',
'locked': 'bool', '*inserted': 'BlockDeviceInfo',
'*tray_open': 'bool', '*io-status': 'BlockDeviceIoStatus',
'*dirty-bitmaps': ['BlockDirtyInfo'] } }
@@ -577,6 +580,7 @@
# }
# }
# },
+# "qdev": "ide_disk",
# "type":"unknown"
# },
# {
@@ -584,12 +588,15 @@
# "device":"ide1-cd0",
# "locked":false,
# "removable":true,
+# "qdev": "/machine/unattached/device[23]",
+# "tray_open": false,
# "type":"unknown"
# },
# {
# "device":"floppy0",
# "locked":false,
# "removable":true,
+# "qdev": "/machine/unattached/device[20]",
# "type":"unknown"
# },
# {
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index ac5946bc4f..3763f13625 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -22,9 +22,9 @@ STEXI
ETEXI
DEF("create", img_create,
- "create [-q] [--object objectdef] [-f fmt] [-b backing_file] [-F backing_fmt] [-o options] filename [size]")
+ "create [-q] [--object objectdef] [-f fmt] [-b backing_file] [-F backing_fmt] [-u] [-o options] filename [size]")
STEXI
-@item create [--object @var{objectdef}] [-q] [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-o @var{options}] @var{filename} [@var{size}]
+@item create [--object @var{objectdef}] [-q] [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-u] [-o @var{options}] @var{filename} [@var{size}]
ETEXI
DEF("commit", img_commit,
diff --git a/qemu-img.c b/qemu-img.c
index 182e697f81..eb32b93e90 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -150,9 +150,11 @@ static void QEMU_NORETURN help(void)
" 'snapshot_id_or_name' is deprecated, use 'snapshot_param'\n"
" instead\n"
" '-c' indicates that target image must be compressed (qcow format only)\n"
- " '-u' enables unsafe rebasing. It is assumed that old and new backing file\n"
- " match exactly. The image doesn't need a working backing file before\n"
- " rebasing in this case (useful for renaming the backing file)\n"
+ " '-u' allows unsafe backing chains. For rebasing, it is assumed that old and\n"
+ " new backing file match exactly. The image doesn't need a working\n"
+ " backing file before rebasing in this case (useful for renaming the\n"
+ " backing file). For image creation, allow creating without attempting\n"
+ " to open the backing file.\n"
" '-h' with or without a command shows this help and lists the supported formats\n"
" '-p' show progress of command (only certain commands)\n"
" '-q' use Quiet mode - do not print any output (except errors)\n"
@@ -429,6 +431,7 @@ static int img_create(int argc, char **argv)
char *options = NULL;
Error *local_err = NULL;
bool quiet = false;
+ int flags = 0;
for(;;) {
static const struct option long_options[] = {
@@ -436,7 +439,7 @@ static int img_create(int argc, char **argv)
{"object", required_argument, 0, OPTION_OBJECT},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, ":F:b:f:ho:q",
+ c = getopt_long(argc, argv, ":F:b:f:ho:qu",
long_options, NULL);
if (c == -1) {
break;
@@ -476,6 +479,9 @@ static int img_create(int argc, char **argv)
case 'q':
quiet = true;
break;
+ case 'u':
+ flags |= BDRV_O_NO_BACKING;
+ break;
case OPTION_OBJECT: {
QemuOpts *opts;
opts = qemu_opts_parse_noisily(&qemu_object_opts,
@@ -528,7 +534,7 @@ static int img_create(int argc, char **argv)
}
bdrv_img_create(filename, fmt, base_filename, base_fmt,
- options, img_size, 0, quiet, &local_err);
+ options, img_size, flags, quiet, &local_err);
if (local_err) {
error_reportf_err(local_err, "%s: ", filename);
goto fail;
diff --git a/qemu-img.texi b/qemu-img.texi
index f11f6036ad..72dabd6b3e 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -233,7 +233,7 @@ If @code{-r} is specified, exit codes representing the image state refer to the
state after (the attempt at) repairing it. That is, a successful @code{-r all}
will yield the exit code 0, independently of the image state before.
-@item create [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-o @var{options}] @var{filename} [@var{size}]
+@item create [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-u] [-o @var{options}] @var{filename} [@var{size}]
Create the new disk image @var{filename} of size @var{size} and format
@var{fmt}. Depending on the file format, you can add one or more @var{options}
@@ -244,6 +244,13 @@ only the differences from @var{backing_file}. No size needs to be specified in
this case. @var{backing_file} will never be modified unless you use the
@code{commit} monitor command (or qemu-img commit).
+Note that a given backing file will be opened to check that it is valid. Use
+the @code{-u} option to enable unsafe backing file mode, which means that the
+image will be created even if the associated backing file cannot be opened. A
+matching backing file must be created or additional options be used to make the
+backing file specification valid when you want to use an image created this
+way.
+
The size can also be specified using the @var{size} option with @code{-o},
it doesn't need to be specified separately in this case.
diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
index c654279a6c..f4e5bb6611 100644
--- a/target/s390x/cpu_models.c
+++ b/target/s390x/cpu_models.c
@@ -75,6 +75,7 @@ static S390CPUDef s390_cpu_defs[] = {
CPUDEF_INIT(0x2964, 13, 1, 47, 0x08000000U, "z13", "IBM z13 GA1"),
CPUDEF_INIT(0x2964, 13, 2, 47, 0x08000000U, "z13.2", "IBM z13 GA2"),
CPUDEF_INIT(0x2965, 13, 2, 47, 0x08000000U, "z13s", "IBM z13s GA1"),
+ CPUDEF_INIT(0x3906, 14, 1, 47, 0x08000000U, "z14", "IBM z14 GA1"),
};
void s390_cpudef_featoff(uint8_t gen, uint8_t ec_ga, S390Feat feat)
@@ -779,14 +780,19 @@ static void add_qemu_cpu_model_features(S390FeatBitmap fbm)
{
static const int feats[] = {
S390_FEAT_DAT_ENH,
+ S390_FEAT_IDTE_SEGMENT,
S390_FEAT_STFLE,
S390_FEAT_EXTENDED_IMMEDIATE,
S390_FEAT_EXTENDED_TRANSLATION_2,
+ S390_FEAT_EXTENDED_TRANSLATION_3,
S390_FEAT_LONG_DISPLACEMENT,
S390_FEAT_LONG_DISPLACEMENT_FAST,
S390_FEAT_ETF2_ENH,
S390_FEAT_STORE_CLOCK_FAST,
S390_FEAT_MOVE_WITH_OPTIONAL_SPEC,
+ S390_FEAT_ETF3_ENH,
+ S390_FEAT_COMPARE_AND_SWAP_AND_STORE,
+ S390_FEAT_COMPARE_AND_SWAP_AND_STORE_2,
S390_FEAT_GENERAL_INSTRUCTIONS_EXT,
S390_FEAT_EXECUTE_EXT,
S390_FEAT_FLOATING_POINT_SUPPPORT_ENH,
diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c
index af14b11199..cf69157610 100644
--- a/target/s390x/gen-features.c
+++ b/target/s390x/gen-features.c
@@ -338,6 +338,14 @@ static uint16_t base_GEN13_GA1[] = {
#define base_GEN13_GA2 EmptyFeat
+static uint16_t base_GEN14_GA1[] = {
+ S390_FEAT_ENTROPY_ENC_COMP,
+ S390_FEAT_MISC_INSTRUCTION_EXT,
+ S390_FEAT_SEMAPHORE_ASSIST,
+ S390_FEAT_TIME_SLICE_INSTRUMENTATION,
+ S390_FEAT_ORDER_PRESERVING_COMPRESSION,
+};
+
/* Full features (in order of release)
* Automatically includes corresponding base features.
* Full features are all features this hardware supports even if kvm/QEMU do not
@@ -442,6 +450,22 @@ static uint16_t full_GEN13_GA1[] = {
#define full_GEN13_GA2 EmptyFeat
+static uint16_t full_GEN14_GA1[] = {
+ S390_FEAT_INSTRUCTION_EXEC_PROT,
+ S390_FEAT_GUARDED_STORAGE,
+ S390_FEAT_VECTOR_PACKED_DECIMAL,
+ S390_FEAT_VECTOR_ENH,
+ S390_FEAT_MULTIPLE_EPOCH,
+ S390_FEAT_TEST_PENDING_EXT_INTERRUPTION,
+ S390_FEAT_INSERT_REFERENCE_BITS_MULT,
+ S390_FEAT_GROUP_MSA_EXT_6,
+ S390_FEAT_GROUP_MSA_EXT_7,
+ S390_FEAT_GROUP_MSA_EXT_8,
+ S390_FEAT_CMM_NT,
+ S390_FEAT_HPMA2,
+ S390_FEAT_SIE_KSS,
+};
+
/* Default features (in order of release)
* Automatically includes corresponding base features.
* Default features are all features this version of QEMU supports for this
@@ -502,6 +526,18 @@ static uint16_t default_GEN13_GA1[] = {
#define default_GEN13_GA2 EmptyFeat
+static uint16_t default_GEN14_GA1[] = {
+ S390_FEAT_ADAPTER_INT_SUPPRESSION,
+ S390_FEAT_INSTRUCTION_EXEC_PROT,
+ S390_FEAT_GUARDED_STORAGE,
+ S390_FEAT_VECTOR_PACKED_DECIMAL,
+ S390_FEAT_VECTOR_ENH,
+ S390_FEAT_GROUP_MSA_EXT_6,
+ S390_FEAT_GROUP_MSA_EXT_7,
+ S390_FEAT_GROUP_MSA_EXT_8,
+ S390_FEAT_SIE_KSS,
+};
+
/****** END FEATURE DEFS ******/
#define _YEARS "2016"
@@ -559,6 +595,7 @@ static CpuFeatDefSpec CpuFeatDef[] = {
CPU_FEAT_INITIALIZER(GEN12_GA2),
CPU_FEAT_INITIALIZER(GEN13_GA1),
CPU_FEAT_INITIALIZER(GEN13_GA2),
+ CPU_FEAT_INITIALIZER(GEN14_GA1),
};
#define FEAT_GROUP_INITIALIZER(_name) \
diff --git a/target/s390x/helper.h b/target/s390x/helper.h
index 964097b2ce..4b0290774e 100644
--- a/target/s390x/helper.h
+++ b/target/s390x/helper.h
@@ -12,7 +12,8 @@ DEF_HELPER_FLAGS_3(divs32, TCG_CALL_NO_WG, s64, env, s64, s64)
DEF_HELPER_FLAGS_3(divu32, TCG_CALL_NO_WG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(divs64, TCG_CALL_NO_WG, s64, env, s64, s64)
DEF_HELPER_FLAGS_4(divu64, TCG_CALL_NO_WG, i64, env, i64, i64, i64)
-DEF_HELPER_4(srst, i64, env, i64, i64, i64)
+DEF_HELPER_3(srst, void, env, i32, i32)
+DEF_HELPER_3(srstu, void, env, i32, i32)
DEF_HELPER_4(clst, i64, env, i64, i64, i64)
DEF_HELPER_FLAGS_4(mvn, TCG_CALL_NO_WG, void, env, i32, i64, i64)
DEF_HELPER_FLAGS_4(mvo, TCG_CALL_NO_WG, void, env, i32, i64, i64)
@@ -33,6 +34,7 @@ DEF_HELPER_3(celgb, i64, env, i64, i32)
DEF_HELPER_3(cdlgb, i64, env, i64, i32)
DEF_HELPER_3(cxlgb, i64, env, i64, i32)
DEF_HELPER_4(cdsg, void, env, i64, i32, i32)
+DEF_HELPER_4(csst, i32, env, i32, i64, i64)
DEF_HELPER_FLAGS_3(aeb, TCG_CALL_NO_WG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(adb, TCG_CALL_NO_WG, i64, env, i64, i64)
DEF_HELPER_FLAGS_5(axb, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i64)
@@ -95,6 +97,7 @@ DEF_HELPER_FLAGS_3(tp, TCG_CALL_NO_WG, i32, env, i64, i32)
DEF_HELPER_FLAGS_4(tr, TCG_CALL_NO_WG, void, env, i32, i64, i64)
DEF_HELPER_4(tre, i64, env, i64, i64, i64)
DEF_HELPER_4(trt, i32, env, i32, i64, i64)
+DEF_HELPER_4(trtr, i32, env, i32, i64, i64)
DEF_HELPER_5(trXX, i32, env, i32, i32, i32, i32)
DEF_HELPER_4(cksm, i64, env, i64, i64, i64)
DEF_HELPER_FLAGS_5(calc_cc, TCG_CALL_NO_RWG_SE, i32, env, i32, i64, i64, i64)
@@ -106,6 +109,12 @@ DEF_HELPER_2(stfle, i32, env, i64)
DEF_HELPER_FLAGS_2(lpq, TCG_CALL_NO_WG, i64, env, i64)
DEF_HELPER_FLAGS_4(stpq, TCG_CALL_NO_WG, void, env, i64, i64, i64)
DEF_HELPER_4(mvcos, i32, env, i64, i64, i64)
+DEF_HELPER_4(cu12, i32, env, i32, i32, i32)
+DEF_HELPER_4(cu14, i32, env, i32, i32, i32)
+DEF_HELPER_4(cu21, i32, env, i32, i32, i32)
+DEF_HELPER_4(cu24, i32, env, i32, i32, i32)
+DEF_HELPER_4(cu41, i32, env, i32, i32, i32)
+DEF_HELPER_4(cu42, i32, env, i32, i32, i32)
#ifndef CONFIG_USER_ONLY
DEF_HELPER_3(servc, i32, env, i64, i64)
diff --git a/target/s390x/insn-data.def b/target/s390x/insn-data.def
index d3bb8516ed..ad84c748e3 100644
--- a/target/s390x/insn-data.def
+++ b/target/s390x/insn-data.def
@@ -265,6 +265,8 @@
D(0xbb00, CDS, RS_a, Z, r3_D32, r1_D32, new, r1_D32, cs, 0, MO_TEQ)
D(0xeb31, CDSY, RSY_a, LD, r3_D32, r1_D32, new, r1_D32, cs, 0, MO_TEQ)
C(0xeb3e, CDSG, RSY_a, Z, 0, 0, 0, 0, cdsg, 0)
+/* COMPARE AND SWAP AND STORE */
+ C(0xc802, CSST, SSF, CASS, la1, a2, 0, 0, csst, 0)
/* COMPARE AND TRAP */
D(0xb972, CRT, RRF_c, GIE, r1_32s, r2_32s, 0, 0, ct, 0, 0)
@@ -311,6 +313,19 @@
C(0xb3a1, CDLGBR, RRF_e, FPE, 0, r2_o, f1, 0, cdlgb, 0)
C(0xb3a2, CXLGBR, RRF_e, FPE, 0, r2_o, x1, 0, cxlgb, 0)
+/* CONVERT UTF-8 TO UTF-16 */
+ D(0xb2a7, CU12, RRF_c, Z, 0, 0, 0, 0, cuXX, 0, 12)
+/* CONVERT UTF-8 TO UTF-32 */
+ D(0xb9b0, CU14, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 14)
+/* CONVERT UTF-16 to UTF-8 */
+ D(0xb2a6, CU21, RRF_c, Z, 0, 0, 0, 0, cuXX, 0, 21)
+/* CONVERT UTF-16 to UTF-32 */
+ D(0xb9b1, CU24, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 24)
+/* CONVERT UTF-32 to UTF-8 */
+ D(0xb9b2, CU41, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 41)
+/* CONVERT UTF-32 to UTF-16 */
+ D(0xb9b3, CU42, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 42)
+
/* DIVIDE */
C(0x1d00, DR, RR_a, Z, r1_D32, r2_32s, new_P, r1_P32, divs32, 0)
C(0x5d00, D, RX_a, Z, r1_D32, m2_32s, new_P, r1_P32, divs32, 0)
@@ -721,7 +736,9 @@
C(0xec57, RXSBG, RIE_f, GIE, 0, r2, r1, 0, rosbg, 0)
/* SEARCH STRING */
- C(0xb25e, SRST, RRE, Z, r1_o, r2_o, 0, 0, srst, 0)
+ C(0xb25e, SRST, RRE, Z, 0, 0, 0, 0, srst, 0)
+/* SEARCH STRING UNICODE */
+ C(0xb9be, SRSTU, RRE, ETF3, 0, 0, 0, 0, srstu, 0)
/* SET ACCESS */
C(0xb24e, SAR, RRE, Z, 0, r2_o, 0, 0, sar, 0)
@@ -899,6 +916,8 @@
C(0xdc00, TR, SS_a, Z, la1, a2, 0, 0, tr, 0)
/* TRANSLATE AND TEST */
C(0xdd00, TRT, SS_a, Z, la1, a2, 0, 0, trt, 0)
+/* TRANSLATE AND TEST REVERSE */
+ C(0xd000, TRTR, SS_a, ETF3, la1, a2, 0, 0, trtr, 0)
/* TRANSLATE EXTENDED */
C(0xb2a5, TRE, RRE, Z, 0, r2, r1_P, 0, tre, 0)
diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c
index ede84711d1..cdc78aa3d4 100644
--- a/target/s390x/mem_helper.c
+++ b/target/s390x/mem_helper.c
@@ -538,18 +538,21 @@ static inline void set_length(CPUS390XState *env, int reg, uint64_t length)
}
/* search string (c is byte to search, r2 is string, r1 end of string) */
-uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
- uint64_t str)
+void HELPER(srst)(CPUS390XState *env, uint32_t r1, uint32_t r2)
{
uintptr_t ra = GETPC();
+ uint64_t end, str;
uint32_t len;
- uint8_t v, c = r0;
+ uint8_t v, c = env->regs[0];
- str = wrap_address(env, str);
- end = wrap_address(env, end);
+ /* Bits 32-55 must contain all 0. */
+ if (env->regs[0] & 0xffffff00u) {
+ cpu_restore_state(ENV_GET_CPU(env), ra);
+ program_interrupt(env, PGM_SPECIFICATION, 6);
+ }
- /* Assume for now that R2 is unmodified. */
- env->retxl = str;
+ str = get_address(env, r2);
+ end = get_address(env, r1);
/* Lest we fail to service interrupts in a timely manner, limit the
amount of work we're willing to do. For now, let's cap at 8k. */
@@ -557,20 +560,61 @@ uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
if (str + len == end) {
/* Character not found. R1 & R2 are unmodified. */
env->cc_op = 2;
- return end;
+ return;
}
v = cpu_ldub_data_ra(env, str + len, ra);
if (v == c) {
/* Character found. Set R1 to the location; R2 is unmodified. */
env->cc_op = 1;
- return str + len;
+ set_address(env, r1, str + len);
+ return;
+ }
+ }
+
+ /* CPU-determined bytes processed. Advance R2 to next byte to process. */
+ env->cc_op = 3;
+ set_address(env, r2, str + len);
+}
+
+void HELPER(srstu)(CPUS390XState *env, uint32_t r1, uint32_t r2)
+{
+ uintptr_t ra = GETPC();
+ uint32_t len;
+ uint16_t v, c = env->regs[0];
+ uint64_t end, str, adj_end;
+
+ /* Bits 32-47 of R0 must be zero. */
+ if (env->regs[0] & 0xffff0000u) {
+ cpu_restore_state(ENV_GET_CPU(env), ra);
+ program_interrupt(env, PGM_SPECIFICATION, 6);
+ }
+
+ str = get_address(env, r2);
+ end = get_address(env, r1);
+
+ /* If the LSB of the two addresses differ, use one extra byte. */
+ adj_end = end + ((str ^ end) & 1);
+
+ /* Lest we fail to service interrupts in a timely manner, limit the
+ amount of work we're willing to do. For now, let's cap at 8k. */
+ for (len = 0; len < 0x2000; len += 2) {
+ if (str + len == adj_end) {
+ /* End of input found. */
+ env->cc_op = 2;
+ return;
+ }
+ v = cpu_lduw_data_ra(env, str + len, ra);
+ if (v == c) {
+ /* Character found. Set R1 to the location; R2 is unmodified. */
+ env->cc_op = 1;
+ set_address(env, r1, str + len);
+ return;
}
}
/* CPU-determined bytes processed. Advance R2 to next byte to process. */
- env->retxl = str + len;
env->cc_op = 3;
- return end;
+ set_address(env, r2, str + len);
}
/* unsigned string compare (c is string terminator) */
@@ -1233,17 +1277,18 @@ uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array,
return array + i;
}
-static uint32_t do_helper_trt(CPUS390XState *env, uint32_t len, uint64_t array,
- uint64_t trans, uintptr_t ra)
+static inline uint32_t do_helper_trt(CPUS390XState *env, int len,
+ uint64_t array, uint64_t trans,
+ int inc, uintptr_t ra)
{
- uint32_t i;
+ int i;
for (i = 0; i <= len; i++) {
- uint8_t byte = cpu_ldub_data_ra(env, array + i, ra);
+ uint8_t byte = cpu_ldub_data_ra(env, array + i * inc, ra);
uint8_t sbyte = cpu_ldub_data_ra(env, trans + byte, ra);
if (sbyte != 0) {
- set_address(env, 1, array + i);
+ set_address(env, 1, array + i * inc);
env->regs[2] = deposit64(env->regs[2], 0, 8, sbyte);
return (i == len) ? 2 : 1;
}
@@ -1255,7 +1300,13 @@ static uint32_t do_helper_trt(CPUS390XState *env, uint32_t len, uint64_t array,
uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array,
uint64_t trans)
{
- return do_helper_trt(env, len, array, trans, GETPC());
+ return do_helper_trt(env, len, array, trans, 1, GETPC());
+}
+
+uint32_t HELPER(trtr)(CPUS390XState *env, uint32_t len, uint64_t array,
+ uint64_t trans)
+{
+ return do_helper_trt(env, len, array, trans, -1, GETPC());
}
/* Translate one/two to one/two */
@@ -1353,6 +1404,195 @@ void HELPER(cdsg)(CPUS390XState *env, uint64_t addr,
env->regs[r1 + 1] = int128_getlo(oldv);
}
+uint32_t HELPER(csst)(CPUS390XState *env, uint32_t r3, uint64_t a1, uint64_t a2)
+{
+#if !defined(CONFIG_USER_ONLY) || defined(CONFIG_ATOMIC128)
+ uint32_t mem_idx = cpu_mmu_index(env, false);
+#endif
+ uintptr_t ra = GETPC();
+ uint32_t fc = extract32(env->regs[0], 0, 8);
+ uint32_t sc = extract32(env->regs[0], 8, 8);
+ uint64_t pl = get_address(env, 1) & -16;
+ uint64_t svh, svl;
+ uint32_t cc;
+
+ /* Sanity check the function code and storage characteristic. */
+ if (fc > 1 || sc > 3) {
+ if (!s390_has_feat(S390_FEAT_COMPARE_AND_SWAP_AND_STORE_2)) {
+ goto spec_exception;
+ }
+ if (fc > 2 || sc > 4 || (fc == 2 && (r3 & 1))) {
+ goto spec_exception;
+ }
+ }
+
+ /* Sanity check the alignments. */
+ if (extract32(a1, 0, 4 << fc) || extract32(a2, 0, 1 << sc)) {
+ goto spec_exception;
+ }
+
+ /* Sanity check writability of the store address. */
+#ifndef CONFIG_USER_ONLY
+ probe_write(env, a2, mem_idx, ra);
+#endif
+
+ /* Note that the compare-and-swap is atomic, and the store is atomic, but
+ the complete operation is not. Therefore we do not need to assert serial
+ context in order to implement this. That said, restart early if we can't
+ support either operation that is supposed to be atomic. */
+ if (parallel_cpus) {
+ int mask = 0;
+#if !defined(CONFIG_ATOMIC64)
+ mask = -8;
+#elif !defined(CONFIG_ATOMIC128)
+ mask = -16;
+#endif
+ if (((4 << fc) | (1 << sc)) & mask) {
+ cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
+ }
+ }
+
+ /* All loads happen before all stores. For simplicity, load the entire
+ store value area from the parameter list. */
+ svh = cpu_ldq_data_ra(env, pl + 16, ra);
+ svl = cpu_ldq_data_ra(env, pl + 24, ra);
+
+ switch (fc) {
+ case 0:
+ {
+ uint32_t nv = cpu_ldl_data_ra(env, pl, ra);
+ uint32_t cv = env->regs[r3];
+ uint32_t ov;
+
+ if (parallel_cpus) {
+#ifdef CONFIG_USER_ONLY
+ uint32_t *haddr = g2h(a1);
+ ov = atomic_cmpxchg__nocheck(haddr, cv, nv);
+#else
+ TCGMemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mem_idx);
+ ov = helper_atomic_cmpxchgl_be_mmu(env, a1, cv, nv, oi, ra);
+#endif
+ } else {
+ ov = cpu_ldl_data_ra(env, a1, ra);
+ cpu_stl_data_ra(env, a1, (ov == cv ? nv : ov), ra);
+ }
+ cc = (ov != cv);
+ env->regs[r3] = deposit64(env->regs[r3], 32, 32, ov);
+ }
+ break;
+
+ case 1:
+ {
+ uint64_t nv = cpu_ldq_data_ra(env, pl, ra);
+ uint64_t cv = env->regs[r3];
+ uint64_t ov;
+
+ if (parallel_cpus) {
+#ifdef CONFIG_ATOMIC64
+# ifdef CONFIG_USER_ONLY
+ uint64_t *haddr = g2h(a1);
+ ov = atomic_cmpxchg__nocheck(haddr, cv, nv);
+# else
+ TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN, mem_idx);
+ ov = helper_atomic_cmpxchgq_be_mmu(env, a1, cv, nv, oi, ra);
+# endif
+#else
+ /* Note that we asserted !parallel_cpus above. */
+ g_assert_not_reached();
+#endif
+ } else {
+ ov = cpu_ldq_data_ra(env, a1, ra);
+ cpu_stq_data_ra(env, a1, (ov == cv ? nv : ov), ra);
+ }
+ cc = (ov != cv);
+ env->regs[r3] = ov;
+ }
+ break;
+
+ case 2:
+ {
+ uint64_t nvh = cpu_ldq_data_ra(env, pl, ra);
+ uint64_t nvl = cpu_ldq_data_ra(env, pl + 8, ra);
+ Int128 nv = int128_make128(nvl, nvh);
+ Int128 cv = int128_make128(env->regs[r3 + 1], env->regs[r3]);
+ Int128 ov;
+
+ if (parallel_cpus) {
+#ifdef CONFIG_ATOMIC128
+ TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
+ ov = helper_atomic_cmpxchgo_be_mmu(env, a1, cv, nv, oi, ra);
+ cc = !int128_eq(ov, cv);
+#else
+ /* Note that we asserted !parallel_cpus above. */
+ g_assert_not_reached();
+#endif
+ } else {
+ uint64_t oh = cpu_ldq_data_ra(env, a1 + 0, ra);
+ uint64_t ol = cpu_ldq_data_ra(env, a1 + 8, ra);
+
+ ov = int128_make128(ol, oh);
+ cc = !int128_eq(ov, cv);
+ if (cc) {
+ nv = ov;
+ }
+
+ cpu_stq_data_ra(env, a1 + 0, int128_gethi(nv), ra);
+ cpu_stq_data_ra(env, a1 + 8, int128_getlo(nv), ra);
+ }
+
+ env->regs[r3 + 0] = int128_gethi(ov);
+ env->regs[r3 + 1] = int128_getlo(ov);
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ /* Store only if the comparison succeeded. Note that above we use a pair
+ of 64-bit big-endian loads, so for sc < 3 we must extract the value
+ from the most-significant bits of svh. */
+ if (cc == 0) {
+ switch (sc) {
+ case 0:
+ cpu_stb_data_ra(env, a2, svh >> 56, ra);
+ break;
+ case 1:
+ cpu_stw_data_ra(env, a2, svh >> 48, ra);
+ break;
+ case 2:
+ cpu_stl_data_ra(env, a2, svh >> 32, ra);
+ break;
+ case 3:
+ cpu_stq_data_ra(env, a2, svh, ra);
+ break;
+ case 4:
+ if (parallel_cpus) {
+#ifdef CONFIG_ATOMIC128
+ TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
+ Int128 sv = int128_make128(svl, svh);
+ helper_atomic_sto_be_mmu(env, a2, sv, oi, ra);
+#else
+ /* Note that we asserted !parallel_cpus above. */
+ g_assert_not_reached();
+#endif
+ } else {
+ cpu_stq_data_ra(env, a2 + 0, svh, ra);
+ cpu_stq_data_ra(env, a2 + 8, svl, ra);
+ }
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ return cc;
+
+ spec_exception:
+ cpu_restore_state(ENV_GET_CPU(env), ra);
+ program_interrupt(env, PGM_SPECIFICATION, 6);
+ g_assert_not_reached();
+}
+
#if !defined(CONFIG_USER_ONLY)
void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
{
@@ -1886,7 +2126,6 @@ void HELPER(ex)(CPUS390XState *env, uint32_t ilen, uint64_t r1, uint64_t addr)
[0x6] = do_helper_oc,
[0x7] = do_helper_xc,
[0xc] = do_helper_tr,
- [0xd] = do_helper_trt,
};
dx_helper helper = dx[opc & 0xf];
@@ -2007,3 +2246,313 @@ uint32_t HELPER(mvcos)(CPUS390XState *env, uint64_t dest, uint64_t src,
return cc;
}
+
+/* Decode a Unicode character. A return value < 0 indicates success, storing
+ the UTF-32 result into OCHAR and the input length into OLEN. A return
+ value >= 0 indicates failure, and the CC value to be returned. */
+typedef int (*decode_unicode_fn)(CPUS390XState *env, uint64_t addr,
+ uint64_t ilen, bool enh_check, uintptr_t ra,
+ uint32_t *ochar, uint32_t *olen);
+
+/* Encode a Unicode character. A return value < 0 indicates success, storing
+ the bytes into ADDR and the output length into OLEN. A return value >= 0
+ indicates failure, and the CC value to be returned. */
+typedef int (*encode_unicode_fn)(CPUS390XState *env, uint64_t addr,
+ uint64_t ilen, uintptr_t ra, uint32_t c,
+ uint32_t *olen);
+
+static int decode_utf8(CPUS390XState *env, uint64_t addr, uint64_t ilen,
+ bool enh_check, uintptr_t ra,
+ uint32_t *ochar, uint32_t *olen)
+{
+ uint8_t s0, s1, s2, s3;
+ uint32_t c, l;
+
+ if (ilen < 1) {
+ return 0;
+ }
+ s0 = cpu_ldub_data_ra(env, addr, ra);
+ if (s0 <= 0x7f) {
+ /* one byte character */
+ l = 1;
+ c = s0;
+ } else if (s0 <= (enh_check ? 0xc1 : 0xbf)) {
+ /* invalid character */
+ return 2;
+ } else if (s0 <= 0xdf) {
+ /* two byte character */
+ l = 2;
+ if (ilen < 2) {
+ return 0;
+ }
+ s1 = cpu_ldub_data_ra(env, addr + 1, ra);
+ c = s0 & 0x1f;
+ c = (c << 6) | (s1 & 0x3f);
+ if (enh_check && (s1 & 0xc0) != 0x80) {
+ return 2;
+ }
+ } else if (s0 <= 0xef) {
+ /* three byte character */
+ l = 3;
+ if (ilen < 3) {
+ return 0;
+ }
+ s1 = cpu_ldub_data_ra(env, addr + 1, ra);
+ s2 = cpu_ldub_data_ra(env, addr + 2, ra);
+ c = s0 & 0x0f;
+ c = (c << 6) | (s1 & 0x3f);
+ c = (c << 6) | (s2 & 0x3f);
+ /* Fold the byte-by-byte range descriptions in the PoO into
+ tests against the complete value. It disallows encodings
+ that could be smaller, and the UTF-16 surrogates. */
+ if (enh_check
+ && ((s1 & 0xc0) != 0x80
+ || (s2 & 0xc0) != 0x80
+ || c < 0x1000
+ || (c >= 0xd800 && c <= 0xdfff))) {
+ return 2;
+ }
+ } else if (s0 <= (enh_check ? 0xf4 : 0xf7)) {
+ /* four byte character */
+ l = 4;
+ if (ilen < 4) {
+ return 0;
+ }
+ s1 = cpu_ldub_data_ra(env, addr + 1, ra);
+ s2 = cpu_ldub_data_ra(env, addr + 2, ra);
+ s3 = cpu_ldub_data_ra(env, addr + 3, ra);
+ c = s0 & 0x07;
+ c = (c << 6) | (s1 & 0x3f);
+ c = (c << 6) | (s2 & 0x3f);
+ c = (c << 6) | (s3 & 0x3f);
+ /* See above. */
+ if (enh_check
+ && ((s1 & 0xc0) != 0x80
+ || (s2 & 0xc0) != 0x80
+ || (s3 & 0xc0) != 0x80
+ || c < 0x010000
+ || c > 0x10ffff)) {
+ return 2;
+ }
+ } else {
+ /* invalid character */
+ return 2;
+ }
+
+ *ochar = c;
+ *olen = l;
+ return -1;
+}
+
+static int decode_utf16(CPUS390XState *env, uint64_t addr, uint64_t ilen,
+ bool enh_check, uintptr_t ra,
+ uint32_t *ochar, uint32_t *olen)
+{
+ uint16_t s0, s1;
+ uint32_t c, l;
+
+ if (ilen < 2) {
+ return 0;
+ }
+ s0 = cpu_lduw_data_ra(env, addr, ra);
+ if ((s0 & 0xfc00) != 0xd800) {
+ /* one word character */
+ l = 2;
+ c = s0;
+ } else {
+ /* two word character */
+ l = 4;
+ if (ilen < 4) {
+ return 0;
+ }
+ s1 = cpu_lduw_data_ra(env, addr + 2, ra);
+ c = extract32(s0, 6, 4) + 1;
+ c = (c << 6) | (s0 & 0x3f);
+ c = (c << 10) | (s1 & 0x3ff);
+ if (enh_check && (s1 & 0xfc00) != 0xdc00) {
+ /* invalid surrogate character */
+ return 2;
+ }
+ }
+
+ *ochar = c;
+ *olen = l;
+ return -1;
+}
+
+static int decode_utf32(CPUS390XState *env, uint64_t addr, uint64_t ilen,
+ bool enh_check, uintptr_t ra,
+ uint32_t *ochar, uint32_t *olen)
+{
+ uint32_t c;
+
+ if (ilen < 4) {
+ return 0;
+ }
+ c = cpu_ldl_data_ra(env, addr, ra);
+ if ((c >= 0xd800 && c <= 0xdbff) || c > 0x10ffff) {
+ /* invalid unicode character */
+ return 2;
+ }
+
+ *ochar = c;
+ *olen = 4;
+ return -1;
+}
+
+static int encode_utf8(CPUS390XState *env, uint64_t addr, uint64_t ilen,
+ uintptr_t ra, uint32_t c, uint32_t *olen)
+{
+ uint8_t d[4];
+ uint32_t l, i;
+
+ if (c <= 0x7f) {
+ /* one byte character */
+ l = 1;
+ d[0] = c;
+ } else if (c <= 0x7ff) {
+ /* two byte character */
+ l = 2;
+ d[1] = 0x80 | extract32(c, 0, 6);
+ d[0] = 0xc0 | extract32(c, 6, 5);
+ } else if (c <= 0xffff) {
+ /* three byte character */
+ l = 3;
+ d[2] = 0x80 | extract32(c, 0, 6);
+ d[1] = 0x80 | extract32(c, 6, 6);
+ d[0] = 0xe0 | extract32(c, 12, 4);
+ } else {
+ /* four byte character */
+ l = 4;
+ d[3] = 0x80 | extract32(c, 0, 6);
+ d[2] = 0x80 | extract32(c, 6, 6);
+ d[1] = 0x80 | extract32(c, 12, 6);
+ d[0] = 0xf0 | extract32(c, 18, 3);
+ }
+
+ if (ilen < l) {
+ return 1;
+ }
+ for (i = 0; i < l; ++i) {
+ cpu_stb_data_ra(env, addr + i, d[i], ra);
+ }
+
+ *olen = l;
+ return -1;
+}
+
+static int encode_utf16(CPUS390XState *env, uint64_t addr, uint64_t ilen,
+ uintptr_t ra, uint32_t c, uint32_t *olen)
+{
+ uint16_t d0, d1;
+
+ if (c <= 0xffff) {
+ /* one word character */
+ if (ilen < 2) {
+ return 1;
+ }
+ cpu_stw_data_ra(env, addr, c, ra);
+ *olen = 2;
+ } else {
+ /* two word character */
+ if (ilen < 4) {
+ return 1;
+ }
+ d1 = 0xdc00 | extract32(c, 0, 10);
+ d0 = 0xd800 | extract32(c, 10, 6);
+ d0 = deposit32(d0, 6, 4, extract32(c, 16, 5) - 1);
+ cpu_stw_data_ra(env, addr + 0, d0, ra);
+ cpu_stw_data_ra(env, addr + 2, d1, ra);
+ *olen = 4;
+ }
+
+ return -1;
+}
+
+static int encode_utf32(CPUS390XState *env, uint64_t addr, uint64_t ilen,
+ uintptr_t ra, uint32_t c, uint32_t *olen)
+{
+ if (ilen < 4) {
+ return 1;
+ }
+ cpu_stl_data_ra(env, addr, c, ra);
+ *olen = 4;
+ return -1;
+}
+
+static inline uint32_t convert_unicode(CPUS390XState *env, uint32_t r1,
+ uint32_t r2, uint32_t m3, uintptr_t ra,
+ decode_unicode_fn decode,
+ encode_unicode_fn encode)
+{
+ uint64_t dst = get_address(env, r1);
+ uint64_t dlen = get_length(env, r1 + 1);
+ uint64_t src = get_address(env, r2);
+ uint64_t slen = get_length(env, r2 + 1);
+ bool enh_check = m3 & 1;
+ int cc, i;
+
+ /* Lest we fail to service interrupts in a timely manner, limit the
+ amount of work we're willing to do. For now, let's cap at 256. */
+ for (i = 0; i < 256; ++i) {
+ uint32_t c, ilen, olen;
+
+ cc = decode(env, src, slen, enh_check, ra, &c, &ilen);
+ if (unlikely(cc >= 0)) {
+ break;
+ }
+ cc = encode(env, dst, dlen, ra, c, &olen);
+ if (unlikely(cc >= 0)) {
+ break;
+ }
+
+ src += ilen;
+ slen -= ilen;
+ dst += olen;
+ dlen -= olen;
+ cc = 3;
+ }
+
+ set_address(env, r1, dst);
+ set_length(env, r1 + 1, dlen);
+ set_address(env, r2, src);
+ set_length(env, r2 + 1, slen);
+
+ return cc;
+}
+
+uint32_t HELPER(cu12)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
+{
+ return convert_unicode(env, r1, r2, m3, GETPC(),
+ decode_utf8, encode_utf16);
+}
+
+uint32_t HELPER(cu14)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
+{
+ return convert_unicode(env, r1, r2, m3, GETPC(),
+ decode_utf8, encode_utf32);
+}
+
+uint32_t HELPER(cu21)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
+{
+ return convert_unicode(env, r1, r2, m3, GETPC(),
+ decode_utf16, encode_utf8);
+}
+
+uint32_t HELPER(cu24)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
+{
+ return convert_unicode(env, r1, r2, m3, GETPC(),
+ decode_utf16, encode_utf32);
+}
+
+uint32_t HELPER(cu41)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
+{
+ return convert_unicode(env, r1, r2, m3, GETPC(),
+ decode_utf32, encode_utf8);
+}
+
+uint32_t HELPER(cu42)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
+{
+ return convert_unicode(env, r1, r2, m3, GETPC(),
+ decode_utf32, encode_utf16);
+}
diff --git a/target/s390x/translate.c b/target/s390x/translate.c
index 592d6b0f38..1dffcee884 100644
--- a/target/s390x/translate.c
+++ b/target/s390x/translate.c
@@ -2033,6 +2033,18 @@ static ExitStatus op_cdsg(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
+static ExitStatus op_csst(DisasContext *s, DisasOps *o)
+{
+ int r3 = get_field(s->fields, r3);
+ TCGv_i32 t_r3 = tcg_const_i32(r3);
+
+ gen_helper_csst(cc_op, cpu_env, t_r3, o->in1, o->in2);
+ tcg_temp_free_i32(t_r3);
+
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
#ifndef CONFIG_USER_ONLY
static ExitStatus op_csp(DisasContext *s, DisasOps *o)
{
@@ -2110,6 +2122,56 @@ static ExitStatus op_ct(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
+static ExitStatus op_cuXX(DisasContext *s, DisasOps *o)
+{
+ int m3 = get_field(s->fields, m3);
+ int r1 = get_field(s->fields, r1);
+ int r2 = get_field(s->fields, r2);
+ TCGv_i32 tr1, tr2, chk;
+
+ /* R1 and R2 must both be even. */
+ if ((r1 | r2) & 1) {
+ gen_program_exception(s, PGM_SPECIFICATION);
+ return EXIT_NORETURN;
+ }
+ if (!s390_has_feat(S390_FEAT_ETF3_ENH)) {
+ m3 = 0;
+ }
+
+ tr1 = tcg_const_i32(r1);
+ tr2 = tcg_const_i32(r2);
+ chk = tcg_const_i32(m3);
+
+ switch (s->insn->data) {
+ case 12:
+ gen_helper_cu12(cc_op, cpu_env, tr1, tr2, chk);
+ break;
+ case 14:
+ gen_helper_cu14(cc_op, cpu_env, tr1, tr2, chk);
+ break;
+ case 21:
+ gen_helper_cu21(cc_op, cpu_env, tr1, tr2, chk);
+ break;
+ case 24:
+ gen_helper_cu24(cc_op, cpu_env, tr1, tr2, chk);
+ break;
+ case 41:
+ gen_helper_cu41(cc_op, cpu_env, tr1, tr2, chk);
+ break;
+ case 42:
+ gen_helper_cu42(cc_op, cpu_env, tr1, tr2, chk);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ tcg_temp_free_i32(tr1);
+ tcg_temp_free_i32(tr2);
+ tcg_temp_free_i32(chk);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
#ifndef CONFIG_USER_ONLY
static ExitStatus op_diag(DisasContext *s, DisasOps *o)
{
@@ -3417,8 +3479,8 @@ static ExitStatus op_risbg(DisasContext *s, DisasOps *o)
}
/* In some cases we can implement this with extract. */
- if (imask == 0 && pos == 0 && len > 0 && rot + len <= 64) {
- tcg_gen_extract_i64(o->out, o->in2, rot, len);
+ if (imask == 0 && pos == 0 && len > 0 && len <= rot) {
+ tcg_gen_extract_i64(o->out, o->in2, 64 - rot, len);
return NO_EXIT;
}
@@ -4225,9 +4287,27 @@ static ExitStatus op_stpq(DisasContext *s, DisasOps *o)
static ExitStatus op_srst(DisasContext *s, DisasOps *o)
{
- gen_helper_srst(o->in1, cpu_env, regs[0], o->in1, o->in2);
+ TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
+ TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2));
+
+ gen_helper_srst(cpu_env, r1, r2);
+
+ tcg_temp_free_i32(r1);
+ tcg_temp_free_i32(r2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_srstu(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
+ TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2));
+
+ gen_helper_srstu(cpu_env, r1, r2);
+
+ tcg_temp_free_i32(r1);
+ tcg_temp_free_i32(r2);
set_cc_static(s);
- return_low128(o->in2);
return NO_EXIT;
}
@@ -4367,6 +4447,15 @@ static ExitStatus op_trt(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
+static ExitStatus op_trtr(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1));
+ gen_helper_trtr(cc_op, cpu_env, l, o->addr1, o->in2);
+ tcg_temp_free_i32(l);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
static ExitStatus op_trXX(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
@@ -5437,7 +5526,6 @@ enum DisasInsnEnum {
/* Give smaller names to the various facilities. */
#define FAC_Z S390_FEAT_ZARCH
#define FAC_CASS S390_FEAT_COMPARE_AND_SWAP_AND_STORE
-#define FAC_CASS2 S390_FEAT_COMPARE_AND_SWAP_AND_STORE_2
#define FAC_DFP S390_FEAT_DFP
#define FAC_DFPR S390_FEAT_FLOATING_POINT_SUPPPORT_ENH /* DFP-rounding */
#define FAC_DO S390_FEAT_STFLE_45 /* distinct-operands */
@@ -5466,6 +5554,7 @@ enum DisasInsnEnum {
#define FAC_EH S390_FEAT_STFLE_49 /* execution-hint */
#define FAC_PPA S390_FEAT_STFLE_49 /* processor-assist */
#define FAC_LZRB S390_FEAT_STFLE_53 /* load-and-zero-rightmost-byte */
+#define FAC_ETF3 S390_FEAT_EXTENDED_TRANSLATION_3
static const DisasInsn insn_info[] = {
#include "insn-data.def"
diff --git a/tests/qemu-iotests/067 b/tests/qemu-iotests/067
index 38d23fce6b..5d4ca4bc61 100755
--- a/tests/qemu-iotests/067
+++ b/tests/qemu-iotests/067
@@ -137,6 +137,19 @@ run_qemu <<EOF
{ "execute": "quit" }
EOF
+echo
+echo === Empty drive with -device and device_del ===
+echo
+
+run_qemu -device virtio-scsi-pci -device scsi-cd,id=cd0 <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "query-block" }
+{ "execute": "device_del", "arguments": { "id": "cd0" } }
+{ "execute": "system_reset" }
+{ "execute": "query-block" }
+{ "execute": "quit" }
+EOF
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/067.out b/tests/qemu-iotests/067.out
index 782eae27a0..bd70557ddc 100644
--- a/tests/qemu-iotests/067.out
+++ b/tests/qemu-iotests/067.out
@@ -57,6 +57,7 @@ Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virti
"file": "TEST_DIR/t.qcow2",
"encryption_key_missing": false
},
+ "qdev": "/machine/peripheral/virtio0/virtio-backend",
"type": "unknown"
}
]
@@ -415,4 +416,43 @@ Testing:
"return": {
}
}
+
+=== Empty drive with -device and device_del ===
+
+Testing: -device virtio-scsi-pci -device scsi-cd,id=cd0
+{
+ QMP_VERSION
+}
+{
+ "return": {
+ }
+}
+{
+ "return": [
+ {
+ "device": "",
+ "locked": false,
+ "removable": true,
+ "qdev": "cd0",
+ "tray_open": false,
+ "type": "unknown"
+ }
+ ]
+}
+{
+ "return": {
+ }
+}
+{
+ "return": {
+ }
+}
+{
+ "return": [
+ ]
+}
+{
+ "return": {
+ }
+}
*** done
diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082
index ad1d9fadc1..d5c83d45ed 100755
--- a/tests/qemu-iotests/082
+++ b/tests/qemu-iotests/082
@@ -85,8 +85,8 @@ run_qemu_img create -f $IMGFMT -o cluster_size=4k -o help "$TEST_IMG" $size
run_qemu_img create -f $IMGFMT -o cluster_size=4k -o \? "$TEST_IMG" $size
# Looks like a help option, but is part of the backing file name
-run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG",,help "$TEST_IMG" $size
-run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG",,\? "$TEST_IMG" $size
+run_qemu_img create -f $IMGFMT -u -o backing_file="$TEST_IMG",,help "$TEST_IMG" $size
+run_qemu_img create -f $IMGFMT -u -o backing_file="$TEST_IMG",,\? "$TEST_IMG" $size
# Try to trick qemu-img into creating escaped commas
run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG", -o help "$TEST_IMG" $size
diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out
index dbed67f2ba..1527fbe1b7 100644
--- a/tests/qemu-iotests/082.out
+++ b/tests/qemu-iotests/082.out
@@ -210,10 +210,10 @@ lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
-Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 128M
+Testing: create -f qcow2 -u -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 128M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2,,help cluster_size=65536 lazy_refcounts=off refcount_bits=16
-Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 128M
+Testing: create -f qcow2 -u -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 128M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2,,? cluster_size=65536 lazy_refcounts=off refcount_bits=16
Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2, -o help TEST_DIR/t.qcow2 128M
diff --git a/tests/qemu-iotests/085 b/tests/qemu-iotests/085
index b97adcd8db..71efe50d34 100755
--- a/tests/qemu-iotests/085
+++ b/tests/qemu-iotests/085
@@ -104,7 +104,7 @@ function add_snapshot_image()
{
base_image="${TEST_DIR}/$((${1}-1))-${snapshot_virt0}"
snapshot_file="${TEST_DIR}/${1}-${snapshot_virt0}"
- _make_test_img -b "${base_image}" "$size"
+ _make_test_img -u -b "${base_image}" "$size"
mv "${TEST_IMG}" "${snapshot_file}"
do_blockdev_add "$1" "'backing': '', " "${snapshot_file}"
}
diff --git a/tests/qemu-iotests/111.out b/tests/qemu-iotests/111.out
index 683c01a679..5279c462fc 100644
--- a/tests/qemu-iotests/111.out
+++ b/tests/qemu-iotests/111.out
@@ -1,3 +1,4 @@
QA output created by 111
qemu-img: TEST_DIR/t.IMGFMT: Could not open 'TEST_DIR/t.IMGFMT.inexistent': No such file or directory
+Could not open backing image to determine size.
*** done
diff --git a/tests/qemu-iotests/139 b/tests/qemu-iotests/139
index 175d8f0008..9ff51d9647 100644
--- a/tests/qemu-iotests/139
+++ b/tests/qemu-iotests/139
@@ -65,7 +65,7 @@ class TestBlockdevDel(iotests.QMPTestCase):
# Add a BlockDriverState that will be used as overlay for the base_img BDS
def addBlockDriverStateOverlay(self, node):
self.checkBlockDriverState(node, False)
- iotests.qemu_img('create', '-f', iotests.imgfmt,
+ iotests.qemu_img('create', '-u', '-f', iotests.imgfmt,
'-b', base_img, new_img, '1M')
opts = {'driver': iotests.imgfmt,
'node-name': node,
diff --git a/tests/qemu-iotests/140.out b/tests/qemu-iotests/140.out
index 0689b2b41c..7295b3d975 100644
--- a/tests/qemu-iotests/140.out
+++ b/tests/qemu-iotests/140.out
@@ -8,7 +8,8 @@ wrote 65536/65536 bytes at offset 0
read 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": {}}
-can't open device nbd+unix:///drv?socket=TEST_DIR/nbd: No export with name 'drv' available
+can't open device nbd+unix:///drv?socket=TEST_DIR/nbd: Requested export not available
+server reported: export 'drv' not present
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
*** done
diff --git a/tests/qemu-iotests/143.out b/tests/qemu-iotests/143.out
index 0978b8985a..1c7fb45543 100644
--- a/tests/qemu-iotests/143.out
+++ b/tests/qemu-iotests/143.out
@@ -1,7 +1,8 @@
QA output created by 143
{"return": {}}
{"return": {}}
-can't open device nbd+unix:///no_such_export?socket=TEST_DIR/nbd: No export with name 'no_such_export' available
+can't open device nbd+unix:///no_such_export?socket=TEST_DIR/nbd: Requested export not available
+server reported: export 'no_such_export' not present
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
*** done
diff --git a/tests/qemu-iotests/156 b/tests/qemu-iotests/156
index d799b73e1e..2c4a06e2d8 100755
--- a/tests/qemu-iotests/156
+++ b/tests/qemu-iotests/156
@@ -66,7 +66,7 @@ _send_qemu_cmd $QEMU_HANDLE \
'return'
# Create snapshot
-TEST_IMG="$TEST_IMG.overlay" _make_test_img -b "$TEST_IMG" 1M
+TEST_IMG="$TEST_IMG.overlay" _make_test_img -u -b "$TEST_IMG" 1M
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'blockdev-snapshot-sync',
'arguments': { 'device': 'source',
diff --git a/tests/qemu-iotests/158 b/tests/qemu-iotests/158
index 823c12002e..24ac600a4a 100755
--- a/tests/qemu-iotests/158
+++ b/tests/qemu-iotests/158
@@ -66,7 +66,7 @@ echo "== verify pattern =="
$QEMU_IO --object $SECRET -c "read -P 0xa 0 $size" --image-opts $IMGSPECBASE | _filter_qemu_io | _filter_testdir
echo "== create overlay =="
-_make_test_img --object $SECRET -o "encryption=on,encrypt.key-secret=sec0" -b "$TEST_IMG_BASE" $size
+_make_test_img -u --object $SECRET -o "encryption=on,encrypt.key-secret=sec0" -b "$TEST_IMG_BASE" $size
echo
echo "== writing part of a cluster =="
diff --git a/tests/qemu-iotests/186 b/tests/qemu-iotests/186
new file mode 100755
index 0000000000..ab83ee402a
--- /dev/null
+++ b/tests/qemu-iotests/186
@@ -0,0 +1,147 @@
+#!/bin/bash
+#
+# Test 'info block' with all kinds of configurations
+#
+# Copyright (C) 2017 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=kwolf@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+if [ "$QEMU_DEFAULT_MACHINE" != "pc" ]; then
+ _notrun "Requires a PC machine"
+fi
+
+function do_run_qemu()
+{
+ echo Testing: "$@"
+
+ (
+ if ! test -t 0; then
+ while read cmd; do
+ echo $cmd
+ done
+ fi
+ echo quit
+ ) | $QEMU -S -nodefaults -display none -device virtio-scsi-pci -monitor stdio "$@"
+ echo
+}
+
+function check_info_block()
+{
+ echo "info block" |
+ QEMU_OPTIONS="" do_run_qemu "$@" | _filter_win32 | _filter_hmp |
+ _filter_qemu | _filter_generated_node_ids
+}
+
+
+size=64M
+_make_test_img $size
+
+removable="floppy ide-cd scsi-cd"
+fixed="ide-hd scsi-hd virtio-blk-pci"
+
+echo
+echo "=== Empty drives ==="
+echo
+
+for dev in $removable; do
+ check_info_block -device $dev
+ check_info_block -device $dev,id=qdev_id
+done
+
+echo
+echo "=== -blockdev/-device=<node-name> ==="
+echo
+
+for dev in $fixed $removable; do
+ check_info_block -blockdev driver=null-co,node-name=null -device $dev,drive=null
+ check_info_block -blockdev driver=null-co,node-name=null -device $dev,drive=null,id=qdev_id
+done
+
+echo
+echo "=== -drive if=none/-device=<node-name> ==="
+echo
+
+# This creates two BlockBackends that will show up in 'info block'!
+# A monitor-owned one from -drive, and anonymous one from -device
+for dev in $fixed $removable; do
+ check_info_block -drive if=none,driver=null-co,node-name=null -device $dev,drive=null,id=qdev_id
+done
+
+echo
+echo "=== -drive if=none/-device=<bb-name> (with medium) ==="
+echo
+
+for dev in $fixed $removable; do
+ check_info_block -drive if=none,driver=null-co,node-name=null -device $dev,drive=none0
+ check_info_block -drive if=none,driver=null-co,node-name=null -device $dev,drive=none0,id=qdev_id
+done
+
+echo
+echo "=== -drive if=none/-device=<bb-name> (without medium) ==="
+echo
+
+check_info_block -drive if=none
+
+for dev in $removable; do
+ check_info_block -drive if=none -device $dev,drive=none0
+ check_info_block -drive if=none -device $dev,drive=none0,id=qdev_id
+done
+
+echo
+echo "=== -drive if=... ==="
+echo
+
+check_info_block -drive if=floppy
+check_info_block -drive if=floppy,driver=null-co
+
+check_info_block -drive if=ide,driver=null-co
+check_info_block -drive if=ide,media=cdrom
+check_info_block -drive if=ide,driver=null-co,media=cdrom
+
+check_info_block -drive if=scsi,driver=null-co
+check_info_block -drive if=scsi,media=cdrom
+check_info_block -drive if=scsi,driver=null-co,media=cdrom
+
+check_info_block -drive if=virtio,driver=null-co
+
+check_info_block -drive if=pflash,driver=null-co,size=1M
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/186.out b/tests/qemu-iotests/186.out
new file mode 100644
index 0000000000..b963b12d64
--- /dev/null
+++ b/tests/qemu-iotests/186.out
@@ -0,0 +1,489 @@
+QA output created by 186
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+
+=== Empty drives ===
+
+Testing: -device floppy
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+/machine/peripheral-anon/device[1]: [not inserted]
+ Attached to: /machine/peripheral-anon/device[1]
+ Removable device: not locked, tray closed
+(qemu) quit
+
+Testing: -device floppy,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+qdev_id: [not inserted]
+ Attached to: qdev_id
+ Removable device: not locked, tray closed
+(qemu) quit
+
+Testing: -device ide-cd
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+/machine/peripheral-anon/device[1]: [not inserted]
+ Attached to: /machine/peripheral-anon/device[1]
+ Removable device: not locked, tray closed
+(qemu) quit
+
+Testing: -device ide-cd,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+qdev_id: [not inserted]
+ Attached to: qdev_id
+ Removable device: not locked, tray closed
+(qemu) quit
+
+Testing: -device scsi-cd
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+/machine/peripheral-anon/device[1]: [not inserted]
+ Attached to: /machine/peripheral-anon/device[1]
+ Removable device: not locked, tray closed
+(qemu) quit
+
+Testing: -device scsi-cd,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+qdev_id: [not inserted]
+ Attached to: qdev_id
+ Removable device: not locked, tray closed
+(qemu) quit
+
+
+=== -blockdev/-device=<node-name> ===
+
+Testing: -blockdev driver=null-co,node-name=null -device ide-hd,drive=null
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+null: null-co:// (null-co)
+ Attached to: /machine/peripheral-anon/device[1]
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -blockdev driver=null-co,node-name=null -device ide-hd,drive=null,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+null: null-co:// (null-co)
+ Attached to: qdev_id
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -blockdev driver=null-co,node-name=null -device scsi-hd,drive=null
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+null: null-co:// (null-co)
+ Attached to: /machine/peripheral-anon/device[1]
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -blockdev driver=null-co,node-name=null -device scsi-hd,drive=null,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+null: null-co:// (null-co)
+ Attached to: qdev_id
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -blockdev driver=null-co,node-name=null -device virtio-blk-pci,drive=null
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+null: null-co:// (null-co)
+ Attached to: /machine/peripheral-anon/device[1]/virtio-backend
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -blockdev driver=null-co,node-name=null -device virtio-blk-pci,drive=null,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+null: null-co:// (null-co)
+ Attached to: /machine/peripheral/qdev_id/virtio-backend
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -blockdev driver=null-co,node-name=null -device floppy,drive=null
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+null: null-co:// (null-co)
+ Attached to: /machine/peripheral-anon/device[1]
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -blockdev driver=null-co,node-name=null -device floppy,drive=null,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+null: null-co:// (null-co)
+ Attached to: qdev_id
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -blockdev driver=null-co,node-name=null -device ide-cd,drive=null
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+null: null-co:// (null-co)
+ Attached to: /machine/peripheral-anon/device[1]
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -blockdev driver=null-co,node-name=null -device ide-cd,drive=null,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+null: null-co:// (null-co)
+ Attached to: qdev_id
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -blockdev driver=null-co,node-name=null -device scsi-cd,drive=null
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+null: null-co:// (null-co)
+ Attached to: /machine/peripheral-anon/device[1]
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -blockdev driver=null-co,node-name=null -device scsi-cd,drive=null,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+null: null-co:// (null-co)
+ Attached to: qdev_id
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+
+=== -drive if=none/-device=<node-name> ===
+
+Testing: -drive if=none,driver=null-co,node-name=null -device ide-hd,drive=null,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+
+null: null-co:// (null-co)
+ Attached to: qdev_id
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device scsi-hd,drive=null,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+
+null: null-co:// (null-co)
+ Attached to: qdev_id
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device virtio-blk-pci,drive=null,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+
+null: null-co:// (null-co)
+ Attached to: /machine/peripheral/qdev_id/virtio-backend
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device floppy,drive=null,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+
+null: null-co:// (null-co)
+ Attached to: qdev_id
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device ide-cd,drive=null,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+
+null: null-co:// (null-co)
+ Attached to: qdev_id
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device scsi-cd,drive=null,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+
+null: null-co:// (null-co)
+ Attached to: qdev_id
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+
+=== -drive if=none/-device=<bb-name> (with medium) ===
+
+Testing: -drive if=none,driver=null-co,node-name=null -device ide-hd,drive=none0
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Attached to: /machine/peripheral-anon/device[1]
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device ide-hd,drive=none0,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Attached to: qdev_id
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device scsi-hd,drive=none0
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Attached to: /machine/peripheral-anon/device[1]
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device scsi-hd,drive=none0,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Attached to: qdev_id
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device virtio-blk-pci,drive=none0
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Attached to: /machine/peripheral-anon/device[1]/virtio-backend
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device virtio-blk-pci,drive=none0,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Attached to: /machine/peripheral/qdev_id/virtio-backend
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device floppy,drive=none0
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Attached to: /machine/peripheral-anon/device[1]
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device floppy,drive=none0,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Attached to: qdev_id
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device ide-cd,drive=none0
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Attached to: /machine/peripheral-anon/device[1]
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device ide-cd,drive=none0,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Attached to: qdev_id
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device scsi-cd,drive=none0
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Attached to: /machine/peripheral-anon/device[1]
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=none,driver=null-co,node-name=null -device scsi-cd,drive=none0,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0 (null): null-co:// (null-co)
+ Attached to: qdev_id
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+
+=== -drive if=none/-device=<bb-name> (without medium) ===
+
+Testing: -drive if=none
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0: [not inserted]
+ Removable device: not locked, tray closed
+(qemu) quit
+
+Testing: -drive if=none -device floppy,drive=none0
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0: [not inserted]
+ Attached to: /machine/peripheral-anon/device[1]
+ Removable device: not locked, tray closed
+(qemu) quit
+
+Testing: -drive if=none -device floppy,drive=none0,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0: [not inserted]
+ Attached to: qdev_id
+ Removable device: not locked, tray closed
+(qemu) quit
+
+Testing: -drive if=none -device ide-cd,drive=none0
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0: [not inserted]
+ Attached to: /machine/peripheral-anon/device[1]
+ Removable device: not locked, tray closed
+(qemu) quit
+
+Testing: -drive if=none -device ide-cd,drive=none0,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0: [not inserted]
+ Attached to: qdev_id
+ Removable device: not locked, tray closed
+(qemu) quit
+
+Testing: -drive if=none -device scsi-cd,drive=none0
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0: [not inserted]
+ Attached to: /machine/peripheral-anon/device[1]
+ Removable device: not locked, tray closed
+(qemu) quit
+
+Testing: -drive if=none -device scsi-cd,drive=none0,id=qdev_id
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+none0: [not inserted]
+ Attached to: qdev_id
+ Removable device: not locked, tray closed
+(qemu) quit
+
+
+=== -drive if=... ===
+
+Testing: -drive if=floppy
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+floppy0: [not inserted]
+ Attached to: /machine/unattached/device[17]
+ Removable device: not locked, tray closed
+(qemu) quit
+
+Testing: -drive if=floppy,driver=null-co
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+floppy0 (NODE_NAME): null-co:// (null-co)
+ Attached to: /machine/unattached/device[17]
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=ide,driver=null-co
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+ide0-hd0 (NODE_NAME): null-co:// (null-co)
+ Attached to: /machine/unattached/device[18]
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=ide,media=cdrom
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+ide0-cd0: [not inserted]
+ Attached to: /machine/unattached/device[18]
+ Removable device: not locked, tray closed
+(qemu) quit
+
+Testing: -drive if=ide,driver=null-co,media=cdrom
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+ide0-cd0 (NODE_NAME): null-co:// (null-co, read-only)
+ Attached to: /machine/unattached/device[18]
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+warning: qemu-system-x86_64: -drive if=scsi,driver=null-co: bus=0,unit=0 is deprecated with this machine type
+Testing: -drive if=scsi,driver=null-co
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+scsi0-hd0 (NODE_NAME): null-co:// (null-co)
+ Attached to: /machine/unattached/device[27]/scsi.0/legacy[0]
+ Cache mode: writeback
+(qemu) quit
+
+warning: qemu-system-x86_64: -drive if=scsi,media=cdrom: bus=0,unit=0 is deprecated with this machine type
+Testing: -drive if=scsi,media=cdrom
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+scsi0-cd0: [not inserted]
+ Attached to: /machine/unattached/device[27]/scsi.0/legacy[0]
+ Removable device: not locked, tray closed
+(qemu) quit
+
+warning: qemu-system-x86_64: -drive if=scsi,driver=null-co,media=cdrom: bus=0,unit=0 is deprecated with this machine type
+Testing: -drive if=scsi,driver=null-co,media=cdrom
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+scsi0-cd0 (NODE_NAME): null-co:// (null-co, read-only)
+ Attached to: /machine/unattached/device[27]/scsi.0/legacy[0]
+ Removable device: not locked, tray closed
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=virtio,driver=null-co
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+virtio0 (NODE_NAME): null-co:// (null-co)
+ Attached to: /machine/peripheral-anon/device[1]/virtio-backend
+ Cache mode: writeback
+(qemu) quit
+
+Testing: -drive if=pflash,driver=null-co,size=1M
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+pflash0 (NODE_NAME): json:{"driver": "null-co", "size": "1M"} (null-co)
+ Attached to: /machine/unattached/device[2]
+ Cache mode: writeback
+(qemu) quit
+
+*** done
diff --git a/tests/qemu-iotests/189 b/tests/qemu-iotests/189
index 54ad980a4e..e695475722 100755
--- a/tests/qemu-iotests/189
+++ b/tests/qemu-iotests/189
@@ -66,7 +66,7 @@ echo "== verify pattern =="
$QEMU_IO --object $SECRET0 -c "read -P 0xa 0 $size" --image-opts $IMGSPECBASE | _filter_qemu_io | _filter_testdir
echo "== create overlay =="
-_make_test_img --object $SECRET1 -o "encrypt.format=luks,encrypt.key-secret=sec1,encrypt.iter-time=10" -b "$TEST_IMG_BASE" $size
+_make_test_img --object $SECRET1 -o "encrypt.format=luks,encrypt.key-secret=sec1,encrypt.iter-time=10" -u -b "$TEST_IMG_BASE" $size
echo
echo "== writing part of a cluster =="
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 2aba585287..0961f8cc4e 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -181,5 +181,6 @@
182 rw auto quick
183 rw auto migration
185 rw auto
+186 rw auto
188 rw auto quick
189 rw auto quick
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index a9201b1fea..768f11dfed 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -228,7 +228,7 @@ static void test_config_functions(void)
read_timer_cb, write_timer_cb, &ts);
/* structure reset by throttle_init previous_leak should be null */
g_assert(!ts.previous_leak);
- throttle_config(&ts, &tt, &orig_cfg);
+ throttle_config(&ts, QEMU_CLOCK_VIRTUAL, &orig_cfg);
/* has previous leak been initialized by throttle_config ? */
g_assert(ts.previous_leak);
@@ -486,7 +486,7 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
throttle_init(&ts);
throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
read_timer_cb, write_timer_cb, &ts);
- throttle_config(&ts, &tt, &cfg);
+ throttle_config(&ts, QEMU_CLOCK_VIRTUAL, &cfg);
/* account a read */
throttle_account(&ts, false, size);
diff --git a/util/throttle.c b/util/throttle.c
index 3570ed25fc..b2a52b8b34 100644
--- a/util/throttle.c
+++ b/util/throttle.c
@@ -388,22 +388,14 @@ static void throttle_unfix_bucket(LeakyBucket *bkt)
}
}
-/* take care of canceling a timer */
-static void throttle_cancel_timer(QEMUTimer *timer)
-{
- assert(timer != NULL);
-
- timer_del(timer);
-}
-
/* Used to configure the throttle
*
* @ts: the throttle state we are working on
- * @tt: the throttle timers we use in this aio context
+ * @clock_type: the group's clock_type
* @cfg: the config to set
*/
void throttle_config(ThrottleState *ts,
- ThrottleTimers *tt,
+ QEMUClockType clock_type,
ThrottleConfig *cfg)
{
int i;
@@ -414,11 +406,7 @@ void throttle_config(ThrottleState *ts,
throttle_fix_bucket(&ts->cfg.buckets[i]);
}
- ts->previous_leak = qemu_clock_get_ns(tt->clock_type);
-
- for (i = 0; i < 2; i++) {
- throttle_cancel_timer(tt->timers[i]);
- }
+ ts->previous_leak = qemu_clock_get_ns(clock_type);
}
/* used to get config