SG2002 WiFi 新同步原语的影响 - Antareske/tgoskits GitHub Wiki
SG2002 WiFi:新同步原语的影响、信用事件化尝试与 raw 修复评估(2026-08-22)
本文写于 2026 年 8 月,记录写作当时的代码状态;文中的文件路径与实现细节与当前主线已有出入。
本文围绕一次主线同步原语重构(dev 的锁统一工作)展开,记录它对 wifi 驱动的三重影响、信用事件化尝试的过程与结果、以及当前修复形态的评估与后续方向。所有 iperf3 测试均为同一块板、同一 AP、同一测试机,服务端跑在板卡上(iperf3 -s),上行指测试机→板卡方向(板卡为接收方),下行指板卡→测试机方向(板卡为发送方)。
一、主线同步原语重构对 wifi 驱动的影响面
1.1 发生了什么
dev 主线的一次锁统一重构把 wifi 驱动使用的全部总线锁,从"原始自旋锁"(获取/释放时什么都不做,不改变任务的运行环境)机械替换为"抢占禁用锁"(获取时禁止内核抢占,即当前任务在锁内不会被调度器换走)。对单核板卡而言,两类锁的"互斥"部分是一样的(都没有真正的并发保护),唯一的差别就是后者每获取一次锁就多一层"禁止换班"的约束。
这个看似中性的替换,实际产生了三重影响。
1.2 影响一:吞吐下降约 20%,且下行被"量化"
变基后的板测与变基前基线对比:上行 9.29/9.61 vs 11.2/12.0,下行 8.54 vs 11.0,双向 3.23/3.34 vs 5.37/5.81(且首次双向出现 3 次流控超时告警)。
下行原始日志(注意大多数区间恰好钉在 8.38-8.39 Mbps = 1.00 MB/s):
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 1.00 MBytes 8.38 Mbits/sec 0 0.00 Bytes
[ 5] 1.00-2.00 sec 1.00 MBytes 8.38 Mbits/sec 0 0.00 Bytes
[ 5] 2.00-3.00 sec 1.00 MBytes 8.39 Mbits/sec 0 0.00 Bytes
[ 5] 3.00-4.00 sec 1.00 MBytes 8.38 Mbits/sec 0 0.00 Bytes
[ 5] 4.00-5.01 sec 1.00 MBytes 8.32 Mbits/sec 0 0.00 Bytes
[ 5] 5.01-6.00 sec 1.00 MBytes 8.47 Mbits/sec 0 0.00 Bytes
[ 5] 6.00-7.00 sec 1.12 MBytes 9.44 Mbits/sec 0 0.00 Bytes
[ 5] 7.00-8.00 sec 1.00 MBytes 8.38 Mbits/sec 0 0.00 Bytes
[ 5] 8.00-9.00 sec 1.00 MBytes 8.38 Mbits/sec 0 0.00 Bytes
[ 5] 9.00-9.95 sec 1.00 MBytes 8.88 Mbits/sec 0 0.00 Bytes
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-9.95 sec 10.1 MBytes 8.54 Mbits/sec 0 sender
吞吐"钉死"在 1.00 MB/s 步进是节奏被定时器接管的指纹:每发一个包要经过几十次加锁解锁,每次锁内都不允许换班,任务之间的交接时机被这些约束窗口整体推迟,wifi 线程的收发节奏从"数据来了就动"退化成"定时巡检才动"。
双向聚合吞吐与变基前持平(总能力没降),说明问题不在能力而在节奏。
1.3 影响二:正确性契约违反(板测实证 panic)
wifi 驱动的总线锁被设计为跨整个传输持有,包括传输末尾"等硬件完成"的睡眠等待(等待期间任务应当睡着、让 CPU 给别人用)。这在原始自旋锁语义下完全合法——锁不改变运行环境,任务可以正常睡觉。
抢占禁用锁把这件事变成了非法操作:禁止换班的锁里不允许睡觉。这在板测中被实证——信用事件化尝试(见第二部分)改变了收发节奏后,某次传输的完成等待恰好没在快速路径内命中,任务带着锁进入睡眠等待,当场触发内核检查:
panicked at os/arceos/modules/axtask/src/api.rs:599:5:
sleeping or rescheduling is not allowed in atomic context: caller=os/arceos/modules/axruntime/src/wifi_glue.rs:98:22, reasons=[preempt_disabled], irq_enabled=true, preempt_count=1, cpu_id=0, task_id=Some(6)
(preempt_count=1 正是"持着一把抢占禁用锁"的痕迹。这不是新代码引入的错误路径——锁跨睡眠等待的写法一直存在,是重构把这条老路从合法变成了违规。任何改动收发时序的工作都可能踩中它。)
1.4 影响三:锁型选择被锁死
wifi 驱动的另一类锁(收发数据队列等"边界锁")是在关中断环境下被网络栈获取的(网络栈持锁穿越驱动收发链)。这使"睡眠锁"(获取时允许任务睡觉、但也因此在关中断下必炸)对边界锁物理上不可用——板测同样实证过一次启动 panic。
于是三条路里两条被堵死:睡眠锁不行(关中断下必炸)、抢占禁用锁代价沉重(1.2/1.3 的全部问题)。唯一同时满足"关中断下可获取、锁内可睡觉、不改变调度节奏"的语义,就是重构前的原始自旋锁。
1.5 根因确认
对照实验:仅把锁恢复为原始自旋语义、其余不动,上行 12.3 / 下行 11.1 / 双向 5.65+6.28、零告警——三方向全部回到变基前水平。重构改写锁语义是上述全部问题的根因。
二、信用事件化尝试,与 raw 修复后的表现
2.1 信用事件化是什么
wifi 芯片的发送受"信用"限制:芯片内有一块有限缓冲(信用 = 空闲单元数),发送前必须确认有足够空闲。旧实现里,信用不足时发送线程会空转 50 次读寄存器 + 让出 CPU,醒来后也没有人通知它"信用恢复了"——只能干等下一次定时巡检。
本次尝试把"信用恢复"变成事件:接收线程每排空一批数据后顺手看一眼信用值,够了就摇发送线程的铃。发送线程信用不足时不再空转,改为登记门铃后睡觉。这一步不新增任何定时兜底,是一次纯事件驱动的实验。
2.2 事件化首先踩中了 1.3 的炸弹
事件化改变了发送节奏(信用恢复当刻立即续写,不再有 50 次空转的间隔),某次传输的完成等待越过了快速路径,任务带着抢占禁用锁进入睡眠——触发 1.3 的 panic(第二次测试启动后)。这次意外实证了隐患的存在:重构后"持锁等传输完成"这条路是随时会炸的,任何时序变化都可能成为引信。
2.3 raw 修复后:三方向全量日志
将总线锁恢复为原始自旋语义后(改动三行锁类型声明,全部 69 处加锁调用点不变),板测一次跑完三方向、零异常:
上行(板卡为接收方):
[ ID] Interval Transfer Bitrate
[ 5] 0.00-1.00 sec 1.50 MBytes 12.6 Mbits/sec
[ 5] 1.00-2.01 sec 1.50 MBytes 12.5 Mbits/sec
[ 5] 2.01-3.00 sec 1.50 MBytes 12.6 Mbits/sec
[ 5] 3.00-4.00 sec 1.50 MBytes 12.6 Mbits/sec
[ 5] 4.00-5.00 sec 1.50 MBytes 12.6 Mbits/sec
[ 5] 5.00-6.00 sec 1.50 MBytes 12.6 Mbits/sec
[ 5] 6.00-7.00 sec 1.62 MBytes 13.7 Mbits/sec
[ 5] 7.00-8.00 sec 1.50 MBytes 12.6 Mbits/sec
[ 5] 8.00-9.00 sec 1.62 MBytes 13.6 Mbits/sec
[ 5] 9.00-10.00 sec 1.50 MBytes 12.6 Mbits/sec
[ ID] Interval Transfer Bitrate
[ 5] 0.00-10.02 sec 15.2 MBytes 12.8 Mbits/sec receiver
下行(板卡为发送方,Retr 全程为 0):
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 1.25 MBytes 10.5 Mbits/sec 0 0.00 Bytes
[ 5] 1.00-2.00 sec 1.25 MBytes 10.5 Mbits/sec 0 0.00 Bytes
[ 5] 2.00-3.00 sec 1.38 MBytes 11.5 Mbits/sec 0 0.00 Bytes
[ 5] 3.00-4.00 sec 1.25 MBytes 10.5 Mbits/sec 0 0.00 Bytes
[ 5] 4.00-5.00 sec 1.25 MBytes 10.5 Mbits/sec 0 0.00 Bytes
[ 5] 5.00-6.00 sec 1.38 MBytes 11.5 Mbits/sec 0 0.00 Bytes
[ 5] 6.00-7.00 sec 1.25 MBytes 10.5 Mbits/sec 0 0.00 Bytes
[ 5] 7.00-8.00 sec 1.25 MBytes 10.5 Mbits/sec 0 0.00 Bytes
[ 5] 8.00-9.00 sec 1.38 MBytes 11.5 Mbits/sec 0 0.00 Bytes
[ 5] 9.00-9.99 sec 1.25 MBytes 10.7 Mbits/sec 0 0.00 Bytes
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-9.99 sec 12.9 MBytes 10.8 Mbits/sec 0 sender
双向(RX-S 为板卡接收、TX-S 为板卡发送):
[ ID][Role] Interval Transfer Bitrate Retr Cwnd
[ 5][RX-S] 0.00-1.00 sec 512 KBytes 4.18 Mbits/sec
[ 8][TX-S] 0.00-1.00 sec 768 KBytes 6.27 Mbits/sec 0 0.00 Bytes
[ 5][RX-S] 1.00-2.00 sec 768 KBytes 6.30 Mbits/sec
[ 8][TX-S] 1.00-2.00 sec 640 KBytes 5.25 Mbits/sec 0 0.00 Bytes
[ 5][RX-S] 2.00-3.00 sec 768 KBytes 6.29 Mbits/sec
[ 8][TX-S] 2.00-3.00 sec 768 KBytes 6.29 Mbits/sec 0 0.00 Bytes
[ 5][RX-S] 3.00-4.00 sec 640 KBytes 5.23 Mbits/sec
[ 8][TX-S] 3.00-4.00 sec 768 KBytes 6.28 Mbits/sec 0 0.00 Bytes
[ 5][RX-S] 4.00-5.00 sec 768 KBytes 6.29 Mbits/sec
[ 8][TX-S] 4.00-5.00 sec 640 KBytes 5.24 Mbits/sec 0 0.00 Bytes
[ 5][RX-S] 5.00-6.00 sec 768 KBytes 6.29 Mbits/sec
[ 8][TX-S] 5.00-6.00 sec 768 KBytes 6.29 Mbits/sec 0 0.00 Bytes
[ 5][RX-S] 6.00-7.00 sec 640 KBytes 5.25 Mbits/sec
[ 8][TX-S] 6.00-7.00 sec 768 KBytes 6.30 Mbits/sec 0 0.00 Bytes
[ 5][RX-S] 7.00-8.00 sec 768 KBytes 6.28 Mbits/sec
[ 8][TX-S] 7.00-8.00 sec 768 KBytes 6.28 Mbits/sec 0 0.00 Bytes
[ 5][RX-S] 8.00-9.00 sec 768 KBytes 6.30 Mbits/sec
[ 8][TX-S] 8.00-9.00 sec 640 KBytes 5.24 Mbits/sec 0 0.00 Bytes
[ 5][RX-S] 9.00-10.00 sec 640 KBytes 5.23 Mbits/sec
[ 8][TX-S] 9.00-10.00 sec 768 KBytes 6.28 Mbits/sec 0 0.00 Bytes
[ ID][Role] Interval Transfer Bitrate Retr
[ 5][RX-S] 0.00-10.02 sec 6.88 MBytes 5.75 Mbits/sec receiver
[ 8][TX-S] 0.00-10.02 sec 7.12 MBytes 5.96 Mbits/sec 0 sender
2.4 对比与解读
| 方向 | 变基前基线 | 重构后 | raw 对照实验 | 本次(事件化 + raw) |
|---|---|---|---|---|
| 上行 | 11.2 / 12.0 | 9.29 / 9.61 | 12.3 | 12.8 |
| 下行 | 11.0 | 8.54(量化钉死) | 11.1 | 10.8 |
| 双向 | 5.37 / 5.81 | 3.23 / 3.34(3 次告警) | 5.65 / 6.28 | 5.75 / 5.96 |
| 异常 | 无 | 量化 + 流控告警 | 无 | 无(零 panic、零告警) |
- 上行与双向恢复到基线之上:事件化的收益(消除 50 次空转的总线/CPU 占用)叠加 raw 的基线恢复,上行 12.8 与双向 5.75/5.96 均为所有轮次最高;
- 量化现象消失:重构后下行"钉死在 1.00 MB/s 步进"的指纹在本次日志中完全不见(区间在 10.5-11.5 间正常波动)——这是信用事件化生效的直接证据,收发节奏重新由数据驱动;
- 下行 10.8 较基线(11.0-11.1)轻微回落约 2%:接收线程现在每排空一批数据多付一次信用检查(一次寄存器读、占用共享总线)。单次样本、幅度在波动范围内,不排除是噪声;若后续复测确认该回落稳定存在,可以按需把信用检查降频(例如每两批查一次),属可调参数而非结构问题;
- 零 panic、零告警:1.3 的契约炸弹被 raw 拆除,双向首秒的流控超时告警不再出现。
三、下一步:新原语的去留与其它优化
3.1 新原语处理:重做 vs wifi 局部 raw
现状:wifi 局部 raw 已实现(一个新模块 + 三行锁类型声明,69 处调用点不变),板测三方向全部达到或超过变基前基线、零异常。安全论证完整:单核运行;中断处理路径从不取这些锁;关中断环境下获取的边界锁与"锁内睡觉"的总线锁都不改变运行环境,恰是 raw 的合法用途。
重做(结构修复:锁不跨睡眠持有)的价值与成本:它从根上消除"持锁睡觉"的契约问题,让锁型选择重新开放(届时睡眠锁对总线锁也重新可用);但需要拆分 SDIO 传输接口为"发起/等待"两段、配套完成归属协议与中断侧改造,改动面是 raw 的数十倍,且单独不解决性能全恢复(每操作一次的"禁止换班"开销仍在)。它是把锁型从"被迫选择"变成"自由选择"的地基,但不是当前必须。
建议:当前以局部 raw 落地为交付形态(改动小、证据硬、安全论证完整);结构修复列为后续演进项。若后续主线有调整(例如上层不再关中断穿越驱动链、或提供新的锁语义),届时锁型可按板测数据重选,raw 模块是单点可替换的隔离层。
3.2 其它优化探索(按证据与依赖排序)
- 发送聚合:厂商驱动的核心流水线机制——把信用值当聚合预算,一次总线写入携带多帧,摊薄每次写入的固定开销。既往优化报告将其估为约 10% 的差距项,与本事件化正交可叠加;
- 既往报告的独立补项:SDIO 总线 25MHz→50MHz 并配置 PHY 延迟(报告中下行 +96% 的独立杠杆);兜底巡检周期 10ms→1ms(报告中 +20%,事件化覆盖后影响力下降,可作保险);
- 异步化深化:传输完成事件令牌(配合结构修复)、兜底巡检降级为纯安全网并加触发计数(触发趋零即验证事件覆盖完备);
- 前提验证:芯片缓冲池是否收发共享(一次板测:发送阻塞时读信用值 → 无发送写入时制造一次接收排水 → 再读信用值)。它决定"接收排水通知发送"这条事件链的收益上限,也决定后续优化该往哪个方向投。