/* * 模块名称:Volume Idle Sync(写回缓存空闲 flush)— MCU 实现 * 模块功能:就绪门控 + 周期节流 + 卷锁 try + disk_ioctl(CTRL_SYNC) 的兜底 * 状态机(设计/使用约定/边界见 vol_idle_sync.h) * 适用平台:STM32F407ZGTx + FreeRTOS * 作者:DTU 项目组 * 创建日期:2026-10-25 * 修改记录: * v1.0 2026-10-25 DTU 项目组 创建(道F 批 2:写回缓存空闲 flush 收口) */ /* 头文件包含区 */ #include "vol_idle_sync.h" #include "test_config.h" #if !defined(TEST_SUITE_STORAGE) #include "cmsis_os.h" #include "FreeRTOS.h" #include "task.h" #include "ff.h" /* FatFs 基础类型(diskio.h 依赖 BYTE/LBA_t 等,须先纳) */ #include "diskio.h" /* disk_ioctl / CTRL_SYNC / DRESULT */ #include "vol_lock.h" #include "sys_events.h" /* 本模块日志仅一条失败留痕(≤60 字符),默认行缓冲即可,不调 DBG_LINE_BUF_MAX */ #include "dbg_log.h" /* Private defines -----------------------------------------------------------*/ /* 空闲同步检查周期(= 漏网脏页的最大掉电驻留窗,另加一个主循环拍 ≈50ms)。 * 取值依据:clean 态为"脏标志 + journal"双短路(零 NAND 触点),高频不伤介质 * 只是白跑;脏态最多一次页编程 + journal 收账,与 f_close 同型,但落在卷 * 已空闲的窗口、远离活跃写会话。5 s 与主循环其余慢观测(栈/堆水位 2 s)同 * 量级而更稀疏——本模块是兜底网,不是持久性主路径(主路径 = close 内同步)。 * 锁忙(卷不空闲)同样按本周期重试:不做欠账补偿,节奏恒定好推理。 */ #define VOL_IDLE_SYNC_PERIOD_MS 5000u /* 静态变量定义区 */ /* 状态量放文件级 static(非函数内):主机侧白盒单测(test/host/vol_idle_sync/) * 以 #include 纳入本文件后需要复位它们;命名带 vol 前缀避免与测试桩撞名 */ static uint32_t s_vol_sync_last_ms; /* 上次尝试时刻(tick ms,uint32 回绕) */ static uint8_t s_vol_sync_armed; /* 0 = 从未尝试(首拍直接放行) */ static uint8_t s_vol_sync_fail_latch; /* 失败留痕边沿 latch:一个 episode 一条 */ /* 函数定义区 */ /* * 函数功能:存储空闲周期同步轮询(就绪门控 → 节流 → try 卷锁 → CTRL_SYNC) * 入口参数:无 void * 返回值:无 void * 限定条件:仅 defaultTask 主循环调用;内部绝不阻塞(try 锁 + 常数级耗时, * 最坏成本 = 一次 f_close 的同步段) * 函数说明:四级门,逐级变贵: * ① 就绪门:sys_evtHandle 未建或 STORAGE_READY 未置 → 静默 * (挂载前 disk_ioctl 只会 RES_NOTRDY,且 bring-up 失败重试期 * 卷状态未定,一律不碰); * ② 节流门:距上次尝试不足 VOL_IDLE_SYNC_PERIOD_MS → 返回。 * 无符号差值判据((uint32)(now - last) < 周期)天然跨 32 位回绕 * 正确,与 stack_probe_poll 同一 idiom;勿"优化"成大小比较; * ③ 空闲门:vol_lock_wait(0) try——锁被持有 = 有文件操作序列在做 * (syslog 落盘 / 配置保存 / lftpd open...close),本轮跳过, * 绝不改变"文件持有期内不同步"的既有语义; * ④ 动作:disk_ioctl(0, CTRL_SYNC)——FatFs 官方幂等落盘原语 * (f_close 尾部同一条路径):FTL 脏页刷入 + dhara_map_sync。 * clean 时两级短路立即返回,零 NAND 操作。 * 失败留痕:RES_ERROR 只按"边沿"打一条 DBG_ERROR(latch),持续失败 * 不刷屏;下一次成功后 latch 复位,新 episode 再报。底层失败形态 * (Cache flush FAIL / Map sync failed)已由 nand_ftl 就地打点, * 本条只补"空闲路径也救不回"的上下文,不重复堆日志锚点。 */ void vol_idle_sync_poll(void) { const uint32_t now = (uint32_t)xTaskGetTickCount(); DRESULT dr; /* ① 就绪门 */ if (sys_evtHandle == NULL) { return; } if ((osEventFlagsGet(sys_evtHandle) & SYS_EVT_STORAGE_READY) == 0u) { return; } /* ② 节流门(回绕安全判据,见函数说明②) */ if ((s_vol_sync_armed != 0u) && ((uint32_t)(now - s_vol_sync_last_ms) < (uint32_t)pdMS_TO_TICKS(VOL_IDLE_SYNC_PERIOD_MS))) { return; } s_vol_sync_last_ms = now; s_vol_sync_armed = 1u; /* ③ 空闲门:try 锁,拿不到 = 卷不空闲,本轮跳过(下周期再试) */ if (vol_lock_wait(0u) == 0u) { return; } /* ④ 幂等落盘:脏页刷入 + journal 收账;clean 时零 NAND 触点 */ dr = disk_ioctl(0, CTRL_SYNC, NULL); vol_lock_release(); /* 成败都放锁:同步失败不产生锁泄漏 */ if (dr != RES_OK) { if (s_vol_sync_fail_latch == 0u) { s_vol_sync_fail_latch = 1u; DBG_ERROR("[FS] idle sync FAIL (dr=%d)", (int)dr); } } else { s_vol_sync_fail_latch = 0u; /* 成功:重新武装,下个 episode 可再报 */ } } #else /* TEST_SUITE_STORAGE */ /* * 函数功能:STORAGE 套件构建下的空实现(编译期裁剪落点) * 入口参数:无 void * 返回值:无 void * 限定条件:仅当 test_config.h 选中 TEST_SUITE_STORAGE 时编入本分支 * 函数说明:该构建里挂载/盘写由 testTask 的 storage_test_run 独占(测试含 * nand_ftl_deinit 掉电模拟),后台同步与它并发会互相踩坏 FTL/FatFs * 状态——必须彻底静默,而非仅靠运行时门控。 */ void vol_idle_sync_poll(void) { } #endif /* TEST_SUITE_STORAGE */