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去处理)。
/**
 * 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;
}



posted on 2012-12-21 11:27  _song  阅读(861)  评论(0编辑  收藏  举报