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 的数据手册。
DMA 缓冲区大小对性能的影响 缓冲区大小 (Bytes) 512 1024 2048 4096 低 高 性能指标 吞吐量 延迟 最佳平衡区 建议:批量传输使用 2048-4096 Bytes,中断传输使用 512-1024 Bytes

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。
中断合并前后对比 未合并 (阈值=1) DMA1 IRQ DMA2 IRQ 合并后 (阈值=4) DMA1 DMA2 DMA3 IRQ 中断合并效果分析 • 未合并:4 次 DMA 传输产生 4 次中断,CPU 频繁响应。 • 合并后:4 次 DMA 传输仅产生 1 次中断,CPU 负载降低 75%。 • 代价:最后一次 DMA 完成到 CPU 响应中断的延迟增加。 • 适用场景:批量传输、文件存储、视频流;不适用:控制传输、HID。

4.4 综合调优流程

以下是一个推荐的 UDC 驱动调优步骤,适用于大多数基于 dwc3 / cdns3 的嵌入式平台:

  1. 基准测试: 使用 usb_gadget_test 或自定义 Gadget 驱动,测量默认配置下的吞吐量和延迟。
  2. 启用 SG 支持: 确认 sg_supported 为 true,并验证硬件 SG 列表长度。
  3. 调整 DMA 缓冲区: 将 bufsize 设置为 maxpacket * 4,并监控内存占用。
  4. 配置中断合并: 从 irq_threshold=4 开始,逐步增加至 16,观察 CPU 占用率和延迟变化。
  5. 验证稳定性: 在满负载下运行 24 小时,检查是否有 DMA 超时或 FIFO 溢出错误。
快速验证命令: 使用 cat /proc/interrupts | grep dwc3 查看中断频率变化。合并前可能每秒数万次,合并后应降至数千次。