mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:56:38 +00:00
tests/functional: add a stress test for monitor hot unplug
When unplugging a monitor there is a careful synchronization dance between the monitor handling the "object-del" command and the command processing for the monitor being deleted. The stress test runs a busy loop of 'query-qmp-schema' on a second monitor, while the primary monitor requests its deletion. Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Tested-by: Peter Krempa <pkrempa@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20260706135824.2623960-31-berrange@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
committed by
Markus Armbruster
parent
f9827a9b9a
commit
8b142ecb34
@@ -6,11 +6,16 @@
|
||||
#
|
||||
# Copyright (c) 2026 Christian Brauner
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import random
|
||||
import threading
|
||||
import time
|
||||
|
||||
from qemu_test import QemuSystemTest
|
||||
|
||||
from qemu.qmp.legacy import QEMUMonitorProtocol
|
||||
from qemu.qmp import QMPClient
|
||||
|
||||
|
||||
class MonitorHotplug(QemuSystemTest):
|
||||
@@ -163,6 +168,63 @@ class MonitorHotplug(QemuSystemTest):
|
||||
qmp.close()
|
||||
self._remove_monitor()
|
||||
|
||||
def stress_mon(self, sock):
|
||||
async def main():
|
||||
qmp = QMPClient('testvm')
|
||||
await qmp.connect(sock)
|
||||
# Run query-version in a tight loop so that the
|
||||
# monitor thread/dispatcher is very busy at the
|
||||
# time we try to delete the monitor
|
||||
while True:
|
||||
try:
|
||||
# A command which returns a lot of data to make
|
||||
# it more likely we're in the I/O reply path
|
||||
# when deleting the monitor
|
||||
res = await qmp.execute('query-qmp-schema')
|
||||
# Some commands which generate async events
|
||||
# as those can trigger different code paths
|
||||
res = await qmp.execute('stop')
|
||||
res = await qmp.execute('cont')
|
||||
except:
|
||||
# we'll get here if the monitor is terminated
|
||||
# by QEMU in which case we must disconnect
|
||||
# out side, but....
|
||||
try:
|
||||
await qmp.disconnect()
|
||||
except (ConnectionResetError, EOFError, BrokenPipeError):
|
||||
# ... disconnect() will probably see
|
||||
# errors too, but we must try to call it
|
||||
# regardless to cleanup asyncio state
|
||||
# and prevent python warnings at GC time
|
||||
pass
|
||||
return
|
||||
asyncio.run(main())
|
||||
|
||||
def test_hotplug_stress(self):
|
||||
"""
|
||||
Repeatedly hotplug and unplug a monitor, while another thread
|
||||
concurrently issues commands on that monitor. This stresses
|
||||
the synchronization with the monitor thread during cleanup
|
||||
"""
|
||||
self.set_machine('none')
|
||||
self.vm.add_args('-nodefaults')
|
||||
self.vm.launch()
|
||||
|
||||
# Each loop sleeps at most 0.5 seconds, so this should
|
||||
# give an upper bound of approx 5 seconds execution
|
||||
# time which is reasonable to run by default
|
||||
repeat = 10
|
||||
for i in range(repeat):
|
||||
# First cycle
|
||||
sock = self._add_monitor()
|
||||
print ("# stress cycle %02d/%02d" % (i, repeat))
|
||||
stress = threading.Thread(target=self.stress_mon, args=[sock])
|
||||
stress.start()
|
||||
# Sleep upto 1/2 second to vary the races
|
||||
time.sleep(random.random() / 2)
|
||||
self._remove_monitor()
|
||||
stress.join()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
QemuSystemTest.main()
|
||||
|
||||
Reference in New Issue
Block a user