SSDsim源碼分析之pre_process_page

來源:互聯網
上載者:User

標籤:

pre_process_page()

從本篇博文開始,我們將會對SSDsim中最核心的部分進行詳細的分析和註解,SSDsim模擬器最核心的部分在於三大函數:

pre_preocess_page()頁讀請求預先處理函數
simmulate()核心類比函數
statistic_output()統計輸出資訊函數

其中最為重要和龐大的函數當屬simmulate()核心類比函數,這個函數主要是負責整個SSD功能的類比,我們會在後面的篇幅中將其展開詳細描述並且加以解析;而本篇博文先解析pre_preocess_page()函數的主要功能和詳細的作用,這其中會涉及到很多SSDsim的工作原理以及架構方面的知識,可以結合程式源碼及解析進行分析。

pre_process_page()主要作用與功能

Pre_process_page()讀請求預先處理函數,主要功能是當讀請求所讀的頁page內沒有資料時需要該函數往page內先寫入data以完成讀操作。

函數首先會定義一些相關的參數,其中包括了一個長度為200char的buf緩衝區,並且調用fopen()以唯讀方式開啟tracefile檔案,若開啟失敗(檔案不存在或者檔案名稱有誤等情況)則直接返回出錯。

若讀取tracefile成功,程式會先置full_page即子頁屏蔽碼為1(初始屏蔽碼預設每一個page下的所有subpage的狀態都是為1),計算largest_lsn:

最大的邏輯扇區號 =( SSD的chip數量 × 每個chip下的die數量 × 每個die下的plane數量 × 每個plane下的block數量 × 每個block下的page數量 × 每個page下的subpage數量) × 預留空間OP比率。

註:SSD都會有一個OP預留空間的概念,這個OP預留空間可以很大程度上提高SSD的效能。

隨後程式會調用fget()逐行開啟tracefile中的所有IOtrace資訊到buf緩衝區中,每次最多讀取一行200個char;

在開啟讀取trace的過程中,程式利用了sscanf()函數按照:

time(long long型) device(int型) lsn(int型) size(int型) ope(int型)。

這樣的固定trace格式逐行讀取IOtrace資料,讀完後便統計fl也就是IO數量計數,並且每次讀完一個IOtrace都需要調用trace_assert()函數進行IO的正確性判斷。隨後初始化add_size即該IO已完成的size計數為0,判斷ope運算元:

若為0則說明是寫請求,但是由於這個pre_process_page只是服務於讀請求,所以程式流會忽略寫請求直接繼續調用fget()讀取下一個IOtrace進行讀請求的操作。

而當ope為1時說明IO為讀請求,程式會判斷已完成的IO長度add_size是否小於IO長度size,若大於或等於size說明讀IO已經處理完畢繼續下一IO的讀取;否則說明該IO讀尚未處理完畢,要繼續下面的步驟:

首先程式會調整lsn(防止lsn比最大的lsn還大),然後計算sub_size即lsn所在的page中實際IO需要操作的長度:

這部分長度 = page子頁數量 - lsn在page中的起始子頁位置;

隨後判斷已經處理的IO長度add_size和當前page中的實際操作長度sub_size之和是否超過該IOtrace的長度size:

若已經超過說明sub_size實際上的長度需要調整(實際上不可能超過IOtrace所規定的size,否則就是錯誤操作,而這裡的sub_size主要是針對首次的lsn和最後一次的lsn,因為其餘中間部分的lsn基本上都會是從page頁的起始位置開始,操作的長度都會是整個page大小)

所以通常是最後一頁的lsn可能需要進行sub_size的調整,此時

sub_size = IO長度size - 已經處理完畢的長度add_size

同時更新已經處理完畢的長度add_size令其加上調整後的sub_size;

若判斷中未超過時說明此時處理的長度尚未到達最後一頁,那麼可以直接跳過上述調整步驟直接判斷sub_size的合法性(即是否超過page大小)和已經處理完畢的長度add_size是否合理(即是否超過了該IOtrace的長度size)

如果非法的話則列印sub_size的資訊;同時程式會繼續統計當前lsn所在的lpn,並判斷該lpn在記憶體dram中的映射表項map_entry[lpn]的相關子頁映射是否有效:

若map_entry[lpn].state不為0則說明此時該lpn的子頁映射可能有效,可能存在有直接可用的子頁,程式會接著判斷state是否>0.

若不成立只能說明此時state<0也就是子頁映射全部有效(這裡是因為map_entry[lpn].state是一個int類型,預設下應該是個有符號的整型取值範圍,因此若state<0那麼換算成位元值便是全1的情況,此時所有子頁都是有效置位)

此時可以直接跳過以下步驟進行lsn和add_size的更新。

而如果state>0則說明可能其中只是有部分子頁有效,而有可能需要讀取資料的子頁並未存在dram和buffer中,所以需要將從SSD中寫入到dram中的子頁進行相應的操作,但是必須要確認到哪些子頁需要進行寫入。

因此程式進行進一步的確認,首先函數會調用set_entry_state()進行子頁映射狀態位的設定,設定後的結果儲存在map_entry_new中

set_entry_state()函數會根據lsn和sub_size的參數對從lsn位置開始的子頁長度為sub_size的部分標記狀態位置1,代表了需要重新在map_entry[lpn].state中進行設定的子頁有效映射。

接著函數會用map_entry_old儲存當前dram中map_entry[lpn].state的位映射狀態資訊,將map_entry_new和map_entry_old進行一個按位或計算操作從而更新映射狀態位,並將結果儲存在modify中

隨後將lpn的映射物理頁pn儲存至ppn中;完成映射更新後就代表了程式已經完成了該lsn在當前lpn下的讀操作。

隨後程式會調用find_location()函數進行物理地址的尋找,緊接著會統計相關的ssd資訊:

更新整個ssd、當前channel和該channel下當前所在的chip中的program_count計數,表示完成一次寫page操作計數;

然後將modify中儲存的映射更新資訊賦值給當前lsn所在的lpn對應的map_entry[lpn].state,表示已經完成了將資料寫入到SSD相對應的page子頁中且已經讀取到了dram中;

同時根據location物理地址結構體的資訊設定page中的子頁有效映射位valid_state也為modify以保持與dram中的同步;

且同時設定更新該page中剩餘free狀態的子頁,接著釋放共置空location。

另一方面,若map_entry[lpn].state為0則說明此時該lpn對應的子頁映射無效,需要程式重新分配出有效狀態的物理子頁給讀操作請求,因此需要從SSD中先將資料寫入該子頁中然後以供讀請求操作。

函數會調用get_ppn_for_pre_process()函數取得當前IOtrace的lsn對應的物理頁號ppn,隨後根據ppn調用find_location()函數取得對應的物理地址資訊.

接著統計更新ssd、當前所在的channel和channel下所在的chip中的program_count計數,表示已經成功從SSD中寫入資料並且讀到了dram中,此時需要更新lpn對應的map_entry[lpn].pn為新擷取到的ppn。

跟著程式會調用set_entry_state()函數重新設定更新子頁的狀態位並且將其賦值給map_entry[lpn].state

以及根據location物理地址更新lsn對應的物理page的相關參數,主要是lpn、valid_state有效狀態位和free_state空閑狀態位標誌,最後釋放共置空location。

當函數完成上述過程後,證明已經成功完成了當前IOtrace的lsn所對應的lpn讀操作,這時候只是讀完成了至多一個page的大小,因此程式此時需要更新lsn的位置即令lsn加上已經讀取完畢的sub_size大小,且同時更新已經處理的IO長度add_size

接著程式流會繼續判斷當前是否已經完成了該IOtrace的處理,如果未完成則重複以上的過程。當完成了該IOtrace的讀請求處理後,程式會回到fgets()函數中繼續讀取tracefile中的下一個IO,周始反覆一直到讀取完所有的IOtrace。

當程式處理完tracefile中所有的讀請求後,便開始列印處理完成資訊,並且調用fclose()關閉tracefile檔案流

隨即用一個多重嵌套for迴圈將SSD目前狀態下每一個plane的free空閑狀態頁的數量都按照固定格式寫入到ssd->outputfile中。
每一次針對每個plane都會用fflush()函數立即將緩衝區中的資料流更新寫入到outputfile檔案中。當完成上述所有操作後,pre_process_page便完成了其任務,返回ssd結構體。

源碼與相關註解
struct ssd_info *pre_process_page(struct ssd_info *ssd)     {    int fl=0;    unsigned int device,lsn,size,ope,lpn,full_page;     //IO操作的相關參數,分別是目標裝置號,邏輯扇區號,操作長度,操作類型,邏輯頁號    unsigned int largest_lsn,sub_size,ppn,add_size=0;    //最大的邏輯扇區號,子頁操作長度,物理頁號,該IO已處理完畢的長度    unsigned int i=0,j,k;    int map_entry_new,map_entry_old,modify;    int flag=0;    char buffer_request[200];   //請求隊列緩衝區    struct local *location;     //local是物理頁號相關的結構體    int64_t time;                   printf("\n");    printf("begin pre_process_page.................\n");    ssd->tracefile=fopen(ssd->tracefilename,"r");    /*以唯讀模式開啟trace檔案從中讀取IO請求*/    if(ssd->tracefile == NULL )          {        printf("the trace file can‘t open\n");           //若開啟失敗則列印錯誤資訊並返回null 指標        return NULL;    }    //full_page的值是根據邏輯子頁頁數N算出的2^N-1    //由代碼可知,此處的full_page是代表了子頁狀態的屏蔽碼,初始時預設設定全為1    full_page=~(0xffffffff<<(ssd->parameter->subpage_page));    /*     *計算出這個ssd的最大邏輯扇區號     *這裡之所以要將總邏輯頁數量乘以(1-ssd->parameter->overprovide)是由於要有一部分空間作為SSD的預留空間OP以提高SSD效能     */    largest_lsn=(unsigned int )((ssd->parameter->chip_num*ssd->parameter->die_chip*ssd->parameter->plane_die*ssd->parameter->block_plane*ssd->parameter->page_block*ssd->parameter->subpage_page)*(1-ssd->parameter->overprovide));    while(fgets(buffer_request,200,ssd->tracefile))      //逐行從tracefile檔案中每次讀取199字元,直到讀完整個trace為止    {        sscanf(buffer_request,"%lld %d %d %d %d",&time,&device,&lsn,&size,&ope);    //讀入IO的相關參數值        fl++;   //已讀的IO數量計數                                                         trace_assert(time,device,lsn,size,ope);          /*斷言,當讀到的time,device,lsn,size,ope不合法時就會處理*/        add_size=0;          /*add_size是這個請求已經預先處理的大小*/                                                          if(ope==1)           /*這裡只是讀請求的預先處理,需要提前將相應位置的資訊進行相應修改*/                                                           {            while(add_size<size)                //操作未完成之前進入迴圈體                                              {                                                                           lsn=lsn%largest_lsn;                  /*防止獲得的lsn比最大的lsn還大*/                                                       sub_size=ssd->parameter->subpage_page-(lsn%ssd->parameter->subpage_page);                       /*                 * 這裡的sub_size主要是為了定位好子請求操作位置的,這個位置是相對於某一個特定的page而言的,從這個page的第一個sub_page開始計算到這個特定的操作位置                 * 因此,sub_size其實就是從lsn扇區位置起始到該page末端的這部分內容,這部分內容是在這個page中需要被讀取的。                 * 因此這裡的subpage_page應該就是所謂的一個page內有多少個扇區數量,每個扇區一般都預設為512B                 */                if(add_size+sub_size>=size)                             /*只有當一個請求的大小小於一個page的大小時或者是處理一個請求的最後一個page時會出現這種情況*/                {                           sub_size=size-add_size;                                                                                                     //此處的sub_size之所以等於size-add_size原因是當請求的大小小於page時,實際上原先sub_size的地區便已經是大於實際請求大小了                    add_size+=sub_size;                                                 }                if((sub_size>ssd->parameter->subpage_page)||(add_size>size))/*當預先處理一個子大小時,這個大小大於一個page或是已經處理的大小大於size就報錯*/                       {                           printf("pre_process sub_size:%d\n",sub_size);                       }                /*******************************************************************************************************                *利用邏輯扇區號lsn計算出邏輯頁號lpn                *判斷這個dram中映射表map中在lpn位置的狀態                *A,這個狀態==0,表示以前沒有寫過,現在需要直接將sub_size大小的子頁寫進去                *B,這個狀態>0,表示,以前有寫過,這需要進一步比較狀態,因為新寫的狀態可以與以前的狀態有重疊的扇區的地方                *因此,狀態==0的情況下,由於記憶體中映射狀態無效,所以必須要重新分配出狀態有效物理頁面給讀操作請求,調用函數get_ppn_for_pre_process得到有效物理頁資訊後                *再通過find_location()將物理頁面地址資訊儲存並且更新該lpn對應的映射表資訊。                *同樣的,當狀態>0時,證明該lpn位置處的記憶體映射表中的映射狀態有效,有直接可以使用的有效物理頁,所以ppn直接可以使用映射表中的pn                ********************************************************************************************************/                lpn=lsn/ssd->parameter->subpage_page;                       //這裡的subpage_page初步推斷應該是指每個page中的扇區數量,也就是子請求每次的操作單位                if(ssd->dram->map->map_entry[lpn].state==0)                 /*狀態為0的情況,所有的頁都無效狀態*/                {                    /**************************************************************                    *獲得利用get_ppn_for_pre_process函數獲得ppn,再得到location                    *修改ssd的相關參數,dram的映射表map,以及location下的page的狀態                     ***************************************************************/                    ppn=get_ppn_for_pre_process(ssd,lsn);                                      location=find_location(ssd,ppn);                    ssd->program_count++;                       ssd->channel_head[location->channel].program_count++;                       ssd->channel_head[location->channel].chip_head[location->chip].program_count++;                             ssd->dram->map->map_entry[lpn].pn=ppn;                      ssd->dram->map->map_entry[lpn].state=set_entry_state(ssd,lsn,sub_size);   //0001                    ssd->channel_head[location->channel].chip_head[location->chip].die_head[location->die].plane_head[location->plane].blk_head[location->block].page_head[location->page].lpn=lpn;                    ssd->channel_head[location->channel].chip_head[location->chip].die_head[location->die].plane_head[location->plane].blk_head[location->block].page_head[location->page].valid_state=ssd->dram->map->map_entry[lpn].state;    //擷取到的讀操作完成之後的子頁狀態                    ssd->channel_head[location->channel].chip_head[location->chip].die_head[location->die].plane_head[location->plane].blk_head[location->block].page_head[location->page].free_state=((~ssd->dram->map->map_entry[lpn].state)&full_page);                     //標識該page的所有子頁都為used狀態                    free(location);                    location=NULL;      //避免野指標                }//if(ssd->dram->map->map_entry[lpn].state==0)                    /*狀態不為0的情況(存在子頁面有效情況)*/                {                    map_entry_new=set_entry_state(ssd,lsn,sub_size);                          /*得到新的狀態,並與原來的狀態相或的到一個狀態*/                    map_entry_old=ssd->dram->map->map_entry[lpn].state;                       modify=map_entry_new|map_entry_old;                                     ppn=ssd->dram->map->map_entry[lpn].pn;                      location=find_location(ssd,ppn);                        ssd->program_count++;                       ssd->channel_head[location->channel].program_count++;                    ssd->channel_head[location->channel].chip_head[location->chip].program_count++;                         ssd->dram->map->map_entry[lsn/ssd->parameter->subpage_page].state=modify;                     ssd->channel_head[location->channel].chip_head[location->chip].die_head[location->die].plane_head[location->plane].blk_head[location->block].page_head[location->page].valid_state=modify;                    ssd->channel_head[location->channel].chip_head[location->chip].die_head[location->die].plane_head[location->plane].blk_head[location->block].page_head[location->page].free_state=((~modify)&full_page);                    free(location);                    location=NULL;                }                lsn=lsn+sub_size;                   /*下個子請求的起始位置*/                                                      add_size+=sub_size;                 /*已經處理了的add_size大小變化*/                                                  }        }    }       printf("\n");    printf("pre_process is complete!\n");    fclose(ssd->tracefile);    for(i=0;i<ssd->parameter->channel_number;i++)    for(j=0;j<ssd->parameter->die_chip;j++)    for(k=0;k<ssd->parameter->plane_die;k++)    {        fprintf(ssd->outputfile,"chip:%d,die:%d,plane:%d have free page: %d\n",i,j,k,ssd->channel_head[i].chip_head[0].die_head[j].plane_head[k].free_page);                        fflush(ssd->outputfile);    }    return ssd;}

SSDsim源碼分析之pre_process_page

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.