linux nand 坏块_linux内核中对nand的坏块管理

本文详细探讨了Linux内核如何管理NAND闪存的坏块。在nand_scan_tail函数的最后阶段,坏块管理开始。如果NAND_SKIP_BBTSCAN选项未设置,则调用nand_scan_bbt()进行坏块表(BBT)的构建。BBT存储在内存或NAND芯片中,依赖于驱动程序的配置。通过分析bbt、bbt_td、bbt_md和badblock_pattern等结构体,了解了坏块信息的存储和识别机制。nand_default_bbt函数根据设备需求选择默认的BBT支持,并调用nand_scan_bbt函数完成实际操作。
摘要由CSDN通过智能技术生成

闲来无事,追踪了下linux内核中对nand的坏块管理代码。大致记录一下。

内核中对nand的坏块管理是在nand的驱动中实现的,一般情况下,我们在实现nand控制器的驱动时不用考虑坏块的管理,这些机制已经在nand驱动的通用框架中实现了,我们要做的就是在nand驱动的框架上对接上nand控制器私有的操作与参数就可以了,例如读写函数以及nand控制器支持的ecc布局等。当然,这里主要是分析坏块管理的部分,就不多说驱动的开发了。

这里的分析是在bcm7231平台的nand驱动上面分析的。

nand驱动在加载的时候会调用nand_scan_tail函数,此函数的前面大部分是在确定所使用ecc方式和函数指针,以及一个页的读写需要作几次ecc校验等,这些都是根据nand驱动初始化的时候指定的一些参数决定的。在此函数的最后面开始关于BBT的操作:

/* Check, if we should skip the bad block table scan */

if(chip->options & NAND_SKIP_BBTSCAN)

return0;

/* Build bad block table */

returnchip->scan_bbt(mtd);

首先会检查chip->options中是否有NAND_SKIP_BBTSCAN选项,如果有,则直接返回,NAND_SKIP_BBTSCAN一般是在nand控制器驱动中选择是否被赋予的,一般不会赋予NAND_SKIP_BBTSCAN选项。如果没有,则调用chip->scan_bbt()函数。chip->scan_bbt()是一个函数指针,一般在nand_set_defaults中被赋值为nand_default_bbt。

首先我们看下struct nand_chip结构体中关于BBT的部分:

/**

*... ...

* @bbt:       [INTERN] bad block table pointer

* @bbt_td:        [REPLACEABLE] bad block table descriptor for flash

*         lookup.

* @bbt_md:        [REPLACEABLE] bad block table mirror descriptor

* @badblock_pattern:  [REPLACEABLE] bad block scan pattern used for initial

*         bad block scan.

*... ...

*/

structnand_chip {

……

uint8_t     *bbt

structnand_bbt_descr    *bbt_td;

structnand_bbt_descr    *bbt_md;

structnand_bbt_descr    *badblock_pattern;

……

};

bbt是在内存中分配的一块内存(nand_scan_bbt中分配的,后面会说到),这块内存会存放BBT。如果定义了NAND_BBT_USE_FLASH(这个可以在编写nand控制器驱动时选择),会先在nand中查找BBT是否存在,如果存在,然后读出放在bbt中,如果不存在(全新flash会有这种情况),则在bbt中建立BBT,然后写入nand中。如果没有定义NAND_BBT_USE_FLASH,那么会直接在bbt指向的内存中建立BBT。

bbt_td、bbt_md和badblock_pattern就是在nand_default_bbt函数中赋值的3个结构体。它们虽然是相同的结构体类型,但却有不同的作用和含义。其中bbt_td和bbt_md是主bbt和镜像bbt的描述符(镜像bbt主要用来对bbt的update和备份),它们只在把bbt存储在NAND芯片的情况下使用,用来从NAND芯片中查找bbt。若bbt存储在内存中,bbt_td和bbt_md将会被赋值为NULL。

badblock_pattern就是坏块信息的pattern,其中定义了坏块信息在oob中的存储位置,以及内容(即用什么值表示这个block是个坏块)。通常用1或2个字节来标志一个block是否为坏块,这1或2个字节就是坏块信息,如果这1或2个字节的内容是0xff,那就说明这个block是好的,否则就是坏块。对于坏块信息在NAND芯片中的存储位置,small page(每页512 Byte)和big page(每页2048 Byte)的两种NAND芯片不尽相同。一般来说,small page的NAND芯片,坏块信息存储在每个block的第一个page的oob的第六个字节中,而big page的NAND芯片,坏块信息存储在每个block的第一个page的oob的第1和第2个字节中。即使某种NAND芯片的坏块信息不是这样的存储方式也没关系,因为我们可以在badblock_pattern中自己指定坏块信息的存储位置,以及用什么值来标志坏块(其实这个值表示的应该是“好块”,因为MTD会把从oob中坏块信息存储位置读出的内容与这个值做比较,若相等,则表示是个“好块”,否则就是坏块)。

下面是nand_default_bbt的实现:

/**

* nand_default_bbt - [NAND Interface] Select a default bad block table for the device

* @mtd: MTD device structure

*

* This function selects the default bad block table support for the device and

* calls the nand_scan_bbt function.

*/

intnand_default_bbt(structmtd_info *mtd)

{

structnand_chip *this= mtd->priv;

/*

* Default for AG-AND. We must use a flash based bad block table as the

* devices have factory marked _good_ blocks. Erasing those blocks

* leads to loss of the good / bad information, so we _must_ store this

* information in a good / bad table during startup.

*/

if(this->options & NAND_IS_AND) {

/* Use the default pattern descriptors */

if(!this->bbt_td) {

this->bbt_td = &bbt_main_descr;

this->bbt_md = &bbt_mirror_descr;

}

this->bbt_options |= NAND_BBT_USE_FLASH;

returnnand_scan_bbt(mtd, &agand_flashbased);

}

/* Is a flash based bad block table requested? */

if(this->bbt_options & NAND_BBT_USE_FLASH) {

/* Use the default pattern descriptors */

if(!this->bbt_td) {

if(this->bbt_options & NAND_BBT_NO_OOB) {

this->bbt_td = &bbt_main_no_bbt_descr;

this->bbt_md = &bbt_mirror_no_bbt_descr;

} else{

this->bbt_td = &bbt_main_descr;

this->bbt_md = &bbt_mirror_descr;

}

}

} else{

this->bbt_td = NULL;

this->bbt_md = NULL;

}

if(!this->badblock_pattern)

nand_create_badblock_pattern(this);

returnnand_scan_bbt(mtd,this->badblock_pattern);

}

在分析nand_scan_bbt函数之前先介绍下struct nand_bbt_descr的各成员的含义。其定义如下:

/**

* struct nand_bbt_descr - bad block table descriptor

* @options:    options for this descriptor

* @pages:  the page(s) where we find the bbt, used with option BBT_ABSPAGE

*      when bbt is searched, then we store the found bbts pages here.

*      Its an array and supports up to 8 chips now

* @offs:   offset of the pattern in the oob area of the page

* @veroffs:    offset of the bbt version counter in the oob are of the page

* @version:    version read from the bbt page during scan

* @len:    length of the pattern, if 0 no pattern check is performed

* @maxblocks:  maximum number of blocks to search for a bbt. This number of

*      blocks is reserved at the end of the device where the tables are

*      written.

* @reserved_block_code: if non-0, this pattern denotes a reserved (rather than

*              bad) block in the stored bbt

* @pattern:    pattern to identify bad block table or factory marked good /

*      bad blocks, can be NULL, if len = 0

*

* Descriptor for the bad block table marker and the descriptor for the

* pattern which identifies good and bad blocks. The assumption is made

* that the pattern and the version count are always located in the oob area

* of the first block.

*/

struct nand_bbt_descr {

int options;

int pages[NAND_MAX_CHIPS];

int offs;

int veroffs;

uint8_t version[NAND_MAX_CHIPS];

int len;

int maxblocks;

int reserved_block_code;

uint8_t *pattern;

};

options:bad block table或者bad block的选项,可用的选择以及各选项具体表示什么含义,可以参考。

pages:bbt专用。在查找bbt的时候,若找到了bbt,就把bbt所在的page号保存在这个成员变量中。若没找到bbt,就会把新建立的bbt的保存位置赋值给它。因为系统中可能会有多个NAND芯片,我们可以为每一片NAND芯片建立一个bbt,也可以只在其中一片NAND芯片中建立唯一的一个bbt,所以这里的pages是个维数为NAND_MAX_CHIPS的数值,用来保存每一片NAND芯片的bbt位置。当然,若只建立了一个bbt,那么就只使用pages[0]。

offs、len和pattern:MTD会从oob的offs中读出len长度的内容,然后与pattern指针指向的内容做比较,若相等,则表示找到了bbt,或者表示这个block是好的。

veroffs和version:bbt专用。MTD会从oob的veroffs中读出一个字节的内容,作为bbt的版本值保存在version中。

maxblocks:bbt专用。MTD在查找bbt的时候,不会查找NAND芯片中所有的block,而是最多查找maxblocks个block。

下面接着分析nand_scan_bbt函数。

bcm7231的nand控制器驱动中有这么一行:chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;所以,上面函数的走向也很明确。

在最后调用了nand_scan_bbt函数。此函数的注释有这么一句话:

/**

* nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)

... ...

*/

可见,BBT的相关工作基本都是在这个函数内完成的。其函数实现如下:

intnand_scan_bbt(structmtd_info *mtd,structnand_bbt_descr *bd)

{

structnand_chip *this= mtd->priv;

intlen, res = 0;

uint8_t *buf;

structnand_bbt_descr *td =this->bbt_td;

structnand_bbt_descr *md =this->bbt_md;

len = mtd->size >> (this->bbt_erase_shift + 2);

/*

* Allocate memory (2bit per block) and clear the memory bad block

* table.

*/

this->bbt = kzalloc(len, GFP_KERNEL);

if(!this->bbt) {

printk(KERN_ERR "nand_scan_bbt: Out of memory\n");

return-ENOMEM;

}

/*

* If no primary table decriptor is given, scan the device to build a

* memory based bad block table.

*/

if(!td) {

if((res = nand_memory_bbt(mtd, bd))) {

pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");

kfree(this->bbt);

this->bbt = NULL;

}

returnres;

}

verify_bbt_descr(mtd, td);

verify_bbt_descr(mtd, md);

/* Allocate a temporary buffer for one eraseblock incl. oob */

len = (1 bbt_erase_shift);

len += (len >> this->page_shift) * mtd->oobsize;

buf = vmalloc(len);

if(!buf) {

printk(KERN_ERR "nand_bbt: Out of memory\n");

kfree(this->bbt);

this->bbt = NULL;

return-ENOMEM;

}

/* Is the bbt at a given page? */

if(td->options & NAND_BBT_ABSPAGE) {

res = read_abs_bbts(mtd, buf, td, md);

} else{

/* Search the bad block table using a pattern in oob */

res = search_read_bbts(mtd, buf, td, md);

}

if(res)

res = check_create(mtd, buf, bd);

/* Prevent the bbt regions from erasing / writing */

mark_bbt_region(mtd, td);

if(md)

mark_bbt_region(mtd, md);

vfree(buf);

returnres;

}

上面已经说了,BBT可以存放在内存中,也可以存放在flash中,nand_scan_bbt会跟据bbt_td的值判断BBT是存放在内存中还是在flash中,bbt_td为NULL,那么BBT就会存放在内存中。在内存中建立BBT的过程是这样的:

直接调用nand_memory_bbt函数,nand_memory_bbt函数的主要工作就是在内存中建立bbt,其实就是调用了create_bbt函数。create_bbt函数的工作方式很简单,就是扫描NAND芯片所有的block,读取每个block中第一个page的oob内容,然后根据oob中的坏块信息建立起bbt,这坏块信息就是根据badblock_pattern来确定的。可以参见关于struct nand_bbt_descr中的offs、len和pattern成员变量的解释。

相对与BBT存储在内存中来说,BBT存储在nand中的过程就稍微复杂一点了。

几下来就是在flash中创建或读取BBT了。

verify_bbt_descr(mtd, td);与verify_bbt_descr(mtd, md);没有做实际的动作,只是检查一下相关的参数。接下来由if (td->options & NAND_BBT_ABSPAGE)可知,从flash上读取读取BBT又分为两种情况。

第一种情况是定义了NAND_BBT_ABSPAGE,那么调用read_abs_bbts函数直接从给定的page地址读取。那么这个page地址在什么时候指定呢?就是在你的NAND driver中指定。前文说过,在struct nand_chip结构体中有两个成员变量,分别是bbt_td和bbt_md,MTD为它们附上了default的值,但是你也可以根据你的需要为它们附上你自己定义的值。假如你为bbt_td和bbt_md的options成员变量定义了NAND_BBT_ABSPAGE,同时又把你的bbt所在的page地址保存在bbt_td和bbt_md的pages成员变量中,MTD就可以直接在这个page地址中读取bbt了。值得一提的是,在实际使用时一般不这么干,因为你不能保证你保存bbt的那个block就永远不会坏,而且这样也不灵活。

第二种情况是调用那个search_read_bbts函数试着在NAND芯片的maxblocks(请见上文关于struct nand_bbt_descr中maxblocks的说明)个block中查找bbt是否存在,若找到,就可以读取bbt了。

MTD查找bbt的过程为:如果你在bbt_td和bbt_md的options 成员变量中定义了 NAND_BBT_LASTBLOCK,那么MTD就会从NAND芯片的最后一个block开始查找(在default情况下,MTD就是这么干的),否则就从第一个block开始查找。

与查找oob中的坏块信息时类似,MTD会从所查找block的第一个page的oob中读取内容,然后与bbt_td或bbt_md中patter指向的内容做比较,若相等,则表示找到了bbt,否则就继续查找下一个block。顺利的情况下,只需查找一个block中就可以找到bbt,否则MTD最多会查找maxblocks个block。若找到了bbt,就把该bbt所在的page地址保存到bbt_td或bbt_md的pages成员变量中,否则pages的值为-1。

如果系统中有多片NAND芯片,并且为每一片NAND芯片都建立一个bbt,那么就会在每片NAND芯片上重复以上过程。

接着,nand_scan_bbt函数会调用check_create函数,该函数会判断是否找到了bbt,其实就是判断bbt_td或者bbt_md中pages成员变量的值是否有效。若找到了bbt,就会把bbt从NAND芯片中读取出来,并保存到struct nand_chip中bbt指针指向的内存中;若没找到,就会调用create_bbt函数建立bbt(与bbt存储在内存中时情况一样),同时把bbt写入到NAND芯片中去。

http://blog.csdn.net/haomcu/article/details/8181622

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值