1. 修正回旧版

This commit is contained in:
coffee 2026-03-27 19:36:20 +08:00
parent 1a2afd46f6
commit c70549293b
3 changed files with 230 additions and 379 deletions

View File

@ -11,11 +11,6 @@
#include <stdint.h> #include <stdint.h>
// 是否使用 hshell
#ifndef HFLASH_USE_HSHELL
#define HFLASH_USE_HSHELL 1
#endif
// 使用定时器任务ID // 使用定时器任务ID
#ifndef HFLASH_TIMER_ID #ifndef HFLASH_TIMER_ID
#define HFLASH_TIMER_ID 0 #define HFLASH_TIMER_ID 0
@ -30,17 +25,13 @@ enum eHFlashMemIO
}; };
///< 内存缓存页, 配置需和 (memSize / 4096) 大小一致 ///< 内存缓存页, 配置需和 (memSize / 4096) 大小一致
///< 这里不再使用 heat 衰减, 改成 referenced + probation 的二次机会策略
///< 旧方案需要周期性降温, 小缓存且写入频繁时维护成本高, 也更容易把一次性读页长期留在缓存里
typedef struct HFlashMemCache typedef struct HFlashMemCache
{ {
uint32_t addr; ///< 地址 uint32_t addr; ///< 地址
uint16_t offset; ///< 内存偏移 uint16_t offset; ///< 内存偏移
uint16_t eraseStatus : 2; ///< 擦除状态 uint16_t eraseStatus : 2; ///< 擦除状态
uint16_t isDirty : 1; ///< 是否脏页 uint16_t isDirty : 1; ///< 是否脏页
uint16_t referenced : 1; ///< 最近是否访问过, 仅在命中/装载时置1, 淘汰扫描时清0给第二次机会 uint16_t heat : 5; ///< 热度优先级
uint16_t probation : 1; ///< 新进入缓存的试用页, 只有再次命中后才转正式页, 这样能优先淘汰一次性访问
uint16_t reserved : 11;
} HFlashMemCache; } HFlashMemCache;
///< 内存操作, 回调返回0表示失败 ///< 内存操作, 回调返回0表示失败
@ -51,16 +42,14 @@ typedef struct HFlashMemOpts
uint8_t (*ioctl)(uint32_t addr, uint32_t cmd, void *arg); ///< 控制Flash, 需要外部实现以上eHFlashMemIO指令 uint8_t (*ioctl)(uint32_t addr, uint32_t cmd, void *arg); ///< 控制Flash, 需要外部实现以上eHFlashMemIO指令
uint8_t *mem; ///< Flash映射内存, 需要4k倍数 uint8_t *mem; ///< Flash映射内存, 需要4k倍数
HFlashMemCache *cache; ///< 内存缓存 HFlashMemCache *cache; ///< 内存缓存
uint32_t memSize; ///< Flash映射内存大小, 改成32位后可避免映射区超过64KB时页数计算溢出 uint16_t memSize; ///< Flash映射内存大小
uint32_t cacheSize; ///< 内存缓存页数量, 需要始终等于 memSize / 4096, 否则 offset 映射会错位 uint16_t cacheSize; ///< 内存缓存大小
uint32_t flashSize; ///< Flash大小 uint32_t flashSize; ///< Flash大小
} HFlashMemOpts; } HFlashMemOpts;
/** /**
* @brief Flash内存映射 * @brief Flash内存映射
* @param opts * @param opts
* mem/cache ,
* cacheSize memSize / 4096 , cache 4K映射页
*/ */
void HFlashMemInit(HFlashMemOpts *opts); void HFlashMemInit(HFlashMemOpts *opts);

View File

@ -3,31 +3,34 @@
#include "HDLog.h" #include "HDLog.h"
#include "HTimer.h" #include "HTimer.h"
#ifndef USE_STD_MEM
#if HFLASH_USE_HSHELL
#include "HShellLex.h"
#endif
#endif
#ifdef USE_STD_MEM #ifdef USE_STD_MEM
#define FATAL_ERROR(format, ...) LogE(format, ##__VA_ARGS__); #define FATAL_ERROR(format, ...) LogE(format, ##__VA_ARGS__);
#elif HFLASH_USE_HSHELL
#define FATAL_ERROR(format, ...) while (1) { LogE(format, ##__VA_ARGS__); HShellRun(); };
#else #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 #endif
// flash块大小 // flash块大小
#define HFLASH_BLOCK_SIZE (4096) #define HFLASH_BLOCK_SIZE (4096)
// 调度热度时间
#define HEAT_TIME_MS (1000)
// 是否对齐页 // 是否对齐页
#define IS_4K(size) ((size & (HFLASH_BLOCK_SIZE - 1)) == 0) #define IS_4K(size) ((size & (HFLASH_BLOCK_SIZE - 1)) == 0)
// 这里允许少量读 miss 直接绕过缓存 // 最大热度
// 当缓存已被脏页占满时, 如果每次只读 miss 都强制抢占缓存, 会触发同步刷写, 小缓存场景抖动很大 #define MAX_HEAT ((1 << 5) - 1)
// 因此只有同一页连续被直读多次, 才认为它值得挤进缓存
// 全脏页下, 同一页连续直读达到该次数后才提升进缓存 // 写转读增加热度
#define BYPASS_PROMOTE_THRESHOLD (3) #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 enum eErase
{ {
@ -38,13 +41,9 @@ enum eErase
struct Info struct Info
{ {
uint32_t bypassAddr; ///< 全脏页读 miss 时, 最近一次直读页地址 uint32_t currHeatTime; ///< 当前热度时间, 用于计算下一次热度下降
uint16_t useNum; ///< 缓存已使用数量 uint16_t useNum; ///< 缓存已使用数量
uint16_t readClockHand; ///< 读页淘汰时的时钟指针
uint16_t dirtyClockHand; ///< 脏页回收时的时钟指针
uint8_t bypassCount; ///< 当前直读页连续命中次数
uint16_t needWaitErase : 1; ///< 用于判断是否在擦除 uint16_t needWaitErase : 1; ///< 用于判断是否在擦除
uint16_t bypassValid : 1; ///< 最近一次直读页地址是否有效
}; };
// 内存操作 // 内存操作
@ -56,16 +55,6 @@ static struct Info sInfo;
// 检查定时器 // 检查定时器
static HTimer_t sCheckTimer = HTIMER_INVALID; 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) { static inline uint32_t Align4K(uint32_t addr) {
return addr & ~(HFLASH_BLOCK_SIZE - 1); 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; 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() 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); LogD("Sync[%d], addr[0x%08x], offset[%d]", i, sOpts->cache[i].addr, sOpts->cache[i].offset);
if (SyncDirtyCache(sOpts->cache + i) == 0) { sOpts->ioctl(sOpts->cache[i].addr, kHFlashMemIOSyncErase, NULL);
LogE("Sync[%d], addr[0x%08x], offset[%d] faild", i, sOpts->cache[i].addr, sOpts->cache[i].offset); 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;
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;
} }
if (fallback) { sOpts->cache[i].isDirty = 0;
return fallback; 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() static void NotifyASyncErase()
{ {
HFlashMemCache *cache = FindDirtyVictim(); for (uint16_t i = 0; i < sInfo.useNum; ++i) {
if (cache) { if (sOpts->cache[i].isDirty == 0) {
// 这里只发起一个异步擦除请求 continue;
// CheckSyncCache 依赖 needWaitErase + eraseStatus 顺序推进状态机, 多个并发擦除会让状态归属变复杂 }
sInfo.needWaitErase = 1; sInfo.needWaitErase = 1;
cache->eraseStatus = kEraseStart; sOpts->cache[i].eraseStatus = kEraseStart;
sOpts->ioctl(cache->addr, kHFlashMemIOErase, NULL); sOpts->ioctl(sOpts->cache[i].addr, kHFlashMemIOErase, NULL);
break;
} }
} }
static void StartSyncCache() static void FastSyncCache()
{ {
if (HasDirtyCache() == 0) { WatiErase();
return; 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) { NotifyASyncErase();
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;
} }
static void CheckSyncCache() static void CheckSyncCache()
{ {
// 检查擦除是否完成
uint8_t status = 0; uint8_t status = 0;
if (sInfo.needWaitErase) { if (sInfo.needWaitErase) {
sOpts->ioctl(0, kHFlashMemIOCheckErase, &status); sOpts->ioctl(0, kHFlashMemIOCheckErase, &status);
@ -352,44 +158,63 @@ static void CheckSyncCache()
} }
sInfo.needWaitErase = 0; sInfo.needWaitErase = 0;
for (uint16_t i = 0; i < sInfo.useNum; ++i) { }
if (sOpts->cache[i].eraseStatus == kEraseStart) {
sOpts->cache[i].eraseStatus = kWaitWrite; uint16_t needErase = sInfo.useNum;
break; 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 侧会一直看不到可回收页 if (sOpts->cache[i].isDirty == 0) {
for (uint16_t i = 0; i < sInfo.useNum; ++i) {
HFlashMemCache *cache = sOpts->cache + i;
if (cache->isDirty == 0 || cache->eraseStatus != kWaitWrite) {
continue; continue;
} }
if (sOpts->write(cache->addr, GetMMap(cache->offset), HFLASH_BLOCK_SIZE) == 0) { // 检查Flash是否已经擦除完成
LogE("addr[0x%08x], write faild", cache->addr); if (sOpts->cache[i].eraseStatus != kWaitWrite) {
cache->eraseStatus = kEraseWait;
continue; continue;
} }
MarkCleanCache(cache); // 如果写入失败, 则需要再次擦除重新写入
} if (sOpts->write(sOpts->cache[i].addr, GetMMap(i), HFLASH_BLOCK_SIZE) == 0) {
LogE("addr[0x%08x], write faild", sOpts->cache[i].addr);
uint8_t hasPendingWrite = 0; sInfo.needWaitErase = 1;
for (uint16_t i = 0; i < sInfo.useNum; ++i) { sOpts->cache[i].eraseStatus = kEraseWait;
if (sOpts->cache[i].isDirty && sOpts->cache[i].eraseStatus == kWaitWrite) { if (needErase == sInfo.useNum) {
hasPendingWrite = 1; heat = sOpts->cache[i].heat;
break; 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()) { if (needErase != sInfo.useNum) {
NotifyASyncErase(); 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) { if (sCheckTimer != HTIMER_INVALID) {
HTimerRemove(sCheckTimer); HTimerRemove(sCheckTimer);
sCheckTimer = HTIMER_INVALID; 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); addr = Align4K(addr);
if (addr + HFLASH_BLOCK_SIZE > sOpts->flashSize) { 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; return NULL;
} }
HFlashMemCache *cache = FindCacheByAddr(addr); for (uint16_t i = 0; i < sInfo.useNum; ++i) {
if (cache) { if (addr != sOpts->cache[i].addr) {
ClearBypassAddr(addr); continue;
TouchCache(cache); }
return cache;
sOpts->cache[i].heat = FAST_MIN(uint8_t, sOpts->cache[i].heat + 1, MAX_HEAT);
return sOpts->cache + i;
} }
// 如果缓存没有满, 直接增加 // 如果缓存没有满, 直接增加
if (sInfo.useNum < sOpts->cacheSize) { if (sInfo.useNum < sOpts->cacheSize) {
const uint16_t index = sInfo.useNum; const uint16_t index = sInfo.useNum;
cache = sOpts->cache + index; ++sInfo.useNum;
InitCache(cache, index, addr, 1);
if (needRead) { 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; return NULL;
} }
} }
ClearBypassAddr(addr); memset(sOpts->cache + index, 0, sizeof(HFlashMemCache));
++sInfo.useNum; sOpts->cache[index].addr = addr;
return cache; 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) { HFlashMemCache *cache = FindMinHeatCache();
// bypass 只允许真正的读 miss if (cache == NULL || cache->heat > READ_MIN_HEAT) {
// 普通读接口可以接受本次直接读 flash, 但像 mmap/callback 这种需要稳定页指针的场景必须强制拿到 cache
RecordBypassAddr(addr);
if (sInfo.bypassCount < BYPASS_PROMOTE_THRESHOLD) {
return NULL;
}
}
if (cache == NULL) {
cache = ReclaimOneCleanCache();
}
if (cache == NULL) {
return NULL; return NULL;
} }
const uint16_t index = cache->offset; 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 (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; return NULL;
} }
} }
ClearBypassAddr(addr);
return cache; return cache;
} }
@ -466,47 +349,38 @@ static HFlashMemCache *FindWriteCache(uint32_t addr, uint8_t needRead)
return NULL; return NULL;
} }
HFlashMemCache *cache = FindCacheByAddr(addr); HFlashMemCache *cache = FindReadCache(addr, needRead);
if (cache) { if (cache) {
ClearBypassAddr(addr);
TouchCache(cache);
return cache; return cache;
} }
if (sInfo.useNum < sOpts->cacheSize) { for (;;) {
const uint16_t index = sInfo.useNum; // 写入模式下, 需要抢占缓存页
cache = sOpts->cache + index; cache = FindMinHeatCache();
InitCache(cache, index, addr, 0); if (cache == NULL) {
if (needRead) { // 如果在创建模式下, 没有一个空闲的缓存, 就需要先同步脏页, 再获取一个
if (LoadCachePage(cache, addr) == 0) { FastSyncCache();
return NULL; 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; 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 (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; 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; return 0;
} }
// 开启逐步同步
StartSyncCache();
// 找到映射表, 先处理首页 // 找到映射表, 先处理首页
const uint8_t *bufPtr = (const uint8_t *)buf; const uint8_t *bufPtr = (const uint8_t *)buf;
const uint32_t offset = addr - cache->addr; 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); cpCall(GetMMap(cache->offset), bufPtr, len, userData);
} }
// 写路径只负责标脏, 后台异步回收逐步推进, 避免小缓存下同步冲刷
StartSyncCache();
return 1; return 1;
} }
@ -586,7 +461,7 @@ static uint8_t FindReadPageCopy(uint32_t addr, void *buf, uint32_t len)
return 0; return 0;
} }
HFlashMemCache *cache = FindReadCache(pageAddr, 1, 1); HFlashMemCache *cache = FindReadCache(pageAddr, 1);
if (cache) { if (cache) {
memcpy(p, GetMMap(cache->offset) + offset, copyLen); memcpy(p, GetMMap(cache->offset) + offset, copyLen);
} else { } else {
@ -626,16 +501,6 @@ void HFlashMemInit(HFlashMemOpts *opts)
FATAL_ERROR("flash Size not 4k align"); FATAL_ERROR("flash Size not 4k align");
return ; return ;
} }
// 重新初始化时会把运行态和定时器一并清掉
// 这些状态记录的是上一次 cache 使用轨迹和异步擦除进度, 沿用会把新配置带进旧状态机
memset(&sInfo, 0, sizeof(sInfo));
if (sCheckTimer != HTIMER_INVALID) {
HTimerRemove(sCheckTimer);
sCheckTimer = HTIMER_INVALID;
}
ResetCacheState();
} }
const HFlashMemOpts *HFlashMemGetOpt() const HFlashMemOpts *HFlashMemGetOpt()
@ -653,6 +518,7 @@ uint8_t HFlashMemRead(uint32_t addr, void *buf, uint32_t len)
return 1; return 1;
} }
ScheduleHeat();
uint8_t *p = (uint8_t *)buf; uint8_t *p = (uint8_t *)buf;
while (len > 0) { while (len > 0) {
const uint32_t pageAddr = Align4K(addr); const uint32_t pageAddr = Align4K(addr);
@ -664,7 +530,7 @@ uint8_t HFlashMemRead(uint32_t addr, void *buf, uint32_t len)
return 0; return 0;
} }
HFlashMemCache *cache = FindReadCache(pageAddr, 1, 1); HFlashMemCache *cache = FindReadCache(pageAddr, 1);
if (cache) { if (cache) {
memcpy(p, GetMMap(cache->offset) + offset, copyLen); memcpy(p, GetMMap(cache->offset) + offset, copyLen);
} else { } else {
@ -699,6 +565,7 @@ uint8_t HFlashMemWrite(uint32_t addr, const void *buf, uint32_t len)
return 1; return 1;
} }
ScheduleHeat();
return WriteData(addr, buf, len, _MemCpyHelper, NULL); 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; return 0;
} }
ScheduleHeat();
return WriteData(addr, NULL, len, _MemSetValueHelper, &value); 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; return 0;
} }
ScheduleHeat();
return WriteData(destAddr, NULL, len, _MemAddrCopy, &srcAddr); 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; return 0;
} }
ScheduleHeat();
uint32_t offset = 0; uint32_t offset = 0;
HFlashMemCache *cache = NULL;
uint16_t len = 0; uint16_t len = 0;
uint8_t result = 0; uint8_t result = 0;
// 先处理未对齐的部分 // 先处理未对齐的部分
if (addr != Align4K(addr)) { if (addr != Align4K(addr)) {
// 这里显式禁止 bypass cache = FindWriteCache(addr + offset, 1);
// 回调拿到的是 cache 内存指针, 如果退化成直读 flash, 本接口就无法提供连续且可复用的页视图
HFlashMemCache *cache = FindReadCache(addr + offset, 1, 0);
if (cache == NULL) { if (cache == NULL) {
LogE("addr[0x%08x], find cache faild", addr); LogE("addr[0x%08x], find cache faild", addr);
return 0; return 0;
@ -763,7 +632,7 @@ uint8_t HFlashMemMMapCall(uint32_t addr, uint8_t(*call)(uint32_t addr, uint32_t
// 剩余处理对齐部分 // 剩余处理对齐部分
while (result == 0) { while (result == 0) {
HFlashMemCache *cache = FindReadCache(addr + offset, 1, 0); cache = FindWriteCache(addr + offset, 1);
if (cache == NULL) { if (cache == NULL) {
LogE("addr[0x%08x], find cache faild", addr + offset); LogE("addr[0x%08x], find cache faild", addr + offset);
return 0; return 0;
@ -782,20 +651,14 @@ uint8_t HFlashMemMMapCall(uint32_t addr, uint8_t(*call)(uint32_t addr, uint32_t
void HFlashMemSync() void HFlashMemSync()
{ {
if (sCheckTimer != HTIMER_INVALID) { ScheduleHeat();
HTimerRemove(sCheckTimer);
sCheckTimer = HTIMER_INVALID;
}
SyncCache(); SyncCache();
} }
void HFlashMemFreeCache() void HFlashMemFreeCache()
{ {
HFlashMemSync(); HFlashMemSync();
if (sCheckTimer != HTIMER_INVALID) { sInfo.useNum = 0;
HTimerRemove(sCheckTimer); sInfo.currHeatTime = HDLogGetTime();
sCheckTimer = HTIMER_INVALID;
}
ResetCacheState();
memset(&sInfo, 0, sizeof(sInfo));
} }

View File

@ -1449,13 +1449,12 @@ static void Shell(HSHELL_FUNC_ARGS)
} }
for (uint8_t i = 0; i < opt->cacheSize; ++i) { 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].addr,
opt->cache[i].offset, opt->cache[i].offset,
opt->cache[i].eraseStatus, opt->cache[i].eraseStatus,
opt->cache[i].isDirty, opt->cache[i].isDirty,
opt->cache[i].referenced, opt->cache[i].heat);
opt->cache[i].probation);
} }
} break; } break;
#endif #endif