3. USB4链路训练状态监控
任务目标
在 Linux 上通过 sysfs 与 debugfs 读取 USB4/Thunderbolt 链路训练状态,解析出当前链路速率(Gen)与链路宽度(lane 数),并给出可复现的读取与解析脚本。
环境与依赖
- 内核已启用
CONFIG_USB4与CONFIG_USB4_DEBUGFS(debugfs 节点依赖后者)。 - 系统已挂载
debugfs:mount -t debugfs none /sys/kernel/debug。 - Python 3(仅用标准库)。
数据来源
USB4 设备在 sysfs 下暴露为 /sys/bus/thunderbolt/devices/<domain>/<port>,其中 link_speed 与 link_width 为链路训练完成后的协商结果;debugfs 的 /sys/kernel/debug/thunderbolt/<domain>/<port>/regs 提供寄存器原始值,用于交叉验证。
实现代码
#!/usr/bin/env python3
# usb4_link_status.py —— 读取并解析 USB4 链路训练状态
import os
import re
import sys
SYSFS_ROOT = "/sys/bus/thunderbolt/devices"
DEBUGFS_ROOT = "/sys/kernel/debug/thunderbolt"
SPEED_MAP = {
"1.25": "Gen1 (1.25 GT/s)",
"2.5": "Gen2 (2.5 GT/s)",
"5.0": "Gen3 (5.0 GT/s)",
"10.0": "Gen4 (10.0 GT/s)",
"20.0": "Gen5 (20.0 GT/s)",
}
def read_file(path):
try:
with open(path, "r") as f:
return f.read().strip()
except (OSError, IOError):
return None
def list_ports():
"""枚举 domain/port 组合。"""
ports = []
if not os.path.isdir(SYSFS_ROOT):
return ports
for domain in sorted(os.listdir(SYSFS_ROOT)):
dpath = os.path.join(SYSFS_ROOT, domain)
if not os.path.isdir(dpath):
continue
for port in sorted(os.listdir(dpath)):
ppath = os.path.join(dpath, port)
if os.path.isdir(ppath) and os.path.exists(os.path.join(ppath, "link_speed")):
ports.append((domain, port, ppath))
return ports
def parse_speed(raw):
"""sysfs link_speed 形如 '20.0' 或 '20.0 Gb/s'。"""
if raw is None:
return None, "未知"
m = re.search(r"(\d+(?:\.\d+)?)", raw)
if not m:
return None, "未知"
val = m.group(1)
return val, SPEED_MAP.get(val, f"{val} GT/s")
def parse_width(raw):
"""sysfs link_width 形如 '2' 或 '2 lanes'。"""
if raw is None:
return None
m = re.search(r"(\d+)", raw)
return int(m.group(1)) if m else None
def read_debugfs_regs(domain, port):
"""读取 debugfs 寄存器原始值,用于交叉验证。"""
path = os.path.join(DEBUGFS_ROOT, domain, port, "regs")
return read_file(path)
def main():
ports = list_ports()
if not ports:
print("未发现 USB4/Thunderbolt 端口,请确认内核与硬件支持。")
return 1
for domain, port, ppath in ports:
raw_speed = read_file(os.path.join(ppath, "link_speed"))
raw_width = read_file(os.path.join(ppath, "link_width"))
speed_val, speed_desc = parse_speed(raw_speed)
width = parse_width(raw_width)
print(f"[{domain}/{port}]")
print(f" link_speed raw : {raw_speed}")
print(f" link_width raw : {raw_width}")
print(f" 解析速率 : {speed_desc}")
print(f" 解析宽度 : {width} lane(s)" if width else " 解析宽度 : 未知")
regs = read_debugfs_regs(domain, port)
if regs:
# 仅展示前几行,避免刷屏
head = "\n".join(regs.splitlines()[:4])
print(f" debugfs regs :\n{head}")
else:
print(" debugfs regs : 不可用(检查 CONFIG_USB4_DEBUGFS 与挂载)")
print()
return 0
if __name__ == "__main__":
sys.exit(main())
运行与验证
chmod +x usb4_link_status.py
sudo ./usb4_link_status.py
判定通过条件:脚本对每个已训练完成的端口打印出 link_speed 与 link_width 的原始值,并给出对应的 Gen 速率与 lane 数;若 debugfs 可用,同时打印寄存器前几行。若某端口未连接设备,link_speed 可能为空或为 0,此时解析结果为“未知”,属正常现象。
常见问题
- 无输出:确认
/sys/bus/thunderbolt/devices存在,且内核已加载thunderbolt模块。 - debugfs 不可用:检查
CONFIG_USB4_DEBUGFS是否编译进内核,并确认已挂载 debugfs。 - 速率与预期不符:链路训练可能降速降宽,属正常协商结果,可结合 debugfs 寄存器进一步排查。