feat(存储): 写回缓存空闲 flush 兜底——卷空闲周期 CTRL_SYNC(登记簿 N-22 收口)
道F 批 2 / task-16。FTL 单页写回缓存(nand_ftl.c s_cache_dirty)与 dhara journal 只在换页/整页写/f_close 时刷出,漏网脏页的掉电驻留窗无上限。本 提交以最小面收口:不碰 GD5F/dhara 驱动,全部走既有 FatFs CTRL_SYNC 路径。 实现 App/fs/vol_idle_sync.[ch](新增,仅 MCU 端,uvprojx App 组注册): - 四级门:STORAGE_READY 就绪门 → 5 s 节流(无符号差回绕安全判据)→ vol_lock_wait(0) try(锁被持有=文件序列在做,跳过,不改 STOR 持有期 语义)→ disk_ioctl(0, CTRL_SYNC); - clean 态脏标志+journal 双短路、零 NAND 触点;脏态最坏 = 一次 f_close 的同步段同型成本,不引入新磨损来源; - 失败按边沿 latch 一条 DBG_ERROR、成功重新武装;失败路径也成对放锁; - TEST_SUITE_STORAGE 构建整体 no-op(卷由 testTask 独占)。 接线:defaultTask 主循环 syslog_drain 之后(default_task.c v1.3)。 文档:docs/架构设计/STM32F4-Base存储架构说明.md §5.5 补空闲兜底与边界; 本板无掉电检测硬件、不做掉电前强制 flush、勿当掉电保护——口径同步落 注释与文档(sys_log_mcu.c 两处旧「暂不做」注释随实现改口)。 测试:新主机单测 test/host/vol_idle_sync/(TC-VOLSYNC-001..012,19 断言 全过);scripts/id_registry.json 建 TC-HOST-VOLSYNC 族(仿 ADCCAL 先例); .pre-commit-config.yaml 加 vol-idle-sync-unit-test hook(只加自己条目)。 越界申报(①②③ 均 lead 批准,④ 为门禁连带必要): ① App/task/default_task.c 单点接线(include + 1 调用 + 头修改记录); ② docs/架构设计/STM32F4-Base存储架构说明.md §5.5; ③ .pre-commit-config.yaml 单 hook + scripts/id_registry.json 单族; ④ scripts/check_registration.py SIM_EXEMPT 加一行(vol_idle_sync.c 模拟器 侧豁免,registration-check GAP 所需;与批准③同类别但未列名,特此申报)。 体积增量(vs scripts/ram_baseline.tsv 2026-10-25 行): Flash(Code+RO) 237140→237328(+188 B)、RW 1224→1232(+8 B)、ZI 0; 增量 = 3 个文件级 static + 一处 DBG_ERROR 调用点;栈/堆无变化。 gate: only mem-budget/mem-numbers pending lead ledger backfill 全量 pre-commit --all-files:40 项 Passed,仅 mem-numbers(D-7 体积档)与 mem-budget 两条红 = 台账数字待回填(增量如上,属 lead 单写者禁区); 其余含 doc-links/id-check/registration/test-coverage/全部主机单测皆绿。 @build 0 错误 0 警告(Code=215932 RO=21396 RW=1232 ZI=131984)。 台架:空闲 sync 实机节奏(clean 零触点、脏态时长、无心跳扰动)属实际 测量采样类——本批跳过,留「待台架实测」。
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 模块名称: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 */
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 模块名称:Volume Idle Sync(写回缓存空闲 flush)
|
||||
* 模块功能:随 defaultTask 主循环周期轮询,在存储**空闲**时对 NAND FatFs 卷
|
||||
* 补发一次 CTRL_SYNC(FatFs 幂等落盘语义 = FTL 单页写回缓存脏页刷入
|
||||
* + dhara journal 待办收账,见 docs/架构设计/STM32F4-Base存储架构说明.md
|
||||
* §5.5),作为写回缓存持久性的兜底网:
|
||||
* - 正常写事务语义不变:f_close 内部 sync_fs 已同步,本模块零干预;
|
||||
* - 价值在漏网兜底:任何"缓存留脏、之后再无调用触发 sync"的状态
|
||||
* (异常路径中途放锁等)其掉电驻留窗从"无限期,直到下一笔写"
|
||||
* 收敛为"不超过本模块周期";
|
||||
* - 卷不空闲(卷锁被持有:syslog 落盘 / 配置保存 / lftpd
|
||||
* open...close 序列进行中)本轮跳过——不改变文件持有期(如 FTP
|
||||
* STOR 进行中)的既有语义。
|
||||
* clean 态零 NAND 触点(脏标志与 journal 双短路);脏态成本与一次
|
||||
* f_close 同型。周期 5 s(.c 内 VOL_IDLE_SYNC_PERIOD_MS)。
|
||||
* ⚠ 边界(勿当成掉电保护):本板无掉电检测硬件,"掉电前强制 flush"
|
||||
* 不可行;"单次写事务进行中掉电"的丢失边界也不因本模块改变。
|
||||
* 适用平台:STM32F407ZGTx + FreeRTOS(CMSIS-RTOS V2);仅 MCU 端,
|
||||
* 主机侧 L0.5 单测 test/host/vol_idle_sync/
|
||||
* 作者:DTU 项目组
|
||||
* 创建日期:2026-10-25(道F 批 2:写回缓存空闲 flush 收口)
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-25 DTU 项目组 创建:STORAGE_READY 门控 + 周期节流 +
|
||||
* 卷锁 try + disk_ioctl(CTRL_SYNC) + 失败边沿 latch
|
||||
*/
|
||||
|
||||
#ifndef VOL_IDLE_SYNC_H
|
||||
#define VOL_IDLE_SYNC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 函数功能:存储空闲周期同步的轮询入口(内部自带就绪门控 / 节流 / 非阻塞
|
||||
* try 锁 / clean 短路,调用点每轮无条件调用即可)
|
||||
* 入口参数:无 void
|
||||
* 返回值:无 void
|
||||
* 限定条件:仅 defaultTask 主循环调用(存储属主任务;本函数内部绝不阻塞——
|
||||
* defaultTask 是看门狗喂狗点,见 vol_lock.h 使用约定);
|
||||
* TEST_SUITE_STORAGE 构建下为空实现(卷由 testTask 独占,后台同步
|
||||
* 必须彻底静默)
|
||||
*/
|
||||
void vol_idle_sync_poll(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VOL_IDLE_SYNC_H */
|
||||
@@ -334,8 +334,10 @@ static int s_append(syslog_id_t id, const char *desc) {
|
||||
* **不改掉电语义**:批次仍在同一次 syslog_drain() 调用内写完并
|
||||
* f_close;close 内部 sync_fs → disk_ioctl(CTRL_SYNC) 会调
|
||||
* ftl_flush_cache() 把 FTL 的单页写回缓存刷进 NAND(nand_ftl.c)。
|
||||
* 落盘时机与旧实现每条 close 等价,批次不跨调用驻留 RAM;残余只到
|
||||
* "单次写事务进行中掉电/复位"为止(登记簿 N-22,2026-10-22 裁定暂不做)。
|
||||
* 落盘时机与旧实现每条 close 等价,批次不跨调用驻留 RAM;漏网脏页
|
||||
* (留脏后再无调用触发刷出)已有写回缓存空闲 flush 兜底
|
||||
* (App/fs/vol_idle_sync.h:卷空闲 + 周期 CTRL_SYNC),掉电残余只到
|
||||
* "单次写事务进行中掉电/复位"为止。
|
||||
* 自愈重试与错误去重的判据与 s_append_rec 一致;容量不足时返回 -3
|
||||
* 而不是就地 unlink——"满了就重建"必须由按条慢路径统一执行,
|
||||
* 否则一批 8 条会跨越多轮重建、语义说不清。
|
||||
@@ -846,8 +848,9 @@ int syslog_last_epoch(syslog_id_t id, uint32_t *p_epoch) {
|
||||
* ⚠ 2026-10-02 round 3~8 那次"复位后写入整段丢失"的根因是 dhara resume 锚错
|
||||
* checkpoint 使 journal 整段回退(docs/架构设计/STM32F4-Base存储架构说明.md
|
||||
* §5.4.1),不是这层缓存:单页缓存最多丢最后未跨页那笔(≤512 B),丢不出
|
||||
* "整段"。真正的残余(无空闲/掉电前周期 flush)= 登记簿 N-22,2026-10-22
|
||||
* 用户裁定暂不做。
|
||||
* "整段"。空闲周期 flush 兜底已接线(App/fs/vol_idle_sync.h:STORAGE_READY
|
||||
* 门控 + 周期节流 + 卷锁 try + CTRL_SYNC,脏页驻留窗由周期封顶);本板无
|
||||
* 掉电检测硬件,"掉电前强制 flush"仍是物理边界,不在该兜底承诺之内。
|
||||
* 顺序硬约束不变:s_append 自行取卷锁(非递归锁),必须在 vol_lock_release()
|
||||
* **之后**调用,绝不可在持锁状态下调用。
|
||||
* 容量护栏:> 8KB 先 unlink 重建,防长跑无界增长(8.3 合法名 wdev.log)。
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
* 耗时(task_prof_report),并在**心跳位图出现 1→0 边沿**
|
||||
* 的瞬间留一份现场快照(task_prof_report_raw,裸 UART);
|
||||
* 另给控制台排空与 syslog 落盘各加一个耗时探针
|
||||
* v1.3 2026-10-25 DTU 项目组 写回缓存空闲 flush 接线:主循环 syslog 排空
|
||||
* 之后调 vol_idle_sync_poll()(卷空闲周期 CTRL_SYNC 兜底,
|
||||
* 模块 = App/fs/vol_idle_sync.[ch],道F 批 2)
|
||||
*/
|
||||
|
||||
#include "default_task.h"
|
||||
@@ -46,6 +49,7 @@
|
||||
#include "task_watchdog.h"
|
||||
#include "task_prof.h" /* 各任务最坏耗时上报 */
|
||||
#include "vol_lock.h"
|
||||
#include "vol_idle_sync.h" /* 写回缓存空闲 flush 兜底(App/fs/vol_idle_sync.h) */
|
||||
#include "task_handles.h" /* 任务句柄与属性块的引用契约(定义处仍是 Src/freertos.c) */
|
||||
|
||||
/* Private defines -----------------------------------------------------------*/
|
||||
@@ -275,6 +279,11 @@ void default_task_func(void *argument) {
|
||||
task_prof_enter(TASK_PROF_DEF_SYSLOG);
|
||||
syslog_drain();
|
||||
task_prof_exit(TASK_PROF_DEF_SYSLOG);
|
||||
/* 写回缓存空闲 flush 兜底:卷空闲(try 锁)+ 周期节流 + 就绪门控全在
|
||||
* 模块内(App/fs/vol_idle_sync.h);clean 态零 NAND 触点,脏态最坏 =
|
||||
* 一次 f_close 同型的同步段。放在 drain 之后:本拍若写过日志,close
|
||||
* 已同步过,这里随即 clean 短路——漏网脏页的驻留窗由周期封顶 */
|
||||
vol_idle_sync_poll();
|
||||
#endif
|
||||
/* 栈水位观测:首次 5s 后报告,此后每 2s 采样、出现更深峰值才打印 */
|
||||
task_wd_seg(TASK_WD_SEG_STACK);
|
||||
|
||||
Reference in New Issue
Block a user