4. 中断延迟优化:临界区保护策略与中断底半部机制

在蓝牙组网驱动中,中断延迟直接决定数据收发的实时性与吞吐量上限。过长的中断关闭时间会导致射频帧丢失、链路层超时重传,甚至触发看门狗复位。本章聚焦三种核心优化手段:临界区保护策略(关中断 vs 挂起调度器)、ISR 最小化原则以及中断底半部机制(Tasklet / Workqueue)。

核心指标:蓝牙 BR/EDR 的 ACL 数据包间隔为 625 µs(1 slot),BLE 连接事件间隔可低至 7.5 ms。中断关闭时间必须控制在 10–50 µs 以内,否则将直接破坏链路时序。

4.1 临界区保护策略:关中断 vs 挂起调度器

临界区(Critical Section)是指访问共享资源(如 HCI 命令队列、连接句柄表、链路层状态机)的代码段。保护临界区有两种主流策略,其性能差异在蓝牙驱动中尤为显著。

策略 原理 中断延迟影响 适用场景
关中断(local_irq_save/restore) 禁止本地 CPU 响应所有中断 直接增加中断延迟,关闭时间 = 临界区执行时间 临界区极短(< 5 µs)、操作寄存器或 FIFO
挂起调度器(preempt_disable/enable) 禁止内核抢占,但中断仍可响应 中断延迟仅增加调度延迟,不阻塞中断响应 临界区较长(5–50 µs)、涉及链表遍历或内存分配
避坑指南:在蓝牙 ISR 中调用 spin_lock_irqsave 时,如果临界区包含 hci_send_frame 或 l2cap_chan_del 等可能触发调度器的函数,必须使用 spin_lock_bh 而非 spin_lock_irqsave,否则会导致递归锁死。

以下 SVG 展示了两种策略在蓝牙 HCI 事件处理中的时序对比:

临界区保护策略时序对比 关中断策略 蓝牙 ISR 进入 关中断 (5 µs) 临界区执行 (10 µs) 开中断 挂起调度器策略 蓝牙 ISR 进入 preempt_disable 临界区执行 (10 µs) preempt_enable 关键差异: 关中断策略下,高优先级定时器中断(如蓝牙微帧定时器)被阻塞,导致链路层调度抖动;挂起调度器策略允许中断嵌套,仅禁止进程抢占。

4.2 中断服务程序(ISR)最小化原则

蓝牙驱动的 ISR 必须遵循“快进快出”原则。ISR 中只做最紧急、最轻量的操作,其余工作推迟到底半部执行。以下是 ISR 最小化的具体规则:

  • 规则 1:只读寄存器,不写慢速外设。例如,读取 HCI UART RX FIFO 的字节数,但不解析 HCI 帧头。
  • 规则 2:不调用可能睡眠的函数。如 kmalloc(GFP_KERNEL)、mutex_lock、hci_send_frame。
  • 规则 3:使用原子操作或自旋锁保护共享标志。例如,atomic_set(&hdev->rx_pending, 1) 通知底半部有新数据。
  • 规则 4:ISR 执行时间 < 蓝牙连接间隔的 1/10。对于 BLE 7.5 ms 连接事件,ISR 应 < 750 µs;对于 BR/EDR 625 µs slot,ISR 应 < 60 µs。
实践案例:某 BLE 5.0 驱动中,ISR 仅执行 3 步:① 读取 RX FIFO 深度寄存器;② 将数据搬运到预分配的 DMA buffer;③ 设置 tasklet_schedule。整个 ISR 耗时约 8 µs,远低于 BLE 连接事件间隔。

以下 SVG 展示了 ISR 最小化原则下的数据流架构:

ISR 最小化数据流架构 蓝牙射频控制器 IRQ ISR(最小化) 读 FIFO + 调度底半部 底半部(Tasklet) HCI 帧解析 + 协议栈提交 HCI ISR 最小化清单(蓝牙驱动典型操作) ✅ ISR 内允许:读 RX/TX 状态寄存器、读 FIFO 深度、写 ACK 寄存器、设置原子标志、调度底半部 ❌ ISR 内禁止:HCI 帧解析、L2CAP 重组、内存分配、互斥锁、打印日志、调用协议栈 API ⏱ 典型 ISR 耗时:8–30 µs(取决于 FIFO 深度和 DMA 配置)

4.3 中断底半部机制:Tasklet vs Workqueue

底半部(Bottom Half)是 Linux 内核将非紧急中断处理推迟到安全上下文中执行的机制。蓝牙驱动中常用的两种底半部是 Tasklet 和 Workqueue。它们的差异直接影响数据收发的吞吐量和延迟。

特性 Tasklet Workqueue
执行上下文 软中断上下文(atomic) 进程上下文(可睡眠)
可睡眠 否(不能调用 msleep、mutex_lock) 是(可执行阻塞 I/O、内存分配)
调度延迟 低(软中断返回后立即执行) 较高(依赖内核工作线程调度)
并发模型 同一 Tasklet 在单 CPU 上串行执行 可绑定到指定 CPU 或 worker pool
蓝牙典型用途 HCI 事件解析、ACL 数据帧重组 HCI 命令发送、连接管理、固件下载
选型建议:对于 BLE 数据通道的 RX/TX 处理,优先使用 Tasklet 以获得最低延迟;对于 HCI 命令响应、连接参数更新等可能阻塞的操作,使用 Workqueue 避免软中断上下文阻塞。

以下 SVG 展示了 Tasklet 与 Workqueue 在蓝牙驱动中的调度流程对比:

Tasklet vs Workqueue 调度流程 Tasklet 路径 ISR 调用 tasklet_schedule() 软中断上下文执行 HCI 帧解析 + 协议栈提交 延迟:低(~10 µs) Workqueue 路径 ISR 调用 queue_work() 内核工作线程调度 HCI 命令发送 / 连接管理 延迟:中(~100 µs–1 ms) 注意:Workqueue 中可调用 hci_send_cmd、hci_conn_put 等可能睡眠的函数;Tasklet 中只能调用 hci_recv_frame 等非阻塞函数。

4.4 综合优化策略与代码示例

在实际蓝牙驱动中,中断延迟优化需要组合使用上述三种技术。以下是一个典型的蓝牙 UART 驱动中断处理代码框架:

/* 蓝牙 UART 驱动 ISR - 最小化实现 */
static irqreturn_t bt_uart_irq_handler(int irq, void *dev_id)
{
    struct bt_uart_dev *bdev = dev_id;
    unsigned int rx_count;

    /* 规则1:只读寄存器,不解析数据 */
    rx_count = ioread32(bdev->regs + UART_RX_CNT);
    if (!rx_count)
        return IRQ_NONE;

    /* 规则2:使用 DMA 或快速 memcpy 搬运数据 */
    dma_sync_single_for_cpu(bdev->dev, bdev->dma_handle, rx_count, DMA_FROM_DEVICE);

    /* 规则3:原子标志通知底半部 */
    atomic_set(&bdev->rx_pending, rx_count);

    /* 规则4:调度 Tasklet(数据路径)或 Workqueue(控制路径) */
    if (bdev->is_data_channel)
        tasklet_schedule(&bdev->rx_tasklet);
    else
        queue_work(bdev->wq, &bdev->ctrl_work);

    return IRQ_HANDLED;
}

/* Tasklet - 数据帧处理(不可睡眠) */
static void bt_uart_rx_tasklet(unsigned long data)
{
    struct bt_uart_dev *bdev = (struct bt_uart_dev *)data;
    unsigned int count = atomic_xchg(&bdev->rx_pending, 0);
    struct sk_buff *skb;

    /* 在软中断上下文中完成 HCI 帧解析 */
    skb = bt_uart_build_skb(bdev, count);
    if (skb)
        hci_recv_frame(bdev->hdev, skb);
}

/* Workqueue - 控制命令处理(可睡眠) */
static void bt_uart_ctrl_work(struct work_struct *work)
{
    struct bt_uart_dev *bdev = container_of(work, struct bt_uart_dev, ctrl_work);
    unsigned int count = atomic_xchg(&bdev->rx_pending, 0);

    /* 进程上下文中可调用 mutex 保护 */
    mutex_lock(&bdev->cmd_lock);
    bt_uart_process_cmd(bdev, count);
    mutex_unlock(&bdev->cmd_lock);
}
性能数据:采用上述优化后,某 BLE 5.2 驱动在 2 Mbps PHY 下的 RX 中断延迟从 120 µs 降至 18 µs,吞吐量从 1.2 Mbps 提升至 1.85 Mbps,丢包率从 0.3% 降至 0.01%。

4.5 本章小结

  • 临界区保护:短临界区(< 5 µs)用关中断;长临界区用挂起调度器,避免阻塞高优先级中断。
  • ISR 最小化:ISR 只做寄存器读取、数据搬运和底半部调度,禁止调用协议栈 API 或睡眠函数。
  • 底半部选型:数据路径用 Tasklet(低延迟、不可睡眠);控制路径用 Workqueue(可睡眠、适合阻塞操作)。
  • 综合实践:通过原子标志 + 双底半部机制,实现中断延迟 < 20 µs,满足蓝牙 BR/EDR 和 BLE 的实时性要求。