Merge remote-tracking branch 'qemu-project/master'
[qemu/ar7.git] / tests / avocado / s390_topology.py
blob9154ac8776244aa91dcb7b6f4d652cb2ba97d3c2
1 # Functional test that boots a Linux kernel and checks the console
3 # Copyright IBM Corp. 2023
5 # Author:
6 # Pierre Morel <pmorel@linux.ibm.com>
8 # This work is licensed under the terms of the GNU GPL, version 2 or
9 # later. See the COPYING file in the top-level directory.
11 import os
12 import shutil
13 import time
15 from avocado_qemu import QemuSystemTest
16 from avocado_qemu import exec_command
17 from avocado_qemu import exec_command_and_wait_for_pattern
18 from avocado_qemu import interrupt_interactive_console_until_pattern
19 from avocado_qemu import wait_for_console_pattern
20 from avocado.utils import process
21 from avocado.utils import archive
24 class S390CPUTopology(QemuSystemTest):
25 """
26 S390x CPU topology consists of 4 topology layers, from bottom to top,
27 the cores, sockets, books and drawers and 2 modifiers attributes,
28 the entitlement and the dedication.
29 See: docs/system/s390x/cpu-topology.rst.
31 S390x CPU topology is setup in different ways:
32 - implicitly from the '-smp' argument by completing each topology
33 level one after the other beginning with drawer 0, book 0 and
34 socket 0.
35 - explicitly from the '-device' argument on the QEMU command line
36 - explicitly by hotplug of a new CPU using QMP or HMP
37 - it is modified by using QMP 'set-cpu-topology'
39 The S390x modifier attribute entitlement depends on the machine
40 polarization, which can be horizontal or vertical.
41 The polarization is changed on a request from the guest.
42 """
43 timeout = 90
44 event_timeout = 10
46 KERNEL_COMMON_COMMAND_LINE = ('printk.time=0 '
47 'root=/dev/ram '
48 'selinux=0 '
49 'rdinit=/bin/sh')
51 def wait_until_booted(self):
52 wait_for_console_pattern(self, 'no job control',
53 failure_message='Kernel panic - not syncing',
54 vm=None)
56 def check_topology(self, c, s, b, d, e, t):
57 res = self.vm.qmp('query-cpus-fast')
58 cpus = res['return']
59 for cpu in cpus:
60 core = cpu['props']['core-id']
61 socket = cpu['props']['socket-id']
62 book = cpu['props']['book-id']
63 drawer = cpu['props']['drawer-id']
64 entitlement = cpu.get('entitlement')
65 dedicated = cpu.get('dedicated')
66 if core == c:
67 self.assertEqual(drawer, d)
68 self.assertEqual(book, b)
69 self.assertEqual(socket, s)
70 self.assertEqual(entitlement, e)
71 self.assertEqual(dedicated, t)
73 def kernel_init(self):
74 """
75 We need a VM that supports CPU topology,
76 currently this only the case when using KVM, not TCG.
77 We need a kernel supporting the CPU topology.
78 We need a minimal root filesystem with a shell.
79 """
80 self.require_accelerator("kvm")
81 kernel_url = ('https://archives.fedoraproject.org/pub/archive'
82 '/fedora-secondary/releases/35/Server/s390x/os'
83 '/images/kernel.img')
84 kernel_hash = '0d1aaaf303f07cf0160c8c48e56fe638'
85 kernel_path = self.fetch_asset(kernel_url, algorithm='md5',
86 asset_hash=kernel_hash)
88 initrd_url = ('https://archives.fedoraproject.org/pub/archive'
89 '/fedora-secondary/releases/35/Server/s390x/os'
90 '/images/initrd.img')
91 initrd_hash = 'a122057d95725ac030e2ec51df46e172'
92 initrd_path_xz = self.fetch_asset(initrd_url, algorithm='md5',
93 asset_hash=initrd_hash)
94 initrd_path = os.path.join(self.workdir, 'initrd-raw.img')
95 archive.lzma_uncompress(initrd_path_xz, initrd_path)
97 self.vm.set_console()
98 kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE
99 self.vm.add_args('-nographic',
100 '-enable-kvm',
101 '-cpu', 'max,ctop=on',
102 '-m', '512',
103 '-kernel', kernel_path,
104 '-initrd', initrd_path,
105 '-append', kernel_command_line)
107 def system_init(self):
108 self.log.info("System init")
109 exec_command_and_wait_for_pattern(self,
110 """ mount proc -t proc /proc;
111 mount sys -t sysfs /sys;
112 cat /sys/devices/system/cpu/dispatching """,
113 '0')
115 def test_single(self):
117 This test checks the simplest topology with a single CPU.
119 :avocado: tags=arch:s390x
120 :avocado: tags=machine:s390-ccw-virtio
122 self.kernel_init()
123 self.vm.launch()
124 self.wait_until_booted()
125 self.check_topology(0, 0, 0, 0, 'medium', False)
127 def test_default(self):
129 This test checks the implicit topology.
131 :avocado: tags=arch:s390x
132 :avocado: tags=machine:s390-ccw-virtio
134 self.kernel_init()
135 self.vm.add_args('-smp',
136 '13,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
137 self.vm.launch()
138 self.wait_until_booted()
139 self.check_topology(0, 0, 0, 0, 'medium', False)
140 self.check_topology(1, 0, 0, 0, 'medium', False)
141 self.check_topology(2, 1, 0, 0, 'medium', False)
142 self.check_topology(3, 1, 0, 0, 'medium', False)
143 self.check_topology(4, 2, 0, 0, 'medium', False)
144 self.check_topology(5, 2, 0, 0, 'medium', False)
145 self.check_topology(6, 0, 1, 0, 'medium', False)
146 self.check_topology(7, 0, 1, 0, 'medium', False)
147 self.check_topology(8, 1, 1, 0, 'medium', False)
148 self.check_topology(9, 1, 1, 0, 'medium', False)
149 self.check_topology(10, 2, 1, 0, 'medium', False)
150 self.check_topology(11, 2, 1, 0, 'medium', False)
151 self.check_topology(12, 0, 0, 1, 'medium', False)
153 def test_move(self):
155 This test checks the topology modification by moving a CPU
156 to another socket: CPU 0 is moved from socket 0 to socket 2.
158 :avocado: tags=arch:s390x
159 :avocado: tags=machine:s390-ccw-virtio
161 self.kernel_init()
162 self.vm.add_args('-smp',
163 '1,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
164 self.vm.launch()
165 self.wait_until_booted()
167 self.check_topology(0, 0, 0, 0, 'medium', False)
168 res = self.vm.qmp('set-cpu-topology',
169 {'core-id': 0, 'socket-id': 2, 'entitlement': 'low'})
170 self.assertEqual(res['return'], {})
171 self.check_topology(0, 2, 0, 0, 'low', False)
173 def test_dash_device(self):
175 This test verifies that a CPU defined with the '-device'
176 command line option finds its right place inside the topology.
178 :avocado: tags=arch:s390x
179 :avocado: tags=machine:s390-ccw-virtio
181 self.kernel_init()
182 self.vm.add_args('-smp',
183 '1,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
184 self.vm.add_args('-device', 'max-s390x-cpu,core-id=10')
185 self.vm.add_args('-device',
186 'max-s390x-cpu,'
187 'core-id=1,socket-id=0,book-id=1,drawer-id=1,entitlement=low')
188 self.vm.add_args('-device',
189 'max-s390x-cpu,'
190 'core-id=2,socket-id=0,book-id=1,drawer-id=1,entitlement=medium')
191 self.vm.add_args('-device',
192 'max-s390x-cpu,'
193 'core-id=3,socket-id=1,book-id=1,drawer-id=1,entitlement=high')
194 self.vm.add_args('-device',
195 'max-s390x-cpu,'
196 'core-id=4,socket-id=1,book-id=1,drawer-id=1')
197 self.vm.add_args('-device',
198 'max-s390x-cpu,'
199 'core-id=5,socket-id=2,book-id=1,drawer-id=1,dedicated=true')
201 self.vm.launch()
202 self.wait_until_booted()
204 self.check_topology(10, 2, 1, 0, 'medium', False)
205 self.check_topology(1, 0, 1, 1, 'low', False)
206 self.check_topology(2, 0, 1, 1, 'medium', False)
207 self.check_topology(3, 1, 1, 1, 'high', False)
208 self.check_topology(4, 1, 1, 1, 'medium', False)
209 self.check_topology(5, 2, 1, 1, 'high', True)
212 def guest_set_dispatching(self, dispatching):
213 exec_command(self,
214 f'echo {dispatching} > /sys/devices/system/cpu/dispatching')
215 self.vm.event_wait('CPU_POLARIZATION_CHANGE', self.event_timeout)
216 exec_command_and_wait_for_pattern(self,
217 'cat /sys/devices/system/cpu/dispatching', dispatching)
220 def test_polarization(self):
222 This test verifies that QEMU modifies the entitlement change after
223 several guest polarization change requests.
225 :avocado: tags=arch:s390x
226 :avocado: tags=machine:s390-ccw-virtio
228 self.kernel_init()
229 self.vm.launch()
230 self.wait_until_booted()
232 self.system_init()
233 res = self.vm.qmp('query-s390x-cpu-polarization')
234 self.assertEqual(res['return']['polarization'], 'horizontal')
235 self.check_topology(0, 0, 0, 0, 'medium', False)
237 self.guest_set_dispatching('1');
238 res = self.vm.qmp('query-s390x-cpu-polarization')
239 self.assertEqual(res['return']['polarization'], 'vertical')
240 self.check_topology(0, 0, 0, 0, 'medium', False)
242 self.guest_set_dispatching('0');
243 res = self.vm.qmp('query-s390x-cpu-polarization')
244 self.assertEqual(res['return']['polarization'], 'horizontal')
245 self.check_topology(0, 0, 0, 0, 'medium', False)
248 def check_polarization(self, polarization):
249 #We need to wait for the change to have been propagated to the kernel
250 exec_command_and_wait_for_pattern(self,
251 "\n".join([
252 "timeout 1 sh -c 'while true",
253 'do',
254 ' syspath="/sys/devices/system/cpu/cpu0/polarization"',
255 ' polarization="$(cat "$syspath")" || exit',
256 f' if [ "$polarization" = "{polarization}" ]; then',
257 ' exit 0',
258 ' fi',
259 ' sleep 0.01',
260 #searched for strings mustn't show up in command, '' to obfuscate
261 "done' && echo succ''ess || echo fail''ure",
263 "success", "failure")
266 def test_entitlement(self):
268 This test verifies that QEMU modifies the entitlement
269 after a guest request and that the guest sees the change.
271 :avocado: tags=arch:s390x
272 :avocado: tags=machine:s390-ccw-virtio
274 self.kernel_init()
275 self.vm.launch()
276 self.wait_until_booted()
278 self.system_init()
280 self.check_polarization('horizontal')
281 self.check_topology(0, 0, 0, 0, 'medium', False)
283 self.guest_set_dispatching('1')
284 self.check_polarization('vertical:medium')
285 self.check_topology(0, 0, 0, 0, 'medium', False)
287 res = self.vm.qmp('set-cpu-topology',
288 {'core-id': 0, 'entitlement': 'low'})
289 self.assertEqual(res['return'], {})
290 self.check_polarization('vertical:low')
291 self.check_topology(0, 0, 0, 0, 'low', False)
293 res = self.vm.qmp('set-cpu-topology',
294 {'core-id': 0, 'entitlement': 'medium'})
295 self.assertEqual(res['return'], {})
296 self.check_polarization('vertical:medium')
297 self.check_topology(0, 0, 0, 0, 'medium', False)
299 res = self.vm.qmp('set-cpu-topology',
300 {'core-id': 0, 'entitlement': 'high'})
301 self.assertEqual(res['return'], {})
302 self.check_polarization('vertical:high')
303 self.check_topology(0, 0, 0, 0, 'high', False)
305 self.guest_set_dispatching('0');
306 self.check_polarization("horizontal")
307 self.check_topology(0, 0, 0, 0, 'high', False)
310 def test_dedicated(self):
312 This test verifies that QEMU adjusts the entitlement correctly when a
313 CPU is made dedicated.
314 QEMU retains the entitlement value when horizontal polarization is in effect.
315 For the guest, the field shows the effective value of the entitlement.
317 :avocado: tags=arch:s390x
318 :avocado: tags=machine:s390-ccw-virtio
320 self.kernel_init()
321 self.vm.launch()
322 self.wait_until_booted()
324 self.system_init()
326 self.check_polarization("horizontal")
328 res = self.vm.qmp('set-cpu-topology',
329 {'core-id': 0, 'dedicated': True})
330 self.assertEqual(res['return'], {})
331 self.check_topology(0, 0, 0, 0, 'high', True)
332 self.check_polarization("horizontal")
334 self.guest_set_dispatching('1');
335 self.check_topology(0, 0, 0, 0, 'high', True)
336 self.check_polarization("vertical:high")
338 self.guest_set_dispatching('0');
339 self.check_topology(0, 0, 0, 0, 'high', True)
340 self.check_polarization("horizontal")
343 def test_socket_full(self):
345 This test verifies that QEMU does not accept to overload a socket.
346 The socket-id 0 on book-id 0 already contains CPUs 0 and 1 and can
347 not accept any new CPU while socket-id 0 on book-id 1 is free.
349 :avocado: tags=arch:s390x
350 :avocado: tags=machine:s390-ccw-virtio
352 self.kernel_init()
353 self.vm.add_args('-smp',
354 '3,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
355 self.vm.launch()
356 self.wait_until_booted()
358 self.system_init()
360 res = self.vm.qmp('set-cpu-topology',
361 {'core-id': 2, 'socket-id': 0, 'book-id': 0})
362 self.assertEqual(res['error']['class'], 'GenericError')
364 res = self.vm.qmp('set-cpu-topology',
365 {'core-id': 2, 'socket-id': 0, 'book-id': 1})
366 self.assertEqual(res['return'], {})
368 def test_dedicated_error(self):
370 This test verifies that QEMU refuses to lower the entitlement
371 of a dedicated CPU
373 :avocado: tags=arch:s390x
374 :avocado: tags=machine:s390-ccw-virtio
376 self.kernel_init()
377 self.vm.launch()
378 self.wait_until_booted()
380 self.system_init()
382 res = self.vm.qmp('set-cpu-topology',
383 {'core-id': 0, 'dedicated': True})
384 self.assertEqual(res['return'], {})
386 self.check_topology(0, 0, 0, 0, 'high', True)
388 self.guest_set_dispatching('1');
390 self.check_topology(0, 0, 0, 0, 'high', True)
392 res = self.vm.qmp('set-cpu-topology',
393 {'core-id': 0, 'entitlement': 'low', 'dedicated': True})
394 self.assertEqual(res['error']['class'], 'GenericError')
396 res = self.vm.qmp('set-cpu-topology',
397 {'core-id': 0, 'entitlement': 'low'})
398 self.assertEqual(res['error']['class'], 'GenericError')
400 res = self.vm.qmp('set-cpu-topology',
401 {'core-id': 0, 'entitlement': 'medium', 'dedicated': True})
402 self.assertEqual(res['error']['class'], 'GenericError')
404 res = self.vm.qmp('set-cpu-topology',
405 {'core-id': 0, 'entitlement': 'medium'})
406 self.assertEqual(res['error']['class'], 'GenericError')
408 res = self.vm.qmp('set-cpu-topology',
409 {'core-id': 0, 'entitlement': 'low', 'dedicated': False})
410 self.assertEqual(res['return'], {})
412 res = self.vm.qmp('set-cpu-topology',
413 {'core-id': 0, 'entitlement': 'medium', 'dedicated': False})
414 self.assertEqual(res['return'], {})
416 def test_move_error(self):
418 This test verifies that QEMU refuses to move a CPU to an
419 nonexistent location
421 :avocado: tags=arch:s390x
422 :avocado: tags=machine:s390-ccw-virtio
424 self.kernel_init()
425 self.vm.launch()
426 self.wait_until_booted()
428 self.system_init()
430 res = self.vm.qmp('set-cpu-topology', {'core-id': 0, 'drawer-id': 1})
431 self.assertEqual(res['error']['class'], 'GenericError')
433 res = self.vm.qmp('set-cpu-topology', {'core-id': 0, 'book-id': 1})
434 self.assertEqual(res['error']['class'], 'GenericError')
436 res = self.vm.qmp('set-cpu-topology', {'core-id': 0, 'socket-id': 1})
437 self.assertEqual(res['error']['class'], 'GenericError')
439 self.check_topology(0, 0, 0, 0, 'medium', False)