并发、队列、生命周期与错误恢复
1. 执行上下文
每条活动USB/IP连接通常有:
- 一个RX kthread;
- 一个TX kthread;
- 共享的全局单线程event workqueue;
- Stub真实URB completion或VHCI giveback上下文;
- sysfs管理进程上下文;
- VUDC额外transfer timer。
Socket收发可睡眠,只在kthread中执行。真实URB completion仅移动链表并唤醒TX。
2. 公共同步
usbip_device:
lock:status、event等短临界区;sysfs_lock:连接建立/拆除与EH串行;eh_waitq:等待event处理完成;- socket shutdown:唤醒阻塞中的RX/TX。
Event list由全局spinlock保护,workqueue对同一设备的多个事件合并。
3. Stub队列状态
一次SUBMIT对应的stub_priv始终位于:
1 | priv_init -> priv_tx -> priv_free |
priv_init:真实URB尚未全部完成;priv_tx:可发送RET_SUBMIT;priv_free:网络结果已发送,可释放。
UNLINK另有unlink_tx和unlink_free。所有链表由priv_lock保护,TX waitqueue由completion和unlink处理唤醒。
Scatter-gather拆分时,多个真实URB共享一个stub_priv。只有completed_urbs == num_urbs才能进入TX,聚合status必须保留首个有效错误。
4. VHCI队列状态
1 | URB enqueue |
urb->hcpriv指向vhci_priv,seqnum是网络请求主键。priv_lock保护submit和unlink链表,vhci->lock同时保护HCD endpoint链接、port status和设备连接状态。
锁内不能执行可能睡眠的socket操作;giveback也常需要临时释放全局锁,防止completion回调重入HCD。
5. Unlink状态机
URB取消与正常完成存在天然竞态:
1 | CMD_UNLINK ----\ |
实现需要满足:
- 一个URB只完成一次;
- 目标seqnum找不到时不释放未知对象;
- RET_UNLINK不等价于原URB一定没有RET_SUBMIT;
- disconnect清理覆盖所有submit/unlink列表。
VHCI分别维护unlink_tx/unlink_rx;Stub在收到取消后调用usb_unlink_urb()并排队RET_UNLINK。
6. Event优先级
EH顺序固定:
SHUTDOWN;RESET;UNUSABLE。
原因是必须先停止socket线程和新I/O,才能安全清理URB及改变状态。BYE用于对象remove,阻止再加入新event。
7. 常见错误入口
usbip_recv()得到0:对端关闭;- send/recv返回负值:TCP错误;
- header command非法:协议错误;
- buffer/URB分配失败:malloc event;
usb_submit_urb()失败:submit event;- USB物理设备remove:removed event;
- sysfs写入无效fd、非stream socket或已占用端口:立即返回错误。
这些错误通常会拆除整条设备连接,而不是只丢弃单个PDU,避免字节流失去消息边界后继续解析。
8. 生命周期规则
正确销毁顺序:
1 | block new sysfs operations |
线程由kthread_create后额外get_task_struct(),退出时需kthread_stop_put()成对释放。
9. 内存与输入验证
网络头控制URB数量、transfer buffer和ISO descriptor分配,是高风险路径。已有保护包括:
- ISO packet上限;
- 负数检查;
- endpoint和direction检查;
- actual length不得超过transfer buffer;
- VUDC按max packet估算packet上限;
- 无效command触发连接错误。
审计时仍应重点检查整数乘法溢出、SG拆分、short read、异常actual length和disconnect并发。
10. PM语义
VHCI的bus suspend/resume仅改变软件HCD状态,不能保证远端物理设备、电源域或网络连接同步休眠。系统休眠前最好显式detach;否则恢复后TCP可能超时,最终表现为热拔插。
正在加载留言…