aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block.c41
-rw-r--r--hw/block/nvme.c19
-rw-r--r--hw/scsi/scsi-disk.c2
-rw-r--r--qemu-img.c3
-rw-r--r--tests/Makefile.include2
-rw-r--r--tests/nvme-test.c68
-rwxr-xr-xtests/qemu-iotests/0416
-rwxr-xr-xtests/qemu-iotests/11820
-rwxr-xr-xtests/qemu-iotests/161137
-rw-r--r--tests/qemu-iotests/161.out39
-rwxr-xr-xtests/qemu-iotests/22343
-rw-r--r--tests/qemu-iotests/223.out32
-rwxr-xr-xtests/qemu-iotests/2339
-rw-r--r--tests/qemu-iotests/common.tls3
-rw-r--r--tests/qemu-iotests/group1
-rw-r--r--tests/qemu-iotests/iotests.py2
16 files changed, 364 insertions, 63 deletions
diff --git a/block.c b/block.c
index 3feac08535..5ba3435f8f 100644
--- a/block.c
+++ b/block.c
@@ -1137,7 +1137,7 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
static void update_flags_from_options(int *flags, QemuOpts *opts)
{
- *flags &= ~BDRV_O_CACHE_MASK;
+ *flags &= ~(BDRV_O_CACHE_MASK | BDRV_O_RDWR | BDRV_O_AUTO_RDONLY);
assert(qemu_opt_find(opts, BDRV_OPT_CACHE_NO_FLUSH));
if (qemu_opt_get_bool_del(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
@@ -1149,8 +1149,6 @@ static void update_flags_from_options(int *flags, QemuOpts *opts)
*flags |= BDRV_O_NOCACHE;
}
- *flags &= ~BDRV_O_RDWR;
-
assert(qemu_opt_find(opts, BDRV_OPT_READ_ONLY));
if (!qemu_opt_get_bool_del(opts, BDRV_OPT_READ_ONLY, false)) {
*flags |= BDRV_O_RDWR;
@@ -2262,6 +2260,18 @@ static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load)
}
}
+/* Return true if you can reach parent going through child->inherits_from
+ * recursively. If parent or child are NULL, return false */
+static bool bdrv_inherits_from_recursive(BlockDriverState *child,
+ BlockDriverState *parent)
+{
+ while (child && child != parent) {
+ child = child->inherits_from;
+ }
+
+ return child != NULL;
+}
+
/*
* Sets the backing file link of a BDS. A new reference is created; callers
* which don't need their own reference any more must call bdrv_unref().
@@ -2269,6 +2279,9 @@ static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load)
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
Error **errp)
{
+ bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) &&
+ bdrv_inherits_from_recursive(backing_hd, bs);
+
if (backing_hd) {
bdrv_ref(backing_hd);
}
@@ -2284,6 +2297,12 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing,
errp);
+ /* If backing_hd was already part of bs's backing chain, and
+ * inherits_from pointed recursively to bs then let's update it to
+ * point directly to bs (else it will become NULL). */
+ if (update_inherits_from) {
+ backing_hd->inherits_from = bs;
+ }
if (!bs->backing) {
bdrv_unref(backing_hd);
}
@@ -3836,6 +3855,8 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs)
int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
const char *backing_file_str)
{
+ BlockDriverState *explicit_top = top;
+ bool update_inherits_from;
BdrvChild *c, *next;
Error *local_err = NULL;
int ret = -EIO;
@@ -3851,6 +3872,16 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
goto exit;
}
+ /* If 'base' recursively inherits from 'top' then we should set
+ * base->inherits_from to top->inherits_from after 'top' and all
+ * other intermediate nodes have been dropped.
+ * If 'top' is an implicit node (e.g. "commit_top") we should skip
+ * it because no one inherits from it. We use explicit_top for that. */
+ while (explicit_top && explicit_top->implicit) {
+ explicit_top = backing_bs(explicit_top);
+ }
+ update_inherits_from = bdrv_inherits_from_recursive(base, explicit_top);
+
/* success - we can delete the intermediate states, and link top->base */
/* TODO Check graph modification op blockers (BLK_PERM_GRAPH_MOD) once
* we've figured out how they should work. */
@@ -3886,6 +3917,10 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
bdrv_unref(top);
}
+ if (update_inherits_from) {
+ base->inherits_from = explicit_top->inherits_from;
+ }
+
ret = 0;
exit:
bdrv_unref(top);
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index d0226e7fdc..9fbe5673cb 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -554,6 +554,7 @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd)
trace_nvme_err_invalid_del_cq_notempty(qid);
return NVME_INVALID_QUEUE_DEL;
}
+ nvme_irq_deassert(n, cq);
trace_nvme_del_cq(qid);
nvme_free_cq(cq, n);
return NVME_SUCCESS;
@@ -797,6 +798,8 @@ static void nvme_clear_ctrl(NvmeCtrl *n)
{
int i;
+ blk_drain(n->conf.blk);
+
for (i = 0; i < n->num_queues; i++) {
if (n->sq[i] != NULL) {
nvme_free_sq(n->sq[i], n);
@@ -1175,23 +1178,13 @@ static void nvme_cmb_write(void *opaque, hwaddr addr, uint64_t data,
unsigned size)
{
NvmeCtrl *n = (NvmeCtrl *)opaque;
-
- if (addr + size > NVME_CMBSZ_GETSIZE(n->bar.cmbsz)) {
- return;
- }
- memcpy(&n->cmbuf[addr], &data, size);
+ stn_le_p(&n->cmbuf[addr], size, data);
}
static uint64_t nvme_cmb_read(void *opaque, hwaddr addr, unsigned size)
{
- uint64_t val;
NvmeCtrl *n = (NvmeCtrl *)opaque;
-
- if (addr + size > NVME_CMBSZ_GETSIZE(n->bar.cmbsz)) {
- return 0;
- }
- memcpy(&val, &n->cmbuf[addr], size);
- return val;
+ return ldn_le_p(&n->cmbuf[addr], size);
}
static const MemoryRegionOps nvme_cmb_ops = {
@@ -1199,7 +1192,7 @@ static const MemoryRegionOps nvme_cmb_ops = {
.write = nvme_cmb_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl = {
- .min_access_size = 2,
+ .min_access_size = 1,
.max_access_size = 8,
},
};
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 6eb258d3f3..0e9027c8f3 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -482,7 +482,7 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed)
if (action == BLOCK_ERROR_ACTION_STOP) {
scsi_req_retry(&r->req);
}
- return false;
+ return true;
}
static void scsi_write_complete_noio(SCSIDiskReq *r, int ret)
diff --git a/qemu-img.c b/qemu-img.c
index 13a6ca31b4..ad04f59565 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -261,8 +261,9 @@ static int print_block_option_help(const char *filename, const char *fmt)
return 1;
}
if (!proto_drv->create_opts) {
- error_report("Protocal driver '%s' does not support image creation",
+ error_report("Protocol driver '%s' does not support image creation",
proto_drv->format_name);
+ qemu_opts_free(create_opts);
return 1;
}
create_opts = qemu_opts_append(create_opts, proto_drv->create_opts);
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 613242bc6e..fb0b449c02 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -730,7 +730,7 @@ tests/test-hmp$(EXESUF): tests/test-hmp.o
tests/machine-none-test$(EXESUF): tests/machine-none-test.o
tests/drive_del-test$(EXESUF): tests/drive_del-test.o $(libqos-virtio-obj-y)
tests/qdev-monitor-test$(EXESUF): tests/qdev-monitor-test.o $(libqos-pc-obj-y)
-tests/nvme-test$(EXESUF): tests/nvme-test.o
+tests/nvme-test$(EXESUF): tests/nvme-test.o $(libqos-pc-obj-y)
tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o
tests/i82801b11-test$(EXESUF): tests/i82801b11-test.o
tests/ac97-test$(EXESUF): tests/ac97-test.o
diff --git a/tests/nvme-test.c b/tests/nvme-test.c
index 7674a446e4..2700ba838a 100644
--- a/tests/nvme-test.c
+++ b/tests/nvme-test.c
@@ -8,25 +8,73 @@
*/
#include "qemu/osdep.h"
+#include "qemu/units.h"
#include "libqtest.h"
+#include "libqos/libqos-pc.h"
+
+static QOSState *qnvme_start(const char *extra_opts)
+{
+ QOSState *qs;
+ const char *arch = qtest_get_arch();
+ const char *cmd = "-drive id=drv0,if=none,file=null-co://,format=raw "
+ "-device nvme,addr=0x4.0,serial=foo,drive=drv0 %s";
+
+ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+ qs = qtest_pc_boot(cmd, extra_opts ? : "");
+ global_qtest = qs->qts;
+ return qs;
+ }
+
+ g_printerr("nvme tests are only available on x86\n");
+ exit(EXIT_FAILURE);
+}
+
+static void qnvme_stop(QOSState *qs)
+{
+ qtest_shutdown(qs);
+}
-/* Tests only initialization so far. TODO: Replace with functional tests */
static void nop(void)
{
+ QOSState *qs;
+
+ qs = qnvme_start(NULL);
+ qnvme_stop(qs);
}
-int main(int argc, char **argv)
+static void nvmetest_cmb_test(void)
{
- int ret;
+ const int cmb_bar_size = 2 * MiB;
+ QOSState *qs;
+ QPCIDevice *pdev;
+ QPCIBar bar;
- g_test_init(&argc, &argv, NULL);
- qtest_add_func("/nvme/nop", nop);
+ qs = qnvme_start("-global nvme.cmb_size_mb=2");
+ pdev = qpci_device_find(qs->pcibus, QPCI_DEVFN(4,0));
+ g_assert(pdev != NULL);
+
+ qpci_device_enable(pdev);
+ bar = qpci_iomap(pdev, 2, NULL);
+
+ qpci_io_writel(pdev, bar, 0, 0xccbbaa99);
+ g_assert_cmpint(qpci_io_readb(pdev, bar, 0), ==, 0x99);
+ g_assert_cmpint(qpci_io_readw(pdev, bar, 0), ==, 0xaa99);
+
+ /* Test partially out-of-bounds accesses. */
+ qpci_io_writel(pdev, bar, cmb_bar_size - 1, 0x44332211);
+ g_assert_cmpint(qpci_io_readb(pdev, bar, cmb_bar_size - 1), ==, 0x11);
+ g_assert_cmpint(qpci_io_readw(pdev, bar, cmb_bar_size - 1), !=, 0x2211);
+ g_assert_cmpint(qpci_io_readl(pdev, bar, cmb_bar_size - 1), !=, 0x44332211);
+ g_free(pdev);
- qtest_start("-drive id=drv0,if=none,file=null-co://,format=raw "
- "-device nvme,drive=drv0,serial=foo");
- ret = g_test_run();
+ qnvme_stop(qs);
+}
- qtest_end();
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ qtest_add_func("/nvme/nop", nop);
+ qtest_add_func("/nvme/cmb_test", nvmetest_cmb_test);
- return ret;
+ return g_test_run();
}
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
index 3615011d98..26bf1701eb 100755
--- a/tests/qemu-iotests/041
+++ b/tests/qemu-iotests/041
@@ -469,7 +469,7 @@ new_state = "1"
self.assert_qmp(event, 'data/id', 'drive0')
event = self.vm.get_qmp_event(wait=True)
- self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
+ self.assertEqual(event['event'], 'BLOCK_JOB_ERROR')
self.assert_qmp(event, 'data/device', 'drive0')
self.assert_qmp(event, 'data/operation', 'read')
result = self.vm.qmp('query-block-jobs')
@@ -494,7 +494,7 @@ new_state = "1"
self.assert_qmp(event, 'data/id', 'drive0')
event = self.vm.get_qmp_event(wait=True)
- self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
+ self.assertEqual(event['event'], 'BLOCK_JOB_ERROR')
self.assert_qmp(event, 'data/device', 'drive0')
self.assert_qmp(event, 'data/operation', 'read')
result = self.vm.qmp('query-block-jobs')
@@ -625,7 +625,7 @@ new_state = "1"
self.assert_qmp(result, 'return', {})
event = self.vm.event_wait(name='BLOCK_JOB_ERROR')
- self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
+ self.assertEqual(event['event'], 'BLOCK_JOB_ERROR')
self.assert_qmp(event, 'data/device', 'drive0')
self.assert_qmp(event, 'data/operation', 'write')
result = self.vm.qmp('query-block-jobs')
diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118
index ff3b2ae3e7..603e10e8a2 100755
--- a/tests/qemu-iotests/118
+++ b/tests/qemu-iotests/118
@@ -53,21 +53,17 @@ class ChangeBaseClass(iotests.QMPTestCase):
if not self.has_real_tray:
return
- timeout = time.clock() + 3
- while not self.has_opened and time.clock() < timeout:
- self.process_events()
- if not self.has_opened:
- self.fail('Timeout while waiting for the tray to open')
+ with iotests.Timeout(3, 'Timeout while waiting for the tray to open'):
+ while not self.has_opened:
+ self.process_events()
def wait_for_close(self):
if not self.has_real_tray:
return
- timeout = time.clock() + 3
- while not self.has_closed and time.clock() < timeout:
- self.process_events()
- if not self.has_opened:
- self.fail('Timeout while waiting for the tray to close')
+ with iotests.Timeout(3, 'Timeout while waiting for the tray to close'):
+ while not self.has_closed:
+ self.process_events()
class GeneralChangeTestsBaseClass(ChangeBaseClass):
@@ -265,7 +261,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
result = self.vm.qmp('blockdev-close-tray', id=self.device_name)
# Should be a no-op
self.assert_qmp(result, 'return', {})
- self.assertEquals(self.vm.get_qmp_events(wait=False), [])
+ self.assertEqual(self.vm.get_qmp_events(wait=False), [])
def test_remove_on_closed(self):
if not self.has_real_tray:
@@ -452,7 +448,7 @@ class TestChangeReadOnly(ChangeBaseClass):
read_only_mode='retain')
self.assert_qmp(result, 'error/class', 'GenericError')
- self.assertEquals(self.vm.get_qmp_events(wait=False), [])
+ self.assertEqual(self.vm.get_qmp_events(wait=False), [])
result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/inserted/ro', False)
diff --git a/tests/qemu-iotests/161 b/tests/qemu-iotests/161
new file mode 100755
index 0000000000..180df17ad6
--- /dev/null
+++ b/tests/qemu-iotests/161
@@ -0,0 +1,137 @@
+#!/bin/bash
+#
+# Test reopening a backing image after block-stream and block-commit
+#
+# Copyright (C) 2018 Igalia, S.L.
+#
+# 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=berto@igalia.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+ rm -f "$TEST_IMG.base"
+ rm -f "$TEST_IMG.int"
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+. ./common.qemu
+
+# Any format implementing BlockDriver.bdrv_change_backing_file
+_supported_fmt qcow2 qed
+_supported_proto file
+_supported_os Linux
+
+IMG_SIZE=1M
+
+# Create the images
+TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE | _filter_imgfmt
+TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" | _filter_imgfmt
+_make_test_img -b "$TEST_IMG.int" | _filter_imgfmt
+
+# First test: reopen $TEST.IMG changing the detect-zeroes option on
+# its backing file ($TEST_IMG.int).
+echo
+echo "*** Change an option on the backing file"
+echo
+_launch_qemu -drive if=none,file="${TEST_IMG}"
+_send_qemu_cmd $QEMU_HANDLE \
+ "{ 'execute': 'qmp_capabilities' }" \
+ 'return'
+
+_send_qemu_cmd $QEMU_HANDLE \
+ "{ 'execute': 'human-monitor-command',
+ 'arguments': { 'command-line':
+ 'qemu-io none0 \"reopen -o backing.detect-zeroes=on\"' } }" \
+ "return"
+
+_cleanup_qemu
+
+# Second test: stream $TEST_IMG.base into $TEST_IMG.int and then
+# reopen $TEST.IMG changing the detect-zeroes option on its new
+# backing file ($TEST_IMG.base).
+echo
+echo "*** Stream and then change an option on the backing file"
+echo
+_launch_qemu -drive if=none,file="${TEST_IMG}"
+_send_qemu_cmd $QEMU_HANDLE \
+ "{ 'execute': 'qmp_capabilities' }" \
+ 'return'
+
+_send_qemu_cmd $QEMU_HANDLE \
+ "{ 'execute': 'block-stream', \
+ 'arguments': { 'device': 'none0',
+ 'base': '${TEST_IMG}.base' } }" \
+ 'return'
+
+# Wait for block-stream to finish
+sleep 0.5
+
+_send_qemu_cmd $QEMU_HANDLE \
+ "{ 'execute': 'human-monitor-command',
+ 'arguments': { 'command-line':
+ 'qemu-io none0 \"reopen -o backing.detect-zeroes=on\"' } }" \
+ "return"
+
+_cleanup_qemu
+
+# Third test: commit $TEST_IMG.int into $TEST_IMG.base and then reopen
+# $TEST.IMG changing the detect-zeroes option on its new backing file
+# ($TEST_IMG.base).
+echo
+echo "*** Commit and then change an option on the backing file"
+echo
+# Create the images again
+TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE | _filter_imgfmt
+TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" | _filter_imgfmt
+_make_test_img -b "$TEST_IMG.int" | _filter_imgfmt
+
+_launch_qemu -drive if=none,file="${TEST_IMG}"
+_send_qemu_cmd $QEMU_HANDLE \
+ "{ 'execute': 'qmp_capabilities' }" \
+ 'return'
+
+_send_qemu_cmd $QEMU_HANDLE \
+ "{ 'execute': 'block-commit', \
+ 'arguments': { 'device': 'none0',
+ 'top': '${TEST_IMG}.int' } }" \
+ 'return'
+
+# Wait for block-commit to finish
+sleep 0.5
+
+_send_qemu_cmd $QEMU_HANDLE \
+ "{ 'execute': 'human-monitor-command',
+ 'arguments': { 'command-line':
+ 'qemu-io none0 \"reopen -o backing.detect-zeroes=on\"' } }" \
+ "return"
+
+_cleanup_qemu
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/161.out b/tests/qemu-iotests/161.out
new file mode 100644
index 0000000000..39951993ee
--- /dev/null
+++ b/tests/qemu-iotests/161.out
@@ -0,0 +1,39 @@
+QA output created by 161
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576
+Formatting 'TEST_DIR/t.IMGFMT.int', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.int
+
+*** Change an option on the backing file
+
+{"return": {}}
+{"return": ""}
+
+*** Stream and then change an option on the backing file
+
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "none0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "none0"}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "none0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "none0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "none0", "len": 1048576, "offset": 1048576, "speed": 0, "type": "stream"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "none0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "none0"}}
+{"return": ""}
+
+*** Commit and then change an option on the backing file
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576
+Formatting 'TEST_DIR/t.IMGFMT.int', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.int
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "none0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "none0"}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "none0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "none0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "none0", "len": 1048576, "offset": 1048576, "speed": 0, "type": "commit"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "none0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "none0"}}
+{"return": ""}
+*** done
diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223
index 72419e0338..397b865d34 100755
--- a/tests/qemu-iotests/223
+++ b/tests/qemu-iotests/223
@@ -57,10 +57,11 @@ run_qemu()
}
echo
-echo "=== Create partially sparse image, then add dirty bitmap ==="
+echo "=== Create partially sparse image, then add dirty bitmaps ==="
echo
-_make_test_img 4M
+# Two bitmaps, to contrast granularity issues
+_make_test_img -o cluster_size=4k 4M
$QEMU_IO -c 'w -P 0x11 1M 2M' "$TEST_IMG" | _filter_qemu_io
run_qemu <<EOF
{ "execute": "qmp_capabilities" }
@@ -78,7 +79,16 @@ run_qemu <<EOF
"arguments": {
"node": "n",
"name": "b",
- "persistent": true
+ "persistent": true,
+ "granularity": 65536
+ }
+}
+{ "execute": "block-dirty-bitmap-add",
+ "arguments": {
+ "node": "n",
+ "name": "b2",
+ "persistent": true,
+ "granularity": 512
}
}
{ "execute": "quit" }
@@ -88,10 +98,11 @@ echo
echo "=== Write part of the file under active bitmap ==="
echo
-$QEMU_IO -c 'w -P 0x22 2M 2M' "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c 'w -P 0x22 512 512' -c 'w -P 0x33 2M 2M' "$TEST_IMG" \
+ | _filter_qemu_io
echo
-echo "=== End dirty bitmap, and start serving image over NBD ==="
+echo "=== End dirty bitmaps, and start serving image over NBD ==="
echo
_launch_qemu 2> >(_filter_nbd)
@@ -103,6 +114,8 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add",
"file":{"driver":"file", "filename":"'"$TEST_IMG"'"}}}' "return"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable",
"arguments":{"node":"n", "name":"b"}}' "return"
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable",
+ "arguments":{"node":"n", "name":"b2"}}' "return"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start",
"arguments":{"addr":{"type":"unix",
"data":{"path":"'"$TEST_DIR/nbd"'"}}}}' "return"
@@ -110,26 +123,40 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
"arguments":{"device":"n"}}' "return"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap",
"arguments":{"name":"n", "bitmap":"b"}}' "return"
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
+ "arguments":{"device":"n", "name":"n2"}}' "return"
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap",
+ "arguments":{"name":"n2", "bitmap":"b2"}}' "return"
echo
-echo "=== Contrast normal status with dirty-bitmap status ==="
+echo "=== Contrast normal status to large granularity dirty-bitmap ==="
echo
QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT
IMG="driver=nbd,export=n,server.type=unix,server.path=$TEST_DIR/nbd"
-$QEMU_IO -r -c 'r -P 0 0 1m' -c 'r -P 0x11 1m 1m' \
- -c 'r -P 0x22 2m 2m' --image-opts "$IMG" | _filter_qemu_io
+$QEMU_IO -r -c 'r -P 0x22 512 512' -c 'r -P 0 512k 512k' -c 'r -P 0x11 1m 1m' \
+ -c 'r -P 0x33 2m 2m' --image-opts "$IMG" | _filter_qemu_io
$QEMU_IMG map --output=json --image-opts \
"$IMG" | _filter_qemu_img_map
$QEMU_IMG map --output=json --image-opts \
"$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b" | _filter_qemu_img_map
echo
+echo "=== Contrast to small granularity dirty-bitmap ==="
+echo
+
+IMG="driver=nbd,export=n2,server.type=unix,server.path=$TEST_DIR/nbd"
+$QEMU_IMG map --output=json --image-opts \
+ "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b2" | _filter_qemu_img_map
+
+echo
echo "=== End NBD server ==="
echo
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove",
"arguments":{"name":"n"}}' "return"
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove",
+ "arguments":{"name":"n2"}}' "return"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "return"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return"
diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out
index 33021c8e6a..de417477de 100644
--- a/tests/qemu-iotests/223.out
+++ b/tests/qemu-iotests/223.out
@@ -1,6 +1,6 @@
QA output created by 223
-=== Create partially sparse image, then add dirty bitmap ===
+=== Create partially sparse image, then add dirty bitmaps ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304
wrote 2097152/2097152 bytes at offset 1048576
@@ -11,15 +11,18 @@ QMP_VERSION
{"return": {}}
{"return": {}}
{"return": {}}
+{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
=== Write part of the file under active bitmap ===
+wrote 512/512 bytes at offset 512
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 2097152/2097152 bytes at offset 2097152
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-=== End dirty bitmap, and start serving image over NBD ===
+=== End dirty bitmaps, and start serving image over NBD ===
{"return": {}}
{"return": {}}
@@ -27,18 +30,32 @@ wrote 2097152/2097152 bytes at offset 2097152
{"return": {}}
{"return": {}}
{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
-=== Contrast normal status with dirty-bitmap status ===
+=== Contrast normal status to large granularity dirty-bitmap ===
-read 1048576/1048576 bytes at offset 0
-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 512
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 524288/524288 bytes at offset 524288
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 1048576/1048576 bytes at offset 1048576
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 2097152/2097152 bytes at offset 2097152
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-[{ "start": 0, "length": 1048576, "depth": 0, "zero": true, "data": false},
+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true},
+{ "start": 4096, "length": 1044480, "depth": 0, "zero": true, "data": false},
{ "start": 1048576, "length": 3145728, "depth": 0, "zero": false, "data": true}]
-[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true},
+[{ "start": 0, "length": 65536, "depth": 0, "zero": false, "data": false},
+{ "start": 65536, "length": 2031616, "depth": 0, "zero": false, "data": true},
+{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}]
+
+=== Contrast to small granularity dirty-bitmap ===
+
+[{ "start": 0, "length": 512, "depth": 0, "zero": false, "data": true},
+{ "start": 512, "length": 512, "depth": 0, "zero": false, "data": false},
+{ "start": 1024, "length": 2096128, "depth": 0, "zero": false, "data": true},
{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}]
=== End NBD server ===
@@ -46,4 +63,5 @@ read 2097152/2097152 bytes at offset 2097152
{"return": {}}
{"return": {}}
{"return": {}}
+{"return": {}}
*** done
diff --git a/tests/qemu-iotests/233 b/tests/qemu-iotests/233
index a4da60d0ad..1814efe333 100755
--- a/tests/qemu-iotests/233
+++ b/tests/qemu-iotests/233
@@ -66,7 +66,7 @@ $QEMU_IO -c 'w -P 0x11 1m 1m' "$TEST_IMG" | _filter_qemu_io
echo
echo "== check TLS client to plain server fails =="
-nbd_server_start_tcp_socket "$TEST_IMG"
+nbd_server_start_tcp_socket -f $IMGFMT "$TEST_IMG"
$QEMU_IMG info --image-opts \
--object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \
@@ -78,7 +78,10 @@ nbd_server_stop
echo
echo "== check plain client to TLS server fails =="
-nbd_server_start_tcp_socket --object tls-creds-x509,dir=${tls_dir}/server1,endpoint=server,id=tls0,verify-peer=yes --tls-creds tls0 "$TEST_IMG"
+nbd_server_start_tcp_socket \
+ --object tls-creds-x509,dir=${tls_dir}/server1,endpoint=server,id=tls0,verify-peer=yes \
+ --tls-creds tls0 \
+ -f $IMGFMT "$TEST_IMG"
$QEMU_IMG info nbd://localhost:$nbd_tcp_port 2>&1 | sed "s/$nbd_tcp_port/PORT/g"
@@ -104,7 +107,7 @@ $QEMU_IO -c 'r -P 0x11 1m 1m' -c 'w -P 0x22 1m 1m' --image-opts \
driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \
2>&1 | _filter_qemu_io
-$QEMU_IO -f qcow2 -r -U -c 'r -P 0x22 1m 1m' "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -f $IMGFMT -r -U -c 'r -P 0x22 1m 1m' "$TEST_IMG" | _filter_qemu_io
# success, all done
echo "*** done"
diff --git a/tests/qemu-iotests/common.tls b/tests/qemu-iotests/common.tls
index 39f17c1b99..eae81789bb 100644
--- a/tests/qemu-iotests/common.tls
+++ b/tests/qemu-iotests/common.tls
@@ -31,6 +31,9 @@ tls_x509_cleanup()
tls_x509_init()
{
+ (certtool --help) >/dev/null 2>&1 || \
+ _notrun "certtool utility not found, skipping test"
+
mkdir -p "${tls_dir}"
# use a fixed key so we don't waste system entropy on
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 2722103381..ddf1a5b549 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -167,6 +167,7 @@
158 rw auto quick
159 rw auto quick
160 rw auto quick
+161 rw auto quick
162 auto quick
163 rw auto
165 rw auto quick
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 27bb2b600c..d537538ba0 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -581,7 +581,7 @@ class QMPTestCase(unittest.TestCase):
def wait_ready_and_cancel(self, drive='drive0'):
self.wait_ready(drive=drive)
event = self.cancel_and_wait(drive=drive)
- self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED')
+ self.assertEqual(event['event'], 'BLOCK_JOB_COMPLETED')
self.assert_qmp(event, 'data/type', 'mirror')
self.assert_qmp(event, 'data/offset', event['data']['len'])