我们来假设模拟一个小型的模型来分析写和垃圾回收的过程

假设只有一个die,4个block,每个block4个page,每个page8KB

那么PageMap就是Page[0][0]到Page[0][15]

BlockMap就是Block[0][0]到Block[0][3]

GcMap就是GC[0][0]到GC[0][4]

那么最初就会如下图:第零块用来保留元数据,最后一块保留作为垃圾回收合并块


接下来从上层来了数据,黄色部分表示更改的数据,红色部分表示更新的数据,最左侧是流程

在这里,lpn0的内容作为page缓存留在SDRAM里面,只把lpn1的内容存入flash


接下来更改的数据缓存命中了,所以lpn0的内容直接在SDRAM里面修改,此时数据末尾刚好占据了一个页,所以直接存入flash,并把之前的保存页无效,此时GC表开始变化


接下来依旧缓存命中,但是请求最后一页并不是完整的一页,于是先预读出其中的内容,修改之后寻找新页再保存进flash


这一次的缓存并没有命中,所以先把SDRAM里面的内容flush进SDRAM里面,因为请求并不满足一页,所以执行read-modify-write,此时lpn1作为新的page缓存,并不直接存进flash而是保留在SDRAM中


缓存依旧不命中,此时block1已经没有页面可用了,于是找到了下一个空闲block,先flush,然后更新page缓存,将lpn1的内容存入flash


接下来缓存不命中,并且消耗掉了所有的页面,于是lpn3的内容无处存取,引发垃圾回收操作


垃圾回收操作的依据是元数据的更新,因此我们来看元数据更新的代码

 void UpdateMetaForOverwrite(u32 lpn)
{
pageMap = (struct pmArray*)(PAGE_MAP_ADDR);
blockMap = (struct bmArray*)(BLOCK_MAP_ADDR);
gcMap = (struct gcArray*)(GC_MAP_ADDR); u32 dieNo = lpn % DIE_NUM;
u32 dieLpn = lpn / DIE_NUM; if(pageMap->pmEntry[dieNo][dieLpn].ppn != 0xffffffff)
{
// GC victim block list management
u32 diePbn = pageMap->pmEntry[dieNo][dieLpn].ppn / PAGE_NUM_PER_BLOCK; // unlink
if((blockMap->bmEntry[dieNo][diePbn].nextBlock != 0xffffffff) && (blockMap->bmEntry[dieNo][diePbn].prevBlock != 0xffffffff))
{
blockMap->bmEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].prevBlock].nextBlock = blockMap->bmEntry[dieNo][diePbn].nextBlock;
blockMap->bmEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].nextBlock].prevBlock = blockMap->bmEntry[dieNo][diePbn].prevBlock;
}
else if((blockMap->bmEntry[dieNo][diePbn].nextBlock == 0xffffffff) && (blockMap->bmEntry[dieNo][diePbn].prevBlock != 0xffffffff))
{
blockMap->bmEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].prevBlock].nextBlock = 0xffffffff;
gcMap->gcEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].invalidPageCnt].tail = blockMap->bmEntry[dieNo][diePbn].prevBlock;
}
else if((blockMap->bmEntry[dieNo][diePbn].nextBlock != 0xffffffff) && (blockMap->bmEntry[dieNo][diePbn].prevBlock == 0xffffffff))
{
blockMap->bmEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].nextBlock].prevBlock = 0xffffffff;
gcMap->gcEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].invalidPageCnt].head = blockMap->bmEntry[dieNo][diePbn].nextBlock;
}
else
{
gcMap->gcEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].invalidPageCnt].head = 0xffffffff;
gcMap->gcEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].invalidPageCnt].tail = 0xffffffff;
} // xil_printf("[unlink] dieNo = %d, invalidPageCnt= %d, diePbn= %d, blockMap.prevBlock= %d, blockMap.nextBlock= %d, gcMap.head= %d, gcMap.tail= %d\r\n", dieNo, blockMap->bmEntry[dieNo][diePbn].invalidPageCnt, diePbn, blockMap->bmEntry[dieNo][diePbn].prevBlock, blockMap->bmEntry[dieNo][diePbn].nextBlock, gcMap->gcEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].invalidPageCnt].head, gcMap->gcEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].invalidPageCnt].tail); // invalidation update
pageMap->pmEntry[dieNo][pageMap->pmEntry[dieNo][dieLpn].ppn].valid = ;
blockMap->bmEntry[dieNo][diePbn].invalidPageCnt++; // insertion
if(gcMap->gcEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].invalidPageCnt].tail != 0xffffffff)
{
blockMap->bmEntry[dieNo][diePbn].prevBlock = gcMap->gcEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].invalidPageCnt].tail;
blockMap->bmEntry[dieNo][diePbn].nextBlock = 0xffffffff;
blockMap->bmEntry[dieNo][gcMap->gcEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].invalidPageCnt].tail].nextBlock = diePbn;
gcMap->gcEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].invalidPageCnt].tail = diePbn;
}
else
{
blockMap->bmEntry[dieNo][diePbn].prevBlock = 0xffffffff;
blockMap->bmEntry[dieNo][diePbn].nextBlock = 0xffffffff;
gcMap->gcEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].invalidPageCnt].head = diePbn;
gcMap->gcEntry[dieNo][blockMap->bmEntry[dieNo][diePbn].invalidPageCnt].tail = diePbn;
}
}
}

其实从上面的写过程分析,我们可以大概了解到GC表其实就是把有相同无效页的block串联在一起

我们假设从block1到block7,其中无效页最终分别为100,120,110,100,110,120,100

当block1进行完之后,GC[0][100].head=1  GC[0][100].tail=1

当block2的无效页也到达100的时候,分析代码可知,GC[0][100].head=1  GC[0][100].tail=2,而且block1和block2会链接起来,但是block2的无效页会继续增加,于是当他的无效页增加的时候,block1和block2又会断开,指向无效的0xffffffff,而block2执行完毕之后GC[0][120].head=2  GC[0][120].tail=2.

只有当block4和block7的无效页最终停在100的时候,block1↔block4↔block7


既然已经了解GC表表示的什么意思,那么再看垃圾回收操作的代码就简单多了

 u32 GarbageCollection(u32 dieNo)
{
xil_printf("GC occurs!\r\n"); pageMap = (struct pmArray*)(PAGE_MAP_ADDR);
blockMap = (struct bmArray*)(BLOCK_MAP_ADDR);
dieBlock = (struct dieArray*)(DIE_MAP_ADDR);
gcMap = (struct gcArray*)(GC_MAP_ADDR); int i;
for(i=PAGE_NUM_PER_BLOCK ; i> ; i--)      //从无效页大的开始回收
{
if(gcMap->gcEntry[dieNo][i].head != 0xffffffff)  //拥有这么多无效页的块存在的话,取出一块进行回收
{
u32 victimBlock = gcMap->gcEntry[dieNo][i].head; // GC victim block // link setting
if(blockMap->bmEntry[dieNo][victimBlock].nextBlock != 0xffffffff)  //更新GC链表
{
gcMap->gcEntry[dieNo][i].head = blockMap->bmEntry[dieNo][victimBlock].nextBlock;
blockMap->bmEntry[dieNo][blockMap->bmEntry[dieNo][victimBlock].nextBlock].prevBlock = 0xffffffff;
}
else
{
gcMap->gcEntry[dieNo][i].head = 0xffffffff;
gcMap->gcEntry[dieNo][i].tail = 0xffffffff;
} // copy valid pages from the victim block to the free block
if(i != PAGE_NUM_PER_BLOCK)    //如果整个块都是无效页就直接擦除就行了
{
int j;
for(j= ; j<PAGE_NUM_PER_BLOCK ; j++)  //
{
if(pageMap->pmEntry[dieNo][(victimBlock * PAGE_NUM_PER_BLOCK) + j].valid)
{
// page copy process
u32 validPage = victimBlock*PAGE_NUM_PER_BLOCK + j;
u32 freeBlock = dieBlock->dieEntry[dieNo].freeBlock;  //最开始有预留一个块
u32 freePage = freeBlock*PAGE_NUM_PER_BLOCK + blockMap->bmEntry[dieNo][freeBlock].currentPage; WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
SsdRead(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, validPage, GC_BUFFER_ADDR);    //将一个块里面的有效页读取到BUFFER里面
WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
SsdProgram(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, freePage, GC_BUFFER_ADDR);  //有效数据写入 // pageMap, blockMap update
u32 lpn = pageMap->pmEntry[dieNo][validPage].lpn; pageMap->pmEntry[dieNo][lpn].ppn = freePage;      //更新页映射信息
pageMap->pmEntry[dieNo][freePage].lpn = lpn;
blockMap->bmEntry[dieNo][freeBlock].currentPage++;
}
}
} // erased victim block becomes the free block for GC migration
EraseBlock(dieNo, victimBlock);
blockMap->bmEntry[dieNo][victimBlock].free = ; u32 currentBlock = dieBlock->dieEntry[dieNo].freeBlock;
dieBlock->dieEntry[dieNo].freeBlock = victimBlock;      //将刚擦出的块作为新的freeblock return currentBlock; // atomic GC completion
}
} // no free space anymore
assert(!"[WARNING] There are no free blocks. Abort terminate this ssd. [WARNING]");
return ;
}

Cosmos OpenSSD--greedy_ftl1.2.0(三)的更多相关文章

  1. Aurora 8B/10B、PCIe 2.0、SRIO 2.0三种协议比较

    在高性能雷达信号处理机研制中,高速串行总线正逐步取代并行总线.业界广泛使用的Xilinx公司Virtex-6系列FPGA支持多种高速串行通信协议,本文针对其中较为常用的Aurora 8B/10B和PC ...

  2. [Android 4.4.3] 泛泰A860 Omni4.4.3 20140610 RC2.0 三版通刷 by syhost

    欢迎关注泛泰非盈利专业第三方开发团队 VegaDevTeam  (本team 由 syhost suky zhaochengw(z大) xuefy(大星星) tenfar(R大师) loogeo cr ...

  3. 13、Cocos2dx 3.0三,找一个小游戏开发3.0中间Director :郝梦主,一统江湖

    重开发人员的劳动成果.转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/27706967 游戏中的基本元素 在曾经文章中,我们具 ...

  4. 14、Cocos2dx 3.0三,找一个小游戏开发Scene and Layer:游戏梦想

    发人员的劳动成果,转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/30474393 Scene :场景 了解了Director ...

  5. 4、Cocos2dx 3.0三,找一个小游戏开发Hello World 分析

    尊重开发人员的劳动成果.转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/27186557 Hello World 分析 打开新 ...

  6. Apache Spark 2.0三种API的传说:RDD、DataFrame和Dataset

    Apache Spark吸引广大社区开发者的一个重要原因是:Apache Spark提供极其简单.易用的APIs,支持跨多种语言(比如:Scala.Java.Python和R)来操作大数据. 本文主要 ...

  7. II7.0 发布 MVC 4.0 三个小问题记录

    1,403.14-Forbidden Web 服务器被配置为不列出此目录的内容 根据提示更改:使用 IIS 管理器启用目录浏览. 打开 IIS 管理器. 在“功能”视图中,双击“目录浏览”. 在“目录 ...

  8. Linux 下从头再走 GTK+-3.0 (三)

    之前我们为窗口添加了一个按钮,接下来让这个按钮丰富一点.并给窗口加上图标. 首先创建 example3,c 的源文件. #include <gtk/gtk.h> static void a ...

  9. 8、Cocos2dx 3.0三,找一个小游戏开发3.0存储器管理的版本号

    重开发人员的劳动成果,转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/27693365 复杂的内存管理 移动设备上的硬件资源十 ...

  10. Cosmos OpenSSD架构分析--FSC

    接口速度: type   bw  read 75μs 1s/75μs*8k/1s=104m/s write 1300μs   1s/1300μs*8k/1s=6m/s erase 3.8ms  1s/ ...

随机推荐

  1. 201521123051《Java程序设计》第九周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. ·所有的异常类是从 java.lang.Exception 类继承的子类. ·Exception 类是 Throwa ...

  2. Eclipse rap 富客户端开发总结(9) : rap上传与下载

    一 上传 上传即将文件上传到服务器上,在客户端需要写相应的脚本,服务器端需要注册相应的 handle 接受客户端的请求. 原理: Rap 的上传和下载是通过普通的 web 的方式进行上传和下载的 ,  ...

  3. 非position:fixed,footer自動調到屏幕底部

    一.980px手機端寫法: $(function(){        var sh=$('section').height();     var ww=$(window).width();       ...

  4. EGit使用教程:第一篇 添加工程到版本控制

    配置 确定身份 当每次提交的时候,Git需要跟踪这次提交,确认是哪个用户提交的.用户由 user.name 和 user.email 组成,这个信息包含在 ~/.gitconfig 文件中. ~ 代表 ...

  5. windows 结束进程的详细过程

    windows上如何结束进程的详细过程,下面附详细,图文说明 在cmd下,输入  netstat   -ano|findstr  8080      //说明:查看占用8080端口的进程 在cmd下, ...

  6. 对redux的理解

     redux原理 某公司有物流(actionType).电商(actionType).广告(actionType)3块业务,在公司财务系统(state)统一记录着三块业务分别赚取到的资金.某天,电商业 ...

  7. Redis学习——Redis事务

    Redis和传统的关系型数据库一样,因为具有持久化的功能,所以也有事务的功能! 有关事务相关的概念和介绍,这里就不做介绍. 在学习Redis的事务之前,首先抛出一个面试的问题. 面试官:请问Redis ...

  8. 用css绘制各种图形

    1.用css绘制三角形 http://www.cnblogs.com/blosaa/p/3823695.html

  9. css3换行的三方式的对比(整理)

    CSS3 文本换行 (转载..) 作者 张歆琳 关注 2016.06.20 10:49* 字数 1101 阅读 676评论 1喜欢 6 文本换行其实是个非常常用但并不起眼的特性.你什么都不用设,浏览器 ...

  10. python堆栈实现

    百度百科定义: 堆栈是一个在计算机科学中经常使用的抽象数据类型.堆栈中的物体具有一个特性: 最后一个放入堆栈中的物体总是被最先拿出来, 这个特性通常称为后进先出(LIFO)队列. 堆栈中定义了一些操作 ...