linux nand bbt 坏块管理部分

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

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

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

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

[cpp] view plain copy print ?
  1. /* Check, if we should skip the bad block table scan */  
  2. if (chip->options & NAND_SKIP_BBTSCAN)  
  3.     return 0;  
  4.   
  5. /* Build bad block table */  
  6. return chip->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的部分:

[cpp] view plain copy print ?
  1.  /**  
  2.   *... ... 
  3.   * @bbt:       [INTERN] bad block table pointer 
  4.   * @bbt_td:        [REPLACEABLE] bad block table descriptor for flash 
  5.   *         lookup. 
  6.   * @bbt_md:        [REPLACEABLE] bad block table mirror descriptor 
  7.   * @badblock_pattern:  [REPLACEABLE] bad block scan pattern used for initial 
  8.   *         bad block scan. 
  9.   *... ... 
  10.   */  
  11. struct nand_chip {  
  12.     ……  
  13.     uint8_t     *bbt  
  14.     struct nand_bbt_descr    *bbt_td;  
  15.     struct nand_bbt_descr    *bbt_md;  
  16.     struct nand_bbt_descr    *badblock_pattern;  
  17.     ……  
  18. };  
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的实现:

[cpp] view plain copy print ?
  1. /** 
  2.  * nand_default_bbt - [NAND Interface] Select a default bad block table for the device 
  3.  * @mtd: MTD device structure 
  4.  * 
  5.  * This function selects the default bad block table support for the device and 
  6.  * calls the nand_scan_bbt function. 
  7.  */  
  8. int nand_default_bbt(struct mtd_info *mtd)  
  9. {  
  10.     struct nand_chip *this = mtd->priv;  
  11.   
  12.     /* 
  13.      * Default for AG-AND. We must use a flash based bad block table as the 
  14.      * devices have factory marked _good_ blocks. Erasing those blocks 
  15.      * leads to loss of the good / bad information, so we _must_ store this 
  16.      * information in a good / bad table during startup. 
  17.      */  
  18.     if (this->options & NAND_IS_AND) {  
  19.         /* Use the default pattern descriptors */  
  20.         if (!this->bbt_td) {  
  21.             this->bbt_td = &bbt_main_descr;  
  22.             this->bbt_md = &bbt_mirror_descr;  
  23.         }  
  24.         this->bbt_options |= NAND_BBT_USE_FLASH;  
  25.         return nand_scan_bbt(mtd, &agand_flashbased);  
  26.     }  
  27.   
  28.     /* Is a flash based bad block table requested? */  
  29.     if (this->bbt_options & NAND_BBT_USE_FLASH) {  
  30.         /* Use the default pattern descriptors */  
  31.         if (!this->bbt_td) {  
  32.             if (this->bbt_options & NAND_BBT_NO_OOB) {  
  33.                 this->bbt_td = &bbt_main_no_bbt_descr;  
  34.                 this->bbt_md = &bbt_mirror_no_bbt_descr;  
  35.             } else {  
  36.                 this->bbt_td = &bbt_main_descr;  
  37.                 this->bbt_md = &bbt_mirror_descr;  
  38.             }  
  39.         }  
  40.     } else {  
  41.         this->bbt_td = NULL;  
  42.         this->bbt_md = NULL;  
  43.     }  
  44.   
  45.     if (!this->badblock_pattern)  
  46.         nand_create_badblock_pattern(this);  
  47.   
  48.     return nand_scan_bbt(mtd, this->badblock_pattern);  
  49. }  

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

[plain] view plain copy print ?
  1. /**  
  2.  * struct nand_bbt_descr - bad block table descriptor  
  3.  * @options:    options for this descriptor  
  4.  * @pages:  the page(s) where we find the bbt, used with option BBT_ABSPAGE  
  5.  *      when bbt is searched, then we store the found bbts pages here.  
  6.  *      Its an array and supports up to 8 chips now  
  7.  * @offs:   offset of the pattern in the oob area of the page  
  8.  * @veroffs:    offset of the bbt version counter in the oob are of the page  
  9.  * @version:    version read from the bbt page during scan  
  10.  * @len:    length of the pattern, if 0 no pattern check is performed  
  11.  * @maxblocks:  maximum number of blocks to search for a bbt. This number of  
  12.  *      blocks is reserved at the end of the device where the tables are  
  13.  *      written.  
  14.  * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than  
  15.  *              bad) block in the stored bbt  
  16.  * @pattern:    pattern to identify bad block table or factory marked good /  
  17.  *      bad blocks, can be NULL, if len = 0  
  18.  *  
  19.  * Descriptor for the bad block table marker and the descriptor for the  
  20.  * pattern which identifies good and bad blocks. The assumption is made  
  21.  * that the pattern and the version count are always located in the oob area  
  22.  * of the first block.  
  23.  */  
  24. struct nand_bbt_descr {  
  25.     int options;  
  26.     int pages[NAND_MAX_CHIPS];  
  27.     int offs;  
  28.     int veroffs;  
  29.     uint8_t version[NAND_MAX_CHIPS];  
  30.     int len;  
  31.     int maxblocks;  
  32.     int reserved_block_code;  
  33.     uint8_t *pattern;  
  34. };  
options:bad block table或者bad block的选项,可用的选择以及各选项具体表示什么含义,可以参考<linux/mtd/nand.h>。
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函数。此函数的注释有这么一句话:

[cpp] view plain copy print ?
  1. /** 
  2.  * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) 
  3.  ... ... 
  4.  */  
可见,BBT的相关工作基本都是在这个函数内完成的。其函数实现如下:

[cpp] view plain copy print ?
  1. int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)  
  2. {  
  3.     struct nand_chip *this = mtd->priv;  
  4.     int len, res = 0;  
  5.     uint8_t *buf;  
  6.     struct nand_bbt_descr *td = this->bbt_td;  
  7.     struct nand_bbt_descr *md = this->bbt_md;  
  8.   
  9.     len = mtd->size >> (this->bbt_erase_shift + 2);  
  10.     /* 
  11.      * Allocate memory (2bit per block) and clear the memory bad block 
  12.      * table. 
  13.      */  
  14.     this->bbt = kzalloc(len, GFP_KERNEL);  
  15.     if (!this->bbt) {  
  16.         printk(KERN_ERR "nand_scan_bbt: Out of memory\n");  
  17.         return -ENOMEM;  
  18.     }  
  19.   
  20.     /* 
  21.      * If no primary table decriptor is given, scan the device to build a 
  22.      * memory based bad block table. 
  23.      */  
  24.     if (!td) {  
  25.         if ((res = nand_memory_bbt(mtd, bd))) {  
  26.             pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");  
  27.             kfree(this->bbt);  
  28.             this->bbt = NULL;  
  29.         }  
  30.         return res;  
  31.     }  
  32.     verify_bbt_descr(mtd, td);  
  33.     verify_bbt_descr(mtd, md);  
  34.   
  35.     /* Allocate a temporary buffer for one eraseblock incl. oob */  
  36.     len = (1 << this->bbt_erase_shift);  
  37.     len += (len >> this->page_shift) * mtd->oobsize;  
  38.     buf = vmalloc(len);  
  39.     if (!buf) {  
  40.         printk(KERN_ERR "nand_bbt: Out of memory\n");  
  41.         kfree(this->bbt);  
  42.         this->bbt = NULL;  
  43.         return -ENOMEM;  
  44.     }  
  45.   
  46.     /* Is the bbt at a given page? */  
  47.     if (td->options & NAND_BBT_ABSPAGE) {  
  48.         res = read_abs_bbts(mtd, buf, td, md);  
  49.     } else {  
  50.         /* Search the bad block table using a pattern in oob */  
  51.         res = search_read_bbts(mtd, buf, td, md);  
  52.     }  
  53.   
  54.     if (res)  
  55.         res = check_create(mtd, buf, bd);  
  56.   
  57.     /* Prevent the bbt regions from erasing / writing */  
  58.     mark_bbt_region(mtd, td);  
  59.     if (md)  
  60.         mark_bbt_region(mtd, md);  
  61.   
  62.     vfree(buf);  
  63.     return res;  
  64. }  

上面已经说了,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芯片中去。

你可能感兴趣的:(linux nand bbt 坏块管理部分)