linux ubi代码分析,UBIFS文件系统分析(一):挂载UBIFS的代码分析

本文详细解析了Linux UBI文件系统挂载过程中涉及的关键步骤和代码,包括ubiattach命令的执行、ubiAttach_mtd_dev函数的逻辑、MTD设备检查、错误处理以及后续的初始化操作。通过对ubi_attach_mtd_dev和io_init等函数的分析,揭示了UBI如何附加到MTD设备、如何处理错误条件以及如何初始化关键数据结构。
摘要由CSDN通过智能技术生成

我们按照挂载ubifs的工序来分析代码:

)ubiattach /dev/ubi_ctrl -m 3

(2)ubimkvol /dev/ubi0 -N ubifs -s

15MiB

(3)mount -t ubifs ubi0:ubifs /mnt

首先先分析(1),相应的代码是ubi_attach_mtd_dev()函数,下面我们紧跟代码来看看究竟干了些什么。

.ubi_attach_mtd_dev

int ubi_attach_mtd_dev(struct mtd_info

*mtd, int ubi_num, int vid_hdr_offset)

{

//ubi_num, vid_hdr_offset是命令传进来的参数

struct

ubi_device *ubi;

int

i, err, do_free = 1;

/*

* Check if we already have the same MTD device

attached.

*

* Note, this function assumes that UBI devices

creations and deletions

* are serialized, so it does not take the

&ubi_devices_lock.

*/

for

(i = 0; i < UBI_MAX_DEVICES; i++) {

ubi

= ubi_devices[i];

if

(ubi && mtd->index == ubi->mtd->index) {

dbg_err("mtd%d

is already attached to ubi%d",

mtd->index,

i);

return

-EEXIST;

}

}

//上面的这段代码可以看英文注释,一个mtd设备(一个分区)不能被attach两次,除非你已经deatch了。所以在这段代码的开始就检查被attach的mtd设备是否已经被attach了。

if

(mtd->type == MTD_UBIVOLUME) {

ubi_err("refuse

attaching mtd%d - it is already emulated on "

"top

of UBI", mtd->index);

return

-EINVAL;

}

上面的代码接着检查被attach的mtd设备时候是一个mtd

volume(卷区),如果已经是一个mtd卷了,那么就不能再被attach了。

if (ubi_num == UBI_DEV_NUM_AUTO) {

for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES;

ubi_num++)

if (!ubi_devices[ubi_num])

break;

如果在终端输入命令的时候没有带ubinum,那么就是自动分配ubinum,系统就会从ubi_device[]数组中找出一个没被使用的ubinum号

if (ubi_num == UBI_MAX_DEVICES) {

dbg_err("only %d UBI devices may be

created",

return -ENFILE;

}

} else {

if (ubi_num >= UBI_MAX_DEVICES)

return -EINVAL;

如果ubi_num > UBI_MAX_DEVICES,就代表没有空余ubinum号可供分配,返回出错

/* Make sure ubi_num is not busy */

if (ubi_devices[ubi_num]) {

dbg_err("ubi%d already exists",

ubi_num);

return -EEXIST;

}

}

ubi = kzalloc(sizeof(struct ubi_device), GFP_KERNEL);

if (!ubi)

return -ENOMEM;

ubi->mtd = mtd;

ubi->ubi_num = ubi_num;

ubi->vid_hdr_offset = vid_hdr_offset;

ubi->autoresize_vol_id = -1;

mutex_init(&ubi->buf_mutex);

mutex_init(&ubi->ckvol_mutex);

mutex_init(&ubi->mult_mutex);

mutex_init(&ubi->volumes_mutex);

spin_lock_init(&ubi->volumes_lock);

初始化信号

ubi_msg("attaching mtd%d to ubi%d", mtd->index,

ubi_num);

err =io_init(ubi);

if (err)

goto out_free;

下面跟着io_init()往下分析:

static int

io_init(struct ubi_device *ubi)

{

if (ubi->mtd->numeraseregions != 0) {

ubi_err("multiple regions, not implemented");

return -EINVAL;

}

Numeraseregions是扫描nandflash得到的信息,如果numeraseregions等于0,代表我们需要attach的设备已经擦除过了

if (ubi->vid_hdr_offset < 0)

return -EINVAL;

ubi->vid_hdr_offset显然应该是一个正数,一般是nandflash的一页,我们的4020上的nandflash页大小为512字节,所以ubi->vid_hdr_offset为512.这儿再稍微说一下,EC header和VID header,是记录我们ubi管理信息。一般EC在一个擦除块的第一页,所以偏移量为0,VID在擦除块的第二页上,所以偏移量为512.,在我们4020的nandflash上,一个擦除块的大小为16K,也就是32页。

下面接着讲我们的扫描信息写进mtd结构体

ubi->peb_size=

ubi->mtd->erasesize;

ubi->peb_count=

ubi->mtd->size / ubi->mtd->erasesize;

是指逻辑块的数目,也就是总的大小除以每一页的大小

ubi->flash_size = ubi->mtd->size;

if (ubi->mtd->block_isbad &&

ubi->mtd->block_markbad)

ubi->bad_allowed = 1;

ubi->min_io_size = ubi->mtd->writesize;

ubi->hdrs_min_io_size = ubi->mtd->writesize >>

ubi->mtd->subpage_sft;

if (!is_power_of_2(ubi->min_io_size)) {

ubi_err("min. I/O unit (%d) is not power of

2",

ubi->min_io_size);

return -EINVAL;

}

ubi_assert(ubi->hdrs_min_io_size > 0);

ubi_assert(ubi->hdrs_min_io_size <=

ubi->min_io_size);

ubi_assert(ubi->min_io_size % ubi->hdrs_min_io_size ==

0);

/* Calculate default

aligned sizes of EC and VID headers */

ubi->ec_hdr_alsize = ALIGN(UBI_EC_HDR_SIZE,

ubi->hdrs_min_io_size);

ubi->vid_hdr_alsize = ALIGN(UBI_VID_HDR_SIZE,

ubi->hdrs_min_io_size);

dbg_msg("min_io_size %d", ubi->min_io_size);

dbg_msg("hdrs_min_io_size %d",

ubi->hdrs_min_io_size);

dbg_msg("ec_hdr_alsize%d", ubi->ec_hdr_alsize);

dbg_msg("vid_hdr_alsize%d", ubi->vid_hdr_alsize);

if (ubi->vid_hdr_offset == 0)

/* Default offset */

ubi->vid_hdr_offset = ubi->vid_hdr_aloffset =

ubi->ec_hdr_alsize;

else {

ubi->vid_hdr_aloffset = ubi->vid_hdr_offset &

~(ubi->hdrs_min_io_size

- 1);

ubi->vid_hdr_shift = ubi->vid_hdr_offset -

ubi->vid_hdr_aloffset;

}

剩余的部分就不分析了,比较容易

接着上面ubi_attach_mtd_dev()往下说:

ubi->peb_buf1 = vmalloc(ubi->peb_size);

if (!ubi->peb_buf1)

goto out_free;

ubi->peb_buf2 = vmalloc(ubi->peb_size);

if (!ubi->peb_buf2)

goto out_free;

分配两个物理擦除块大小的buf,具体的用途下面再说

err = attach_by_scanning(ubi);

if (err) {

dbg_err("failed to attach by scanning, error

%d", err);

goto out_free;

}

我们再跟着attach_by_scanning(ubi)细说

static int

attach_by_scanning(struct ubi_device *ubi)

{

int err;

struct ubi_scan_info *si;

si = ubi_scan(ubi);

**********************************************************************************

这儿通过ubi_scan函数来扫描MTD分区的每一块。具体是调用static int

process_eb(struct ubi_device *ubi, struct ubi_scan_info *si,int pnum)函数来读取EC和VID头(即没一块的前两页),在读每一页的时候,会调用check_pattern函数来判断这一页是否为空,如果每一页都是空的,那么就会发现这个MTD分区是空的。

**********************************************************************************

if (IS_ERR(si))

return PTR_ERR(si);

ubi->bad_peb_count = si->bad_peb_count;

ubi->good_peb_count = ubi->peb_count -

ubi->bad_peb_count;

ubi->max_ec = si->max_ec;

ubi->mean_ec = si->mean_ec;

err = ubi_read_volume_table(ubi, si);

if (err)

goto out_si;

err = ubi_wl_init_scan(ubi, si);

**********************************************************************************

取之ubi_wl_init_scan(ubi, si);函数片段

list_for_each_entry_safe(seb,

tmp, &si->erase, u.list) {

cond_resched();

e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL);

if (!e)

goto out_free;

e->pnum = seb->pnum;

e->ec = seb->ec;

ubi->lookuptbl[e->pnum] = e;

if (schedule_erase(ubi, e, 0)) {

kmem_cache_free(ubi_wl_entry_slab, e);

goto out_free;

}

}

在初始化wl的时候会将为每一个空页建立一个struct ubi_work *wl_wrk;结构体(该结构体的具体处理函数为erase_worker,擦除一块,并写入EC头),并添加到ubi->works队列中(list_add_tail(&wrk->list,

&));这儿我们渐渐的认识到ubi->works这个队列的作用,后台进程ubi_thread就是循环的处理该队列中的工作的。

在第一次attach的时候,在这儿ubi_thread进程还没有被唤醒,所以这些工作要等到进程被唤醒的时候才能被处理

**********************************************************************************

if (err)

goto out_vtbl;

err =

ubi_eba_init_scan(ubi, si);

**********************************************************************************

前面我们看到了ubi_scan,其实这个这个过程是建立ubifs的基础,因为所有关于ubi和ubifs的基本信息都是在scan的过程中建立在内存中的,现在调用ubi_eba_init_scan来建立起EBA子系统就是利用前面的扫描信息,建立起没一个volumn的vtl。

if (err)

goto out_wl;

ubi_scan_destroy_si(si);

return 0;

out_wl:

ubi_wl_close(ubi);

out_vtbl:

free_internal_volumes(ubi);

vfree(ubi->vtbl);

out_si:

ubi_scan_destroy_si(si);

return err;

}

.Ubi_scan

struct ubi_scan_info

*ubi_scan(struct ubi_device *ubi)

{

int err, pnum;

struct rb_node *rb1, *rb2;

struct ubi_scan_volume *sv;

struct ubi_scan_leb *seb;

struct ubi_scan_info *si;

si = kzalloc(sizeof(struct ubi_scan_info), GFP_KERNEL);

if (!si)

return ERR_PTR(-ENOMEM);

初始化si的corrupt队列

INIT_LIST_HEAD(&si->free);// //初始化si的corrupt队列

初始化si的corrupt队列

INIT_LIST_HEAD(&si->alien); //初始化si的corrupt队列

si->volumes = RB_ROOT;

只是空的,哈哈

si->is_empty = 1;

err = -ENOMEM;

ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);//为ec头部分配空间,用于暂存后面读出的每一个peb的ec头部信息

if (!ech)

goto out_si;

vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);//为vid头部分配空间,用于暂存后面读出的每一个peb的vid头部信息,注意扫描的目的就是收集EC和VID中信息,在内存中建立相关的信息

if (!vidh)

goto out_ech;

for (pnum = 0; pnum < ubi->peb_count; pnum++) {

cond_resched();

dbg_gen("process PEB %d", pnum);

err = process_eb(ubi, si, pnum);//具体的扫描每一个物理块

if (err < 0)

goto out_vidh;

}

dbg_msg("scanning is finished");

/* Calculate mean erase counter */

if (si->ec_count)//算平均擦除次数

si->mean_ec = div_u64(si->ec_sum,

si->ec_count);

if (si->is_empty)//判断这是否是一个空的MTD,如果是空的话,那么后面的mount的时候调用create_default_filesystem在建立初始的ubifs数据

ubi_msg("empty MTD device detected");

/*

* Few corrupted PEBs

are not a problem and may be just a result of

* unclean reboots.

However, many of them may indicate some problems

* with the flash HW or

driver. Print a warning in this case.

*/

if (si->corr_count >= 8 || si->corr_count >=

ubi->peb_count / 4) {

ubi_warn("%d PEBs are corrupted",

si->corr_count);

printk(KERN_WARNING "corrupted PEBs are:");

list_for_each_entry(seb, &si->corr, u.list)

printk(KERN_CONT " %d", seb->pnum);

printk(KERN_CONT "\n");

}

/*

* In case of unknown

erase counter we use the mean erase counter

* value.

*/

ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) {

ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb)

if (seb->ec == UBI_SCAN_UNKNOWN_EC)

seb->ec = si->mean_ec;

}

list_for_each_entry(seb, &si->free, u.list) {

if (seb->ec == UBI_SCAN_UNKNOWN_EC)

seb->ec = si->mean_ec;

}

list_for_each_entry(seb, &si->corr, u.list)

if (seb->ec == UBI_SCAN_UNKNOWN_EC)

seb->ec = si->mean_ec;

list_for_each_entry(seb, &si->erase, u.list)

if (seb->ec == UBI_SCAN_UNKNOWN_EC)

seb->ec = si->mean_ec;

err = paranoid_check_si(ubi, si);

if (err) {

if (err > 0)

err = -EINVAL;

goto out_vidh;

}

ubi_free_vid_hdr(ubi, vidh);

kfree(ech);

return si;

out_vidh:

ubi_free_vid_hdr(ubi, vidh);

out_ech:

kfree(ech);

out_si:

ubi_scan_destroy_si(si);

return ERR_PTR(err);

}

.process_eb

static int

process_eb(struct ubi_device *ubi, struct ubi_scan_info *si,int pnum)

{

long long uninitialized_var(ec);

int err, bitflips = 0, vol_id, ec_corr = 0;

dbg_bld("scan PEB %d", pnum);

/* Skip bad physical eraseblocks */

err = ubi_io_is_bad(ubi, pnum);

判断一个块是否为坏块,直接调用mtd层的mtd->block_isbad

if (err < 0)

return err;

else if (err) {

/*

* FIXME: this is

actually duty of the I/O sub-system to

* initialize

this, but MTD does not provide enough

* information.

*/

si->bad_peb_count += 1;

return 0;

}

err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);//读ec header,一般为一块的第一页

if (err < 0)

return err;

else if (err == UBI_IO_BITFLIPS)

bitflips = 1;

else if (err == UBI_IO_PEB_EMPTY)

return add_to_list(si, pnum, UBI_SCAN_UNKNOWN_EC,

&si->erase);

//注意这儿,为什么这个块是empty(也就是全是0xff),还要丢到si->erase队列中呢?这是因为MTD所谓的空与UBI所谓的空不是一回事。在UBI中,空块是指只包含EC头部的块。所以这些需要将全0xff的块进行擦除,写入EC头部

else if (err == UBI_IO_BAD_EC_HDR) {

/*

* We have to

also look at the VID header, possibly it is not

* corrupted. Set

%bitflips flag in order to make this PEB be

* moved and EC

be re-created.

*/

ec_corr = 1;

ec = UBI_SCAN_UNKNOWN_EC;

bitflips = 1;

}

si->is_empty = 0;

if (!ec_corr) {

int image_seq;

/* Make sure UBI version is OK */

if (ech->version != UBI_VERSION) {

ubi_err("this UBI version is %d, image

version is %d",

UBI_VERSION, (int)ech->version);

return -EINVAL;

}

ec = be64_to_cpu(ech->ec);

if (ec > UBI_MAX_ERASECOUNTER) {

/*

* Erase

counter overflow. The EC headers have 64 bits

*

reserved, but we anyway make use of only 31 bit

* values,

as this seems to be enough for any existing

* flash.

Upgrade UBI and use 64-bit erase counters

*

internally.

*/

ubi_err("erase counter overflow, max is

%d",

ubi_dbg_dump_ec_hdr(ech);

return -EINVAL;

}

/*

* Make sure that

all PEBs have the same image sequence number.

* This allows us

to detect situations when users flash UBI

* images

incorrectly, so that the flash has the new UBI image

* and leftovers

from the old one. This feature was added

* relatively

recently, and the sequence number was always

* zero, because

old UBI implementations always set it to zero.

* For this

reasons, we do not panic if some PEBs have zero

* sequence

number, while other PEBs have non-zero sequence

* number.

*/

image_seq = be32_to_cpu(ech->image_seq);

if (!ubi->image_seq && image_seq)

ubi->image_seq = image_seq;

if (ubi->image_seq && image_seq &&

ubi->image_seq

!= image_seq) {

ubi_err("bad image sequence number %d in

PEB %d, "

"expected %d", image_seq, pnum,

ubi->image_seq);

ubi_dbg_dump_ec_hdr(ech);

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值