Commit 4ccf8cff authored by Artem Bityutskiy's avatar Artem Bityutskiy

UBI: add auto-resize feature

The problem: NAND flashes have different amount of initial bad physical
eraseblocks (marked as bad by the manufacturer). For example, for 256MiB
Samsung OneNAND flash there might be from 0 to 40 bad initial eraseblocks,
which is about 2%. When UBI is used as the base system, one needs to know
the exact amount of good physical eraseblocks, because this number is
needed to create the UBI image which is put to the devices during
production. But this number is not know, which forces us to use the
minimum number of good physical eraseblocks. And UBI additionally
reserves some percentage of physical eraseblocks for bad block handling
(default is 1%), so we have 1-3% of PEBs reserved at the end, depending
on the amount of initial bad PEBs. But it is desired to always have
1% (or more, depending on the configuration).

Solution: this patch adds an "auto-resize" flag to the volume table.
The volume which has the "auto-resize" flag will automatically be re-sized
(enlarged) on the first UBI initialization. UBI clears the flag when
the volume is re-sized. Only one volume may have the "auto-resize" flag.

So, the production UBI image may have one volume with "auto-resize"
flag set, and its size is automatically adjusted on the first boot
of the device.
Signed-off-by: default avatarArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
parent 896c0c06
...@@ -366,9 +366,6 @@ static int uif_init(struct ubi_device *ubi) ...@@ -366,9 +366,6 @@ static int uif_init(struct ubi_device *ubi)
int i, err; int i, err;
dev_t dev; dev_t dev;
mutex_init(&ubi->volumes_mutex);
spin_lock_init(&ubi->volumes_lock);
sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num);
/* /*
...@@ -623,6 +620,58 @@ static int io_init(struct ubi_device *ubi) ...@@ -623,6 +620,58 @@ static int io_init(struct ubi_device *ubi)
return 0; return 0;
} }
/**
* autoresize - re-size the volume which has the "auto-resize" flag set.
* @ubi: UBI device description object
* @vol_id: ID of the volume to re-size
*
* This function re-sizes the volume marked by the @UBI_VTBL_AUTORESIZE_FLG in
* the volume table to the largest possible size. See comments in ubi-header.h
* for more description of the flag. Returns zero in case of success and a
* negative error code in case of failure.
*/
static int autoresize(struct ubi_device *ubi, int vol_id)
{
struct ubi_volume_desc desc;
struct ubi_volume *vol = ubi->volumes[vol_id];
int err, old_reserved_pebs = vol->reserved_pebs;
/*
* Clear the auto-resize flag in the volume in-memory copy of the
* volume table, and 'ubi_resize_volume()' will propogate this change
* to the flash.
*/
ubi->vtbl[vol_id].flags &= ~UBI_VTBL_AUTORESIZE_FLG;
if (ubi->avail_pebs == 0) {
struct ubi_vtbl_record vtbl_rec;
/*
* No avalilable PEBs to re-size the volume, clear the flag on
* flash and exit.
*/
memcpy(&vtbl_rec, &ubi->vtbl[vol_id],
sizeof(struct ubi_vtbl_record));
err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
if (err)
ubi_err("cannot clean auto-resize flag for volume %d",
vol_id);
} else {
desc.vol = vol;
err = ubi_resize_volume(&desc,
old_reserved_pebs + ubi->avail_pebs);
if (err)
ubi_err("cannot auto-resize volume %d", vol_id);
}
if (err)
return err;
ubi_msg("volume %d (\"%s\") re-sized from %d to %d LEBs", vol_id,
vol->name, old_reserved_pebs, vol->reserved_pebs);
return 0;
}
/** /**
* ubi_attach_mtd_dev - attach an MTD device. * ubi_attach_mtd_dev - attach an MTD device.
* @mtd_dev: MTD device description object * @mtd_dev: MTD device description object
...@@ -699,6 +748,12 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) ...@@ -699,6 +748,12 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
ubi->mtd = mtd; ubi->mtd = mtd;
ubi->ubi_num = ubi_num; ubi->ubi_num = ubi_num;
ubi->vid_hdr_offset = vid_hdr_offset; 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);
dbg_msg("attaching mtd%d to ubi%d: VID header offset %d", dbg_msg("attaching mtd%d to ubi%d: VID header offset %d",
mtd->index, ubi_num, vid_hdr_offset); mtd->index, ubi_num, vid_hdr_offset);
...@@ -707,8 +762,6 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) ...@@ -707,8 +762,6 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
if (err) if (err)
goto out_free; goto out_free;
mutex_init(&ubi->buf_mutex);
mutex_init(&ubi->ckvol_mutex);
ubi->peb_buf1 = vmalloc(ubi->peb_size); ubi->peb_buf1 = vmalloc(ubi->peb_size);
if (!ubi->peb_buf1) if (!ubi->peb_buf1)
goto out_free; goto out_free;
...@@ -730,6 +783,12 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) ...@@ -730,6 +783,12 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
goto out_free; 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); err = uif_init(ubi);
if (err) if (err)
goto out_detach; goto out_detach;
......
...@@ -341,9 +341,6 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, ...@@ -341,9 +341,6 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
{ {
int err, pnum, vol_id = vol->vol_id; int err, pnum, vol_id = vol->vol_id;
ubi_assert(ubi->ref_count > 0);
ubi_assert(vol->ref_count > 0);
if (ubi->ro_mode) if (ubi->ro_mode)
return -EROFS; return -EROFS;
...@@ -392,9 +389,6 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, ...@@ -392,9 +389,6 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
struct ubi_vid_hdr *vid_hdr; struct ubi_vid_hdr *vid_hdr;
uint32_t uninitialized_var(crc); uint32_t uninitialized_var(crc);
ubi_assert(ubi->ref_count > 0);
ubi_assert(vol->ref_count > 0);
err = leb_read_lock(ubi, vol_id, lnum); err = leb_read_lock(ubi, vol_id, lnum);
if (err) if (err)
return err; return err;
...@@ -618,9 +612,6 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, ...@@ -618,9 +612,6 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
int err, pnum, tries = 0, vol_id = vol->vol_id; int err, pnum, tries = 0, vol_id = vol->vol_id;
struct ubi_vid_hdr *vid_hdr; struct ubi_vid_hdr *vid_hdr;
ubi_assert(ubi->ref_count > 0);
ubi_assert(vol->ref_count > 0);
if (ubi->ro_mode) if (ubi->ro_mode)
return -EROFS; return -EROFS;
...@@ -754,9 +745,6 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, ...@@ -754,9 +745,6 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
struct ubi_vid_hdr *vid_hdr; struct ubi_vid_hdr *vid_hdr;
uint32_t crc; uint32_t crc;
ubi_assert(ubi->ref_count > 0);
ubi_assert(vol->ref_count > 0);
if (ubi->ro_mode) if (ubi->ro_mode)
return -EROFS; return -EROFS;
...@@ -871,9 +859,6 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, ...@@ -871,9 +859,6 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
struct ubi_vid_hdr *vid_hdr; struct ubi_vid_hdr *vid_hdr;
uint32_t crc; uint32_t crc;
ubi_assert(ubi->ref_count > 0);
ubi_assert(vol->ref_count > 0);
if (ubi->ro_mode) if (ubi->ro_mode)
return -EROFS; return -EROFS;
......
...@@ -250,9 +250,11 @@ struct ubi_wl_entry; ...@@ -250,9 +250,11 @@ struct ubi_wl_entry;
* @rsvd_pebs: count of reserved physical eraseblocks * @rsvd_pebs: count of reserved physical eraseblocks
* @avail_pebs: count of available physical eraseblocks * @avail_pebs: count of available physical eraseblocks
* @beb_rsvd_pebs: how many physical eraseblocks are reserved for bad PEB * @beb_rsvd_pebs: how many physical eraseblocks are reserved for bad PEB
* handling * handling
* @beb_rsvd_level: normal level of PEBs reserved for bad PEB handling * @beb_rsvd_level: normal level of PEBs reserved for bad PEB handling
* *
* @autoresize_vol_id: ID of the volume which has to be auto-resized at the end
* of UBI ititializetion
* @vtbl_slots: how many slots are available in the volume table * @vtbl_slots: how many slots are available in the volume table
* @vtbl_size: size of the volume table in bytes * @vtbl_size: size of the volume table in bytes
* @vtbl: in-RAM volume table copy * @vtbl: in-RAM volume table copy
...@@ -333,12 +335,14 @@ struct ubi_device { ...@@ -333,12 +335,14 @@ struct ubi_device {
int beb_rsvd_pebs; int beb_rsvd_pebs;
int beb_rsvd_level; int beb_rsvd_level;
int autoresize_vol_id;
int vtbl_slots; int vtbl_slots;
int vtbl_size; int vtbl_size;
struct ubi_vtbl_record *vtbl; struct ubi_vtbl_record *vtbl;
struct mutex volumes_mutex; struct mutex volumes_mutex;
int max_ec; int max_ec;
/* TODO: mean_ec is not updated run-time, fix */
int mean_ec; int mean_ec;
/* EBA unit's stuff */ /* EBA unit's stuff */
......
...@@ -497,8 +497,6 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) ...@@ -497,8 +497,6 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
dbg_msg("re-size volume %d to from %d to %d PEBs", dbg_msg("re-size volume %d to from %d to %d PEBs",
vol_id, vol->reserved_pebs, reserved_pebs); vol_id, vol->reserved_pebs, reserved_pebs);
ubi_assert(desc->mode == UBI_EXCLUSIVE);
ubi_assert(vol == ubi->volumes[vol_id]);
if (vol->vol_type == UBI_STATIC_VOLUME && if (vol->vol_type == UBI_STATIC_VOLUME &&
reserved_pebs < vol->used_ebs) { reserved_pebs < vol->used_ebs) {
......
...@@ -514,6 +514,17 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, ...@@ -514,6 +514,17 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
vol->name[vol->name_len] = '\0'; vol->name[vol->name_len] = '\0';
vol->vol_id = i; vol->vol_id = i;
if (vtbl[i].flags & UBI_VTBL_AUTORESIZE_FLG) {
/* Auto re-size flag may be set only for one volume */
if (ubi->autoresize_vol_id != -1) {
ubi_err("more then one auto-resize volume (%d "
"and %d)", ubi->autoresize_vol_id, i);
return -EINVAL;
}
ubi->autoresize_vol_id = i;
}
ubi_assert(!ubi->volumes[i]); ubi_assert(!ubi->volumes[i]);
ubi->volumes[i] = vol; ubi->volumes[i] = vol;
ubi->vol_count += 1; ubi->vol_count += 1;
......
...@@ -1303,7 +1303,6 @@ int ubi_wl_flush(struct ubi_device *ubi) ...@@ -1303,7 +1303,6 @@ int ubi_wl_flush(struct ubi_device *ubi)
* Make sure all the works which have been done in parallel are * Make sure all the works which have been done in parallel are
* finished. * finished.
*/ */
ubi_assert(ubi->ref_count > 0);
down_write(&ubi->work_sem); down_write(&ubi->work_sem);
up_write(&ubi->work_sem); up_write(&ubi->work_sem);
......
...@@ -57,6 +57,43 @@ enum { ...@@ -57,6 +57,43 @@ enum {
UBI_VID_STATIC = 2 UBI_VID_STATIC = 2
}; };
/*
* Volume flags used in the volume table record.
*
* @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume
*
* %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume
* table. UBI automatically re-sizes the volume which has this flag and makes
* the volume to be of largest possible size. This means that if after the
* initialization UBI finds out that there are available physical eraseblocks
* present on the device, it automatically appends all of them to the volume
* (the physical eraseblocks reserved for bad eraseblocks handling and other
* reserved physical eraseblocks are not taken). So, if there is a volume with
* the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical
* eraseblocks will be zero after UBI is loaded, because all of them will be
* reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared
* after the volume had been initialized.
*
* The auto-resize feature is useful for device production purposes. For
* example, different NAND flash chips may have different amount of initial bad
* eraseblocks, depending of particular chip instance. Manufacturers of NAND
* chips usually guarantee that the amount of initial bad eraseblocks does not
* exceed certain percent, e.g. 2%. When one creates an UBI image which will be
* flashed to the end devices in production, he does not know the exact amount
* of good physical eraseblocks the NAND chip on the device will have, but this
* number is required to calculate the volume sized and put them to the volume
* table of the UBI image. In this case, one of the volumes (e.g., the one
* which will store the root file system) is marked as "auto-resizable", and
* UBI will adjust its size on the first boot if needed.
*
* Note, first UBI reserves some amount of physical eraseblocks for bad
* eraseblock handling, and then re-sizes the volume, not vice-versa. This
* means that the pool of reserved physical eraseblocks will always be present.
*/
enum {
UBI_VTBL_AUTORESIZE_FLG = 0x01,
};
/* /*
* Compatibility constants used by internal volumes. * Compatibility constants used by internal volumes.
* *
...@@ -289,7 +326,8 @@ struct ubi_vid_hdr { ...@@ -289,7 +326,8 @@ struct ubi_vid_hdr {
* @upd_marker: if volume update was started but not finished * @upd_marker: if volume update was started but not finished
* @name_len: volume name length * @name_len: volume name length
* @name: the volume name * @name: the volume name
* @padding2: reserved, zeroes * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG)
* @padding: reserved, zeroes
* @crc: a CRC32 checksum of the record * @crc: a CRC32 checksum of the record
* *
* The volume table records are stored in the volume table, which is stored in * The volume table records are stored in the volume table, which is stored in
...@@ -324,7 +362,8 @@ struct ubi_vtbl_record { ...@@ -324,7 +362,8 @@ struct ubi_vtbl_record {
__u8 upd_marker; __u8 upd_marker;
__be16 name_len; __be16 name_len;
__u8 name[UBI_VOL_NAME_MAX+1]; __u8 name[UBI_VOL_NAME_MAX+1];
__u8 padding2[24]; __u8 flags;
__u8 padding[23];
__be32 crc; __be32 crc;
} __attribute__ ((packed)); } __attribute__ ((packed));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment