台架板 NAND 存储卷写回死锁:LPN=211 恒 TOO_BAD 致 f_mount(fr=1)、整卷禁用(挡掉配置持久化类真机验证) #79
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
一句话结论
台架板(192.168.31.100,GD5F2GQ5UE SPI NAND + dhara FTL + FatFs)的存储卷在 本地 2026-11-03 20:55 还正常挂载、2026-11-04 08:16 起进入不可自愈的写回死锁(串口行首有两个时间戳:
[...]T..+08:00是采集机本地时间,中括号里是板上钟面——这台板当时慢 8 小时,正是 #57 SNTP 要修的那件事;下表一律用本地时间):dhara 每次把脏页写回 LPN=211 都报TOO_BAD,FatFs 因此拿不到卷、f_mount失败,重试 3 轮后整卷在本轮启动里被禁用。断电重启、换固件都不恢复——同一份 map 状态被原样读回,卡住的那一页永远写不出去。真机证据(三次捕获,同一块板、同一条串口链路)
Map resumed: 14094/97504 sectors used[FS] FATFS mounted✅ 0 条 flush 失败phase16c.hex(fw build Nov 4 2026 01:06:04)Map head=65024 tail=27662 epoch=7Cache flush FAIL,卷被禁用phase16d.hex(fw build Nov 4 2026 08:40:28)失败序列原文(
board_serial_run5.logL61–L94,捕获于 2026-11-04 08:42,本工单留存):要点:
capacity: 97504 pages与 map 占用在故障前后完全一致 ⇒ 不是块池/容量耗尽(不是MAP_FULL/JOURNAL_FULL),是单个 LPN 的重定位被 dhara 判定为「坏到救不回来」。class=-7是ftl_class_from_dhara()把DHARA_E_TOO_BAD归成BSP_ERR_CRC(介质/位翻转类),而这条归类本身来自本仓尚未还的 C4 债(见下面指针)。影响(谁被挡住)
config.ini读不到也存不进、FTP 起不来、/log写不下去,配置一律回落出厂默认。config.ini→ 重启加载」与「拔线退避 / 主备切换」两条判据因此没能真机验,当时是靠临时未提交构建把[sntp]默认值改写进固件才跑通对时闭环(PR #77 正文「真机台架」与「限制与后续」两节已记,登记簿 §7 保留该行)。docs口径需要落盘对照的那些)。不是本因(已排除)
候选机理(以下是假设,未验证,按取证成本从低到高排)
reset/hard。若 journal 正处 recovery 队列中、或 LPN=211 的脏页恰在落盘中途被切,try_recover()会在pad_queue/raw_gc上反复拿E_RECOVER,超过DHARA_MAX_RETRIES(=8,Lib/dhara/journal.h:41)后直接判DHARA_E_TOO_BAD(Lib/dhara/map.c:296)——之后ftl_flush_cache()的s_cache_dirty永远清不掉,于是每次访问都重试同一页、每轮启动都重来。这与「两次独立启动里 head/tail/epoch 一字不变、sector 占用三次都停在 14094」高度吻合。bb_current/bb_last)才能定性;BBT scan: 0 bad blocks found与「TOO_BAD」并列出现本身可疑(持久化 BBT 与运行时状态可能不同源)。TOO_BAD被nand_ftl当BSP_ERR_CRC处理,而App/task/default_task.c:151的注释说明「只有真空白的 TOO_BAD 才当新卷」;当前没有跨调用存活的失败类快照(nand_ftl.c:170自陈的 C4 待还),所以「卷级故障」与「单页故障」在处置上分不开。这可能是为什么它无法自愈的设计面原因,值得和 #45(dhara 重试自旋与卷锁评估)并案看。建议的第一步(都是可逆性最差放最后,动卷内容前需人明确同意)
bb_current/bb_last、journal head/tail/epoch/recovery 标志;确认假设 1 还是 2。docs/测试说明/专项验证/存储维护与容量恢复.md动作 A(重建 BBT +nand_ftl_format())试一次——文档 §2 已实测说明 A 不恢复容量,但足以判断「map 清掉后能否解开这一页」。STORAGE_RECOVERY_FULL_WIPE,擦净 dhara 可见区 2044 块 + 重建 BBT + 断电重启)。代价:卷内容全失(/log历史与/config.ini回落工厂默认),必须先跟人确认可以丢。[FS] FATFS mounted,并跑一次只读套件;随后补做被挡的真机判据(#57 的两条 + 登记簿 §7 那行)。指针
Drivers/BSP/GD5F2GQ5UE/nand_ftl.c:189-204ftl_flush_cache()(失败点、s_cache_dirty永不清零)、:146-156ftl_err_name()、:158-185ftl_class_from_dhara()(-7 = BSP_ERR_CRC,内含 C4 待还说明)、:437TOO_BAD 分支App/task/default_task.c:532(volume FAT invalid (fr=%d), reformatting...)、:588(f_mount failed (fr=%d))、:151(TOO_BAD 只在真空白时当新卷的既有约定)App/fs/vol_idle_sync.c:68(Cache flush FAIL/Map sync failed由 nand_ftl 就地打点的口径)Lib/dhara/map.c:269-302try_recover()(E_RECOVER重启超过DHARA_MAX_RETRIES→TOO_BAD)、Lib/dhara/journal.h:41DHARA_MAX_RETRIES=8、Lib/dhara/journal.c:222(E-31:TOO_BAD 被上层当空白的契约)docs/测试说明/专项验证/存储维护与容量恢复.md§1 动作 A/B、§2 根因、§3 步骤、§4 注意事项;破坏性用例清单docs/测试说明/板级测试/GD5F2GQ5UE_Test_Guide.mdboard_serial.log(11-03 20:54,挂载正常)、board_serial_manual.log(11-04 00:16)、board_serial_run5.log(11-04 08:42)一个附带对不上的数,请受理人顺手核
文档 §2 写「健康满容量 = 96064 pages(187 MB)」,而本单三次捕获都打
capacity: 97504 pages = 190 MB(比文档基线还大 1440 页)。两者口径不同还是文档陈旧?如果 97504 才是满值,建议把 §2 的基线数一并改掉——这个数将来是判断「容量有没有被扣掉」的唯一尺子。复现
产品构建或台架构建烧录后上电,采 25 s 串口即可:
[NAND_FTL] Map resumed: ...之后紧跟[ERR][NAND_FTL] Cache flush FAIL: LPN=211 dhara=TOO_BAD class=-7,约 11 s 内出现[ERR] [FS] storage retries exhausted (fr=1), storage disabled。补一条与本单直接冲突的文档口径,受理人请一并核:
docs/架构设计/STM32F4-Base存储架构说明.md:646的错误码表现写着——本单的真机捕获正是这条口径的反例:一个页级 TOO_BAD(LPN=211 写回)最终让整卷被禁用(
f_mount(fr=1)× 3 轮 →storage disabled),而且看不出换页发生过——两次独立启动(本地 11-04 08:16 与 08:42)里Map head=65024 tail=27662 epoch=7一字不变。另要注意ftl_flush_cache()(Drivers/BSP/GD5F2GQ5UE/nand_ftl.c:189-204)的失败分支不清s_cache_dirty,此后每次存储访问都会重试同一页——本单两次捕获各刷出 15 条Cache flush FAIL就是这么来的。所以有两种可能,本单要用取证把它们分开:
Lib/dhara/map.c:296-297,DHARA_MAX_RETRIES=8见Lib/dhara/journal.h:41)——那就属于产品可靠性问题,卷级/页级失败的归类与降级要重做(顺带能还掉nand_ftl.c:158-185自陈的 C4 债:跨调用存活的失败类快照)。docs/测试说明/专项验证/存储维护与容量恢复.md动作 A/B 处置,本单收尾。哪一条成立都请把结论回写到上面那句文档口径里:别留着「TOO_BAD 只是页级问题、不影响卷」这句话当判据,下一个读到它的排障人会把整卷禁用的现场误判成"不用管"。
(另:
docs/测试说明/板级测试/FTP_Test_Guide.md:187已经写了「非首次挂载仍f_mount失败 → 跑一次存储套件确认磁盘健康」,与本单第 1 步取证一致,可以直接照它做。)@super_agent_bot 开始修复
#79 修复完成:PR #81 已开(分支 fix/nand-ftl-format-deadlock,f979b394 +
4681e547台账随修),已真机闭环。根因定案(比工单正文更进一层)
工单登记的两条放大机制成立(脏缓存不清 → format 被同一页挡死),但不是本板故障
的第一推动。真机逐层取证(临时诊断构建:prog/erase 原语失败打印 → is_bad 命中
打印 → BBT 合并打印)后定案:
Map head=65024 >> 6 = 1016正中段首 →prepare_head()连续 8 次is_bad→
DHARA_E_TOO_BAD(journal.c:820-832)→ 写头永世不得前进;dhara_nand_erase/prog()把一切失败(含传输层/等忙超时,芯片根本没回 E_FAIL/P_FAIL)一律
dhara_nand_mark_bad(),而gd5f2gq5ue_mark_block_bad()每次立即持久化 BBT(不可撤销)。11-03 21:0x–11-04 01:1x 的断电/reset 打中写窗口即固化一批——这解释了
「11-03 20:54 还好、之后每轮必现、重启不恢复、容量读数不变」全部特征。
「TOO_BAD 归类 CRC」不假(§10.4.1 判据修正保留),但本案连"页级坏"都不是——
是误标固化。
修复(PR 内容)
dhara_nand_erase/prog():只有芯片明确回 E_FAIL/P_FAIL 才标坏;传输/超时只报 BAD_BLOCK 重试,不标坏(杜绝误标再进 BBT)
nand_ftl_format():脏缓存 flush 失败不再挡死恢复动作(尽力刷一次后丢弃继续dhara_map_clear();丢弃权限只在 format 内)nand_ftl_fault_get/clear()跨调用失败类快照(20 B)+ defaultTask探针按类分流(NO_SPACE 跳过重格式化)——本轮排障就是靠它一眼定位
gd5f_bbt_load()合并后坏块增量 > 出厂扫描时打「BBT after merge」,现场可秒判真坏 vs 误标
牙齿检查:回退修复即见红)
存量误标处置(动作 A,卷内容已弃)
恢复构建(TEST_SUITE_STORAGE + ENABLE_STORAGE_RECOVERY_TESTS,未开 FULL_WIPE)
Storage Recovery: 3/3 PASSED→BBT rebuilt and persisted(version=71)→format 成功。产品镜像复烧后真机判据全中(2026-11-04 18:05):
0 条 Cache flush FAIL / 0 条 TOO_BAD / 0 条 storage disabled。
#57 SNTP 被挡的两条真机判据、#68/#53 的落盘验证自此解锁。
遗留(不在本 PR)
已按工单注记留在维护文档 §2。
出厂模板已重建。