summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--block/backup.c5
-rw-r--r--block/qcow2-refcount.c2
-rw-r--r--block/qcow2.c3
-rw-r--r--blockdev.c7
-rw-r--r--include/qemu/coroutine.h15
-rw-r--r--qapi/block-core.json18
-rw-r--r--qemu-options.hx22
-rwxr-xr-xtests/qemu-iotests/1183
-rw-r--r--tests/qemu-iotests/iotests.py10
9 files changed, 73 insertions, 12 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);
}
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) {
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));
}
}
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
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',
@@ -3502,6 +3503,18 @@
'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:
#
# An enumeration of quorum read patterns.
@@ -4028,6 +4041,7 @@
'blkdebug': 'BlockdevOptionsBlkdebug',
'blklogwrites':'BlockdevOptionsBlklogwrites',
'blkverify': 'BlockdevOptionsBlkverify',
+ 'blkreplay': 'BlockdevOptionsBlkreplay',
'bochs': 'BlockdevOptionsGenericFormat',
'cloop': 'BlockdevOptionsGenericFormat',
'copy-on-read':'BlockdevOptionsGenericFormat',
diff --git a/qemu-options.hx b/qemu-options.hx
index 88b05599b1..b95bf9fbed 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
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)