一万年太久,只争朝夕
分类: Android平台
2015-02-10 17:09:38
按照nand方案,ubi卷创建过程分析如下:
1、载入分区配置表
用下载工具下载编译镜像时,完成下载功能代码之后,首先会载入分区配置表,作为一个打包的packet发送到手机端。按照packet的封装方式:
unsigned short size = pakcet->packet_body.size;
unsigned short *data = (unsigned short *)
(pakcet->packet_body.content);
获取包内容:data
获取分区数:total_partition_num =
size/(MAX_PARTITION_NAME_SIZE + PARTITION_SIZE_LENGTH);
可以看出封装包内容由一个纯粹的列表构成:分区名+分区大小
所以总的分区数就可以确定下来。
2、重分区
接下来调用重分区函数:
repartition(void* vol_cfg, unsigned short total_vol_num);
其中,
vol_cfg = data;
total_vol_num = total_partition_num;
根据data和分区数total_partition_num,检测原有的卷配置,首先分配了total_partition_num个卷:
fdl_ubi_vtbl_t *vtbl=NULL;
vtbl = malloc(total_vol_num * sizeof(fdl_ubi_vtbl_t));
2.1、解析卷配置
调用_parse_volume_cfg(vol_cfg, total_vol_num, vtbl);
此函数实际上是根据vol_cfg来填充vtbl,卷表主要包含三个元素:
typedef struct{
char name[UBI_VOL_NAME_MAX+1];
long long size;//size in byte
int autoresize;
}fdl_ubi_vtbl_t;
因此,此函数就是根据分区表来填充相应的位域;
size = *(unsigned long *)(vol_cfg+38*(i+1)-2);
vtbl[i].name[j] = *(vol_cfg+38*i+j) & 0xFF;//j=1~36,最大卷名长度
autoresize 由AUTO_RESIZE_FLAG确定。
2.2、卷表检测
_vtbl_check(vtbl, total_vol_num, &remove, &create);
卷表检测需要决定出:
全部重分区:卷表记录与NAND存储卷表完全不一致
局部重分区:卷表记录与NAND存储卷表部分一致
不进行重分区:卷表记录与NAND存储卷表完全一致
struct ubi_device {
struct cdev cdev;
struct device dev;
int ubi_num;
char ubi_name[sizeof(UBI_NAME_STR)+5];
int vol_count;
struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT];
……
int rsvd_pebs;
int avail_pebs;
……
struct ubi_vtbl_record *vtbl;
struct mutex volumes_mutex;
int max_ec;
/* TODO: mean_ec is not updated run-time, fix */
int mean_ec;
/* EBA unit's stuff */
unsigned long long global_sqnum;
spinlock_t ltree_lock;
struct rb_root ltree;
struct mutex alc_mutex;
/* Wear-leveling unit's stuff */
struct rb_root used;
struct rb_root free;
struct rb_root scrub;
……
struct ubi_wl_entry **lookuptbl;
unsigned long long abs_ec;
struct ubi_wl_entry *move_from;
struct ubi_wl_entry *move_to;
……
/* I/O unit's stuff */
long long flash_size;
int peb_count;
int peb_size;
int bad_peb_count;
int good_peb_count;
int min_io_size;
int hdrs_min_io_size;
int ro_mode;
int leb_size;
int leb_start;
……
struct mtd_info *mtd;
……
};
struct ubi_volume {
struct device dev;
struct cdev cdev;
struct ubi_device *ubi;
int vol_id;
……
int reserved_pebs;
int vol_type;
……
char name[UBI_VOL_NAME_MAX+1];
……
int *eba_tbl;
……
};
初始化两条双向链表remove和create:
INIT_LIST_HEAD(&remove);
INIT_LIST_HEAD(&create);
获取ubi设备:struct ubi_device *ubi = cur_ubi.dev;
定义卷,struct ubi_volume *vol;
vol= ubi->volumes[j];//j=1~ubi->vtbl_slots
获取ubi设备上的每一个ubi卷,strcmp(vol->name, vtbl[i].name),比较ubi设备上与下载工具下载的卷配置获取的卷表记录,卷名进行比较:
若卷名相同,再比较if(new_rsvd_pebs == vol->reserved_pebs),若新的分区表配置的保留块数目与旧的配置相同,那么就认为当前处理的分区相同。如果不相同,将当前分区记录(vtbl[i].name,0,0)添加到remove链表;若不相同,也将当前分区记录(vtbl[i].name,vtbl[i].size,vtbl[i].autoresize)添加到create链表。
2.3、全部重分区
根据上述check的结果,进入到三个不同的分支。
2.3.1 _mtd_part_erase(UBIPAC_PART)
对于全部重分区的情况,首先会调用_mtd_part_erase(UBIPAC_PART),将建ubi卷的UBIPAC_PART分区全部擦除。
2.3.2 _ubi_dev_init
然后调用_ubi_dev_init函数初始化,此函数执行了mtdparts_init和ubi_init函数,然后执行_ubi_dev_attach(UBIPAC_PART),将ubi设备attach到给定的mtd分区UBIPAC_PART,并且返回新的ubi设备号。
typedef struct disk_partition {
ulong start; /* # of first block in partition */
ulong size; /* number of blocks in partition */
ulong blksz; /* block size in bytes */
uchar name[72]; /* partition name */
uchar type[32]; /* string type description */
} disk_partition_t;
_ubi_dev_attach(const char *mtdpart)解析:
1)、此函数首先调用find_dev_and_part (const char *id, struct mtd_device **dev,u8 *part_num, struct part_info **part),根据分区名“ubipac”(即此处的参数id),调用mtd_id_parse函数解析得到mtd设备类型(nand/nor/onenand)和设备号
2)根据设备类型和设备号在设备链表(需确认是所有的设备构成的设备链表,还是仅mtd设备)上遍历,仅当设备类型和设备号都一致时,返回所找到的mtd设备:*dev = device_find(type, dnum)。
3)*part = mtd_part_info(*dev, pnum),从设备dev上按照给定的分区号pnum,寻找对应这个分区号的分区,并返回一个指向此分区的指针。此分区part是disk_partition类型的分区。
4)sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(dev->id->type), dev->id->num);
mtd = get_mtd_device_nm(mtd_dev);
通过设备名mtd_dev(由设备类型和设备号,nand+x),获取一个mtd设备的handle
5)填充mtd分区信息mtd_partition:struct mtd_partition mtd_part;
mtd_part.name = mtdpart;// mtdpart=“ubipac”
mtd_part.size = part->size;
mtd_part.offset = part->offset;//part即为步骤3)获取的磁盘类型分区
add_mtd_partitions(mtd, &mtd_part, 1);//此函数在mtd原始设备mtd上创建1个mtd分区mtd_part.
6)调用ubi_attach_mtd_dev函数,将上述mtd设备attach到ubi_num为UBI_DEV_NUM_AUTO的ubi设备。
总结一下,这个函数主要是将mtd和ubi进行初始化,然后将分区名为“ubipac”的mtd分区attach到ubi卷。
Tips:通常mtd分区包含spl、uboot、ubipac这几个分区,ubipac分区在这一步挂接到ubi设备上,然后在ubi设备上创建ubi卷,也即ubi分区,包括boot、system、userdata、cache等分区。
2.3.3 _ubi_create_vol
创建卷,对卷表每一个卷轮询:
调用函数:
fdl_ubi_create_vol(cur_ubi.dev, vtbl[i].name, &vol_id, vtbl[i].size, 1);
通过卷名、卷id、卷大小,在当前ubi设备cur_ubi.dev上一一创建动态卷,最后一个参数1表示所创建的卷是动态卷。
1)创建一个卷请求req:struct ubi_mkvol_req req;并填充卷请求结构:
req.vol_type = UBI_DYNAMIC_VOLUME;//动态卷
req.vol_id = UBI_VOL_NUM_AUTO;//自动分配卷id
req.alignment = 1;
req.bytes = size;//卷大小
strcpy(req.name, volume);//卷名
req.name_len = strlen(volume);//卷名长度
req.name[req.name_len] = '\0';
req.padding1 = 0;
2)调用ubi_create_volume(ubi, &req)函数,创建一个描述为req的卷。依据ubi->slots(最大卷数目),进行轮询,找到未占用的卷id号vol_id,分配给新创建的卷。然后将该id填充到卷创建请求:req->vol_id = vol_id;
3)进行一系列校验:根据卷id确认ubi设备中的卷索引指针ubi->volumes[vol_id]为空,证明当前卷不是已存在的卷;
确认当前的卷名唯一,将req->name逐一轮询ubi->volume[i]对比卷名,确认新请求创建的卷卷名唯一。
4)计算需要的擦除块数目
vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment;
bytes = req->bytes;
if (do_div(bytes, vol->usable_leb_size))
vol->reserved_pebs = 1;
vol->reserved_pebs += bytes;
5)保留物理擦除块:
ubi->avail_pebs -= vol->reserved_pebs;//ubi可用的擦除块要减去新卷占用的擦除块数目
ubi->rsvd_pebs += vol->reserved_pebs;//ubi保留的擦除块数目加上新增卷占用的保留块数目。
6)将卷请求req的信息填充到新建卷vol
vol->vol_id = vol_id;
vol->alignment = req->alignment;
vol->data_pad = ubi->leb_size % vol->alignment;
vol->vol_type = req->vol_type;
vol->name_len = req->name_len;
memcpy(vol->name, req->name, vol->name_len + 1);
vol->ubi = ubi;//记录当前卷所属ubi设备
7)完成所有待定擦除,源于可能有一些LEB块属于相同的卷ID,此工作调用ubi_wl_flush(ubi)函数实现。
8)初始化eba表:
vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), GFP_KERNEL);
for (i = 0; i < vol->reserved_pebs; i++)
vol->eba_tbl[i] = UBI_LEB_UNMAPPED;//一一映射,初始化为未映射
9)将卷注册为字符设备:
cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
// ubi_vol_cdev_operations为file_operations操作函数集
vol->cdev.owner = THIS_MODULE;
dev = MKDEV(MAJOR(ubi->cdev.dev), vol_id + 1);
err = cdev_add(&vol->cdev, dev, 1);//添加字符设备
ubi_create_gluebi(ubi, vol);
vol->dev.release = vol_release;
vol->dev.parent = &ubi->dev;
vol->dev.devt = dev;
vol->dev.class = ubi_class;
sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id);
err = device_register(&vol->dev);
volume_sysfs_init(ubi, vol);
10)填充卷表记录struct ubi_vtbl_record vtbl_rec;
vtbl_rec.reserved_pebs = cpu_to_be32(vol->reserved_pebs);
vtbl_rec.alignment = cpu_to_be32(vol->alignment);
vtbl_rec.data_pad = cpu_to_be32(vol->data_pad);
vtbl_rec.name_len = cpu_to_be16(vol->name_len);
vtbl_rec.vol_type = UBI_VID_DYNAMIC;
memcpy(vtbl_rec.name, vol->name, vol->name_len + 1);
ubi_change_vtbl_record(ubi, vol_id,
&vtbl_rec);
计算卷记录的CRC,存储于vtbl_rec->CRC;
擦除旧的布局卷(layout volume),两个块
将卷表记录写入布局卷。
struct ubi_vtbl_record {
__be32 reserved_pebs;
__be32 alignment;
__be32 data_pad;
__u8 vol_type;
__u8 upd_marker;
__be16 name_len;
__u8 name[UBI_VOL_NAME_MAX+1];
__u8 flags;
__u8 padding[23];
__be32 crc;
} __attribute__ ((packed));
需要进行autoresize的卷调用fdl_ubi_volume_autoresize(cur_ubi.dev, auto_resize_id)函数进行resize。
至此,当前卷创建完毕,其他的卷也按照这个流程进行创建。