Cosmos OpenSSD--greedy_ftl1.2.0(一)
从主函数跳到ReqHandler,在ReqHandler内先初始化SSD--InitNandReset,然后建立映射表InitFtlMapTable
1 void InitNandReset() 2 { 3 // reset SSD 4 int i, j; 5 for(i=0; i<CHANNEL_NUM; ++i) 6 { 7 for(j=0; j<WAY_NUM; ++j) 8 { 9 WaitWayFree(i, j); 10 SsdReset(i, j); 11 } 12 } 13 14 // change SSD mode 15 for(i=0; i<CHANNEL_NUM; ++i) 16 { 17 for(j=0; j<WAY_NUM; ++j) 18 { 19 WaitWayFree(i, j); 20 SsdModeChange(i, j); 21 } 22 } 23 24 print("\n[ ssd NAND device reset complete. ]\r\n"); 25 }
遍历每条channel每条way来重启,change mode
接下来来看怎么建立映射表InitFtlMapTable
1 void InitFtlMapTable() 2 { 3 InitPageMap(); 4 InitBlockMap(); 5 InitDieBlock(); 6 7 InitGcMap(); 8 }
这里有四步,我们一步一步来分析
首先是页表建立,InitPageMap
#define RAM_DISK_BASE_ADDR 0x10000000
#define PAGE_MAP_ADDR (RAM_DISK_BASE_ADDR + (0x1 << 27)) //PAGE_MAP_ADDR =0x18000000
#define PAGE_NUM_PER_DIE (PAGE_NUM_PER_BLOCK * BLOCK_NUM_PER_DIE)
struct pmEntry {
u32 ppn; // Physical Page Number (PPN) to which a logical page is mapped
u32 valid : 1; // validity of a physical page
u32 lpn : 31; // Logical Page Number (LPN) of a physical page
};
每个entry页表构造如下图
每个入口8Byte
struct pmArray {
struct pmEntry pmEntry[DIE_NUM][PAGE_NUM_PER_DIE]; //页表entry个数为DIE_NUM * PAGE_NUM_PER_DIE = 16*4096*256 = 224
};
这样页表大小就为 224 * 8Byte = 128MB
1 void InitPageMap() 2 { 3 pageMap = (struct pmArray*)(PAGE_MAP_ADDR); 4 5 // page status initialization, allows lpn, ppn access 6 int i, j; 7 for(i=0 ; i<DIE_NUM ; i++) 8 { 9 for(j=0 ; j<PAGE_NUM_PER_DIE ; j++) 10 { 11 pageMap->pmEntry[i][j].ppn = 0xffffffff; 12 13 pageMap->pmEntry[i][j].valid = 1; 14 pageMap->pmEntry[i][j].lpn = 0x7fffffff; 15 } 16 } 17 18 xil_printf("[ ssd page map initialized. ]\r\n"); 19 }
这里将设置每个页表entry的初始值,
接下来分析InitBlockMap
#define BLOCK_MAP_ADDR (PAGE_MAP_ADDR + sizeof(struct pmEntry) * PAGE_NUM_PER_SSD) //块表是在页表之后继续建立
struct bmEntry {
u32 bad : 1;
u32 free : 1;
u32 eraseCnt : 30;
u32 invalidPageCnt : 16;
u32 currentPage : 16;
u32 prevBlock;
u32 nextBlock;
};
每个块entry构造图如下,占据16Byte
struct bmArray {
struct bmEntry bmEntry[DIE_NUM][BLOCK_NUM_PER_DIE]; //块表入口数为 16 * 4096 = 216,所以块表大小为216 * 16Byte = 1MB
};
分配块表之后,首先先检测坏块--CheckBadBlock
blockMap = (struct bmArray*)(BLOCK_MAP_ADDR); u32 dieNo, diePpn, blockNo, tempBuffer, badBlockCount; u8* shifter; u8* markPointer; int loop; markPointer = (u8*)(RAM_DISK_BASE_ADDR + BAD_BLOCK_MARK_POSITION);
#define BAD_BLOCK_MARK_POSITION (7972) //代表着坏块标记的偏移量
//read badblock marks loop = DIE_NUM *BLOCK_NUM_PER_DIE; dieNo = METADATA_BLOCK_PPN % DIE_NUM; diePpn = METADATA_BLOCK_PPN / DIE_NUM; tempBuffer = RAM_DISK_BASE_ADDR; while(loop > 0) { SsdRead(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, diePpn, tempBuffer); WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM); diePpn++; tempBuffer += PAGE_SIZE; loop -= PAGE_SIZE; }
疑问:dieNo =0,diePpn=0,进入循环之后,读取channel0,way0的第01234567页存在tempbuffer里面,8页大小为64KB,一个字节记录一个块的信息的话,那么大小也为1Byte*16*4096=64KB,其中因为第一个块厂家保证是好的,所以不需要保存是否为坏块,所以里面可以存一个标记位,表示是否有现成的坏块信息表
1 if(*shifter == EMPTY_BYTE) //check whether badblock marks exist 2 { 3 // static bad block management 4 for(blockNo=0; blockNo < BLOCK_NUM_PER_DIE; blockNo++) 5 for(dieNo=0; dieNo < DIE_NUM; dieNo++) 6 { 7 blockMap->bmEntry[dieNo][blockNo].bad = 0; 8 9 SsdRead(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, (blockNo*PAGE_NUM_PER_BLOCK+1), RAM_DISK_BASE_ADDR); 10 WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM); 11 12 if(CountBits(*markPointer)<4) 13 { 14 xil_printf("Bad block is detected on: Ch %d Way %d Block %d \r\n",dieNo%CHANNEL_NUM, dieNo/CHANNEL_NUM, blockNo); 15 blockMap->bmEntry[dieNo][blockNo].bad = 1; 16 badBlockCount++; 17 } 18 shifter= (u8*)(GC_BUFFER_ADDR + blockNo + dieNo *BLOCK_NUM_PER_DIE );//gather badblock mark at GC buffer 19 *shifter = blockMap->bmEntry[dieNo][blockNo].bad; 20 } 21 22 // save bad block mark 23 loop = DIE_NUM *BLOCK_NUM_PER_DIE; 24 dieNo = METADATA_BLOCK_PPN % DIE_NUM; 25 diePpn = METADATA_BLOCK_PPN / DIE_NUM; 26 blockNo = diePpn / PAGE_NUM_PER_BLOCK; 27 28 SsdErase(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, blockNo); 29 WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM); 30 31 tempBuffer = GC_BUFFER_ADDR; 32 while(loop>0) 33 { 34 WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM); 35 SsdProgram(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, diePpn, tempBuffer); 36 diePpn++; 37 tempBuffer += PAGE_SIZE; 38 loop -= PAGE_SIZE; 39 } 40 xil_printf("[ Bad block Marks are saved. ]\r\n"); 41 }
第九行为什么是读取每个块第一页的内容而不是第零页的内容?
12行位数小于4位就是坏块?
else //read existing bad block marks { for(blockNo=0; blockNo<BLOCK_NUM_PER_DIE; blockNo++) for(dieNo=0; dieNo<DIE_NUM; dieNo++) { shifter = (u8*)(RAM_DISK_BASE_ADDR + blockNo + dieNo *BLOCK_NUM_PER_DIE ); blockMap->bmEntry[dieNo][blockNo].bad = *shifter; if(blockMap->bmEntry[dieNo][blockNo].bad) { xil_printf("Bad block mark is checked at: Ch %d Way %d Block %d \r\n",dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, blockNo ); badBlockCount++; } } xil_printf("[ Bad blocks are checked. ]\r\n"); } // save bad block size BAD_BLOCK_SIZE = badBlockCount * BLOCK_SIZE_MB;
接下来是InitBlockMap的代码
blockMap = (struct bmArray*)(BLOCK_MAP_ADDR); CheckBadBlock(); // block status initialization except bad block marks, allows only physical access int i, j; for(i=0 ; i<BLOCK_NUM_PER_DIE ; i++) { for(j=0 ; j<DIE_NUM ; j++) { blockMap->bmEntry[j][i].free = 1; blockMap->bmEntry[j][i].eraseCnt = 0; blockMap->bmEntry[j][i].invalidPageCnt = 0; blockMap->bmEntry[j][i].currentPage = 0x0; blockMap->bmEntry[j][i].prevBlock = 0xffffffff; blockMap->bmEntry[j][i].nextBlock = 0xffffffff; } }
初始化块表的一些值
for (i = 0; i < BLOCK_NUM_PER_DIE; ++i) for (j = 0; j < DIE_NUM; ++j) if (!blockMap->bmEntry[j][i].bad && ((i != METADATA_BLOCK_PPN % DIE_NUM)|| (j != (METADATA_BLOCK_PPN / DIE_NUM) / PAGE_NUM_PER_BLOCK))) { // initial block erase WaitWayFree(j % CHANNEL_NUM, j / CHANNEL_NUM); SsdErase(j % CHANNEL_NUM, j / CHANNEL_NUM, i); }
xil_printf("[ ssd entire block erasure completed. ]\r\n");
除了die0的block0之外,全部擦除
for(i=0 ; i<DIE_NUM ; i++) { // initially, 0th block of each die is allocated for storage start point blockMap->bmEntry[i][0].free = 0; blockMap->bmEntry[i][0].currentPage = 0xffff; // initially, the last block of each die is reserved as free block for GC migration blockMap->bmEntry[i][BLOCK_NUM_PER_DIE-1].free = 0; } //block0 of die0 is metadata block blockMap->bmEntry[0][1].free = 0; blockMap->bmEntry[0][1].currentPage = 0xffff; xil_printf("[ ssd block map initialized. ]\r\n");
因为die0的第一个block是用来存储元数据,所以他开始的块指针为第一块
每个die的开始和最后一块都不能用,die0的第一块也不让用
#define DIE_MAP_ADDR (BLOCK_MAP_ADDR + sizeof(struct bmEntry) * BLOCK_NUM_PER_SSD)
struct dieEntry {
u32 currentBlock;
u32 freeBlock;
};
struct dieArray {
struct dieEntry dieEntry[DIE_NUM];
};
void InitDieBlock() { dieBlock = (struct dieArray*)(DIE_MAP_ADDR); // xil_printf("DIE_MAP_ADDR : %8x\r\n", DIE_MAP_ADDR); int i; for(i=0 ; i<DIE_NUM ; i++) { if(i==0) // prevent to write at meta data block dieBlock->dieEntry[i].currentBlock = 1; else dieBlock->dieEntry[i].currentBlock = 0; dieBlock->dieEntry[i].freeBlock = BLOCK_NUM_PER_DIE - 1; } xil_printf("[ ssd die map initialized. ]\r\n"); }
freeblock用作垃圾回收
struct gcEntry {
u32 head;
u32 tail;
};
struct gcArray {
struct gcEntry gcEntry[DIE_NUM][PAGE_NUM_PER_BLOCK+1];
};
void InitGcMap() { gcMap = (struct gcArray*)(GC_MAP_ADDR); // xil_printf("GC_MAP_ADDR : %8x\r\n", GC_MAP_ADDR); // gc table status initialization int i, j; for(i=0 ; i<DIE_NUM ; i++) { for(j=0 ; j<PAGE_NUM_PER_BLOCK+1 ; j++) { gcMap->gcEntry[i][j].head = 0xffffffff; gcMap->gcEntry[i][j].tail = 0xffffffff; } } xil_printf("[ ssd gc map initialized. ]\r\n"); }