nand ubi - 5 kernel和ubi
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去处理)。
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
而
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);
}
ubi_scan-->process_eb(处理每个物理擦除块)-->ubi_io_read_vid_hdr(读取vid头)-->
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; }