4. UDC 驱动调优:关键参数、DMA 缓冲区与中断合并
UDC(USB Device Controller)驱动是 Linux USB Gadget 协议栈中直接与硬件交互的底层软件层。其配置参数、DMA 缓冲区大小以及中断处理策略,直接决定了数据传输的吞吐量、延迟和 CPU 占用率。本章将深入剖析这三个核心调优维度,并提供可操作的配置建议。
核心观点: UDC 驱动调优的目标是在“高吞吐量”与“低 CPU 占用”之间找到平衡点。盲目增大缓冲区或合并中断,可能导致延迟激增或硬件 FIFO 溢出。
4.1 UDC 驱动关键参数解析
UDC 驱动通常通过 struct usb_gadget_ops 和 struct usb_ep 结构体暴露可调参数。以下是最关键的三个参数及其影响:
| 参数名称 | 所属结构 | 典型值范围 | 性能影响 |
|---|---|---|---|
max_speed |
usb_gadget |
USB_SPEED_HIGH / SUPER | 限制 Gadget 协商的最高速率。设为 SUPER 可启用 SuperSpeed 流式传输。 |
max_ep_buf_size |
usb_ep |
512 / 1024 / 4096 (Bytes) | 决定单次端点传输的最大数据量。过小会触发频繁的硬件事务,过大则浪费 FIFO 空间。 |
sg_supported |
usb_ep |
true / false | 启用分散/聚集 I/O。为 true 时,允许一次请求携带多个不连续的内存缓冲区,减少 DMA 描述符开销。 |
调优建议: 对于批量传输(Bulk)场景,优先将
sg_supported 设为 true,并确保 UDC 硬件支持 SG 模式。这通常能带来 15%-30% 的吞吐量提升。
4.2 DMA 缓冲区大小调整策略
DMA 缓冲区是 UDC 与系统内存之间的数据中转站。其大小直接影响数据传输的效率和延迟。
- 缓冲区过小: 导致频繁的 DMA 中断和上下文切换,CPU 占用率飙升,吞吐量受限。
- 缓冲区过大: 增加内存占用,且单次 DMA 传输延迟变长,不适合实时性要求高的场景(如音频、控制传输)。
调整通常通过修改 UDC 驱动中的 struct usb_ep 的 maxpacket 和 bufsize 字段实现。以下是一个在 dwc3 驱动中调整缓冲区大小的示例:
/* 在 dwc3_gadget_init_endpoint() 函数中 */
struct dwc3_ep *dep;
struct usb_ep *ep;
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
ep = &dep->endpoint;
/* 设置端点最大包大小,对于 Bulk IN,通常设为 1024 (HS) 或 1024 (SS) */
ep->maxpacket = 1024;
/* 设置 DMA 缓冲区大小,建议为 maxpacket 的 2-4 倍,以支持乒乓操作 */
dep->bufsize = ep->maxpacket * 4; /* 4096 Bytes */
/* 注册端点 */
dwc3_gadget_ep_ops.enable(ep, &desc);
避坑指南: 不要将
bufsize 设置为超过 UDC 硬件 FIFO 深度的大小。例如,某些 dwc3 控制器的 FIFO 深度为 4KB,若设置 8KB,驱动会触发回退机制,反而降低性能。请查阅具体 UDC 的数据手册。
4.3 中断合并(Interrupt Coalescing)配置
中断合并是一种减少 CPU 中断频率的技术。UDC 驱动在完成一次 DMA 传输后,通常会产生中断。在高吞吐量场景下,每秒可能产生数万次中断,导致 CPU 被完全占用。
中断合并通过等待多个传输完成或等待一个超时周期后,再统一上报中断,从而降低中断频率。
4.3.1 配置参数
在 Linux 内核中,中断合并通常通过 struct usb_gadget 的 irq_threshold 或 UDC 私有驱动参数控制。以 dwc3 为例:
| 参数 | 位置 | 说明 | 典型值 |
|---|---|---|---|
irq_threshold |
dwc3 模块参数 | 触发中断前等待的 DMA 事件数量 | 1 (禁用合并) / 4 / 8 / 16 |
tx_thr_num |
dwc3 端点私有数据 | 发送方向中断合并阈值 | 4 |
rx_thr_num |
dwc3 端点私有数据 | 接收方向中断合并阈值 | 4 |
4.3.2 配置示例
通过模块参数启用 dwc3 的中断合并:
# 加载 dwc3 模块时设置中断合并阈值
modprobe dwc3 irq_threshold=8
# 或通过 sysfs 动态调整(如果驱动支持)
echo 8 > /sys/module/dwc3/parameters/irq_threshold
在驱动代码中,可以在端点使能时设置合并参数:
/* dwc3_gadget_ep_enable() 中 */
struct dwc3_ep *dep = to_dwc3_ep(ep);
struct dwc3 *dwc = dep->dwc;
/* 设置发送和接收阈值 */
dep->tx_thr_num = 4;
dep->rx_thr_num = 4;
/* 写入硬件寄存器 */
dwc3_writel(dwc->regs, DWC3_DEPCFG_PARAM(dep->number),
DWC3_DEPCFG_BURST_SIZE(16) |
DWC3_DEPCFG_XFER_COMPLETE_EN |
DWC3_DEPCFG_INT_NUM(dep->number) |
DWC3_DEPCFG_STREAM_CAPABLE |
DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(&desc)));
性能权衡: 中断合并阈值设为 8 时,CPU 中断频率可降低 87.5%,但单次传输延迟会增加约 7 个 USB 微帧(约 875μs 在 SuperSpeed 下)。对于批量传输,这是可接受的;对于等时传输,建议保持阈值为 1。
4.4 综合调优流程
以下是一个推荐的 UDC 驱动调优步骤,适用于大多数基于 dwc3 / cdns3 的嵌入式平台:
- 基准测试: 使用
usb_gadget_test或自定义 Gadget 驱动,测量默认配置下的吞吐量和延迟。 - 启用 SG 支持: 确认
sg_supported为true,并验证硬件 SG 列表长度。 - 调整 DMA 缓冲区: 将
bufsize设置为maxpacket * 4,并监控内存占用。 - 配置中断合并: 从
irq_threshold=4开始,逐步增加至 16,观察 CPU 占用率和延迟变化。 - 验证稳定性: 在满负载下运行 24 小时,检查是否有 DMA 超时或 FIFO 溢出错误。
快速验证命令: 使用
cat /proc/interrupts | grep dwc3 查看中断频率变化。合并前可能每秒数万次,合并后应降至数千次。