[U-Boot] [PATCH] [UBI] Basic Unsorted Block Image (UBI) support v3 (#1)
Magnus Lilja
lilja.magnus at gmail.com
Sun Nov 2 22:01:33 CET 2008
Dear Kyungmin Park,
Some more comments after testing your patches.
2008/10/28 Kyungmin Park <kmpark at infradead.org>:
> UBI (Latin: "where?") stands for "Unsorted Block Images". It is a volume management system for flash devices which manages multiple logical volumes on a single physical flash device and spreads the I/O load (i.e, wear-leveling) across the whole flash chip.
>
> In a sense, UBI may be compared to the Logical Volume Manager (LVM). Whereas LVM maps logical sectors to physical sectors, UBI maps logical eraseblocks to physical eraseblocks. But besides the mapping, UBI implements global wear-leveling and I/O errors handling.
>
> For more details, Please visit the following URL.
> http://www.linux-mtd.infradead.org/doc/ubi.html
>
> Signed-off-by: Kyungmin Park <kyungmin.park at samsung.com>
> ---
> diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
> new file mode 100644
> index 0000000..b6f8699
> --- /dev/null
> +++ b/drivers/mtd/ubi/build.c
> +static int io_init(struct ubi_device *ubi)
> +{
> + if (ubi->mtd->numeraseregions != 0) {
> + /*
> + * Some flashes have several erase regions. Different regions
> + * may have different eraseblock size and other
> + * characteristics. It looks like mostly multi-region flashes
> + * have one "main" region and one or more small regions to
> + * store boot loader code or boot parameters or whatever. I
> + * guess we should just pick the largest region. But this is
> + * not implemented.
> + */
> + ubi_err("multiple regions, not implemented");
> + return -EINVAL;
> + }
> +
> + if (ubi->vid_hdr_offset < 0)
> + return -EINVAL;
> +
> + /*
> + * Note, in this implementation we support MTD devices with 0x7FFFFFFF
> + * physical eraseblocks maximum.
> + */
> +
> + 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;
> +
> + /*
> + * Make sure minimal I/O unit is power of 2. Note, there is no
> + * fundamental reason for this assumption. It is just an optimization
> + * which allows us to avoid costly division operations.
> + */
> + 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;
> + }
> +
> + /* Similar for the data offset */
> + ubi->leb_start = ubi->vid_hdr_offset + UBI_EC_HDR_SIZE;
> + ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size);
> +
> + dbg_msg("vid_hdr_offset %d", ubi->vid_hdr_offset);
> + dbg_msg("vid_hdr_aloffset %d", ubi->vid_hdr_aloffset);
> + dbg_msg("vid_hdr_shift %d", ubi->vid_hdr_shift);
> + dbg_msg("leb_start %d", ubi->leb_start);
> +
> + /* The shift must be aligned to 32-bit boundary */
> + if (ubi->vid_hdr_shift % 4) {
> + ubi_err("unaligned VID header shift %d",
> + ubi->vid_hdr_shift);
> + return -EINVAL;
> + }
> +
> + /* Check sanity */
> + if (ubi->vid_hdr_offset < UBI_EC_HDR_SIZE ||
> + ubi->leb_start < ubi->vid_hdr_offset + UBI_VID_HDR_SIZE ||
> + ubi->leb_start > ubi->peb_size - UBI_VID_HDR_SIZE ||
> + ubi->leb_start & (ubi->min_io_size - 1)) {
> + ubi_err("bad VID header (%d) or data offsets (%d)",
> + ubi->vid_hdr_offset, ubi->leb_start);
> + return -EINVAL;
> + }
> +
> + /*
> + * It may happen that EC and VID headers are situated in one minimal
> + * I/O unit. In this case we can only accept this UBI image in
> + * read-only mode.
> + */
> + if (ubi->vid_hdr_offset + UBI_VID_HDR_SIZE <= ubi->hdrs_min_io_size) {
> + ubi_warn("EC and VID headers are in the same minimal I/O unit, "
> + "switch to read-only mode");
> + ubi->ro_mode = 1;
> + }
> +
> + ubi->leb_size = ubi->peb_size - ubi->leb_start;
> +
> + if (!(ubi->mtd->flags & MTD_WRITEABLE)) {
> + ubi_msg("MTD device %d is write-protected, attach in "
> + "read-only mode", ubi->mtd->index);
> + ubi->ro_mode = 1;
> + }
> +
> + ubi_msg("physical eraseblock size: %d bytes (%d KiB)",
> + ubi->peb_size, ubi->peb_size >> 10);
> + ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size);
> + ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size);
> + if (ubi->hdrs_min_io_size != ubi->min_io_size)
> + ubi_msg("sub-page size: %d",
> + ubi->hdrs_min_io_size);
> + ubi_msg("VID header offset: %d (aligned %d)",
> + ubi->vid_hdr_offset, ubi->vid_hdr_aloffset);
> + ubi_msg("data offset: %d", ubi->leb_start);
I got these messages on my console, my suggestion is that (almost) all
of the standard info messages are off by default.
> +int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
> +{
> + struct ubi_device *ubi;
> + int i, err;
> +
> + /*
> + * 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;
> + }
> + }
> +
> + /*
> + * Make sure this MTD device is not emulated on top of an UBI volume
> + * already. Well, generally this recursion works fine, but there are
> + * different problems like the UBI module takes a reference to itself
> + * by attaching (and thus, opening) the emulated MTD device. This
> + * results in inability to unload the module. And in general it makes
> + * no sense to attach emulated MTD devices, so we prohibit this.
> + */
> + if (mtd->type == MTD_UBIVOLUME) {
> + ubi_err("refuse attaching mtd%d - it is already emulated on "
> + "top of UBI", mtd->index);
> + return -EINVAL;
> + }
> +
> + if (ubi_num == UBI_DEV_NUM_AUTO) {
> + /* Search for an empty slot in the @ubi_devices array */
> + for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++)
> + if (!ubi_devices[ubi_num])
> + break;
> + if (ubi_num == UBI_MAX_DEVICES) {
> + dbg_err("only %d UBI devices may be created", UBI_MAX_DEVICES);
> + return -ENFILE;
> + }
> + } else {
> + if (ubi_num >= UBI_MAX_DEVICES)
> + return -EINVAL;
> +
> + /* 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->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;
> +
> + 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;
> +
> +#ifdef CONFIG_MTD_UBI_DEBUG
> + mutex_init(&ubi->dbg_buf_mutex);
> + ubi->dbg_peb_buf = vmalloc(ubi->peb_size);
> + if (!ubi->dbg_peb_buf)
> + goto out_free;
> +#endif
> +
> + err = attach_by_scanning(ubi);
> + if (err) {
> + dbg_err("failed to attach by scanning, error %d", err);
> + goto out_free;
> + }
> +
> + if (ubi->autoresize_vol_id != -1) {
> + err = autoresize(ubi, ubi->autoresize_vol_id);
> + if (err)
> + goto out_detach;
> + }
> +
> + err = uif_init(ubi);
> + if (err)
> + goto out_detach;
> +
> + ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name);
> + if (IS_ERR(ubi->bgt_thread)) {
> + err = PTR_ERR(ubi->bgt_thread);
> + ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name,
> + err);
> + goto out_uif;
> + }
> +
> + ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi_num);
> + ubi_msg("MTD device name: \"%s\"", mtd->name);
> + ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20);
> + ubi_msg("number of good PEBs: %d", ubi->good_peb_count);
> + ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count);
> + ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots);
> + ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD);
> + ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT);
> + ubi_msg("number of user volumes: %d",
> + ubi->vol_count - UBI_INT_VOL_COUNT);
> + ubi_msg("available PEBs: %d", ubi->avail_pebs);
> + ubi_msg("total number of reserved PEBs: %d", ubi->rsvd_pebs);
> + ubi_msg("number of PEBs reserved for bad PEB handling: %d",
> + ubi->beb_rsvd_pebs);
> + ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec);
All these should also be off by default IMO.
Regards, Magnus
More information about the U-Boot
mailing list