From c70549293bf30ce85047520adc82c41ea8fa519d Mon Sep 17 00:00:00 2001 From: coffee Date: Fri, 27 Mar 2026 19:36:20 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E4=BF=AE=E6=AD=A3=E5=9B=9E=E6=97=A7?= =?UTF-8?q?=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/HFlashMem.h | 17 +- src/HFlashMem.c | 587 +++++++++++++++++--------------------------- src/HFlashServer.c | 5 +- 3 files changed, 230 insertions(+), 379 deletions(-) diff --git a/include/HFlashMem.h b/include/HFlashMem.h index e64f230..554e51b 100644 --- a/include/HFlashMem.h +++ b/include/HFlashMem.h @@ -11,11 +11,6 @@ #include -// 是否使用 hshell -#ifndef HFLASH_USE_HSHELL -#define HFLASH_USE_HSHELL 1 -#endif - // 使用定时器任务ID #ifndef HFLASH_TIMER_ID #define HFLASH_TIMER_ID 0 @@ -30,17 +25,13 @@ enum eHFlashMemIO }; ///< 内存缓存页, 配置需和 (memSize / 4096) 大小一致 -///< 这里不再使用 heat 衰减, 改成 referenced + probation 的二次机会策略 -///< 旧方案需要周期性降温, 小缓存且写入频繁时维护成本高, 也更容易把一次性读页长期留在缓存里 typedef struct HFlashMemCache { uint32_t addr; ///< 地址 uint16_t offset; ///< 内存偏移 uint16_t eraseStatus : 2; ///< 擦除状态 uint16_t isDirty : 1; ///< 是否脏页 - uint16_t referenced : 1; ///< 最近是否访问过, 仅在命中/装载时置1, 淘汰扫描时清0给第二次机会 - uint16_t probation : 1; ///< 新进入缓存的试用页, 只有再次命中后才转正式页, 这样能优先淘汰一次性访问 - uint16_t reserved : 11; + uint16_t heat : 5; ///< 热度优先级 } HFlashMemCache; ///< 内存操作, 回调返回0表示失败 @@ -51,16 +42,14 @@ typedef struct HFlashMemOpts uint8_t (*ioctl)(uint32_t addr, uint32_t cmd, void *arg); ///< 控制Flash, 需要外部实现以上eHFlashMemIO指令 uint8_t *mem; ///< Flash映射内存, 需要4k倍数 HFlashMemCache *cache; ///< 内存缓存 - uint32_t memSize; ///< Flash映射内存大小, 改成32位后可避免映射区超过64KB时页数计算溢出 - uint32_t cacheSize; ///< 内存缓存页数量, 需要始终等于 memSize / 4096, 否则 offset 映射会错位 + uint16_t memSize; ///< Flash映射内存大小 + uint16_t cacheSize; ///< 内存缓存大小 uint32_t flashSize; ///< Flash大小 } HFlashMemOpts; /** * @brief 初始化Flash内存映射 * @param opts 内存操作信息 - * mem/cache 内存由外部分配并长期持有, 初始化不会复制这两块内存 - * cacheSize 必须和 memSize / 4096 完全一致, 因为每个 cache 结构固定对应一个4K映射页 */ void HFlashMemInit(HFlashMemOpts *opts); diff --git a/src/HFlashMem.c b/src/HFlashMem.c index 0b36036..709563d 100644 --- a/src/HFlashMem.c +++ b/src/HFlashMem.c @@ -3,31 +3,34 @@ #include "HDLog.h" #include "HTimer.h" -#ifndef USE_STD_MEM -#if HFLASH_USE_HSHELL -#include "HShellLex.h" -#endif -#endif #ifdef USE_STD_MEM #define FATAL_ERROR(format, ...) LogE(format, ##__VA_ARGS__); -#elif HFLASH_USE_HSHELL -#define FATAL_ERROR(format, ...) while (1) { LogE(format, ##__VA_ARGS__); HShellRun(); }; #else -#define FATAL_ERROR(format, ...) while (1) { LogE(format, ##__VA_ARGS__); }; +#include "HShellLex.h" +#define FATAL_ERROR(format, ...) while (1) { LogE(format, ##__VA_ARGS__); HShellRun(); }; #endif // flash块大小 #define HFLASH_BLOCK_SIZE (4096) +// 调度热度时间 +#define HEAT_TIME_MS (1000) + // 是否对齐页 #define IS_4K(size) ((size & (HFLASH_BLOCK_SIZE - 1)) == 0) -// 这里允许少量读 miss 直接绕过缓存 -// 当缓存已被脏页占满时, 如果每次只读 miss 都强制抢占缓存, 会触发同步刷写, 小缓存场景抖动很大 -// 因此只有同一页连续被直读多次, 才认为它值得挤进缓存 -// 全脏页下, 同一页连续直读达到该次数后才提升进缓存 -#define BYPASS_PROMOTE_THRESHOLD (3) +// 最大热度 +#define MAX_HEAT ((1 << 5) - 1) + +// 写转读增加热度 +#define WRITE_CONV_READ_ADD_HEAT (2) + +// 读最小热度 +#define READ_MIN_HEAT (0) + +// 快速最小值 +#define FAST_MIN(type, a, b) ((b) ^ (((a) ^ (b)) & (-(type)((a) < (b))))) enum eErase { @@ -38,13 +41,9 @@ enum eErase struct Info { - uint32_t bypassAddr; ///< 全脏页读 miss 时, 最近一次直读页地址 + uint32_t currHeatTime; ///< 当前热度时间, 用于计算下一次热度下降 uint16_t useNum; ///< 缓存已使用数量 - uint16_t readClockHand; ///< 读页淘汰时的时钟指针 - uint16_t dirtyClockHand; ///< 脏页回收时的时钟指针 - uint8_t bypassCount; ///< 当前直读页连续命中次数 uint16_t needWaitErase : 1; ///< 用于判断是否在擦除 - uint16_t bypassValid : 1; ///< 最近一次直读页地址是否有效 }; // 内存操作 @@ -56,16 +55,6 @@ static struct Info sInfo; // 检查定时器 static HTimer_t sCheckTimer = HTIMER_INVALID; -static void WatiErase(); -static void CheckSyncCache(); - -static void ResetCacheState() -{ - if (sOpts && sOpts->cache && sOpts->cacheSize) { - memset(sOpts->cache, 0, sizeof(HFlashMemCache) * sOpts->cacheSize); - } -} - // 对齐页地址, 向下取整 static inline uint32_t Align4K(uint32_t addr) { return addr & ~(HFLASH_BLOCK_SIZE - 1); @@ -76,110 +65,6 @@ static inline uint8_t *GetMMap(uint16_t offset) { return (uint8_t *)sOpts->mem + offset * HFLASH_BLOCK_SIZE; } -static inline uint8_t IsReusableCleanCache(const HFlashMemCache *cache) -{ - return cache->isDirty == 0 && cache->eraseStatus == kEraseWait; -} - -static void TouchCache(HFlashMemCache *cache) -{ - // 这里同时清 probation - // 新页首次装入时先标记为“试用页”, 只有再次命中才认为它真的是热点, 避免一次性读页长期占坑 - cache->referenced = 1; - cache->probation = 0; -} - -static void InitCache(HFlashMemCache *cache, uint16_t index, uint32_t addr, uint8_t probation) -{ - memset(cache, 0, sizeof(HFlashMemCache)); - cache->addr = addr; - cache->offset = index; - cache->referenced = 1; - cache->probation = probation; -} - -static void MarkCleanCache(HFlashMemCache *cache) -{ - // 脏页刷回后会被重新当作试用页 - // 刚完成回写只代表它“被写过”, 不代表后续仍然高频访问, 立即降为 probation 能避免旧脏页长期压制新页 - cache->isDirty = 0; - cache->eraseStatus = kEraseWait; - cache->referenced = 0; - cache->probation = 1; -} - -static void ClearBypassAddr(uint32_t addr) -{ - if (sInfo.bypassValid && sInfo.bypassAddr == addr) { - sInfo.bypassValid = 0; - sInfo.bypassCount = 0; - } -} - -static void RecordBypassAddr(uint32_t addr) -{ - if (sInfo.bypassValid && sInfo.bypassAddr == addr) { - if (sInfo.bypassCount < BYPASS_PROMOTE_THRESHOLD) { - ++sInfo.bypassCount; - } - return; - } - - sInfo.bypassAddr = addr; - sInfo.bypassValid = 1; - sInfo.bypassCount = 1; -} - -static uint8_t HasDirtyCache() -{ - for (uint16_t i = 0; i < sInfo.useNum; ++i) { - if (sOpts->cache[i].isDirty) { - return 1; - } - } - - return 0; -} - -static HFlashMemCache *FindCacheByAddr(uint32_t addr) -{ - for (uint16_t i = 0; i < sInfo.useNum; ++i) { - if (addr == sOpts->cache[i].addr) { - return sOpts->cache + i; - } - } - - return NULL; -} - -static uint8_t LoadCachePage(HFlashMemCache *cache, uint32_t addr) -{ - WatiErase(); - if (sOpts->read(addr, GetMMap(cache->offset), HFLASH_BLOCK_SIZE) == 0) { - LogE("addr[0x%08x], read faild", addr); - return 0; - } - - return 1; -} - -static uint8_t SyncDirtyCache(HFlashMemCache *cache) -{ - if (cache == NULL || cache->isDirty == 0) { - return cache != NULL; - } - - WatiErase(); - sOpts->ioctl(cache->addr, kHFlashMemIOSyncErase, NULL); - if (sOpts->write(cache->addr, GetMMap(cache->offset), HFLASH_BLOCK_SIZE) == 0) { - LogE("addr[0x%08x], write faild", cache->addr); - return 0; - } - - MarkCleanCache(cache); - return 1; -} - static void WatiErase() { // 如果存在异步等待擦除的, 先等擦除完成 @@ -205,145 +90,66 @@ static void SyncCache() } LogD("Sync[%d], addr[0x%08x], offset[%d]", i, sOpts->cache[i].addr, sOpts->cache[i].offset); - if (SyncDirtyCache(sOpts->cache + i) == 0) { - LogE("Sync[%d], addr[0x%08x], offset[%d] faild", i, sOpts->cache[i].addr, sOpts->cache[i].offset); - } - } -} - -static HFlashMemCache *FindCleanVictim() -{ - HFlashMemCache *fallback = NULL; - if (sInfo.useNum == 0) { - return NULL; - } - - // 两轮时钟扫描: - // 1. referenced=1 的页先清位, 给第二次机会, 不立刻淘汰 - // 2. 优先淘汰 probation 页, 因为它们还没被二次命中过, 更可能只是一次性访问 - // 3. 如果没有 probation 页, 再回退到普通 clean 页 - for (uint8_t round = 0; round < 2; ++round) { - for (uint16_t count = 0; count < sInfo.useNum; ++count) { - const uint16_t index = sInfo.readClockHand; - HFlashMemCache *cache = sOpts->cache + index; - sInfo.readClockHand = (index + 1) % sInfo.useNum; - if (IsReusableCleanCache(cache) == 0) { - continue; - } - - if (cache->referenced) { - cache->referenced = 0; - continue; - } - - if (cache->probation) { - return cache; - } - - fallback = cache; + sOpts->ioctl(sOpts->cache[i].addr, kHFlashMemIOSyncErase, NULL); + if (sOpts->write(sOpts->cache[i].addr, GetMMap(i), HFLASH_BLOCK_SIZE) == 0) { + LogD("Sync[%d], addr[0x%08x], offset[%d] faild", i, sOpts->cache[i].addr, sOpts->cache[i].offset); + --i; + continue; } - if (fallback) { - return fallback; - } + sOpts->cache[i].isDirty = 0; + sOpts->cache[i].eraseStatus = kEraseWait; + + // 写入页转换空闲页, 热度持续增加 + sOpts->cache[i].heat = FAST_MIN(uint8_t, sOpts->cache[i].heat + WRITE_CONV_READ_ADD_HEAT, MAX_HEAT); } - - for (uint16_t i = 0; i < sInfo.useNum; ++i) { - if (IsReusableCleanCache(sOpts->cache + i)) { - return sOpts->cache + i; - } - } - - return NULL; -} - -static HFlashMemCache *FindDirtyVictim() -{ - HFlashMemCache *fallback = NULL; - if (sInfo.useNum == 0) { - return NULL; - } - - // 脏页选择也走 clock, 不再依赖 heat - // 这里只挑 eraseStatus=kEraseWait 的脏页, 避免和正在擦除或等待写回的页并发冲突 - // 当前底层 ioctl 的擦除状态是全局查询模型, 同一时刻只维护一个异步擦除流程更安全 - for (uint8_t round = 0; round < 2; ++round) { - HFlashMemCache *candidate = NULL; - for (uint16_t count = 0; count < sInfo.useNum; ++count) { - const uint16_t index = sInfo.dirtyClockHand; - HFlashMemCache *cache = sOpts->cache + index; - sInfo.dirtyClockHand = (index + 1) % sInfo.useNum; - if (cache->isDirty == 0 || cache->eraseStatus != kEraseWait) { - continue; - } - - fallback = cache; - - if (cache->referenced) { - cache->referenced = 0; - continue; - } - - candidate = cache; - } - - if (candidate) { - return candidate; - } - } - - return fallback; } static void NotifyASyncErase() { - HFlashMemCache *cache = FindDirtyVictim(); - if (cache) { - // 这里只发起一个异步擦除请求 - // CheckSyncCache 依赖 needWaitErase + eraseStatus 顺序推进状态机, 多个并发擦除会让状态归属变复杂 + for (uint16_t i = 0; i < sInfo.useNum; ++i) { + if (sOpts->cache[i].isDirty == 0) { + continue; + } + sInfo.needWaitErase = 1; - cache->eraseStatus = kEraseStart; - sOpts->ioctl(cache->addr, kHFlashMemIOErase, NULL); + sOpts->cache[i].eraseStatus = kEraseStart; + sOpts->ioctl(sOpts->cache[i].addr, kHFlashMemIOErase, NULL); + break; } } -static void StartSyncCache() +static void FastSyncCache() { - if (HasDirtyCache() == 0) { - return; + WatiErase(); + for (uint16_t i = 0; i < sInfo.useNum; ++i) { + // 存在空闲页, 可以立刻解除 + if (sOpts->cache[i].isDirty == 0) { + break; + } + + LogD("FastSync[%d], addr[0x%08x], offset[%d]", i, sOpts->cache[i].addr, sOpts->cache[i].offset); + sOpts->ioctl(sOpts->cache[i].addr, kHFlashMemIOSyncErase, NULL); + if (sOpts->write(sOpts->cache[i].addr, GetMMap(i), HFLASH_BLOCK_SIZE) == 0) { + LogD("FastSync[%d], addr[0x%08x], offset[%d] faild", i, sOpts->cache[i].addr, sOpts->cache[i].offset); + --i; + continue; + } + + sOpts->cache[i].isDirty = 0; + sOpts->cache[i].eraseStatus = kEraseWait; + + // 写入页转换空闲页, 热度持续增加 + sOpts->cache[i].heat = FAST_MIN(uint8_t, sOpts->cache[i].heat + WRITE_CONV_READ_ADD_HEAT, MAX_HEAT); + break; } - if (sCheckTimer != HTIMER_INVALID) { - return; - } - - sCheckTimer = HTimerAdd(HFLASH_TIMER_ID, 0, CheckSyncCache, kHTimerLoop); -} - -static HFlashMemCache *ReclaimOneCleanCache() -{ - HFlashMemCache *cache = FindCleanVictim(); - if (cache) { - return cache; - } - - cache = FindDirtyVictim(); - if (cache == NULL) { - return NULL; - } - - // 这里退化成同步回收一个脏页 - // 写路径必须拿到可用 cache, 如果此时全是脏页, 只能牺牲一次阻塞把一个页刷回, 否则无法继续分配 - LogD("Reclaim addr[0x%08x], offset[%d]", cache->addr, cache->offset); - if (SyncDirtyCache(cache) == 0) { - return NULL; - } - - return cache; + NotifyASyncErase(); } static void CheckSyncCache() { + // 检查擦除是否完成 uint8_t status = 0; if (sInfo.needWaitErase) { sOpts->ioctl(0, kHFlashMemIOCheckErase, &status); @@ -352,44 +158,63 @@ static void CheckSyncCache() } sInfo.needWaitErase = 0; - for (uint16_t i = 0; i < sInfo.useNum; ++i) { - if (sOpts->cache[i].eraseStatus == kEraseStart) { - sOpts->cache[i].eraseStatus = kWaitWrite; - break; + } + + uint16_t needErase = sInfo.useNum; + uint8_t heat = MAX_HEAT; + uint8_t waitWrite = 0; + for (uint16_t i = 0; i < sInfo.useNum; ++i) { + // 检查哪个页的异步擦除, 现在擦除完成 + if (sOpts->cache[i].eraseStatus == kEraseStart) { + sOpts->cache[i].eraseStatus = kWaitWrite; + } + + waitWrite |= sOpts->cache[i].isDirty; + // 寻找需要擦除的页 + if (sOpts->cache[i].isDirty && sOpts->cache[i].eraseStatus == kEraseWait) { + // 查找最低热度的优先擦除, 方便低热度页转换空闲 + if (sOpts->cache[i].heat <= heat) { + heat = sOpts->cache[i].heat; + needErase = i; } } - } - // 这里先处理 kWaitWrite, 再决定是否继续提交新的擦除 - // 写回完成后才能把页重新放回 clean 集合, 否则 read/write 侧会一直看不到可回收页 - for (uint16_t i = 0; i < sInfo.useNum; ++i) { - HFlashMemCache *cache = sOpts->cache + i; - if (cache->isDirty == 0 || cache->eraseStatus != kWaitWrite) { + // 如果不是脏页就跳过 + if (sOpts->cache[i].isDirty == 0) { continue; } - if (sOpts->write(cache->addr, GetMMap(cache->offset), HFLASH_BLOCK_SIZE) == 0) { - LogE("addr[0x%08x], write faild", cache->addr); - cache->eraseStatus = kEraseWait; + // 检查Flash是否已经擦除完成 + if (sOpts->cache[i].eraseStatus != kWaitWrite) { continue; } - MarkCleanCache(cache); - } - - uint8_t hasPendingWrite = 0; - for (uint16_t i = 0; i < sInfo.useNum; ++i) { - if (sOpts->cache[i].isDirty && sOpts->cache[i].eraseStatus == kWaitWrite) { - hasPendingWrite = 1; - break; + // 如果写入失败, 则需要再次擦除重新写入 + if (sOpts->write(sOpts->cache[i].addr, GetMMap(i), HFLASH_BLOCK_SIZE) == 0) { + LogE("addr[0x%08x], write faild", sOpts->cache[i].addr); + sInfo.needWaitErase = 1; + sOpts->cache[i].eraseStatus = kEraseWait; + if (needErase == sInfo.useNum) { + heat = sOpts->cache[i].heat; + needErase = i; + } + continue; } + + sOpts->cache[i].isDirty = 0; + sOpts->cache[i].eraseStatus = kEraseWait; + + // 写入页转换空闲页, 热度持续增加 + sOpts->cache[i].heat = FAST_MIN(uint8_t, sOpts->cache[i].heat + WRITE_CONV_READ_ADD_HEAT, MAX_HEAT); } - if (sInfo.needWaitErase == 0 && hasPendingWrite == 0 && HasDirtyCache()) { - NotifyASyncErase(); + if (needErase != sInfo.useNum) { + sInfo.needWaitErase = 1; + sOpts->cache[needErase].eraseStatus = kEraseStart; + sOpts->ioctl(sOpts->cache[needErase].addr, kHFlashMemIOErase, NULL); } - if (sInfo.needWaitErase == 0 && hasPendingWrite == 0 && HasDirtyCache() == 0) { + if (sInfo.needWaitErase == 0 && waitWrite == 0) { if (sCheckTimer != HTIMER_INVALID) { HTimerRemove(sCheckTimer); sCheckTimer = HTIMER_INVALID; @@ -397,7 +222,67 @@ static void CheckSyncCache() } } -static HFlashMemCache *FindReadCache(uint32_t addr, uint8_t needRead, uint8_t allowBypass) +static void StartSyncCache() +{ + if (sCheckTimer != HTIMER_INVALID) { + HTimerRemove(sCheckTimer); + sCheckTimer = HTIMER_INVALID; + } + + sCheckTimer = HTimerAdd(HFLASH_TIMER_ID, 0, CheckSyncCache, kHTimerLoop); +} + +static void ScheduleHeat() +{ + if (HDLogGetTime() - sInfo.currHeatTime < HEAT_TIME_MS) { + return; + } + + for (uint16_t i = 0; i < sInfo.useNum; ++i) { + if (sOpts->cache[i].eraseStatus || sOpts->cache[i].isDirty) { + continue; + } + + // 仅对空闲页降温 + sOpts->cache[i].heat >>= 1; + } + + sInfo.currHeatTime = HDLogGetTime(); +} + +static HFlashMemCache *FindMinHeatCache() +{ + int16_t index = -1; + uint8_t minHeat = MAX_HEAT; + for (uint16_t i = 0; i < sInfo.useNum; ++i) { + if (sOpts->cache[i].eraseStatus || sOpts->cache[i].isDirty) { + continue; + } + + // 寻找最小热度 + const uint8_t currHeat = sOpts->cache[i].heat; + if (index == -1) { + index = i; + minHeat = currHeat; + continue; + } + + if (currHeat > minHeat) { + continue; + } + + index = i; + minHeat = currHeat; + } + + if (index == -1) { + return NULL; + } + + return sOpts->cache + index; +} + +static HFlashMemCache *FindReadCache(uint32_t addr, uint8_t needRead) { addr = Align4K(addr); if (addr + HFLASH_BLOCK_SIZE > sOpts->flashSize) { @@ -405,56 +290,54 @@ static HFlashMemCache *FindReadCache(uint32_t addr, uint8_t needRead, uint8_t al return NULL; } - HFlashMemCache *cache = FindCacheByAddr(addr); - if (cache) { - ClearBypassAddr(addr); - TouchCache(cache); - return cache; + for (uint16_t i = 0; i < sInfo.useNum; ++i) { + if (addr != sOpts->cache[i].addr) { + continue; + } + + sOpts->cache[i].heat = FAST_MIN(uint8_t, sOpts->cache[i].heat + 1, MAX_HEAT); + return sOpts->cache + i; } // 如果缓存没有满, 直接增加 if (sInfo.useNum < sOpts->cacheSize) { const uint16_t index = sInfo.useNum; - cache = sOpts->cache + index; - InitCache(cache, index, addr, 1); + ++sInfo.useNum; if (needRead) { - if (LoadCachePage(cache, addr) == 0) { + WatiErase(); + if (sOpts->read(addr, GetMMap(index), HFLASH_BLOCK_SIZE) == 0) { + LogE("addr[0x%08x], read faild", addr); return NULL; } } - ClearBypassAddr(addr); - ++sInfo.useNum; - return cache; + memset(sOpts->cache + index, 0, sizeof(HFlashMemCache)); + sOpts->cache[index].addr = addr; + sOpts->cache[index].offset = index; + sOpts->cache[index].heat = FAST_MIN(uint8_t, sOpts->cache[index].heat + 1, MAX_HEAT); + return sOpts->cache + index; } - cache = FindCleanVictim(); - if (cache == NULL && allowBypass && needRead) { - // bypass 只允许真正的读 miss - // 普通读接口可以接受本次直接读 flash, 但像 mmap/callback 这种需要稳定页指针的场景必须强制拿到 cache - RecordBypassAddr(addr); - if (sInfo.bypassCount < BYPASS_PROMOTE_THRESHOLD) { - return NULL; - } - } - - if (cache == NULL) { - cache = ReclaimOneCleanCache(); - } - if (cache == NULL) { + // 查找空闲的最低热度页 + HFlashMemCache *cache = FindMinHeatCache(); + if (cache == NULL || cache->heat > READ_MIN_HEAT) { return NULL; } const uint16_t index = cache->offset; - InitCache(cache, index, addr, 1); + memset(cache, 0, sizeof(HFlashMemCache)); + cache->addr = addr; + cache->offset = index; + cache->heat = READ_MIN_HEAT; if (needRead) { - if (LoadCachePage(cache, addr) == 0) { + WatiErase(); + if (sOpts->read(addr, GetMMap(index), HFLASH_BLOCK_SIZE) == 0) { + LogE("addr[0x%08x], read faild", addr); return NULL; } } - ClearBypassAddr(addr); return cache; } @@ -466,47 +349,38 @@ static HFlashMemCache *FindWriteCache(uint32_t addr, uint8_t needRead) return NULL; } - HFlashMemCache *cache = FindCacheByAddr(addr); + HFlashMemCache *cache = FindReadCache(addr, needRead); if (cache) { - ClearBypassAddr(addr); - TouchCache(cache); return cache; } - if (sInfo.useNum < sOpts->cacheSize) { - const uint16_t index = sInfo.useNum; - cache = sOpts->cache + index; - InitCache(cache, index, addr, 0); - if (needRead) { - if (LoadCachePage(cache, addr) == 0) { - return NULL; - } + for (;;) { + // 写入模式下, 需要抢占缓存页 + cache = FindMinHeatCache(); + if (cache == NULL) { + // 如果在创建模式下, 没有一个空闲的缓存, 就需要先同步脏页, 再获取一个 + FastSyncCache(); + continue; } - ClearBypassAddr(addr); - ++sInfo.useNum; - return cache; - } - cache = FindCleanVictim(); - if (cache == NULL) { - cache = ReclaimOneCleanCache(); - } - if (cache == NULL) { - return NULL; - } - - { const uint16_t index = cache->offset; - InitCache(cache, index, addr, 0); + memset(cache, 0, sizeof(HFlashMemCache)); + cache->addr = addr; + cache->offset = index; + cache->heat = READ_MIN_HEAT; if (needRead) { - if (LoadCachePage(cache, addr) == 0) { + WatiErase(); + // 找到一个空闲且最低温度的 + if (sOpts->read(addr, GetMMap(index), HFLASH_BLOCK_SIZE) == 0) { + LogE("addr[0x%08x], read faild", addr); return NULL; } } - ClearBypassAddr(addr); + + return cache; } - return cache; + return NULL; } @@ -519,6 +393,9 @@ static uint8_t WriteData(uint32_t addr, const void *buf, uint32_t len, void (*cp return 0; } + // 开启逐步同步 + StartSyncCache(); + // 找到映射表, 先处理首页 const uint8_t *bufPtr = (const uint8_t *)buf; const uint32_t offset = addr - cache->addr; @@ -553,8 +430,6 @@ static uint8_t WriteData(uint32_t addr, const void *buf, uint32_t len, void (*cp cpCall(GetMMap(cache->offset), bufPtr, len, userData); } - // 写路径只负责标脏, 后台异步回收逐步推进, 避免小缓存下同步冲刷 - StartSyncCache(); return 1; } @@ -586,7 +461,7 @@ static uint8_t FindReadPageCopy(uint32_t addr, void *buf, uint32_t len) return 0; } - HFlashMemCache *cache = FindReadCache(pageAddr, 1, 1); + HFlashMemCache *cache = FindReadCache(pageAddr, 1); if (cache) { memcpy(p, GetMMap(cache->offset) + offset, copyLen); } else { @@ -626,16 +501,6 @@ void HFlashMemInit(HFlashMemOpts *opts) FATAL_ERROR("flash Size not 4k align"); return ; } - - // 重新初始化时会把运行态和定时器一并清掉 - // 这些状态记录的是上一次 cache 使用轨迹和异步擦除进度, 沿用会把新配置带进旧状态机 - memset(&sInfo, 0, sizeof(sInfo)); - if (sCheckTimer != HTIMER_INVALID) { - HTimerRemove(sCheckTimer); - sCheckTimer = HTIMER_INVALID; - } - - ResetCacheState(); } const HFlashMemOpts *HFlashMemGetOpt() @@ -653,6 +518,7 @@ uint8_t HFlashMemRead(uint32_t addr, void *buf, uint32_t len) return 1; } + ScheduleHeat(); uint8_t *p = (uint8_t *)buf; while (len > 0) { const uint32_t pageAddr = Align4K(addr); @@ -664,7 +530,7 @@ uint8_t HFlashMemRead(uint32_t addr, void *buf, uint32_t len) return 0; } - HFlashMemCache *cache = FindReadCache(pageAddr, 1, 1); + HFlashMemCache *cache = FindReadCache(pageAddr, 1); if (cache) { memcpy(p, GetMMap(cache->offset) + offset, copyLen); } else { @@ -699,6 +565,7 @@ uint8_t HFlashMemWrite(uint32_t addr, const void *buf, uint32_t len) return 1; } + ScheduleHeat(); return WriteData(addr, buf, len, _MemCpyHelper, NULL); } @@ -714,6 +581,7 @@ uint8_t HFlashMemSetValue(uint32_t addr, uint8_t value, uint32_t len) return 0; } + ScheduleHeat(); return WriteData(addr, NULL, len, _MemSetValueHelper, &value); } @@ -729,6 +597,7 @@ uint8_t HFlashMemAddrCopy(uint32_t srcAddr, uint32_t destAddr, uint32_t len) return 0; } + ScheduleHeat(); return WriteData(destAddr, NULL, len, _MemAddrCopy, &srcAddr); } @@ -738,15 +607,15 @@ uint8_t HFlashMemMMapCall(uint32_t addr, uint8_t(*call)(uint32_t addr, uint32_t return 0; } + ScheduleHeat(); uint32_t offset = 0; + HFlashMemCache *cache = NULL; uint16_t len = 0; uint8_t result = 0; // 先处理未对齐的部分 if (addr != Align4K(addr)) { - // 这里显式禁止 bypass - // 回调拿到的是 cache 内存指针, 如果退化成直读 flash, 本接口就无法提供连续且可复用的页视图 - HFlashMemCache *cache = FindReadCache(addr + offset, 1, 0); + cache = FindWriteCache(addr + offset, 1); if (cache == NULL) { LogE("addr[0x%08x], find cache faild", addr); return 0; @@ -763,7 +632,7 @@ uint8_t HFlashMemMMapCall(uint32_t addr, uint8_t(*call)(uint32_t addr, uint32_t // 剩余处理对齐部分 while (result == 0) { - HFlashMemCache *cache = FindReadCache(addr + offset, 1, 0); + cache = FindWriteCache(addr + offset, 1); if (cache == NULL) { LogE("addr[0x%08x], find cache faild", addr + offset); return 0; @@ -782,20 +651,14 @@ uint8_t HFlashMemMMapCall(uint32_t addr, uint8_t(*call)(uint32_t addr, uint32_t void HFlashMemSync() { - if (sCheckTimer != HTIMER_INVALID) { - HTimerRemove(sCheckTimer); - sCheckTimer = HTIMER_INVALID; - } + ScheduleHeat(); SyncCache(); } void HFlashMemFreeCache() { HFlashMemSync(); - if (sCheckTimer != HTIMER_INVALID) { - HTimerRemove(sCheckTimer); - sCheckTimer = HTIMER_INVALID; - } - ResetCacheState(); - memset(&sInfo, 0, sizeof(sInfo)); + sInfo.useNum = 0; + sInfo.currHeatTime = HDLogGetTime(); } + diff --git a/src/HFlashServer.c b/src/HFlashServer.c index 84dc63a..54b1dda 100644 --- a/src/HFlashServer.c +++ b/src/HFlashServer.c @@ -1449,13 +1449,12 @@ static void Shell(HSHELL_FUNC_ARGS) } for (uint8_t i = 0; i < opt->cacheSize; ++i) { - HSHELL_PRINTFL("addr[0x%08x], offset[%d], eraseStatus[%d], isDirty[%d], referenced[%d], probation[%d]", + HSHELL_PRINTFL("addr[0x%08x], offset[%d], eraseStatus[%d], isDirty[%d], heat[%d]", opt->cache[i].addr, opt->cache[i].offset, opt->cache[i].eraseStatus, opt->cache[i].isDirty, - opt->cache[i].referenced, - opt->cache[i].probation); + opt->cache[i].heat); } } break; #endif