Merge pull request #1321 from kernelkit/cn9130-wdg

CN9130 Watchdog
This commit is contained in:
Tobias Waldekranz
2025-12-16 11:17:05 +01:00
committed by GitHub
15 changed files with 179 additions and 8 deletions
+1
View File
@@ -394,6 +394,7 @@ CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
CONFIG_WQ_WATCHDOG=y
CONFIG_WQ_CPU_INTENSIVE_REPORT=y
CONFIG_TEST_LOCKUP=m
# CONFIG_SCHED_DEBUG is not set
# CONFIG_RCU_TRACE is not set
CONFIG_FUNCTION_TRACER=y
+2
View File
@@ -407,6 +407,7 @@ CONFIG_WATCHDOG=y
CONFIG_WATCHDOG_SYSFS=y
CONFIG_SOFT_WATCHDOG=y
CONFIG_GPIO_WATCHDOG=y
CONFIG_ARM_SBSA_WATCHDOG=y
CONFIG_ARMADA_37XX_WATCHDOG=y
CONFIG_I6300ESB_WDT=y
CONFIG_MFD_MAX77620=y
@@ -564,6 +565,7 @@ CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
CONFIG_WQ_WATCHDOG=y
CONFIG_WQ_CPU_INTENSIVE_REPORT=y
CONFIG_TEST_LOCKUP=m
# CONFIG_SCHED_DEBUG is not set
# CONFIG_RCU_TRACE is not set
CONFIG_FUNCTION_TRACER=y
@@ -59,3 +59,9 @@
XSWP(a, "e13", 13, &sfp0);
};
};
&cp0_spi1 {
spi-flash@0 {
broken-flash-reset;
};
};
+1
View File
@@ -481,6 +481,7 @@ CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
CONFIG_WQ_WATCHDOG=y
CONFIG_WQ_CPU_INTENSIVE_REPORT=y
CONFIG_TEST_LOCKUP=m
# CONFIG_SCHED_DEBUG is not set
CONFIG_STACKTRACE=y
CONFIG_RCU_CPU_STALL_TIMEOUT=60
+1
View File
@@ -270,5 +270,6 @@ CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
CONFIG_WQ_WATCHDOG=y
CONFIG_WQ_CPU_INTENSIVE_REPORT=y
CONFIG_TEST_LOCKUP=m
CONFIG_FUNCTION_TRACER=y
CONFIG_UNWINDER_FRAME_POINTER=y
+3
View File
@@ -5,7 +5,10 @@ Tests verifying hardware monitoring and management:
- USB device detection and enumeration
- Multiple USB port management and device handling
- Watchdog reset capability
include::usb/Readme.adoc[]
include::usb_two_ports/Readme.adoc[]
include::watchdog/Readme.adoc[]
+3
View File
@@ -4,3 +4,6 @@
- name: USB configuration with two USB ports
case: usb_two_ports/test.py
- name: Watchdog reset on system lockup
case: watchdog/test.py
+1
View File
@@ -0,0 +1 @@
test.adoc
+28
View File
@@ -0,0 +1,28 @@
=== Watchdog reset on system lockup
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/hardware/watchdog]
==== Description
Verify that a system's watchdog trips and successfully reboots the
system back to a working state if a lockup occurs.
This is tested by using the Linux kernel's `test_lockup` module to
inject a hard lockup (i.e., blocking servicing of all interrupts) on
all CPU cores that lasts for twice as long as the watchdog's reported
timeout.
==== Topology
image::topology.svg[Watchdog reset on system lockup topology, align=center, scaledwidth=75%]
==== Sequence
. Set up topology and attach to target DUT
. Verify the presence of a watchdog device
. Verify the presence of the test_lockup module
. Trigger a hard lockup on all CPU cores
. Wait for the watchdog to trip
. Verify that the system reboots
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""Watchdog reset on system lockup
Verify that a system's watchdog trips and successfully reboots the
system back to a working state if a lockup occurs.
This is tested by using the Linux kernel's `test_lockup` module to
inject a hard lockup (i.e., blocking servicing of all interrupts) on
all CPU cores that lasts for twice as long as the watchdog's reported
timeout.
"""
import base64
import infamy
import json
import subprocess
import time
with infamy.Test() as test:
with test.step("Set up topology and attach to target DUT"):
env = infamy.Env()
target = env.attach("target", "mgmt")
tgtssh = env.attach("target", "mgmt", "ssh")
with test.step("Verify the presence of a watchdog device"):
wctl = tgtssh.run(["watchdogctl"], stdout=subprocess.PIPE)
conf = json.loads(wctl.stdout)
dogs = [ dog for dog in conf.get("device", []) if dog.get("name", "") == "/dev/watchdog" ]
if len(dogs) < 1:
test.fail("No watchdog device available")
else:
dog = dogs[0]
print(f"Found {dog['name']} ({dog['identity']}), timeout:{dog['timeout']}s")
with test.step("Verify the presence of the test_lockup module"):
if tgtssh.run(["modprobe", "-q", "-n", "test_lockup"]).returncode != 0:
test.fail("test_lockup module is not available")
with test.step("Trigger a hard lockup on all CPU cores"):
tgtssh.runsh(f"""
lockup()
{{
# Give the SSH session some time to properly shut down
sleep 3
sudo modprobe test_lockup \
disable_irq=1 \
all_cpus=1 \
time_secs={dog['timeout'] * 2}
}}
lockup </dev/null &>/dev/null &
""")
with test.step("Wait for the watchdog to trip"):
time.sleep(dog["timeout"])
with test.step("Verify that the system reboots"):
infamy.util.wait_boot(target, env)
test.succeed()
+23
View File
@@ -0,0 +1,23 @@
graph "1x1" {
layout="neato";
overlap="false";
esep="+80";
node [shape=record, fontname="DejaVu Sans Mono, Book"];
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
host [
label="host | { <mgmt> mgmt }",
pos="0,12!",
requires="controller",
];
target [
label="{ <mgmt> mgmt } | target",
pos="10,12!",
requires="infix watchdog",
];
host:mgmt -- target:mgmt [requires="mgmt", color="lightgray"]
}
+33
View File
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Title: 1x1 Pages: 1 -->
<svg width="424pt" height="45pt"
viewBox="0.00 0.00 424.03 45.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 41)">
<title>1x1</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-41 420.03,-41 420.03,4 -4,4"/>
<!-- host -->
<g id="node1" class="node">
<title>host</title>
<polygon fill="none" stroke="black" points="0,-0.5 0,-36.5 100,-36.5 100,-0.5 0,-0.5"/>
<text text-anchor="middle" x="25" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
<polyline fill="none" stroke="black" points="50,-0.5 50,-36.5 "/>
<text text-anchor="middle" x="75" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
</g>
<!-- target -->
<g id="node2" class="node">
<title>target</title>
<polygon fill="none" stroke="black" points="300.03,-0.5 300.03,-36.5 416.03,-36.5 416.03,-0.5 300.03,-0.5"/>
<text text-anchor="middle" x="325.03" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="350.03,-0.5 350.03,-36.5 "/>
<text text-anchor="middle" x="383.03" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">target</text>
</g>
<!-- host&#45;&#45;target -->
<g id="edge1" class="edge">
<title>host:mgmt&#45;&#45;target:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M100,-18.5C100,-18.5 300.03,-18.5 300.03,-18.5"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+8 -2
View File
@@ -1,7 +1,7 @@
import subprocess
from dataclasses import dataclass
from . import env
from . import env, netutil, util
@dataclass
@@ -15,6 +15,9 @@ class Location:
import subprocess
import os
def ssh_syn(addr, port=22):
return netutil.tcp_port_is_open(addr, port)
def fetch_file(remote_user, remote_address, remote_file, local_file, key_file, check=False, remove=False):
"""
Fetches a file over SSH using scp and the provided private key.
@@ -62,9 +65,12 @@ def fetch_file(remote_user, remote_address, remote_file, local_file, key_file, c
print(f"Error removing fetched file {local_file}: {e}")
class Device(object):
def __init__(self, name: str, location: Location):
def __init__(self, name: str, location: Location, wait: bool=True):
self.name = name
self.location = location
if wait:
util.until(lambda: ssh_syn(location.host, location.port))
def __str__(self):
nm = f"{self.name}"
if env.ENV.ltop:
+2 -2
View File
@@ -24,7 +24,7 @@ graph "dual" {
dut1 [
label="{ <e1> e1 | <e2> e2 | <e3> e3 } | dut1 | { <e4> e4 | <e5> e5 | <e6> e6 }",
pos="10,18!",
provides="infix",
provides="infix watchdog",
expected_boot="primary",
qn_console=9001,
qn_mem="384M",
@@ -33,7 +33,7 @@ graph "dual" {
dut2 [
label="{ <e1> e1 | <e2> e2 | <e3> e3 } | dut2 | { <e4> e4 | <e5> e5 | <e6> e6 }",
pos="10,12!",
provides="infix",
provides="infix watchdog",
expected_boot="primary",
qn_console=9002,
qn_mem="384M",
+4 -4
View File
@@ -23,7 +23,7 @@ graph "quad" {
dut1 [
label="{ <e1> e1 | <e2> e2 | <e3> e3 | <e4> e4 } | dut1 | { <e5> e5 | <e6> e6 | <e7> e7 | <e8> e8}",
pos="10,30!",
provides="infix",
provides="infix watchdog",
expected_boot="primary",
qn_console=9001,
qn_mem="384M",
@@ -32,7 +32,7 @@ graph "quad" {
dut2 [
label="{ <e1> e1 | <e2> e2 | <e3> e3 | <e4> e4 } | dut2 | { <e5> e5 | <e6> e6 | <e7> e7 | <e8> e8}",
pos="0,20!",
provides="infix",
provides="infix watchdog",
expected_boot="primary",
qn_console=9002,
qn_mem="384M",
@@ -41,7 +41,7 @@ graph "quad" {
dut3 [
label="{ <e1> e1 | <e2> e2 | <e3> e3 | <e4> e4 } | dut3 | { <e5> e5 | <e6> e6 | <e7> e7 | <e8> e8}",
pos="0,10!",
provides="infix",
provides="infix watchdog",
expected_boot="primary",
qn_console=9003,
qn_mem="384M",
@@ -51,7 +51,7 @@ graph "quad" {
dut4 [
label="{ <e1> e1 | <e2> e2 | <e3> e3 | <e4> e4 } | dut4 | { <e5> e5 | <e6> e6 | <e7> e7 | <e8> e8}",
pos="10,0!",
provides="infix",
provides="infix watchdog",
expected_boot="primary",
qn_console=9004,
qn_mem="384M",