mirror of https://github.com/xemu-project/xemu.git
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:
commit
5d51042602
2
block.c
2
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;
|
||||
}
|
||||
|
||||
|
|
104
block/qcow2.c
104
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) {
|
||||
|
|
56
block/vmdk.c
56
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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -751,7 +751,7 @@ static int send_remove_regions(struct vhost_dev *dev,
|
|||
vhost_user_fill_msg_region(®ion_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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'])
|
|
@ -0,0 +1,5 @@
|
|||
.
|
||||
----------------------------------------------------------------------
|
||||
Ran 1 tests
|
||||
|
||||
OK
|
|
@ -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'])
|
|
@ -0,0 +1,5 @@
|
|||
.
|
||||
----------------------------------------------------------------------
|
||||
Ran 1 tests
|
||||
|
||||
OK
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue