1 | The following changes since commit e3acc2c1961cbe22ca474cd5da4163b7bbf7cea3: | 1 | The following changes since commit 2e3408b3cc7de4e87a9adafc8c19bfce3abec947: |
---|---|---|---|
2 | 2 | ||
3 | tests/docker/dockerfiles: Bump fedora-i386-cross to fedora 34 (2021-10-05 16:40:39 -0700) | 3 | Merge tag 'misc-pull-request' of gitlab.com:marcandre.lureau/qemu into staging (2022-05-03 09:13:17 -0700) |
4 | 4 | ||
5 | are available in the Git repository at: | 5 | are available in the Git repository at: |
6 | 6 | ||
7 | git://repo.or.cz/qemu/kevin.git tags/for-upstream | 7 | git://repo.or.cz/qemu/kevin.git tags/for-upstream |
8 | 8 | ||
9 | for you to fetch changes up to 3765315d4c84f9c0799744f43a314169baaccc05: | 9 | for you to fetch changes up to c1fe694357a328c807ae3cc6961c19e923448fcc: |
10 | 10 | ||
11 | iotests: Update for pylint 2.11.1 (2021-10-06 10:25:55 +0200) | 11 | coroutine-win32: use QEMU_DEFINE_STATIC_CO_TLS() (2022-05-04 15:55:23 +0200) |
12 | 12 | ||
13 | ---------------------------------------------------------------- | 13 | ---------------------------------------------------------------- |
14 | Block layer patches | 14 | Block layer patches |
15 | 15 | ||
16 | - Fix I/O errors because of incorrectly detected max_iov | 16 | - Fix and re-enable GLOBAL_STATE_CODE assertions |
17 | - Fix not white-listed copy-before-write | 17 | - vhost-user: Fixes for VHOST_USER_ADD/REM_MEM_REG |
18 | - qemu-storage-daemon: Only display FUSE help when FUSE is built-in | 18 | - vmdk: Fix reopening bs->file |
19 | - iotests: update environment and linting configuration | 19 | - coroutine: use QEMU_DEFINE_STATIC_CO_TLS() |
20 | - docs/qemu-img: Fix list of formats which implement check | ||
20 | 21 | ||
21 | ---------------------------------------------------------------- | 22 | ---------------------------------------------------------------- |
22 | Emanuele Giuseppe Esposito (1): | 23 | Denis V. Lunev (1): |
23 | include/block.h: remove outdated comment | 24 | qemu-img: properly list formats which have consistency check implemented |
24 | 25 | ||
25 | John Snow (5): | 26 | Hanna Reitz (6): |
26 | iotests: add 'qemu' package location to PYTHONPATH in testenv | 27 | block: Classify bdrv_get_flags() as I/O function |
27 | iotests/linters: check mypy files all at once | 28 | qcow2: Do not reopen data_file in invalidate_cache |
28 | iotests/mirror-top-perms: Adjust imports | 29 | Revert "main-loop: Disable GLOBAL_STATE_CODE() assertions" |
29 | iotests/migrate-bitmaps-test: delint | 30 | iotests: Add regression test for issue 945 |
30 | iotests: Update for pylint 2.11.1 | 31 | block/vmdk: Fix reopening bs->file |
32 | iotests/reopen-file: Test reopening file child | ||
31 | 33 | ||
32 | Paolo Bonzini (1): | 34 | Kevin Wolf (3): |
33 | block: introduce max_hw_iov for use in scsi-generic | 35 | docs/vhost-user: Clarifications for VHOST_USER_ADD/REM_MEM_REG |
36 | libvhost-user: Fix extra vu_add/rem_mem_reg reply | ||
37 | vhost-user: Don't pass file descriptor for VHOST_USER_REM_MEM_REG | ||
34 | 38 | ||
35 | Philippe Mathieu-Daudé (1): | 39 | Stefan Hajnoczi (3): |
36 | qemu-storage-daemon: Only display FUSE help when FUSE is built-in | 40 | coroutine-ucontext: use QEMU_DEFINE_STATIC_CO_TLS() |
41 | coroutine: use QEMU_DEFINE_STATIC_CO_TLS() | ||
42 | coroutine-win32: use QEMU_DEFINE_STATIC_CO_TLS() | ||
37 | 43 | ||
38 | Vladimir Sementsov-Ogievskiy (5): | 44 | docs/interop/vhost-user.rst | 17 ++++ |
39 | block: implement bdrv_new_open_driver_opts() | 45 | docs/tools/qemu-img.rst | 4 +- |
40 | block: bdrv_insert_node(): fix and improve error handling | 46 | include/block/block-global-state.h | 1 - |
41 | block: bdrv_insert_node(): doc and style | 47 | include/block/block-io.h | 1 + |
42 | block: bdrv_insert_node(): don't use bdrv_open() | 48 | include/qemu/main-loop.h | 3 +- |
43 | iotests/image-fleecing: declare requirement of copy-before-write | 49 | block.c | 2 +- |
44 | 50 | block/qcow2.c | 104 ++++++++++++--------- | |
45 | include/block/block.h | 8 ++- | 51 | block/vmdk.c | 56 ++++++++++- |
46 | include/block/block_int.h | 7 +++ | 52 | hw/virtio/vhost-user.c | 2 +- |
47 | include/sysemu/block-backend.h | 1 + | 53 | subprojects/libvhost-user/libvhost-user.c | 17 ++-- |
48 | block.c | 79 ++++++++++++++++++++++----- | 54 | util/coroutine-ucontext.c | 38 +++++--- |
49 | block/block-backend.c | 6 ++ | 55 | util/coroutine-win32.c | 18 +++- |
50 | block/file-posix.c | 2 +- | 56 | util/qemu-coroutine.c | 41 ++++---- |
51 | block/io.c | 1 + | 57 | tests/qemu-iotests/tests/export-incoming-iothread | 81 ++++++++++++++++ |
52 | hw/scsi/scsi-generic.c | 2 +- | 58 | .../tests/export-incoming-iothread.out | 5 + |
53 | storage-daemon/qemu-storage-daemon.c | 2 + | 59 | tests/qemu-iotests/tests/reopen-file | 89 ++++++++++++++++++ |
54 | tests/qemu-iotests/iotests.py | 2 - | 60 | tests/qemu-iotests/tests/reopen-file.out | 5 + |
55 | tests/qemu-iotests/testenv.py | 15 +++-- | 61 | 17 files changed, 388 insertions(+), 96 deletions(-) |
56 | tests/qemu-iotests/testrunner.py | 7 ++- | 62 | create mode 100755 tests/qemu-iotests/tests/export-incoming-iothread |
57 | tests/qemu-iotests/235 | 2 - | 63 | create mode 100644 tests/qemu-iotests/tests/export-incoming-iothread.out |
58 | tests/qemu-iotests/297 | 52 +++++++----------- | 64 | create mode 100755 tests/qemu-iotests/tests/reopen-file |
59 | tests/qemu-iotests/300 | 5 +- | 65 | create mode 100644 tests/qemu-iotests/tests/reopen-file.out |
60 | tests/qemu-iotests/pylintrc | 6 +- | ||
61 | tests/qemu-iotests/tests/image-fleecing | 1 + | ||
62 | tests/qemu-iotests/tests/migrate-bitmaps-test | 50 +++++++++-------- | ||
63 | tests/qemu-iotests/tests/mirror-top-perms | 12 ++-- | ||
64 | 19 files changed, 164 insertions(+), 96 deletions(-) | ||
65 | |||
66 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | From: "Denis V. Lunev" <den@openvz.org> |
---|---|---|---|
2 | 2 | ||
3 | We need to import subpackages from the qemu namespace package; importing | 3 | Simple grep for the .bdrv_co_check callback presence gives the following |
4 | the namespace package alone doesn't bring the subpackages with it -- | 4 | list of block drivers |
5 | unless someone else (like iotests.py) imports them too. | 5 | * QED |
6 | * VDI | ||
7 | * VHDX | ||
8 | * VMDK | ||
9 | * Parallels | ||
10 | which have this callback. The presense of the callback means that | ||
11 | consistency check is supported. | ||
6 | 12 | ||
7 | Adjust the imports. | 13 | The patch updates documentation accordingly. |
8 | 14 | ||
9 | Signed-off-by: John Snow <jsnow@redhat.com> | 15 | Signed-off-by: Denis V. Lunev <den@openvz.org> |
10 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | 16 | CC: Kevin Wolf <kwolf@redhat.com> |
11 | Reviewed-by: Hanna Reitz <hreitz@redhat.com> | 17 | CC: Hanna Reitz <hreitz@redhat.com> |
12 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 18 | Message-Id: <20220407083932.531965-1-den@openvz.org> |
13 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 19 | Reviewed-by: Eric Blake <eblake@redhat.com> |
14 | Message-Id: <20210923180715.4168522-5-jsnow@redhat.com> | ||
15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 20 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
16 | --- | 21 | --- |
17 | tests/qemu-iotests/tests/mirror-top-perms | 7 ++++--- | 22 | docs/tools/qemu-img.rst | 4 ++-- |
18 | 1 file changed, 4 insertions(+), 3 deletions(-) | 23 | 1 file changed, 2 insertions(+), 2 deletions(-) |
19 | 24 | ||
20 | diff --git a/tests/qemu-iotests/tests/mirror-top-perms b/tests/qemu-iotests/tests/mirror-top-perms | 25 | diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst |
21 | index XXXXXXX..XXXXXXX 100755 | 26 | index XXXXXXX..XXXXXXX 100644 |
22 | --- a/tests/qemu-iotests/tests/mirror-top-perms | 27 | --- a/docs/tools/qemu-img.rst |
23 | +++ b/tests/qemu-iotests/tests/mirror-top-perms | 28 | +++ b/docs/tools/qemu-img.rst |
24 | @@ -XXX,XX +XXX,XX @@ | 29 | @@ -XXX,XX +XXX,XX @@ Command description: |
25 | 30 | ``-r all`` fixes all kinds of errors, with a higher risk of choosing the | |
26 | import os | 31 | wrong fix or hiding corruption that has already occurred. |
27 | 32 | ||
28 | -import qemu | 33 | - Only the formats ``qcow2``, ``qed`` and ``vdi`` support |
29 | +from qemu import qmp | 34 | - consistency checks. |
30 | +from qemu.machine import machine | 35 | + Only the formats ``qcow2``, ``qed``, ``parallels``, ``vhdx``, ``vmdk`` and |
31 | 36 | + ``vdi`` support consistency checks. | |
32 | import iotests | 37 | |
33 | from iotests import qemu_img | 38 | In case the image does not have any inconsistencies, check exits with ``0``. |
34 | @@ -XXX,XX +XXX,XX @@ class TestMirrorTopPerms(iotests.QMPTestCase): | 39 | Other exit codes indicate the kind of inconsistency found or if another error |
35 | def tearDown(self): | ||
36 | try: | ||
37 | self.vm.shutdown() | ||
38 | - except qemu.machine.machine.AbnormalShutdown: | ||
39 | + except machine.AbnormalShutdown: | ||
40 | pass | ||
41 | |||
42 | if self.vm_b is not None: | ||
43 | @@ -XXX,XX +XXX,XX @@ class TestMirrorTopPerms(iotests.QMPTestCase): | ||
44 | self.vm_b.launch() | ||
45 | print('ERROR: VM B launched successfully, this should not have ' | ||
46 | 'happened') | ||
47 | - except qemu.qmp.QMPConnectError: | ||
48 | + except qmp.QMPConnectError: | ||
49 | assert 'Is another process using the image' in self.vm_b.get_log() | ||
50 | |||
51 | result = self.vm.qmp('block-job-cancel', | ||
52 | -- | 40 | -- |
53 | 2.31.1 | 41 | 2.35.1 |
54 | |||
55 | diff view generated by jsdifflib |
1 | From: Emanuele Giuseppe Esposito <eesposit@redhat.com> | 1 | The specification for VHOST_USER_ADD/REM_MEM_REG messages is unclear |
---|---|---|---|
2 | in several points, which has led to clients having incompatible | ||
3 | implementations. This changes the specification to be more explicit | ||
4 | about them: | ||
2 | 5 | ||
3 | There are a couple of errors in bdrv_drained_begin header comment: | 6 | * VHOST_USER_ADD_MEM_REG is not specified as receiving a file |
4 | - block_job_pause does not exist anymore, it has been replaced | 7 | descriptor, though it obviously does need to do so. All |
5 | with job_pause in b15de82867 | 8 | implementations agree on this one, fix the specification. |
6 | - job_pause is automatically invoked as a .drained_begin callback | ||
7 | (child_job_drained_begin) by the child_job BdrvChildClass struct | ||
8 | in blockjob.c. So no additional pause should be required. | ||
9 | 9 | ||
10 | Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> | 10 | * VHOST_USER_REM_MEM_REG is not specified as receiving a file |
11 | Message-Id: <20210903113800.59970-1-eesposit@redhat.com> | 11 | descriptor either, and it also has no reason to do so. rust-vmm does |
12 | not send file descriptors for removing a memory region (in agreement | ||
13 | with the specification), libvhost-user and QEMU do (which is a bug), | ||
14 | though libvhost-user doesn't actually make any use of it. | ||
15 | |||
16 | Change the specification so that for compatibility QEMU's behaviour | ||
17 | becomes legal, even if discouraged, but rust-vmm's behaviour becomes | ||
18 | the explicitly recommended mode of operation. | ||
19 | |||
20 | * VHOST_USER_ADD_MEM_REG doesn't have a documented return value, which | ||
21 | is the desired behaviour in the non-postcopy case. It also implemented | ||
22 | like this in QEMU and rust-vmm, though libvhost-user is buggy and | ||
23 | sometimes sends an unexpected reply. This will be fixed in a separate | ||
24 | patch. | ||
25 | |||
26 | However, in postcopy mode it does reply like VHOST_USER_SET_MEM_TABLE. | ||
27 | This behaviour is shared between libvhost-user and QEMU; rust-vmm | ||
28 | doesn't implement postcopy mode yet. Mention it explicitly in the | ||
29 | spec. | ||
30 | |||
31 | * The specification doesn't mention how VHOST_USER_REM_MEM_REG | ||
32 | identifies the memory region to be removed. Change it to describe the | ||
33 | existing behaviour of libvhost-user (guest address, user address and | ||
34 | size must match). | ||
35 | |||
36 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
37 | Message-Id: <20220407133657.155281-2-kwolf@redhat.com> | ||
38 | Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com> | ||
12 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 39 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 40 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
14 | --- | 41 | --- |
15 | include/block/block.h | 4 +--- | 42 | docs/interop/vhost-user.rst | 17 +++++++++++++++++ |
16 | 1 file changed, 1 insertion(+), 3 deletions(-) | 43 | 1 file changed, 17 insertions(+) |
17 | 44 | ||
18 | diff --git a/include/block/block.h b/include/block/block.h | 45 | diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst |
19 | index XXXXXXX..XXXXXXX 100644 | 46 | index XXXXXXX..XXXXXXX 100644 |
20 | --- a/include/block/block.h | 47 | --- a/docs/interop/vhost-user.rst |
21 | +++ b/include/block/block.h | 48 | +++ b/docs/interop/vhost-user.rst |
22 | @@ -XXX,XX +XXX,XX @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, | 49 | @@ -XXX,XX +XXX,XX @@ replies. Here is a list of the ones that do: |
23 | * bdrv_drained_begin: | 50 | There are several messages that the master sends with file descriptors passed |
24 | * | 51 | in the ancillary data: |
25 | * Begin a quiesced section for exclusive access to the BDS, by disabling | 52 | |
26 | - * external request sources including NBD server and device model. Note that | 53 | +* ``VHOST_USER_ADD_MEM_REG`` |
27 | - * this doesn't block timers or coroutines from submitting more requests, which | 54 | * ``VHOST_USER_SET_MEM_TABLE`` |
28 | - * means block_job_pause is still necessary. | 55 | * ``VHOST_USER_SET_LOG_BASE`` (if ``VHOST_USER_PROTOCOL_F_LOG_SHMFD``) |
29 | + * external request sources including NBD server, block jobs, and device model. | 56 | * ``VHOST_USER_SET_LOG_FD`` |
30 | * | 57 | @@ -XXX,XX +XXX,XX @@ Master message types |
31 | * This function can be recursive. | 58 | ``VHOST_USER_REM_MEM_REG`` message, this message is used to set and |
32 | */ | 59 | update the memory tables of the slave device. |
60 | |||
61 | + Exactly one file descriptor from which the memory is mapped is | ||
62 | + passed in the ancillary data. | ||
63 | + | ||
64 | + In postcopy mode (see ``VHOST_USER_POSTCOPY_LISTEN``), the slave | ||
65 | + replies with the bases of the memory mapped region to the master. | ||
66 | + For further details on postcopy, see ``VHOST_USER_SET_MEM_TABLE``. | ||
67 | + They apply to ``VHOST_USER_ADD_MEM_REG`` accordingly. | ||
68 | + | ||
69 | ``VHOST_USER_REM_MEM_REG`` | ||
70 | :id: 38 | ||
71 | :equivalent ioctl: N/A | ||
72 | @@ -XXX,XX +XXX,XX @@ Master message types | ||
73 | ``VHOST_USER_ADD_MEM_REG`` message, this message is used to set and | ||
74 | update the memory tables of the slave device. | ||
75 | |||
76 | + The memory region to be removed is identified by its guest address, | ||
77 | + user address and size. The mmap offset is ignored. | ||
78 | + | ||
79 | + No file descriptors SHOULD be passed in the ancillary data. For | ||
80 | + compatibility with existing incorrect implementations, the slave MAY | ||
81 | + accept messages with one file descriptor. If a file descriptor is | ||
82 | + passed, the slave MUST close it without using it otherwise. | ||
83 | + | ||
84 | ``VHOST_USER_SET_STATUS`` | ||
85 | :id: 39 | ||
86 | :equivalent ioctl: VHOST_VDPA_SET_STATUS | ||
33 | -- | 87 | -- |
34 | 2.31.1 | 88 | 2.35.1 |
35 | |||
36 | diff view generated by jsdifflib |
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 1 | Outside of postcopy mode, neither VHOST_USER_ADD_MEM_REG nor |
---|---|---|---|
2 | VHOST_USER_REM_MEM_REG are supposed to send a reply unless explicitly | ||
3 | requested with the need_reply flag. Their current implementation always | ||
4 | sends a reply, even if it isn't requested. This confuses the master | ||
5 | because it will interpret the reply as a reply for the next message for | ||
6 | which it actually expects a reply. | ||
2 | 7 | ||
3 | Use bdrv_new_open_driver_opts() instead of complicated bdrv_open(). | 8 | need_reply is already handled correctly by vu_dispatch(), so just don't |
9 | send a reply in the non-postcopy part of the message handler for these | ||
10 | two commands. | ||
4 | 11 | ||
5 | Among other extra things bdrv_open() also check for white-listed | 12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | formats, which we don't want for internal node creation: currently | 13 | Message-Id: <20220407133657.155281-3-kwolf@redhat.com> |
7 | backup doesn't work when copy-before-write filter is not white-listed. | 14 | Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com> |
8 | As well block-stream doesn't work when copy-on-read is not | 15 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
9 | white-listed. | ||
10 | |||
11 | Fixes: 751cec7a261adaf1145dc7adf6de7c9c084e5a0b | ||
12 | Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2004812 | ||
13 | Reported-by: Yanan Fu | ||
14 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
15 | Message-Id: <20210920115538.264372-5-vsementsov@virtuozzo.com> | ||
16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
17 | --- | 17 | --- |
18 | block.c | 34 ++++++++++++++++++++++++++++------ | 18 | subprojects/libvhost-user/libvhost-user.c | 9 +++------ |
19 | 1 file changed, 28 insertions(+), 6 deletions(-) | 19 | 1 file changed, 3 insertions(+), 6 deletions(-) |
20 | 20 | ||
21 | diff --git a/block.c b/block.c | 21 | diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c |
22 | index XXXXXXX..XXXXXXX 100644 | 22 | index XXXXXXX..XXXXXXX 100644 |
23 | --- a/block.c | 23 | --- a/subprojects/libvhost-user/libvhost-user.c |
24 | +++ b/block.c | 24 | +++ b/subprojects/libvhost-user/libvhost-user.c |
25 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, | 25 | @@ -XXX,XX +XXX,XX @@ vu_add_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { |
26 | { | 26 | |
27 | ERRP_GUARD(); | 27 | DPRINT("Successfully added new region\n"); |
28 | int ret; | 28 | dev->nregions++; |
29 | - BlockDriverState *new_node_bs; | 29 | - vmsg_set_reply_u64(vmsg, 0); |
30 | + BlockDriverState *new_node_bs = NULL; | 30 | - return true; |
31 | + const char *drvname, *node_name; | 31 | + return false; |
32 | + BlockDriver *drv; | ||
33 | + | ||
34 | + drvname = qdict_get_try_str(options, "driver"); | ||
35 | + if (!drvname) { | ||
36 | + error_setg(errp, "driver is not specified"); | ||
37 | + goto fail; | ||
38 | + } | ||
39 | + | ||
40 | + drv = bdrv_find_format(drvname); | ||
41 | + if (!drv) { | ||
42 | + error_setg(errp, "Unknown driver: '%s'", drvname); | ||
43 | + goto fail; | ||
44 | + } | ||
45 | |||
46 | - new_node_bs = bdrv_open(NULL, NULL, options, flags, errp); | ||
47 | - if (new_node_bs == NULL) { | ||
48 | + node_name = qdict_get_try_str(options, "node-name"); | ||
49 | + | ||
50 | + new_node_bs = bdrv_new_open_driver_opts(drv, node_name, options, flags, | ||
51 | + errp); | ||
52 | + options = NULL; /* bdrv_new_open_driver() eats options */ | ||
53 | + if (!new_node_bs) { | ||
54 | error_prepend(errp, "Could not create node: "); | ||
55 | - return NULL; | ||
56 | + goto fail; | ||
57 | } | 32 | } |
58 | 33 | } | |
59 | bdrv_drained_begin(bs); | 34 | |
60 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, | 35 | @@ -XXX,XX +XXX,XX @@ vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { |
61 | 36 | } | |
62 | if (ret < 0) { | ||
63 | error_prepend(errp, "Could not replace node: "); | ||
64 | - bdrv_unref(new_node_bs); | ||
65 | - return NULL; | ||
66 | + goto fail; | ||
67 | } | 37 | } |
68 | 38 | ||
69 | return new_node_bs; | 39 | - if (found) { |
70 | + | 40 | - vmsg_set_reply_u64(vmsg, 0); |
71 | +fail: | 41 | - } else { |
72 | + qobject_unref(options); | 42 | + if (!found) { |
73 | + bdrv_unref(new_node_bs); | 43 | vu_panic(dev, "Specified region not found\n"); |
74 | + return NULL; | 44 | } |
45 | |||
46 | close(vmsg->fds[0]); | ||
47 | |||
48 | - return true; | ||
49 | + return false; | ||
75 | } | 50 | } |
76 | 51 | ||
77 | /* | 52 | static bool |
78 | -- | 53 | -- |
79 | 2.31.1 | 54 | 2.35.1 |
80 | |||
81 | diff view generated by jsdifflib |
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 1 | The spec clarifies now that QEMU should not send a file descriptor in a |
---|---|---|---|
2 | request to remove a memory region. Change it accordingly. | ||
2 | 3 | ||
3 | - use ERRP_GUARD(): function calls error_prepend(), so it must use | 4 | For libvhost-user, this is a bug fix that makes it compatible with |
4 | ERRP_GUARD(), otherwise error_prepend() would not be called when | 5 | rust-vmm's implementation that doesn't send a file descriptor. Keep |
5 | passed errp is error_fatal | 6 | accepting, but ignoring a file descriptor for compatibility with older |
7 | QEMU versions. | ||
6 | 8 | ||
7 | - drop error propagation, handle return code instead | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
8 | 10 | Message-Id: <20220407133657.155281-4-kwolf@redhat.com> | |
9 | - for symmetry, do error_prepend() for the second failure | 11 | Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com> |
10 | 12 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | |
11 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
12 | Message-Id: <20210920115538.264372-3-vsementsov@virtuozzo.com> | ||
13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
14 | --- | 14 | --- |
15 | block.c | 9 +++++---- | 15 | hw/virtio/vhost-user.c | 2 +- |
16 | 1 file changed, 5 insertions(+), 4 deletions(-) | 16 | subprojects/libvhost-user/libvhost-user.c | 8 ++++---- |
17 | 2 files changed, 5 insertions(+), 5 deletions(-) | ||
17 | 18 | ||
18 | diff --git a/block.c b/block.c | 19 | diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c |
19 | index XXXXXXX..XXXXXXX 100644 | 20 | index XXXXXXX..XXXXXXX 100644 |
20 | --- a/block.c | 21 | --- a/hw/virtio/vhost-user.c |
21 | +++ b/block.c | 22 | +++ b/hw/virtio/vhost-user.c |
22 | @@ -XXX,XX +XXX,XX @@ static void bdrv_delete(BlockDriverState *bs) | 23 | @@ -XXX,XX +XXX,XX @@ static int send_remove_regions(struct vhost_dev *dev, |
23 | BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, | 24 | vhost_user_fill_msg_region(®ion_buffer, shadow_reg, 0); |
24 | int flags, Error **errp) | 25 | msg->payload.mem_reg.region = region_buffer; |
25 | { | 26 | |
26 | + ERRP_GUARD(); | 27 | - ret = vhost_user_write(dev, msg, &fd, 1); |
27 | + int ret; | 28 | + ret = vhost_user_write(dev, msg, NULL, 0); |
28 | BlockDriverState *new_node_bs; | 29 | if (ret < 0) { |
29 | - Error *local_err = NULL; | 30 | return ret; |
30 | 31 | } | |
31 | new_node_bs = bdrv_open(NULL, NULL, node_options, flags, errp); | 32 | diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c |
32 | if (new_node_bs == NULL) { | 33 | index XXXXXXX..XXXXXXX 100644 |
33 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, | 34 | --- a/subprojects/libvhost-user/libvhost-user.c |
35 | +++ b/subprojects/libvhost-user/libvhost-user.c | ||
36 | @@ -XXX,XX +XXX,XX @@ vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { | ||
37 | int i; | ||
38 | bool found = false; | ||
39 | |||
40 | - if (vmsg->fd_num != 1) { | ||
41 | + if (vmsg->fd_num > 1) { | ||
42 | vmsg_close_fds(vmsg); | ||
43 | - vu_panic(dev, "VHOST_USER_REM_MEM_REG received %d fds - only 1 fd " | ||
44 | + vu_panic(dev, "VHOST_USER_REM_MEM_REG received %d fds - at most 1 fd " | ||
45 | "should be sent for this message type", vmsg->fd_num); | ||
46 | return false; | ||
34 | } | 47 | } |
35 | 48 | ||
36 | bdrv_drained_begin(bs); | 49 | if (vmsg->size < VHOST_USER_MEM_REG_SIZE) { |
37 | - bdrv_replace_node(bs, new_node_bs, &local_err); | 50 | - close(vmsg->fds[0]); |
38 | + ret = bdrv_replace_node(bs, new_node_bs, errp); | 51 | + vmsg_close_fds(vmsg); |
39 | bdrv_drained_end(bs); | 52 | vu_panic(dev, "VHOST_USER_REM_MEM_REG requires a message size of at " |
40 | 53 | "least %d bytes and only %d bytes were received", | |
41 | - if (local_err) { | 54 | VHOST_USER_MEM_REG_SIZE, vmsg->size); |
42 | + if (ret < 0) { | 55 | @@ -XXX,XX +XXX,XX @@ vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { |
43 | + error_prepend(errp, "Could not replace node: "); | 56 | vu_panic(dev, "Specified region not found\n"); |
44 | bdrv_unref(new_node_bs); | ||
45 | - error_propagate(errp, local_err); | ||
46 | return NULL; | ||
47 | } | 57 | } |
48 | 58 | ||
59 | - close(vmsg->fds[0]); | ||
60 | + vmsg_close_fds(vmsg); | ||
61 | |||
62 | return false; | ||
63 | } | ||
49 | -- | 64 | -- |
50 | 2.31.1 | 65 | 2.35.1 |
51 | |||
52 | diff view generated by jsdifflib |
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | - options & flags is common pair for open-like functions, let's use it | 3 | This function is safe to call in an I/O context, and qcow2_do_open() |
4 | - add a comment that specifies use of @options | 4 | does so (invoked in an I/O context by qcow2_co_invalidate_cache()). |
5 | 5 | ||
6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 6 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> |
7 | Message-Id: <20210920115538.264372-4-vsementsov@virtuozzo.com> | 7 | Message-Id: <20220427114057.36651-2-hreitz@redhat.com> |
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
9 | --- | 10 | --- |
10 | block.c | 13 +++++++++++-- | 11 | include/block/block-global-state.h | 1 - |
11 | 1 file changed, 11 insertions(+), 2 deletions(-) | 12 | include/block/block-io.h | 1 + |
13 | block.c | 2 +- | ||
14 | 3 files changed, 2 insertions(+), 2 deletions(-) | ||
12 | 15 | ||
16 | diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h | ||
17 | index XXXXXXX..XXXXXXX 100644 | ||
18 | --- a/include/block/block-global-state.h | ||
19 | +++ b/include/block/block-global-state.h | ||
20 | @@ -XXX,XX +XXX,XX @@ void bdrv_next_cleanup(BdrvNextIterator *it); | ||
21 | BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs); | ||
22 | void bdrv_iterate_format(void (*it)(void *opaque, const char *name), | ||
23 | void *opaque, bool read_only); | ||
24 | -int bdrv_get_flags(BlockDriverState *bs); | ||
25 | char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp); | ||
26 | char *bdrv_dirname(BlockDriverState *bs, Error **errp); | ||
27 | |||
28 | diff --git a/include/block/block-io.h b/include/block/block-io.h | ||
29 | index XXXXXXX..XXXXXXX 100644 | ||
30 | --- a/include/block/block-io.h | ||
31 | +++ b/include/block/block-io.h | ||
32 | @@ -XXX,XX +XXX,XX @@ int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg, | ||
33 | bool bdrv_is_read_only(BlockDriverState *bs); | ||
34 | bool bdrv_is_writable(BlockDriverState *bs); | ||
35 | bool bdrv_is_sg(BlockDriverState *bs); | ||
36 | +int bdrv_get_flags(BlockDriverState *bs); | ||
37 | bool bdrv_is_inserted(BlockDriverState *bs); | ||
38 | void bdrv_lock_medium(BlockDriverState *bs, bool locked); | ||
39 | void bdrv_eject(BlockDriverState *bs, bool eject_flag); | ||
13 | diff --git a/block.c b/block.c | 40 | diff --git a/block.c b/block.c |
14 | index XXXXXXX..XXXXXXX 100644 | 41 | index XXXXXXX..XXXXXXX 100644 |
15 | --- a/block.c | 42 | --- a/block.c |
16 | +++ b/block.c | 43 | +++ b/block.c |
17 | @@ -XXX,XX +XXX,XX @@ static void bdrv_delete(BlockDriverState *bs) | 44 | @@ -XXX,XX +XXX,XX @@ const char *bdrv_get_device_or_node_name(const BlockDriverState *bs) |
18 | g_free(bs); | 45 | |
46 | int bdrv_get_flags(BlockDriverState *bs) | ||
47 | { | ||
48 | - GLOBAL_STATE_CODE(); | ||
49 | + IO_CODE(); | ||
50 | return bs->open_flags; | ||
19 | } | 51 | } |
20 | 52 | ||
21 | -BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, | ||
22 | + | ||
23 | +/* | ||
24 | + * Replace @bs by newly created block node. | ||
25 | + * | ||
26 | + * @options is a QDict of options to pass to the block drivers, or NULL for an | ||
27 | + * empty set of options. The reference to the QDict belongs to the block layer | ||
28 | + * after the call (even on failure), so if the caller intends to reuse the | ||
29 | + * dictionary, it needs to use qobject_ref() before calling bdrv_open. | ||
30 | + */ | ||
31 | +BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, | ||
32 | int flags, Error **errp) | ||
33 | { | ||
34 | ERRP_GUARD(); | ||
35 | int ret; | ||
36 | BlockDriverState *new_node_bs; | ||
37 | |||
38 | - new_node_bs = bdrv_open(NULL, NULL, node_options, flags, errp); | ||
39 | + new_node_bs = bdrv_open(NULL, NULL, options, flags, errp); | ||
40 | if (new_node_bs == NULL) { | ||
41 | error_prepend(errp, "Could not create node: "); | ||
42 | return NULL; | ||
43 | -- | 53 | -- |
44 | 2.31.1 | 54 | 2.35.1 |
45 | |||
46 | diff view generated by jsdifflib |
1 | From: Paolo Bonzini <pbonzini@redhat.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Linux limits the size of iovecs to 1024 (UIO_MAXIOV in the kernel | 3 | qcow2_co_invalidate_cache() closes and opens the qcow2 file, by calling |
4 | sources, IOV_MAX in POSIX). Because of this, on some host adapters | 4 | qcow2_close() and qcow2_do_open(). These two functions must thus be |
5 | requests with many iovecs are rejected with -EINVAL by the | 5 | usable from both a global-state and an I/O context. |
6 | io_submit() or readv()/writev() system calls. | 6 | |
7 | 7 | As they are, they are not safe to call in an I/O context, because they | |
8 | In fact, the same limit applies to SG_IO as well. To fix both the | 8 | use bdrv_unref_child() and bdrv_open_child() to close/open the data_file |
9 | EINVAL and the possible performance issues from using fewer iovecs | 9 | child, respectively, both of which are global-state functions. When |
10 | than allowed by Linux (some HBAs have max_segments as low as 128), | 10 | used from qcow2_co_invalidate_cache(), we do not need to close/open the |
11 | introduce a separate entry in BlockLimits to hold the max_segments | 11 | data_file child, though (we do not do this for bs->file or bs->backing |
12 | value from sysfs. This new limit is used only for SG_IO and clamped | 12 | either), and so we should skip it in the qcow2_co_invalidate_cache() |
13 | to bs->bl.max_iov anyway, just like max_hw_transfer is clamped to | 13 | path. |
14 | bs->bl.max_transfer. | 14 | |
15 | 15 | To do so, add a parameter to qcow2_do_open() and qcow2_close() to make | |
16 | Reported-by: Halil Pasic <pasic@linux.ibm.com> | 16 | them skip handling s->data_file, and have qcow2_co_invalidate_cache() |
17 | Cc: Hanna Reitz <hreitz@redhat.com> | 17 | exempt it from the memset() on the BDRVQcow2State. |
18 | Cc: Kevin Wolf <kwolf@redhat.com> | 18 | |
19 | Cc: qemu-block@nongnu.org | 19 | (Note that the QED driver similarly closes/opens the QED image by |
20 | Cc: qemu-stable@nongnu.org | 20 | invoking bdrv_qed_close()+bdrv_qed_do_open(), but both functions seem |
21 | Fixes: 18473467d5 ("file-posix: try BLKSECTGET on block devices too, do not round to power of 2", 2021-06-25) | 21 | safe to use in an I/O context.) |
22 | Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> | 22 | |
23 | Message-Id: <20210923130436.1187591-1-pbonzini@redhat.com> | 23 | Fixes: https://gitlab.com/qemu-project/qemu/-/issues/945 |
24 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> | ||
25 | Message-Id: <20220427114057.36651-3-hreitz@redhat.com> | ||
26 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
24 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 27 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
25 | --- | 28 | --- |
26 | include/block/block_int.h | 7 +++++++ | 29 | block/qcow2.c | 104 ++++++++++++++++++++++++++++++-------------------- |
27 | include/sysemu/block-backend.h | 1 + | 30 | 1 file changed, 62 insertions(+), 42 deletions(-) |
28 | block/block-backend.c | 6 ++++++ | 31 | |
29 | block/file-posix.c | 2 +- | 32 | diff --git a/block/qcow2.c b/block/qcow2.c |
30 | block/io.c | 1 + | ||
31 | hw/scsi/scsi-generic.c | 2 +- | ||
32 | 6 files changed, 17 insertions(+), 2 deletions(-) | ||
33 | |||
34 | diff --git a/include/block/block_int.h b/include/block/block_int.h | ||
35 | index XXXXXXX..XXXXXXX 100644 | 33 | index XXXXXXX..XXXXXXX 100644 |
36 | --- a/include/block/block_int.h | 34 | --- a/block/qcow2.c |
37 | +++ b/include/block/block_int.h | 35 | +++ b/block/qcow2.c |
38 | @@ -XXX,XX +XXX,XX @@ typedef struct BlockLimits { | 36 | @@ -XXX,XX +XXX,XX @@ static int validate_compression_type(BDRVQcow2State *s, Error **errp) |
39 | */ | 37 | |
40 | uint64_t max_hw_transfer; | 38 | /* Called with s->lock held. */ |
41 | 39 | static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, | |
42 | + /* Maximal number of scatter/gather elements allowed by the hardware. | 40 | - int flags, Error **errp) |
43 | + * Applies whenever transfers to the device bypass the kernel I/O | 41 | + int flags, bool open_data_file, |
44 | + * scheduler, for example with SG_IO. If larger than max_iov | 42 | + Error **errp) |
45 | + * or if zero, blk_get_max_hw_iov will fall back to max_iov. | 43 | { |
46 | + */ | 44 | ERRP_GUARD(); |
47 | + int max_hw_iov; | 45 | BDRVQcow2State *s = bs->opaque; |
48 | + | 46 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, |
49 | /* memory alignment, in bytes so that no bounce buffer is needed */ | 47 | goto fail; |
50 | size_t min_mem_alignment; | 48 | } |
51 | 49 | ||
52 | diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h | 50 | - /* Open external data file */ |
53 | index XXXXXXX..XXXXXXX 100644 | 51 | - s->data_file = bdrv_open_child(NULL, options, "data-file", bs, |
54 | --- a/include/sysemu/block-backend.h | 52 | - &child_of_bds, BDRV_CHILD_DATA, |
55 | +++ b/include/sysemu/block-backend.h | 53 | - true, errp); |
56 | @@ -XXX,XX +XXX,XX @@ uint32_t blk_get_request_alignment(BlockBackend *blk); | 54 | - if (*errp) { |
57 | uint32_t blk_get_max_transfer(BlockBackend *blk); | 55 | - ret = -EINVAL; |
58 | uint64_t blk_get_max_hw_transfer(BlockBackend *blk); | 56 | - goto fail; |
59 | int blk_get_max_iov(BlockBackend *blk); | 57 | - } |
60 | +int blk_get_max_hw_iov(BlockBackend *blk); | 58 | + if (open_data_file) { |
61 | void blk_set_guest_block_size(BlockBackend *blk, int align); | 59 | + /* Open external data file */ |
62 | void *blk_try_blockalign(BlockBackend *blk, size_t size); | 60 | + s->data_file = bdrv_open_child(NULL, options, "data-file", bs, |
63 | void *blk_blockalign(BlockBackend *blk, size_t size); | 61 | + &child_of_bds, BDRV_CHILD_DATA, |
64 | diff --git a/block/block-backend.c b/block/block-backend.c | 62 | + true, errp); |
65 | index XXXXXXX..XXXXXXX 100644 | 63 | + if (*errp) { |
66 | --- a/block/block-backend.c | 64 | + ret = -EINVAL; |
67 | +++ b/block/block-backend.c | 65 | + goto fail; |
68 | @@ -XXX,XX +XXX,XX @@ uint32_t blk_get_max_transfer(BlockBackend *blk) | 66 | + } |
69 | return ROUND_DOWN(max, blk_get_request_alignment(blk)); | 67 | |
68 | - if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) { | ||
69 | - if (!s->data_file && s->image_data_file) { | ||
70 | - s->data_file = bdrv_open_child(s->image_data_file, options, | ||
71 | - "data-file", bs, &child_of_bds, | ||
72 | - BDRV_CHILD_DATA, false, errp); | ||
73 | + if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) { | ||
74 | + if (!s->data_file && s->image_data_file) { | ||
75 | + s->data_file = bdrv_open_child(s->image_data_file, options, | ||
76 | + "data-file", bs, &child_of_bds, | ||
77 | + BDRV_CHILD_DATA, false, errp); | ||
78 | + if (!s->data_file) { | ||
79 | + ret = -EINVAL; | ||
80 | + goto fail; | ||
81 | + } | ||
82 | + } | ||
83 | if (!s->data_file) { | ||
84 | + error_setg(errp, "'data-file' is required for this image"); | ||
85 | ret = -EINVAL; | ||
86 | goto fail; | ||
87 | } | ||
88 | - } | ||
89 | - if (!s->data_file) { | ||
90 | - error_setg(errp, "'data-file' is required for this image"); | ||
91 | - ret = -EINVAL; | ||
92 | - goto fail; | ||
93 | - } | ||
94 | |||
95 | - /* No data here */ | ||
96 | - bs->file->role &= ~BDRV_CHILD_DATA; | ||
97 | + /* No data here */ | ||
98 | + bs->file->role &= ~BDRV_CHILD_DATA; | ||
99 | |||
100 | - /* Must succeed because we have given up permissions if anything */ | ||
101 | - bdrv_child_refresh_perms(bs, bs->file, &error_abort); | ||
102 | - } else { | ||
103 | - if (s->data_file) { | ||
104 | - error_setg(errp, "'data-file' can only be set for images with an " | ||
105 | - "external data file"); | ||
106 | - ret = -EINVAL; | ||
107 | - goto fail; | ||
108 | - } | ||
109 | + /* Must succeed because we have given up permissions if anything */ | ||
110 | + bdrv_child_refresh_perms(bs, bs->file, &error_abort); | ||
111 | + } else { | ||
112 | + if (s->data_file) { | ||
113 | + error_setg(errp, "'data-file' can only be set for images with " | ||
114 | + "an external data file"); | ||
115 | + ret = -EINVAL; | ||
116 | + goto fail; | ||
117 | + } | ||
118 | |||
119 | - s->data_file = bs->file; | ||
120 | + s->data_file = bs->file; | ||
121 | |||
122 | - if (data_file_is_raw(bs)) { | ||
123 | - error_setg(errp, "data-file-raw requires a data file"); | ||
124 | - ret = -EINVAL; | ||
125 | - goto fail; | ||
126 | + if (data_file_is_raw(bs)) { | ||
127 | + error_setg(errp, "data-file-raw requires a data file"); | ||
128 | + ret = -EINVAL; | ||
129 | + goto fail; | ||
130 | + } | ||
131 | } | ||
132 | } | ||
133 | |||
134 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, | ||
135 | |||
136 | fail: | ||
137 | g_free(s->image_data_file); | ||
138 | - if (has_data_file(bs)) { | ||
139 | + if (open_data_file && has_data_file(bs)) { | ||
140 | bdrv_unref_child(bs, s->data_file); | ||
141 | s->data_file = NULL; | ||
142 | } | ||
143 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn qcow2_open_entry(void *opaque) | ||
144 | BDRVQcow2State *s = qoc->bs->opaque; | ||
145 | |||
146 | qemu_co_mutex_lock(&s->lock); | ||
147 | - qoc->ret = qcow2_do_open(qoc->bs, qoc->options, qoc->flags, qoc->errp); | ||
148 | + qoc->ret = qcow2_do_open(qoc->bs, qoc->options, qoc->flags, true, | ||
149 | + qoc->errp); | ||
150 | qemu_co_mutex_unlock(&s->lock); | ||
70 | } | 151 | } |
71 | 152 | ||
72 | +int blk_get_max_hw_iov(BlockBackend *blk) | 153 | @@ -XXX,XX +XXX,XX @@ static int qcow2_inactivate(BlockDriverState *bs) |
154 | return result; | ||
155 | } | ||
156 | |||
157 | -static void qcow2_close(BlockDriverState *bs) | ||
158 | +static void qcow2_do_close(BlockDriverState *bs, bool close_data_file) | ||
159 | { | ||
160 | BDRVQcow2State *s = bs->opaque; | ||
161 | qemu_vfree(s->l1_table); | ||
162 | @@ -XXX,XX +XXX,XX @@ static void qcow2_close(BlockDriverState *bs) | ||
163 | g_free(s->image_backing_file); | ||
164 | g_free(s->image_backing_format); | ||
165 | |||
166 | - if (has_data_file(bs)) { | ||
167 | + if (close_data_file && has_data_file(bs)) { | ||
168 | bdrv_unref_child(bs, s->data_file); | ||
169 | s->data_file = NULL; | ||
170 | } | ||
171 | @@ -XXX,XX +XXX,XX @@ static void qcow2_close(BlockDriverState *bs) | ||
172 | qcow2_free_snapshots(bs); | ||
173 | } | ||
174 | |||
175 | +static void qcow2_close(BlockDriverState *bs) | ||
73 | +{ | 176 | +{ |
74 | + return MIN_NON_ZERO(blk->root->bs->bl.max_hw_iov, | 177 | + qcow2_do_close(bs, true); |
75 | + blk->root->bs->bl.max_iov); | ||
76 | +} | 178 | +} |
77 | + | 179 | + |
78 | int blk_get_max_iov(BlockBackend *blk) | 180 | static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, |
181 | Error **errp) | ||
79 | { | 182 | { |
80 | return blk->root->bs->bl.max_iov; | 183 | ERRP_GUARD(); |
81 | diff --git a/block/file-posix.c b/block/file-posix.c | 184 | BDRVQcow2State *s = bs->opaque; |
82 | index XXXXXXX..XXXXXXX 100644 | 185 | + BdrvChild *data_file; |
83 | --- a/block/file-posix.c | 186 | int flags = s->flags; |
84 | +++ b/block/file-posix.c | 187 | QCryptoBlock *crypto = NULL; |
85 | @@ -XXX,XX +XXX,XX @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) | 188 | QDict *options; |
86 | 189 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, | |
87 | ret = hdev_get_max_segments(s->fd, &st); | 190 | crypto = s->crypto; |
88 | if (ret > 0) { | 191 | s->crypto = NULL; |
89 | - bs->bl.max_iov = ret; | 192 | |
90 | + bs->bl.max_hw_iov = ret; | 193 | - qcow2_close(bs); |
91 | } | 194 | + /* |
92 | } | 195 | + * Do not reopen s->data_file (i.e., have qcow2_do_close() not close it, |
93 | } | 196 | + * and then prevent qcow2_do_open() from opening it), because this function |
94 | diff --git a/block/io.c b/block/io.c | 197 | + * runs in the I/O path and as such we must not invoke global-state |
95 | index XXXXXXX..XXXXXXX 100644 | 198 | + * functions like bdrv_unref_child() and bdrv_open_child(). |
96 | --- a/block/io.c | 199 | + */ |
97 | +++ b/block/io.c | 200 | |
98 | @@ -XXX,XX +XXX,XX @@ static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) | 201 | + qcow2_do_close(bs, false); |
99 | dst->min_mem_alignment = MAX(dst->min_mem_alignment, | 202 | + |
100 | src->min_mem_alignment); | 203 | + data_file = s->data_file; |
101 | dst->max_iov = MIN_NON_ZERO(dst->max_iov, src->max_iov); | 204 | memset(s, 0, sizeof(BDRVQcow2State)); |
102 | + dst->max_hw_iov = MIN_NON_ZERO(dst->max_hw_iov, src->max_hw_iov); | 205 | + s->data_file = data_file; |
103 | } | 206 | + |
104 | 207 | options = qdict_clone_shallow(bs->options); | |
105 | typedef struct BdrvRefreshLimitsState { | 208 | |
106 | diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c | 209 | flags &= ~BDRV_O_INACTIVE; |
107 | index XXXXXXX..XXXXXXX 100644 | 210 | qemu_co_mutex_lock(&s->lock); |
108 | --- a/hw/scsi/scsi-generic.c | 211 | - ret = qcow2_do_open(bs, options, flags, errp); |
109 | +++ b/hw/scsi/scsi-generic.c | 212 | + ret = qcow2_do_open(bs, options, flags, false, errp); |
110 | @@ -XXX,XX +XXX,XX @@ static int scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s, int len) | 213 | qemu_co_mutex_unlock(&s->lock); |
111 | page = r->req.cmd.buf[2]; | 214 | qobject_unref(options); |
112 | if (page == 0xb0) { | 215 | if (ret < 0) { |
113 | uint64_t max_transfer = blk_get_max_hw_transfer(s->conf.blk); | ||
114 | - uint32_t max_iov = blk_get_max_iov(s->conf.blk); | ||
115 | + uint32_t max_iov = blk_get_max_hw_iov(s->conf.blk); | ||
116 | |||
117 | assert(max_transfer); | ||
118 | max_transfer = MIN_NON_ZERO(max_transfer, max_iov * qemu_real_host_page_size) | ||
119 | -- | 216 | -- |
120 | 2.31.1 | 217 | 2.35.1 |
121 | |||
122 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | 1. Ignore the new f-strings warning, we're not interested in doing a | 3 | This reverts commit b1c073490553f80594b903ceedfc7c1aef6b1b19. (We |
4 | full conversion at this time. | 4 | wanted to do so once the 7.1 tree opens, which has happened. The issue |
5 | reported in https://gitlab.com/qemu-project/qemu/-/issues/945 should be | ||
6 | fixed by the preceding patches.) | ||
5 | 7 | ||
6 | 2. Just mute the unbalanced-tuple-unpacking warning, it's not a real | 8 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> |
7 | error in this case and muting the dozens of callsites is just not | 9 | Message-Id: <20220427114057.36651-4-hreitz@redhat.com> |
8 | worth it. | 10 | Reviewed-by: Eric Blake <eblake@redhat.com> |
9 | |||
10 | 3. Add encodings to read_text(). | ||
11 | |||
12 | Signed-off-by: John Snow <jsnow@redhat.com> | ||
13 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
14 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
15 | Message-Id: <20210923180715.4168522-7-jsnow@redhat.com> | ||
16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
17 | --- | 12 | --- |
18 | tests/qemu-iotests/testrunner.py | 7 ++++--- | 13 | include/qemu/main-loop.h | 3 +-- |
19 | tests/qemu-iotests/pylintrc | 6 +++++- | 14 | 1 file changed, 1 insertion(+), 2 deletions(-) |
20 | 2 files changed, 9 insertions(+), 4 deletions(-) | ||
21 | 15 | ||
22 | diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py | 16 | diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h |
23 | index XXXXXXX..XXXXXXX 100644 | 17 | index XXXXXXX..XXXXXXX 100644 |
24 | --- a/tests/qemu-iotests/testrunner.py | 18 | --- a/include/qemu/main-loop.h |
25 | +++ b/tests/qemu-iotests/testrunner.py | 19 | +++ b/include/qemu/main-loop.h |
26 | @@ -XXX,XX +XXX,XX @@ def do_run_test(self, test: str) -> TestResult: | 20 | @@ -XXX,XX +XXX,XX @@ bool qemu_in_main_thread(void); |
27 | diff=file_diff(str(f_reference), str(f_bad))) | 21 | #else |
28 | 22 | #define GLOBAL_STATE_CODE() \ | |
29 | if f_notrun.exists(): | 23 | do { \ |
30 | - return TestResult(status='not run', | 24 | - /* FIXME: Re-enable after 7.0 release */ \ |
31 | - description=f_notrun.read_text().strip()) | 25 | - /* assert(qemu_in_main_thread()); */ \ |
32 | + return TestResult( | 26 | + assert(qemu_in_main_thread()); \ |
33 | + status='not run', | 27 | } while (0) |
34 | + description=f_notrun.read_text(encoding='utf-8').strip()) | 28 | #endif /* CONFIG_COCOA */ |
35 | |||
36 | casenotrun = '' | ||
37 | if f_casenotrun.exists(): | ||
38 | - casenotrun = f_casenotrun.read_text() | ||
39 | + casenotrun = f_casenotrun.read_text(encoding='utf-8') | ||
40 | |||
41 | diff = file_diff(str(f_reference), str(f_bad)) | ||
42 | if diff: | ||
43 | diff --git a/tests/qemu-iotests/pylintrc b/tests/qemu-iotests/pylintrc | ||
44 | index XXXXXXX..XXXXXXX 100644 | ||
45 | --- a/tests/qemu-iotests/pylintrc | ||
46 | +++ b/tests/qemu-iotests/pylintrc | ||
47 | @@ -XXX,XX +XXX,XX @@ disable=invalid-name, | ||
48 | too-many-public-methods, | ||
49 | # pylint warns about Optional[] etc. as unsubscriptable in 3.9 | ||
50 | unsubscriptable-object, | ||
51 | + # pylint's static analysis causes false positivies for file_path(); | ||
52 | + # If we really care to make it statically knowable, we'll use mypy. | ||
53 | + unbalanced-tuple-unpacking, | ||
54 | # Sometimes we need to disable a newly introduced pylint warning. | ||
55 | # Doing so should not produce a warning in older versions of pylint. | ||
56 | bad-option-value, | ||
57 | # These are temporary, and should be removed: | ||
58 | missing-docstring, | ||
59 | too-many-return-statements, | ||
60 | - too-many-statements | ||
61 | + too-many-statements, | ||
62 | + consider-using-f-string, | ||
63 | |||
64 | [FORMAT] | ||
65 | 29 | ||
66 | -- | 30 | -- |
67 | 2.31.1 | 31 | 2.35.1 |
68 | |||
69 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | We can circumvent the '__main__' redefinition problem by passing | 3 | Create a VM with a BDS in an iothread, add -incoming defer to the |
4 | --scripts-are-modules. Take mypy out of the loop per-filename and check | 4 | command line, and then export this BDS via NBD. Doing so should not |
5 | everything in one go: it's quite a bit faster. | 5 | fail an assertion. |
6 | 6 | ||
7 | Signed-off-by: John Snow <jsnow@redhat.com> | 7 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> |
8 | Reviewed-by: Hanna Reitz <hreitz@redhat.com> | 8 | Message-Id: <20220427114057.36651-5-hreitz@redhat.com> |
9 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | 9 | Reviewed-by: Eric Blake <eblake@redhat.com> |
10 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 10 | Tested-by: Eric Blake <eblake@redhat.com> |
11 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
12 | Message-Id: <20210923180715.4168522-4-jsnow@redhat.com> | ||
13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
14 | --- | 12 | --- |
15 | tests/qemu-iotests/297 | 46 +++++++++++++++++++----------------------- | 13 | .../tests/export-incoming-iothread | 81 +++++++++++++++++++ |
16 | 1 file changed, 21 insertions(+), 25 deletions(-) | 14 | .../tests/export-incoming-iothread.out | 5 ++ |
15 | 2 files changed, 86 insertions(+) | ||
16 | create mode 100755 tests/qemu-iotests/tests/export-incoming-iothread | ||
17 | create mode 100644 tests/qemu-iotests/tests/export-incoming-iothread.out | ||
17 | 18 | ||
18 | diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297 | 19 | diff --git a/tests/qemu-iotests/tests/export-incoming-iothread b/tests/qemu-iotests/tests/export-incoming-iothread |
19 | index XXXXXXX..XXXXXXX 100755 | 20 | new file mode 100755 |
20 | --- a/tests/qemu-iotests/297 | 21 | index XXXXXXX..XXXXXXX |
21 | +++ b/tests/qemu-iotests/297 | 22 | --- /dev/null |
22 | @@ -XXX,XX +XXX,XX @@ def run_linters(): | 23 | +++ b/tests/qemu-iotests/tests/export-incoming-iothread |
23 | print('=== mypy ===') | 24 | @@ -XXX,XX +XXX,XX @@ |
24 | sys.stdout.flush() | 25 | +#!/usr/bin/env python3 |
25 | 26 | +# group: rw quick migration | |
26 | - # We have to call mypy separately for each file. Otherwise, it | 27 | +# |
27 | - # will interpret all given files as belonging together (i.e., they | 28 | +# Regression test for issue 945: |
28 | - # may not both define the same classes, etc.; most notably, they | 29 | +# https://gitlab.com/qemu-project/qemu/-/issues/945 |
29 | - # must not both define the __main__ module). | 30 | +# Test adding an export on top of an iothread-ed block device while in |
30 | env['MYPYPATH'] = env['PYTHONPATH'] | 31 | +# -incoming defer. |
31 | - for filename in files: | 32 | +# |
32 | - p = subprocess.run(('mypy', | 33 | +# Copyright (C) 2022 Red Hat, Inc. |
33 | - '--warn-unused-configs', | 34 | +# |
34 | - '--disallow-subclassing-any', | 35 | +# This program is free software; you can redistribute it and/or modify |
35 | - '--disallow-any-generics', | 36 | +# it under the terms of the GNU General Public License as published by |
36 | - '--disallow-incomplete-defs', | 37 | +# the Free Software Foundation; either version 2 of the License, or |
37 | - '--disallow-untyped-decorators', | 38 | +# (at your option) any later version. |
38 | - '--no-implicit-optional', | 39 | +# |
39 | - '--warn-redundant-casts', | 40 | +# This program is distributed in the hope that it will be useful, |
40 | - '--warn-unused-ignores', | 41 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
41 | - '--no-implicit-reexport', | 42 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
42 | - '--namespace-packages', | 43 | +# GNU General Public License for more details. |
43 | - filename), | 44 | +# |
44 | - env=env, | 45 | +# You should have received a copy of the GNU General Public License |
45 | - check=False, | 46 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
46 | - stdout=subprocess.PIPE, | 47 | +# |
47 | - stderr=subprocess.STDOUT, | ||
48 | - universal_newlines=True) | ||
49 | - | ||
50 | - if p.returncode != 0: | ||
51 | - print(p.stdout) | ||
52 | + p = subprocess.run(('mypy', | ||
53 | + '--warn-unused-configs', | ||
54 | + '--disallow-subclassing-any', | ||
55 | + '--disallow-any-generics', | ||
56 | + '--disallow-incomplete-defs', | ||
57 | + '--disallow-untyped-decorators', | ||
58 | + '--no-implicit-optional', | ||
59 | + '--warn-redundant-casts', | ||
60 | + '--warn-unused-ignores', | ||
61 | + '--no-implicit-reexport', | ||
62 | + '--namespace-packages', | ||
63 | + '--scripts-are-modules', | ||
64 | + *files), | ||
65 | + env=env, | ||
66 | + check=False, | ||
67 | + stdout=subprocess.PIPE, | ||
68 | + stderr=subprocess.STDOUT, | ||
69 | + universal_newlines=True) | ||
70 | + | 48 | + |
71 | + if p.returncode != 0: | 49 | +import os |
72 | + print(p.stdout) | 50 | +import iotests |
73 | 51 | +from iotests import qemu_img_create | |
74 | 52 | + | |
75 | for linter in ('pylint-3', 'mypy'): | 53 | + |
54 | +image_size = 1 * 1024 * 1024 | ||
55 | +test_img = os.path.join(iotests.test_dir, 'test.img') | ||
56 | +node_name = 'node0' | ||
57 | +iothread_id = 'iothr0' | ||
58 | + | ||
59 | +nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock') | ||
60 | + | ||
61 | + | ||
62 | +class TestExportIncomingIothread(iotests.QMPTestCase): | ||
63 | + def setUp(self) -> None: | ||
64 | + qemu_img_create('-f', iotests.imgfmt, test_img, str(image_size)) | ||
65 | + | ||
66 | + self.vm = iotests.VM() | ||
67 | + self.vm.add_object(f'iothread,id={iothread_id}') | ||
68 | + self.vm.add_blockdev(( | ||
69 | + f'driver={iotests.imgfmt}', | ||
70 | + f'node-name={node_name}', | ||
71 | + 'file.driver=file', | ||
72 | + f'file.filename={test_img}' | ||
73 | + )) | ||
74 | + self.vm.add_incoming('defer') | ||
75 | + self.vm.launch() | ||
76 | + | ||
77 | + def tearDown(self): | ||
78 | + self.vm.shutdown() | ||
79 | + os.remove(test_img) | ||
80 | + | ||
81 | + def test_export_add(self): | ||
82 | + result = self.vm.qmp('nbd-server-start', { | ||
83 | + 'addr': { | ||
84 | + 'type': 'unix', | ||
85 | + 'data': { | ||
86 | + 'path': nbd_sock | ||
87 | + } | ||
88 | + } | ||
89 | + }) | ||
90 | + self.assert_qmp(result, 'return', {}) | ||
91 | + | ||
92 | + # Regression test for issue 945: This should not fail an assertion | ||
93 | + result = self.vm.qmp('block-export-add', { | ||
94 | + 'type': 'nbd', | ||
95 | + 'id': 'exp0', | ||
96 | + 'node-name': node_name, | ||
97 | + 'iothread': iothread_id | ||
98 | + }) | ||
99 | + self.assert_qmp(result, 'return', {}) | ||
100 | + | ||
101 | + | ||
102 | +if __name__ == '__main__': | ||
103 | + iotests.main(supported_fmts=['generic'], | ||
104 | + unsupported_fmts=['luks'], # Would need a secret | ||
105 | + supported_protocols=['file']) | ||
106 | diff --git a/tests/qemu-iotests/tests/export-incoming-iothread.out b/tests/qemu-iotests/tests/export-incoming-iothread.out | ||
107 | new file mode 100644 | ||
108 | index XXXXXXX..XXXXXXX | ||
109 | --- /dev/null | ||
110 | +++ b/tests/qemu-iotests/tests/export-incoming-iothread.out | ||
111 | @@ -XXX,XX +XXX,XX @@ | ||
112 | +. | ||
113 | +---------------------------------------------------------------------- | ||
114 | +Ran 1 tests | ||
115 | + | ||
116 | +OK | ||
76 | -- | 117 | -- |
77 | 2.31.1 | 118 | 2.35.1 |
78 | |||
79 | diff view generated by jsdifflib |
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Add version of bdrv_new_open_driver() that supports QDict options. | 3 | VMDK disk data is stored in extents, which may or may not be separate |
4 | We'll use it in further commit. | 4 | from bs->file. VmdkExtent.file points to where they are stored. Each |
5 | that is stored in bs->file will simply reuse the exact pointer value of | ||
6 | bs->file. | ||
5 | 7 | ||
6 | Simply add one more argument to bdrv_new_open_driver() is worse, as | 8 | (That is why vmdk_free_extents() will unref VmdkExtent.file (e->file) |
7 | there are too many invocations of bdrv_new_open_driver() to update | 9 | only if e->file != bs->file.) |
8 | then. | ||
9 | 10 | ||
10 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 11 | Reopen operations can change bs->file (they will replace the whole |
11 | Suggested-by: Kevin Wolf <kwolf@redhat.com> | 12 | BdrvChild object, not just the BDS stored in that BdrvChild), and then |
12 | Message-Id: <20210920115538.264372-2-vsementsov@virtuozzo.com> | 13 | we will need to change all .file pointers of all such VmdkExtents to |
14 | point to the new BdrvChild. | ||
15 | |||
16 | In vmdk_reopen_prepare(), we have to check which VmdkExtents are | ||
17 | affected, and in vmdk_reopen_commit(), we can modify them. We have to | ||
18 | split this because: | ||
19 | - The new BdrvChild is created only after prepare, so we can change | ||
20 | VmdkExtent.file only in commit | ||
21 | - In commit, there no longer is any (valid) reference to the old | ||
22 | BdrvChild object, so there would be nothing to compare VmdkExtent.file | ||
23 | against to see whether it was equal to bs->file before reopening | ||
24 | (There is BDRVReopenState.old_file_bs, but the old bs->file | ||
25 | BdrvChild's .bs pointer will be NULL-ed when the new BdrvChild is | ||
26 | created, and so we cannot compare VmdkExtent.file->bs against | ||
27 | BDRVReopenState.old_file_bs) | ||
28 | |||
29 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> | ||
30 | Message-Id: <20220314162719.65384-2-hreitz@redhat.com> | ||
13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 31 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
14 | --- | 32 | --- |
15 | include/block/block.h | 4 ++++ | 33 | block/vmdk.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++- |
16 | block.c | 25 +++++++++++++++++++++---- | 34 | 1 file changed, 55 insertions(+), 1 deletion(-) |
17 | 2 files changed, 25 insertions(+), 4 deletions(-) | ||
18 | 35 | ||
19 | diff --git a/include/block/block.h b/include/block/block.h | 36 | diff --git a/block/vmdk.c b/block/vmdk.c |
20 | index XXXXXXX..XXXXXXX 100644 | 37 | index XXXXXXX..XXXXXXX 100644 |
21 | --- a/include/block/block.h | 38 | --- a/block/vmdk.c |
22 | +++ b/include/block/block.h | 39 | +++ b/block/vmdk.c |
23 | @@ -XXX,XX +XXX,XX @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, | 40 | @@ -XXX,XX +XXX,XX @@ typedef struct BDRVVmdkState { |
24 | const char *bdref_key, Error **errp); | 41 | char *create_type; |
25 | BlockDriverState *bdrv_open(const char *filename, const char *reference, | 42 | } BDRVVmdkState; |
26 | QDict *options, int flags, Error **errp); | 43 | |
27 | +BlockDriverState *bdrv_new_open_driver_opts(BlockDriver *drv, | 44 | +typedef struct BDRVVmdkReopenState { |
28 | + const char *node_name, | 45 | + bool *extents_using_bs_file; |
29 | + QDict *options, int flags, | 46 | +} BDRVVmdkReopenState; |
30 | + Error **errp); | 47 | + |
31 | BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, | 48 | typedef struct VmdkMetaData { |
32 | int flags, Error **errp); | 49 | unsigned int l1_index; |
33 | BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, | 50 | unsigned int l2_index; |
34 | diff --git a/block.c b/block.c | 51 | @@ -XXX,XX +XXX,XX @@ static int vmdk_is_cid_valid(BlockDriverState *bs) |
35 | index XXXXXXX..XXXXXXX 100644 | 52 | return 1; |
36 | --- a/block.c | ||
37 | +++ b/block.c | ||
38 | @@ -XXX,XX +XXX,XX @@ open_failed: | ||
39 | return ret; | ||
40 | } | 53 | } |
41 | 54 | ||
42 | -BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, | 55 | -/* We have nothing to do for VMDK reopen, stubs just return success */ |
43 | - int flags, Error **errp) | 56 | static int vmdk_reopen_prepare(BDRVReopenState *state, |
44 | +/* | 57 | BlockReopenQueue *queue, Error **errp) |
45 | + * Create and open a block node. | ||
46 | + * | ||
47 | + * @options is a QDict of options to pass to the block drivers, or NULL for an | ||
48 | + * empty set of options. The reference to the QDict belongs to the block layer | ||
49 | + * after the call (even on failure), so if the caller intends to reuse the | ||
50 | + * dictionary, it needs to use qobject_ref() before calling bdrv_open. | ||
51 | + */ | ||
52 | +BlockDriverState *bdrv_new_open_driver_opts(BlockDriver *drv, | ||
53 | + const char *node_name, | ||
54 | + QDict *options, int flags, | ||
55 | + Error **errp) | ||
56 | { | 58 | { |
57 | BlockDriverState *bs; | 59 | + BDRVVmdkState *s; |
58 | int ret; | 60 | + BDRVVmdkReopenState *rs; |
59 | 61 | + int i; | |
60 | bs = bdrv_new(); | 62 | + |
61 | bs->open_flags = flags; | 63 | assert(state != NULL); |
62 | - bs->explicit_options = qdict_new(); | 64 | assert(state->bs != NULL); |
63 | - bs->options = qdict_new(); | 65 | + assert(state->opaque == NULL); |
64 | + bs->options = options ?: qdict_new(); | 66 | + |
65 | + bs->explicit_options = qdict_clone_shallow(bs->options); | 67 | + s = state->bs->opaque; |
66 | bs->opaque = NULL; | 68 | + |
67 | 69 | + rs = g_new0(BDRVVmdkReopenState, 1); | |
68 | update_options_from_flags(bs->options, flags); | 70 | + state->opaque = rs; |
69 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, | 71 | + |
70 | return bs; | 72 | + /* |
73 | + * Check whether there are any extents stored in bs->file; if bs->file | ||
74 | + * changes, we will need to update their .file pointers to follow suit | ||
75 | + */ | ||
76 | + rs->extents_using_bs_file = g_new(bool, s->num_extents); | ||
77 | + for (i = 0; i < s->num_extents; i++) { | ||
78 | + rs->extents_using_bs_file[i] = s->extents[i].file == state->bs->file; | ||
79 | + } | ||
80 | + | ||
81 | return 0; | ||
71 | } | 82 | } |
72 | 83 | ||
73 | +/* Create and open a block node. */ | 84 | +static void vmdk_reopen_clean(BDRVReopenState *state) |
74 | +BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, | ||
75 | + int flags, Error **errp) | ||
76 | +{ | 85 | +{ |
77 | + return bdrv_new_open_driver_opts(drv, node_name, NULL, flags, errp); | 86 | + BDRVVmdkReopenState *rs = state->opaque; |
87 | + | ||
88 | + g_free(rs->extents_using_bs_file); | ||
89 | + g_free(rs); | ||
90 | + state->opaque = NULL; | ||
78 | +} | 91 | +} |
79 | + | 92 | + |
80 | QemuOptsList bdrv_runtime_opts = { | 93 | +static void vmdk_reopen_commit(BDRVReopenState *state) |
81 | .name = "bdrv_common", | 94 | +{ |
82 | .head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head), | 95 | + BDRVVmdkState *s = state->bs->opaque; |
96 | + BDRVVmdkReopenState *rs = state->opaque; | ||
97 | + int i; | ||
98 | + | ||
99 | + for (i = 0; i < s->num_extents; i++) { | ||
100 | + if (rs->extents_using_bs_file[i]) { | ||
101 | + s->extents[i].file = state->bs->file; | ||
102 | + } | ||
103 | + } | ||
104 | + | ||
105 | + vmdk_reopen_clean(state); | ||
106 | +} | ||
107 | + | ||
108 | +static void vmdk_reopen_abort(BDRVReopenState *state) | ||
109 | +{ | ||
110 | + vmdk_reopen_clean(state); | ||
111 | +} | ||
112 | + | ||
113 | static int vmdk_parent_open(BlockDriverState *bs) | ||
114 | { | ||
115 | char *p_name; | ||
116 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vmdk = { | ||
117 | .bdrv_open = vmdk_open, | ||
118 | .bdrv_co_check = vmdk_co_check, | ||
119 | .bdrv_reopen_prepare = vmdk_reopen_prepare, | ||
120 | + .bdrv_reopen_commit = vmdk_reopen_commit, | ||
121 | + .bdrv_reopen_abort = vmdk_reopen_abort, | ||
122 | .bdrv_child_perm = bdrv_default_perms, | ||
123 | .bdrv_co_preadv = vmdk_co_preadv, | ||
124 | .bdrv_co_pwritev = vmdk_co_pwritev, | ||
83 | -- | 125 | -- |
84 | 2.31.1 | 126 | 2.35.1 |
85 | |||
86 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Mostly uninteresting stuff. Move the test injections under a function | 3 | This should work for all format drivers that support reopening, so test |
4 | named main() so that the variables used during that process aren't in | 4 | it. |
5 | the global scope. | ||
6 | 5 | ||
7 | Signed-off-by: John Snow <jsnow@redhat.com> | 6 | (This serves as a regression test for HEAD^: This test used to fail for |
8 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | 7 | VMDK before HEAD^.) |
9 | Reviewed-by: Hanna Reitz <hreitz@redhat.com> | 8 | |
10 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 9 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> |
11 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 10 | Message-Id: <20220314162719.65384-3-hreitz@redhat.com> |
12 | Message-Id: <20210923180715.4168522-6-jsnow@redhat.com> | ||
13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
14 | --- | 12 | --- |
15 | tests/qemu-iotests/tests/migrate-bitmaps-test | 50 +++++++++++-------- | 13 | tests/qemu-iotests/tests/reopen-file | 89 ++++++++++++++++++++++++ |
16 | 1 file changed, 28 insertions(+), 22 deletions(-) | 14 | tests/qemu-iotests/tests/reopen-file.out | 5 ++ |
15 | 2 files changed, 94 insertions(+) | ||
16 | create mode 100755 tests/qemu-iotests/tests/reopen-file | ||
17 | create mode 100644 tests/qemu-iotests/tests/reopen-file.out | ||
17 | 18 | ||
18 | diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test b/tests/qemu-iotests/tests/migrate-bitmaps-test | 19 | diff --git a/tests/qemu-iotests/tests/reopen-file b/tests/qemu-iotests/tests/reopen-file |
19 | index XXXXXXX..XXXXXXX 100755 | 20 | new file mode 100755 |
20 | --- a/tests/qemu-iotests/tests/migrate-bitmaps-test | 21 | index XXXXXXX..XXXXXXX |
21 | +++ b/tests/qemu-iotests/tests/migrate-bitmaps-test | 22 | --- /dev/null |
23 | +++ b/tests/qemu-iotests/tests/reopen-file | ||
22 | @@ -XXX,XX +XXX,XX @@ | 24 | @@ -XXX,XX +XXX,XX @@ |
23 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | 25 | +#!/usr/bin/env python3 |
24 | # | 26 | +# group: rw quick |
25 | 27 | +# | |
26 | -import os | 28 | +# Test reopening a format driver's file child |
27 | import itertools | 29 | +# |
28 | import operator | 30 | +# Copyright (C) 2022 Red Hat, Inc. |
31 | +# | ||
32 | +# This program is free software; you can redistribute it and/or modify | ||
33 | +# it under the terms of the GNU General Public License as published by | ||
34 | +# the Free Software Foundation; either version 2 of the License, or | ||
35 | +# (at your option) any later version. | ||
36 | +# | ||
37 | +# This program is distributed in the hope that it will be useful, | ||
38 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
39 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
40 | +# GNU General Public License for more details. | ||
41 | +# | ||
42 | +# You should have received a copy of the GNU General Public License | ||
43 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
44 | +# | ||
45 | + | ||
29 | +import os | 46 | +import os |
30 | import re | 47 | +import iotests |
31 | + | 48 | +from iotests import imgfmt, qemu_img_create, QMPTestCase |
32 | import iotests | ||
33 | from iotests import qemu_img, qemu_img_create, Timeout | ||
34 | |||
35 | @@ -XXX,XX +XXX,XX @@ def inject_test_case(klass, suffix, method, *args, **kwargs): | ||
36 | setattr(klass, 'test_' + method + suffix, lambda self: mc(self)) | ||
37 | |||
38 | |||
39 | -for cmb in list(itertools.product((True, False), repeat=5)): | ||
40 | - name = ('_' if cmb[0] else '_not_') + 'persistent_' | ||
41 | - name += ('_' if cmb[1] else '_not_') + 'migbitmap_' | ||
42 | - name += '_online' if cmb[2] else '_offline' | ||
43 | - name += '_shared' if cmb[3] else '_nonshared' | ||
44 | - if cmb[4]: | ||
45 | - name += '__pre_shutdown' | ||
46 | - | ||
47 | - inject_test_case(TestDirtyBitmapMigration, name, 'do_test_migration', | ||
48 | - *list(cmb)) | ||
49 | - | ||
50 | -for cmb in list(itertools.product((True, False), repeat=2)): | ||
51 | - name = ('_' if cmb[0] else '_not_') + 'persistent_' | ||
52 | - name += ('_' if cmb[1] else '_not_') + 'migbitmap' | ||
53 | - | ||
54 | - inject_test_case(TestDirtyBitmapMigration, name, | ||
55 | - 'do_test_migration_resume_source', *list(cmb)) | ||
56 | - | ||
57 | - | ||
58 | class TestDirtyBitmapBackingMigration(iotests.QMPTestCase): | ||
59 | def setUp(self): | ||
60 | qemu_img_create('-f', iotests.imgfmt, base_a, size) | ||
61 | @@ -XXX,XX +XXX,XX @@ class TestDirtyBitmapBackingMigration(iotests.QMPTestCase): | ||
62 | self.assert_qmp(result, 'return', {}) | ||
63 | |||
64 | |||
65 | +def main() -> None: | ||
66 | + for cmb in list(itertools.product((True, False), repeat=5)): | ||
67 | + name = ('_' if cmb[0] else '_not_') + 'persistent_' | ||
68 | + name += ('_' if cmb[1] else '_not_') + 'migbitmap_' | ||
69 | + name += '_online' if cmb[2] else '_offline' | ||
70 | + name += '_shared' if cmb[3] else '_nonshared' | ||
71 | + if cmb[4]: | ||
72 | + name += '__pre_shutdown' | ||
73 | + | ||
74 | + inject_test_case(TestDirtyBitmapMigration, name, 'do_test_migration', | ||
75 | + *list(cmb)) | ||
76 | + | ||
77 | + for cmb in list(itertools.product((True, False), repeat=2)): | ||
78 | + name = ('_' if cmb[0] else '_not_') + 'persistent_' | ||
79 | + name += ('_' if cmb[1] else '_not_') + 'migbitmap' | ||
80 | + | ||
81 | + inject_test_case(TestDirtyBitmapMigration, name, | ||
82 | + 'do_test_migration_resume_source', *list(cmb)) | ||
83 | + | ||
84 | + iotests.main( | ||
85 | + supported_fmts=['qcow2'], | ||
86 | + supported_protocols=['file'] | ||
87 | + ) | ||
88 | + | 49 | + |
89 | + | 50 | + |
90 | if __name__ == '__main__': | 51 | +image_size = 1 * 1024 * 1024 |
91 | - iotests.main(supported_fmts=['qcow2'], | 52 | +test_img = os.path.join(iotests.test_dir, 'test.img') |
92 | - supported_protocols=['file']) | 53 | + |
93 | + main() | 54 | + |
55 | +class TestReopenFile(QMPTestCase): | ||
56 | + def setUp(self) -> None: | ||
57 | + res = qemu_img_create('-f', imgfmt, test_img, str(image_size)) | ||
58 | + assert res.returncode == 0 | ||
59 | + | ||
60 | + # Add format driver node ('format') on top of the file ('file'), then | ||
61 | + # add another raw node ('raw') on top of 'file' so for the reopen we | ||
62 | + # can just switch from 'file' to 'raw' | ||
63 | + self.vm = iotests.VM() | ||
64 | + self.vm.add_blockdev(self.vm.qmp_to_opts({ | ||
65 | + 'driver': imgfmt, | ||
66 | + 'node-name': 'format', | ||
67 | + 'file': { | ||
68 | + 'driver': 'file', | ||
69 | + 'node-name': 'file', | ||
70 | + 'filename': test_img | ||
71 | + } | ||
72 | + })) | ||
73 | + self.vm.add_blockdev(self.vm.qmp_to_opts({ | ||
74 | + 'driver': 'raw', | ||
75 | + 'node-name': 'raw', | ||
76 | + 'file': 'file' | ||
77 | + })) | ||
78 | + self.vm.launch() | ||
79 | + | ||
80 | + def tearDown(self) -> None: | ||
81 | + self.vm.shutdown() | ||
82 | + os.remove(test_img) | ||
83 | + | ||
84 | + # Check if there was any qemu-io run that failed | ||
85 | + if 'Pattern verification failed' in self.vm.get_log(): | ||
86 | + print('ERROR: Pattern verification failed:') | ||
87 | + print(self.vm.get_log()) | ||
88 | + self.fail('qemu-io pattern verification failed') | ||
89 | + | ||
90 | + def test_reopen_file(self) -> None: | ||
91 | + result = self.vm.qmp('blockdev-reopen', options=[{ | ||
92 | + 'driver': imgfmt, | ||
93 | + 'node-name': 'format', | ||
94 | + 'file': 'raw' | ||
95 | + }]) | ||
96 | + self.assert_qmp(result, 'return', {}) | ||
97 | + | ||
98 | + # Do some I/O to the image to see whether it still works | ||
99 | + # (Pattern verification will be checked by tearDown()) | ||
100 | + result = self.vm.qmp('human-monitor-command', | ||
101 | + command_line='qemu-io format "write -P 42 0 64k"') | ||
102 | + self.assert_qmp(result, 'return', '') | ||
103 | + | ||
104 | + result = self.vm.qmp('human-monitor-command', | ||
105 | + command_line='qemu-io format "read -P 42 0 64k"') | ||
106 | + self.assert_qmp(result, 'return', '') | ||
107 | + | ||
108 | + | ||
109 | +if __name__ == '__main__': | ||
110 | + # Must support creating images and reopen | ||
111 | + iotests.main(supported_fmts=['qcow', 'qcow2', 'qed', 'raw', 'vdi', 'vhdx', | ||
112 | + 'vmdk', 'vpc'], | ||
113 | + supported_protocols=['file']) | ||
114 | diff --git a/tests/qemu-iotests/tests/reopen-file.out b/tests/qemu-iotests/tests/reopen-file.out | ||
115 | new file mode 100644 | ||
116 | index XXXXXXX..XXXXXXX | ||
117 | --- /dev/null | ||
118 | +++ b/tests/qemu-iotests/tests/reopen-file.out | ||
119 | @@ -XXX,XX +XXX,XX @@ | ||
120 | +. | ||
121 | +---------------------------------------------------------------------- | ||
122 | +Ran 1 tests | ||
123 | + | ||
124 | +OK | ||
94 | -- | 125 | -- |
95 | 2.31.1 | 126 | 2.35.1 |
96 | |||
97 | diff view generated by jsdifflib |
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 1 | From: Stefan Hajnoczi <stefanha@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Now test fails if copy-before-write is not white-listed. | 3 | Thread-Local Storage variables cannot be used directly from coroutine |
4 | Let's skip test instead. | 4 | code because the compiler may optimize TLS variable accesses across |
5 | qemu_coroutine_yield() calls. When the coroutine is re-entered from | ||
6 | another thread the TLS variables from the old thread must no longer be | ||
7 | used. | ||
5 | 8 | ||
6 | Fixes: c0605985696a19ef034fa25d04f53f3b3b383896 | 9 | Use QEMU_DEFINE_STATIC_CO_TLS() for the current and leader variables. |
7 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 10 | |
8 | Message-Id: <20210920115538.264372-6-vsementsov@virtuozzo.com> | 11 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> |
12 | Message-Id: <20220307153853.602859-2-stefanha@redhat.com> | ||
13 | Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | --- | 15 | --- |
11 | tests/qemu-iotests/tests/image-fleecing | 1 + | 16 | util/coroutine-ucontext.c | 38 ++++++++++++++++++++++++-------------- |
12 | 1 file changed, 1 insertion(+) | 17 | 1 file changed, 24 insertions(+), 14 deletions(-) |
13 | 18 | ||
14 | diff --git a/tests/qemu-iotests/tests/image-fleecing b/tests/qemu-iotests/tests/image-fleecing | 19 | diff --git a/util/coroutine-ucontext.c b/util/coroutine-ucontext.c |
15 | index XXXXXXX..XXXXXXX 100755 | 20 | index XXXXXXX..XXXXXXX 100644 |
16 | --- a/tests/qemu-iotests/tests/image-fleecing | 21 | --- a/util/coroutine-ucontext.c |
17 | +++ b/tests/qemu-iotests/tests/image-fleecing | 22 | +++ b/util/coroutine-ucontext.c |
18 | @@ -XXX,XX +XXX,XX @@ from iotests import log, qemu_img, qemu_io, qemu_io_silent | 23 | @@ -XXX,XX +XXX,XX @@ |
19 | iotests.script_initialize( | 24 | #include "qemu/osdep.h" |
20 | supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk', 'vhdx', 'raw'], | 25 | #include <ucontext.h> |
21 | supported_platforms=['linux'], | 26 | #include "qemu/coroutine_int.h" |
22 | + required_fmts=['copy-before-write'], | 27 | +#include "qemu/coroutine-tls.h" |
23 | ) | 28 | |
24 | 29 | #ifdef CONFIG_VALGRIND_H | |
25 | patterns = [('0x5d', '0', '64k'), | 30 | #include <valgrind/valgrind.h> |
31 | @@ -XXX,XX +XXX,XX @@ typedef struct { | ||
32 | /** | ||
33 | * Per-thread coroutine bookkeeping | ||
34 | */ | ||
35 | -static __thread CoroutineUContext leader; | ||
36 | -static __thread Coroutine *current; | ||
37 | +QEMU_DEFINE_STATIC_CO_TLS(Coroutine *, current); | ||
38 | +QEMU_DEFINE_STATIC_CO_TLS(CoroutineUContext, leader); | ||
39 | |||
40 | /* | ||
41 | * va_args to makecontext() must be type 'int', so passing | ||
42 | @@ -XXX,XX +XXX,XX @@ static inline __attribute__((always_inline)) | ||
43 | void finish_switch_fiber(void *fake_stack_save) | ||
44 | { | ||
45 | #ifdef CONFIG_ASAN | ||
46 | + CoroutineUContext *leaderp = get_ptr_leader(); | ||
47 | const void *bottom_old; | ||
48 | size_t size_old; | ||
49 | |||
50 | __sanitizer_finish_switch_fiber(fake_stack_save, &bottom_old, &size_old); | ||
51 | |||
52 | - if (!leader.stack) { | ||
53 | - leader.stack = (void *)bottom_old; | ||
54 | - leader.stack_size = size_old; | ||
55 | + if (!leaderp->stack) { | ||
56 | + leaderp->stack = (void *)bottom_old; | ||
57 | + leaderp->stack_size = size_old; | ||
58 | } | ||
59 | #endif | ||
60 | #ifdef CONFIG_TSAN | ||
61 | @@ -XXX,XX +XXX,XX @@ static void coroutine_trampoline(int i0, int i1) | ||
62 | |||
63 | /* Initialize longjmp environment and switch back the caller */ | ||
64 | if (!sigsetjmp(self->env, 0)) { | ||
65 | - start_switch_fiber_asan(COROUTINE_YIELD, &fake_stack_save, leader.stack, | ||
66 | - leader.stack_size); | ||
67 | + CoroutineUContext *leaderp = get_ptr_leader(); | ||
68 | + | ||
69 | + start_switch_fiber_asan(COROUTINE_YIELD, &fake_stack_save, | ||
70 | + leaderp->stack, leaderp->stack_size); | ||
71 | start_switch_fiber_tsan(&fake_stack_save, self, true); /* true=caller */ | ||
72 | siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); | ||
73 | } | ||
74 | @@ -XXX,XX +XXX,XX @@ qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, | ||
75 | int ret; | ||
76 | void *fake_stack_save = NULL; | ||
77 | |||
78 | - current = to_; | ||
79 | + set_current(to_); | ||
80 | |||
81 | ret = sigsetjmp(from->env, 0); | ||
82 | if (ret == 0) { | ||
83 | @@ -XXX,XX +XXX,XX @@ qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, | ||
84 | |||
85 | Coroutine *qemu_coroutine_self(void) | ||
86 | { | ||
87 | - if (!current) { | ||
88 | - current = &leader.base; | ||
89 | + Coroutine *self = get_current(); | ||
90 | + CoroutineUContext *leaderp = get_ptr_leader(); | ||
91 | + | ||
92 | + if (!self) { | ||
93 | + self = &leaderp->base; | ||
94 | + set_current(self); | ||
95 | } | ||
96 | #ifdef CONFIG_TSAN | ||
97 | - if (!leader.tsan_co_fiber) { | ||
98 | - leader.tsan_co_fiber = __tsan_get_current_fiber(); | ||
99 | + if (!leaderp->tsan_co_fiber) { | ||
100 | + leaderp->tsan_co_fiber = __tsan_get_current_fiber(); | ||
101 | } | ||
102 | #endif | ||
103 | - return current; | ||
104 | + return self; | ||
105 | } | ||
106 | |||
107 | bool qemu_in_coroutine(void) | ||
108 | { | ||
109 | - return current && current->caller; | ||
110 | + Coroutine *self = get_current(); | ||
111 | + | ||
112 | + return self && self->caller; | ||
113 | } | ||
26 | -- | 114 | -- |
27 | 2.31.1 | 115 | 2.35.1 |
28 | 116 | ||
29 | 117 | diff view generated by jsdifflib |
1 | From: Philippe Mathieu-Daudé <philmd@redhat.com> | 1 | From: Stefan Hajnoczi <stefanha@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | When configuring QEMU with --disable-fuse, the qemu-storage-daemon | 3 | Thread-Local Storage variables cannot be used directly from coroutine |
4 | still reports FUSE command line options in its help: | 4 | code because the compiler may optimize TLS variable accesses across |
5 | qemu_coroutine_yield() calls. When the coroutine is re-entered from | ||
6 | another thread the TLS variables from the old thread must no longer be | ||
7 | used. | ||
5 | 8 | ||
6 | $ qemu-storage-daemon -h | 9 | Use QEMU_DEFINE_STATIC_CO_TLS() for the current and leader variables. |
7 | Usage: qemu-storage-daemon [options] | 10 | The alloc_pool QSLIST needs a typedef so the return value of |
8 | QEMU storage daemon | 11 | get_ptr_alloc_pool() can be stored in a local variable. |
9 | 12 | ||
10 | --export [type=]fuse,id=<id>,node-name=<node-name>,mountpoint=<file> | 13 | One example of why this code is necessary: a coroutine that yields |
11 | [,growable=on|off][,writable=on|off] | 14 | before calling qemu_coroutine_create() to create another coroutine is |
12 | export the specified block node over FUSE | 15 | affected by the TLS issue. |
13 | 16 | ||
14 | Remove this help message when FUSE is disabled, to avoid: | 17 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> |
15 | 18 | Message-Id: <20220307153853.602859-3-stefanha@redhat.com> | |
16 | $ qemu-storage-daemon --export fuse | 19 | Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> |
17 | qemu-storage-daemon: --export fuse: Invalid parameter 'fuse' | ||
18 | |||
19 | Reported-by: Qing Wang <qinwang@redhat.com> | ||
20 | Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
21 | Message-Id: <20210816180442.2000642-1-philmd@redhat.com> | ||
22 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
23 | Reviewed-by: Hanna Reitz <hreitz@redhat.com> | ||
24 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 20 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
25 | --- | 21 | --- |
26 | storage-daemon/qemu-storage-daemon.c | 2 ++ | 22 | util/qemu-coroutine.c | 41 ++++++++++++++++++++++++----------------- |
27 | 1 file changed, 2 insertions(+) | 23 | 1 file changed, 24 insertions(+), 17 deletions(-) |
28 | 24 | ||
29 | diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c | 25 | diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c |
30 | index XXXXXXX..XXXXXXX 100644 | 26 | index XXXXXXX..XXXXXXX 100644 |
31 | --- a/storage-daemon/qemu-storage-daemon.c | 27 | --- a/util/qemu-coroutine.c |
32 | +++ b/storage-daemon/qemu-storage-daemon.c | 28 | +++ b/util/qemu-coroutine.c |
33 | @@ -XXX,XX +XXX,XX @@ static void help(void) | 29 | @@ -XXX,XX +XXX,XX @@ |
34 | " export the specified block node over NBD\n" | 30 | #include "qemu/atomic.h" |
35 | " (requires --nbd-server)\n" | 31 | #include "qemu/coroutine.h" |
36 | "\n" | 32 | #include "qemu/coroutine_int.h" |
37 | +#ifdef CONFIG_FUSE | 33 | +#include "qemu/coroutine-tls.h" |
38 | " --export [type=]fuse,id=<id>,node-name=<node-name>,mountpoint=<file>\n" | 34 | #include "block/aio.h" |
39 | " [,growable=on|off][,writable=on|off]\n" | 35 | |
40 | " export the specified block node over FUSE\n" | 36 | /** Initial batch size is 64, and is increased on demand */ |
41 | "\n" | 37 | @@ -XXX,XX +XXX,XX @@ enum { |
42 | +#endif /* CONFIG_FUSE */ | 38 | static QSLIST_HEAD(, Coroutine) release_pool = QSLIST_HEAD_INITIALIZER(pool); |
43 | " --monitor [chardev=]name[,mode=control][,pretty[=on|off]]\n" | 39 | static unsigned int pool_batch_size = POOL_INITIAL_BATCH_SIZE; |
44 | " configure a QMP monitor\n" | 40 | static unsigned int release_pool_size; |
45 | "\n" | 41 | -static __thread QSLIST_HEAD(, Coroutine) alloc_pool = QSLIST_HEAD_INITIALIZER(pool); |
42 | -static __thread unsigned int alloc_pool_size; | ||
43 | -static __thread Notifier coroutine_pool_cleanup_notifier; | ||
44 | + | ||
45 | +typedef QSLIST_HEAD(, Coroutine) CoroutineQSList; | ||
46 | +QEMU_DEFINE_STATIC_CO_TLS(CoroutineQSList, alloc_pool); | ||
47 | +QEMU_DEFINE_STATIC_CO_TLS(unsigned int, alloc_pool_size); | ||
48 | +QEMU_DEFINE_STATIC_CO_TLS(Notifier, coroutine_pool_cleanup_notifier); | ||
49 | |||
50 | static void coroutine_pool_cleanup(Notifier *n, void *value) | ||
51 | { | ||
52 | Coroutine *co; | ||
53 | Coroutine *tmp; | ||
54 | + CoroutineQSList *alloc_pool = get_ptr_alloc_pool(); | ||
55 | |||
56 | - QSLIST_FOREACH_SAFE(co, &alloc_pool, pool_next, tmp) { | ||
57 | - QSLIST_REMOVE_HEAD(&alloc_pool, pool_next); | ||
58 | + QSLIST_FOREACH_SAFE(co, alloc_pool, pool_next, tmp) { | ||
59 | + QSLIST_REMOVE_HEAD(alloc_pool, pool_next); | ||
60 | qemu_coroutine_delete(co); | ||
61 | } | ||
62 | } | ||
63 | @@ -XXX,XX +XXX,XX @@ Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque) | ||
64 | Coroutine *co = NULL; | ||
65 | |||
66 | if (CONFIG_COROUTINE_POOL) { | ||
67 | - co = QSLIST_FIRST(&alloc_pool); | ||
68 | + CoroutineQSList *alloc_pool = get_ptr_alloc_pool(); | ||
69 | + | ||
70 | + co = QSLIST_FIRST(alloc_pool); | ||
71 | if (!co) { | ||
72 | if (release_pool_size > qatomic_read(&pool_batch_size)) { | ||
73 | /* Slow path; a good place to register the destructor, too. */ | ||
74 | - if (!coroutine_pool_cleanup_notifier.notify) { | ||
75 | - coroutine_pool_cleanup_notifier.notify = coroutine_pool_cleanup; | ||
76 | - qemu_thread_atexit_add(&coroutine_pool_cleanup_notifier); | ||
77 | + Notifier *notifier = get_ptr_coroutine_pool_cleanup_notifier(); | ||
78 | + if (!notifier->notify) { | ||
79 | + notifier->notify = coroutine_pool_cleanup; | ||
80 | + qemu_thread_atexit_add(notifier); | ||
81 | } | ||
82 | |||
83 | /* This is not exact; there could be a little skew between | ||
84 | * release_pool_size and the actual size of release_pool. But | ||
85 | * it is just a heuristic, it does not need to be perfect. | ||
86 | */ | ||
87 | - alloc_pool_size = qatomic_xchg(&release_pool_size, 0); | ||
88 | - QSLIST_MOVE_ATOMIC(&alloc_pool, &release_pool); | ||
89 | - co = QSLIST_FIRST(&alloc_pool); | ||
90 | + set_alloc_pool_size(qatomic_xchg(&release_pool_size, 0)); | ||
91 | + QSLIST_MOVE_ATOMIC(alloc_pool, &release_pool); | ||
92 | + co = QSLIST_FIRST(alloc_pool); | ||
93 | } | ||
94 | } | ||
95 | if (co) { | ||
96 | - QSLIST_REMOVE_HEAD(&alloc_pool, pool_next); | ||
97 | - alloc_pool_size--; | ||
98 | + QSLIST_REMOVE_HEAD(alloc_pool, pool_next); | ||
99 | + set_alloc_pool_size(get_alloc_pool_size() - 1); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | @@ -XXX,XX +XXX,XX @@ static void coroutine_delete(Coroutine *co) | ||
104 | qatomic_inc(&release_pool_size); | ||
105 | return; | ||
106 | } | ||
107 | - if (alloc_pool_size < qatomic_read(&pool_batch_size)) { | ||
108 | - QSLIST_INSERT_HEAD(&alloc_pool, co, pool_next); | ||
109 | - alloc_pool_size++; | ||
110 | + if (get_alloc_pool_size() < qatomic_read(&pool_batch_size)) { | ||
111 | + QSLIST_INSERT_HEAD(get_ptr_alloc_pool(), co, pool_next); | ||
112 | + set_alloc_pool_size(get_alloc_pool_size() + 1); | ||
113 | return; | ||
114 | } | ||
115 | } | ||
46 | -- | 116 | -- |
47 | 2.31.1 | 117 | 2.35.1 |
48 | 118 | ||
49 | 119 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | From: Stefan Hajnoczi <stefanha@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | We can drop the sys.path hacking in various places by doing | 3 | Thread-Local Storage variables cannot be used directly from coroutine |
4 | this. Additionally, by doing it in one place right up top, we can print | 4 | code because the compiler may optimize TLS variable accesses across |
5 | interesting warnings in case the environment does not look correct. (See | 5 | qemu_coroutine_yield() calls. When the coroutine is re-entered from |
6 | next commit.) | 6 | another thread the TLS variables from the old thread must no longer be |
7 | used. | ||
7 | 8 | ||
8 | If we ever decide to change how the environment is crafted, all of the | 9 | Use QEMU_DEFINE_STATIC_CO_TLS() for the current and leader variables. |
9 | "help me find my python packages" goop is all in one place, right in one | ||
10 | function. | ||
11 | 10 | ||
12 | Signed-off-by: John Snow <jsnow@redhat.com> | 11 | I think coroutine-win32.c could get away with __thread because the |
13 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | 12 | variables are only used in situations where either the stale value is |
14 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 13 | correct (current) or outside coroutine context (loading leader when |
15 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 14 | current is NULL). Due to the difficulty of being sure that this is |
16 | Message-Id: <20210923180715.4168522-2-jsnow@redhat.com> | 15 | really safe in all scenarios it seems worth converting it anyway. |
16 | |||
17 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
18 | Message-Id: <20220307153853.602859-4-stefanha@redhat.com> | ||
19 | Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> | ||
17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 20 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
18 | --- | 21 | --- |
19 | tests/qemu-iotests/iotests.py | 2 -- | 22 | util/coroutine-win32.c | 18 +++++++++++++----- |
20 | tests/qemu-iotests/testenv.py | 15 +++++++++------ | 23 | 1 file changed, 13 insertions(+), 5 deletions(-) |
21 | tests/qemu-iotests/235 | 2 -- | ||
22 | tests/qemu-iotests/297 | 6 ------ | ||
23 | tests/qemu-iotests/300 | 5 ++--- | ||
24 | tests/qemu-iotests/tests/mirror-top-perms | 7 +++---- | ||
25 | 6 files changed, 14 insertions(+), 23 deletions(-) | ||
26 | 24 | ||
27 | diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py | 25 | diff --git a/util/coroutine-win32.c b/util/coroutine-win32.c |
28 | index XXXXXXX..XXXXXXX 100644 | 26 | index XXXXXXX..XXXXXXX 100644 |
29 | --- a/tests/qemu-iotests/iotests.py | 27 | --- a/util/coroutine-win32.c |
30 | +++ b/tests/qemu-iotests/iotests.py | 28 | +++ b/util/coroutine-win32.c |
31 | @@ -XXX,XX +XXX,XX @@ | 29 | @@ -XXX,XX +XXX,XX @@ |
32 | 30 | ||
33 | from contextlib import contextmanager | 31 | #include "qemu/osdep.h" |
34 | 32 | #include "qemu/coroutine_int.h" | |
35 | -# pylint: disable=import-error, wrong-import-position | 33 | +#include "qemu/coroutine-tls.h" |
36 | -sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) | 34 | |
37 | from qemu.machine import qtest | 35 | typedef struct |
38 | from qemu.qmp import QMPMessage | 36 | { |
39 | 37 | @@ -XXX,XX +XXX,XX @@ typedef struct | |
40 | diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py | 38 | CoroutineAction action; |
41 | index XXXXXXX..XXXXXXX 100644 | 39 | } CoroutineWin32; |
42 | --- a/tests/qemu-iotests/testenv.py | 40 | |
43 | +++ b/tests/qemu-iotests/testenv.py | 41 | -static __thread CoroutineWin32 leader; |
44 | @@ -XXX,XX +XXX,XX @@ def init_directories(self) -> None: | 42 | -static __thread Coroutine *current; |
45 | SAMPLE_IMG_DIR | 43 | +QEMU_DEFINE_STATIC_CO_TLS(CoroutineWin32, leader); |
46 | OUTPUT_DIR | 44 | +QEMU_DEFINE_STATIC_CO_TLS(Coroutine *, current); |
47 | """ | 45 | |
48 | - self.pythonpath = os.getenv('PYTHONPATH') | 46 | /* This function is marked noinline to prevent GCC from inlining it |
49 | - if self.pythonpath: | 47 | * into coroutine_trampoline(). If we allow it to do that then it |
50 | - self.pythonpath = self.source_iotests + os.pathsep + \ | 48 | @@ -XXX,XX +XXX,XX @@ qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, |
51 | - self.pythonpath | 49 | CoroutineWin32 *from = DO_UPCAST(CoroutineWin32, base, from_); |
52 | - else: | 50 | CoroutineWin32 *to = DO_UPCAST(CoroutineWin32, base, to_); |
53 | - self.pythonpath = self.source_iotests | 51 | |
52 | - current = to_; | ||
53 | + set_current(to_); | ||
54 | |||
55 | to->action = action; | ||
56 | SwitchToFiber(to->fiber); | ||
57 | @@ -XXX,XX +XXX,XX @@ void qemu_coroutine_delete(Coroutine *co_) | ||
58 | |||
59 | Coroutine *qemu_coroutine_self(void) | ||
60 | { | ||
61 | + Coroutine *current = get_current(); | ||
54 | + | 62 | + |
55 | + # Path where qemu goodies live in this source tree. | 63 | if (!current) { |
56 | + qemu_srctree_path = Path(__file__, '../../../python').resolve() | 64 | - current = &leader.base; |
65 | - leader.fiber = ConvertThreadToFiber(NULL); | ||
66 | + CoroutineWin32 *leader = get_ptr_leader(); | ||
57 | + | 67 | + |
58 | + self.pythonpath = os.pathsep.join(filter(None, ( | 68 | + current = &leader->base; |
59 | + self.source_iotests, | 69 | + set_current(current); |
60 | + str(qemu_srctree_path), | 70 | + leader->fiber = ConvertThreadToFiber(NULL); |
61 | + os.getenv('PYTHONPATH'), | 71 | } |
62 | + ))) | 72 | return current; |
63 | 73 | } | |
64 | self.test_dir = os.getenv('TEST_DIR', | 74 | |
65 | os.path.join(os.getcwd(), 'scratch')) | 75 | bool qemu_in_coroutine(void) |
66 | diff --git a/tests/qemu-iotests/235 b/tests/qemu-iotests/235 | 76 | { |
67 | index XXXXXXX..XXXXXXX 100755 | 77 | + Coroutine *current = get_current(); |
68 | --- a/tests/qemu-iotests/235 | ||
69 | +++ b/tests/qemu-iotests/235 | ||
70 | @@ -XXX,XX +XXX,XX @@ import os | ||
71 | import iotests | ||
72 | from iotests import qemu_img_create, qemu_io, file_path, log | ||
73 | |||
74 | -sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) | ||
75 | - | ||
76 | from qemu.machine import QEMUMachine | ||
77 | |||
78 | iotests.script_initialize(supported_fmts=['qcow2']) | ||
79 | diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297 | ||
80 | index XXXXXXX..XXXXXXX 100755 | ||
81 | --- a/tests/qemu-iotests/297 | ||
82 | +++ b/tests/qemu-iotests/297 | ||
83 | @@ -XXX,XX +XXX,XX @@ def run_linters(): | ||
84 | # Todo notes are fine, but fixme's or xxx's should probably just be | ||
85 | # fixed (in tests, at least) | ||
86 | env = os.environ.copy() | ||
87 | - qemu_module_path = os.path.join(os.path.dirname(__file__), | ||
88 | - '..', '..', 'python') | ||
89 | - try: | ||
90 | - env['PYTHONPATH'] += os.pathsep + qemu_module_path | ||
91 | - except KeyError: | ||
92 | - env['PYTHONPATH'] = qemu_module_path | ||
93 | subprocess.run(('pylint-3', '--score=n', '--notes=FIXME,XXX', *files), | ||
94 | env=env, check=False) | ||
95 | |||
96 | diff --git a/tests/qemu-iotests/300 b/tests/qemu-iotests/300 | ||
97 | index XXXXXXX..XXXXXXX 100755 | ||
98 | --- a/tests/qemu-iotests/300 | ||
99 | +++ b/tests/qemu-iotests/300 | ||
100 | @@ -XXX,XX +XXX,XX @@ import random | ||
101 | import re | ||
102 | from typing import Dict, List, Optional | ||
103 | |||
104 | +from qemu.machine import machine | ||
105 | + | 78 | + |
106 | import iotests | 79 | return current && current->caller; |
107 | 80 | } | |
108 | -# Import qemu after iotests.py has amended sys.path | ||
109 | -# pylint: disable=wrong-import-order | ||
110 | -from qemu.machine import machine | ||
111 | |||
112 | BlockBitmapMapping = List[Dict[str, object]] | ||
113 | |||
114 | diff --git a/tests/qemu-iotests/tests/mirror-top-perms b/tests/qemu-iotests/tests/mirror-top-perms | ||
115 | index XXXXXXX..XXXXXXX 100755 | ||
116 | --- a/tests/qemu-iotests/tests/mirror-top-perms | ||
117 | +++ b/tests/qemu-iotests/tests/mirror-top-perms | ||
118 | @@ -XXX,XX +XXX,XX @@ | ||
119 | # | ||
120 | |||
121 | import os | ||
122 | -import iotests | ||
123 | -from iotests import qemu_img | ||
124 | |||
125 | -# Import qemu after iotests.py has amended sys.path | ||
126 | -# pylint: disable=wrong-import-order | ||
127 | import qemu | ||
128 | |||
129 | +import iotests | ||
130 | +from iotests import qemu_img | ||
131 | + | ||
132 | |||
133 | image_size = 1 * 1024 * 1024 | ||
134 | source = os.path.join(iotests.test_dir, 'source.img') | ||
135 | -- | 81 | -- |
136 | 2.31.1 | 82 | 2.35.1 |
137 | 83 | ||
138 | 84 | diff view generated by jsdifflib |