@samitouri / QOSamiQemu / commits / 63f5ba8092

tests/functional: Add remote interrupts test for PowerNV

In the past there have been hard to recreate issues where XIVE changes cause qemu crashes due to multi-socket interrupts such as in [1]. Add a functional test explicitly to test whether remote interrupts work. The test can also work as additional boot test for multi-socket boot, initrd boot test, as well as a check for e1000e to be working in powernv, though that's not a target goal, and are additional benefits. >From docs/system/devices/net.rst: In order to check that the user mode network is working, you can ping the address 10.0.2.2 and verify that you got an address in the range 10.0.2.x from the QEMU virtual DHCP server. Hence use 10.0.2.2 with ping. [1]: https://lore.kernel.org/qemu-devel/baf6c854-832b-4a2e-922f-d34e6dadf821@redhat.com/ Tested-by: Shivang Upadhyay <shivangu@linux.ibm.com> Reviewed-by: Shivang Upadhyay <shivangu@linux.ibm.com> Tested-by: Misbah Anjum N <misanjum@linux.ibm.com> Signed-off-by: Aditya Gupta <adityag@linux.ibm.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Link: https://lore.kernel.org/qemu-devel/20260703085955.2318600-2-adityag@linux.ibm.com Signed-off-by: Harsh Prateek Bora <harshpb@linux.ibm.com>

Aditya Gupta committed Jul 3, 2026 at 14:29 UTC 63f5ba8092166fd975dea8ee050e65d004209e34
1 file changed +95
tests/functional/ppc64/test_powernv.py
+95
@@ -9,6 +9,7 @@
9
10 from qemu_test import LinuxKernelTest, Asset
11 from qemu_test import wait_for_console_pattern
12 +from qemu_test import exec_command_and_wait_for_pattern
13
14 class PowernvMachine(LinuxKernelTest):
15
@@ -32,6 +33,17 @@ class PowernvMachine(LinuxKernelTest):
33 ('https://github.com/roz3x/qemu/raw/refs/heads/sample-dtb/qemu-powernv11.dtb'),
34 'ea1271516264eea1eb58a067a99d0c2ca9528be8dc7d4e46bb2d5ae0d42fc568')
35
36 + def shell_exec_command_check_fail(self, command):
37 + fail_msg="Fail"
38 + self.shell_exec_command(f"export __FAIL_MSG={fail_msg}")
39 +
40 + # If the exit code for the command is non 0, print fail message
41 + command = command + " || echo $__FAIL_MSG"
42 + exec_command_and_wait_for_pattern(self, command, '#', fail_msg)
43 +
44 + def shell_exec_command(self, command):
45 + exec_command_and_wait_for_pattern(self, command, '#', self.panic_message)
46 +
47 def do_test_linux_boot(self, command_line = KERNEL_COMMON_COMMAND_LINE):
48 self.require_accelerator("tcg")
49 kernel_path = self.ASSET_KERNEL.fetch()
@@ -76,6 +88,89 @@ class PowernvMachine(LinuxKernelTest):
88 wait_for_console_pattern(self, console_pattern, self.panic_message)
89 wait_for_console_pattern(self, self.good_message, self.panic_message)
90
91 + def test_linux_remote_interrupts(self):
92 + self.require_accelerator("tcg")
93 + self.set_machine('powernv')
94 +
95 + # Have below setup in this test:
96 + # 1. e1000e attached to pcie.6, which is from 7th PHB, belonging to 2nd
97 + # socket (chip 1), in a powernv boot with default 6 PHBs per socket
98 + # 2. CPU on 2nd socket (chip 1) disabled
99 + # 3. RX IRQ's affinity to chip 2, and TX IRQ's affinity to chip 3
100 + #
101 + # Then ping is done, to generate interrupts from e1000e which should go
102 + # to IRQ server on the remote sockets
103 + self.vm.add_args('-smp', '4,sockets=4,cores=1,threads=1')
104 + self.vm.add_args('-netdev', 'user,id=net0')
105 + self.vm.add_args('-device', 'e1000e,netdev=net0,bus=pcie.6')
106 +
107 + kernel_path = self.ASSET_KERNEL.fetch()
108 + rootfs_path = self.ASSET_INITRD.fetch()
109 + self.vm.set_console()
110 + self.vm.add_args('-kernel', kernel_path,
111 + '-drive',
112 + f'file={rootfs_path},format=raw,if=none,id=drive0,readonly=on',
113 + '-append', 'root=/dev/nvme0n1 console=hvc0',
114 + '-device', 'nvme,drive=drive0,bus=pcie.2,addr=0x0,serial=1234')
115 + self.vm.launch()
116 +
117 + # Wait for boot to complete
118 + console_pattern = 'CPU maps initialized for 1 thread per core'
119 + wait_for_console_pattern(self, console_pattern, self.panic_message)
120 + console_pattern = 'smp: Brought up 4 nodes, 4 CPUs'
121 + wait_for_console_pattern(self, console_pattern, self.panic_message)
122 + wait_for_console_pattern(self, 'Run /sbin/init as init process',
123 + self.panic_message)
124 +
125 + # Wait for login prompt and login as root (no password in buildroot)
126 + wait_for_console_pattern(self, 'login:', self.panic_message)
127 + exec_command_and_wait_for_pattern(self, 'root', '#', self.panic_message)
128 +
129 + # e1000e is connected to socket 1, disable the CPU on socket 1
130 + self.shell_exec_command("echo 0 > /sys/devices/system/cpu/cpu1/online")
131 + self.shell_exec_command(
132 + "export CPU1_STATE=$(cat /sys/devices/system/cpu/cpu1/online)")
133 + self.shell_exec_command_check_fail("[ $CPU1_STATE -eq 0 ]")
134 +
135 + # RX, TX interrupts to chip/cpu 2 & 3 respectively
136 + self.shell_exec_command(
137 + "export RX_IRQ=$(awk '/eth0-rx/ {print $1}' /proc/interrupts | tr -d ':')")
138 + self.shell_exec_command(
139 + "export TX_IRQ=$(awk '/eth0-tx/ {print $1}' /proc/interrupts | tr -d ':')")
140 + self.shell_exec_command("echo 2 > /proc/irq/$RX_IRQ/smp_affinity_list")
141 + self.shell_exec_command("echo 3 > /proc/irq/$TX_IRQ/smp_affinity_list")
142 +
143 + # Capture interrupt counts before generating traffic
144 + self.shell_exec_command(
145 + "export RX_BEFORE=$(awk '/eth0-rx/ {print $3}' /proc/interrupts)")
146 + self.shell_exec_command(
147 + "export TX_BEFORE=$(awk '/eth0-tx/ {print $4}' /proc/interrupts)")
148 +
149 + # Wait up to 15 seconds for eth0 link to come up
150 + self.shell_exec_command(
151 + "c=0; while ! ip addr show eth0 | grep 'inet 10.0.2'; do "
152 + "sleep 1; c=$((c+1)); [ $c -gt 15 ] && break; done")
153 +
154 + self.shell_exec_command_check_fail(
155 + "ip addr show eth0 | grep 'inet 10.0.2'")
156 +
157 + # Generate network traffic to trigger remote interrupts
158 + # Ping QEMU's user-mode network gateway (10.0.2.2)
159 + self.shell_exec_command("ping -W2 -c5 10.0.2.2")
160 +
161 + # Show final interrupt counts to verify remote interrupts occurred
162 + self.shell_exec_command("cat /proc/interrupts | grep eth0")
163 +
164 + # Verify interrupt counts increased (whether interrupts were delivered)
165 + self.shell_exec_command(
166 + "export RX_AFTER=$(awk '/eth0-rx/ {print $3}' /proc/interrupts)")
167 + self.shell_exec_command(
168 + "export TX_AFTER=$(awk '/eth0-tx/ {print $4}' /proc/interrupts)")
169 +
170 + # Check that interrupt counts increased
171 + self.shell_exec_command_check_fail("[ $RX_AFTER -gt $RX_BEFORE ]")
172 + self.shell_exec_command_check_fail("[ $TX_AFTER -gt $TX_BEFORE ]")
173 +
174 def test_linux_big_boot(self):
175 self.set_machine('powernv')
176 self.vm.add_args('-smp', '16,threads=4,cores=2,sockets=2')