系统挂起期间的 USB 设备持久性¶
- 作者:
Alan Stern <stern@rowland.harvard.edu>
- 日期:
2006 年 9 月 2 日(2008 年 2 月 25 日更新)
问题是什么?¶
根据 USB 规范,当 USB 总线挂起时,总线必须继续提供挂起电流(大约 1-5 mA)。 这是为了让设备可以保持其内部状态,并且集线器可以检测连接更改事件(插入或拔出设备)。 技术术语是“电源会话”。
如果 USB 设备的电源会话中断,则系统需要表现得好像该设备已拔出一样。 这是一种保守的方法; 在没有挂起电流的情况下,计算机无法知道实际发生了什么。 也许同一设备仍然连接,或者它被移除并且不同的设备插入到端口中。 系统必须假设最坏的情况。
默认情况下,Linux 的行为符合规范。 如果 USB 主机控制器在系统挂起期间断电,那么当系统唤醒时,连接到该控制器的所有设备都被视为好像它们已断开连接。 这始终是安全的,并且是“官方正确”的做法。
对于许多类型的设备,这种行为根本无关紧要。 如果内核想要相信您的 USB 键盘在系统休眠时已拔出,并在系统唤醒时插入了一个新键盘,谁在乎呢? 当您在上面打字时,它仍然以相同的方式工作。
不幸的是,_可能_会出现问题,特别是对于大容量存储设备。 效果与设备在系统挂起时真的被拔出完全相同。 如果您在设备上挂载了文件系统,那么您就倒霉了 -- 该文件系统中的所有内容现在都无法访问。 如果您的根文件系统位于该设备上,这尤其令人恼火,因为您的系统会立即崩溃。
断电不是唯一需要担心的机制。 任何中断电源会话的操作都会产生相同的效果。 例如,即使在系统休眠时可能保持了挂起电流,但在许多系统上,在唤醒的初始阶段,固件(即 BIOS)会重置主板的 USB 主机控制器。 结果:所有电源会话都被破坏,并且再次就像您拔掉了所有 USB 设备一样。 是的,这完全是 BIOS 的错,但是除非您可以说服 BIOS 供应商修复问题(祝您好运!),否则这对 _您_ 没有什么好处。
在许多系统上,USB 主机控制器将在挂起到 RAM 后重置。 在几乎所有系统上,休眠期间(也称为 swsusp 或挂起到磁盘)没有可用的挂起电流。 您可以在恢复后检查内核日志,以查看是否发生了这些情况; 查找显示“root hub lost power or was reset”的行。
在实践中,人们被迫在挂起之前卸载 USB 设备上的任何文件系统。 如果根文件系统位于 USB 设备上,则根本无法挂起系统。 (好吧,_可以_ 挂起 -- 但它会在唤醒后立即崩溃,这也好不到哪里去。)
解决方案是什么?¶
内核包含一个名为 USB-persist 的功能。 它试图通过允许核心 USB 设备数据结构在电源会话中断期间保持存在来解决这些问题。
它的工作方式如下。 如果内核看到 USB 主机控制器在恢复期间未处于预期状态(即,如果控制器已重置或以其他方式断电),那么它会对该控制器下方的每个设置了“persist”属性的 USB 设备应用持久性检查。 它不会尝试恢复设备; 一旦电源会话结束,这就不起作用了。 相反,它会发出 USB 端口重置,然后重新枚举设备。 (这与每次重置 USB 设备时发生的情况完全相同。)如果重新枚举显示现在连接到该端口的设备与之前的设备具有相同的描述符,包括供应商和产品 ID,那么内核将继续使用相同的设备结构。 实际上,内核将设备视为仅仅被重置而不是拔出。
如果主机控制器处于预期状态,但 USB 设备已拔出然后重新插入,或者 USB 设备无法执行正常恢复,则会发生同样的情况。
如果没有设备现在连接到端口,或者描述符与内核记住的不同,那么处理方式将是您所期望的。 内核会销毁旧的设备结构,并表现得好像旧设备已被拔出,新设备已插入。
最终结果是 USB 设备仍然可用和可使用。 文件系统挂载和内存映射不受影响,世界现在变得美好而快乐。
请注意,“USB-persist”功能仅适用于已启用的设备。 您可以通过执行以下操作来启用该功能(以 root 身份)
echo 1 >/sys/bus/usb/devices/.../power/persist
其中“...”应填充设备的 ID。 通过写入 0 而不是 1 来禁用该功能。 对于集线器,该功能会自动且永久启用,并且 power/persist 文件甚至不存在,因此您只需担心为真正重要的设备设置它。
这是最好的解决方案吗?¶
可能不是。 可以说,跨设备断开连接跟踪已挂载的文件系统和内存映射应该由集中的逻辑卷管理器处理。 这样的解决方案将允许您插入 USB 闪存设备,创建与其关联的持久卷,拔出闪存设备,稍后重新插入,并且仍然具有与该设备关联的相同持久卷。 因此,它将比 USB-persist 更具深远意义。
另一方面,编写持久卷管理器将是一项繁重的工作,并且使用它需要用户的大量投入。 这个解决方案更快,更容易 -- 而且它现在已经存在,这是它的一大优势!
此外,USB-persist 功能适用于 _所有_ USB 设备,而不仅仅是大容量存储设备。 事实证明,它对于其他设备类型(例如网络接口)也同样有用。
警告:USB-persist 可能很危险!!¶
在恢复中断的电源会话时,内核会尽最大努力确保 USB 设备没有更改; 也就是说,与以前一样,同一个设备仍然插入到端口中。 但是,检查不能保证 100% 准确。
如果您将一个 USB 设备替换为另一个相同类型的设备(相同的制造商、相同的 ID 等),则内核很有可能不会检测到该更改。 序列号字符串和其他描述符与内核的存储值进行比较,但这可能没有帮助,因为制造商经常完全在其设备中省略序列号。
此外,完全有可能在更改其媒体的同时保持 USB 设备完全相同。 如果您在系统休眠时更换了 USB 读卡器中的闪存卡,则内核将无法知道您做了什么。 内核将假设没有任何事情发生,并将继续使用旧卡的分区表、inode 和内存映射。
如果内核以这种方式被愚弄,几乎肯定会导致数据损坏并导致您的系统崩溃。 您只能责怪自己。
对于设置了 avoid_reset_quirk 属性的那些设备,持久化可能会失败,因为它们可能会在重置后变形。
您已被警告! 使用风险自负!
话虽如此,大多数时候应该没有任何问题。 USB-persist 功能可能非常有用。 充分利用它。