技术指南:从AMO到Zabha与Zacas——RISC-V原子扩展演进与内核适配

摘要

A 扩展的原子读改写只覆盖字与双字宽度,子字原子长期依赖 LR/SC 模拟。本文从指令编码与内存语义出发,梳理 Zabha 与 Zacas 对 8/16 位 AMO 与 amocas 的补充,并说明 Linux 6.13 之后的内核适配路径与 qspinlock 的启用条件。

一、引言:原子操作的宽度边界

A 扩展提供两组原语。AMO 指令在单条指令内完成读—改—写,amo{add,swap,and,or,xor,min,max,minu,maxu} 覆盖加法、交换、位运算与最值;LR/SC 由 lr 与 sc 组成保留—条件存对,由软件组织重试循环。两组原语的操作宽度都以 XLEN 为基准:RV32 只有 .w,RV64 增加 .d,不存在字节与半字形式。

子字原子因此长期依靠模拟。位运算可以在字宽 AMO 之上做掩码;其余运算需要 lr.w/sc.w 循环,在 32 位字内按目标字节的偏移构造掩码并保留相邻位。这类序列功能上可用,代价体现在四处,Zabha 规范的动机段落对它们有明确列举:

  • 大规模或 NUMA 系统中,基于 LR/SC 的模拟在高竞争条件下存在可扩展性与公平性问题;
  • 用更宽的 AMO 模拟窄 AMO,在非幂等 IO 内存区域可能产生非预期副作用;
  • 更宽的 AMO 可能触发无关的断点或观察点;
  • 缺少原生子字原子时,编译器倾向于内联模拟序列,代码体积增大。

Zabha 与 Zacas 分别从宽度与操作类型两侧补上缺口。把视角放回 Profile 层面,这组扩展各自约束一个侧面:Zabha 与 Zacas 管指令的宽度与 CAS,Ziccrse 管 LR/SC 的前向进展,Ziccamoc 管主存区域的 CAS 支持等级,Zama16b 管不对齐访问的原子粒度。

二、Zabha:字节与半字 AMO

Zabha(v1.0,2024 年 4 月批准)依赖 Zaamo,提供 AMO[ADD|AND|OR|XOR|SWAP|MIN[U]|MAX[U]].[B|H];若实现同时包含 Zacas,则进一步提供 AMOCAS.[B|H]。

语义上有几点需要留意。字节与半字 AMO 写入 rd 的值按宽度做符号扩展,并忽略 rs2 中宽度以外的高位;AMOCAS.[B|H] 同样忽略 rd 中的高位。rs1 内的地址必须按操作数大小自然对齐,不对齐时的异常选项与 A 扩展相同。与 A 扩展、Zacas 一致,aq/rl 位可选地提供释放一致性语义。Zabha 不提供字节与半字的 LR/SC,规范给出的理由是效用低——需要子字 CAS 的场景由 amocas.b/h 覆盖,无需为子字单独维护一套保留集语义。

编码层面,宽度落在 AMO 指令的 funct3 域:

funct3宽度后缀来源
000.bZabha
001.hZabha
010.wA
011.dA(RV64)
100.qZacas(RV64)
101–111保留—

运算类型由 funct5 域区分(add 00000、swap 00001、xor 00100、or 01000、and 01100、min 10000、max 10100、minu 11000、maxu 11100),CAS 使用 Zacas 新增的 00101,aq/rl 位于 bit 26 与 bit 25,AMO 主 opcode 为 0101111。需要区分的是,这里描述的是指令编码层面的宽度;编码存在不等于所有实现都必须提供,扩展是否可用仍以实现的 ISA 字符串为准。

三、Zacas:amocas 的语义边界

Zacas(v1.0.0)同样依赖 Zaamo,定义 amocas.w/.d/.q,其中 .q 仅在 RV64 可用。规范把 CAS 定位为 C++11 原子 compare-exchange 的硬件对应物,并说明:XLEN 宽度的 CAS 可以用 LR/SC 实现,但专用 CAS 指令在高并行系统下的可扩展性更好;指针类算法需要把指针与计数器打包成双字或四字一起比较,以规避 ABA 问题。

宽度与寄存器规则因 XLEN 而异。RV64 下 amocas.w 比较并交换 32 位,加载值符号扩展后写入 rd;amocas.d 操作 64 位;amocas.q 使用 rd/rd+1 与 rs2/rs2+1 两组寄存器对,起始寄存器必须为偶数编号。RV32 下 amocas.d 同样使用寄存器对,寄存器对首寄存器为 x0 时该半读作零,作为目的寄存器对时结果整体丢弃。

失败路径的语义是这一节的重点,也是实现中最容易写错的部分:

情形语义
成功aq=1 具 acquire,rl=1 具 release
失败aq=1 具 acquire,不具有 release,与 rl 无关
失败时的写可以不产生写,也可以写回加载到的旧值;若产生写,该写不具备 release 语义
权限始终需要写权限

无论是否实际产生写,失败的 AMOCAS 在 RVWMO 的 PPO 规则下仍按 AMO 处理。地址必须按操作数大小自然对齐(四字 16 字节、双字 8 字节、字 4 字节),不对齐时沿用 A 扩展的异常选项。

四、PMA 层级与 Profile 地位

原子操作的支持程度在内存区域语义上同样是分层的。A 扩展为 AMO 定义了四级 PMA 支持;Zacas 在其上增加三级:AMOCASW、AMOCASD、AMOCASQ,后者依次要求前者的能力,并且都要求 AMOArithmetic 级支持——CAS 需要完成算术比较与交换。

RVA23U64 把相关能力归入两类。强制项中包含 A 与 Ziccamoa,后者要求同时具备 cacheability 与 coherence PMA 的主存区域支持 A 扩展中的全部原子操作。development options 中列出 Zabha、Zacas、Ziccamoc 与 Zama16b,规范表述是这些选项预期在后续 RVA Profile 中转为强制。

其中 Ziccamoc 与 Zama16b 属 Profile 定义的扩展。前者要求上述主存区域提供 AMOCASQ 级 PMA 支持;后者把不对齐的加载、存储与 AMO 在“不跨自然对齐 16 字节边界”条件下的原子粒度定为 16 字节,对应 Sm1p13 引入的 Misaligned Atomicity Granule 特性。两者约束的对象是 PMA 与内存区域语义,规范说明将并入特权架构手册的 PMA 章节。

选型与适配时,扩展清单之外还需一并核对设备树中的 ISA 字符串。型号层面公开的扩展组合通常发布在处理器系列页面上,例如玄铁处理器系列页,而内核启动时的解析结果才是运行期行为的依据。

五、Linux 侧落地:从 [cmp]xchg 宏到 qspinlock

Linux 6.13 的 RISC-V 合并窗口公告中列出的能力之一,是在具备 Zacas 与 Zabha 的系统上支持 qspinlock。对应补丁系列为 “Zacas/Zabha support and qspinlocks”(2024 年 11 月的 v6),由 Alexandre Ghiti 提交,并包含 Guo Ren 对 asm-generic ticket-lock 的整理。

内核适配分为三层:编译期开关、运行期探测、探测失败时的回退。cmpxchg/xchg 宏是第一条落点——cmpxchg32/64 走 Zacas,cmpxchg8/16 走 Zabha 的 amocas.b/h,arch_cmpxchg128 走 Zacas 的 amocas.q,xchg8/16 走 Zabha 的 amoswap.b/h。

以 xchg8/16 为例,原实现是在含目标字节的 32 位字上做掩码重试:

启用 Zabha 后,同一语义由单条指令完成:

运行期探测使用 riscv_has_extension_unlikely(RISCV_ISA_EXT_ZABHA) 一类的接口,编译期由 CONFIG_RISCV_ISA_ZABHA 与 CONFIG_RISCV_ISA_ZACAS 控制,后者还要求工具链具备对应能力。

qspinlock 的启用条件是这条链路上更值得展开的部分。内核提供三档配置:RISCV_TICKET_SPINLOCKS、RISCV_QUEUED_SPINLOCKS 与默认选中的 RISCV_COMBO_SPINLOCKS。queued 选项的 help 文本说明它对前向进展的要求:cmpxchg()/xchg() 原子操作的前向进展保证由 CAS(配合 Zabha)或 LR/SC(配合 Ziccrse)提供,因此该选项只应在至少具备其中一项扩展的平台上选择。Ziccrse 是 RVA23U64 的强制项,要求具备 cacheability 与 coherence PMA 的主存区域支持 RsrvEventual。

combo 选项把两种实现同时编入内核,运行期通过静态键 qspinlock_key 分派到 queued 或 ticket 实现,在 Zabha 或 Ziccrse 缺席时回退 ticket。实现选择静态键而非 alternatives,原因写在该补丁的说明中:扩展字符串的解析时机过晚,而自旋锁在启动早期即被使用;切换顺序必须从 qspinlock 实现开始,因为 ticket 实现会污染 spinlock 值。代码中的 TODO 注释记录了后续方向:待扩展解析提前到启动更早阶段后改用 alternative。

六、坑位与关键结论

坑位:

  1. 把 Ziccamoc、Zama16b 当作普通 ISA 扩展理解。它们是 Profile 定义的扩展,约束对象是 PMA 与内存区域语义,规范说明将并入特权架构手册的 PMA 章节。
  1. amocas 失败路径仍具 acquire 语义。重试实现中若只按成功路径补屏障,失败时的 acquire 就被忽略。
  1. 不能假定失败等于不写。规范允许失败的 AMOCAS 写回旧值,且该写不具备 release 语义。
  1. 对齐是硬约束。子字 AMO 与 amocas 都要求自然对齐,跨边界访问不会因为扩展存在而变为原子;16 字节以内的不对齐原子性属于 Zama16b 的定义范围。
  1. combo 的探测顺序是约束而非实现细节。静态键既是对解析时机的迁就,也是不能先用 ticket 再切换的结果。
  1. 工具链能力与内核能力需分开判断。CONFIG_RISCV_ISA_ZABHA/ZACAS 取决于工具链支持,汇编中还需 .option arch 或 -march 配合;工具链不支持的扩展,内核也不会使用。
  1. combo 的探测结论在启动早期即被使用。任何把扩展解析或静态键初始化推迟到更晚阶段的改动,都会直接反映为启动路径上的锁行为变化。
0
0
0
0
评论
未登录
暂无评论