ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

TinyUSB HIL 设备卡死恢复指南:Linux 内核侧 usbfs 设备锁与 D 状态进程的排查与处置

TinyUSB HIL 设备卡死恢复指南:Linux 内核侧 usbfs 设备锁与 D 状态进程的排查与处置 嵌入式驱动开发通信物联网【免费下载链接】tinyusbAn open source cross-platform USB stack for embedded system项目地址https://gitcode.com/gh_mirrors/ti/tinyusb点击查看免费下载导读本文以 TinyUSB 仓库中.claude/skills/usb-kernel-recover/SKILL.md为骨架系统讲解 HILHardware-in-the-Loop测试机架上 Linux 宿主机侧 USB 设备卡死wedge的完整恢复流程从 D 状态进程的定位、设备锁机制的原理到分级的恢复动作rung 1~4、控制器故障处置与机架健康验收。读者学完后能在 ci.lan、hifiphile/tusb 或开发机bench PC上对无法枚举、进程挂死在 D 状态的设备或 fixture 做出安全、可验证的恢复并规避常见的误操作。1. 问题本质usbfs ioctl 与设备锁的不可中断性TinyUSB HIL 机架上的卡死wedged通常表现为设备不再枚举或触及 USB 的进程testusb、JLinkExe、uhubctl、openocd、libusb 工具陷入 D 状态不可中断睡眠无法退出。核心机制在 Linux 内核一个卡住的 usbfs ioctl 持有该设备的device_lock。usbdev_do_ioctl调用的是不可中断版本的usb_lock_device内核 v6.12.96 的devio.c:2609。其下的驱动在无超时的wait_for_completion()上等待usbtest.c:1404usb_sg_waitmessage.c:765。任何同样需要该锁的路径都无法介入——只有两条杠杆可行在设备端让 URB 失败rung 1与端口侧数据线断电rung 2。2. Triage先找到锁的持有者ps -eo pid,stat,etimes,wchan:22,args | awk $2 ~ /D/ sudo cat /proc/pid/stack # 不会打开节点因此不会阻塞判断规则S 受害者victim。加锁的 sysfs读操作使用usb_lock_device_interruptiblesysfs.c:124-139共 11 处可被信号杀死timeout能限定它们。可忽略它们会自行解除。D 持有者或走了不可中断路径的写入者。栈内容含义处置usbdev_ioctl 驱动模块[usbtest]所有者持有锁rung 3 — 终局usbdev_ioctl无驱动帧所有者等待一个 URBrung 1DUT/ rung 2probeusbdev_open、sysfs 读受害者忽略tee .../usbtest/new_id、bind、unbind会扩散锁的受害者停止发出这些命令kworker 中的hub_event卡在所有者身后的拆卸rung 3驱动绑定写入并非被动__device_driver_lockdrivers/base/dd.c以不可中断方式获取device_lock()并获取device_lock(parent)——因为usb_bus_type设置了.need_parent_lock truedriver.c:2048每次都会持有 HUB 的锁这就是一个卡死的端口拖垮整条总线的机制。用无锁属性lock-free attrs把持有者映射到 busportdevnum、idVendor、idProduct是usb_descriptor_attr*纯sysfs_emitsysfs.c:688-705for d in /sys/bus/usb/devices/bus-*/; do [ $(cat $d/devnum) devnum ] echo $d $(cat $d/idVendor):$(cat $d/idProduct) done grep -l SERIAL /sys/bus/usb/devices/*/serial # 仅在健康设备上可用3. Shield使用 libusb 类工具的前置条件卡死的设备会阻塞每个读取其加锁属性的枚举器——JLinkExe、uhubctl、openocd 的 HID 回退都在此列。chmod 000让 VFS 在-show()运行前就拒绝读取于是这些枚举器跳过它继续枚举。chmod永不阻塞inode setattr无show()因此在完全卡死的设备上依然有效。sudo .claude/skills/usb-kernel-recover/scripts/usb_recover.sh shield busport $$ # 叶子 父 hub 根 hub先记录原始模式 sudo .claude/skills/usb-kernel-recover/scripts/usb_recover.sh shield-status # 谁持有什么、所有者存活/已死、哪些已重新枚举 sudo .claude/skills/usb-kernel-recover/scripts/usb_recover.sh unshield busport $$ # 恢复已记录的模式所有者为 pid 或已死时空要点所有者 pid 是恢复会话你的 shell、编排进程不是短命的sudo陈旧 shield 的所有者已消失shield-status会说明。绝不盲目 unshield存活的异主 shield 会被拒绝。根 hub 是共享的因此第二个触碰它的 shield 会被拒绝先完成或 unshield 第一个。以非 root 身份运行恢复工具root 拥有CAP_DAC_OVERRIDE会无视000照样阻塞。叶子 shield 会在重新枚举时消失新 kobject、新 inodeunshield只恢复存活的原始对象其余报 gone。它绝不从兄弟设备复制模式九个属性中两个是 644其余是 444。JLinkExe 总是需要 shield即使带-USB/-SelectEmuBySN也一样为了找到其 probe它会读取宿主机上每个 USB 设备的product、serial与bNumInterfacesstraceci.lan 2026-09-21而这些属性都在各自设备的锁下提供。对固定了vid_pid或走interface/jlink.cfg的 openocd 则不需要hil_flash.convoy_safe——它们在libusb_open之前就跳过异设备不读取其他设备的加锁属性。4. 恢复阶梯The Rungs直接跳到 triage 指定的那一级Rung 1 — 卡死的 DUT通过其自身的 probe 复位从 HIL 名册解析该板卡的恢复烧录器recovery flasher取flasher_recover否则取主flasherhil_flash.recover_flasherprobe-sn无论哪种都是主烧录器的uid。convoy_safehil_flash.convoy_safe的烧录器无需 shieldopenocd 形式openocd -c adapter serial probe-sn flasher args -c init; reset run; shutdown其他工具只能在 shield第 3 节后运行然后 unshieldJLink 形式sudo .claude/skills/usb-kernel-recover/scripts/usb_recover.sh shield busport $$ printf r\ng\nq\n /tmp/rec.jlink JLinkExe -device DEV -if SWD -speed 4000 -SelectEmuBySN probe-sn \ -autoconnect 1 -nogui 1 -CommandFile /tmp/rec.jlink sudo .claude/skills/usb-kernel-recover/scripts/usb_recover.sh unshield busport $$在 park-flash 之前复位非破坏性被测固件保留供事后分析、无 flash 磨损、不会留下坏 park 镜像——wfe/wfipark 曾在 mimxrt1064_evk 与 max32666fthr 上通过一次断电把 SWD 变砖。ResetTarget实测 128-129 ms单次分别清除了 57 → 0、26 → 0、5 → 0 个 D 状态进程。机制芯片复位拉低上拉 →usb_hcd_flush_endpoint以-ESHUTDOWN解除 URBhcd.c:1783→ completion 触发 → ioctl 返回 → 锁释放。在 i.MX RT 上有效USBCMD.RS 0 脱离RT1050 RM Rev 3 p.2453在 DWC2 上也有效——2026-08-16 在 stm32f407disco 上实测r; g得到usb 13-2.2: USB disconnect, device number 107325 ms 后重新枚举。单纯的halt不行核心仍在运行并保持上拉。Park-flash--recover-board/--recover-fw即usbtest.py自动化的动作是复位够不到外设时的回退。投递必须 convoy-safe固定vid_pid或走interface/jlink.cfg的 openocd或 esptool-p ttyACM。JLinkExe 需要 shield第 3 节。Rung 2 — 卡死的 PROBEroot-cycleprobe 没有 probe 可复位它端口侧断电是唯一避开 KERNEL 设备锁的杠杆。它命令 ROOT hub绝不触碰卡死设备的device_lock——这正是 rung 1 和 3 危险而这一级不危险的原因。这与机架的板卡 flock是不同锁而本 rung 仍需要那些 flock它会弹跳根端口下的每个 fixture包括另一任务正在烧录途中的板卡。先取锁用后释放python3 test/hil/helper/hil_lock.py hold --all --config this hosts config --reason root-cycle busport sudo .claude/skills/usb-kernel-recover/scripts/usb_recover.sh root-cycle busport [expected-serial] python3 test/hil/helper/hil_lock.py release --all # 无需 --config它会遍历锁目录--all对单根端口来说很粗但没有任何机制能把 sysfs busport 映射到板卡名因此它是唯一真正覆盖爆炸半径的预留方式hold接受任意字符串所以手写仅兄弟板的 hold 看似成功实则没有预留任何东西。被以hil_test.py名义拒绝说明 CI 正在测试中——等待不要强推。给脚本卡死 probe 自己的 busport如13-1.6而不是13-1这个 hub 路径脚本自己推导根端口而 expected-serial 守卫与成功检查都读取你传入的路径。这会弹跳该根端口下的每个fixture本机最多 25 个。Renesas 卡宣称支持ppps但并不实现VBUS 保持供电只有 D/D− 跌落因此这是强制重新枚举绝不是断电——固件卡死的设备可以安然度过。成功判据是 sysfs inode 变化而不是 uhubctl 的退出码。Rung 3 — 终局持有锁的驱动 ioctl无软件解药任务不可中断SIGKILL 只排队不送达。用sysrq重启绝不用reboot(2)——优雅重启会运行device_shutdown()获取每个设备锁并在卡死的那个上卡住。echo b | sudo tee /proc/sysrq-trigger # 之后sync; sudo umount -aRung 4 — 虚拟机管理程序仅 ci.lan且在记录的八次卡死中从未需要qm stop vmid qm start vmid在 PVE 宿主机上。VM重启不可靠——hub 可能跨 PCIe 复位锁存。5. 控制器死了怎么办pci-rebind特征xhci-pci-renesas addr: Timeout while waiting for setup device command、该控制器上的设备枚举失败或总线消失——与单个设备卡死相对。以上 rung 无效控制器本身需要重新初始化。sudo .claude/skills/usb-kernel-recover/scripts/usb_recover.sh pci-rebind pciaddr # unbind bind 整个 xHCI sudo .claude/skills/usb-kernel-recover/scripts/usb_recover.sh pci-bind pciaddr # 仅在它最终无驱动时使用成功 rebind 后每个 fixture 都会重新枚举并且它重编该控制器拥有的每条总线号ci.lan 上 2026-08-17 总线 17、18 回来变成 1、2因此 rebind 前要按 rung 2 的方式取机架级 hold、之后释放并重新推导 busport。设备锁 convoy 存活期间不要使用它——参见常见错误。6. 如果没有 D 状态进程设备是死了或沉默了不是卡死。sudo .claude/skills/usb-kernel-recover/scripts/usb_recover.sh authorized busport会反配置并重新配置它usb_set_configuration(dev, -1)然后重新选择hub.c——修复陈旧的驱动/接口状态不会重新插拔usb_device存活因此多数 probe 保留 sysfs 节点。若仍无效设备是卡死而非沉默——转 rung 1 或 2。resolve dev-node把/dev/ttyACM3映射为 busport。它不可中断地取usb_lock_devicehub.cusb_deauthorize_device因此只在无任何 D 状态进程时安全。7. 宣布机架健康前的验收ps -eo stat,args | awk $1 ~ /D/ | wc -l # 必须为 0 timeout 15 lsusb # rc 0 且设备数合理 sudo uhubctl -l bus -p port # 0000 off 再也没回来 sudo uhubctl -l bus -p port -a on实测教训D 状态列表完全干净时仍有 5 块板缺失因为usb17-port2停在disable1。8. 常见错误清单uhubctl -a cycle对根端口不使用-S。它写 sysfsdisable而disable_store不可中断地取usb_lock_device(hdev)并在其内部调用usb_disconnect(child)port.c——面对卡死的子设备它会阻塞并持有根 hub 的锁毒化整条总线。usb_recover.sh传-S。echo 1 .../remove想让卡死设备消失remove_store是 sysfs.c 中唯一取不可中断usb_lock_device的属性sysfs.c:765。它会加入 convoy 而不是清除它。对任何卡死设备执行authorized——同样的不可中断锁。驱动 unbind/bind/sys/bus/usb/drivers/usb/{unbind,bind}通过usb_generic_driver_disconnectgeneric.c做同样的反配置/重配置但还取父 hub 的锁need_parent_lock严格更糟它因此被从usb_recover.sh中移除。对卡死的设备用pci-rebind。它是死控制器的解药见第 5 节不是设备锁 convoy 的解药在活跃的 D 状态 URB 下重新 bind 可能挂死并把控制器留在无驱动状态、所有 fixture 离线实测一次。用pci-bind addr恢复。写/sys/bus/pci/devices/addr/reset——本机架无控制器支持 FLR因此它在活跃驱动之后变成总线复位卡停止、宿主机断电。复位受害者的板卡。曾有两块板在找到持有者之前被无辜复位。按devnum映射不要按哪块板应该运行。假定只有一个控制器。实测三个 xHCI 控制器上 26 个 D 状态进程全部由一台设备上的一次 probe 复位清除。9. 机架布局与脚本实现要点ci.lanreadlink -f /sys/bus/usb/devices/usbN得到其 PCI 地址sudo uhubctl列出它能驱动的根 hub 及其 PCI 地址。八块 Renesas uPD720201 控制器——0000:01:00.0至0000:08:00.0在两块卡上——在其 USB2 与 USB3 根 hub 上都宣称每端口ppps但不实现硅片从不拉低 VBUS在05–08卡上实测01–04未测假设相同因此 cycle 只是重新枚举端口仅此而已。不要在本机架上把 uhubctl 的ppps读作电源控制。哪棵树挂着哪些 probe 随重新布线而变化因此要现场推导lsusb -s bus:而非信任静态映射。2026-09-22 验证此处hathach的sudo免密因此上面每一级 rung 都无需提示直接运行。从脚本usb_recover.sh的源码可进一步确认几个关键实现细节路径脚本被 shield 的加锁属性列表固定为bNumInterfaces bmAttributes bMaxPower configuration bConfigurationValue product manufacturer serial avoid_reset_quirk而descriptors、busnum、devnum、speed、idVendor、idProduct是无锁属性libusb 需要它们永不列入第 31 行注释。lock_read()对锁下属性的每次读取用timeout 2限定有界返回?表示无应答——这是 root-cycle 成功判定的前提防止在真卡死时无限期阻塞。重新枚举的成功判据sysfs_gen()用stat -c %i busport/目录 inode 的尾随斜杠/sys/bus/usb/devices/busport是带独立 inode 的符号链接无斜杠时 stat 的是链接而非设备本身值永不变化第 95-97 行注释kernfs 的 inode 单调分配真实断开会重建 kobject 使 inode 改变。shield先快照全部对象与其 inode、模式再执行 chmod失败时按记录回滚绝不从兄弟设备复制模式unshield只恢复仍携带记录 inode 的存活对象其余报 gone。参数用正则白名单校验USBPATH_RE、PCI_RE、DRIVER_RE以阻断路径穿越require_usb_controller拒绝非 USB 控制器非0x0c03xx类的 PCI 函数防止误伤共享 HIL 宿主上的存储/NIC。root-cycle通过-S强制走 libusb 路径直接向根 hub 发断电控制传输前置无子设备断开并在循环内以最多 10 秒等待sysfs_gen变化作为唯一成功判定——uhubctl 即使无操作也返回 0其退出码不证明任何事。与 HIL 测试体系的联动该恢复流程在 TinyUSB HIL 测试体系中的位置板卡锁由 hil_lock.py 用/tmp/tinyusb-hil-locks/下的内核 flock 按板卡仲裁CI runner 的hil_test.py自我加锁reasonhil_test.py被该 reason 拒绝说明 CI 正在测试等待数分钟重试而非强推。机架级操作root-cycle、pci-rebind、控制器复位——都会重编总线号必须先hold --all --config host config再release --all。flasher_recover与convoy_safehil_flash.py名册中可选的flasher_recover键指定恢复投递方式convoy_safe判定烧录器能否绕过毒化节点送达恢复——三种形态合格固定vid_pid的 openocd在libusb_open前就continue跳过异节点、esptool-p ttyACM命名端口从不枚举 usbfs、走interface/jlink.cfg的 openocdlibjaylink 只开 SEGGER 设备。名册实例可见 tinyusb.json如 frdm_k64f 的主烧录器为 jlink、flasher_recover为 openocd over jlink.cfg。后池恢复阶段hil_recover.pyhil_test.py对带.wedged标记的板卡在 worker 池关闭后逐卡恢复——按需 shield仅当恢复烧录器不 convoy-safe、probe 复位、持有者幸存则重刷、unshield、验证完整持有者扫描为空 DUT 重新枚举、以该证据hil_lock.py wedged clear清除标记。恢复在 fork 出的 supervisor 会话中运行预算由HIL_RECOVERY_TIMEOUT600 s限定shield 需要非 root 免密 sudoroot 无视 shield 的 000 模式。usbtest.py的 wedge 判定usbtest.py通过wedged_pids()的 /proc 扫描把 D 状态持有者关联到 DUT 节点配合confirmation_of(stuck, complete)产出wedge_confirmation/cleared/confirmed/unverifiedconfirmed即写入.wedged标记。结语Linux 内核的usb_lock_device不可中断性决定了面对 usbfs 层卡死的设备任何也取同一把锁的软件路径sysfs 写、remove、authorized、优雅重启都会加入 convoy 而非清除它。TinyUSB HIL 机架的恢复策略因此围绕两条真正的杠杆展开——在设备端失败 URBrung 1与端口侧数据线跌落rung 2——以usb_recover.sh的 shield、root-cycle、pci-rebind 等原语落实为可脚本化、有守卫、可验证的操作并用.wedged标记与证据验证把恢复闭环回 HIL 测试流程。掌握这一套流程就能把一次整机架宕机级的 USB 卡死收敛为几分钟内、单点、无破坏的定点处置。赞分享嵌入式驱动开发通信物联网【免费下载链接】tinyusbAn open source cross-platform USB stack for embedded system项目地址https://gitcode.com/gh_mirrors/ti/tinyusb点击查看免费下载相关推荐Matter设备状态恢复出厂重置与配置备份在智能家居系统中Matter设备原Project CHIP的状态管理是确保设备稳定运行的关键环节。当设备出现连接故障、配置错误或需要转移所有权时 出厂重物联网智能家居嵌入式通信MemOS内存管理系统深度探索高效处理LLM长期记忆的5个技巧MemOS内存管理系统深度探索高效处理LLM长期记忆的5个技巧 MemOS作为一款开源的Memory OS框架专为构建内存原生AI智能体而设计核心功能涵盖人工智能大模型Agent 记忆AI AgentRAG知识图谱dsh-plugin如何用3分钟从国家中小学智慧教育平台批量下载电子课本tchMaterial-parser下载工具完全指南如何用3分钟从国家中小学智慧教育平台批量下载电子课本tchMaterial parser下载工具完全指南 还在为寻找教学资源而奔波吗每次备课都需要在多个平台网页爬虫教育上一篇边缘计算三步落地Baetyl 在 AI 一体机与 5G 路侧盒子的实战下一篇在 Fairseq 中复现大规模反向翻译以 WMT18 英德翻译为例Understanding Back-Translation at Scale 实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表