Block layer patches:

- block: Fix update of BDRV_O_AUTO_RDONLY in update_flags_from_options()
 - block: Fix option inheritance after stream/commit job graph changes
 - qemu-img: Fix memory leak and typo in error message
 - nvme: Fixes for lockups and crashes
 - scsi-disk: Fix crash if underlying host file or disk returns error
 - Several qemu-iotests fixes and improvements
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJb9vemAAoJEH8JsnLIjy/WWJQQAKiW7/Ku1i4DgZz6n97+HumE
 CryEgO/Hx9YnQPJPcEWNQVNtdO311rJSprLkmt83tOwQ1ew9lvqdcq9ptEUb4dW9
 9bV31nP2nRkR3/cyLgWuYALH3Y6eSMwLQiYb0DaWKL2sxiMdghvK3gALXb3r+AtI
 F2uuQC6N6xNKj5srrTcxMman22QYIqkeuONWY9La9mQWG44WI5Gc+mF1LKMNk7x9
 bBsvP5+ukm3O4+vvGylZjOXYpCgwQngSLvGG/rsHwBTBrDQiy3m1JoxxXzqr+Z4K
 p2b5wU2vFgfJ6cDGZJQWHQ/XAH3njmuPJEg+DOe7SUuGio2gQZCHlHU0xtp0GrQh
 BkYMfRUfb+rJ/t/mamX1y45XSBdvR0hJkgdgZYeHbFfV80Do63GqfqzOCDkKBocg
 /cbcDAvLiztoQpSa3aLMCHobs2X4Jn41ODUEidRbWsn3W099R2vIAUnxht/RI8Dm
 A5a7zfzXgMyEeXywxdCGM2k2oQsDnZoZLqNYJjkVmn3eqSIt3aLaSWtX0U7EVbEC
 PwNXAhz0Gl1YFhkgzEd8qkNH60lUb7Lone+3471iKACY0gjEeN5Ljsv/+HhaTQi/
 a5HTAf+eUUO9OJt3DNE4pbUORDH0XMZBb3vTqfqf9a0iKWxXnuX25xm5YfVyagDi
 54ZVHTqL+4zskS7uHD1O
 =dCUx
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block layer patches:

- block: Fix update of BDRV_O_AUTO_RDONLY in update_flags_from_options()
- block: Fix option inheritance after stream/commit job graph changes
- qemu-img: Fix memory leak and typo in error message
- nvme: Fixes for lockups and crashes
- scsi-disk: Fix crash if underlying host file or disk returns error
- Several qemu-iotests fixes and improvements

# gpg: Signature made Thu 22 Nov 2018 18:38:30 GMT
# gpg:                using RSA key 7F09B272C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* remotes/kevin/tags/for-upstream:
  block: Update BlockDriverState.inherits_from on bdrv_drop_intermediate()
  block: Update BlockDriverState.inherits_from on bdrv_set_backing_hd()
  iotests: Enhance 223 to cover multiple bitmap granularities
  nvme: fix bug with PCI IRQ pins on teardown
  nvme: fix CMB endianness confusion
  Revert "nvme: fix oob access issue(CVE-2018-16847)"
  nvme: fix out-of-bounds access to the CMB
  nvme: call blk_drain in NVMe reset code to avoid lockups
  iotests: fix nbd test 233 to work correctly with raw images
  block: Fix update of BDRV_O_AUTO_RDONLY in update_flags_from_options()
  scsi-disk: Fix crash if underlying host file or disk returns error
  qemu-img: Fix leak
  qemu-img: Fix typo
  iotests: Skip 233 if certtool not installed
  iotests: Replace assertEquals() with assertEqual()
  iotests: Replace time.clock() with Timeout

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-11-23 08:54:52 +00:00
commit 5298f4d67a
16 changed files with 364 additions and 63 deletions

41
block.c
View File

@ -1137,7 +1137,7 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
static void update_flags_from_options(int *flags, QemuOpts *opts) 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)); assert(qemu_opt_find(opts, BDRV_OPT_CACHE_NO_FLUSH));
if (qemu_opt_get_bool_del(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) { 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_NOCACHE;
} }
*flags &= ~BDRV_O_RDWR;
assert(qemu_opt_find(opts, BDRV_OPT_READ_ONLY)); assert(qemu_opt_find(opts, BDRV_OPT_READ_ONLY));
if (!qemu_opt_get_bool_del(opts, BDRV_OPT_READ_ONLY, false)) { if (!qemu_opt_get_bool_del(opts, BDRV_OPT_READ_ONLY, false)) {
*flags |= BDRV_O_RDWR; *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 * 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(). * 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, void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
Error **errp) Error **errp)
{ {
bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) &&
bdrv_inherits_from_recursive(backing_hd, bs);
if (backing_hd) { if (backing_hd) {
bdrv_ref(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, bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing,
errp); 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) { if (!bs->backing) {
bdrv_unref(backing_hd); bdrv_unref(backing_hd);
} }
@ -3836,6 +3855,8 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs)
int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
const char *backing_file_str) const char *backing_file_str)
{ {
BlockDriverState *explicit_top = top;
bool update_inherits_from;
BdrvChild *c, *next; BdrvChild *c, *next;
Error *local_err = NULL; Error *local_err = NULL;
int ret = -EIO; int ret = -EIO;
@ -3851,6 +3872,16 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
goto exit; 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 */ /* success - we can delete the intermediate states, and link top->base */
/* TODO Check graph modification op blockers (BLK_PERM_GRAPH_MOD) once /* TODO Check graph modification op blockers (BLK_PERM_GRAPH_MOD) once
* we've figured out how they should work. */ * we've figured out how they should work. */
@ -3886,6 +3917,10 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
bdrv_unref(top); bdrv_unref(top);
} }
if (update_inherits_from) {
base->inherits_from = explicit_top->inherits_from;
}
ret = 0; ret = 0;
exit: exit:
bdrv_unref(top); bdrv_unref(top);

View File

@ -554,6 +554,7 @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd)
trace_nvme_err_invalid_del_cq_notempty(qid); trace_nvme_err_invalid_del_cq_notempty(qid);
return NVME_INVALID_QUEUE_DEL; return NVME_INVALID_QUEUE_DEL;
} }
nvme_irq_deassert(n, cq);
trace_nvme_del_cq(qid); trace_nvme_del_cq(qid);
nvme_free_cq(cq, n); nvme_free_cq(cq, n);
return NVME_SUCCESS; return NVME_SUCCESS;
@ -797,6 +798,8 @@ static void nvme_clear_ctrl(NvmeCtrl *n)
{ {
int i; int i;
blk_drain(n->conf.blk);
for (i = 0; i < n->num_queues; i++) { for (i = 0; i < n->num_queues; i++) {
if (n->sq[i] != NULL) { if (n->sq[i] != NULL) {
nvme_free_sq(n->sq[i], n); 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) unsigned size)
{ {
NvmeCtrl *n = (NvmeCtrl *)opaque; NvmeCtrl *n = (NvmeCtrl *)opaque;
stn_le_p(&n->cmbuf[addr], size, data);
if (addr + size > NVME_CMBSZ_GETSIZE(n->bar.cmbsz)) {
return;
}
memcpy(&n->cmbuf[addr], &data, size);
} }
static uint64_t nvme_cmb_read(void *opaque, hwaddr addr, unsigned size) static uint64_t nvme_cmb_read(void *opaque, hwaddr addr, unsigned size)
{ {
uint64_t val;
NvmeCtrl *n = (NvmeCtrl *)opaque; NvmeCtrl *n = (NvmeCtrl *)opaque;
return ldn_le_p(&n->cmbuf[addr], size);
if (addr + size > NVME_CMBSZ_GETSIZE(n->bar.cmbsz)) {
return 0;
}
memcpy(&val, &n->cmbuf[addr], size);
return val;
} }
static const MemoryRegionOps nvme_cmb_ops = { static const MemoryRegionOps nvme_cmb_ops = {
@ -1199,7 +1192,7 @@ static const MemoryRegionOps nvme_cmb_ops = {
.write = nvme_cmb_write, .write = nvme_cmb_write,
.endianness = DEVICE_LITTLE_ENDIAN, .endianness = DEVICE_LITTLE_ENDIAN,
.impl = { .impl = {
.min_access_size = 2, .min_access_size = 1,
.max_access_size = 8, .max_access_size = 8,
}, },
}; };

View File

@ -482,7 +482,7 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed)
if (action == BLOCK_ERROR_ACTION_STOP) { if (action == BLOCK_ERROR_ACTION_STOP) {
scsi_req_retry(&r->req); scsi_req_retry(&r->req);
} }
return false; return true;
} }
static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) static void scsi_write_complete_noio(SCSIDiskReq *r, int ret)

View File

@ -261,8 +261,9 @@ static int print_block_option_help(const char *filename, const char *fmt)
return 1; return 1;
} }
if (!proto_drv->create_opts) { 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); proto_drv->format_name);
qemu_opts_free(create_opts);
return 1; return 1;
} }
create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); create_opts = qemu_opts_append(create_opts, proto_drv->create_opts);

View File

@ -730,7 +730,7 @@ tests/test-hmp$(EXESUF): tests/test-hmp.o
tests/machine-none-test$(EXESUF): tests/machine-none-test.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/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/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/pvpanic-test$(EXESUF): tests/pvpanic-test.o
tests/i82801b11-test$(EXESUF): tests/i82801b11-test.o tests/i82801b11-test$(EXESUF): tests/i82801b11-test.o
tests/ac97-test$(EXESUF): tests/ac97-test.o tests/ac97-test$(EXESUF): tests/ac97-test.o

View File

@ -8,25 +8,73 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/units.h"
#include "libqtest.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) static void nop(void)
{ {
QOSState *qs;
qs = qnvme_start(NULL);
qnvme_stop(qs);
}
static void nvmetest_cmb_test(void)
{
const int cmb_bar_size = 2 * MiB;
QOSState *qs;
QPCIDevice *pdev;
QPCIBar bar;
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);
qnvme_stop(qs);
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int ret;
g_test_init(&argc, &argv, NULL); g_test_init(&argc, &argv, NULL);
qtest_add_func("/nvme/nop", nop); qtest_add_func("/nvme/nop", nop);
qtest_add_func("/nvme/cmb_test", nvmetest_cmb_test);
qtest_start("-drive id=drv0,if=none,file=null-co://,format=raw " return g_test_run();
"-device nvme,drive=drv0,serial=foo");
ret = g_test_run();
qtest_end();
return ret;
} }

View File

@ -469,7 +469,7 @@ new_state = "1"
self.assert_qmp(event, 'data/id', 'drive0') self.assert_qmp(event, 'data/id', 'drive0')
event = self.vm.get_qmp_event(wait=True) 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/device', 'drive0')
self.assert_qmp(event, 'data/operation', 'read') self.assert_qmp(event, 'data/operation', 'read')
result = self.vm.qmp('query-block-jobs') result = self.vm.qmp('query-block-jobs')
@ -494,7 +494,7 @@ new_state = "1"
self.assert_qmp(event, 'data/id', 'drive0') self.assert_qmp(event, 'data/id', 'drive0')
event = self.vm.get_qmp_event(wait=True) 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/device', 'drive0')
self.assert_qmp(event, 'data/operation', 'read') self.assert_qmp(event, 'data/operation', 'read')
result = self.vm.qmp('query-block-jobs') result = self.vm.qmp('query-block-jobs')
@ -625,7 +625,7 @@ new_state = "1"
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
event = self.vm.event_wait(name='BLOCK_JOB_ERROR') 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/device', 'drive0')
self.assert_qmp(event, 'data/operation', 'write') self.assert_qmp(event, 'data/operation', 'write')
result = self.vm.qmp('query-block-jobs') result = self.vm.qmp('query-block-jobs')

View File

@ -53,21 +53,17 @@ class ChangeBaseClass(iotests.QMPTestCase):
if not self.has_real_tray: if not self.has_real_tray:
return return
timeout = time.clock() + 3 with iotests.Timeout(3, 'Timeout while waiting for the tray to open'):
while not self.has_opened and time.clock() < timeout: while not self.has_opened:
self.process_events() self.process_events()
if not self.has_opened:
self.fail('Timeout while waiting for the tray to open')
def wait_for_close(self): def wait_for_close(self):
if not self.has_real_tray: if not self.has_real_tray:
return return
timeout = time.clock() + 3 with iotests.Timeout(3, 'Timeout while waiting for the tray to close'):
while not self.has_closed and time.clock() < timeout: while not self.has_closed:
self.process_events() self.process_events()
if not self.has_opened:
self.fail('Timeout while waiting for the tray to close')
class GeneralChangeTestsBaseClass(ChangeBaseClass): class GeneralChangeTestsBaseClass(ChangeBaseClass):
@ -265,7 +261,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
result = self.vm.qmp('blockdev-close-tray', id=self.device_name) result = self.vm.qmp('blockdev-close-tray', id=self.device_name)
# Should be a no-op # Should be a no-op
self.assert_qmp(result, 'return', {}) 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): def test_remove_on_closed(self):
if not self.has_real_tray: if not self.has_real_tray:
@ -452,7 +448,7 @@ class TestChangeReadOnly(ChangeBaseClass):
read_only_mode='retain') read_only_mode='retain')
self.assert_qmp(result, 'error/class', 'GenericError') 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') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/inserted/ro', False) self.assert_qmp(result, 'return[0]/inserted/ro', False)

137
tests/qemu-iotests/161 Executable file
View File

@ -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

View File

@ -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

View File

@ -57,10 +57,11 @@ run_qemu()
} }
echo echo
echo "=== Create partially sparse image, then add dirty bitmap ===" echo "=== Create partially sparse image, then add dirty bitmaps ==="
echo 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 $QEMU_IO -c 'w -P 0x11 1M 2M' "$TEST_IMG" | _filter_qemu_io
run_qemu <<EOF run_qemu <<EOF
{ "execute": "qmp_capabilities" } { "execute": "qmp_capabilities" }
@ -78,7 +79,16 @@ run_qemu <<EOF
"arguments": { "arguments": {
"node": "n", "node": "n",
"name": "b", "name": "b",
"persistent": true "persistent": true,
"granularity": 65536
}
}
{ "execute": "block-dirty-bitmap-add",
"arguments": {
"node": "n",
"name": "b2",
"persistent": true,
"granularity": 512
} }
} }
{ "execute": "quit" } { "execute": "quit" }
@ -88,10 +98,11 @@ echo
echo "=== Write part of the file under active bitmap ===" echo "=== Write part of the file under active bitmap ==="
echo 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
echo "=== End dirty bitmap, and start serving image over NBD ===" echo "=== End dirty bitmaps, and start serving image over NBD ==="
echo echo
_launch_qemu 2> >(_filter_nbd) _launch_qemu 2> >(_filter_nbd)
@ -103,6 +114,8 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add",
"file":{"driver":"file", "filename":"'"$TEST_IMG"'"}}}' "return" "file":{"driver":"file", "filename":"'"$TEST_IMG"'"}}}' "return"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable", _send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable",
"arguments":{"node":"n", "name":"b"}}' "return" "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", _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start",
"arguments":{"addr":{"type":"unix", "arguments":{"addr":{"type":"unix",
"data":{"path":"'"$TEST_DIR/nbd"'"}}}}' "return" "data":{"path":"'"$TEST_DIR/nbd"'"}}}}' "return"
@ -110,26 +123,40 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
"arguments":{"device":"n"}}' "return" "arguments":{"device":"n"}}' "return"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap", _send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap",
"arguments":{"name":"n", "bitmap":"b"}}' "return" "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
echo "=== Contrast normal status with dirty-bitmap status ===" echo "=== Contrast normal status to large granularity dirty-bitmap ==="
echo echo
QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT
IMG="driver=nbd,export=n,server.type=unix,server.path=$TEST_DIR/nbd" 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' \ $QEMU_IO -r -c 'r -P 0x22 512 512' -c 'r -P 0 512k 512k' -c 'r -P 0x11 1m 1m' \
-c 'r -P 0x22 2m 2m' --image-opts "$IMG" | _filter_qemu_io -c 'r -P 0x33 2m 2m' --image-opts "$IMG" | _filter_qemu_io
$QEMU_IMG map --output=json --image-opts \ $QEMU_IMG map --output=json --image-opts \
"$IMG" | _filter_qemu_img_map "$IMG" | _filter_qemu_img_map
$QEMU_IMG map --output=json --image-opts \ $QEMU_IMG map --output=json --image-opts \
"$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b" | _filter_qemu_img_map "$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
echo "=== End NBD server ===" echo "=== End NBD server ==="
echo echo
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove",
"arguments":{"name":"n"}}' "return" "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":"nbd-server-stop"}' "return"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return" _send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return"

View File

@ -1,6 +1,6 @@
QA output created by 223 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 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304
wrote 2097152/2097152 bytes at offset 1048576 wrote 2097152/2097152 bytes at offset 1048576
@ -11,15 +11,18 @@ QMP_VERSION
{"return": {}} {"return": {}}
{"return": {}} {"return": {}}
{"return": {}} {"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
=== Write part of the file under active bitmap === === 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 wrote 2097152/2097152 bytes at offset 2097152
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 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": {}}
{"return": {}} {"return": {}}
@ -27,18 +30,32 @@ wrote 2097152/2097152 bytes at offset 2097152
{"return": {}} {"return": {}}
{"return": {}} {"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 read 512/512 bytes at offset 512
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 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 read 1048576/1048576 bytes at offset 1048576
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 2097152/2097152 bytes at offset 2097152 read 2097152/2097152 bytes at offset 2097152
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 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": 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}] { "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}]
=== End NBD server === === End NBD server ===
@ -46,4 +63,5 @@ read 2097152/2097152 bytes at offset 2097152
{"return": {}} {"return": {}}
{"return": {}} {"return": {}}
{"return": {}} {"return": {}}
{"return": {}}
*** done *** done

View File

@ -66,7 +66,7 @@ $QEMU_IO -c 'w -P 0x11 1m 1m' "$TEST_IMG" | _filter_qemu_io
echo echo
echo "== check TLS client to plain server fails ==" 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 \ $QEMU_IMG info --image-opts \
--object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \ --object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \
@ -78,7 +78,10 @@ nbd_server_stop
echo echo
echo "== check plain client to TLS server fails ==" 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" $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 \ driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \
2>&1 | _filter_qemu_io 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 # success, all done
echo "*** done" echo "*** done"

View File

@ -31,6 +31,9 @@ tls_x509_cleanup()
tls_x509_init() tls_x509_init()
{ {
(certtool --help) >/dev/null 2>&1 || \
_notrun "certtool utility not found, skipping test"
mkdir -p "${tls_dir}" mkdir -p "${tls_dir}"
# use a fixed key so we don't waste system entropy on # use a fixed key so we don't waste system entropy on

View File

@ -167,6 +167,7 @@
158 rw auto quick 158 rw auto quick
159 rw auto quick 159 rw auto quick
160 rw auto quick 160 rw auto quick
161 rw auto quick
162 auto quick 162 auto quick
163 rw auto 163 rw auto
165 rw auto quick 165 rw auto quick

View File

@ -581,7 +581,7 @@ class QMPTestCase(unittest.TestCase):
def wait_ready_and_cancel(self, drive='drive0'): def wait_ready_and_cancel(self, drive='drive0'):
self.wait_ready(drive=drive) self.wait_ready(drive=drive)
event = self.cancel_and_wait(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/type', 'mirror')
self.assert_qmp(event, 'data/offset', event['data']['len']) self.assert_qmp(event, 'data/offset', event['data']['len'])