On Thu, 2026-06-25 at 01:29 +0530, Aditya Gupta wrote:
> 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/[email protected]/
> 
> Signed-off-by: Aditya Gupta <[email protected]>
> ---
>  tests/functional/ppc64/test_powernv.py | 95
> ++++++++++++++++++++++++++
>  1 file changed, 95 insertions(+)
> 
> diff --git a/tests/functional/ppc64/test_powernv.py
> b/tests/functional/ppc64/test_powernv.py
> index 0ea6c93e4287..9760f58ee41e 100755
> --- a/tests/functional/ppc64/test_powernv.py
> +++ b/tests/functional/ppc64/test_powernv.py
> @@ -9,6 +9,7 @@
>  
>  from qemu_test import LinuxKernelTest, Asset
>  from qemu_test import wait_for_console_pattern
> +from qemu_test import exec_command_and_wait_for_pattern
>  
>  class PowernvMachine(LinuxKernelTest):
>  
> @@ -27,6 +28,17 @@ class PowernvMachine(LinuxKernelTest):
>           'buildroot/qemu_ppc64le_powernv8-2025.02/rootfs.ext2'),
>         
> 'aee2192b692077c4bde31cb56ce474424b358f17cec323d5c94af3970c9aada2')
>  
> +    def shell_exec_command_check_fail(self, command):
> +        fail_msg="Fail"
> +        self.shell_exec_command(f"export __FAIL_MSG={fail_msg}")

nice idea.

> +
> +        # If the exit code for the command is non 0, print fail
> message
> +        command = command + " || echo $__FAIL_MSG"
> +        exec_command_and_wait_for_pattern(self, command, '#',
> fail_msg)
> +
> +    def shell_exec_command(self, command):
> +        exec_command_and_wait_for_pattern(self, command, '#',
> self.panic_message)
> +
>      def do_test_linux_boot(self, command_line =
> KERNEL_COMMON_COMMAND_LINE):
>          self.require_accelerator("tcg")
>          kernel_path = self.ASSET_KERNEL.fetch()
> @@ -71,6 +83,89 @@ def test_linux_smt_boot(self):
>          wait_for_console_pattern(self, console_pattern,
> self.panic_message)
>          wait_for_console_pattern(self, self.good_message,
> self.panic_message)
>  
> +    def test_linux_remote_interrupts(self):
> +        self.require_accelerator("tcg")
> +        self.set_machine('powernv')
> +
> +        # Have below setup in this test:
> +        # 1. e1000e attached to pcie.6, which is from 7th PHB,
> belonging to 2nd
> +        #    socket (chip 1), in a powernv boot with default 6 PHBs
> per socket
> +        # 2. CPU on 2nd socket (chip 1) disabled
> +        # 3. RX IRQ's affinity to chip 2, and TX IRQ's affinity to
> chip 3
> +        #
> +        # Then ping is done, to generate interrupts from e1000e
> which should go
> +        # to IRQ server on the remote sockets
> +        self.vm.add_args('-smp', '4,sockets=4,threads=1')
> +        self.vm.add_args('-netdev', 'user,id=net0')
> +        self.vm.add_args('-device', 'e1000e,netdev=net0,bus=pcie.6')
> +
> +        kernel_path = self.ASSET_KERNEL.fetch()
> +        rootfs_path = self.ASSET_INITRD.fetch()
> +        self.vm.set_console()
> +        self.vm.add_args('-kernel', kernel_path,
> +            '-drive',
> +           
> f'file={rootfs_path},format=raw,if=none,id=drive0,readonly=on',
> +            '-append', 'root=/dev/nvme0n1 console=hvc0',
> +            '-device',
> 'nvme,drive=drive0,bus=pcie.2,addr=0x0,serial=1234')
> +        self.vm.launch()
> +
> +        # Wait for boot to complete
> +        console_pattern = 'CPU maps initialized for 1 thread per
> core'
> +        wait_for_console_pattern(self, console_pattern,
> self.panic_message)
> +        console_pattern = 'smp: Brought up 4 nodes, 4 CPUs'
> +        wait_for_console_pattern(self, console_pattern,
> self.panic_message)
> +        wait_for_console_pattern(self, 'Run /sbin/init as init
> process',
> +                                 self.panic_message)
> +
> +        # Wait for login prompt and login as root (no password in
> buildroot)
> +        wait_for_console_pattern(self, 'login:', self.panic_message)
> +        exec_command_and_wait_for_pattern(self, 'root', '#',
> self.panic_message)
> +
> +        # e1000e is connected to socket 1, disable the CPU on socket
> 1
> +        self.shell_exec_command("echo 0 >
> /sys/devices/system/cpu/cpu1/online")
> +        self.shell_exec_command(
> +            "export CPU1_STATE=$(cat
> /sys/devices/system/cpu/cpu1/online)")
> +        self.shell_exec_command_check_fail("[ $CPU1_STATE -eq 0 ]")
> +
> +        # RX, TX interrupts to chip/cpu 2 & 3 respectively
> +        self.shell_exec_command(
> +            "export RX_IRQ=$(awk '/eth0-rx/ {print $1}'
> /proc/interrupts | tr -d ':')")
> +        self.shell_exec_command(
> +            "export TX_IRQ=$(awk '/eth0-tx/ {print $1}'
> /proc/interrupts | tr -d ':')")
> +        self.shell_exec_command("echo 2 >
> /proc/irq/$RX_IRQ/smp_affinity_list")
> +        self.shell_exec_command("echo 3 >
> /proc/irq/$TX_IRQ/smp_affinity_list")
> +
> +        # Capture interrupt counts before generating traffic
> +        self.shell_exec_command(
> +            "export RX_BEFORE=$(awk '/eth0-rx/ {print $3}'
> /proc/interrupts)")
> +        self.shell_exec_command(
> +            "export TX_BEFORE=$(awk '/eth0-tx/ {print $4}'
> /proc/interrupts)")
> +
> +        # Wait up to 15 seconds for eth0 link to come up
> +        self.shell_exec_command(
> +            "c=0; while ! ip addr show eth0 | grep 'inet 10.0.2'; do
> "
> +            "sleep 1; c=$((c+1)); [ $c -gt 15 ] && break; done")
> +
> +        self.shell_exec_command_check_fail(
> +            "ip addr show eth0 | grep 'inet 10.0.2'")
> +
> +        # Generate network traffic to trigger remote interrupts
> +        # Ping QEMU's user-mode network gateway (10.0.2.2)
> +        self.shell_exec_command("ping -W2 -c5 10.0.2.2")
> +
> +        # Show final interrupt counts to verify remote interrupts
> occurred
> +        self.shell_exec_command("cat /proc/interrupts | grep eth0")
> +
> +        # Verify interrupt counts increased (whether interrupts were
> delivered)
> +        self.shell_exec_command(
> +            "export RX_AFTER=$(awk '/eth0-rx/ {print $3}'
> /proc/interrupts)")
> +        self.shell_exec_command(
> +            "export TX_AFTER=$(awk '/eth0-tx/ {print $4}'
> /proc/interrupts)")
> +
> +        # Check that interrupt counts increased
> +        self.shell_exec_command_check_fail("[ $RX_AFTER -gt
> $RX_BEFORE ]")
> +        self.shell_exec_command_check_fail("[ $TX_AFTER -gt
> $TX_BEFORE ]")
> +
>      def test_linux_big_boot(self):
>          self.set_machine('powernv')
>          self.vm.add_args('-smp', '16,threads=4,cores=2,sockets=2')

Here's some of the log

2026-06-25 21:21:41,820: --- 10.0.2.2 ping statistics ---
2026-06-25 21:21:41,820: 5 packets transmitted, 5 packets received, 0%
packet loss
2026-06-25 21:21:41,820: round-trip min/avg/max = 0.547/2.077/7.575 ms
2026-06-25 21:21:41,823: #
2026-06-25 21:21:41,826: cat /proc/interrupts | grep eth0
2026-06-25 21:21:41,874: 131:          0         26          0  PNV-
PCI-MSI 2416443392 Edge      eth0-rx-0
2026-06-25 21:21:41,875: 132:          0          0         18  PNV-
PCI-MSI 2416443393 Edge      eth0-tx-0
2026-06-25 21:21:41,875: 133:          0          0          0  PNV-
PCI-MSI 2416443394 Edge      eth0
2026-06-25 21:21:41,877: #
2026-06-25 21:21:41,881: export RX_AFTER=$(awk '/eth0-rx/ {print $3}'
/proc/interrupts)
2026-06-25 21:21:41,920: #
2026-06-25 21:21:41,926: export TX_AFTER=$(awk '/eth0-tx/ {print $4}'
/proc/interrupts)
2026-06-25 21:21:41,969: #
2026-06-25 21:21:41,971: export __FAIL_MSG=Fail
2026-06-25 21:21:41,971: #
2026-06-25 21:21:41,974: [ $RX_AFTER -gt $RX_BEFORE ] || echo
$__FAIL_MSG
2026-06-25 21:21:41,974: #
2026-06-25 21:21:41,976: export __FAIL_MSG=Fail
2026-06-25 21:21:41,976: #

Maybe we can design this __FAIL_MSG, that doesn't need to interfere
with stdout directly. But This is alright currently.


Tested-by: Shivang Upadhyay <[email protected]>
Reviewed-by: Shivang Upadhyay <[email protected]>

Regards
~Shivang.

Reply via email to