F: hw/s390x/
F: include/hw/s390x/
F: configs/devices/s390x-softmmu/default.mak
-F: tests/avocado/machine_s390_ccw_virtio.py
+F: tests/functional/test_s390x_ccw_virtio.py
T: git https://github.com/borntraeger/qemu.git s390-next
L: qemu-s390x@nongnu.org
F: target/s390x/kvm/stsi-topology.c
F: docs/devel/s390-cpu-topology.rst
F: docs/system/s390x/cpu-topology.rst
-F: tests/avocado/s390_topology.py
+F: tests/functional/test_s390x_topology.py
X86 Machines
------------
+++ /dev/null
-# Functional test that boots an s390x Linux guest with ccw and PCI devices
-# attached and checks whether the devices are recognized by Linux
-#
-# Copyright (c) 2020 Red Hat, Inc.
-#
-# Author:
-# Cornelia Huck <cohuck@redhat.com>
-#
-# This work is licensed under the terms of the GNU GPL, version 2 or
-# later. See the COPYING file in the top-level directory.
-
-import os
-import tempfile
-
-from avocado import skipUnless
-from avocado_qemu import QemuSystemTest
-from avocado_qemu import exec_command_and_wait_for_pattern
-from avocado_qemu import wait_for_console_pattern
-from avocado.utils import archive
-
-class S390CCWVirtioMachine(QemuSystemTest):
- KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
-
- timeout = 120
-
- def wait_for_console_pattern(self, success_message, vm=None):
- wait_for_console_pattern(self, success_message,
- failure_message='Kernel panic - not syncing',
- vm=vm)
-
- def wait_for_crw_reports(self):
- exec_command_and_wait_for_pattern(self,
- 'while ! (dmesg -c | grep CRW) ; do sleep 1 ; done',
- 'CRW reports')
-
- dmesg_clear_count = 1
- def clear_guest_dmesg(self):
- exec_command_and_wait_for_pattern(self, 'dmesg -c > /dev/null; '
- r'echo dm_clear\ ' + str(self.dmesg_clear_count),
- r'dm_clear ' + str(self.dmesg_clear_count))
- self.dmesg_clear_count += 1
-
- def test_s390x_devices(self):
-
- """
- :avocado: tags=arch:s390x
- :avocado: tags=machine:s390-ccw-virtio
- """
-
- kernel_url = ('https://snapshot.debian.org/archive/debian/'
- '20201126T092837Z/dists/buster/main/installer-s390x/'
- '20190702+deb10u6/images/generic/kernel.debian')
- kernel_hash = '5821fbee57d6220a067a8b967d24595621aa1eb6'
- kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
-
- initrd_url = ('https://snapshot.debian.org/archive/debian/'
- '20201126T092837Z/dists/buster/main/installer-s390x/'
- '20190702+deb10u6/images/generic/initrd.debian')
- initrd_hash = '81ba09c97bef46e8f4660ac25b4ac0a5be3a94d6'
- initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
-
- self.vm.set_console()
- kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
- 'console=sclp0 root=/dev/ram0 BOOT_DEBUG=3')
- self.vm.add_args('-nographic',
- '-kernel', kernel_path,
- '-initrd', initrd_path,
- '-append', kernel_command_line,
- '-cpu', 'max,prno-trng=off',
- '-device', 'virtio-net-ccw,devno=fe.1.1111',
- '-device',
- 'virtio-rng-ccw,devno=fe.2.0000,max_revision=0,id=rn1',
- '-device',
- 'virtio-rng-ccw,devno=fe.3.1234,max_revision=2,id=rn2',
- '-device', 'zpci,uid=5,target=zzz',
- '-device', 'virtio-net-pci,id=zzz',
- '-device', 'zpci,uid=0xa,fid=12,target=serial',
- '-device', 'virtio-serial-pci,id=serial',
- '-device', 'virtio-balloon-ccw')
- self.vm.launch()
-
- shell_ready = "sh: can't access tty; job control turned off"
- self.wait_for_console_pattern(shell_ready)
- # first debug shell is too early, we need to wait for device detection
- exec_command_and_wait_for_pattern(self, 'exit', shell_ready)
-
- ccw_bus_ids="0.1.1111 0.2.0000 0.3.1234"
- pci_bus_ids="0005:00:00.0 000a:00:00.0"
- exec_command_and_wait_for_pattern(self, 'ls /sys/bus/ccw/devices/',
- ccw_bus_ids)
- exec_command_and_wait_for_pattern(self, 'ls /sys/bus/pci/devices/',
- pci_bus_ids)
- # check that the device at 0.2.0000 is in legacy mode, while the
- # device at 0.3.1234 has the virtio-1 feature bit set
- virtio_rng_features="00000000000000000000000000001100" + \
- "10000000000000000000000000000000"
- virtio_rng_features_legacy="00000000000000000000000000001100" + \
- "00000000000000000000000000000000"
- exec_command_and_wait_for_pattern(self,
- 'cat /sys/bus/ccw/devices/0.2.0000/virtio?/features',
- virtio_rng_features_legacy)
- exec_command_and_wait_for_pattern(self,
- 'cat /sys/bus/ccw/devices/0.3.1234/virtio?/features',
- virtio_rng_features)
- # check that /dev/hwrng works - and that it's gone after ejecting
- exec_command_and_wait_for_pattern(self,
- 'dd if=/dev/hwrng of=/dev/null bs=1k count=10',
- '10+0 records out')
- self.clear_guest_dmesg()
- self.vm.cmd('device_del', id='rn1')
- self.wait_for_crw_reports()
- self.clear_guest_dmesg()
- self.vm.cmd('device_del', id='rn2')
- self.wait_for_crw_reports()
- exec_command_and_wait_for_pattern(self,
- 'dd if=/dev/hwrng of=/dev/null bs=1k count=10',
- 'dd: /dev/hwrng: No such device')
- # verify that we indeed have virtio-net devices (without having the
- # virtio-net driver handy)
- exec_command_and_wait_for_pattern(self,
- 'cat /sys/bus/ccw/devices/0.1.1111/cutype',
- '3832/01')
- exec_command_and_wait_for_pattern(self,
- r'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_vendor',
- r'0x1af4')
- exec_command_and_wait_for_pattern(self,
- r'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_device',
- r'0x0001')
- # check fid propagation
- exec_command_and_wait_for_pattern(self,
- r'cat /sys/bus/pci/devices/000a\:00\:00.0/function_id',
- r'0x0000000c')
- # add another device
- self.clear_guest_dmesg()
- self.vm.cmd('device_add', driver='virtio-net-ccw',
- devno='fe.0.4711', id='net_4711')
- self.wait_for_crw_reports()
- exec_command_and_wait_for_pattern(self, 'for i in 1 2 3 4 5 6 7 ; do '
- 'if [ -e /sys/bus/ccw/devices/*4711 ]; then break; fi ;'
- 'sleep 1 ; done ; ls /sys/bus/ccw/devices/',
- '0.0.4711')
- # and detach it again
- self.clear_guest_dmesg()
- self.vm.cmd('device_del', id='net_4711')
- self.vm.event_wait(name='DEVICE_DELETED',
- match={'data': {'device': 'net_4711'}})
- self.wait_for_crw_reports()
- exec_command_and_wait_for_pattern(self,
- 'ls /sys/bus/ccw/devices/0.0.4711',
- 'No such file or directory')
- # test the virtio-balloon device
- exec_command_and_wait_for_pattern(self, 'head -n 1 /proc/meminfo',
- 'MemTotal: 115640 kB')
- self.vm.cmd('human-monitor-command', command_line='balloon 96')
- exec_command_and_wait_for_pattern(self, 'head -n 1 /proc/meminfo',
- 'MemTotal: 82872 kB')
- self.vm.cmd('human-monitor-command', command_line='balloon 128')
- exec_command_and_wait_for_pattern(self, 'head -n 1 /proc/meminfo',
- 'MemTotal: 115640 kB')
-
-
- def test_s390x_fedora(self):
-
- """
- :avocado: tags=arch:s390x
- :avocado: tags=machine:s390-ccw-virtio
- :avocado: tags=device:virtio-gpu
- :avocado: tags=device:virtio-crypto
- :avocado: tags=device:virtio-net
- :avocado: tags=flaky
- """
-
- kernel_url = ('https://archives.fedoraproject.org/pub/archive'
- '/fedora-secondary/releases/31/Server/s390x/os'
- '/images/kernel.img')
- kernel_hash = 'b93d1efcafcf29c1673a4ce371a1f8b43941cfeb'
- kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
-
- initrd_url = ('https://archives.fedoraproject.org/pub/archive'
- '/fedora-secondary/releases/31/Server/s390x/os'
- '/images/initrd.img')
- initrd_hash = '3de45d411df5624b8d8ef21cd0b44419ab59b12f'
- initrd_path_xz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
- initrd_path = os.path.join(self.workdir, 'initrd-raw.img')
- archive.lzma_uncompress(initrd_path_xz, initrd_path)
-
- self.vm.set_console()
- kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + ' audit=0 '
- 'rd.plymouth=0 plymouth.enable=0 rd.rescue')
- self.vm.add_args('-nographic',
- '-smp', '4',
- '-m', '512',
- '-name', 'Some Guest Name',
- '-uuid', '30de4fd9-b4d5-409e-86a5-09b387f70bfa',
- '-kernel', kernel_path,
- '-initrd', initrd_path,
- '-append', kernel_command_line,
- '-device', 'zpci,uid=7,target=n',
- '-device', 'virtio-net-pci,id=n,mac=02:ca:fe:fa:ce:12',
- '-device', 'virtio-rng-ccw,devno=fe.1.9876',
- '-device', 'virtio-gpu-ccw,devno=fe.2.5432')
- self.vm.launch()
- self.wait_for_console_pattern('Kernel command line: %s'
- % kernel_command_line)
- self.wait_for_console_pattern('Entering emergency mode')
-
- # Some tests to see whether the CLI options have been considered:
- self.log.info("Test whether QEMU CLI options have been considered")
- exec_command_and_wait_for_pattern(self,
- 'while ! (dmesg | grep enP7p0s0) ; do sleep 1 ; done',
- 'virtio_net virtio0 enP7p0s0: renamed')
- exec_command_and_wait_for_pattern(self, 'lspci',
- '0007:00:00.0 Class 0200: Device 1af4:1000')
- exec_command_and_wait_for_pattern(self,
- 'cat /sys/class/net/enP7p0s0/address',
- '02:ca:fe:fa:ce:12')
- exec_command_and_wait_for_pattern(self, 'lscss', '0.1.9876')
- exec_command_and_wait_for_pattern(self, 'lscss', '0.2.5432')
- exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
- 'processors : 4')
- exec_command_and_wait_for_pattern(self, 'grep MemTotal /proc/meminfo',
- 'MemTotal: 499848 kB')
- exec_command_and_wait_for_pattern(self, 'grep Name /proc/sysinfo',
- 'Extended Name: Some Guest Name')
- exec_command_and_wait_for_pattern(self, 'grep UUID /proc/sysinfo',
- '30de4fd9-b4d5-409e-86a5-09b387f70bfa')
-
- # Disable blinking cursor, then write some stuff into the framebuffer.
- # QEMU's PPM screendumps contain uncompressed 24-bit values, while the
- # framebuffer uses 32-bit, so we pad our text with some spaces when
- # writing to the framebuffer. Since the PPM is uncompressed, we then
- # can simply read the written "magic bytes" back from the PPM file to
- # check whether the framebuffer is working as expected.
- # Unfortunately, this test is flaky, so we don't run it by default
- if os.getenv('QEMU_TEST_FLAKY_TESTS'):
- self.log.info("Test screendump of virtio-gpu device")
- exec_command_and_wait_for_pattern(self,
- 'while ! (dmesg | grep gpudrmfb) ; do sleep 1 ; done',
- 'virtio_gpudrmfb frame buffer device')
- exec_command_and_wait_for_pattern(self,
- r'echo -e "\e[?25l" > /dev/tty0', ':/#')
- exec_command_and_wait_for_pattern(self, 'for ((i=0;i<250;i++)); do '
- 'echo " The qu ick fo x j ump s o ver a laz y d og" >> fox.txt;'
- 'done',
- ':/#')
- exec_command_and_wait_for_pattern(self,
- 'dd if=fox.txt of=/dev/fb0 bs=1000 oflag=sync,nocache ; rm fox.txt',
- '12+0 records out')
- with tempfile.NamedTemporaryFile(suffix='.ppm',
- prefix='qemu-scrdump-') as ppmfile:
- self.vm.cmd('screendump', filename=ppmfile.name)
- ppmfile.seek(0)
- line = ppmfile.readline()
- self.assertEqual(line, b"P6\n")
- line = ppmfile.readline()
- self.assertEqual(line, b"1280 800\n")
- line = ppmfile.readline()
- self.assertEqual(line, b"255\n")
- line = ppmfile.readline(256)
- self.assertEqual(line, b"The quick fox jumps over a lazy dog\n")
- else:
- self.log.info("Skipped flaky screendump of virtio-gpu device test")
-
- # Hot-plug a virtio-crypto device and see whether it gets accepted
- self.log.info("Test hot-plug virtio-crypto device")
- self.clear_guest_dmesg()
- self.vm.cmd('object-add', qom_type='cryptodev-backend-builtin',
- id='cbe0')
- self.vm.cmd('device_add', driver='virtio-crypto-ccw', id='crypdev0',
- cryptodev='cbe0', devno='fe.0.2342')
- exec_command_and_wait_for_pattern(self,
- 'while ! (dmesg -c | grep Accelerator.device) ; do'
- ' sleep 1 ; done', 'Accelerator device is ready')
- exec_command_and_wait_for_pattern(self, 'lscss', '0.0.2342')
- self.vm.cmd('device_del', id='crypdev0')
- self.vm.cmd('object-del', id='cbe0')
- exec_command_and_wait_for_pattern(self,
- 'while ! (dmesg -c | grep Start.virtcrypto_remove) ; do'
- ' sleep 1 ; done', 'Start virtcrypto_remove.')
+++ /dev/null
-# Functional test that boots a Linux kernel and checks the console
-#
-# Copyright IBM Corp. 2023
-#
-# Author:
-# Pierre Morel <pmorel@linux.ibm.com>
-#
-# This work is licensed under the terms of the GNU GPL, version 2 or
-# later. See the COPYING file in the top-level directory.
-
-import os
-import shutil
-import time
-
-from avocado_qemu import QemuSystemTest
-from avocado_qemu import exec_command
-from avocado_qemu import exec_command_and_wait_for_pattern
-from avocado_qemu import interrupt_interactive_console_until_pattern
-from avocado_qemu import wait_for_console_pattern
-from avocado.utils import process
-from avocado.utils import archive
-
-
-class S390CPUTopology(QemuSystemTest):
- """
- S390x CPU topology consists of 4 topology layers, from bottom to top,
- the cores, sockets, books and drawers and 2 modifiers attributes,
- the entitlement and the dedication.
- See: docs/system/s390x/cpu-topology.rst.
-
- S390x CPU topology is setup in different ways:
- - implicitly from the '-smp' argument by completing each topology
- level one after the other beginning with drawer 0, book 0 and
- socket 0.
- - explicitly from the '-device' argument on the QEMU command line
- - explicitly by hotplug of a new CPU using QMP or HMP
- - it is modified by using QMP 'set-cpu-topology'
-
- The S390x modifier attribute entitlement depends on the machine
- polarization, which can be horizontal or vertical.
- The polarization is changed on a request from the guest.
- """
- timeout = 90
- event_timeout = 10
-
- KERNEL_COMMON_COMMAND_LINE = ('printk.time=0 '
- 'root=/dev/ram '
- 'selinux=0 '
- 'rdinit=/bin/sh')
-
- def wait_until_booted(self):
- wait_for_console_pattern(self, 'no job control',
- failure_message='Kernel panic - not syncing',
- vm=None)
-
- def check_topology(self, c, s, b, d, e, t):
- res = self.vm.qmp('query-cpus-fast')
- cpus = res['return']
- for cpu in cpus:
- core = cpu['props']['core-id']
- socket = cpu['props']['socket-id']
- book = cpu['props']['book-id']
- drawer = cpu['props']['drawer-id']
- entitlement = cpu.get('entitlement')
- dedicated = cpu.get('dedicated')
- if core == c:
- self.assertEqual(drawer, d)
- self.assertEqual(book, b)
- self.assertEqual(socket, s)
- self.assertEqual(entitlement, e)
- self.assertEqual(dedicated, t)
-
- def kernel_init(self):
- """
- We need a VM that supports CPU topology,
- currently this only the case when using KVM, not TCG.
- We need a kernel supporting the CPU topology.
- We need a minimal root filesystem with a shell.
- """
- self.require_accelerator("kvm")
- kernel_url = ('https://archives.fedoraproject.org/pub/archive'
- '/fedora-secondary/releases/35/Server/s390x/os'
- '/images/kernel.img')
- kernel_hash = '0d1aaaf303f07cf0160c8c48e56fe638'
- kernel_path = self.fetch_asset(kernel_url, algorithm='md5',
- asset_hash=kernel_hash)
-
- initrd_url = ('https://archives.fedoraproject.org/pub/archive'
- '/fedora-secondary/releases/35/Server/s390x/os'
- '/images/initrd.img')
- initrd_hash = 'a122057d95725ac030e2ec51df46e172'
- initrd_path_xz = self.fetch_asset(initrd_url, algorithm='md5',
- asset_hash=initrd_hash)
- initrd_path = os.path.join(self.workdir, 'initrd-raw.img')
- archive.lzma_uncompress(initrd_path_xz, initrd_path)
-
- self.vm.set_console()
- kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE
- self.vm.add_args('-nographic',
- '-enable-kvm',
- '-cpu', 'max,ctop=on',
- '-m', '512',
- '-kernel', kernel_path,
- '-initrd', initrd_path,
- '-append', kernel_command_line)
-
- def system_init(self):
- self.log.info("System init")
- exec_command_and_wait_for_pattern(self,
- """ mount proc -t proc /proc;
- mount sys -t sysfs /sys;
- cat /sys/devices/system/cpu/dispatching """,
- '0')
-
- def test_single(self):
- """
- This test checks the simplest topology with a single CPU.
-
- :avocado: tags=arch:s390x
- :avocado: tags=machine:s390-ccw-virtio
- """
- self.kernel_init()
- self.vm.launch()
- self.wait_until_booted()
- self.check_topology(0, 0, 0, 0, 'medium', False)
-
- def test_default(self):
- """
- This test checks the implicit topology.
-
- :avocado: tags=arch:s390x
- :avocado: tags=machine:s390-ccw-virtio
- """
- self.kernel_init()
- self.vm.add_args('-smp',
- '13,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
- self.vm.launch()
- self.wait_until_booted()
- self.check_topology(0, 0, 0, 0, 'medium', False)
- self.check_topology(1, 0, 0, 0, 'medium', False)
- self.check_topology(2, 1, 0, 0, 'medium', False)
- self.check_topology(3, 1, 0, 0, 'medium', False)
- self.check_topology(4, 2, 0, 0, 'medium', False)
- self.check_topology(5, 2, 0, 0, 'medium', False)
- self.check_topology(6, 0, 1, 0, 'medium', False)
- self.check_topology(7, 0, 1, 0, 'medium', False)
- self.check_topology(8, 1, 1, 0, 'medium', False)
- self.check_topology(9, 1, 1, 0, 'medium', False)
- self.check_topology(10, 2, 1, 0, 'medium', False)
- self.check_topology(11, 2, 1, 0, 'medium', False)
- self.check_topology(12, 0, 0, 1, 'medium', False)
-
- def test_move(self):
- """
- This test checks the topology modification by moving a CPU
- to another socket: CPU 0 is moved from socket 0 to socket 2.
-
- :avocado: tags=arch:s390x
- :avocado: tags=machine:s390-ccw-virtio
- """
- self.kernel_init()
- self.vm.add_args('-smp',
- '1,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
- self.vm.launch()
- self.wait_until_booted()
-
- self.check_topology(0, 0, 0, 0, 'medium', False)
- res = self.vm.qmp('set-cpu-topology',
- {'core-id': 0, 'socket-id': 2, 'entitlement': 'low'})
- self.assertEqual(res['return'], {})
- self.check_topology(0, 2, 0, 0, 'low', False)
-
- def test_dash_device(self):
- """
- This test verifies that a CPU defined with the '-device'
- command line option finds its right place inside the topology.
-
- :avocado: tags=arch:s390x
- :avocado: tags=machine:s390-ccw-virtio
- """
- self.kernel_init()
- self.vm.add_args('-smp',
- '1,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
- self.vm.add_args('-device', 'max-s390x-cpu,core-id=10')
- self.vm.add_args('-device',
- 'max-s390x-cpu,'
- 'core-id=1,socket-id=0,book-id=1,drawer-id=1,entitlement=low')
- self.vm.add_args('-device',
- 'max-s390x-cpu,'
- 'core-id=2,socket-id=0,book-id=1,drawer-id=1,entitlement=medium')
- self.vm.add_args('-device',
- 'max-s390x-cpu,'
- 'core-id=3,socket-id=1,book-id=1,drawer-id=1,entitlement=high')
- self.vm.add_args('-device',
- 'max-s390x-cpu,'
- 'core-id=4,socket-id=1,book-id=1,drawer-id=1')
- self.vm.add_args('-device',
- 'max-s390x-cpu,'
- 'core-id=5,socket-id=2,book-id=1,drawer-id=1,dedicated=true')
-
- self.vm.launch()
- self.wait_until_booted()
-
- self.check_topology(10, 2, 1, 0, 'medium', False)
- self.check_topology(1, 0, 1, 1, 'low', False)
- self.check_topology(2, 0, 1, 1, 'medium', False)
- self.check_topology(3, 1, 1, 1, 'high', False)
- self.check_topology(4, 1, 1, 1, 'medium', False)
- self.check_topology(5, 2, 1, 1, 'high', True)
-
-
- def guest_set_dispatching(self, dispatching):
- exec_command(self,
- f'echo {dispatching} > /sys/devices/system/cpu/dispatching')
- self.vm.event_wait('CPU_POLARIZATION_CHANGE', self.event_timeout)
- exec_command_and_wait_for_pattern(self,
- 'cat /sys/devices/system/cpu/dispatching', dispatching)
-
-
- def test_polarization(self):
- """
- This test verifies that QEMU modifies the entitlement change after
- several guest polarization change requests.
-
- :avocado: tags=arch:s390x
- :avocado: tags=machine:s390-ccw-virtio
- """
- self.kernel_init()
- self.vm.launch()
- self.wait_until_booted()
-
- self.system_init()
- res = self.vm.qmp('query-s390x-cpu-polarization')
- self.assertEqual(res['return']['polarization'], 'horizontal')
- self.check_topology(0, 0, 0, 0, 'medium', False)
-
- self.guest_set_dispatching('1');
- res = self.vm.qmp('query-s390x-cpu-polarization')
- self.assertEqual(res['return']['polarization'], 'vertical')
- self.check_topology(0, 0, 0, 0, 'medium', False)
-
- self.guest_set_dispatching('0');
- res = self.vm.qmp('query-s390x-cpu-polarization')
- self.assertEqual(res['return']['polarization'], 'horizontal')
- self.check_topology(0, 0, 0, 0, 'medium', False)
-
-
- def check_polarization(self, polarization):
- #We need to wait for the change to have been propagated to the kernel
- exec_command_and_wait_for_pattern(self,
- "\n".join([
- "timeout 1 sh -c 'while true",
- 'do',
- ' syspath="/sys/devices/system/cpu/cpu0/polarization"',
- ' polarization="$(cat "$syspath")" || exit',
- f' if [ "$polarization" = "{polarization}" ]; then',
- ' exit 0',
- ' fi',
- ' sleep 0.01',
- #searched for strings mustn't show up in command, '' to obfuscate
- "done' && echo succ''ess || echo fail''ure",
- ]),
- "success", "failure")
-
-
- def test_entitlement(self):
- """
- This test verifies that QEMU modifies the entitlement
- after a guest request and that the guest sees the change.
-
- :avocado: tags=arch:s390x
- :avocado: tags=machine:s390-ccw-virtio
- """
- self.kernel_init()
- self.vm.launch()
- self.wait_until_booted()
-
- self.system_init()
-
- self.check_polarization('horizontal')
- self.check_topology(0, 0, 0, 0, 'medium', False)
-
- self.guest_set_dispatching('1')
- self.check_polarization('vertical:medium')
- self.check_topology(0, 0, 0, 0, 'medium', False)
-
- res = self.vm.qmp('set-cpu-topology',
- {'core-id': 0, 'entitlement': 'low'})
- self.assertEqual(res['return'], {})
- self.check_polarization('vertical:low')
- self.check_topology(0, 0, 0, 0, 'low', False)
-
- res = self.vm.qmp('set-cpu-topology',
- {'core-id': 0, 'entitlement': 'medium'})
- self.assertEqual(res['return'], {})
- self.check_polarization('vertical:medium')
- self.check_topology(0, 0, 0, 0, 'medium', False)
-
- res = self.vm.qmp('set-cpu-topology',
- {'core-id': 0, 'entitlement': 'high'})
- self.assertEqual(res['return'], {})
- self.check_polarization('vertical:high')
- self.check_topology(0, 0, 0, 0, 'high', False)
-
- self.guest_set_dispatching('0');
- self.check_polarization("horizontal")
- self.check_topology(0, 0, 0, 0, 'high', False)
-
-
- def test_dedicated(self):
- """
- This test verifies that QEMU adjusts the entitlement correctly when a
- CPU is made dedicated.
- QEMU retains the entitlement value when horizontal polarization is in effect.
- For the guest, the field shows the effective value of the entitlement.
-
- :avocado: tags=arch:s390x
- :avocado: tags=machine:s390-ccw-virtio
- """
- self.kernel_init()
- self.vm.launch()
- self.wait_until_booted()
-
- self.system_init()
-
- self.check_polarization("horizontal")
-
- res = self.vm.qmp('set-cpu-topology',
- {'core-id': 0, 'dedicated': True})
- self.assertEqual(res['return'], {})
- self.check_topology(0, 0, 0, 0, 'high', True)
- self.check_polarization("horizontal")
-
- self.guest_set_dispatching('1');
- self.check_topology(0, 0, 0, 0, 'high', True)
- self.check_polarization("vertical:high")
-
- self.guest_set_dispatching('0');
- self.check_topology(0, 0, 0, 0, 'high', True)
- self.check_polarization("horizontal")
-
-
- def test_socket_full(self):
- """
- This test verifies that QEMU does not accept to overload a socket.
- The socket-id 0 on book-id 0 already contains CPUs 0 and 1 and can
- not accept any new CPU while socket-id 0 on book-id 1 is free.
-
- :avocado: tags=arch:s390x
- :avocado: tags=machine:s390-ccw-virtio
- """
- self.kernel_init()
- self.vm.add_args('-smp',
- '3,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
- self.vm.launch()
- self.wait_until_booted()
-
- self.system_init()
-
- res = self.vm.qmp('set-cpu-topology',
- {'core-id': 2, 'socket-id': 0, 'book-id': 0})
- self.assertEqual(res['error']['class'], 'GenericError')
-
- res = self.vm.qmp('set-cpu-topology',
- {'core-id': 2, 'socket-id': 0, 'book-id': 1})
- self.assertEqual(res['return'], {})
-
- def test_dedicated_error(self):
- """
- This test verifies that QEMU refuses to lower the entitlement
- of a dedicated CPU
-
- :avocado: tags=arch:s390x
- :avocado: tags=machine:s390-ccw-virtio
- """
- self.kernel_init()
- self.vm.launch()
- self.wait_until_booted()
-
- self.system_init()
-
- res = self.vm.qmp('set-cpu-topology',
- {'core-id': 0, 'dedicated': True})
- self.assertEqual(res['return'], {})
-
- self.check_topology(0, 0, 0, 0, 'high', True)
-
- self.guest_set_dispatching('1');
-
- self.check_topology(0, 0, 0, 0, 'high', True)
-
- res = self.vm.qmp('set-cpu-topology',
- {'core-id': 0, 'entitlement': 'low', 'dedicated': True})
- self.assertEqual(res['error']['class'], 'GenericError')
-
- res = self.vm.qmp('set-cpu-topology',
- {'core-id': 0, 'entitlement': 'low'})
- self.assertEqual(res['error']['class'], 'GenericError')
-
- res = self.vm.qmp('set-cpu-topology',
- {'core-id': 0, 'entitlement': 'medium', 'dedicated': True})
- self.assertEqual(res['error']['class'], 'GenericError')
-
- res = self.vm.qmp('set-cpu-topology',
- {'core-id': 0, 'entitlement': 'medium'})
- self.assertEqual(res['error']['class'], 'GenericError')
-
- res = self.vm.qmp('set-cpu-topology',
- {'core-id': 0, 'entitlement': 'low', 'dedicated': False})
- self.assertEqual(res['return'], {})
-
- res = self.vm.qmp('set-cpu-topology',
- {'core-id': 0, 'entitlement': 'medium', 'dedicated': False})
- self.assertEqual(res['return'], {})
-
- def test_move_error(self):
- """
- This test verifies that QEMU refuses to move a CPU to an
- nonexistent location
-
- :avocado: tags=arch:s390x
- :avocado: tags=machine:s390-ccw-virtio
- """
- self.kernel_init()
- self.vm.launch()
- self.wait_until_booted()
-
- self.system_init()
-
- res = self.vm.qmp('set-cpu-topology', {'core-id': 0, 'drawer-id': 1})
- self.assertEqual(res['error']['class'], 'GenericError')
-
- res = self.vm.qmp('set-cpu-topology', {'core-id': 0, 'book-id': 1})
- self.assertEqual(res['error']['class'], 'GenericError')
-
- res = self.vm.qmp('set-cpu-topology', {'core-id': 0, 'socket-id': 1})
- self.assertEqual(res['error']['class'], 'GenericError')
-
- self.check_topology(0, 0, 0, 0, 'medium', False)
# Timeouts for individual tests that can be slow e.g. with debugging enabled
test_timeouts = {
'netdev_ethtool' : 180,
+ 's390x_ccw_virtio' : 180,
}
tests_generic_system = [
'ppc_bamboo',
]
+tests_s390x_system_thorough = [
+ 's390x_ccw_virtio',
+ 's390x_topology',
+]
+
tests_sparc64_system_thorough = [
'sparc64_sun4u',
]
# This work is licensed under the terms of the GNU GPL, version 2 or
# later. See the COPYING file in the top-level directory.
+import lzma
+import os
+import shutil
import tarfile
def archive_extract(archive, dest_dir, member=None):
tf.extract(member=member, path=dest_dir)
else:
tf.extractall(path=dest_dir)
+
+def lzma_uncompress(xz_path, output_path):
+ if os.path.exists(output_path):
+ return
+ with lzma.open(xz_path, 'rb') as lzma_in:
+ try:
+ with open(output_path, 'wb') as raw_out:
+ shutil.copyfileobj(lzma_in, raw_out)
+ except:
+ os.remove(output_path)
+ raise
--- /dev/null
+#!/usr/bin/env python3
+#
+# Functional test that boots an s390x Linux guest with ccw and PCI devices
+# attached and checks whether the devices are recognized by Linux
+#
+# Copyright (c) 2020 Red Hat, Inc.
+#
+# Author:
+# Cornelia Huck <cohuck@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+import tempfile
+
+from qemu_test import QemuSystemTest, Asset
+from qemu_test import exec_command_and_wait_for_pattern
+from qemu_test import wait_for_console_pattern
+from qemu_test.utils import lzma_uncompress
+
+class S390CCWVirtioMachine(QemuSystemTest):
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
+
+ timeout = 120
+
+ ASSET_BUSTER_KERNEL = Asset(
+ ('https://snapshot.debian.org/archive/debian/'
+ '20201126T092837Z/dists/buster/main/installer-s390x/'
+ '20190702+deb10u6/images/generic/kernel.debian'),
+ 'd411d17c39ae7ad38d27534376cbe88b68b403c325739364122c2e6f1537e818')
+ ASSET_BUSTER_INITRD = Asset(
+ ('https://snapshot.debian.org/archive/debian/'
+ '20201126T092837Z/dists/buster/main/installer-s390x/'
+ '20190702+deb10u6/images/generic/initrd.debian'),
+ '836bbd0fe6a5ca81274c28c2b063ea315ce1868660866e9b60180c575fef9fd5')
+
+ ASSET_F31_KERNEL = Asset(
+ ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/31/Server/s390x/os'
+ '/images/kernel.img'),
+ '480859574f3f44caa6cd35c62d70e1ac0609134e22ce2a954bbed9b110c06e0b')
+ ASSET_F31_INITRD = Asset(
+ ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/31/Server/s390x/os'
+ '/images/initrd.img'),
+ '04c46095b2c49020b1c2327158898b7db747e4892ae319726192fb949716aa9c')
+
+ def wait_for_console_pattern(self, success_message, vm=None):
+ wait_for_console_pattern(self, success_message,
+ failure_message='Kernel panic - not syncing',
+ vm=vm)
+
+ def wait_for_crw_reports(self):
+ exec_command_and_wait_for_pattern(self,
+ 'while ! (dmesg -c | grep CRW) ; do sleep 1 ; done',
+ 'CRW reports')
+
+ dmesg_clear_count = 1
+ def clear_guest_dmesg(self):
+ exec_command_and_wait_for_pattern(self, 'dmesg -c > /dev/null; '
+ r'echo dm_clear\ ' + str(self.dmesg_clear_count),
+ r'dm_clear ' + str(self.dmesg_clear_count))
+ self.dmesg_clear_count += 1
+
+ def test_s390x_devices(self):
+ self.set_machine('s390-ccw-virtio')
+
+ kernel_path = self.ASSET_BUSTER_KERNEL.fetch()
+ initrd_path = self.ASSET_BUSTER_INITRD.fetch()
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=sclp0 root=/dev/ram0 BOOT_DEBUG=3')
+ self.vm.add_args('-nographic',
+ '-kernel', kernel_path,
+ '-initrd', initrd_path,
+ '-append', kernel_command_line,
+ '-cpu', 'max,prno-trng=off',
+ '-device', 'virtio-net-ccw,devno=fe.1.1111',
+ '-device',
+ 'virtio-rng-ccw,devno=fe.2.0000,max_revision=0,id=rn1',
+ '-device',
+ 'virtio-rng-ccw,devno=fe.3.1234,max_revision=2,id=rn2',
+ '-device', 'zpci,uid=5,target=zzz',
+ '-device', 'virtio-net-pci,id=zzz',
+ '-device', 'zpci,uid=0xa,fid=12,target=serial',
+ '-device', 'virtio-serial-pci,id=serial',
+ '-device', 'virtio-balloon-ccw')
+ self.vm.launch()
+
+ shell_ready = "sh: can't access tty; job control turned off"
+ self.wait_for_console_pattern(shell_ready)
+ # first debug shell is too early, we need to wait for device detection
+ exec_command_and_wait_for_pattern(self, 'exit', shell_ready)
+
+ ccw_bus_ids="0.1.1111 0.2.0000 0.3.1234"
+ pci_bus_ids="0005:00:00.0 000a:00:00.0"
+ exec_command_and_wait_for_pattern(self, 'ls /sys/bus/ccw/devices/',
+ ccw_bus_ids)
+ exec_command_and_wait_for_pattern(self, 'ls /sys/bus/pci/devices/',
+ pci_bus_ids)
+ # check that the device at 0.2.0000 is in legacy mode, while the
+ # device at 0.3.1234 has the virtio-1 feature bit set
+ virtio_rng_features="00000000000000000000000000001100" + \
+ "10000000000000000000000000000000"
+ virtio_rng_features_legacy="00000000000000000000000000001100" + \
+ "00000000000000000000000000000000"
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/bus/ccw/devices/0.2.0000/virtio?/features',
+ virtio_rng_features_legacy)
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/bus/ccw/devices/0.3.1234/virtio?/features',
+ virtio_rng_features)
+ # check that /dev/hwrng works - and that it's gone after ejecting
+ exec_command_and_wait_for_pattern(self,
+ 'dd if=/dev/hwrng of=/dev/null bs=1k count=10',
+ '10+0 records out')
+ self.clear_guest_dmesg()
+ self.vm.cmd('device_del', id='rn1')
+ self.wait_for_crw_reports()
+ self.clear_guest_dmesg()
+ self.vm.cmd('device_del', id='rn2')
+ self.wait_for_crw_reports()
+ exec_command_and_wait_for_pattern(self,
+ 'dd if=/dev/hwrng of=/dev/null bs=1k count=10',
+ 'dd: /dev/hwrng: No such device')
+ # verify that we indeed have virtio-net devices (without having the
+ # virtio-net driver handy)
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/bus/ccw/devices/0.1.1111/cutype',
+ '3832/01')
+ exec_command_and_wait_for_pattern(self,
+ r'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_vendor',
+ r'0x1af4')
+ exec_command_and_wait_for_pattern(self,
+ r'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_device',
+ r'0x0001')
+ # check fid propagation
+ exec_command_and_wait_for_pattern(self,
+ r'cat /sys/bus/pci/devices/000a\:00\:00.0/function_id',
+ r'0x0000000c')
+ # add another device
+ self.clear_guest_dmesg()
+ self.vm.cmd('device_add', driver='virtio-net-ccw',
+ devno='fe.0.4711', id='net_4711')
+ self.wait_for_crw_reports()
+ exec_command_and_wait_for_pattern(self, 'for i in 1 2 3 4 5 6 7 ; do '
+ 'if [ -e /sys/bus/ccw/devices/*4711 ]; then break; fi ;'
+ 'sleep 1 ; done ; ls /sys/bus/ccw/devices/',
+ '0.0.4711')
+ # and detach it again
+ self.clear_guest_dmesg()
+ self.vm.cmd('device_del', id='net_4711')
+ self.vm.event_wait(name='DEVICE_DELETED',
+ match={'data': {'device': 'net_4711'}})
+ self.wait_for_crw_reports()
+ exec_command_and_wait_for_pattern(self,
+ 'ls /sys/bus/ccw/devices/0.0.4711',
+ 'No such file or directory')
+ # test the virtio-balloon device
+ exec_command_and_wait_for_pattern(self, 'head -n 1 /proc/meminfo',
+ 'MemTotal: 115640 kB')
+ self.vm.cmd('human-monitor-command', command_line='balloon 96')
+ exec_command_and_wait_for_pattern(self, 'head -n 1 /proc/meminfo',
+ 'MemTotal: 82872 kB')
+ self.vm.cmd('human-monitor-command', command_line='balloon 128')
+ exec_command_and_wait_for_pattern(self, 'head -n 1 /proc/meminfo',
+ 'MemTotal: 115640 kB')
+
+
+ def test_s390x_fedora(self):
+ self.set_machine('s390-ccw-virtio')
+
+ kernel_path = self.ASSET_F31_KERNEL.fetch()
+
+ initrd_path_xz = self.ASSET_F31_INITRD.fetch()
+ initrd_path = os.path.join(self.workdir, 'initrd-raw.img')
+ lzma_uncompress(initrd_path_xz, initrd_path)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + ' audit=0 '
+ 'rd.plymouth=0 plymouth.enable=0 rd.rescue')
+ self.vm.add_args('-nographic',
+ '-smp', '4',
+ '-m', '512',
+ '-name', 'Some Guest Name',
+ '-uuid', '30de4fd9-b4d5-409e-86a5-09b387f70bfa',
+ '-kernel', kernel_path,
+ '-initrd', initrd_path,
+ '-append', kernel_command_line,
+ '-device', 'zpci,uid=7,target=n',
+ '-device', 'virtio-net-pci,id=n,mac=02:ca:fe:fa:ce:12',
+ '-device', 'virtio-rng-ccw,devno=fe.1.9876',
+ '-device', 'virtio-gpu-ccw,devno=fe.2.5432')
+ self.vm.launch()
+ self.wait_for_console_pattern('Kernel command line: %s'
+ % kernel_command_line)
+ self.wait_for_console_pattern('Entering emergency mode')
+
+ # Some tests to see whether the CLI options have been considered:
+ self.log.info("Test whether QEMU CLI options have been considered")
+ exec_command_and_wait_for_pattern(self,
+ 'while ! (dmesg | grep enP7p0s0) ; do sleep 1 ; done',
+ 'virtio_net virtio0 enP7p0s0: renamed')
+ exec_command_and_wait_for_pattern(self, 'lspci',
+ '0007:00:00.0 Class 0200: Device 1af4:1000')
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/class/net/enP7p0s0/address',
+ '02:ca:fe:fa:ce:12')
+ exec_command_and_wait_for_pattern(self, 'lscss', '0.1.9876')
+ exec_command_and_wait_for_pattern(self, 'lscss', '0.2.5432')
+ exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+ 'processors : 4')
+ exec_command_and_wait_for_pattern(self, 'grep MemTotal /proc/meminfo',
+ 'MemTotal: 499848 kB')
+ exec_command_and_wait_for_pattern(self, 'grep Name /proc/sysinfo',
+ 'Extended Name: Some Guest Name')
+ exec_command_and_wait_for_pattern(self, 'grep UUID /proc/sysinfo',
+ '30de4fd9-b4d5-409e-86a5-09b387f70bfa')
+
+ # Disable blinking cursor, then write some stuff into the framebuffer.
+ # QEMU's PPM screendumps contain uncompressed 24-bit values, while the
+ # framebuffer uses 32-bit, so we pad our text with some spaces when
+ # writing to the framebuffer. Since the PPM is uncompressed, we then
+ # can simply read the written "magic bytes" back from the PPM file to
+ # check whether the framebuffer is working as expected.
+ # Unfortunately, this test is flaky, so we don't run it by default
+ if os.getenv('QEMU_TEST_FLAKY_TESTS'):
+ self.log.info("Test screendump of virtio-gpu device")
+ exec_command_and_wait_for_pattern(self,
+ 'while ! (dmesg | grep gpudrmfb) ; do sleep 1 ; done',
+ 'virtio_gpudrmfb frame buffer device')
+ exec_command_and_wait_for_pattern(self,
+ r'echo -e "\e[?25l" > /dev/tty0', ':/#')
+ exec_command_and_wait_for_pattern(self, 'for ((i=0;i<250;i++)); do '
+ 'echo " The qu ick fo x j ump s o ver a laz y d og" >> fox.txt;'
+ 'done',
+ ':/#')
+ exec_command_and_wait_for_pattern(self,
+ 'dd if=fox.txt of=/dev/fb0 bs=1000 oflag=sync,nocache ; rm fox.txt',
+ '12+0 records out')
+ with tempfile.NamedTemporaryFile(suffix='.ppm',
+ prefix='qemu-scrdump-') as ppmfile:
+ self.vm.cmd('screendump', filename=ppmfile.name)
+ ppmfile.seek(0)
+ line = ppmfile.readline()
+ self.assertEqual(line, b"P6\n")
+ line = ppmfile.readline()
+ self.assertEqual(line, b"1280 800\n")
+ line = ppmfile.readline()
+ self.assertEqual(line, b"255\n")
+ line = ppmfile.readline(256)
+ self.assertEqual(line, b"The quick fox jumps over a lazy dog\n")
+ else:
+ self.log.info("Skipped flaky screendump of virtio-gpu device test")
+
+ # Hot-plug a virtio-crypto device and see whether it gets accepted
+ self.log.info("Test hot-plug virtio-crypto device")
+ self.clear_guest_dmesg()
+ self.vm.cmd('object-add', qom_type='cryptodev-backend-builtin',
+ id='cbe0')
+ self.vm.cmd('device_add', driver='virtio-crypto-ccw', id='crypdev0',
+ cryptodev='cbe0', devno='fe.0.2342')
+ exec_command_and_wait_for_pattern(self,
+ 'while ! (dmesg -c | grep Accelerator.device) ; do'
+ ' sleep 1 ; done', 'Accelerator device is ready')
+ exec_command_and_wait_for_pattern(self, 'lscss', '0.0.2342')
+ self.vm.cmd('device_del', id='crypdev0')
+ self.vm.cmd('object-del', id='cbe0')
+ exec_command_and_wait_for_pattern(self,
+ 'while ! (dmesg -c | grep Start.virtcrypto_remove) ; do'
+ ' sleep 1 ; done', 'Start virtcrypto_remove.')
+
+if __name__ == '__main__':
+ QemuSystemTest.main()
--- /dev/null
+#!/usr/bin/env python3
+#
+# Functional test that boots a Linux kernel and checks the console
+#
+# Copyright IBM Corp. 2023
+#
+# Author:
+# Pierre Morel <pmorel@linux.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+import time
+
+from qemu_test import QemuSystemTest, Asset
+from qemu_test import exec_command
+from qemu_test import exec_command_and_wait_for_pattern
+from qemu_test import wait_for_console_pattern
+from qemu_test.utils import lzma_uncompress
+
+
+class S390CPUTopology(QemuSystemTest):
+ """
+ S390x CPU topology consists of 4 topology layers, from bottom to top,
+ the cores, sockets, books and drawers and 2 modifiers attributes,
+ the entitlement and the dedication.
+ See: docs/system/s390x/cpu-topology.rst.
+
+ S390x CPU topology is setup in different ways:
+ - implicitly from the '-smp' argument by completing each topology
+ level one after the other beginning with drawer 0, book 0 and
+ socket 0.
+ - explicitly from the '-device' argument on the QEMU command line
+ - explicitly by hotplug of a new CPU using QMP or HMP
+ - it is modified by using QMP 'set-cpu-topology'
+
+ The S390x modifier attribute entitlement depends on the machine
+ polarization, which can be horizontal or vertical.
+ The polarization is changed on a request from the guest.
+ """
+ timeout = 90
+ event_timeout = 10
+
+ KERNEL_COMMON_COMMAND_LINE = ('printk.time=0 '
+ 'root=/dev/ram '
+ 'selinux=0 '
+ 'rdinit=/bin/sh')
+ ASSET_F35_KERNEL = Asset(
+ ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/35/Server/s390x/os'
+ '/images/kernel.img'),
+ '1f2dddfd11bb1393dd2eb2e784036fbf6fc11057a6d7d27f9eb12d3edc67ef73')
+
+ ASSET_F35_INITRD = Asset(
+ ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/35/Server/s390x/os'
+ '/images/initrd.img'),
+ '1100145fbca00240c8c372ae4b89b48c99844bc189b3dfbc3f481dc60055ca46')
+
+ def wait_until_booted(self):
+ wait_for_console_pattern(self, 'no job control',
+ failure_message='Kernel panic - not syncing',
+ vm=None)
+
+ def check_topology(self, c, s, b, d, e, t):
+ res = self.vm.qmp('query-cpus-fast')
+ cpus = res['return']
+ for cpu in cpus:
+ core = cpu['props']['core-id']
+ socket = cpu['props']['socket-id']
+ book = cpu['props']['book-id']
+ drawer = cpu['props']['drawer-id']
+ entitlement = cpu.get('entitlement')
+ dedicated = cpu.get('dedicated')
+ if core == c:
+ self.assertEqual(drawer, d)
+ self.assertEqual(book, b)
+ self.assertEqual(socket, s)
+ self.assertEqual(entitlement, e)
+ self.assertEqual(dedicated, t)
+
+ def kernel_init(self):
+ """
+ We need a VM that supports CPU topology,
+ currently this only the case when using KVM, not TCG.
+ We need a kernel supporting the CPU topology.
+ We need a minimal root filesystem with a shell.
+ """
+ self.require_accelerator("kvm")
+ kernel_path = self.ASSET_F35_KERNEL.fetch()
+ initrd_path_xz = self.ASSET_F35_INITRD.fetch()
+ initrd_path = os.path.join(self.workdir, 'initrd-raw.img')
+ lzma_uncompress(initrd_path_xz, initrd_path)
+
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE
+ self.vm.add_args('-nographic',
+ '-enable-kvm',
+ '-cpu', 'max,ctop=on',
+ '-m', '512',
+ '-kernel', kernel_path,
+ '-initrd', initrd_path,
+ '-append', kernel_command_line)
+
+ def system_init(self):
+ self.log.info("System init")
+ exec_command_and_wait_for_pattern(self,
+ """ mount proc -t proc /proc;
+ mount sys -t sysfs /sys;
+ cat /sys/devices/system/cpu/dispatching """,
+ '0')
+
+ def test_single(self):
+ """
+ This test checks the simplest topology with a single CPU.
+ """
+ self.set_machine('s390-ccw-virtio')
+ self.kernel_init()
+ self.vm.launch()
+ self.wait_until_booted()
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+
+ def test_default(self):
+ """
+ This test checks the implicit topology.
+ """
+ self.set_machine('s390-ccw-virtio')
+ self.kernel_init()
+ self.vm.add_args('-smp',
+ '13,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
+ self.vm.launch()
+ self.wait_until_booted()
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+ self.check_topology(1, 0, 0, 0, 'medium', False)
+ self.check_topology(2, 1, 0, 0, 'medium', False)
+ self.check_topology(3, 1, 0, 0, 'medium', False)
+ self.check_topology(4, 2, 0, 0, 'medium', False)
+ self.check_topology(5, 2, 0, 0, 'medium', False)
+ self.check_topology(6, 0, 1, 0, 'medium', False)
+ self.check_topology(7, 0, 1, 0, 'medium', False)
+ self.check_topology(8, 1, 1, 0, 'medium', False)
+ self.check_topology(9, 1, 1, 0, 'medium', False)
+ self.check_topology(10, 2, 1, 0, 'medium', False)
+ self.check_topology(11, 2, 1, 0, 'medium', False)
+ self.check_topology(12, 0, 0, 1, 'medium', False)
+
+ def test_move(self):
+ """
+ This test checks the topology modification by moving a CPU
+ to another socket: CPU 0 is moved from socket 0 to socket 2.
+ """
+ self.set_machine('s390-ccw-virtio')
+ self.kernel_init()
+ self.vm.add_args('-smp',
+ '1,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'socket-id': 2, 'entitlement': 'low'})
+ self.assertEqual(res['return'], {})
+ self.check_topology(0, 2, 0, 0, 'low', False)
+
+ def test_dash_device(self):
+ """
+ This test verifies that a CPU defined with the '-device'
+ command line option finds its right place inside the topology.
+ """
+ self.set_machine('s390-ccw-virtio')
+ self.kernel_init()
+ self.vm.add_args('-smp',
+ '1,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
+ self.vm.add_args('-device', 'max-s390x-cpu,core-id=10')
+ self.vm.add_args('-device',
+ 'max-s390x-cpu,'
+ 'core-id=1,socket-id=0,book-id=1,drawer-id=1,entitlement=low')
+ self.vm.add_args('-device',
+ 'max-s390x-cpu,'
+ 'core-id=2,socket-id=0,book-id=1,drawer-id=1,entitlement=medium')
+ self.vm.add_args('-device',
+ 'max-s390x-cpu,'
+ 'core-id=3,socket-id=1,book-id=1,drawer-id=1,entitlement=high')
+ self.vm.add_args('-device',
+ 'max-s390x-cpu,'
+ 'core-id=4,socket-id=1,book-id=1,drawer-id=1')
+ self.vm.add_args('-device',
+ 'max-s390x-cpu,'
+ 'core-id=5,socket-id=2,book-id=1,drawer-id=1,dedicated=true')
+
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.check_topology(10, 2, 1, 0, 'medium', False)
+ self.check_topology(1, 0, 1, 1, 'low', False)
+ self.check_topology(2, 0, 1, 1, 'medium', False)
+ self.check_topology(3, 1, 1, 1, 'high', False)
+ self.check_topology(4, 1, 1, 1, 'medium', False)
+ self.check_topology(5, 2, 1, 1, 'high', True)
+
+
+ def guest_set_dispatching(self, dispatching):
+ exec_command(self,
+ f'echo {dispatching} > /sys/devices/system/cpu/dispatching')
+ self.vm.event_wait('CPU_POLARIZATION_CHANGE', self.event_timeout)
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/devices/system/cpu/dispatching', dispatching)
+
+
+ def test_polarization(self):
+ """
+ This test verifies that QEMU modifies the entitlement change after
+ several guest polarization change requests.
+ """
+ self.set_machine('s390-ccw-virtio')
+ self.kernel_init()
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.system_init()
+ res = self.vm.qmp('query-s390x-cpu-polarization')
+ self.assertEqual(res['return']['polarization'], 'horizontal')
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+
+ self.guest_set_dispatching('1');
+ res = self.vm.qmp('query-s390x-cpu-polarization')
+ self.assertEqual(res['return']['polarization'], 'vertical')
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+
+ self.guest_set_dispatching('0');
+ res = self.vm.qmp('query-s390x-cpu-polarization')
+ self.assertEqual(res['return']['polarization'], 'horizontal')
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+
+
+ def check_polarization(self, polarization):
+ #We need to wait for the change to have been propagated to the kernel
+ exec_command_and_wait_for_pattern(self,
+ "\n".join([
+ "timeout 1 sh -c 'while true",
+ 'do',
+ ' syspath="/sys/devices/system/cpu/cpu0/polarization"',
+ ' polarization="$(cat "$syspath")" || exit',
+ f' if [ "$polarization" = "{polarization}" ]; then',
+ ' exit 0',
+ ' fi',
+ ' sleep 0.01',
+ #searched for strings mustn't show up in command, '' to obfuscate
+ "done' && echo succ''ess || echo fail''ure",
+ ]),
+ "success", "failure")
+
+
+ def test_entitlement(self):
+ """
+ This test verifies that QEMU modifies the entitlement
+ after a guest request and that the guest sees the change.
+ """
+ self.set_machine('s390-ccw-virtio')
+ self.kernel_init()
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.system_init()
+
+ self.check_polarization('horizontal')
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+
+ self.guest_set_dispatching('1')
+ self.check_polarization('vertical:medium')
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'low'})
+ self.assertEqual(res['return'], {})
+ self.check_polarization('vertical:low')
+ self.check_topology(0, 0, 0, 0, 'low', False)
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'medium'})
+ self.assertEqual(res['return'], {})
+ self.check_polarization('vertical:medium')
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'high'})
+ self.assertEqual(res['return'], {})
+ self.check_polarization('vertical:high')
+ self.check_topology(0, 0, 0, 0, 'high', False)
+
+ self.guest_set_dispatching('0');
+ self.check_polarization("horizontal")
+ self.check_topology(0, 0, 0, 0, 'high', False)
+
+
+ def test_dedicated(self):
+ """
+ This test verifies that QEMU adjusts the entitlement correctly when a
+ CPU is made dedicated.
+ QEMU retains the entitlement value when horizontal polarization is in effect.
+ For the guest, the field shows the effective value of the entitlement.
+ """
+ self.set_machine('s390-ccw-virtio')
+ self.kernel_init()
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.system_init()
+
+ self.check_polarization("horizontal")
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'dedicated': True})
+ self.assertEqual(res['return'], {})
+ self.check_topology(0, 0, 0, 0, 'high', True)
+ self.check_polarization("horizontal")
+
+ self.guest_set_dispatching('1');
+ self.check_topology(0, 0, 0, 0, 'high', True)
+ self.check_polarization("vertical:high")
+
+ self.guest_set_dispatching('0');
+ self.check_topology(0, 0, 0, 0, 'high', True)
+ self.check_polarization("horizontal")
+
+
+ def test_socket_full(self):
+ """
+ This test verifies that QEMU does not accept to overload a socket.
+ The socket-id 0 on book-id 0 already contains CPUs 0 and 1 and can
+ not accept any new CPU while socket-id 0 on book-id 1 is free.
+ """
+ self.set_machine('s390-ccw-virtio')
+ self.kernel_init()
+ self.vm.add_args('-smp',
+ '3,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.system_init()
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 2, 'socket-id': 0, 'book-id': 0})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 2, 'socket-id': 0, 'book-id': 1})
+ self.assertEqual(res['return'], {})
+
+ def test_dedicated_error(self):
+ """
+ This test verifies that QEMU refuses to lower the entitlement
+ of a dedicated CPU
+ """
+ self.set_machine('s390-ccw-virtio')
+ self.kernel_init()
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.system_init()
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'dedicated': True})
+ self.assertEqual(res['return'], {})
+
+ self.check_topology(0, 0, 0, 0, 'high', True)
+
+ self.guest_set_dispatching('1');
+
+ self.check_topology(0, 0, 0, 0, 'high', True)
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'low', 'dedicated': True})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'low'})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'medium', 'dedicated': True})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'medium'})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'low', 'dedicated': False})
+ self.assertEqual(res['return'], {})
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'medium', 'dedicated': False})
+ self.assertEqual(res['return'], {})
+
+ def test_move_error(self):
+ """
+ This test verifies that QEMU refuses to move a CPU to an
+ nonexistent location
+ """
+ self.set_machine('s390-ccw-virtio')
+ self.kernel_init()
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.system_init()
+
+ res = self.vm.qmp('set-cpu-topology', {'core-id': 0, 'drawer-id': 1})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ res = self.vm.qmp('set-cpu-topology', {'core-id': 0, 'book-id': 1})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ res = self.vm.qmp('set-cpu-topology', {'core-id': 0, 'socket-id': 1})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+
+if __name__ == '__main__':
+ QemuSystemTest.main()