On Fri, Oct 09, 2026 at 07:52:29PM +0200, Alexander Mikhalitsyn wrote:
> Am Mi., 7. Okt. 2026 um 15:27 Uhr schrieb Peter Xu <[email protected]>:
> >
> > On Mon, Sep 21, 2026 at 08:03:45PM +0100, Alex Bennée wrote:
> > > This is a hacky patch for comment but I've included the diagnostic
> > > hacks to the migration test as well as my workaround to get itworking.
> > >
> > > This test was failing on my KVM system. On investigation I found the
> > > nVME activity meant the migration never completed. I ended up
> > > inserting some sleeps into the workload script until I was able to
> > > get it to run.
> >
> > Indeed, I can reproduce it too:
> 
> Hi Peter,

Hi, Alexander,

Thanks for loooking into this, and sorry for a late update.  Before saying
this is a right fix, I have some questions inline.

> 
> thank you checking on your system and sharing a stacktrace.
> Unfortunately, I wasn't able to reproduce this failure on my system,
> but I think I have a good theory what's going on there. I think it is
> because we use `snapshot=on` for a disk device and at the same time
> keep important stuff like `/tmp/test_nvme_mig_workload.pid` on a root
> filesystem. Easy fix is to use tmpfs for /tmp, so all data is actually
> migrated.
> 
> Please, can you try the following patch and let me know if this fixes
> the issue or not:
> 
> --- a/tests/functional/x86_64/test_nvme_migration.py
> +++ b/tests/functional/x86_64/test_nvme_migration.py
> @@ -75,6 +75,11 @@ def configure_machine(self, vm):
>      def launch_source_vm(self, vm):
>          vm.launch()
> 
> +        vm.cmd('migrate-set-parameters', **{
> +            'downtime-limit': 2000000,

One thing to mention is downtime-limit is in milliseconds, this is 2000sec,
so I would expect whatever guest runs, this converges in the 1st second..

> +            'max-bandwidth': 10 * 1024**3,

Yes we want some sane bandwidth maybe, indeed, default is normally too
small.

> +        })
> +
>          self.wait_for_console_pattern('Entering emergency mode.', vm)
>          prompt = '# '
>          self.wait_for_console_pattern(prompt, vm)
> @@ -94,6 +99,8 @@ def launch_source_vm(self, vm):
>                                            prompt, vm=vm)
>          exec_command_and_wait_for_pattern(self, 'mount -t sysfs sysfs /sys',
>                                            prompt, vm=vm)
> +        exec_command_and_wait_for_pattern(self, 'mount -t tmpfs tmpfs /tmp',
> +                                          prompt, vm=vm)

Why this is needed?  I was expecting /tmp normally is already mounted with
tmpfs, no?

One thing that might be relevant is the use of snapshot=on on the drives,
which means, IIUC, there's a top layer qcow2 overlay on the image file
specified.

Now thinking about it, if snapshot=on implies an anon invisible overlay, it
should not support migration at all?  It means, this QEMU instance contains
some anonymous qcow2 overlay that can never (am I right?) be migrated,
since it's neither part of device state, nor guest memory (unlike /tmp
mount in guest).

So I feel like there's still something is off, e.g. I wonder if we should:

- add a migration blocker for snapshot=on whenever used.. because I can't
  see how it can work at all...

- for this test, should we remove snapshot=on but instead create an
  explicit overlay that will be used on both src/dst qemu?

Thanks,

> 
>          # Run workload before migration to check if it continues
>          # to run properly after migration.
> 
> Kind regards,
> Alex
> 
> >
> > $ ./run ../tests/functional/x86_64/test_nvme_migration.py 
> > X8664NVMeMigrationTest.test_migration_with_unix
> > TAP version 13
> > Traceback (most recent call last):
> >   File 
> > "/home/zhexu/git/qemu/tests/functional/x86_64/test_nvme_migration.py", line 
> > 162, in test_migration_with_unix
> >     self.migration_with_unix()
> >     ~~~~~~~~~~~~~~~~~~~~~~~~^^
> >   File "/home/zhexu/git/qemu/tests/functional/migration.py", line 98, in 
> > migration_with_unix
> >     self.migrate(dst_uri)
> >     ~~~~~~~~~~~~^^^^^^^^^
> >   File "/home/zhexu/git/qemu/tests/functional/migration.py", line 78, in 
> > migrate
> >     self.migrate_vms(dst_uri, src_uri, dst_vm, src_vm)
> >     ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> >   File "/home/zhexu/git/qemu/tests/functional/migration.py", line 63, in 
> > migrate_vms
> >     self.assert_dest_vm(dst_vm)
> >     ~~~~~~~~~~~~~~~~~~~^^^^^^^^
> >   File 
> > "/home/zhexu/git/qemu/tests/functional/x86_64/test_nvme_migration.py", line 
> > 128, in assert_dest_vm
> >     self.exec_command_and_check(
> >     ~~~~~~~~~~~~~~~~~~~~~~~~~~~^
> >         "kill -0 $(cat /tmp/test_nvme_mig_workload.pid)", vm)
> >         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> >   File 
> > "/home/zhexu/git/qemu/tests/functional/x86_64/test_nvme_migration.py", line 
> > 48, in exec_command_and_check
> >     wait_for_console_pattern(self, 'OK', failure_message="FAIL", vm=vm)
> >     ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> >   File "/home/zhexu/git/qemu/tests/functional/qemu_test/cmd.py", line 191, 
> > in wait_for_console_pattern
> >     return _console_interaction(test, success_message, failure_message,
> >                                 None, vm=vm)
> >   File "/home/zhexu/git/qemu/tests/functional/qemu_test/cmd.py", line 134, 
> > in _console_interaction
> >     done, line = _console_read_line_until_match(test, vm,
> >                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
> >                                                 success_message_b,
> >                                                 ^^^^^^^^^^^^^^^^^^
> >                                                 failure_message_b)
> >                                                 ^^^^^^^^^^^^^^^^^^
> >   File "/home/zhexu/git/qemu/tests/functional/qemu_test/cmd.py", line 70, 
> > in _console_read_line_until_match
> >     test.fail(
> >     ~~~~~~~~~^
> >         f"'{failure}' found in console, expected '{success}'")
> >         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > AssertionError: 'b'FAIL'' found in console, expected 'b'OK''
> >
> > not ok 1 test_nvme_migration.X8664NVMeMigrationTest.test_migration_with_unix
> > 1..1
> >
> > Copy the author too (Alexander).
> >
> > >
> > > *NOTE*: obviously this can't be merged as is but I think we could
> > > certainly do with more tracking in the test to see when things aren't
> > > working with possibly the sleep as another patch.
> > >
> > > Signed-off-by: Alex Bennée <[email protected]>
> > > ---
> > >  tests/functional/migration.py                 | 37 +++++++++++++++----
> > >  .../functional/x86_64/test_nvme_migration.py  |  1 +
> > >  2 files changed, 31 insertions(+), 7 deletions(-)
> > >
> > > diff --git a/tests/functional/migration.py b/tests/functional/migration.py
> > > index 8d2428efc24..e51a82c22f4 100644
> > > --- a/tests/functional/migration.py
> > > +++ b/tests/functional/migration.py
> > > @@ -22,18 +22,41 @@ class MigrationTest(QemuSystemTest):
> > >      timeout = 10
> > >
> > >      @staticmethod
> > > -    def migration_finished(vm):
> > > -        return vm.cmd('query-migrate')['status'] in ('completed', 
> > > 'failed')
> > > +    def migration_finished(query):
> > > +        return query['status'] in ('completed', 'failed')
> > >
> > > -    def assert_migration(self, src_vm, dst_vm):
> > > +    def wait_for_vm(self, src_or_dst, vm):
> > > +        "Track the state of one VM in the migration"
> > > +        current_state = last_state = vm.cmd('query-migrate')
> > >
> > > -        end = time.monotonic() + self.timeout
> > > -        while time.monotonic() < end and not 
> > > self.migration_finished(src_vm):
> > > -            time.sleep(0.1)
> > > +        self.log.info(f"waiting for {src_or_dst} to complete")
> > >
> > >          end = time.monotonic() + self.timeout
> > > -        while time.monotonic() < end and not 
> > > self.migration_finished(dst_vm):
> > > +        while time.monotonic() < end:
> > > +            if self.migration_finished(current_state):
> > > +                break
> > > +            else:
> > > +                last_remain = last_state['remaining']
> > > +                current_remain = current_state['remaining']
> > > +
> > > +                if current_remain > last_remain:
> > > +                    self.log.warning(f"slow migration {current_remain} > 
> > > {last_remain}")
> > > +
> > >              time.sleep(0.1)
> > > +            last_state = current_state
> > > +            current_state = vm.cmd('query-migrate')
> > > +
> > > +
> > > +    def assert_migration(self, src_vm, dst_vm):
> > > +
> > > +        self.wait_for_vm("src", src_vm)
> > > +
> > > +        # if we hit this we never completed...
> > > +        self.assertEqual(src_vm.cmd('query-migrate')['status'], 
> > > 'completed')
> > > +
> > > +        self.wait_for_vm("dst", dst_vm)
> > > +
> > > +        self.log.info("checking everything is OK")
> > >
> > >          self.assertEqual(src_vm.cmd('query-migrate')['status'], 
> > > 'completed')
> > >          self.assertEqual(dst_vm.cmd('query-migrate')['status'], 
> > > 'completed')
> > > diff --git a/tests/functional/x86_64/test_nvme_migration.py 
> > > b/tests/functional/x86_64/test_nvme_migration.py
> > > index 890f0aab6d6..58596f63cd3 100755
> > > --- a/tests/functional/x86_64/test_nvme_migration.py
> > > +++ b/tests/functional/x86_64/test_nvme_migration.py
> > > @@ -109,6 +109,7 @@ def launch_source_vm(self, vm):
> > >                  rm -f /tmp/test_nvme_mig_workload.iter_finished; \
> > >                  echo 3 > /proc/sys/vm/drop_caches; \
> > >                  find /usr/bin -type f -exec cksum {} \\;; \
> > > +                sleep 0.3; \
> > >                  touch /tmp/test_nvme_mig_workload.iter_finished; \
> > >              done) > /dev/null 2> /tmp/test_nvme_mig_workload.errors &",
> > >              prompt, vm=vm)
> > > --
> > > 2.47.3
> > >
> >
> > --
> > Peter Xu
> >
> 

-- 
Peter Xu


Reply via email to