Trap 21 触发模型修订为三因子(2×2 台架实证);新增 Trap 22 快速重连监听死亡与 N-6 待办、604 复现/对照规则
This commit is contained in:
+2
-1
@@ -9,9 +9,10 @@
|
||||
|
||||
| # | 事项 | 发现 | 证据与现状 | 出处 |
|
||||
|---|---|---|---|---|
|
||||
| N-1 | **TC-NET-604:芯片"缓冲恰好填满 + 排空"后 RX 窗口冻结(根因已确认,代码侧已闭环)**——同连接前序突发(64B×10+1460B)后 4096B 突发:前 2KB 正常回显,其余永不被芯片确认/交付,停摆 10s、无 RST、缓冲全程空、窗口不更新;断开重连后恢复正常(台架 10/10 复现,F1 组合) | 2026-09-22 17:00/17:03(L3 台架 F1 组合) | **根因 = CH395F 片内 TCP 状态机角(WCH 官方 FAQ 留档的已知局限,W5500 同类),非我方代码缺陷**:① 驱动"超限读静默丢弃"排除(DMA 缓冲 2052B 可容整缓冲,失败运行 0 次丢弃事件)② L3 排除(A/B 逐字节同现)③ "RX>2×MSS" 裕量假设**证伪**——MSS=512 实验原序列 10/10 仍停摆,且新连接 4096B 确定性停摆恒剩 64B(已回退,维持 MSS=1024:新连接 4096B 31ms 正常)。复现探针(本地未跟踪工件,MDK-ARM/ 下):完整序列 = `test/ch395f_socket_test.py tcp_client --ip 192.168.1.100 --port 8080 --count 10`(F1 组合构建);独立 4096B = `MDK-ARM/probe_4096.py`。代码侧闭环(已提交):缓冲块布局与 MSS 解耦 + 超限读 OVERSIZE 金丝雀 + 注释留痕。**残留决策项**:冻结不自恢复(唯一恢复 = 断连 + 客户端重试,重连路径台架已验证);lftpd 数据通道停摆预算现为 30s/读(`LFTPD_TOTAL_BUDGET_MS`,与控制行共用)——是否将数据通道单独缩短至 10–15s 以压缩停摆暴露(代价:慢速健康传输亦被中断;机器客户端不受影响)待用户决策 | `CH395F_Trap_Records.md` Trap 21;`错误码传播审计.md` E-02;`链路延迟测试说明.md` §6.3 |
|
||||
| N-1 | **TC-NET-604:芯片"缓冲恰好填满 + 排空"后 RX 窗口冻结(根因已确认,代码侧已闭环)**——同连接前序突发(64B×10+1460B)后 4096B 突发:前 2KB 正常回显,其余永不被芯片确认/交付,停摆 10s、无 RST、缓冲全程空、窗口不更新;断开重连后恢复正常(台架 10/10 复现,F1 组合) | 2026-09-22 17:00/17:03(L3 台架 F1 组合) | **根因 = CH395F 片内 TCP 状态机角(WCH 官方 FAQ 留档的已知局限,W5500 同类),非我方代码缺陷**:① 驱动"超限读静默丢弃"排除(DMA 缓冲 2052B 可容整缓冲,失败运行 0 次丢弃事件)② L3 排除(A/B 逐字节同现)③ "RX>2×MSS" 裕量假设**证伪**——MSS=512 实验原序列 10/10 仍停摆,且新连接 4096B 确定性停摆恒剩 64B(已回退,维持 MSS=1024:新连接 4096B 31ms 正常)。复现探针(本地未跟踪工件,MDK-ARM/ 下):完整序列 = `test/ch395f_socket_test.py tcp_client --ip 192.168.1.100 --port 8080 --count 10`(F1 组合构建);独立 4096B = `MDK-ARM/probe_4096.py`。代码侧闭环(已提交):缓冲块布局与 MSS 解耦 + 超限读 OVERSIZE 金丝雀 + 注释留痕。**2026-09-22 二次 2×2 台架将触发模型细化为三因子**(MCU 侧负载 + 前序突发 + 超容突发,缺一不触发——轻载 Phase 6-only 构建即使带前序突发也 10/10 全过)→ 生产暴露面 = 数据连接带前序部分传输 + 系统有并发任务负载时的超容突发;同台架副产品见 N-6。**残留决策项**:冻结不自恢复(唯一恢复 = 断连 + 客户端重试,重连路径台架已验证);lftpd 数据通道停摆预算现为 30s/读(`LFTPD_TOTAL_BUDGET_MS`,与控制行共用)——是否将数据通道单独缩短至 10–15s 以压缩停摆暴露(代价:慢速健康传输亦被中断;机器客户端不受影响)待用户决策 | `CH395F_Trap_Records.md` Trap 21;`错误码传播审计.md` E-02;`链路延迟测试说明.md` §6.3 |
|
||||
| N-2 | **49s 全系统日志静默(defaultTask/NAND syslog 排空停滞)**——串口日志 49s 无行(testTask 10s 周期行/netTask 事件行/[STACK] 行全缺),期间 net 栈照常处理外部连接(FTP banner 92ms)→ 日志管线(defaultTask 排空 → NAND+UART)停滞,队列满后各任务日志行被丢;系统功能不受损 | 2026-09-22 17:23:52~17:24:41(L3 台架 F2 运行中,gen 181) | **A/B 归因:L3 不触碰 defaultTask/syslog 路径 → 既有存储侧现象**。待办:① 复现条件定位(NAND 写/GC 长尾 vs UART TX 卡滞——对照 `CH395F` 无关的 SD2506 侧日志)② 日志队列满的丢弃计数/告警(现静默丢行,排障不可见)③ 与 S8(非阻塞日志)合并评估 | `错误码传播审计.md` E-02 阶段二"台架新发现②";`链路延迟测试说明.md` §6.7 注 |
|
||||
| N-3 | **L5 队列往返基线(1223/988µs)的 L3 后重验**——L5 测 DONTWAIT 队列全程;L3 对该路径逐位不变、pending tick 无 pending 时为 µs 级指针读 → 机制上无影响,但基线数值属分片时代,严格重锁需跑一轮 Phase 13(需 PC peer,窗口与 N-1 定位可并案) | 2026-09-22(L3 收尾留档) | 低风险:10ms/100ms 阈值余量 >8×;下次 Phase 13 窗口顺带重锁即可 | `链路延迟测试说明.md` §6.4 L5 注¹ |
|
||||
| N-6 | **Trap 22:快速重连致 CONNECT 中断丢失 → 单连接监听静默死亡(P1,修复待批准)**——轻负载 + 重监听历史 + 断连后 <~1s 重连:芯片握手正常但 MCU 无 standalone accept(状态滞留 LISTENING)→ `net_send` 全失败(-1)→ 断开时 DISCONNECT 落 LISTENING 分支仅告警 → 芯片自动关闭 Socket → 端口永久拒连(不自恢复) | 2026-09-22(Trap 21 二次台架 A 组 run2;出现 1/1,未做 10/10 规模复验) | 修复方案:`handle_disconnect_event` LISTENING 分支回读芯片 socket 状态,非可监听即 `net_relisten_try` 重建(D20 的 LISTENING 分支姊妹情形)+ 可选:重监听后清残留中断位;应用侧规避:单连接 socket 断连后重连 ≥1~2s 退避(lftpd 快速 PASV / 客户端快速重试是生产风险场景)。生产影响:数据通道监听静默死亡 → 后续数据连接全拒,严重度等同端口失效 | `CH395F_Trap_Records.md` Trap 22 |
|
||||
|
||||
## 存储 / 日志管线
|
||||
|
||||
|
||||
@@ -643,6 +643,12 @@ sequenceDiagram
|
||||
**通过标准**:4096B 完全匹配。任一失败 → 标记 FAIL
|
||||
**覆盖原则**:DP-04(2KB 接收缓冲边界 + 单次 DMA 上限) |
|
||||
|
||||
> **TC-NET-604 复现/对照规则(2026-09-22 二次 2×2 台架,三因子模型见 Trap 21)**:
|
||||
> - **复现(预期 10/10 停摆)**:Phase 6/9/11 构建 + `tcp_client` 全序列——负载是触发因子,必须用 6/9/11 组合构建;
|
||||
> - **对照(预期 10/10 全过)**:Phase 6-only 构建 + `tcp_client` 每轮间隔 ≥15s(轻载不触发);
|
||||
> - **Phase 6-only 构建禁止无间隔连跑**:<~1s 重连间隔会触发 Trap 22 失同步(自 run2 起:无 standalone accept、`send -1`、其后全部 connection refused)——快速重连场景须用 6/9/11 构建或留 15s 间隔;
|
||||
> - **MSS/块配额改动前**:先跑 6/9/11 复现序列(基线 = 10/10 停摆,为芯片侧已知角,改动后不得恶化/不得出现新停摆模式)+ 新连接独立 4096B 对照(必须过,MSS=512 实验在此失败被回退)+ Phase 6-only 15s 对照(必须过)。
|
||||
|
||||
### TC-NET-605: 断开重连
|
||||
|
||||
| 字段 | 值 |
|
||||
|
||||
@@ -540,17 +540,26 @@ NET 层 TCP Client(阶段 7)`net_connect()` 返回 0,但 `net_poll()` 报
|
||||
## Trap 21:接收缓冲"恰好填满 + 排空"窗口冻结(TC-NET-604)
|
||||
|
||||
### 现象
|
||||
单条 TCP 连接在同连接前序突发(64B×10 + 1460B)之后,再发 >2KB 突发(4096B):前 ~2KB 正常回显,其余数据芯片不再接收/确认,对端重传 10 秒无效;连接不 RST(芯片自身发送侧干净,重传耗尽拆连逻辑不触发),芯片接收缓冲全程为空(无 RECV_OK 事件),窗口值不再更新——"静默冻结"。对端断开重连(新 Socket)后恢复正常。复现:F1 测试构建(Phase 6/9/11)+ `test/ch395f_socket_test.py tcp_client` 全序列,10/10(2026-09-22 台架)。
|
||||
单条 TCP 连接在同连接前序突发(64B×10 + 1460B)之后,再发 >2KB 突发(4096B):前 ~2KB 正常回显,其余数据芯片不再接收/确认,对端重传 10 秒无效;连接不 RST(芯片自身发送侧干净,重传耗尽拆连逻辑不触发),芯片接收缓冲全程为空(无 RECV_OK 事件),窗口值不再更新——"静默冻结"。对端断开重连(新 Socket)后恢复正常。复现:F1 测试构建(Phase 6/9/11)+ `test/ch395f_socket_test.py tcp_client` 全序列,10/10(2026-09-22 台架,二次 2×2 台架复核再 10/10;三因子模型见下)。
|
||||
|
||||
### 根因
|
||||
CH395F 片内 TCP 状态机的角(芯片固件,MCU 侧无法修补),与"窗口 = 接收缓冲剩余空间"的硬连线模型共同构成触发结构:
|
||||
|
||||
1. **结构性前提**:窗口=剩余空间(手册 L1281)→ 守规矩的对端永远把窗口灌满 → 每个稳态轮次缓冲**恰好填满**(2048/2048,窗口=0)是结构性必达状态,无法通过几何裕量规避。
|
||||
2. **冻结机制**:缓冲恰好填满的状态与"在途段接收 / 窗口重通告 / MCU 排空"时序交叠时,芯片接收路径状态机卡死——停止对后续段的确认与接收(数据滞留线上、对端重传),缓冲保持空、窗口不更新、连接保持。WCH 在 CH395 系列官方 FAQ 中留档了同类现象:"接收缓冲区需要大于 2 倍设置的 MSS 值,否则……若对端设备不会根据 tcp 窗口来分包发送数据时,有可能会造成 CH395 收不到数据且 TCP 窗口不更新现象"(www.cnblogs.com/sw111/p/18750601,2025-03)。WIZnet W5500(同类:每 Socket 小型缓冲 + 片内 TCP + 窗口=剩余空间)官方文档同为"缓冲满则挂起连接等待空闲空间"模型,zero-window 停摆是该类芯片的经典病理(docs.wiznet.io W5500 TCP Application 文档;Wireshark 论坛 49849 实例)。
|
||||
3. **触发模式(实测,均为确定性而非随机竞态)**:
|
||||
- MSS=1024:新连接 4096B 突发(4×1024 段)正常(31ms 全通);仅"同连接前序突发 + 新突发 + 任务负载(netTask 队列竞争)"模式触发(10/10)。
|
||||
- MSS=512:新连接 4096B 突发(8×512 段,4 段装不进 2KB 缓冲)**确定性停摆**,恒剩 64B 未达(4032/4096,2/2 同值)——满周期内段数越多越易触发,且一旦触发必现。
|
||||
- 单轮无溢出突发(≤2KB,如 1460B)在任何几何下均正常。
|
||||
3. **触发模式(2026-09-22 二次台架 2×2 复核,均为确定性而非随机竞态)**:
|
||||
- **三因子模型(MSS=1024 基线)**:① MCU 侧负载(testTask/netTask 活动使缓冲排空滞后)+ ② 同连接前序突发(缓冲经前序填/排周期预置)+ ③ 后续突发超缓冲容量(4096B > 2KB,需 ≥2 个填周期)——**三者齐备才触发,缺一不触发**(socket 新旧/历史无关,开机后首条连接在重负载下同样触发):
|
||||
|
||||
| 构建(负载) | PC 序列 | 运行间隔 | 结果 |
|
||||
|---|---|---|---|
|
||||
| Phase 6/9/11(重) | 64B×10+1460B+4096B | ~12s | **10/10 停摆**(4096B 步:前 2048B 回显正常,余停) |
|
||||
| Phase 6 only(轻) | 同上 | ~15s | **10/10 全过**(1460B/4096B 完全匹配) |
|
||||
| Phase 6 only(轻) | 同上 | 600ms 连续 | run1 全过;run2 起转 **Trap 22 失同步模式**(非本条冻结) |
|
||||
| 重负载、无前序突发 | 新连接独立 4096B | — | 过(31ms;probe_mss 对照) |
|
||||
|
||||
"负载"= testTask 每 tick 的 net_* 调用密度(Phase 9/11 的 select/recv/串口日志使排空节拍变稀);旧"队列竞争"表述作废(net_select 不产生队列消息,只读 RAM 状态)。
|
||||
- MSS=512:新连接 4096B 突发(8×512 段,4 段装不进 2KB 缓冲)**确定性停摆**,恒剩 64B 未达(4032/4096,2/2 同值)——段密度维度:满周期内段数越多越易触发,且一旦触发必现(该实验在重负载构建上执行,与三因子模型自洽)。
|
||||
- 单轮无溢出突发(≤2KB,如 1460B)在任何几何/负载下均正常。
|
||||
4. **排除项**:① 驱动层"超限读静默丢弃"排除——SPI DMA 缓冲 = 2×MSS+4 = 2052B(载荷 2048 = 整个接收缓冲),整缓冲读单事务完成,失败运行中丢弃事件 0 次(Trap 13 所记 1500B DMA 缓冲为 8-Socket 改造前旧值,改造后扩至 2052B);② L3 pending 化排除(切片构建 A/B 行为逐字节一致);③ "RX > 2×MSS"裕量假设排除——MSS 1024→512 实验(2KB=4×MSS、1KB 裕量)既未消除原复现序列的停摆(10/10),又新增新连接停摆 → 已回退 1024。
|
||||
|
||||
### 解决(2026-09-22)
|
||||
@@ -562,7 +571,8 @@ CH395F 片内 TCP 状态机的角(芯片固件,MCU 侧无法修补),与"
|
||||
4. **生产缓解(决策项,未实施)**:冻结不自恢复(对端重传 10s 无效;芯片无限期保持连接)。唯一恢复 = 断开受影响连接(释放芯片 per-socket 状态)+ 对端重试(新连接成功率高;台架实测停摆后重连回显正常)。现 lftpd 数据路径预算 `LFTPD_TOTAL_BUDGET_MS=30000`(每次读 30s)即实际停摆超时——若将**数据通道**预算单独缩短(10–15s,与控制行预算分离),可把停摆暴露时间从 30s 压缩到客户端重试路径;代价 = 慢速但健康的传输也会被中断(机器客户端不受影响,人工客户端受影响)→ 已列 `docs/待处理内容.md` N-1 待决策。
|
||||
|
||||
### 注意事项
|
||||
- 复现模式 = "前序突发 + 新突发 + 任务负载";生产中同数据连接带前序部分传输的 FTP STOR/RETR 是主要风险场景;纯新连接大突发(4×1024)在 MSS=1024 下不触发。
|
||||
- 复现模式 = 三因子齐备(负载 + 前序突发 + 超容突发,见触发模式表):**台架复现 TC-NET-604 必须用 Phase 6/9/11 构建**(轻载 Phase 6-only 构建不触发,10/10 全过);生产中同数据连接带前序部分传输的 FTP STOR/RETR(叠加并发任务负载时)是主要风险场景;纯新连接大突发(4×1024)在 MSS=1024 下不触发。
|
||||
- 与 Trap 22 的鉴别(两者都可在断连/重监听后出现):本条 = RX 路径冻结(有 standalone accept、前段回显正常、其后 RECV_OK 永不再来、无 send 失败);Trap 22 = 连接状态失同步(缺 standalone accept + `send -1` + `WARNING: listen` + 监听死亡)。
|
||||
- 冻结是静默的:无 RST、无芯片中断、MCU 侧只见"缓冲空 + 无 RECV_OK + 连接存活"(与正常空闲完全同貌)——MCU 无直接信号,只能靠应用层数据预算(lftpd 30s / 客户端自身超时)发现。
|
||||
- E-02 审计"STOR 方向缓冲满 + 重传耗尽 → 必断"的预警方向成立(STOR 方向 = 致命),机制修正为本条 RX 冻结(非 RST 拆连)。
|
||||
- WCH FAQ"发送缓冲必须大于 MSS"为严格不等式;本设计发送缓冲 = MSS(1KB = 1024)取等值——台架实测芯片在缓冲边界可正常成包(L3/L8 基线在 1KB 分块写入下全部正常),该条按保守表述处理,非运行约束。
|
||||
@@ -573,3 +583,26 @@ CH395F 片内 TCP 状态机的角(芯片固件,MCU 侧无法修补),与"
|
||||
|
||||
### 发现时间
|
||||
2026-09-22(TC-NET-604;根因确认、MSS=512 证伪与回退、布局解耦同日完成,提交于 wip/v2-pending-recv 分支)。
|
||||
|
||||
## Trap 22:重监听后快速重连丢失 CONNECT 中断 → 监听静默死亡
|
||||
|
||||
### 现象
|
||||
轻负载构建(Phase 6 only)+ 带历史的重监听 socket:上一条连接关闭后约 600ms 到达的第二条连接,芯片侧硬件完成 TCP 握手(PC 连接成功、数据可流入),但 MCU 侧**无 `standalone accept`**(socket 控制块滞留 LISTENING)→ `net_send` 全部被状态机拒绝(`echo: send -1 != recv N`),PC 侧回显 0 字节;接收路径仍可读出数据(RECV_OK 正常、缓冲可排空)。断开时 DISCONNECT 中断命中 `handle_disconnect_event` 的 LISTENING 分支 → 仅记 `WARNING: listen sockN got disconnect` 而无任何处置 → 芯片在 DISCONNECT 后**自动关闭 Socket**(FUN_PARA 默认行为)→ MCU 未再重开 → **监听静默死亡**:此后所有 SYN 被拒(connection refused),不自恢复(仅复位/网络重建可恢复)。
|
||||
复现:Phase 6-only 构建 + `test/ch395f_socket_test.py tcp_client` 无间隔连续运行 → run1 全过、run2 失同步、run3 起全部 connection refused(2026-09-22 台架 A 组;出现 1/1,未做 10/10 规模复验)。
|
||||
|
||||
### 机制(实证边界;芯片侧微观机理未解)
|
||||
1. 芯片硬件完成握手但 CONNECT 中断未上报(或 MCU 侧丢失)→ `handle_connect_event` 未执行 → 状态滞留 LISTENING。
|
||||
2. **实证边界(2×2 台架)**:失同步仅现于"轻负载 + 重监听历史 + 快速重连(~600ms)"组合;重负载(Phase 6/9/11)下断连后 ~500ms 重连仍同步正常(B' 组 20/20 standalone accept、10/10 全连接成功);轻负载 + 15s 重连间隔亦全正常(A' 组 20/20)。即中断丢失窗口 ≈ "close → 重监听 → SYN" 间隔 < ~1s 且 netTask 相对空闲。芯片侧丢中断的具体原因未解(列为后续观察项,不阻塞下述修复)。
|
||||
3. 后果链确定:状态滞留 LISTENING → `net_send` 被拒(send -1)→ 断开时 DISCONNECT 落 LISTENING 分支(现有代码仅告警)→ 芯片自动关闭 Socket → MCU 不再重开 → 监听死亡。
|
||||
4. 与 Trap 21 的关系:**独立失效模式**——Trap 21 = RX 路径冻结(缓冲空、无 RECV_OK、窗口冻结、连接状态正常);本条 = 连接状态失同步(数据可流但发送被拒,且可致监听死亡)。日志签名鉴别:Trap 21 无 standalone accept 缺失、无 send -1;本条三者齐现(缺 standalone accept + `send -1` + `WARNING: listen`)。
|
||||
|
||||
### 修复(方案,未实施 → N-6)
|
||||
1. `handle_disconnect_event` LISTENING 分支:回读芯片 socket 状态,非可监听状态(即芯片已自动关闭)时执行 `net_relisten_try` 重建(重 OPEN + TCP_LISTEN)——D20 陈旧事件修复的 LISTENING 分支姊妹情形(D20 只处理 ESTABLISHED 分支)。
|
||||
2. 可选加固:重监听完成后清除该 socket 残留中断位(`handle_connect_event` 已有同模式),防陈旧位致下一次事件误判。
|
||||
3. 应用侧规避(可立即执行):单连接 socket 断连后重连留 ≥1~2s 退避——客户端快速重试 / lftpd 快速 PASV 序列是生产主要风险场景。
|
||||
|
||||
### 对应文件
|
||||
`Drivers/BSP/NET/net_socket.c`(handle_disconnect_event LISTENING 分支);`test/net_test_task.c`(复现:Phase 6-only 构建 + 快速 tcp_client);`docs/待处理内容.md` N-6(修复待办登记)。
|
||||
|
||||
### 发现时间
|
||||
2026-09-22(Trap 21 二次 2×2 复核台架副产品,A 组 run2)。
|
||||
|
||||
Reference in New Issue
Block a user