Block layer patches

- Fix and re-enable GLOBAL_STATE_CODE assertions
 - vhost-user: Fixes for VHOST_USER_ADD/REM_MEM_REG
 - vmdk: Fix reopening bs->file
 - coroutine: use QEMU_DEFINE_STATIC_CO_TLS()
 - docs/qemu-img: Fix list of formats which implement check
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAmJyi+YRHGt3b2xmQHJl
 ZGhhdC5jb20ACgkQfwmycsiPL9aphxAAt0sXEOWlcIeU87NOk+V30rRiBup0K/HZ
 wqsE6e0EMbygmC2aS/xqNu3naQ/TMY6UaoVBWSpf0D3sK2GnWEJW8bjV05ObZBwp
 6QUgqljk1QAAVv0o2/nViAcV8mEW+OzZLveP+qxFRNlNGoJDsbGzWj939SHM13eu
 ZD+/GGs/qXL3Gxp6adhOBjxbXYjvxm13F3pVjoyAugjMSqoSuCI0eXu1xkwXNHSP
 /wqObH3dQSzIvEXfE/1BOp3ofZwvg+XzeZ6MM4I/lvHDZWuQBfCQcBYKL9mMNWGc
 ijFEeolWt7hER50ik4XPvBmbj0jU2nPXQwo1XcFeWX3MSoNsha2jCZsz4LqzadIN
 YijGQHmkfDRmG2LSoIGcgM7chdwj88K8pfMnrrTsVEB6Dl4QrK6FjXviL5mG+rcX
 5FbKpgRwm3fmtug7Ttpgm1LJQmwK5A3YPenPH+CC2FoK3Rje46ZoMwQR5PBuHvM1
 rg9RB01eGJQGrw5Rt3VFk7304O/yT2J5m96x6CMejx4CuGK78VpkBC54HixTTh0R
 nXxLLZdqawVqwrPP6sE02FEajM931///nhU6fuN/832m3bYUsfM8SZNVqJh5xoex
 SK8x/a5HnTIkp7kys3f4juc+jzb4Yvka8IBIZi/gqzoqf8POGKLBCTpEXa3lBWIT
 gnCCnWWEdgY=
 =ETW/
 -----END PGP SIGNATURE-----

Merge tag 'for-upstream' of git://repo.or.cz/qemu/kevin into staging

Block layer patches

- Fix and re-enable GLOBAL_STATE_CODE assertions
- vhost-user: Fixes for VHOST_USER_ADD/REM_MEM_REG
- vmdk: Fix reopening bs->file
- coroutine: use QEMU_DEFINE_STATIC_CO_TLS()
- docs/qemu-img: Fix list of formats which implement check

# -----BEGIN PGP SIGNATURE-----
#
# iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAmJyi+YRHGt3b2xmQHJl
# ZGhhdC5jb20ACgkQfwmycsiPL9aphxAAt0sXEOWlcIeU87NOk+V30rRiBup0K/HZ
# wqsE6e0EMbygmC2aS/xqNu3naQ/TMY6UaoVBWSpf0D3sK2GnWEJW8bjV05ObZBwp
# 6QUgqljk1QAAVv0o2/nViAcV8mEW+OzZLveP+qxFRNlNGoJDsbGzWj939SHM13eu
# ZD+/GGs/qXL3Gxp6adhOBjxbXYjvxm13F3pVjoyAugjMSqoSuCI0eXu1xkwXNHSP
# /wqObH3dQSzIvEXfE/1BOp3ofZwvg+XzeZ6MM4I/lvHDZWuQBfCQcBYKL9mMNWGc
# ijFEeolWt7hER50ik4XPvBmbj0jU2nPXQwo1XcFeWX3MSoNsha2jCZsz4LqzadIN
# YijGQHmkfDRmG2LSoIGcgM7chdwj88K8pfMnrrTsVEB6Dl4QrK6FjXviL5mG+rcX
# 5FbKpgRwm3fmtug7Ttpgm1LJQmwK5A3YPenPH+CC2FoK3Rje46ZoMwQR5PBuHvM1
# rg9RB01eGJQGrw5Rt3VFk7304O/yT2J5m96x6CMejx4CuGK78VpkBC54HixTTh0R
# nXxLLZdqawVqwrPP6sE02FEajM931///nhU6fuN/832m3bYUsfM8SZNVqJh5xoex
# SK8x/a5HnTIkp7kys3f4juc+jzb4Yvka8IBIZi/gqzoqf8POGKLBCTpEXa3lBWIT
# gnCCnWWEdgY=
# =ETW/
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 04 May 2022 09:21:26 AM CDT
# gpg:                using RSA key DC3DEB159A9AF95D3D7456FE7F09B272C88F2FD6
# gpg:                issuer "kwolf@redhat.com"
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full]

* tag 'for-upstream' of git://repo.or.cz/qemu/kevin:
  coroutine-win32: use QEMU_DEFINE_STATIC_CO_TLS()
  coroutine: use QEMU_DEFINE_STATIC_CO_TLS()
  coroutine-ucontext: use QEMU_DEFINE_STATIC_CO_TLS()
  iotests/reopen-file: Test reopening file child
  block/vmdk: Fix reopening bs->file
  iotests: Add regression test for issue 945
  Revert "main-loop: Disable GLOBAL_STATE_CODE() assertions"
  qcow2: Do not reopen data_file in invalidate_cache
  block: Classify bdrv_get_flags() as I/O function
  vhost-user: Don't pass file descriptor for VHOST_USER_REM_MEM_REG
  libvhost-user: Fix extra vu_add/rem_mem_reg reply
  docs/vhost-user: Clarifications for VHOST_USER_ADD/REM_MEM_REG
  qemu-img: properly list formats which have consistency check implemented

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2022-05-04 15:44:15 -05:00
commit 5d51042602
17 changed files with 388 additions and 96 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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'])

View File

@ -0,0 +1,5 @@
.
----------------------------------------------------------------------
Ran 1 tests
OK

View 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'])

View File

@ -0,0 +1,5 @@
.
----------------------------------------------------------------------
Ran 1 tests
OK

View File

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

View File

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

View File

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