1. 修正回旧版
This commit is contained in:
parent
1a2afd46f6
commit
c70549293b
@ -11,11 +11,6 @@
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
// 是否使用 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);
|
||||
|
||||
|
||||
587
src/HFlashMem.c
587
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();
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user