GIT 9ed8d7a6437c569ecf71df10d5e6355848b84d9c git://git.infradead.org/ubi-2.6.git commit Author: Artem Bityutskiy Date: Thu Dec 7 11:49:20 2006 +0200 [MTD] UBI: remove code duplication Remove offsets checking from scanning as we anyway always do this in the sanity check functions. Also improve error message printing. Signed-off-by: Artem Bityutskiy commit e0efa0921b6442d8086bc63b19c65d96487fb0b2 Author: Artem Bityutskiy Date: Wed Dec 6 15:09:31 2006 +0200 [MTD] UBI: fix debugging output This patches makes hexdump to go to the debugging buffer instead of being printed to console. Just a minor fix in debugging stuff. Signed-off-by: Artem Bityutskiy commit 8ed89f834deacb06b8128f773b2a9b8b28cf6e3e Author: Andrew Morton Date: Thu Nov 30 17:06:24 2006 -0800 [MTD] UBI: bugfix in vol_cdev_direct_write() Signed-off-by: Andrew Morton Signed-off-by: Artem Bityutskiy commit 2cc5e1a685f4c8130e774535ee008ff10d299630 Author: Artem Bityutskiy Date: Wed Oct 4 19:15:21 2006 +0300 [JFFS2] add UBI support This patch make JFFS2 able to work with UBI volumes via the emulated MTD devices which are directly mapped to these volumes. Signed-off-by: Artem Bityutskiy commit ed4f89dae7c85fecdbd99f0235e90ec1be5d7c40 Author: Artem B. Bityutskiy Date: Wed Aug 30 15:20:20 2006 +0400 [PATCH] export kallsyms_lookup This patch exports 'kallsyms_lookup()' and makes it accessible for modules too. It is needed for UBI debugging module. The module maintans its internal debugging log buffer which is later exposed via debugfs. This is extremely handy. To print the calling finction to the debugging log buffer, UBI needs 'kallsyms_lookup()' to be available (print_symbol() is not appropriate here). Signed-off-by: Artem B. Bityutskiy commit cc040b8128e80c62c4a66213e16c2c7b9b0fbd73 Author: Artem B. Bityutskiy Date: Tue Jun 27 12:22:22 2006 +0400 [MTD] UBI: Unsorted Block Images UBI (Latin: "where?") manages multiple logical volumes on a single flash device, specifically supporting NAND flash devices. UBI provides a flexible partitioning concept which still allows for wear-levelling across the whole flash device. In a sense, UBI may be compared to the Logical Volume Manager (LVM). Whereas LVM maps logical sector numbers to physical HDD sector numbers, UBI maps logical eraseblocks to physical eraseblocks. More information may be found in the UBI design documentation: ubidesign.pdf. Which can be found here: http://www.linux-mtd.infradead.org/doc/ubi.html Partitioning/Re-partitioning An UBI volume occupies a certain number of erase blocks. This is limited by a configured maximum volume size, which could also be viewed as the partition size. Each individual UBI volume's size can be changed independently of the other UBI volumes, provided that the sum of all volume sizes doesn't exceed a certain limit. UBI supports dynamic volumes and static volumes. Static volumes are read-only and their contents are protected by CRC check sums. Bad eraseblocks handling UBI transparently handles bad eraseblocks. When a physical eraseblock becomes bad, it is substituted by a good physical eraseblock, and the user does not even notice this. Scrubbing On a NAND flash bit flips can occur on any write operation, sometimes also on read. If bit flips persist on the device, at first they can still be corrected by ECC, but once they accumulate, correction will become impossible. Thus it is best to actively scrub the affected eraseblock, by first copying it to a free eraseblock and then erasing the original. The UBI layer performs this type of scrubbing under the covers, transparently to the UBI volume users. Erase Counts UBI maintains an erase count header per eraseblock. This frees higher-level layers (like file systems) from doing this and allows for centralized erase count management instead. The erase counts are used by the wear-levelling algorithm in the UBI layer. The algorithm itself is exchangeable. Booting from NAND For booting directly from NAND flash the hardware must at least be capable of fetching and executing a small portion of the NAND flash. Some NAND flash controllers have this kind of support. They usually limit the window to a few kilobytes in erase block 0. This "initial program loader" (IPL) must then contain sufficient logic to load and execute the next boot phase. Due to bad eraseblocks, which may be randomly scattered over the flash device, it is problematic to store the "secondary program loader" (SPL) statically. Also, due to bit-flips it may become corrupted over time. UBI allows to solve this problem gracefully by storing the SPL in a small static UBI volume. UBI volumes vs. static partitions UBI volumes are still very similar to static MTD partitions: * both consist of eraseblocks (logical eraseblocks in case of UBI volumes, and physical eraseblocks in case of static partitions; * both support three basic operations - read, write, erase. But UBI volumes have the following advantages over traditional static MTD partitions: * there are no eraseblock wear-leveling constraints in case of UBI volumes, so the user should not care about this; * there are no bit-flips and bad eraseblocks in case of UBI volumes. So, UBI volumes may be considered as flash devices with relaxed restrictions. Where can it be found? Documentation, kernel code and applications can be found in the MTD gits. What are the applications for? The applications help to create binary flash images for two purposes: pfi files (partial flash images) for in-system update of UBI volumes, and plain binary images, with or without OOB data in case of NAND, for a manufacturing step. Furthermore some tools are/and will be created that allow flash content analysis after a system has crashed. Who did UBI? The original ideas, where UBI is based on, were developed by Andreas Arnez, Frank Haverkamp and Thomas Gleixner. Josh W. Boyer and some others were involved too. The implementation of the kernel layer was done by Artem B. Bityutskiy. The user-space applications and tools were written by Oliver Lohmann with contributions from Frank Haverkamp, Andreas Arnez, and Artem. Joern Engel contributed a patch which modifies JFFS2 so that it can be run on a UBI volume. Thomas Gleixner did modifications to the NAND layer and also some to JFFS2 to make it work. Signed-off-by: Artem B. Bityutskiy Signed-off-by: Frank Haverkamp drivers/mtd/Kconfig | 2 drivers/mtd/Makefile | 2 drivers/mtd/ubi/Kconfig | 47 + drivers/mtd/ubi/Kconfig.debug | 245 ++++++ drivers/mtd/ubi/Makefile | 7 drivers/mtd/ubi/account.c | 288 +++++++ drivers/mtd/ubi/account.h | 119 +++ drivers/mtd/ubi/alloc.c | 609 +++++++++++++++ drivers/mtd/ubi/alloc.h | 236 ++++++ drivers/mtd/ubi/background.c | 328 ++++++++ drivers/mtd/ubi/background.h | 175 ++++ drivers/mtd/ubi/badeb.c | 236 ++++++ drivers/mtd/ubi/badeb.h | 109 +++ drivers/mtd/ubi/build.c | 185 ++++ drivers/mtd/ubi/build.h | 64 ++ drivers/mtd/ubi/cdev.c | 1212 +++++++++++++++++++++++++++++ drivers/mtd/ubi/cdev.h | 84 ++ drivers/mtd/ubi/debug.c | 1082 ++++++++++++++++++++++++++ drivers/mtd/ubi/debug.h | 297 +++++++ drivers/mtd/ubi/dtbl.c | 287 +++++++ drivers/mtd/ubi/dtbl.h | 139 +++ drivers/mtd/ubi/eba.c | 1210 +++++++++++++++++++++++++++++ drivers/mtd/ubi/eba.h | 357 +++++++++ drivers/mtd/ubi/gluebi.c | 351 ++++++++ drivers/mtd/ubi/gluebi.h | 88 ++ drivers/mtd/ubi/init.c | 371 +++++++++ drivers/mtd/ubi/io.c | 1261 ++++++++++++++++++++++++++++++ drivers/mtd/ubi/io.h | 400 ++++++++++ drivers/mtd/ubi/ivol.c | 123 +++ drivers/mtd/ubi/ivol.h | 132 +++ drivers/mtd/ubi/misc.h | 177 ++++ drivers/mtd/ubi/scan.c | 1371 +++++++++++++++++++++++++++++++++ drivers/mtd/ubi/scan.h | 279 +++++++ drivers/mtd/ubi/sysfs.c | 552 +++++++++++++ drivers/mtd/ubi/sysfs.h | 82 ++ drivers/mtd/ubi/ubi.h | 137 +++ drivers/mtd/ubi/uif.c | 927 ++++++++++++++++++++++ drivers/mtd/ubi/uif.h | 212 +++++ drivers/mtd/ubi/upd.c | 184 ++++ drivers/mtd/ubi/upd.h | 112 +++ drivers/mtd/ubi/volmgmt.c | 450 +++++++++++ drivers/mtd/ubi/volmgmt.h | 130 +++ drivers/mtd/ubi/vtbl.c | 1077 ++++++++++++++++++++++++++ drivers/mtd/ubi/vtbl.h | 166 ++++ drivers/mtd/ubi/wl.c | 1717 +++++++++++++++++++++++++++++++++++++++++ drivers/mtd/ubi/wl.h | 285 +++++++ fs/jffs2/fs.c | 12 fs/jffs2/os-linux.h | 6 fs/jffs2/wbuf.c | 24 + include/linux/mtd/ubi.h | 343 ++++++++ include/mtd/Kbuild | 2 include/mtd/mtd-abi.h | 1 include/mtd/ubi-header.h | 365 +++++++++ include/mtd/ubi-user.h | 161 ++++ kernel/kallsyms.c | 1 55 files changed, 18819 insertions(+), 0 deletions(-) diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 26f75c2..6d1b91b 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -292,5 +292,7 @@ source "drivers/mtd/nand/Kconfig" source "drivers/mtd/onenand/Kconfig" +source "drivers/mtd/ubi/Kconfig" + endmenu diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index c130e62..9205540 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -28,3 +28,5 @@ nftl-objs := nftlcore.o nftlmount.o inftl-objs := inftlcore.o inftlmount.o obj-y += chips/ maps/ devices/ nand/ onenand/ + +obj-$(CONFIG_MTD_UBI) += ubi/ diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig new file mode 100644 index 0000000..1d4297f --- /dev/null +++ b/drivers/mtd/ubi/Kconfig @@ -0,0 +1,47 @@ +# drivers/mtd/ubi/Kconfig + +menu "UBI - Unsorted block images" + depends on MTD + +config MTD_UBI + tristate "Enable UBI" + depends on MTD + select CRC32 + +config MTD_UBI_WL_THRESHOLD + int "UBI wear-leveling threshold" + default 4096 + range 2 65536 + depends on MTD_UBI + help + This parameter defines the maximal difference between the highest + eraseblock erase counter value and the lowest eraseblock erase + counter value of UBI devices. When this threshold is exceeded, UBI + starts doing wear leveling by means of moving data from eraseblock + with low erase counter to eraseblocks with high erase counter. + +config MTD_UBI_BEB_RESERVE + int "Percentage of reserved eraseblocks for bad eraseblocks handling" + default 1 + range 0 25 + depends on MTD_UBI + help + If the MTD device admits of bad eraseblocks, UBI reserves some amount + of physical eraseblocks to gracefully handle new bad eraseblocks. + This option specifies how many physical eraseblocks will be reserved + for bad eraseblock handling. + +config MTD_UBI_GLUEBI + bool "Emulate MTD devices" + default n + depends on MTD_UBI + help + This option enables MTD devices emulation on top of UBI volume - for + each UBI volumes an MTD device is created, and all I/O to this MTD + device is redirected to the UBI volume. This is handy to make + MTD-oriented software (like JFFS2) work on top of UBI. + +# There are a lot of debugging options, so they are moved to a distinct file +source "drivers/mtd/ubi/Kconfig.debug" + +endmenu diff --git a/drivers/mtd/ubi/Kconfig.debug b/drivers/mtd/ubi/Kconfig.debug new file mode 100644 index 0000000..69a8343 --- /dev/null +++ b/drivers/mtd/ubi/Kconfig.debug @@ -0,0 +1,245 @@ +# UBI debugging configuration options, part of drivers/mtd/ubi/Kconfig + +comment "UBI debugging options" + depends on MTD_UBI + +config MTD_UBI_DEBUG + bool "UBI debugging" + default n + depends on MTD_UBI + select DEBUG_FS + select KALLSYMS_ALL + help + This enables UBI debugging support. UBI exposes its debugging stuff + via the Linux debugfs virtual file-system under the "ubi" directory. + You should mount debugfs to access it: + mount -t debugfs none + +config MTD_UBI_DEBUG_BUF_SIZE + int "UBI messages buffer size (KiB)" + depends on MTD_UBI_DEBUG + default 1024 + help + Unless logging to console, UBI stores the debugging log in an + internal buffer and exposes it via the /ubi/log file. + This parameter defines the buffer size in Kilobytes. + +config MTD_UBI_DEBUG_CONSOLE_LOGGING + bool "Logging to console" + depends on MTD_UBI_DEBUG + help + By default UBI stores the debugging log at an internal buffer which + may be read via the "/ubi/log" file. This option directs the + debugging output to the console instead. + +config MTD_UBI_USERSPACE_IO + bool "UBI user-space write/erase" + depends on MTD_UBI_DEBUG + default n + help + By default, users cannot directly write and erase individual + eraseblocks of dynamic volumes (the update operation must be used + instead). This option enables this capability - this is often useful + for debugging. + +config MTD_UBI_DEBUG_EMULATE_BITFLIPS + bool "Emulate flash bit-flips" + depends on MTD_UBI_DEBUG + default n + help + This option emulates bit-flips with probability 1/50, which in turn + causes scrubbing. Useful for debugging. + +config MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES + bool "Emulate flash write failures" + depends on MTD_UBI_DEBUG + default n + help + This option emulates write failures with probability 1/100. Useful for + debugging. + +config MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES + bool "Emulate flash erase failures" + depends on MTD_UBI_DEBUG + default n + help + This option emulates erase failures with probability 1/100. Useful for + debugging. + +menu "UBI debugging messages" + depends on MTD_UBI_DEBUG + +config MTD_UBI_DEBUG_MSG_VB_ERR + bool "Verbose errors reporting" + depends on MTD_UBI_DEBUG + default y + help + This option enables verbose reporting about errors occurred in UBI. + Without this option UBI does not print any error message in many + cases. + +config MTD_UBI_DEBUG_MSG_UIF + bool "User interface unit messages" + depends on MTD_UBI_DEBUG + default n + help + This option enables debugging messages from the UBI user interfaces + unit. + +config MTD_UBI_DEBUG_MSG_CDEV + bool "Messages from the character device sub-unit" + depends on MTD_UBI_DEBUG + default n + help + This option enables debugging messages from the UBI character device + handling sub-unit of the user interfaces unit. + +config MTD_UBI_DEBUG_MSG_GLUEBI + bool "Messages from the gluebi sub-unit" + depends on MTD_UBI_DEBUG + depends on MTD_UBI_GLUEBI + default n + help + This option enables debugging messages from the gluebi (MTD devices + emulation) sub-unit of the user interfaces unit. + +config MTD_UBI_DEBUG_MSG_VMT + bool "Volume management unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI volume management + unit. + +config MTD_UBI_DEBUG_MSG_UPD + bool "Update unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI update unit. + +config MTD_UBI_DEBUG_MSG_VTBL + bool "Volume table unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI volume table + unit. + +config MTD_UBI_DEBUG_MSG_DTBL + bool "Data table unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI data table unit. + +config MTD_UBI_DEBUG_MSG_ACC + bool "Accounting unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI accountig unit. + +config MTD_UBI_DEBUG_MSG_EBA + bool "Eraseblock association unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI eraseblock + association unit. + +config MTD_UBI_DEBUG_MSG_WL + bool "Wear-leveling unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI wear-leveling + unit. + +config MTD_UBI_DEBUG_MSG_BGT + bool "Background thread unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI background thread + unit. + +config MTD_UBI_DEBUG_MSG_ALLOC + bool "Memory allocation unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI memory allocation + unit. + +config MTD_UBI_DEBUG_MSG_IO + bool "Input/output unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI input/output unit. + +config MTD_UBI_DEBUG_MSG_BLD + bool "Build unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI build unit. + +config MTD_UBI_DEBUG_MSG_SCAN + bool "Scanning unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI scanning unit. + +endmenu # UBI debugging messages + +menu "UBI paranoid checks" + depends on MTD_UBI_DEBUG + +config MTD_UBI_DEBUG_PARANOID_VMT + bool "Paranoid checks in the volume management unit" + depends on MTD_UBI_DEBUG + default n + +config MTD_UBI_DEBUG_PARANOID_VTBL + bool "Paranoid checks in the volume table unit" + depends on MTD_UBI_DEBUG + default n + +config MTD_UBI_DEBUG_PARANOID_DTBL + bool "Paranoid checks in the data table unit" + depends on MTD_UBI_DEBUG + default n + +config MTD_UBI_DEBUG_PARANOID_EBA + bool "Paranoid checks in the eraseblock association unit" + depends on MTD_UBI_DEBUG + default n + +config MTD_UBI_DEBUG_PARANOID_WL + bool "Paranoid checks in the wear-leveling unit" + depends on MTD_UBI_DEBUG + default n + +config MTD_UBI_DEBUG_PARANOID_ALLOC + bool "Paranoid checks in the memory allocation unit" + depends on MTD_UBI_DEBUG + default n + +config MTD_UBI_DEBUG_PARANOID_IO + bool "Paranoid checks in the input/output unit" + depends on MTD_UBI_DEBUG + default n + help + Warning, this is rather heavy-weight and will slow UBI down. + +config MTD_UBI_DEBUG_PARANOID_SCAN + bool "Paranoid checks in the scanning unit" + depends on MTD_UBI_DEBUG + default n + help + Warning, this is rather heavy-weight and will slow UBI down. + +endmenu # UBI paranoid checks diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile new file mode 100644 index 0000000..fa0a264 --- /dev/null +++ b/drivers/mtd/ubi/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_MTD_UBI) += ubi.o + +ubi-y += badeb.o upd.o sysfs.o cdev.o uif.o vtbl.o volmgmt.o eba.o io.o wl.o +ubi-y += scan.o build.o background.o alloc.o init.o ivol.o account.o dtbl.o + +ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o +ubi-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o diff --git a/drivers/mtd/ubi/account.c b/drivers/mtd/ubi/account.c new file mode 100644 index 0000000..181065c --- /dev/null +++ b/drivers/mtd/ubi/account.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ubi.h" +#include "vtbl.h" +#include "ivol.h" +#include "account.h" +#include "scan.h" +#include "alloc.h" +#include "badeb.h" +#include "io.h" +#include "debug.h" + +int ubi_acc_mkvol(const struct ubi_info *ubi, int reserved_pebs) +{ + struct ubi_acc_info *acc = ubi->acc; + + dbg_acc("reserve %d PEBs for a new volume (uvol_count %d" + "rsvd_pebs %d, avail_pebs %d)", reserved_pebs, + acc->uvol_count, acc->rsvd_pebs, acc->avail_pebs); + ubi_assert(reserved_pebs > 0); + + spin_lock(&acc->lock); + if (acc->uvol_count + 1 > acc->max_volumes) { + dbg_err("no room for the volume"); + goto out; + } + if (reserved_pebs > acc->avail_pebs) { + dbg_err("no enough PEBs"); + goto out; + } + acc->uvol_count += 1; + acc->avail_pebs -= reserved_pebs; + acc->rsvd_pebs += reserved_pebs; + ubi_assert(acc->avail_pebs >= 0); + spin_unlock(&acc->lock); + return 0; + +out: + spin_unlock(&acc->lock); + return -ENOSPC; +} + +void ubi_acc_rmvol(const struct ubi_info *ubi, int reserved_pebs) +{ + struct ubi_acc_info *acc = ubi->acc; + + dbg_acc("remove volume and get back %d PEBs (uvol_count %d, " + "rsvd_pebs %d, avail_pebs %d)", reserved_pebs, + acc->uvol_count, acc->rsvd_pebs, acc->avail_pebs); + ubi_assert(reserved_pebs > 0 && reserved_pebs <= acc->rsvd_pebs); + + spin_lock(&acc->lock); + acc->uvol_count -= 1; + acc->avail_pebs += reserved_pebs; + acc->rsvd_pebs -= reserved_pebs; + ubi_assert(acc->uvol_count >= 0); + ubi_assert(acc->rsvd_pebs >= 0); + spin_unlock(&acc->lock); + + /* Take care about PEBs reserved for bad PEB handling */ + ubi_beb_maintain_reserved(ubi); +} + +int ubi_acc_reserve(const struct ubi_info *ubi, int pebs) +{ + struct ubi_acc_info *acc = ubi->acc; + + dbg_acc("reserve %d PEBs (rsvd_pebs %d, avail_pebs %d)", + pebs, acc->rsvd_pebs, acc->avail_pebs); + ubi_assert(pebs > 0); + + spin_lock(&acc->lock); + if (unlikely(pebs > acc->avail_pebs)) { + dbg_err("no enough PEBs"); + spin_unlock(&acc->lock); + return -ENOSPC; + } + acc->avail_pebs -= pebs; + acc->rsvd_pebs += pebs; + spin_unlock(&acc->lock); + return 0; +} + +void ubi_acc_free(const struct ubi_info *ubi, int pebs) +{ + struct ubi_acc_info *acc = ubi->acc; + + dbg_acc("free %d PEBs (rsvd_pebs %d, avail_pebs %d)", + pebs, acc->rsvd_pebs, acc->avail_pebs); + spin_lock(&acc->lock); + ubi_assert(pebs > 0 && pebs <= acc->rsvd_pebs); + acc->rsvd_pebs -= pebs; + acc->avail_pebs += pebs; + spin_unlock(&acc->lock); +} + +static int __init acc_info_check(const struct ubi_info *ubi, + const struct ubi_scan_info *si); + +int __init ubi_acc_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si) +{ + int err, i; + struct ubi_acc_info *acc; + const struct ubi_vtbl_vtr *vtr; + const struct ubi_vtbl_info *vtbl = ubi->vtbl; + const struct ubi_io_info *io = ubi->io; + + dbg_acc("initialize the accounting unit"); + + acc = ubi_kzalloc(sizeof(struct ubi_acc_info)); + if (!acc) + return -ENOMEM; + ubi->acc = acc; + + spin_lock_init(&acc->lock); + acc->ivol_count = UBI_INT_VOL_COUNT; + + for (i = 0; i < acc->ivol_count; i++) { + cond_resched(); + vtr = ubi_ivol_get_vtr(ubi, UBI_INTERNAL_VOL_START + i); + ubi_assert(!IS_ERR(vtr)); + acc->rsvd_pebs += vtr->reserved_pebs; + } + + /* + * The maximum number of volumes may be less then the volume table + * fits if there are too few available eraseblocks on the flash. + */ + acc->max_volumes = vtbl->vt_slots; + i = io->good_peb_count - acc->rsvd_pebs; + if (i <= 0) { + ubi_err("too small flash, at least %d good physical eraseblock" + " needed", acc->rsvd_pebs + 1); + err = -EINVAL; + goto out_acc; + } + + if (acc->max_volumes > i) + acc->max_volumes = i; + + for (i = 0; i < acc->max_volumes; i++) { + cond_resched(); + vtr = ubi_vtbl_get_vtr(ubi, i); + if (IS_ERR(vtr)) + continue; + acc->uvol_count += 1; + acc->rsvd_pebs += vtr->reserved_pebs; + } + + acc->rsvd_pebs += si->alien_peb_count; + acc->avail_pebs = io->good_peb_count - acc->rsvd_pebs; + + /* Check accounting information sanity and consistency */ + err = acc_info_check(ubi, si); + if (err) + goto out_acc; + + dbg_acc("uvol_count %d, ivol_count %d, avail_pebs %d rsvd_pebs %d " + "max_volumes %d", acc->uvol_count, acc->ivol_count, + acc->avail_pebs, acc->rsvd_pebs, acc->max_volumes); + return 0; + +out_acc: + ubi_kfree(acc); + return err; +} + +void __exit ubi_acc_close(const struct ubi_info *ubi) +{ + dbg_acc("close the accounting unit"); + ubi_kfree(ubi->acc); +} + +/** + * acc_info_check - check sanity and consistency of accounting information. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information which must be consistent to accounting + * information + * + * As we try not to trust the data we read from the flash media, we have to + * check that the accounting information is sane and consistent, as it is + * formed using on-flash information. This function returns zero if all is fine + * and a negative error code if some inconsistency was found. + */ +static int __init acc_info_check(const struct ubi_info *ubi, + const struct ubi_scan_info *si) +{ + int i; + const struct ubi_vtbl_vtr *vtr; + const struct ubi_acc_info *acc = ubi->acc; + const struct ubi_vtbl_info *vtbl = ubi->vtbl; + const struct ubi_io_info *io = ubi->io; + + if (acc->avail_pebs < 0 || acc->rsvd_pebs < 0 || acc->uvol_count < 0 || + acc->ivol_count < 0) { + dbg_err("negative values"); + goto bad; + } + + if (acc->avail_pebs > io->good_peb_count) { + dbg_err("bad avail_pebs"); + goto bad; + } + + if (acc->rsvd_pebs > io->good_peb_count) { + dbg_err("bad rsvd_pebs"); + goto bad; + } + + if (acc->avail_pebs + acc->rsvd_pebs != io->good_peb_count) { + dbg_err("accounting error"); + goto bad; + } + + if (acc->max_volumes > vtbl->vt_slots) { + dbg_err("bad max_volumes"); + goto bad; + } + + if (acc->ivol_count + acc->uvol_count > acc->max_volumes) { + dbg_err("vol. count (%d + %d) > max_volumes", + acc->ivol_count, acc->uvol_count); + goto bad; + } + + /* + * Ensure that there are no volumes which exceed acc->max_volumes + * exist. + */ + for (i = acc->max_volumes; i < vtbl->vt_slots; i++) { + cond_resched(); + vtr = ubi_vtbl_get_vtr(ubi, i); + if (unlikely(!IS_ERR(vtr))) { + dbg_err("volume %d exists", i); + goto bad; + } + } + + if (si->vols_found > acc->ivol_count + acc->uvol_count) { + dbg_err("scanning found volumes %d > %d + %d", + si->vols_found, acc->ivol_count, acc->uvol_count); + goto bad; + } + + if (si->highest_vol_id >= acc->max_volumes && + si->highest_vol_id < UBI_INTERNAL_VOL_START) { + dbg_err("too large volume ID %d found by scanning", + si->highest_vol_id); + goto bad; + } + + return 0; + +bad: + ubi_err("accounting check failed"); + dbg_err("uvol_count %d, ivol_count %d, avail_pebs %d, rsvd_pebs %d " + "io->good_peb_count %d, max_volumes %d, vtbl->vt_slots %d", + acc->uvol_count, acc->ivol_count, acc->avail_pebs, + acc->rsvd_pebs, io->good_peb_count, acc->max_volumes, + vtbl->vt_slots); + return -EINVAL; +} diff --git a/drivers/mtd/ubi/account.h b/drivers/mtd/ubi/account.h new file mode 100644 index 0000000..c352305 --- /dev/null +++ b/drivers/mtd/ubi/account.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * UBI accounting unit. + * + * This unit is responsible for maintaining the correct physical eraseblock + * accounting to prevent overcommitment. + */ + +#ifndef __UBI_ACCOUNT_H__ +#define __UBI_ACCOUNT_H__ + +#include +#include + +struct ubi_info; +struct ubi_scan_info; + +/** + * ubi_acc_mkvol - account creation of a volume. + * + * @ubi: the UBI device description object + * @reserved_pebs: how many eraseblocks are reserved for the volume + * + * This function reserves @reserved_pebs physical eraseblocks for the newly + * created volume. Returns zero in case of success and a %-ENOSPC if there are + * no enough physical eraseblocks. + */ +int ubi_acc_mkvol(const struct ubi_info *ubi, int reserved_pebs); + +/** + * ubi_acc_rmvol - account removal of a volume. + * + * @ubi: the UBI device description object + * @reserved_pebs: how many eraseblocks were reserved for the volume + * + * This function reclaims the physical eraseblocks occupied by a volume. Note, + * UBI is trying to maintain a constant level of physical eraseblock reserved + * for bad PEB handling. So, if there is a lack of reserved physical + * eraseblock, this function will reserve them at once. + */ +void ubi_acc_rmvol(const struct ubi_info *ubi, int reserved_pebs); + +/** + * ubi_acc_reserve - reserve a number of physical eraseblocks. + * + * @ubi: the UBI device description object + * @pebs: how many physical eraseblocks to reserve + * + * This function returns zero in case of success and %-ENOSPC if there are no + * enough physical eraseblocks. + */ +int ubi_acc_reserve(const struct ubi_info *ubi, int pebs); + +/** + * ubi_acc_free - free a number of reserved physical eraseblocks. + * + * @ubi: the UBI device description object + * @pebs: how many physical eraseblocks to free + */ +void ubi_acc_free(const struct ubi_info *ubi, int pebs); + +/** + * ubi_acc_init_scan - initialize the accounting unit using scanning + * information. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int __init ubi_acc_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si); + +/** + * ubi_acc_close - close the accounting unit. + * + * @ubi: the UBI device description object + */ +void __exit ubi_acc_close(const struct ubi_info *ubi); + +/** + * struct ubi_acc_info - the UBI accounting unit's description data structure. + * + * @ivol_count: count of internal volumes + * @uvol_count: count of user volumes + * @rsvd_pebs: count of reserved physical eraseblocks + * @avail_pebs: count of available physical eraseblocks + * @max_volumes: maximum number of volumes that users may create + * @lock: protects the accounting data + */ +struct ubi_acc_info { + int ivol_count; /* public */ + int uvol_count; /* public */ + int rsvd_pebs; /* public */ + int avail_pebs; /* public */ + int max_volumes; /* public */ + spinlock_t lock; /* private */ +}; + +#endif /* !__UBI_ACCOUNT_H__ */ diff --git a/drivers/mtd/ubi/alloc.c b/drivers/mtd/ubi/alloc.c new file mode 100644 index 0000000..36cd436 --- /dev/null +++ b/drivers/mtd/ubi/alloc.c @@ -0,0 +1,609 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include "ubi.h" +#include "alloc.h" +#include "io.h" +#include "background.h" +#include "wl.h" +#include "debug.h" +#include "eba.h" +#include "scan.h" + +#define BGT_WORK_SLAB_NAME "ubi_bgt_work_slab" +#define WL_ERASE_WORK_SLAB_NAME "ubi_wl_erase_work_slab" +#define WL_ENTRY_SLAB_NAME "ubi_wl_entry_slab" +#define WL_PROT_ENTRY_SLAB_NAME "ubi_wl_prow_entry_slab" +#define EBA_LTREE_ENTRY_SLAB_NAME "ubi_eba_ltree_entry_slab" +#define SCAN_EB_SLAB_NAME "ubi_scan_leb" +#define SCAN_VOLUME_SLAB_NAME "ubi_scan_volume" + +static struct kmem_cache *bgt_work_slab; +static struct kmem_cache *wl_erase_work_slab; +static struct kmem_cache *wl_entries_slab; +static struct kmem_cache *wl_prot_entry_slab; +static struct kmem_cache *eba_ltree_entry_slab; +static struct kmem_cache *scan_eb_slab; +static struct kmem_cache *scan_volume_slab; + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_ALLOC +static int paranoid_alloc(const void *p); +static int paranoid_free(const void *p); +static void __exit paranoid_check(void); +#else +#define paranoid_alloc(p) 0 +#define paranoid_free(p) 0 +#define paranoid_check() +#endif + +void *ubi_kzalloc(size_t size) +{ + void *ret; + + ret = kzalloc(size, GFP_KERNEL); + if (unlikely(!ret)) { + ubi_err("cannot allocate %zd bytes", size); + dump_stack(); + return NULL; + } + + dbg_alloc("%p (%zd bytes)", ret, size); + if (unlikely(paranoid_alloc(ret))) + return NULL; + return ret; +} + +void *ubi_kmalloc(size_t size) +{ + void *ret; + + ret = kmalloc(size, GFP_KERNEL); + if (unlikely(!ret)) { + ubi_err("cannot allocate %zd bytes", size); + dump_stack(); + return NULL; + } + + dbg_alloc("%p (%zd bytes)", ret, size); + if (unlikely(paranoid_alloc(ret))) + return NULL; + return ret; +} + +void ubi_kfree(const void *obj) +{ + if (unlikely(!obj)) + return; + if (unlikely(paranoid_free(obj))) + return; + dbg_alloc("%p", obj); + kfree(obj); +} + +struct ubi_ec_hdr *ubi_zalloc_ec_hdr(const struct ubi_info *ubi) +{ + struct ubi_ec_hdr *ec_hdr; + const struct ubi_io_info *io = ubi->io; + + ec_hdr = kzalloc(io->ec_hdr_alsize, GFP_KERNEL); + if (unlikely(!ec_hdr)) { + ubi_err("cannot allocate %zd bytes", io->ec_hdr_alsize); + dump_stack(); + return NULL; + } + + dbg_alloc("%p (%zd bytes)", ec_hdr, io->ec_hdr_alsize); + if (unlikely(paranoid_alloc(ec_hdr))) + return NULL; + + return ec_hdr; +} + +void ubi_free_ec_hdr(const struct ubi_info *ubi, struct ubi_ec_hdr *ec_hdr) +{ + if (unlikely(!ec_hdr)) + return; + if (unlikely(paranoid_free(ec_hdr))) + return; + dbg_alloc("%p", ec_hdr); + kfree(ec_hdr); +} + +struct ubi_vid_hdr *ubi_zalloc_vid_hdr(const struct ubi_info *ubi) +{ + char *vid_hdr; + const struct ubi_io_info *io = ubi->io; + + vid_hdr = kzalloc(io->vid_hdr_alsize, GFP_KERNEL); + if (unlikely(!vid_hdr)) { + ubi_err("cannot allocate %zd bytes", io->vid_hdr_alsize); + dump_stack(); + return NULL; + } + + /* + * If VID headers are stored at non-aligned addresses, we have to shift + * the pointer. + */ + if (likely(vid_hdr)) + vid_hdr = vid_hdr + io->vid_hdr_shift; + + dbg_alloc("%p (%zd bytes)", vid_hdr, io->vid_hdr_alsize); + if (unlikely(paranoid_alloc(vid_hdr))) + return NULL; + + return (struct ubi_vid_hdr *)vid_hdr; +} + +void ubi_free_vid_hdr(const struct ubi_info *ubi, struct ubi_vid_hdr *vid_hdr) +{ + if (unlikely(!vid_hdr)) + return; + if (unlikely(paranoid_free(vid_hdr))) + return; + vid_hdr = (struct ubi_vid_hdr *)((char *)vid_hdr - ubi->io->vid_hdr_shift); + dbg_alloc("%p", vid_hdr); + kfree(vid_hdr); +} + +struct ubi_bgt_work *ubi_alloc_bgt_work(void) +{ + struct ubi_bgt_work *wrk; + + wrk = kmem_cache_alloc(bgt_work_slab, GFP_KERNEL); + if (unlikely(!wrk)) { + ubi_err("failed to allocate memory"); + dump_stack(); + return NULL; + } + dbg_alloc("%p (%zd bytes)", wrk, sizeof(struct ubi_bgt_work)); + if (unlikely(paranoid_alloc(wrk))) + return NULL; + return wrk; +} + +void ubi_free_bgt_work(struct ubi_bgt_work *wrk) +{ + if (unlikely(!wrk)) + return; + if (unlikely(paranoid_free(wrk))) + return; + dbg_alloc("%p", wrk); + kmem_cache_free(bgt_work_slab, wrk); +} + +struct ubi_wl_erase_work *ubi_alloc_wl_erase_work(void) +{ + struct ubi_wl_erase_work *wrk; + + wrk = kmem_cache_alloc(wl_erase_work_slab, GFP_KERNEL); + if (unlikely(!wrk)) { + ubi_err("failed to allocate memory"); + dump_stack(); + return NULL; + } + dbg_alloc("%p (%zd bytes)", wrk, sizeof(struct ubi_wl_erase_work)); + if (unlikely(paranoid_alloc(wrk))) + return NULL; + return wrk; +} + +void ubi_free_wl_erase_work(struct ubi_wl_erase_work *wrk) +{ + if (unlikely(!wrk)) + return; + if (unlikely(paranoid_free(wrk))) + return; + dbg_alloc("%p", wrk); + kmem_cache_free(wl_erase_work_slab, wrk); +} + +struct ubi_wl_entry *ubi_alloc_wl_entry(void) +{ + struct ubi_wl_entry *wle; + + wle = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + if (unlikely(!wle)) { + ubi_err("failed to allocate memory"); + dump_stack(); + return NULL; + } + dbg_alloc("%p (%zd bytes)", wle, sizeof(struct ubi_wl_entry)); + if (unlikely(paranoid_alloc(wle))) + return NULL; + return wle; +} + +void ubi_free_wl_entry(struct ubi_wl_entry *wle) +{ + if (unlikely(!wle)) + return; + if (unlikely(paranoid_free(wle))) + return; + dbg_alloc("%p", wle); + kmem_cache_free(wl_entries_slab, wle); +} + +struct ubi_wl_prot_entry *ubi_alloc_wl_prot_entry(void) +{ + struct ubi_wl_prot_entry *pe; + + pe = kmem_cache_alloc(wl_prot_entry_slab, GFP_KERNEL); + if (unlikely(!pe)) { + ubi_err("failed to allocate memory"); + dump_stack(); + return NULL; + } + dbg_alloc("%p (%zd bytes)", pe, sizeof(struct ubi_wl_prot_entry)); + if (unlikely(paranoid_alloc(pe))) + return NULL; + return pe; +} + +void ubi_free_wl_prot_entry(struct ubi_wl_prot_entry *pe) +{ + if (unlikely(!pe)) + return; + if (unlikely(paranoid_free(pe))) + return; + dbg_alloc("%p", pe); + kmem_cache_free(wl_prot_entry_slab, pe); +} + +struct ubi_eba_ltree_entry *ubi_alloc_eba_ltree_entry(void) +{ + struct ubi_eba_ltree_entry *le; + + le = kmem_cache_alloc(eba_ltree_entry_slab, GFP_KERNEL); + if (unlikely(!le)) { + ubi_err("failed to allocate memory"); + dump_stack(); + return NULL; + } + dbg_alloc("%p (%zd bytes)", le, sizeof(struct ubi_eba_ltree_entry)); + if (unlikely(paranoid_alloc(le))) + return NULL; + return le; +} + +void ubi_free_eba_ltree_entry(struct ubi_eba_ltree_entry *le) +{ + if (unlikely(!le)) + return; + if (unlikely(paranoid_free(le))) + return; + dbg_alloc("%p", le); + kmem_cache_free(eba_ltree_entry_slab, le); +} + +struct ubi_scan_leb *ubi_alloc_scan_leb(void) +{ + struct ubi_scan_leb *seb; + + seb = kmem_cache_alloc(scan_eb_slab, GFP_KERNEL); + if (unlikely(!seb)) { + ubi_err("failed to allocate memory"); + dump_stack(); + return NULL; + } + + dbg_alloc("%p (%zd bytes)", seb, sizeof(struct ubi_scan_leb)); + if (unlikely(paranoid_alloc(seb))) + return NULL; + return seb; +} + +void ubi_free_scan_leb(struct ubi_scan_leb *seb) +{ + if (unlikely(!seb)) + return; + if (unlikely(paranoid_free(seb))) + return; + dbg_alloc("%p", seb); + kmem_cache_free(scan_eb_slab, seb); +} + +struct ubi_scan_volume *ubi_alloc_scan_volume(void) +{ + struct ubi_scan_volume *sv; + + sv = kmem_cache_alloc(scan_volume_slab, GFP_KERNEL); + if (unlikely(!sv)) { + ubi_err("failed to allocate memory"); + dump_stack(); + return NULL; + } + + dbg_alloc("%p (%zd bytes)", sv, sizeof(struct ubi_scan_volume)); + if (unlikely(paranoid_alloc(sv))) + return NULL; + return sv; +} + +void ubi_free_scan_volume(struct ubi_scan_volume *sv) +{ + if (unlikely(!sv)) + return; + if (unlikely(paranoid_free(sv))) + return; + dbg_alloc("%p", sv); + kmem_cache_free(scan_volume_slab, sv); +} + +static void ltree_entry_ctor(void *obj, struct kmem_cache *cache, + unsigned long flags); + +int __init ubi_alloc_init(void) +{ + const char *name; + size_t size; + + name = BGT_WORK_SLAB_NAME; + size = sizeof(struct ubi_bgt_work); + bgt_work_slab = kmem_cache_create(name, size, 0, 0, NULL, NULL); + if (!bgt_work_slab) + goto out; + + name = WL_ERASE_WORK_SLAB_NAME; + size = sizeof(struct ubi_wl_erase_work); + wl_erase_work_slab = kmem_cache_create(name, size, 0, 0, NULL, NULL); + if (!wl_erase_work_slab) + goto out; + + name = WL_ENTRY_SLAB_NAME; + size = sizeof(struct ubi_wl_entry); + wl_entries_slab = kmem_cache_create(name, size, 0, 0, NULL, NULL); + if (!wl_entries_slab) + goto out; + + name = WL_PROT_ENTRY_SLAB_NAME; + size = sizeof(struct ubi_wl_prot_entry); + wl_prot_entry_slab = kmem_cache_create(name, size, 0, 0, NULL, NULL); + if (!wl_prot_entry_slab) + goto out; + + name = EBA_LTREE_ENTRY_SLAB_NAME; + size = sizeof(struct ubi_eba_ltree_entry); + eba_ltree_entry_slab = kmem_cache_create(name, size, 0, 0, + <ree_entry_ctor, NULL); + if (!eba_ltree_entry_slab) + goto out; + + name = SCAN_EB_SLAB_NAME; + size = sizeof(struct ubi_scan_leb); + scan_eb_slab = kmem_cache_create(name, size, 0, 0, NULL, NULL); + if (!scan_eb_slab) + goto out; + + name = SCAN_VOLUME_SLAB_NAME; + size = sizeof(struct ubi_scan_volume); + scan_volume_slab = kmem_cache_create(name, size, 0, 0, NULL, NULL); + if (!scan_volume_slab) + goto out; + + return 0; + +out: + ubi_err("cannot create \"%s\" slab", name); + ubi_alloc_close(); + return -ENOMEM; +} + +void __exit ubi_alloc_close(void) +{ + paranoid_check(); + if (scan_volume_slab) + kmem_cache_destroy(scan_volume_slab); + if (scan_eb_slab) + kmem_cache_destroy(scan_eb_slab); + if (eba_ltree_entry_slab) + kmem_cache_destroy(eba_ltree_entry_slab); + if (wl_prot_entry_slab) + kmem_cache_destroy(wl_prot_entry_slab); + if (wl_entries_slab) + kmem_cache_destroy(wl_entries_slab); + if (wl_erase_work_slab) + kmem_cache_destroy(wl_erase_work_slab); + if (bgt_work_slab) + kmem_cache_destroy(bgt_work_slab); +} + +/* Lock tree entries slab cache constructor */ +static void ltree_entry_ctor(void *obj, struct kmem_cache *cache, + unsigned long flags) +{ + struct ubi_eba_ltree_entry *le = obj; + + if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) != + SLAB_CTOR_CONSTRUCTOR) + return; + + le->users = 0; + init_rwsem(&le->mutex); +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_ALLOC + +/* + * If UBI memory allocation unit paranoid checking is enabled, we maintain an + * allocation RB-tree, remember all allocations and issue a warning if + * something was not freed when the module is being unloaded. + */ + +#include +#include +#include +#include "misc.h" + +/* + * struct alloc_tree_entry - an entry in the tree of allocations. + * + * @rb: the RB-tree link + * @add: the address of the allocated object + * @caller0: the caller address + * @caller1: the caller's caller address + */ +struct alloc_tree_entry { + struct rb_node rb; + unsigned long addr; + unsigned long caller0; + unsigned long caller1; +}; + +/* The root of the allocation tree */ +static struct rb_root alloc_tree = RB_ROOT; + +/* Protects the allocation tree */ +static spinlock_t alloc_tree_lock = SPIN_LOCK_UNLOCKED; + +static void print_allocated(void); + +/** + * paranoid_alloc - a notifier that the object was allocated. + * + * @a: the object address + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int paranoid_alloc(const void *a) +{ + struct rb_node **p, *parent = NULL; + struct alloc_tree_entry *te; + unsigned long addr = (unsigned long)a; + + if (unlikely(!a)) + return 0; + + te = kmalloc(sizeof(struct alloc_tree_entry), GFP_KERNEL); + if (unlikely(!te)) { + ubi_err("no memory for a \"struct alloc_tree_entry\" onject"); + dump_stack(); + ubi_msg("allocated objects:"); + print_allocated(); + return -ENOMEM; + } + + te->addr = addr; + te->caller0 = (unsigned long)__builtin_return_address(0); + te->caller1 = (unsigned long)__builtin_return_address(1); + + spin_lock(&alloc_tree_lock); + p = &alloc_tree.rb_node; + while (*p) { + struct alloc_tree_entry *te1; + + parent = *p; + te1 = rb_entry(parent, struct alloc_tree_entry, rb); + + BUG_ON(addr == te1->addr); + + if (addr < te1->addr) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + rb_link_node(&te->rb, parent, p); + rb_insert_color(&te->rb, &alloc_tree); + spin_unlock(&alloc_tree_lock); + return 0; +} + +/** + * paranoid_free - a notifier that an object was freed. + * + * @a: the object address + * + * This function returns zero if the object was previously allocated and + * %1 if not. + */ +static int paranoid_free(const void *a) +{ + struct rb_node *p; + unsigned long addr = (unsigned long)a; + struct alloc_tree_entry *te; + + if (unlikely(!a)) + return 0; + + spin_lock(&alloc_tree_lock); + p = alloc_tree.rb_node; + while (p) { + te = rb_entry(p, struct alloc_tree_entry, rb); + + if (addr == te->addr) + break; + + if (addr < te->addr) + p = p->rb_left; + else + p = p->rb_right; + } + + if (unlikely(!p)) { + ubi_err("free a non-allocated object %p", a); + dump_stack(); + spin_unlock(&alloc_tree_lock); + return 1; + } + + rb_erase(&te->rb, &alloc_tree); + spin_unlock(&alloc_tree_lock); + kfree(te); + return 0; +} + +/** + * paranoid_check - check that all the allocated objects were freed. + */ +static void __exit paranoid_check(void) +{ + if (alloc_tree.rb_node == NULL) + return; + + ubi_err("paranoid check failed"); + ubi_msg("the following objects were not freed:"); + print_allocated(); +} + +/** + * print_allocated - print the list of allocated objects. + */ +static void print_allocated(void) +{ + struct rb_node *p; + struct alloc_tree_entry *te; + + spin_lock(&alloc_tree_lock); + rb_for_each_entry(p, te, &alloc_tree, rb) { + printk(UBI_MSG_PREF " %#lx, callers: ", te->addr); + __print_symbol("%s <== ", te->caller0); + __print_symbol("%s\n", te->caller1); + } + spin_unlock(&alloc_tree_lock); +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID_ALLOC */ diff --git a/drivers/mtd/ubi/alloc.h b/drivers/mtd/ubi/alloc.h new file mode 100644 index 0000000..f70eeb5 --- /dev/null +++ b/drivers/mtd/ubi/alloc.h @@ -0,0 +1,236 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * UBI memory allocation unit. + * + * This unit provides memory allocation/deallocation calls and wrappers, plus + * some debugging stuff. + */ + +#ifndef __UBI_ALLOC_H__ +#define __UBI_ALLOC_H__ + +#include + +struct ubi_info; +struct ubi_ec_hdr; +struct ubi_vid_hdr; +struct ubi_bgt_work; +struct ubi_wl_erase_work; +struct ubi_wl_entry; +struct ubi_wl_prot_entry; +struct ubi_eba_ltree_entry; +struct ubi_scan_leb; +struct ubi_scan_volume; + +/** + * ubi_kmalloc - allocate memory. + * + * @size: how many bytes to allocate + * + * This function is just a wrapper over the standard Linux 'kmalloc()' + * function with %GFP_KERNEL argument. + */ +void *ubi_kmalloc(size_t size); + +/** + * ubi_kzalloc - allocate and zero memory. + * + * @size: how many bytes to allocate + * + * This function is just a wrapper over the standard Linux 'kzalloc()' + * function with %GFP_KERNEL argument. + */ +void *ubi_kzalloc(size_t size); + +/** + * ubi_kfree - free memory allocated by 'ubi_kmalloc()' or 'ubi_kzalloc()'. + * + * @obj: a pointer to the object to free + * + * This is just a wrapper over the standard Linux 'kfree()' function. + */ +void ubi_kfree(const void *obj); + +/** + * ubi_zalloc_ec_hdr - allocate a &struct ubi_ec_hdr object. + * + * @ubi: the UBI device description object + * + * This function returns a pointer to the newly allocated and zero-filled erase + * counter header object in case of success and %NULL in case of failure. + */ +struct ubi_ec_hdr *ubi_zalloc_ec_hdr(const struct ubi_info *ubi); + +/** + * ubi_free_ec_hdr - free a &struct ubi_ec_hdr object. + * + * @ubi: the UBI device description object + * @ec_hdr: a pointer to the object to free + */ +void ubi_free_ec_hdr(const struct ubi_info *ubi, struct ubi_ec_hdr *ec_hdr); + +/** + * ubi_zalloc_vid_hdr - allocate a &struct ubi_vid_hdr object. + * + * @ubi: the UBI device description object + * + * This function returns a pointer to the newly allocated and zero-filled + * volume identifier header object in case of success and %NULL in case of + * failure. + */ +struct ubi_vid_hdr *ubi_zalloc_vid_hdr(const struct ubi_info *ubi); + +/** + * ubi_free_vid_hdr - free a &struct ubi_vid_hdr object. + * + * @ubi: the UBI device description object + * @vid_hdr: a pointer to the object to free + */ +void ubi_free_vid_hdr(const struct ubi_info *ubi, struct ubi_vid_hdr *vid_hdr); + +/** + * ubi_alloc_bgt_work - allocate a &struct ubi_bgt_work object. + * + * This function returns a pointer to the newly allocated &struct ubi_bgt_work + * object in case of success and %NULL in case of failure. The allocated object + * is not zeroed. + */ +struct ubi_bgt_work *ubi_alloc_bgt_work(void); + +/** + * ubi_free_bgt_work - free a &struct ubi_bgt_work object. + * + * @wrk: a pointer to the object to free + */ +void ubi_free_bgt_work(struct ubi_bgt_work *wrk); + +/** + * ubi_alloc_wl_erase_work - allocate a &struct ubi_wl_erase_work object. + * + * This function returns a pointer to the newly allocated &struct ubi_wl_erase_work + * object in case of success and %NULL in case of failure. The allocated object + * is not zeroed. + */ +struct ubi_wl_erase_work *ubi_alloc_wl_erase_work(void); + +/** + * ubi_free_wl_erase_work - free a &struct ubi_wl_erase_work object. + * + * @wrk: a pointer to the object to free + */ +void ubi_free_wl_erase_work(struct ubi_wl_erase_work *wrk); + +/** + * ubi_alloc_wl_entry - allocate a &struct ubi_wl_entry object. + * + * This function returns a pointer to the newly allocated &struct ubi_wl_entry + * object in case of success and %NULL in case of failure. The allocated object + * is not zeroed. + */ +struct ubi_wl_entry *ubi_alloc_wl_entry(void); + +/** + * ubi_free_wl_entry - free a &struct ubi_wl_entry object. + * + * @wle: a pointer to the object to free + */ +void ubi_free_wl_entry(struct ubi_wl_entry *wle); + +/** + * ubi_alloc_wl_prot_entry - allocate a &struct ubi_wl_prot_entry object. + * + * This function returns a pointer to the newly allocated + * &struct ubi_wl_prot_entry object in case of success and %NULL in case of + * failure. The allocated object is not zeroed. + */ +struct ubi_wl_prot_entry *ubi_alloc_wl_prot_entry(void); + +/** + * ubi_free_wl_prot_entry - free a &struct ubi_wl_prot_entry object. + * + * @pe: a pointer to the object to free + */ +void ubi_free_wl_prot_entry(struct ubi_wl_prot_entry *pe); + +/** + * ubi_alloc_eba_ltree_entry - allocate a &struct ubi_eba_ltree_entry object. + * + * This function returns a pointer to the newly allocated + * &struct ubi_eba_ltree_entry object in case of success and %NULL in case of + * failure. The allocated object is not zeroed, but the @users and @mutex + * fields are initialized by slab constructor. + */ +struct ubi_eba_ltree_entry *ubi_alloc_eba_ltree_entry(void); + +/** + * ubi_free_eba_ltree_entry - free a &struct ubi_eba_ltree_entry object. + * + * @le: a pointer to the object to free + */ +void ubi_free_eba_ltree_entry(struct ubi_eba_ltree_entry *le); + +/** + * ubi_alloc_scan_leb - allocate a &struct ubi_scan_leb object. + * + * This function returns a pointer to the newly allocated &struct ubi_scan_leb + * object in case of success, or %NULL in case of failure. The allocated object + * is not zeroed. + */ +struct ubi_scan_leb *ubi_alloc_scan_leb(void); + +/** + * ubi_free_scan_leb - free a &struct ubi_scan_leb object. + * + * @seb: a pinter to the &struct ubi_scan_leb object to free + */ +void ubi_free_scan_leb(struct ubi_scan_leb *seb); + +/** + * ubi_alloc_scan_volume - allocate a &struct ubi_scan_volume object. + * + * This function returns a pointer to the newly allocated &struct + * ubi_scan_volume object in cases of success, or %NULL in case of failure. The + * allocated object is not zeroed. + */ +struct ubi_scan_volume *ubi_alloc_scan_volume(void); + +/** + * ubi_free_scan_volume - free a &struct ubi_scan_volume object. + * + * @sv: a pinter to the &struct ubi_scan_volume object to free + */ +void ubi_free_scan_volume(struct ubi_scan_volume *sv); + +/** + * ubi_alloc_init - initialize the UBI memory allocation unit. + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int __init ubi_alloc_init(void); + +/** + * ubi_alloc_close - close the UBI memory allocation unit. + */ +void __exit ubi_alloc_close(void); + +#endif /* !__UBI_ALLOC_H__ */ diff --git a/drivers/mtd/ubi/background.c b/drivers/mtd/ubi/background.c new file mode 100644 index 0000000..c1ffd0a --- /dev/null +++ b/drivers/mtd/ubi/background.c @@ -0,0 +1,328 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Thomas Gleixner, Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include "ubi.h" +#include "alloc.h" +#include "debug.h" +#include "background.h" + +/* Background thread name pattern */ +#define BGT_NAME_PATTERN "ubi_bgt%dd" + +/* Highest number of pending works */ +#define BGT_MAX_PENDING_WORKS 0x7FFFFFFF + +static void bgt_do_wrk(const struct ubi_info *ubi); + +int ubi_bgt_schedule(const struct ubi_info *ubi, struct ubi_bgt_work *wrk) +{ + struct ubi_bgt_info *bgt = ubi->bgt; + +retry: + spin_lock(&bgt->lock); + dbg_bgt("%s: schedule work %p (func %p, priv %p)", + bgt->bgt_name, wrk, wrk->func, wrk->priv); + + if (unlikely(!bgt->task)) { + ubi_err("task \"%s\" was killed", bgt->bgt_name); + spin_unlock(&bgt->lock); + return -ENODEV; + } + + if (unlikely(bgt->pending_works_count == BGT_MAX_PENDING_WORKS)) { + /* Too many pending works */ + spin_unlock(&bgt->lock); + dbg_bgt("pending queue is too long, do a work now"); + bgt_do_wrk(ubi); + goto retry; + } + + list_add_tail(&wrk->list, &bgt->pending_works); + bgt->pending_works_count += 1; + + if (!bgt->active_work && likely(bgt->enabled)) + wake_up_process(bgt->task); + spin_unlock(&bgt->lock); + return 0; +} + +int ubi_bgt_reschedule(const struct ubi_info *ubi, struct ubi_bgt_work *wrk) +{ + struct ubi_bgt_info *bgt = ubi->bgt; + + spin_lock(&bgt->lock); + dbg_bgt("%s: re-schedule work %p (func %p, priv %p)", + bgt->bgt_name, wrk, wrk->func, wrk->priv); + + if (unlikely(!bgt->task)) { + ubi_err("task \"%s\" was killed", bgt->bgt_name); + spin_unlock(&bgt->lock); + return -ENODEV; + } + + list_add_tail(&wrk->list, &bgt->pending_works); + bgt->pending_works_count += 1; + + if (!bgt->active_work && likely(bgt->enabled)) + wake_up_process(bgt->task); + spin_unlock(&bgt->lock); + return 0; +} + +struct ubi_bgt_work *ubi_bgt_next_work(const struct ubi_info *ubi) +{ + struct ubi_bgt_work *wrk; + struct ubi_bgt_info *bgt = ubi->bgt; + + spin_lock(&bgt->lock); + if (list_empty(&bgt->pending_works)) { + wrk = NULL; + dbg_bgt("%s: no works", bgt->bgt_name); + } else { + wrk = list_entry(bgt->pending_works.next, struct ubi_bgt_work, + list); + list_del(&wrk->list); + bgt->pending_works_count -= 1; + dbg_bgt("%s: next work %p (func %p, priv %p)", + bgt->bgt_name, wrk, wrk->func, wrk->priv); + } + spin_unlock(&bgt->lock); + return wrk; +} + +int ubi_bgt_enable(const struct ubi_info *ubi) +{ + struct ubi_bgt_info *bgt = ubi->bgt; + + spin_lock(&bgt->lock); + dbg_bgt("enable \"%s\"", bgt->bgt_name); + + if (!bgt->task) { + ubi_err("task \"%s\" was killed", bgt->bgt_name); + spin_unlock(&bgt->lock); + return -ENODEV; + } + + bgt->enabled = 1; + wake_up_process(bgt->task); + spin_unlock(&bgt->lock); + return 0; +} + +void ubi_bgt_disable(const struct ubi_info *ubi) +{ + struct ubi_bgt_info *bgt = ubi->bgt; + + spin_lock(&bgt->lock); + dbg_bgt("disable \"%s\"", bgt->bgt_name); + bgt->enabled = 0; + spin_unlock(&bgt->lock); +} + +void ubi_bgt_kill_thread(const struct ubi_info *ubi) +{ + struct ubi_bgt_info *bgt = ubi->bgt; + + dbg_bgt("disable \"%s\"", bgt->bgt_name); + if (bgt->task) { + send_sig(SIGKILL, bgt->task, 1); + wait_for_completion(&bgt->thread_stop); + } +} + +/** + * bgt_do_wrk - run pending works. + * + * @ubi: the UBI device description object + */ +static void bgt_do_wrk(const struct ubi_info *ubi) +{ + int err; + struct ubi_bgt_info *bgt = ubi->bgt; + + spin_lock(&bgt->lock); + while (!list_empty(&bgt->pending_works) && likely(bgt->enabled)) { + struct ubi_bgt_work *wrk; + + bgt->active_work = wrk = list_entry(bgt->pending_works.next, + struct ubi_bgt_work, list); + list_del(&wrk->list); + bgt->pending_works_count -= 1; + ubi_assert(bgt->pending_works_count >= 0); + spin_unlock(&bgt->lock); + + cond_resched(); + + /* + * Call the worker function. Do not touch the work structure + * after this call as it will have been freed or reused by that + * time by the worker function. + */ + dbg_bgt("%s: run work %p (func %p, priv %p)", + bgt->bgt_name, wrk, wrk->func, wrk->priv); + + err = wrk->func(ubi, wrk, 0); + if (unlikely(err)) + ubi_warn("%s: work failed with error code %d", + bgt->bgt_name, err); + + spin_lock(&bgt->lock); + bgt->active_work = NULL; + } + spin_unlock(&bgt->lock); +} + +/** + * ubi_thread - UBI background thread. + * + * @u: the UBI device description object pointer + */ +static int ubi_thread(void *u) +{ + const struct ubi_info *ubi = u; + struct ubi_bgt_info *bgt = ubi->bgt; + + daemonize(bgt->bgt_name); + allow_signal(SIGKILL); + allow_signal(SIGSTOP); + + ubi_msg("background thread \"%s\" started, PID %d", + bgt->bgt_name, current->pid); + + bgt->task = current; + complete(&bgt->thread_start); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + + for (;;) { + cond_resched(); + + if (unlikely(!bgt->enabled) || + list_empty(&bgt->pending_works)) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + + if (try_to_freeze()) + continue; + + while (signal_pending(current)) { + siginfo_t info; + unsigned long nr; + + nr = dequeue_signal_lock(current, ¤t->blocked, + &info); + if (nr == SIGKILL) + goto out; + if (nr == SIGSTOP) { + bgt->enabled = !bgt->enabled; + ubi_msg("%s the background thread", + bgt->enabled ? "enable" : "disable"); + } + } + + bgt_do_wrk(ubi); + } + +out: + dbg_bgt("killing background thread \"%s\"", bgt->bgt_name); + + /* Cancel all pending works before exiting */ + spin_lock(&bgt->lock); + bgt->task = NULL; + + bgt->enabled = 0; + while (!list_empty(&bgt->pending_works)) { + struct ubi_bgt_work *wrk; + + wrk = list_entry(bgt->pending_works.next, struct ubi_bgt_work, + list); + list_del(&wrk->list); + bgt->pending_works_count -= 1; + spin_unlock(&bgt->lock); + wrk->func(ubi, wrk, 1); + spin_lock(&bgt->lock); + } + spin_unlock(&bgt->lock); + + complete_and_exit(&bgt->thread_stop, 0); +} + +int __init ubi_bgt_init(struct ubi_info *ubi) +{ + int err; + pid_t pid; + struct ubi_bgt_info *bgt; + + dbg_bgt("initialize the UBI background thread unit"); + + bgt = ubi_kzalloc(sizeof(struct ubi_bgt_info)); + if (!bgt) + return -ENOMEM; + ubi->bgt = bgt; + + init_completion(&bgt->thread_start); + init_completion(&bgt->thread_stop); + INIT_LIST_HEAD(&bgt->pending_works); + spin_lock_init(&bgt->lock); + + bgt->bgt_name = ubi_kmalloc(sizeof(BGT_NAME_PATTERN) + 20); + if (!bgt->bgt_name) { + err = -ENOMEM; + goto out_bgt; + } + sprintf(bgt->bgt_name, BGT_NAME_PATTERN, ubi->ubi_num); + + pid = kernel_thread(ubi_thread, ubi, CLONE_FS | CLONE_FILES); + if (pid < 0) { + err = pid; + ubi_err("cannot spawn \"%s\", error %d", bgt->bgt_name, err); + goto out_name; + } + + wait_for_completion(&bgt->thread_start); + dbg_bgt("the UBI background thread unit is initialized"); + return 0; + +out_name: + ubi_kfree(bgt->bgt_name); +out_bgt: + ubi_kfree(bgt); + return err; +} + +void __exit ubi_bgt_close(struct ubi_info *ubi) +{ + struct ubi_bgt_info *bgt = ubi->bgt; + + dbg_bgt("close the UBI background thread unit"); + + ubi_assert(!bgt->enabled); + ubi_assert(bgt->pending_works_count == 0); + ubi_assert(list_empty(&bgt->pending_works)); + + ubi_kfree(bgt->bgt_name); + ubi_kfree(bgt); +} diff --git a/drivers/mtd/ubi/background.h b/drivers/mtd/ubi/background.h new file mode 100644 index 0000000..4709492 --- /dev/null +++ b/drivers/mtd/ubi/background.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Thomas Gleixner, Artem B. Bityutskiy + */ + +/* + * UBI background thread unit. + * + * This unit maintains a per-UBI device thread which is supposed to do + * different background works. It is mostly used by the WL unit to perform + * eraseblock erasure and movement, but may also be used for other works. + */ + +#ifndef __UBI_BACKGROUND_H__ +#define __UBI_BACKGROUND_H__ + +#include +#include +#include +#include + +struct ubi_info; +struct ubi_bgt_work; + +/** + * ubi_bgt_schedule - schedule a work. + * + * @ubi: the UBI device description object + * @wrk: the work to schedule + * + * This function enqueues a work defined by @wrk to the tail of the pending + * works list. Returns zero in case of success and %-ENODEV if the background + * thread was killed. + */ +int ubi_bgt_schedule(const struct ubi_info *ubi, struct ubi_bgt_work *wrk); + +/** + * ubi_bgt_reschedule - re-schedule a work. + * + * @ubi: the UBI device description object + * @wrk: the work to re-schedule. + * + * This function enqueues a work defined by @wrk to the tail of the pending + * works list. Returns zero in case of success and %-ENODEV if the background + * thread was killed. + */ +int ubi_bgt_reschedule(const struct ubi_info *ubi, struct ubi_bgt_work *wrk); + +/** + * ubi_bgt_next_work - get the next pending work. + * + * @ubi: the UBI device description object + * + * This function dequeues a work form the head of the pending works queue and + * returns a pointer on it. If the queue is empty, %NULL us returned. + */ +struct ubi_bgt_work *ubi_bgt_next_work(const struct ubi_info *ubi); + +/** + * ubi_bgt_enable - enable the background thread. + * + * @ubi: the UBI device description object + * + * This function enables the background thread for UBI device defined by @ubi. + * Returns zero in case of success and %-ENODEV if the background thread was + * killed. + */ +int ubi_bgt_enable(const struct ubi_info *ubi); + +/** + * ubi_bgt_disable - disable the background thread. + * + * @ubi: the UBI device description object + */ +void ubi_bgt_disable(const struct ubi_info *ubi); + +/** + * ubi_bgt_kill_thread - kill the background thread. + * + * @ubi: the UBI device description object + * + * This function kills the background thread for UBI device defined by @ubi. + * This function also makes sure all the pending tasks are done. + */ +void ubi_bgt_kill_thread(const struct ubi_info *ubi); + +/** + * ubi_bgt_init - initialize the background thread unit for an UBI device. + * + * @ubi: the UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int __init ubi_bgt_init(struct ubi_info *ubi); + +/** + * ubi_bgt_close - close the background thread unit for an UBI device. + * + * @ubi: the UBI device description object + */ +void __exit ubi_bgt_close(struct ubi_info *ubi); + +/** + * ubi_bgt_worker_t - background worker function prototype. + * + * @ubi: the UBI device description object + * @wrk: the work object pointer + * @cancel: non-zero if the work has to be canceled + * + * The @cancel argument is not zero when the background thread is being killed + * and just wants the worker to free everything associated with this work + * (@wrk). + */ +typedef int ubi_bgt_worker_t(const struct ubi_info *ubi, + struct ubi_bgt_work *wrk, int cancel); + +/** + * struct ubi_bgt_work - a background work. + * + * @list: a link in the list of pending works + * @func: the worker function + * @priv: private data of the worker function + * + * To schedule a background work users have to construct a + * &struct ubi_bgt_work object, initialize the @func and @priv fields and call + * 'ubi_bgt_schedule()'. + */ +struct ubi_bgt_work { + struct list_head list; + ubi_bgt_worker_t *func; + void *priv; +}; + +/** + * struct ubi_bgt_work - UBI background thread unit description data structure. + * + * @pending_works: the list of pending works + * @active_work: the work which is currently running + * @pending_works_count: count of pending works + * @lock: protects the @pending_works, @active_work, @enabled, and @task fields + * @enabled: if the background thread is enabled + * @task: a pointer to the &struct task_struct of the background thread + * @bgt_name: the background thread name + * @thread_start: used to synchronize with starting of the background thread + * @thread_stop: used to synchronize with killing of the background thread + */ +struct ubi_bgt_info { + struct list_head pending_works; /* private */ + struct ubi_bgt_work *active_work; /* private */ + int pending_works_count; /* public */ + spinlock_t lock; /* private */ + int enabled; /* public */ + struct task_struct *task; /* private */ + char *bgt_name; /* public */ + struct completion thread_start; /* private */ + struct completion thread_stop; /* private */ +}; + +#endif /* !__UBI_BACKGROUND_H__ */ diff --git a/drivers/mtd/ubi/badeb.c b/drivers/mtd/ubi/badeb.c new file mode 100644 index 0000000..117acce --- /dev/null +++ b/drivers/mtd/ubi/badeb.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include "ubi.h" +#include "alloc.h" +#include "badeb.h" +#include "debug.h" +#include "io.h" +#include "eba.h" +#include "account.h" +#include "wl.h" +#include "misc.h" + +/* The lowest number PEBs reserved for bad PEB handling */ +#define MIN_RESEVED_PEBS 1 + +static void calculate_reserved_max(const struct ubi_info *ubi); + +int ubi_beb_mark_bad(const struct ubi_info *ubi, int pnum) +{ + int err; + struct ubi_beb_info *beb = ubi->beb; + + ubi_assert(ubi->io->bad_allowed); + + ubi_msg("PEB %d wend bad, mark it as bad", pnum); + + err = ubi_io_mark_bad(ubi, pnum); + if (err) + return err; + + spin_lock(&beb->lock); + if (beb->reserved_pebs <= 0) + ubi_warn("no reserved physical eraseblocks!"); + ubi->io->bad_peb_count += 1; + ubi->io->good_peb_count -= 1; + calculate_reserved_max(ubi); + beb->reserved_pebs -= 1; + if (beb->reserved_pebs < beb->reserved_max && !ubi_acc_reserve(ubi, 1)) + beb->reserved_pebs += 1; + spin_unlock(&beb->lock); + + return err; +} + +void ubi_beb_maintain_reserved(const struct ubi_info *ubi) +{ + int err, i, needed; + struct ubi_beb_info *beb = ubi->beb; + + if (!ubi->io->bad_allowed) + return; + + spin_lock(&beb->lock); + needed = beb->reserved_max - beb->reserved_pebs; + for (i = needed; i > 0; i--) { + err = ubi_acc_reserve(ubi, i); + if (!err) { + ubi_msg("reserved %d PEBs for bad PEB handling", i); + beb->reserved_pebs += i; + break; + } + } + spin_unlock(&beb->lock); +} + +int ubi_beb_recover_peb(const struct ubi_info *ubi, int pnum, int vol_id, + int lnum, const void *buf, int offset, int len) +{ + int err, new_pnum, data_size, read, tries = 0; + struct ubi_vid_hdr *vid_hdr; + unsigned char *new_buf; + + ubi_assert(ubi->io->bad_allowed); + +retry: + new_pnum = ubi_wl_get_peb(ubi, UBI_DATA_UNKNOWN); + if (new_pnum < 0) + return new_pnum; + + ubi_msg("recover PEB %d, move its data to PEB %d", pnum, new_pnum); + + /* At first recover the VID header */ + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) { + err = -ENOMEM; + goto out_put; + } + + err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1); + if (err && err != UBI_IO_BITFLIPS) { + if (err > 0) + err = -EIO; + goto out_vid_hdr; + } + + vid_hdr->leb_ver = cpu_to_ubi32(ubi32_to_cpu(vid_hdr->leb_ver) + 1); + err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr); + if (err) + goto vid_write_error; + + /* Now recover the data */ + + data_size = offset + len; + new_buf = ubi_kmalloc(data_size); + if (unlikely(!new_buf)) { + err = -ENOMEM; + goto out_vid_hdr; + } + memset(new_buf + offset, 0xFF, len); + + /* Read everything before the area where the write failure happened */ + if (offset > 0) { + err = ubi_io_read_data(ubi, new_buf, pnum, 0, offset, + &read); + if (err && err != UBI_IO_BITFLIPS) + goto out_new_buf; + } + + /* + * Now we assume that before the failed write the (offset, offset+len) + * area contained all 0xFF bytes. This is true for NAND. This is not + * always true for NOR, but NOR don't admit of bad PEBs. + */ + memcpy(new_buf + offset, buf, len); + + err = ubi_io_write_data(ubi, new_buf, new_pnum, 0, data_size, &read); + if (err) + goto vid_data_write_error; + + ubi_kfree(new_buf); + ubi_free_vid_hdr(ubi, vid_hdr); + ubi_eba_leb_remap(ubi, vol_id, lnum, new_pnum); + ubi_wl_put_peb(ubi, pnum, 1); + ubi_msg("data was successfully recovered"); + return 0; + +out_new_buf: + ubi_kfree(new_buf); +out_vid_hdr: + ubi_free_vid_hdr(ubi, vid_hdr); +out_put: + ubi_wl_put_peb(ubi, new_pnum, 1); + return err; + +vid_data_write_error: + ubi_kfree(new_buf); +vid_write_error: + /* + * Bad luck? This physical eraseblock is bad too? Crud. Let's try to + * get another one. + */ + ubi_warn("failed to write to PEB %d", new_pnum); + ubi_free_vid_hdr(ubi, vid_hdr); + ubi_wl_put_peb(ubi, new_pnum, 1); + if (++tries > 5) + /* We've tried too many times */ + return err; + ubi_msg("try again"); + goto retry; +} + +int __init ubi_beb_init(struct ubi_info *ubi) +{ + int i; + struct ubi_beb_info *beb; + const struct ubi_io_info *io = ubi->io; + + beb = ubi_kzalloc(sizeof(struct ubi_beb_info)); + if (!beb) + return -ENOMEM; + ubi->beb = beb; + + spin_lock_init(&beb->lock); + + if (!io->bad_allowed) + return 0; + + calculate_reserved_max(ubi); + + for (i = beb->reserved_max; i > 0; i--) + if (!ubi_acc_reserve(ubi, i)) { + beb->reserved_pebs = i; + break; + } + + if (beb->reserved_pebs < beb->reserved_max) + /* No enough free physical eraseblocks */ + ubi_warn("cannot reserve enough PEBs"); + + return 0; +} + +void __exit ubi_beb_close(struct ubi_info *ubi) +{ + ubi_kfree(ubi->beb); +} + +/** + * calculate_reserved_max - calculate how many PEBs must be reserved for bad + * eraseblock handling. + * + * @ubi: the UBI device description object + */ +static void calculate_reserved_max(const struct ubi_info *ubi) +{ + struct ubi_beb_info *beb = ubi->beb; + + /* Reserve some amount of PEBs for bad PEB handling */ + beb->reserved_max = ubi->io->good_peb_count/100; + beb->reserved_max *= CONFIG_MTD_UBI_BEB_RESERVE; + if (beb->reserved_max < MIN_RESEVED_PEBS) + beb->reserved_max = MIN_RESEVED_PEBS; +} diff --git a/drivers/mtd/ubi/badeb.h b/drivers/mtd/ubi/badeb.h new file mode 100644 index 0000000..6fb8c3c --- /dev/null +++ b/drivers/mtd/ubi/badeb.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * Bad eraseblock handling unit. + * + * This unit is responsible for marking physical eraseblocks as bad and for + * recovering data from supposedly bad physical eraseblocks. + */ + +#ifndef __UBI_BADEB_H__ +#define __UBI_BADEB_H__ + +#include +#include + +/** + * ubi_beb_mark_bad - mark a physical eraseblock as bad. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock number to mark + * + * This function returns error in case of success and a negative error code in + * case of failure. + */ +int ubi_beb_mark_bad(const struct ubi_info *ubi, int pnum); + +/** + * ubi_beb_maintain_reserved - maintain a certain level of reserved physical + * eraseblock. + * + * @ubi: the UBI device description object + * + * This function tries to maintain a fixed number of reserved physical + * eraseblocks for bad eraseblock handling. This number is configurable. + */ +void ubi_beb_maintain_reserved(const struct ubi_info *ubi); + +/** + * ubi_beb_recover_peb - recover a physical eraseblock after a write failure. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock to recover + * @vol_id: volume ID this LEB belongs to + * @lnum: logical eraseblock number + * @buf: the data which was not be written because of a write failure + * @offset: offset of the failed write + * @len: how many bytes are should have been written + * + * This function has to be called in case of a write failure to move all the + * good data foam the potentially bad physical eraseblock to a good physical + * eraseblock. This function also writes the data which was not written due to + * the failure. This function returns the new physical eraseblock number in + * case of success, and a negative error code in case of failure. + */ +int ubi_beb_recover_peb(const struct ubi_info *ubi, int pnum, int vol_id, + int lnum, const void *buf, int offset, int len); + +/** + * ubi_beb_init - initialize the bad eraseblock handling unit. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * + * This function returns zero in case of success, and a negative error code in + * case of failure. + */ +int __init ubi_beb_init(struct ubi_info *ubi); + +/** + * ubi_beb_close - close the bad eraseblock handling unit. + * + * @ubi: the UBI device description object + */ +void __exit ubi_beb_close(struct ubi_info *ubi); + +/** + * struct ubi_beb_info - UBI bad PEB handling unit description data structure. + * + * @reserved_pebs: how many physical eraseblocks are reserved for bad PEB + * handling + * @reserved_max: how many PEBs have to be reserved for bad PEB handling, i.e., + * the normal level of reserved PEBs + * @lock: protects @reserved_pebs + */ +struct ubi_beb_info { + int reserved_pebs; /* public */ + int reserved_max; /* public */ + spinlock_t lock; /* private */ +}; + +#endif /* !__UBI_BADEB_H__ */ diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c new file mode 100644 index 0000000..35d1bb4 --- /dev/null +++ b/drivers/mtd/ubi/build.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include "ubi.h" +#include "alloc.h" +#include "io.h" +#include "wl.h" +#include "volmgmt.h" +#include "account.h" +#include "background.h" +#include "vtbl.h" +#include "eba.h" +#include "build.h" +#include "uif.h" +#include "scan.h" +#include "badeb.h" +#include "misc.h" +#include "debug.h" + +static int __init attach_by_scanning(struct ubi_info *ubi); + +int __init ubi_bld_attach_mtd_dev(struct ubi_info *ubi, int mtd_num, + int vid_hdr_offset, int data_offset) +{ + int err; + const struct ubi_io_info *io; + const struct ubi_acc_info *acc; + + dbg_bld("attaching mtd%d to ubi%d", mtd_num, ubi->ubi_num); + + err = ubi_io_init(ubi, mtd_num, vid_hdr_offset, data_offset); + if (err) { + dbg_err("failed to initialize I/O unit, error %d", err); + return err; + } + + err = ubi_bgt_init(ubi); + if (err) { + dbg_err("failed to initialize background thread unit, error %d", + err); + goto out_io; + } + + err = attach_by_scanning(ubi); + if (err) { + dbg_err("failed to attach MTD device, error %d", err); + goto out_bgt; + } + + err = ubi_beb_init(ubi); + if (err) { + dbg_err("failed to initialize bad eraseblock handling unit, " + "error %d", err); + goto out_detach; + } + + err = ubi_uif_init(ubi); + if (err) { + dbg_err("failed to initialize user interfaces unit for UBI " + "device %d, error %d", ubi->ubi_num, err); + goto out_beb; + } + + io = ubi->io; + acc = ubi->acc; + + ubi_msg("attached mtd%d to ubi%d", mtd_num, ubi->ubi_num); + ubi_msg("MTD device name: \"%s\"", io->mtd_name); + ubi_msg("MTD device size: %llu MiB", io->flash_size >> 20); + ubi_msg("physical eraseblock size: %d bytes (%d KiB)", + io->peb_size, io->peb_size >> 10); + ubi_msg("logical eraseblock size: %d bytes", io->leb_size); + ubi_msg("number of good PEBs: %d", io->good_peb_count); + ubi_msg("number of bad PEBs: %d", io->bad_peb_count); + ubi_msg("smallest flash I/O unit: %d", io->min_io_size); + ubi_msg("VID header offset: %d (aligned %d)", + io->vid_hdr_offset, io->vid_hdr_aloffset); + ubi_msg("data offset: %d", io->leb_start); + ubi_msg("max. allowed volumes: %d", acc->max_volumes); + ubi_msg("wear-levelling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD); + ubi_msg("number of internal volumes: %d", acc->ivol_count); + ubi_msg("number of user volumes: %d", acc->uvol_count); + ubi_msg("available PEBs: %d", acc->avail_pebs); + ubi_msg("total number of reserved PEBs: %d", acc->rsvd_pebs); + ubi_msg("number of PEBs reserved for bad PEB handling: %d", + ubi->beb->reserved_pebs); + + if (!io->ro_mode) + ubi_bgt_enable(ubi); + return 0; + +out_beb: + ubi_beb_close(ubi); +out_detach: + ubi_eba_close(ubi); + ubi_wl_close(ubi); + ubi_vmt_close(ubi); +out_bgt: + ubi_bgt_kill_thread(ubi); + ubi_bgt_close(ubi); +out_io: + ubi_io_close(ubi); + return err; +} + +void __exit ubi_bld_detach_mtd_dev(struct ubi_info *ubi) +{ + int mtd_num = ubi->io->mtd_num, ubi_num = ubi->ubi_num; + + dbg_bld("detaching mtd%d from ubi%d", mtd_num, ubi_num); + + ubi_bgt_kill_thread(ubi); + ubi_uif_close(ubi); + ubi_beb_close(ubi); + ubi_eba_close(ubi); + ubi_wl_close(ubi); + ubi_vmt_close(ubi); + ubi_bgt_close(ubi); + ubi_io_close(ubi); + ubi_msg("detached mtd%d from ubi%d", mtd_num, ubi_num); +} + +/** + * attach_by_scanning - attach a 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. + */ +static int __init attach_by_scanning(struct ubi_info *ubi) +{ + int err; + struct ubi_scan_info *si; + + dbg_bld("attach mtd device by scanning"); + + err = ubi_scan(ubi, &si); + if (err) + return err; + + err = ubi_vmt_init_scan(ubi, si); + if (err) + goto out_si; + + err = ubi_wl_init_scan(ubi, si); + if (err) + goto out_vmt; + + err = ubi_eba_init_scan(ubi, si); + if (err) + goto out_wl; + + ubi_msg("mean erase counter: %d", si->mean_ec); + ubi_scan_destroy_si(si); + return 0; + +out_wl: + ubi_wl_close(ubi); +out_vmt: + ubi_vmt_close(ubi); +out_si: + ubi_scan_destroy_si(si); + return err; +} diff --git a/drivers/mtd/ubi/build.h b/drivers/mtd/ubi/build.h new file mode 100644 index 0000000..a3df6bc --- /dev/null +++ b/drivers/mtd/ubi/build.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * UBI build unit. + * + * This unit is responsible for attaching MTD devices to UBI devices. At the + * moment there is only one attachment method exists - full scan. But in future + * one may add a superblock-based attachment method and improve UBI + * scalability. But anyways, the scanning method will always be useful because + * in case of superblock corruptions UBI can always scan the device and + * re-build all the core data structures. + */ + +#ifndef __UBI_BUILD_H__ +#define __UBI_BUILD_H__ + +#include + +struct ubi_info; + +/** + * ubi_bld_attach_mtd_dev - attach an MTD device. + * + * @ubi: the UBI device description object + * @mtd_num: MTD device number + * @vid_hdr_offset: volume identifier headers offset + * @data_offset: data offset + * + * This function attaches an MTD device number @mtd_num. If @vid_hdr_offset and + * @data_offset are zero, the default layout of UBI headers is assumed. See the + * I/O unit. + * + * This function returns a positive number of the new UBI device in case of + * success and a negative error code in case of failure. + */ +int __init ubi_bld_attach_mtd_dev(struct ubi_info *ubi, int mtd_num, + int vid_hdr_offset, int data_offset); + +/** + * ubi_bld_detach_mtd_dev - detach an MTD device. + * + * @ubi: the UBI device description object + */ +void __exit ubi_bld_detach_mtd_dev(struct ubi_info *ubi); + +#endif /* __UBI_BUILD_H__ */ diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c new file mode 100644 index 0000000..469cc47 --- /dev/null +++ b/drivers/mtd/ubi/cdev.c @@ -0,0 +1,1212 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ubi.h" +#include "uif.h" +#include "eba.h" +#include "cdev.h" +#include "account.h" +#include "debug.h" +#include "alloc.h" +#include "vtbl.h" +#include "io.h" +#include "dtbl.h" +#include "volmgmt.h" +#include "misc.h" + +static int vol_cdev_open(struct inode *inode, struct file *file); +static int vol_cdev_release(struct inode *inode, struct file *file); +loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin); +static ssize_t vol_cdev_read(struct file *file, __user char *buf, + size_t count, loff_t * offp); +static ssize_t vol_cdev_write(struct file *file, const char __user *buf, + size_t count, loff_t *offp); +static int vol_cdev_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +#ifdef CONFIG_MTD_UBI_USERSPACE_IO +static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, + size_t count, loff_t *offp); +#else +#define vol_cdev_direct_write(file, buf, count, offp) -EROFS +#endif + +/* Volume character device operations */ +static struct file_operations vol_cdev_operations = { + .owner = THIS_MODULE, + .open = vol_cdev_open, + .release = vol_cdev_release, + .llseek = vol_cdev_llseek, + .read = vol_cdev_read, + .write = vol_cdev_write, + .ioctl = vol_cdev_ioctl +}; + +int ubi_cdev_vol_init(const struct ubi_info *ubi, struct ubi_uif_volume *vol) +{ + int err; + const struct ubi_uif_info *uif = ubi->uif; + + cdev_init(&vol->cdev, &vol_cdev_operations); + vol->cdev.owner = THIS_MODULE; + err = cdev_add(&vol->cdev, MKDEV(uif->major, vol->vol_id + 1), 1); + if (err) { + ubi_err("cannot add char dev for volume %d", vol->vol_id); + goto out; + } + + return 0; +out: + cdev_del(&vol->cdev); + return err; +} + +void ubi_cdev_vol_close(struct ubi_uif_volume *vol) +{ + cdev_del(&vol->cdev); +} + +static int ubi_cdev_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +/* UBI character device operations */ +static struct file_operations ubi_cdev_operations = { + .owner = THIS_MODULE, + .ioctl = ubi_cdev_ioctl, + .llseek = no_llseek +}; + +int __init ubi_cdev_init(struct ubi_info *ubi) +{ + int err; + dev_t dev; + struct ubi_uif_info *uif = ubi->uif; + const struct ubi_acc_info *acc = ubi->acc; + + /* + * Major numbers for the UBI character devices are allocated + * dynamically. Major numbers of volume character devices are + * equivalent to ones of the corresponding UBI character device. Minor + * numbers of UBI character devices are 0, while minor numbers of + * volume character devices start from 1. Thus, we allocate one major + * number and acc->max_volumes + 1 minor numbers. + */ + err = alloc_chrdev_region(&dev, 0, acc->max_volumes + 1, uif->ubi_name); + if (err) { + ubi_err("cannot register UBI char devs"); + return err; + } + + cdev_init(&uif->cdev, &ubi_cdev_operations); + uif->major = MAJOR(dev); + uif->cdev.owner = THIS_MODULE; + + dev = MKDEV(uif->major, 0); + err = cdev_add(&uif->cdev, dev, 1); + if (err) { + ubi_err("cannot add char dev %s", uif->ubi_name); + goto out_unreg; + } + + dbg_cdev("%s major:minor is %u:0", uif->ubi_name, uif->major); + return 0; + +out_unreg: + unregister_chrdev_region(MKDEV(uif->major, 0), acc->max_volumes + 1); + return err; + +} + +void __exit ubi_cdev_close(const struct ubi_info *ubi) +{ + struct ubi_uif_info *uif = ubi->uif; + const struct ubi_acc_info *acc = ubi->acc; + + cdev_del(&uif->cdev); + unregister_chrdev_region(MKDEV(uif->major, 0), acc->max_volumes + 1); +} + +static struct ubi_info *major2ubi_info(int major); + +/** + * vol_cdev_open - open method of volume character devices. + * + * @inode: inode of the volume character device + * @file: &struct file object of the volume character device + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int vol_cdev_open(struct inode *inode, struct file *file) +{ + struct ubi_vol_desc *desc; + const struct ubi_info *ubi = major2ubi_info(imajor(inode)); + int vol_id = iminor(inode) - 1; + int mode; + + if (file->f_mode & FMODE_WRITE) + mode = UBI_READWRITE; + else + mode = UBI_READONLY; + + desc = ubi_open_volume(ubi->ubi_num, vol_id, mode); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + file->private_data = desc; + return 0; +} + +/** + * vol_cdev_release - release method of volume character devices. + * + * @inode: inode of the volume character device + * @file: &struct file object of the volume character device + */ +static int vol_cdev_release(struct inode *inode, struct file *file) +{ + struct ubi_vol_desc *desc = file->private_data; + + ubi_close_volume(desc); + return 0; +} + +/** + * vol_cdev_llseek - llseek method of volume character devices. + * + * @file: &struct file object of the volume character device + * @offset: file offset + * @origin: defines the starting point of the @offset + * + * If an update is in progress, seeking is prohibited. This function returns + * positive offset in case of success and a negative error code in case of + * failure. + */ +loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin) +{ + const struct ubi_dtbl_dtr *dtr; + struct ubi_vol_desc *desc = file->private_data; + const struct ubi_info *ubi = desc->vol->ubi; + struct ubi_uif_volume *vol = desc->vol; + loff_t new_offset; + + if (vol->updating) { + dbg_cdev("updating"); + return -EBUSY; + } + + dtr = ubi_dtbl_get_dtr(ubi, vol->vol_id); + + switch (origin) { + case 0: /* SEEK_SET */ + new_offset = offset; + break; + case 1: /* SEEK_CUR */ + new_offset = file->f_pos + offset; + break; + case 2: /* SEEK_END */ + new_offset = dtr->used_bytes + offset; + break; + default: + return -EINVAL; + } + + if (new_offset < 0 || new_offset > dtr->used_bytes) { + dbg_err("bad seek (%lld)", new_offset); + return -EINVAL; + } + + dbg_cdev("set volume %d offset at %lld", vol->vol_id, new_offset); + file->f_pos = new_offset; + return new_offset; +} + +/** + * vol_cdev_read - read method of volume character devices + * + * @file: &struct file object of volume character device + * @buf: user-space buffer where to put read data + * @count: how many bytes to read + * @offp: the read position + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static ssize_t vol_cdev_read(struct file *file, __user char *buf, + size_t count, loff_t * offp) +{ + int err; + const struct ubi_dtbl_dtr *dtr; + const struct ubi_vtbl_vtr *vtr; + struct ubi_vol_desc *desc = file->private_data; + struct ubi_uif_volume *vol = desc->vol; + const struct ubi_info *ubi = vol->ubi; + const struct ubi_io_info *io = ubi->io; + int lnum, off, len, vol_id = desc->vol->vol_id; + size_t tbuf_size, count_save = count; + void *tbuf; + uint64_t tmp; + + dbg_cdev("request: read %zd bytes from offset %lld of volume %u", + count, *offp, desc->vol->vol_id); + + if (unlikely(count == 0)) + return 0; + + if (vol->updating) { + dbg_err("updating"); + return -EBUSY; + } + + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + dtr = ubi_dtbl_get_dtr(ubi, vol_id); + ubi_assert(!IS_ERR(vtr)); + ubi_assert(!IS_ERR(dtr)); + ubi_assert(*offp >= 0 && *offp <= dtr->used_bytes); + + if (*offp == dtr->used_bytes) + return 0; + + if (*offp + count > dtr->used_bytes) + count_save = count = dtr->used_bytes - *offp; + + /* + * To optimize reading, we read in fractions of the minimum + * input/output units of the flash. + */ + tbuf_size = (PAGE_SIZE / io->min_io_size) * io->min_io_size; + if (tbuf_size == 0) + tbuf_size = io->min_io_size; + if (tbuf_size > io->leb_size) + tbuf_size = io->leb_size; + + tbuf = ubi_kmalloc(tbuf_size); + if (!tbuf) + return -ENOMEM; + + /* + * We read in portions of the minimal flash input/output unit. If we are + * requested to read form a non-aligned offset, we first read up to the + * nearest boundary, and later only read in units of 'tbuf_size'. + */ + if (count > tbuf_size) { + int rem; + + tmp = *offp; + rem = do_div(tmp, io->min_io_size); + if (rem == 0) + len = tbuf_size; + else + len = io->min_io_size - rem; + } else + len = count; + + tmp = *offp; + off = do_div(tmp, vtr->usable_leb_size); + lnum = tmp; + + do { + int read; + + cond_resched(); + dbg_cdev("read %d bytes from LEB %d, offset %d", len, lnum, off); + + if (off + len >= vtr->usable_leb_size) + len = vtr->usable_leb_size - off; + + err = ubi_eba_read_leb(ubi, vol_id, lnum, tbuf, off, len, 0, + &read); + if (unlikely(err)) { + int err1; + + err1 = copy_to_user(buf, tbuf, read); + if (unlikely(err1)) { + dbg_err("memory access error"); + err = err1; + break; + } + + dbg_err("%d bytes were read, but with error %d", + err, read); + count -= read; + *offp += read; + break; + } + + off += len; + if (off == vtr->usable_leb_size) { + lnum += 1; + off -= vtr->usable_leb_size; + } + + count -= len; + *offp += len; + + err = copy_to_user(buf, tbuf, len); + if (unlikely(err)) { + dbg_err("memory access error"); + break; + } + + buf += len; + len = count > tbuf_size ? tbuf_size : count; + } while (count); + + ubi_kfree(tbuf); + return err ? err : count_save - count; +} + +static int update_leb(const struct ubi_info *ubi, int vol_id, int lnum, + void *buf, int len, int used_ebs, int *written); + +/** + * vol_cdev_write - write method of volume character devices + * + * @file: &struct file object of volume character device + * @buf: user-space buffer with the data to write + * @count: how many bytes to write + * @offp: the write position + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static ssize_t vol_cdev_write(struct file *file, const char __user *buf, + size_t count, loff_t *offp) +{ + const struct ubi_dtbl_dtr *dtr; + const struct ubi_vtbl_vtr *vtr; + struct ubi_vol_desc *desc = file->private_data; + struct ubi_uif_volume *vol = desc->vol; + const struct ubi_info *ubi = vol->ubi; + int lnum, off, len, vol_id = vol->vol_id, written, err = 0; + size_t count_save = count; + uint64_t tmp; + + dbg_cdev("requested: write %zd bytes to offset %lld of volume %u", + count, *offp, desc->vol->vol_id); + + if (unlikely(count == 0)) + return 0; + + if (!vol->updating) + return vol_cdev_direct_write(file, buf, count, offp); + + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + dtr = ubi_dtbl_get_dtr(ubi, vol_id); + + ubi_assert(!IS_ERR(vtr)); + ubi_assert(!IS_ERR(dtr)); + ubi_assert(vol->upd_received == *offp); + ubi_assert(!dtr->corrupted); + ubi_assert(*offp >= 0 && *offp <= vol->upd_bytes); + + tmp = *offp; + off = do_div(tmp, vtr->usable_leb_size); + lnum = tmp; + + if (*offp + count > vol->upd_bytes) + count_save = count = vol->upd_bytes - *offp; + + /* + * When updating volumes, we accumulate whole eraseblocks and write + * them at once. + */ + + if (off != 0) { + + /* + * This is a write to the middle of the logical eraseblock. We + * copy the data to our update buffer and wait for more data or + * flush it (if the whole eraseblock is written to or the + * update is finished). + */ + + len = vtr->usable_leb_size - off; + if (len > count) + len = count; + + dbg_cdev("copy more %d bytes of user data", len); + + err = copy_from_user(vol->upd_buf + off, buf, len); + if (err) { + dbg_err("memory access error"); + return -EFAULT; + } + + if (off + len == vtr->usable_leb_size || + vol->upd_received + len == vol->upd_bytes) { + int flush_len = off + len; + + /* + * OK, we gathered either the whole eraseblock or this + * is the last chunk, it's time to flush our buffer. + */ + + dbg_cdev("flush %d bytes to LEB %d:%d", + flush_len, vol_id, lnum); + ubi_assert(flush_len <= vtr->usable_leb_size); + + err = update_leb(ubi, vol_id, lnum, vol->upd_buf, flush_len, + vol->upd_ebs, &written); + if (err) { + *offp += written; + count -= written; + vol->upd_received += written; + goto finish; + } + } + + *offp += len; + count -= len; + vol->upd_received += len; + buf += len; + lnum += 1; + } + + /* + * If we've got more to write, let's continue. At this point we know we + * are starting from the beginning of an eraseblock. + */ + + while (count) { + cond_resched(); + + if (count > vtr->usable_leb_size) + len = vtr->usable_leb_size; + else + len = count; + + dbg_cdev("copy %d bytes of user data", len); + err = copy_from_user(vol->upd_buf, buf, len); + if (err) { + dbg_err("memory access error"); + err = -EFAULT; + break; + } + + if (len == vtr->usable_leb_size || + vol->upd_received + len == vol->upd_bytes) { + dbg_cdev("write %d bytes to LEB %d:%d", + len, vol_id, lnum); + + err = update_leb(ubi, vol_id, lnum, vol->upd_buf, len, + vol->upd_ebs, &written); + if (unlikely(err)) { + count -= written; + *offp += written; + vol->upd_received += written; + break; + } + } + + count -= len; + *offp += len; + vol->upd_received += len; + lnum += 1; + buf += len; + } + +finish: + ubi_assert(vol->upd_received <= vol->upd_bytes); + if (vol->upd_received == vol->upd_bytes) + err = ubi_uif_finish_update(desc); + + return err ? err : count_save - count; +} + +/** + * vol_cdev_ioctl - ioctl method of volume character devices. + * + * @inode: inode of the volume character device + * @file: &struct file object of the volume character device + * @cmd: ioctl command + * @arg: ioctl arguments + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int vol_cdev_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct ubi_vol_desc *desc = file->private_data; + const struct ubi_info *ubi = desc->vol->ubi; + void __user *argp = (void __user *)arg; + + if (_IOC_NR(cmd) > VOL_CDEV_IOC_MAX_SEQ || + _IOC_TYPE(cmd) != UBI_VOL_IOC_MAGIC) { + dbg_err("bad ioctl command"); + return -ENOTTY; + } + + if (_IOC_DIR(cmd) && _IOC_READ) + err = !access_ok(VERIFY_WRITE, argp, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) && _IOC_WRITE) + err = !access_ok(VERIFY_READ, argp, _IOC_SIZE(cmd)); + if (err) { + dbg_err("memory access error"); + return -EFAULT; + } + + switch (cmd) { + + /* Volume update command */ + case UBI_IOCVOLUP: + { + int64_t bytes; + long long rsvd_bytes; + const struct ubi_vtbl_vtr *vtr; + + if (!capable(CAP_SYS_RESOURCE)) { + dbg_err("no rights"); + err = -EPERM; + break; + } + + err = copy_from_user(&bytes, argp, sizeof(int64_t)); + if (err) { + dbg_err("memory access error"); + err = -EFAULT; + break; + } + + dbg_cdev("update volume %u, %lld bytes", + desc->vol->vol_id, bytes); + + if (desc->mode == UBI_READONLY) { + dbg_err("read-only mode"); + err = -EROFS; + break; + } + + vtr = ubi_vtbl_get_vtr(ubi, desc->vol->vol_id); + rsvd_bytes = vtr->reserved_pebs * + (ubi->io->leb_size - vtr->data_pad); + if (bytes < 0 || bytes > rsvd_bytes) { + dbg_err("bad data size %lld", bytes); + err = -EINVAL; + break; + } + + err = ubi_uif_start_update(desc, bytes); + file->f_pos = 0; + break; + } + +#ifdef CONFIG_MTD_UBI_USERSPACE_IO + /* An eraseblock erasure command */ + case UBI_IOCEBER: + { + int32_t lnum; + const struct ubi_vtbl_vtr *vtr; + + err = __get_user(lnum, (__user int32_t *)argp); + if (err) { + dbg_err("memory access error"); + err = -EFAULT; + break; + } + + if (desc->mode == UBI_READONLY) { + dbg_err("read-only mode"); + err = -EROFS; + break; + } + + vtr = ubi_vtbl_get_vtr(ubi, desc->vol->vol_id); + ubi_assert(!IS_ERR(vtr)); + if (lnum < 0 || lnum >= vtr->reserved_pebs) { + dbg_err("bad lnum %d", lnum); + err = -EINVAL; + break; + } + + if (vtr->vol_type != UBI_DYNAMIC_VOLUME) { + dbg_err("static volume"); + err = -EROFS; + break; + } + + dbg_cdev("erase LEB %d:%d", desc->vol->vol_id, lnum); + err = ubi_eba_erase_leb(ubi, desc->vol->vol_id, lnum); + break; + } +#endif + + default: + err = -ENOTTY; + break; + } + + return err; +} + +static int check_mkvol_req(const struct ubi_info *ubi, + const struct ubi_mkvol_req *req); + +static int check_rsvol_req(const struct ubi_info *ubi, + const struct ubi_rsvol_req *req); + +/** + * ubi_cdev_ioctl - ioctl method of UBI character devices. + * + * @inode: inode of the UBI character device + * @file: &struct file object of the UBI character device + * @cmd: ioctl command + * @arg: ioctl arguments + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int ubi_cdev_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + const struct ubi_info *ubi; + void __user *argp = (void __user *)arg; + + if (_IOC_NR(cmd) > UBI_CDEV_IOC_MAX_SEQ || + _IOC_TYPE(cmd) != UBI_IOC_MAGIC) { + dbg_err("bad ioctl command"); + return -ENOTTY; + } + + if (_IOC_DIR(cmd) && _IOC_READ) + err = !access_ok(VERIFY_WRITE, argp, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) && _IOC_WRITE) + err = !access_ok(VERIFY_READ, argp, _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + + if (!capable(CAP_SYS_RESOURCE)) { + dbg_err("no rights"); + return -EPERM; + } + + ubi = major2ubi_info(imajor(inode)); + if (IS_ERR(ubi)) + return PTR_ERR(ubi); + + switch (cmd) { + + /* Create volume command */ + case UBI_IOCMKVOL: + { + char *name; + struct ubi_vtbl_vtr vtr; + struct ubi_mkvol_req req; + const struct ubi_io_info *io = ubi->io; + int pebs; + + err = __copy_from_user(&req, argp, + sizeof(struct ubi_mkvol_req)); + if (err) { + err = -EFAULT; + break; + } + + /* Make sure that user passed us sane data */ + pebs = check_mkvol_req(ubi, &req); + if (pebs < 0) { + err = pebs; + break; + } + + name = ubi_kmalloc(req.name_len + 1); + if (!name) { + err = -ENOMEM; + break; + } + + err = copy_from_user(name, req.name, req.name_len + 1); + if (err) { + err = -EFAULT; + goto out_free; + } + + vtr.reserved_pebs = pebs; + vtr.alignment = req.alignment; + vtr.vol_type = req.vol_type; + vtr.name_len = req.name_len; + vtr.name = name; + vtr.data_pad = io->leb_size % vtr.alignment; + + dbg_cdev("create volume ID %d, size %d, type %d, name %s", + req.vol_id, vtr.reserved_pebs, vtr.vol_type, + vtr.name); + + err = ubi_vmt_mkvol(ubi, req.vol_id, &vtr); + if (err < 0) + goto out_free; + + req.vol_id = err; + err = ubi_uif_mkvol(ubi, req.vol_id); + if (err) + goto out_rmvol; + + ubi_kfree(name); + + err = __copy_to_user(argp, &req, + sizeof(struct ubi_mkvol_req)); + if (err) { + err = -EFAULT; + break; + } + + break; + +out_rmvol: + ubi_vmt_rmvol(ubi, req.vol_id); +out_free: + ubi_kfree(name); + break; + } + + /* Remove volume command */ + case UBI_IOCRMVOL: + { + int32_t vol_id; + struct ubi_vol_desc *desc; + + err = __get_user(vol_id, (__user int32_t *)argp); + if (err) { + err = -EFAULT; + break; + } + + dbg_cdev("remove volume %u", vol_id); + + desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_EXCLUSIVE); + if (IS_ERR(desc)) { + err = PTR_ERR(desc); + break; + } + + err = ubi_uif_close_and_rmvol(desc); + if (err) { + ubi_close_volume(desc); + break; + } + + err = ubi_vmt_rmvol(ubi, vol_id); + break; + } + + /* Re-size volume command */ + case UBI_IOCRSVOL: + { + int err, rem, pebs; + uint64_t tmp; + const struct ubi_vtbl_vtr *vtr; + struct ubi_rsvol_req req; + struct ubi_vol_desc *desc; + + err = __copy_from_user(&req, argp, + sizeof(struct ubi_rsvol_req)); + if (err) { + err = -EFAULT; + break; + } + + /* Make sure that user passed us sane data */ + err = check_rsvol_req(ubi, &req); + if (err) + break; + + dbg_cdev("re-size volume %d to size %lld bytes", + req.vol_id, req.bytes); + + desc = ubi_open_volume(ubi->ubi_num, req.vol_id, UBI_EXCLUSIVE); + if (IS_ERR(desc)) { + err = PTR_ERR(desc); + break; + } + + vtr = ubi_vtbl_get_vtr(ubi, req.vol_id); + ubi_assert(!IS_ERR(vtr)); + + tmp = req.bytes; + rem = do_div(tmp, vtr->usable_leb_size); + pebs = tmp; + if (rem) + pebs += 1; + + err = ubi_vmt_rsvol(ubi, req.vol_id, pebs); + ubi_close_volume(desc); + break; + } + + default: + err = -ENOTTY; + break; + } + + return err; +} + +/** + * update_leb - write a portion of update data to a logical eraseblock. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID where to write + * @lnum: the logical eraseblock number to write + * @buf: the data to write + * @len: how many bytes to write + * @used_ebs: how many logical eraseblocks will this volume contain + * @written: how many bytes were actually written + * + * This is a helper function for 'vol_cdev_write()'. This function writes a + * portion of update data to the corresponding logical eraseblock. If a dynamic + * volume is being updated, this function checks if the data contains 0xFF + * bytes at the end. If yes, the 0xFF bytes are not written. If the whole + * buffer contains only 0xFF bytes, the LEB is left unmapped. + * + * 0xFF bytes LEBs are skipped in case of dynamic volumes because writing of + * 0xFF bytes may have side effects and this PEB won't be writable anymore. In + * case of static volume 0xFF bytes are not got rid of because static volumes + * are treated specially in UBI: per-LEB CRC is calculated and checked and + * presence of all LEBs is taken care of. So we cannot cut amount of data + * written to LEBs of static volume or to skip some LEBs in case of static + * volumes. + */ +static int update_leb(const struct ubi_info *ubi, int vol_id, int lnum, + void *buf, int len, int used_ebs, int *written) +{ + int err; + const struct ubi_vtbl_vtr *vtr; + + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + ubi_assert(!IS_ERR(vtr)); + + if (vtr->vol_type == UBI_DYNAMIC_VOLUME) { + int l; + + ubi_assert(len = vtr->usable_leb_size); + + /* This is dynamic volume - skip the ending 0xFFs */ + l = ubi_calc_data_len(ubi, buf, len); + if (l == 0) { + *written = len; + dbg_cdev("LEB %d:%d contains only 0xFF bytes - skip", + vol_id, lnum); + return 0; + } + if (len != l) + dbg_cdev("skip last %d bytes of data for LEB %d:%d", + len - l, vol_id, lnum); + + err = ubi_eba_write_leb(ubi, vol_id, lnum, buf, 0, l, + UBI_DATA_UNKNOWN, written, NULL); + } else { + /* + * When writing to static volumes, and this is the last logical + * eraseblock, the length (@len) does not have to be aligned to + * the minimal flash I/O unit. The 'ubi_eba_write_leb_st()' + * function needs the exact (unaligned) length to store in the + * VID header. And it it will take care about proper alignment + * by padding the buffer. Here we just make sure the padding + * will contain zeros, not random trash. + */ + memset(buf + len, 0, vtr->usable_leb_size - len); + err = ubi_eba_write_leb_st(ubi, vol_id, lnum, buf, len, + UBI_DATA_UNKNOWN, written, used_ebs); + } + + return err; +} + +/** + * major2ubi_info - find the UBI device description by major number of the + * corresponding character device. + * + * @major: major number + * + * This function returns a pointer to the UBI description object. + */ +static struct ubi_info *major2ubi_info(int major) +{ + int i; + + for (i = 0; i < ubis_num; i++) + if (ubis[i] && ubis[i]->uif->major == major) + return ubis[i]; + + BUG(); + return NULL; +} + +/** + * check_mkvol_req - check sanity of a volume creation request. + * + * @ubi: the UBI device description object + * @req: the request to check + * + * This function returns a positive volume size in eraseblocks if the request + * is sane, and %-EINVAL if not. + */ +static int check_mkvol_req(const struct ubi_info *ubi, + const struct ubi_mkvol_req *req) +{ + int n, err, rem, ebs, usable_leb_size; + char *name = NULL; + const struct ubi_io_info *io = ubi->io; + uint64_t bytes; + + name = ubi_kmalloc(req->name_len + 1); + if (!name) + return -ENOMEM; + + if (req->bytes < 0 || req->alignment < 0 || req->vol_type < 0 || + req->name_len < 0) { + dbg_err("negative values"); + goto bad; + } + + if ((req->vol_id < 0 || req->vol_id >= ubi->acc->max_volumes) && + req->vol_id != UBI_VOL_NUM_AUTO) { + dbg_err("bad vol_id %d", req->vol_id); + goto bad; + } + + if (req->alignment == 0) { + dbg_err("zero alignment"); + goto bad; + } + + if (req->bytes == 0) { + dbg_err("zero bytes"); + goto bad; + } + + if (req->vol_type != UBI_DYNAMIC_VOLUME && + req->vol_type != UBI_STATIC_VOLUME) { + dbg_err("bad vol_type"); + goto bad; + } + + if (req->alignment > io->leb_size) { + dbg_err("too large alignment"); + goto bad; + } + + n = req->alignment % io->min_io_size; + if (req->alignment != 1 && n) { + dbg_err("alignment is not multiple of min I/O unit size"); + goto bad; + } + + if (req->name_len > UBI_VOL_NAME_MAX) { + dbg_err("too long volume name, max is %d", UBI_VOL_NAME_MAX); + goto bad; + } + + if (!req->name) { + dbg_err("NULL volume name"); + goto bad; + } + + err = copy_from_user(name, req->name, req->name_len + 1); + if (err) + return err; + + n = strnlen(name, req->name_len + 1); + if (n != req->name_len) { + dbg_err("bad name_len"); + goto bad; + } + + ubi_kfree(name); + + /* Calculate how many eraseblocks are requested */ + usable_leb_size = io->leb_size - io->leb_size % req->alignment; + bytes = req->bytes; + rem = do_div(bytes, usable_leb_size); + ebs = bytes; + if (rem) + ebs += 1; + + return ebs; + +bad: + dbg_err("bad volume creation request"); + ubi_dbg_dump_mkvol_req(req, name); + ubi_kfree(name); + return -EINVAL; +} + +/** + * check_rsvol_req - check sanity of a volume re-size request. + * + * @ubi: the UBI device description object + * @req: the re-size request to check + * + * This function returns zero if the request is sane, and %-EINVAL if not. + */ +static int check_rsvol_req(const struct ubi_info *ubi, + const struct ubi_rsvol_req *req) +{ + if (req->bytes <= 0) { + dbg_err("bad bytes %lld", req->bytes); + return -EINVAL; + } + + if (req->vol_id < 0 || req->vol_id >= ubi->acc->max_volumes) { + dbg_err("bad vol_id %d", req->vol_id); + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_MTD_UBI_USERSPACE_IO + +/** + * vol_cdev_direct_write - write to volume character devices directly (nothing + * to do with O_DIRECT!). + * + * @file: &struct file object of volume character device + * @buf: user-space buffer with the data to write + * @count: how many bytes to write + * @offp: the write position + * + * This function allows to directly write to dynamic UBI volumes, without + * issuing the volume update operation. This function returns zero in case of + * success and a negative error code in case of failure. + */ +static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, + size_t count, loff_t *offp) +{ + int written, err = 0; + const struct ubi_dtbl_dtr *dtr; + const struct ubi_vtbl_vtr *vtr; + struct ubi_vol_desc *desc = file->private_data; + struct ubi_uif_volume *vol = desc->vol; + const struct ubi_info *ubi = vol->ubi; + const struct ubi_io_info *io = ubi->io; + int lnum, off, len, vol_id = vol->vol_id; + size_t tbuf_size, count_save = count; + char *tbuf; + uint64_t tmp; + + dbg_cdev("requested: write %zd bytes to offset %lld of volume %u", + count, *offp, desc->vol->vol_id); + + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + dtr = ubi_dtbl_get_dtr(ubi, vol_id); + + ubi_assert(!IS_ERR(vtr)); + ubi_assert(!IS_ERR(dtr)); + ubi_assert(!vol->updating); + ubi_assert(!dtr->corrupted); + ubi_assert(*offp >= 0 && *offp <= dtr->used_bytes); + + if (vtr->vol_type == UBI_STATIC_VOLUME) { + dbg_err("static volume"); + return -EROFS; + } + + tmp = *offp; + off = do_div(tmp, vtr->usable_leb_size); + lnum = tmp; + + if (off % io->min_io_size) { + dbg_err("unaligned position"); + return -EIO; + } + + if (*offp + count > dtr->used_bytes) + count_save = count = dtr->used_bytes - *offp; + + /* + * We can only write in fractions of the minimum input/output unit of + * the flash. + */ + if (count % io->min_io_size) { + dbg_err("unaligned write length"); + return -EINVAL; + } + + tbuf_size = (PAGE_SIZE / io->min_io_size) * io->min_io_size; + if (tbuf_size == 0) + tbuf_size = io->min_io_size; + if (tbuf_size > io->leb_size) + tbuf_size = io->leb_size; + + tbuf = ubi_kmalloc(tbuf_size); + if (!tbuf) + return -ENOMEM; + + len = count > tbuf_size ? tbuf_size : count; + + while (count) { + cond_resched(); + + if (off + len >= vtr->usable_leb_size) + len = vtr->usable_leb_size - off; + + dbg_cdev("copy %d bytes of user data", len); + err = copy_from_user(tbuf, buf, len); + if (err) { + dbg_err("memory access error"); + err = -EFAULT; + break; + } + + dbg_cdev("write %d bytes to LEB %d:%d, offset %d", + len, vol_id, lnum, off); + + err = ubi_eba_write_leb(ubi, vol_id, lnum, tbuf, off, len, + UBI_DATA_UNKNOWN, &written, NULL); + if (unlikely(err)) { + count -= written; + *offp += written; + break; + } + + off += len; + if (off == vtr->usable_leb_size) { + lnum += 1; + off -= vtr->usable_leb_size; + } + + count -= len; + *offp += len; + buf += len; + len = count > tbuf_size ? tbuf_size : count; + } + + ubi_kfree(tbuf); + written = count_save - count; + return err ? err : written; +} + +#endif /* CONFIG_MTD_UBI_USERSPACE_IO */ diff --git a/drivers/mtd/ubi/cdev.h b/drivers/mtd/ubi/cdev.h new file mode 100644 index 0000000..ed3fbc3 --- /dev/null +++ b/drivers/mtd/ubi/cdev.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * This is a part of the user interface unit and includes all the character + * device-related stuff. + */ + +#ifndef __UBI_CDEV_H__ +#define __UBI_CDEV_H__ + +#include + +/* Maximum sequence numbers of UBI and volume character device IOCTLs */ +#define UBI_CDEV_IOC_MAX_SEQ 2 + +#ifndef CONFIG_MTD_UBI_USERSPACE_IO +#define VOL_CDEV_IOC_MAX_SEQ 1 +#else +#define VOL_CDEV_IOC_MAX_SEQ 2 +#endif + +struct ubi_info; +struct inode; +struct file; +struct ubi_uif_volume; + +/** + * ubi_cdev_vol_init - initialize all the character device-related stuff for + * an UBI volume. + * + * @ubi: the UBI device description object + * @vol: user interfaces unit volume description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_cdev_vol_init(const struct ubi_info *ubi, struct ubi_uif_volume *vol); + +/** + * ubi_cdev_vol_close - close all the character device-related stuff for an + * UBI volume. + * + * @vol: user interfaces unit volume description object + */ +void ubi_cdev_vol_close(struct ubi_uif_volume *vol); + +/** + * ubi_cdev_init - initialize all the character device-related stuff for + * an UBI device. + * + * @ubi: the UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int __init ubi_cdev_init(struct ubi_info *ubi); + +/** + * ubi_cdev_close - close all the character device-related stuff for an UBI + * device. + * + * @ubi: the UBI device description object + */ +void __exit ubi_cdev_close(const struct ubi_info *ubi); + +#endif /* !__UBI_CDEV_H__ */ diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c new file mode 100644 index 0000000..1e1c8fd --- /dev/null +++ b/drivers/mtd/ubi/debug.c @@ -0,0 +1,1082 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ubi.h" +#include "debug.h" +#include "vtbl.h" +#include "dtbl.h" +#include "scan.h" +#include "alloc.h" + +/* Level of UBI debugging prints */ +#define UBI_DBG_LEVEL KERN_CRIT + +/* Prefixes of debugging messages */ +#define UBI_DBG_VB_ERR_PREF "[UBI DBG error]" +#define UBI_DBG_UIF_PREF "[UBI DBG uif]" +#define UBI_DBG_CDEV_PREF "[UBI DBG cdev]" +#define UBI_DBG_GLUEBI_PREF "[UBI DBG gluebi]" +#define UBI_DBG_VMT_PREF "[UBI DBG vmt]" +#define UBI_DBG_UPD_PREF "[UBI DBG upd]" +#define UBI_DBG_VTBL_PREF "[UBI DBG vtbl]" +#define UBI_DBG_DTBL_PREF "[UBI DBG dtbl]" +#define UBI_DBG_ACC_PREF "[UBI DBG acc]" +#define UBI_DBG_EBA_PREF "[UBI DBG eba]" +#define UBI_DBG_WL_PREF "[UBI DBG wl]" +#define UBI_DBG_BGT_PREF "[UBI DBG bgt]" +#define UBI_DBG_ALLOC_PREF "[UBI DBG alloc]" +#define UBI_DBG_IO_PREF "[UBI DBG io]" +#define UBI_DBG_BLD_PREF "[UBI DBG bld]" +#define UBI_DBG_SCAN_PREF "[UBI DBG scan]" + +#define dump_msg(fmt, ...) \ + ubi_dbg_print_nolock(UBI_DBG_VB_ERR, NULL, fmt, ##__VA_ARGS__); + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_VB_ERR +static int vb_err_prints = 1; +#else +static int vb_err_prints; +#endif +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_UIF +int uif_prints = 1; +#else +int uif_prints; +#endif +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_CDEV +int cdev_prints = 1; +#else +int cdev_prints; +#endif +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_GLUEBI +int gluebi_prints = 1; +#else +int gluebi_prints; +#endif +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_VMT +int vmt_prints = 1; +#else +int vmt_prints; +#endif +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_UPD +int upd_prints = 1; +#else +int upd_prints; +#endif +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_VTBL +int vtbl_prints = 1; +#else +int vtbl_prints; +#endif +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_DTBL +int dtbl_prints = 1; +#else +int dtbl_prints; +#endif +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_ACC +int acc_prints = 1; +#else +int acc_prints; +#endif +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA +int eba_prints = 1; +#else +int eba_prints; +#endif +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL +int wl_prints = 1; +#else +int wl_prints; +#endif +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_BGT +int bgt_prints = 1; +#else +int bgt_prints; +#endif +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_ALLOC +int alloc_prints = 1; +#else +int alloc_prints; +#endif +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO +int io_prints = 1; +#else +int io_prints; +#endif +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD +int bld_prints = 1; +#else +int bld_prints; +#endif +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_SCAN +int scan_prints = 1; +#else +int scan_prints; +#endif + +/* If debugging messages should also go to the console */ +#ifdef CONFIG_MTD_UBI_DEBUG_CONSOLE_LOGGING +static int console_logging = 1; +#else +static int console_logging; +#endif + +/* If bit-flips should be emulated */ +#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS +static int emulate_bitflips = 1; +#else +static int emulate_bitflips; +#endif + +/* If write failures should be emulated */ +#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES +static int emulate_write_failures = 1; +#else +static int emulate_write_failures; +#endif + +/* If erase failures should be emulated */ +#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES +static int emulate_erase_failures = 1; +#else +static int emulate_erase_failures; +#endif + +/* A temporary buffer for the debugging messages */ +static unsigned char *tmp_buf; +/* Size of the temporary debugging messages buffer */ +#define TMP_BUF_SIZE 1024 + +/* Direntries of the UBI debugfs files */ + +/* /ubi */ +static struct dentry *debugfs_root; +/* /ubi/log */ +static struct dentry *debugfs_log; +/* /ubi/console_logging */ +static struct dentry *debugfs_console; +/* /ubi/vb_err_prints */ +static struct dentry *debugfs_vb_err_prints; +/* /ubi/uif_prints */ +static struct dentry *debugfs_uif_prints; +/* /ubi/cdev_prints */ +static struct dentry *debugfs_cdev_prints; +/* /ubi/gluebi_prints */ +static struct dentry *debugfs_gluebi_prints; +/* /ubi/vmt_prints */ +static struct dentry *debugfs_vmt_prints; +/* /ubi/upd_prints */ +static struct dentry *debugfs_upd_prints; +/* /ubi/vtbl_prints */ +static struct dentry *debugfs_vtbl_prints; +/* /ubi/dtbl_prints */ +static struct dentry *debugfs_dtbl_prints; +/* /ubi/acc_prints */ +static struct dentry *debugfs_acc_prints; +/* /ubi/eba_prints */ +static struct dentry *debugfs_eba_prints; +/* /ubi/wl_prints */ +static struct dentry *debugfs_wl_prints; +/* /ubi/bgt_prints */ +static struct dentry *debugfs_bgt_prints; +/* /ubi/alloc_prints */ +static struct dentry *debugfs_alloc_prints; +/* /ubi/io_prints */ +static struct dentry *debugfs_io_prints; +/* /ubi/bld_prints */ +static struct dentry *debugfs_bld_prints; +/* /ubi/scan_prints */ +static struct dentry *debugfs_scan_prints; + +/* Memory pages used for the debugging log */ +static unsigned char **pages; +/* Number of memory pages used for the debugging log */ +static int pages_count = CONFIG_MTD_UBI_DEBUG_BUF_SIZE * 1024 / PAGE_SIZE; +/* Debugging log head and tail */ +static int head, tail; +/* Size of the debugging log in bytes */ +static int log_size; +/* If the debugging log is empty */ +static int log_empty = 1; +/* If a message was printed to the debugging log */ +static int printed; + +/* + * Serializes prints and data structures related to the debugging log ring + * buffer support. + */ +static spinlock_t dbg_prints_lock = SPIN_LOCK_UNLOCKED; + +/* Operations of the debugging log file */ + +static ssize_t log_read(struct file *file, char __user *buf, size_t count, + loff_t *pos); +static ssize_t log_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos); +static loff_t log_llseek(struct file *file, loff_t off, int origin); + +static struct file_operations log_file_operations = { + .owner = THIS_MODULE, + .read = &log_read, + .write = &log_write, + .llseek = &log_llseek +}; + +int __init ubi_dbg_init(void) +{ + int i, err = -ENODEV; + + i = pages_count & (PAGE_SIZE - 1); + if (i) + pages_count += 1; + if (pages_count == 0) + pages_count = 1; + log_size = pages_count * PAGE_SIZE; + + /* Create debugging files and directories */ + + debugfs_root = debugfs_create_dir("ubi", NULL); + if (!debugfs_root || IS_ERR(debugfs_root)) + goto out; + + debugfs_log = debugfs_create_file("log", S_IFREG | S_IRUGO | S_IWUGO, + debugfs_root, NULL, &log_file_operations); + if (!debugfs_log || IS_ERR(debugfs_log)) + goto out_root; + + debugfs_console = debugfs_create_bool("console_logging", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &console_logging); + if (!debugfs_console || IS_ERR(debugfs_console)) + goto out_log; + + debugfs_vb_err_prints = debugfs_create_bool("vb_err_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &vb_err_prints); + if (!debugfs_vb_err_prints || IS_ERR(debugfs_vb_err_prints)) + goto out_console; + + debugfs_uif_prints = debugfs_create_bool("uif_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &uif_prints); + if (!debugfs_uif_prints || IS_ERR(debugfs_uif_prints)) + goto out_vb_err; + + debugfs_cdev_prints = debugfs_create_bool("cdev_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &cdev_prints); + if (!debugfs_cdev_prints || IS_ERR(debugfs_cdev_prints)) + goto out_uif; + + debugfs_gluebi_prints = debugfs_create_bool("gluebi_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &gluebi_prints); + if (!debugfs_gluebi_prints || IS_ERR(debugfs_gluebi_prints)) + goto out_cdev; + + debugfs_vmt_prints = debugfs_create_bool("vmt_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &vmt_prints); + if (!debugfs_vmt_prints || IS_ERR(debugfs_vmt_prints)) + goto out_gluebi; + + debugfs_upd_prints = debugfs_create_bool("upd_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &upd_prints); + if (!debugfs_upd_prints || IS_ERR(debugfs_upd_prints)) + goto out_vmt; + + debugfs_vtbl_prints = debugfs_create_bool("vtbl_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &vtbl_prints); + if (!debugfs_vtbl_prints || IS_ERR(debugfs_vtbl_prints)) + goto out_upd; + + debugfs_dtbl_prints = debugfs_create_bool("dtbl_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &dtbl_prints); + if (!debugfs_dtbl_prints || IS_ERR(debugfs_dtbl_prints)) + goto out_vtbl; + + debugfs_acc_prints = debugfs_create_bool("acc_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &acc_prints); + if (!debugfs_acc_prints || IS_ERR(debugfs_acc_prints)) + goto out_dtbl; + + debugfs_eba_prints = debugfs_create_bool("eba_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &eba_prints); + if (!debugfs_eba_prints || IS_ERR(debugfs_eba_prints)) + goto out_acc; + + debugfs_wl_prints = debugfs_create_bool("wl_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &wl_prints); + if (!debugfs_wl_prints || IS_ERR(debugfs_wl_prints)) + goto out_eba; + + debugfs_bgt_prints = debugfs_create_bool("bgt_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &bgt_prints); + if (!debugfs_bgt_prints || IS_ERR(debugfs_bgt_prints)) + goto out_wl; + + debugfs_alloc_prints = debugfs_create_bool("alloc_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &alloc_prints); + if (!debugfs_alloc_prints || IS_ERR(debugfs_alloc_prints)) + goto out_bgt; + + debugfs_io_prints = debugfs_create_bool("io_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &io_prints); + if (!debugfs_io_prints || IS_ERR(debugfs_io_prints)) + goto out_alloc; + + debugfs_bld_prints = debugfs_create_bool("bld_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &bld_prints); + if (!debugfs_bld_prints || IS_ERR(debugfs_bld_prints)) + goto out_io; + + debugfs_scan_prints = debugfs_create_bool("scan_prints", + S_IFREG | S_IRUGO | S_IWUGO, debugfs_root, &scan_prints); + if (!debugfs_scan_prints || IS_ERR(debugfs_scan_prints)) + goto out_bld; + + /* Allocate memory for the debugging log */ + + err = -ENOMEM; + pages = kzalloc(pages_count * sizeof(void *), GFP_KERNEL); + if (!pages) + goto out_array; + + for (i = 0; i < pages_count; i++) { + struct page *pg = alloc_page(GFP_KERNEL); + if (!pg) + goto out_pages; + pages[i] = page_address(pg); + } + + tmp_buf = kmalloc(TMP_BUF_SIZE, GFP_KERNEL); + if (!tmp_buf) + goto out_pages; + + return 0; + +out_pages: + for (i = 0; i < pages_count; i++) + free_page((unsigned long)pages[i]); +out_array: + kfree(pages); + debugfs_remove(debugfs_scan_prints); +out_bld: + debugfs_remove(debugfs_bld_prints); +out_io: + debugfs_remove(debugfs_io_prints); +out_alloc: + debugfs_remove(debugfs_alloc_prints); +out_bgt: + debugfs_remove(debugfs_bgt_prints); +out_wl: + debugfs_remove(debugfs_wl_prints); +out_eba: + debugfs_remove(debugfs_eba_prints); +out_acc: + debugfs_remove(debugfs_acc_prints); +out_dtbl: + debugfs_remove(debugfs_dtbl_prints); +out_vtbl: + debugfs_remove(debugfs_vtbl_prints); +out_upd: + debugfs_remove(debugfs_upd_prints); +out_vmt: + debugfs_remove(debugfs_vmt_prints); +out_gluebi: + debugfs_remove(debugfs_gluebi_prints); +out_cdev: + debugfs_remove(debugfs_cdev_prints); +out_uif: + debugfs_remove(debugfs_uif_prints); +out_vb_err: + debugfs_remove(debugfs_vb_err_prints); +out_console: + debugfs_remove(debugfs_console); +out_log: + debugfs_remove(debugfs_log); +out_root: + debugfs_remove(debugfs_root); +out: + return err; +} + +void __exit ubi_dbg_close(void) +{ + int i; + + kfree(tmp_buf); + for (i = 0; i < pages_count; i++) + free_page((unsigned long)pages[i]); + kfree(pages); + debugfs_remove(debugfs_scan_prints); + debugfs_remove(debugfs_bld_prints); + debugfs_remove(debugfs_io_prints); + debugfs_remove(debugfs_alloc_prints); + debugfs_remove(debugfs_bgt_prints); + debugfs_remove(debugfs_wl_prints); + debugfs_remove(debugfs_eba_prints); + debugfs_remove(debugfs_acc_prints); + debugfs_remove(debugfs_dtbl_prints); + debugfs_remove(debugfs_vtbl_prints); + debugfs_remove(debugfs_upd_prints); + debugfs_remove(debugfs_vmt_prints); + debugfs_remove(debugfs_gluebi_prints); + debugfs_remove(debugfs_cdev_prints); + debugfs_remove(debugfs_uif_prints); + debugfs_remove(debugfs_vb_err_prints); + debugfs_remove(debugfs_console); + debugfs_remove(debugfs_log); + debugfs_remove(debugfs_root); +} + +static void dbg_log(const char *buf, int len); +static void ubi_dbg_vprint_nolock(int type, const char *func, const char *fmt, + va_list args); + +void ubi_dbg_print(int type, const char *func, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + spin_lock(&dbg_prints_lock); + ubi_dbg_vprint_nolock(type, func, fmt, args); + spin_unlock(&dbg_prints_lock); + va_end(args); +} + +static void ubi_dbg_print_nolock(int type, const char *func, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ubi_dbg_vprint_nolock(type, func, fmt, args); + va_end(args); +} + +static void ubi_dbg_vprint_nolock(int type, const char *func, const char *fmt, + va_list args) +{ + char cnamebuf[KSYM_NAME_LEN+1]; + int pid = 1, caller = 0, force_console = 0, len; + const char *cname, *prefix; + unsigned long coffs; + + switch (type) { + case UBI_DBG_MSG: + prefix = UBI_MSG_PREF; + pid = 0; + force_console = 1; + break; + case UBI_DBG_WARN: + prefix = UBI_WARN_PREF; + pid = 0; + force_console = 1; + break; + case UBI_DBG_ERR: + prefix = UBI_ERR_PREF; + pid = 0; + force_console = 1; + break; + case UBI_DBG_VB_ERR: + prefix = UBI_DBG_VB_ERR_PREF; + if (!vb_err_prints) + return; + break; + case UBI_DBG_UIF: + if (!uif_prints) + return; + prefix = UBI_DBG_UIF_PREF; + break; + case UBI_DBG_CDEV: + if (!cdev_prints) + return; + prefix = UBI_DBG_CDEV_PREF; + break; + case UBI_DBG_GLUEBI: + if (!gluebi_prints) + return; + prefix = UBI_DBG_GLUEBI_PREF; + break; + case UBI_DBG_VMT: + if (!vmt_prints) + return; + prefix = UBI_DBG_VMT_PREF; + break; + case UBI_DBG_UPD: + if (!upd_prints) + return; + prefix = UBI_DBG_UPD_PREF; + break; + case UBI_DBG_VTBL: + if (!vtbl_prints) + return; + prefix = UBI_DBG_VTBL_PREF; + break; + case UBI_DBG_DTBL: + if (!dtbl_prints) + return; + prefix = UBI_DBG_DTBL_PREF; + break; + case UBI_DBG_ACC: + if (!acc_prints) + return; + prefix = UBI_DBG_ACC_PREF; + break; + case UBI_DBG_EBA: + if (!eba_prints) + return; + prefix = UBI_DBG_EBA_PREF; + break; + case UBI_DBG_WL: + if (!wl_prints) + return; + prefix = UBI_DBG_WL_PREF; + break; + case UBI_DBG_BGT: + if (!bgt_prints) + return; + prefix = UBI_DBG_BGT_PREF; + break; + case UBI_DBG_ALLOC: + if (!alloc_prints) + return; + prefix = UBI_DBG_ALLOC_PREF; + break; + case UBI_DBG_IO: + if (!io_prints) + return; + prefix = UBI_DBG_IO_PREF; + break; + case UBI_DBG_BLD: + if (!bld_prints) + return; + prefix = UBI_DBG_BLD_PREF; + break; + case UBI_DBG_SCAN: + if (!scan_prints) + return; + prefix = UBI_DBG_SCAN_PREF; + break; + default: + BUG(); + return; + } + + if (caller) { + char *modname; + unsigned long size, addr; + + addr = (unsigned long) __builtin_return_address(2); + cname = kallsyms_lookup(addr, &size, &coffs, &modname, + cnamebuf); + if (!cname) + cname = "kallsyms disabled"; + } + + dbg_log(prefix, strlen(prefix)); + dbg_log(" ", 1); + if (pid) { + len = snprintf(tmp_buf, TMP_BUF_SIZE, "(pid:%d) ", + current->pid); + dbg_log(tmp_buf, len); + } + if (caller && cname) { + len = scnprintf(tmp_buf, TMP_BUF_SIZE, "(caller: %s+%#lx) ", + cname, coffs); + dbg_log(tmp_buf, len); + } + if (func) { + len = snprintf(tmp_buf, TMP_BUF_SIZE, "%s: ", func); + dbg_log(tmp_buf, len); + } + len = vscnprintf(tmp_buf, TMP_BUF_SIZE, fmt, args); + dbg_log(tmp_buf, len); + dbg_log("\n", 1); + + if (!force_console && !console_logging) + return; + + printk(UBI_DBG_LEVEL "%s ", prefix); + if (pid) + printk("(pid:%d) ", current->pid); + if (caller && cname) + printk("(caller: %s+%#lx) ", cname, coffs); + if (func) + printk("%s: ", func); + vprintk(fmt, args); + printk("\n"); +} + +/** + * dbg_log - put a atring to the debugging log file. + * + * @str: the string to put + * @len: length of the string + */ +static void dbg_log(const char *str, int len) +{ + int avail, cpy, index, off, to_copy; + + to_copy = len; + if (len == 0) + return; + + printed = 1; + if (log_empty) { + avail = log_size; + log_empty = 0; + } else { + if (head > tail) + avail = log_size - head + tail; + else + avail = tail - head; + } + + if (len > avail) { + int add = len - avail; + + if (tail + add > log_size) + tail = add - log_size + tail; + else + tail += add; + } + + index = head >> PAGE_SHIFT; + off = head & (PAGE_SIZE - 1); + + avail = PAGE_SIZE - off; + cpy = min_t(int, avail, len); + memcpy(pages[index] + off, str, cpy); + + len -= cpy; + str += cpy; + + while (len) { + if (++index == pages_count) + index = 0; + + cpy = min_t(int, len, PAGE_SIZE); + memcpy(pages[index], str, cpy); + str += cpy; + len -= cpy; + } + + if (head + to_copy >= log_size) + head = to_copy - log_size + head; + else + head += to_copy; +} + +static ssize_t log_read(struct file *f, char __user *buf, size_t count, + loff_t *pos) +{ + int index, from, cpy, offs, to_read, avail; + int head1, tail1, log_empty1; + size_t count1; + char __user *buf1; + +again: + spin_lock(&dbg_prints_lock); + head1 = head; + tail1 = tail; + log_empty1 = log_empty; + printed = 0; + spin_unlock(&dbg_prints_lock); + + count1 = count; + buf1 = buf; + + if (log_empty1) + return 0; + + if (head1 <= tail1) + avail = log_size + head1 - tail1; + else + avail = head1 - tail1; + avail -= *pos; + + if (count1 > avail) + count1 = avail; + if (count1 == 0) + return 0; + + to_read = count1; + + if (tail1 + *pos > log_size) + from = *pos - log_size + tail1; + else + from = tail1 + *pos; + + index = from >> PAGE_SHIFT; + offs = from & (PAGE_SIZE - 1); + cpy = min_t(int, count1, PAGE_SIZE - offs); + + if (copy_to_user(buf1, pages[index] + offs, cpy)) + return -EFAULT; + + count1 -= cpy; + buf1 += cpy; + + while (count1) { + if (++index == pages_count) + index = 0; + + cpy = min_t(int, count1, PAGE_SIZE); + if (copy_to_user(buf1, pages[index], cpy)) + return -EFAULT; + + count1 -= cpy; + buf1 += cpy; + } + + spin_lock(&dbg_prints_lock); + if (printed) { + /* Something was printed to the log while we were copying it */ + spin_unlock(&dbg_prints_lock); + goto again; + } + spin_unlock(&dbg_prints_lock); + + *pos += to_read; + return to_read; +} + +static ssize_t log_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + /* Any write operation just clears the log */ + spin_lock(&dbg_prints_lock); + head = tail = 0; + log_empty = 1; + *pos = 0; + spin_unlock(&dbg_prints_lock); + + return count; +} + +static loff_t log_llseek(struct file *file, loff_t offset, int origin) +{ + int new_off, avail; + + spin_lock(&dbg_prints_lock); + + if (log_empty) + avail = 0; + else { + if (head < tail) + avail = log_size + head - tail; + else + avail = head - tail; + } + + switch (origin) { + case 0: /* SEEK_SET */ + new_off = offset; + break; + case 1: /* SEEK_CUR */ + new_off = file->f_pos + offset; + break; + case 2: /* SEEK_END */ + new_off = avail + offset; + break; + default: + goto out; + } + + if (log_empty) { + if (new_off != 0) + goto out; + } else { + if (new_off < 0 || new_off >= avail) + goto out; + } + + spin_unlock(&dbg_prints_lock); + + file->f_pos = new_off; + return new_off; + +out: + spin_unlock(&dbg_prints_lock); + return -EINVAL; +} + +static void ubi_dbg_hexdump_nolock(const void *ptr, int size); + +void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr) +{ + spin_lock(&dbg_prints_lock); + dump_msg("erase counter header dump:"); + dump_msg("magic %#08x", ubi32_to_cpu(ec_hdr->magic)); + dump_msg("version %d", (int)ec_hdr->version); + dump_msg("ec %llu", ubi64_to_cpu(ec_hdr->ec)); + dump_msg("vid_hdr_offset %d", ubi32_to_cpu(ec_hdr->vid_hdr_offset)); + dump_msg("data_offset %d", ubi32_to_cpu(ec_hdr->data_offset)); + dump_msg("hdr_crc %#08x", ubi32_to_cpu(ec_hdr->hdr_crc)); + dump_msg("erase counter header hexdump:"); + ubi_dbg_hexdump_nolock(ec_hdr, UBI_EC_HDR_SIZE); + spin_unlock(&dbg_prints_lock); +} + +void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr) +{ + spin_lock(&dbg_prints_lock); + dump_msg("volume identifier header dump:"); + dump_msg("magic %08x", ubi32_to_cpu(vid_hdr->magic)); + dump_msg("version %d", (int)vid_hdr->version); + dump_msg("vol_type %d", (int)vid_hdr->vol_type); + dump_msg("copy_flag %d", (int)vid_hdr->copy_flag); + dump_msg("compat %d", (int)vid_hdr->compat); + dump_msg("vol_id %d", ubi32_to_cpu(vid_hdr->vol_id)); + dump_msg("lnum %d", ubi32_to_cpu(vid_hdr->lnum)); + dump_msg("leb_ver %u", ubi32_to_cpu(vid_hdr->leb_ver)); + dump_msg("data_size %d", ubi32_to_cpu(vid_hdr->data_size)); + dump_msg("used_ebs %d", ubi32_to_cpu(vid_hdr->used_ebs)); + dump_msg("data_pad %d", ubi32_to_cpu(vid_hdr->data_pad)); + dump_msg("hdr_crc %08x", ubi32_to_cpu(vid_hdr->hdr_crc)); + dump_msg("volume identifier header hexdump:"); + ubi_dbg_hexdump_nolock(vid_hdr, UBI_VID_HDR_SIZE_CRC); + spin_unlock(&dbg_prints_lock); +} + +void ubi_dbg_dump_vtr(const struct ubi_vtbl_vtr *vtr) +{ + spin_lock(&dbg_prints_lock); + dump_msg("volume table record dump:"); + dump_msg("reserved_pebs %d", vtr->reserved_pebs); + dump_msg("alignment %d", vtr->alignment); + dump_msg("data_pad %d", vtr->data_pad); + dump_msg("vol_type %d", vtr->vol_type); + dump_msg("name_len %d", vtr->name_len); + dump_msg("usable_leb_size %d", vtr->usable_leb_size); + + if (vtr->name == NULL) { + dump_msg("name NULL"); + spin_unlock(&dbg_prints_lock); + return; + } + + if (vtr->name_len <= UBI_VOL_NAME_MAX && + strnlen(vtr->name, vtr->name_len + 1) == vtr->name_len) { + dump_msg("name %s", vtr->name); + } else { + dump_msg("the 1st 5 characters of the name: %c%c%c%c%c", + vtr->name[0], vtr->name[1], vtr->name[2], + vtr->name[3], vtr->name[4]); + } + spin_unlock(&dbg_prints_lock); +} + +void ubi_dbg_dump_dtr(const struct ubi_dtbl_dtr *dtr) +{ + spin_lock(&dbg_prints_lock); + dump_msg("data table record dump:"); + dump_msg("used_ebs %d", dtr->used_ebs); + dump_msg("used_bytes %lld", dtr->used_bytes); + dump_msg("last_eb_bytes %d", dtr->last_eb_bytes); + dump_msg("corrupted %d", dtr->corrupted); + spin_unlock(&dbg_prints_lock); +} + +void ubi_dbg_dump_raw_vtr(const struct ubi_vol_tbl_record *r) +{ + int name_len = ubi16_to_cpu(r->name_len); + + spin_lock(&dbg_prints_lock); + dump_msg("raw volume table record dump:"); + dump_msg("reserved_pebs %d", ubi32_to_cpu(r->reserved_pebs)); + dump_msg("alignment %d", ubi32_to_cpu(r->alignment)); + dump_msg("data_pad %d", ubi32_to_cpu(r->data_pad)); + dump_msg("vol_type %d", (int)r->vol_type); + dump_msg("name_len %d", name_len); + + if (r->name[0] == '\0') { + dump_msg("name NULL"); + spin_unlock(&dbg_prints_lock); + return; + } + + if (name_len <= UBI_VOL_NAME_MAX && + strnlen(&r->name[0], name_len + 1) == name_len) { + dump_msg("name %s", &r->name[0]); + } else { + dump_msg("the 1st 5 characters of the name: %c%c%c%c%c", + r->name[0], r->name[1], r->name[2], r->name[3], + r->name[4]); + } + spin_unlock(&dbg_prints_lock); +} + +void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv) +{ + spin_lock(&dbg_prints_lock); + dump_msg("volume scanning information dump:"); + dump_msg("vol_id %d", sv->vol_id); + dump_msg("highest_lnum %d", sv->highest_lnum); + dump_msg("leb_count %d", sv->leb_count); + dump_msg("compat %d", sv->compat); + dump_msg("vol_type %d", sv->vol_type); + dump_msg("used_ebs %d", sv->used_ebs); + dump_msg("last_data_size %d", sv->last_data_size); + dump_msg("data_pad %d", sv->data_pad); + spin_unlock(&dbg_prints_lock); +} + +void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type) +{ + spin_lock(&dbg_prints_lock); + dump_msg("eraseblock scanning information dump:"); + dump_msg("ec %d", seb->ec); + dump_msg("pnum %d", seb->pnum); + switch (type) { + case 0: + dump_msg("lnum %d", seb->lnum); + dump_msg("leb_ver %u", seb->leb_ver); + break; + case 1: + break; + } + spin_unlock(&dbg_prints_lock); +} + +void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req, const char *name) +{ + spin_lock(&dbg_prints_lock); + dump_msg("volume creation request dump:"); + dump_msg("vol_id %d", req->vol_id); + dump_msg("alignment %d", req->alignment); + dump_msg("bytes %lld", req->bytes); + dump_msg("vol_type %d", req->vol_type); + dump_msg("name_len %d", req->name_len); + + if (name == NULL) { + dump_msg("name NULL"); + spin_unlock(&dbg_prints_lock); + return; + } + + if (req->name_len <= UBI_VOL_NAME_MAX && + strnlen(name, req->name_len + 1) == req->name_len) { + dump_msg("name %s", name); + } else { + dump_msg("the 1st 5 characters of the name: %c%c%c%c%c", + name[0], name[1], name[2], name[3], name[4]); + } + spin_unlock(&dbg_prints_lock); +} + +void ubi_dbg_hexdump(const void *ptr, int size) +{ + spin_lock(&dbg_prints_lock); + ubi_dbg_hexdump_nolock(ptr, size); + spin_unlock(&dbg_prints_lock); +} + +#define BYTES_PER_LINE 32 +static void ubi_dbg_hexdump_nolock(const void *ptr, int size) +{ + int i, k = 0; + int rows, columns; + const uint8_t *p = ptr; + + size = ALIGN(size, 4); + rows = size/BYTES_PER_LINE + size % BYTES_PER_LINE; + for (i = 0; i < rows; i++) { + int j, len; + + columns = min(size - k, BYTES_PER_LINE) / 4; + if (columns == 0) + break; + + len = sprintf(tmp_buf, "%5d: ", i*BYTES_PER_LINE); + + for (j = 0; j < columns; j++) { + int n, N; + + N = size - k > 4 ? 4 : size - k; + for (n = 0; n < N; n++) + len += sprintf(tmp_buf + len, "%02x", p[k++]); + len += sprintf(tmp_buf + len, " "); + } + len += sprintf(tmp_buf + len, "\n"); + dbg_log(tmp_buf, len); + if (console_logging) + printk(tmp_buf); + } +} + +static spinlock_t random_lock = SPIN_LOCK_UNLOCKED; +static unsigned long RandomValue = 1; + +/** + * random - pseudo random generator. + * + * Borrowed from XFS. + */ +static unsigned long random(void) +{ + /* cycles pseudo-randomly through all values between 1 and 2^31 - 2 */ + register long rv; + register long lo; + register long hi; + + spin_lock(&random_lock); + rv = RandomValue; + hi = rv / 127773; + lo = rv % 127773; + rv = 16807 * lo - 2836 * hi; + if (rv <= 0) rv += 2147483647; + RandomValue = rv; + spin_unlock(&random_lock); + return rv; +} + +int ubi_dbg_is_bitflip(void) +{ + if (emulate_bitflips) + return !(random() % 50); + else + return 0; +} + +int ubi_dbg_is_write_failure(void) +{ + if (emulate_write_failures) + return !(random() % 100); + else + return 0; +} + +int ubi_dbg_is_erase_failure(void) +{ + if (emulate_erase_failures) + return !(random() % 100); + else + return 0; +} diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h new file mode 100644 index 0000000..87a3c82 --- /dev/null +++ b/drivers/mtd/ubi/debug.h @@ -0,0 +1,297 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/** + * UBI debugging unit. + * + * UBI provides rich debugging capabilities which are implemented in + * this unit. * The main feature provided by this unit is the debugging log. + * The debugging log is accessed via the "/ubi/log" file from + * user-space. The debugging prints may also be directed to the console. + * + * UBI distinguishes between debugging messages from different units and may + * switch them on and off separately. + */ + +#ifndef __UBI_DEBUG_H__ +#define __UBI_DEBUG_H__ + +#include + +#ifdef CONFIG_MTD_UBI_DEBUG + +#define ubi_assert(expr) BUG_ON(!(expr)) + +/* Debugging messages from different UBI units */ + +/* A verbose error message */ +#define dbg_err(fmt, ...) \ + ubi_dbg_print(UBI_DBG_VB_ERR, __FUNCTION__, fmt, ##__VA_ARGS__) +/* User interface unit */ +#define dbg_uif(fmt, ...) \ + ubi_dbg_print(UBI_DBG_UIF, __FUNCTION__, fmt, ##__VA_ARGS__) +/* Character device handling sub-unit */ +#define dbg_cdev(fmt, ...) \ + ubi_dbg_print(UBI_DBG_CDEV, __FUNCTION__, fmt, ##__VA_ARGS__) +/* Gluebi sub-unit */ +#define dbg_gluebi(fmt, ...) \ + ubi_dbg_print(UBI_DBG_GLUEBI, __FUNCTION__, fmt, ##__VA_ARGS__) +/* Volume management unit */ +#define dbg_vmt(fmt, ...) \ + ubi_dbg_print(UBI_DBG_VMT, __FUNCTION__, fmt, ##__VA_ARGS__) +/* Update unit */ +#define dbg_upd(fmt, ...) \ + ubi_dbg_print(UBI_DBG_UPD, __FUNCTION__, fmt, ##__VA_ARGS__) +/* Volume table unit */ +#define dbg_vtbl(fmt, ...) \ + ubi_dbg_print(UBI_DBG_VTBL, __FUNCTION__, fmt, ##__VA_ARGS__) +/* Data table unit */ +#define dbg_dtbl(fmt, ...) \ + ubi_dbg_print(UBI_DBG_DTBL, __FUNCTION__, fmt, ##__VA_ARGS__) +/* Accounting unit */ +#define dbg_acc(fmt, ...) \ + ubi_dbg_print(UBI_DBG_ACC, __FUNCTION__, fmt, ##__VA_ARGS__) +/* Eraseblock association unit */ +#define dbg_eba(fmt, ...) \ + ubi_dbg_print(UBI_DBG_EBA, __FUNCTION__, fmt, ##__VA_ARGS__) +/* Wear-levelling unit */ +#define dbg_wl(fmt, ...) \ + ubi_dbg_print(UBI_DBG_WL, __FUNCTION__, fmt, ##__VA_ARGS__) +/* Background thread unit */ +#define dbg_bgt(fmt, ...) \ + ubi_dbg_print(UBI_DBG_BGT, __FUNCTION__, fmt, ##__VA_ARGS__) +/* Memory allocation unit */ +#define dbg_alloc(fmt, ...) \ + ubi_dbg_print(UBI_DBG_ALLOC, __FUNCTION__, fmt, ##__VA_ARGS__) +/* Input/output unit */ +#define dbg_io(fmt, ...) \ + ubi_dbg_print(UBI_DBG_IO, __FUNCTION__, fmt, ##__VA_ARGS__) +/* Build unit */ +#define dbg_bld(fmt, ...) \ + ubi_dbg_print(UBI_DBG_BLD, __FUNCTION__, fmt, ##__VA_ARGS__) +/* Scanning unit */ +#define dbg_scan(fmt, ...) \ + ubi_dbg_print(UBI_DBG_SCAN, __FUNCTION__, fmt, ##__VA_ARGS__) + +/** + * UBI message types. + * + * @UBI_DBG_MSG: a normal message + * @UBI_DBG_WARN: a warning message + * @UBI_DBG_ERR: an error message + * @UBI_DBG_ERR: a verbose error message + * @UBI_DBG_UIF: a debugging message from the user interfaces unit + * @UBI_DBG_CDEV: a debugging message from the character device handling + * sub-unit. + * @UBI_DBG_GLUEBI: a debugging message from the gluebi sub-unit. + * @UBI_DBG_VMT: a debugging message from the volume management unit + * @UBI_DBG_UPD: a debugging message from the update unit + * @UBI_DBG_VTBL: a debugging message from the volume table unit + * @UBI_DBG_DTBL: a debugging message from the data table unit + * @UBI_DBG_ACC: a debugging message from the accounting unit + * @UBI_DBG_EBA: a debugging message from the eraseblock association unit + * @UBI_DBG_WL: a debugging message from the wear-levelling unit + * @UBI_DBG_BGT: a debugging message from the background thread unit + * @UBI_DBG_ALLOC: a debugging message from the memory allocation unit + * @UBI_DBG_IO: a debugging message from the input/output unit + * @UBI_DBG_BLD: a debugging message from the build unit + * @UBI_DBG_SCAN: a debugging message from the scanning unit + */ +enum { + UBI_DBG_MSG, + UBI_DBG_WARN, + UBI_DBG_ERR, + UBI_DBG_VB_ERR, + UBI_DBG_UIF, + UBI_DBG_CDEV, + UBI_DBG_GLUEBI, + UBI_DBG_VMT, + UBI_DBG_UPD, + UBI_DBG_VTBL, + UBI_DBG_DTBL, + UBI_DBG_ACC, + UBI_DBG_EBA, + UBI_DBG_WL, + UBI_DBG_BGT, + UBI_DBG_ALLOC, + UBI_DBG_IO, + UBI_DBG_BLD, + UBI_DBG_SCAN +}; + +/** + * ubi_dbg_print - print a message. + * + * @type: type of the message + * @func: printing function name + * @fmt: format string + * + * This function prints a message to the console, the debugging log, or both. + * Normal, warning, and error messages always go to both console and debugging + * log. Debugging messages always go to the debugging log, and if the + * corresponding option is enables, they also go to the console. + */ +void ubi_dbg_print(int type, const char *func, const char *fmt, ...); + +struct ubi_info; +struct ubi_ec_hdr; +struct ubi_vid_hdr; +struct ubi_vtbl_vtr; +struct ubi_dtbl_dtr; +struct ubi_vol_tbl_record; +struct ubi_scan_volume; +struct ubi_scan_leb; +struct ubi_mkvol_req; + +/** + * ubi_dbg_dump_ec_hdr - dump an erase counter header. + * + * @ec_hdr: the erase counter header to dump + */ +void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr); + +/** + * ubi_dbg_dump_vid_hdr - dump a volume identifier header. + * + * @vid_hdr: the volume identifier header to dump + */ +void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr); + +/** + * ubi_dbg_dump_vtr - dump a &struct ubi_vtbl_vtr object. + * + * @vtr: the object to dump + */ +void ubi_dbg_dump_vtr(const struct ubi_vtbl_vtr *vtr); + +/** + * ubi_dbg_dump_dtr - dump a &struct ubi_vtbl_dtr object. + * + * @dtr: the object to dump + */ +void ubi_dbg_dump_dtr(const struct ubi_dtbl_dtr *dtr); + +/** + * ubi_dbg_dump_raw_vtr - dump a &struct ubi_vol_tbl_record object. + * + * @r: the object to dump + */ +void ubi_dbg_dump_raw_vtr(const struct ubi_vol_tbl_record *r); + +/** + * ubi_dbg_dump_sv - dump a &struct ubi_scan_volume object. + * + * @sv: the object to dump + */ +void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv); + + +/** + * ubi_dbg_dump_seb - dump a &struct ubi_scan_leb object. + * + * @seb: the object to dump + * @type: object type: 0 - not corrupted, 1 - corrupted + */ +void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type); + +/** + * ubi_dbg_dump_mkvol_req - dump a &struct ubi_mkvol_req object. + * + * @req: the object to dump + * @name: volume name in kernel memory + */ +void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req, const char *name); + +/** + * ubi_dbg_hexdump - dump a buffer. + * + * @buf: the buffer to dump + * @size: buffer size which must be multiple of 4 bytes + */ +void ubi_dbg_hexdump(const void *buf, int size); + +/** + * ubi_dbg_is_bitflip - if its time to emulate a bit-flip. + */ +int ubi_dbg_is_bitflip(void); + +/** + * ubi_dbg_is_write_failure - if its time to emulate a write failure. + */ +int ubi_dbg_is_write_failure(void); + +/** + * ubi_dbg_is_erase_failure - if its time to emulate an erase failure. + */ +int ubi_dbg_is_erase_failure(void); + +/** + * ubi_dbg_init - initialize the debugging unit. + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int __init ubi_dbg_init(void); + +/** + * ubi_dbg_close - close the debugging unit. + */ +void __exit ubi_dbg_close(void); + +#else + +#define ubi_assert(expr) ({}) + +#define dbg_err(fmt, ...) ({}) +#define dbg_uif(fmt, ...) ({}) +#define dbg_cdev(fmt, ...) ({}) +#define dbg_gluebi(fmt, ...) ({}) +#define dbg_vmt(fmt, ...) ({}) +#define dbg_upd(fmt, ...) ({}) +#define dbg_vtbl(fmt, ...) ({}) +#define dbg_dtbl(fmt, ...) ({}) +#define dbg_acc(fmt, ...) ({}) +#define dbg_eba(fmt, ...) ({}) +#define dbg_wl(fmt, ...) ({}) +#define dbg_bgt(fmt, ...) ({}) +#define dbg_alloc(fmt, ...) ({}) +#define dbg_io(fmt, ...) ({}) +#define dbg_bld(fmt, ...) ({}) +#define dbg_scan(fmt, ...) ({}) + +#define ubi_dbg_print(func, fmt, ...) ({}) +#define ubi_dbg_dump_ec_hdr(ec_hdr) ({}) +#define ubi_dbg_dump_vid_hdr(vid_hdr) ({}) +#define ubi_dbg_dump_vtr(vtr) ({}) +#define ubi_dbg_dump_dtr(dtr) ({}) +#define ubi_dbg_dump_raw_vtr(r) ({}) +#define ubi_dbg_dump_sv(sv) ({}) +#define ubi_dbg_dump_seb(seb, type) ({}) +#define ubi_dbg_dump_mkvol_req(req, name) ({}) +#define ubi_dbg_hexdump(buf, size) ({}) +#define ubi_dbg_is_bitflip() 0 +#define ubi_dbg_is_write_failure() 0 +#define ubi_dbg_is_erase_failure() 0 + +#define ubi_dbg_init() 0 +#define ubi_dbg_close() + +#endif /* !CONFIG_MTD_UBI_DEBUG */ +#endif /* !__UBI_DEBUG_H__ */ diff --git a/drivers/mtd/ubi/dtbl.c b/drivers/mtd/ubi/dtbl.c new file mode 100644 index 0000000..c9faa71 --- /dev/null +++ b/drivers/mtd/ubi/dtbl.c @@ -0,0 +1,287 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include "ubi.h" +#include "dtbl.h" +#include "alloc.h" +#include "scan.h" +#include "vtbl.h" +#include "io.h" +#include "ivol.h" +#include "account.h" +#include "debug.h" + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_DTBL +static int paranoid_check_dtr(const struct ubi_info *ubi, int vol_id); +#else +#define paranoid_check_dtr(ubi, vol_id) 0 +#endif + +const struct ubi_dtbl_dtr *ubi_dtbl_get_dtr(const struct ubi_info *ubi, + int vol_id) +{ + int err; + const struct ubi_dtbl_info *dtbl = ubi->dtbl; + + if (ubi_is_ivol(vol_id)) + return ubi_ivol_get_dtr(ubi, vol_id); + + ubi_assert(vol_id >= 0 && vol_id < dtbl->dt_slots); + ubi_assert(!IS_ERR(ubi_vtbl_get_vtr(ubi, vol_id))); + err = paranoid_check_dtr(ubi, vol_id); + return &dtbl->dt[vol_id]; +} + +void ubi_dtbl_set_corrupted(const struct ubi_info *ubi, int vol_id) +{ + const struct ubi_vtbl_vtr *vtr; + const struct ubi_dtbl_info *dtbl = ubi->dtbl; + + ubi_msg("mark volume %d as corrupted", vol_id); + ubi_assert(vol_id >= 0 && vol_id < dtbl->dt_slots); + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + ubi_assert(!IS_ERR(vtr)); + + if (vtr->vol_type == UBI_STATIC_VOLUME) + dtbl->dt[vol_id].corrupted = 1; +} + +void ubi_dtbl_set_dtr(const struct ubi_info *ubi, int vol_id, long long bytes) +{ + int err; + uint64_t tmp; + struct ubi_dtbl_dtr *dtr; + const struct ubi_vtbl_vtr *vtr; + const struct ubi_dtbl_info *dtbl = ubi->dtbl; + + ubi_assert(vol_id >= 0 && vol_id < dtbl->dt_slots); + ubi_assert(bytes >= 0); + + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + + ubi_assert(!IS_ERR(vtr)); + ubi_assert(bytes <= vtr->reserved_pebs * vtr->usable_leb_size); + + dtr = &dtbl->dt[vol_id]; + + if (vtr->vol_type == UBI_DYNAMIC_VOLUME) { + dtr->last_eb_bytes = vtr->usable_leb_size; + dtr->used_ebs = vtr->reserved_pebs; + dtr->used_bytes = dtr->used_ebs * vtr->usable_leb_size; + dtr->corrupted = 0; + dbg_dtbl("set DTR for volume %d: last_eb_bytes %d, " + "used_ebs %d, used_bytes %lld", vol_id, + dtr->last_eb_bytes, dtr->used_ebs, dtr->used_bytes); + err = paranoid_check_dtr(ubi, vol_id); + return; + } + + tmp = bytes; + dtr->last_eb_bytes = do_div(tmp, vtr->usable_leb_size); + dtr->used_ebs = tmp; + if (dtr->last_eb_bytes) + dtr->used_ebs += 1; + else + dtr->last_eb_bytes = vtr->usable_leb_size; + dtr->used_bytes = bytes; + dtr->corrupted = 0; + dbg_dtbl("set DTR for volume %d: last_eb_bytes %d, used_ebs %d, " + "used_bytes %lld", vol_id, dtr->last_eb_bytes, dtr->used_ebs, + dtr->used_bytes); + err = paranoid_check_dtr(ubi, vol_id); +} + +static void __init init_dtbl(struct ubi_info *ubi, struct ubi_scan_info *si); + +int __init ubi_dtbl_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si) +{ + int err; + struct ubi_dtbl_info *dtbl; + + dbg_dtbl("initialize the data table unit"); + + dtbl = ubi_kzalloc(sizeof(struct ubi_dtbl_info)); + if (!dtbl) + return -ENOMEM; + ubi->dtbl = dtbl; + + dtbl->dt_slots = ubi->vtbl->vt_slots; + dtbl->dt = ubi_kzalloc(sizeof(struct ubi_dtbl_dtr) * dtbl->dt_slots); + if (!dtbl->dt) { + err = -ENOMEM; + goto out_dtbl; + } + + init_dtbl(ubi, si); + dbg_dtbl("the data table unit is initialized"); + return 0; + +out_dtbl: + ubi_kfree(ubi->dtbl); + return err; +} + +void __exit ubi_dtbl_close(const struct ubi_info *ubi) +{ + dbg_dtbl("close the data table unit"); + ubi_kfree(ubi->dtbl->dt); + ubi_kfree(ubi->dtbl); +} + +/** + * init_dtbl - initialize the data table using scanning information. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + */ +static void __init init_dtbl(struct ubi_info *ubi, struct ubi_scan_info *si) +{ + int i, err; + + for (i = 0; i < ubi->acc->max_volumes; i++) { + const struct ubi_vtbl_vtr *vtr; + struct ubi_scan_volume *sv; + struct ubi_dtbl_dtr *dtr = &ubi->dtbl->dt[i]; + + cond_resched(); + + vtr = ubi_vtbl_get_vtr(ubi, i); + if (IS_ERR(vtr)) + continue; + + if (vtr->vol_type == UBI_DYNAMIC_VOLUME) { + dtr->used_ebs = vtr->reserved_pebs; + dtr->last_eb_bytes = vtr->usable_leb_size; + dtr->used_bytes = dtr->used_ebs * vtr->usable_leb_size; + dtr->corrupted = 0; + continue; + } + + sv = ubi_scan_get_scan_volume(si, i); + if (!sv) + /* + * We don't actually know whether this volume is + * completely corrupted or just contains no data. And + * we cannot know this as long as the data table is not + * maintained on flash. So we assume the latter. + */ + continue; + + if (unlikely(sv->leb_count != sv->used_ebs)) { + ubi_warn("static volume %d misses %d LEBs", + sv->vol_id, sv->used_ebs - sv->leb_count); + dtr->corrupted = 1; + continue; + } + + dtr->used_ebs = sv->used_ebs; + dtr->used_bytes = (dtr->used_ebs - 1) * vtr->usable_leb_size; + dtr->used_bytes += sv->last_data_size; + dtr->last_eb_bytes = sv->last_data_size; + + err = paranoid_check_dtr(ubi, i); + } +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_DTBL + +/** + * paranoid_check_dtr - check that the data table record of a volume is sane. + * + * @ubi: the UBI device description object + * @vol_id: ID of the volume to check + * + * This function returns %0 if the data table record is sane and %1 if it is + * not. + */ +static int paranoid_check_dtr(const struct ubi_info *ubi, int vol_id) +{ + long long n; + struct ubi_dtbl_dtr *dtr; + const struct ubi_vtbl_vtr *vtr; + + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + if (IS_ERR(vtr)) + return 0; + + dtr = &ubi->dtbl->dt[vol_id]; + n = dtr->used_ebs * vtr->usable_leb_size; + + if (vtr->vol_type == UBI_DYNAMIC_VOLUME) { + if (unlikely(dtr->used_ebs != vtr->reserved_pebs)) { + ubi_err("bad used_ebs"); + goto bad_dtr; + } + + if (unlikely(dtr->last_eb_bytes != vtr->usable_leb_size)) { + ubi_err("bad last_eb_bytes"); + goto bad_dtr; + } + + if (unlikely(dtr->used_bytes != n)) { + ubi_err("bad used_bytes"); + goto bad_dtr; + } + + if (unlikely(dtr->corrupted)) { + ubi_err("corrupted dynamic volume"); + goto bad_dtr; + } + + return 0; + } + + if (unlikely(dtr->corrupted != 0 && dtr->corrupted != 1)) { + ubi_err("bad corrupted"); + goto bad_dtr; + } + + if (unlikely(dtr->used_ebs < 0 || + dtr->used_ebs > vtr->reserved_pebs)) { + ubi_err("bad used_ebs"); + goto bad_dtr; + } + + if (unlikely(dtr->last_eb_bytes < 0 || + dtr->last_eb_bytes > vtr->usable_leb_size)) { + ubi_err("bad last_eb_bytes"); + goto bad_dtr; + } + + if (unlikely(dtr->used_bytes < 0 || dtr->used_bytes > n || + dtr->used_bytes < n - vtr->usable_leb_size)) { + ubi_err("bad used_bytes"); + goto bad_dtr; + } + + return 0; + +bad_dtr: + ubi_err("bad data table record %d", vol_id); + ubi_dbg_dump_dtr(dtr); + ubi_dbg_dump_vtr(vtr); + dump_stack(); + return 1; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID_DTBL */ diff --git a/drivers/mtd/ubi/dtbl.h b/drivers/mtd/ubi/dtbl.h new file mode 100644 index 0000000..748f9d7 --- /dev/null +++ b/drivers/mtd/ubi/dtbl.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * The data table unit. + * + * This unit is responsible for maintaining the data table. The data table + * describes data stored in volumes. UBI only cares about the contents of + * static volumes, and mostly does not care about the contents of dynamic + * volumes. Indeed, UBI protects static volumes, so this is a relation wit + * hit's data. UBI has to know how many data are stored in static volumes, + * whether the data are all right tor corrupted, etc. + * + * In this implementation the data table is maintained only in RAM and there is + * no on-flash data table. The data table is initialized using the scanning + * information. + * + * There is an advantage of keeping data table on flash. Suppose we have a + * volume X, and all its physical eraseblocks have gone bad. Suppose we are + * attaching the corresponding MTD device, the scanning unit finds no logical + * eraseblocks of the volume X. According to the volume table volume X does + * exist. So we don't know whether it is just empty or all its physical + * eraseblocks went bad. + * + * In future, one may add an on-flash data table support. + */ + +#ifndef __UBI_DTBL_H__ +#define __UBI_DTBL_H__ + +#include + +struct ubi_info; +struct ubi_scan_info; + +/** + * ubi_dtbl_get_dtr - retrieve the data table record of a volume + * + * @ubi: the UBI device description object + * @vol_id: ID of the volume + * + * This function returns the requested data table record. The requested volume + * must exist. + */ +const struct ubi_dtbl_dtr *ubi_dtbl_get_dtr(const struct ubi_info *ubi, + int vol_id); + +/** + * ubi_dtbl_set_corrupted - mark a volume as corrupted. + * + * @ubi: the UBI device description object + * @vol_id: ID of the volume + * + * This function must only be used with existing user volumes. The requested + * volume must exist and must not be an internal volume. As dynamic volumes + * cannot be in corrupted state, this function does nothing in case of a + * dynamic volume. + */ +void ubi_dtbl_set_corrupted(const struct ubi_info *ubi, int vol_id); + +/** + * ubi_dtbl_set_dtr - set data table record of a volume + * + * @ubi: the UBI device description object + * @vol_id: ID of the volume + * @bytes: how many bytes of data this volume contains + * + * The requested volume must exist and must not be an internal volume. + */ +void ubi_dtbl_set_dtr(const struct ubi_info *ubi, int vol_id, long long bytes); + +/** + * ubi_dtbl_init_scan - initialize the UBI data table unit using scanning + * information. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int __init ubi_dtbl_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si); + +/** + * ubi_dtbl_close - close the UBI data table unit. + * + * @ubi: the UBI device description object + */ +void __exit ubi_dtbl_close(const struct ubi_info *ubi); + +/** + * struct ubi_dtbl_dtr - a data table record. + * + * @used_ebs: how many eraseblock the data occupies + * @last_eb_bytes: how many bytes are stored in the last eraseblock + * @used_bytes: how many bytes of data this volume contains + * @corrupted: non-zero if the data is corrupted + * + * This data structure mostly matters only for static volumes. For dynamic + * volumes, @used_ebs is always equivalent to the number of reserved + * eraseblocks, @used_bytes is always equivalent to the total size of the + * reserved space. + */ +struct ubi_dtbl_dtr { + int used_ebs; + int last_eb_bytes; + long long used_bytes; + int corrupted; +}; + +/** + * struct ubi_dtbl_info - UBI data table unit description data structure. + * + * @dt_slots: how many data table records are stored in the data table + * @dt: the data table + */ +struct ubi_dtbl_info { + int dt_slots; /* public */ + struct ubi_dtbl_dtr *dt; /* private */ +}; + +#endif /* __UBI_DTBL_H__ */ diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c new file mode 100644 index 0000000..0792ca0 --- /dev/null +++ b/drivers/mtd/ubi/eba.c @@ -0,0 +1,1210 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ubi.h" +#include "alloc.h" +#include "eba.h" +#include "badeb.h" +#include "io.h" +#include "wl.h" +#include "volmgmt.h" +#include "vtbl.h" +#include "ivol.h" +#include "account.h" +#include "background.h" +#include "scan.h" +#include "misc.h" +#include "debug.h" + +/* + * The highest bit in logical-to-physical eraseblock mappings is used to + * indicate that the logical eraseblock is not mapped. + */ +#define NOT_MAPPED 0x80000000 + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_EBA +static int paranoid_check_leb(const struct ubi_info *ubi, int pnum, int vol_id, + int lnum, int leb_ver, + const struct ubi_vid_hdr *vid_hdr); +static int paranoid_check_leb_locked(const struct ubi_info *ubi, int vol_id, + int lnum); +#else +#define paranoid_check_leb(ubi, vol_id, pnum, lnum, leb_ver, vid_hdr) 0 +#define paranoid_check_leb_locked(ubi, vol_id, lnum) +#endif + +/** + * vol_id2idx - turn a volume ID to the EBA table index. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID + */ +static inline int vol_id2idx(const struct ubi_info *ubi, int vol_id) +{ + const struct ubi_acc_info *acc = ubi->acc; + + if (vol_id >= UBI_INTERNAL_VOL_START) + return vol_id - UBI_INTERNAL_VOL_START + acc->max_volumes; + else + return vol_id; +} + +/** + * idx2vol_id - turn an EBA table index to the volume ID. + * + * @ubi: the UBI device description object + * @idx: the EBA table index + */ +static inline int idx2vol_id(const struct ubi_info *ubi, int idx) +{ + const struct ubi_acc_info *acc = ubi->acc; + + if (idx >= acc->max_volumes) + return idx - acc->max_volumes + UBI_INTERNAL_VOL_START; + else + return idx; +} + +/** + * leb_get_ver - get logical eraseblock version. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID + * @lnum: the logical eraseblock number + * + * The logical eraseblock has to be locked. + */ +static inline int leb_get_ver(const struct ubi_info *ubi, int vol_id, int lnum) +{ + int idx, leb_ver; + struct ubi_eba_info *eba = ubi->eba; + + idx = vol_id2idx(ubi, vol_id); + + spin_lock(&eba->eba_tbl_lock); + ubi_assert(eba->eba_tbl[idx].recs); + leb_ver = eba->eba_tbl[idx].recs[lnum].leb_ver; + spin_unlock(&eba->eba_tbl_lock); + return leb_ver; +} + +/** + * leb_map - map a logical eraseblock to a physical eraseblock. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID + * @lnum: the logical eraseblock number + * @pnum: the physical eraseblock + * + * The logical eraseblock has to be locked. + */ +static inline void leb_map(const struct ubi_info *ubi, int vol_id, int lnum, + int pnum) +{ + int idx; + struct ubi_eba_info *eba = ubi->eba; + + idx = vol_id2idx(ubi, vol_id); + + spin_lock(&eba->eba_tbl_lock); + ubi_assert(eba->eba_tbl[idx].recs); + ubi_assert(eba->eba_tbl[idx].recs[lnum].pnum < 0); + eba->eba_tbl[idx].recs[lnum].pnum = pnum; + spin_unlock(&eba->eba_tbl_lock); +} + +/** + * leb_unmap - unmap a logical eraseblock. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID + * @lnum: the logical eraseblock number to unmap + * + * This function unmaps a logical eraseblock and increases its version. The + * logical eraseblock has to be locked. + */ +static inline void leb_unmap(const struct ubi_info *ubi, int vol_id, int lnum) +{ + int idx; + struct ubi_eba_info *eba = ubi->eba; + + idx = vol_id2idx(ubi, vol_id); + + spin_lock(&eba->eba_tbl_lock); + ubi_assert(eba->eba_tbl[idx].recs); + ubi_assert(eba->eba_tbl[idx].recs[lnum].pnum >= 0); + + eba->eba_tbl[idx].recs[lnum].pnum |= NOT_MAPPED; + eba->eba_tbl[idx].recs[lnum].leb_ver += 1; + spin_unlock(&eba->eba_tbl_lock); +} + +/** + * leb2peb - get physical eraseblock number the logical eraseblock is mapped + * to. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID + * @lnum: the logical eraseblock number + * + * If the logical eraseblock is mapped, this function returns a positive + * physical eraseblock number. If it is not mapped, this function returns + * a negative number. + */ +static inline int leb2peb(const struct ubi_info *ubi, int vol_id, int lnum) +{ + int idx, pnum; + struct ubi_eba_info *eba = ubi->eba; + + idx = vol_id2idx(ubi, vol_id); + + spin_lock(&eba->eba_tbl_lock); + ubi_assert(eba->eba_tbl[idx].recs); + pnum = eba->eba_tbl[idx].recs[lnum].pnum; + spin_unlock(&eba->eba_tbl_lock); + + return pnum; +} + +int ubi_eba_mkvol(const struct ubi_info *ubi, int vol_id, int reserved_pebs) +{ + int i, idx, sz; + struct ubi_eba_tbl_rec *new_ebs; + struct ubi_eba_info *eba = ubi->eba; + struct ubi_eba_tbl_volume *eba_tbl = eba->eba_tbl; + + dbg_eba("create volume %d, size %d", vol_id, reserved_pebs); + + /* Input arguments sanity check */ + ubi_assert(vol_id >= 0); + ubi_assert(reserved_pebs > 0); + ubi_assert(!ubi_is_ivol(vol_id)); + ubi_assert(vol_id < ubi->acc->max_volumes); + + if (ubi->io->ro_mode) { + dbg_err("read-only mode"); + return -EROFS; + } + + sz = reserved_pebs * sizeof(struct ubi_eba_tbl_rec); + new_ebs = ubi_kmalloc(sz); + if (!new_ebs) + return -ENOMEM; + + for (i = 0; i < reserved_pebs; i++) { + new_ebs[i].pnum = NOT_MAPPED; + new_ebs[i].leb_ver = 0xFFFFFFF0; + } + + idx = vol_id2idx(ubi, vol_id); + + spin_lock(&eba->eba_tbl_lock); + ubi_assert(!eba_tbl[idx].recs); + eba_tbl[idx].recs = new_ebs; + eba_tbl[idx].leb_count = reserved_pebs; + spin_unlock(&eba->eba_tbl_lock); + + return 0; +} + +int ubi_eba_rmvol(const struct ubi_info *ubi, int vol_id) +{ + int idx; + struct ubi_eba_tbl_rec *rm_ebs; + struct ubi_eba_info *eba = ubi->eba; + struct ubi_eba_tbl_volume *eba_tbl = eba->eba_tbl; + + dbg_eba("remove volume %d", vol_id); + + /* Input arguments sanity check */ + ubi_assert(vol_id >= 0); + ubi_assert(!ubi_is_ivol(vol_id)); + ubi_assert(vol_id < ubi->acc->max_volumes); + + if (ubi->io->ro_mode) { + dbg_err("read-only mode"); + return -EROFS; + } + + idx = vol_id2idx(ubi, vol_id); + + spin_lock(&eba->eba_tbl_lock); + ubi_assert(eba_tbl[idx].recs); + rm_ebs = eba_tbl[idx].recs; + eba_tbl[idx].recs = NULL; + eba_tbl[idx].leb_count = 0; + spin_unlock(&eba->eba_tbl_lock); + + ubi_kfree(rm_ebs); + return 0; +} + +int ubi_eba_rsvol(const struct ubi_info *ubi, int vol_id, int reserved_pebs) +{ + int err = 0, i, idx, min, to_put, sz; + struct ubi_eba_tbl_rec *new_ebs, *old_ebs; + struct ubi_eba_info *eba = ubi->eba; + struct ubi_eba_tbl_volume *eba_tbl = eba->eba_tbl; + + dbg_eba("re-size volume %d to %d PEBs", vol_id, reserved_pebs); + + /* Input arguments sanity check */ + ubi_assert(vol_id >= 0); + ubi_assert(!ubi_is_ivol(vol_id)); + ubi_assert(vol_id < ubi->acc->max_volumes); + ubi_assert(reserved_pebs > 0); + + if (ubi->io->ro_mode) { + dbg_err("read-only mode"); + return -EROFS; + } + + sz = reserved_pebs * sizeof(struct ubi_eba_tbl_rec); + new_ebs = ubi_kmalloc(sz); + if (!new_ebs) + return -ENOMEM; + + for (i = 0; i < reserved_pebs; i++) { + new_ebs[i].pnum = NOT_MAPPED; + new_ebs[i].leb_ver = 0; + } + + idx = vol_id2idx(ubi, vol_id); + + spin_lock(&eba->eba_tbl_lock); + ubi_assert(eba_tbl[idx].recs); + + if (reserved_pebs < eba_tbl[idx].leb_count) { + min = reserved_pebs; + to_put = eba_tbl[idx].leb_count - reserved_pebs; + } else { + min = eba_tbl[idx].leb_count; + to_put = 0; + } + + for (i = 0; i < min; i++) { + new_ebs[i].pnum = eba_tbl[idx].recs[i].pnum; + new_ebs[i].leb_ver = eba_tbl[idx].recs[i].leb_ver; + } + old_ebs = eba_tbl[idx].recs; + eba_tbl[idx].recs = new_ebs; + eba_tbl[idx].leb_count = reserved_pebs; + spin_unlock(&eba->eba_tbl_lock); + + for (i = 0; i < to_put; i++) + if (old_ebs[i].pnum >= 0) { + err = ubi_wl_put_peb(ubi, old_ebs[i].pnum, 0); + if (err) + break; + } + + ubi_kfree(old_ebs); + return err; +} + +int ubi_eba_erase_leb(const struct ubi_info *ubi, int vol_id, int lnum) +{ + int err, pnum; + + /* Input arguments sanity check */ + ubi_assert(vol_id >= 0); + ubi_assert(vol_id < ubi->acc->max_volumes || ubi_is_ivol(vol_id)); + ubi_assert(lnum >= 0); + ubi_assert(ubi->eba->eba_tbl[vol_id2idx(ubi, vol_id)].recs); + ubi_assert(lnum < ubi->eba->eba_tbl[vol_id2idx(ubi, vol_id)].leb_count); + + if (unlikely(ubi->io->ro_mode)) { + dbg_err("read-only mode"); + return -EROFS; + } + + err = ubi_eba_leb_write_lock(ubi, vol_id, lnum); + if (unlikely(err)) + return err; + + pnum = leb2peb(ubi, vol_id, lnum); + if (pnum < 0) { + /* This logical eraseblock is already unmapped */ + dbg_eba("erase LEB %d:%d (unmapped)", vol_id, lnum); + goto out_unlock; + } + dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum); + + leb_unmap(ubi, vol_id, lnum); + + err = ubi_wl_put_peb(ubi, pnum, 0); + +out_unlock: + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + return err; +} + +int ubi_eba_read_leb(const struct ubi_info *ubi, int vol_id, int lnum, + void *buf, int offset, int len, int check, int *read) +{ + int err, pnum, scrub = 0; + const struct ubi_vtbl_vtr *vtr; + uint32_t data_crc; + struct ubi_vid_hdr *vid_hdr; + + /* Input arguments sanity check */ + ubi_assert(vol_id >= 0); + ubi_assert(vol_id < ubi->acc->max_volumes || ubi_is_ivol(vol_id)); + ubi_assert(lnum >= 0); + ubi_assert(offset >= 0); + ubi_assert(len > 0); + + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + ubi_assert(!IS_ERR(vtr)); + ubi_assert(offset + len <= ubi->io->leb_size - vtr->data_pad); + ubi_assert(lnum < ubi->eba->eba_tbl[vol_id2idx(ubi, vol_id)].leb_count); + + *read = 0; + + err = ubi_eba_leb_read_lock(ubi, vol_id, lnum); + if (unlikely(err)) + return err; + + pnum = leb2peb(ubi, vol_id, lnum); + + if (pnum < 0) { + /* + * The logical eraseblock is not mapped, fill the whole buffer + * by 0xFF bytes. The exception is static volumes for which it + * is an error to read unmapped logical eraseblocks. + */ + dbg_eba("read %d bytes from offset %d of LEB %d:%d (unmapped)", + len, offset, vol_id, lnum); + ubi_eba_leb_read_unlock(ubi, vol_id, lnum); + ubi_assert(vtr->vol_type != UBI_STATIC_VOLUME); + memset(buf, 0xFF, len); + *read = len; + return 0; + } + dbg_eba("read %d bytes from offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + if (vtr->vol_type == UBI_DYNAMIC_VOLUME) + /* In case of dynamic volumes no checking is needed */ + check = 0; + + if (check) { + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (unlikely(!vid_hdr)) { + err = -ENOMEM; + goto out_unlock; + } + + err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1); + if (unlikely(err) && err != UBI_IO_BITFLIPS) { + if (err > 0) { + /* + * The header is either absent or corrupted. + * The former case means there is a bug - + * switch to read-only mode just in case. + * The latter case means a real corruption - we + * may try to recover data. FIXME: but this is + * not implemented. + */ + if (err == UBI_IO_BAD_VID_HDR) { + ubi_warn("bad VID header at PEB %d, LEB" + "%d:%d", pnum, vol_id, lnum); + err = -EBADMSG; + } else + ubi_eba_ro_mode(ubi); + } + goto out_free; + } + + if (unlikely(err == UBI_IO_BITFLIPS)) + scrub = 1; + + err = paranoid_check_leb(ubi, pnum, vol_id, lnum, + leb_get_ver(ubi, vol_id, lnum), + vid_hdr); + if (unlikely(err)) { + if (err > 0) + err = -EINVAL; + goto out_free; + } + + ubi_assert(lnum < ubi32_to_cpu(vid_hdr->used_ebs)); + ubi_assert(len == ubi32_to_cpu(vid_hdr->data_size)); + + data_crc = ubi32_to_cpu(vid_hdr->data_crc); + ubi_free_vid_hdr(ubi, vid_hdr); + } + + err = ubi_io_read_data(ubi, buf, pnum, offset, len, read); + if (unlikely(err) && err != UBI_IO_BITFLIPS && len != *read) + goto out_unlock; + if (unlikely(err == UBI_IO_BITFLIPS)) + scrub = 1; + + if (check) { + uint32_t crc; + + crc = crc32(UBI_CRC32_INIT, buf, len); + if (unlikely(crc != data_crc)) { + ubi_warn("CRC error: calculated %#08x, must be %#08x", + crc, data_crc); + err = -EBADMSG; + goto out_unlock; + } + + err = 0; + dbg_eba("data is OK, CRC matches"); + } + + if (unlikely(scrub)) + err = ubi_wl_scrub_peb(ubi, pnum); + + ubi_eba_leb_read_unlock(ubi, vol_id, lnum); + return err; + +out_free: + ubi_free_vid_hdr(ubi, vid_hdr); +out_unlock: + ubi_eba_leb_read_unlock(ubi, vol_id, lnum); + return err; +} + +int ubi_eba_write_leb(const struct ubi_info *ubi, int vol_id, int lnum, + const void *buf, int offset, int len, + enum ubi_data_type dtype, int *written, + const void *ivol_data) +{ + int err, pnum, tries = 0; + uint32_t leb_ver; + struct ubi_vid_hdr *vid_hdr; + const struct ubi_vtbl_vtr *vtr; + const struct ubi_io_info *io = ubi->io; + +retry: + /* Input arguments sanity check */ + ubi_assert(vol_id >= 0); + ubi_assert(vol_id < ubi->acc->max_volumes || ubi_is_ivol(vol_id)); + ubi_assert(lnum >= 0); + ubi_assert(offset >= 0); + ubi_assert(len >= 0); + ubi_assert(dtype == UBI_DATA_LONGTERM || dtype == UBI_DATA_SHORTTERM || + dtype == UBI_DATA_UNKNOWN); + + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + ubi_assert(!IS_ERR(vtr)); + ubi_assert(offset + len <= io->leb_size - vtr->data_pad); + ubi_assert(lnum < ubi->eba->eba_tbl[vol_id2idx(ubi, vol_id)].leb_count); + ubi_assert(len % io->min_io_size == 0); + ubi_assert(offset % io->min_io_size == 0); + ubi_assert(vtr->vol_type == UBI_DYNAMIC_VOLUME); + + *written = 0; + + if (unlikely(ubi->io->ro_mode)) { + dbg_err("read-only mode"); + return -EROFS; + } + + err = ubi_eba_leb_write_lock(ubi, vol_id, lnum); + if (unlikely(err)) + return err; + + pnum = leb2peb(ubi, vol_id, lnum); + leb_ver = leb_get_ver(ubi, vol_id, lnum); + if (pnum >= 0) { + dbg_eba("write %d bytes at offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + if (len != 0) { + err = ubi_io_write_data(ubi, buf, pnum, offset, len, + written); + if (unlikely(err)) + goto data_write_error; + } + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + return err; + } + + /* + * The logical eraseblock is not mapped. We have to get a free physical + * eraseblock and write the volume identifier header there first. + */ + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (unlikely(!vid_hdr)) { + err = -ENOMEM; + goto out_unlock; + } + + vid_hdr->vol_type = UBI_VID_DYNAMIC; + vid_hdr->leb_ver = cpu_to_ubi32(leb_ver); + vid_hdr->vol_id = cpu_to_ubi32(vol_id); + vid_hdr->lnum = cpu_to_ubi32(lnum); + vid_hdr->compat = ubi_ivol_get_compat(ubi, vol_id); + vid_hdr->data_pad = cpu_to_ubi32(vtr->data_pad); + if (ivol_data) { + ubi_assert(ubi_is_ivol(vol_id)); + memcpy(&vid_hdr->ivol_data[0], ivol_data, + UBI_VID_HDR_IVOL_DATA_SIZE); + } + + pnum = ubi_wl_get_peb(ubi, dtype); + if (unlikely(pnum < 0)) { + err = pnum; + goto out_vid_hdr; + } + dbg_eba("write VID hdr and %d bytes at offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (unlikely(err)) + goto hdr_write_error; + + leb_map(ubi, vol_id, lnum, pnum); + + if (len == 0) + *written = 0; + else { + err = ubi_io_write_data(ubi, buf, pnum, offset, len, written); + if (unlikely(err)) + goto data_write_error_free; + } + + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + +out_vid_hdr: + ubi_free_vid_hdr(ubi, vid_hdr); +out_unlock: + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + return err; + + /* Failed to write the volume identifier header */ +hdr_write_error: + ubi_warn("failed to write VID header to PEB %d", pnum); + ubi_free_vid_hdr(ubi, vid_hdr); + if (err != -EIO || !io->bad_allowed) + goto no_bad_eraseblocks; + + /* + * Fortunately, we did not write any data there yet, so just put this + * physical eraseblock and request a new one. We assume that if this + * physical eraseblock went bad, the erase code will handle that. + */ + ubi_msg("try to recover form the error"); + err = ubi_wl_put_peb(ubi, pnum, 1); + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + if (err || ++tries > 5) + return err; + goto retry; + + /* Failed to write data */ +data_write_error_free: + ubi_free_vid_hdr(ubi, vid_hdr); +data_write_error: + ubi_warn("failed to write data to PEB %d", pnum); + if (err != -EIO || !io->bad_allowed) + goto no_bad_eraseblocks; + + err = ubi_beb_recover_peb(ubi, pnum, vol_id, lnum, buf, offset, len); + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + return err; + + /* + * This flash device does not admit of bad eraseblocks or something + * nasty and unexpected happened. Switch to read-only mode just in + * case. + */ +no_bad_eraseblocks: + ubi_eba_ro_mode(ubi); + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + return err; +} + +int ubi_eba_write_leb_st(const struct ubi_info *ubi, int vol_id, int lnum, + const void *buf, int len, enum ubi_data_type dtype, + int *written, int used_ebs) +{ + int err, pnum, data_size = len, tries = 0; + uint32_t leb_ver, crc; + struct ubi_vid_hdr *vid_hdr; + const struct ubi_vtbl_vtr *vtr; + const struct ubi_io_info *io = ubi->io; + +retry: + /* Input arguments sanity check */ + ubi_assert(vol_id >= 0); + ubi_assert(vol_id < ubi->acc->max_volumes || ubi_is_ivol(vol_id)); + ubi_assert(lnum >= 0); + ubi_assert(len >= 0); + ubi_assert(dtype == UBI_DATA_LONGTERM || dtype == UBI_DATA_SHORTTERM || + dtype == UBI_DATA_UNKNOWN); + + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + ubi_assert(!IS_ERR(vtr)); + ubi_assert(lnum < ubi->eba->eba_tbl[vol_id2idx(ubi, vol_id)].leb_count); + ubi_assert(lnum < used_ebs); + ubi_assert(len > 0); + ubi_assert(used_ebs >= 0); + ubi_assert(vtr->vol_type == UBI_STATIC_VOLUME); + + *written = 0; + + if (lnum == used_ebs - 1) { + /* + * If this is the last logical eraseblock of a static + * volume, @len may be unaligned. + */ + ubi_assert(len <= io->leb_size - vtr->data_pad); + len = align_up(data_size, io->min_io_size); + } else { + ubi_assert(len == io->leb_size - vtr->data_pad); + ubi_assert(len % io->min_io_size == 0); + } + + if (unlikely(ubi->io->ro_mode)) { + dbg_err("read-only mode"); + return -EROFS; + } + + err = ubi_eba_leb_write_lock(ubi, vol_id, lnum); + if (unlikely(err)) + return err; + + ubi_assert(leb2peb(ubi, vol_id, lnum) < 0); + + /* + * Get a free physical eraseblock and write the volume identifier + * header. + */ + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (unlikely(!vid_hdr)) { + err = -ENOMEM; + goto out_unlock; + } + + leb_ver = leb_get_ver(ubi, vol_id, lnum); + vid_hdr->leb_ver = cpu_to_ubi32(leb_ver); + vid_hdr->vol_id = cpu_to_ubi32(vol_id); + vid_hdr->lnum = cpu_to_ubi32(lnum); + vid_hdr->compat = ubi_ivol_get_compat(ubi, vol_id); + vid_hdr->data_pad = cpu_to_ubi32(vtr->data_pad); + + crc = crc32(UBI_CRC32_INIT, buf, data_size); + vid_hdr->vol_type = UBI_VID_STATIC; + vid_hdr->data_size = cpu_to_ubi32(data_size); + vid_hdr->used_ebs = cpu_to_ubi32(used_ebs); + vid_hdr->data_crc = cpu_to_ubi32(crc); + + pnum = ubi_wl_get_peb(ubi, dtype); + if (unlikely(pnum < 0)) { + err = pnum; + goto out_vid_hdr; + } + dbg_eba("write VID hdr and %d bytes at of LEB %d:%d, PEB %d", + len, vol_id, lnum, pnum); + + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (unlikely(err)) { + ubi_warn("failed to write VID header to PEB %d", pnum); + goto write_error; + } + + leb_map(ubi, vol_id, lnum, pnum); + + err = ubi_io_write_data(ubi, buf, pnum, 0, len, written); + if (unlikely(err)) { + ubi_warn("failed to write data to PEB %d", pnum); + goto write_error; + } + + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + +out_vid_hdr: + ubi_free_vid_hdr(ubi, vid_hdr); +out_unlock: + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + return err; + + /* Write failure */ +write_error: + ubi_free_vid_hdr(ubi, vid_hdr); + ubi_free_vid_hdr(ubi, vid_hdr); + if (err != -EIO || !io->bad_allowed) + goto no_bad_eraseblocks; + + /* + * We assume that if this physical eraseblock went bad - the erase code + * will handle that. + */ + ubi_msg("try to recover form the error"); + err = ubi_wl_put_peb(ubi, pnum, 1); + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + if (err || ++tries > 5) + return err; + goto retry; + + /* + * This flash device does not admit of bad eraseblocks or something + * nasty and unexpected happened. Switch to read-only mode just in + * case. + */ +no_bad_eraseblocks: + ubi_eba_ro_mode(ubi); + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + return err; +} + +int ubi_eba_leb_is_mapped(const struct ubi_info *ubi, int vol_id, int lnum) +{ + dbg_eba("check LEB %d:%d PEBs", vol_id, lnum); + + /* Input arguments sanity check */ + ubi_assert(vol_id >= 0); + ubi_assert(vol_id < ubi->acc->max_volumes); + ubi_assert(lnum >= 0); + ubi_assert(lnum < ubi->eba->eba_tbl[vol_id2idx(ubi, vol_id)].leb_count); + + return leb2peb(ubi, vol_id, lnum) >= 0; +} + +void ubi_eba_ro_mode(const struct ubi_info *ubi) +{ + ubi_bgt_disable(ubi); + ubi->io->ro_mode = 1; + ubi_warn("switched to read-only mode"); +} + +/** + * ltree_lookup - look up the lock tree. + * + * @eba: the EBA unit description data structure + * @vol_id: volume ID of the logical eraseblock to look up + * @lnum: the logical eraseblock number to look up + * + * This function returns a pointer to the corresponding &struct ubi_eba_info + * object if the logical eraseblock is locked and %NULL if it is not locked. + * + * The @eba->ltree_lock has to be locked. + * + * This is a helper function for the logical eraseblock locking/unlocking + * functions. + */ +static inline struct ubi_eba_ltree_entry * +ltree_lookup(struct ubi_eba_info *eba, int vol_id, int lnum) +{ + struct rb_node *p; + + p = eba->ltree.rb_node; + while (p) { + struct ubi_eba_ltree_entry *le; + + le = rb_entry(p, struct ubi_eba_ltree_entry, rb); + + if (vol_id < le->vol_id) + p = p->rb_left; + else if (vol_id > le->vol_id) + p = p->rb_right; + else { + if (lnum < le->lnum) + p = p->rb_left; + else if (lnum > le->lnum) + p = p->rb_right; + else + return le; + } + } + + return NULL; +} + +static struct ubi_eba_ltree_entry *ltree_add_entry(const struct ubi_info *ubi, + int vol_id, int lnum); + +int ubi_eba_leb_read_lock(const struct ubi_info *ubi, int vol_id, int lnum) +{ + struct ubi_eba_ltree_entry *le; + + le = ltree_add_entry(ubi, vol_id, lnum); + if (unlikely(IS_ERR(le))) + return PTR_ERR(le); + down_read(&le->mutex); + return 0; +} + +int ubi_eba_leb_write_lock(const struct ubi_info *ubi, int vol_id, int lnum) +{ + struct ubi_eba_ltree_entry *le; + + le = ltree_add_entry(ubi, vol_id, lnum); + if (unlikely(IS_ERR(le))) + return PTR_ERR(le); + down_write(&le->mutex); + return 0; +} + +void ubi_eba_leb_read_unlock(const struct ubi_info *ubi, int vol_id, int lnum) +{ + int free = 0; + struct ubi_eba_ltree_entry *le; + struct ubi_eba_info *eba = ubi->eba; + + spin_lock(&eba->ltree_lock); + le = ltree_lookup(ubi->eba, vol_id, lnum); + le->users -= 1; + ubi_assert(le->users >= 0); + if (le->users == 0) { + rb_erase(&le->rb, &eba->ltree); + free = 1; + } + spin_unlock(&eba->ltree_lock); + + up_read(&le->mutex); + if (free) + ubi_free_eba_ltree_entry(le); +} + +void ubi_eba_leb_write_unlock(const struct ubi_info *ubi, int vol_id, int lnum) +{ + int free; + struct ubi_eba_ltree_entry *le; + struct ubi_eba_info *eba = ubi->eba; + + spin_lock(&eba->ltree_lock); + le = ltree_lookup(ubi->eba, vol_id, lnum); + le->users -= 1; + ubi_assert(le->users >= 0); + if (le->users == 0) { + rb_erase(&le->rb, &eba->ltree); + free = 1; + } else + free = 0; + spin_unlock(&eba->ltree_lock); + + up_write(&le->mutex); + if (free) + ubi_free_eba_ltree_entry(le); +} + +void ubi_eba_leb_remap(const struct ubi_info *ubi, int vol_id, int lnum, + int pnum) +{ + /* The logical eraseblock is supposed to be locked */ + paranoid_check_leb_locked(ubi, vol_id, lnum); + leb_unmap(ubi, vol_id, lnum); + leb_map(ubi, vol_id, lnum, pnum); +} + +static int __init build_eba_tbl(const struct ubi_info *ubi, + const struct ubi_scan_info *si); + +int __init ubi_eba_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si) +{ + int err; + size_t sz; + struct ubi_eba_info *eba; + struct ubi_acc_info *acc = ubi->acc; + + dbg_eba("initialize the EBA unit"); + + eba = ubi_kzalloc(sizeof(struct ubi_eba_info)); + if (!eba) + return -ENOMEM; + ubi->eba = eba; + + spin_lock_init(&eba->eba_tbl_lock); + spin_lock_init(&eba->ltree_lock); + eba->ltree = RB_ROOT; + + eba->num_volumes = acc->max_volumes + acc->ivol_count; + sz = eba->num_volumes * sizeof(struct ubi_eba_tbl_volume); + eba->eba_tbl = ubi_kzalloc(sz); + if (!eba->eba_tbl) { + err = -ENOMEM; + goto out; + } + + err = build_eba_tbl(ubi, si); + if (err) + goto out; + + dbg_eba("the EBA unit is initialized"); + return 0; + +out: + ubi_kfree(eba->eba_tbl); + ubi_kfree(eba); + return err; +} + +void __exit ubi_eba_close(const struct ubi_info *ubi) +{ + unsigned int i; + struct ubi_eba_info *eba = ubi->eba; + + dbg_eba("close EBA management unit"); + + + for (i = 0; i < eba->num_volumes; i++) + ubi_kfree(eba->eba_tbl[i].recs); + ubi_kfree(eba->eba_tbl); + ubi_kfree(eba); +} + +/** + * build_eba_tbl - build the eraseblock association table. + * + * @ubi: the UBI device description object + * @si: scanning info + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int __init build_eba_tbl(const struct ubi_info *ubi, + const struct ubi_scan_info *si) +{ + int i, err, idx; + struct ubi_eba_info *eba = ubi->eba; + struct ubi_eba_tbl_volume *eba_tbl = eba->eba_tbl; + + for (idx = 0; idx < eba->num_volumes; idx++) { + struct rb_node *rb; + struct ubi_scan_leb *seb; + struct ubi_scan_volume *sv; + const struct ubi_vtbl_vtr *vtr; + size_t sz; + + cond_resched(); + + vtr = ubi_vtbl_get_vtr(ubi, idx2vol_id(ubi, idx)); + if (IS_ERR(vtr)) + continue; + + dbg_eba("found volume %d (idx %d)", idx2vol_id(ubi, idx), idx); + + eba_tbl[idx].leb_count = vtr->reserved_pebs; + + sz = vtr->reserved_pebs * sizeof(struct ubi_eba_tbl_rec); + eba_tbl[idx].recs = ubi_kmalloc(sz); + if (unlikely(!eba_tbl[idx].recs)) { + err = -ENOMEM; + goto out; + } + + for (i = 0; i < vtr->reserved_pebs; i++) { + eba->eba_tbl[idx].recs[i].pnum = NOT_MAPPED; + eba->eba_tbl[idx].recs[i].leb_ver = 0; + } + + sv = ubi_scan_get_scan_volume(si, idx2vol_id(ubi, idx)); + if (!sv) + continue; + + rb_for_each_entry(rb, seb, &sv->root, rb) { + eba->eba_tbl[idx].recs[seb->lnum].pnum = seb->pnum; + eba->eba_tbl[idx].recs[seb->lnum].leb_ver = seb->leb_ver; + } + } + + return 0; + +out: + for (i = 0; i < eba->num_volumes; i++) + ubi_kfree(eba->eba_tbl[i].recs); + + return err; +} + +/** + * ltree_add_entry - add new entry to the lock tree. + * + * @ubi: the UBI device description object + * @vol_id: volume ID of the logical eraseblock + * @lnum: the logical eraseblock number + * + * This function add new lock tree entry for logical eraseblock + * (@vol_id,@lnum). If the corresponding entry is already there, its usage + * counter is increased. This function returns a pointer to the lock tree + * entry. + */ +static struct ubi_eba_ltree_entry *ltree_add_entry(const struct ubi_info *ubi, + int vol_id, int lnum) +{ + struct ubi_eba_info *eba = ubi->eba; + struct ubi_eba_ltree_entry *le, *le1, *le_free; + + le = ubi_alloc_eba_ltree_entry(); + if (unlikely(!le)) + return ERR_PTR(-ENOMEM); + + le->vol_id = vol_id; + le->lnum = lnum; + + spin_lock(&eba->ltree_lock); + le1 = ltree_lookup(eba, vol_id, lnum); + + if (le1) { + /* + * This logical eraseblock is already locked. The newly + * allocated lock entry is not needed. + */ + le_free = le; + le = le1; + } else { + struct rb_node **p, *parent = NULL; + + /* + * No lock entry, add the newly allocated one to the + * @eba->ltree RB-tree. + */ + le_free = NULL; + + p = &eba->ltree.rb_node; + while (*p) { + parent = *p; + le1 = rb_entry(parent, struct ubi_eba_ltree_entry, rb); + + if (vol_id < le1->vol_id) + p = &(*p)->rb_left; + else if (vol_id > le1->vol_id) + p = &(*p)->rb_right; + else { + ubi_assert(lnum != le1->lnum); + if (lnum < le1->lnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + } + + rb_link_node(&le->rb, parent, p); + rb_insert_color(&le->rb, &eba->ltree); + } + le->users += 1; + spin_unlock(&eba->ltree_lock); + + if (le_free) + ubi_free_eba_ltree_entry(le_free); + + return le; +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_EBA + +/** + * paranoid_check_leb - check that a logical eraseblock has correct erase + * counter and volume identifier headers. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock number + * @vol_id: the volume ID to check + * @lnum: the logical eraseblock number to check + * @leb_ver: the logical eraseblock version to check + * @vid_hdr: volume identifier header to check + * + * This function returns zero if the headers are all right, %1 if not, and a + * negative error code in case of error. + */ +static int paranoid_check_leb(const struct ubi_info *ubi, int pnum, int vol_id, + int lnum, int leb_ver, + const struct ubi_vid_hdr *vid_hdr) +{ + int err, hdr_vol_id, hdr_lnum, hdr_leb_ver; + struct ubi_ec_hdr *ec_hdr; + + /* Check the EC header */ + ec_hdr = ubi_zalloc_ec_hdr(ubi); + if (unlikely(!ec_hdr)) + return -ENOMEM; + + err = ubi_io_read_ec_hdr(ubi, pnum, ec_hdr, 1); + ubi_free_ec_hdr(ubi, ec_hdr); + if (unlikely(err) && err != UBI_IO_BITFLIPS) { + if (err < 0) + return err; + goto fail; + } + + hdr_vol_id = ubi32_to_cpu(vid_hdr->vol_id); + hdr_lnum = ubi32_to_cpu(vid_hdr->lnum); + hdr_leb_ver = ubi32_to_cpu(vid_hdr->leb_ver); + + if (unlikely(vol_id != hdr_vol_id)) { + ubi_err("bad vol_id %d, should be %d", hdr_vol_id, vol_id); + goto fail; + } + + if (unlikely(lnum != hdr_lnum)) { + ubi_err("bad lnum %d, should be %d", hdr_lnum, lnum); + goto fail; + } + + if (unlikely(leb_ver != hdr_leb_ver)) { + ubi_err("bad leb_ver %d, should be %d", hdr_leb_ver, leb_ver); + goto fail; + } + + return 0; + +fail: + ubi_err("paranoid check failed"); + dump_stack(); + return 1; +} + +/** + * paranoid_check_leb_locked - ensure that a logical eraseblock is locked. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID to check + * @lnum: the logical eraseblock number to check + * + * This function returns zero if the logical eraseblock is locked and %1 if + * not. + */ +static int paranoid_check_leb_locked(const struct ubi_info *ubi, int vol_id, + int lnum) +{ + struct ubi_eba_ltree_entry *le; + struct ubi_eba_info *eba = ubi->eba; + + spin_lock(&eba->ltree_lock); + le = ltree_lookup(ubi->eba, vol_id, lnum); + spin_unlock(&eba->ltree_lock); + if (likely(le)) + return 0; + + ubi_err("paranoid check failed"); + dump_stack(); + return 1; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID_EBA */ diff --git a/drivers/mtd/ubi/eba.h b/drivers/mtd/ubi/eba.h new file mode 100644 index 0000000..a40f300 --- /dev/null +++ b/drivers/mtd/ubi/eba.h @@ -0,0 +1,357 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * The UBI Eraseblock Association (EBA) unit. + * + * The main goal of this unit is to maintain the Eraseblock Association Table + * (EBA table). The EBA table is a data structure which maps (volume ID, + * logical eraseblock number) pairs to physical eraseblock numbers. + * + * Note, it is supposed that all the UBI input/output goes via the EBA unit. + * The only reservation should be made for the initialization time when + * different units may directly do input/output from physical eraseblocks. + * + * Although in this implementation the EBA table is fully kept and managed in + * RAM, which assumes poor UBI scalability, it might be (partially) maintained + * on flash in future implementations. + */ + +#ifndef __UBI_EBA_H__ +#define __UBI_EBA_H__ + +#include +#include +#include +#include +#include + +struct ubi_info; +struct ubi_scan_info; + +/** + * ubi_eba_mkvol - create EBA mapping for a new volume. + * + * @ubi: the UBI device description object + * @vol_id: ID of the new volume + * @leb_count: how many eraseblocks are reserved for this volume + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_eba_mkvol(const struct ubi_info *ubi, int vol_id, int leb_count); + +/** + * ubi_eba_rmvol - remove EBA mapping for a volume. + * + * @ubi: the UBI device description object + * @vol_id: ID of the volume to be removed + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_eba_rmvol(const struct ubi_info *ubi, int vol_id); + +/** + * ubi_eba_rsvol - re-size EBA mapping for a volume. + * + * @ubi: the UBI device description object + * @vol_id: ID of the volume to be re-sized + * @reserved_pebs: new count of physical eraseblocks in this volume + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_eba_rsvol(const struct ubi_info *ubi, int vol_id, int reserved_pebs); + +/** + * ubi_eba_erase_leb - erase a logical eraseblock. + * + * @ubi: the UBI device description object + * @vol_id: volume ID + * @lnum: the logical eraseblock number to erase + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_eba_erase_leb(const struct ubi_info *ubi, int vol_id, int lnum); + +/** + * ubi_eba_read_leb - read data from a logical eraseblock. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID from where to read + * @lnum: the logical eraseblock number to read from + * @buf: the buffer to store the read data + * @offset: the offset within the logical eraseblock from where to read + * @len: how many bytes to read + * @check: data CRC check flag + * @read: the number of actually read bytes is returned here + * + * If the logical eraseblock @lnum is unmapped, @buf is filled by 0xFF bytes. + * The @check flag only makes sense for static volumes and forces eraseblock + * data CRC checking. The @read field contains the number of successfully read + * bytes. + * + * In case of success this function returns zero. If the @check flag is set, + * @vol_id is a static volume, and the data CRC mismatches - %-EBADMSG is + * returned. %-EBADMSG may also be returned for any volume type if an ECC error + * was detected by the MTD device driver. + * + * Other negative error cored may be returned in case of other errors. In any + * case, the @read argument contains the number of read bytes. Note, if an + * error is returned, the read data may be incorrect. + */ +int ubi_eba_read_leb(const struct ubi_info *ubi, int vol_id, int lnum, + void *buf, int offset, int len, int check, int *read); + +/** + * ubi_eba_write_leb - write data to a logical eraseblock of a dynamic volume. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID where to write + * @lnum: the logical eraseblock number to write + * @buf: the data to write + * @offset: the offset within the logical eraseblock where to write + * @len: how many bytes to write + * @dtype: data type + * @written: how many bytes were actually written + * @ivol_data: private data to put to the VID header (used only for internal + * volumes) + * + * This function writes data to a logical eraseblock of a dynamic volume. + * Returns zero in case of success and a negative error code in case of + * failure. The @written field contains the number of successfully written + * bytes. + */ +int ubi_eba_write_leb(const struct ubi_info *ubi, int vol_id, int lnum, + const void *buf, int offset, int len, + enum ubi_data_type dtype, int *written, + const void *ivol_data); + +/** + * ubi_eba_write_leb_st - write data to a logical eraseblock of a static volume. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID where to write + * @lnum: the logical eraseblock number to write + * @buf: the data to write + * @len: how many bytes to write + * @dtype: data type + * @written: how many bytes were actually written + * @used_ebs: how many logical eraseblocks will this volume contain (used only + * for static volumes) + * + * This function writes data to a logical eraseblock of a static volume. The + * @used_ebs argument should contain total number of logical eraseblock which + * will contain any data in this static volume. + * + * When writing to the last logical eraseblock of a static volume, the @len + * argument doesn't have to be aligned to the minimal I/O unit size. Instead, + * it has to be equivalent to the real data size, although the @buf buffer has + * to contain the alignment. In all other cases, @len has to be aligned. + * + * Note, it is prohibited to write more then once to logical eraseblocks of + * static volumes. + * + * This function returns zero in case of success and a negative error code in + * case of failure. The @written field contains the number of successfully + * written bytes. + */ +int ubi_eba_write_leb_st(const struct ubi_info *ubi, int vol_id, int lnum, + const void *buf, int len, enum ubi_data_type dtype, + int *written, int used_ebs); + +/** + * ubi_eba_leb_is_mapped - check if a logical eraseblock is mapped. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID + * @lnum: the logical eraseblock number to check + * + * This function checks if a logical eraseblock is mapped to a physical + * eraseblock. Returns %1 if it is mapped, %0 if not, and a negative error + * code in case of failure. + */ +int ubi_eba_leb_is_mapped(const struct ubi_info *ubi, int vol_id, int lnum); + +/** + * ubi_eba_leb_read_lock - lock a logical eraseblock for reading. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID + * @lnum: the logical eraseblock number to lock + * + * This function locks a logical eraseblock for reading which means that all + * writers will be locked waiting while the logical eraseblock is stopped being + * used. + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_eba_leb_read_lock(const struct ubi_info *ubi, int vol_id, int lnum); + +/** + * ubi_eba_leb_write_lock - lock a logical eraseblock for writing. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID + * @lnum: the logical eraseblock number to lock + * + * This function locks a logical eraseblock for writing which means that all + * further readers and writers will be locked waiting while the logical + * eraseblock is stopped being used. + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_eba_leb_write_lock(const struct ubi_info *ubi, int vol_id, int lnum); + +/** + * ubi_eba_leb_read_unlock - unlock a logical eraseblock locked for reading. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID + * @lnum: the logical eraseblock number to unlock + */ +void ubi_eba_leb_read_unlock(const struct ubi_info *ubi, int vol_id, int lnum); + +/** + * ubi_eba_leb_write_unlock - unlock a logical eraseblock locked for writing. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID + * @lnum: the logical eraseblock number to unlock + */ +void ubi_eba_leb_write_unlock(const struct ubi_info *ubi, int vol_id, int lnum); + +/** + * ubi_eba_leb_remap - re-map a logical eraseblock to another physical + * eraseblock. + * + * @ubi: the UBI device description object + * @vol_id: the volume ID + * @lnum: the logical eraseblock number + * @pnum: new physical eraseblock to map to + * + * The logical eraseblock must be locked before re-mapping. + */ +void ubi_eba_leb_remap(const struct ubi_info *ubi, int vol_id, int lnum, + int pnum); + +/** + * ubi_eba_ro_mode - switch to read-only mode. + * + * @ubi: the UBI device description object + */ +void ubi_eba_ro_mode(const struct ubi_info *ubi); + +/** + * ubi_eba_init_scan - initialize the EBA unit using scanning information. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int __init ubi_eba_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si); + +/** + * ubi_eba_close - close the EBA unit. + * + * @ubi: the UBI device description object + */ +void __exit ubi_eba_close(const struct ubi_info *ubi); + +/** + * struct ubi_eba_tbl_rec - a record in the eraseblock association table. + * + * @pnum: physical eraseblock number + * @leb_ver: logical eraseblock version + * + * This structure represents a record in the eraseblock association table. + */ +struct ubi_eba_tbl_rec { + int pnum; + uint32_t leb_ver; +}; + +/** + * struct ubi_eba_tbl_volume - a volume in the the eraseblock association + * table. + * + * @recs: an array of per-logical eraseblock records (for each logical + * eraseblock of this volume) + * @leb_count: how many logical eraseblock this volume has + */ +struct ubi_eba_tbl_volume { + struct ubi_eba_tbl_rec *recs; + int leb_count; +}; + +/** + * struct ubi_eba_ltree_entry - an entry in the lock tree. + * + * @rb: link in the RB-tree + * @vol_id: volume ID of the locked logical eraseblock + * @lnum: the locked logical eraseblock number + * @users: how many tasks are using this logical eraseblock or wait for it + * @mutex: a read/write mutex to implement read/write access serialization to + * the (@vol_id, @lnum) logical eraseblock + * + * This data structured is used to lock a logical eraseblock - a corresponding + * &struct ubi_eba_ltree_entry is created and inserted to the lock tree + * (@eba->ltree). + */ +struct ubi_eba_ltree_entry { + struct rb_node rb; + int vol_id; + int lnum; + int users; + struct rw_semaphore mutex; +}; + +/** + * struct ubi_eba_info - UBI EBA unit description data structure. + * + * @eba_tbl: the eraseblock association table + * @eba_tbl_lock: protects the EBA table + * @ltree: the lock tree + * @ltree_lock: protects the lock tree + * @num_volumes: number of volumes mapped by the EBA table + * + * The EBA unit implements per-logical eraseblock locking. Before accessing a + * logical eraseblock it is locked for reading or writing. The per-logical + * eraseblock locking is implemented by means of the lock tree. + * + * The lock tree is an RB-tree which refers all the currently locked logical + * eraseblocks. The lock tree elements are &struct ubi_eba_ltree_entry objects. + * They are indexed by (@vol_id,@lnum) pairs. + */ +struct ubi_eba_info { + struct ubi_eba_tbl_volume *eba_tbl; /* private */ + spinlock_t eba_tbl_lock; /* private */ + struct rb_root ltree; /* private */ + spinlock_t ltree_lock; /* private */ + size_t num_volumes; /* private */ +}; + +#endif /* !__UBI_EBA_H__ */ diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c new file mode 100644 index 0000000..6e5a0de --- /dev/null +++ b/drivers/mtd/ubi/gluebi.c @@ -0,0 +1,351 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy (based on code written by Joern Engel) + */ + +#include +#include +#include +#include "ubi.h" +#include "uif.h" +#include "gluebi.h" +#include "vtbl.h" +#include "misc.h" +#include "alloc.h" +#include "io.h" +#include "eba.h" + +static int gluebi_get_device(struct mtd_info *mtd); +static void gluebi_put_device(struct mtd_info *mtd); +static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, unsigned char *buf); +static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf); +static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr); + +/** + * mtd2vol - take the user interface volume description object by an MTD object. + * + * @mtd: the MTD object + * + * This function returns the user interface volume description object + * corresponding to the @mtd object. + */ +static inline struct ubi_uif_volume *mtd2vol(struct mtd_info *mtd) +{ + struct ubi_gluebi_volume *gluebi_vol; + struct ubi_uif_volume *vol; + + gluebi_vol = container_of(mtd, struct ubi_gluebi_volume, mtd); + vol = container_of(gluebi_vol, struct ubi_uif_volume, gluebi_vol); + return vol; +} + +int ubi_gluebi_vol_init(const struct ubi_info *ubi, struct ubi_uif_volume *vol) +{ + int err; + struct mtd_info *mtd = &vol->gluebi_vol.mtd; + const struct ubi_vtbl_vtr *vtr; + const struct ubi_io_info *io = ubi->io; + + vtr = ubi_vtbl_get_vtr(ubi, vol->vol_id); + ubi_assert(!IS_ERR(vtr)); + + mtd->name = strdup_len(vtr->name, vtr->name_len); + if (!mtd->name) + return -ENOMEM; + + mtd->type = MTD_UBIVOLUME; + if (!io->ro_mode) + mtd->flags = MTD_WRITEABLE; + mtd->writesize = io->min_io_size; + mtd->owner = THIS_MODULE; + mtd->size = vtr->usable_leb_size * vtr->reserved_pebs; + mtd->erasesize = vtr->usable_leb_size; + mtd->read = gluebi_read; + mtd->write = gluebi_write; + mtd->erase = gluebi_erase; + mtd->get_device = gluebi_get_device; + mtd->put_device = gluebi_put_device; + + if (add_mtd_device(mtd)) { + ubi_err("cannot not add MTD device\n"); + + /* + * Unfortunately, add_mtd_device() does not return sane error + * code. So, let's name it -ENOMEM; + */ + err = -ENOMEM; + goto out_free; + } + + dbg_gluebi("added mtd%d (\"%s\"), size %u, EB size %u", + mtd->index, mtd->name, mtd->size, mtd->erasesize); + + return 0; + +out_free: + ubi_kfree(mtd->name); + return err; +} + +int ubi_gluebi_vol_close(struct ubi_uif_volume *vol) +{ + int err; + struct mtd_info *mtd = &vol->gluebi_vol.mtd; + + dbg_gluebi("remove mtd%d", mtd->index); + + err = del_mtd_device(mtd); + if (err) + return err; + + ubi_kfree(mtd->name); + return 0; +} + +/** + * gluebi_get_device - get MTD device reference. + * + * @mtd: the MTD device description object + * + * This function is called every time the MTD device is being got. Returns + * zero in case of success and a negative error code in case of failure. + */ +static int gluebi_get_device(struct mtd_info *mtd) +{ + struct ubi_uif_volume *vol = mtd2vol(mtd); + struct ubi_gluebi_volume *gluebi_vol = &vol->gluebi_vol; + + /* + * We do not introduce locks for gluebi reference count because the + * get_device()/put_device() calls are already serialized. + */ + if (gluebi_vol->refcount > 0) { + /* + * The MTD device is already referenced and this is just one + * more reference. MTD allows opening many users to open the + * same volume simultaniously and do not distinguish between + * readers/writers/exclusive openers as UBI does. So we do not + * open the UBI volume again - just increase the reference + * counter and return. + */ + gluebi_vol->refcount += 1; + return 0; + } + + /* + * This is the first reference to this UBI volume via the MTD device + * interface. Open the corresponding volume in read-write mode. + */ + gluebi_vol->desc = ubi_open_volume(vol->ubi->ubi_num, vol->vol_id, + UBI_READWRITE); + if (IS_ERR(gluebi_vol->desc)) + return PTR_ERR(gluebi_vol->desc); + gluebi_vol->refcount += 1; + return 0; +} + +/** + * gluebi_put_device - put MTD device reference. + * + * @mtd: the MTD device description object + * + * This function is called every time the MTD device is being put. Returns + * zero in case of success and a negative error code in case of failure. + */ +static void gluebi_put_device(struct mtd_info *mtd) +{ + struct ubi_gluebi_volume *gluebi_vol = &mtd2vol(mtd)->gluebi_vol; + + gluebi_vol->refcount -= 1; + ubi_assert(gluebi_vol->refcount >= 0); + if (gluebi_vol->refcount == 0) + ubi_close_volume(gluebi_vol->desc); +} + +/** + * gluebi_read - read operation of emulated MTD devices. + * + * @mtd: the MTD device description object + * @from: absolute offset from where to read + * @len: how many bytes to read + * @retlen: count of read bytes is returned here + * @buf: the buffer to store the data to + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, unsigned char *buf) +{ + int err = 0, lnum, offs, read = 0; + struct ubi_uif_volume *vol; + const struct ubi_info *ubi; + + dbg_gluebi("read %zd bytes from offset %lld", len, from); + + if (len < 0 || from < 0 || from + len > mtd->size) + return -EINVAL; + + vol = mtd2vol(mtd); + ubi = vol->ubi; + + offs = do_div(from, mtd->erasesize); + lnum = from; + + while (len) { + int to_read = mtd->erasesize - offs; + + if (to_read > len) + to_read = len; + + dbg_gluebi("read %d bytes from LEB %d:%d, offset %d", + to_read, vol->vol_id, lnum, offs); + + err = ubi_eba_read_leb(ubi, vol->vol_id, lnum, buf, offs, + to_read, 0, retlen); + read += *retlen; + if (unlikely(err)) + break; + + lnum += 1; + offs = 0; + len -= to_read; + buf += to_read; + } + + *retlen = read; + return err; +} + +/** + * gluebi_write - write operation of emulated MTD devices. + * + * @mtd: the MTD device description object + * @to: absolute offset where to write + * @len: how many bytes to write + * @retlen: count of written bytes is returned here + * @buf: the buffer with data to write + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + int err = 0, lnum, offs, written = 0; + struct ubi_uif_volume *vol; + const struct ubi_info *ubi; + + dbg_gluebi("write %zd bytes to offset %lld", len, to); + + if (len < 0 || to < 0 || len + to > mtd->size) + return -EINVAL; + + vol = mtd2vol(mtd); + ubi = vol->ubi; + + if (ubi->io->ro_mode) + return -EROFS; + + offs = do_div(to, mtd->erasesize); + lnum = to; + + if (len % mtd->writesize || offs % mtd->writesize) + return -EINVAL; + + while (len) { + int to_write = mtd->erasesize - offs; + + if (to_write > len) + to_write = len; + + dbg_gluebi("write %d bytes to LEB %d:%d, offset %d", + to_write, vol->vol_id, lnum, offs); + + err = ubi_eba_write_leb(ubi, vol->vol_id, lnum, buf, offs, + to_write, UBI_DATA_UNKNOWN, retlen, + NULL); + written += *retlen; + if (unlikely(err)) + break; + + lnum += 1; + offs = 0; + len -= to_write; + buf += to_write; + } + + *retlen = written; + return err; +} + +/** + * gluebi_erase - erase operation of emulated MTD devices. + * + * @mtd: the MTD device description object + * @instr: the erase operation description + * + * This function calls the erase callback when finishes. Returns zero in case + * of success and a negative error code in case of failure. + */ +static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + int i, lnum, count; + struct ubi_uif_volume *vol; + const struct ubi_info *ubi; + + dbg_gluebi("erase %u bytes at offset %u", instr->len, instr->addr); + + if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize) + return -EINVAL; + + if (instr->len < 0 || instr->addr + instr->len > mtd->size) + return -EINVAL; + + if (instr->addr % mtd->writesize || instr->len % mtd->writesize) + return -EINVAL; + + lnum = instr->addr / mtd->erasesize; + count = instr->len / mtd->erasesize; + + vol = mtd2vol(mtd); + ubi = vol->ubi; + + if (ubi->io->ro_mode) + return -EROFS; + + for (i = 0; i < count; i++) { + int err; + + dbg_gluebi("erase LEB %d", lnum); + + err = ubi_eba_erase_leb(ubi, vol->vol_id, lnum + i); + if (unlikely(err)) { + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = lnum * mtd->erasesize; + return err; + } + } + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; +} diff --git a/drivers/mtd/ubi/gluebi.h b/drivers/mtd/ubi/gluebi.h new file mode 100644 index 0000000..6c15477 --- /dev/null +++ b/drivers/mtd/ubi/gluebi.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * This unit is responsible for emulating MTD devices on top of UBI devices. + * This sounds strange, but it is in fact quite useful to make legacy software + * work on top of UBI. New software should use native UBI API instead. + * + * Gluebi emulated MTD devices of "MTD_UBIVOLUME" type. Their minimal I/O unit + * size (mtd->writesize) is equivalent to the underlying flash minimal I/O + * unit. The eraseblock size is equivalent to the logical UBI volume eraseblock + * size. + */ + +#ifndef __UBI_GLUEBI_H__ +#define __UBI_GLUEBI_H__ + +#include + +#if defined(CONFIG_MTD_UBI_GLUEBI) + +struct ubi_uif_volume; + +/** + * struct ubi_gluebi_volume - emulated MTD device description data structure. + * + * @desc: UBI volume descriptor + * @mtd: MTD device description object + */ +struct ubi_gluebi_volume +{ + struct ubi_vol_desc *desc; + int refcount; + struct mtd_info mtd; +}; + +/** + * ubi_gluebi_vol_init - initialize all the gluebi-related stuff for an UBI + * volume. + * + * @ubi: the UBI device description object + * @vol: user interfaces unit volume description object + * + * This function is called when an UBI volume is created in order to create + * corresponding MTD device. Returns zero in case of success and a negative + * error code in case of failure. + */ +int ubi_gluebi_vol_init(const struct ubi_info *ubi, struct ubi_uif_volume *vol); + +/** + * ubi_gluebi_vol_close - close all the gluebi-related stuff for an UBI volume. + * + * @vol: user interfaces unit volume description object + * + * This function is called when an UBI volume is removed in order to remove + * corresponding MTD device. Returns zero in case of success and a negative + * error code in case of failure. + */ +int ubi_gluebi_vol_close(struct ubi_uif_volume *vol); + +#else + +struct ubi_gluebi_volume +{ +}; + +#define ubi_gluebi_vol_init(ubi, vol) ({int __ret; __ret = 0;}) +#define ubi_gluebi_vol_close(vol) ({int __ret; __ret = 0;}) + +#endif /* CONFIG_MTD_UBI_GLUEBI */ +#endif /* __UBI_GLUEBI_H__ */ diff --git a/drivers/mtd/ubi/init.c b/drivers/mtd/ubi/init.c new file mode 100644 index 0000000..65f46f0 --- /dev/null +++ b/drivers/mtd/ubi/init.c @@ -0,0 +1,371 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy, + * Frank Haverkamp + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ubi.h" +#include "alloc.h" +#include "uif.h" +#include "io.h" +#include "build.h" +#include "debug.h" + +/* Maximum MTD device specification parameter length */ +#define UBI_MTD_PARAM_LEN_MAX 64 + +/** + * struct mtd_dev_param - MTD device parameter description data structure. + * + * @name: MTD device name or number string + * @vid_hdr_offs: VID header offset + * @data_offs: data offset + */ +struct mtd_dev_param +{ + char name[UBI_MTD_PARAM_LEN_MAX]; + int vid_hdr_offs; + int data_offs; +}; + +/* Numbers of elements set in the @mtd_dev_param array. */ +static int mtd_devs = 0; + +/* MTD devices specification parameters */ +static struct mtd_dev_param mtd_dev_param[UBI_MAX_INSTANCES]; + +/* Number of UBI devices in system */ +int ubis_num; + +/* All the UBI devices in system */ +struct ubi_info *ubis[UBI_MAX_INSTANCES]; + +/* UBI headers must take 64 bytes. The below is a hacky way to ensure this */ +static int __ubi_check_ec_hdr_size[(UBI_EC_HDR_SIZE == 64) - 1] + __attribute__ ((__unused__)); +static int __ubi_check_ec_hdr_size[(UBI_VID_HDR_SIZE == 64) - 1] + __attribute__ ((__unused__)); + +static int __init ubi_attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, + int data_offset); +static void __exit ubi_destroy_dev(int ubi_num); + +static int __init ubi_init(void) +{ + int err, i, k; + + if (mtd_devs > UBI_MAX_INSTANCES) { + printk("UBI error: too many MTD devices, max. is %d\n", + UBI_MAX_INSTANCES); + return -EINVAL; + } + + err = ubi_dbg_init(); + if (err) { + printk("UBI error: failed to initialize debugging unit, " + "error %d", err); + return err; + } + + err = ubi_alloc_init(); + if (err) { + dbg_err("failed to initialize memory allocation unit, " + "error %d", err); + goto out_dbg; + } + + /* Initialize the user interface unit */ + err = ubi_uif_global_init(); + if (err) { + dbg_err("failed to initialize user interfaces unit, error %d", + err); + goto out_alloc; + } + + /* Attach MTD devices */ + for (i = 0; i < mtd_devs; i++) { + struct mtd_dev_param *p = &mtd_dev_param[i]; + + cond_resched(); + err = -EINVAL; + + /* First suppose this is MTD device name */ + err = ubi_attach_mtd_dev(p->name, p->vid_hdr_offs, + p->data_offs); + if (err) + goto out_detach; + } + + return 0; + +out_detach: + for (k = 0; k < i; k++) + ubi_destroy_dev(k); + ubi_uif_global_close(); +out_alloc: + ubi_alloc_close(); +out_dbg: + ubi_dbg_close(); + return err; +} +module_init(ubi_init); + +static void __exit ubi_exit(void) +{ + int i; + + for (i = 0; i < ubis_num; i++) + ubi_destroy_dev(i); + ubi_uif_global_close(); + ubi_alloc_close(); + ubi_dbg_close(); +} +module_exit(ubi_exit); + +/** + * ubi_attach_mtd_dev - attach an MTD device. + * + * @mtd_dev: MTD device name or number string to attach + * @vid_hdr_offset: volume identifier header offset in physical eraseblocks + * @data_offset: data offset in physical eraseblock + * + * This function attaches an MTD device to UBI. It first treats @mtd_dev as the + * MTD device name, and tries to open it by this name. If it is unable to open, + * it tries to convert @mtd_dev to an integer and open the MTD device by its + * number. Returns zero in case of success and a negative error code in case of + * failure. + */ +static int __init ubi_attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, + int data_offset) +{ + struct mtd_info *mtd; + int i, err, mtd_num, ubi_num; + + if (!mtd_dev) + return -EINVAL; + + if (ubis_num == UBI_MAX_INSTANCES) { + ubi_err("too many UBI devices, max. is %d", UBI_MAX_INSTANCES); + return -EINVAL; + } + + mtd = get_mtd_device_nm(mtd_dev); + if (IS_ERR(mtd)) { + char *endp; + + if (PTR_ERR(mtd) != -ENODEV) + return PTR_ERR(mtd); + + mtd_num = simple_strtoul(mtd_dev, &endp, 0); + if (*endp != '\0' || mtd_dev == endp) { + ubi_err("incorrect MTD device: \"%s\"", mtd_dev); + return -ENODEV; + } + + mtd = get_mtd_device(NULL, mtd_num); + if (IS_ERR(mtd)) + return PTR_ERR(mtd); + } + + mtd_num = mtd->index; + put_mtd_device(mtd); + + /* Check is we already have the same MTD device attached */ + for (i = 0; i < ubis_num; i++) + if (ubis[i]->io->mtd_num == mtd_num) { + ubi_err("mtd%d is already attached to ubi%d", + mtd_num, i); + return -EINVAL; + } + + ubi_num = ubis_num++; + + ubis[ubi_num] = ubi_kzalloc(sizeof(struct ubi_info)); + if (!ubis[ubi_num]) + return -ENOMEM; + + ubis[ubi_num]->ubi_num = ubi_num; + + err = ubi_bld_attach_mtd_dev(ubis[ubi_num], mtd_num, vid_hdr_offset, + data_offset); + if (err) + goto out_free; + + if (ubi_num == ubis_num) + ubis_num += 1; + + return 0; + +out_free: + ubi_kfree(ubis[i]); + return -ENODEV; +} + +/** + * ubi_destroy_dev - destroy an UBI device. + * + * @ubi_num: UBI device number to destroy + * + * In current UBI implementation UBI devices are static and cannot dynamically + * go and come. So this function is only used when UBI is de-initialized. + */ +static void __exit ubi_destroy_dev(int ubi_num) +{ + ubi_bld_detach_mtd_dev(ubis[ubi_num]); + ubi_kfree(ubis[ubi_num]); +} + +static int __init bytes_str_to_int(const char *str); + +/** + * 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) +{ + int i, len; + struct mtd_dev_param *p; + char buf[UBI_MTD_PARAM_LEN_MAX]; + char *pbuf = &buf[0]; + char *tokens[3] = {NULL, NULL, NULL}; + + if (mtd_devs == UBI_MAX_INSTANCES) { + printk("UBI error: too many parameters, max. is %d\n", + UBI_MAX_INSTANCES); + return -EINVAL; + } + + len = strnlen(val, UBI_MTD_PARAM_LEN_MAX); + if (len > UBI_MTD_PARAM_LEN_MAX) { + printk("UBI error: parameter \"%s\" is too long, max. is %d\n", + val, UBI_MTD_PARAM_LEN_MAX); + return -EINVAL; + } + + if (len == 0) { + printk("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 < 3; i++) + tokens[i] = strsep(&pbuf, ","); + + if (pbuf) { + printk("UBI error: too many arguments at \"%s\"\n", val); + return -EINVAL; + } + + if (tokens[0] == '\0') + 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 (tokens[2]) + p->data_offs = bytes_str_to_int(tokens[2]); + + if (p->vid_hdr_offs < 0) + return p->vid_hdr_offs; + if (p->data_offs < 0) + return p->data_offs; + + mtd_devs += 1; + + return 0; +} + +/* + * bytes_str_to_int - convert a string representing a number of bytes to an + * integer. + * + * @str: the string to convert + * + * This function returns positive resulting integer in case of success and a + * negative error code in case of failure. + */ +static int __init bytes_str_to_int(const char *str) +{ + char *endp; + unsigned long result; + + result = simple_strtoul(str, &endp, 0); + if (str == endp || result < 0) { + printk("UBI error: incorrect bytes count: \"%s\"\n", str); + return -EINVAL; + } + + switch (*endp) { + case 'G': + result *= 1024; + case 'M': + result *= 1024; + case 'K': + case 'k': + result *= 1024; + if (endp[1] == 'i' && (endp[2] == '\0' || + endp[2] == 'B' || endp[2] == 'b')) + endp += 2; + case '\0': + break; + default: + printk("UBI error: incorrect bytes count: \"%s\"\n", str); + return -EINVAL; + } + + return result; +} + +module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000); +MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: " + "mtd=[,,]. " + "Multiple \"mtd\" parameters may be specified.\n" + "MTD devices may be specified by their number or name. " + "Optional \"vid_hdr_offs\" and \"data_offs\" parameters " + "specify UBI VID header position and data starting " + "position to be used by UBI.\n" + "Example: mtd=content,1984,2048 mtd=4 - attach MTD device" + "with name content using VID header offset 1984 and data " + "start 2048, and MTD device number 4 using default " + "offsets"); + +MODULE_VERSION(__stringify(UBI_VERSION)); +MODULE_DESCRIPTION("UBI - Unsorted Block Images"); +MODULE_AUTHOR("Artem B. Bityutskiy"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c new file mode 100644 index 0000000..14e153a --- /dev/null +++ b/drivers/mtd/ubi/io.c @@ -0,0 +1,1261 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ubi.h" +#include "alloc.h" +#include "io.h" +#include "misc.h" +#include "debug.h" + +/* + * In case of an input/output error, UBI tries to repeat the operation several + * times before returning error. The below constant defines how many times + * UBI re-tries. + */ +#define IO_RETRIES 3 + +/* + * "Paranoid" checks of the UBI I/O unit. Note, they substantially slow down + * the system. + */ +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_IO + +static int paranoid_check_not_bad(const struct ubi_info *ubi, int pnum); +static int paranoid_check_peb_ec_hdr(const struct ubi_info *ubi, int pnum); +static int paranoid_check_ec_hdr(const struct ubi_info *ubi, int pnum, + const struct ubi_ec_hdr *ec_hdr); +static int paranoid_check_peb_vid_hdr(const struct ubi_info *ubi, int pnum); +static int paranoid_check_vid_hdr(const struct ubi_info *ubi, int pnum, + const struct ubi_vid_hdr *vid_hdr); +static int paranoid_check_all_ff(const struct ubi_info *ubi, int pnum, + int offset, int len); +#else +#define paranoid_check_not_bad(ubi, pnum) 0 +#define paranoid_check_peb_ec_hdr(ubi, pnum) 0 +#define paranoid_check_ec_hdr(ubi, pnum, ec_hdr) 0 +#define paranoid_check_peb_vid_hdr(ubi, pnum) 0 +#define paranoid_check_vid_hdr(ubi, pnum, vid_hdr) 0 +#define paranoid_check_all_ff(ubi, pnum, offset, len) 0 +#endif /* !CONFIG_MTD_UBI_DEBUG_PARANOID_IO */ + +/** + * mtd_read - read data from flash. + * + * @ubi: the UBI device description object + * @buf: a buffer where to store the read data + * @addr: absolute flash address to read from + * @len: how many bytes to read + * @read: how many bytes were actually read is returned here + * + * This is a simple wrapper over mtd->read(). + */ +static inline int mtd_read(const struct ubi_info *ubi, void *buf, + loff_t addr, int len, int *read) +{ + struct mtd_info *mtd = ubi->io->mtd; + + return mtd->read(mtd, addr, len, read, buf); +} + +/** + * mtd_write - write data to flash. + * + * @ubi: the UBI device description object + * @buf: the data to write + * @addr: absolute flash address to write to + * @len: how many bytes to write + * @written: how many bytes were actually written + * + * This is a simple wrapper over mtd->write(). + */ +static inline int mtd_write(const struct ubi_info *ubi, const void *buf, + loff_t addr, int len, int *written) +{ + struct mtd_info *mtd = ubi->io->mtd; + + return mtd->write(mtd, addr, len, written, buf); +} + +int ubi_io_read(const struct ubi_info *ubi, void *buf, int pnum, int offset, + int len, int *read) +{ + int err, tries = 0; + const struct ubi_io_info *io = ubi->io; + loff_t addr; + + dbg_io("read %d bytes from PEB %d, offset %d", len, pnum, offset); + + /* Input arguments sanity check */ + ubi_assert(pnum >= 0); + ubi_assert(offset >= 0); + ubi_assert(len > 0); + ubi_assert(pnum < io->peb_count); + ubi_assert(offset + len <= io->peb_size); + + /* The below has to be compiled out if paranoid checks are disabled */ + err = paranoid_check_not_bad(ubi, pnum); + if (unlikely(err != 0)) + return err > 0 ? -EINVAL : err; + + addr = (loff_t)pnum * io->peb_size + offset; + +retry: + err = mtd_read(ubi, buf, addr, len, read); + if (unlikely(err) && *read != len) { + if (++tries <= IO_RETRIES) { + yield(); + goto retry; + } + ubi_err("error %d while reading %d bytes from PEB %d, " + "offset %d, read %d bytes", + err, len, pnum, offset, *read); + dump_stack(); + } + + if (unlikely(err == -EUCLEAN)) + err = UBI_IO_BITFLIPS; + + /* The below is just for debugging and is compiled out if disabled */ + if (ubi_dbg_is_bitflip() && !err) + return UBI_IO_BITFLIPS; + + return err; +} + +int ubi_io_write(const struct ubi_info *ubi, const void *buf, int pnum, + int offset, int len, int *written) +{ + int err; + loff_t addr; + const struct ubi_io_info *io = ubi->io; + + dbg_io("write %d bytes to PEB %d, offset %d", len, pnum, offset); + + /* Input arguments sanity check */ + ubi_assert(pnum >= 0); + ubi_assert(offset >= 0); + ubi_assert(len > 0); + ubi_assert(pnum < io->peb_count); + ubi_assert(offset + len <= io->peb_size); + ubi_assert(offset % io->hdrs_min_io_size == 0); + ubi_assert(len % io->hdrs_min_io_size == 0); + + /* Ensure we are not in read-only mode */ + if (unlikely(io->ro_mode)) { + ubi_err("read-only mode"); + return -EROFS; + } + + /* The below has to be compiled out if paranoid checks are disabled */ + err = paranoid_check_not_bad(ubi, pnum); + if (unlikely(err != 0)) + return err > 0 ? -EINVAL : err; + + /* The area we are writing to has to contain all 0xFF bytes */ + err = paranoid_check_all_ff(ubi, pnum, offset, len); + if (unlikely(err)) + return -EINVAL; + + if (offset >= io->leb_start) { + /* + * We write to the data area of the physical eraseblock. Make + * sure it has valid EC and VID headers. + */ + err = paranoid_check_peb_ec_hdr(ubi, pnum); + if (unlikely(err > 0)) + return -EINVAL; + err = paranoid_check_peb_vid_hdr(ubi, pnum); + if (unlikely(err)) + return -EINVAL; + } + + addr = (loff_t)pnum * io->peb_size + offset; + + err = mtd_write(ubi, buf, addr, len, written); + if (unlikely(err)) { + ubi_err("error %d while writing %d bytes to PEB %d, offset %d, " + "written %d bytes", + err, len, pnum, offset, *written); + dump_stack(); + } + + /* The below is just for debugging and is compiled out if disabled */ + if (ubi_dbg_is_write_failure() && !err) { + ubi_err("cannot write %d bytes to PEB %d, offset %d (emulated)", + len, pnum, offset); + dump_stack(); + return -EIO; + } + + return err; +} + +static void erase_callback(struct erase_info *ei) +{ + wake_up_interruptible((wait_queue_head_t *)ei->priv); +} + +static int sync_erase(const struct ubi_info *ubi, int pnum); +static int ubi_io_torture_peb(const struct ubi_info *ubi, int pnum); + +int ubi_io_sync_erase(const struct ubi_info *ubi, int pnum, int torture) +{ + int err, ret; + + /* Ensure we are not in read-only mode */ + if (unlikely(ubi->io->ro_mode)) { + ubi_err("read-only mode"); + return -EROFS; + } + + if (!torture) { + err = sync_erase(ubi, pnum); + if (unlikely(err)) + return err; + return 1; + } + + ret = ubi_io_torture_peb(ubi, pnum); + if (unlikely(!ret)) + return -EIO; + if (unlikely(ret < 0)) + return ret; + + err = sync_erase(ubi, pnum); + if (unlikely(err)) + return err; + + return ret + 1; +} + +int ubi_io_is_bad(const struct ubi_info *ubi, int pnum) +{ + const struct ubi_io_info *io = ubi->io; + struct mtd_info *mtd = io->mtd; + + /* Input arguments sanity check */ + ubi_assert(pnum >= 0); + ubi_assert(pnum < io->peb_count); + + if (io->bad_allowed) { + int ret; + + ret = mtd->block_isbad(mtd, (loff_t)pnum * io->peb_size); + if (unlikely(ret < 0)) + ubi_err("error %d while checking if PEB %d is bad", + ret, pnum); + else if (ret) + dbg_io("PEB %d is bad", pnum); + return ret; + } + + return 0; +} + +int ubi_io_mark_bad(const struct ubi_info *ubi, int pnum) +{ + int err; + const struct ubi_io_info *io = ubi->io; + struct mtd_info *mtd = io->mtd; + + /* Input arguments sanity check */ + ubi_assert(pnum >= 0); + ubi_assert(pnum < io->peb_count); + + /* Ensure we are not in read-only mode */ + if (unlikely(io->ro_mode)) { + ubi_err("read-only mode"); + return -EROFS; + } + + if (!io->bad_allowed) + return 0; + + err = mtd->block_markbad(mtd, (loff_t)pnum * io->peb_size); + if (unlikely(err)) + ubi_err("cannot mark PEB %d bad, error %d", pnum, err); + return err; +} + +static int ec_hdr_sanity_check(const struct ubi_info *ubi, int pnum, + const struct ubi_ec_hdr *ec_hdr); + +int ubi_io_read_ec_hdr(const struct ubi_info *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr, int verbose) +{ + int err, err1, read; + uint32_t crc, magic, hdr_crc; + + dbg_io("read EC header from PEB %d", pnum); + + /* Input arguments sanity check */ + ubi_assert(pnum >= 0); + ubi_assert(pnum < ubi->io->peb_count); + + err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE, &read); + if (unlikely(err)) { + if (err == -EIO || read != UBI_EC_HDR_SIZE) + return err; + + /* + * Although an error occurred, we have read the requested + * number of bytes, so keep working. The read data may be + * corrupted, but the CRC checksum has to identify this. If the + * CRC checksum is OK, this physical eraseblock needs + * scrubbing. + */ + err = UBI_IO_BITFLIPS; + } + + magic = ubi32_to_cpu(ec_hdr->magic); + if (unlikely(magic != UBI_EC_HDR_MAGIC)) { + /* + * The magic field is wrong. Let's check if we have read all + * 0xFF. If yes, this physical eraseblock is assumed to be + * empty. + */ + if (ubi_buf_all_ff(ec_hdr, UBI_EC_HDR_SIZE)) { + /* The physical eraseblock is supposedly empty */ + + /* + * The below is just a paranoid check, it has to be + * compiled out if paranoid checks are disabled. + */ + err1 = paranoid_check_all_ff(ubi, pnum, 0, + ubi->io->peb_size); + if (unlikely(err1)) + return UBI_IO_BAD_EC_HDR; + + if (verbose) + ubi_err("no EC header found at PEB %d", pnum); + return UBI_IO_PEB_EMPTY; + } + if (verbose) { + ubi_err("bad magic number at PEB %d", pnum); + ubi_dbg_dump_ec_hdr(ec_hdr); + } + return UBI_IO_BAD_EC_HDR; + } + + crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); + hdr_crc = ubi32_to_cpu(ec_hdr->hdr_crc); + + if (unlikely(hdr_crc != crc)) { + if (verbose) { + ubi_err("bad CRC at PEB %d, calculated %#08x, " + "read %#08x", pnum, crc, hdr_crc); + ubi_dbg_dump_ec_hdr(ec_hdr); + } + return UBI_IO_BAD_EC_HDR; + } + + /* Be on our guard and do not trust to what is read from the media */ + err1 = ec_hdr_sanity_check(ubi, pnum, ec_hdr); + if (unlikely(err1 > 0)) + return -EINVAL; + + return err; +} + +int ubi_io_write_ec_hdr(const struct ubi_info *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr) +{ + int err, written; + uint32_t crc; + const struct ubi_io_info *io = ubi->io; + + dbg_io("write EC header to PEB %d", pnum); + + /* Input arguments sanity check */ + ubi_assert(pnum >= 0); + ubi_assert(pnum < io->peb_count); + + ec_hdr->magic = cpu_to_ubi32(UBI_EC_HDR_MAGIC); + ec_hdr->version = UBI_VERSION; + ec_hdr->vid_hdr_offset = cpu_to_ubi32(io->vid_hdr_offset); + ec_hdr->data_offset = cpu_to_ubi32(io->leb_start); + crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); + ec_hdr->hdr_crc = cpu_to_ubi32(crc); + + /* The below has to be compiled out if paranoid checks are disabled */ + err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr); + if (unlikely(err > 0)) + return -EINVAL; + + err = ubi_io_write(ubi, ec_hdr, pnum, 0, io->ec_hdr_alsize, &written); + return err; +} + +static int vid_hdr_sanity_check(const struct ubi_info *ubi, int pnum, + const struct ubi_vid_hdr *vid_hdr); + +int ubi_io_read_vid_hdr(const struct ubi_info *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr, int verbose) +{ + int err, err1, read; + uint32_t crc, magic, hdr_crc; + const struct ubi_io_info *io = ubi->io; + void *p; + + dbg_io("read VID header from PEB %d", pnum); + + /* Input arguments sanity check */ + ubi_assert(pnum >= 0); + ubi_assert(pnum < io->peb_count); + + p = (char *)vid_hdr - io->vid_hdr_shift; + err = ubi_io_read(ubi, p, pnum, io->vid_hdr_aloffset, + io->vid_hdr_alsize, &read); + if (unlikely(err)) { + if (err == -EIO || read != io->vid_hdr_alsize) + return err; + + /* + * Although an error occurred, we have read the requested + * number of bytes, so keep working. The read data may be + * corrupted, but the CRC checksum has to identify this. If the + * CRC checksum is OK, this physical eraseblock needs + * scrubbing. + */ + err = UBI_IO_BITFLIPS; + } + + magic = ubi32_to_cpu(vid_hdr->magic); + if (unlikely(magic != UBI_VID_HDR_MAGIC)) { + /* + * If we have read all 0xFF bytes, the VID header probably does + * not exist and the physical eraseblock is assumed to be free. + */ + if (ubi_buf_all_ff(vid_hdr, UBI_VID_HDR_SIZE)) { + /* The physical eraseblock is supposedly free */ + + /* + * The below is just a paranoid check, it has to be + * compiled out if paranoid checks are disabled. + */ + err1 = paranoid_check_all_ff(ubi, pnum, io->leb_start, + io->leb_size); + if (unlikely(err1)) + return UBI_IO_BAD_VID_HDR; + if (verbose) + ubi_err("no VID header found at PEB %d", pnum); + return UBI_IO_PEB_FREE; + } + if (verbose) { + ubi_err("bad magic number at PEB %d", pnum); + ubi_dbg_dump_vid_hdr(vid_hdr); + } + return UBI_IO_BAD_VID_HDR; + } + + crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC); + hdr_crc = ubi32_to_cpu(vid_hdr->hdr_crc); + + if (unlikely(hdr_crc != crc)) { + if (verbose) { + ubi_err("bad CRC at PEB %d, calculated %#08x, " + "read %#08x", pnum, crc, hdr_crc); + ubi_dbg_dump_vid_hdr(vid_hdr); + } + return UBI_IO_BAD_VID_HDR; + } + + /* Play safe and do not trust to what is read from the media */ + err1 = vid_hdr_sanity_check(ubi, pnum, vid_hdr); + if (unlikely(err1 > 0)) + return -EINVAL; + + return err; +} + +int ubi_io_write_vid_hdr(const struct ubi_info *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr) +{ + int err, written; + uint32_t crc; + const struct ubi_io_info *io = ubi->io; + void *p; + + dbg_io("write VID header to PEB %d", pnum); + + /* Input arguments sanity check */ + ubi_assert(pnum >= 0); + ubi_assert(pnum < io->peb_count); + + /* The below has to be compiled out if paranoid checks are disabled */ + err = paranoid_check_peb_ec_hdr(ubi, pnum); + if (unlikely(err > 0)) + return -EINVAL; + + vid_hdr->magic = cpu_to_ubi32(UBI_VID_HDR_MAGIC); + vid_hdr->version = UBI_VERSION; + crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_EC_HDR_SIZE_CRC); + vid_hdr->hdr_crc = cpu_to_ubi32(crc); + + /* The below has to be compiled out if paranoid checks are disabled */ + err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr); + if (unlikely(err > 0)) + return -EINVAL; + + /* + * The VID header may reside on a non-aligned offset, take care about + * this. + */ + p = (char *)vid_hdr - io->vid_hdr_shift; + err = ubi_io_write(ubi, p, pnum, io->vid_hdr_aloffset, + io->vid_hdr_alsize, &written); + return err; +} + +static int __init mtd_device_check(const struct mtd_info *mtd); + +int __init ubi_io_init(struct ubi_info *ubi, int mtd_num, int vid_hdr_offset, + int data_offset) +{ + int err; + struct mtd_info *mtd; + struct ubi_io_info *io; + + dbg_io("initialize the UBI I/O unit for MTD device %d", mtd_num); + + io = ubi_kzalloc(sizeof(struct ubi_io_info)); + if (!io) + return -ENOMEM; + ubi->io = io; + + mtd = io->mtd = get_mtd_device(NULL, mtd_num); + if (IS_ERR(mtd)) { + ubi_err("cannot open MTD device %d", mtd_num); + err = PTR_ERR(mtd); + goto out_io; + } + io->mtd_num = mtd_num; + + /* Ensure that we can work with this MTD device */ + err = mtd_device_check(mtd); + if (err) + goto out_mtd; + + io->mtd_name = mtd->name; + io->peb_size = mtd->erasesize; + io->peb_count = mtd->size / mtd->erasesize; + io->flash_size = mtd->size; + + if (mtd->block_isbad && mtd->block_markbad) + io->bad_allowed = 1; + + io->min_io_size = mtd->writesize; + io->hdrs_min_io_size = mtd->writesize >> mtd->subpage_sft; + + ubi_assert(io->hdrs_min_io_size > 0 && io->hdrs_min_io_size <= io->min_io_size); + ubi_assert(io->min_io_size % io->hdrs_min_io_size == 0); + + /* + * Calculate default sizes of EC and VID headers which are aligned to + * the minimal flash I/O unit. + */ + io->ec_hdr_alsize = align_up(UBI_EC_HDR_SIZE, io->hdrs_min_io_size); + io->vid_hdr_alsize = align_up(UBI_VID_HDR_SIZE, io->hdrs_min_io_size); + + if (vid_hdr_offset == 0) + /* default offset */ + io->vid_hdr_offset = io->vid_hdr_aloffset = io->ec_hdr_alsize; + else { + io->vid_hdr_offset = vid_hdr_offset; + io->vid_hdr_aloffset = vid_hdr_offset; + io->vid_hdr_aloffset -= vid_hdr_offset % io->hdrs_min_io_size; + io->vid_hdr_shift = vid_hdr_offset - io->vid_hdr_aloffset; + + /* The shift must be aligned to 32-bit boundary */ + if (io->vid_hdr_shift % 4) { + ubi_err("unaligned VID header shift %d", + io->vid_hdr_shift); + goto out_mtd; + } + } + + /* Similar for the data offset */ + if (data_offset == 0) { + io->leb_start = io->vid_hdr_offset + io->vid_hdr_alsize; + io->leb_start = align_up(io->leb_start, io->min_io_size); + } else + io->leb_start = data_offset; + + /* Check sanity */ + if (io->vid_hdr_offset < UBI_EC_HDR_SIZE || + io->leb_start < io->vid_hdr_offset + UBI_VID_HDR_SIZE || + io->leb_start > io->peb_size - UBI_VID_HDR_SIZE || + io->leb_start % io->min_io_size) { + ubi_err("bad VID header (%d) or data offsets (%d)", + io->vid_hdr_offset, io->leb_start); + goto out_mtd; + } + + /* + * 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 (io->vid_hdr_offset + UBI_VID_HDR_SIZE <= io->hdrs_min_io_size) { + ubi_warn("EC and VID headers are in the same minimal I/O unit, " + "switch to read-only mode"); + io->ro_mode = 1; + } + + io->leb_size = io->peb_size - io->leb_start; + + if (!(mtd->flags & MTD_WRITEABLE)) { + ubi_msg("MTD device %d is write-protected, attach in " + "read-only mode", mtd_num); + io->ro_mode = 1; + } + + /* + * FIXME: ideally, we have to initialize io->bad_peb_count here. But + * unfortunately, MTD does not provide this information. We should loop + * over all physical eraseblocks and invoke mtd->block_is_bad() which + * is not optimal. So, we skip io->bad_peb_count uninitialized and let + * the scanning unit to initialize it. This is not nice. + */ + + dbg_io("the UBI I/O unit is initialized"); + return 0; + +out_mtd: + put_mtd_device(mtd); +out_io: + ubi_kfree(io); + return err; +} + +void __exit ubi_io_close(const struct ubi_info *ubi) +{ + const struct ubi_io_info *io = ubi->io; + + dbg_io("close the UBI I/O unit for mtd device %d", io->mtd_num); + put_mtd_device(io->mtd); + ubi_kfree(io); +} + +/** + * sync_erase - synchronously erase a physical eraseblock. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock number to erase + * + * This function synchronously erases physical eraseblock @pnum and returns + * zero in case of success and a negative error code in case of failure. If + * %-EIO is returned, the physical eraseblock went bad. + */ +static int sync_erase(const struct ubi_info *ubi, int pnum) +{ + int err, tries = 0; + struct erase_info ei; + wait_queue_head_t wq; + const struct ubi_io_info *io = ubi->io; + + /* + * Note, even though MTD erase interface is asynchronous, all the + * current implementations are synchronous. + */ + + dbg_io("erase PEB %d", pnum); + + /* Input arguments sanity check */ + ubi_assert(pnum >= 0); + ubi_assert(pnum < io->peb_count); + + /* The below has to be compiled out if paranoid checks are disabled */ + err = paranoid_check_not_bad(ubi, pnum); + if (unlikely(err != 0)) + return err > 0 ? -EINVAL : err; + +retry: + init_waitqueue_head(&wq); + memset(&ei, 0, sizeof(struct erase_info)); + + ei.mtd = io->mtd; + ei.addr = pnum * io->peb_size; + ei.len = io->peb_size; + ei.retries = 2; + ei.callback = erase_callback; + ei.priv = (unsigned long)&wq; + + err = io->mtd->erase(io->mtd, &ei); + if (unlikely(err)) { + ubi_err("error while erasing PEB %d", pnum); + if (++tries <= IO_RETRIES) { + yield(); + ubi_msg("retry"); + goto retry; + } + dump_stack(); + return err; + } + + err = wait_event_interruptible(wq, ei.state == MTD_ERASE_DONE || + ei.state == MTD_ERASE_FAILED); + if (unlikely(err)) { + ubi_err("interrupted PEB %d erasure", pnum); + return -EINTR; + } + + if (unlikely(ei.state == MTD_ERASE_FAILED)) { + ubi_err("cannot erase PEB %d", pnum); + if (++tries <= IO_RETRIES) { + yield(); + ubi_msg("retry"); + goto retry; + } + return -EIO; + } + + /* The below has to be compiled out if paranoid checks are disabled */ + err = paranoid_check_all_ff(ubi, pnum, 0, io->peb_size); + if (unlikely(err)) + return -EINVAL; + + /* The below is just for debugging and is compiled out if disabled */ + if (ubi_dbg_is_erase_failure() && !err) { + ubi_err("cannot erase PEB %d (emulated)", pnum); + return -EIO; + } + + return 0; +} + +/* Patterns to write to a physical eraseblock when torturing it */ +static int patterns[] = {0xa5, 0x5a, 0x0}; + +/** + * ubi_io_torture_peb - test a supposedly bad physical eraseblock. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock number to test + * + * This function returns %0 if the physical eraseblock did not pass the test, + * the number of erase operations done if the test was successfully passed, and + * a negative error code if an error occurred. + */ +static int ubi_io_torture_peb(const struct ubi_info *ubi, int pnum) +{ + int err, i, patt_count; + void *buf; + const struct ubi_io_info *io = ubi->io; + + buf = ubi_kmalloc(io->peb_size); + if (unlikely(!buf)) + return -ENOMEM; + + patt_count = sizeof(patterns)/sizeof(patterns[0]); + for (i = 0; i < patt_count; i++) { + int read, written; + + err = sync_erase(ubi, pnum); + if (unlikely(err)) + goto out; + + /* Make sure the PEB contains only 0xFF bytes */ + err = ubi_io_read(ubi, buf, pnum, 0, io->peb_size, &read); + if (unlikely(err)) + goto out; + + err = ubi_buf_all_ff(buf, io->peb_size); + if (unlikely(err == 0)) { + ubi_err("erased PEB %d, but a non-0xFF byte found", + pnum); + goto out; + } + + /* Write a pattern and check it */ + memset(buf, patterns[i], io->peb_size); + err = ubi_io_write(ubi, buf, pnum, 0, io->peb_size, &written); + if (unlikely(err)) + goto out; + + memset(buf, ~patterns[i], io->peb_size); + err = ubi_io_read(ubi, buf, pnum, 0, io->peb_size, &read); + if (unlikely(err)) + goto out; + + err = ubi_check_pattern(buf, patterns[i], io->peb_size); + if (unlikely(err == 0)) { + ubi_err("pattern %x checking failed for PEB %d", + patterns[i], pnum); + goto out; + } + } + + err = patt_count; +out: + ubi_kfree(buf); + return err; +} + + +/** + * mtd_device_check - check that characteristics of underlying MTD + * device are OK for us. + * + * @mtd: MTD device descriptor. + * + * This function returns zero if the MTD device is OK and a negative error code + * if not. + */ +static int __init mtd_device_check(const struct mtd_info *mtd) +{ + /* + * Note, in this implementation we support MTD devices with 0x7FFFFFFF + * physical eraseblocks maximum. + */ + + if (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. + * + * Later note by dedekind: it is better is to re-work MTD and + * to get rid of this "regions" stuff. It is better to expose + * different regions as different MTD devices, I think. + */ + ubi_err("not implemented"); + goto out; + } + + return 0; + +out: + ubi_err("bad or unsupported mtd device"); + return -EINVAL; +} + +/** + * ec_hdr_sanity_check - check if an erase counter header is sane. + * + * @ubi: the UBI device description object + * @pnum: physical eraseblock number the erase counter header belongs to + * @ec_hdr: the erase counter header to check + * + * This function returns zero if the erase counter header contains valid + * values, and %1 if not. + */ +static int ec_hdr_sanity_check(const struct ubi_info *ubi, int pnum, + const struct ubi_ec_hdr *ec_hdr) +{ + int64_t ec; + int vid_hdr_offset, leb_start; + const struct ubi_io_info *io = ubi->io; + + ec = ubi64_to_cpu(ec_hdr->ec); + vid_hdr_offset = ubi32_to_cpu(ec_hdr->vid_hdr_offset); + leb_start = ubi32_to_cpu(ec_hdr->data_offset); + + if (unlikely(ec_hdr->version != UBI_VERSION)) { + ubi_err("node with incompatible UBI version found: " + "this UBI version is %d, image version is %d", + UBI_VERSION, (int)ec_hdr->version); + goto bad; + } + if (unlikely(vid_hdr_offset != io->vid_hdr_offset)) { + ubi_err("bad VID header offset %d at PEB %d, expected %d", + vid_hdr_offset, pnum, io->vid_hdr_offset); + goto bad; + } + if (unlikely(leb_start != io->leb_start)) { + ubi_err("bad data offset %d at PEB %d, expected %d", + pnum, leb_start, io->leb_start); + goto bad; + } + if (unlikely(ec < 0 || ec > UBI_MAX_ERASECOUNTER)) { + ubi_err("bad erase counter %lld at EC header at PEB %d", + ec, pnum); + goto bad; + } + + return 0; + +bad: + dbg_err("dump of the erroneous EC header at PEB %d", pnum); + ubi_dbg_dump_ec_hdr(ec_hdr); + dump_stack(); + return 1; +} + +/** + * vid_hdr_sanity_check - check that if a volume identifier header is sane. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock number the VID header came from + * @vid_hdr: the volume identifier header to check + * + * This function checks that data stored in the volume identifier header + * @vid_hdr is sane. This function returns zero if the VID header is OK and %1 + * if not. + */ +static int vid_hdr_sanity_check(const struct ubi_info *ubi, int pnum, + const struct ubi_vid_hdr *vid_hdr) +{ + const struct ubi_io_info *io = ubi->io; + int vol_type = vid_hdr->vol_type; + int copy_flag = vid_hdr->copy_flag; + int vol_id = ubi32_to_cpu(vid_hdr->vol_id); + int lnum = ubi32_to_cpu(vid_hdr->lnum); + int compat = vid_hdr->compat; + int data_size = ubi32_to_cpu(vid_hdr->data_size); + int used_ebs = ubi32_to_cpu(vid_hdr->used_ebs); + int data_pad = ubi32_to_cpu(vid_hdr->data_pad); + int data_crc = ubi32_to_cpu(vid_hdr->data_crc); + int usable_leb_size = io->leb_size - data_pad; + + if (unlikely(copy_flag != 0 && copy_flag != 1)) { + dbg_err("bad copy_flag"); + goto bad; + } + + if (unlikely(vol_id < 0 || lnum < 0 || data_size < 0 || used_ebs < 0 || + data_pad < 0)) { + dbg_err("negative values"); + goto bad; + } + + if (unlikely(vol_id >= UBI_MAX_VOLUMES && + vol_id < UBI_INTERNAL_VOL_START)) { + dbg_err("bad vol_id"); + goto bad; + } + + if (unlikely(vol_id < UBI_INTERNAL_VOL_START && compat != 0)) { + dbg_err("bad compat"); + goto bad; + } + + if (unlikely(vol_id >= UBI_INTERNAL_VOL_START && + compat != UBI_COMPAT_DELETE && + compat != UBI_COMPAT_RO && + compat != UBI_COMPAT_PRESERVE && + compat != UBI_COMPAT_REJECT)) { + dbg_err("bad compat"); + goto bad; + } + + if (unlikely(vol_type != UBI_VID_DYNAMIC && + vol_type != UBI_VID_STATIC)) { + dbg_err("bad vol_type"); + goto bad; + } + + if (unlikely(data_pad >= io->leb_size / 2)) { + dbg_err("bad data_pad"); + goto bad; + } + + if (vol_type == UBI_VID_STATIC) { + if (unlikely(used_ebs == 0)) { + dbg_err("zero used_ebs"); + goto bad; + } + if (unlikely(data_size == 0)) { + dbg_err("zero data_size"); + goto bad; + } + if (lnum < used_ebs - 1) { + if (unlikely(data_size != usable_leb_size)) { + dbg_err("bad data_size"); + goto bad; + } + } else if (lnum == used_ebs - 1) { + if (unlikely(data_size == 0)) { + dbg_err("bad data_size at last LEB"); + goto bad; + } + } else { + dbg_err("too high lnum %d", lnum); + goto bad; + } + } else { + if (copy_flag == 0) { + if (unlikely(data_crc != 0)) { + dbg_err("bad data CRC"); + goto bad; + } + if (unlikely(data_size != 0)) { + dbg_err("bad data_size"); + goto bad; + } + } else { + if (unlikely(data_size == 0)) { + dbg_err("zero data_size of copy"); + goto bad; + } + } + if (unlikely(used_ebs != 0)) { + dbg_err("bad used_ebs"); + goto bad; + } + } + + return 0; + +bad: + ubi_err("bad VID header at PEB %d", pnum); + ubi_dbg_dump_vid_hdr(vid_hdr); + return 1; +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_IO + +/** + * paranoid_check_not_bad - ensure that a physical eraseblock is not bad. + * + * @ubi: the UBI device description object + * @pnum: physical eraseblock number to check + * + * This function returns zero if the physical eraseblock is good, a positive + * number if it is bad and a negative error code if an error occurred. + */ +static int paranoid_check_not_bad(const struct ubi_info *ubi, int pnum) +{ + int err; + + err = ubi_io_is_bad(ubi, pnum); + if (likely(!err)) + return err; + + ubi_err("paranoid check failed for PEB %d", pnum); + dump_stack(); + return err; +} + +/** + * paranoid_check_ec_hdr - check if an erase counter header is all right. + * + * @ubi: the UBI device description object + * @pnum: physical eraseblock number the erase counter header belongs to + * @ec_hdr: the erase counter header to check + * + * This function returns zero if the erase counter header contains valid + * values, and %1 if not. + */ +static int paranoid_check_ec_hdr(const struct ubi_info *ubi, int pnum, + const struct ubi_ec_hdr *ec_hdr) +{ + uint32_t magic; + + magic = ubi32_to_cpu(ec_hdr->magic); + if (unlikely(magic != UBI_EC_HDR_MAGIC)) { + ubi_err("bad magic %#08x, must be %#08x", + magic, UBI_EC_HDR_MAGIC); + ubi_dbg_dump_ec_hdr(ec_hdr); + dump_stack(); + return 1; + } + + return ec_hdr_sanity_check(ubi, pnum, ec_hdr); +} + +/** + * paranoid_check_peb_ec_hdr - check that the erase counter header of a + * physical eraseblock is in-place and is all right. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock number to check + * + * This function returns zero if the erase counter header is all right, + * %-EINVAL if not, and a negative error code if an error occurred. + */ +static int paranoid_check_peb_ec_hdr(const struct ubi_info *ubi, int pnum) +{ + int err, read; + uint32_t crc, hdr_crc; + struct ubi_ec_hdr *ec_hdr; + + ec_hdr = ubi_zalloc_ec_hdr(ubi); + if (unlikely(!ec_hdr)) + return -ENOMEM; + + err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE, &read); + if (unlikely(err) && err != UBI_IO_BITFLIPS) + goto exit; + + crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); + hdr_crc = ubi32_to_cpu(ec_hdr->hdr_crc); + if (unlikely(hdr_crc != crc)) { + ubi_err("bad CRC, calculated %#08x, read %#08x", crc, hdr_crc); + goto fail; + } + + err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr); +exit: + ubi_free_ec_hdr(ubi, ec_hdr); + return err; + +fail: + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_dbg_dump_ec_hdr(ec_hdr); + dump_stack(); + ubi_free_ec_hdr(ubi, ec_hdr); + return 1; +} + +/** + * paranoid_check_vid_hdr - check that a volume identifier header is all right. + * + * @ubi: the UBI device description object + * @pnum: physical eraseblock number the volume identifier header belongs to + * @vid_hdr: the volume identifier header to check + * + * This function returns zero if the volume identifier header is all right, and + * %1 if not. + */ +static int paranoid_check_vid_hdr(const struct ubi_info *ubi, int pnum, + const struct ubi_vid_hdr *vid_hdr) +{ + uint32_t magic; + + magic = ubi32_to_cpu(vid_hdr->magic); + if (unlikely(magic != UBI_VID_HDR_MAGIC)) { + ubi_err("bad VID header magic %#08x at PEB %d, must be %#08x", + magic, pnum, UBI_VID_HDR_MAGIC); + ubi_dbg_dump_vid_hdr(vid_hdr); + dump_stack(); + return 1; + } + + return vid_hdr_sanity_check(ubi, pnum, vid_hdr); +} + +/** + * paranoid_check_peb_vid_hdr - check that the volume identifier header of a + * physical eraseblock is in-place and is all right. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock number to check + * + * This function returns zero if the volume identifier header is all right, + * %-EINVAL if not, and a negative error code if an error occurred. + */ +static int paranoid_check_peb_vid_hdr(const struct ubi_info *ubi, int pnum) +{ + int err, read; + uint32_t crc, hdr_crc; + struct ubi_vid_hdr *vid_hdr; + const struct ubi_io_info *io = ubi->io; + void *p; + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (unlikely(!vid_hdr)) + return -ENOMEM; + + p = (char *)vid_hdr - io->vid_hdr_shift; + err = ubi_io_read(ubi, p, pnum, io->vid_hdr_aloffset, + io->vid_hdr_alsize, &read); + if (unlikely(err) && err != UBI_IO_BITFLIPS) + goto exit; + + crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_EC_HDR_SIZE_CRC); + hdr_crc = ubi32_to_cpu(vid_hdr->hdr_crc); + if (unlikely(hdr_crc != crc)) { + ubi_err("bad VID header CRC at PEB %d, calculated %#08x, " + "read %#08x", pnum, crc, hdr_crc); + goto fail; + } + + err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr); + +exit: + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +fail: + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_dbg_dump_vid_hdr(vid_hdr); + dump_stack(); + ubi_free_vid_hdr(ubi, vid_hdr); + return -EINVAL; +} + +/** + * paranoid_check_all_ff - check that a region of flash is empty. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock number to check + * @offset: the starting offset within the physical eraseblock to check + * @len: the length of the region to check + * + * This function returns zero if only 0xFF bytes are present at offset + * @offset of the physical eraseblock @pnum, %1 if not, and a negative error + * code if an error occurred. + */ +static int paranoid_check_all_ff(const struct ubi_info *ubi, int pnum, + int offset, int len) +{ + int read, err; + void *buf; + const struct ubi_io_info *io = ubi->io; + loff_t off; + + buf = ubi_kzalloc(len); + if (unlikely(!buf)) + return -ENOMEM; + + off = (loff_t)pnum * io->peb_size + offset; + + err = mtd_read(ubi, buf, off, len, &read); + if (unlikely(err) && read != len) { + ubi_err("error %d while reading %d bytes from PEB %d, " + "offset %d, read %d bytes", + err, len, pnum, offset, read); + ubi_kfree(buf); + return err; + } + + err = ubi_buf_all_ff(buf, len); + if (unlikely(err == 0)) { + ubi_err("flash region at PEB %d, offset %d, length %d does " + "not contain all 0xFF bytes", pnum, offset, len); + goto fail; + } + + ubi_kfree(buf); + return 0; + +fail: + ubi_err("paranoid check failed for PEB %d", pnum); + dbg_err("hex dump of the %d-%d region", offset, offset + len); + ubi_dbg_hexdump(buf, len); + dump_stack(); + ubi_kfree(buf); + return 1; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID_IO */ diff --git a/drivers/mtd/ubi/io.h b/drivers/mtd/ubi/io.h new file mode 100644 index 0000000..312770d --- /dev/null +++ b/drivers/mtd/ubi/io.h @@ -0,0 +1,400 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * UBI input/output unit. + * + * This unit provides a uniform way to work with all kinds of the underlying + * MTD devices. This unit is also responsible for basic sanity checking of + * everything which is read from flash. + * + * We are trying to have a paranoid mindset and do not trust to what we read + * from the flash media in order to be more secure and robust. For example, we + * do not want to have vulnerability when attackers feed us with an + * inconsistent image end ends up with a buffer overflow which is especially + * actual if UBI is ever used on removable media. So we keep our wits about + * us. + */ + +#ifndef __UBI_IO_H__ +#define __UBI_IO_H__ + +struct ubi_info; +struct ubi_ec_hdr; +struct ubi_vid_hdr; +struct ubi_io_info; +struct mtd_info; + +/* + * Error codes returned by this unit. + * + * @UBI_IO_PEB_EMPTY: the physical eraseblock is empty, i.e. it contains only + * %0xFF bytes + * @UBI_IO_PEB_FREE: the physical eraseblock is free, i.e. it only contains a + * valid erase counter header, and the rest are %0xFF bytes + * @UBI_IO_BAD_EC_HDR: the erase counter header is corrupted (bad magic or CRC) + * @UBI_IO_BAD_VID_HDR: the volume identifier header is corrupted (bad magic or + * CRC) + * @UBI_IO_BITFLIPS: bit-flips were detected and corrected + */ +enum { + UBI_IO_PEB_EMPTY = 1, + UBI_IO_PEB_FREE, + UBI_IO_BAD_EC_HDR, + UBI_IO_BAD_VID_HDR, + UBI_IO_BITFLIPS +}; + +/** + * ubi_io_read - read data from a physical eraseblock. + * + * @ubi: the UBI device description object + * @buf: a buffer where to store the read data + * @pnum: the physical eraseblock number to read from + * @offset: the offset within the physical eraseblock from where to read + * @len: how many bytes to read + * @read: how many bytes were actually read is returned here + * + * This function reads data from offset @offset of physical eraseblock @pnum + * and stores the read data in the @buf buffer. The following are return codes: + * + * o %0 if all the requested data were successfully read; + * o %UBI_IO_BITFLIPS if all the requested data were successfully read, but + * correctable bit-flips were detected; + * o %-EBADMSG if an ECC error occurred; + * o %-EIO if some I/O error occurred; + * o Other negative error codes in case of other errors. + * + * In any case, the number of read bytes is returned at @read. In case of an + * error, the read data does not have to be correct. + */ +int ubi_io_read(const struct ubi_info *ubi, void *buf, int pnum, int offset, + int len, int *read); + +/** + * ubi_io_read_data - read data from a physical eraseblock owned by a logical + * eraseblock. + * + * @ubi: the UBI device description object + * @buf: a buffer where to store the read data + * @pnum: the physical eraseblock number to read from + * @offset: the offset within the logical eraseblock from where to read + * @len: how many bytes to read + * @read: how many bytes were actually read is returned here + * + * This function is equivalent to 'ubi_io_read()', but @offset is relative to + * the beginning of the logical eraseblock, not to the beginning of the + * physical eraseblock. + */ +#define ubi_io_read_data(ubi, buf, pnum, offset, len, read) ({ \ + int __err; \ + ubi_assert((offset) >= 0); \ + __err = ubi_io_read(ubi, buf, pnum, (offset) + ubi->io->leb_start, \ + len, read); \ + __err = __err; \ +}) + +/** + * ubi_io_write - write data to a physical eraseblock. + * + * @ubi: the UBI device description object + * @buf: the data to write + * @pnum: the physical eraseblock number to write to + * @offset: offset within the physical eraseblock where to write + * @len: how many bytes from @buf to write + * @written: how many bytes were actually written + * + * This function writes @len bytes of data from buffer @buf to offset @offset + * of physical eraseblock @pnum. If all the data were successfully written, + * zero is returned. If an error occurred, this function returns a negative + * error code and stores the number of written bytes at @written (but there is + * no guarantee they were written correctly). If %-EIO is returned, the + * physical eraseblock most probably went bad. + */ +int ubi_io_write(const struct ubi_info *ubi, const void *buf, int pnum, + int offset, int len, int *written); + +/** + * ubi_io_write_data - write data to a physical eraseblock owned by a logical + * eraseblock. + * + * @ubi: the UBI device description object + * @buf: the data to write + * @pnum: the physical eraseblock number to write to + * @offset: offset within the logical eraseblock where to write + * @len: how many bytes from @buf to write + * @written: how many bytes were actually written + * + * This function is equivalent to 'ubi_io_write()', but @offset is relative to + * the beginning of the logical eraseblock, not to the beginning of the + * physical eraseblock. + */ +#define ubi_io_write_data(ubi, buf, pnum, offset, len, written) ({ \ + int __err; \ + ubi_assert((offset) >= 0); \ + __err = ubi_io_write(ubi, buf, pnum, (offset) + ubi->io->leb_start, \ + len, written); \ + __err = __err; \ +}) + +/** + * ubi_io_sync_erase - synchronously erase a physical eraseblock. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock number to erase + * @torture: if this physical eraseblock has to be tortured + * + * This function synchronously erases physical eraseblock @pnum. If @torture + * flag is not zero, the physical eraseblock is checked by means of writing + * different patterns and reading them back. If the torturing is enabled, the + * physical eraseblock is erased more then once. + * + * This function returns the number of erasures made in case of success, %-EIO + * if erasure failed or the torturing test failed, and other negative error + * codes in case of other errors. Note, %-EIO means that the physical + * eraseblock went bad. + */ +int ubi_io_sync_erase(const struct ubi_info *ubi, int pnum, int torture); + +/** + * ubi_io_is_bad - check if a physical eraseblock is bad. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock number to check + * + * This function returns a positive number if the physical eraseblock is bad, + * zero if not, and a negative error code if an error occurred. + */ +int ubi_io_is_bad(const struct ubi_info *ubi, int pnum); + +/** + * ubi_io_mark_bad - mark a physical eraseblock as bad. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock number to mark + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_io_mark_bad(const struct ubi_info *ubi, int pnum); + +/** + * ubi_io_read_ec_hdr - read and check an erase counter header. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock to read from + * @ec_hdr: a &struct ubi_ec_hdr object where to store the read erase counter + * header + * @verbose: be verbose in error-cases if non-zero + * + * This function reads the erase counter header from physical eraseblock @pnum + * and stores it in @ec_hdr. This function also checks CRC checksum of the read + * erase counter header. The following may be returned: + * + * o %0 if the CRC checksum is correct and the header was successfully read; + * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected + * and corrected by the flash driver; + * o %UBI_IO_BAD_EC_HDR if the erase counter header is corrupted due to a CRC + * error; + * o %UBI_IO_PEB_EMPTY if the physical eraseblock is empty; + * o %-EIO if an input/output error occurred and this function failed to read + * the erase counter header; this may indicate serious problems; + * o other negative values in case of other errors. + */ +int ubi_io_read_ec_hdr(const struct ubi_info *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr, int verbose); + +/** + * ubi_io_write_ec_hdr - write an erase counter header. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock to write to + * @ec_hdr: the erase counter header to write + * + * This function writes the erase counter header described by @ec_hdr to + * physical eraseblock @pnum. This function also fills some of the fields of + * @ec_hdrs before writing, so callers do not have to fill them. Callers + * must only fill the @ec_hdr->ec field. + * + * This function returns zero in case of success and a negative error code in + * case of failure. If %-EIO is returned, the physical eraseblock probably went + * bad. + */ +int ubi_io_write_ec_hdr(const struct ubi_info *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr); + +/** + * ubi_io_read_vid_hdr - read and check a volume identifier header. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock number to read from + * @vid_hdr: the &struct ubi_vid_hdr object where to store the read volume + * identifier header + * @verbose: be verbose in errors printing if non-zero + * + * This function reads the volume identifier header from physical eraseblock + * @pnum and stores it in @vid_hdr. It also checks the CRC checksum of the read + * volume identifier header. + * + * o %0 if the CRC checksum is correct and the header was successfully read; + * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected + * and corrected by the flash driver; + * o %UBI_IO_BAD_VID_HRD if the volume identifier header is corrupted - a CRC + * error detected; + * o %UBI_IO_PEB_FREE if the physical eraseblock is free; + * o %-EIO if an input/output error occurred and this function failed to read + * the erase counter header; this may indicate serious problems; + * o other negative values in case of other errors. + */ +int ubi_io_read_vid_hdr(const struct ubi_info *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr, int verbose); + +/** + * ubi_io_write_vid_hdr - write a volume identifier header. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock number to write to + * @vid_hdr: the contents of the volume identifier header + * + * This function writes the volume identifier header described by @vid_hdr to + * physical eraseblock @pnum. Callers do not have to fill all the fields of the + * passed volume identifier header object as this function fills many of them + * automatically. Callers have to only fill @vid_hdr->vol_type, + * @vid_hdr->leb_ver, @vid_hdr->vol_id, @vid_hdr->lnum, @vid_hdr->compat, + * @vid_hdr->data_size, @vid_hdr->used_ebs, @vid_hdr->ivol_data, and + * @vid_hdr->data_pad fields. + * + * This function returns zero in case of success and a negative error code in + * case of failure. If %-EIO is returned, the physical eraseblock probably went + * bad. + */ +int ubi_io_write_vid_hdr(const struct ubi_info *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr); + +/** + * ubi_io_init - initialize the UBI I/O unit for a given UBI device. + * + * @ubi: the UBI device description object + * @mtd_num: the underlying MTD device number + * @vid_hdr_offset: volume identifier headers offset + * @data_offset: logical eraseblock data offset + * + * If the @vid_hdr_offset and @data_offset parameters are zero, the default + * offsets are assumed, otherwise these ones are used. This function returns + * zero in case of success and a negative error code in case of failure. + */ +int __init ubi_io_init(struct ubi_info *ubi, int mtd_num, int vid_hdr_offset, + int data_offset); + +/** + * ubi_io_close - close the UBI I/O unit for a given UBI device. + * + * @ubi: the UBI device description object + */ +void __exit ubi_io_close(const struct ubi_info *ubi); + +/** + * struct ubi_io_info - UBI I/O units description data structure. + * + * @flash_size: the underlying MTD device size (in bytes) + * @peb_count: count of physical eraseblocks on the MTD device + * @peb_size: physical eraseblock size + * @bad_peb_count: number of bad physical eraseblocks + * @good_peb_count: number of good physical eraseblocks + * @min_io_size: the minimal input/output unit size of the underlying MTD + * device + * @hdrs_min_io_size: minimal I/O unit size used for VID and EC headers + * @ro_mode: if the UBI device is in read-only mode + * @leb_size: logical eraseblock size + * @leb_start: starting offset of logical eraseblocks within physical + * eraseblocks + * @ec_hdr_alsize: size of the erase counter header aligned to + * @hdrs_min_io_size + * @vid_hdr_alsize: size of the volume identifier header aligned to + * @hdrs_min_io_size + * @vid_hdr_offset: starting offset of volume identifier headers (might be + * unaligned) + * @vid_hdr_aloffset: starting offset of volume identifier headers aligned to + * @hdrs_min_io_size + * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset + * @bad_allowed: whether the underlying MTD device admits of bad physical + * eraseblocks or not + * @mtd_num: the underlying MTD device number + * @mtd_name: the underlying MTD device name + * @mtd: the underlying MTD device descriptor + * + * The erase counter header is always stored at offset zero. By default, the + * VID header is stored after the EC header at the closest aligned offset + * (note, aligned means aligned to the minimum I/O unit in this context). Data + * starts next to the VID header at the closest aligned offset. But for + * different reasons (e.g., optimization), UBI may be asked to put the VID + * header at another offset, and even at an unaligned offset. Of course, if the + * VID offset is unaligned, UBI adds proper padding before it. + * + * About minimal I/O units. In general, UBI assumes flash device model where + * there is only one minimal I/O unit size. E.g., in case of NOR flash it is 1, + * in case of NAND flash it is a NAND page, etc. This is reported my MTD in the + * @writesize field.. But as an exception, UBI admits of using another + * (smaller) minimal I/O unit size for EC and VID headers to make it possible + * to do different optimizations. So as you see, UBI admits of do many hack + * with your headers. + * + * For example, this is extremely useful in case of NAND flashes which admit of + * several write operations to one NAND page. In this case UBI can fit EC and + * VID headers at one NAND page. So, in case of such NAND flashes UBI uses + * "sub-page" size as a minimal I/O unit for the headers (the @hdrs_min_io_size + * field). But it still reports NAND page size (min_io_size) as a minimal I/O + * unit for the UBI users. + * + * Example: some Samsung NANDs with 2KiB pages allow 4x 512-byte writes, so + * although the minimal I/O unit is 2K, UBI uses 512 bytes for UBI and VID + * headers. + * + * Q: why not just to treat sub-page as a minimal I/O unit of this flash + * device, e.g., make @min_io_size = 512 in the example above? + * + * A: because when writing a sub-page, MTD still writes a full 2K page but the + * bytes which are no relevant to the sub-page are 0xFF. So, basically, writing + * 4x512 sub-pages is 4x times slower then writing one 2KiB NAND page. Thus, we + * prefer to use sub-pages only for EV and VID headers. + */ +struct ubi_io_info { + long long flash_size; /* public */ + int peb_count; /* public */ + int peb_size; /* public */ + int bad_peb_count; /* public */ + int good_peb_count; /* public */ + int min_io_size; /* public */ + int hdrs_min_io_size; /* public */ + int ro_mode; /* public */ + int leb_size; /* public */ + int leb_start; /* public */ + int ec_hdr_alsize; /* public */ + int vid_hdr_alsize; /* public */ + int vid_hdr_offset; /* public */ + int vid_hdr_aloffset; /* public */ + int vid_hdr_shift; /* public */ + int bad_allowed; /* public */ + int mtd_num; /* public */ + const char *mtd_name; /* public */ + struct mtd_info *mtd; /* private */ +}; + +#endif /* !__UBI_IO_H__ */ diff --git a/drivers/mtd/ubi/ivol.c b/drivers/mtd/ubi/ivol.c new file mode 100644 index 0000000..8904358 --- /dev/null +++ b/drivers/mtd/ubi/ivol.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include "ubi.h" +#include "scan.h" +#include "debug.h" +#include "alloc.h" +#include "vtbl.h" +#include "dtbl.h" +#include "io.h" +#include "ivol.h" +#include "account.h" + +const struct ubi_vtbl_vtr *ubi_ivol_get_vtr(const struct ubi_info *ubi, + int vol_id) +{ + const struct ubi_ivol_info *ivol = ubi->ivol; + + ubi_assert(ubi_is_ivol(vol_id)); + return &ivol->ivol_vtrs[vol_id - UBI_INTERNAL_VOL_START]; +} + +const struct ubi_dtbl_dtr *ubi_ivol_get_dtr(const struct ubi_info *ubi, + int vol_id) +{ + const struct ubi_ivol_info *ivol = ubi->ivol; + + ubi_assert(ubi_is_ivol(vol_id)); + return &ivol->ivol_dtrs[vol_id - UBI_INTERNAL_VOL_START]; +} + +int ubi_ivol_get_compat(const struct ubi_info *ubi, int vol_id) +{ + if (!ubi_is_ivol(vol_id)) { + ubi_assert(vol_id >= 0 && vol_id < ubi->acc->max_volumes); + return 0; + } + + switch (vol_id) { + case UBI_LAYOUT_VOL_ID: + return UBI_LAYOUT_VOLUME_COMPAT; + case UBI_UPDATE_VOL_ID: + return UBI_UPDATE_VOLUME_COMPAT; + default: + BUG(); + } + + return -ENODEV; +} + +int __init ubi_ivol_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si) +{ + struct ubi_vtbl_vtr *vtr; + struct ubi_dtbl_dtr *dtr; + struct ubi_ivol_info *ivol; + const struct ubi_io_info *io = ubi->io; + + ivol = ubi_kzalloc(sizeof(struct ubi_ivol_info)); + if (!ivol) + return -ENOMEM; + ubi->ivol = ivol; + + /* The layout volume */ + vtr = &ivol->ivol_vtrs[0]; + vtr->reserved_pebs = UBI_LAYOUT_VOLUME_EBS; + vtr->alignment = 1; + vtr->data_pad = 0; + vtr->vol_type = UBI_DYNAMIC_VOLUME; + vtr->name_len = sizeof(UBI_LAYOUT_VOLUME_NAME) - 1; + vtr->name = UBI_LAYOUT_VOLUME_NAME; + vtr->usable_leb_size = io->leb_size; + + dtr = &ivol->ivol_dtrs[0]; + dtr->used_ebs = vtr->reserved_pebs; + dtr->last_eb_bytes = vtr->reserved_pebs; + dtr->used_bytes = dtr->used_ebs * (io->leb_size - vtr->data_pad); + dtr->corrupted = 0; + + /* The update volume */ + vtr = &ivol->ivol_vtrs[1]; + vtr->reserved_pebs = UBI_UPDATE_VOLUME_EBS; + vtr->alignment = 1; + vtr->data_pad = 0; + vtr->vol_type = UBI_DYNAMIC_VOLUME; + vtr->name_len = sizeof(UBI_UPDATE_VOLUME_NAME) - 1; + vtr->name = UBI_UPDATE_VOLUME_NAME; + vtr->usable_leb_size = io->leb_size; + + dtr = &ivol->ivol_dtrs[1]; + dtr->used_ebs = vtr->reserved_pebs; + dtr->last_eb_bytes = vtr->reserved_pebs; + dtr->used_bytes = dtr->used_ebs * (io->leb_size - vtr->data_pad); + dtr->corrupted = 0; + + return 0; +} + +void __exit ubi_ivol_close(const struct ubi_info *ubi) +{ + ubi_kfree(ubi->ivol); +} diff --git a/drivers/mtd/ubi/ivol.h b/drivers/mtd/ubi/ivol.h new file mode 100644 index 0000000..b4efc02 --- /dev/null +++ b/drivers/mtd/ubi/ivol.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * Internal volume management unit. + * + * This unit provides information about internal volumes (this information is + * not stored on flash on flash). + */ + +#ifndef __UBI_IVOL_H__ +#define __UBI_IVOL_H__ + +#include +#include +#include "vtbl.h" +#include "dtbl.h" + +struct ubi_info; +struct ubi_scan_info; + +/** + * ubi_ivol_get_vtr - get volume table record of an internal volume. + * + * @ubi: the UBI device description object + * @vol_id: the requested volume ID + * + * This function returns a pointer to the volume tabe record. The @vol_id must + * be correct. + */ +const struct ubi_vtbl_vtr *ubi_ivol_get_vtr(const struct ubi_info *ubi, + int vol_id); + +/** + * ubi_ivol_get_dtr - get data table record of an internal volume. + * + * @ubi: the UBI device description object + * @vol_id: the requested volume ID + * + * This function returns a pointer to the data table record. The @vol_id must be + * correct. + */ +const struct ubi_dtbl_dtr *ubi_ivol_get_dtr(const struct ubi_info *ubi, + int vol_id); + +/** + * ubi_ivol_get_compat - get compatibility flags of a volume. + * + * @ubi: the UBI device description object + * @vol_id: volume ID + * + * This function returns compatibility flags of volumes. User volumes have no + * compatibility flags, so %0 is returned. The @vol_id must be correct. + */ +int ubi_ivol_get_compat(const struct ubi_info *ubi, int vol_id); + +/** + * ubi_ivol_init_scan - initialize the internal volume management unit + * using scanning information. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int __init ubi_ivol_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si); + +/** + * ubi_ivol_close - close the internal volume management unit. + * + * @ubi: the UBI device description object + */ +void __exit ubi_ivol_close(const struct ubi_info *ubi); + +/** + * ubi_is_ivol - test if a volume is internal volume. + * + * @vol_id: ID of the volume to test. + * + * If the volume is internal volume, %1 is returned, otherwise %0 is returned. + */ +static inline int ubi_is_ivol(int vol_id) +{ + return vol_id >= UBI_INTERNAL_VOL_START && + vol_id < UBI_INTERNAL_VOL_START + UBI_INT_VOL_COUNT; +} + +/* + * ubi_ivol_is_known - check if this is a known internal volume. + * + * @vol_id: ID of the volume to check. + * + * This function returns non-zero if this is a supported internal volume and + * non-zero if not. + */ +static inline int ubi_ivol_is_known(int vol_id) +{ + return vol_id == UBI_LAYOUT_VOL_ID || vol_id == UBI_UPDATE_VOL_ID; +} + +/** + * struct ubi_ivol_info - internal volume management unit description data + * structure. + * + * @ivol_vtrs: volume table records correspondind to internal volumes + * @ivol_dtrs: data table records correspondind to internal volumes + */ +struct ubi_ivol_info { + struct ubi_vtbl_vtr ivol_vtrs[UBI_INT_VOL_COUNT]; /* private */ + struct ubi_dtbl_dtr ivol_dtrs[UBI_INT_VOL_COUNT]; /* private */ +}; + +#endif /* !__UBI_IVOL_H__ */ + diff --git a/drivers/mtd/ubi/misc.h b/drivers/mtd/ubi/misc.h new file mode 100644 index 0000000..bf4443c --- /dev/null +++ b/drivers/mtd/ubi/misc.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#ifndef __UBI_MISC_H__ +#define __UBI_MISC_H__ + +#include +#include +#include "alloc.h" +#include "debug.h" +#include "io.h" + +#define xquotise(s) #s +#define quotise(s) xquotise(s) + +/** + * rb_for_each_entry - walk an RB-tree. + * + * @rb: a pointer to type 'struct rb_node' to to use as a loop counter + * @pos: a pointer to RB-tree entry type to use as a loop counter + * @root: RB-tree's root + * @member: the name of the 'struct rb_node' within the RB-tree entry + */ +#define rb_for_each_entry(rb, pos, root, member) \ + for (rb = rb_first(root), \ + pos = (rb ? container_of(rb, typeof(*pos), member) : NULL); \ + rb; \ + rb = rb_next(rb), pos = container_of(rb, typeof(*pos), member)) + +/** + * ubi_buf_all_ff - check if buffer contains only 0xFF bytes. + * + * @buf: buffer to check + * @size: buffer size in bytes + * + * This function returns non-zero in there are only 0xFF bytes in @buf, and + * zero if something else was also found. + */ +static inline int ubi_buf_all_ff(const void *buf, size_t size) +{ + int i; + + for (i = 0; i < size / sizeof(unsigned int); i++) + if (((const unsigned int *)buf)[i] != ~((unsigned int)0)) + return 0; + + for (i = i; i < size; i++) + if (((const uint8_t *) buf)[i] != 0xFF) + return 0; + + return 1; +} + +/** + * ubi_buf_all_zeroes - check if buffer contains only zeroes. + * + * @buf: buffer to check + * @size: buffer size in bytes + * + * This function returns non-zero in there are only 0 bytes in @buf, and + * zero if something else was also found. + */ +static inline int ubi_buf_all_zeroes(const void *buf, size_t size) +{ + int i; + + for (i = 0; i < size / sizeof(unsigned int); i++) + if (((const unsigned int *)buf)[i] != 0) + return 0; + + for (i = i; i < size; i++) + if (((const uint8_t *) buf)[i] != '\0') + return 0; + + return 1; +} + +/** + * ubi_buf_all_ff - check if buffer contains only a certain byte pattern. + * + * @buf: buffer to check + * @patt: the pattern to check + * @size: buffer size in bytes + * + * This function returns non-zero in there are only @patt bytes in @buf, and + * zero if something else was also found. + */ +static inline int ubi_check_pattern(const void *buf, uint8_t patt, size_t size) +{ + int i; + + for (i = 0; i < size; i++) + if (((const uint8_t *)buf)[i] != patt) + return 0; + return 1; +} + +/** + * strdup_len - duplacate a string. + * + * @str: original string + * @len: the length of the string + */ +static inline char *strdup_len(const char *str, int len) +{ + char *dup; + + ubi_assert(strnlen(str, len + 1) == len); + + dup = ubi_kmalloc(len + 1); + if (!dup) + return NULL; + + memcpy(dup, str, len); + dup[len] = '\0'; + + return dup; +} + +/* + * align_up - align an integer to another integer + * + * @x: the integer to align + * @y: the integer to align to + * + * This function returns the lowest number which is multiple to @y and not less + * then @x. + */ +static inline int align_up(int x, int y) +{ + return ((x + y - 1) / y) * y; +} + +/** + * calc_data_len - calculate how much data is stored in a physical eraseblock. + * + * @ubi: the UBI device description object + * @buf: a buffer with the contents of the physical eraseblock + * @length: the buffer length + * + * This function calculates how much real data is stored in @buf. Continuous + * 0xFF bytes at the end of the buffer are not considered as data. + */ +static inline int ubi_calc_data_len(const struct ubi_info *ubi, + const unsigned char *buf, int length) +{ + int i; + + ubi_assert(length % ubi->io->min_io_size == 0); + + for (i = length - 1; i >= 0; i--) + if (buf[i] != 0xFF) + break; + + /* The resulting length must be aligned to the minimum flash I/O size */ + length = align_up(i + 1, ubi->io->min_io_size); + return length; +} + +#endif /* !__UBI_MISC_H__ */ diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c new file mode 100644 index 0000000..8a3111e --- /dev/null +++ b/drivers/mtd/ubi/scan.c @@ -0,0 +1,1371 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ubi.h" +#include "alloc.h" +#include "scan.h" +#include "io.h" +#include "misc.h" +#include "ivol.h" +#include "debug.h" + +static int __init vid_hdr_sanity_check(const struct ubi_info *ubi, + const struct ubi_vid_hdr *vid_hdr, + const struct ubi_scan_volume *sv, + int pnum); + +static int __init add_to_erase(const struct ubi_info *ubi, + struct ubi_scan_info *si, int pnum, int ec); + +static int __init add_volume(const struct ubi_info *ubi, + struct ubi_scan_info *si, int vol_id, int pnum, + const struct ubi_vid_hdr *vid_hdr, + struct ubi_scan_volume **vol_info); + +static int __init compare_lebs(const struct ubi_info *ubi, + const struct ubi_scan_leb *seb, int pnum, + const struct ubi_vid_hdr *vid_hdr); + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_SCAN +static int __init paranoid_check_si(const struct ubi_info *ubi, + struct ubi_scan_info *si); +#else +#define paranoid_check_si(ubi, si) 0 +#endif + +int __init ubi_scan_add_volume(const struct ubi_info *ubi, + struct ubi_scan_info *si, int pnum, int ec, + const struct ubi_vid_hdr *vid_hdr, int bitflips) +{ + int err, vol_id, lnum; + uint32_t leb_ver; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb; + struct rb_node **p, *parent = NULL; + + vol_id = ubi32_to_cpu(vid_hdr->vol_id); + lnum = ubi32_to_cpu(vid_hdr->lnum); + leb_ver = ubi32_to_cpu(vid_hdr->leb_ver); + + dbg_scan("PEB %d, LEB %d, volume ID %d, EC %d, LEB ver %u, bitflips %d", + pnum, lnum, vol_id, ec, leb_ver, bitflips); + + err = add_volume(ubi, si, vol_id, pnum, vid_hdr, &sv); + if (unlikely(err) < 0) + return err; + else if (unlikely(err > 0)) { + /* Data CRC failed, drop this physical eraseblock */ + return add_to_erase(ubi, si, pnum, ec); + } + + /* + * Walk the RB-tree of logical eraseblocks of volume @volume ID to look + * if this is the first instance of this logical eraseblock or not. + */ + p = &sv->root.rb_node; + while (*p) { + int cmp_res; + + parent = *p; + seb = rb_entry(parent, struct ubi_scan_leb, rb); + + if (lnum != seb->lnum) { + if (lnum < seb->lnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + continue; + } + + /* + * There is already a logical eraseblock with this logical + * eraseblock number present. + */ + + dbg_scan("this LEB already exists: PEB %d, LEB ver %u, EC %d", + seb->pnum, seb->leb_ver, seb->ec); + + /* + * Make sure that the logical eraseblocks have different + * versions. Otherwise the image is bad. + */ + if (unlikely(seb->leb_ver == leb_ver)) { + ubi_err("two LEBs with same version %u", leb_ver); + ubi_dbg_dump_seb(seb, 0); + ubi_dbg_dump_vid_hdr(vid_hdr); + return -EINVAL; + } + + /* + * Now we have to drop the older one and get the newer + * one. + */ + cmp_res = compare_lebs(ubi, seb, pnum, vid_hdr); + if (unlikely(cmp_res < 0)) + return cmp_res; + + if (cmp_res) { + /* + * This logical eraseblock is newer then the one we + * found earlier. + */ + err = vid_hdr_sanity_check(ubi, vid_hdr, sv, + pnum); + if (unlikely(err)) + return err; + + err = add_to_erase(ubi, si, seb->pnum, seb->ec); + if (unlikely(err)) + return err; + + seb->ec = ec; + seb->pnum = pnum; + seb->leb_ver = leb_ver; + seb->scrub = cmp_res == 2 ? 1 : 0; + seb->scrub = bitflips; + + if (sv->highest_lnum == lnum) + sv->last_data_size = + ubi32_to_cpu(vid_hdr->data_size); + + return 0; + } else + /* This logical eraseblock is old, wipe it */ + return add_to_erase(ubi, si, pnum, ec); + } + + /* + * We've met this logical eraseblock for the first time, add it to the + * scanning information. + */ + + err = vid_hdr_sanity_check(ubi, vid_hdr, sv, pnum); + if (unlikely(err)) + return err; + + seb = ubi_alloc_scan_leb(); + if (unlikely(!seb)) + return -ENOMEM; + + seb->ec = ec; + seb->pnum = pnum; + seb->lnum = lnum; + seb->leb_ver = leb_ver; + seb->scrub = bitflips; + + if (sv->highest_lnum <= lnum) { + sv->highest_lnum = lnum; + sv->last_data_size = ubi32_to_cpu(vid_hdr->data_size); + } + + sv->leb_count += 1; + rb_link_node(&seb->rb, parent, p); + rb_insert_color(&seb->rb, &sv->root); + return 0; +} + +int __init ubi_scan_add_to_corrupted(struct ubi_scan_info *si, int pnum, int ec) +{ + struct ubi_scan_leb *seb; + + dbg_scan("PEB %d (EC %d) is corrupted", pnum, ec); + + seb = ubi_alloc_scan_leb(); + if (unlikely(!seb)) + return -ENOMEM; + + seb->pnum = pnum; + seb->ec = ec; + list_add_tail(&seb->list, &si->corr); + return 0; +} + +struct ubi_scan_volume __init * +ubi_scan_get_scan_volume(const struct ubi_scan_info *si, int vol_id) +{ + struct ubi_scan_volume *sv; + struct rb_node *p = si->volumes.rb_node; + + while (p) { + sv = rb_entry(p, struct ubi_scan_volume, rb); + + if (vol_id == sv->vol_id) + return sv; + + if (vol_id > sv->vol_id) + p = p->rb_left; + else + p = p->rb_right; + } + + return NULL; +} + +struct ubi_scan_leb __init * +ubi_scan_get_scan_leb(const struct ubi_scan_volume *sv, int lnum) +{ + struct ubi_scan_leb *seb; + struct rb_node *p = sv->root.rb_node; + + while (p) { + seb = rb_entry(p, struct ubi_scan_leb, rb); + + if (lnum == seb->lnum) + return seb; + + if (lnum > seb->lnum) + p = p->rb_left; + else + p = p->rb_right; + } + + return NULL; +} + +static int __init process_eb(const struct ubi_info *ubi, + struct ubi_scan_info *si, int pnum); +static void __init commit_to_mean_value(struct ubi_scan_info *si); + +/* Temporary variables used during scanning */ +static struct ubi_ec_hdr *ech; +static struct ubi_vid_hdr *vidh; + +int __init ubi_scan(struct ubi_info *ubi, struct ubi_scan_info **info) +{ + int err, pnum; + struct rb_node *rb1, *rb2; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb; + struct ubi_scan_info *si; + struct ubi_io_info *io = ubi->io; + + *info = NULL; + + si = ubi_kzalloc(sizeof(struct ubi_scan_info)); + if (!si) + return -ENOMEM; + + INIT_LIST_HEAD(&si->corr); + INIT_LIST_HEAD(&si->free); + INIT_LIST_HEAD(&si->erase); + INIT_LIST_HEAD(&si->alien); + si->volumes = RB_ROOT; + si->is_empty = 1; + + err = -ENOMEM; + ech = ubi_zalloc_ec_hdr(ubi); + if (!ech) + goto out_si; + + vidh = ubi_zalloc_vid_hdr(ubi); + if (!vidh) + goto out_ech; + + for (pnum = 0; pnum < io->peb_count; pnum++) { + cond_resched(); + + err = process_eb(ubi, si, pnum); + if (unlikely(err < 0)) + goto out_vidh; + + if (err != UBI_IO_PEB_EMPTY) + si->is_empty = 0; + } + + /* Finish mean erase counter calculations */ + if (si->ec_count) + commit_to_mean_value(si); + + /* + * FIXME: this is actually duty of the I/O unit to initialize this, but + * MTD does not provide enough information. + */ + io->bad_peb_count = si->bad_peb_count; + io->good_peb_count = io->peb_count - io->bad_peb_count; + + *info = si; + + if (si->is_empty) + ubi_msg("empty MTD device detected"); + + /* + * In case of unknown erase counter we use the mean erase counter + * value. + */ + rb_for_each_entry(rb1, sv, &si->volumes, rb) { + cond_resched(); + rb_for_each_entry(rb2, seb, &sv->root, rb) + if (seb->ec == -1) + seb->ec = si->mean_ec; + } + + cond_resched(); + list_for_each_entry(seb, &si->free, list) + if (seb->ec == -1) + seb->ec = si->mean_ec; + + cond_resched(); + list_for_each_entry(seb, &si->corr, list) + if (seb->ec == -1) + seb->ec = si->mean_ec; + + cond_resched(); + list_for_each_entry(seb, &si->erase, list) + if (seb->ec == -1) + seb->ec = si->mean_ec; + + err = paranoid_check_si(ubi, si); + if (err > 0) { + err = -EINVAL; + goto out_vidh; + } + + ubi_free_vid_hdr(ubi, vidh); + ubi_free_ec_hdr(ubi, ech); + return err; + +out_vidh: + ubi_free_vid_hdr(ubi, vidh); +out_ech: + ubi_free_ec_hdr(ubi, ech); +out_si: + ubi_scan_destroy_si(si); + return err; +} + +void __init ubi_scan_rm_volume(const struct ubi_info *ubi, + struct ubi_scan_info *si, + struct ubi_scan_volume *sv) +{ + struct rb_node *rb; + struct ubi_scan_leb *seb; + + dbg_scan("remove scanning information about volume %d", sv->vol_id); + + while ((rb = rb_first(&sv->root))) { + cond_resched(); + + seb = rb_entry(rb, struct ubi_scan_leb, rb); + + /* The physical eraseblock will be erased later */ + rb_erase(&seb->rb, &sv->root); + list_add_tail(&seb->list, &si->erase); + } + + rb_erase(&sv->rb, &si->volumes); + ubi_free_scan_volume(sv); + si->vols_found -= 1; +} + +int __init ubi_scan_early_erase_peb(const struct ubi_info *ubi, + const struct ubi_scan_info *si, int pnum, + int ec) +{ + int err; + struct ubi_ec_hdr *ec_hdr; + + ec_hdr = ubi_zalloc_ec_hdr(ubi); + if (!ec_hdr) + return -ENOMEM; + + if (unlikely(ec >= UBI_MAX_ERASECOUNTER)) { + /* + * Erase counter overflow. Upgrade UBI and use 64-bit + * erase counters internally. + */ + ubi_err("erase counter overflow at PEB %d, EC %d", + pnum, ec); + return -EINVAL; + } + + ec_hdr->ec = cpu_to_ubi64(ec); + + err = ubi_io_sync_erase(ubi, pnum, 0); + if (unlikely(err < 0)) + goto out_free; + + err = ubi_io_write_ec_hdr(ubi, pnum, ec_hdr); + +out_free: + ubi_free_ec_hdr(ubi, ec_hdr); + return err; +} + +struct ubi_scan_leb __init *ubi_scan_get_free_peb(const struct ubi_info *ubi, + struct ubi_scan_info *si) +{ + int err = 0, i; + struct ubi_scan_leb *seb; + + if (!list_empty(&si->free)) { + seb = list_entry(si->free.next, struct ubi_scan_leb, + list); + list_del(&seb->list); + return seb; + } + + if (unlikely(list_empty(&si->erase) && list_empty(&si->corr))) { + ubi_err("no vacant eraseblocks found"); + return ERR_PTR(-ENOSPC); + } + + for (i = 0; i < 2; i++) { + struct list_head *head; + struct ubi_scan_leb *tmp_seb; + + if (i == 0) + head = &si->erase; + else + head = &si->corr; + + /* + * We try to erase the first physical eraseblock from the @head + * listand pick it if we succeed, or try to erase the + * next one if not. And so forth. We don't want to take care + * about bad eraseblocks here - they'll be handled later. + */ + list_for_each_entry_safe(seb, tmp_seb, head, list) { + cond_resched(); + + if (seb->ec == -1) + seb->ec = si->mean_ec; + + err = ubi_scan_early_erase_peb(ubi, si, seb->pnum, + seb->ec); + if (unlikely(err)) + continue; + + list_del(&seb->list); + dbg_scan("return PEB %d, EC %d", seb->pnum, seb->ec); + return seb; + } + } + + return ERR_PTR(err ? err : -ENOSPC); +} + +/** + * add_to_alien - add a physical eraseblock to the @si->alien list. + * + * @si: a pointer to the scanning information + * @pnum: the physical eraseblock number + * + * This function returns zero in case of success and a negative error + * code in case of failure. + */ +static int __init add_to_alien(struct ubi_scan_info *si, int pnum) +{ + struct ubi_scan_leb *seb; + + dbg_scan("PEB %d is alien", pnum); + + seb = ubi_alloc_scan_leb(); + if (unlikely(!seb)) + return -ENOMEM; + + seb->pnum = pnum; + list_add_tail(&seb->list, &si->alien); + return 0; +} + +/** + * add_volume - add a volume tho the scanning information. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * @vol_id: ID of the volume to add + * @pnum: physical eraseblock number + * @vid_hdr: volume identifier header + * @vol_info: a pointer to the corresponding volume scanning information is + * returned here. + * + * If the volume corresponding to the @vid_hdr logical eraseblock is already + * present in the scanning information, this function does nothing and just + * returns. This function returns zero in case of success and a negative error + * code in case of failure. + */ +static int __init add_volume(const struct ubi_info *ubi, + struct ubi_scan_info *si, int vol_id, int pnum, + const struct ubi_vid_hdr *vid_hdr, + struct ubi_scan_volume **vol_info) +{ + int ret = 0; + struct ubi_scan_volume *sv; + struct rb_node **p = &si->volumes.rb_node, *parent = NULL; + + ubi_assert(vol_id == ubi32_to_cpu(vid_hdr->vol_id)); + + /* + * Walk the volume RB-tree to look if a volume @vol_id is already + * present there. + */ + while (*p) { + parent = *p; + sv = rb_entry(parent, struct ubi_scan_volume, rb); + + if (vol_id == sv->vol_id) { + *vol_info = sv; + return 0; + } + + if (vol_id > sv->vol_id) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + /* + * The volume is absent, we have to add it. But before this, we have to + * check data CRC of this logical eraseblock. + */ + if (vid_hdr->copy_flag) { + int len, read, err; + void *buf; + uint32_t data_crc, crc; + + len = ubi32_to_cpu(vid_hdr->data_size); + buf = ubi_kmalloc(len); + if (unlikely(!buf)) + return -ENOMEM; + + err = ubi_io_read_data(ubi, buf, pnum, 0, len, &read); + if (unlikely(read != len)) { + ubi_kfree(buf); + return err; + } + + data_crc = ubi32_to_cpu(vid_hdr->data_crc); + crc = crc32(UBI_CRC32_INIT, buf, len); + if (unlikely(crc != data_crc)) { + dbg_scan("PEB %d CRC error: calculated %#08x, must be %#08x", + pnum, crc, data_crc); + ubi_kfree(buf); + return 1; + } + ubi_kfree(buf); + } + + sv = ubi_alloc_scan_volume(); + if (unlikely(!sv)) + return -ENOMEM; + *vol_info = sv; + + sv->highest_lnum = sv->leb_count = 0; + sv->vol_id = vol_id; + sv->root = RB_ROOT; + sv->used_ebs = ubi32_to_cpu(vid_hdr->used_ebs); + sv->data_pad = ubi32_to_cpu(vid_hdr->data_pad); + sv->compat = vid_hdr->compat; + sv->vol_type = vid_hdr->vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME + : UBI_STATIC_VOLUME; + if (vol_id > si->highest_vol_id) + si->highest_vol_id = vol_id; + + rb_link_node(&sv->rb, parent, p); + rb_insert_color(&sv->rb, &si->volumes); + si->vols_found += 1; + dbg_scan("added volume %d", vol_id); + return ret; +} + +/** + * compare_lebs - find out which logical eraseblock is newer. + * + * @ubi: the UBI device description object + * @seb: the first logical eraseblock to compare + * @pnum: the physical eraseblock number of the second logical eraseblock to + * compare + * @vid_hdr: the volume identifier header of the second logical eraseblock + * + * This function returns zero if the first logical eraseblock (described by + * @seb) is newer then the second logical eraseblock (described by @pnum and + * @vid_hdr), %1 if the second is newer, and %2 id the second is newer, but + * needs scrubbing. In case of failure, a negative error code is returned. + */ +static int __init compare_lebs(const struct ubi_info *ubi, + const struct ubi_scan_leb *seb, int pnum, + const struct ubi_vid_hdr *vid_hdr) +{ + void *buf; + int err, ret, read, len; + uint32_t data_crc, crc; + uint32_t leb_ver = ubi32_to_cpu(vid_hdr->leb_ver); + long long abs, v1 = seb->leb_ver, v2 = leb_ver; + + /* + * UBI constantly increases the logical eraseblock version number and + * it can overflow. Thus, we have to bear in mind that versions that + * are close to %0xFFFFFFFF are less then versions that are close to + * %0. + * + * The UBI WL unit guarantees that the number of pending tasks is not + * greater then %0x7FFFFFFF. So, if the difference between any two + * versions is greater or equivalent to %0x7FFFFFFF, there was an + * overflow and the logical eraseblock with lower version is actually + * newer then the one with higher version. + */ + + abs = v1 - v2; + if (abs < 0) + abs = -abs; + + if (abs < 0x7FFFFFFF) { + /* Non-overflow situation */ + if (seb->leb_ver > leb_ver) + return 0; + if (!vid_hdr->copy_flag) + return 1; + } else { + if (seb->leb_ver > leb_ver) + return 1; + if (!vid_hdr->copy_flag) + return 0; + } + + /* OK, the second LEB is newer */ + + if (!vid_hdr->copy_flag) + return 1; + + /* + * The second logical eraseblock has 'copy_flag' set which means that + * it was moved by the wear-leveling unit. We have to check its data + * CRC to ensure the copy process was not interrupted and the logical + * eraseblock was copied fully. + */ + + len = ubi32_to_cpu(vid_hdr->data_size); + buf = ubi_kmalloc(len); + if (unlikely(!buf)) + return -ENOMEM; + + err = ubi_io_read_data(ubi, buf, pnum, 0, len, &read); + if (unlikely(read != len)) { + ubi_kfree(buf); + return err; + } + + data_crc = ubi32_to_cpu(vid_hdr->data_crc); + crc = crc32(UBI_CRC32_INIT, buf, len); + if (unlikely(crc != data_crc)) { + dbg_scan("PEB %d CRC error: calculated %#08x, must be %#08x", + pnum, crc, data_crc); + ret = 0; + } else + ret = err == UBI_IO_BITFLIPS ? 2 : 1; + dbg_scan("PEB %d CRC is OK", pnum); + ubi_kfree(buf); + return ret; +} + +/** + * add_to_erase - add a physical eraseblock to the list of physical eraseblocks + * which have to be erased. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * @pnum: the physical eraseblock number + * @ec: erase counter of this physical eraseblock + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int __init add_to_erase(const struct ubi_info *ubi, + struct ubi_scan_info *si, int pnum, int ec) +{ + struct ubi_scan_leb *seb; + + dbg_scan("PEB %d, EC %d", pnum, ec); + + seb = ubi_alloc_scan_leb(); + if (unlikely(!seb)) + return -ENOMEM; + + seb->pnum = pnum; + seb->ec = ec; + list_add_tail(&seb->list, &si->erase); + return 0; +} + +static void __init destroy_sv(struct ubi_scan_volume *sv); + +void __init ubi_scan_destroy_si(struct ubi_scan_info *si) +{ + struct ubi_scan_leb *seb, *seb_tmp; + struct ubi_scan_volume *sv; + struct rb_node *rb; + + list_for_each_entry_safe(seb, seb_tmp, &si->alien, list) { + list_del(&seb->list); + ubi_free_scan_leb(seb); + } + list_for_each_entry_safe(seb, seb_tmp, &si->erase, list) { + list_del(&seb->list); + ubi_free_scan_leb(seb); + } + list_for_each_entry_safe(seb, seb_tmp, &si->corr, list) { + list_del(&seb->list); + ubi_free_scan_leb(seb); + } + list_for_each_entry_safe(seb, seb_tmp, &si->free, list) { + list_del(&seb->list); + ubi_free_scan_leb(seb); + } + + /* Destroy the volume RB-tree */ + rb = si->volumes.rb_node; + while (rb) { + if (rb->rb_left) + rb = rb->rb_left; + else if (rb->rb_right) + rb = rb->rb_right; + else { + sv = rb_entry(rb, struct ubi_scan_volume, rb); + + rb = rb_parent(rb); + if (rb) { + if (rb->rb_left == &sv->rb) + rb->rb_left = NULL; + else + rb->rb_right = NULL; + } + + destroy_sv(sv); + } + } + + ubi_kfree(si); +} + +/** + * add_to_free - add a physical eraseblock to the list of free physical + * eraseblocks. + * + * @si: a pointer to the scanning information + * @pnum: the physical eraseblock number + * @ec: erase counter of this physical eraseblock + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +__init static inline int add_to_free(struct ubi_scan_info *si, int pnum, int ec) +{ + struct ubi_scan_leb *seb; + + dbg_scan("PEB %d, EC %d", pnum, ec); + ubi_assert(ec >= 0); + + seb = ubi_alloc_scan_leb(); + if (unlikely(!seb)) + return -ENOMEM; + + seb->pnum = pnum; + seb->ec = ec; + list_add_tail(&seb->list, &si->free); + return 0; +} + +/** + * process_eb - read UBI headers, check them and add corresponding data + * to the scanning information. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * @pnum: the physical eraseblock number + * + * This function returns a negative error code in case of failure, or th + * physical eraseblock type (%UBI_IO_PEB_EMPTY, %UBI_IO_PEB_FREE, + * %UBI_IO_BAD_EC_HDR, or %UBI_IO_BAD_VID_HDR), or %0 if the physical + * eraseblock is bad. + */ +static int __init process_eb(const struct ubi_info *ubi, + struct ubi_scan_info *si, int pnum) +{ + int64_t ec; + int err, bitflips = 0, vol_id, ec_corr = 0; + + /* Skip bad physical eraseblocks */ + err = ubi_io_is_bad(ubi, pnum); + if (unlikely(err < 0)) + return err; + else if (err) { + si->bad_peb_count += 1; + return UBI_IO_PEB_EMPTY; + } + + err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); + if (unlikely(err < 0)) + return err; + else if (unlikely(err == UBI_IO_BITFLIPS)) + bitflips = 1; + else if (err == UBI_IO_PEB_EMPTY) { + int err1; + + err1 = ubi_scan_add_to_corrupted(si, pnum, -1); + if (unlikely(err1)) + err = err1; + return err; + } else if (err == UBI_IO_BAD_EC_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_corr = 1; + ec = -1; + bitflips = 1; + } + + if (!ec_corr) { + /* Make sure UBI version is OK */ + if (unlikely(ech->version != UBI_VERSION)) { + ubi_err("this UBI version is %d, image version is %d", + UBI_VERSION, (int)ech->version); + return -EINVAL; + } + + ec = ubi64_to_cpu(ech->ec); + if (unlikely(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; + } + } + + /* 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 (unlikely(err < 0)) + return err; + + if (err == UBI_IO_BAD_VID_HDR || + (err == UBI_IO_PEB_FREE && ec_corr)) { + /* VID header is corrupted */ + err = ubi_scan_add_to_corrupted(si, pnum, ec); + if (unlikely(err)) + return err; + err = UBI_IO_BAD_VID_HDR; + goto adjust_mean_ec; + } + + if (err == UBI_IO_PEB_FREE) { + /* No VID header - the physical eraseblock is free */ + err = add_to_free(si, pnum, ec); + if (unlikely(err)) + return err; + err = UBI_IO_PEB_FREE; + goto adjust_mean_ec; + } + + if (unlikely(err == UBI_IO_BITFLIPS)) + bitflips = 1; + + vol_id = ubi32_to_cpu(vidh->vol_id); + if (unlikely(!ubi_ivol_is_known(vol_id))) { + int lnum = ubi32_to_cpu(vidh->lnum); + + /* Unsupported internal volume */ + switch (vidh->compat) { + case UBI_COMPAT_DELETE: + ubi_msg("\"delete\" compatible internal volume %d:%d" + " found, remove it", vol_id, lnum); + err = ubi_scan_add_to_corrupted(si, pnum, ec); + if (unlikely(err)) + return err; + break; + + case UBI_COMPAT_RO: + ubi_msg("read-only compatible internal volume %d:%d" + " found, switch to read-only mode", + vol_id, lnum); + ubi->io->ro_mode = 1; + break; + + case UBI_COMPAT_PRESERVE: + ubi_msg("\"preserve\" compatible internal volume %d:%d" + " found", vol_id, lnum); + err = add_to_alien(si, pnum); + if (unlikely(err)) + return err; + si->alien_peb_count += 1; + return UBI_IO_PEB_EMPTY; + + case UBI_COMPAT_REJECT: + ubi_err("incompatible internal volume %d:%d found", + vol_id, lnum); + return -EINVAL; + } + } + + /* Both UBI headers seem to be fine */ + err = ubi_scan_add_volume(ubi, si, pnum, ec, vidh, bitflips); + if (unlikely(err)) + return err; + +adjust_mean_ec: + if (!ec_corr) { + if (si->ec_sum + ec < ec) { + commit_to_mean_value(si); + si->ec_sum = 0; + si->ec_count = 0; + } else { + 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 err; +} + +/** + * destroy_sv - free the scanning volume information + * + * @sv: scanning volume information + * + * This function destroys the volume RB-tree (@sv->root) and the scanning + * volume information. + */ +static void __init destroy_sv(struct ubi_scan_volume *sv) +{ + struct ubi_scan_leb *seb; + struct rb_node *this = sv->root.rb_node; + + while (this) { + if (this->rb_left) + this = this->rb_left; + else if (this->rb_right) + this = this->rb_right; + else { + seb = rb_entry(this, struct ubi_scan_leb, rb); + this = rb_parent(this); + if (this) { + if (this->rb_left == &seb->rb) + this->rb_left = NULL; + else + this->rb_right = NULL; + } + + ubi_free_scan_leb(seb); + } + } + ubi_free_scan_volume(sv); +} + +/** + * commit_to_mean_value - commit intermediate results to the final mean erase + * counter value. + * + * @si: the scanning information + * + * This function is a helper function which calculates partial mean value and + * adds it to the resulting mean value. As we can work only in integer + * arithmetics and we want to calculate the mean value of erase counter + * accurately, we first sum erase counter values in @si->ec_sum variable and + * count these components in @si->ec_count. If this temporary @si->ec_sum is + * going to overflow, we calculate the partial mean value + * (@si->ec_sum/@si->ec_count) and add it to @si->mean_ec. + */ +static void __init commit_to_mean_value(struct ubi_scan_info *si) +{ + int rem; + + rem = si->ec_sum % si->ec_count; + si->ec_sum /= si->ec_count; + if (rem >= si->ec_count / 2) + si->mean_ec += 1; + si->mean_ec += si->ec_sum; +} + +/** + * vid_hdr_sanity_check - check that a volume identifier header is sane. + * + * @ubi: the UBI device description object + * @vid_hdr: the volume identifier header to check + * @sv: information about the volume this logical eraseblock belongs to + * @pnum: the physical eraseblock number the VID header came from + * + * This function checks that data stored in the volume identifier header + * @vid_hdr is consistent. This function returns non-zero if an inconsistency + * was found and zero if not. + * + * Note, UBI does sanity check of everything it reads from the flash media. + * Most of the checks are done in the I/O unit. Here we check that the + * information in this VID header is consistent to information in other VID + * headers of the same volume. + */ +static int __init vid_hdr_sanity_check(const struct ubi_info *ubi, + const struct ubi_vid_hdr *vid_hdr, + const struct ubi_scan_volume *sv, + int pnum) +{ + int vol_type = vid_hdr->vol_type; + int vol_id = ubi32_to_cpu(vid_hdr->vol_id); + int used_ebs = ubi32_to_cpu(vid_hdr->used_ebs); + int data_pad = ubi32_to_cpu(vid_hdr->data_pad); + + if (sv->leb_count != 0) { + /* + * This is not the first logical eraseblock belonging to this + * volume. Ensure that the data in its VID header is consistent + * to the data in previous logical eraseblocks' headers. + */ + int sv_vol_type; + + if (unlikely(vol_id != sv->vol_id)) { + dbg_err("inconsistent vol_id"); + goto bad; + } + + if (sv->vol_type == UBI_STATIC_VOLUME) + sv_vol_type = UBI_VID_STATIC; + else + sv_vol_type = UBI_VID_DYNAMIC; + + if (unlikely(vol_type != sv_vol_type)) { + dbg_err("inconsistent vol_type"); + goto bad; + } + + if (unlikely(used_ebs != sv->used_ebs)) { + dbg_err("inconsistent used_ebs"); + goto bad; + } + + if (unlikely(data_pad != sv->data_pad)) { + dbg_err("inconsistent data_pad"); + goto bad; + } + } + + return 0; + +bad: + ubi_err("bad VID header at PEB %d", pnum); + ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_dbg_dump_sv(sv); + return -EINVAL; +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_SCAN + +/** + * paranoid_check_si - check if the scanning information is sane and correct. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * + * This function returns zero if the scanning information is all right, %1 if + * not and a negative error code if an error occurred. + */ +static int __init paranoid_check_si(const struct ubi_info *ubi, + struct ubi_scan_info *si) +{ + int pnum, err, vols_found = 0; + struct rb_node *rb1, *rb2; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb, *last_seb; + const struct ubi_io_info *io = ubi->io; + uint8_t *buf; + + /* + * At first, check that scanning information is sane. + */ + rb_for_each_entry(rb1, sv, &si->volumes, rb) { + int leb_count = 0; + + cond_resched(); + + vols_found += 1; + + if (unlikely(si->is_empty)) { + ubi_err("bad is_empty flag"); + goto bad_sv; + } + + if (unlikely(sv->vol_id < 0 || sv->highest_lnum < 0 || + sv->leb_count < 0 || sv->vol_type < 0 || + sv->used_ebs < 0 || sv->data_pad < 0 || + sv->last_data_size < 0)) { + ubi_err("negative values"); + goto bad_sv; + } + + if (unlikely(sv->vol_id >= UBI_MAX_VOLUMES && + sv->vol_id < UBI_INTERNAL_VOL_START)) { + ubi_err("bad vol_id"); + goto bad_sv; + } + + if (unlikely(sv->vol_id > si->highest_vol_id)) { + ubi_err("highest_vol_id is %d, but vol_id %d is there", + si->highest_vol_id, sv->vol_id); + goto out; + } + + if (unlikely(sv->vol_type != UBI_DYNAMIC_VOLUME && + sv->vol_type != UBI_STATIC_VOLUME)) { + ubi_err("bad vol_type"); + goto bad_sv; + } + + if (unlikely(sv->data_pad > io->leb_size / 2)) { + ubi_err("bad data_pad"); + goto bad_sv; + } + + last_seb = NULL; + rb_for_each_entry(rb2, seb, &sv->root, rb) { + cond_resched(); + + last_seb = seb; + leb_count += 1; + + if (unlikely(seb->pnum < 0 || seb->ec < 0)) { + ubi_err("negative values"); + goto bad_seb; + } + + if (unlikely(seb->ec < si->min_ec)) { + ubi_err("bad si->min_ec (%d), %d found", + si->min_ec, seb->ec); + goto bad_seb; + } + + if (unlikely(seb->ec > si->max_ec)) { + ubi_err("bad si->max_ec (%d), %d found", + si->max_ec, seb->ec); + goto bad_seb; + } + + if (unlikely(seb->pnum >= io->peb_count)) { + ubi_err("too high PEB number %d, total PEBs %d", + seb->pnum, io->peb_count); + goto bad_seb; + } + + if (sv->vol_type == UBI_STATIC_VOLUME) { + if (unlikely(seb->lnum >= sv->used_ebs)) { + ubi_err("bad lnum or used_ebs"); + goto bad_seb; + } + } else { + if (unlikely(sv->used_ebs != 0)) { + ubi_err("non-zero used_ebs"); + goto bad_seb; + } + } + + if (unlikely(seb->lnum > sv->highest_lnum)) { + ubi_err("incorrect highest_lnum or lnum"); + goto bad_seb; + } + } + + if (unlikely(sv->leb_count != leb_count)) { + ubi_err("bad leb_count, %d objects in the tree", + leb_count); + goto bad_sv; + } + + if (!last_seb) + continue; + + seb = last_seb; + + if (unlikely(seb->lnum != sv->highest_lnum)) { + ubi_err("bad highest_lnum"); + goto bad_seb; + } + } + + if (vols_found != si->vols_found) { + ubi_err("bad si->vols_found %d, should be %d", + si->vols_found, vols_found); + goto out; + } + + /* Check that scanning information is correct */ + rb_for_each_entry(rb1, sv, &si->volumes, rb) { + last_seb = NULL; + rb_for_each_entry(rb2, seb, &sv->root, rb) { + int vol_type; + + cond_resched(); + + last_seb = seb; + + err = ubi_io_read_vid_hdr(ubi, seb->pnum, vidh, 1); + if (unlikely(err) && err != UBI_IO_BITFLIPS) { + ubi_err("VID header is not OK (%d)", err); + if (err > 0) + err = -EIO; + return err; + } + + vol_type = vidh->vol_type == UBI_VID_DYNAMIC ? + UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; + if (unlikely(sv->vol_type != vol_type)) { + ubi_err("bad vol_type"); + goto bad_vid_hdr; + } + + if (unlikely(seb->leb_ver != + ubi32_to_cpu(vidh->leb_ver))) { + ubi_err("bad leb_ver %u", seb->leb_ver); + goto bad_vid_hdr; + } + + if (unlikely(sv->vol_id != + ubi32_to_cpu(vidh->vol_id))) { + ubi_err("bad vol_id %d", sv->vol_id); + goto bad_vid_hdr; + } + + if (unlikely(sv->compat != vidh->compat)) { + ubi_err("bad compat %d", vidh->compat); + goto bad_vid_hdr; + } + + if (unlikely(seb->lnum != + ubi32_to_cpu(vidh->lnum))) { + ubi_err("bad lnum %d", seb->lnum); + goto bad_vid_hdr; + } + + if (unlikely(sv->used_ebs != + ubi32_to_cpu(vidh->used_ebs))) { + ubi_err("bad used_ebs %d", sv->used_ebs); + goto bad_vid_hdr; + } + + if (unlikely(sv->data_pad != + ubi32_to_cpu(vidh->data_pad))) { + ubi_err("bad data_pad %d", sv->data_pad); + goto bad_vid_hdr; + } + } + + if (!last_seb) + continue; + + if (unlikely(sv->highest_lnum != ubi32_to_cpu(vidh->lnum))) { + ubi_err("bad highest_lnum %d", sv->highest_lnum); + goto bad_vid_hdr; + } + + if (unlikely(sv->last_data_size != + ubi32_to_cpu(vidh->data_size))) { + ubi_err("bad last_data_size %d", sv->last_data_size); + goto bad_vid_hdr; + } + } + + /* + * Make sure that all the physical eraseblocks are in one of the lists + * or trees. + */ + buf = ubi_kmalloc(io->peb_count); + if (!buf) + return -ENOMEM; + + memset(buf, 1, io->peb_count); + for (pnum = 0; pnum < io->peb_count; pnum++) { + err = ubi_io_is_bad(ubi, pnum); + if (unlikely(err < 0)) + return err; + else if (err) + buf[pnum] = 0; + } + + rb_for_each_entry(rb1, sv, &si->volumes, rb) + rb_for_each_entry(rb2, seb, &sv->root, rb) + buf[seb->pnum] = 0; + + cond_resched(); + list_for_each_entry(seb, &si->free, list) + buf[seb->pnum] = 0; + + cond_resched(); + list_for_each_entry(seb, &si->corr, list) + buf[seb->pnum] = 0; + + cond_resched(); + list_for_each_entry(seb, &si->erase, list) + buf[seb->pnum] = 0; + + cond_resched(); + list_for_each_entry(seb, &si->alien, list) + buf[seb->pnum] = 0; + + err = 0; + for (pnum = 0; pnum < io->peb_count; pnum++) + if (unlikely(buf[pnum])) { + ubi_err("PEB %d is not referred", pnum); + err = 1; + } + + ubi_kfree(buf); + if (err) + goto out; + return 0; + +bad_seb: + ubi_err("bad scanning information about LEB %d", seb->lnum); + ubi_dbg_dump_seb(seb, 0); + ubi_dbg_dump_sv(sv); + goto out; + +bad_sv: + ubi_err("bad scanning information about volume %d", sv->vol_id); + ubi_dbg_dump_sv(sv); + goto out; + +bad_vid_hdr: + ubi_err("bad scanning information about volume %d", sv->vol_id); + ubi_dbg_dump_sv(sv); + ubi_dbg_dump_vid_hdr(vidh); + +out: + dump_stack(); + return 1; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID_SCAN */ diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h new file mode 100644 index 0000000..05ca7c0 --- /dev/null +++ b/drivers/mtd/ubi/scan.h @@ -0,0 +1,279 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * UBI scanning unit. + * + * This unit is responsible for scanning the flash media, checking UBI + * headers and providing complete information about the UBI flash image. + * + * This unit properly handles old or corrupted logical eraseblocks which may + * appear due to UBI crashes (if an eraseblock movement was interrupted or some + * eraseblocks scheduled for erasure were not erased). + */ + +#ifndef __UBI_SCAN_H__ +#define __UBI_SCAN_H__ + +#include +#include +#include + +struct ubi_info; +struct ubi_scan_info; +struct ubi_scan_volume; +struct ubi_scan_leb; +struct ubi_vid_hdr; + +/** + * ubi_scan_add_volume - add information about a physical eraseblock to the + * scanning information. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * @pnum: the physical eraseblock number + * @ec: erase counter + * @vid_hdr: the volume identifier header + * @bitflips: if a bit-flips were detected while reading this physical + * eraseblock + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int __init ubi_scan_add_volume(const struct ubi_info *ubi, + struct ubi_scan_info *si, int pnum, int ec, + const struct ubi_vid_hdr *vid_hdr, int bitflips); + +/** + * ubi_scan_add_to_corrupted - add a physical eraseblock to the list of + * corrupted physical eraseblocks. + * + * @si: a pointer to the scanning information + * @pnum: the physical eraseblock number + * @ec: erase counter of this physical eraseblock + * + * If @ec is not known, %-1 has to be passed and mean erase counter will be + * used. This function returns zero in case of success and a negative error + * code in case of failure. + */ +int __init ubi_scan_add_to_corrupted(struct ubi_scan_info *si, int pnum, + int ec); + +/** + * ubi_scan_get_scan_volume - find information about a particular volume in the + * scanning information. + * + * @si: a pointer to the scanning information + * @vol_id: the requested volume ID + * + * This function returns a pointer to the volume description or %NULL if there + * are no data about this volume in the scanning information. + */ +struct ubi_scan_volume __init * +ubi_scan_get_scan_volume(const struct ubi_scan_info *si, int vol_id); + +/** + * ubi_scan_get_scan_leb - find information about a particular logical + * eraseblock in the volume scanning information. + * + * @sv: a pointer to the volume scanning information + * @lnum: the requested logical eraseblock + * + * This function returns a pointer to the scanning logical eraseblock or %NULL + * if there are no data about it in the scanning volume information. + */ +struct ubi_scan_leb __init * +ubi_scan_get_scan_leb(const struct ubi_scan_volume *sv, int lnum); + +/** + * ubi_scan_early_erase_peb - erase a physical eraseblock. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * @pnum: physical eraseblock number to erase; + * @ec: erase counter value to write (-1 if it is unknown) + */ +int __init ubi_scan_early_erase_peb(const struct ubi_info *ubi, + const struct ubi_scan_info *si, int pnum, + int ec); + +/** + * ubi_scan_get_free_peb - get a free physical eraseblock. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * + * This function returns a free physical eraseblock. It is supposed to be + * called on the UBI initialization stages when the wear-leveling unit is not + * initialized yet. This function picks a physical eraseblocks from one of the + * lists, writes the EC header if it is needed, and removes it from the list. + * + * This function returns scanning physical eraseblock information in case of + * success and an error code in case of failure. + */ +struct ubi_scan_leb __init *ubi_scan_get_free_peb(const struct ubi_info *ubi, + struct ubi_scan_info *si); + +/** + * ubi_scan_rm_volume - delete scanning information about a volume. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * @sv: the volume scanning information to delete + */ +void __init ubi_scan_rm_volume(const struct ubi_info *ubi, + struct ubi_scan_info *si, + struct ubi_scan_volume *sv); + +/** + * ubi_scan - scan an MTD device. + * + * @ubi: the UBI device description object + * @info: full scanning information is returned here + * + * This function does full scanning of an MTD device and returns complete + * information about it in @info. This function returns zero in case of success + * and a negative error code in case of failure. + */ +int __init ubi_scan(struct ubi_info *ubi, struct ubi_scan_info **info); + +/** + * ubi_scan_destroy_si - destroy scanning information. + * + * @si: a pointer to the scanning information + */ +void __init ubi_scan_destroy_si(struct ubi_scan_info *si); + +/** + * struct ubi_scan_volume - scanning information about a volume. + * + * @vol_id: volume ID + * @highest_lnum: the highest logical eraseblock number found in this volume + * @leb_count: the number of found logical eraseblocks belonging to this volume + * @vol_type: volume type + * @data_pad: how many bytes at the end of logical eraseblocks of this volume + * are not used (due to the volume alignment) + * @used_ebs: the number of used logical eraseblocks in this volume (only for + * static volumes) + * @last_data_size: amount of data in the last found logical eraseblock of this + * volume (always equivalent to the usable logical eraseblock size fro dynamic + * volumes) + * @data_size: how many bytes of data logical eraseblock contain (only for static + * volumes, invalid for the last logical eraseblock) + * @compat: compatibility flags of the volume + * @rb: link in the volume RB-tree + * @root: the root of RB-tree containing all the found eraseblock belonging to + * this volume (&struct ubi_scan_leb objects) + */ +struct ubi_scan_volume { + int vol_id; + int highest_lnum; + int leb_count; + int vol_type; + int used_ebs; + int last_data_size; + int data_pad; + int compat; + struct rb_node rb; + struct rb_root root; +}; + +/** + * struct ubi_scan_leb - scanning information about a physical eraseblock. + * + * @ec: erase counter (%-1 if it is unknown) + * @pnum: physical eraseblock number + * @lnum: logical eraseblock number + * @scrub: if this physical eraseblock needs scrubbing + * @leb_ver: version of this logical eraseblock + * @rb: link in the per-volume RB-tree of &struct ubi_scan_leb objects + * @list: link in one of the eraseblock lists + * + * One object of this type is allocated for each physical eraseblock during + * scanning. + */ +struct ubi_scan_leb { + int ec; + int pnum; + int lnum; + int scrub; + uint32_t leb_ver; + /* FIXME: the below 2 fields may be actually union-ed */ + struct rb_node rb; + struct list_head list; +}; + +/** + * struct ubi_scan_info - UBI scanning information. + * + * @volumes: root of the volume RB-tree + * @corr: list of corrupted eraseblocks + * @free: list of free eraseblocks + * @erase: list of eraseblocks which have to be erased + * @alien: count of physical eraseblocks which should not be used by UBI (e.g., + * those belonging to "preserve"-compatible internal volumes) + * @vols_found: total count of volumes found during scanning + * @highest_vol_id: highest volume ID found during scanning + * @bad_peb_count: count of bad physical eraseblocks found during scanning + * @alien_peb_count: count of physical eraseblocks in the @@alien list + * @is_empty: a flag indicating whether the flash device is empty or not + * @min_ec: the lowest found erase counter value + * @max_ec: the highest found erase counter value + * @mean_ec: mean erase counter value + * @ec_sum: a temporary variable used when calculating @mean_ec + * @ec_count: a temporary variable used when calculating @mean_ec + * + * This data structure contains the result of scanning and may be used by other + * UBI units to build final UBI data structures, further error-recovery and so + * on. + * + * Information about found volumes is represented by &struct ubi_scan_volume + * objects which are kept in volume RB-tree with root at the @volumes field. The + * RB-tree is indexed by the volume ID. + * + * Found logical eraseblocks are represented by &struct ubi_scan_leb objects. + * These objects are kept in per-volume RB-trees with the root at the + * corresponding &struct ubi_scan_volume object. To put it differently, we keep + * an RB-tree of per-volume objects and each of these objects is the root of + * RB-tree of per-eraseblock objects. + * + * Corrupted physical eraseblocks are put to the @corr list, free physical + * eraseblocks are put to the @free list and the physical eraseblock to be + * erased are put to the @erase list. + */ +struct ubi_scan_info { + struct rb_root volumes; /* public */ + struct list_head corr; /* public */ + struct list_head free; /* public */ + struct list_head erase; /* public */ + struct list_head alien; /* public */ + int vols_found; /* public */ + int highest_vol_id; /* public */ + int bad_peb_count; /* public */ + int alien_peb_count; /* public */ + int is_empty; /* public */ + int min_ec; /* public */ + int max_ec; /* public */ + int mean_ec; /* public */ + int ec_sum; /* private */ + int ec_count; /* private */ +}; + +#endif /* !__UBI_SCAN_H__ */ diff --git a/drivers/mtd/ubi/sysfs.c b/drivers/mtd/ubi/sysfs.c new file mode 100644 index 0000000..5cbb903 --- /dev/null +++ b/drivers/mtd/ubi/sysfs.c @@ -0,0 +1,552 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ubi.h" +#include "uif.h" +#include "upd.h" +#include "debug.h" +#include "sysfs.h" +#include "io.h" +#include "wl.h" +#include "badeb.h" +#include "account.h" +#include "vtbl.h" +#include "dtbl.h" +#include "alloc.h" + +static struct class *ubi_class; + +static ssize_t ubi_version_show(struct class *class, char *buf); + +/* Class attributes corresponding to files in '//class/ubi/' */ +static struct class_attribute ubi_version = + __ATTR(version, S_IRUGO, ubi_version_show, NULL); + +int __init ubi_sysfs_global_init(void) +{ + int err; + + ubi_class = class_create(THIS_MODULE, UBI_NAME_STR); + if (IS_ERR(ubi_class)) { + err = PTR_ERR(ubi_class); + goto out; + } + + err = class_create_file(ubi_class, &ubi_version); + if (err) + goto out_class; + + return 0; + +out_class: + class_destroy(ubi_class); +out: + return err; +} + +void __exit ubi_sysfs_global_close(void) +{ + class_remove_file(ubi_class, &ubi_version); + class_destroy(ubi_class); +} + +static void dev_release(struct class_device *dev); +static ssize_t dev_eraseblock_size_show(struct class_device *dev, char *buf); +static ssize_t dev_avail_eraseblocks_show(struct class_device *dev, char *buf); +static ssize_t dev_total_eraseblocks_show(struct class_device *dev, char *buf); +static ssize_t dev_volumes_count_show(struct class_device *dev, char *buf); +static ssize_t dev_max_ec_show(struct class_device *dev, char *buf); +static ssize_t dev_update_show(struct class_device *dev, char *buf); +static ssize_t dev_reserved_for_bad_show(struct class_device *dev, char *buf); +static ssize_t dev_bad_peb_count_show(struct class_device *dev, char *buf); +static ssize_t dev_max_vol_count_show(struct class_device *dev, char *buf); +static ssize_t dev_min_io_size_show(struct class_device *dev, char *buf); + +/* + * Class device attributes corresponding to files in '//class/ubi/ubiX'. + */ +static struct class_device_attribute dev_eraseblock_size = + __ATTR(eraseblock_size, S_IRUGO, dev_eraseblock_size_show, NULL); +static struct class_device_attribute dev_avail_eraseblocks = + __ATTR(avail_eraseblocks, S_IRUGO, dev_avail_eraseblocks_show, NULL); +static struct class_device_attribute dev_total_eraseblocks = + __ATTR(total_eraseblocks, S_IRUGO, dev_total_eraseblocks_show, NULL); +static struct class_device_attribute dev_volumes_count = + __ATTR(volumes_count, S_IRUGO, dev_volumes_count_show, NULL); +static struct class_device_attribute dev_max_ec = + __ATTR(max_ec, S_IRUGO, dev_max_ec_show, NULL); +static struct class_device_attribute dev_update = + __ATTR(update, S_IRUGO, dev_update_show, NULL); +static struct class_device_attribute dev_reserved_for_bad = + __ATTR(reserved_for_bad, S_IRUGO, dev_reserved_for_bad_show, NULL); +static struct class_device_attribute dev_bad_peb_count = + __ATTR(bad_peb_count, S_IRUGO, dev_bad_peb_count_show, NULL); +static struct class_device_attribute dev_max_vol_count = + __ATTR(max_vol_count, S_IRUGO, dev_max_vol_count_show, NULL); +static struct class_device_attribute dev_min_io_size = + __ATTR(min_io_size, S_IRUGO, dev_min_io_size_show, NULL); + +int __init ubi_sysfs_init(const struct ubi_info *ubi) +{ + int err; + struct ubi_uif_info *uif = ubi->uif; + + uif->dev.release = dev_release; + uif->dev.devt = MKDEV(uif->major, 0); + uif->dev.class = ubi_class; + sprintf(&uif->dev.class_id[0], UBI_NAME_STR"%d", ubi->ubi_num); + err = class_device_register(&uif->dev); + if (err) + goto out; + + err = class_device_create_file(&uif->dev, &dev_eraseblock_size); + if (err) + goto out_unregister; + err = class_device_create_file(&uif->dev, &dev_avail_eraseblocks); + if (err) + goto out_eraseblock_size; + err = class_device_create_file(&uif->dev, &dev_total_eraseblocks); + if (err) + goto out_avail_eraseblocks; + err = class_device_create_file(&uif->dev, &dev_volumes_count); + if (err) + goto out_total_eraseblocks; + err = class_device_create_file(&uif->dev, &dev_max_ec); + if (err) + goto out_volumes_count; + err = class_device_create_file(&uif->dev, &dev_update); + if (err) + goto out_volumes_max_ec; + err = class_device_create_file(&uif->dev, &dev_reserved_for_bad); + if (err) + goto out_update; + err = class_device_create_file(&uif->dev, &dev_bad_peb_count); + if (err) + goto out_reserved_for_bad; + err = class_device_create_file(&uif->dev, &dev_max_vol_count); + if (err) + goto out_bad_peb_count; + err = class_device_create_file(&uif->dev, &dev_min_io_size); + if (err) + goto out_max_vol_count; + + return 0; + +out_max_vol_count: + class_device_remove_file(&uif->dev, &dev_max_vol_count); +out_bad_peb_count: + class_device_remove_file(&uif->dev, &dev_bad_peb_count); +out_reserved_for_bad: + class_device_remove_file(&uif->dev, &dev_reserved_for_bad); +out_update: + class_device_remove_file(&uif->dev, &dev_update); +out_volumes_max_ec: + class_device_remove_file(&uif->dev, &dev_max_ec); +out_volumes_count: + class_device_remove_file(&uif->dev, &dev_volumes_count); +out_total_eraseblocks: + class_device_remove_file(&uif->dev, &dev_total_eraseblocks); +out_avail_eraseblocks: + class_device_remove_file(&uif->dev, &dev_avail_eraseblocks); +out_eraseblock_size: + class_device_remove_file(&uif->dev, &dev_eraseblock_size); +out_unregister: + class_device_unregister(&uif->dev); +out: + ubi_err("failed to initialize sysfs for UBI device %d", ubi->ubi_num); + return err; +} + +void __exit ubi_sysfs_close(const struct ubi_info *ubi) +{ + struct ubi_uif_info *uif = ubi->uif; + + class_device_remove_file(&uif->dev, &dev_min_io_size); + class_device_remove_file(&uif->dev, &dev_max_vol_count); + class_device_remove_file(&uif->dev, &dev_bad_peb_count); + class_device_remove_file(&uif->dev, &dev_reserved_for_bad); + class_device_remove_file(&uif->dev, &dev_update); + class_device_remove_file(&uif->dev, &dev_max_ec); + class_device_remove_file(&uif->dev, &dev_volumes_count); + class_device_remove_file(&uif->dev, &dev_total_eraseblocks); + class_device_remove_file(&uif->dev, &dev_avail_eraseblocks); + class_device_remove_file(&uif->dev, &dev_eraseblock_size); + class_device_unregister(&uif->dev); +} + +static void vol_release(struct class_device *dev); +static ssize_t vol_reserved_ebs_show(struct class_device *dev, char *buf); +static ssize_t vol_type_show(struct class_device *dev, char *buf); +static ssize_t vol_name_show(struct class_device *dev, char *buf); +static ssize_t vol_corrupted_show(struct class_device *dev, char *buf); +static ssize_t vol_alignment_show(struct class_device *dev, char *buf); +static ssize_t vol_usable_eb_size_show(struct class_device *dev, char *buf); +static ssize_t vol_data_bytes_show(struct class_device *dev, char *buf); + +/* + * Class device attributes corresponding to files in + * '//class/ubi/ubiX/Y'. + */ +static struct class_device_attribute vol_reserved_ebs = + __ATTR(reserved_ebs, S_IRUGO, vol_reserved_ebs_show, NULL); +static struct class_device_attribute vol_type = + __ATTR(type, S_IRUGO, vol_type_show, NULL); +static struct class_device_attribute vol_name = + __ATTR(name, S_IRUGO, vol_name_show, NULL); +static struct class_device_attribute vol_corrupted = + __ATTR(corrupted, S_IRUGO, vol_corrupted_show, NULL); +static struct class_device_attribute vol_alignment = + __ATTR(alignment, S_IRUGO, vol_alignment_show, NULL); +static struct class_device_attribute vol_usable_eb_size = + __ATTR(usable_eb_size, S_IRUGO, vol_usable_eb_size_show, NULL); +static struct class_device_attribute vol_data_bytes = + __ATTR(data_bytes, S_IRUGO, vol_data_bytes_show, NULL); + +/* + * Note, this function does not free allocated resources in case of failure - + * the caller does it. This is because this would cause release() here and the + * caller would oops. + */ +int ubi_sysfs_vol_init(const struct ubi_info *ubi, struct ubi_uif_volume *vol) +{ + int err; + + vol->dev.release = vol_release; + vol->dev.parent = &ubi->uif->dev; + vol->dev.devt = MKDEV(ubi->uif->major, vol->vol_id + 1); + vol->dev.class = ubi_class; + sprintf(&vol->dev.class_id[0], "%d", vol->vol_id); + err = class_device_register(&vol->dev); + if (err) + return err; + + err = class_device_create_file(&vol->dev, &vol_reserved_ebs); + if (err) + return err; + err = class_device_create_file(&vol->dev, &vol_type); + if (err) + return err; + err = class_device_create_file(&vol->dev, &vol_name); + if (err) + return err; + err = class_device_create_file(&vol->dev, &vol_corrupted); + if (err) + return err; + err = class_device_create_file(&vol->dev, &vol_alignment); + if (err) + return err; + err = class_device_create_file(&vol->dev, &vol_usable_eb_size); + if (err) + return err; + err = class_device_create_file(&vol->dev, &vol_data_bytes); + if (err) + return err; + return 0; +} + +void ubi_sysfs_vol_close(struct ubi_uif_volume *vol) +{ + class_device_remove_file(&vol->dev, &vol_data_bytes); + class_device_remove_file(&vol->dev, &vol_usable_eb_size); + class_device_remove_file(&vol->dev, &vol_alignment); + class_device_remove_file(&vol->dev, &vol_corrupted); + class_device_remove_file(&vol->dev, &vol_name); + class_device_remove_file(&vol->dev, &vol_type); + class_device_remove_file(&vol->dev, &vol_reserved_ebs); + class_device_unregister(&vol->dev); +} + +/** + * dev2ubi -- find UBI device description object by the pointer to the class + * device object. + * + * @dev: class device object pointer + * + * This function returns a pointer to the UBI device description object. + */ +static inline struct ubi_info *dev2ubi(struct class_device *dev) +{ + struct ubi_uif_info *uif; + + uif = container_of(dev, struct ubi_uif_info, dev); + return uif->ubi; +} + +/* "Show" and "store" methods for files in '//class/ubi/' */ +static ssize_t ubi_version_show(struct class *class, char *buf) +{ + return sprintf(buf, "%d\n", UBI_VERSION); +} + +/* "Release" method for UBI devices */ +static void dev_release(struct class_device *dev) +{ + return; +} + +/* "Show" method for files in '//class/ubi/ubiX/' */ +static ssize_t dev_eraseblock_size_show(struct class_device *dev, char *buf) +{ + const struct ubi_info *ubi = dev2ubi(dev); + + return sprintf(buf, "%d\n", ubi->io->leb_size); +} + +static ssize_t dev_avail_eraseblocks_show(struct class_device *dev, char *buf) +{ + const struct ubi_info *ubi = dev2ubi(dev); + + return sprintf(buf, "%d\n", ubi->acc->avail_pebs); +} + +static ssize_t dev_total_eraseblocks_show(struct class_device *dev, char *buf) +{ + const struct ubi_info *ubi = dev2ubi(dev); + + return sprintf(buf, "%d\n", ubi->io->good_peb_count); +} + +static ssize_t dev_volumes_count_show(struct class_device *dev, char *buf) +{ + const struct ubi_info *ubi = dev2ubi(dev); + + return sprintf(buf, "%d\n", ubi->acc->uvol_count); +} + +static ssize_t dev_max_ec_show(struct class_device *dev, char *buf) +{ + const struct ubi_info *ubi = dev2ubi(dev); + + return sprintf(buf, "%d\n", ubi->wl->max_ec); +} + +static ssize_t dev_update_show(struct class_device *dev, char *buf) +{ + const struct ubi_info *ubi = dev2ubi(dev); + int vol_id = ubi->upd->vol_id; + + if (vol_id == -1) + return 0; + return sprintf(buf, "%d\n", vol_id); +} + +static ssize_t dev_reserved_for_bad_show(struct class_device *dev, char *buf) +{ + const struct ubi_info *ubi = dev2ubi(dev); + + return sprintf(buf, "%d\n", ubi->beb->reserved_pebs); +} + +static ssize_t dev_bad_peb_count_show(struct class_device *dev, char *buf) +{ + const struct ubi_info *ubi = dev2ubi(dev); + + return sprintf(buf, "%d\n", ubi->io->bad_peb_count); +} + +static ssize_t dev_max_vol_count_show(struct class_device *dev, char *buf) +{ + const struct ubi_info *ubi = dev2ubi(dev); + + return sprintf(buf, "%d\n", ubi->acc->max_volumes); +} + +static ssize_t dev_min_io_size_show(struct class_device *dev, char *buf) +{ + const struct ubi_info *ubi = dev2ubi(dev); + + return sprintf(buf, "%d\n", ubi->io->min_io_size); +} + +/** + * dev2ubi -- find volume description object by the pointer to the class device + * object. + * + * @dev: class device object pointer + * + * This function returns a pointer to the UBI volume description object. + */ +static inline struct ubi_uif_volume *dev2vol(struct class_device *dev) +{ + return container_of(dev, struct ubi_uif_volume, dev); +} + +/* Release method for volume devices */ +static void vol_release(struct class_device *dev) +{ + const struct ubi_uif_volume *vol = dev2vol(dev); + + dbg_uif("release volume %d", vol->vol_id); + ubi_kfree(vol); +} + +/* + * "Show" methods for files in '//class/ubi/ubiX/Y/'. + * + * Consider a situation: + * A. process 1 opens a sysfs file related to volume Y, say + * //class/ubi/ubiX/Y/reserved_ebs; + * B. process 2 removes volume Y; + * C. process 1 starts reading the //class/ubi/ubiX/Y/reserved_ebs file; + * + * What we want to do in a situation like that is to return error when the file + * is read. This is done by means of the 'removed' flag and the 'vol_lock' of + * the UBI UIF volume information structure. + */ + +static ssize_t vol_reserved_ebs_show(struct class_device *dev, char *buf) +{ + int ret; + const struct ubi_vtbl_vtr *vtr; + struct ubi_uif_volume *vol = dev2vol(dev); + + spin_lock(&vol->vol_lock); + if (vol->removed) { + spin_unlock(&vol->vol_lock); + dbg_uif("volume %d was removed", vol->vol_id); + return -EIO; + } + vtr = ubi_vtbl_get_vtr(vol->ubi, vol->vol_id); + ret = sprintf(buf, "%d\n", vtr->reserved_pebs); + spin_unlock(&vol->vol_lock); + return ret; +} + +static ssize_t vol_type_show(struct class_device *dev, char *buf) +{ + int ret; + const char *tp; + const struct ubi_vtbl_vtr *vtr; + struct ubi_uif_volume *vol = dev2vol(dev); + + spin_lock(&vol->vol_lock); + if (vol->removed) { + spin_unlock(&vol->vol_lock); + dbg_uif("volume %d was removed", vol->vol_id); + return -EIO; + } + vtr = ubi_vtbl_get_vtr(vol->ubi, vol->vol_id); + tp = vtr->vol_type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static"; + ret = sprintf(buf, "%s\n", tp); + spin_unlock(&vol->vol_lock); + return ret; +} + +static ssize_t vol_name_show(struct class_device *dev, char *buf) +{ + int ret; + const struct ubi_vtbl_vtr *vtr; + struct ubi_uif_volume *vol = dev2vol(dev); + + spin_lock(&vol->vol_lock); + if (vol->removed) { + spin_unlock(&vol->vol_lock); + dbg_uif("volume %d was removed", vol->vol_id); + return -EIO; + } + vtr = ubi_vtbl_get_vtr(vol->ubi, vol->vol_id); + ret = sprintf(buf, "%s\n", vtr->name); + spin_unlock(&vol->vol_lock); + return ret; +} + +static ssize_t vol_corrupted_show(struct class_device *dev, char *buf) +{ + int ret; + const struct ubi_dtbl_dtr *dtr; + struct ubi_uif_volume *vol = dev2vol(dev); + + spin_lock(&vol->vol_lock); + if (vol->removed) { + spin_unlock(&vol->vol_lock); + dbg_uif("volume %d was removed", vol->vol_id); + return -EIO; + } + dtr = ubi_dtbl_get_dtr(vol->ubi, vol->vol_id); + if (vol->ubi->upd->vol_id == vol->vol_id) + ret = sprintf(buf, "1\n"); + else + ret = sprintf(buf, "%d\n", dtr->corrupted); + spin_unlock(&vol->vol_lock); + return ret; +} + +static ssize_t vol_alignment_show(struct class_device *dev, char *buf) +{ + int ret; + const struct ubi_vtbl_vtr *vtr; + struct ubi_uif_volume *vol = dev2vol(dev); + + spin_lock(&vol->vol_lock); + if (vol->removed) { + spin_unlock(&vol->vol_lock); + dbg_uif("volume %d was removed", vol->vol_id); + return -EIO; + } + vtr = ubi_vtbl_get_vtr(vol->ubi, vol->vol_id); + ret = sprintf(buf, "%d\n", vtr->alignment); + spin_unlock(&vol->vol_lock); + return ret; +} + +static ssize_t vol_usable_eb_size_show(struct class_device *dev, char *buf) +{ + int ret, usable_eb_size; + const struct ubi_vtbl_vtr *vtr; + struct ubi_uif_volume *vol = dev2vol(dev); + const struct ubi_io_info *io = vol->ubi->io; + + spin_lock(&vol->vol_lock); + if (vol->removed) { + spin_unlock(&vol->vol_lock); + dbg_uif("volume %d was removed", vol->vol_id); + return -EIO; + } + vtr = ubi_vtbl_get_vtr(vol->ubi, vol->vol_id); + usable_eb_size = io->leb_size - vtr->data_pad; + ret = sprintf(buf, "%d\n", usable_eb_size); + spin_unlock(&vol->vol_lock); + return ret; +} + +static ssize_t vol_data_bytes_show(struct class_device *dev, char *buf) +{ + int ret; + const struct ubi_dtbl_dtr *dtr; + struct ubi_uif_volume *vol = dev2vol(dev); + + spin_lock(&vol->vol_lock); + if (vol->removed) { + spin_unlock(&vol->vol_lock); + dbg_uif("volume %d was removed", vol->vol_id); + return -EIO; + } + dtr = ubi_dtbl_get_dtr(vol->ubi, vol->vol_id); + ret = sprintf(buf, "%lld\n", dtr->used_bytes); + spin_unlock(&vol->vol_lock); + return ret; +} diff --git a/drivers/mtd/ubi/sysfs.h b/drivers/mtd/ubi/sysfs.h new file mode 100644 index 0000000..7dc63c1 --- /dev/null +++ b/drivers/mtd/ubi/sysfs.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * This is a part of the UBI user interface unit and contains all the + * sysfs-related stuff. + */ + +#ifndef __UBI_SYSFS_H__ +#define __UBI_SYSFS_H__ + +#include + +struct ubi_info; +struct ubi_uif_volume; + +/** + * ubi_sysfs_vol_init - initialize sysfs for an UBI volume. + * + * @ubi: the UBI device description object + * @vol: user interfaces unit volume description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_sysfs_vol_init(const struct ubi_info *ubi, struct ubi_uif_volume *vol); + +/** + * ubi_sysfs_vol_close - close sysfs for an UBI volume. + * + * @vol: user interfaces unit volume description object + */ +void ubi_sysfs_vol_close(struct ubi_uif_volume *vol); + +/** + * ubi_sysfs_init - initialize sysfs for an UBI device. + * + * @ubi: the UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int __init ubi_sysfs_init(const struct ubi_info *ubi); + +/** + * ubi_sysfs_close - close sysfs for an UBI device. + * + * @ubi: the UBI device description object + */ +void __exit ubi_sysfs_close(const struct ubi_info *ubi); + +/** + * ubi_sysfs_global_init - initialize UBI sysfs support. + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int __init ubi_sysfs_global_init(void); + +/** + * ubi_sysfs_global_close - close UBI sysfs support. + */ +void __exit ubi_sysfs_global_close(void); + +#endif /* !__UBI_SYSFS_H__ */ diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h new file mode 100644 index 0000000..42661ad --- /dev/null +++ b/drivers/mtd/ubi/ubi.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#ifndef __UBI_UBI_H__ +#define __UBI_UBI_H__ + +#include + +/* + * The following abbreviations are used throughout the code: + * + * dtr - data table record + * EB - Erasable Block + * ebs - eraseblocks + * EC - Erase Counter + * ivol - internal volume + * LEB - Logical EraseBlock + * leb - logical eraseblock + * lnum - logical eraseblock number + * PEB - Physical EraseBlock + * pnum - physical eraseblock number + * si - scanning information + * VID - Volume IDentifier + * vol - volume + * vtr - volume table record + */ + +/* Version of this UBI implementation */ +#define UBI_VERSION 1 + +/* Maximum number of supported UBI devices */ +#define UBI_MAX_INSTANCES 32 + +/* UBI messages printk level */ +#define UBI_MSG_LEVEL KERN_CRIT + +/* Prefixes of UBI messages */ +#define UBI_MSG_PREF "UBI:" +#define UBI_WARN_PREF "UBI warning:" +#define UBI_ERR_PREF "UBI error:" + +#if !defined(CONFIG_MTD_UBI_DEBUG) && !defined(CONFIG_MTD_UBI_DEBUG_MODULE) + +#include "debug.h" +/* Normal UBI messages */ +#define ubi_msg(fmt, ...) \ + printk(UBI_MSG_LEVEL UBI_MSG_PREF " " fmt "\n", ##__VA_ARGS__) +/* UBI warning messages */ +#define ubi_warn(fmt, ...) \ + printk(UBI_MSG_LEVEL UBI_WARN_PREF " %s: " fmt "\n", __FUNCTION__, \ + ##__VA_ARGS__) +/* UBI error messages */ +#define ubi_err(fmt, ...) \ + printk(UBI_MSG_LEVEL UBI_ERR_PREF " %s " fmt "\n", __FUNCTION__, \ + ##__VA_ARGS__) + +#else + +/* Normal UBI messages */ +#define ubi_msg(fmt, ...) \ + ubi_dbg_print(UBI_DBG_MSG, NULL, fmt, ##__VA_ARGS__); +/* UBI warning messages */ +#define ubi_warn(fmt, ...) \ + ubi_dbg_print(UBI_DBG_WARN, __FUNCTION__, fmt, ##__VA_ARGS__) +/* UBI error messages */ +#define ubi_err(fmt, ...) \ + ubi_dbg_print(UBI_DBG_ERR, __FUNCTION__, fmt, ##__VA_ARGS__) + +#endif /* CONFIG_MTD_UBI_DEBUG || CONFIG_MTD_UBI_DEBUG_MODULE */ + +struct ubi_io_info; +struct ubi_bgt_info; +struct ubi_wl_info; +struct ubi_beb_info; +struct ubi_vmt_info; +struct ubi_ivol_info; +struct ubi_vtbl_info; +struct ubi_dtbl_info; +struct ubi_acc_info; +struct ubi_upd_info; +struct ubi_eba_info; +struct ubi_uif_info; + +/** + * struct ubi_info - UBI device description structure + * + * @ubi_num: number of the UBI device + * @io: input/output unit information + * @bgt: background thread unit information + * @wl: wear-leveling unit information + * @beb: bad eraseblock handling unit information + * @vmt: volume management unit information + * @ivol: internal volume management unit information + * @vtbl: volume table unit information + * @dtbl: data table unit information + * @acc: accounting unit information + * @upd: update unit information + * @eba: EBA unit information unit information + * @uif: user interface unit information + */ +struct ubi_info { + int ubi_num; + struct ubi_io_info *io; + struct ubi_bgt_info *bgt; + struct ubi_wl_info *wl; + struct ubi_beb_info *beb; + struct ubi_vmt_info *vmt; + struct ubi_ivol_info *ivol; + struct ubi_vtbl_info *vtbl; + struct ubi_dtbl_info *dtbl; + struct ubi_acc_info *acc; + struct ubi_upd_info *upd; + struct ubi_eba_info *eba; + struct ubi_uif_info *uif; +}; + +extern int ubis_num; +extern struct ubi_info *ubis[UBI_MAX_INSTANCES]; + +#endif /* !__UBI_UBI_H__ */ diff --git a/drivers/mtd/ubi/uif.c b/drivers/mtd/ubi/uif.c new file mode 100644 index 0000000..063d188 --- /dev/null +++ b/drivers/mtd/ubi/uif.c @@ -0,0 +1,927 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ubi.h" +#include "misc.h" +#include "sysfs.h" +#include "upd.h" +#include "wl.h" +#include "uif.h" +#include "cdev.h" +#include "alloc.h" +#include "debug.h" +#include "vtbl.h" +#include "dtbl.h" +#include "volmgmt.h" +#include "io.h" +#include "eba.h" +#include "account.h" + +int ubi_get_device_info(int ubi_num, struct ubi_dev_info *di) +{ + const struct ubi_info *ubi; + int err = -ENODEV; + + if (!try_module_get(THIS_MODULE)) + return err; + + if (ubi_num < 0 || ubi_num >= UBI_MAX_INSTANCES) { + dbg_err("bad UBI device number %d", ubi_num); + goto out_put; + } + + if (!ubis[ubi_num]) { + dbg_err("UBI device %d does not exist", ubi_num); + goto out_put; + } + ubi = ubis[ubi_num]; + + di->ubi_num = ubi->ubi_num; + di->leb_size = ubi->io->leb_size; + di->min_io_size = ubi->io->min_io_size; + di->ro_mode = ubi->io->ro_mode; + di->cdev = MKDEV(ubi->uif->major, 0); + err = 0; + +out_put: + module_put(THIS_MODULE); + return err; +} +EXPORT_SYMBOL_GPL(ubi_get_device_info); + +static void fill_ubi_vol_info(const struct ubi_info *ubi, int vol_id, + struct ubi_vol_info *vi); + +void ubi_get_volume_info(struct ubi_vol_desc *udesc, struct ubi_vol_info *vi) +{ + struct ubi_vol_desc *desc = udesc; + const struct ubi_info *ubi = desc->vol->ubi; + int vol_id = desc->vol->vol_id; + + fill_ubi_vol_info(ubi, vol_id, vi); +} +EXPORT_SYMBOL_GPL(ubi_get_volume_info); + +static int check_volume(struct ubi_uif_volume *vol); + +struct ubi_vol_desc *ubi_open_volume(int ubi_num, int vol_id, + enum ubi_open_mode mode) +{ + int err, found = 0; + struct ubi_vol_desc *desc; + const struct ubi_info *ubi; + struct ubi_uif_info *uif; + struct ubi_uif_volume *vol; + + dbg_uif("open device %d volume %d, mode %d", ubi_num, vol_id, mode); + + err = -ENODEV; + if (!try_module_get(THIS_MODULE)) + return ERR_PTR(err); + + if (ubi_num < 0 || ubi_num >= UBI_MAX_INSTANCES) { + dbg_err("bad UBI device number %d", ubi_num); + goto out_put; + } + + if (!ubis[ubi_num]) { + dbg_err("UBI device %d does not exist", ubi_num); + goto out_put; + } + + ubi = ubis[ubi_num]; + ubi_assert(ubi->ubi_num == ubi_num); + uif = ubi->uif; + + desc = ubi_kzalloc(sizeof(struct ubi_vol_desc)); + if (!desc) { + err = -ENOMEM; + goto out_put; + } + + err = -EINVAL; + if (vol_id < 0 || vol_id >= ubi->acc->max_volumes) { + dbg_err("bad vol_id %d", vol_id); + goto out_free; + } + + if (mode != UBI_READONLY && mode != UBI_READWRITE && + mode != UBI_EXCLUSIVE) { + dbg_err("bad mode %d", mode); + goto out_free; + } + + mutex_lock(&uif->volumes_list_lock); + list_for_each_entry(vol, &uif->volumes, list) + if (vol->vol_id == vol_id) { + found = 1; + break; + } + if (!found) { + dbg_err("volume %d does not exist", vol_id); + err = -ENODEV; + goto out_unlock; + } + + err = -EBUSY; + spin_lock(&vol->vol_lock); + if (vol->updating) { + /* If the volume is being updated, no one can open it */ + dbg_err("device busy - updating"); + goto out_unlock_vol; + } + + switch (mode) { + case UBI_READONLY: + if (vol->exclusive) { + dbg_err("device is busy - exclusive"); + goto out_unlock_vol; + } + vol->readers += 1; + break; + + case UBI_READWRITE: + if (vol->exclusive || vol->writers > 0) { + if (vol->exclusive) + dbg_err("device is busy - exclusive"); + else + dbg_err("device is busy - writers"); + goto out_unlock_vol; + } + vol->writers += 1; + break; + + case UBI_EXCLUSIVE: + if (vol->exclusive || vol->writers || vol->readers) { + if (vol->exclusive) + dbg_err("device is busy - exclusive"); + else if (vol->writers) + dbg_err("device is busy - writers"); + else + dbg_err("device is busy - readers"); + goto out_unlock_vol; + } + vol->exclusive = 1; + break; + } + spin_unlock(&vol->vol_lock); + mutex_unlock(&uif->volumes_list_lock); + + desc->vol = vol; + desc->mode = mode; + + mutex_lock(&uif->vol_check); + if (!vol->checked) { + /* + * This is the first time this volume is being opened, we have + * to check it. If the volume is corrupted, we still return + * success. + */ + err = check_volume(vol); + if (err) { + mutex_unlock(&uif->vol_check); + ubi_close_volume(desc); + return ERR_PTR(err); + } + vol->checked = 1; + } + mutex_unlock(&uif->vol_check); + + return desc; + +out_unlock_vol: + spin_unlock(&vol->vol_lock); +out_unlock: + mutex_unlock(&uif->volumes_list_lock); +out_free: + ubi_kfree(desc); +out_put: + module_put(THIS_MODULE); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(ubi_open_volume); + +struct ubi_vol_desc *ubi_open_volume_nm(int ubi_num, const char *name, + enum ubi_open_mode mode) +{ + int err, len, vol_id = -1; + struct ubi_vol_desc *ret; + const struct ubi_info *ubi; + struct ubi_uif_info *uif; + struct ubi_uif_volume *vol; + + dbg_uif("open volume by name %s, mode %d", name, mode); + + if (!name) { + dbg_err("bad name"); + return ERR_PTR(-EINVAL); + } + len = strnlen(name, UBI_VOL_NAME_MAX + 1); + if (len > UBI_VOL_NAME_MAX) { + dbg_err("bad name"); + return ERR_PTR(-EINVAL); + } + + err = -ENODEV; + if (!try_module_get(THIS_MODULE)) + return ERR_PTR(err); + + if (ubi_num < 0 || ubi_num >= UBI_MAX_INSTANCES) { + dbg_err("bad UBI device number %d", ubi_num); + goto out_put; + } + + if (!ubis[ubi_num]) { + dbg_err("UBI device %d does not exist", ubi_num); + goto out_put; + } + + ubi = ubis[ubi_num]; + ubi_assert(ubi->ubi_num == ubi_num); + uif = ubi->uif; + + /* Walk all volumes of this UBI device */ + mutex_lock(&uif->volumes_list_lock); + list_for_each_entry(vol, &uif->volumes, list) { + const struct ubi_vtbl_vtr *vtr; + + spin_lock(&vol->vol_lock); + vtr = ubi_vtbl_get_vtr(vol->ubi, vol->vol_id); + if (len == vtr->name_len && !strcmp(name, vtr->name)) { + vol_id = vol->vol_id; + dbg_err("found volume volume %d", vol_id); + spin_unlock(&vol->vol_lock); + break; + } + spin_unlock(&vol->vol_lock); + } + mutex_unlock(&uif->volumes_list_lock); + + if (vol_id < 0) { + dbg_err("volume %s does not exist", name); + goto out_put; + } + + ret = ubi_open_volume(ubi_num, vol_id, mode); + if (!IS_ERR(ret)) + return ret; + + err = PTR_ERR(ret); + +out_put: + module_put(THIS_MODULE); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(ubi_open_volume_nm); + +void ubi_close_volume(struct ubi_vol_desc *udesc) +{ + struct ubi_vol_desc *desc = udesc; + struct ubi_uif_volume *vol = desc->vol; + void *free_buf = NULL; + + dbg_uif("close volume %d, mode %d", vol->vol_id, desc->mode); + + spin_lock(&vol->vol_lock); + if (vol->updating) { + /* + * Update was not finished. The update marker is still there + * and no other volumes may be updated until this update is + * finished. + */ + ubi_warn("update of volume %d was not finished", vol->vol_id); + free_buf = vol->upd_buf; + vol->upd_buf = NULL; + vol->updating = 0; + } + + switch (desc->mode) { + case UBI_READONLY: + ubi_assert(vol->readers > 0); + vol->readers -= 1; + break; + + case UBI_READWRITE: + ubi_assert(vol->writers > 0); + vol->writers -= 1; + break; + + case UBI_EXCLUSIVE: + ubi_assert(vol->exclusive > 0); + vol->exclusive = 0; + } + spin_unlock(&vol->vol_lock); + + ubi_kfree(free_buf); + ubi_kfree(desc); + module_put(THIS_MODULE); +} +EXPORT_SYMBOL_GPL(ubi_close_volume); + +int ubi_eraseblock_read(struct ubi_vol_desc *udesc, int lnum, char *buf, + int offset, int len, int check, int *read) +{ + const struct ubi_dtbl_dtr *dtr; + const struct ubi_vtbl_vtr *vtr; + struct ubi_vol_desc *desc = udesc; + const struct ubi_info *ubi = desc->vol->ubi; + int err, vol_id = desc->vol->vol_id; + + dbg_uif("read %d bytes from offset %d of LEB %d:%d", + len, offset, vol_id, lnum); + + *read = 0; + if (unlikely(vol_id < 0 || vol_id >= ubi->acc->max_volumes)) { + dbg_err("bad vol_id %d", vol_id); + return -EINVAL; + } + + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + ubi_assert(!IS_ERR(vtr)); + dtr = ubi_dtbl_get_dtr(ubi, vol_id); + + if (unlikely(lnum < 0 || lnum >= dtr->used_ebs)) { + dbg_err("bad lnum %d", lnum); + return -EINVAL; + } + + if (unlikely(offset < 0 || len < 0 || + offset + len > vtr->usable_leb_size)) { + dbg_err("bad offset %d or len %d", offset, len); + return -EINVAL; + } + + if (unlikely(vtr->vol_type == UBI_STATIC_VOLUME && + lnum == dtr->used_ebs - 1 && + offset + len > dtr->last_eb_bytes)) { + dbg_err("bad offset %d or len %d for last LEB", offset, len); + return -EINVAL; + } + + if (unlikely(len == 0)) + return 0; + + err = ubi_eba_read_leb(ubi, vol_id, lnum, buf, offset, len, check, + read); + if (unlikely(err)) + return err; + if (unlikely(dtr->corrupted)) { + ubi_assert(vtr->vol_type == UBI_STATIC_VOLUME); + dbg_err("corrupted volume"); + err = -EUCLEAN; + } + return err; +} +EXPORT_SYMBOL_GPL(ubi_eraseblock_read); + +int ubi_eraseblock_write(struct ubi_vol_desc *udesc, int lnum, const void *buf, + int offset, int len, enum ubi_data_type dtype, + int *written) +{ + const struct ubi_vtbl_vtr *vtr; + struct ubi_vol_desc *desc = udesc; + const struct ubi_info *ubi = desc->vol->ubi; + const struct ubi_io_info *io = ubi->io; + int vol_id = desc->vol->vol_id; + + dbg_uif("write %d bytes at offset %d of LEB %d:%d", + len, offset, vol_id, lnum); + + *written = 0; + if (unlikely(vol_id < 0 || vol_id >= ubi->acc->max_volumes)) { + dbg_err("bad vol_id %d", vol_id); + return -EINVAL; + } + + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + ubi_assert(!IS_ERR(vtr)); + + if (unlikely(desc->mode == UBI_READONLY)) { + dbg_err("read-only mode"); + return -EROFS; + } + + if (unlikely(vtr->vol_type == UBI_STATIC_VOLUME)) { + dbg_err("static volume"); + return -EROFS; + } + + if (unlikely(lnum < 0 || lnum >= vtr->reserved_pebs)) { + dbg_err("bad lnum %d", lnum); + return -EINVAL; + } + + if (unlikely(offset < 0 || len < 0 || + offset + len > vtr->usable_leb_size)) { + dbg_err("bad offset %d or len %d", offset, len); + return -EINVAL; + } + + if (unlikely(offset % io->min_io_size || len % io->min_io_size)) { + dbg_err("unaligned offset %d or len %d", offset, len); + return -EINVAL; + } + + if (unlikely(dtype != UBI_DATA_LONGTERM && + dtype != UBI_DATA_SHORTTERM && + dtype != UBI_DATA_UNKNOWN)) { + dbg_err("bad dtype %d", dtype); + return -EINVAL; + } + + if (unlikely(len == 0)) + return 0; + + return ubi_eba_write_leb(ubi, vol_id, lnum, buf, offset, len, dtype, + written, NULL); +} +EXPORT_SYMBOL_GPL(ubi_eraseblock_write); + +int ubi_eraseblock_erase(struct ubi_vol_desc *udesc, int lnum) +{ + const struct ubi_vtbl_vtr *vtr; + struct ubi_vol_desc *desc = udesc; + const struct ubi_info *ubi = desc->vol->ubi; + int vol_id = desc->vol->vol_id; + + dbg_uif("erase LEB %d:%d", vol_id, lnum); + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + ubi_assert(!IS_ERR(vtr)); + + if (unlikely(desc->mode == UBI_READONLY)) { + dbg_err("read-only mode"); + return -EROFS; + } + + if (unlikely(vtr->vol_type == UBI_STATIC_VOLUME)) { + dbg_err("static volume"); + return -EROFS; + } + + if (unlikely(lnum < 0 || lnum >= vtr->reserved_pebs)) { + dbg_err("bad lnum %d", lnum); + return -EINVAL; + } + + return ubi_eba_erase_leb(ubi, vol_id, lnum); +} +EXPORT_SYMBOL_GPL(ubi_eraseblock_erase); + +int ubi_eraseblock_is_mapped(struct ubi_vol_desc *udesc, int lnum) +{ + const struct ubi_vtbl_vtr *vtr; + struct ubi_vol_desc *desc = udesc; + const struct ubi_info *ubi = desc->vol->ubi; + int vol_id = desc->vol->vol_id; + + dbg_uif("check LEB %d:%d", vol_id, lnum); + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + ubi_assert(!IS_ERR(vtr)); + + if (unlikely(lnum < 0 || lnum >= vtr->reserved_pebs)) { + dbg_err("bad lnum %d", lnum); + return -EINVAL; + } + + return ubi_eba_leb_is_mapped(ubi, vol_id, lnum); +} +EXPORT_SYMBOL_GPL(ubi_eraseblock_is_mapped); + +int ubi_uif_mkvol(const struct ubi_info *ubi, int vol_id) +{ + int err; + struct ubi_uif_volume *vol; + struct ubi_uif_info *uif = ubi->uif; + const struct ubi_vtbl_vtr *vtr; + + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + ubi_assert(!IS_ERR(vtr)); + + dbg_uif("create volume %d, size %d, type %d", + vol_id, vtr->reserved_pebs, vtr->vol_type); + + vol = ubi_kzalloc(sizeof(struct ubi_uif_volume)); + if (!vol) + return -ENOMEM; + vol->ubi = ubi; + vol->vol_id = vol_id; + vol->exclusive = 1; + spin_lock_init(&vol->vol_lock); + + mutex_lock(&uif->volumes_list_lock); + list_add(&vol->list, &uif->volumes); + mutex_unlock(&uif->volumes_list_lock); + + err = ubi_sysfs_vol_init(ubi, vol); + if (err) + goto out_sysfs; + + err = ubi_cdev_vol_init(ubi, vol); + if (err) + goto out_sysfs; + + err = ubi_gluebi_vol_init(ubi, vol); + if (err) + goto out_cdev; + + spin_lock(&vol->vol_lock); + vol->exclusive = 0; + spin_unlock(&vol->vol_lock); + + return 0; + +out_cdev: + ubi_cdev_vol_close(vol); +out_sysfs: + mutex_lock(&uif->volumes_list_lock); + spin_lock(&vol->vol_lock); + vol->removed = 1; + spin_unlock(&vol->vol_lock); + list_del(&vol->list); + mutex_unlock(&uif->volumes_list_lock); + ubi_sysfs_vol_close(vol); + return err; +} + +int ubi_uif_close_and_rmvol(struct ubi_vol_desc *desc) +{ + int err; + struct ubi_uif_volume *vol = desc->vol; + struct ubi_uif_info *uif = vol->ubi->uif; + + dbg_uif("remove UBI volume %d", vol->vol_id); + ubi_assert(desc->mode == UBI_EXCLUSIVE); + + err = ubi_gluebi_vol_close(vol); + if (err) + /* Somebody still holds the emulated MTD device */ + return err; + + spin_lock(&vol->vol_lock); + vol->removed = 1; + spin_unlock(&vol->vol_lock); + + mutex_lock(&uif->volumes_list_lock); + list_del(&vol->list); + mutex_unlock(&uif->volumes_list_lock); + + ubi_cdev_vol_close(vol); + ubi_sysfs_vol_close(vol); + ubi_kfree(desc); + module_put(THIS_MODULE); + return err; +} + +int ubi_uif_finish_update(struct ubi_vol_desc *desc); + +int ubi_uif_start_update(struct ubi_vol_desc *desc, long long bytes) +{ + int users, err = -EBUSY, rem; + struct ubi_uif_volume *vol = desc->vol; + const struct ubi_info *ubi = vol->ubi; + const struct ubi_vtbl_vtr *vtr; + void *upd_buf; + uint64_t tmp; + + dbg_uif("update UBI volume %d (%lld bytes)", vol->vol_id, bytes); + vtr = ubi_vtbl_get_vtr(ubi, vol->vol_id); + + upd_buf = ubi_kmalloc(vtr->usable_leb_size); + if (!upd_buf) + return -ENOMEM; + + spin_lock(&vol->vol_lock); + if (vol->updating) { + dbg_err("already being updated"); + goto out_unlock; + } + + /* The volume must only have one user */ + users = vol->readers + vol->writers + vol->exclusive; + if (users > 1) { + dbg_err("multiple users (%d)", users); + goto out_unlock; + } + vol->updating = 1; + spin_unlock(&vol->vol_lock); + + tmp = bytes; + rem = do_div(tmp, vtr->usable_leb_size); + vol->upd_ebs = tmp + !!rem; + vol->upd_bytes = bytes; + vol->upd_received = 0; + + err = ubi_upd_put_marker(ubi, vol->vol_id); + if (err) { + if (err != -EBUSY) + goto out_cancel; + + /* + * The update marker is busy. If this update + * marker belongs to this volume, we + * proceed. This may happen if the previous + * update of this volume was interrupted. + */ + if (ubi->upd->vol_id != vol->vol_id) + goto out_cancel; + } + + /* Before updating, we erase the whole volume */ + err = ubi_vmt_truncate_volume(ubi, vol->vol_id); + if (err) + goto out_corrupted; + + vol->upd_buf = upd_buf; + + /* + * Special case: f the number of bytes is zero, the volume is just + * fully freed. + */ + if (bytes == 0) + return ubi_uif_finish_update(desc); + + return 0; + +out_unlock: + spin_unlock(&vol->vol_lock); + ubi_kfree(upd_buf); + return err; + +out_corrupted: + ubi_dtbl_set_corrupted(ubi, vol->vol_id); +out_cancel: + ubi_kfree(upd_buf); + spin_lock(&vol->vol_lock); + vol->updating = 0; + spin_unlock(&vol->vol_lock); + return err; +} + +/** + * ubi_uif_finish_update - finish volume update. + * + * @desc: volume descriptor + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_uif_finish_update(struct ubi_vol_desc *desc) +{ + int err; + struct ubi_uif_volume *vol = desc->vol; + const struct ubi_info *ubi = vol->ubi; + + dbg_uif("finish volume %d update", vol->vol_id); + spin_lock(&vol->vol_lock); + if (!vol->updating || ubi->upd->vol_id != vol->vol_id) { + dbg_err("not under update"); + spin_unlock(&vol->vol_lock); + return -EINVAL; + } + spin_unlock(&vol->vol_lock); + + ubi_kfree(vol->upd_buf); + vol->upd_buf = NULL; + + err = ubi_upd_remove_marker(ubi); + if (err) { + ubi_dtbl_set_corrupted(ubi, vol->vol_id); + goto out; + } + + ubi_dtbl_set_dtr(ubi, vol->vol_id, vol->upd_bytes); + + mutex_lock(&ubi->uif->vol_check); + err = check_volume(vol); + vol->checked = 1; + mutex_unlock(&ubi->uif->vol_check); + +out: + spin_lock(&vol->vol_lock); + vol->updating = 0; + spin_unlock(&vol->vol_lock); + return err; +} + + +static void delete_volumes(const struct ubi_info *ubi); + +int __init ubi_uif_init(struct ubi_info *ubi) +{ + int i, err; + struct ubi_uif_info *uif; + const struct ubi_acc_info *acc = ubi->acc; + + dbg_uif("initialize the user interface unit"); + + uif = ubi_kzalloc(sizeof(struct ubi_uif_info)); + if (!uif) + return -ENOMEM; + ubi->uif = uif; + uif->ubi = ubi; + + mutex_init(&uif->volumes_list_lock); + mutex_init(&uif->vol_check); + INIT_LIST_HEAD(&uif->volumes); + + uif->ubi_name = ubi_kmalloc(sizeof(UBI_NAME_STR) + 5); + if (!uif->ubi_name) { + err = -ENOMEM; + goto out_uif; + } + sprintf(uif->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); + + err = ubi_cdev_init(ubi); + if (err) + goto out_name; + + err = ubi_sysfs_init(ubi); + if (err) + goto out_cdev; + + for (i = 0; i < acc->max_volumes; i++) { + const struct ubi_vtbl_vtr *vtr; + + cond_resched(); + + vtr = ubi_vtbl_get_vtr(ubi, i); + if (IS_ERR(vtr)) + continue; + + err = ubi_uif_mkvol(ubi, i); + if (unlikely(err)) + goto out_volumes; + } + + dbg_uif("the user interface unit is initialized"); + return 0; + +out_volumes: + delete_volumes(ubi); + ubi_sysfs_close(ubi); + +out_cdev: + ubi_cdev_close(ubi); + +out_name: + ubi_kfree(uif->ubi_name); + +out_uif: + ubi_kfree(uif); + return err; +} + +void __exit ubi_uif_close(const struct ubi_info *ubi) +{ + struct ubi_uif_info *uif = ubi->uif; + + dbg_uif("close the user interface unit for %s", uif->ubi_name); + + delete_volumes(ubi); + ubi_sysfs_close(ubi); + ubi_cdev_close(ubi); + ubi_kfree(uif->ubi_name); + ubi_kfree(uif); +} + +int __init ubi_uif_global_init(void) +{ + return ubi_sysfs_global_init(); +} + +void __exit ubi_uif_global_close(void) +{ + return ubi_sysfs_global_close(); +} + +/** + * delete_volumes - delete all the volume information. + * + * @ubi: the UBI device description object + */ +static void __exit delete_volumes(const struct ubi_info *ubi) +{ + struct ubi_uif_volume *vol, *vol_tmp; + struct ubi_uif_info *uif = ubi->uif; + + list_for_each_entry_safe(vol, vol_tmp, &uif->volumes, list) { + list_del(&vol->list); + vol->removed = 1; + ubi_gluebi_vol_close(vol); + ubi_cdev_vol_close(vol); + ubi_sysfs_vol_close(vol); + } +} + +/** + * check_volume - check the contents of a static volume. + * + * @vol: the volume to check + * + * This function checks that a static volume is not corrupted by fully reading + * it and checking data CRC. This function returns zero if the volume is not + * corrupted, %-EUCLEAN if it is corrupted and a negative error code in case of + * failure. + */ +static int check_volume(struct ubi_uif_volume *vol) +{ + void *buf; + int err = 0, i, read; + const struct ubi_dtbl_dtr *dtr; + const struct ubi_vtbl_vtr *vtr; + const struct ubi_info *ubi = vol->ubi; + + dtr = ubi_dtbl_get_dtr(ubi, vol->vol_id); + vtr = ubi_vtbl_get_vtr(ubi, vol->vol_id); + + if (vtr->vol_type != UBI_STATIC_VOLUME) + return 0; + + buf = ubi_kmalloc(vtr->usable_leb_size); + if (!buf) + return -ENOMEM; + + for (i = 0; i < dtr->used_ebs; i++) { + int size; + + if (i == dtr->used_ebs - 1) + size = dtr->last_eb_bytes; + else + size = vtr->usable_leb_size; + + err = ubi_eba_read_leb(ubi, vol->vol_id, i, buf, 0, size, 1, + &read); + if (err) { + if (err == -EUCLEAN) { + ubi_warn("static volume %d is corrupted", + vol->vol_id); + ubi_dtbl_set_corrupted(ubi, vol->vol_id); + err = 0; + } + break; + } + } + + ubi_kfree(buf); + return err; +} + +/** + * fill_ubi_vol_info - fill an "user volume information data structure". + * + * @ubi: the UBI device description object + * @vol_id: ID of the volume + * @vi: the volume information to fill + * + * This function must be invoked when the @vol_id volume will for sure not go. + */ +static void fill_ubi_vol_info(const struct ubi_info *ubi, int vol_id, + struct ubi_vol_info *vi) +{ + const struct ubi_vtbl_vtr *vtr; + const struct ubi_dtbl_dtr *dtr; + + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + dtr = ubi_dtbl_get_dtr(ubi, vol_id); + ubi_assert(!IS_ERR(vtr)); + ubi_assert(!IS_ERR(dtr)); + + vi->vol_id = vol_id; + vi->ubi_num = ubi->ubi_num; + vi->size = vtr->reserved_pebs; + vi->used_bytes = dtr->used_bytes; + vi->vol_type = vtr->vol_type; + vi->corrupted = dtr->corrupted; + vi->alignment = vtr->alignment; + vi->usable_leb_size = vtr->usable_leb_size; + vi->name_len = vtr->name_len; + vi->name = vtr->name; + vi->cdev = MKDEV(ubi->uif->major, vi->vol_id + 1); +} diff --git a/drivers/mtd/ubi/uif.h b/drivers/mtd/ubi/uif.h new file mode 100644 index 0000000..72b5a4b --- /dev/null +++ b/drivers/mtd/ubi/uif.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * UBI user interface unit. + * + * This unit implements all the UBI user interfaces: kernel interfaces, + * character device interfaces, and sysfs interfaces. + * + * There are two kinds of character devices: UBI character devices and volume + * character devices. UBI character devices allow users to manipulate by whole + * volumes: create, remove, and resize them. Volume character devices provide + * volume read and update capabilities. + * + * Major and minor numbers are assigned dynamically to both UBI and volume + * character devices. + */ + +#ifndef __UBI_UIF_H__ +#define __UBI_UIF_H__ + +#include +#include +#include +#include +#include +#include +#include +#include "gluebi.h" + +#define UBI_NAME_STR "ubi" + +struct ubi_info; +struct ubi_vtbl_vtr; +struct ubi_vol_desc; + +/** + * ubi_uif_mkvol - create a volume. + * + * @ubi: the UBI device description object + * @vol_id: ID of the new volume + * + * This functions creates all the user interface-related data structures of a + * new volume. Returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_uif_mkvol(const struct ubi_info *ubi, int vol_id); + +/** + * ubi_uif_close_and_rmvol - close a and remove a volume. + * + * @desc: volume descriptor + * + * This functions closes a volume and removes all the user interface-related + * data structures of this volume. Returns zero in case of success and a + * negative error code in case of failure. + */ +int ubi_uif_close_and_rmvol(struct ubi_vol_desc *desc); + +/** + * ubi_uif_start_update - start a volume update. + * + * @desc: volume descriptor + * @bytes: how many bytes will be written + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_uif_start_update(struct ubi_vol_desc *desc, long long bytes); + +/** + * ubi_uif_finish_update - finish volume update. + * + * @desc: volume descriptor + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_uif_finish_update(struct ubi_vol_desc *desc); + +/** + * ubi_uif_init - initialize the UBI user interface unit for an UBI device. + * + * @ubi: the UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int __init ubi_uif_init(struct ubi_info *ubi); + +/** + * ubi_uif_close - close the UBI user interface unit for an UBI device. + * + * @ubi: the UBI device description object + */ +void __exit ubi_uif_close(const struct ubi_info *ubi); + +/** + * ubi_uif_global_init - initialize the UBI user interface unit. + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int __init ubi_uif_global_init(void); + +/** + * ubi_uif_global_close - close the UBI user interface unit. + */ +void __exit ubi_uif_global_close(void); + +/** + * struct ubi_uif_volume - a per-volume user interface data structure. + * + * @dev: a class device object to make use of the the Linux device model + * @cdev: a Linux character device object to create a character device of this + * volume + * @ubi: a reference to the UBI description object this volume belongs to + * @vol_id: volume ID + * @list: the link in the list of UIF volume information + * @readers: number of users who are using this volume in read-only mode + * @writers: number of users who are using this volume in read-write mode + * @exclusive: whether somebody is using this volume in exclusive mode + * @removed: if the volume was removed from the UBI device + * @checked: if this static volume was checked + * @vol_lock: protects the @readers, @writers, @exclusive, @removed and + * @updating fields + * @updating: whether the volume is being updated + * @upd_ebs: how many eraseblocks are going to be updated + * @upd_received: how many bytes were already received from userspace + * @upd_bytes: how many bytes are expected to be received more + * @upd_buf: a temporary buffer where small update writes are gathered + */ +struct ubi_uif_volume { + struct class_device dev; + struct cdev cdev; + const struct ubi_info *ubi; + int vol_id; + struct list_head list; + int readers; + int writers; + int exclusive; + int removed; + int checked; + spinlock_t vol_lock; + int updating; + int upd_ebs; + long long upd_received; + long long upd_bytes; + void *upd_buf; + struct ubi_gluebi_volume gluebi_vol; +}; + +/** + * struct ubi_vol_desc - UBI opened volume descriptor + * + * @vol: reference to the corresponding volume description object + * @mode: volume open mode + */ +struct ubi_vol_desc { + struct ubi_uif_volume *vol; + enum ubi_open_mode mode; +}; + +/** + * struct ubi_uif_info - UBI user interfaces unit description structure. + * + * @cdev: a Linux character device object to create a character device of this + * UBI device + * @dev: the class device structure to use the the Linux device model + * @ubi: a reference to the UBI description structure this volume belongs to + * @major: major number of the UBI character device + * @ubi_name: name of this UBI device + * @volumes: a list of 'struct ubi_uif_volume' object for all existing volumes + * @volumes_list_lock: protects the the @volumes list + * @vol_check: serializes volume checking + * + * The @volumes_list_lock mutex protects the list of volumes of this UBI device + * from being changed. So it has to be locked when the @volumes list is being + * accessed. + * + * The @vol_check lock is used only when checking static volumes consistency to + * prevent from simultanious volume checks. + */ +struct ubi_uif_info { + struct cdev cdev; + struct class_device dev; + struct ubi_info *ubi; + int major; + char *ubi_name; + struct list_head volumes; + struct mutex volumes_list_lock; + struct mutex vol_check; +}; + +#endif /* !__UBI_UIF_H__ */ diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c new file mode 100644 index 0000000..47eed37 --- /dev/null +++ b/drivers/mtd/ubi/upd.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include "ubi.h" +#include "upd.h" +#include "wl.h" +#include "ivol.h" +#include "io.h" +#include "eba.h" +#include "account.h" +#include "alloc.h" +#include "scan.h" +#include "debug.h" + +int ubi_upd_put_marker(const struct ubi_info *ubi, int vol_id) +{ + int err, written; + struct ubi_upd_info *upd = ubi->upd; + + dbg_upd("put update marker for volume %d", vol_id); + + mutex_lock(&upd->mutex); + if (upd->vol_id != -1) { + dbg_upd("update marker is already used by volume %d", + upd->vol_id); + mutex_unlock(&upd->mutex); + return -EBUSY; + } + + upd->vol_id = vol_id; + upd->hdr_data.vol_id = cpu_to_ubi32(vol_id); + + err = ubi_eba_write_leb(ubi, UBI_UPDATE_VOL_ID, 0, NULL, 0, 0, + UBI_DATA_SHORTTERM, &written, &upd->hdr_data); + + mutex_unlock(&upd->mutex); + return err; +} + +int ubi_upd_remove_marker(const struct ubi_info *ubi) +{ + int err; + struct ubi_upd_info *upd = ubi->upd; + + mutex_lock(&upd->mutex); + + dbg_upd("remove update marker for volume %d", upd->vol_id); + + err = ubi_eba_erase_leb(ubi, UBI_UPDATE_VOL_ID, 0); + if (err) { + mutex_unlock(&upd->mutex); + return err; + } + upd->vol_id = -1; + err = ubi_wl_erase_flush(ubi); + mutex_unlock(&upd->mutex); + return err; +} + +int __init ubi_upd_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si) +{ + int err; + struct ubi_scan_volume *sv; + struct ubi_vid_hdr *vid_hdr; + struct ubi_upd_info *upd; + + dbg_upd("initialize the update unit"); + + upd = ubi_kzalloc(sizeof(struct ubi_upd_info)); + if (!upd) + return -ENOMEM; + ubi->upd = upd; + + mutex_init(&upd->mutex); + upd->vol_id = -1; + + sv = ubi_scan_get_scan_volume(si, UBI_UPDATE_VOL_ID); + if (sv) { + const struct ubi_vtbl_vtr *vtr; + const struct ubi_scan_leb *seb; + + /* + * Some transaction was interrupted. We have to mark the + * corresponding volume as corrupted. + */ + + /* There may be only one update marker */ + err = -EINVAL; + if (sv->leb_count > 1) { + dbg_err("too many update markers %d", sv->leb_count); + goto out_free_upd; + } + + seb = ubi_scan_get_scan_leb(sv, 0); + if (!seb) { + dbg_err("bad update marker"); + goto out_free_upd; + } + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) { + err = -ENOMEM; + goto out_free_upd; + } + + err = ubi_io_read_vid_hdr(ubi, seb->pnum, vid_hdr, 1); + if (unlikely(err < 0)) + goto out_vid_hdr; + else if (unlikely(err > 0) && err != UBI_IO_BITFLIPS) { + /* + * Cannot read the update marker. But we read it + * earlier, during scanning. No idea what happened. + * Don't erase this physical eraseblock because some + * corrupted volume will then be treated as good. + */ + err = -EIO; + goto out_vid_hdr; + } + + memcpy(&upd->hdr_data, &vid_hdr->ivol_data[0], + UBI_VID_HDR_IVOL_DATA_SIZE); + upd->vol_id = ubi32_to_cpu(upd->hdr_data.vol_id); + ubi_free_vid_hdr(ubi, vid_hdr); + + /* Check sanity */ + if (upd->vol_id < 0 || upd->vol_id >= ubi->acc->max_volumes) { + dbg_err("bad update marker for volume ID %d", + upd->vol_id); + goto out_free_upd; + } + + /* + * Lets see if the update marker belongs to an existing volume. + * It yes, this volume is marked as corrupted and the marker is + * not removed.If not, remove the marker. This is probably + * owing to an unclean reboot during volume removal. + */ + ubi_warn("volume %d update was interrupted", upd->vol_id); + vtr = ubi_vtbl_get_vtr(ubi, upd->vol_id); + if (IS_ERR(vtr)) { + err = ubi_upd_remove_marker(ubi); + if (err) + goto out_free_upd; + } else + ubi_dtbl_set_corrupted(ubi, upd->vol_id); + } + + dbg_upd("the update unit is initialized"); + return 0; + +out_vid_hdr: + ubi_free_vid_hdr(ubi, vid_hdr); +out_free_upd: + ubi_kfree(upd); + return err; +} + +void __exit ubi_upd_close(const struct ubi_info *ubi) +{ + dbg_upd("close the update unit"); + ubi_kfree(ubi->upd); +} diff --git a/drivers/mtd/ubi/upd.h b/drivers/mtd/ubi/upd.h new file mode 100644 index 0000000..98f6a43 --- /dev/null +++ b/drivers/mtd/ubi/upd.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * The update unit. + * + * This unit is responsible for maintaining atomicity of the update operation. + * This is achieved using so-called "update marker". The update marker is + * written to flash before the update starts, and removed from flash after the + * update has been finished. So, if the update was interrupted by an unclean + * reboot, the scanning will hit on the update marker and we'll know that the + * volume is corrupted. + * + * The update marker is implemented as follows. We maintain an internal + * volume, called "the update volume", which has only one logical eraseblock. + * And this logical eraseblock is effectively the update marker. Thus, to put + * the update marker means to write to the only eraseblock of the update + * volume, and to remove the update marker is to erase that eraseblock. + * + * Note, more generic approach for things like this is a journal, but we + * avoided introducing journal because it is more complex. Indeed, for + * boot loaders it would be much more difficult to parse the journal. In the + * next UBI generation, when the EBA table is maintained on flash, the journal + * will be needed anyway, so the update volume will go. + */ + +#ifndef __UBI_UPD_H__ +#define __UBI_UPD_H__ + +#include +#include +#include +#include + +struct ubi_info; +struct ubi_scan_info; + +/** + * ubi_upd_put_marker - put update marker. + * + * @ubi: the UBI device description object + * @vol_id: for which volume to put the update marker. + * + * This function returns zero in case of success, and a negative error code in + * case of failure. + */ +int ubi_upd_put_marker(const struct ubi_info *ubi, int vol_id); + +/** + * ubi_upd_remove_marker - remove update marker. + * + * @ubi: the UBI device description object + * + * This function returns zero in case of success, and a negative error code in + * case of failure. + */ +int ubi_upd_remove_marker(const struct ubi_info *ubi); + +/** + * ubi_upd_init_scan - initialize the update volume unit using scanning + * information. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * + * This function returns zero in case of success, and a negative error code in + * case of failure. + */ +int __init ubi_upd_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si); + +/** + * ubi_upd_close - close the update volume unit. + * + * @ubi: the UBI device description object + */ +void __exit ubi_upd_close(const struct ubi_info *ubi); + +/** + * struct ubi_upd_info - UBI update unit description data structure. + * + * @vol_id: which volume utilizes the update marker + * @hdr_data: data put to the VID header of the update marker eraseblock + * @mutex: serializes access to the update marker + * + * Note, at this implementation the update volume consists on only one + * eraseblock, so only one update marker may be put at a time, so only one + * update at a time is allowed. + */ +struct ubi_upd_info { + int vol_id; /* public */ + struct ubi_vid_hdr_upd_vol hdr_data; /* private */ + struct mutex mutex; /* private */ +}; + +#endif /* !__UBI_UPD_H__ */ diff --git a/drivers/mtd/ubi/volmgmt.c b/drivers/mtd/ubi/volmgmt.c new file mode 100644 index 0000000..750dfa1 --- /dev/null +++ b/drivers/mtd/ubi/volmgmt.c @@ -0,0 +1,450 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include "ubi.h" +#include "alloc.h" +#include "io.h" +#include "wl.h" +#include "upd.h" +#include "volmgmt.h" +#include "vtbl.h" +#include "dtbl.h" +#include "ivol.h" +#include "misc.h" +#include "eba.h" +#include "account.h" +#include "scan.h" +#include "debug.h" + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_VMT +static int paranoid_check_vtr(const struct ubi_info *ubi, + const struct ubi_vtbl_vtr *vtr); +#else +#define paranoid_check_vtr(ubi, vtr) 0 +#endif + +static int find_vacant_vol_id(const struct ubi_info *ubi); + +int ubi_vmt_mkvol(const struct ubi_info *ubi, int vol_id, + const struct ubi_vtbl_vtr *vtr) +{ + int i, err = 0; + struct ubi_vmt_info *vmt = ubi->vmt; + const struct ubi_vtbl_vtr *vtr_ck; + + dbg_vmt("create volume ID %d, reserved_pebs %d, type %d, name %s", + vol_id, vtr->reserved_pebs, vtr->vol_type, vtr->name); + + mutex_lock(&vmt->mutex); + + if (vol_id == UBI_VOL_NUM_AUTO) { + vol_id = find_vacant_vol_id(ubi); + if (vol_id < 0) { + err = vol_id; + goto out_unlock; + } + } else + ubi_assert(vol_id >= 0 && vol_id < ubi->acc->max_volumes); + + err = paranoid_check_vtr(ubi, vtr); + if (err) { + err = -EINVAL; + goto out_unlock; + } + + /* Get sure that this volume does not exist */ + err = -EEXIST; + vtr_ck = ubi_vtbl_get_vtr(ubi, vol_id); + if (!IS_ERR(vtr_ck)) { + dbg_err("volume %d already exists", vol_id); + goto out_unlock; + } + + /* Ensure that this volume has a unique name */ + for (i = 0; i < ubi->acc->max_volumes; i++) { + cond_resched(); + + vtr_ck = ubi_vtbl_get_vtr(ubi, i); + if (IS_ERR(vtr_ck)) + continue; + + if (unlikely(vtr->name_len == vtr_ck->name_len && + !strcmp(vtr->name, vtr_ck->name))) { + dbg_err("not unique name \"%s\", volume %d has it", + vtr->name, i); + goto out_unlock; + } + } + + err = ubi_acc_mkvol(ubi, vtr->reserved_pebs); + if (err) + goto out_unlock; + + /* + * Finish all the pending erases because there may be some LEBs + * belonging to the same volume ID. We don't want to be messed-up. + */ + err = ubi_wl_erase_flush(ubi); + if (err) + goto out_acc; + + err = ubi_eba_mkvol(ubi, vol_id, vtr->reserved_pebs); + if (err) + goto out_acc; + + err = ubi_vtbl_mkvol(ubi, vol_id, vtr); + if (err) + goto out_eba; + + ubi_dtbl_set_dtr(ubi, vol_id, 0); + + mutex_unlock(&vmt->mutex); + return vol_id; + +out_eba: + ubi_eba_rmvol(ubi, vol_id); +out_acc: + ubi_acc_rmvol(ubi, vtr->reserved_pebs); +out_unlock: + mutex_unlock(&vmt->mutex); + return err; +} + +int ubi_vmt_rmvol(const struct ubi_info *ubi, int vol_id) +{ + int err, reserved_pebs; + const struct ubi_vtbl_vtr *vtr; + struct ubi_vmt_info *vmt = ubi->vmt; + + dbg_vmt("remove volume %d", vol_id); + ubi_assert(vol_id >= 0 && vol_id < ubi->acc->max_volumes); + + mutex_lock(&vmt->mutex); + + /* Ensure that this volume exists */ + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + if (IS_ERR(vtr)) { + err = PTR_ERR(vtr); + goto out_unlock; + } + + err = ubi_vmt_truncate_volume(ubi, vol_id); + if (err) + goto out_corr; + + reserved_pebs = vtr->reserved_pebs; + + err = ubi_vtbl_rmvol(ubi, vol_id); + if (err) + goto out_corr; + + /* If there is an update marker for this volume, remove it */ + if (ubi->upd->vol_id == vol_id) { + err = ubi_upd_remove_marker(ubi); + if (err) + goto out_unlock; + } + + err = ubi_eba_rmvol(ubi, vol_id); + if (err) + goto out_unlock; + + ubi_acc_rmvol(ubi, reserved_pebs); + + err = ubi_wl_erase_flush(ubi); + if (err) + return err; + +out_unlock: + mutex_unlock(&vmt->mutex); + return err; + +out_corr: + ubi_dtbl_set_corrupted(ubi, vol_id); + mutex_unlock(&vmt->mutex); + return err; +} + +int ubi_vmt_rsvol(const struct ubi_info *ubi, int vol_id, int reserved_pebs) +{ + int err, pebs, old_reserved_pebs; + struct ubi_vmt_info *vmt = ubi->vmt; + const struct ubi_vtbl_vtr *vtr; + const struct ubi_dtbl_dtr *dtr; + + dbg_vmt("re-size volume %d to %d PEBs", vol_id, reserved_pebs); + ubi_assert(vol_id >= 0 && vol_id < ubi->acc->max_volumes); + ubi_assert(reserved_pebs > 0); + + mutex_lock(&vmt->mutex); + + /* Ensure that this volume exists */ + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + if (IS_ERR(vtr)) { + err = PTR_ERR(vtr); + goto out_unlock; + } + + dtr = ubi_dtbl_get_dtr(ubi, vol_id); + + if (vtr->vol_type == UBI_STATIC_VOLUME && + reserved_pebs < dtr->used_ebs) { + dbg_err("too small size %d, static volume %d has %d used LEBs", + reserved_pebs, vol_id, dtr->used_ebs); + err = -EINVAL; + goto out_unlock; + } + + /* If the size is the same, we have nathing to do */ + if (reserved_pebs == vtr->reserved_pebs) { + err = 0; + goto out_unlock; + } + + old_reserved_pebs = vtr->reserved_pebs; + + err = ubi_vtbl_rsvol(ubi, vol_id, reserved_pebs); + if (err) + goto out_unlock; + + pebs = reserved_pebs - old_reserved_pebs; + if (pebs > 0) { + err = ubi_acc_reserve(ubi, pebs); + if (err) + goto out_unlock; + } else + ubi_acc_free(ubi, -pebs); + + err = ubi_eba_rsvol(ubi, vol_id, reserved_pebs); + if (err) + goto out_unlock; + + ubi_dtbl_set_dtr(ubi, vol_id, reserved_pebs * vtr->usable_leb_size); + + err = ubi_wl_erase_flush(ubi); + +out_unlock: + mutex_unlock(&vmt->mutex); + return err; +} + +int ubi_vmt_truncate_volume(const struct ubi_info *ubi, int vol_id) +{ + int i, err; + const struct ubi_vtbl_vtr *vtr; + + dbg_vmt("truncate volume %d", vol_id); + ubi_assert(vol_id >= 0 && vol_id < ubi->acc->max_volumes); + + vtr = ubi_vtbl_get_vtr(ubi, vol_id); + for (i = 0; i < vtr->reserved_pebs; i++) { + cond_resched(); + + err = ubi_eba_erase_leb(ubi, vol_id, i); + if (unlikely(err)) + return err; + } + + /* + * As we don't have an on-flash data table, we have to ensure the + * eraseblocks were really erased, not just scheduled for erasure. + * If we had a on-flash data table, we could make a mark there instead. + */ + err = ubi_wl_erase_flush(ubi); + if (err) + return err; + + ubi_dtbl_set_dtr(ubi, vol_id, 0); + return 0; +} + +int __init ubi_vmt_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si) +{ + int err; + struct ubi_vmt_info *vmt; + + dbg_vmt("initialize the volume management unit"); + + vmt = ubi_kzalloc(sizeof(struct ubi_vmt_info)); + if (!vmt) + return -ENOMEM; + ubi->vmt = vmt; + + mutex_init(&vmt->mutex); + + err = ubi_ivol_init_scan(ubi, si); + if (err) + goto out_vmt; + + err = ubi_vtbl_init_scan(ubi, si); + if (err) + goto out_ivol; + + err = ubi_acc_init_scan(ubi, si); + if (err) + goto out_vtbl; + + err = ubi_dtbl_init_scan(ubi, si); + if (err) + goto out_acc; + + err = ubi_upd_init_scan(ubi, si); + if (err) + goto out_dtbl; + + dbg_vmt("the volume management unit is initialized"); + return 0; + +out_dtbl: + ubi_dtbl_close(ubi); +out_acc: + ubi_acc_close(ubi); +out_vtbl: + ubi_vtbl_close(ubi); +out_ivol: + ubi_ivol_close(ubi); +out_vmt: + ubi_kfree(vmt); + return err; +} + +void __exit ubi_vmt_close(const struct ubi_info *ubi) +{ + dbg_vmt("close the volume management unit"); + ubi_upd_close(ubi); + ubi_dtbl_close(ubi); + ubi_acc_close(ubi); + ubi_vtbl_close(ubi); + ubi_ivol_close(ubi); + ubi_kfree(ubi->vmt); +} + +/** + * find_vacant_vol_id - find an unused volume ID. + * + * @ubi: the UBI device description object + * + * This function returns a positive volume ID or %-ENOSPC if there are no free + * volume IDs. + */ +static int find_vacant_vol_id(const struct ubi_info *ubi) +{ + int i; + + for (i = 0; i < ubi->acc->max_volumes; i++) { + const struct ubi_vtbl_vtr *vtr; + + cond_resched(); + + vtr = ubi_vtbl_get_vtr(ubi, i); + if (IS_ERR(vtr)) { + dbg_vmt("found volume ID %d", i); + return i; + } + } + + dbg_vmt("vacant volume ID not found"); + return -ENOSPC; +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_VMT + +/** + * paranoid_check_vtr - check sanity of a &struct ubi_vtbl_vtr object. + * + * @ubi: the UBI device description object + * @vtr: an object to check + * + * This function returns zero if the volume table record is sane, and %1 if + * not. + */ +static int paranoid_check_vtr(const struct ubi_info *ubi, + const struct ubi_vtbl_vtr *vtr) +{ + int n; + const struct ubi_io_info *io = ubi->io; + + if (vtr->reserved_pebs == 0) + return 0; + + if (unlikely(vtr->reserved_pebs < 0 || vtr->alignment < 0 || + vtr->data_pad < 0 || vtr->name_len < 0)) { + ubi_err("negative values"); + goto bad; + } + + if (unlikely(vtr->alignment > io->leb_size)) { + ubi_err("too large alignment"); + goto bad; + } + + if (unlikely(vtr->alignment == 0)) { + ubi_err("zero alignment"); + goto bad; + } + + n = vtr->alignment % io->min_io_size; + if (vtr->alignment != 1 && unlikely(n)) { + ubi_err("alignment is not multiple of min I/O unit size"); + goto bad; + } + + n = io->leb_size % vtr->alignment; + if (unlikely(vtr->data_pad != n)) { + ubi_err("bad data_pad, has to be %d", n); + goto bad; + } + + if (unlikely(vtr->vol_type != UBI_DYNAMIC_VOLUME && + vtr->vol_type != UBI_STATIC_VOLUME)) { + ubi_err("bad vol_type"); + goto bad; + } + + if (unlikely(vtr->name_len > UBI_VOL_NAME_MAX)) { + ubi_err("too long volume name, max is %d", UBI_VOL_NAME_MAX); + goto bad; + } + + if (unlikely(!vtr->name)) { + ubi_err("NULL volume name"); + goto bad; + } + + n = strnlen(vtr->name, vtr->name_len + 1); + if (unlikely(n != vtr->name_len)) { + ubi_err("bad name_len"); + goto bad; + } + + return 0; + +bad: + ubi_err("volume table record paranoid check failed"); + ubi_dbg_dump_vtr(vtr); + return 1; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID_VMT */ diff --git a/drivers/mtd/ubi/volmgmt.h b/drivers/mtd/ubi/volmgmt.h new file mode 100644 index 0000000..ed2fc94 --- /dev/null +++ b/drivers/mtd/ubi/volmgmt.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * UBI volume management unit. + * + * The unit is responsible for creation, deletion, updating and resizing + * of volumes. + */ + +#ifndef __UBI_VOLMGMT_H__ +#define __UBI_VOLMGMT_H__ + +#include +#include + +struct ubi_info; +struct ubi_scan_info; +struct ubi_vol_info; +struct ubi_vtbl_vtr; + +/** + * ubi_vmt_get_data_info - get volume data description. + * + * @ubi: the UBI device description object + * @vol_id: ID of the requested volume + * + * This function returns a pointer to the corresponding data description + * object. + */ +const struct ubi_vmt_data_info * +ubi_vmt_get_data_info(const struct ubi_info *ubi, int vol_id); + +/** + * ubi_vmt_mkvol - create a volume. + * + * @ubi: the UBI device description object + * @vol_id: ID to assign to the new volume + * @vtr: volume table record corresponding to the new volume + * + * If @vol_id id %UBI_VOL_NUM_AUTO then new volume is automatically given an + * unused volume identifier. The @vtr->usable_leb_size field is ignored. + * + * This function returns the ID of the newly created volume in case of success, + * and a negative error code in case of failure. + */ +int ubi_vmt_mkvol(const struct ubi_info *ubi, int vol_id, + const struct ubi_vtbl_vtr *vtr); + +/** + * ubi_vmt_rmvol - remove a volume. + * + * @ubi: the UBI device description object + * @vol_id: ID of the volume to remove + * + * This function returns zero in case of success, and a negative error code in + * case of failure. + */ +int ubi_vmt_rmvol(const struct ubi_info *ubi, int vol_id); + +/** + * ubi_vmt_rsvol - re-size a volume. + * + * @ubi: the UBI device description object + * @vol_id: ID of the volume to re-size + * @reserved_pebs: new volume size + * + * This function returns zero in case of success, and a negative error code in + * case of failure. + */ +int ubi_vmt_rsvol(const struct ubi_info *ubi, int vol_id, int reserved_pebs); + +/** + * ubi_vmt_truncate_volume - make sure the volume contains only 0xFF bytes. + * + * @ubi: the UBI device description object + * @vol_id: ID of the volume to free + * + * This function erases all the volume's eraseblocks. Returns zero in case of + * success, and a negative error code in case of failure. + */ +int ubi_vmt_truncate_volume(const struct ubi_info *ubi, int vol_id); + +/** + * ubi_vmt_init_scan - initialize the volume management unit using scanning + * information. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * + * This function returns zero in case of success, and a negative error code in + * case of failure. + */ +int __init ubi_vmt_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si); + +/** + * ubi_vmt_close - close the volume management unit. + * + * @ubi: the UBI device description object + */ +void __exit ubi_vmt_close(const struct ubi_info *ubi); + +/** + * struct ubi_vmt_info - volume management unit description data + * structure. + * + * @mutex: a mutex to serialize volume changes + */ +struct ubi_vmt_info { + struct mutex mutex; /* private */ +}; + +#endif /* !__UBI_VOLMGMT_H__ */ diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c new file mode 100644 index 0000000..0ef8c68 --- /dev/null +++ b/drivers/mtd/ubi/vtbl.c @@ -0,0 +1,1077 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ubi.h" +#include "alloc.h" +#include "wl.h" +#include "io.h" +#include "vtbl.h" +#include "ivol.h" +#include "eba.h" +#include "scan.h" +#include "misc.h" +#include "debug.h" + +static int change_volume(const struct ubi_info *ubi, + int vol_id, const struct ubi_vtbl_vtr *vtr); + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_VTBL +static int paranoid_check_vtr(const struct ubi_info *ubi, + const struct ubi_vtbl_vtr *vtr); +#else +#define paranoid_check_vtr(ubi, vtr) 0 +#endif + +/* Empty volume table record pattern */ +static const struct ubi_vol_tbl_record empty_rec; + +int ubi_vtbl_mkvol(const struct ubi_info *ubi, int vol_id, + const struct ubi_vtbl_vtr *vtr) +{ + int err; + + dbg_vtbl("create volume: vol_id %d, reserved_pebs %d, " + "alignment %d, data_pad %d, vol_type %d, " + "name_len %d, name %s", vol_id, vtr->reserved_pebs, + vtr->alignment, vtr->data_pad, vtr->vol_type, + vtr->name_len, vtr->name); + + /* Input arguments sanity check */ + ubi_assert(vol_id >= 0 && vol_id < ubi->vtbl->vt_slots); + ubi_assert(vtr->reserved_pebs > 0); + ubi_assert(ubi->vtbl->vt[vol_id].reserved_pebs == 0); + ubi_assert(!ubi_is_ivol(vol_id)); + + + err = change_volume(ubi, vol_id, vtr); + return err; +} + +int ubi_vtbl_rmvol(const struct ubi_info *ubi, int vol_id) +{ + int err; + struct ubi_vtbl_vtr empty_vtr; + + dbg_vtbl("remove volume %d", vol_id); + + /* Input arguments sanity check */ + ubi_assert(vol_id >= 0 && vol_id < ubi->vtbl->vt_slots); + ubi_assert(ubi->vtbl->vt[vol_id].reserved_pebs != 0); + ubi_assert(!ubi_is_ivol(vol_id)); + + empty_vtr.reserved_pebs = 0; + err = change_volume(ubi, vol_id, &empty_vtr); + return err; +} + +int ubi_vtbl_rsvol(const struct ubi_info *ubi, int vol_id, int reserved_pebs) +{ + int err; + struct ubi_vtbl_vtr *vtr; + const struct ubi_vtbl_info *vtbl = ubi->vtbl; + + dbg_vtbl("re-size volume %d to %d EBs, old size %d EBs", vol_id, + reserved_pebs, vtbl->vt[vol_id].reserved_pebs); + + /* Input arguments sanity check */ + ubi_assert(vol_id >= 0 && vol_id < vtbl->vt_slots); + ubi_assert(reserved_pebs > 0); + ubi_assert(vtbl->vt[vol_id].reserved_pebs != 0); + ubi_assert(!ubi_is_ivol(vol_id)); + + vtr = ubi_kmalloc(sizeof(struct ubi_vtbl_vtr)); + if (unlikely(!vtr)) + return -ENOMEM; + + memcpy(vtr, &vtbl->vt[vol_id], sizeof(struct ubi_vtbl_vtr)); + + vtr->name = strdup_len(vtbl->vt[vol_id].name, + vtbl->vt[vol_id].name_len); + if (!vtr->name) { + err = -ENOMEM; + goto out; + } + + vtr->reserved_pebs = reserved_pebs; + err = change_volume(ubi, vol_id, vtr); +out: + ubi_kfree(vtr->name); + ubi_kfree(vtr); + return err; +} + +const struct ubi_vtbl_vtr *ubi_vtbl_get_vtr(const struct ubi_info *ubi, + int vol_id) +{ + int err; + const struct ubi_vtbl_info *vtbl = ubi->vtbl; + + if (ubi_is_ivol(vol_id)) + return ubi_ivol_get_vtr(ubi, vol_id); + + ubi_assert(vol_id >= 0 && vol_id < vtbl->vt_slots); + + if (vtbl->vt[vol_id].reserved_pebs == 0) + return ERR_PTR(-ENODEV); + + err = paranoid_check_vtr(ubi, &vtbl->vt[vol_id]); + return &vtbl->vt[vol_id]; +} + +static int __init init_ram_vt(const struct ubi_info *ubi, + const struct ubi_vol_tbl_record *vol_tbl); + +static struct ubi_vol_tbl_record __init * +create_empty_lvol(const struct ubi_info *ubi, struct ubi_scan_info *si); + +static struct ubi_vol_tbl_record __init * +process_lvol(const struct ubi_info *ubi, struct ubi_scan_info *si, + struct ubi_scan_volume *sv); + +static int __init check_scanning_info(const struct ubi_info *ubi, + struct ubi_scan_info *si); + +static void __exit free_volume_info(const struct ubi_info *ubi); + +int __init ubi_vtbl_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si) +{ + int err; + uint32_t crc; + struct ubi_vol_tbl_record *vol_tbl; + struct ubi_vtbl_info *vtbl; + struct ubi_scan_volume *sv; + const struct ubi_io_info *io = ubi->io; + + dbg_vtbl("initialize the volume table unit"); + + vtbl = ubi_kzalloc(sizeof(struct ubi_vtbl_info)); + if (!vtbl) + return -ENOMEM; + ubi->vtbl = vtbl; + + /* Initialize the empty volume table record pattern */ + vol_tbl = (struct ubi_vol_tbl_record *)&empty_rec; + crc = crc32(UBI_CRC32_INIT, vol_tbl, UBI_VTBL_RECORD_SIZE_CRC); + vol_tbl->crc = cpu_to_ubi32(crc); + + /* + * The number of supported volumes is restricted by the eraseblock size + * and by the UBI_MAX_VOLUMES constant. + */ + vtbl->vt_slots = io->leb_size / UBI_VTBL_RECORD_SIZE; + if (vtbl->vt_slots > UBI_MAX_VOLUMES) + vtbl->vt_slots = UBI_MAX_VOLUMES; + + /* + * We are going to calculate size of the volume table. It must be less + * then the logical eraseblock size or equivalent to it. Here we also + * ensure that @vtbl->vt_size has correct alignment (i.e., it is + * multiple of the flash input/output unit size). + */ + vtbl->vt_size = vtbl->vt_slots * UBI_VTBL_RECORD_SIZE; + if (io->min_io_size > 1) { + int mod, rest; + + mod = vtbl->vt_size % io->min_io_size; + rest = io->min_io_size - mod; + if (vtbl->vt_size + rest <= io->leb_size) + vtbl->vt_size += rest; + else { + vtbl->vt_size -= mod; + vtbl->vt_slots -= mod / UBI_VTBL_RECORD_SIZE; + } + } + + sv = ubi_scan_get_scan_volume(si, UBI_LAYOUT_VOL_ID); + if (!sv) { + /* + * No logical eraseblocks belonging to the layout volume were + * found. This could mean that the flash is just empty. In + * this case we "UBI-nize" this flash creating an empty layout + * volume. + * + * But if flash is not empty this must be a serious corruption + * or we were just fed by an bad/random/etc data. We could try + * to do some recovery, but it seems its better to do this + * using some userspace tool. + */ + if (si->is_empty) { + vol_tbl = create_empty_lvol(ubi, si); + if (IS_ERR(vol_tbl)) { + err = PTR_ERR(vol_tbl); + goto out; + } + } else { + ubi_err("the layout volume was not found"); + err = -EINVAL; + goto out; + } + } else { + if (sv->leb_count > UBI_LAYOUT_VOLUME_EBS) { + /* This must not happen with sane UBI images */ + ubi_err("too many logical LEBs (%d) belonging to the " + "layout volume found", sv->leb_count); + err = -EINVAL; + goto out; + } + + /* + * The layout volume was found during scanning, lets look at + * it, check it, etc. + */ + vol_tbl = process_lvol(ubi, si, sv); + if (IS_ERR(vol_tbl)) { + err = PTR_ERR(vol_tbl); + goto out; + } + } + + /* + * The layout volume is OK, initialize the corresponding in-RAM data + * structures. + */ + err = init_ram_vt(ubi, vol_tbl); + if (err) + goto out; + + ubi_kfree(vol_tbl); + ubi->vtbl = vtbl; + + /* + * Get sure that the scanning information about the layout volume is + * consistent to what it contains. + */ + err = check_scanning_info(ubi, si); + if (err) + goto out_vi; + + dbg_vtbl("the volume table unit is initialized"); + return 0; + +out_vi: + free_volume_info(ubi); +out: + ubi_kfree(vtbl); + return err; +} + +void __exit ubi_vtbl_close(const struct ubi_info *ubi) +{ + dbg_vtbl("close the volume table unit"); + free_volume_info(ubi); + ubi_kfree(ubi->vtbl); +} + +/** + * change_volume - change geometry of an user volume. + * + * @ubi: the UBI device description object + * @vol_id: ID of the volume to change + * @vtr: new volume table record + * + * This function accepts a new volume table record in @vtr and changes the + * volume table correspondingly (both in RAM and on flash). If + * @vtr->reserved_pebs contains zero, the volume is be deleted. + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int change_volume(const struct ubi_info *ubi, + int vol_id, const struct ubi_vtbl_vtr *vtr) +{ + int i, err, tries = 0; + struct ubi_vol_tbl_record *vol_tbl; + const struct ubi_io_info *io = ubi->io; + const struct ubi_vtbl_info *vtbl = ubi->vtbl; + + vol_tbl = ubi_kzalloc(vtbl->vt_size); + if (!vol_tbl) + return -ENOMEM; + + /* Generate the on-flash volume table contents */ + for (i = 0; i < vtbl->vt_slots; i++) { + uint32_t crc; + const struct ubi_vtbl_vtr *tmp_vtr; + + cond_resched(); + tmp_vtr = &vtbl->vt[i]; + + err = paranoid_check_vtr(ubi, tmp_vtr); + if (unlikely(err)) + goto out; + + if (unlikely(i == vol_id)) + tmp_vtr = vtr; + + if (tmp_vtr->reserved_pebs == 0) { + /* Volume is empty */ + memcpy(&vol_tbl[i], &empty_rec, UBI_VTBL_RECORD_SIZE); + continue; + } + + vol_tbl[i].reserved_pebs = cpu_to_ubi32(tmp_vtr->reserved_pebs); + vol_tbl[i].alignment = cpu_to_ubi32(tmp_vtr->alignment); + vol_tbl[i].data_pad = cpu_to_ubi32(tmp_vtr->data_pad); + if (tmp_vtr->vol_type == UBI_DYNAMIC_VOLUME) + vol_tbl[i].vol_type = UBI_VID_DYNAMIC; + else + vol_tbl[i].vol_type = UBI_VID_STATIC; + vol_tbl[i].name_len = cpu_to_ubi16((uint16_t)tmp_vtr->name_len); + + memcpy(&vol_tbl[i].name, tmp_vtr->name, tmp_vtr->name_len); + vol_tbl[i].name[tmp_vtr->name_len] = '\0'; + + crc = crc32(UBI_CRC32_INIT, &vol_tbl[i], + UBI_VTBL_RECORD_SIZE_CRC); + vol_tbl[i].crc = cpu_to_ubi32(crc); + } + + + /* Now just update both volume table copies */ + for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { + int written; + + cond_resched(); + + err = ubi_eba_erase_leb(ubi, UBI_LAYOUT_VOL_ID, i); + if (unlikely(err)) + goto out; + + err = ubi_wl_erase_flush(ubi); + if (unlikely(err)) + goto out; + + err = ubi_eba_write_leb(ubi, UBI_LAYOUT_VOL_ID, i, vol_tbl, 0, + vtbl->vt_size, UBI_DATA_LONGTERM, + &written, NULL); + if (unlikely(err)) { + if (++tries <= 5) + i -= 1; /* Try again */ + else + goto out; + } + } + + /* Change the in-RAM volume table correspondingly */ + ubi_kfree(vtbl->vt[vol_id].name); + if (vtr->reserved_pebs != 0) { + memcpy(&vtbl->vt[vol_id], vtr, sizeof(struct ubi_vtbl_vtr)); + vtbl->vt[vol_id].usable_leb_size = io->leb_size - vtr->data_pad; + vtbl->vt[vol_id].name = strdup_len(vtr->name, vtr->name_len); + if (!vtbl->vt[vol_id].name) { + err = -ENOMEM; + goto out; + } + if (unlikely(paranoid_check_vtr(ubi, &vtbl->vt[vol_id]))) { + err = -EINVAL; + goto out; + } + } else + memset(&vtbl->vt[vol_id], 0, sizeof(struct ubi_vtbl_vtr)); + + ubi_kfree(vol_tbl); + return 0; + +out: + /* + * The volume table is probably in an inconsistent state now, so switch + * to read-only mode. + */ + ubi_eba_ro_mode(ubi); + ubi_kfree(vol_tbl); + return err; +} + +static int __init create_vtbl(const struct ubi_info *ubi, + struct ubi_scan_info *si, int copy, + void *vol_tbl); + +/** + * create_empty_lvol - create an empty layout volume. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * + * If during scanning it was found out that the flash device is empty, this + * function is called to create an empty layout volume. + * + * This function returns the volume table contents in case of success and an + * error code in case of failure. + */ +static struct ubi_vol_tbl_record __init * +create_empty_lvol(const struct ubi_info *ubi, struct ubi_scan_info *si) +{ + int i, err; + struct ubi_vol_tbl_record *vol_tbl; + struct ubi_vtbl_info *vtbl = ubi->vtbl; + + vol_tbl = ubi_kmalloc(vtbl->vt_size); + if (!vol_tbl) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < vtbl->vt_slots; i++) + memcpy(&vol_tbl[i], &empty_rec, UBI_VTBL_RECORD_SIZE); + + for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { + cond_resched(); + + err = create_vtbl(ubi, si, i, vol_tbl); + if (unlikely(err)) + goto out_free; + } + + return vol_tbl; + +out_free: + ubi_kfree(vol_tbl); + return ERR_PTR(err); +} + +static int vol_tbl_check(const struct ubi_info *ubi, + const struct ubi_vol_tbl_record *vol_tbl); + +/** + * process_lvol - process the layout volume. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * @sv: scanning information about the layout volume + * + * This function is responsible for reading the layout volume, ensuring it is + * not corrupted, and recovering from corruptions if needed. + * + * This function returns the volume table in case of success and a negative + * error code in case of failure. + */ +static struct ubi_vol_tbl_record __init * +process_lvol(const struct ubi_info *ubi, struct ubi_scan_info *si, + struct ubi_scan_volume *sv) +{ + int err, read; + struct rb_node *rb; + struct ubi_scan_leb *seb; + struct ubi_vtbl_info *vtbl = ubi->vtbl; + struct ubi_vol_tbl_record *leb[UBI_LAYOUT_VOLUME_EBS] = { NULL, NULL }; + int leb_corrupted[UBI_LAYOUT_VOLUME_EBS] = { 1, 1 }; + + /* + * UBI goes through the following steps when it updates the layout + * volume: + * a. erase LEB 0; + * b. write new data to LEB 0; + * c. erase LEB 1; + * d. write new data to LEB 1. + * Before being updated, LEBs 0 and 1 contain the same data. + * + * Owing to unclean reboots, we may lose the contents of LEB 0 but there + * is always LEB 1 present. Thus, it is normal situation when LEB 0 is + * corrupted while LEB 1 is OK. Also, due to unclean reboots, the LEB 1 + * may be corrupted, but there has to be LEB 0. And finally, unclean + * reboots may result in a situation when neither LEB 0 nor LEB 1 are + * corrupted, but are different. In this case, LEB 0 contains more + * recent information. + * + * So the plan is to first check LEB 0. Then + * a. if LEB 0 is OK, it contains the most resent data; then we + * compare its contents with LEB 1, and if they are different, we copy + * LEB 0 to LEB 1. + * b. if LEB 0 is corrupted, but LEB 1 is OK, we copy LEB 1 to LEB 0. + */ + + dbg_vtbl("check the layout volume"); + + /* Read both LEB 0 and LEB 1 into RAM */ + rb_for_each_entry(rb, seb, &sv->root, rb) { + cond_resched(); + + leb[seb->lnum] = ubi_kzalloc(vtbl->vt_size); + if (!leb[seb->lnum]) { + err = -ENOMEM; + goto out_free; + } + + err = ubi_io_read_data(ubi, leb[seb->lnum], seb->pnum, 0, + vtbl->vt_size, &read); + /* FIXME: if there is a bitflip, we could try to move it */ + if (err == UBI_IO_BITFLIPS) + seb->scrub = 1; + else if (err) + goto out_free; + } + + if (leb[0]) + leb_corrupted[0] = vol_tbl_check(ubi, leb[0]); + + if (leb_corrupted[0] == 0) { + /* LEB 0 is OK */ + + if (leb[1]) + leb_corrupted[1] = memcmp(leb[0], leb[1], + vtbl->vt_size); + if (leb_corrupted[1]) { + ubi_warn("the volume table copy #2 is corrupted"); + err = create_vtbl(ubi, si, 1, leb[0]); + if (err) + goto out_free; + } + + /* Both LEB 1 and LEB 2 are OK and consistent */ + ubi_kfree(leb[1]); + return leb[0]; + } else { + /* LEB 0 is corrupted or does not exist */ + if (leb[1]) + leb_corrupted[1] = vol_tbl_check(ubi, leb[1]); + if (leb_corrupted[1]) { + /* + * Both LEB 0 and LEB 1 are corrupted. We don't try to + * restore them and let userspace tools do this. + */ + ubi_err("the layout volume is corrupted"); + err = -EINVAL; + goto out_free; + } + + ubi_warn("the volume table copy #1 is corrupted"); + err = create_vtbl(ubi, si, 0, leb[1]); + if (err) + goto out_free; + + ubi_kfree(leb[0]); + return leb[1]; + } + +out_free: + ubi_kfree(leb[0]); + ubi_kfree(leb[1]); + return ERR_PTR(err); +} + +/** + * create_vtbl - create a copy of the volume table. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * @copy: the number of the volume table copy + * @vol_tbl: the contents of the volume table + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int __init create_vtbl(const struct ubi_info *ubi, + struct ubi_scan_info *si, int copy, void *vol_tbl) +{ + int written, err, tries = 0, pnum, ec; + unsigned int leb_ver; + struct ubi_vtbl_info *vtbl = ubi->vtbl; + static struct ubi_vid_hdr *vid_hdr; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *new_seb, *old_seb = NULL; + + ubi_msg("create volume table (copy #%d)", copy + 1); + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) + return -ENOMEM; + + /* + * First we look if there is a logical eraseblock which would have to + * contain this volume table copy was found during scanning. We have + * to wipe it. + */ + sv = ubi_scan_get_scan_volume(si, UBI_LAYOUT_VOL_ID); + if (sv) + old_seb = ubi_scan_get_scan_leb(sv, copy); + +retry: + new_seb = ubi_scan_get_free_peb(ubi, si); + if (IS_ERR(new_seb)) { + err = PTR_ERR(new_seb); + goto out_free; + } + pnum = new_seb->pnum; + ec = new_seb->ec; + ubi_free_scan_leb(new_seb); + + vid_hdr->vol_type = UBI_VID_DYNAMIC; + vid_hdr->vol_id = cpu_to_ubi32(UBI_LAYOUT_VOL_ID); + vid_hdr->compat = UBI_LAYOUT_VOLUME_COMPAT; + vid_hdr->data_size = vid_hdr->used_ebs = + vid_hdr->data_pad = cpu_to_ubi32(0); + vid_hdr->lnum = cpu_to_ubi32(copy); + leb_ver = old_seb ? old_seb->leb_ver + 1: 0; + vid_hdr->leb_ver = cpu_to_ubi32(leb_ver); + + /* The EC header is already there, write the VID header */ + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (err) + goto write_error; + + /* Write the layout volume contents */ + err = ubi_io_write_data(ubi, vol_tbl, pnum, 0, vtbl->vt_size, &written); + if (err) + goto write_error; + + /* + * And add it to the scanning information. Don't delete the old + * @old_seb as it will be deleted and freed in + * 'ubi_scan_add_volume()'. + */ + err = ubi_scan_add_volume(ubi, si, pnum, ec, vid_hdr, 0); + if (err) + goto out_free; + +out_free: + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +write_error: + /* May be this physical eraseblock went bad, try to pick another one */ + if (++tries <= 5) { + err = ubi_scan_add_to_corrupted(si, pnum, ec); + if (err) + goto out_free; + goto retry; + } + ubi_free_vid_hdr(ubi, vid_hdr); + return err; +} + +/** + * init_ram_vt - initialize the in-RAM copy of the volume table. + * + * @ubi: the UBI device description object + * @vol_tbl: the volume table + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int __init init_ram_vt(const struct ubi_info *ubi, + const struct ubi_vol_tbl_record *vol_tbl) +{ + int i; + struct ubi_vtbl_info *vtbl = ubi->vtbl; + + vtbl->vt = ubi_kzalloc(vtbl->vt_slots * sizeof(struct ubi_vtbl_vtr)); + if (!vtbl->vt) + return -ENOMEM; + + for (i = 0; i < vtbl->vt_slots; i++) { + struct ubi_vtbl_vtr *vtr = &vtbl->vt[i]; + int name_len; + char *name; + + cond_resched(); + + vtr->reserved_pebs = ubi32_to_cpu(vol_tbl[i].reserved_pebs); + + /* Skip empty records */ + if (vtr->reserved_pebs == 0) + continue; + + vtr->alignment = ubi32_to_cpu(vol_tbl[i].alignment); + vtr->data_pad = ubi32_to_cpu(vol_tbl[i].data_pad); + vtr->vol_type = vol_tbl[i].vol_type == UBI_VID_DYNAMIC ? + UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; + name_len = ubi16_to_cpu(vol_tbl[i].name_len); + vtr->name_len = name_len; + vtr->usable_leb_size = ubi->io->leb_size - vtr->data_pad; + + vtr->name = ubi_kmalloc(name_len + 1); + if (unlikely(!vtr->name)) { + free_volume_info(ubi); + return -ENOMEM; + } + + name = (char *)vtr->name; + memcpy(name, vol_tbl[i].name, name_len + 1); + name[name_len] = '\0'; + } + + return 0; +} + +/** + * free_volume_info - free the in-RAM copy of the volume table. + * + * @ubi: the UBI device description object + */ +static void __exit free_volume_info(const struct ubi_info *ubi) +{ + int i; + + for (i = 0; i < ubi->vtbl->vt_slots; i++) + ubi_kfree(ubi->vtbl->vt[i].name); + + ubi_kfree(ubi->vtbl->vt); +} + +/** + * vol_tbl_check - check if the volume table is not corrupted and contains sane + * data. + * + * @ubi: the UBI device description object + * @vol_tbl: the volume table + * + * This function returns zero if the volume table is all right and %-EINVAL if + * not. + */ +static int vol_tbl_check(const struct ubi_info *ubi, + const struct ubi_vol_tbl_record *vol_tbl) +{ + int i, reserved_pebs, alignment, data_pad, vol_type, name_len; + const char *name; + const struct ubi_vtbl_info *vtbl = ubi->vtbl; + const struct ubi_io_info *io = ubi->io; + + for (i = 0; i < vtbl->vt_slots; i++) { + int n; + uint32_t crc; + + cond_resched(); + + reserved_pebs = ubi32_to_cpu(vol_tbl[i].reserved_pebs); + alignment = ubi32_to_cpu(vol_tbl[i].alignment); + data_pad = ubi32_to_cpu(vol_tbl[i].data_pad); + vol_type = vol_tbl[i].vol_type; + name_len = ubi16_to_cpu(vol_tbl[i].name_len); + name = &vol_tbl[i].name[0]; + + crc = crc32(UBI_CRC32_INIT, &vol_tbl[i], + UBI_VTBL_RECORD_SIZE_CRC); + + if (unlikely(ubi32_to_cpu(vol_tbl[i].crc) != crc)) { + ubi_err("wrong CRC at record %u: %#08x, not %#08x", + i, crc, ubi32_to_cpu(vol_tbl[i].crc)); + return -EINVAL; + } + + if (reserved_pebs == 0) { + int is_zero; + + is_zero = ubi_buf_all_zeroes(&vol_tbl[i], + UBI_VTBL_RECORD_SIZE_CRC); + if (unlikely(is_zero == 0)) { + dbg_err("zero reserved_pebs"); + goto bad; + } + + continue; + } + + if (unlikely(reserved_pebs < 0 || alignment < 0 || + data_pad < 0 || name_len < 0)) { + dbg_err("negative values"); + goto bad; + } + + if (unlikely(alignment > io->leb_size)) { + dbg_err("too large alignment"); + goto bad; + } + + if (unlikely(alignment == 0)) { + dbg_err("zero alignment"); + goto bad; + } + + n = alignment % io->min_io_size; + if (alignment != 1 && unlikely(n)) { + dbg_err("alignment is not multiple of min I/O unit" + "size"); + goto bad; + } + + n = io->leb_size % alignment; + if (unlikely(data_pad != n)) { + dbg_err("bad data_pad, has to be %d", n); + goto bad; + } + + if (likely(vol_type != UBI_VID_DYNAMIC && + vol_type != UBI_VID_STATIC)) { + dbg_err("bad vol_type"); + goto bad; + } + + if (unlikely(reserved_pebs > io->good_peb_count)) { + dbg_err("too large reserved_pebs"); + goto bad; + } + + if (unlikely(name_len > UBI_VOL_NAME_MAX)) { + dbg_err("too long volume name, max is %d", + UBI_VOL_NAME_MAX); + goto bad; + } + + if (unlikely(name[0] == '\0')) { + dbg_err("NULL volume name"); + goto bad; + } + + n = strnlen(name, name_len + 1); + if (unlikely(name_len != n)) { + dbg_err("bad name_len"); + goto bad; + } + } + + return 0; + +bad: + ubi_err("volume table check failed"); + dbg_err("volume record %d dump:", i); + ubi_dbg_dump_raw_vtr(&vol_tbl[i]); + return -EINVAL; +} + +static int check_sv(const struct ubi_info *ubi, + const struct ubi_scan_volume *sv, + const struct ubi_vtbl_vtr *vtr); + +/** + * check_scanning_info - check that scanning information is consistent to the + * information from the volume table. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * + * Even though we protect on-flash data by CRC checksums, we still don't trust + * the media. Who knows what users are trying to feed us. + * + * This function returns zero if the scanning information is sane and %-EINVAL + * if it is not. + */ +static int __init check_scanning_info(const struct ubi_info *ubi, + struct ubi_scan_info *si) +{ + int err, i; + const struct ubi_vtbl_vtr *vtr; + struct ubi_scan_volume *sv; + const struct ubi_vtbl_info *vtbl = ubi->vtbl; + + for (i = 0; i < vtbl->vt_slots; i++) { + cond_resched(); + + vtr = &vtbl->vt[i]; + sv = ubi_scan_get_scan_volume(si, i); + + if (vtr->reserved_pebs == 0) { + if (likely(!sv)) + continue; + + /* + * The scanning unit has found a volume which does not + * exist according to the information in the volume + * table. This must have happened due to an unclean + * reboot while the volume was being removed, or if the + * eraseblocks of a removed volumes were not erased. + * Get rid of this volume. + */ + dbg_vtbl("volume %d removal was interrupted, finish it", + sv->vol_id); + ubi_scan_rm_volume(ubi, si, sv); + continue; + } + + if (!sv) + continue; + + err = check_sv(ubi, sv, vtr); + if (unlikely(err)) + goto out; + } + + /* Check that scanning information about internal UBI volumes is sane */ + for (i = 0; i < UBI_INT_VOL_COUNT; i++) { + cond_resched(); + + vtr = ubi_ivol_get_vtr(ubi, i + UBI_INTERNAL_VOL_START); + ubi_assert(!IS_ERR(vtr)); + + sv = ubi_scan_get_scan_volume(si, i + UBI_INTERNAL_VOL_START); + + /* + * If an internal volume was not found, the corresponding + * UBI unit will handle this. + */ + if (!sv) + continue; + + err = check_sv(ubi, sv, vtr); + if (unlikely(err)) + goto out; + } + + return 0; + +out: + return -EINVAL; +} + +/** + * check_sv - check sanity of scanning information about a volume. + * + * @ubi: the UBI device description object + * @sv: volume scanning information + * @vtr: corresponding volume table record (supposed to be correct) + * + * This function returns zero if the volume scanning information is sane, and + * %-EINVAL if not. + */ +static int check_sv(const struct ubi_info *ubi, + const struct ubi_scan_volume *sv, + const struct ubi_vtbl_vtr *vtr) +{ + if (unlikely(sv->highest_lnum >= vtr->reserved_pebs)) { + dbg_err("bad highest_lnum"); + goto bad; + } + + if (unlikely(sv->leb_count > vtr->reserved_pebs)) { + dbg_err("bad leb_count"); + goto bad; + } + + if (unlikely(sv->vol_type != vtr->vol_type)) { + dbg_err("bad vol_type"); + goto bad; + } + + if (unlikely(sv->used_ebs > vtr->reserved_pebs)) { + dbg_err("bad used_ebs"); + goto bad; + } + + if (unlikely(sv->data_pad != vtr->data_pad)) { + dbg_err("bad data_pad"); + goto bad; + } + + return 0; + +bad: + ubi_err("scanning information is not consistent to volume table"); + ubi_dbg_dump_sv(sv); + ubi_dbg_dump_vtr(vtr); + return -EINVAL; +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_VTBL + +/** + * paranoid_check_vtr - check a &struct ubi_vtbl_vtr object. + * + * @ubi: the UBI device description object + * @vtr: the object pointer to check + * + * This function returns zero if the volume table record is sane, and %1 if + * not. + */ +static int paranoid_check_vtr(const struct ubi_info *ubi, + const struct ubi_vtbl_vtr *vtr) +{ + int n; + const struct ubi_io_info *io = ubi->io; + + if (vtr->reserved_pebs == 0) + return 0; + + if (unlikely(vtr->reserved_pebs < 0 || vtr->alignment < 0 || + vtr->data_pad < 0 || vtr->name_len < 0)) { + dbg_err("negative values"); + goto fail; + } + + if (unlikely(vtr->alignment > io->leb_size)) { + dbg_err("too large alignment %d", vtr->alignment); + goto fail; + } + + if (unlikely(vtr->alignment == 0)) { + dbg_err("zero alignment"); + goto fail; + } + + n = vtr->alignment % io->min_io_size; + if (vtr->alignment != 1 && unlikely(n)) { + dbg_err("alignment %d is not multiple of min I/O unit size", + vtr->alignment); + goto fail; + } + + n = io->leb_size % vtr->alignment; + if (unlikely(vtr->data_pad != n)) { + dbg_err("bad data_pad %d, has to be %d", vtr->data_pad, n); + goto fail; + } + + if (unlikely(vtr->vol_type != UBI_DYNAMIC_VOLUME && + vtr->vol_type != UBI_STATIC_VOLUME)) { + dbg_err("bad vol_type %d", vtr->vol_type); + goto fail; + } + + if (unlikely(vtr->reserved_pebs > io->good_peb_count)) { + dbg_err("too large reserved_pebs %d", vtr->reserved_pebs); + goto fail; + } + + if (unlikely(vtr->usable_leb_size != io->leb_size - vtr->data_pad)) { + dbg_err("bad usable_leb_size %d, has to be %d", + vtr->usable_leb_size, io->leb_size - vtr->data_pad); + goto fail; + } + + if (unlikely(vtr->name_len > UBI_VOL_NAME_MAX)) { + dbg_err("too long volume name %d, max is %d", + vtr->name_len, UBI_VOL_NAME_MAX); + goto fail; + } + + if (unlikely(!vtr->name)) { + dbg_err("NULL volume name"); + goto fail; + } + + n = strnlen(vtr->name, vtr->name_len + 1); + if (unlikely(n != vtr->name_len)) { + dbg_err("bad name_len %d", vtr->name_len); + goto fail; + } + + return 0; + +fail: + ubi_err("paranoid check failed"); + ubi_dbg_dump_vtr(vtr); + dump_stack(); + return 1; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID_VTBL */ diff --git a/drivers/mtd/ubi/vtbl.h b/drivers/mtd/ubi/vtbl.h new file mode 100644 index 0000000..9267847 --- /dev/null +++ b/drivers/mtd/ubi/vtbl.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +/* + * The volume table unit. + * + * This unit is responsible for maintaining the volume table. The volume table + * is an on-flash table containing volume metadata like volume name, number of + * reserved physical eraseblocks, type, etc. The volume table is stored in the + * so-called "layout volume". + * + * The layout volume is an internal volume where the volume tables are stored. + * It it organized as follows. It consists of two logical eraseblocks - LEB 0 + * and LEB 1. Each logical eraseblock stores a copy the volume table, i.e. LEB + * 0 and LEB 1 duplicate each other. This redundancy guarantees robustness and + * tolerance to unclean reboots. The volume table is a mere array of so-called + * "volume table records". Each record contains full information about the + * volume and is protected by a CRC checksum. + * + * The volume table is changed as follows. It is first changed in RAM. Then LEB + * 0 is erased, and the updated volume table is written back to LEB 0. The same + * is done with LEB 1. This scheme guarantees recoverability from unclean + * reboots. + * + * NOTE: this unit does not do any serialization and it is supposed that the + * serialization is provided by its users. This means that it is prohibited to + * change a volume while somebody else is working with it. + */ + +#ifndef __UBI_VTBL_H__ +#define __UBI_VTBL_H__ + +#include + +struct ubi_info; +struct ubi_scan_info; +struct ubi_vtbl_vtr; + +/** + * ubi_vtbl_mkvol - create volume table record for a new volume. + * + * @ubi: the UBI device description object + * @vol_id: ID of the new volume + * @vtr: volume table record of the new volume + * + * This function returns zero in case of success and a negative error code in + * case of failure. The @vtr->usable_leb_size field is ignored. + */ +int ubi_vtbl_mkvol(const struct ubi_info *ubi, int vol_id, + const struct ubi_vtbl_vtr *vtr); + +/** + * ubi_vtbl_rmvol - clear the volume table record of a volume. + * + * @ubi: the UBI device description object + * @vol_id: ID of the volume to remove + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_vtbl_rmvol(const struct ubi_info *ubi, int vol_id); + +/** + * ubi_vtbl_rsvol - change volume size in the volume table record. + * + * @ubi: the UBI device description object + * @vol_id: re-sized volume's ID + * @reserved_pebs: new size, i.e. new number of reserved eraseblocks. + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_vtbl_rsvol(const struct ubi_info *ubi, int vol_id, int reserved_pebs); + +/** + * ubi_vtbl_get_vtr - retrieve a volume table record. + * + * @ubi: the UBI device description object + * @vol_id: the requested volume ID + * + * This function returns a pointer to the volume record or an error code. + * If the volume ID is incorrect, %-EINVAL is returned, if the volume does + * not exist, %-ENODEV is returned. + * + * This function does not access the flash media as retrieves the information + * from the in-RAM volume table copy. So it does not sleep. + */ +const struct ubi_vtbl_vtr *ubi_vtbl_get_vtr(const struct ubi_info *ubi, + int vol_id); + +/** + * ubi_vtbl_init_scan - initialize the volume table unit using scanning + * information. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int __init ubi_vtbl_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si); + +/** + * ubi_vtbl_close - close the volume table unit. + * + * @ubi: the UBI device description object + */ +void __exit ubi_vtbl_close(const struct ubi_info *ubi); + +/** + * struct ubi_vtbl_vtr - in-memory representation of volume table records. + * + * @reserved_pebs: how many physical eraseblocks are reserved for this volume + * @alignment: volume alignment + * @data_pad: how many bytes are not used at the end of eraseblocks to + * satisfy the requested alignment + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @name_len: volume name length + * @name: volume name + * @usable_leb_size: logical eraseblock size without padding + * + * Note, the @usable_leb_size field is not stored on flash, as it is easily + * calculated with help of the @data_pad field. But it is just very handy, so + * we keep it in the in-RAM volume table record representation. + */ +struct ubi_vtbl_vtr { + int reserved_pebs; + int alignment; + int data_pad; + int vol_type; + int name_len; + const char *name; + int usable_leb_size; +}; + +/** + * struct ubi_vtbl_info - volume table unit description data structure. + * + * @vt_slots: how many volume table records are stored in the volume table + * @vt_size: size of the volume table in bytes + * @vt: the in-RAM copy of the volume table + */ +struct ubi_vtbl_info { + int vt_slots; /* public */ + int vt_size; /* private */ + struct ubi_vtbl_vtr *vt; /* private */ +}; + +#endif /* __UBI_VTBL_H__ */ diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c new file mode 100644 index 0000000..4baf8d8 --- /dev/null +++ b/drivers/mtd/ubi/wl.c @@ -0,0 +1,1717 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Artem B. Bityutskiy, Thomas Gleixner + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ubi.h" +#include "alloc.h" +#include "wl.h" +#include "badeb.h" +#include "io.h" +#include "account.h" +#include "eba.h" +#include "background.h" +#include "scan.h" +#include "misc.h" +#include "debug.h" + +/* Number of physical eraseblocks reserved for wear-leveling purposes */ +#define WL_RESERVED_PEBS 1 + +/* + * How many erase cycles are short term, unknown, and long term physical + * eraseblocks protected. + */ +#define ST_PROTECTION 16 +#define U_PROTECTION 10 +#define LT_PROTECTION 4 + +/* + * Maximum difference between two erase counters. If this threshold is + * exceeded, the WL unit starts moving data from used physical eraseblocks with + * low erase counter to free physical eraseblocks with high erase counter. + */ +#define UBI_WL_THRESHOLD CONFIG_MTD_UBI_WL_THRESHOLD + +/* + * When a physical eraseblock is moved, the WL unit has to pick the target + * physical eraseblock to move to. The simplest way would be just to pick the + * one with the highest erase counter. But in certain workload this could lead + * to an unbounded wearing of one or few physical eraseblock. Indeed, imagine a + * situation when the picked physical eraseblock is constantly erased after the + * data is written to it. So, we have a constant which limits the highest + * erase counter of the free physical eraseblock to pick. Namely, the WL unit + * does not pick eraseblocks with erase counter greater then the lowest erase + * counter plus %WL_FREE_MAX_DIFF. + */ +#define WL_FREE_MAX_DIFF (2*UBI_WL_THRESHOLD) + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_WL +static int paranoid_check_ec(const struct ubi_info *ubi, int pnum, int ec); +static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root); +#else +#define paranoid_check_ec(ubi, pnum, ec) 0 +#define paranoid_check_in_wl_tree(e, root) +#endif + +/** + * tree_empty - a helper function to check if an RB-tree is empty. + * + * @root: the root of the tree + * + * This function returns non-zero if the tree is empty and zero if not. + */ +static inline int tree_empty(struct rb_root *root) +{ + return root->rb_node == NULL; +} + +static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root); + +/* Functions to add and delete wear-leveling entries from different trees */ + +static inline void free_tree_add(struct ubi_wl_info *wl, + struct ubi_wl_entry *e) +{ + wl_tree_add(e, &wl->free); +} +static inline void used_tree_add(struct ubi_wl_info *wl, + struct ubi_wl_entry *e) +{ + wl_tree_add(e, &wl->used); +} +static inline void scrub_tree_add(struct ubi_wl_info *wl, + struct ubi_wl_entry *e) +{ + wl_tree_add(e, &wl->scrub); +} + +static inline void free_tree_del(struct ubi_wl_info *wl, + struct ubi_wl_entry *e) +{ + paranoid_check_in_wl_tree(e, &wl->free); + rb_erase(&e->rb, &wl->free); +} +static inline void used_tree_del(struct ubi_wl_info *wl, + struct ubi_wl_entry *e) +{ + paranoid_check_in_wl_tree(e, &wl->used); + rb_erase(&e->rb, &wl->used); +} +static inline void scrub_tree_del(struct ubi_wl_info *wl, + struct ubi_wl_entry *e) +{ + paranoid_check_in_wl_tree(e, &wl->scrub); + rb_erase(&e->rb, &wl->scrub); +} + +static int erase_one_pending(const struct ubi_info *ubi); +static struct ubi_wl_entry *pick_long_term(struct ubi_wl_info *wl); +static struct ubi_wl_entry *pick_unknown(struct ubi_wl_info *wl); +static struct ubi_wl_entry *pick_short_term(struct ubi_wl_info *wl); +static void prot_tree_add(struct ubi_wl_info *wl, struct ubi_wl_entry *e, + struct ubi_wl_prot_entry *pe, int abs_ec); + +int ubi_wl_get_peb(const struct ubi_info *ubi, enum ubi_data_type dtype) +{ + int err, protect; + struct ubi_wl_entry *e; + struct ubi_wl_info *wl = ubi->wl; + struct ubi_wl_prot_entry *pe; + + might_sleep(); + + /* Input arguments sanity check */ + ubi_assert(dtype == UBI_DATA_LONGTERM || dtype == UBI_DATA_SHORTTERM || + dtype == UBI_DATA_UNKNOWN); + + pe = ubi_alloc_wl_prot_entry(); + if (unlikely(!pe)) + return -ENOMEM; + +retry: + spin_lock(&wl->lock); + if (unlikely(tree_empty(&wl->free))) { + if (unlikely(wl->erase_pending == 0)) { + ubi_err("no free eraseblocks"); + spin_unlock(&wl->lock); + ubi_free_wl_prot_entry(pe); + return -ENOSPC; + } + spin_unlock(&wl->lock); + + err = erase_one_pending(ubi); + if (unlikely(err < 0)) { + ubi_free_wl_prot_entry(pe); + return err; + } + goto retry; + } + + switch (dtype) { + case UBI_DATA_LONGTERM: + e = pick_long_term(wl); + protect = LT_PROTECTION; + break; + case UBI_DATA_UNKNOWN: + e = pick_unknown(wl); + protect = U_PROTECTION; + break; + case UBI_DATA_SHORTTERM: + e = pick_short_term(wl); + protect = ST_PROTECTION; + break; + default: + protect = 0; + e = NULL; + BUG(); + } + + /* + * Move the physical eraseblock to the protection trees where it will + * be protected from being moved for some time. + */ + free_tree_del(wl, e); + prot_tree_add(wl, e, pe, protect); + + dbg_wl("PEB %d EC %d, protection %d", e->pnum, e->ec, protect); + spin_unlock(&wl->lock); + + return e->pnum; +} + +static int in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root); +static int schedule_erase(const struct ubi_info *ubi, struct ubi_wl_entry *e, + int torture); +static void check_protection_over(struct ubi_wl_info *wl); +static void prot_tree_del(struct ubi_wl_info *wl, int pnum); + +int ubi_wl_put_peb(const struct ubi_info *ubi, int pnum, int torture) +{ + int err; + struct ubi_wl_entry *e; + struct ubi_wl_info *wl = ubi->wl; + + dbg_wl("PEB %d", pnum); + might_sleep(); + + /* Input arguments sanity check */ + ubi_assert(pnum >= 0); + ubi_assert(pnum < ubi->io->peb_count); + + spin_lock(&wl->lock); + ubi_assert(wl->erase_pending >= 0); + wl->erase_pending += 1; + + e = wl->lookuptbl[pnum]; + if (unlikely(e == wl->move)) { + /* + * User is putting a physical eraseblock which was selected to + * be moved. We cancel the movement by setting @wl->move to + * %NULL. The wear-leveling worker has to notice this and + * cancel. + * + * Note, the physical eraseblock was removed from the @wl->used + * tree by the wear-leveling worker and is not in any tree now. + */ + dbg_wl("cancel PEB %d movement", pnum); + wl->move = NULL; + } else { + if (in_wl_tree(e, &wl->used)) + used_tree_del(wl, e); + else if (unlikely(in_wl_tree(e, &wl->scrub))) + scrub_tree_del(wl, e); + else + prot_tree_del(wl, e->pnum); + } + spin_unlock(&wl->lock); + + err = schedule_erase(ubi, e, torture); + if (unlikely(err)) { + spin_lock(&wl->lock); + wl->erase_pending -= 1; + used_tree_add(wl, e); + spin_unlock(&wl->lock); + } + + return err; +} + +static int ensure_wear_leveling(const struct ubi_info *ubi); + +int ubi_wl_scrub_peb(const struct ubi_info *ubi, int pnum) +{ + struct ubi_wl_entry *e; + struct ubi_wl_info *wl = ubi->wl; + + dbg_wl("schedule PEB %d for scrubbing", pnum); + + spin_lock(&wl->lock); + e = wl->lookuptbl[pnum]; + if (e == wl->move || in_wl_tree(e, &wl->scrub)) { + spin_unlock(&wl->lock); + return 0; + } + + if (in_wl_tree(e, &wl->used)) + used_tree_del(wl, e); + else + prot_tree_del(wl, pnum); + + scrub_tree_add(wl, e); + spin_unlock(&wl->lock); + + /* + * Technically scrubbing is the same as wear-levelling, so it is done + * by the WL worker. Schedule it. + */ + return ensure_wear_leveling(ubi); +} + +static int erase_worker(const struct ubi_info *ubi, struct ubi_bgt_work *wrk, + int cancel); + +int ubi_wl_erase_flush(const struct ubi_info *ubi) +{ + int err, pending_count; + struct ubi_bgt_work *wrk; + const struct ubi_bgt_info *bgt = ubi->bgt; + + pending_count = bgt->pending_works_count; + + dbg_wl("flush (%d pending works)", pending_count); + + /* + * Erase while the pending works queue is not empty, but not more then + * the number of currently pending works. + */ + while (pending_count != 0 && (wrk = ubi_bgt_next_work(ubi))) { + pending_count -= 1; + + if (wrk->func != &erase_worker) { + /* We are only interested in "erase" works" */ + err = ubi_bgt_reschedule(ubi, wrk); + if (unlikely(err)) { + /* + * The background thread was killed. Cancel + * this work. We cannot really operate without + * the background thread, so switch to + * read-only mode. + */ + wrk->func(ubi, wrk, 1); + ubi_eba_ro_mode(ubi); + return err; + } + } else { + err = wrk->func(ubi, wrk, 0); + if (unlikely(err)) + return err; + } + } + + return 0; +} + +static void __exit tree_destroy(struct rb_root *root); + +int __init ubi_wl_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si) +{ + int err; + struct rb_node *rb1, *rb2; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb, *tmp; + struct ubi_wl_entry *e; + struct ubi_wl_info *wl; + const struct ubi_io_info *io = ubi->io; + + dbg_wl("initialize the UBI wear-leveling unit"); + + wl = ubi_kzalloc(sizeof(struct ubi_wl_info)); + if (!wl) + return -ENOMEM; + ubi->wl = wl; + + wl->used = wl->free = wl->scrub = RB_ROOT; + wl->prot.pnum = wl->prot.aec = RB_ROOT; + spin_lock_init(&wl->lock); + wl->max_ec = si->max_ec; + + err = -ENOMEM; + wl->lookuptbl = ubi_kzalloc(io->peb_count * sizeof(void *)); + if (!wl->lookuptbl) + goto out_free; + + /* + * The way how we distinguish between older LEB and newer LEB is based + * on the following principles: + * 1 if we have LEB with versions A and B, A < B, then B is newer then + * A when abs(B - A) < %0x7FFFFFFF + * 2 as the WL unit guarantees that the length of the pending works + * queue is shorter then %0x7FFFFFFF works, and the works are put at + * the tail of the queue and got from its head, the above algorithm + * works correctly. + * + * Now we've got a list of eraseblocks to erase, and they are now + * out-of-order, which does not satisfy the 2nd item, so we've got to + * erase them now instead of deferring this. + */ + list_for_each_entry_safe(seb, tmp, &si->erase, list) { + cond_resched(); + + dbg_wl("erase PEB %d", seb->pnum); + err = ubi_scan_early_erase_peb(ubi, si, seb->pnum, seb->ec); + if (unlikely(err)) { + if (err != -EIO && err != -EROFS) + goto out_free; + list_del(&seb->list); + list_add_tail(&seb->list, &si->corr); + } else { + list_del(&seb->list); + list_add_tail(&seb->list, &si->free); + seb->ec += 1; + } + } + + list_for_each_entry(seb, &si->free, list) { + cond_resched(); + + e = ubi_alloc_wl_entry(); + if (unlikely(!e)) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi_assert(e->ec >= 0); + free_tree_add(wl, e); + wl->lookuptbl[e->pnum] = e; + } + + list_for_each_entry(seb, &si->corr, list) { + cond_resched(); + + e = ubi_alloc_wl_entry(); + if (unlikely(!e)) { + err = -ENOMEM; + goto out_free; + } + + e->pnum = seb->pnum; + e->ec = seb->ec; + wl->lookuptbl[e->pnum] = e; + wl->erase_pending += 1; + err = schedule_erase(ubi, e, 0); + if (unlikely(err)) { + ubi_free_wl_entry(e); + goto out_free; + } + } + + rb_for_each_entry(rb1, sv, &si->volumes, rb) { + rb_for_each_entry(rb2, seb, &sv->root, rb) { + cond_resched(); + + e = ubi_alloc_wl_entry(); + if (unlikely(!e)) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + wl->lookuptbl[e->pnum] = e; + if (!seb->scrub) { + dbg_wl("add PEB %d EC %d to the used tree", + e->pnum, e->ec); + used_tree_add(wl, e); + } else { + dbg_wl("add PEB %d EC %d to the scrub tree", + e->pnum, e->ec); + scrub_tree_add(wl, e); + } + } + } + + err = ubi_acc_reserve(ubi, WL_RESERVED_PEBS); + if (err) + goto out_free; + + /* Schedule wear-leveling if needed */ + err = ensure_wear_leveling(ubi); + if (err) + goto out_free; + + return 0; + +out_free: + tree_destroy(&wl->used); + tree_destroy(&wl->free); + tree_destroy(&wl->scrub); + ubi_kfree(wl->lookuptbl); + ubi_kfree(wl); + return err; +} + +static void __exit protection_trees_destroy(struct ubi_wl_info *wl); + +void __exit ubi_wl_close(struct ubi_info *ubi) +{ + struct ubi_wl_info *wl = ubi->wl; + + dbg_wl("close the UBI wear-leveling unit"); + + protection_trees_destroy(wl); + tree_destroy(&wl->used); + tree_destroy(&wl->free); + tree_destroy(&wl->scrub); + ubi_kfree(wl->lookuptbl); + ubi_kfree(wl); +} + +/** + * find_wl_entry - find a wl entry closest to certain erase counter. + * + * @root: the RB-tree where to look for + * @max: highest erase possible counter + * + * This function looks for a wear leveling entry erase counter closest to @max + * and less then @max. + */ +static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int max) +{ + struct rb_node *p; + struct ubi_wl_entry *e; + + e = rb_entry(rb_first(root), struct ubi_wl_entry, rb); + max += e->ec; + + p = root->rb_node; + while (p) { + struct ubi_wl_entry *e1; + + e1 = rb_entry(p, struct ubi_wl_entry, rb); + if (e1->ec >= max) + p = p->rb_left; + else { + p = p->rb_right; + e = e1; + } + } + + return e; +} + +/** + * pick_long_term - select a "long-term" physical eraseblock. + * + * @wl: the wear-leveling unit description data structure + * + * This function returns the requested physical eraseblock. The wl->lock must + * be locked. The @wl->free list must not be empty. + */ +static struct ubi_wl_entry *pick_long_term(struct ubi_wl_info *wl) +{ + struct ubi_wl_entry *e; + + /* + * For long term data we pick a physical eraseblock with high erase + * counter. But the highest erase counter we can pick is bounded by + * the the lowest erase counter plus %WL_FREE_MAX_DIFF. + */ + e = find_wl_entry(&wl->free, WL_FREE_MAX_DIFF); + return e; +} + +/** + * pick_unknown - select an "unknown" physical eraseblock. + * + * @wl: the wear-leveling unit description data structure + * + * This function returns the requested physical eraseblock. The wl->lock must + * be locked. The @wl->free list must not be empty. + */ +static struct ubi_wl_entry *pick_unknown(struct ubi_wl_info *wl) +{ + int medium_ec; + struct rb_node *p; + struct ubi_wl_entry *first, *last, *e; + + /* + * For unknown data we are trying to pick a physical eraseblock with + * medium erase counter. But we by no means can pick a physical + * eraseblock with erase counter greater or equivalent then the the + * lowest erase counter plus %WL_FREE_MAX_DIFF. + */ + + first = rb_entry(rb_first(&wl->free), struct ubi_wl_entry, rb); + last = rb_entry(rb_last(&wl->free), struct ubi_wl_entry, rb); + + if (last->ec - first->ec < WL_FREE_MAX_DIFF) + return rb_entry(wl->free.rb_node, struct ubi_wl_entry, rb); + + medium_ec = (first->ec + WL_FREE_MAX_DIFF)/2; + e = first; + + p = wl->free.rb_node; + while (p) { + struct ubi_wl_entry *e1; + + e1 = rb_entry(p, struct ubi_wl_entry, rb); + if (e1->ec >= medium_ec) + p = p->rb_left; + else { + p = p->rb_right; + e = e1; + } + } + + return e; +} + +/** + * pick_short_term - select a "short term" physical eraseblock. + * + * @wl: the wear-leveling unit description data structure + * + * This function returns the requested physical eraseblock. The wl->lock must + * be locked. The @wl->free list must not be empty. + */ +static struct ubi_wl_entry *pick_short_term(struct ubi_wl_info *wl) +{ + struct ubi_wl_entry *e; + + /* + * For short term data we pick a physical eraseblock with the lowest + * erase counter as we expect it will be erased soon. + */ + e = rb_entry(rb_first(&wl->free), struct ubi_wl_entry, rb); + return e; +} + +/** + * prot_tree_add - add a the physical eraseblock to the protection trees. + * + * @wl: the wear-leveling unit description data structure + * @e: the physical eraseblock to add + * @pe: a protection entry object to use + * @abs_ec: the absolute erase counter value when this physical eraseblock has + * to be removed from the protection trees. + * + * @wl->lock has to be locked. + */ +static void prot_tree_add(struct ubi_wl_info *wl, struct ubi_wl_entry *e, + struct ubi_wl_prot_entry *pe, int abs_ec) +{ + struct rb_node **p, *parent = NULL; + struct ubi_wl_prot_entry *pe1; + + pe->e = e; + pe->abs_ec = wl->abs_ec + abs_ec; + + p = &wl->prot.pnum.rb_node; + while (*p) { + parent = *p; + pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_pnum); + + if (e->pnum < pe1->e->pnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&pe->rb_pnum, parent, p); + rb_insert_color(&pe->rb_pnum, &wl->prot.pnum); + + p = &wl->prot.aec.rb_node; + parent = NULL; + while (*p) { + parent = *p; + pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_aec); + + if (pe->abs_ec < pe1->abs_ec) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&pe->rb_aec, parent, p); + rb_insert_color(&pe->rb_aec, &wl->prot.aec); +} + +/** + * check_protection_over - check if it is time to stop protecting some + * physical eraseblocks. + * + * @wl: the wear-leveling unit description data structure + * + * This function is called after each erase operation, when the absolute erase + * counter is incremented, to check if some physical eraseblock have not to be + * protected any longer. These physical eraseblocks are moved from the + * protection trees to the used tree. + */ +static void check_protection_over(struct ubi_wl_info *wl) +{ + struct ubi_wl_prot_entry *pe; + + /* + * There may be several protected physical eraseblock to remove, + * process them all. + */ + while (1) { + spin_lock(&wl->lock); + if (tree_empty(&wl->prot.aec)) { + spin_unlock(&wl->lock); + break; + } + + pe = rb_entry(rb_first(&wl->prot.aec), + struct ubi_wl_prot_entry, rb_aec); + + if (pe->abs_ec > wl->abs_ec) { + spin_unlock(&wl->lock); + break; + } + + dbg_wl("PEB %d protection over, abs_ec %lld, PEB abs_ec %lld", + pe->e->pnum, wl->abs_ec, pe->abs_ec); + rb_erase(&pe->rb_aec, &wl->prot.aec); + rb_erase(&pe->rb_pnum, &wl->prot.pnum); + used_tree_add(wl, pe->e); + spin_unlock(&wl->lock); + + ubi_free_wl_prot_entry(pe); + cond_resched(); + } +} + +/** + * prot_tree_del - remove a physical eraseblock from the protection trees + * + * @wl: the wear-leveling unit description data structure + * @pnum: the physical eraseblock number to remove + */ +static void prot_tree_del(struct ubi_wl_info *wl, int pnum) +{ + struct rb_node *p; + struct ubi_wl_prot_entry *pe = NULL; + + p = wl->prot.pnum.rb_node; + while (p) { + + pe = rb_entry(p, struct ubi_wl_prot_entry, rb_pnum); + + if (pnum == pe->e->pnum) + break; + + if (pnum < pe->e->pnum) + p = p->rb_left; + else + p = p->rb_right; + } + + ubi_assert(pe->e->pnum == pnum); + rb_erase(&pe->rb_aec, &wl->prot.aec); + rb_erase(&pe->rb_pnum, &wl->prot.pnum); + ubi_free_wl_prot_entry(pe); +} + +static int wear_leveling_worker(const struct ubi_info *ubi, + struct ubi_bgt_work *wrk, int cancel); + +/** + * ensure_wear_leveling - schedule wear-leveling if it is needed. + * + * @ubi: the UBI device description object + * + * This function checks if it is time to start wear-leveling and schedules it + * if yes. This function returns zero in case of success and a negative error + * code in case of failure. + */ +static int ensure_wear_leveling(const struct ubi_info *ubi) +{ + int err = 0; + struct ubi_wl_entry *e1; + struct ubi_wl_entry *e2; + struct ubi_bgt_work *wrk; + struct ubi_wl_info *wl = ubi->wl; + + spin_lock(&wl->lock); + if (wl->wl_scheduled) + /* Wear-leveling is already in the work queue */ + goto out_unlock; + + /* + * If the wl->scrub tree is not empty, scrubbing is needed, and the the + * WL worker has to be scheduled anyway. + */ + if (tree_empty(&wl->scrub)) { + if (tree_empty(&wl->used) || tree_empty(&wl->free)) + /* No physical eraseblocks - no deal */ + goto out_unlock; + + /* + * We schedule wear-leveling only if the difference between the + * lowest erase counter of used physical eraseblocks and a high + * erase counter of free physical eraseblocks is greater then + * %UBI_WL_THRESHOLD. + */ + e1 = rb_entry(rb_first(&wl->used), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&wl->free, WL_FREE_MAX_DIFF); + + if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) + goto out_unlock; + dbg_wl("schedule wear-leveling"); + } else + dbg_wl("schedule scrubbing"); + + wl->wl_scheduled = 1; + spin_unlock(&wl->lock); + + wrk = ubi_alloc_bgt_work(); + if (unlikely(!wrk)) { + err = -ENOMEM; + goto out_cancel; + } + + wrk->func = &wear_leveling_worker; + err = ubi_bgt_schedule(ubi, wrk); + if (unlikely(err)) { + /* + * The background was thread is killed, don't clear the + * @wl->wl_scheduled flag to prevent this error from happening + * again and again. And switch to read-only mode. + */ + ubi_free_bgt_work(wrk); + ubi_eba_ro_mode(ubi); + } + return err; + +out_unlock: + spin_unlock(&wl->lock); + return err; + +out_cancel: + spin_lock(&wl->lock); + wl->wl_scheduled = 0; + spin_unlock(&wl->lock); + return err; +} + +/** + * schedule_erase - schedule an erase work. + * + * @ubi: the UBI device description object + * @e: the WL entry of the physical eraseblock to erase + * @torture: if the physical eraseblock has to be tortured + * + * This function returns zero in case of success and a negative error code in + * case of failure. + * + * Note: @wl->erase_pending must be incremented before this function is called. + */ +static int schedule_erase(const struct ubi_info *ubi, struct ubi_wl_entry *e, + int torture) +{ + int err; + struct ubi_wl_erase_work *wl_wrk; + + dbg_wl("schedule erasure of PEB %d, EC %d, torture %d", + e->pnum, e->ec, torture); + + wl_wrk = ubi_alloc_wl_erase_work(); + if (unlikely(!wl_wrk)) + return -ENOMEM; + + wl_wrk->wrk.func = &erase_worker; + wl_wrk->wrk.priv = wl_wrk; + wl_wrk->e = e; + wl_wrk->torture = torture; + + err = ubi_bgt_schedule(ubi, &wl_wrk->wrk); + if (unlikely(err)) { + /* + * The background thread was killed, but we really need it. We + * can only work in read-only mode without it. + */ + ubi_free_wl_erase_work(wl_wrk); + ubi_eba_ro_mode(ubi); + } + return err; +} + +static int sync_erase(const struct ubi_info *ubi, struct ubi_wl_entry *e, + int torture); + +/** + * erase_worker - physical eraseblock erase worker function. + * + * @ubi: the UBI device description object + * @wrk: the work object + * @cancel: non-zero if the worker has to free memory and exit + * + * This function returns zero in case of success and a negative error code in + * case of failure. This function also takes care about marking the physical + * eraseblock bad if it cannot be erased. + */ +static int erase_worker(const struct ubi_info *ubi, struct ubi_bgt_work *wrk, + int cancel) +{ + int err; + struct ubi_wl_info *wl = ubi->wl; + struct ubi_wl_erase_work *wl_wrk = wrk->priv; + struct ubi_wl_entry *e = wl_wrk->e; + int pnum = e->pnum; + + if (unlikely(cancel)) { + dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec); + ubi_free_wl_erase_work(wl_wrk); + ubi_free_wl_entry(e); + return 0; + } + + dbg_wl("erase PEB %d EC %d", pnum, e->ec); + + err = sync_erase(ubi, e, wl_wrk->torture); + if (likely(!err)) { + /* Fine, we've erased it successfully */ + ubi_free_wl_erase_work(wl_wrk); + + spin_lock(&wl->lock); + wl->erase_pending -= 1; + ubi_assert(wl->erase_pending >= 0); + wl->abs_ec += 1; + free_tree_add(wl, e); + spin_unlock(&wl->lock); + + /* + * One more erase operation has happened, take care about protected + * physical eraseblocks. + */ + check_protection_over(wl); + + /* And take care about wear-leveling */ + err = ensure_wear_leveling(ubi); + return err; + } + + /* + * Some error occurred during erasure. If this is something like + * %-EINTR, we just re-schedule this physical eraseblock for + * erasure. + */ + + if (err == -EINTR || err == -EAGAIN || err == -ENOMEM || + err == -EBUSY) { + ubi_bgt_reschedule(ubi, wrk); /* Must not return error */ + return err; + } + + ubi_free_wl_erase_work(wl_wrk); + ubi_free_wl_entry(e); + + spin_lock(&wl->lock); + wl->erase_pending -= 1; + spin_unlock(&wl->lock); + + /* + * If this is not %-EIO, we have no idea what to do. Scheduling + * this physical eraseblock for erasure again will cause repeated + * errors. + */ + if (err != -EIO) { + ubi_eba_ro_mode(ubi); + return err; + } + + /* It is %-EIO, the PEB went bad */ + + if (!ubi->io->bad_allowed) { + ubi_err("flash device may be severly bad"); + ubi_eba_ro_mode(ubi); + err = -EIO; + } else { + err = ubi_beb_mark_bad(ubi, pnum); + if (err) + ubi_eba_ro_mode(ubi); + } + return err; +} + +/** + * wear_leveling_worker - wear-leveling worker function. + * + * @ubi: the UBI device description object + * @wrk: the work object + * @cancel: non-zero if the worker has to free memory and exit + * + * This function copies a less worn out physical eraseblock to a more worn out + * one. Returns zero in case of success and a negative error code in case of + * failure. + */ +static int wear_leveling_worker(const struct ubi_info *ubi, + struct ubi_bgt_work *wrk, int cancel) +{ + int err, tmp, data_size, aldata_size, vol_id, lnum, scrub = 0; + struct ubi_wl_entry *e1, *e2; + struct ubi_vid_hdr *vid_hdr; + void *buf; + uint32_t crc; + struct ubi_wl_info *wl = ubi->wl; + struct ubi_wl_prot_entry *pe; + const struct ubi_io_info *io = ubi->io; + + ubi_free_bgt_work(wrk); + + if (unlikely(cancel)) + return 0; + + spin_lock(&wl->lock); + if (tree_empty(&wl->free) || + (tree_empty(&wl->used) && tree_empty(&wl->scrub))) { + /* + * No free physical eraseblocks? Well, we cancel wear-leveling + * then. It will be triggered again when a free physical + * eraseblock appears. + * + * No used physical eraseblocks? They must be temporarily + * protected from being moved. They will be moved to the + * @wl->used tree later and the wear-leveling will be + * triggered again. + */ + dbg_wl("cancel WL, a list is empty: free %d, used %d", + tree_empty(&wl->free), tree_empty(&wl->used)); + goto out; + } + + if (tree_empty(&wl->scrub)) { + /* + * Now pick the least worn-out used physical eraseblock and a + * highly worn-out free physical eraseblock. If the erase + * counters differ much enough, start wear-leveling. + */ + e1 = rb_entry(rb_first(&wl->used), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&wl->free, WL_FREE_MAX_DIFF); + + if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) { + dbg_wl("no WL needed: min used EC %d, max free EC %d", + e1->ec, e2->ec); + goto out; + } + used_tree_del(wl, e1); + dbg_wl("move PEB %d EC %d to PEB %d EC %d", + e1->pnum, e1->ec, e2->pnum, e2->ec); + } else { + scrub = 1; + e1 = rb_entry(rb_first(&wl->scrub), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&wl->free, WL_FREE_MAX_DIFF); + scrub_tree_del(wl, e1); + dbg_wl("scrub PEB %d to PEB %d", e1->pnum, e2->pnum); + } + + free_tree_del(wl, e2); + wl->move = e1; + spin_unlock(&wl->lock); + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (unlikely(!vid_hdr)) { + err = -ENOMEM; + goto out_err_cancel; + } + + /* + * Now we are going to copy physical eraseblock @e1->pnum to @e2->pnum. + * We've selected a victim (@e1) and @wl->move refers it. But, user may + * call 'ubi_wl_put_peb()' for @e1, and the movement has to be + * canceled. + * + * We so far do not know which logical eraseblock our physical + * eraseblock (@e1) belongs to. This means we cannot lock it. We have + * to read the volume identifier header first. But if @e1 is put, it is + * scheduled for erasure, and we may have a race with the erasure. + * So, we may easily get an I/O error when we read the VID header. + * This does not necessarily mean something nasty. + */ + + err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0); + if (unlikely(err) && err != UBI_IO_BITFLIPS) { + /* OK, error. If the movement was canceled, don't panic */ + spin_lock(&wl->lock); + if (!wl->move) { + spin_unlock(&wl->lock); + /* This physical eraseblock was put meanwhile */ + goto out_cancel_wl; + } + spin_unlock(&wl->lock); + /* Well, this means there is a problem */ + dbg_wl("VID hdr read error (%d)", err); + goto vid_hdr_read_error; + } + + if (vid_hdr->vol_type == UBI_VID_STATIC) { + data_size = ubi32_to_cpu(vid_hdr->data_size); + aldata_size = align_up(data_size, io->min_io_size); + } else + data_size = aldata_size = + io->leb_size - ubi32_to_cpu(vid_hdr->data_pad); + + ubi_assert(aldata_size % io->min_io_size == 0); + + buf = ubi_kmalloc(aldata_size); + if (unlikely(!buf)) { + err = -ENOMEM; + goto out_err_cancel_vid_hdr; + } + + vol_id = ubi32_to_cpu(vid_hdr->vol_id); + lnum = ubi32_to_cpu(vid_hdr->lnum); + + /* + * We do not want anybody to write to this physical eraseblock while + * we are copying it, so we lock it. + */ + err = ubi_eba_leb_write_lock(ubi, vol_id, lnum); + if (unlikely(err)) + goto out_err_cancel_buf; + + spin_lock(&wl->lock); + if (!wl->move) + /* This physical eraseblock was put meanwhile, cancel */ + goto out_cancel_wl_unlock; + spin_unlock(&wl->lock); + + /* + * From now on nobody can access this physical eraseblock as we locked + * the corresponding logical eraseblock. + */ + dbg_wl("read %d bytes of data", aldata_size); + err = ubi_io_read_data(ubi, buf, e1->pnum, 0, aldata_size, &tmp); + if (unlikely(err) && err != UBI_IO_BITFLIPS) { + ubi_warn("error %d while reading data from PEB %d", + err, e1->pnum); + goto data_read_error; + } + + /* + * Now we have got to calculate how much data we have to to copy. In + * case of a static volume it is fairly easy - the VID header contains + * the data size. In case of a dynamic volume it is more difficult - we + * have to read the contents, cut 0xFF bytes from the end and copy only + * the first part. We must do this to avoid writing 0xFF bytes as it + * may have some side-effects. And not only this. It is important not + * to include those 0xFFs to CRC because later the user may fill them + * by his data! + */ + if (vid_hdr->vol_type == UBI_VID_DYNAMIC) + aldata_size = data_size = + ubi_calc_data_len(ubi, buf, data_size); + + cond_resched(); + crc = crc32(UBI_CRC32_INIT, buf, data_size); + + /* + * It may turn out that the whole @e1->pnum physical eraseblock + * contains only 0xFF bytes. Then we have to only write the VID header + * and do not write any data. This also means we should not set + * @vid_hdr->copy_flag, @vid_hdr->data_size, and @vid_hdr->data_crc. + */ + if (likely(data_size > 0)) { + vid_hdr->copy_flag = 1; + vid_hdr->data_size = cpu_to_ubi32(data_size); + vid_hdr->data_crc = cpu_to_ubi32(crc); + } + vid_hdr->leb_ver = cpu_to_ubi32(ubi32_to_cpu(vid_hdr->leb_ver) + 1); + + cond_resched(); + err = ubi_io_write_vid_hdr(ubi, e2->pnum, vid_hdr); + if (unlikely(err)) + goto write_error; + + /* Read the VID header back and check if it was written correctly */ + cond_resched(); + err = ubi_io_read_vid_hdr(ubi, e2->pnum, vid_hdr, 1); + if (unlikely(err)) { + if (err != UBI_IO_BITFLIPS) { + ubi_warn("cannot read VID header back from PEB %d", e2->pnum); + goto write_error; + } + goto bitflip; + } + + if (likely(data_size > 0)) { + void *buf1; + + err = ubi_io_write_data(ubi, buf, e2->pnum, 0, aldata_size, + &tmp); + if (unlikely(err)) + goto write_error; + + /* + * We've written the data and are going to read it back to make + * sure it was written correctly. + */ + buf1 = ubi_kmalloc(aldata_size); + if (unlikely(!buf1)) { + err = -ENOMEM; + goto write_error; + } + + cond_resched(); + err = ubi_io_read_data(ubi, buf1, e2->pnum, 0, aldata_size, + &tmp); + if (unlikely(err)) { + ubi_kfree(buf1); + if (err != UBI_IO_BITFLIPS) { + ubi_warn("cannot read data back from PEB %d", + e2->pnum); + goto write_error; + } + goto bitflip; + } + + cond_resched(); + if (unlikely(memcmp(buf, buf1, aldata_size))) { + ubi_warn("read data back from PEB %d - it is different", + e2->pnum); + err = -EINVAL; + ubi_kfree(buf1); + goto write_error; + } + ubi_kfree(buf1); + } + + /* + * Re-map the logical eraseblock to the new physical eraseblock + * (@e2->pnum). + */ + ubi_eba_leb_remap(ubi, vol_id, lnum, e2->pnum); + + /* + * The physical eraseblock was successfully copied and re-mapped. Add + * the new copy to the @wl->used tree and schedule the old one for + * erasure. + */ + spin_lock(&wl->lock); + wl->erase_pending += 1; + used_tree_add(wl, e2); + wl->wl_scheduled = 0; + ubi_assert(wl->move); + wl->move = NULL; + spin_unlock(&wl->lock); + + /* Unlock the logical eraseblock */ + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + + ubi_kfree(buf); + ubi_free_vid_hdr(ubi, vid_hdr); + + /* + * Note, we do not check if more wear-leveling is needed here. We + * schedule the physical eraseblock for erasure so we know that the + * erase worker will take care about that. + */ + err = schedule_erase(ubi, e1, 0); + if (unlikely(err)) { + /* This may only happen if there is no memory */ + ubi_free_wl_entry(e1); + ubi_eba_ro_mode(ubi); + } + dbg_wl("done"); + return err; + +out: + wl->wl_scheduled = 0; + spin_unlock(&wl->lock); + return 0; + + /* + * The physical eraseblock we have selected was put. It was scheduled + * for erasure and removed from the @wl->used tree, so we only need to + * return @e2 back to the @wl->free tree. + */ +out_cancel_wl_unlock: + spin_unlock(&wl->lock); + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + ubi_kfree(buf); +out_cancel_wl: + dbg_wl("PEB %d was put, don't move it, cancel", e1->pnum); + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&wl->lock); + ubi_assert(wl->move == NULL); + free_tree_add(wl, e2); + wl->wl_scheduled = 0; + spin_unlock(&wl->lock); + return 0; + + /* + * Some non-I/O error occurred. Neither @e1 nor @e2 were changed, just + * get them back to the lists they were taken from. + */ +out_err_cancel_buf: + ubi_kfree(buf); +out_err_cancel_vid_hdr: + ubi_free_vid_hdr(ubi, vid_hdr); +out_err_cancel: + spin_lock(&wl->lock); + wl->wl_scheduled = 0; + if (wl->move) { + if (scrub) + scrub_tree_add(wl, e1); + else + used_tree_add(wl, e1); + wl->move = NULL; + } + free_tree_add(wl, e2); + spin_unlock(&wl->lock); + return err; + + /* + * Failed to read from the @e1->pnum physical eraseblock. Something + * nasty happened. We don't want to move this physical eraseblock. + * We also don't want this physical eraseblock to be repeatedly + * selected for wear-leveling, so protect it. + * + * FIXME: It would be better to notify upper layers about this and let + * them handle this. But this is not implemented. + */ +data_read_error: + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + ubi_kfree(buf); +vid_hdr_read_error: + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&wl->lock); + free_tree_add(wl, e2); + spin_unlock(&wl->lock); + + pe = ubi_alloc_wl_prot_entry(); + if (!pe) { + spin_lock(&wl->lock); + wl->wl_scheduled = 0; + if (wl->move) + used_tree_add(wl, e1); + wl->move = NULL; + spin_unlock(&wl->lock); + return -ENOMEM; + } + spin_lock(&wl->lock); + wl->wl_scheduled = 0; + if (wl->move) { + prot_tree_add(wl, e1, pe, ST_PROTECTION); + wl->move = NULL; + spin_unlock(&wl->lock); + } else { + spin_unlock(&wl->lock); + ubi_free_wl_prot_entry(pe); + } + return 0; + + /* + * An error occurred during writing. Something was written to @e2-pnum, + * so we cannot treat it as free any longer. Put @e1 back to the + * @wl->used tree and schedule @e2->pnum for erasure. + * + * Normally, this happens if @e2->pnum went bad - then it will be + * handled in the erase function. But if the flash does not admit of + * bad physical eraseblock, we switch to read-only mode. + */ +write_error: + ubi_kfree(buf); + ubi_free_vid_hdr(ubi, vid_hdr); + + spin_lock(&wl->lock); + wl->wl_scheduled = 0; + ubi_assert(wl->move); + used_tree_add(wl, e1); + wl->move = NULL; + spin_unlock(&wl->lock); + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + + if (ubi->io->bad_allowed) { + spin_lock(&wl->lock); + wl->erase_pending += 1; + spin_unlock(&wl->lock); + tmp = schedule_erase(ubi, e2, 1); + if (tmp) { + /* No memory - bad, switch to read-only mode */ + ubi_free_wl_entry(e2); + spin_lock(&wl->lock); + wl->erase_pending -= 1; + spin_unlock(&wl->lock); + ubi_eba_ro_mode(ubi); + err = tmp; + } + } else { + ubi_err("flash device may be severly bad"); + ubi_free_wl_entry(e2); + ubi_eba_ro_mode(ubi); + } + return err; + + /* + * We successfully wrote the data to @e2->pnum, but when we red it back + * we detected a bit-flip. So we cancel the operation. + */ +bitflip: + dbg_wl("bit-flip at the copied data, cancel"); + ubi_kfree(buf); + ubi_free_vid_hdr(ubi, vid_hdr); + + spin_lock(&wl->lock); + wl->wl_scheduled = 0; + ubi_assert(wl->move); + if (scrub) + scrub_tree_add(wl, e1); + else + used_tree_add(wl, e1); + wl->move = NULL; + spin_unlock(&wl->lock); + ubi_eba_leb_write_unlock(ubi, vol_id, lnum); + + spin_lock(&wl->lock); + wl->erase_pending += 1; + spin_unlock(&wl->lock); + err = schedule_erase(ubi, e2, 0); + if (err) { + /* No memory - bad, switch to read-only mode */ + ubi_free_wl_entry(e2); + spin_lock(&wl->lock); + wl->erase_pending -= 1; + spin_unlock(&wl->lock); + ubi_eba_ro_mode(ubi); + } + + return err; + +} + +/** + * sync_erase - synchronously erase a physical eraseblock. + * + * @ubi: the UBI device description object + * @e: the the physical eraseblock to erase + * @torture: if the physical eraseblock has to be tortured + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int sync_erase(const struct ubi_info *ubi, struct ubi_wl_entry *e, + int torture) +{ + int err; + struct ubi_ec_hdr *ec_hdr; + struct ubi_wl_info *wl = ubi->wl; + uint64_t ec = e->ec + 1; + + dbg_wl("erase PEB %d, new EC %lld", e->pnum, ec); + + err = paranoid_check_ec(ubi, e->pnum, e->ec); + if (unlikely(err > 0)) + return -EINVAL; + + if (unlikely(ec > UBI_MAX_ERASECOUNTER)) { + /* + * Erase counter overflow. Upgrade UBI and use 64-bit + * erase counters internally. + */ + ubi_err("erase counter overflow at PEB %d, EC %d", + e->pnum, e->ec); + return -EINVAL; + } + + ec_hdr = ubi_zalloc_ec_hdr(ubi); + if (unlikely(!ec_hdr)) + return -ENOMEM; + + err = ubi_io_sync_erase(ubi, e->pnum, torture); + if (unlikely(err < 0)) + goto out_free; + + ec_hdr->ec = cpu_to_ubi64(ec); + + err = ubi_io_write_ec_hdr(ubi, e->pnum, ec_hdr); + if (unlikely(err)) + goto out_free; + + e->ec += 1; + if (e->ec > wl->max_ec) + wl->max_ec = e->ec; + +out_free: + ubi_free_ec_hdr(ubi, ec_hdr); + return err; +} + +/** + * erase_one_pending - erase one pending physical eraseblock. + * + * @ubi: the UBI device description object + * + * This function returns %1 if a physical eraseblock was successfully erased, + * %0 if no physical eraseblocks were erased and a negative error code in case + * of error. + */ +static int erase_one_pending(const struct ubi_info *ubi) +{ + int err, pending_count; + struct ubi_bgt_work *wrk; + const struct ubi_bgt_info *bgt = ubi->bgt; + + pending_count = bgt->pending_works_count; + while (pending_count != 0 && (wrk = ubi_bgt_next_work(ubi))) { + pending_count -= 1; + + if (wrk->func != &erase_worker) { + /* We are only interested in "erase" works" */ + err = ubi_bgt_reschedule(ubi, wrk); + if (unlikely(err)) { + /* + * The background thread was killed. Cancel + * this work. We cannot normally operate + * without the background thread, so switch to + * read-only mode. + */ + wrk->func(ubi, wrk, 1); + ubi_eba_ro_mode(ubi); + return err; + } + } else { + dbg_wl("erase"); + err = wrk->func(ubi, wrk, 0); + if (unlikely(err)) { + return err; + } + return 1; + } + } + + dbg_wl("no PEB was erased"); + return 0; +} + +/** + * wl_tree_add - add a wear-leveling entry to a WL RB-tree. + * + * @e: the wear-leveling entry to add + * @root: the root of the tree + * + * Note, we use (erase counter, physical eraseblock number) pairs as keys in + * the @wl->used and @wl->free RB-trees. + */ +static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root) +{ + struct rb_node **p, *parent = NULL; + + p = &root->rb_node; + while (*p) { + struct ubi_wl_entry *e1; + + parent = *p; + e1 = rb_entry(parent, struct ubi_wl_entry, rb); + + if (e->ec < e1->ec) + p = &(*p)->rb_left; + else if (e->ec > e1->ec) + p = &(*p)->rb_right; + else { + ubi_assert(e->pnum != e1->pnum); + if (e->pnum < e1->pnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + } + + rb_link_node(&e->rb, parent, p); + rb_insert_color(&e->rb, root); +} + +/** + * in_wl_tree - check if a wear-leveling entry is present in a WL RB-tree. + * + * @e: the wear-leveling entry to check + * @root: the root of the tree + * + * This function returns non-zero if @e is in the @root RB-tree and zero if it + * is not. + */ +static int in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root) +{ + struct rb_node *p; + + p = root->rb_node; + while (p) { + struct ubi_wl_entry *e1; + + e1 = rb_entry(p, struct ubi_wl_entry, rb); + + if (e->pnum == e1->pnum) { + ubi_assert(e == e1); + return 1; + } + + if (e->ec < e1->ec) + p = p->rb_left; + else if (e->ec > e1->ec) + p = p->rb_right; + else { + ubi_assert(e->pnum != e1->pnum); + if (e->pnum < e1->pnum) + p = p->rb_left; + else + p = p->rb_right; + } + } + + return 0; +} + +/** + * tree_destroy - destroy an RB-tree. + * + * @root: the root of the tree to destroy + */ +static void __exit tree_destroy(struct rb_root *root) +{ + struct rb_node *rb; + struct ubi_wl_entry *e; + + rb = root->rb_node; + while (rb) { + if (rb->rb_left) + rb = rb->rb_left; + else if (rb->rb_right) + rb = rb->rb_right; + else { + e = rb_entry(rb, struct ubi_wl_entry, rb); + + rb = rb_parent(rb); + if (rb) { + if (rb->rb_left == &e->rb) + rb->rb_left = NULL; + else + rb->rb_right = NULL; + } + + ubi_free_wl_entry(e); + } + } +} + +/** + * protection_trees_destroy - destroy the protection RB-trees. + * + * @wl: the wear-leveling unit description data structure + */ +static void __exit protection_trees_destroy(struct ubi_wl_info *wl) +{ + struct rb_node *rb; + struct ubi_wl_prot_entry *pe; + + rb = wl->prot.aec.rb_node; + while (rb) { + if (rb->rb_left) + rb = rb->rb_left; + else if (rb->rb_right) + rb = rb->rb_right; + else { + pe = rb_entry(rb, struct ubi_wl_prot_entry, rb_aec); + + rb = rb_parent(rb); + if (rb) { + if (rb->rb_left == &pe->rb_aec) + rb->rb_left = NULL; + else + rb->rb_right = NULL; + } + + ubi_free_wl_entry(pe->e); + ubi_free_wl_prot_entry(pe); + } + } +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID_WL + +/** + * paranoid_check_ec - make sure that the erase counter of a physical eraseblock + * is correct. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock number to check + * @ec: the erase counter to check + * + * This function returns zero if the erase counter of physical eraseblock @pnum + * is equivalent to @ec, %1 if not, and a negative error code if an error + * occurred. + */ +static int paranoid_check_ec(const struct ubi_info *ubi, int pnum, int ec) +{ + int err; + int64_t read_ec; + struct ubi_ec_hdr *ec_hdr; + + ec_hdr = ubi_zalloc_ec_hdr(ubi); + if (unlikely(!ec_hdr)) + return -ENOMEM; + + err = ubi_io_read_ec_hdr(ubi, pnum, ec_hdr, 0); + if (unlikely(err) && err != UBI_IO_BITFLIPS) { + /* The header does not have to exist */ + err = 0; + goto out_free; + } + + read_ec = ubi64_to_cpu(ec_hdr->ec); + if (unlikely(ec != read_ec)) { + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_err("read EC is %lld, should be %d", read_ec, ec); + dump_stack(); + err = 1; + } else + err = 0; + +out_free: + ubi_free_ec_hdr(ubi, ec_hdr); + return err; +} + +/** + * paranoid_check_in_wl_tree - make sure that a wear-leveling entry is present + * in a WL RB-tree. + * + * @e: the wear-leveling entry to check + * @root: the root of the tree + * + * This function returns zero if @e is in the @root RB-tree and %1 if it + * is not. + */ +static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root) +{ + if (likely(in_wl_tree(e, root))) + return 0; + + ubi_err("paranoid check failed for PEB %d, EC %d, RB-tree %p ", + e->pnum, e->ec, root); + dump_stack(); + return 1; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID_WL */ diff --git a/drivers/mtd/ubi/wl.h b/drivers/mtd/ubi/wl.h new file mode 100644 index 0000000..7113ab3 --- /dev/null +++ b/drivers/mtd/ubi/wl.h @@ -0,0 +1,285 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Artem B. Bityutskiy, Thomas Gleixner + */ + +/* + * UBI wear-leveling unit. + * + * This unit is responsible for wear-leveling. This unit works in terms of + * physical eraseblocks and erase counters and knows nothing about logical + * eraseblocks, volumes, etc (with one exception). From this unit's perspective + * all physical eraseblocks are of two types - used and free. Used physical + * eraseblocks are those that were "get" by the 'ubi_wl_get_peb()' function, + * and free physical eraseblocks are those that were put by the + * 'ubi_wl_put_peb()' function. Actually, the above two functions are the main + * in this unit. + * + * Physical eraseblocks returned by 'ubi_wl_get_peb()' have only the erase + * counter header. The rest of the physical eraseblock contains only 0xFF bytes. + * + * When physical eraseblocks are returned to the WL unit by means of the + * 'ubi_wl_put_peb()' function, they are scheduled for erasure. The erasure is + * not done synchronously. Instead, it is done in background in context of the + * per-UBI device background thread (see the background thread unit). Actually, + * the WL unit strongly depends on the background thread and cannot operate + * without it. + * + * The wear-leveling is ensured by means of moving the contents of used + * physical eraseblocks with low erase counter to free physical eraseblocks + * with high erase counter. The movement is also done in background. + * + * When eraseblocks are moved, the WL unit cooperates with the EBA unit to + * provide proper eraseblock locking. This means, the WL unit uses the EBA unit + * to lock the logical eraseblock corresponding to the physical eraseblock + * which is being moved. This is the only place where the WL unit "knows" about + * logical eraseblocks and volume identifier headers. + * + * The 'ubi_wl_get_peb()' function accepts data type hints which help to pick + * an "optimal" physical eraseblock. Indeed, for example, when it knows that + * the physical eraseblock will be "put" soon, it may pick a free physical + * eraseblock with low erase counter, and so forth. + * + * If the WL unit fails to erase a physical eraseblock, it marks the physical + * eraseblock as bad (using the bad eraseblock handling unit). + * + * This unit is also responsible for scrubbing. If a bit-flip is detected in a + * physical eraseblock, it has to be moved. Technically this is the same as + * moving it for wear-leveling reasons. + * + * As it was said, for the UBI unit all physical eraseblocks are either "free" + * or "used". Free eraseblock are kept in the @wl->free RB-tree, while used + * eraseblocks are kept in a set of different RB-trees: @wl->used, + * @wl->prot.pnum, @wl->prot.aec, and @wl->scrub. + * + * Note, in this implementation, we keep a small in-RAM object for each physical + * eraseblock. This is surely not a scalable solution. But it appears to be good + * enough for moderately large flashes and it is simple. In future, one may + * re-work this unit and make it more scalable. + */ + +#ifndef __UBI_WL_H__ +#define __UBI_WL_H__ + +#include +#include +#include +#include +#include +#include +#include +#include "background.h" + +struct ubi_info; +struct ubi_scan_info; + +/** + * ubi_wl_get_peb - get a physical eraseblock. + * + * @ubi: the UBI device description object + * @dtype: type of data which will be stored in this physical eraseblock + * + * This function returns a physical eraseblock in case of success and a + * negative error code in case of failure. Might sleep. + */ +int ubi_wl_get_peb(const struct ubi_info *ubi, enum ubi_data_type dtype); + +/** + * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling + * unit. + * + * @ubi: the UBI device description object + * @pnum: physical eraseblock to return + * @torture: if this physical eraseblock has to be tortured + * + * If an error occurred during I/O to @pnum, and the caller suspects @pnum to be + * bad, it will be tested for badness if @torture flag is not zero. This function + * returns zero in case of success and a negative error code in case of + * failure. Might sleep. + */ +int ubi_wl_put_peb(const struct ubi_info *ubi, int pnum, int torture); + +/** + * ubi_wl_erase_flush - erase all pending physical eraseblocks. + * + * @ubi: the UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_wl_erase_flush(const struct ubi_info *ubi); + +/** + * ubi_wl_scrub_peb - schedule a physical eraseblock for scrubbing. + * + * @ubi: the UBI device description object + * @pnum: the physical eraseblock to schedule + * + * If a bit-flip in a physical eraseblock is detected, this physical eraseblock + * needs scrubbing. This function schedules a physical eraseblock for + * scrubbing which is done in background. This function returns zero in case of + * success and a negative error code in case of failure. + */ +int ubi_wl_scrub_peb(const struct ubi_info *ubi, int pnum); + +/** + * ubi_wl_init_scan - initialize the wear-leveling unit using scanning + * information. + * + * @ubi: the UBI device description object + * @si: a pointer to the scanning information + * + * This function returns zero in case of success, and a negative error code in + * case of failure. + */ +int __init ubi_wl_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si); + +/** + * ubi_wl_close - close the wear-leveling unit. + * + * @ubi: the UBI device description object + */ +void __exit ubi_wl_close(struct ubi_info *ubi); + +/** + * struct ubi_wl_entry - a wear-leveling entry. + * + * @rb: link in the corresponding RB-tree + * @ec: erase counter + * @pnum: physical eraseblock number + * + * Each physical eraseblock has a corresponding &struct wl_entry object which + * may be kept in different RB-trees. + */ +struct ubi_wl_entry { + struct rb_node rb; + int ec; + int pnum; +}; + +/** + * struct ubi_wl_prot_entry - a protection entry. + * + * @rb_pnum: link in the @wl->prot.pnum RB-tree + * @rb_aec: link in the @wl->prot.aec RB-tree + * @abs_ec: the absolute erase counter value when the protection ends + * @e: the wear-levelling entry of the physical eraseblock under protection + * + * When the WL unit returns a physical eraseblock, the physical eraseblock is + * protected from being moved for some "time". For this reason, the physical + * eraseblock is not directly moved from the @wl->free tree to the @wl->used + * tree. There is one more tree in between where this physical eraseblock is + * temporarily stored (@wl->prot). + * + * All this protection stuff is needed because: + * o we don't want to move physical eraseblocks just after we have given them + * to the user; instead, we first want to let users fill them up with data; + * + * o there is a chance that the user will put the physical eraseblock very + * soon, so it makes sense not to move it for some time, but wait; this is + * especially important in case of "short term" physical eraseblocks. + * + * Physical eraseblocks stay protected only for limited time. But the "time" is + * measured in erase cycles in this case. This is implemented with help of the + * absolute erase counter (@wl->abs_ec). When it reaches certain value, the + * physical eraseblocks are moved from the protection trees (@wl->prot.*) to + * the @wl->used tree. + * + * Protected physical eraseblocks are searched by physical eraseblock number + * (when they are put) and by the absolute erase counter (to check if it is + * time to move them to the @wl->used tree). So there are actually 2 RB-trees + * storing the protected physical eraseblocks: @wl->prot.pnum and + * @wl->prot.aec. They are referred to as the "protection" trees. The + * first one is indexed by the physical eraseblock number. The second one is + * indexed by the absolute erase counter. Both trees store + * &struct ubi_wl_prot_entry objects. + */ +struct ubi_wl_prot_entry { + struct rb_node rb_pnum; + struct rb_node rb_aec; + unsigned long long abs_ec; + struct ubi_wl_entry *e; +}; + +/** + * struct ubi_wl_erase_work - physical eraseblock erasure work description data + * structure. + * + * @wrk: the background thread work descriptor + * @e: the physical eraseblock to erase + * @torture: if the physical eraseblock has to be tortured + * + * This data structure is used for erasure background works. The @torture flag + * indicates whether the physical eraseblock should be tested. Testing physical + * eraseblocks may be needed if an error occurred and they are likely to become + * bad. + */ +struct ubi_wl_erase_work { + struct ubi_bgt_work wrk; + struct ubi_wl_entry *e; + int torture; +}; + +/** + * struct ubi_wl_info - the UBI WL unit description data structure. + * + * @used: RB-tree of used physical eraseblocks + * @free: RB-tree of free physical eraseblocks + * @scrub: RB-tree of physical eraseblocks which need scrubbing + * @prot.pnum: the protection tree indexed by physical eraseblock numbers + * @prot: embraces protection trees + * @prot.aec: the protection tree indexed the absolute erase counter + * @lock: protects the @used, @free, @prot, @lookuptbl, @abs_ec, @move, + * @wl_scheduled, and @erase_pending fields + * @wl_scheduled: non-zero if the wear leveling was scheduled + * @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any + * physical eraseblock + * @erase_pending: how many physical eraseblock are waiting for erasure + * @abs_ec: the absolute erase counter + * @move: if a physical eraseblock is being moved, it is referred to here + * @max_ec: current highest erase counter value + * + * Each physical eraseblock has 2 main states: free and used. The former state + * corresponds to the @free RB-tree. The latter state is split up on several + * sub-states: + * o the WL movement is allowed (@used RB-tree); + * o the WL movement is temporarily prohibited (@prot.pnum and @prot.aec + * RB-trees); + * o scrubbing is needed (@scrub RB-tree), + * + * Depending on the sub-state, wear-levelling entries of the used physical + * eraseblocks may be kept in one of those trees. + */ +struct ubi_wl_info { + struct rb_root used; /* private */ + struct rb_root free; /* private */ + struct rb_root scrub; /* private */ + struct { + struct rb_root pnum; /* private */ + struct rb_root aec; /* private */ + } prot; + spinlock_t lock; /* private */ + int wl_scheduled; /* private */ + struct ubi_wl_entry **lookuptbl; /* private */ + int erase_pending; /* private */ + unsigned long long abs_ec; /* public */ + struct ubi_wl_entry *move; /* private */ + int max_ec; /* public */ +}; + +#endif /* __UBI_WL_H__ */ diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index abb90c0..8a649f6 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -672,6 +672,13 @@ static int jffs2_flash_setup(struct jffs return ret; } + /* and an UBI volume */ + if (jffs2_ubivol(c)) { + ret = jffs2_ubivol_setup(c); + if (ret) + return ret; + } + return ret; } @@ -690,4 +697,9 @@ void jffs2_flash_cleanup(struct jffs2_sb if (jffs2_nor_wbuf_flash(c)) { jffs2_nor_wbuf_flash_cleanup(c); } + + /* and an UBI volume */ + if (jffs2_ubivol(c)) { + jffs2_ubivol_cleanup(c); + } } diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index 9f41fc0..29db226 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -98,6 +98,9 @@ #define jffs2_dataflash_cleanup(c) do {} #define jffs2_nor_wbuf_flash(c) (0) #define jffs2_nor_wbuf_flash_setup(c) (0) #define jffs2_nor_wbuf_flash_cleanup(c) do {} while (0) +#define jffs2_ubivol(c) (0) +#define jffs2_ubivol_setup(c) (0) +#define jffs2_ubivol_cleanup(c) do {} while (0) #else /* NAND and/or ECC'd NOR support present */ @@ -133,6 +136,9 @@ void jffs2_nand_flash_cleanup(struct jff #define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH) int jffs2_dataflash_setup(struct jffs2_sb_info *c); void jffs2_dataflash_cleanup(struct jffs2_sb_info *c); +#define jffs2_ubivol(c) (c->mtd->type == MTD_UBIVOLUME) +int jffs2_ubivol_setup(struct jffs2_sb_info *c); +void jffs2_ubivol_cleanup(struct jffs2_sb_info *c); #define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && ! (c->mtd->flags & MTD_BIT_WRITEABLE)) int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c); diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index dcb18e9..b2dca72 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -1273,3 +1273,27 @@ int jffs2_nor_wbuf_flash_setup(struct jf void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c) { kfree(c->wbuf); } + +int jffs2_ubivol_setup(struct jffs2_sb_info *c) { + c->cleanmarker_size = 0; + + if (c->mtd->writesize == 1) + /* We do not need write-buffer */ + return 0; + + init_rwsem(&c->wbuf_sem); + + c->wbuf_pagesize = c->mtd->writesize; + c->wbuf_ofs = 0xFFFFFFFF; + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf) + return -ENOMEM; + + printk(KERN_INFO "JFFS2 write-buffering enabled buffer (%d) erasesize (%d)\n", c->wbuf_pagesize, c->sector_size); + + return 0; +} + +void jffs2_ubivol_cleanup(struct jffs2_sb_info *c) { + kfree(c->wbuf); +} diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h new file mode 100644 index 0000000..b796aa4 --- /dev/null +++ b/include/linux/mtd/ubi.h @@ -0,0 +1,343 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#ifndef __LINUX_UBI_H__ +#define __LINUX_UBI_H__ + +#include +#include +#include + +/** + * enum ubi_data_type - UBI data type hint constants. + * + * @UBI_DATA_LONGTERM: long-term data + * @UBI_DATA_SHORTTERM: short-term data + * @UBI_DATA_UNKNOWN: data persistency is unknown + * + * These constants are used when data is written to UBI volumes in order to + * help the UBI wear-leveling unit to find more appropriate physical + * eraseblocks. + */ +enum ubi_data_type { + UBI_DATA_LONGTERM = 1, + UBI_DATA_SHORTTERM, + UBI_DATA_UNKNOWN +}; + +/** + * enum ubi_open_mode - UBI volume open mode constants. + * + * @UBI_READONLY: read-only mode + * @UBI_READWRITE: read-write mode + * @UBI_EXCLUSIVE: exclusive mode + */ +enum ubi_open_mode { + UBI_READONLY = 1, + UBI_READWRITE, + UBI_EXCLUSIVE +}; + +/** + * struct ubi_vol_info - UBI volume description data structure. + * + * @vol_id: volume ID + * @ubi_num: UBI device number this volume belongs to + * @size: how many physical eraseblocks are reserved for this volume + * @used_bytes: how many bytes of data this volume contains + * @used_ebs: how many physical eraseblocks of this volume actually contain any + * data + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @corrupted: non-zero if the volume is corrupted + * @alignment: volume alignment + * @usable_leb_size: how many bytes are available in logical eraseblocks of + * this volume + * @name_len: volume name length + * @name: volume name + * @cdev: UBI volume character device major and minor numbers + * + * The @used_bytes and @used_ebs fields are only really needed for static volumes + * and contain the number of bytes stored in this static volume and how many + * eraseblock this data occupies. In case of dynamic volumes, the @used_bytes + * field is equivalent to @size*@usable_leb_size, and the @used_ebs field is + * equivalent to @size. + * + * In general, logical eraseblock size is a property of the UBI device, not + * of the UBI volume. Indeed, the logical eraseblock size depends on the + * physical eraseblock size and on how much bytes UBI headers consume. But + * because of the volume alignment (@alignment), the usable size of logical + * eraseblocks if a volume may be less. The following equation is true: + * @usable_leb_size = LEB size - (LEB size mod @alignment), + * where LEB size is the logical eraseblock size defined by the UBI device. + * + * The alignment is multiple to the minimal flash input/output unit size or %1 + * if all the available space is used. + * + * To put this differently, alignment may be considered is a way to change + * volume logical eraseblock sizes. + */ +struct ubi_vol_info { + int ubi_num; + int vol_id; + int size; + long long used_bytes; + int used_ebs; + int vol_type; + int corrupted; + int alignment; + int usable_leb_size; + int name_len; + const char *name; + dev_t cdev; +}; + +/** + * struct ubi_dev_info - UBI device description data structure. + * + * @ubi_num: ubi device number + * @leb_size: logical eraseblock size on this UBI device + * @min_io_size: minimal I/O unit size + * @ro_mode: if this device is in read-only mode + * @cdev: UBI character device major and minor numbers + * + * Note, @leb_size is the logical eraseblock size offered by the UBI device. + * Volumes of this UBI device may have smaller logical eraseblock size if their + * alignment is not equivalent to %1. + */ +struct ubi_dev_info { + int ubi_num; + int leb_size; + int min_io_size; + int ro_mode; + dev_t cdev; +}; + +/** + * ubi_get_device_info - get information about an UBI device. + * + * @ubi_num: UBI device number + * @di: the volume information is returned here + * + * This function returns 0 in case of success and a %-ENODEV if there is no + * such UBI device. + */ +int ubi_get_device_info(int ubi_num, struct ubi_dev_info *di); + +/* UBI descriptor given to users when they open UBI volumes */ +struct ubi_vol_desc; + +/** + * ubi_get_volume_info - get information about an UBI volume. + * + * @udesc: volume descriptor + * @vi: the volume information is returned here + */ +void ubi_get_volume_info(struct ubi_vol_desc *udesc, struct ubi_vol_info *vi); + +/* + * ubi_open_volume - open an UBI volume. + * + * @ubi_num: the UBI device number + * @vol_id: ID of the volume to open + * @mode: volume open mode + * + * This function opens a UBI volume. The @mode parameter specifies if the + * volume is opened in read-only mode, read-write mode, or exclusive mode. The + * exclusive mode means that nobody else will be allowed to open this volume. + * Note, UBI allows to have many volume readers and one writer at a time. And + * note, static volumes may only be opened in read-only mode. + * + * In case of success, this function returns an UBI volume descriptor. In case + * of failure, the following error codes may be returned: + * + * o %-EBUSY if the volume is busy (it is being updated, or it is already + * opened in exclusive or read-write mode by somebody else); + * o %-EINVAL if the input arguments are invalid; + * o %-ENODEV if this volume does not exist or the UBI device does not exist; + * o other negative error codes in case of other errors. + * + * If a volume is corrupted for some other reasons (e.g., because of an + * interrupted update), this function returns success so that it is still + * possible to read some useful valid data from it. So users have to check + * status of volumes after open (with help of the 'ubi_get_volume_info()' + * function). + * + * Additional node: if the volume is being opened for the first time since the + * last reboot, it is fully checked by this function. + */ +struct ubi_vol_desc *ubi_open_volume(int ubi_num, int vol_id, + enum ubi_open_mode mode); + +/* + * ubi_open_volume_nm - open an UBI volume by volume name. + * + * @ubi_num: the UBI device number + * @name: volume name + * @mode: volume open mode + * + * This function is similar to the 'ubi_open_volume()' function, but opens UBI + * volumes by name. + */ +struct ubi_vol_desc *ubi_open_volume_nm(int ubi_num, const char *name, + enum ubi_open_mode mode); + +/** + * ubi_close_volume - close an UBI volume. + * + * @udesc: UBI volume descriptor + */ +void ubi_close_volume(struct ubi_vol_desc *udesc); + +/** + * ubi_eraseblock_read - read data from a logical eraseblock. + * + * @udesc: volume descriptor + * @lnum: the logical eraseblock number to read from + * @buf: a buffer where to store the read data + * @offset: the offset within the logical eraseblock from where to read + * @len: how many bytes to read + * @check: whether UBI has to check the read data's CRC or not. + * @read: how many bytes were actually read is returned here + * + * This function reads data from offset @offset of the logical eraseblock @lnum + * and stores the read data at @buf. When reading from static volumes, @check + * may be used to specify whether the read data has to be checked or not. If + * checking is requested, the whole logical eraseblock will be read and its CRC + * checksum will be checked, so checking may substantially slow down the read + * speed. The @check argument is ignored in case of dynamic volumes. + * + * In case of success, this function returns zero and the @read argument has + * to contain @len. In case of error, this function returns a negative error + * code and the @read argument contains the number of read bytes, but they + * don't have to be correct. + * + * %-EBADMSG error code may be returned: + * o for both static and dynamic volumes if the MTD driver has detected an + * unrecoverable ECC checksum mismatch; + * o for static volumes if the data CRC mismatches + * + * If a corrupted static volume is read, and the data were read from flash + * without errors, this function anyway returns %-EUCLEAN, not zero. So, there + * is still a possibility to read from corrupted static volumes. + */ +int ubi_eraseblock_read(struct ubi_vol_desc *udesc, int lnum, char *buf, + int offset, int len, int check, int *read); + +/** + * ubi_read - read data from an logical eraseblock (simplified). + * + * @udesc: volume descriptor + * @lnum: the logical eraseblock number to read from + * @buf: a buffer where to store the read data + * @offset: the offset within the logical eraseblock from where to read + * @len: how many bytes to read + * @read: how many bytes were actually read is returned here + * + * This function is the same as the 'ubi_eraseblock_read()' function, but it + * does not provide the checking capability. + */ +static inline int ubi_read(struct ubi_vol_desc *udesc, int lnum, char *buf, + int offset, int len, int *read) +{ + return ubi_eraseblock_read(udesc, lnum, buf, offset, len, 0, read); +} + +/** + * ubi_eraseblock_write - write data to a logical eraseblock. + * + * @udesc: volume descriptor + * @lnum: the logical eraseblock number to write to + * @buf: the data to write + * @offset: offset within the logical eraseblock where to write + * @len: how many bytes from @buf to write + * @dtype: expected data type + * @written: how many bytes were actually written + * + * This function writes @len bytes of data from buffer @buf to offset @offset + * of logical eraseblock @lnum. The @dtype argument describes the expected + * lifetime of the data being written. + * + * Note, this function takes care about write failures. If a write to the physical + * eraseblock (the one this logical eraseblock is mapped to) fails, the logical + * eraseblock is re-mapped to another physical eraseblock, the data is + * recovered, and the write finishes. + * + * If all the data were successfully written, zero is returned. If an error + * occurred, this function returns a negative error code and stores the number + * of written bytes at @written. But there is no guarantee that the were + * written correctly. + */ +int ubi_eraseblock_write(struct ubi_vol_desc *udesc, int lnum, const void *buf, + int offset, int len, enum ubi_data_type dtype, + int *written); + +/** + * ubi_write - write data to a logical eraseblock (simplified). + * + * @udesc: volume descriptor + * @lnum: the logical eraseblock number to write to + * @buf: the data to write + * @offset: offset within the logical eraseblock where to write + * @len: how many bytes from @buf to write + * @written: how many bytes were actually written + * + * This function is the same as the 'ubi_eraseblock_write()' functions, but it + * does not have the data type argument. + */ +static inline int ubi_write(struct ubi_vol_desc *udesc, int lnum, + const void *buf, int offset, int len, int *written) +{ + return ubi_eraseblock_write(udesc, lnum, buf, offset, len, + UBI_DATA_UNKNOWN, written); +} + +/** + * ubi_eraseblock_erase - erase a logical eraseblock. + * + * @udesc: volume descriptor + * @lnum: the logical eraseblock number to erase + * + * This function returns zero in case of success and a negative error code on + * case of failure. + * + * Note, UBI erases eraseblocks asynchronously. This means that this function + * will basically unmap this logical eraseblock from its physical eraseblock, + * schedule the physical eraseblock for erasure and return. + */ +int ubi_eraseblock_erase(struct ubi_vol_desc *udesc, int lnum); + +/** + * ubi_eraseblock_is_mapped - check if a logical eraseblock is mapped. + * + * @udesc: volume descriptor + * @lnum: the logical eraseblock number to erase + * + * This function checks if a logical eraseblock is mapped to a physical + * eraseblock. Unmapped logical eraseblocks are equivalent to erased logical + * eraseblocks and contain only 0xFF bytes. Mapped logical eraseblocks are + * those that were explicitely written to. They may also contain only 0xFF + * bytes if these were written. + * + * This function returns %1 if the LEB is mapped, %0 if not, and a negative + * error code in case of failure. + */ +int ubi_eraseblock_is_mapped(struct ubi_vol_desc *udesc, int lnum); + +#endif /* !__LINUX_UBI_H__ */ diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild index e0fe92b..4d46b3b 100644 --- a/include/mtd/Kbuild +++ b/include/mtd/Kbuild @@ -3,3 +3,5 @@ header-y += jffs2-user.h header-y += mtd-abi.h header-y += mtd-user.h header-y += nftl-user.h +header-y += ubi-header.h +header-y += ubi-user.h diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index f913c30..2aaca03 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -24,6 +24,7 @@ #define MTD_ROM 2 #define MTD_NORFLASH 3 #define MTD_NANDFLASH 4 #define MTD_DATAFLASH 6 +#define MTD_UBIVOLUME 7 #define MTD_WRITEABLE 0x400 /* Device is writeable */ #define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ diff --git a/include/mtd/ubi-header.h b/include/mtd/ubi-header.h new file mode 100644 index 0000000..73add4d --- /dev/null +++ b/include/mtd/ubi-header.h @@ -0,0 +1,365 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Artem B. Bityutskiy + * Thomas Gleixner + * Frank Haverkamp + * Oliver Lohmann + * Andreas Arnez + */ + +/* + * This file defines the layout of UBI headers and all the other UBI on-flash + * data structures. + */ + +#ifndef __UBI_HEADER_H__ +#define __UBI_HEADER_H__ + +#include + +/* The version of this UBI implementation */ +#define UBI_VERSION 1 + +/* The highest erase counter value supported by this implementation of UBI */ +#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF + +/* The initial CRC32 value used when calculating CRC checksums */ +#define UBI_CRC32_INIT 0xFFFFFFFFU + +/* + * Magic numbers of the UBI headers. + * + * @UBI_EC_HDR_MAGIC: erase counter header magic number (ASCII "UBI#") + * @UBI_VID_HDR_MAGIC: volume identifier header magic number (ASCII "UBI!") + */ +enum { + UBI_EC_HDR_MAGIC = 0x55424923, + UBI_VID_HDR_MAGIC = 0x55424921 +}; + +/* + * Molume type constants used in volume identifier headers. + * + * @UBI_VID_DYNAMIC: dynamic volume + * @UBI_VID_STATIC: static volume + */ +enum { + UBI_VID_DYNAMIC = 1, + UBI_VID_STATIC = 2 +}; + +/* + * Compatibility constants used by internal volumes. + * + * @UBI_COMPAT_DELETE: delete this internal volume before anything is written + * to the flash + * @UBI_COMPAT_RO: attach this device in read-only mode + * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its + * physical eraseblocks, don't allow the wear-leveling unit to move them + * @UBI_COMPAT_REJECT: reject this UBI image + */ +enum { + UBI_COMPAT_DELETE = 1, + UBI_COMPAT_RO = 2, + UBI_COMPAT_PRESERVE = 4, + UBI_COMPAT_REJECT = 5 +}; + +/* + * ubi16_t/ubi32_t/ubi64_t - 16, 32, and 64-bit integers used in UBI on-flash + * data structures. + */ +typedef struct { + uint16_t int16; +} __attribute__ ((packed)) ubi16_t; + +typedef struct { + uint32_t int32; +} __attribute__ ((packed)) ubi32_t; + +typedef struct { + uint64_t int64; +} __attribute__ ((packed)) ubi64_t; + +/* + * In this implementation UBI uses the big-endian format for on-flash integers. + * The below are the corresponding endianess conversion macros. + */ +#define cpu_to_ubi16(x) ((ubi16_t){__cpu_to_be16(x)}) +#define ubi16_to_cpu(x) ((uint16_t)__be16_to_cpu((x).int16)) + +#define cpu_to_ubi32(x) ((ubi32_t){__cpu_to_be32(x)}) +#define ubi32_to_cpu(x) ((uint32_t)__be32_to_cpu((x).int32)) + +#define cpu_to_ubi64(x) ((ubi64_t){__cpu_to_be64(x)}) +#define ubi64_to_cpu(x) ((uint64_t)__be64_to_cpu((x).int64)) + +/* Sizes of UBI headers */ +#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr) +#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr) + +/* Sizes of UBI headers without the ending CRC */ +#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(ubi32_t)) +#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(ubi32_t)) + +/* How much private data may internal volumes store in the VID header */ +#define UBI_VID_HDR_IVOL_DATA_SIZE 12 + +/** + * struct ubi_ec_hdr - UBI erase counter header. + * + * @magic: the erase counter header magic number (%UBI_EC_HDR_MAGIC) + * @version: the version of UBI implementation which is supposed to accept this + * UBI image (%UBI_VERSION) + * @padding1: reserved for future, zeroes + * @ec: the erase counter + * @vid_hdr_offset: where the VID header begins + * @data_offset: where the user data begins + * @padding2: reserved for future, zeroes + * @hdr_crc: the erase counter header CRC checksum + * + * The erase counter header takes 64 bytes and has a plenty of unused space for + * future usage. The unused fields are zeroed. The @version field is used to + * indicate the version of UBI implementation which is supposed to be able to + * work with this UBI image. If @version is greater then the current UBI + * version, the image is rejecter. This may be useful in future if something + * is changed radically. This field is duplicated in the volume identifier + * header. + * + * The @vid_hdr_offset and @data_offset fields contain the offset of the the + * volume identifier header and user data, relative to the beginning of the + * eraseblock. These values have to be the same for all eraseblocks. + */ +struct ubi_ec_hdr { + ubi32_t magic; + uint8_t version; + uint8_t padding1[3]; + ubi64_t ec; /* Warning: the current limit is 31-bit anyway! */ + ubi32_t vid_hdr_offset; + ubi32_t data_offset; + uint8_t padding2[36]; + ubi32_t hdr_crc; +} __attribute__ ((packed)); + +/** + * struct ubi_vid_hdr - on-flash UBI volume identifier header. + * + * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) + * @version: UBI implementation version which is supposed to accept this UBI + * image (%UBI_VERSION) + * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) + * @copy_flag: a flag indicating if this physical eraseblock was created by + * means of copying an original physical eraseblock to ensure wear-leveling. + * @compat: compatibility of this volume (%UBI_COMPAT_DELETE, + * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @leb_ver: eraseblock copy number + * @data_size: how many bytes of data this eraseblock contains. + * @used_ebs: total number of used logical eraseblocks in this volume + * @data_pad: how many bytes at the end of this eraseblock are not used + * @data_crc: CRC checksum of data containing in this eraseblock + * @padding1: reserved for future, zeroes + * @ivol_data: private data of internal volumes + * @hdr_crc: volume identifier header CRC checksum + * + * The @leb_ver and the @copy_flag fields are used to distinguish between older + * and newer copies of logical eraseblocks, as well as to guarantee robustness + * to unclean reboots. As UBI erases logical eraseblocks asynchronously, it has + * to distinguish between older and newer copies of eraseblocks. This is done + * using the @version field. On the other hand, when UBI moves an eraseblock, + * its version is also increased and the @copy_flag is set to 1. Additionally, + * when moving eraseblocks, UBI calculates data CRC and stores it in the + * @data_crc field, even for dynamic volumes. + * + * Thus, if there are 2 eraseblocks of the same volume and logical number, UBI + * uses the following algorithm to pick one of them. It first picks the one + * with larger version (say, A). If @copy_flag is not set, then A is picked. If + * @copy_flag is set, UBI checks the CRC of the eraseblock (@data_crc). This is + * needed to ensure that copying was finished. If the CRC is all right, A is + * picked. If not, the older eraseblock is picked. + * + * Note, the @leb_ver field may overflow. Thus, if you have 2 versions A and B, + * then A > B if abs(A-B) < 0x7FFFFFFF, and A < B otherwise. + * + * There are 2 sorts of volumes in UBI: user volumes and internal volumes. + * Internal volumes are not seen from outside and are used for different + * internal UBI purposes. In this implementation there are only two internal + * volumes: the layout volume and the update volume. Internal volumes are the + * main mechanism of UBI extensions. For example, in future one may introduce a + * journal internal volume. + * + * The @compat field is only used for internal volumes and contains the degree + * of their compatibility. This field is always zero for user volumes. This + * field provides a mechanism to introduce UBI extensions and to be still + * compatible with older UBI binaries. For example, if someone introduced an + * journal internal volume in future, he would probably use %UBI_COMPAT_DELETE + * compatibility. And in this case, older UBI binaries, which know nothing + * about the journal volume, would just delete this and work perfectly fine. + * This is somewhat similar to what Ext2fs does when it is fed by an Ext3fs + * image - it just ignores the Ext3fs journal. + * + * The @data_crc field contains the CRC checksum of the contents of the logical + * eraseblock if this is a static volume. In case of dynamic volumes, it does + * not contain the CRC checksum as a rule. The only exception is when the + * logical eraseblock was moved by the wear-leveling unit, then the + * wear-leveling unit calculates the eraseblocks' CRC and stores it at + * @data_crc. + * + * The @data_size field is always used for static volumes because we want to + * know about how many bytes of data are stored in this eraseblock. For + * dynamic eraseblocks, this field usually contains zero. The only exception is + * when the logical eraseblock is moved to another physical eraseblock due to + * wear-leveling reasons. In this case, UBI calculates CRC checksum of the + * contents and uses both @data_crc and @data_size fields. In this case, the + * @data_size field contains the size of logical eraseblock of this volume + * (which may vary owing to @alignment). + * + * The @used_ebs field is used only for static volumes and indicates how many + * eraseblocks the data of the volume takes. For dynamic volumes this field is + * not used and always contains zero. + * + * The @data_pad is calculated when volumes are created using the alignment + * parameter. So, effectively, the @data_pad field reduces the size of logical + * eraseblocks of this volume. This is very handy when one uses block-oriented + * software (say, cramfs) on top of the UBI volume. + * + * The @ivol_data contains private data of internal volumes. This might be very + * handy to store data in the VID header, not in the eraseblock's contents. For + * example it may make life of simple boot-loaders easier. The @ivol_data field + * contains zeroes for user volumes. + */ +struct ubi_vid_hdr { + ubi32_t magic; + uint8_t version; + uint8_t vol_type; + uint8_t copy_flag; + uint8_t compat; + ubi32_t vol_id; + ubi32_t lnum; + ubi32_t leb_ver; + ubi32_t data_size; + ubi32_t used_ebs; + ubi32_t data_pad; + ubi32_t data_crc; + uint8_t padding1[12]; + uint8_t ivol_data[UBI_VID_HDR_IVOL_DATA_SIZE]; + ubi32_t hdr_crc; +} __attribute__ ((packed)); + +/** + * struct ubi_vid_hdr_upd_vol - private data of the update internal volume + * stored in volume identifier headers. + * + * @vol_id: volume ID of the volume under update + * @padding: zeroes + */ +struct ubi_vid_hdr_upd_vol { + ubi32_t vol_id; + uint8_t padding[UBI_VID_HDR_IVOL_DATA_SIZE-4]; +} __attribute__ ((packed)); + +/* Count of internal UBI volumes */ +#define UBI_INT_VOL_COUNT 2 + +/* + * Internal volume IDs start from this digit. There is a reserved room for 4096 + * internal volumes. + */ +#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096) + +/* + * enum ubi_internal_volume_numbers - volume IDs of internal UBI volumes. + * + * %UBI_LAYOUT_VOL_ID: volume ID of the layout volume + * %UBI_UPDATE_VOL_ID: volume ID of the update volume + */ +enum { + UBI_LAYOUT_VOL_ID = UBI_INTERNAL_VOL_START, + UBI_UPDATE_VOL_ID = UBI_INTERNAL_VOL_START + 1 +}; + +/* Number of logical eraseblocks reserved for internal volumes */ +#define UBI_LAYOUT_VOLUME_EBS 2 +#define UBI_UPDATE_VOLUME_EBS 1 + +/* Names of internal volumes */ +#define UBI_LAYOUT_VOLUME_NAME "The layout volume" +#define UBI_UPDATE_VOLUME_NAME "The update volume" + +/* Compatibility flags of internal volumes */ +#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT +#define UBI_UPDATE_VOLUME_COMPAT UBI_COMPAT_REJECT + +/* The maximum number of volumes per one UBI device */ +#define UBI_MAX_VOLUMES 128 + +/* The maximum volume name length */ +#define UBI_VOL_NAME_MAX 127 + +/* Size of volume table records */ +#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vol_tbl_record) + +/* Size of volume table records without the ending CRC */ +#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(ubi32_t)) + +/** + * struct ubi_vol_tbl_record - a record in the volume table. + * + * @reserved_pebs: how many physical eraseblocks are reserved for this volume + * @alignment: volume alignment + * @data_pad: how many bytes are not used at the end of the eraseblocks to + * satisfy the requested alignment + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @padding1: reserved, zeroes + * @name_len: the volume name length + * @name: the volume name + * @padding2: reserved, zeroes + * @crc: a CRC32 checksum of the record + * + * The layout volume consists of 2 logical eraseblock, each of which contains + * the volume table (i.e., the volume table is duplicated). The volume table is + * an array of &struct ubi_vol_tbl_record objects indexed by the volume ID. + * + * If the size of the logical eraseblock is large enough to fit + * %UBI_MAX_VOLUMES, the volume table contains %UBI_MAX_VOLUMES records. + * Otherwise, it contains as much records as can be fit (i.e., size of logical + * eraseblock divided by sizeof(struct ubi_vol_tbl_record)). + * + * The @alignment field is specified when the volume is created and cannot be + * later changed. It may be useful, for example, when a block-oriented file + * system works on top of UBI. The @data_pad field is calculated using the + * logical eraseblock size and @alignment. The alignment must be multiple to the + * minimal flash I/O unit. If @alignment is 1, all the available space of + * eraseblocks is used. + * + * Empty records contain all zeroes and the CRC checksum of those zeroes. + */ +struct ubi_vol_tbl_record { + ubi32_t reserved_pebs; + ubi32_t alignment; + ubi32_t data_pad; + uint8_t vol_type; + uint8_t padding1; + ubi16_t name_len; + uint8_t name[UBI_VOL_NAME_MAX+1]; + uint8_t padding2[24]; + ubi32_t crc; +} __attribute__ ((packed)); + +#endif /* !__UBI_HEADER_H__ */ diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h new file mode 100644 index 0000000..0de5441 --- /dev/null +++ b/include/mtd/ubi-user.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#ifndef __UBI_USER_H__ +#define __UBI_USER_H__ + +/* + * UBI volume creation + * ~~~~~~~~~~~~~~~~~~~ + * + * UBI volumes are created via the %UBI_IOCMKVOL IOCTL command of UBI character + * device. A &struct ubi_mkvol_req object has to be properly filled and a + * pointer to it has to be passed to the IOCTL. + * + * UBI volume deletion + * ~~~~~~~~~~~~~~~~~~~ + * + * To delete a volume, the %UBI_IOCRMVOL IOCTL command of the UBI character + * device should be used. A pointer to the 32-bit volume ID hast to be passed + * to the IOCTL. + * + * UBI volume re-size + * ~~~~~~~~~~~~~~~~~~ + * + * To re-size a volume, the %UBI_IOCRSVOL IOCTL command of the UBI character + * device should be used. A &struct ubi_rsvol_req object has to be properly + * filled and a pointer to it has to be passed to the IOCTL. + * + * UBI volume update + * ~~~~~~~~~~~~~~~~~ + * + * Volume update should be done via the %UBI_IOCVOLUP IOCTL command of the + * corresponding UBI volume character device. A pointer to a 64-bit update + * size should be passed to the IOCTL. After then, UBI expects user to write + * this number of bytes to the volume character device. The update is finished + * when the claimed number of bytes is passed. So, the volume update sequence + * is something like: + * + * fd = open("/dev/my_volume"); + * ioctl(fd, UBI_IOCVOLUP, &image_size); + * write(fd, buf, image_size); + * close(fd); + */ + +/* + * When a new volume is created, users may either specify the volume number they + * want to create or to let UBI automatically assign a volume number using this + * constant. + */ +#define UBI_VOL_NUM_AUTO (-1) + +/* Maximum volume name length */ +#define UBI_MAX_VOLUME_NAME 127 + +/* IOCTL commands of UBI character devices */ + +#define UBI_IOC_MAGIC 'o' + +/* Create an UBI volume */ +#define UBI_IOCMKVOL _IOW(UBI_IOC_MAGIC, 0, struct ubi_mkvol_req) +/* Remove an UBI volume */ +#define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, int32_t) +/* Re-size an UBI volume */ +#define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req) + +/* IOCTL commands of UBI volume character devices */ + +#define UBI_VOL_IOC_MAGIC 'O' + +/* Start UBI volume update */ +#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t) +/* An eraseblock erasure command, used for debugging, disabled by default */ +#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t) + +/* + * UBI volume type constants. + * + * @UBI_DYNAMIC_VOLUME: dynamic volume + * @UBI_STATIC_VOLUME: static volume + */ +enum { + UBI_DYNAMIC_VOLUME = 3, + UBI_STATIC_VOLUME = 4 +}; + +/** + * struct ubi_mkvol_req - volume description data structure used in + * volume creation requests. + * + * @vol_id: volume number + * @alignment: volume alignment + * @bytes: volume size in bytes + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @padding: reserved for future, not used + * @name_len: volume name length + * @name: volume name + * + * This structure is used by userspace programs when creating new volumes. The + * @used_bytes field is only necessary when creating static volumes. + * + * The @alignment field specifies the required alignment of the volume logical + * eraseblock. This means, that the size of logical eraseblocks will be aligned + * to this number, i.e., + * (UBI device logical eraseblock size) mod (@alignment) = 0. + * + * To put it differently, the logical eraseblock of this volume may be slightly + * shortened in order to make it properly aligned. The alignment has to be + * multiple of the flash minimal input/output unit, or %1 to utilize the entire + * available space of logical eraseblocks. + * + * The @alignment field may be useful, for example, when one wants to maintain + * a block device on top of an UBI volume. In this case, it is desirable to fit + * an integer number of blocks in logical eraseblocks of this UBI volume. With + * alignment it is possible to update this volume using plane UBI volume image + * BLOBs, without caring about how to properly write them. + */ +struct ubi_mkvol_req { + int32_t vol_id; + int32_t alignment; + int64_t bytes; + int8_t vol_type; + int8_t padding[9]; + int16_t name_len; + __user const char *name; +} __attribute__ ((packed)); + +/** + * struct ubi_rsvol_req - a data structure used in volume re-size requests. + * + * @vol_id: ID of the volume to re-size + * @bytes: new size of the volume in bytes + * + * Re-sizing is possible for both dynamic and static volumes. But while dynamic + * volumes may be re-sized arbitrarily, static volumes cannot be made to be + * smaller then the number of bytes they bear. To arbitrarily shrink a static + * volume, it must be wiped out first (by means of volume update operation with + * zero number of bytes). + */ +struct ubi_rsvol_req { + int64_t bytes; + int32_t vol_id; +} __attribute__ ((packed)); + +#endif /* __UBI_USER_H__ */ diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index eeac3e3..bc6e110 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -265,6 +265,7 @@ const char *kallsyms_lookup(unsigned lon return NULL; } +EXPORT_SYMBOL_GPL(kallsyms_lookup); /* Replace "%s" in format with address, or returns -errno. */ void __print_symbol(const char *fmt, unsigned long address)