ubi文件系统分析
http://download.csdn.net/detail/songqqnew/4919666
drivers/mtd/ubi/build.c
mtd_devs是ubi卷和mtd分区绑定的数目。mtd_devs初始值=0,每执行一次ubi_mtd_param_parse,mtd_devs+1
ubi_mtd_param_parse在内核初期初始化执行parse_args时调用到(而build.c里的module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000)指定在解析到mtd=时,调用ubi_mtd_param_parse去处理)。
/**
* ubi_mtd_param_parse - parse the 'mtd=' UBI parameter.
* @val: the parameter value to parse
* @kp: not used
*
* This function returns zero in case of success and a negative error code in
* case of error.
*/
static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp)
{
//比如 Linux-CommandLine = root=ubi0:FriendlyARM-root ubi.mtd=2 rootfstype=ubifs init=/linuxrc console=ttySAC0,115200
// 则val = 2
int i, len;
struct mtd_dev_param *p;
char buf[MTD_PARAM_LEN_MAX];
char *pbuf = &buf[0];
char *tokens[2] = {NULL, NULL};
if (!val)
return -EINVAL;
if (mtd_devs == UBI_MAX_DEVICES) {
printk(KERN_ERR "UBI error: too many parameters, max. is %d\n",
UBI_MAX_DEVICES);
return -EINVAL;
}
len = strnlen(val, MTD_PARAM_LEN_MAX);
if (len == MTD_PARAM_LEN_MAX) {
printk(KERN_ERR "UBI error: parameter \"%s\" is too long, "
"max. is %d\n", val, MTD_PARAM_LEN_MAX);
return -EINVAL;
}
if (len == 0) {
printk(KERN_WARNING "UBI warning: empty 'mtd=' parameter - "
"ignored\n");
return 0;
}
strcpy(buf, val);
/* Get rid of the final newline */
if (buf[len - 1] == '\n')
buf[len - 1] = '\0';
for (i = 0; i < 2; i++)
tokens[i] = strsep(&pbuf, ",");
if (pbuf) {
printk(KERN_ERR "UBI error: too many arguments at \"%s\"\n",
val);
return -EINVAL;
}
p = &mtd_dev_param[mtd_devs];
strcpy(&p->name[0], tokens[0]);
if (tokens[1])
p->vid_hdr_offs = bytes_str_to_int(tokens[1]);
if (p->vid_hdr_offs < 0)
return p->vid_hdr_offs;
mtd_devs += 1;
return 0;
}
build.c中模块入口函数是ubi_init,此函数中使用ubi_attach_mtd_dev将ubi volume和mtd分区绑定,绑定mtd_devs次
在ubi_attach_mtd_dev函数中,使用io_init和attach_by_scanning函数对ubi结构体的成员初始化或赋值
其中io_init主要是初始化leb,peb的大小数量,最小读写字节数等全局一些的成员
而attach_by_scanning读取整个mtd分区的所有peb(物理擦除块)的ec和vid,对块进行检查和统计
attach_by_scanning中调用了ubi_scan函数对整个mtd分区扫描
drivers/mtd/ubi/build.c
/**
* attach_by_scanning - attach an MTD device using scanning method.
* @ubi: UBI device descriptor
*
* This function returns zero in case of success and a negative error code in
* case of failure.
*
* Note, currently this is the only method to attach UBI devices. Hopefully in
* the future we'll have more scalable attaching methods and avoid full media
* scanning. But even in this case scanning will be needed as a fall-back
* attaching method if there are some on-flash table corruptions.
*/
static int attach_by_scanning(struct ubi_device *ubi)
{
int err;
struct ubi_scan_info *si;
si = ubi_scan(ubi);
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->corr_peb_count = si->corr_peb_count;
ubi->max_ec = si->max_ec;
ubi->mean_ec = si->mean_ec;
ubi_msg("max. sequence number: %llu", si->max_sqnum);
err = ubi_read_volume_table(ubi, si);
if (err)
goto out_si;
err = ubi_wl_init_scan(ubi, si);
if (err)
goto out_vtbl;
err = ubi_eba_init_scan(ubi, si);
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---------->process_eb(处理每个物理擦除块)-------->ubi_io_read_ec_hdr(读取ec头)--------->ubi_io_read-->ubi->mtd->read------------而read函数就是每个分区对应的mtd_info结构体的read成员函数,即mtdpart.c中的part_read(drivers/mtd/mtdpart.c)--------->part->master->read,这个read函数调用nand_do_read_ops(drivers/mtd/nand/nand_base.c),但此时还不是真正的从nand上读取数据,如下代码所示,此函数又会根据对其方式等,而调用其下3者之1
chip->ecc.read_page_raw-------->nand_read_page_raw------->chip->read_buf (在nand_base.c)
chip->ecc.read_subpage-------->nand_read_subpage--------->chip->read_buf (在nand_base.c)
chip->ecc.read_page--------->s3c_nand_read_page_1bit(如果是1GB SLC是调用的这个函数,在s3c_nand.c)
而
if (!chip->read_buf)//如果在s3c_nand.c中未定义,则使用nand_base.c中的下面的2个函数之1
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
/**
* nand_read_buf - [DEFAULT] read chip data into buffer
* @mtd: MTD device structure
* @buf: buffer to store date
* @len: number of bytes to read
*
* Default read function for 8bit buswith
*/
static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
int i;
struct nand_chip *chip = mtd->priv;
for (i = 0; i < len; i++)
buf[i] = readb(chip->IO_ADDR_R);
}
static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
int chipnr, page, realpage, col, bytes, aligned;
struct nand_chip *chip = mtd->priv;
struct mtd_ecc_stats stats;
int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
int sndcmd = 1;
int ret = 0;
uint32_t readlen = ops->len;
uint32_t oobreadlen = ops->ooblen;
uint32_t max_oobsize = ops->mode == MTD_OOB_AUTO ?
mtd->oobavail : mtd->oobsize;
uint8_t *bufpoi, *oob, *buf;
stats = mtd->ecc_stats;
chipnr = (int)(from >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
realpage = (int)(from >> chip->page_shift);
page = realpage & chip->pagemask;
col = (int)(from & (mtd->writesize - 1));
buf = ops->datbuf;
oob = ops->oobbuf;
while (1) {
bytes = min(mtd->writesize - col, readlen);
aligned = (bytes == mtd->writesize);
/* Is the current page in the buffer ? */
if (realpage != chip->pagebuf || oob) {
bufpoi = aligned ? buf : chip->buffers->databuf;
if (likely(sndcmd)) {
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
sndcmd = 0;
}
/* Now read the page into the buffer ******************************************************************/
if (unlikely(ops->mode == MTD_OOB_RAW))
ret = chip->ecc.read_page_raw(mtd, chip,
bufpoi, page);
else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
ret = chip->ecc.read_subpage(mtd, chip,
col, bytes, bufpoi);
else
ret = chip->ecc.read_page(mtd, chip, bufpoi,
page);
if (ret < 0)
break;
/* Transfer not aligned data ****************************************************************************/
if (!aligned) {
if (!NAND_SUBPAGE_READ(chip) && !oob &&
!(mtd->ecc_stats.failed - stats.failed))
chip->pagebuf = realpage;
memcpy(buf, chip->buffers->databuf + col, bytes);
}
buf += bytes;
if (unlikely(oob)) {
int toread = min(oobreadlen, max_oobsize);
if (toread) {
oob = nand_transfer_oob(chip,
oob, ops, toread);
oobreadlen -= toread;
}
}
if (!(chip->options & NAND_NO_READRDY)) {
/*
* Apply delay or wait for ready/busy pin. Do
* this before the AUTOINCR check, so no
* problems arise if a chip which does auto
* increment is marked as NOAUTOINCR by the
* board driver.
*/
if (!chip->dev_ready)
udelay(chip->chip_delay);
else
nand_wait_ready(mtd);
}
} else {
memcpy(buf, chip->buffers->databuf + col, bytes);
buf += bytes;
}
readlen -= bytes;
if (!readlen)
break;
/* For subsequent reads align to page boundary. */
col = 0;
/* Increment page address */
realpage++;
page = realpage & chip->pagemask;
/* Check, if we cross a chip boundary */
if (!page) {
chipnr++;
chip->select_chip(mtd, -1);
chip->select_chip(mtd, chipnr);
}
/* Check, if the chip supports auto page increment
* or if we have hit a block boundary.
*/
if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
sndcmd = 1;
}
ops->retlen = ops->len - (size_t) readlen;
if (oob)
ops->oobretlen = ops->ooblen - oobreadlen;
if (ret)
return ret;
if (mtd->ecc_stats.failed - stats.failed)
return -EBADMSG;
return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
}
ubi_scan-->process_eb(处理每个物理擦除块)-->ubi_io_read_vid_hdr(读取vid头)-->
/**
* process_eb - read, check UBI headers, and add them to scanning information.
* @ubi: UBI device description object
* @si: scanning information
* @pnum: the physical eraseblock number
*
* This function returns a zero if the physical eraseblock was successfully
* handled and a negative error code in case of failure.
*/
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_err = 0;
dbg_bld("scan PEB %d", pnum);
/* Skip bad physical eraseblocks */
err = ubi_io_is_bad(ubi, pnum);
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);
if (err < 0)
return err;
switch (err) {
case 0:
break;
case UBI_IO_BITFLIPS:
bitflips = 1;
break;
case UBI_IO_FF:
si->empty_peb_count += 1;
return add_to_list(si, pnum, UBI_SCAN_UNKNOWN_EC, 0,
&si->erase);
case UBI_IO_FF_BITFLIPS:
si->empty_peb_count += 1;
return add_to_list(si, pnum, UBI_SCAN_UNKNOWN_EC, 1,
&si->erase);
case UBI_IO_BAD_HDR_EBADMSG:
case UBI_IO_BAD_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_err = err;
ec = UBI_SCAN_UNKNOWN_EC;
bitflips = 1;
break;
default:
ubi_err("'ubi_io_read_ec_hdr()' returned unknown code %d", err);
return -EINVAL;
}
if (!ec_err) {
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_MAX_ERASECOUNTER);
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);
return -EINVAL;
}
}
/* OK, we've done with the EC header, let's look at the VID header */
err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0);
if (err < 0)
return err;
switch (err) {
case 0:
break;
case UBI_IO_BITFLIPS:
bitflips = 1;
break;
case UBI_IO_BAD_HDR_EBADMSG:
if (ec_err == UBI_IO_BAD_HDR_EBADMSG)
/*
* Both EC and VID headers are corrupted and were read
* with data integrity error, probably this is a bad
* PEB, bit it is not marked as bad yet. This may also
* be a result of power cut during erasure.
*/
si->maybe_bad_peb_count += 1;
case UBI_IO_BAD_HDR:
if (ec_err)
/*
* Both headers are corrupted. There is a possibility
* that this a valid UBI PEB which has corresponding
* LEB, but the headers are corrupted. However, it is
* impossible to distinguish it from a PEB which just
* contains garbage because of a power cut during erase
* operation. So we just schedule this PEB for erasure.
*
* Besides, in case of NOR flash, we deliberatly
* corrupt both headers because NOR flash erasure is
* slow and can start from the end.
*/
err = 0;
else
/*
* The EC was OK, but the VID header is corrupted. We
* have to check what is in the data area.
*/
err = check_corruption(ubi, vidh, pnum);
if (err < 0)
return err;
else if (!err)
/* This corruption is caused by a power cut */
err = add_to_list(si, pnum, ec, 1, &si->erase);
else
/* This is an unexpected corruption */
err = add_corrupted(si, pnum, ec);
if (err)
return err;
goto adjust_mean_ec;
case UBI_IO_FF_BITFLIPS:
err = add_to_list(si, pnum, ec, 1, &si->erase);
if (err)
return err;
goto adjust_mean_ec;
case UBI_IO_FF:
if (ec_err)
err = add_to_list(si, pnum, ec, 1, &si->erase);
else
err = add_to_list(si, pnum, ec, 0, &si->free);
if (err)
return err;
goto adjust_mean_ec;
default:
ubi_err("'ubi_io_read_vid_hdr()' returned unknown code %d",
err);
return -EINVAL;
}
vol_id = be32_to_cpu(vidh->vol_id);
if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOLUME_ID) {
int lnum = be32_to_cpu(vidh->lnum);
/* Unsupported internal volume */
switch (vidh->compat) {
case UBI_COMPAT_DELETE:
ubi_msg("\"delete\" compatible internal volume %d:%d"
" found, will remove it", vol_id, lnum);
err = add_to_list(si, pnum, ec, 1, &si->erase);
if (err)
return err;
return 0;
case UBI_COMPAT_RO:
ubi_msg("read-only compatible internal volume %d:%d"
" found, switch to read-only mode",
vol_id, lnum);
ubi->ro_mode = 1;
break;
case UBI_COMPAT_PRESERVE:
ubi_msg("\"preserve\" compatible internal volume %d:%d"
" found", vol_id, lnum);
err = add_to_list(si, pnum, ec, 0, &si->alien);
if (err)
return err;
return 0;
case UBI_COMPAT_REJECT:
ubi_err("incompatible internal volume %d:%d found",
vol_id, lnum);
return -EINVAL;
}
}
if (ec_err)
ubi_warn("valid VID header but corrupted EC header at PEB %d",
pnum);
err = ubi_scan_add_used(ubi, si, pnum, ec, vidh, bitflips);
if (err)
return err;
adjust_mean_ec:
if (!ec_err) {
si->ec_sum += ec;
si->ec_count += 1;
if (ec > si->max_ec)
si->max_ec = ec;
if (ec < si->min_ec)
si->min_ec = ec;
}
return 0;
}