闲来无事,追踪了下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芯片中去。

linux nand 坏块_linux内核中对nand的坏块管理相关推荐

  1. linux nand 坏块_Linux内核中NAND Flash坏块管理

    由于NAND Flash的现有工艺不能保证NAND的Memory Array在其生命周期中保持性能的可靠,因此在NAND芯片出厂的时候,厂家只能保证block 0不是坏块,对于其它block,则均有可 ...

  2. linux NAND驱动之一:内核中的NAND代码布局

    在Linux 内核中,MTD 源代码放在/driver/mtd 目录中,该目录中包含chips .devices .maps .nand .onenand 和ubi 六个子目录.其中只有nand 和o ...

  3. linux c++ 获取当前时间毫秒_Linux内核中的形形色色的“钟表”,你了解多少?

    如果Linux也是一个普通人的话,那么她的手腕上应该有十几块手表,包括:CLOCK_REALTIME.CLOCK_MONOTONIC.CLOCK_PROCESS_CPUTIME_ID.CLOCK_TH ...

  4. linux内核中的 哈希表_Linux内核中的设备模型及SCSI示例解析

    关于硬件架构 想要了解Linux操作系统的内核设备和驱动模型,最好先了解一下现在计算机硬件的架构.对计算机硬件有一定了解之后,对理解Linux内核中的设备和驱动模型非常有帮助.如图1是常规计算机的硬件 ...

  5. arch linux引导不启动_Linux 内核源代码的目录结构

    内核技术点合集 Linux 内核源代码包括三个主要部分: 1. 内核核心代码,包括第 3 章所描述的各个子系统和子模块,以及其它的支撑子系统,例 如电源管理.Linux 初始化等 2. 其它非核心代码 ...

  6. linux tcp文件分包_Linux内核参数优化

    前言: 1:介绍下linux内核的整个知识体系,(学会它,你肯定对linux内核有不一样的理解.) 2:谈谈Linux内核参数优化 一:linux内核技术点 Linux内核知识体系分为五个部分 1:l ...

  7. Linux 4.13/4.14内核中带来的ULP(Upper Layer Protocol)

    序 过了一个很爽的国庆假期,跟小小的小男朋友家长一起回其老家尝到了潮汕美食,南澳岛捕鱼捕虾,海鲜撑到爆,回到深圳次日小小另一个小朋友家长又带我们到东莞长安尝到了正宗的恩施土家菜,几天下来喝了几顿爽酒, ...

  8. linux netstat Netstat是在内核中访问网络连接状态及其相关信息的程序,它能提供TCP连接,TCP和UDP监听,进程内存管理的相关报告。

    在Internet RFC标准中,Netstat的定义是: Netstat是在内核中访问网络连接状态及其相关信息的程序,它能提供TCP连接,TCP和UDP监听,进程内存管理的相关报告. Netstat ...

  9. linux shell 宏定义_linux内核修炼之系统调用

    fork()这个系统调用是有两个返回值的,在子进程中的返回值是0,在父进程中的返回值是PID,如下 图 fork一次 返回两次 关于0x80中断和特权级检查 在mian函数的sched_init()函 ...

最新文章

  1. Windows快捷键集锦
  2. 22. 一个题来探查对 字符串,指针,数组三方面的关联使用方面的概念是否清晰,分析下面三个printf打印什么?...
  3. 互联网研发中负载均衡算法一点探索
  4. Android之基于AssetManager实现换肤方案
  5. android自定义optionmenu,android - 自定义onOptionMenu外观 - 堆栈内存溢出
  6. 【工作总结】银行的等级架构
  7. Intel 64/x86_64/IA-32/x86处理器 - 通用指令(3) - 逻辑指令/移位指令
  8. 编写访问数据库的应用层程序,经常catch出的一些错误
  9. 【OpenCV学习笔记】【教程翻译】二(车牌识别算法框架)
  10. mysql数据迁移性能_百万级MySQL的数据量,该如何快速的完成数据迁移?
  11. 在EWF上启用一个Hibernate Once/Resume Many环境
  12. Pascal VOC Dataset 下载地址
  13. 3D打印文件格式:STL、OBJ、AMF、3MF
  14. python 取数组最后一个_在numpy数组中查找最后一个值
  15. 百度云搭建微信公众平台服务器,微信大众开放平台开发03-百度BAE上搭建属于自己的微信公众平台 -JAVA,微信公众开放平台部署到百度云中BASE2.0,进行调试,木有钱买云服务器的亲们试试...
  16. windows录屏html文件,windows录屏怎么录?还有其他方法吗?
  17. NDIS(NDIS开发详解)
  18. 为了保证页面输出安全,我们经常需要对一些特殊的字符进行转义,请写一个函数 escapeHtml,将<, >, , “进行转义
  19. 百度云管家使用QQ第三方登录时提示“由于网络原因无法载入页面 请点击刷新后重试”
  20. 原语科技宣布完成千万级天使+轮融资,致力于打造隐私计算标准化产品

热门文章

  1. 皮一皮:他为我承受了太多太多...
  2. 常用的JVM参数,你现在就记好!
  3. SQL 语句中 left join 后用 on 还是 where,区别大了!
  4. 推荐一款免费的数据库管理工具,比 Navicat 还要好用,功能还很强大
  5. 还记得那个提速8倍的IDEA插件吗?VS Code版本也发布啦!
  6. 并发Bug之源有三,请睁大眼睛看清它们
  7. 死磕Java并发:J.U.C之AQS:CLH同步队列
  8. 小学数学开灯问题_包含数学暑假答案的一年级数学假期作业题
  9. elasticsearch match模糊查询
  10. win nvcc warning : The 'compute_20', 'sm_20', and 'sm_21' architectures are depr