** Description changed:

  Hello,
  
  Please backport the following upstream commit that fixes reccurent QMP
  timeouts :
  
  https://gitlab.com/qemu-
  project/qemu/-/commit/effd60c878176bcaf97fa7ce2b12d04bb8ead6f7
  
  This has been fixed in Noble and newer releases.
  
  Here is a reproducer to help identify the fix. Details on its usage is
  in comments
  
  #!/usr/bin/env python3
  ##############################################################################
  # Reproducer script for QEMU hang in snapshot at boot                        #
  # Requires: `qemu.qmp` python package                                        #
  # Fix: effd60c878176bcaf97fa7ce2b12d04bb8ead6f7                              #
  # Description:                                                               #
  # Linux appears to write _something_ to the UEFI variables at boot. If a qmp #
  # transaction is stated during the write operation it can deadlock qemu.     #
  ##############################################################################
  
  ##############################
  # Expected backtrace         #
  # [...]                      #
  # aio_poll                   #
  # [...]                      #
  # qmp_blockdev_snapshot_sync #
  # [...]                      #
  # aio_poll                   #
  # [...]                      #
  # pflash_write               #
  # [...]                      #
  ##############################
  
  ######################## Setup #########################
  # $ pip install qemu.qmp
  # $ cp /usr/share/OVMF/OVMF_VARS_4M.fd /tmp/vars.fd
  # $ wget 
https://github.com/cirros-dev/cirros/releases/download/0.6.3/cirros-0.6.3-x86_64-rootfs.img.gz
  # $ gunzip cirros-0.6.3-x86_64-rootfs.img.gz
- # $ qemu-img convert -f raw -O qcow2 cirros-0.6.3-x86_64-rootfs.img 
/tmp/disk.qcow2.ref
+ # $ qemu-img convert -f raw -O qcow2 cirros-0.6.3-x86_64-rootfs.img 
/tmp/disk.qcow2
  # $ rm -f cirros-0.6.3-x86_64-rootfs.img
  ########################################################
  
  import asyncio
  import logging
  import os
  import subprocess
  
  from qemu.qmp import QMPClient
  
  SOCKET = '/tmp/qmp-deadlock.sock'
  FW = '/usr/share/OVMF/OVMF_CODE_4M.fd'
  DISK = '/tmp/disk.qcow2'
  SNAP_FILE = '/tmp/snap.qcow2'
  FW_VARS = '/tmp/vars.fd'
  
  async def spawn_qemu():
-     blk_args = [
-         'driver=qcow2',
-         'node-name=snap-disk',
-         'file.driver=file',
-         f'file.filename={DISK}',
-     ]
-     cmd = [
-         'qemu-system-x86_64',
-         '-qmp', f'unix:{SOCKET}',
-         '-blockdev', ','.join(blk_args),
-         '-device', 'virtio-blk,drive=snap-disk',
-         '-drive', f'if=pflash,format=raw,readonly=on,file={FW}',
-         '-drive', f'if=pflash,format=raw,file={FW_VARS}',
-         '-m', '1G',
-         '-nographic',
-         #'-enable-kvm',
-     ]
-     #print(' '.join(cmd))
-     return await asyncio.create_subprocess_exec(
-         *cmd,
-         stdin=subprocess.DEVNULL,
-         stdout=subprocess.DEVNULL,
-     )
+     blk_args = [
+         'driver=qcow2',
+         'node-name=snap-disk',
+         'file.driver=file',
+         f'file.filename={DISK}',
+     ]
+     cmd = [
+         'qemu-system-x86_64',
+         '-qmp', f'unix:{SOCKET}',
+         '-blockdev', ','.join(blk_args),
+         '-device', 'virtio-blk,drive=snap-disk',
+         '-drive', f'if=pflash,format=raw,readonly=on,file={FW}',
+         '-drive', f'if=pflash,format=raw,file={FW_VARS}',
+         '-m', '1G',
+         '-nographic',
+         #'-enable-kvm',
+     ]
+     #print(' '.join(cmd))
+     return await asyncio.create_subprocess_exec(
+         *cmd,
+         stdin=subprocess.DEVNULL,
+         stdout=subprocess.DEVNULL,
+     )
  
  async def snap_rollback(qmp):
-     await qmp.execute('blockdev-snapshot-sync', {
-         'node-name': 'snap-disk',
-         'snapshot-node-name': 'snap',
-         'snapshot-file': SNAP_FILE,
-     })
-     with qmp.listener('BLOCK_JOB_READY') as listener:
-         await qmp.execute('block-commit', {
-             'device': 'snap',
-             'job-id': 'commit',
-         })
-         async for event in listener:
-             if event.get('data', {}).get('device') == 'commit':
-                 break
-     with qmp.listener('BLOCK_JOB_COMPLETED') as listener:
-         await qmp.execute('block-job-complete', {
-             'device': 'commit',
-         })
-         async for event in listener:
-             if event.get('data', {}).get('device') == 'commit':
-                 break
+     await qmp.execute('blockdev-snapshot-sync', {
+         'node-name': 'snap-disk',
+         'snapshot-node-name': 'snap',
+         'snapshot-file': SNAP_FILE,
+     })
+     with qmp.listener('BLOCK_JOB_READY') as listener:
+         await qmp.execute('block-commit', {
+             'device': 'snap',
+             'job-id': 'commit',
+         })
+         async for event in listener:
+             if event.get('data', {}).get('device') == 'commit':
+                 break
+     with qmp.listener('BLOCK_JOB_COMPLETED') as listener:
+         await qmp.execute('block-job-complete', {
+             'device': 'commit',
+         })
+         async for event in listener:
+             if event.get('data', {}).get('device') == 'commit':
+                 break
  
  async def qmp_main(qmp):
-     while True:
-         await asyncio.wait_for(snap_rollback(qmp), timeout=15)
+     while True:
+         await asyncio.wait_for(snap_rollback(qmp), timeout=15)
  
  async def main():
-     #logging.basicConfig(level=logging.DEBUG)
-     qmp = QMPClient('test-deadlock')
-     await qmp.start_server(SOCKET)
-     qemu, _ = await asyncio.gather(spawn_qemu(), qmp.accept())
-     print(f'qemu pid: {qemu.pid}')
+     #logging.basicConfig(level=logging.DEBUG)
+     qmp = QMPClient('test-deadlock')
+     await qmp.start_server(SOCKET)
+     qemu, _ = await asyncio.gather(spawn_qemu(), qmp.accept())
+     print(f'qemu pid: {qemu.pid}')
  
-     try:
-         await qmp_main(qmp)
-     except asyncio.TimeoutError:
-         print("QMP timeout, exiting")
-     finally:
-         try:
-             await qmp.disconnect()
-         finally:
-             qemu.kill()
-             await qemu.wait()
+     try:
+         await qmp_main(qmp)
+     except asyncio.TimeoutError:
+         print("QMP timeout, exiting")
+     finally:
+         try:
+             await qmp.disconnect()
+         finally:
+             qemu.kill()
+             await qemu.wait()
  
  asyncio.run(main())
  
  A debdiff with the patch will come shortly

-- 
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2091013

Title:
  Jammy : Please backport upstream fix to qmp timeouts

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/qemu/+bug/2091013/+subscriptions


-- 
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to