Chinaunix首页 | 论坛 | 博客
  • 博客访问: 395915
  • 博文数量: 53
  • 博客积分: 1910
  • 博客等级: 中尉
  • 技术积分: 1130
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-10 14:56
文章分类

全部博文(53)

文章存档

2013年(1)

2012年(17)

2011年(33)

2010年(2)

分类: LINUX

2011-08-09 00:54:45

  ubi 是位于 ubifs 文件系统和mtd 层之间,负责ubi 卷管理

先看下两个 on-flash 数据结构 (ubi-media.h)
1. UBI erase counter header.

  1. 1. UBI erase counter header.
  2.  struct ubi_ec_hdr { /* 64 byte */
  3.     __be32 magic;
  4.     __u8 version;
  5.     __u8 padding1[3];
  6.     __be64 ec; /* Warning: the current limit is 31-bit */
  7.     __be32 vid_hdr_offset;
  8.     __be32 data_offset;
  9.     __u8 padding2[36];
  10.     __be32 hdr_crc;
  11. } __attribute__ ((packed));
其中主要字段:
ec             表示该逻辑块被擦除过的次数
vid_hdr_offset 表示vid 头的偏移,一般跟在ec header 后面
data_offset    表示用户数据的偏移位置

2. UBI volume identifier header
虽然叫卷标识头,但实际是描述logical 块的信息,
可以看ubi_eba_write_leb 中关于未映射logical block 处理部分的代码

  1. struct ubi_vid_hdr {
  2.     __be32 magic;
  3.     __u8 version;
  4.     __u8 vol_type;
  5.     __u8 copy_flag;
  6.     __u8 compat;
  7.     __be32 vol_id;
  8.     __be32 lnum; /* leb num !!! */
  9.     __u8 padding1[4];
  10.     __be32 data_size;
  11.     __be32 used_ebs;
  12.     __be32 data_pad;
  13.     __be32 data_crc;
  14.     __u8 padding2[4];
  15.     __be64 sqnum;
  16.     __u8 padding3[12];
  17.     __be32 hdr_crc;
  18. } __attribute__ ((packed));

其中主要字段:
vol_id          卷id号
used_ebs;       total number of used logical eraseblocks in this volume
lnum            逻辑块号
data_size       逻辑块包含字节数
data_crc        存储在该逻辑块上数据的CRC checksum
sqnum           该逻辑块的全局唯一串号

注释中提到了1个leb(逻辑块)对应2个peb(物理块的)情况
发生在两种情况,但主要都是块写操作过程中发生异常reset引起
所以引入data_crc ,和sqnum
当发生1对2情况,在选择peb 时的依据是:
1. 如果sqnum 大的块,data_crc 正确,那么选择sqnum 大的
2. 否则寻找sqnum 小的那个块
详细可看(ubi-media.h) 中大段注释


下面顺着ubi_init个过程,了解下所有相关的数据结构

首先 ubi_attach_mtd_dev 中会创建一个ubi_device,
主要字段介绍下:

  1. struct ubi_device {
  2.  /* 下面这两个 结构,表示 ubi device 是个char device
  3.  并且 ,属于linux 2.6的设备模型,支持sysfs */
  4.     struct cdev cdev;
  5.     struct device dev;
  6.     
  7.     int ubi_num;
  8.     char ubi_name[sizeof(UBI_NAME_STR)+5];
  9. /*下面表示该ubi device 上有几个卷,以及ubi volume数组
  10.   该数组最大外部卷数(128)+内部卷数(1) */    
  11.     int vol_count;
  12.     struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT];
  13.     
  14.     spinlock_t volumes_lock;
  15.     int ref_count;

  16.     int rsvd_pebs; /*保留的物理块 */
  17.     int avail_pebs; /* 有效物理块*/
  18.     int beb_rsvd_pebs; /* 为处理坏块保留的peb*/
  19.     
  20.     /* 同上,但只是个百分比,由CONFIG_MTD_UBI_BEB_RESERVE 决定*/
  21.     int beb_rsvd_level;

  22.         /* ubi init 之后,有该标志的必须被resize */
  23.     int autoresize_vol_id;
  24.     
  25.     /* 卷表(vtabl)中 slot数 ,和vtbl 的大小
  26.     具体计算方法(ubi_read_volume_table):
  27.     ubi->vtbl_slots = ubi->leb_size / UBI_VTBL_RECORD_SIZE;
  28.     if (ubi->vtbl_slots > UBI_MAX_VOLUMES)
  29.         ubi->vtbl_slots = UBI_MAX_VOLUMES;
  30.     ubi->vtbl_size = ubi->vtbl_slots * UBI_VTBL_RECORD_SIZE;
  31.     */
  32.     int vtbl_slots;
  33.     int vtbl_size;
  34.     
  35.     /* 放卷信息的结构,也是on flash 的,在内部卷中,
  36.      将它读入内存 */
  37.     struct ubi_vtbl_record *vtbl;
  38.     struct mutex volumes_mutex;

  39.     int max_ec;
  40.     /* Note, mean_ec is not updated run-time - should be fixed */
  41.     int mean_ec;

  42.     /* EBA sub-system's stuff */
  43.     /*EBA => Eraseblock Association */
  44.     unsigned long long global_sqnum;
  45.     spinlock_t ltree_lock;
  46.     struct rb_root ltree;
  47.     struct mutex alc_mutex;

  48.     /* Wear-leveling sub-system's stuff */
  49.     /* 已使用的pebs 的rb tree 根*/
  50.     struct rb_root used;
  51.     /* 空闲的pebs 的rb tree 根*/
  52.     struct rb_root free;
  53.     /* 需要擦写的pebs 的rb tree 根*/
  54.     struct rb_root scrub;
  55.     /*上面这写rb-tree 都是以pnum ,物理块号做权重*/
  56.     
  57.     /*用于磨损平衡保护的队列 ,联接被保护的物理块*/
  58.     struct list_head pq[UBI_PROT_QUEUE_LEN];
  59.     int pq_head;
  60.     spinlock_t wl_lock;
  61.     struct mutex move_mutex;
  62.     struct rw_semaphore work_sem;
  63.     int wl_scheduled;
  64.     /* ubi_wl_entry ,peb 的写平衡entry,可能挂接到不同的地方*/
  65.     struct ubi_wl_entry **lookuptbl;
  66.     struct ubi_wl_entry *move_from;
  67.     struct ubi_wl_entry *move_to;
  68.     int move_to_put;
  69.     struct list_head works;
  70.     int works_count;
  71.     struct task_struct *bgt_thread;
  72.     int thread_enabled;
  73.     char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2];

  74.     /* I/O sub-system's stuff */
  75.     /* 从下面(io_init)可以看到,这些ubi 变量与 mtd的关系
  76.     ubi->peb_size = ubi->mtd->erasesize;
  77.     ubi->peb_count = mtd_div_by_eb(ubi->mtd->size, ubi->mtd);
  78.     ubi->flash_size = ubi->mtd->size;
  79.     ubi->min_io_size = ubi->mtd->writesize;
  80.     ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft;
  81.     ubi->ec_hdr_alsize = ALIGN(UBI_EC_HDR_SIZE, ubi->hdrs_min_io_size);
  82.     ubi->vid_hdr_alsize = ALIGN(UBI_VID_HDR_SIZE, ubi->hdrs_min_io_size);
  83.     ubi->leb_start = ubi->vid_hdr_offset + UBI_EC_HDR_SIZE;
  84.     ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size);
  85.     ubi->leb_size = ubi->peb_size - ubi->leb_start;
  86.     */
  87.     long long flash_size;
  88.     int peb_count;
  89.     int peb_size; /* peb_size: physical eraseblock size */
  90.     int bad_peb_count;
  91.     int good_peb_count;
  92.     int min_io_size;
  93.     int hdrs_min_io_size;
  94.     int ro_mode;
  95.     
  96.     /* logical eraseblock size
  97.      其实就是leb 中去掉vid hdr,ec hdr
  98.      并且对齐到write size 后,剩余的部分*/
  99.     int leb_size;
  100.     int leb_start;
  101.     int ec_hdr_alsize;
  102.     int vid_hdr_alsize;
  103.     int vid_hdr_offset;
  104.     int vid_hdr_aloffset;
  105.     int vid_hdr_shift;
  106.     int bad_allowed;
  107.     struct mtd_info *mtd;

  108.     void *peb_buf1;
  109.     void *peb_buf2;
  110.     struct mutex buf_mutex;
  111.     struct mutex ckvol_mutex;
  112.     struct mutex mult_mutex;
  113. };

 

ubi_init=>ubi_attach_mtd_dev=>attach_by_scanning
  =>ubi_scan
其中 相关数据结构

  1. struct ubi_scan_info {
  2.     struct rb_root volumes;
  3.     struct list_head corr;
  4.     struct list_head free;
  5.     struct list_head erase;
  6.     struct list_head alien;
  7.     int bad_peb_count;
  8.     int vols_found;
  9.     int highest_vol_id;
  10.     int alien_peb_count;
  11.     int is_empty;
  12.     int min_ec;
  13.     int max_ec;
  14.     unsigned long long max_sqnum; /* 和ubi_vid_hdr 的sqnum 有关*/
  15.     int mean_ec;
  16.     uint64_t ec_sum;
  17.     int ec_count;
  18. };

volumes    RB-tree 的根 (point to ubi_scan_volume 's struct rb_node rb)
corr       链接 数据无效的block 的list
           比如write copy 抛弃的,或者其他数据损坏的block
free       链接 已经写上ubi_ec_hdr的block 的list
erase      erase和free 的区别就是mtd 层意义上的Free ,没有写过ubi层的东西
           比如ubi_ec_hdr
alien      链接卷上保留的block 的list
bad_peb_count   
           卷上 坏块数
is_empty   记录卷对应的mtd 区是否是空闲的
max_ec,max_ec,mean_ec 写平衡用的,记录卷上块当前最大最小的擦写次数
           和
max_sqnum   卷上最大串号          

ec_sum,ec_count 是临时变量,用于process_eb 时,对used, free(no vid)的块进行
           erase count累加和记数,然后最后用来计算mean_ec 的
           mean_ec =    ec_sum/ec_count    
           scan 过程的最后会将 各seb 的 ec 设置为 si->mean_ec;
           因为一开始总为0,后面因为写平衡原因,各块应该差不多的ec count,
           设置为平均值比较好

 

下面这个结构是在scan mtd  时 将物理block 根据扫描状态
挂到(add_to_list)ubi_scan_info 的 corr,free,earse,alien list
上的 一个物理块扫描信息结构
scanning information about a physical eraseblock :

  1. struct ubi_scan_leb {
  2.     int ec;
  3.     int pnum;
  4.     int lnum;
  5.     int scrub;
  6.     unsigned long long sqnum;
  7.     union {
  8.         struct rb_node rb;
  9.         struct list_head list;
  10. /* 表示该ubi_scan_leb 可以连接到per-volume RB-tree 或者 eraseblock list !!! */
  11.     } u;
  12. };

 

scan 中获得的卷信息
scanning information about a volume:

  1. struct ubi_scan_volume {
  2.     int vol_id;
  3.     int highest_lnum;
  4.     int leb_count;
  5.     int vol_type;
  6.     int used_ebs;
  7.     int last_data_size;
  8.     int data_pad;
  9.     int compat;
  10.     struct rb_node rb; /* 向上连到scan info */
  11.     struct rb_root root; /* root ,向下连到 scan leb info */
  12. };

root   链接所有属于该卷的scan leb info (ubi_scan_leb)的根
rb     红黑树节点,连接到 ubi_scan_info 的 volumes

具体可见下图:

 

在图中的连接有rb tree ,和list
大概层次为 ubi_scan_info
           对应于多个卷,向下通过rb tree连接ubi_scan_volume,
           volume id 大小做为rb-tree 的权重
           或者通过list (free,erase,corr) 连接到非隶属于某volume
           的ubi_scan_leb 
          
           ubi_scan_volume
           对应于某一个卷,向下通过rb tree 连接到ubi_scan_leb
           erase count 做为rb tree  的权重
          
           ubi_scan_leb
           对应于某一个逻辑块扫描信息,可以通过union ,rb node
           或 list 连接到 ubi_scan_info 和ubi_scan_volume
          
所以当flash 第1次初始化,并且没有坏块,那么所有ubi_scan_leb
都将list 到ubi_scan_info  的  erase list 上

上面就是scan 过程中的所有扫描结构,这些结构的产生,都是由
ec_hdr,vid_hdr 两个on-flash 结构 ,做判断依据的

 

ubi_attach_mtd_dev =>attach_by_scanning => ubi_scan
执行完成回到attach_by_scanning 可以通过si (struct ubi_scan_info)
获得的信息去填充ubi的一些数据:
ubi->bad_peb_count = si->bad_peb_count;
ubi->max_ec = si->max_ec;
ubi->mean_ec = si->mean_ec;

 

ubi_attach_mtd_dev =>attach_by_scanning =>ubi_read_volume_table
接下来ubi_read_volume_table 过程中会涉及到 ubi_vtbl_record

  1. struct ubi_vtbl_record {
  2.         /* 卷上保留 物理块数*/
  3.     __be32 reserved_pebs;
  4.     __be32 alignment;
  5.     /* 每个物理为对齐而保留的byte数*/
  6.     __be32 data_pad;
  7.     /* static or dynamic */
  8.     __u8 vol_type;
  9.     /* 卷记录开始更新,还没完成标志*/
  10.     __u8 upd_marker;
  11.     __be16 name_len;
  12.     __u8 name[UBI_VOL_NAME_MAX+1];
  13.     __u8 flags;
  14.     __u8 padding[23];
  15.     __be32 crc;
  16. } __attribute__ ((packed));

这个结构也是个 on flash 结构,被写到一个叫UBI_LAYOUT_VOLUME_ID
的内部卷,
ubi_read_volume_table 有两个分支,首先执行ubi_scan_find_sv
查找UBI_LAYOUT_VOLUME_ID的scan volume (ubi_scan_volume)
1. 如果没找到 create_empty_lvol=>create_vtbl创建
  最先设置一个 empty_vtbl_record,只是设置他CRC为0xf116c36b,代表
  是个empty record
  然后ubi_scan_get_free_peb 获得free peb ,写入layout volume 头
  vid_hdr->vol_type = UBI_VID_DYNAMIC;
  vid_hdr->vol_id = cpu_to_be32(UBI_LAYOUT_VOLUME_ID);
 
  然后将整个empty vtbl ,  最大为128slots,写入,
  最后执行ubi_scan_add_used,将前面分配并写入的peb 添加到ubi_scan_info
  (如果有旧的ubi_scan_leb,这个过程会根据 1个leb 对应2个peb 情况,
   释放old seb 对应的原来那个peb )
  
2. 如果layout 卷已经存在,执行process_lvol  ,
   对这个卷用ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb)
   进行编历,(layout volume 只包含vulome table及其备份)
   所以应该是两个leb(logical eraseblock),
   使用ubi_io_read_data,将leb0,leb1 ,全部读出来,经常检查,如果有
   需要,就进行修复
  
   注释中提到这两个块发生变化时的保存流程:
   * a. erase LEB 0;
   * b. write new data to LEB 0;
   * c. erase LEB 1;
   * d. write new data to LEB 1.
  

 

处理过ubi_vtbl_record之后进行init_volumes
ubi_attach_mtd_dev = >attach_by_scanning =>ubi_read_volume_table
  =>init_volumes  
在  init_volumes 过程中主要就是把 获得的
 on flash 的 volume 信息 (ubi_vtbl_record) 去初始化
 ubi_device中的
 struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT];
下面看下 ubi_volume 结构:

  1. struct ubi_volume {
  2.     struct device dev;
  3.     struct cdev cdev;
  4.     struct ubi_device *ubi;
  5.     int vol_id;
  6.     int ref_count;
  7.     int readers;
  8.     int writers;
  9.     int exclusive;

  10. /* 卷上保留的peb */
  11.     int reserved_pebs;
  12.     int vol_type;
  13.     int usable_leb_size;
  14. /* 卷上包含数据的leb */    
  15.     int used_ebs;
  16. /* 卷上包含数据的最后leb的实际字节数 */
  17.     int last_eb_bytes;
  18. /* 卷上包含数据的总节数*/    
  19.     long long used_bytes;
  20.     int alignment;
  21.     int data_pad;
  22.     int name_len;
  23.     char name[UBI_VOL_NAME_MAX + 1];
  24.     
  25. /*执行UBI_IOCVOLUP 卷更新命令时,更新的bytes
  26. 转换成不带data_pad的usable_leb_size(leb_size-data_pad)
  27. 后的块数 */
  28.     int upd_ebs;
  29.     
  30. /* UBI_IOCEBCH 相关 (ubi_start_leb_change)*/    
  31.     int ch_lnum;
  32.     int ch_dtype;
  33.     long long upd_bytes;
  34.     long long upd_received;
  35.     void *upd_buf;

  36. /* 这个很重要,leb 到peb 的映射表 */
  37.     int *eba_tbl;
  38. /* 一些标志*/    
  39.     unsigned int checked:1;
  40.     unsigned int corrupted:1;
  41.     unsigned int upd_marker:1;
  42. /*卷正在被更新*/    
  43.     unsigned int updating:1;
  44.     
  45.     unsigned int changing_leb:1;
  46. /* UBI_IOCSETPROP */    
  47.     unsigned int direct_writes:1;
  48. };

 

下面代码显示ubi_volume的相关信息如何从vtbl(ubi_vtbl_record)中获得的:

  1. vol->reserved_pebs = be32_to_cpu(vtbl[i].reserved_pebs);
  2.         vol->alignment = be32_to_cpu(vtbl[i].alignment);
  3.         vol->data_pad = be32_to_cpu(vtbl[i].data_pad);
  4.         vol->vol_type = vtbl[i].vol_type == UBI_VID_DYNAMIC ?
  5.                     UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME;
  6.         vol->name_len = be16_to_cpu(vtbl[i].name_len);
  7.         vol->usable_leb_size = ubi->leb_size - vol->data_pad;
  8.         memcpy(vol->name, vtbl[i].name, vol->name_len);
  9.         vol->name[vol->name_len] = '\0';
  10.         vol->vol_id = i;

 

接下来ubi_scan_find_sv(si, i);       
从扫描信息ubi_scan_info 中根据volume id 找到volume scan info (sv)
接下来用sv (ubi_scan_volume)的信息更新vol(ubi_volume)

  1. vol->used_ebs = sv->used_ebs;
  2.   vol->used_bytes =
  3.    (long long)(vol->used_ebs - 1) * vol->usable_leb_size;
  4.   vol->used_bytes += sv->last_data_size;
  5.   vol->last_eb_bytes = sv->last_data_size;

 上面这些操作,循环ubi->vtbl_slots次后,排除所有empty record
(判断条件为vtbl[i].reserved_pebs)为0,是因为empty_vtbl_record
  被设置为static 全局变量) 

接下来添加 layout volume 相关的ubi_volume ,这个ubi_volume 放到
ubi->volumes数组中 ubi->vtbl_slots 下标后
(ubi->volumes[vol_id2idx(ubi, vol->vol_id)] = vol;)
最后更新 整个ubi 的reserved pebs 和avail_pebs
代码如下:

  1. ubi->rsvd_pebs += reserved_pebs;
  2.  ubi->avail_pebs -= reserved_pebs;

 

ubi_attach_mtd_dev = >attach_by_scanning =>ubi_read_volume_tabl
  =>check_scanning_info
check 主要是 扫描所有vtbl_slots个static  + 1个内部卷(layout),
使用ubi_scan_find_sv 查找sv(ubi_scan_volume),对于sv 存在,而该vol_id对应的
ubi_volume不存在的情况,通过 ubi_scan_rm_volume 做下面两步
 1. delete sv 's seb rb-tree (&struct ubi_scan_leb objects),
 2 .delete sv from si->volumes rb-tree
(si =>sv=>seb !!! 注意这样一个层次)

做ubi_scan_rm_volume的还有一种情况是vol->reserved_pebs 为0

 
 
ubi_init=>ubi_attach_mtd_dev=>attach_by_scanning
  =>ubi_wl_init_scan
 
其中数据结构: 

  1. struct ubi_wl_entry {
  2.     union {
  3. /* link 到free 或者used RB tree*/    
  4.         struct rb_node rb;
  5. /* link 到写平衡保护队列(ubi_device 的
  6. struct list_head pq[UBI_PROT_QUEUE_LEN];)*/        
  7.         struct list_head list;
  8.     } u;
  9.     int ec; /*erase count */
  10.     int pnum; /* phiscal block number */
  11. };

ubi_wl_init_scan 是通过si(ubi_scan_info)来初始化,磨损平衡子系统的
1.先对si->erase 上每个peb 分配1个ubi_wl_entry ,放到ubi->lookuptbl[e->pnum]中
  以pnum 为下标,然后调度erase work (对erase list 上的seb 继续erase 操作)
  具体操作可以看下erase_worker,正真执行是在ubi_thread
 
2.对si->free list 上的每个seb, 分配并初始ubi_wl_entry, 然后将该ubi_wl_entry
  插入ubi_devic 的free RB tree

3.对si上corrupted  list 的每个seb进行处理,基本同si->erase

4.最后扫描 si->volumes,获得各sv(ubi_scan_volume),然后遍历sv 上
  挂seb(ubi_scan_leb)的RB tree,然后对每个seb分配1个ubi_wl_entry
  然后根据seb的scrub 判断是否需要擦除,如果需要,就挂到ubi_device
  的scrub RB tree 上,否则挂到used RB tree上
 
5. 执行ensure_wear_leveling,判断是否要进行写平衡处理(wear_leveling_worker)
   判断的条件是,ubi_device used RB-tree 上最左边(ec 最小的)和
   ubi_device free RB tree 上接ec近于WL_FREE_MAX_DIFF的节点
   两个node 之间ec的差值大于等于UBI_WL_THRESHOLD
  
   wear_leveling_worker 的工作就是将used 上ec 大的,copy 到 free 上ec
   小的block 上

  
关于写平衡数据结构关联图:

上图主要描述了ubi_device,ubi_volume,ubi_wl_entry
的关系,
1.ubi_device 上free,used,scrub,上根据不同情况的3个rb tree root
  各ubi_wl_entry根据其状态free ,used or scrub挂到各自的rb tree上
 
2.  ubi_device 上pq 是个保护队列,比如free 到used 的 rb tree的移动
    过程中需要保护下, 这时就先挂到pq上,具体原因可见 wl.c的
    UBI wear-leveling sub-system 部分注释
3.   lookuptbl 是个ubi_wl_entry的指针数组,为了快速查找ubi_wl_entry,
    每个ubi_wl_entry pointer都在lookuptbl数组里,以pnum 做下标
   
4.  ubi_volume 通过 ubi_device 类型指针ubi回指向ubi_device
    ubi_volume 中另一个比较重要的就是LEB 和 PEB 的映射表
     int *eba_tbl

 

ubi_init=>ubi_attach_mtd_dev=>attach_by_scanning
  =>ubi_eba_init_scan
  
这个函数作用,如其注释,就是init eraseblock Association 子系统
主要完成对ubi_volume上 int *eba_tbl (LEB->PEB mapping)的
初始化工作

1.先分配eba_tbl
2.然后对小于reserved_pebs物理块号的设置为unmap
3.遍历sv root, 获得seb lnum ,pnum ,来对eab_tbl 进行初始化

 

ubi_init=>ubi_attach_mtd_dev=>attach_by_scanning
   =>ubi_scan_destroy_si
  
   把前面为scan 所生成的si, sv,seb 全部释放,这些数据
只是为生成上面ubi_device,ubi_volume 结构中数据,所使用的中间变量
   
 
ubi_init=>ubi_attach_mtd_dev
  =>uif_init
该函数完成用户接口初始化
主要两个层次
    ubi_device
    1.init ubi device cdev 的操作为ubi_cdev_operations
      主要是ioctrl
    2.以char dev 注册 ubi device  到系统
    3.ubi_sysfs_init 实现ubi device sysfs 接口部分属性
   
    ubi_volme
     对 ubi->vtbl_slots个volum 进行
     1.volem cdev 的操作设置为ubi_vol_cdev_operations
     2.以char dev 注册 ubi volume dev  到系统
     3.volume_sysfs_init 实现ubi volume sysfs 接口部分属性


ubi_init=>ubi_attach_mtd_dev
最后ubi_attach_mtd_dev 中创建了  ubi_thread
该内核线程,用来执行ubi_device 上的works,主要就是后台擦除,和wear level
处理

 

到这里整个ubi init 基本完成,大体可以看出
 每个mtd partition 可以attach 到一个ubi device上,
 在每个ubi device上又可以创建很多ubi volume,
 而每个ubi volume又被作为一个mtd device 保存于mtd table 中    

(上面提到的写平衡和磨损平衡是同一个意思)

阅读(7523) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

Crazy_bun2013-09-13 17:22:02

给你刷到65535次怎么样?