USB xHCI err -71 略解

kernel:linux 4.9.1
USB host controler:xHCI

最近遇到两个报 err -71 错误的问题,同一个device (PL2303),分别在USB枚举阶段与数据传输阶段出错。一开始我是心累的,毕竟手里已经积攒了6个测试单,4个客户单,4个内部群在拉我对问题。对于这两个问题在我做完三件事后,心中的大石总算落下了一半,真是守得云开见月明。

1、对以往报 err -71 的问题进行梳理、总结。  
2、与同事进行沟通,借助他们的经验。  
3、针对性的梳理 xHCI 上报 err -71 的流程。  

问题梳理
场景是环境不断的reboot压测,即在device不掉电的情况下,host间断地与其握手、枚举、数据传输。

usb1-2: device not accepting address2, error -71  

log分析,-71 在内核的标准错误表里面是 -EPROTO /** Protocol error **/,即协议错误,对于USB场景,为USB协议传输错误。出问题的环境有两个,只复现了一次,还要做别的测试,比较紧缺,协调不到。而且device模组PL2303焊在单板上,没有USB口可以抓取协议包。在问题不复现,log较少,无法抓包的情况下,到底是host发包错了,还是device回复错了呢,或是外部因素影响了,真是没啥头绪啊!

回溯以往问题
我在问题单系统里搜索了一番,搜索出来的问题单很多,我有权限看的问题单就二张。不过有,总好过没。
问题单①,USB host control是ehci,U盘插进单板,开始能识别,几分钟过后不识别,输出以下log,低概率发生。

usb1-2: device not accepting address8, error -71  
usb1-2: reset high-speed USB device number8 using ehci-platform  
usb1-2: usb-storage timed out onep0out len=0/0  
usb1-2: device not accepting address8, error -71  
usb1-2: reset high-speed USB device number8 using ehci-platform  
usb1-2: device descriptor read/64, error -71  
usb1-2: device descriptor read/64, error -71
usb1-2: USB disconnect, device number8
sd1:0:0:0:[sdb] tag#0 UNKNOWN(0x2003) Result: hostbyte=0x03 driverbyte=0x00
sd1:0:0:0:[sdb] tag#0 CDB: opcode=0x28 28 00 00 00 0a27 00 00 01 00
blk_update_request: I/O error, dev sdb, sector2599

从log分析,先是reset,后报I/O error,指向了第2599个sector 。一开始觉得很纳闷,都枚举识别完了,怎么又reset,重新识别一次呢?还有操作相关sector失败的I/O error。后面想起与Android 储存同事沟通时获取到的一个信息,Android会在U盘插入后,进行一次fsck扫描,判断该盘的健康状态。从这个思路出发,我对U盘进行了一次坏块扫描。发现U盘处于亚健康状态,存在662个严重块,而严重块的响应时间在2~6s,同时U盘的分区较多,存在分区表恰好落在严重块上的情况。在上层读取分区参数或者进行fsck扫描时,存在读取超时的情况,包括底层USB通信超时。USB通信超时,就会与设备重新握手识别,log输出 “reset high-speed USB device number2 using ehci-platform” 。

问题单②,USB host control是ehci,U盘插进单板,上电识别 (device先插入host,host再上电启动) 报 err -71 ,必现。

usb1-2: device not accepting address2, error -71  
usb1-2: reset high-speed USB device number2 using ehci-platform  
usb1-2: device not accepting address3, error -71  

通过USB夹具先给U盘供电,再插入已启动完全的环境中,问题也能复现。出现这个问题的设备也不少。其中部分设备如果先供电时间小于3s,不复现该问题。这个与U盘响应host枚举的时间窗口有关,原因直接定位为设备问题。
虽然是设备问题,但也与方案整体的兼容性有关。可能存在因兼容性不够,而导致软件需要二次识别设备并在过程中报 -71 错误。
解决方案:在不考虑成本的情况下 (GPIO可是珍稀资源),通过GPIO控制vbus,在识别过程中让设备重新上电,避开设备存在的识别窗口时间问题;低成本的方法就是让软件多次去retry,握手识别,留下 err -71 的error log。

从上面两个案例来看都是设备自身问题,和同事沟通后,了解到也有因硬件导致信号质量不好报 -71 的问题。

xHCI 代码流程
因为两个err -71问题的环境使用的都是xHCI,所以我们只关心xHCI。usb软件层面的错误码我们已经清楚是error -71,下面我们就要去找逻辑层面的错误码了,即xHCI的 TRB Completion Code。我们主要关注 xhci.c、xhci.h 和 xhci-ring.c这三个文件。根据error是 -71,我们直接搜索 -EPROTO,结合问题点发生在枚举和数据传输阶段,确定了两个函数。

xhci.c --> static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, 
                                enum xhci_setup_dev setup)
{
    ......
    case COMP_TX_ERR:
        dev_warn(&udev->dev, "Device not responding to setup %s.\n", act);
        ret = -EPROTO;
        break;
    case COMP_DEV_ERR:
    ......
}

在上述函数中,我们看到进入 COMP_TX_ERR 分支会引起 -EPROTO。

xhci-ring.c --> static int handle_tx_event(struct xhci_hcd *xhci,
                                struct xhci_transfer_event *event)
                            __releases(&xhci->lock)
                            __acquires(&xhci->lock)
{
    ......
    case COMP_SPLIT_ERR:
    case COMP_TX_ERR:
        xhci_dbg(xhci, "Transfer error on endpoint\n");
        status = -EPROTO;
        break;
    case COMP_BABBLE:
        xhci_dbg(xhci, "Babble error on endpoint\n");
        status = -EOVERFLOW;
        break;
    ......
        goto cleanup;
       case COMP_DEV_ERR:
        xhci_warn(xhci, "WARN: detect an incompatible device");
        status = -EPROTO;
        break;
    ......
}

在上述函数中,我们看到进入 COMP_TX_ERR 或者 COMP_DEV_ERR 分支都会引起 -EPROTO,为方便后续定位问题,我们把 COMP_TX_ERR分支里面的 xhci_dbg 修改为 xhci_warn。

我们把加了打印的版本拿去复现问题,在复现log中,有 “Transfer error on endpoint” 而没有 “WARN: detect an incompatible device”,即我们可以确定 TRB Completion Code 是 COMP_TX_ERR。

xhci.h -->
/* USB Transaction Error */
#define COMP_TX_ERR    4

在 xhci.h 里面我们找到了 COMP_TX_ERR,usb的事务错误,值为 4 。既然拿到了逻辑层面的错误码的值,我们就可以去看xhci的手册了。
这个是手册的下载链接:
https://www.intel.com/content/www/us/en/products/docs/io/universal-serial-bus/extensible-host-controler-interface-usb-xhci.html

在手册 6.4.5 TRB Completion Code 中详细说明了每个错误码的意思。我们看到 value 4 ,USB Transaction Error的描述:“Asserted in the case where the host did not receive a vaild response from the device (Timeout, CRC, Bad PID, unexpected NYET, etc).“ 大意是 “主机未从设备收到有效响应例如超时,CRC错误,错误的PID,意外的NYET等。” 结合上述步骤,我们大概可以确定,两个err -71问题都是设备问题,如果要明确,那就需要抓协议包了。

尾声
后续沟通,了解到一个情况,PL2303在高波特率的情况下会工作不稳定,而两个问题单的PL2303波特率都是4M,我们平时接触的115200,也就0.1M而已。


USB xHCI err -71 略解
http://xxxdk.xyz/xxx/2019/11/USB-xHCI-err-71-略解/
作者
sni
发布于
2019年11月23日
许可协议