Chinaunix首页 | 论坛 | 博客
  • 博客访问: 572713
  • 博文数量: 114
  • 博客积分: 1620
  • 博客等级: 上尉
  • 技术积分: 1104
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-30 09:16
文章分类

全部博文(114)

文章存档

2016年(1)

2015年(2)

2014年(4)

2013年(9)

2012年(20)

2011年(78)

分类:

2012-07-24 14:58:54

       leeming的话来说,一个大的工程中,最最核心的往往是数据结构体的定义。所以看代码不急着看c文件,而是主要看documenth文件,来理解设计者的思路,这样才能走对路。

1. struct ubi_device

       UBI中对于一个UBI设备的抽象是以struct ubi_device来定义,其中包括了该UBI设备的各种信息。

struct ubi_device {

       struct cdev cdev;

       struct device dev;

       int ubi_num;//UBI设备的标号,在ubiattach用户程序时以-d选项来输入

       char ubi_name[sizeof(UBI_NAME_STR)+5];//ubi设备的名称

       int vol_count;//在该UBI设备中有多少个volume

       struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT];

       spinlock_t volumes_lock;

       int ref_count;

       int image_seq;

       int rsvd_pebs;//保留的LEB数目

       int avail_pebs;//可用的LEB数目

       int beb_rsvd_pebs;//为坏块处理而保留的LEB数目

       int beb_rsvd_level;//为坏块处理而保留的LEB的正常数目

       int autoresize_vol_id;

       int vtbl_slots;

       int vtbl_size;//volume表的大小(bytes

       struct ubi_vtbl_record *vtbl;//内存中volume表的拷贝

       struct mutex device_mutex;

       int max_ec;//最大的erase counter

       /* Note, mean_ec is not updated run-time - should be fixed */

       int mean_ec;//平均erase counter

       /* EBA sub-system's stuff */

       unsigned long long global_sqnum;

       spinlock_t ltree_lock;

       struct rb_root ltree;

       struct mutex alc_mutex;

       /* Wear-leveling sub-system's stuff */

       struct rb_root used;//一个红黑树,其中是已用的blcok

       struct rb_root erroneous;// RB-tree of erroneous used physical eraseblocks

       struct rb_root free;//红黑树的根,其中是没有用到的block

       struct rb_root scrub;//需要擦除的blcok

       struct list_head pq[UBI_PROT_QUEUE_LEN];

       int pq_head;

       spinlock_t wl_lock;

       struct mutex move_mutex;

       struct rw_semaphore work_sem;

       int wl_scheduled;

       struct ubi_wl_entry **lookuptbl;// a table to quickly find a &struct ubi_wl_entry object for any  physical eraseblock,,一个struct ubi_wl_entry类型的数组,以pnum为下标,记录该UBI设备的每一个block

       struct ubi_wl_entry *move_from;// physical eraseblock from where the data is being moved

       struct ubi_wl_entry *move_to;// physical eraseblock where the data is being moved to

       int move_to_put;//标志位,用于标志目的LEB是否被put

       struct list_head works;// list of pending works

       int works_count;// count of pending works

       struct task_struct *bgt_thread;//UBI的后台进程

       int thread_enabled;

       char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2];//后台进程的名字

       struct notifier_block reboot_notifier;//内核通知链

       /* I/O sub-system's stuff */

       long long flash_size;//MTD分区的大小

       int peb_count;//LEB的数目

       int peb_size;//LEB的大小(每一个block的大小)

       int bad_peb_count;//坏块数目

       int good_peb_count;//能使用的LEB数目

       int erroneous_peb_count;

       int max_erroneous;

       int min_io_size;//最小操作单元的大小,也就是一个page的大小

       int hdrs_min_io_size;

       int ro_mode;

       int leb_size;//逻辑块的大小,一般等于peb_size

       int leb_start;//逻辑块块从物理块中那一块开始算,也就是之前的物理块保留用于其他目的

       int ec_hdr_alsize;// size of the EC header aligned to @hdrs_min_io_size

       int vid_hdr_alsize; //size of the VID header aligned to @hdrs_min_io_size

       int vid_hdr_offset;//VID头部在一块之中的偏移量。一般是一个pagesize

       int vid_hdr_aloffset;// starting offset of the VID header aligned to @hdrs_min_io_size

       int vid_hdr_shift// contains @vid_hdr_offset - @vid_hdr_aloffset

       unsigned int bad_allowed:1;

       unsigned int nor_flash:1;// non-zero if working on top of NOR flash

       struct mtd_info *mtd;//指向MTD分区信息,我们知道,UBI层是构建在MTD层之上的。

       void *peb_buf1;//一个缓冲区,大小为一个block的大小

       void *peb_buf2; //一个缓冲区,大小为一个block的大小

       struct mutex buf_mutex;

       struct mutex ckvol_mutex;

#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID

       void *dbg_peb_buf;

       struct mutex dbg_buf_mutex;

#endif

};

2. struct ubi_vtbl_record

下一个重要的结构体struct ubi_vtbl_record,在认识这个结构体之前我们先看一副截图,

这幅截图是我们在attach一个设备时候的打印内容,红色的划线部分是我们要注意的内容:internal volume

什么是internal volume?它是与下面的user volume相区别的。

internal volume是内核使用来保持相应的信息的,那么它保持的是什么呢?它保持的是volume table。它是以struct ubi_vtbl_record数据结构的格式来保持的。

struct ubi_vtbl_record {

       __be32  reserved_pebs;// how many physical eraseblocks are reserved for this volume

       __be32  alignment;// volume alignment

       __be32  data_pad;// how many bytes are unused at the end of the each physical eraseblock to satisfy the requested alignment

       __u8    vol_type;//volume的类型,分为动态和静态两种,动态volume可以动态的改变它的大小

       __u8    upd_marker;

       __be16  name_len;//volume name length

       __u8    name[UBI_VOL_NAME_MAX+1]; //volume name

       __u8    flags;

       __u8    padding[23];

       __be32  crc;

} __attribute__ ((packed));

3. struct ubi_volume

struct ubi_volume是对UBI设备上每一个volume的抽象。

struct ubi_volume {

       struct device dev;

       struct cdev cdev;

       struct ubi_device *ubi;//volume在哪一个UBI设备上

       int vol_id;//volume标号

       int ref_count;//引用次数(不知道什么用途)

       int readers;// number of users holding this volume in read-only mode

       int writers;// number of users holding this volume in read-write mode

       int exclusive;// whether somebody holds this volume in exclusive mode

       int reserved_pebs;//volume中保留的peb

       int vol_type;//volume类型

       int usable_leb_size;// logical eraseblock size without padding

       int used_ebs//可用PEB数目

       int last_eb_bytes;// how many bytes are stored in the last logical eraseblock

       long long used_bytes;//已用空间大小

       int alignment;

       int data_pad;

       int name_len;//volume名字的长度

       char name[UBI_VOL_NAME_MAX + 1];

       int upd_ebs;

       int ch_lnum; LEB number which is being changing by the atomic LEB change operation(这样在后面修改LEB数据的操作中可以看到)

       int ch_dtype;

       long long upd_bytes;

       long long upd_received;

       void *upd_buf;

       int *eba_tbl;// EBA table of this volume,极其重要,LEBPEB得影射关系需要查该表来获得

       unsigned int checked:1;

       unsigned int corrupted:1;

       unsigned int upd_marker:1;

       unsigned int updating:1;

       unsigned int changing_leb:1;

       unsigned int direct_writes:1;

};

4. struct ubi_scan_info

这个结构体是在attach的过程中使用的。在attach的过程中,UBIFS需要获知该设备上每一个PEB的状态,然后为重新挂载文件系统做准备。

struct ubi_scan_info {

       struct rb_root volumes;//volume的红黑树的根节点

       //下面是4个链表,是在扫描的过程将扫描的block进行分类,然后连接到下面4个链表中的其中一个。

       struct list_head corr;

       struct list_head free;

       struct list_head erase;

       struct list_head alien;

       int bad_peb_count;//坏块数

       int vols_found;//volume

       int highest_vol_id;//volume 的最高标号

       int alien_peb_count;

       int is_empty;//标志位,用于表示该UBI设备是否为空的,在上面所说的扫描过程被置位

       int min_ec;//最小erase counter

       int max_ec;//最大erase counter

       unsigned long long max_sqnum;//64位的sqnum

       int mean_ec;//平均erase counter

       uint64_t ec_sum;

       int ec_count;

       int corr_count;

};

5 struct ubi_scan_leb

在上面的struct ubi_scan_info中我们说到了在attach操作中的扫描过程,并且说到了struct ubi_scan_info中的4个队列,是将扫描的每一个block的信息抽象,然后挂载到这些队列中去,下面就简单的说一下对于block扫描信息的抽象。

struct ubi_scan_leb {

       int ec;//erase counter,用于均衡损耗目的,以后详细介绍

       //每一个卷的eba_table就是由下面两个成员构成的。

       int pnum;//物理块标号

       int lnum;//逻辑块标号

       int scrub;

       unsigned long long sqnum;

       union {

              struct rb_node rb;

              struct list_head list;

       } u;

};

6. struct ubi_ec_hdr

我们知道UBIFS是一个Wear-level的文件系统,即均衡损耗。我们就以struct ubi_ec_hdr这个开始重要结构体的介绍。

struct ubi_ec_hdr {

       __be32  magic;

       __u8    version;

       __u8    padding1[3];

       __be64  ec; /* Warning: the current limit is 31-bit anyway! */

       __be32  vid_hdr_offset;

       __be32  data_offset;

       __be32  image_seq;

       __u8    padding2[32];

       __be32  hdr_crc;

} __attribute__ ((packed));

我们注意其中的一个成员变量为_be64  ecec是什么,ec就是erase counter。我们知道NANDFLASH是的擦除是有次数限制的,当擦除的次数太多的时候,就会变成坏块。什么是均衡损耗,就是在文件系统的管理下,我们不能对其中的一块进行过多的擦除操作。

我们来看函数ensure_wear_leveling,它只要是来判断UBI设备是否需要进行均衡损耗的相关处理,这儿就有两个问题了。1.它判断的依据是什么。2.它会进行什么样的相关来避免对一个可擦出块进行过多的擦除操作。

那么我们先来回答第一个问题,在WL子系统中,所有的可擦出块都归WL子系统来进行管理。这是一个RB数,我们来其中的每一个结点。

struct ubi_wl_entry {

       union {

              struct rb_node rb;

              struct list_head list;

       } u;

       int ec;

       int pnum;

};

说白了,WL只关心一个东西,那么就是ec的数值。下面是wear_leveling_worker函数中的一段核心代码:

              e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, u.rb);

              e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);

if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD))

used中队里中取出一个LEB,显然EC是最小的(每擦除一次,EC值加一),再从free队列中取出一个EC值最大的LEB

如果两个LEBec差值大于了UBI_WL_THRESHOLD,那么就需要进行WL操作了。

那么多操作是什么呢?

err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr);

将内容从一个LEB搬到另外一个LEB中去。

6.struct ubi_vid_hdr

在上面EC头部中有一个成员变量是vid_hdr_offset,是指vid_hdrFLASH中的偏移量,接着分析第二重要的数据结构struct ubi_vid_hdr

struct ubi_vid_hdr {

       __be32  magic;

       __u8    version;

       __u8    vol_type;

       __u8    copy_flag;

       __u8    compat;

       __be32  vol_id;

       __be32  lnum;

       __u8    padding1[4];

       __be32  data_size;

       __be32  used_ebs;

       __be32  data_pad;

       __be32  data_crc;

       __u8    padding2[4];

       __be64  sqnum;

       __u8    padding3[12];

       __be32  hdr_crc;

} __attribute__ ((packed));

这其中最重要的成员变量是__be32  vol_id__be32  lnumvol_id是标示该LEB属于哪儿一个volumeLnum是指与该PNUM相对于的lnum。对于上层而言,我们操作的是逻辑块,也就是lnum,但是最终需要将数据写进pnum中去,在ubi_eba_write_leb函数中有这样的一句:

pnum = vol->eba_tbl[lnum];

每一个volume都有一个eba_tbl,是在扫描的时候建立的。如果该lnum没有影射,那么调用ubi_wl_get_peb来获得一个pnum,并相应的修改volumeeba_tbl;

7. struct ubi_scan_volume

struct ubi_scan_volume {

       int vol_id;//volume标号

       int highest_lnum;//volume中的最高逻辑块标号

       int leb_count;//leb数目

       int vol_type;//volume类型

       int used_ebs;//已用PEB

       int last_data_size;

       int data_pad;

       int compat;

       struct rb_node rb;//不清楚具体目的,猜想应该是一个节点的cache,缓存的是刚刚访问的结点

       struct rb_root root;

};

这儿主要注意一下上面的struct rb_root root变量,这个成员是一个红黑树的根,用来链接在扫描的过程中发现的属于该volumePEB

这个结构体是在扫描的过程中读VID头部建立起来的关于volume的临时信息。

 

 

ubifs-media.h中定义了很多的结构体,下面简单的解释一下。

在《A brief 。。。》中讲到了,UBIFS采用的node-structure。它的所有的数据都是以node的形式处理的。

struct ubifs_ch,其中ch的意思是指common header,是下面的所有结构体的共同部分。

struct ubifs_ino_node,是用来保存inode结点相关信息的。

struct ubifs_dent_node,用来来保存dent的相关信息。关于dentinode请参考VFS部分的相关内容。

struct ubifs_data_node,用来保存具体数据的结点。

struct ubifs_trun_node,用来在trunk的时候写入journal区中的,只存在于journal区中。

struct ubifs_pad_node,用来进行数据填充的。

struct ubifs_sb_node,超结块结点, 用来记载superblock的相关信息,只存在在superblock 区中。

struct ubifs_mst_node,记录master结点的数据结构,记载node树的根结点以及其他信息。只存在在master area中。

struct ubifs_ref_node ,用于在更新数据的时候写入journal区,在commit的时候更新index树和LPT树,只存在在journal区中。

struct ubifs_idx_nodeidx结点的头部,关于idx结点,请参考《a brief introduction to design of UBIFS》,只存在在main区中。

struct ubifs_branchidx结点中的分支。

struct ubifs_cs_nodecs = commit start,用于在journal中表示一次commit的开始,只存在在journal区中。一次commit由一个ubifs_cs_node 和若干ubifs_ref_node 组成。

struct ubifs_orph_node ,用于在orphan区中记录相关信息的结点,关于orphan请参考《a brief introduction to design of UBIFS》。


---------------以后再补充吧,如有不对,请多多指教

 

 

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