feat(topo): per-socket bar layout with disks under their controller

Rework the /topo diagram from side-by-side stacked cards into one tall
vertical bar per CPU socket with everything attached to it branching off
sideways (socket 0 left/branches right, socket 1 right/branches left).

Disks are now parented under the storage controller they physically hang
off (SATA/AHCI, SAS HBA, RAID) — itself a NUMA-affine PCIe device under one
socket — instead of a synthetic catch-all node. The disk->controller link
is read from a new storage-controllers.txt techdump
(platform.StorageControllerMapScript, a /sys/block walk); disks with no
resolvable controller fall back to an "Other" bar. No board/root node.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SYjrG6bVmeG1Z2Wmc8kg3o
This commit is contained in:
Mikhail Chusavitin
2026-09-03 12:15:49 +03:00
co-authored by Claude Sonnet 5
parent b1ab866f58
commit 319f08ac4b
5 changed files with 634 additions and 100 deletions
+29
View File
@@ -23,6 +23,7 @@ var techDumpFixedCommands = []struct {
{Name: "lspci", Args: []string{"-vvv"}, File: "lspci-vvv.txt"},
{Name: "lscpu", Args: nil, File: "lscpu.txt"},
{Name: "lsblk", Args: []string{"-J", "-d", "-o", "NAME,TYPE,SIZE,SERIAL,MODEL,TRAN,HCTL"}, File: "lsblk.json"},
{Name: "sh", Args: []string{"-c", StorageControllerMapScript}, File: "storage-controllers.txt"},
{Name: "sensors", Args: []string{"-j"}, File: "sensors.json"},
{Name: "ipmitool", Args: []string{"fru", "print"}, File: "ipmitool-fru.txt"},
{Name: "ipmitool", Args: []string{"sdr"}, File: "ipmitool-sdr.txt"},
@@ -85,6 +86,34 @@ wait "$burn_pid" 2>/dev/null || true
rm -f /tmp/bee-pcie-load-burn.log
`
// StorageControllerMapScript walks /sys/block to record, for every real
// disk, the PCI function of the controller it hangs off and its SCSI HCTL
// address. Consumed by webui's /topo page to parent each disk under its
// actual storage controller (SATA/AHCI, SAS HBA, RAID) node in the topology
// tree instead of a synthetic catch-all branch. Output is one
// "name hctl=<h:c:t:l> ctrl=<dddd:bb:dd.f>" line per disk; hctl is empty for
// NVMe (which is its own PCIe endpoint).
const StorageControllerMapScript = `
for dev in /sys/block/*; do
n=$(basename "$dev")
case "$n" in loop*|ram*|zram*|sr*|md*|dm-*|nbd*|fd*) continue;; esac
[ -e "$dev/device" ] || continue
real=$(readlink -f "$dev/device") || continue
ctrl=""; hctl=""; seen_pci=0
oldIFS=$IFS; IFS=/
for seg in $real; do
case "$seg" in
pci[0-9]*:[0-9]*) seen_pci=1 ;;
[0-9a-f][0-9a-f][0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f].[0-9a-f]) [ "$seen_pci" = 1 ] && ctrl=$seg ;;
[0-9]*:[0-9]*:[0-9]*:[0-9]*) hctl=$seg ;;
*) : ;;
esac
done
IFS=$oldIFS
echo "$n hctl=$hctl ctrl=$ctrl"
done
`
// KernelAERNvidiaScript filters dmesg for PCIe AER, NVRM, and Xid lines —
// cheap (dmesg is already in memory) and the fastest way to tell a real
// PCIe/GPU hardware fault from power-management noise.