summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--block.c2
-rw-r--r--block/qcow2.c104
-rw-r--r--block/vmdk.c56
-rw-r--r--docs/interop/vhost-user.rst17
-rw-r--r--docs/tools/qemu-img.rst4
-rw-r--r--hw/virtio/vhost-user.c2
-rw-r--r--include/block/block-global-state.h1
-rw-r--r--include/block/block-io.h1
-rw-r--r--include/qemu/main-loop.h3
-rw-r--r--subprojects/libvhost-user/libvhost-user.c17
-rwxr-xr-xtests/qemu-iotests/tests/export-incoming-iothread81
-rw-r--r--tests/qemu-iotests/tests/export-incoming-iothread.out5
-rwxr-xr-xtests/qemu-iotests/tests/reopen-file89
-rw-r--r--tests/qemu-iotests/tests/reopen-file.out5
-rw-r--r--util/coroutine-ucontext.c38
-rw-r--r--util/coroutine-win32.c18
-rw-r--r--util/qemu-coroutine.c41
17 files changed, 388 insertions, 96 deletions
diff --git a/block.c b/block.c
index 8cd16e757e..2c00dddd80 100644
--- a/block.c
+++ b/block.c
@@ -6298,7 +6298,7 @@ const char *bdrv_get_device_or_node_name(const BlockDriverState *bs)
int bdrv_get_flags(BlockDriverState *bs)
{
- GLOBAL_STATE_CODE();
+ IO_CODE();
return bs->open_flags;
}
diff --git a/block/qcow2.c b/block/qcow2.c
index b5c47931ef..4f5e6440fb 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1296,7 +1296,8 @@ static int validate_compression_type(BDRVQcow2State *s, Error **errp)
/* Called with s->lock held. */
static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
- int flags, Error **errp)
+ int flags, bool open_data_file,
+ Error **errp)
{
ERRP_GUARD();
BDRVQcow2State *s = bs->opaque;
@@ -1614,50 +1615,52 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
goto fail;
}
- /* Open external data file */
- s->data_file = bdrv_open_child(NULL, options, "data-file", bs,
- &child_of_bds, BDRV_CHILD_DATA,
- true, errp);
- if (*errp) {
- ret = -EINVAL;
- goto fail;
- }
+ if (open_data_file) {
+ /* Open external data file */
+ s->data_file = bdrv_open_child(NULL, options, "data-file", bs,
+ &child_of_bds, BDRV_CHILD_DATA,
+ true, errp);
+ if (*errp) {
+ ret = -EINVAL;
+ goto fail;
+ }
- if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) {
- if (!s->data_file && s->image_data_file) {
- s->data_file = bdrv_open_child(s->image_data_file, options,
- "data-file", bs, &child_of_bds,
- BDRV_CHILD_DATA, false, errp);
+ if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) {
+ if (!s->data_file && s->image_data_file) {
+ s->data_file = bdrv_open_child(s->image_data_file, options,
+ "data-file", bs, &child_of_bds,
+ BDRV_CHILD_DATA, false, errp);
+ if (!s->data_file) {
+ ret = -EINVAL;
+ goto fail;
+ }
+ }
if (!s->data_file) {
+ error_setg(errp, "'data-file' is required for this image");
ret = -EINVAL;
goto fail;
}
- }
- if (!s->data_file) {
- error_setg(errp, "'data-file' is required for this image");
- ret = -EINVAL;
- goto fail;
- }
- /* No data here */
- bs->file->role &= ~BDRV_CHILD_DATA;
+ /* No data here */
+ bs->file->role &= ~BDRV_CHILD_DATA;
- /* Must succeed because we have given up permissions if anything */
- bdrv_child_refresh_perms(bs, bs->file, &error_abort);
- } else {
- if (s->data_file) {
- error_setg(errp, "'data-file' can only be set for images with an "
- "external data file");
- ret = -EINVAL;
- goto fail;
- }
+ /* Must succeed because we have given up permissions if anything */
+ bdrv_child_refresh_perms(bs, bs->file, &error_abort);
+ } else {
+ if (s->data_file) {
+ error_setg(errp, "'data-file' can only be set for images with "
+ "an external data file");
+ ret = -EINVAL;
+ goto fail;
+ }
- s->data_file = bs->file;
+ s->data_file = bs->file;
- if (data_file_is_raw(bs)) {
- error_setg(errp, "data-file-raw requires a data file");
- ret = -EINVAL;
- goto fail;
+ if (data_file_is_raw(bs)) {
+ error_setg(errp, "data-file-raw requires a data file");
+ ret = -EINVAL;
+ goto fail;
+ }
}
}
@@ -1839,7 +1842,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
fail:
g_free(s->image_data_file);
- if (has_data_file(bs)) {
+ if (open_data_file && has_data_file(bs)) {
bdrv_unref_child(bs, s->data_file);
s->data_file = NULL;
}
@@ -1876,7 +1879,8 @@ static void coroutine_fn qcow2_open_entry(void *opaque)
BDRVQcow2State *s = qoc->bs->opaque;
qemu_co_mutex_lock(&s->lock);
- qoc->ret = qcow2_do_open(qoc->bs, qoc->options, qoc->flags, qoc->errp);
+ qoc->ret = qcow2_do_open(qoc->bs, qoc->options, qoc->flags, true,
+ qoc->errp);
qemu_co_mutex_unlock(&s->lock);
}
@@ -2714,7 +2718,7 @@ static int qcow2_inactivate(BlockDriverState *bs)
return result;
}
-static void qcow2_close(BlockDriverState *bs)
+static void qcow2_do_close(BlockDriverState *bs, bool close_data_file)
{
BDRVQcow2State *s = bs->opaque;
qemu_vfree(s->l1_table);
@@ -2740,7 +2744,7 @@ static void qcow2_close(BlockDriverState *bs)
g_free(s->image_backing_file);
g_free(s->image_backing_format);
- if (has_data_file(bs)) {
+ if (close_data_file && has_data_file(bs)) {
bdrv_unref_child(bs, s->data_file);
s->data_file = NULL;
}
@@ -2749,11 +2753,17 @@ static void qcow2_close(BlockDriverState *bs)
qcow2_free_snapshots(bs);
}
+static void qcow2_close(BlockDriverState *bs)
+{
+ qcow2_do_close(bs, true);
+}
+
static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs,
Error **errp)
{
ERRP_GUARD();
BDRVQcow2State *s = bs->opaque;
+ BdrvChild *data_file;
int flags = s->flags;
QCryptoBlock *crypto = NULL;
QDict *options;
@@ -2767,14 +2777,24 @@ static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs,
crypto = s->crypto;
s->crypto = NULL;
- qcow2_close(bs);
+ /*
+ * Do not reopen s->data_file (i.e., have qcow2_do_close() not close it,
+ * and then prevent qcow2_do_open() from opening it), because this function
+ * runs in the I/O path and as such we must not invoke global-state
+ * functions like bdrv_unref_child() and bdrv_open_child().
+ */
+ qcow2_do_close(bs, false);
+
+ data_file = s->data_file;
memset(s, 0, sizeof(BDRVQcow2State));
+ s->data_file = data_file;
+
options = qdict_clone_shallow(bs->options);
flags &= ~BDRV_O_INACTIVE;
qemu_co_mutex_lock(&s->lock);
- ret = qcow2_do_open(bs, options, flags, errp);
+ ret = qcow2_do_open(bs, options, flags, false, errp);
qemu_co_mutex_unlock(&s->lock);
qobject_unref(options);
if (ret < 0) {
diff --git a/block/vmdk.c b/block/vmdk.c
index 37c0946066..38e5ab3806 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -178,6 +178,10 @@ typedef struct BDRVVmdkState {
char *create_type;
} BDRVVmdkState;
+typedef struct BDRVVmdkReopenState {
+ bool *extents_using_bs_file;
+} BDRVVmdkReopenState;
+
typedef struct VmdkMetaData {
unsigned int l1_index;
unsigned int l2_index;
@@ -400,15 +404,63 @@ static int vmdk_is_cid_valid(BlockDriverState *bs)
return 1;
}
-/* We have nothing to do for VMDK reopen, stubs just return success */
static int vmdk_reopen_prepare(BDRVReopenState *state,
BlockReopenQueue *queue, Error **errp)
{
+ BDRVVmdkState *s;
+ BDRVVmdkReopenState *rs;
+ int i;
+
assert(state != NULL);
assert(state->bs != NULL);
+ assert(state->opaque == NULL);
+
+ s = state->bs->opaque;
+
+ rs = g_new0(BDRVVmdkReopenState, 1);
+ state->opaque = rs;
+
+ /*
+ * Check whether there are any extents stored in bs->file; if bs->file
+ * changes, we will need to update their .file pointers to follow suit
+ */
+ rs->extents_using_bs_file = g_new(bool, s->num_extents);
+ for (i = 0; i < s->num_extents; i++) {
+ rs->extents_using_bs_file[i] = s->extents[i].file == state->bs->file;
+ }
+
return 0;
}
+static void vmdk_reopen_clean(BDRVReopenState *state)
+{
+ BDRVVmdkReopenState *rs = state->opaque;
+
+ g_free(rs->extents_using_bs_file);
+ g_free(rs);
+ state->opaque = NULL;
+}
+
+static void vmdk_reopen_commit(BDRVReopenState *state)
+{
+ BDRVVmdkState *s = state->bs->opaque;
+ BDRVVmdkReopenState *rs = state->opaque;
+ int i;
+
+ for (i = 0; i < s->num_extents; i++) {
+ if (rs->extents_using_bs_file[i]) {
+ s->extents[i].file = state->bs->file;
+ }
+ }
+
+ vmdk_reopen_clean(state);
+}
+
+static void vmdk_reopen_abort(BDRVReopenState *state)
+{
+ vmdk_reopen_clean(state);
+}
+
static int vmdk_parent_open(BlockDriverState *bs)
{
char *p_name;
@@ -3072,6 +3124,8 @@ static BlockDriver bdrv_vmdk = {
.bdrv_open = vmdk_open,
.bdrv_co_check = vmdk_co_check,
.bdrv_reopen_prepare = vmdk_reopen_prepare,
+ .bdrv_reopen_commit = vmdk_reopen_commit,
+ .bdrv_reopen_abort = vmdk_reopen_abort,
.bdrv_child_perm = bdrv_default_perms,
.bdrv_co_preadv = vmdk_co_preadv,
.bdrv_co_pwritev = vmdk_co_pwritev,
diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst
index 4dbc84fd00..f9e721ba5f 100644
--- a/docs/interop/vhost-user.rst
+++ b/docs/interop/vhost-user.rst
@@ -308,6 +308,7 @@ replies. Here is a list of the ones that do:
There are several messages that the master sends with file descriptors passed
in the ancillary data:
+* ``VHOST_USER_ADD_MEM_REG``
* ``VHOST_USER_SET_MEM_TABLE``
* ``VHOST_USER_SET_LOG_BASE`` (if ``VHOST_USER_PROTOCOL_F_LOG_SHMFD``)
* ``VHOST_USER_SET_LOG_FD``
@@ -1334,6 +1335,14 @@ Master message types
``VHOST_USER_REM_MEM_REG`` message, this message is used to set and
update the memory tables of the slave device.
+ Exactly one file descriptor from which the memory is mapped is
+ passed in the ancillary data.
+
+ In postcopy mode (see ``VHOST_USER_POSTCOPY_LISTEN``), the slave
+ replies with the bases of the memory mapped region to the master.
+ For further details on postcopy, see ``VHOST_USER_SET_MEM_TABLE``.
+ They apply to ``VHOST_USER_ADD_MEM_REG`` accordingly.
+
``VHOST_USER_REM_MEM_REG``
:id: 38
:equivalent ioctl: N/A
@@ -1349,6 +1358,14 @@ Master message types
``VHOST_USER_ADD_MEM_REG`` message, this message is used to set and
update the memory tables of the slave device.
+ The memory region to be removed is identified by its guest address,
+ user address and size. The mmap offset is ignored.
+
+ No file descriptors SHOULD be passed in the ancillary data. For
+ compatibility with existing incorrect implementations, the slave MAY
+ accept messages with one file descriptor. If a file descriptor is
+ passed, the slave MUST close it without using it otherwise.
+
``VHOST_USER_SET_STATUS``
:id: 39
:equivalent ioctl: VHOST_VDPA_SET_STATUS
diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst
index 8885ea11cf..85a6e05b35 100644
--- a/docs/tools/qemu-img.rst
+++ b/docs/tools/qemu-img.rst
@@ -332,8 +332,8 @@ Command description:
``-r all`` fixes all kinds of errors, with a higher risk of choosing the
wrong fix or hiding corruption that has already occurred.
- Only the formats ``qcow2``, ``qed`` and ``vdi`` support
- consistency checks.
+ Only the formats ``qcow2``, ``qed``, ``parallels``, ``vhdx``, ``vmdk`` and
+ ``vdi`` support consistency checks.
In case the image does not have any inconsistencies, check exits with ``0``.
Other exit codes indicate the kind of inconsistency found or if another error
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index a80315ecfc..2d434ff0bc 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -751,7 +751,7 @@ static int send_remove_regions(struct vhost_dev *dev,
vhost_user_fill_msg_region(&region_buffer, shadow_reg, 0);
msg->payload.mem_reg.region = region_buffer;
- ret = vhost_user_write(dev, msg, &fd, 1);
+ ret = vhost_user_write(dev, msg, NULL, 0);
if (ret < 0) {
return ret;
}
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
index 25bb69bbef..21265e3966 100644
--- a/include/block/block-global-state.h
+++ b/include/block/block-global-state.h
@@ -172,7 +172,6 @@ void bdrv_next_cleanup(BdrvNextIterator *it);
BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs);
void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
void *opaque, bool read_only);
-int bdrv_get_flags(BlockDriverState *bs);
char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp);
char *bdrv_dirname(BlockDriverState *bs, Error **errp);
diff --git a/include/block/block-io.h b/include/block/block-io.h
index 5e3f346806..62c84f0519 100644
--- a/include/block/block-io.h
+++ b/include/block/block-io.h
@@ -103,6 +103,7 @@ int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg,
bool bdrv_is_read_only(BlockDriverState *bs);
bool bdrv_is_writable(BlockDriverState *bs);
bool bdrv_is_sg(BlockDriverState *bs);
+int bdrv_get_flags(BlockDriverState *bs);
bool bdrv_is_inserted(BlockDriverState *bs);
void bdrv_lock_medium(BlockDriverState *bs, bool locked);
void bdrv_eject(BlockDriverState *bs, bool eject_flag);
diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
index d3750c8e76..89bd9edefb 100644
--- a/include/qemu/main-loop.h
+++ b/include/qemu/main-loop.h
@@ -284,8 +284,7 @@ bool qemu_in_main_thread(void);
#else
#define GLOBAL_STATE_CODE() \
do { \
- /* FIXME: Re-enable after 7.0 release */ \
- /* assert(qemu_in_main_thread()); */ \
+ assert(qemu_in_main_thread()); \
} while (0)
#endif /* CONFIG_COCOA */
diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c
index 47d2efc60f..d0041c864b 100644
--- a/subprojects/libvhost-user/libvhost-user.c
+++ b/subprojects/libvhost-user/libvhost-user.c
@@ -800,8 +800,7 @@ vu_add_mem_reg(VuDev *dev, VhostUserMsg *vmsg) {
DPRINT("Successfully added new region\n");
dev->nregions++;
- vmsg_set_reply_u64(vmsg, 0);
- return true;
+ return false;
}
}
@@ -823,15 +822,15 @@ vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) {
int i;
bool found = false;
- if (vmsg->fd_num != 1) {
+ if (vmsg->fd_num > 1) {
vmsg_close_fds(vmsg);
- vu_panic(dev, "VHOST_USER_REM_MEM_REG received %d fds - only 1 fd "
+ vu_panic(dev, "VHOST_USER_REM_MEM_REG received %d fds - at most 1 fd "
"should be sent for this message type", vmsg->fd_num);
return false;
}
if (vmsg->size < VHOST_USER_MEM_REG_SIZE) {
- close(vmsg->fds[0]);
+ vmsg_close_fds(vmsg);
vu_panic(dev, "VHOST_USER_REM_MEM_REG requires a message size of at "
"least %d bytes and only %d bytes were received",
VHOST_USER_MEM_REG_SIZE, vmsg->size);
@@ -874,15 +873,13 @@ vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) {
}
}
- if (found) {
- vmsg_set_reply_u64(vmsg, 0);
- } else {
+ if (!found) {
vu_panic(dev, "Specified region not found\n");
}
- close(vmsg->fds[0]);
+ vmsg_close_fds(vmsg);
- return true;
+ return false;
}
static bool
diff --git a/tests/qemu-iotests/tests/export-incoming-iothread b/tests/qemu-iotests/tests/export-incoming-iothread
new file mode 100755
index 0000000000..7679e49103
--- /dev/null
+++ b/tests/qemu-iotests/tests/export-incoming-iothread
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+# group: rw quick migration
+#
+# Regression test for issue 945:
+# https://gitlab.com/qemu-project/qemu/-/issues/945
+# Test adding an export on top of an iothread-ed block device while in
+# -incoming defer.
+#
+# Copyright (C) 2022 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/>.
+#
+
+import os
+import iotests
+from iotests import qemu_img_create
+
+
+image_size = 1 * 1024 * 1024
+test_img = os.path.join(iotests.test_dir, 'test.img')
+node_name = 'node0'
+iothread_id = 'iothr0'
+
+nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock')
+
+
+class TestExportIncomingIothread(iotests.QMPTestCase):
+ def setUp(self) -> None:
+ qemu_img_create('-f', iotests.imgfmt, test_img, str(image_size))
+
+ self.vm = iotests.VM()
+ self.vm.add_object(f'iothread,id={iothread_id}')
+ self.vm.add_blockdev((
+ f'driver={iotests.imgfmt}',
+ f'node-name={node_name}',
+ 'file.driver=file',
+ f'file.filename={test_img}'
+ ))
+ self.vm.add_incoming('defer')
+ self.vm.launch()
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(test_img)
+
+ def test_export_add(self):
+ result = self.vm.qmp('nbd-server-start', {
+ 'addr': {
+ 'type': 'unix',
+ 'data': {
+ 'path': nbd_sock
+ }
+ }
+ })
+ self.assert_qmp(result, 'return', {})
+
+ # Regression test for issue 945: This should not fail an assertion
+ result = self.vm.qmp('block-export-add', {
+ 'type': 'nbd',
+ 'id': 'exp0',
+ 'node-name': node_name,
+ 'iothread': iothread_id
+ })
+ self.assert_qmp(result, 'return', {})
+
+
+if __name__ == '__main__':
+ iotests.main(supported_fmts=['generic'],
+ unsupported_fmts=['luks'], # Would need a secret
+ supported_protocols=['file'])
diff --git a/tests/qemu-iotests/tests/export-incoming-iothread.out b/tests/qemu-iotests/tests/export-incoming-iothread.out
new file mode 100644
index 0000000000..ae1213e6f8
--- /dev/null
+++ b/tests/qemu-iotests/tests/export-incoming-iothread.out
@@ -0,0 +1,5 @@
+.
+----------------------------------------------------------------------
+Ran 1 tests
+
+OK
diff --git a/tests/qemu-iotests/tests/reopen-file b/tests/qemu-iotests/tests/reopen-file
new file mode 100755
index 0000000000..8590a94d53
--- /dev/null
+++ b/tests/qemu-iotests/tests/reopen-file
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+# group: rw quick
+#
+# Test reopening a format driver's file child
+#
+# Copyright (C) 2022 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/>.
+#
+
+import os
+import iotests
+from iotests import imgfmt, qemu_img_create, QMPTestCase
+
+
+image_size = 1 * 1024 * 1024
+test_img = os.path.join(iotests.test_dir, 'test.img')
+
+
+class TestReopenFile(QMPTestCase):
+ def setUp(self) -> None:
+ res = qemu_img_create('-f', imgfmt, test_img, str(image_size))
+ assert res.returncode == 0
+
+ # Add format driver node ('format') on top of the file ('file'), then
+ # add another raw node ('raw') on top of 'file' so for the reopen we
+ # can just switch from 'file' to 'raw'
+ self.vm = iotests.VM()
+ self.vm.add_blockdev(self.vm.qmp_to_opts({
+ 'driver': imgfmt,
+ 'node-name': 'format',
+ 'file': {
+ 'driver': 'file',
+ 'node-name': 'file',
+ 'filename': test_img
+ }
+ }))
+ self.vm.add_blockdev(self.vm.qmp_to_opts({
+ 'driver': 'raw',
+ 'node-name': 'raw',
+ 'file': 'file'
+ }))
+ self.vm.launch()
+
+ def tearDown(self) -> None:
+ self.vm.shutdown()
+ os.remove(test_img)
+
+ # Check if there was any qemu-io run that failed
+ if 'Pattern verification failed' in self.vm.get_log():
+ print('ERROR: Pattern verification failed:')
+ print(self.vm.get_log())
+ self.fail('qemu-io pattern verification failed')
+
+ def test_reopen_file(self) -> None:
+ result = self.vm.qmp('blockdev-reopen', options=[{
+ 'driver': imgfmt,
+ 'node-name': 'format',
+ 'file': 'raw'
+ }])
+ self.assert_qmp(result, 'return', {})
+
+ # Do some I/O to the image to see whether it still works
+ # (Pattern verification will be checked by tearDown())
+ result = self.vm.qmp('human-monitor-command',
+ command_line='qemu-io format "write -P 42 0 64k"')
+ self.assert_qmp(result, 'return', '')
+
+ result = self.vm.qmp('human-monitor-command',
+ command_line='qemu-io format "read -P 42 0 64k"')
+ self.assert_qmp(result, 'return', '')
+
+
+if __name__ == '__main__':
+ # Must support creating images and reopen
+ iotests.main(supported_fmts=['qcow', 'qcow2', 'qed', 'raw', 'vdi', 'vhdx',
+ 'vmdk', 'vpc'],
+ supported_protocols=['file'])
diff --git a/tests/qemu-iotests/tests/reopen-file.out b/tests/qemu-iotests/tests/reopen-file.out
new file mode 100644
index 0000000000..ae1213e6f8
--- /dev/null
+++ b/tests/qemu-iotests/tests/reopen-file.out
@@ -0,0 +1,5 @@
+.
+----------------------------------------------------------------------
+Ran 1 tests
+
+OK
diff --git a/util/coroutine-ucontext.c b/util/coroutine-ucontext.c
index ed368e1a3e..ddc98fb4f8 100644
--- a/util/coroutine-ucontext.c
+++ b/util/coroutine-ucontext.c
@@ -25,6 +25,7 @@
#include "qemu/osdep.h"
#include <ucontext.h>
#include "qemu/coroutine_int.h"
+#include "qemu/coroutine-tls.h"
#ifdef CONFIG_VALGRIND_H
#include <valgrind/valgrind.h>
@@ -66,8 +67,8 @@ typedef struct {
/**
* Per-thread coroutine bookkeeping
*/
-static __thread CoroutineUContext leader;
-static __thread Coroutine *current;
+QEMU_DEFINE_STATIC_CO_TLS(Coroutine *, current);
+QEMU_DEFINE_STATIC_CO_TLS(CoroutineUContext, leader);
/*
* va_args to makecontext() must be type 'int', so passing
@@ -97,14 +98,15 @@ static inline __attribute__((always_inline))
void finish_switch_fiber(void *fake_stack_save)
{
#ifdef CONFIG_ASAN
+ CoroutineUContext *leaderp = get_ptr_leader();
const void *bottom_old;
size_t size_old;
__sanitizer_finish_switch_fiber(fake_stack_save, &bottom_old, &size_old);
- if (!leader.stack) {
- leader.stack = (void *)bottom_old;
- leader.stack_size = size_old;
+ if (!leaderp->stack) {
+ leaderp->stack = (void *)bottom_old;
+ leaderp->stack_size = size_old;
}
#endif
#ifdef CONFIG_TSAN
@@ -161,8 +163,10 @@ static void coroutine_trampoline(int i0, int i1)
/* Initialize longjmp environment and switch back the caller */
if (!sigsetjmp(self->env, 0)) {
- start_switch_fiber_asan(COROUTINE_YIELD, &fake_stack_save, leader.stack,
- leader.stack_size);
+ CoroutineUContext *leaderp = get_ptr_leader();
+
+ start_switch_fiber_asan(COROUTINE_YIELD, &fake_stack_save,
+ leaderp->stack, leaderp->stack_size);
start_switch_fiber_tsan(&fake_stack_save, self, true); /* true=caller */
siglongjmp(*(sigjmp_buf *)co->entry_arg, 1);
}
@@ -297,7 +301,7 @@ qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
int ret;
void *fake_stack_save = NULL;
- current = to_;
+ set_current(to_);
ret = sigsetjmp(from->env, 0);
if (ret == 0) {
@@ -315,18 +319,24 @@ qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
Coroutine *qemu_coroutine_self(void)
{
- if (!current) {
- current = &leader.base;
+ Coroutine *self = get_current();
+ CoroutineUContext *leaderp = get_ptr_leader();
+
+ if (!self) {
+ self = &leaderp->base;
+ set_current(self);
}
#ifdef CONFIG_TSAN
- if (!leader.tsan_co_fiber) {
- leader.tsan_co_fiber = __tsan_get_current_fiber();
+ if (!leaderp->tsan_co_fiber) {
+ leaderp->tsan_co_fiber = __tsan_get_current_fiber();
}
#endif
- return current;
+ return self;
}
bool qemu_in_coroutine(void)
{
- return current && current->caller;
+ Coroutine *self = get_current();
+
+ return self && self->caller;
}
diff --git a/util/coroutine-win32.c b/util/coroutine-win32.c
index c196f956d2..7db2e8f8c8 100644
--- a/util/coroutine-win32.c
+++ b/util/coroutine-win32.c
@@ -24,6 +24,7 @@
#include "qemu/osdep.h"
#include "qemu/coroutine_int.h"
+#include "qemu/coroutine-tls.h"
typedef struct
{
@@ -33,8 +34,8 @@ typedef struct
CoroutineAction action;
} CoroutineWin32;
-static __thread CoroutineWin32 leader;
-static __thread Coroutine *current;
+QEMU_DEFINE_STATIC_CO_TLS(CoroutineWin32, leader);
+QEMU_DEFINE_STATIC_CO_TLS(Coroutine *, current);
/* This function is marked noinline to prevent GCC from inlining it
* into coroutine_trampoline(). If we allow it to do that then it
@@ -51,7 +52,7 @@ qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
CoroutineWin32 *from = DO_UPCAST(CoroutineWin32, base, from_);
CoroutineWin32 *to = DO_UPCAST(CoroutineWin32, base, to_);
- current = to_;
+ set_current(to_);
to->action = action;
SwitchToFiber(to->fiber);
@@ -88,14 +89,21 @@ void qemu_coroutine_delete(Coroutine *co_)
Coroutine *qemu_coroutine_self(void)
{
+ Coroutine *current = get_current();
+
if (!current) {
- current = &leader.base;
- leader.fiber = ConvertThreadToFiber(NULL);
+ CoroutineWin32 *leader = get_ptr_leader();
+
+ current = &leader->base;
+ set_current(current);
+ leader->fiber = ConvertThreadToFiber(NULL);
}
return current;
}
bool qemu_in_coroutine(void)
{
+ Coroutine *current = get_current();
+
return current && current->caller;
}
diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c
index c03b2422ff..f3e8300c8d 100644
--- a/util/qemu-coroutine.c
+++ b/util/qemu-coroutine.c
@@ -18,6 +18,7 @@
#include "qemu/atomic.h"
#include "qemu/coroutine.h"
#include "qemu/coroutine_int.h"
+#include "qemu/coroutine-tls.h"
#include "block/aio.h"
/** Initial batch size is 64, and is increased on demand */
@@ -29,17 +30,20 @@ enum {
static QSLIST_HEAD(, Coroutine) release_pool = QSLIST_HEAD_INITIALIZER(pool);
static unsigned int pool_batch_size = POOL_INITIAL_BATCH_SIZE;
static unsigned int release_pool_size;
-static __thread QSLIST_HEAD(, Coroutine) alloc_pool = QSLIST_HEAD_INITIALIZER(pool);
-static __thread unsigned int alloc_pool_size;
-static __thread Notifier coroutine_pool_cleanup_notifier;
+
+typedef QSLIST_HEAD(, Coroutine) CoroutineQSList;
+QEMU_DEFINE_STATIC_CO_TLS(CoroutineQSList, alloc_pool);
+QEMU_DEFINE_STATIC_CO_TLS(unsigned int, alloc_pool_size);
+QEMU_DEFINE_STATIC_CO_TLS(Notifier, coroutine_pool_cleanup_notifier);
static void coroutine_pool_cleanup(Notifier *n, void *value)
{
Coroutine *co;
Coroutine *tmp;
+ CoroutineQSList *alloc_pool = get_ptr_alloc_pool();
- QSLIST_FOREACH_SAFE(co, &alloc_pool, pool_next, tmp) {
- QSLIST_REMOVE_HEAD(&alloc_pool, pool_next);
+ QSLIST_FOREACH_SAFE(co, alloc_pool, pool_next, tmp) {
+ QSLIST_REMOVE_HEAD(alloc_pool, pool_next);
qemu_coroutine_delete(co);
}
}
@@ -49,27 +53,30 @@ Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque)
Coroutine *co = NULL;
if (CONFIG_COROUTINE_POOL) {
- co = QSLIST_FIRST(&alloc_pool);
+ CoroutineQSList *alloc_pool = get_ptr_alloc_pool();
+
+ co = QSLIST_FIRST(alloc_pool);
if (!co) {
if (release_pool_size > qatomic_read(&pool_batch_size)) {
/* Slow path; a good place to register the destructor, too. */
- if (!coroutine_pool_cleanup_notifier.notify) {
- coroutine_pool_cleanup_notifier.notify = coroutine_pool_cleanup;
- qemu_thread_atexit_add(&coroutine_pool_cleanup_notifier);
+ Notifier *notifier = get_ptr_coroutine_pool_cleanup_notifier();
+ if (!notifier->notify) {
+ notifier->notify = coroutine_pool_cleanup;
+ qemu_thread_atexit_add(notifier);
}
/* This is not exact; there could be a little skew between
* release_pool_size and the actual size of release_pool. But
* it is just a heuristic, it does not need to be perfect.
*/
- alloc_pool_size = qatomic_xchg(&release_pool_size, 0);
- QSLIST_MOVE_ATOMIC(&alloc_pool, &release_pool);
- co = QSLIST_FIRST(&alloc_pool);
+ set_alloc_pool_size(qatomic_xchg(&release_pool_size, 0));
+ QSLIST_MOVE_ATOMIC(alloc_pool, &release_pool);
+ co = QSLIST_FIRST(alloc_pool);
}
}
if (co) {
- QSLIST_REMOVE_HEAD(&alloc_pool, pool_next);
- alloc_pool_size--;
+ QSLIST_REMOVE_HEAD(alloc_pool, pool_next);
+ set_alloc_pool_size(get_alloc_pool_size() - 1);
}
}
@@ -93,9 +100,9 @@ static void coroutine_delete(Coroutine *co)
qatomic_inc(&release_pool_size);
return;
}
- if (alloc_pool_size < qatomic_read(&pool_batch_size)) {
- QSLIST_INSERT_HEAD(&alloc_pool, co, pool_next);
- alloc_pool_size++;
+ if (get_alloc_pool_size() < qatomic_read(&pool_batch_size)) {
+ QSLIST_INSERT_HEAD(get_ptr_alloc_pool(), co, pool_next);
+ set_alloc_pool_size(get_alloc_pool_size() + 1);
return;
}
}