zoukankan      html  css  js  c++  java
  • Cosmos OpenSSD--greedy_ftl1.2.0(二)

    FTL的整个流程如下:

    下面先来看写的流程:

    写的代码如下:

     1         if((hostCmd.reqInfo.Cmd == IDE_COMMAND_WRITE_DMA) ||  (hostCmd.reqInfo.Cmd == IDE_COMMAND_WRITE))
     2             {
     3 //                xil_printf("write(%d, %d)
    ", hostCmd.reqInfo.CurSect, hostCmd.reqInfo.ReqSect);
     4 
     5                 PrePmRead(&hostCmd, RAM_DISK_BASE_ADDR);
     6 
     7                 deviceAddr = RAM_DISK_BASE_ADDR + (hostCmd.reqInfo.CurSect % SECTOR_NUM_PER_PAGE)*SECTOR_SIZE;
     8                 reqSize = hostCmd.reqInfo.ReqSect * SECTOR_SIZE;
     9                 scatterLength = hostCmd.reqInfo.HostScatterNum;
    10 
    11                 DmaHostToDevice(&hostCmd, deviceAddr, reqSize, scatterLength);
    12 
    13                 PmWrite(&hostCmd, RAM_DISK_BASE_ADDR);
    14 
    15                 CompleteCmd(&hostCmd);
    16             }

    首先来看PrePmRead,其中最开始会涉及一个FlushPageBuf函数,FlushPageBuf里面有个FindFreePage函数,所以我们先分析FindFreePage函数的功能

    lpn = hostCmd->reqInfo.CurSect / SECTOR_NUM_PER_PAGE;

    u32 dieNo = lpn % DIE_NUM;

    这里传入一个dieNo参数

     1 int FindFreePage(u32 dieNo)
     2 {
     3     blockMap = (struct bmArray*)(BLOCK_MAP_ADDR);
     4     dieBlock = (struct dieArray*)(DIE_MAP_ADDR);
     5 
     6     if(blockMap->bmEntry[dieNo][dieBlock->dieEntry[dieNo].currentBlock].currentPage == PAGE_NUM_PER_BLOCK-1)    //当前块已经写完最后一页,则用下一个block
     7     {
     8         dieBlock->dieEntry[dieNo].currentBlock++;
     9 
    10         int i;
    11         for(i=dieBlock->dieEntry[dieNo].currentBlock ; i<(dieBlock->dieEntry[dieNo].currentBlock + BLOCK_NUM_PER_DIE) ; i++)  /*遍历整个die的所有block,到结尾之后又从开始找,直到找到一个可用的block*/
    12         {
    13             if((blockMap->bmEntry[dieNo][i % BLOCK_NUM_PER_DIE].free) && (!blockMap->bmEntry[dieNo][i % BLOCK_NUM_PER_DIE].bad))  //块free且不是坏块就可用
    14             {
    15                 blockMap->bmEntry[dieNo][i % BLOCK_NUM_PER_DIE].free = 0;
    16                 dieBlock->dieEntry[dieNo].currentBlock = i % BLOCK_NUM_PER_DIE;
    17 
    18 //                xil_printf("allocated free block: %4d at %d-%d
    ", dieBlock->dieEntry[dieNo].currentBlock, dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
    19 
    20                 return dieBlock->dieEntry[dieNo].currentBlock * PAGE_NUM_PER_BLOCK;      //返回页号
    21             }
    22         }
    23 
    24         dieBlock->dieEntry[dieNo].currentBlock = GarbageCollection(dieNo);    //整个die没有可用块之后就进行垃圾回收
    25 
    26 //        xil_printf("allocated free block by GC: %4d at %d-%d
    ", dieBlock->dieEntry[dieNo].currentBlock, dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
    27 
    28         return (dieBlock->dieEntry[dieNo].currentBlock * PAGE_NUM_PER_BLOCK) + blockMap->bmEntry[dieNo][dieBlock->dieEntry[dieNo].currentBlock].currentPage;
    29     }
    30     else    //当前块还有页可用就直接接着上一页继续写
    31     {
    32         blockMap->bmEntry[dieNo][dieBlock->dieEntry[dieNo].currentBlock].currentPage++;
    33         return (dieBlock->dieEntry[dieNo].currentBlock * PAGE_NUM_PER_BLOCK) + blockMap->bmEntry[dieNo][dieBlock->dieEntry[dieNo].currentBlock].currentPage;
    34     }
    35 }

    由此可见,FindFreePage这个函数其实就是找一个可用的页,没有空间了就进行垃圾回收操作

    接下来看上一级的函数FlushPageBuf

     1 void FlushPageBuf(u32 lpn, u32 bufAddr)
     2 {
     3     if (lpn == 0xffffffff)            //最开始page缓存内是没有东西的,所以无需flush
     4         return;
     5 
     6     u32 dieNo = lpn % DIE_NUM;          //计算出die number
     7     u32 dieLpn = lpn / DIE_NUM;          //计算出lpn在die中是第几个lpn,可以理解为die0上是lpn0,lpn16……对应为dieLpn0,dieLpn1
     8     u32 ppn = pageMap->pmEntry[dieNo][dieLpn].ppn;
     9 
    10     if (ppn == 0xffffffff)    //表示page缓存还没有写入ppn
    11     {
    12         u32 freePageNo = FindFreePage(dieNo);
    13 
    14 //        xil_printf("free page: %6d(%d, %d, %4d)
    ", freePageNo, dieNo%CHANNEL_NUM, dieNo/CHANNEL_NUM, freePageNo/PAGE_NUM_PER_BLOCK);
    15 
    16         WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
    17         SsdProgram(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, freePageNo, bufAddr);
    18         WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
    19 
    20         // pageMap update
    21         pageMap->pmEntry[dieNo][dieLpn].ppn = freePageNo;
    22         pageMap->pmEntry[dieNo][freePageNo].lpn = dieLpn;
    23     }
    24 }

    继续来看再上一级PrePmRead函数

    int PrePmRead(P_HOST_CMD hostCmd, u32 bufferAddr)
    {
        u32 lpn;
        u32 dieNo;
        u32 dieLpn;
    
        pageMap = (struct pmArray*)(PAGE_MAP_ADDR);
        lpn = hostCmd->reqInfo.CurSect / SECTOR_NUM_PER_PAGE;
    
        if (lpn != pageBufLpn)  //新的请求和上个请求不是同一个lpn
        {
            FlushPageBuf(pageBufLpn, bufferAddr);

    上面这一段进行了FlushPageBuf操作

            if((((hostCmd->reqInfo.CurSect)%SECTOR_NUM_PER_PAGE) != 0)
                        || ((hostCmd->reqInfo.CurSect / SECTOR_NUM_PER_PAGE) == (((hostCmd->reqInfo.CurSect)+(hostCmd->reqInfo.ReqSect))/SECTOR_NUM_PER_PAGE)))
            {
                dieNo = lpn % DIE_NUM;
                dieLpn = lpn / DIE_NUM;
    
                if(pageMap->pmEntry[dieNo][dieLpn].ppn != 0xffffffff)
                {
    //                xil_printf("PrePmRead pdie, ppn = %d, %d
    ", dieNo, pageMap->pmEntry[dieNo][dieLpn].ppn);
    
                    WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
                    SsdRead(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, pageMap->pmEntry[dieNo][dieLpn].ppn, bufferAddr);
                    WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
    
                    pageBufLpn = lpn;
                }
            }
        }

    疑问:这个判断条件第一个是请求开端不要是每个lpn的开始,第二个是请求的大小在同一页,那么这个条件就是只要满足不是请求跨页且从一个lpn的开始的就进入判断?有什么实际意义呢?

    答:这里很关键,涉及一段数据,假如开始的地方没有和page对齐的话,那么在每个page里面请求开始前面的数据就得先读出来,如果是缓存命中的话,就无需操作,因为可以直接修改,那么没命中的话,即使是page对齐了,如果数据没有跨页的话,也还是要读出来,不然请求末尾内page的内容就丢失了。如下图

    假如我要改写345678这些数据,因为数据是按页保存的,所以我只修改这些数据的话,我还得绿色部分的读取出来,然后修改后一起保存到一个页里面,所以请求的开始的lpn如果不是页对齐,我就得read-modify-write,同理,即使页对齐了,但是数据不足一页,那么一页后面几项数据也得先读出来。如果对齐且大小刚好等于一页的话,if失败,这个时候一页也是刚好可以直接修改。至于中间页的数据,本来就是一整页的,所以直接把原来的页无效,然后写入新页即可。这里这个判断条件是在页缓存没有命中的情况下,如果命中了,因为此页还没有刷新到nandflash,所以就无需取出而直接在SDRAM里面修改就行了

        if(((((hostCmd->reqInfo.CurSect)+(hostCmd->reqInfo.ReqSect))% SECTOR_NUM_PER_PAGE) != 0)
                && ((hostCmd->reqInfo.CurSect / SECTOR_NUM_PER_PAGE) != (((hostCmd->reqInfo.CurSect)+(hostCmd->reqInfo.ReqSect))/SECTOR_NUM_PER_PAGE)))
        {
            lpn = ((hostCmd->reqInfo.CurSect)+(hostCmd->reqInfo.ReqSect))/SECTOR_NUM_PER_PAGE;
            dieNo = lpn % DIE_NUM;
            dieLpn = lpn / DIE_NUM;
    
            if(pageMap->pmEntry[dieNo][dieLpn].ppn != 0xffffffff)
            {
    
    //            xil_printf("PrePmRead pdie, ppn = %d, %d
    ", dieNo, pageMap->pmEntry[dieNo][dieLpn].ppn);
    
                WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
                SsdRead(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, pageMap->pmEntry[dieNo][dieLpn].ppn,
                        bufferAddr + ((((hostCmd->reqInfo.CurSect)% SECTOR_NUM_PER_PAGE) + hostCmd->reqInfo.ReqSect)/SECTOR_NUM_PER_PAGE*PAGE_SIZE));
                WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
            }
        }
    
        return 0;
    }

     上面是头,那这一部分就是尾,尾部如果不是请求对齐到page尾的话,那么也会有数据得不到更新,就如同上图lpn1的9到15,要把page内没修改的数据一起读出来更新,这里不仅是要不是结尾,而且是要跨页,这里分几种情况,假如缓存命中,没有跨页的话直接更新缓存就行了,只有跨页了,才需要进行read-modify-write操作;假如缓存没有命中,系统先把之前的缓存flush到nandflash里面,如果此时数据没有跨页的话,那么上面的操作就已经会读取那个页,也就无需下面再多此一举了,具体的写操作可以看下一篇文档。


     接下来来看真正的写操作PmWrite

     1 int PmWrite(P_HOST_CMD hostCmd, u32 bufferAddr)
     2 {
     3     u32 tempBuffer = bufferAddr;
     4     
     5     u32 lpn = hostCmd->reqInfo.CurSect / SECTOR_NUM_PER_PAGE;
     6     
     7     int loop = (hostCmd->reqInfo.CurSect % SECTOR_NUM_PER_PAGE) + hostCmd->reqInfo.ReqSect;
     8     
     9     u32 dieNo;
    10     u32 dieLpn;
    11     u32 freePageNo;
    12 
    13     pageMap = (struct pmArray*)(PAGE_MAP_ADDR);
    14 
    15     // page buffer utilization
    16     if (lpn != pageBufLpn)
    17         pageBufLpn = lpn;
    18 
    19     UpdateMetaForOverwrite(lpn);
    20 
    21     // pageMap update
    22     dieNo = lpn % DIE_NUM;
    23     dieLpn = lpn / DIE_NUM;
    24     pageMap->pmEntry[dieNo][dieLpn].ppn = 0xffffffff;    //写入一页不立即更新
    25 
    26     lpn++;
    27     tempBuffer += PAGE_SIZE;
    28     loop -= SECTOR_NUM_PER_PAGE;
    29 
    30     while(loop > 0)  //接下来还有页请求的话,寻找新页,写入,更新页表
    31     {
    32         dieNo = lpn % DIE_NUM;
    33         dieLpn = lpn / DIE_NUM;
    34         freePageNo = FindFreePage(dieNo);
    35 
    36 //        xil_printf("free page: %6d(%d, %d, %4d)
    ", freePageNo, dieNo%CHANNEL_NUM, dieNo/CHANNEL_NUM, freePageNo/PAGE_NUM_PER_BLOCK);
    37 
    38         WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
    39         SsdProgram(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, freePageNo, tempBuffer);
    40         
    41         UpdateMetaForOverwrite(lpn);
    42 
    43         // pageMap update
    44         pageMap->pmEntry[dieNo][dieLpn].ppn = freePageNo;
    45         pageMap->pmEntry[dieNo][freePageNo].lpn = dieLpn;
    46 
    47         lpn++;
    48         tempBuffer += PAGE_SIZE;
    49         loop -= SECTOR_NUM_PER_PAGE;
    50     }
    51 
    52     int i;
    53     for(i=0 ; i<DIE_NUM ; ++i)
    54         WaitWayFree(i%CHANNEL_NUM, i/CHANNEL_NUM);
    55 
    56     return 0;
    57 }

    接下来看读

     1 int PmRead(P_HOST_CMD hostCmd, u32 bufferAddr)
     2 {
     3     u32 tempBuffer = bufferAddr;
     4     
     5     u32 lpn = hostCmd->reqInfo.CurSect / SECTOR_NUM_PER_PAGE;
     6     int loop = (hostCmd->reqInfo.CurSect % SECTOR_NUM_PER_PAGE) + hostCmd->reqInfo.ReqSect;
     7     
     8     u32 dieNo;
     9     u32 dieLpn;
    10 
    11     pageMap = (struct pmArray*)(PAGE_MAP_ADDR);
    12 
    13     if (lpn == pageBufLpn)  //缓存命中,就无需读取第一页,直接就在内存里面
    14     {
    15         lpn++;
    16         tempBuffer += PAGE_SIZE;
    17         loop -= SECTOR_NUM_PER_PAGE;
    18     }
    19     else
    20     {
    21         dieNo = lpn % DIE_NUM;
    22         dieLpn = lpn / DIE_NUM;
    23 
    24         if(pageMap->pmEntry[dieNo][dieLpn].ppn != 0xffffffff)    //防止第一次读空页
    25         {
    26             FlushPageBuf(pageBufLpn, bufferAddr);
    27             pageBufLpn = lpn;
    28         }
    29     }
    30 
    31     while(loop > 0)    //把接下来的页一次读取出来
    32     {
    33         dieNo = lpn % DIE_NUM;
    34         dieLpn = lpn / DIE_NUM;
    35 
    36 //        xil_printf("requested read lpn = %d
    ", lpn);
    37 //        xil_printf("read pdie, ppn = %d, %d
    ", dieNo, pageMap->pmEntry[dieNo][dieLpn].ppn);
    38 
    39         if(pageMap->pmEntry[dieNo][dieLpn].ppn != 0xffffffff)
    40         {
    41 //            xil_printf("read at (%d, %2d, %4x)
    ", dieNo%CHANNEL_NUM, dieNo/CHANNEL_NUM, pageMap->pmEntry[dieNo][dieLpn].ppn);
    42 
    43             WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
    44             SsdRead(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, pageMap->pmEntry[dieNo][dieLpn].ppn, tempBuffer);
    45         }
    46 
    47         lpn++;
    48         tempBuffer += PAGE_SIZE;
    49         loop -= SECTOR_NUM_PER_PAGE;
    50     }
    51 
    52     int i;
    53     for(i=0 ; i<DIE_NUM ; ++i)
    54         WaitWayFree(i%CHANNEL_NUM, i/CHANNEL_NUM);
    55 
    56     return 0;
    57 }
  • 相关阅读:
    集合总结
    dagger2系列之Scope
    dagger2系列之依赖方式dependencies、包含方式(从属方式)SubComponent
    dagger2系列之生成类实例
    Dagger2系列之使用方法
    Handler系列之内存泄漏
    Handler系列之创建子线程Handler
    Handler系列之原理分析
    Handler系列之使用
    HTML标签
  • 原文地址:https://www.cnblogs.com/losing-1216/p/4930523.html
Copyright © 2011-2022 走看看