arm smmu v3 队列实现机制

ARM SMMU v3 队列实现详解

1. 队列结构设计

1.1 核心数据结构
490:504:drivers/iommu/arm-smmu-v3.c 复制代码
struct arm_smmu_ll_queue {
    union {
        u64            val;
        struct {
            u32        prod;
            u32        cons;
        };
        struct {
            atomic_t    prod;
            atomic_t    cons;
        } atomic;
        u8            __pad[SMP_CACHE_BYTES];
    } ____cacheline_aligned_in_smp;
    u32                max_n_shift;
};
1.2 队列宏定义
183:188:drivers/iommu/arm-smmu-v3.c 复制代码
#define Q_IDX(llq, p)            ((p) & ((1 << (llq)->max_n_shift) - 1)) // 队列索引计算
#define Q_WRP(llq, p)            ((p) & (1 << (llq)->max_n_shift)) // 队列环绕
#define Q_OVERFLOW_FLAG            (1U << 31)
#define Q_OVF(p)            ((p) & Q_OVERFLOW_FLAG) // 溢出检测
#define Q_ENT(q, p)            ((q)->base +            \
                     Q_IDX(&((q)->llq), p) *    \
                     (q)->ent_dwords)  // 队列中某条目的地址  基地址 + 索引 × 条目大小

2. 队列类型分析

2.1 是环形队列吗?

是的,这是环形队列

  • 使用 prod (生产者指针) 和 cons (消费者指针)
  • 通过 Q_IDX 宏实现环形索引:(p) & ((1 << max_n_shift) - 1)
  • 队列大小是2的幂次方,便于位运算优化
2.2 支持多生产者多消费者吗?

部分支持

多生产者

  • 使用 atomic_t 类型的 prodcons
  • 通过原子操作保证并发安全
  • 支持多个CPU核心同时写入

多消费者

  • 命令队列(CMDQ):单消费者(硬件消费)
  • 事件队列(EVTQ):单消费者(软件消费)
  • 优先级队列(PRIQ):单消费者(软件消费)
2.3 是无锁队列吗?

不是完全无锁

无锁部分

  • 使用原子操作进行指针更新
  • 生产者使用 atomic_t 保证并发安全

有锁部分

527:532:drivers/iommu/arm-smmu-v3.c 复制代码
struct arm_smmu_cmdq {
    struct arm_smmu_queue        q;
    atomic_long_t            *valid_map;
    atomic_t            owner_prod;
    atomic_t            lock;
};

3. 队列操作机制

3.1 空间检查
713:727:drivers/iommu/arm-smmu-v3.c 复制代码
static bool queue_has_space(struct arm_smmu_ll_queue *q, u32 n)
{
    u32 space, prod, cons;

    prod = Q_IDX(q, q->prod);
    cons = Q_IDX(q, q->cons);

    if (Q_WRP(q, q->prod) == Q_WRP(q, q->cons))
        space = (1 << q->max_n_shift) - (prod - cons);
    else
        space = cons - prod;

    return space >= n;
}
3.2 状态判断
728:736:drivers/iommu/arm-smmu-v3.c 复制代码
static bool queue_full(struct arm_smmu_ll_queue *q)
{
    return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) &&
           Q_WRP(q, q->prod) != Q_WRP(q, q->cons);
}

static bool queue_empty(struct arm_smmu_ll_queue *q)
{
    return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) &&
           Q_WRP(q, q->prod) == Q_WRP(q, q->cons);
}

4. 特殊设计特点

4.1 Wrap位机制
  • 使用 Q_WRP 宏检测队列是否已环绕一圈
  • 通过比较wrap位判断队列状态
  • 避免队列满/空的歧义
4.2 溢出检测
  • 使用 Q_OVERFLOW_FLAG (第31位) 检测溢出
  • 防止32位计数器溢出问题
4.3 缓存行对齐
501:502:drivers/iommu/arm-smmu-v3.c 复制代码
        u8            __pad[SMP_CACHE_BYTES];
    } ____cacheline_aligned_in_smp;
  • 确保队列结构体按缓存行对齐
  • 减少伪共享(false sharing)问题

5. 三种队列类型

5.1 命令队列(CMDQ)
  • 用途:CPU向SMMU发送命令
  • 特点:多生产者,单消费者(硬件)
  • 同步:需要等待硬件处理完成
5.2 事件队列(EVTQ)
  • 用途:SMMU向CPU报告事件
  • 特点:单生产者(硬件),单消费者(软件)
  • 处理:通过中断处理
5.3 优先级队列(PRIQ)
  • 用途:处理页面请求
  • 特点:单生产者(硬件),单消费者(软件)
  • 可选:根据硬件特性决定是否启用

6. 总结

ARM SMMU v3的队列实现是一个高性能的环形队列,具有以下特点:

  1. 环形设计:使用位运算实现高效的环形索引
  2. 并发支持:支持多生产者,使用原子操作保证线程安全
  3. 硬件协同:与SMMU硬件紧密配合,支持硬件直接访问
  4. 性能优化:缓存行对齐,减少内存访问冲突
  5. 可靠性:包含溢出检测和wrap位机制,确保队列状态正确

这种设计既保证了高性能,又确保了在复杂硬件环境下的可靠性。