[Qemu OpenChannelSSD] Bad Block Table

Intro

BBT是一個lun上所有block狀態(tài)的列表,每個block可能的狀態(tài)如下:
一個BBT的項目數(shù)(entry number)是geo->nplanes * geo-nblocks.

另外断楷,Bad-Block Table是需要由開發(fā)者自己維護的,可以通過mark&&flush來修改在內(nèi)存中的BBT或持久化BBT(將內(nèi)存中的BBT寫入設(shè)備:寫到哪里可能還需要去探索qemu中相關(guān)的處理).

/**
 * Representation of valid values of bad-block-table states
 */
enum nvm_bbt_state {
    NVM_BBT_FREE = 0x0, ///< Block is free AKA good
    NVM_BBT_BAD = 0x1,  ///< Block is bad
    NVM_BBT_GBAD = 0x2, ///< Block has grown bad
    NVM_BBT_DMRK = 0x4, ///< Block has been marked by device side
    NVM_BBT_HMRK = 0x8  ///< Block has been marked by host side
};
struct nvm_dev {
    char name[NVM_DEV_NAME_LEN];    ///< Device name e.g. "nvme0n1"
    char path[NVM_DEV_PATH_LEN];    ///< Device path e.g. "/dev/nvme0n1"
    struct nvm_addr_fmt fmt;    ///< Device address format
    struct nvm_addr_fmt_mask mask;  ///< Device address format mask
    struct nvm_geo geo;     ///< Device geometry
    uint64_t ssw;           ///< Bit-width for LBA fmt conversion
    int pmode;          ///< Default plane-mode I/O
    int fd;             ///< Device fd / IOCTL handle
    int erase_naddrs_max;       ///< Maximum # of address for erase
    int read_naddrs_max;        ///< Maximum # of address for read
    int write_naddrs_max;       ///< Maximum # of address for write
    int bbts_cached;        ///< Whether to cache bbts
    size_t nbbts;           ///< Number of entries in cache
    struct nvm_bbt **bbts;      ///< Cache of bad-block-tables
    enum meta_mode meta_mode;   ///< Flag to indicate the how meta is w
};
  • 從dev_open可以看出:
  1. 一個lun一個bbt.
  2. 初始時每個lun對應(yīng)的bbt(dev->bbts[i])都是空(NULL).
struct nvm_dev *nvm_dev_open(const char *dev_path)
{
...
    dev->bbts_cached = 0;
    dev->nbbts = dev->geo.nchannels * dev->geo.nluns;
    dev->bbts = malloc(sizeof(*dev->bbts) * dev->nbbts);
    for (size_t i = 0; i < dev->nbbts; ++i)
        dev->bbts[i] = NULL;

    return dev;
}
  • 使用bbt的時候如果沒有設(shè)置標(biāo)記nvm_dev_set_bbts_cached(dev, 1)可能會出現(xiàn)一些預(yù)想不到的結(jié)果桌肴,具體原因正在分析中.(可提issue)

  • 使用bbt設(shè)置時(nvm_bbt_mark其最大addrs也是不能超過NVM_NADDR_MAX

  • test code:

#include <liblightnvm.h>
#include <malloc.h>
#include <nvm.h>
#include <stdio.h>
#include <string.h>

static char nvm_dev_path[NVM_DEV_PATH_LEN] = "/dev/nvme0n1";
static struct nvm_dev *dev;
static const struct nvm_geo *geo;
static struct nvm_addr lun_addr;

static int channel = 0;
static int lun = 0;
static void *Buf_for_1_read = NULL; //Buf for reading 1 sector
static void *Buf_for_1_meta_read = NULL; // Buf for reading 1 metadata

const int FirstBLK = 0;
const int SecondBLK = 1;
const int Plane = 0;

const uint16_t State_HBAD = NVM_BBT_HMRK;
const uint16_t State_FREE = NVM_BBT_FREE;

const char* TestStr = "K:Foo;V:Bar";

#include "common.h"


int setup(void)
{
    dev = nvm_dev_open(nvm_dev_path);
    if (!dev) {
        perror("nvm_dev_open");
        return -1;
    }
    geo = nvm_dev_get_geo(dev);
    lun_addr.ppa = 0;
    lun_addr.g.ch = channel;
    lun_addr.g.lun = lun;

    Buf_for_1_read =  nvm_buf_alloc(geo, 2 * geo->sector_nbytes);
    if (!Buf_for_1_read) {
        FAIL_ALLOC;
        return -1;
    }
    
    Buf_for_1_meta_read = nvm_buf_alloc(geo, 2 * geo->meta_nbytes);
    if (!Buf_for_1_meta_read) {
        FAIL_ALLOC;
        return -1;
    }

    return 0;
}

int teardown(void)
{
    nvm_dev_close(dev);
    if ( Buf_for_1_read ) {
        free(Buf_for_1_read);
    }
    if ( Buf_for_1_meta_read) {
        free(Buf_for_1_meta_read);
    }
    return 0;
}

static inline int _bbt_idx(const struct nvm_dev *dev,
               const struct nvm_addr addr)
{
    return addr.g.ch * dev->geo.nluns + addr.g.lun;
}

uint64_t alignblk(struct nvm_addr addr)
{
    struct nvm_addr alg;
    alg.ppa = addr.ppa;
    alg.g.pg = 0;
    alg.g.sec = 0;
    return alg.ppa;
}

int GetPmode(int npl)
{
    int x;
    switch (npl) {
    case 1: 
        x = NVM_FLAG_PMODE_SNGL; break;
    case 2:
        x = NVM_FLAG_PMODE_DUAL; break;
    case 4:
        x = NVM_FLAG_PMODE_QUAD; break;
    default:
        x = -1;
    }
    return x;
}

void EraseNpl_1Blk(struct nvm_addr wh)//
{
    struct nvm_ret ret;
    ssize_t res;
    
    const int npl = geo->nplanes;
    int pmode = GetPmode(npl);
    struct nvm_addr whichblk[npl];
    for(int i = 0; i < npl; ++i){
        whichblk[i].ppa = alignblk(wh);
        whichblk[i].g.pl = i;
    }

    res = nvm_addr_erase(dev, whichblk, npl, pmode, &ret);//Erase 1 block of all planes inside a lun.
    if(res < 0){
        FAIL_ERASE;
        nvm_ret_pr(&ret);
    }
}
void Write_1Sector(struct nvm_addr wh, const char *IO, int use_meta, const char *Meta)
{
    struct nvm_ret ret;
    ssize_t res;
    int pmode = NVM_FLAG_PMODE_SNGL;
    void *bufptr = NULL, *bufptr_meta = NULL;

    int addr_valid = nvm_addr_check(wh, geo);
    if(addr_valid){
        ADDR_INVALID;
        nvm_bounds_pr(addr_valid);
        goto OUT;
    }

    //buf setup
    bufptr = nvm_buf_alloc(geo, geo->sector_nbytes);//sector size
    if(!bufptr){
        FAIL_ALLOC;
        goto OUT;
    }
    memcpy(bufptr, IO, strlen(IO));

    if(use_meta){
        bufptr_meta = nvm_buf_alloc(geo, geo->meta_nbytes);
        if(!bufptr_meta){
            FAIL_ALLOC;
            goto FREE_OUT1;
        }
        memcpy(bufptr_meta, Meta, geo->meta_nbytes);
    }


    //2. write
    res = nvm_addr_write(dev, &wh, 1, bufptr, 
        use_meta ? bufptr_meta : NULL, pmode, &ret);//Write 1 sector
    if(res < 0){
        FAIL_WRITE;
    }

    free(bufptr_meta);
FREE_OUT1:
    free(bufptr);
OUT:
    if(res < 0){
        nvm_ret_pr(&ret);
    }
    return;
}
void Read_1Sector(struct nvm_addr wh, int use_meta)
{
    struct nvm_ret ret;
    ssize_t res;

    int pmode = NVM_FLAG_PMODE_SNGL;

    res = nvm_addr_read(dev, &wh, 1, Buf_for_1_read,
        use_meta ? Buf_for_1_meta_read : NULL, pmode, &ret);//only use the first sector_nbytes
    if(res < 0){
        FAIL_READ;
        nvm_ret_pr(&ret);
    }
}


void Erase1LunALLBlk(struct nvm_addr lun_addr)//
{
//#define TEST_EraseAllLunBlk_FUNC
#define WAY1 1
#define WAY2 2
#define WAY WAY2        //WAY1 is not good!!
    struct nvm_ret ret;
    ssize_t res;
    int i, j;
    const int npl = geo->nplanes;
    const int nblks = geo->nblocks;
    int pmode = GetPmode(npl);
//    int pmode = NVM_FLAG_PMODE_SNGL;
    if (pmode < 0) {
        PLANE_EINVAL;
        return;
    }
    
    const int max_naddrs_1_time = NVM_NADDR_MAX;
    const int naddrs_1_time = max_naddrs_1_time / npl;

    struct nvm_addr whichblks[NVM_NADDR_MAX];//for all block
   
    for(i = 0; i < NVM_NADDR_MAX; ++i){
        whichblks[i].ppa = 0;
        whichblks[i].g.ch = lun_addr.g.ch; 
        whichblks[i].g.lun = lun_addr.g.lun;
    }
    printf("Nblks: %d, Npl: %d\n", nblks, npl);
    int t = 0;
    int erased = 0;
    while (erased < nblks) {
        int issue_nblks = NVM_MIN(nblks - erased, naddrs_1_time);
        int naddrs = issue_nblks * npl;
        //SUM: issue_nblks * npl
        
        if (WAY == WAY1) {      //TEST Result: 2 ways of addr layout are both OK.
            //Way 1:
            for (i = 0; i < issue_nblks; ++i) {
                for (j = 0; j < npl; j++) {
                    whichblks[j * issue_nblks + i].g.blk = erased + i;
                    whichblks[j * issue_nblks + i].g.pl = j;
                }
            }
        }else{
            //Way 2:
            for (i = 0; i < issue_nblks; ++i) {
                for (j = 0; j < npl; j++) {
                    whichblks[i * npl + j].g.blk = erased + i;
                    whichblks[i * npl + j].g.pl = j;
                }
            }
        }

        
#ifdef TEST_EraseAllLunBlk_FUNC
        char title[10];
        sprintf(title, "idx%d", t);
        My_pr_addr_cap(title);

        for (int idx = 0; idx < naddrs; ++idx) {
            My_pr_nvm_addr(whichblks[idx]);
        }
#endif
        res = nvm_addr_erase(dev, whichblks, naddrs, pmode, &ret);//Erase SUM blocks
        if(res < 0){
            FAIL_ERASE;
            printf("%d naadrs From:\n", naddrs);
            My_pr_addr_with_str("Bad Addr", whichblks[0]);
            nvm_ret_pr(&ret);
            printf("Stop\n");
            return ;
        }

        erased += naddrs_1_time;
        t++;
    }
}

void Set1LunALLFree(struct nvm_addr lun_addr)
{    
    struct nvm_ret ret;
    int res;
    int i, j;
    const int npl = geo->nplanes;
    const int nblks = geo->nblocks;

    const int max_naddrs_1_time = NVM_NADDR_MAX;
    const int naddrs_1_time = max_naddrs_1_time / npl;
   

    struct nvm_addr whichblks[NVM_NADDR_MAX];//for all block
    for(i = 0; i < NVM_NADDR_MAX; ++i){
        whichblks[i].ppa = 0;
        whichblks[i].g.ch = lun_addr.g.ch; 
        whichblks[i].g.lun = lun_addr.g.lun;
    }
    printf("Nblks: %d, Npl: %d\n", nblks, npl);
    int t = 0;
    int erased = 0;
    while (erased < nblks) {
        int issue_nblks = NVM_MIN(nblks - erased, naddrs_1_time);
        int naddrs = issue_nblks * npl;
        //SUM: issue_nblks * npl

        if (WAY == WAY1) {      //TEST Result: 2 ways of addr layout are both OK.
            //Way 1:
            for (i = 0; i < issue_nblks; ++i) {
                for (j = 0; j < npl; j++) {
                    whichblks[j * issue_nblks + i].g.blk = erased + i;
                    whichblks[j * issue_nblks + i].g.pl = j;
                }
            }
        }else{
            //Way 2:
            for (i = 0; i < issue_nblks; ++i) {
                for (j = 0; j < npl; j++) {
                    whichblks[i * npl + j].g.blk = erased + i;
                    whichblks[i * npl + j].g.pl = j;
                }
            }
        }

        res = nvm_bbt_mark(dev, whichblks, naddrs, State_FREE, &ret);
        if (res < 0) {
            MARK_BBT_FAIL;
            printf("%d naadrs From:\n", naddrs);
            My_pr_addr_with_str("Bad Addr", whichblks[0]);
            nvm_ret_pr(&ret);
            printf("Stop\n");
            return ;
        }

        erased += naddrs_1_time;
        t++;
    }
}




void test_get_bbt_1(void)
{
    struct nvm_ret ret = {};
    const struct nvm_bbt *bbt;
    bbt = nvm_bbt_get(dev, lun_addr, &ret);
    if (!bbt) {
        GET_BBT_FAIL;
        nvm_ret_pr(&ret);
    }
    My_nvm_bbt_pr(bbt);
}

void test_run_erase_blk_1_lun(void)
{
    Erase1LunALLBlk(lun_addr);
}

void test_write_to_blk(int plnum, int blknum)//write all pages inside a blk of block=blknum, plane=plnum
{
    const int npl = geo->nplanes;
    struct nvm_addr a0;
    a0.ppa = lun_addr.ppa;
    a0.g.pl = plnum;
    a0.g.blk = blknum;

    for (int i = 0; i < geo->npages; i++) {
        a0.g.pg = i;
        Write_1Sector(a0, TestStr, 0, NULL);
        ((char*)Buf_for_1_read)[0] = '\0';
        Read_1Sector(a0, 0);
        if (strcmp((char*)Buf_for_1_read, TestStr) != 0) {
            NOT_THE_SAME_IO;
            My_pr_addr_with_str("not same:", a0);
        }
    }
}

void Test_Erase1()
{
    struct nvm_ret ret;
    ssize_t res;
    int pl[4] = {0,1,0,1};
    int blk[4] = {0,0,1,1};
    struct nvm_addr a0[4];
    for (int i = 0; i < 4; i++) {
        a0[i].ppa = 0;
        a0[i].g.pl = pl[i];
        a0[i].g.blk = blk[i];
    }
    res = nvm_addr_erase(dev, a0, 4, NVM_FLAG_PMODE_DUAL, &ret);//Erase 4 blocks
    if(res < 0){
        FAIL_ERASE;
        nvm_ret_pr(&ret);
    }
}

void Test_Erase2()
{
    struct nvm_ret ret;
    ssize_t res;
    int pl[4] = {0,0,1,1};
    int blk[4] = {0,1,0,1};
    struct nvm_addr a0[4];
    for (int i = 0; i < 4; i++) {
        a0[i].ppa = 0;
        a0[i].g.pl = pl[i];
        a0[i].g.blk = blk[i];
    }
    res = nvm_addr_erase(dev, a0, 4, NVM_FLAG_PMODE_DUAL, &ret);//Erase 4 blocks
    if(res < 0){
        FAIL_ERASE;
        nvm_ret_pr(&ret);
    }
}
void test_E1_ok()
{
    struct nvm_addr a0;
    a0.ppa = 0;
    a0.g.blk = 1;
    Test_Erase1();
    Write_1Sector(a0,"This is the test.", 0, NULL);
}
void test_E2_fail()
{
    struct nvm_addr a0;
    a0.ppa = 0;
    a0.g.blk = 1;
    Test_Erase2();
    Write_1Sector(a0,"This is the test.", 0, NULL);
}
void test_set_cache_bbt(void)
{
    nvm_dev_set_bbts_cached(dev, 1);
}

void test_clean_all_mark_all_free(void)
{
    struct nvm_ret ret;
    ssize_t res;
    Erase1LunALLBlk(lun_addr);
    Set1LunALLFree(lun_addr);
    if(nvm_bbt_flush(dev, lun_addr, &ret)){//flush the lun's bbt
        FLUSH_BBT_FAIL;
        nvm_ret_pr(&ret);
    }
}

void test_write_into_1st_2ed_blk(void)//write something into all pages inside the 1st/2ed block of all plane inside <lun_addr>
{
    for (int i = 0; i < geo->nplanes; i++) {
        test_write_to_blk(i, FirstBLK);
        test_write_to_blk(i, SecondBLK); 
    }
}

void set_1st_2ed_blk_flush(uint16_t flag)
{
    struct nvm_ret ret;
    int res;
    const int nblks = 2;//1st 2ed
    const int naddrs = nblks * geo->nplanes;//the 1st/2ed block of LUN 0 across all plane
    struct nvm_addr addrs[naddrs];
    //*****Sequence: Must Be Like WAY2*****
    for (int i = 0; i < geo->nplanes; i++) {
        for (int j = 0; j < nblks; j++) {
            addrs[j * geo->nplanes + i].ppa = lun_addr.ppa;
            addrs[j * geo->nplanes + i].g.pl = i;
            addrs[j * geo->nplanes + i].g.blk = j;
        }
    }
    //My_pr_naddrs_with_str("Test", addrs, naddrs);
    
    res = nvm_bbt_mark(dev, addrs, naddrs, flag, &ret); 
    if (res < 0) {
       MARK_BBT_FAIL;
       nvm_ret_pr(&ret);
    }
    if(nvm_bbt_flush(dev, lun_addr,&ret)){//flush the lun's bbt
        FLUSH_BBT_FAIL;
        nvm_ret_pr(&ret);
    }
}


void test_set_1st_2ed_blk_bad(void)//set them as HBAD block so that background GC will then GC them.
{
    set_1st_2ed_blk_flush(State_HBAD);
}



void test_GC(void)//**find all HBAD blocks and erase them**(erase operation must be issued to all plane inside a LUN)
{
    struct nvm_ret ret = {};
    const struct nvm_bbt *bbt;
    int nbad = 0;
    ssize_t res;
    bbt = nvm_bbt_get(dev, lun_addr, &ret);
    if (!bbt) {
        GET_BBT_FAIL;
        nvm_ret_pr(&ret);
    }

    const int max_naddrs_1_time = NVM_NADDR_MAX;
    struct nvm_addr whichblks[NVM_NADDR_MAX];//for all block
    int idx = 0;


    //iterator all block, from nvm_bbt_pr
    for (int i = 0; i < bbt->nblks; i += bbt->dev->geo.nplanes) {
        int blk = i / bbt->dev->geo.nplanes;
        int blk_num = blk;
        int pl_num = 0;
        for (int blk = i; blk < (i+ bbt->dev->geo.nplanes); ++blk, ++pl_num) {
            if(bbt->blks[blk] == State_HBAD){
                printf("HBAD: %d %d\n", pl_num, blk_num);

                //setup address array
                whichblks[idx].ppa = lun_addr.ppa;
                whichblks[idx].g.pl = pl_num;
                whichblks[idx].g.blk = blk_num;
                idx++;

                nbad++;
            }
        }
        
        if (max_naddrs_1_time - idx < bbt->dev->geo.nplanes) {
            //do erase.
            
            res = nvm_addr_erase(dev, whichblks, idx, NVM_FLAG_PMODE_DUAL, &ret);
            if(res < 0){
                FAIL_ERASE;
                nvm_ret_pr(&ret);
            }
            
            //set them as FREE again
            if (nvm_bbt_mark(dev, whichblks, idx, State_FREE, &ret) < 0) {
               MARK_BBT_FAIL;
               nvm_ret_pr(&ret);
            }
            if(nvm_bbt_flush(dev, lun_addr,&ret)){//flush the lun's bbt
                FLUSH_BBT_FAIL;
                nvm_ret_pr(&ret);
            } 
            
            idx = 0;
        }

    }
    if (idx > 0) {
        //do erase.
        
        res = nvm_addr_erase(dev, whichblks, idx, NVM_FLAG_PMODE_DUAL, &ret);
        if(res < 0){
            FAIL_ERASE;
            nvm_ret_pr(&ret);
        }
        
        //set them as FREE again
        if (nvm_bbt_mark(dev, whichblks, idx, State_FREE, &ret) < 0) {
           MARK_BBT_FAIL;
           nvm_ret_pr(&ret);
        }
        if(nvm_bbt_flush(dev, lun_addr,&ret)){//flush the lun's bbt
            FLUSH_BBT_FAIL;
            nvm_ret_pr(&ret);
        } 
        
        idx = 0;
    }
    printf("HBAD block number: %d\n", nbad);
 }
 typedef void (* FuncType) (void);
void RunTests()
{
    FuncType tests[] = { 
//        test_run_erase_blk_1_lun,        
//        test_write_to_1st_blk,
//        test_write_to_2ed_blk,
//        test_get_bbt_1,
//        test_E1_ok,
//        test_E2_fail
        test_set_cache_bbt,     
        test_clean_all_mark_all_free,
        test_get_bbt_1,
        test_write_into_1st_2ed_blk,
        test_set_1st_2ed_blk_bad,
        test_get_bbt_1,
        test_GC,
        test_get_bbt_1
        };
    const char *teststr[] = {
//        "test_run_erase_blk_1_lun",        
//        "test_write_to_1st_blk",
//        "test_write_to_2ed_blk",
//        "test_get_bbt_1",
//        "test_E1_ok",
//        "test_E2_fail"
        "test_set_cache_bbt",
        "test_clean_all_mark_all_free",
        "test_get_bbt_1",
        "test_write_into_1st_2ed_blk",
        "test_set_1st_2ed_blk_bad",
        "test_get_bbt_1",
        "test_GC",
        "test_get_bbt_1"
        };
    for(int i = 0; i < (sizeof(tests) / sizeof(FuncType)); i++){
        printf("====Test %d : %s====\n", i, teststr[i]);
        tests[i]();
    }
}
int main()
{
    if( setup() < 0){
        goto OUT;
    }
    RunTests();

OUT:
    teardown();
    return 0;
}
common.h:
#ifndef COMMON_H
#define COMMON_H
#include <liblightnvm.h>
#include <nvm.h>

#define DEBUG_MSG(MSG) do{ printf("[DEBUG] - "#MSG"\n"); }while(0)

//for io_issue:
#define FAIL_ERASE DEBUG_MSG(FAIL_ERASE)
#define FAIL_ALLOC DEBUG_MSG(FAIL_ALLOC)
#define FAIL_WRITE DEBUG_MSG(FAIL_WRITE)
#define FAIL_READ  DEBUG_MSG(FAIL_READ)
#define ADDR_INVALID DEBUG_MSG(ADDR_INVALID)
#define THE_SAME_IO DEBUG_MSG(THE_SAME_IO) 
#define NOT_THE_SAME_IO DEBUG_MSG(NOT_THE_SAME_IO)
#define THE_SAME_META DEBUG_MSG(THE_SAME_META)
#define NOT_THE_SAME_META DEBUG_MSG(NOT_THE_SAME_META)

//for bbt_issue:
#define PLANE_EINVAL DEBUG_MSG(PLANE_EINVAL)
#define GET_BBT_FAIL DEBUG_MSG(GET_BBT_FAIL)
#define FLUSH_BBT_FAIL DEBUG_MSG(FLUSH_BBT_FAIL)
#define MARK_BBT_FAIL DEBUG_MSG(MARK_BBT_FAIL)
//for common use:
#define NVM_MIN(x, y) ({                \
        __typeof__(x) _min1 = (x);      \
        __typeof__(y) _min2 = (y);      \
        (void) (&_min1 == &_min2);      \
        _min1 < _min2 ? _min1 : _min2; })


inline static void My_pr_addr_cap(const char* str)
{
#define digestlen 8
    char digest[digestlen];
    strncpy(digest, str, digestlen);
    digest[digestlen - 1] = '\0';

    printf("%8s | %s | %s | %s | %-4s | %3s | %s \n",
        digest,"ch", "lun", "pl", "blk", "pg", "sec");
}
inline static void My_pr_nvm_addr(struct nvm_addr addr)
{
    printf("         | %2d | %3d | %2d | %4d | %3d | %d\n",
           addr.g.ch, addr.g.lun, addr.g.pl,
           addr.g.blk, addr.g.pg, addr.g.sec);
}

inline static void My_pr_addr_with_str(const char *str, struct nvm_addr x)
{
    My_pr_addr_cap(str);
    My_pr_nvm_addr(x);
}
inline static void My_pr_naddrs_with_str(const char *str, struct nvm_addr x[], int naddrs)
{
    My_pr_addr_cap(str);
    for (int i = 0; i < naddrs; i++) {
        My_pr_nvm_addr(x[i]);
    }
}

inline static void My_nvm_bbt_pr(const struct nvm_bbt *bbt)
{
    int nnotfree = 0;
    const int Pr_num = 4;
    int pred = 0, pr_sr = 0;
    if (!bbt) {
        printf("bbt { NULL }\n");
        return;
    }

    printf("bbt {\n");
    printf("  addr"); nvm_addr_pr(bbt->addr);
    printf("  nblks(%lu) {", bbt->nblks);
    for (int i = 0; i < bbt->nblks; i += bbt->dev->geo.nplanes) {
        int blk = i / bbt->dev->geo.nplanes;
        if (pred < Pr_num/*first Pr_num ones*/ 
            || i == bbt->nblks - bbt->dev->geo.nplanes/*last one*/) {
            printf("\n    blk(%04d): [ ", blk);
            for (int blk = i; blk < (i + bbt->dev->geo.nplanes); ++blk) {
                nvm_bbt_state_pr(bbt->blks[blk]);
                printf(" ");
                if (bbt->blks[blk]) {
                    ++nnotfree;
                }
            }
            printf("]");
            pred++;
        }else if(!pr_sr){
            printf("\n....");
            pr_sr = 1;
        }


    }
    printf("\n  }\n");
    printf("  #notfree(%d)\n", nnotfree);
    printf("}\n");
}


#endif

1.設(shè)置cache,
2.擦除整個lun
3.拿bbt
4.寫入lun_0的每個plane的前2個block(一共4個block在我的環(huán)境geo下)
5.設(shè)置這4個block為HBAD
5.1拿bbt看是否有變化
6.模擬一個GC過程,把設(shè)為HBAD的block擦除掉
7.拿bbt
Output:

====Test 0 : test_set_cache_bbt====
====Test 1 : test_clean_all_mark_all_free====
Nblks: 2044, Npl: 2
Nblks: 2044, Npl: 2
====Test 2 : test_get_bbt_1====
bbt {
  addr(0x0000000000000000){ ch(00), lun(00), pl(0), blk(0000), pg(000), sec(0) }
  nblks(4088) {
    blk(0000): [ FREE(0) FREE(0) ]
    blk(0001): [ FREE(0) FREE(0) ]
    blk(0002): [ FREE(0) FREE(0) ]
    blk(0003): [ FREE(0) FREE(0) ]
....
    blk(2043): [ FREE(0) FREE(0) ]
  }
  #notfree(0)
}
====Test 3 : test_write_into_1st_2ed_blk====
====Test 4 : test_set_1st_2ed_blk_bad====
====Test 5 : test_get_bbt_1====
bbt {
  addr(0x0000000000000000){ ch(00), lun(00), pl(0), blk(0000), pg(000), sec(0) }
  nblks(4088) {
    blk(0000): [ HBAD(8) HBAD(8) ]
    blk(0001): [ HBAD(8) HBAD(8) ]
    blk(0002): [ FREE(0) FREE(0) ]
    blk(0003): [ FREE(0) FREE(0) ]
....
    blk(2043): [ FREE(0) FREE(0) ]
  }
  #notfree(4)
}
====Test 6 : test_GC====
HBAD: 0 0
HBAD: 1 0
HBAD: 0 1
HBAD: 1 1
HBAD block number: 4
====Test 7 : test_get_bbt_1====
bbt {
  addr(0x0000000000000000){ ch(00), lun(00), pl(0), blk(0000), pg(000), sec(0) }
  nblks(4088) {
    blk(0000): [ FREE(0) FREE(0) ]
    blk(0001): [ FREE(0) FREE(0) ]
    blk(0002): [ FREE(0) FREE(0) ]
    blk(0003): [ FREE(0) FREE(0) ]
....
    blk(2043): [ FREE(0) FREE(0) ]
  }
  #notfree(0)
}

總結(jié)

  1. 一個bbt對應(yīng)一個lun
  2. bbt是由用戶自己維護的,并且可以持久化到設(shè)備中.(設(shè)備中對應(yīng)邏輯需要到qemu中查看)
  3. 使用bbt時需要設(shè)置dev->bbts_cached標(biāo)記
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末煮仇,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谎仲,更是在濱河造成了極大的恐慌欺抗,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件强重,死亡現(xiàn)場離奇詭異绞呈,居然都是意外死亡,警方通過查閱死者的電腦和手機间景,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門佃声,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人倘要,你說我怎么就攤上這事圾亏。” “怎么了封拧?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵志鹃,是天一觀的道長。 經(jīng)常有香客問我泽西,道長曹铃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任捧杉,我火速辦了婚禮陕见,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘味抖。我一直安慰自己评甜,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布仔涩。 她就那樣靜靜地躺著忍坷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪熔脂。 梳的紋絲不亂的頭發(fā)上佩研,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機與錄音锤悄,去河邊找鬼韧骗。 笑死,一個胖子當(dāng)著我的面吹牛零聚,可吹牛的內(nèi)容都是我干的袍暴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼隶症,長吁一口氣:“原來是場噩夢啊……” “哼政模!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蚂会,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤淋样,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后胁住,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體趁猴,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡刊咳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了儡司。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娱挨。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖捕犬,靈堂內(nèi)的尸體忽然破棺而出跷坝,到底是詐尸還是另有隱情,我是刑警寧澤碉碉,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布柴钻,位于F島的核電站,受9級特大地震影響垢粮,放射性物質(zhì)發(fā)生泄漏贴届。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一足丢、第九天 我趴在偏房一處隱蔽的房頂上張望粱腻。 院中可真熱鬧,春花似錦斩跌、人聲如沸绍些。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柬批。三九已至,卻和暖如春袖订,著一層夾襖步出監(jiān)牢的瞬間氮帐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工洛姑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留上沐,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓楞艾,卻偏偏與公主長得像参咙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子硫眯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容