修复 P2 全部 10 项:错误传播/竞态/门控/文档失真

待办池 docs/待处理内容.md §3.2 的 10 个 P2 全部闭环;逐项落代码 + 注释说明。

P2-1  cfg_file: 受管键行超 CFG_LINE_TEST_MAX 拒绝保存(防重复键行)
P2-2  ch395f: DMA 失败传播状态 + 锁存通道故障 + 读不拷不可信数据
P2-7  net_select: LISTENING 不再恒判可读(消除 select 忙轮询地雷)
P2-8  time_sync: 核实 L3 pending 改写已闭环(net_recv_to 分片 + 总预算)
P2-9  sys_log: 挂载改幂等 + 卷锁,defaultTask 2s 重试建 /log
P2-10 sys_log: s_read_raw 纳入卷锁 try(读者/写者 unlink 互斥)
P2-11 net_task: RECONFIG 改 osEventFlagsWait 原子取走 + 清除
P2-12 app_cfg: APP_*_ENABLE 如实标注为未接线占位,删“开关”说法
P2-13 freertos: 产品构建 vTaskDelete(testTask) 回收 8KB CCM 栈
P2-15 download.bat: 先删旧日志 + 增“未产出日志”判定

P2-13 用 USER CODE 区回收而非生成区 #if 门控:生成区改动会被 CubeMX
重生成覆盖,本方案重生成不丢。P2-12 因产品定义 B0 未定、APP_NET_ENABLE
无单一接线点,按修复建议备选(删“开关”说法)处理。

验证:@build 0 错 0 警;主机单测 watchdog 39/39 + sys_clock 100/100 +
cfg_file 149/149(新增 TC-CFG-044);pre-commit --all-files 17/17。
This commit is contained in:
2026-10-04 08:30:30 +08:00
parent 13eeba944b
commit e0b02d397c
16 changed files with 332 additions and 93 deletions
+45 -4
View File
@@ -16,6 +16,12 @@
* v1.3 2026-10-02 王建锋 新增 sys_log_evidence():把看门狗停喂现场
* 证据落 /log/wdev.log(掉电不丢)+ flush 诱导
* (round 7 纯取证,见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)
* v1.4 2026-10-04 王建锋 P2-9:syslog_mcu_mounted() 改为幂等 + 卷锁保护;
* defaultTask 每 2s 复查,建 /log 曾失败时自动重试
* (原"仅调用一次"→ 失败后 drain 永久 no-op)。
* P2-10:s_read_raw() 纳入卷锁 try 语义,与写者
* s_append 的"容量满 unlink 重建"互斥,消除
* "UI 读记录(FA_READ) 时文件被 unlink"竞态窗口。
*/
/* 头文件包含区 */
@@ -49,6 +55,7 @@ static volatile uint8_t s_preboot_cnt; /* 数组条目数 */
static osMessageQueueId_t s_queue; /* 运行期消息队列(syslog_drain 首次调用时惰性创建) */
static const osMessageQueueAttr_t s_queue_attr = { .name = "syslog" };
static volatile uint8_t s_fs_ready; /* 卷挂载 + /log 目录就绪后置位 */
static volatile uint8_t s_mount_fail_reported; /* P2-9:建目录失败日志去重(成功复位) */
/* 函数定义区 */
@@ -153,18 +160,28 @@ static int s_append(syslog_id_t id, const char *desc) {
* 入口参数:id - 日志 ID syslog_id_t
* idx - 记录序号(0 起) uint32_t
* p_rec- 输出缓冲(SYSLOG_REC_SIZE 字节,不可为 NULL) uint8_t *
* 返回值:0 - 成功;-1 - 越界/读失败 int
* 返回值:0 - 成功;-1 - 越界/读失败/卷忙 int
* 函数说明:P2-10(2026-10-04):读者纳入卷锁(try),与写者 s_append 的
* "容量满 unlink 重建"互斥——原实现读时无锁,写者 unlink 与读者
* open/read 可交错,读到的可能是半删除态文件。try 语义(timeout=0):
* 本路径可由 uiTask 调用,uiTask 在 required_mask 内、须每 100ms 喂
* 心跳,绝不能阻塞等锁;busy = 本次读失败(UI 下轮刷新重读,非故障)。
*/
static int s_read_raw(syslog_id_t id, uint32_t idx, uint8_t *p_rec) {
FIL f;
FRESULT fr;
UINT br = 0u;
int rc;
if (p_rec == NULL || idx >= syslog_count(id)) {
return -1;
}
if (vol_lock_wait(0u) == 0u) {
return -1; /* 卷被占用:本次读失败(非故障,下轮重读) */
}
fr = f_open(&f, s_paths[id], FA_READ);
if (fr != FR_OK) {
vol_lock_release();
return -1;
}
fr = f_lseek(&f, (FSIZE_t)idx * SYSLOG_REC_SIZE);
@@ -172,7 +189,9 @@ static int s_read_raw(syslog_id_t id, uint32_t idx, uint8_t *p_rec) {
fr = f_read(&f, p_rec, SYSLOG_REC_SIZE, &br);
}
(void)f_close(&f);
return (fr == FR_OK && br == SYSLOG_REC_SIZE) ? 0 : -1;
rc = (fr == FR_OK && br == SYSLOG_REC_SIZE) ? 0 : -1;
vol_lock_release();
return rc;
}
/*
@@ -194,20 +213,42 @@ int syslog_mcu_init(void) {
* 函数功能:卷挂载成功后的一次性处理(建 /log 目录 + 写"上电/重启"分类记录)
* 入口参数:无
* 返回值:无
* 限定条件:defaultTask f_mount/f_mkfs 成功且置位 SYS_EVT_STORAGE_READY 前调用一次
* 限定条件:defaultTask f_mount/f_mkfs 成功后调用;**可重复调用(幂等)**——
* 产品运行期 defaultTask 每 2s 复查一次(见下 P2-9 说明)
* 函数说明:P2-9(2026-10-04):原实现"仅调用一次",若此处建 /log 失败则
* s_fs_ready 恒 0 → syslog_drain 永久 no-op(运行期日志与自检全部
* 滞留/丢弃),且无任何重试。现改为幂等:s_fs_ready 已置位则立即
* 返回;否则重试建目录。defaultTask 在 STORAGE_READY 置位后每 2s
* 调用本函数——卷已就绪但日志后端未就绪时自动补建目录。
* FS 操作纳入卷锁 try:运行期重试时 ftpTask 可能正操作卷,try 失败
* = 本轮顺延(下轮再试),绝不阻塞(本任务 = 看门狗喂狗点)。
*/
void syslog_mcu_mounted(void) {
FILINFO finfo;
uint32_t last = 0u;
const char *desc = "power on";
if (s_fs_ready != 0u) {
return; /* 幂等:已就绪 */
}
/* 卷锁 try:运行期重试时与 ftpTask 卷访问串行;busy = 本轮顺延 */
if (vol_lock_wait(0u) == 0u) {
return;
}
if (f_stat("/log", &finfo) != FR_OK) {
FRESULT fr = f_mkdir("/log");
if (fr != FR_OK && fr != FR_EXIST) {
DBG_ERROR("mkdir /log fail (%d)", (int)fr);
/* 失败日志去重:持续失败只报第一次(防 2s 周期刷屏),成功后复位 */
if (s_mount_fail_reported == 0u) {
s_mount_fail_reported = 1u;
DBG_ERROR("mkdir /log fail (%d), will retry", (int)fr);
}
vol_lock_release();
return;
}
}
vol_lock_release();
s_mount_fail_reported = 0u;
s_fs_ready = 1u;
/* 上电/重启分类:末条 epoch 距 now < SYSLOG_REBOOT_WINDOW_S → 重启,否则上电 */