* [PATCH 1/3] iotests: run the test pool with the 'fork' start method
2026-07-10 16:13 [PATCH 0/3] iotests: fix three spurious local failures Denis V. Lunev
@ 2026-07-10 16:13 ` Denis V. Lunev
2026-07-14 11:08 ` Kevin Wolf
2026-07-10 16:13 ` [PATCH 2/3] iotests: skip FUSE tests when FUSE is not usable Denis V. Lunev
2026-07-10 16:13 ` [PATCH 3/3] iotests/migrate-bitmaps-postcopy-test: drop the postcopy timing assertion Denis V. Lunev
2 siblings, 1 reply; 8+ messages in thread
From: Denis V. Lunev @ 2026-07-10 16:13 UTC (permalink / raw)
To: qemu-block, qemu-devel; +Cc: den, Kevin Wolf, Hanna Reitz
run_tests_pool() shares the runner via the class attribute
TestRunner.shared_self, relying on worker processes to inherit it.
That only works with the 'fork' start method. Python 3.14 switched
the Linux default to 'forkserver', so workers see shared_self as
None and parallel runs abort with:
assert runner is not None
AssertionError
Only reproduces with Python 3.14+ and 'check -jN' (N > 1); meson
runs one test per process and never calls run_tests_pool(), so CI
is unaffected.
Request get_context('fork') explicitly; it is available on all
supported Python versions and a no-op before 3.14.
Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Kevin Wolf <kwolf@redhat.com>
CC: Hanna Reitz <hreitz@redhat.com>
---
tests/qemu-iotests/testrunner.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py
index dbe2dddc32..5b2664d9f3 100644
--- a/tests/qemu-iotests/testrunner.py
+++ b/tests/qemu-iotests/testrunner.py
@@ -26,7 +26,7 @@
import json
import shutil
import sys
-from multiprocessing import Pool
+from multiprocessing import Pool, get_context
from typing import List, Optional, Any, Sequence, Dict
from testenv import TestEnv
@@ -125,7 +125,7 @@ def run_tests_pool(self, tests: List[str],
assert TestRunner.shared_self is None
TestRunner.shared_self = self
- with Pool(jobs) as p:
+ with get_context('fork').Pool(jobs) as p:
results = p.starmap(self.proc_run_test,
zip(tests, [test_field_width] * len(tests)))
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH 1/3] iotests: run the test pool with the 'fork' start method
2026-07-10 16:13 ` [PATCH 1/3] iotests: run the test pool with the 'fork' start method Denis V. Lunev
@ 2026-07-14 11:08 ` Kevin Wolf
0 siblings, 0 replies; 8+ messages in thread
From: Kevin Wolf @ 2026-07-14 11:08 UTC (permalink / raw)
To: Denis V. Lunev; +Cc: qemu-block, qemu-devel, Hanna Reitz
Am 10.07.2026 um 18:13 hat Denis V. Lunev geschrieben:
> run_tests_pool() shares the runner via the class attribute
> TestRunner.shared_self, relying on worker processes to inherit it.
> That only works with the 'fork' start method. Python 3.14 switched
> the Linux default to 'forkserver', so workers see shared_self as
> None and parallel runs abort with:
>
> assert runner is not None
> AssertionError
>
> Only reproduces with Python 3.14+ and 'check -jN' (N > 1); meson
> runs one test per process and never calls run_tests_pool(), so CI
> is unaffected.
>
> Request get_context('fork') explicitly; it is available on all
> supported Python versions and a no-op before 3.14.
>
> Signed-off-by: Denis V. Lunev <den@openvz.org>
> CC: Kevin Wolf <kwolf@redhat.com>
> CC: Hanna Reitz <hreitz@redhat.com>
> ---
> tests/qemu-iotests/testrunner.py | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py
> index dbe2dddc32..5b2664d9f3 100644
> --- a/tests/qemu-iotests/testrunner.py
> +++ b/tests/qemu-iotests/testrunner.py
> @@ -26,7 +26,7 @@
> import json
> import shutil
> import sys
> -from multiprocessing import Pool
> +from multiprocessing import Pool, get_context
> from typing import List, Optional, Any, Sequence, Dict
> from testenv import TestEnv
>
> @@ -125,7 +125,7 @@ def run_tests_pool(self, tests: List[str],
> assert TestRunner.shared_self is None
> TestRunner.shared_self = self
>
> - with Pool(jobs) as p:
> + with get_context('fork').Pool(jobs) as p:
> results = p.starmap(self.proc_run_test,
> zip(tests, [test_field_width] * len(tests)))
Makes sense, but the Pool import is now unused:
=== pylint ===
+************* Module testrunner
+testrunner.py:29:0: W0611: Unused Pool imported from multiprocessing (unused-import)
=== mypy ===
Kevin
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/3] iotests: skip FUSE tests when FUSE is not usable
2026-07-10 16:13 [PATCH 0/3] iotests: fix three spurious local failures Denis V. Lunev
2026-07-10 16:13 ` [PATCH 1/3] iotests: run the test pool with the 'fork' start method Denis V. Lunev
@ 2026-07-10 16:13 ` Denis V. Lunev
2026-07-14 11:19 ` Kevin Wolf
2026-07-10 16:13 ` [PATCH 3/3] iotests/migrate-bitmaps-postcopy-test: drop the postcopy timing assertion Denis V. Lunev
2 siblings, 1 reply; 8+ messages in thread
From: Denis V. Lunev @ 2026-07-10 16:13 UTC (permalink / raw)
To: qemu-block, qemu-devel; +Cc: den, Kevin Wolf, Hanna Reitz
file-io-error, fuse-allow-other and fuse-mmap-shared skip only when
FUSE is not compiled in. When FUSE is built in but unusable at run
time (no /dev/fuse access, fusermount lacking permissions), the
export fails to mount with "Failed to mount FUSE session to export"
and the tests report a spurious failure instead of skipping, like
NBD tests already do for missing NBD support.
Add _notrun_on_no_fuse() to common.rc and use it in the shell tests.
fuse-mmap-shared is Python, so it gets an equivalent inline check.
Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Kevin Wolf <kwolf@redhat.com>
CC: Hanna Reitz <hreitz@redhat.com>
---
tests/qemu-iotests/common.rc | 14 ++++++++++++++
tests/qemu-iotests/tests/file-io-error | 4 +---
tests/qemu-iotests/tests/fuse-allow-other | 2 ++
tests/qemu-iotests/tests/fuse-mmap-shared | 9 ++++++---
4 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index 298bc483e0..62a2bc146b 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -981,6 +981,20 @@ _require_drivers()
done
}
+# Skip if FUSE is unusable: not compiled in, or the export failed to
+# mount. $1 is the failing 'block-export-add' reply.
+_notrun_on_no_fuse()
+{
+ case "$1" in
+ *"Parameter 'type' does not accept value 'fuse'"*)
+ _notrun "No FUSE support"
+ ;;
+ *"Failed to mount FUSE session"*)
+ _notrun "FUSE not usable in this environment"
+ ;;
+ esac
+}
+
# Check that we have a file system that allows huge (but very sparse) files
#
_require_large_file()
diff --git a/tests/qemu-iotests/tests/file-io-error b/tests/qemu-iotests/tests/file-io-error
index fb8db73b31..3cdb41f33d 100755
--- a/tests/qemu-iotests/tests/file-io-error
+++ b/tests/qemu-iotests/tests/file-io-error
@@ -89,9 +89,7 @@ output=$(_send_qemu_cmd $QEMU_HANDLE \
'return' \
| grep -v 'option allow_other only allowed if')
-if echo "$output" | grep -q "Parameter 'type' does not accept value 'fuse'"; then
- _notrun 'No FUSE support'
-fi
+_notrun_on_no_fuse "$output"
echo "$output"
echo
diff --git a/tests/qemu-iotests/tests/fuse-allow-other b/tests/qemu-iotests/tests/fuse-allow-other
index eaa39f8f23..bcba191951 100755
--- a/tests/qemu-iotests/tests/fuse-allow-other
+++ b/tests/qemu-iotests/tests/fuse-allow-other
@@ -77,6 +77,8 @@ fuse_export_add()
_notrun "allow_other not supported"
fi
+ _notrun_on_no_fuse "$output"
+
echo "$output"
}
diff --git a/tests/qemu-iotests/tests/fuse-mmap-shared b/tests/qemu-iotests/tests/fuse-mmap-shared
index 52941a3bb6..b190105894 100755
--- a/tests/qemu-iotests/tests/fuse-mmap-shared
+++ b/tests/qemu-iotests/tests/fuse-mmap-shared
@@ -28,9 +28,12 @@ def test_fuse_support(mount_point):
})
test_qsd.stop()
if 'error' in res:
- assert (res['error']['desc'] ==
- "Parameter 'type' does not accept value 'fuse'")
- iotests.notrun('No FUSE support')
+ desc = res['error']['desc']
+ if desc == "Parameter 'type' does not accept value 'fuse'":
+ iotests.notrun('No FUSE support')
+ if 'Failed to mount FUSE session' in desc:
+ iotests.notrun('FUSE not usable in this environment')
+ assert False, desc
# Shared mmap when using direct IO is only supported for Linux kernels >= 6.6
# with commit e78662e818f94 ("fuse: add a new fuse init flag to relax
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH 2/3] iotests: skip FUSE tests when FUSE is not usable
2026-07-10 16:13 ` [PATCH 2/3] iotests: skip FUSE tests when FUSE is not usable Denis V. Lunev
@ 2026-07-14 11:19 ` Kevin Wolf
0 siblings, 0 replies; 8+ messages in thread
From: Kevin Wolf @ 2026-07-14 11:19 UTC (permalink / raw)
To: Denis V. Lunev; +Cc: qemu-block, qemu-devel, Hanna Reitz
Am 10.07.2026 um 18:13 hat Denis V. Lunev geschrieben:
> file-io-error, fuse-allow-other and fuse-mmap-shared skip only when
> FUSE is not compiled in. When FUSE is built in but unusable at run
> time (no /dev/fuse access, fusermount lacking permissions), the
> export fails to mount with "Failed to mount FUSE session to export"
> and the tests report a spurious failure instead of skipping, like
> NBD tests already do for missing NBD support.
>
> Add _notrun_on_no_fuse() to common.rc and use it in the shell tests.
> fuse-mmap-shared is Python, so it gets an equivalent inline check.
>
> Signed-off-by: Denis V. Lunev <den@openvz.org>
> CC: Kevin Wolf <kwolf@redhat.com>
> CC: Hanna Reitz <hreitz@redhat.com>
> ---
> tests/qemu-iotests/common.rc | 14 ++++++++++++++
> tests/qemu-iotests/tests/file-io-error | 4 +---
> tests/qemu-iotests/tests/fuse-allow-other | 2 ++
> tests/qemu-iotests/tests/fuse-mmap-shared | 9 ++++++---
> 4 files changed, 23 insertions(+), 6 deletions(-)
>
> diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
> index 298bc483e0..62a2bc146b 100644
> --- a/tests/qemu-iotests/common.rc
> +++ b/tests/qemu-iotests/common.rc
> @@ -981,6 +981,20 @@ _require_drivers()
> done
> }
>
> +# Skip if FUSE is unusable: not compiled in, or the export failed to
> +# mount. $1 is the failing 'block-export-add' reply.
> +_notrun_on_no_fuse()
I'm not completely happy with the name, though this is not a blocker.
My first thought was to keep the name consistent with the other
functions in common.rc and have something like _require_fuse. On second
thoughts, this is probably confusing because it's different from the
existing _require_* functions in that it takes an error message from the
test case instead of doing its own probing.
So maybe at least remove the double negative? _notrun_on_fuse_error
sounds slightly better to me.
> +{
> + case "$1" in
> + *"Parameter 'type' does not accept value 'fuse'"*)
> + _notrun "No FUSE support"
> + ;;
> + *"Failed to mount FUSE session"*)
> + _notrun "FUSE not usable in this environment"
> + ;;
> + esac
> +}
> +
Adding these checks is a good idea, of course.
Kevin
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 3/3] iotests/migrate-bitmaps-postcopy-test: drop the postcopy timing assertion
2026-07-10 16:13 [PATCH 0/3] iotests: fix three spurious local failures Denis V. Lunev
2026-07-10 16:13 ` [PATCH 1/3] iotests: run the test pool with the 'fork' start method Denis V. Lunev
2026-07-10 16:13 ` [PATCH 2/3] iotests: skip FUSE tests when FUSE is not usable Denis V. Lunev
@ 2026-07-10 16:13 ` Denis V. Lunev
2026-07-14 11:28 ` Kevin Wolf
2 siblings, 1 reply; 8+ messages in thread
From: Denis V. Lunev @ 2026-07-10 16:13 UTC (permalink / raw)
To: qemu-block, qemu-devel; +Cc: den, Kevin Wolf, Hanna Reitz
test_postcopy_success asserted `downtime * 10 < postcopy_time` to
confirm the run spent meaningful time in postcopy. This is an
unnormalized wall-clock heuristic tuned to old hardware (commit
e80a4150a5), and fails on fast hosts where the bitmap payload now
transfers in under a second while switch-over downtime stays fixed
(e.g. downtime=0.6s, postcopy_time=0.8s).
It is also a weak proxy for the regression it guards against
(postcopy degenerating to precopy): that is already covered by the
bitmap sha256, mid-migration update and persistence checks, none of
which depend on this timing.
Drop the assertion; keep printing the timings under debug.
Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Kevin Wolf <kwolf@redhat.com>
CC: Hanna Reitz <hreitz@redhat.com>
---
tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test | 1 -
1 file changed, 1 deletion(-)
diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test
index c519e6db8c..f9c55b6395 100755
--- a/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test
+++ b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test
@@ -186,7 +186,6 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase):
downtime = event_dist(event_stop, event_resume)
postcopy_time = event_dist(event_resume, event_complete)
- assert downtime * 10 < postcopy_time
if debug:
print('downtime:', downtime)
print('postcopy_time:', postcopy_time)
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH 3/3] iotests/migrate-bitmaps-postcopy-test: drop the postcopy timing assertion
2026-07-10 16:13 ` [PATCH 3/3] iotests/migrate-bitmaps-postcopy-test: drop the postcopy timing assertion Denis V. Lunev
@ 2026-07-14 11:28 ` Kevin Wolf
2026-07-14 12:36 ` Vladimir Sementsov-Ogievskiy
0 siblings, 1 reply; 8+ messages in thread
From: Kevin Wolf @ 2026-07-14 11:28 UTC (permalink / raw)
To: Denis V. Lunev; +Cc: qemu-block, qemu-devel, Hanna Reitz, vsementsov
Am 10.07.2026 um 18:13 hat Denis V. Lunev geschrieben:
> test_postcopy_success asserted `downtime * 10 < postcopy_time` to
> confirm the run spent meaningful time in postcopy. This is an
> unnormalized wall-clock heuristic tuned to old hardware (commit
> e80a4150a5), and fails on fast hosts where the bitmap payload now
> transfers in under a second while switch-over downtime stays fixed
> (e.g. downtime=0.6s, postcopy_time=0.8s).
>
> It is also a weak proxy for the regression it guards against
> (postcopy degenerating to precopy): that is already covered by the
> bitmap sha256, mid-migration update and persistence checks, none of
> which depend on this timing.
>
> Drop the assertion; keep printing the timings under debug.
>
> Signed-off-by: Denis V. Lunev <den@openvz.org>
> CC: Kevin Wolf <kwolf@redhat.com>
> CC: Hanna Reitz <hreitz@redhat.com>
I agree that this assertion failure doesn't mean that the test failed.
What it might mean is that maybe we didn't test the code as well as we
would have liked to.
Should we try to slow down/throttle the migration artificially to
trigger the cases we want to test here more reliably?
Kevin
> tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test | 1 -
> 1 file changed, 1 deletion(-)
>
> diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test
> index c519e6db8c..f9c55b6395 100755
> --- a/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test
> +++ b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test
> @@ -186,7 +186,6 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase):
> downtime = event_dist(event_stop, event_resume)
> postcopy_time = event_dist(event_resume, event_complete)
>
> - assert downtime * 10 < postcopy_time
> if debug:
> print('downtime:', downtime)
> print('postcopy_time:', postcopy_time)
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH 3/3] iotests/migrate-bitmaps-postcopy-test: drop the postcopy timing assertion
2026-07-14 11:28 ` Kevin Wolf
@ 2026-07-14 12:36 ` Vladimir Sementsov-Ogievskiy
0 siblings, 0 replies; 8+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-14 12:36 UTC (permalink / raw)
To: Kevin Wolf, Denis V. Lunev; +Cc: qemu-block, qemu-devel, Hanna Reitz
On 14.07.26 14:28, Kevin Wolf wrote:
> Am 10.07.2026 um 18:13 hat Denis V. Lunev geschrieben:
>> test_postcopy_success asserted `downtime * 10 < postcopy_time` to
>> confirm the run spent meaningful time in postcopy. This is an
>> unnormalized wall-clock heuristic tuned to old hardware (commit
>> e80a4150a5), and fails on fast hosts where the bitmap payload now
>> transfers in under a second while switch-over downtime stays fixed
>> (e.g. downtime=0.6s, postcopy_time=0.8s).
>>
>> It is also a weak proxy for the regression it guards against
>> (postcopy degenerating to precopy): that is already covered by the
>> bitmap sha256, mid-migration update and persistence checks, none of
>> which depend on this timing.
>>
>> Drop the assertion; keep printing the timings under debug.
>>
>> Signed-off-by: Denis V. Lunev <den@openvz.org>
>> CC: Kevin Wolf <kwolf@redhat.com>
>> CC: Hanna Reitz <hreitz@redhat.com>
>
> I agree that this assertion failure doesn't mean that the test failed.
> What it might mean is that maybe we didn't test the code as well as we
> would have liked to.
Yes, the aim was to guarantee, that we don't simply pass the whole bitmap
in downtime, not starting any postcopy at all.
Possible alternative is to do query-migrate exactly after RESUME, and check
that remaining is significantly more than zero: this data is transferred
in postcopy for sure.
>
> Should we try to slow down/throttle the migration artificially to
> trigger the cases we want to test here more reliably?
>
> Kevin
>
>> tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test | 1 -
>> 1 file changed, 1 deletion(-)
>>
>> diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test
>> index c519e6db8c..f9c55b6395 100755
>> --- a/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test
>> +++ b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test
>> @@ -186,7 +186,6 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase):
>> downtime = event_dist(event_stop, event_resume)
>> postcopy_time = event_dist(event_resume, event_complete)
>>
>> - assert downtime * 10 < postcopy_time
>> if debug:
>> print('downtime:', downtime)
>> print('postcopy_time:', postcopy_time)
>> --
>> 2.53.0
>>
>
--
Best regards,
Vladimir
^ permalink raw reply [flat|nested] 8+ messages in thread