From 35e32d9e2e8a1128dba75531aa763108c06f25f4 Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Wed, 16 Oct 2019 11:40:39 +0300 Subject: [PATCH 1/7] qapi: add support for blkreplay driver This patch adds support for blkreplay driver to the blockdev options. Now blkreplay can be used with -blockdev command line option in the following format: -blockdev driver=blkreplay,image=file-node-name,node-name=replay-node-name This option makes possible implementation of the better command line support for record/replay invocations. Signed-off-by: Pavel Dovgalyuk Signed-off-by: Kevin Wolf --- qapi/block-core.json | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index b274aef713..aa97ee2641 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2883,12 +2883,13 @@ # @nvme: Since 2.12 # @copy-on-read: Since 3.0 # @blklogwrites: Since 3.0 +# @blkreplay: Since 4.2 # # Since: 2.9 ## { 'enum': 'BlockdevDriver', - 'data': [ 'blkdebug', 'blklogwrites', 'blkverify', 'bochs', 'cloop', - 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster', + 'data': [ 'blkdebug', 'blklogwrites', 'blkreplay', 'blkverify', 'bochs', + 'cloop', 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd', @@ -3501,6 +3502,18 @@ 'data': { 'test': 'BlockdevRef', 'raw': 'BlockdevRef' } } +## +# @BlockdevOptionsBlkreplay: +# +# Driver specific block device options for blkreplay. +# +# @image: disk image which should be controlled with blkreplay +# +# Since: 4.2 +## +{ 'struct': 'BlockdevOptionsBlkreplay', + 'data': { 'image': 'BlockdevRef' } } + ## # @QuorumReadPattern: # @@ -4028,6 +4041,7 @@ 'blkdebug': 'BlockdevOptionsBlkdebug', 'blklogwrites':'BlockdevOptionsBlklogwrites', 'blkverify': 'BlockdevOptionsBlkverify', + 'blkreplay': 'BlockdevOptionsBlkreplay', 'bochs': 'BlockdevOptionsGenericFormat', 'cloop': 'BlockdevOptionsGenericFormat', 'copy-on-read':'BlockdevOptionsGenericFormat', From d926f4ddd284d8d9566937e4deff65f8284db09f Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 18 Oct 2019 13:46:42 +0200 Subject: [PATCH 2/7] iotests: Skip read-only cases in 118 when run as root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some tests in 118 use chmod to remove write permissions from the file and assume that the image can indeed not be opened read-write afterwards. This doesn't work when the test is run as root, because root can still open the file as writable even when the permission bit isn't set. Introduce a @skip_if_root decorator and use it in 118 to skip the tests in question when the script is run as root. Signed-off-by: Kevin Wolf Reviewed-by: Philippe Mathieu-Daudé --- tests/qemu-iotests/118 | 3 +++ tests/qemu-iotests/iotests.py | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118 index ea0b326ae0..e20080e9a6 100755 --- a/tests/qemu-iotests/118 +++ b/tests/qemu-iotests/118 @@ -446,6 +446,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/ro', True) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + @iotests.skip_if_user_is_root def test_rw_ro_retain(self): os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') @@ -530,6 +531,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/ro', True) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + @iotests.skip_if_user_is_root def test_make_ro_rw(self): os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') @@ -571,6 +573,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/ro', True) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + @iotests.skip_if_user_is_root def test_make_ro_rw_by_retain(self): os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 693fde155a..709def4d5d 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -931,6 +931,16 @@ def skip_if_unsupported(required_formats=[], read_only=False): return func_wrapper return skip_test_decorator +def skip_if_user_is_root(func): + '''Skip Test Decorator + Runs the test only without root permissions''' + def func_wrapper(*args, **kwargs): + if os.getuid() == 0: + case_notrun('{}: cannot be run as root'.format(args[0])) + else: + return func(*args, **kwargs) + return func_wrapper + def execute_unittest(output, verbosity, debug): runner = unittest.TextTestRunner(stream=output, descriptions=True, verbosity=verbosity) From 46741111baab169482916d120d8949676ad16a77 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 27 May 2019 18:01:41 +0200 Subject: [PATCH 3/7] blockdev: Use error_report() in hmp_commit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using monitor_printf() to report errors, hmp_commit() should use error_report() like other places do. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Philippe Mathieu-Daudé --- blockdev.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/blockdev.c b/blockdev.c index 03c7cd7651..ba491e3ef5 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1088,11 +1088,11 @@ void hmp_commit(Monitor *mon, const QDict *qdict) blk = blk_by_name(device); if (!blk) { - monitor_printf(mon, "Device '%s' not found\n", device); + error_report("Device '%s' not found", device); return; } if (!blk_is_available(blk)) { - monitor_printf(mon, "Device '%s' has no medium\n", device); + error_report("Device '%s' has no medium", device); return; } @@ -1105,8 +1105,7 @@ void hmp_commit(Monitor *mon, const QDict *qdict) aio_context_release(aio_context); } if (ret < 0) { - monitor_printf(mon, "'commit' error for '%s': %s\n", device, - strerror(-ret)); + error_report("'commit' error for '%s': %s", device, strerror(-ret)); } } From 8ccf458af599673f622d14c8af84f063f69bba2d Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 17 Oct 2019 17:21:22 +0300 Subject: [PATCH 4/7] block/backup: drop dead code from backup_job_create After commit 00e30f05de1d195, there is no more "goto error" points after job creation, so after "error:" @job is always NULL and we don't need roll-back job creation. Reported-by: Coverity (CID 1406402) Signed-off-by: Vladimir Sementsov-Ogievskiy Acked-by: Stefano Garzarella Signed-off-by: Kevin Wolf --- block/backup.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/block/backup.c b/block/backup.c index dddcf77f53..cf62b1a38c 100644 --- a/block/backup.c +++ b/block/backup.c @@ -474,10 +474,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, if (sync_bitmap) { bdrv_reclaim_dirty_bitmap(sync_bitmap, NULL); } - if (job) { - backup_clean(&job->common.job); - job_early_fail(&job->common.job); - } else if (backup_top) { + if (backup_top) { bdrv_backup_top_drop(backup_top); } From c9b749d7bcaf035de4dc32cd6225a2ff51200d7b Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 15 Oct 2019 12:29:58 +0200 Subject: [PATCH 5/7] doc: Describe missing generic -blockdev options We added more generic options after introducing -blockdev and forgot to update the documentation (man page and --help output) accordingly. Do that now. Signed-off-by: Kevin Wolf Reviewed-by: Peter Maydell --- qemu-options.hx | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/qemu-options.hx b/qemu-options.hx index 996b6fba74..19709f973d 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -864,7 +864,8 @@ ETEXI DEF("blockdev", HAS_ARG, QEMU_OPTION_blockdev, "-blockdev [driver=]driver[,node-name=N][,discard=ignore|unmap]\n" " [,cache.direct=on|off][,cache.no-flush=on|off]\n" - " [,read-only=on|off][,detect-zeroes=on|off|unmap]\n" + " [,read-only=on|off][,auto-read-only=on|off]\n" + " [,force-share=on|off][,detect-zeroes=on|off|unmap]\n" " [,driver specific parameters...]\n" " configure a block backend\n", QEMU_ARCH_ALL) STEXI @@ -900,6 +901,25 @@ name is not intended to be predictable and changes between QEMU invocations. For the top level, an explicit node name must be specified. @item read-only Open the node read-only. Guest write attempts will fail. + +Note that some block drivers support only read-only access, either generally or +in certain configurations. In this case, the default value +@option{read-only=off} does not work and the option must be specified +explicitly. +@item auto-read-only +If @option{auto-read-only=on} is set, QEMU may fall back to read-only usage +even when @option{read-only=off} is requested, or even switch between modes as +needed, e.g. depending on whether the image file is writable or whether a +writing user is attached to the node. +@item force-share +Override the image locking system of QEMU by forcing the node to utilize +weaker shared access for permissions where it would normally request exclusive +access. When there is the potential for multiple instances to have the same +file open (whether this invocation of QEMU is the first or the second +instance), both instances must permit shared access for the second instance to +succeed at opening the file. + +Enabling @option{force-share=on} requires @option{read-only=on}. @item cache.direct The host page cache can be avoided with @option{cache.direct=on}. This will attempt to do disk IO directly to the guest's memory. QEMU may still perform an From 944f3d5dd216fcd8cb007eddd4f82dced0a15b3d Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 24 Oct 2019 16:26:57 +0200 Subject: [PATCH 6/7] coroutine: Add qemu_co_mutex_assert_locked() Some functions require that the caller holds a certain CoMutex for them to operate correctly. Add a function so that they can assert the lock is really held. Cc: qemu-stable@nongnu.org Signed-off-by: Kevin Wolf Tested-by: Michael Weiser Reviewed-by: Michael Weiser Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Denis V. Lunev Reviewed-by: Max Reitz --- include/qemu/coroutine.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index 8d55663062..dfd261c5b1 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -167,6 +167,21 @@ void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex); */ void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex); +/** + * Assert that the current coroutine holds @mutex. + */ +static inline coroutine_fn void qemu_co_mutex_assert_locked(CoMutex *mutex) +{ + /* + * mutex->holder doesn't need any synchronisation if the assertion holds + * true because the mutex protects it. If it doesn't hold true, we still + * don't mind if another thread takes or releases mutex behind our back, + * because the condition will be false no matter whether we read NULL or + * the pointer for any other coroutine. + */ + assert(atomic_read(&mutex->locked) && + mutex->holder == qemu_coroutine_self()); +} /** * CoQueues are a mechanism to queue coroutines in order to continue executing From 5e9785505210e2477e590e61b1ab100d0ec22b01 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 24 Oct 2019 16:26:58 +0200 Subject: [PATCH 7/7] qcow2: Fix corruption bug in qcow2_detect_metadata_preallocation() qcow2_detect_metadata_preallocation() calls qcow2_get_refcount() which requires s->lock to be taken to protect its accesses to the refcount table and refcount blocks. However, nothing in this code path actually took the lock. This could cause the same cache entry to be used by two requests at the same time, for different tables at different offsets, resulting in image corruption. As it would be preferable to base the detection on consistent data (even though it's just heuristics), let's take the lock not only around the qcow2_get_refcount() calls, but around the whole function. This patch takes the lock in qcow2_co_block_status() earlier and asserts in qcow2_detect_metadata_preallocation() that we hold the lock. Fixes: 69f47505ee66afaa513305de0c1895a224e52c45 Cc: qemu-stable@nongnu.org Reported-by: Michael Weiser Signed-off-by: Kevin Wolf Tested-by: Michael Weiser Reviewed-by: Michael Weiser Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Max Reitz --- block/qcow2-refcount.c | 2 ++ block/qcow2.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index ef965d7895..0d64bf5a5e 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -3455,6 +3455,8 @@ int qcow2_detect_metadata_preallocation(BlockDriverState *bs) int64_t i, end_cluster, cluster_count = 0, threshold; int64_t file_length, real_allocation, real_clusters; + qemu_co_mutex_assert_locked(&s->lock); + file_length = bdrv_getlength(bs->file->bs); if (file_length < 0) { return file_length; diff --git a/block/qcow2.c b/block/qcow2.c index 8b05933565..0bc69e6996 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1916,6 +1916,8 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, unsigned int bytes; int status = 0; + qemu_co_mutex_lock(&s->lock); + if (!s->metadata_preallocation_checked) { ret = qcow2_detect_metadata_preallocation(bs); s->metadata_preallocation = (ret == 1); @@ -1923,7 +1925,6 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, } bytes = MIN(INT_MAX, count); - qemu_co_mutex_lock(&s->lock); ret = qcow2_get_cluster_offset(bs, offset, &bytes, &cluster_offset); qemu_co_mutex_unlock(&s->lock); if (ret < 0) {