]> git.sur5r.net Git - u-boot/commitdiff
UBI: Add basic UBI support to U-Boot (Part 2/8)
authorKyungmin Park <kyungmin.park@samsung.com>
Wed, 19 Nov 2008 15:25:44 +0000 (16:25 +0100)
committerStefan Roese <sr@denx.de>
Wed, 19 Nov 2008 19:34:39 +0000 (20:34 +0100)
This patch adds basic UBI (Unsorted Block Image) support to U-Boot.
It's based on the Linux UBI version and basically has a "OS"
translation wrapper that defines most Linux specific calls
(spin_lock() etc.) into no-ops. Some source code parts have been
uncommented by "#ifdef UBI_LINUX". This makes it easier to compare
this version with the Linux version and simplifies future UBI
ports/bug-fixes from the Linux version.

Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Stefan Roese <sr@denx.de>
drivers/mtd/ubi/crc32defs.h [new file with mode: 0644]
drivers/mtd/ubi/crc32table.h [new file with mode: 0644]
drivers/mtd/ubi/debug.c [new file with mode: 0644]
drivers/mtd/ubi/debug.h [new file with mode: 0644]
drivers/mtd/ubi/eba.c [new file with mode: 0644]

diff --git a/drivers/mtd/ubi/crc32defs.h b/drivers/mtd/ubi/crc32defs.h
new file mode 100644 (file)
index 0000000..f5a5401
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * There are multiple 16-bit CRC polynomials in common use, but this is
+ * *the* standard CRC-32 polynomial, first popularized by Ethernet.
+ * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
+ */
+#define CRCPOLY_LE 0xedb88320
+#define CRCPOLY_BE 0x04c11db7
+
+/* How many bits at a time to use.  Requires a table of 4<<CRC_xx_BITS bytes. */
+/* For less performance-sensitive, use 4 */
+#ifndef CRC_LE_BITS
+# define CRC_LE_BITS 8
+#endif
+#ifndef CRC_BE_BITS
+# define CRC_BE_BITS 8
+#endif
+
+/*
+ * Little-endian CRC computation.  Used with serial bit streams sent
+ * lsbit-first.  Be sure to use cpu_to_le32() to append the computed CRC.
+ */
+#if CRC_LE_BITS > 8 || CRC_LE_BITS < 1 || CRC_LE_BITS & CRC_LE_BITS-1
+# error CRC_LE_BITS must be a power of 2 between 1 and 8
+#endif
+
+/*
+ * Big-endian CRC computation.  Used with serial bit streams sent
+ * msbit-first.  Be sure to use cpu_to_be32() to append the computed CRC.
+ */
+#if CRC_BE_BITS > 8 || CRC_BE_BITS < 1 || CRC_BE_BITS & CRC_BE_BITS-1
+# error CRC_BE_BITS must be a power of 2 between 1 and 8
+#endif
diff --git a/drivers/mtd/ubi/crc32table.h b/drivers/mtd/ubi/crc32table.h
new file mode 100644 (file)
index 0000000..0438af4
--- /dev/null
@@ -0,0 +1,136 @@
+/* this file is generated - do not edit */
+
+static const u32 crc32table_le[] = {
+tole(0x00000000L), tole(0x77073096L), tole(0xee0e612cL), tole(0x990951baL),
+tole(0x076dc419L), tole(0x706af48fL), tole(0xe963a535L), tole(0x9e6495a3L),
+tole(0x0edb8832L), tole(0x79dcb8a4L), tole(0xe0d5e91eL), tole(0x97d2d988L),
+tole(0x09b64c2bL), tole(0x7eb17cbdL), tole(0xe7b82d07L), tole(0x90bf1d91L),
+tole(0x1db71064L), tole(0x6ab020f2L), tole(0xf3b97148L), tole(0x84be41deL),
+tole(0x1adad47dL), tole(0x6ddde4ebL), tole(0xf4d4b551L), tole(0x83d385c7L),
+tole(0x136c9856L), tole(0x646ba8c0L), tole(0xfd62f97aL), tole(0x8a65c9ecL),
+tole(0x14015c4fL), tole(0x63066cd9L), tole(0xfa0f3d63L), tole(0x8d080df5L),
+tole(0x3b6e20c8L), tole(0x4c69105eL), tole(0xd56041e4L), tole(0xa2677172L),
+tole(0x3c03e4d1L), tole(0x4b04d447L), tole(0xd20d85fdL), tole(0xa50ab56bL),
+tole(0x35b5a8faL), tole(0x42b2986cL), tole(0xdbbbc9d6L), tole(0xacbcf940L),
+tole(0x32d86ce3L), tole(0x45df5c75L), tole(0xdcd60dcfL), tole(0xabd13d59L),
+tole(0x26d930acL), tole(0x51de003aL), tole(0xc8d75180L), tole(0xbfd06116L),
+tole(0x21b4f4b5L), tole(0x56b3c423L), tole(0xcfba9599L), tole(0xb8bda50fL),
+tole(0x2802b89eL), tole(0x5f058808L), tole(0xc60cd9b2L), tole(0xb10be924L),
+tole(0x2f6f7c87L), tole(0x58684c11L), tole(0xc1611dabL), tole(0xb6662d3dL),
+tole(0x76dc4190L), tole(0x01db7106L), tole(0x98d220bcL), tole(0xefd5102aL),
+tole(0x71b18589L), tole(0x06b6b51fL), tole(0x9fbfe4a5L), tole(0xe8b8d433L),
+tole(0x7807c9a2L), tole(0x0f00f934L), tole(0x9609a88eL), tole(0xe10e9818L),
+tole(0x7f6a0dbbL), tole(0x086d3d2dL), tole(0x91646c97L), tole(0xe6635c01L),
+tole(0x6b6b51f4L), tole(0x1c6c6162L), tole(0x856530d8L), tole(0xf262004eL),
+tole(0x6c0695edL), tole(0x1b01a57bL), tole(0x8208f4c1L), tole(0xf50fc457L),
+tole(0x65b0d9c6L), tole(0x12b7e950L), tole(0x8bbeb8eaL), tole(0xfcb9887cL),
+tole(0x62dd1ddfL), tole(0x15da2d49L), tole(0x8cd37cf3L), tole(0xfbd44c65L),
+tole(0x4db26158L), tole(0x3ab551ceL), tole(0xa3bc0074L), tole(0xd4bb30e2L),
+tole(0x4adfa541L), tole(0x3dd895d7L), tole(0xa4d1c46dL), tole(0xd3d6f4fbL),
+tole(0x4369e96aL), tole(0x346ed9fcL), tole(0xad678846L), tole(0xda60b8d0L),
+tole(0x44042d73L), tole(0x33031de5L), tole(0xaa0a4c5fL), tole(0xdd0d7cc9L),
+tole(0x5005713cL), tole(0x270241aaL), tole(0xbe0b1010L), tole(0xc90c2086L),
+tole(0x5768b525L), tole(0x206f85b3L), tole(0xb966d409L), tole(0xce61e49fL),
+tole(0x5edef90eL), tole(0x29d9c998L), tole(0xb0d09822L), tole(0xc7d7a8b4L),
+tole(0x59b33d17L), tole(0x2eb40d81L), tole(0xb7bd5c3bL), tole(0xc0ba6cadL),
+tole(0xedb88320L), tole(0x9abfb3b6L), tole(0x03b6e20cL), tole(0x74b1d29aL),
+tole(0xead54739L), tole(0x9dd277afL), tole(0x04db2615L), tole(0x73dc1683L),
+tole(0xe3630b12L), tole(0x94643b84L), tole(0x0d6d6a3eL), tole(0x7a6a5aa8L),
+tole(0xe40ecf0bL), tole(0x9309ff9dL), tole(0x0a00ae27L), tole(0x7d079eb1L),
+tole(0xf00f9344L), tole(0x8708a3d2L), tole(0x1e01f268L), tole(0x6906c2feL),
+tole(0xf762575dL), tole(0x806567cbL), tole(0x196c3671L), tole(0x6e6b06e7L),
+tole(0xfed41b76L), tole(0x89d32be0L), tole(0x10da7a5aL), tole(0x67dd4accL),
+tole(0xf9b9df6fL), tole(0x8ebeeff9L), tole(0x17b7be43L), tole(0x60b08ed5L),
+tole(0xd6d6a3e8L), tole(0xa1d1937eL), tole(0x38d8c2c4L), tole(0x4fdff252L),
+tole(0xd1bb67f1L), tole(0xa6bc5767L), tole(0x3fb506ddL), tole(0x48b2364bL),
+tole(0xd80d2bdaL), tole(0xaf0a1b4cL), tole(0x36034af6L), tole(0x41047a60L),
+tole(0xdf60efc3L), tole(0xa867df55L), tole(0x316e8eefL), tole(0x4669be79L),
+tole(0xcb61b38cL), tole(0xbc66831aL), tole(0x256fd2a0L), tole(0x5268e236L),
+tole(0xcc0c7795L), tole(0xbb0b4703L), tole(0x220216b9L), tole(0x5505262fL),
+tole(0xc5ba3bbeL), tole(0xb2bd0b28L), tole(0x2bb45a92L), tole(0x5cb36a04L),
+tole(0xc2d7ffa7L), tole(0xb5d0cf31L), tole(0x2cd99e8bL), tole(0x5bdeae1dL),
+tole(0x9b64c2b0L), tole(0xec63f226L), tole(0x756aa39cL), tole(0x026d930aL),
+tole(0x9c0906a9L), tole(0xeb0e363fL), tole(0x72076785L), tole(0x05005713L),
+tole(0x95bf4a82L), tole(0xe2b87a14L), tole(0x7bb12baeL), tole(0x0cb61b38L),
+tole(0x92d28e9bL), tole(0xe5d5be0dL), tole(0x7cdcefb7L), tole(0x0bdbdf21L),
+tole(0x86d3d2d4L), tole(0xf1d4e242L), tole(0x68ddb3f8L), tole(0x1fda836eL),
+tole(0x81be16cdL), tole(0xf6b9265bL), tole(0x6fb077e1L), tole(0x18b74777L),
+tole(0x88085ae6L), tole(0xff0f6a70L), tole(0x66063bcaL), tole(0x11010b5cL),
+tole(0x8f659effL), tole(0xf862ae69L), tole(0x616bffd3L), tole(0x166ccf45L),
+tole(0xa00ae278L), tole(0xd70dd2eeL), tole(0x4e048354L), tole(0x3903b3c2L),
+tole(0xa7672661L), tole(0xd06016f7L), tole(0x4969474dL), tole(0x3e6e77dbL),
+tole(0xaed16a4aL), tole(0xd9d65adcL), tole(0x40df0b66L), tole(0x37d83bf0L),
+tole(0xa9bcae53L), tole(0xdebb9ec5L), tole(0x47b2cf7fL), tole(0x30b5ffe9L),
+tole(0xbdbdf21cL), tole(0xcabac28aL), tole(0x53b39330L), tole(0x24b4a3a6L),
+tole(0xbad03605L), tole(0xcdd70693L), tole(0x54de5729L), tole(0x23d967bfL),
+tole(0xb3667a2eL), tole(0xc4614ab8L), tole(0x5d681b02L), tole(0x2a6f2b94L),
+tole(0xb40bbe37L), tole(0xc30c8ea1L), tole(0x5a05df1bL), tole(0x2d02ef8dL)
+};
+#ifdef UBI_LINUX
+static const u32 crc32table_be[] = {
+tobe(0x00000000L), tobe(0x04c11db7L), tobe(0x09823b6eL), tobe(0x0d4326d9L),
+tobe(0x130476dcL), tobe(0x17c56b6bL), tobe(0x1a864db2L), tobe(0x1e475005L),
+tobe(0x2608edb8L), tobe(0x22c9f00fL), tobe(0x2f8ad6d6L), tobe(0x2b4bcb61L),
+tobe(0x350c9b64L), tobe(0x31cd86d3L), tobe(0x3c8ea00aL), tobe(0x384fbdbdL),
+tobe(0x4c11db70L), tobe(0x48d0c6c7L), tobe(0x4593e01eL), tobe(0x4152fda9L),
+tobe(0x5f15adacL), tobe(0x5bd4b01bL), tobe(0x569796c2L), tobe(0x52568b75L),
+tobe(0x6a1936c8L), tobe(0x6ed82b7fL), tobe(0x639b0da6L), tobe(0x675a1011L),
+tobe(0x791d4014L), tobe(0x7ddc5da3L), tobe(0x709f7b7aL), tobe(0x745e66cdL),
+tobe(0x9823b6e0L), tobe(0x9ce2ab57L), tobe(0x91a18d8eL), tobe(0x95609039L),
+tobe(0x8b27c03cL), tobe(0x8fe6dd8bL), tobe(0x82a5fb52L), tobe(0x8664e6e5L),
+tobe(0xbe2b5b58L), tobe(0xbaea46efL), tobe(0xb7a96036L), tobe(0xb3687d81L),
+tobe(0xad2f2d84L), tobe(0xa9ee3033L), tobe(0xa4ad16eaL), tobe(0xa06c0b5dL),
+tobe(0xd4326d90L), tobe(0xd0f37027L), tobe(0xddb056feL), tobe(0xd9714b49L),
+tobe(0xc7361b4cL), tobe(0xc3f706fbL), tobe(0xceb42022L), tobe(0xca753d95L),
+tobe(0xf23a8028L), tobe(0xf6fb9d9fL), tobe(0xfbb8bb46L), tobe(0xff79a6f1L),
+tobe(0xe13ef6f4L), tobe(0xe5ffeb43L), tobe(0xe8bccd9aL), tobe(0xec7dd02dL),
+tobe(0x34867077L), tobe(0x30476dc0L), tobe(0x3d044b19L), tobe(0x39c556aeL),
+tobe(0x278206abL), tobe(0x23431b1cL), tobe(0x2e003dc5L), tobe(0x2ac12072L),
+tobe(0x128e9dcfL), tobe(0x164f8078L), tobe(0x1b0ca6a1L), tobe(0x1fcdbb16L),
+tobe(0x018aeb13L), tobe(0x054bf6a4L), tobe(0x0808d07dL), tobe(0x0cc9cdcaL),
+tobe(0x7897ab07L), tobe(0x7c56b6b0L), tobe(0x71159069L), tobe(0x75d48ddeL),
+tobe(0x6b93dddbL), tobe(0x6f52c06cL), tobe(0x6211e6b5L), tobe(0x66d0fb02L),
+tobe(0x5e9f46bfL), tobe(0x5a5e5b08L), tobe(0x571d7dd1L), tobe(0x53dc6066L),
+tobe(0x4d9b3063L), tobe(0x495a2dd4L), tobe(0x44190b0dL), tobe(0x40d816baL),
+tobe(0xaca5c697L), tobe(0xa864db20L), tobe(0xa527fdf9L), tobe(0xa1e6e04eL),
+tobe(0xbfa1b04bL), tobe(0xbb60adfcL), tobe(0xb6238b25L), tobe(0xb2e29692L),
+tobe(0x8aad2b2fL), tobe(0x8e6c3698L), tobe(0x832f1041L), tobe(0x87ee0df6L),
+tobe(0x99a95df3L), tobe(0x9d684044L), tobe(0x902b669dL), tobe(0x94ea7b2aL),
+tobe(0xe0b41de7L), tobe(0xe4750050L), tobe(0xe9362689L), tobe(0xedf73b3eL),
+tobe(0xf3b06b3bL), tobe(0xf771768cL), tobe(0xfa325055L), tobe(0xfef34de2L),
+tobe(0xc6bcf05fL), tobe(0xc27dede8L), tobe(0xcf3ecb31L), tobe(0xcbffd686L),
+tobe(0xd5b88683L), tobe(0xd1799b34L), tobe(0xdc3abdedL), tobe(0xd8fba05aL),
+tobe(0x690ce0eeL), tobe(0x6dcdfd59L), tobe(0x608edb80L), tobe(0x644fc637L),
+tobe(0x7a089632L), tobe(0x7ec98b85L), tobe(0x738aad5cL), tobe(0x774bb0ebL),
+tobe(0x4f040d56L), tobe(0x4bc510e1L), tobe(0x46863638L), tobe(0x42472b8fL),
+tobe(0x5c007b8aL), tobe(0x58c1663dL), tobe(0x558240e4L), tobe(0x51435d53L),
+tobe(0x251d3b9eL), tobe(0x21dc2629L), tobe(0x2c9f00f0L), tobe(0x285e1d47L),
+tobe(0x36194d42L), tobe(0x32d850f5L), tobe(0x3f9b762cL), tobe(0x3b5a6b9bL),
+tobe(0x0315d626L), tobe(0x07d4cb91L), tobe(0x0a97ed48L), tobe(0x0e56f0ffL),
+tobe(0x1011a0faL), tobe(0x14d0bd4dL), tobe(0x19939b94L), tobe(0x1d528623L),
+tobe(0xf12f560eL), tobe(0xf5ee4bb9L), tobe(0xf8ad6d60L), tobe(0xfc6c70d7L),
+tobe(0xe22b20d2L), tobe(0xe6ea3d65L), tobe(0xeba91bbcL), tobe(0xef68060bL),
+tobe(0xd727bbb6L), tobe(0xd3e6a601L), tobe(0xdea580d8L), tobe(0xda649d6fL),
+tobe(0xc423cd6aL), tobe(0xc0e2d0ddL), tobe(0xcda1f604L), tobe(0xc960ebb3L),
+tobe(0xbd3e8d7eL), tobe(0xb9ff90c9L), tobe(0xb4bcb610L), tobe(0xb07daba7L),
+tobe(0xae3afba2L), tobe(0xaafbe615L), tobe(0xa7b8c0ccL), tobe(0xa379dd7bL),
+tobe(0x9b3660c6L), tobe(0x9ff77d71L), tobe(0x92b45ba8L), tobe(0x9675461fL),
+tobe(0x8832161aL), tobe(0x8cf30badL), tobe(0x81b02d74L), tobe(0x857130c3L),
+tobe(0x5d8a9099L), tobe(0x594b8d2eL), tobe(0x5408abf7L), tobe(0x50c9b640L),
+tobe(0x4e8ee645L), tobe(0x4a4ffbf2L), tobe(0x470cdd2bL), tobe(0x43cdc09cL),
+tobe(0x7b827d21L), tobe(0x7f436096L), tobe(0x7200464fL), tobe(0x76c15bf8L),
+tobe(0x68860bfdL), tobe(0x6c47164aL), tobe(0x61043093L), tobe(0x65c52d24L),
+tobe(0x119b4be9L), tobe(0x155a565eL), tobe(0x18197087L), tobe(0x1cd86d30L),
+tobe(0x029f3d35L), tobe(0x065e2082L), tobe(0x0b1d065bL), tobe(0x0fdc1becL),
+tobe(0x3793a651L), tobe(0x3352bbe6L), tobe(0x3e119d3fL), tobe(0x3ad08088L),
+tobe(0x2497d08dL), tobe(0x2056cd3aL), tobe(0x2d15ebe3L), tobe(0x29d4f654L),
+tobe(0xc5a92679L), tobe(0xc1683bceL), tobe(0xcc2b1d17L), tobe(0xc8ea00a0L),
+tobe(0xd6ad50a5L), tobe(0xd26c4d12L), tobe(0xdf2f6bcbL), tobe(0xdbee767cL),
+tobe(0xe3a1cbc1L), tobe(0xe760d676L), tobe(0xea23f0afL), tobe(0xeee2ed18L),
+tobe(0xf0a5bd1dL), tobe(0xf464a0aaL), tobe(0xf9278673L), tobe(0xfde69bc4L),
+tobe(0x89b8fd09L), tobe(0x8d79e0beL), tobe(0x803ac667L), tobe(0x84fbdbd0L),
+tobe(0x9abc8bd5L), tobe(0x9e7d9662L), tobe(0x933eb0bbL), tobe(0x97ffad0cL),
+tobe(0xafb010b1L), tobe(0xab710d06L), tobe(0xa6322bdfL), tobe(0xa2f33668L),
+tobe(0xbcb4666dL), tobe(0xb8757bdaL), tobe(0xb5365d03L), tobe(0xb1f740b4L)
+};
+#endif
diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c
new file mode 100644 (file)
index 0000000..492ab5c
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * 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 Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * Here we keep all the UBI debugging stuff which should normally be disabled
+ * and compiled-out, but it is extremely helpful when hunting bugs or doing big
+ * changes.
+ */
+#include <ubi_uboot.h>
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG
+
+#include "ubi.h"
+
+/**
+ * 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)
+{
+       dbg_msg("erase counter header dump:");
+       dbg_msg("magic          %#08x", be32_to_cpu(ec_hdr->magic));
+       dbg_msg("version        %d",    (int)ec_hdr->version);
+       dbg_msg("ec             %llu",  (long long)be64_to_cpu(ec_hdr->ec));
+       dbg_msg("vid_hdr_offset %d",    be32_to_cpu(ec_hdr->vid_hdr_offset));
+       dbg_msg("data_offset    %d",    be32_to_cpu(ec_hdr->data_offset));
+       dbg_msg("hdr_crc        %#08x", be32_to_cpu(ec_hdr->hdr_crc));
+       dbg_msg("erase counter header hexdump:");
+       print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
+                      ec_hdr, UBI_EC_HDR_SIZE, 1);
+}
+
+/**
+ * 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)
+{
+       dbg_msg("volume identifier header dump:");
+       dbg_msg("magic     %08x", be32_to_cpu(vid_hdr->magic));
+       dbg_msg("version   %d",   (int)vid_hdr->version);
+       dbg_msg("vol_type  %d",   (int)vid_hdr->vol_type);
+       dbg_msg("copy_flag %d",   (int)vid_hdr->copy_flag);
+       dbg_msg("compat    %d",   (int)vid_hdr->compat);
+       dbg_msg("vol_id    %d",   be32_to_cpu(vid_hdr->vol_id));
+       dbg_msg("lnum      %d",   be32_to_cpu(vid_hdr->lnum));
+       dbg_msg("leb_ver   %u",   be32_to_cpu(vid_hdr->leb_ver));
+       dbg_msg("data_size %d",   be32_to_cpu(vid_hdr->data_size));
+       dbg_msg("used_ebs  %d",   be32_to_cpu(vid_hdr->used_ebs));
+       dbg_msg("data_pad  %d",   be32_to_cpu(vid_hdr->data_pad));
+       dbg_msg("sqnum     %llu",
+               (unsigned long long)be64_to_cpu(vid_hdr->sqnum));
+       dbg_msg("hdr_crc   %08x", be32_to_cpu(vid_hdr->hdr_crc));
+       dbg_msg("volume identifier header hexdump:");
+}
+
+/**
+ * ubi_dbg_dump_vol_info- dump volume information.
+ * @vol: UBI volume description object
+ */
+void ubi_dbg_dump_vol_info(const struct ubi_volume *vol)
+{
+       dbg_msg("volume information dump:");
+       dbg_msg("vol_id          %d", vol->vol_id);
+       dbg_msg("reserved_pebs   %d", vol->reserved_pebs);
+       dbg_msg("alignment       %d", vol->alignment);
+       dbg_msg("data_pad        %d", vol->data_pad);
+       dbg_msg("vol_type        %d", vol->vol_type);
+       dbg_msg("name_len        %d", vol->name_len);
+       dbg_msg("usable_leb_size %d", vol->usable_leb_size);
+       dbg_msg("used_ebs        %d", vol->used_ebs);
+       dbg_msg("used_bytes      %lld", vol->used_bytes);
+       dbg_msg("last_eb_bytes   %d", vol->last_eb_bytes);
+       dbg_msg("corrupted       %d", vol->corrupted);
+       dbg_msg("upd_marker      %d", vol->upd_marker);
+
+       if (vol->name_len <= UBI_VOL_NAME_MAX &&
+           strnlen(vol->name, vol->name_len + 1) == vol->name_len) {
+               dbg_msg("name            %s", vol->name);
+       } else {
+               dbg_msg("the 1st 5 characters of the name: %c%c%c%c%c",
+                       vol->name[0], vol->name[1], vol->name[2],
+                       vol->name[3], vol->name[4]);
+       }
+}
+
+/**
+ * ubi_dbg_dump_vtbl_record - dump a &struct ubi_vtbl_record object.
+ * @r: the object to dump
+ * @idx: volume table index
+ */
+void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx)
+{
+       int name_len = be16_to_cpu(r->name_len);
+
+       dbg_msg("volume table record %d dump:", idx);
+       dbg_msg("reserved_pebs   %d", be32_to_cpu(r->reserved_pebs));
+       dbg_msg("alignment       %d", be32_to_cpu(r->alignment));
+       dbg_msg("data_pad        %d", be32_to_cpu(r->data_pad));
+       dbg_msg("vol_type        %d", (int)r->vol_type);
+       dbg_msg("upd_marker      %d", (int)r->upd_marker);
+       dbg_msg("name_len        %d", name_len);
+
+       if (r->name[0] == '\0') {
+               dbg_msg("name            NULL");
+               return;
+       }
+
+       if (name_len <= UBI_VOL_NAME_MAX &&
+           strnlen(&r->name[0], name_len + 1) == name_len) {
+               dbg_msg("name            %s", &r->name[0]);
+       } else {
+               dbg_msg("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]);
+       }
+       dbg_msg("crc             %#08x", be32_to_cpu(r->crc));
+}
+
+/**
+ * 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)
+{
+       dbg_msg("volume scanning information dump:");
+       dbg_msg("vol_id         %d", sv->vol_id);
+       dbg_msg("highest_lnum   %d", sv->highest_lnum);
+       dbg_msg("leb_count      %d", sv->leb_count);
+       dbg_msg("compat         %d", sv->compat);
+       dbg_msg("vol_type       %d", sv->vol_type);
+       dbg_msg("used_ebs       %d", sv->used_ebs);
+       dbg_msg("last_data_size %d", sv->last_data_size);
+       dbg_msg("data_pad       %d", sv->data_pad);
+}
+
+/**
+ * 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)
+{
+       dbg_msg("eraseblock scanning information dump:");
+       dbg_msg("ec       %d", seb->ec);
+       dbg_msg("pnum     %d", seb->pnum);
+       if (type == 0) {
+               dbg_msg("lnum     %d", seb->lnum);
+               dbg_msg("scrub    %d", seb->scrub);
+               dbg_msg("sqnum    %llu", seb->sqnum);
+               dbg_msg("leb_ver  %u", seb->leb_ver);
+       }
+}
+
+/**
+ * ubi_dbg_dump_mkvol_req - dump a &struct ubi_mkvol_req object.
+ * @req: the object to dump
+ */
+void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req)
+{
+       char nm[17];
+
+       dbg_msg("volume creation request dump:");
+       dbg_msg("vol_id    %d",   req->vol_id);
+       dbg_msg("alignment %d",   req->alignment);
+       dbg_msg("bytes     %lld", (long long)req->bytes);
+       dbg_msg("vol_type  %d",   req->vol_type);
+       dbg_msg("name_len  %d",   req->name_len);
+
+       memcpy(nm, req->name, 16);
+       nm[16] = 0;
+       dbg_msg("the 1st 16 characters of the name: %s", nm);
+}
+
+#endif /* CONFIG_MTD_UBI_DEBUG_MSG */
diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h
new file mode 100644 (file)
index 0000000..b44380b
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * 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 Bityutskiy (Битюцкий Артём)
+ */
+
+#ifndef __UBI_DEBUG_H__
+#define __UBI_DEBUG_H__
+
+#ifdef CONFIG_MTD_UBI_DEBUG
+#ifdef UBI_LINUX
+#include <linux/random.h>
+#endif
+
+#define ubi_assert(expr)  BUG_ON(!(expr))
+#define dbg_err(fmt, ...) ubi_err(fmt, ##__VA_ARGS__)
+#else
+#define ubi_assert(expr)  ({})
+#define dbg_err(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT
+#define DBG_DISABLE_BGT 1
+#else
+#define DBG_DISABLE_BGT 0
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG
+/* Generic debugging message */
+#define dbg_msg(fmt, ...)                                    \
+       printk(KERN_DEBUG "UBI DBG: %s: " fmt "\n", \
+              __FUNCTION__, ##__VA_ARGS__)
+
+#define ubi_dbg_dump_stack() dump_stack()
+
+struct ubi_ec_hdr;
+struct ubi_vid_hdr;
+struct ubi_volume;
+struct ubi_vtbl_record;
+struct ubi_scan_volume;
+struct ubi_scan_leb;
+struct ubi_mkvol_req;
+
+void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr);
+void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr);
+void ubi_dbg_dump_vol_info(const struct ubi_volume *vol);
+void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx);
+void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv);
+void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type);
+void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req);
+
+#else
+
+#define dbg_msg(fmt, ...)    ({})
+#define ubi_dbg_dump_stack() ({})
+#define ubi_dbg_dump_ec_hdr(ec_hdr)      ({})
+#define ubi_dbg_dump_vid_hdr(vid_hdr)    ({})
+#define ubi_dbg_dump_vol_info(vol)       ({})
+#define ubi_dbg_dump_vtbl_record(r, idx) ({})
+#define ubi_dbg_dump_sv(sv)              ({})
+#define ubi_dbg_dump_seb(seb, type)      ({})
+#define ubi_dbg_dump_mkvol_req(req)      ({})
+
+#endif /* CONFIG_MTD_UBI_DEBUG_MSG */
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA
+/* Messages from the eraseblock association unit */
+#define dbg_eba(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
+#else
+#define dbg_eba(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL
+/* Messages from the wear-leveling unit */
+#define dbg_wl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
+#else
+#define dbg_wl(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO
+/* Messages from the input/output unit */
+#define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
+#else
+#define dbg_io(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD
+/* Initialization and build messages */
+#define dbg_bld(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
+#else
+#define dbg_bld(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS
+/**
+ * ubi_dbg_is_bitflip - if it is time to emulate a bit-flip.
+ *
+ * Returns non-zero if a bit-flip should be emulated, otherwise returns zero.
+ */
+static inline int ubi_dbg_is_bitflip(void)
+{
+       return !(random32() % 200);
+}
+#else
+#define ubi_dbg_is_bitflip() 0
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES
+/**
+ * ubi_dbg_is_write_failure - if it is time to emulate a write failure.
+ *
+ * Returns non-zero if a write failure should be emulated, otherwise returns
+ * zero.
+ */
+static inline int ubi_dbg_is_write_failure(void)
+{
+       return !(random32() % 500);
+}
+#else
+#define ubi_dbg_is_write_failure() 0
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES
+/**
+ * ubi_dbg_is_erase_failure - if its time to emulate an erase failure.
+ *
+ * Returns non-zero if an erase failure should be emulated, otherwise returns
+ * zero.
+ */
+static inline int ubi_dbg_is_erase_failure(void)
+{
+               return !(random32() % 400);
+}
+#else
+#define ubi_dbg_is_erase_failure() 0
+#endif
+
+#endif /* !__UBI_DEBUG_H__ */
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
new file mode 100644 (file)
index 0000000..d523c94
--- /dev/null
@@ -0,0 +1,1256 @@
+/*
+ * 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 Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * The UBI Eraseblock Association (EBA) unit.
+ *
+ * This unit is responsible for I/O to/from logical eraseblock.
+ *
+ * Although in this implementation the EBA table is fully kept and managed in
+ * RAM, which assumes poor scalability, it might be (partially) maintained on
+ * flash in future implementations.
+ *
+ * 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_ltree_entry objects. They are indexed by
+ * (@vol_id, @lnum) pairs.
+ *
+ * EBA also maintains the global sequence counter which is incremented each
+ * time a logical eraseblock is mapped to a physical eraseblock and it is
+ * stored in the volume identifier header. This means that each VID header has
+ * a unique sequence number. The sequence number is only increased an we assume
+ * 64 bits is enough to never overflow.
+ */
+
+#ifdef UBI_LINUX
+#include <linux/slab.h>
+#include <linux/crc32.h>
+#include <linux/err.h>
+#endif
+
+#include <ubi_uboot.h>
+#include "ubi.h"
+
+/* Number of physical eraseblocks reserved for atomic LEB change operation */
+#define EBA_RESERVED_PEBS 1
+
+/**
+ * next_sqnum - get next sequence number.
+ * @ubi: UBI device description object
+ *
+ * This function returns next sequence number to use, which is just the current
+ * global sequence counter value. It also increases the global sequence
+ * counter.
+ */
+static unsigned long long next_sqnum(struct ubi_device *ubi)
+{
+       unsigned long long sqnum;
+
+       spin_lock(&ubi->ltree_lock);
+       sqnum = ubi->global_sqnum++;
+       spin_unlock(&ubi->ltree_lock);
+
+       return sqnum;
+}
+
+/**
+ * ubi_get_compat - get compatibility flags of a volume.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ *
+ * This function returns compatibility flags for an internal volume. User
+ * volumes have no compatibility flags, so %0 is returned.
+ */
+static int ubi_get_compat(const struct ubi_device *ubi, int vol_id)
+{
+       if (vol_id == UBI_LAYOUT_VOLUME_ID)
+               return UBI_LAYOUT_VOLUME_COMPAT;
+       return 0;
+}
+
+/**
+ * ltree_lookup - look up the lock tree.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function returns a pointer to the corresponding &struct ubi_ltree_entry
+ * object if the logical eraseblock is locked and %NULL if it is not.
+ * @ubi->ltree_lock has to be locked.
+ */
+static struct ubi_ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id,
+                                           int lnum)
+{
+       struct rb_node *p;
+
+       p = ubi->ltree.rb_node;
+       while (p) {
+               struct ubi_ltree_entry *le;
+
+               le = rb_entry(p, struct ubi_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;
+}
+
+/**
+ * ltree_add_entry - add new entry to the lock tree.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function adds new entry for logical eraseblock (@vol_id, @lnum) to the
+ * lock tree. If such entry is already there, its usage counter is increased.
+ * Returns pointer to the lock tree entry or %-ENOMEM if memory allocation
+ * failed.
+ */
+static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi,
+                                              int vol_id, int lnum)
+{
+       struct ubi_ltree_entry *le, *le1, *le_free;
+
+       le = kmalloc(sizeof(struct ubi_ltree_entry), GFP_NOFS);
+       if (!le)
+               return ERR_PTR(-ENOMEM);
+
+       le->users = 0;
+       init_rwsem(&le->mutex);
+       le->vol_id = vol_id;
+       le->lnum = lnum;
+
+       spin_lock(&ubi->ltree_lock);
+       le1 = ltree_lookup(ubi, 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
+                * @ubi->ltree RB-tree.
+                */
+               le_free = NULL;
+
+               p = &ubi->ltree.rb_node;
+               while (*p) {
+                       parent = *p;
+                       le1 = rb_entry(parent, struct ubi_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, &ubi->ltree);
+       }
+       le->users += 1;
+       spin_unlock(&ubi->ltree_lock);
+
+       if (le_free)
+               kfree(le_free);
+
+       return le;
+}
+
+/**
+ * leb_read_lock - lock logical eraseblock for reading.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function locks a logical eraseblock for reading. Returns zero in case
+ * of success and a negative error code in case of failure.
+ */
+static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+       struct ubi_ltree_entry *le;
+
+       le = ltree_add_entry(ubi, vol_id, lnum);
+       if (IS_ERR(le))
+               return PTR_ERR(le);
+       down_read(&le->mutex);
+       return 0;
+}
+
+/**
+ * leb_read_unlock - unlock logical eraseblock.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ */
+static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+       int _free = 0;
+       struct ubi_ltree_entry *le;
+
+       spin_lock(&ubi->ltree_lock);
+       le = ltree_lookup(ubi, vol_id, lnum);
+       le->users -= 1;
+       ubi_assert(le->users >= 0);
+       if (le->users == 0) {
+               rb_erase(&le->rb, &ubi->ltree);
+               _free = 1;
+       }
+       spin_unlock(&ubi->ltree_lock);
+
+       up_read(&le->mutex);
+       if (_free)
+               kfree(le);
+}
+
+/**
+ * leb_write_lock - lock logical eraseblock for writing.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function locks a logical eraseblock for writing. Returns zero in case
+ * of success and a negative error code in case of failure.
+ */
+static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+       struct ubi_ltree_entry *le;
+
+       le = ltree_add_entry(ubi, vol_id, lnum);
+       if (IS_ERR(le))
+               return PTR_ERR(le);
+       down_write(&le->mutex);
+       return 0;
+}
+
+/**
+ * leb_write_lock - lock logical eraseblock for writing.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function locks a logical eraseblock for writing if there is no
+ * contention and does nothing if there is contention. Returns %0 in case of
+ * success, %1 in case of contention, and and a negative error code in case of
+ * failure.
+ */
+static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+       int _free;
+       struct ubi_ltree_entry *le;
+
+       le = ltree_add_entry(ubi, vol_id, lnum);
+       if (IS_ERR(le))
+               return PTR_ERR(le);
+       if (down_write_trylock(&le->mutex))
+               return 0;
+
+       /* Contention, cancel */
+       spin_lock(&ubi->ltree_lock);
+       le->users -= 1;
+       ubi_assert(le->users >= 0);
+       if (le->users == 0) {
+               rb_erase(&le->rb, &ubi->ltree);
+               _free = 1;
+       } else
+               _free = 0;
+       spin_unlock(&ubi->ltree_lock);
+       if (_free)
+               kfree(le);
+
+       return 1;
+}
+
+/**
+ * leb_write_unlock - unlock logical eraseblock.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ */
+static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+       int _free;
+       struct ubi_ltree_entry *le;
+
+       spin_lock(&ubi->ltree_lock);
+       le = ltree_lookup(ubi, vol_id, lnum);
+       le->users -= 1;
+       ubi_assert(le->users >= 0);
+       if (le->users == 0) {
+               rb_erase(&le->rb, &ubi->ltree);
+               _free = 1;
+       } else
+               _free = 0;
+       spin_unlock(&ubi->ltree_lock);
+
+       up_write(&le->mutex);
+       if (_free)
+               kfree(le);
+}
+
+/**
+ * ubi_eba_unmap_leb - un-map logical eraseblock.
+ * @ubi: UBI device description object
+ * @vol: volume description object
+ * @lnum: logical eraseblock number
+ *
+ * This function un-maps logical eraseblock @lnum and schedules corresponding
+ * physical eraseblock for erasure. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
+                     int lnum)
+{
+       int err, pnum, vol_id = vol->vol_id;
+
+       if (ubi->ro_mode)
+               return -EROFS;
+
+       err = leb_write_lock(ubi, vol_id, lnum);
+       if (err)
+               return err;
+
+       pnum = vol->eba_tbl[lnum];
+       if (pnum < 0)
+               /* This logical eraseblock is already unmapped */
+               goto out_unlock;
+
+       dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum);
+
+       vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED;
+       err = ubi_wl_put_peb(ubi, pnum, 0);
+
+out_unlock:
+       leb_write_unlock(ubi, vol_id, lnum);
+       return err;
+}
+
+/**
+ * ubi_eba_read_leb - read data.
+ * @ubi: UBI device description object
+ * @vol: volume description object
+ * @lnum: logical eraseblock number
+ * @buf: buffer to store the read data
+ * @offset: offset from where to read
+ * @len: how many bytes to read
+ * @check: data CRC check flag
+ *
+ * If the logical eraseblock @lnum is unmapped, @buf is filled with 0xFF
+ * bytes. The @check flag only makes sense for static volumes and forces
+ * eraseblock data CRC checking.
+ *
+ * In case of success this function returns zero. In case of a static volume,
+ * if 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.
+ */
+int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
+                    void *buf, int offset, int len, int check)
+{
+       int err, pnum, scrub = 0, vol_id = vol->vol_id;
+       struct ubi_vid_hdr *vid_hdr;
+       uint32_t uninitialized_var(crc);
+
+       err = leb_read_lock(ubi, vol_id, lnum);
+       if (err)
+               return err;
+
+       pnum = vol->eba_tbl[lnum];
+       if (pnum < 0) {
+               /*
+                * The logical eraseblock is not mapped, fill the whole buffer
+                * with 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);
+               leb_read_unlock(ubi, vol_id, lnum);
+               ubi_assert(vol->vol_type != UBI_STATIC_VOLUME);
+               memset(buf, 0xFF, len);
+               return 0;
+       }
+
+       dbg_eba("read %d bytes from offset %d of LEB %d:%d, PEB %d",
+               len, offset, vol_id, lnum, pnum);
+
+       if (vol->vol_type == UBI_DYNAMIC_VOLUME)
+               check = 0;
+
+retry:
+       if (check) {
+               vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
+               if (!vid_hdr) {
+                       err = -ENOMEM;
+                       goto out_unlock;
+               }
+
+               err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1);
+               if (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_ro_mode(ubi);
+                       }
+                       goto out_free;
+               } else if (err == UBI_IO_BITFLIPS)
+                       scrub = 1;
+
+               ubi_assert(lnum < be32_to_cpu(vid_hdr->used_ebs));
+               ubi_assert(len == be32_to_cpu(vid_hdr->data_size));
+
+               crc = be32_to_cpu(vid_hdr->data_crc);
+               ubi_free_vid_hdr(ubi, vid_hdr);
+       }
+
+       err = ubi_io_read_data(ubi, buf, pnum, offset, len);
+       if (err) {
+               if (err == UBI_IO_BITFLIPS) {
+                       scrub = 1;
+                       err = 0;
+               } else if (err == -EBADMSG) {
+                       if (vol->vol_type == UBI_DYNAMIC_VOLUME)
+                               goto out_unlock;
+                       scrub = 1;
+                       if (!check) {
+                               ubi_msg("force data checking");
+                               check = 1;
+                               goto retry;
+                       }
+               } else
+                       goto out_unlock;
+       }
+
+       if (check) {
+               uint32_t crc1 = crc32(UBI_CRC32_INIT, buf, len);
+               if (crc1 != crc) {
+                       ubi_warn("CRC error: calculated %#08x, must be %#08x",
+                                crc1, crc);
+                       err = -EBADMSG;
+                       goto out_unlock;
+               }
+       }
+
+       if (scrub)
+               err = ubi_wl_scrub_peb(ubi, pnum);
+
+       leb_read_unlock(ubi, vol_id, lnum);
+       return err;
+
+out_free:
+       ubi_free_vid_hdr(ubi, vid_hdr);
+out_unlock:
+       leb_read_unlock(ubi, vol_id, lnum);
+       return err;
+}
+
+/**
+ * recover_peb - recover from write failure.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock to recover
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ * @buf: data which was not written because of the write failure
+ * @offset: offset of the failed write
+ * @len: how many bytes should have been written
+ *
+ * This function is called in case of a write failure and moves all good data
+ * from the potentially bad physical eraseblock to a good physical eraseblock.
+ * This function also writes the data which was not written due to the failure.
+ * Returns new physical eraseblock number in case of success, and a negative
+ * error code in case of failure.
+ */
+static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum,
+                      const void *buf, int offset, int len)
+{
+       int err, idx = vol_id2idx(ubi, vol_id), new_pnum, data_size, tries = 0;
+       struct ubi_volume *vol = ubi->volumes[idx];
+       struct ubi_vid_hdr *vid_hdr;
+
+       vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
+       if (!vid_hdr) {
+               return -ENOMEM;
+       }
+
+       mutex_lock(&ubi->buf_mutex);
+
+retry:
+       new_pnum = ubi_wl_get_peb(ubi, UBI_UNKNOWN);
+       if (new_pnum < 0) {
+               mutex_unlock(&ubi->buf_mutex);
+               ubi_free_vid_hdr(ubi, vid_hdr);
+               return new_pnum;
+       }
+
+       ubi_msg("recover PEB %d, move data to PEB %d", pnum, new_pnum);
+
+       err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1);
+       if (err && err != UBI_IO_BITFLIPS) {
+               if (err > 0)
+                       err = -EIO;
+               goto out_put;
+       }
+
+       vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+       err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr);
+       if (err)
+               goto write_error;
+
+       data_size = offset + len;
+       memset(ubi->peb_buf1 + offset, 0xFF, len);
+
+       /* Read everything before the area where the write failure happened */
+       if (offset > 0) {
+               err = ubi_io_read_data(ubi, ubi->peb_buf1, pnum, 0, offset);
+               if (err && err != UBI_IO_BITFLIPS)
+                       goto out_put;
+       }
+
+       memcpy(ubi->peb_buf1 + offset, buf, len);
+
+       err = ubi_io_write_data(ubi, ubi->peb_buf1, new_pnum, 0, data_size);
+       if (err)
+               goto write_error;
+
+       mutex_unlock(&ubi->buf_mutex);
+       ubi_free_vid_hdr(ubi, vid_hdr);
+
+       vol->eba_tbl[lnum] = new_pnum;
+       ubi_wl_put_peb(ubi, pnum, 1);
+
+       ubi_msg("data was successfully recovered");
+       return 0;
+
+out_put:
+       mutex_unlock(&ubi->buf_mutex);
+       ubi_wl_put_peb(ubi, new_pnum, 1);
+       ubi_free_vid_hdr(ubi, vid_hdr);
+       return err;
+
+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_wl_put_peb(ubi, new_pnum, 1);
+       if (++tries > UBI_IO_RETRIES) {
+               mutex_unlock(&ubi->buf_mutex);
+               ubi_free_vid_hdr(ubi, vid_hdr);
+               return err;
+       }
+       ubi_msg("try again");
+       goto retry;
+}
+
+/**
+ * ubi_eba_write_leb - write data to dynamic volume.
+ * @ubi: UBI device description object
+ * @vol: volume description object
+ * @lnum: logical eraseblock number
+ * @buf: the data to write
+ * @offset: offset within the logical eraseblock where to write
+ * @len: how many bytes to write
+ * @dtype: data type
+ *
+ * This function writes data to logical eraseblock @lnum of a dynamic volume
+ * @vol. Returns zero in case of success and a negative error code in case
+ * of failure. In case of error, it is possible that something was still
+ * written to the flash media, but may be some garbage.
+ */
+int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
+                     const void *buf, int offset, int len, int dtype)
+{
+       int err, pnum, tries = 0, vol_id = vol->vol_id;
+       struct ubi_vid_hdr *vid_hdr;
+
+       if (ubi->ro_mode)
+               return -EROFS;
+
+       err = leb_write_lock(ubi, vol_id, lnum);
+       if (err)
+               return err;
+
+       pnum = vol->eba_tbl[lnum];
+       if (pnum >= 0) {
+               dbg_eba("write %d bytes at offset %d of LEB %d:%d, PEB %d",
+                       len, offset, vol_id, lnum, pnum);
+
+               err = ubi_io_write_data(ubi, buf, pnum, offset, len);
+               if (err) {
+                       ubi_warn("failed to write data to PEB %d", pnum);
+                       if (err == -EIO && ubi->bad_allowed)
+                               err = recover_peb(ubi, pnum, vol_id, lnum, buf,
+                                                 offset, len);
+                       if (err)
+                               ubi_ro_mode(ubi);
+               }
+               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, GFP_NOFS);
+       if (!vid_hdr) {
+               leb_write_unlock(ubi, vol_id, lnum);
+               return -ENOMEM;
+       }
+
+       vid_hdr->vol_type = UBI_VID_DYNAMIC;
+       vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+       vid_hdr->vol_id = cpu_to_be32(vol_id);
+       vid_hdr->lnum = cpu_to_be32(lnum);
+       vid_hdr->compat = ubi_get_compat(ubi, vol_id);
+       vid_hdr->data_pad = cpu_to_be32(vol->data_pad);
+
+retry:
+       pnum = ubi_wl_get_peb(ubi, dtype);
+       if (pnum < 0) {
+               ubi_free_vid_hdr(ubi, vid_hdr);
+               leb_write_unlock(ubi, vol_id, lnum);
+               return pnum;
+       }
+
+       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 (err) {
+               ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
+                        vol_id, lnum, pnum);
+               goto write_error;
+       }
+
+       if (len) {
+               err = ubi_io_write_data(ubi, buf, pnum, offset, len);
+               if (err) {
+                       ubi_warn("failed to write %d bytes at offset %d of "
+                                "LEB %d:%d, PEB %d", len, offset, vol_id,
+                                lnum, pnum);
+                       goto write_error;
+               }
+       }
+
+       vol->eba_tbl[lnum] = pnum;
+
+       leb_write_unlock(ubi, vol_id, lnum);
+       ubi_free_vid_hdr(ubi, vid_hdr);
+       return 0;
+
+write_error:
+       if (err != -EIO || !ubi->bad_allowed) {
+               ubi_ro_mode(ubi);
+               leb_write_unlock(ubi, vol_id, lnum);
+               ubi_free_vid_hdr(ubi, vid_hdr);
+               return err;
+       }
+
+       /*
+        * Fortunately, this is the first write operation to this physical
+        * eraseblock, so just put it and request a new one. We assume that if
+        * this physical eraseblock went bad, the erase code will handle that.
+        */
+       err = ubi_wl_put_peb(ubi, pnum, 1);
+       if (err || ++tries > UBI_IO_RETRIES) {
+               ubi_ro_mode(ubi);
+               leb_write_unlock(ubi, vol_id, lnum);
+               ubi_free_vid_hdr(ubi, vid_hdr);
+               return err;
+       }
+
+       vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+       ubi_msg("try another PEB");
+       goto retry;
+}
+
+/**
+ * ubi_eba_write_leb_st - write data to static volume.
+ * @ubi: UBI device description object
+ * @vol: volume description object
+ * @lnum: logical eraseblock number
+ * @buf: data to write
+ * @len: how many bytes to write
+ * @dtype: data type
+ * @used_ebs: how many logical eraseblocks will this volume contain
+ *
+ * This function writes data to logical eraseblock @lnum of static volume
+ * @vol. The @used_ebs argument should contain total number of logical
+ * eraseblock in this static volume.
+ *
+ * When writing to the last logical eraseblock, 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.
+ *
+ * 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.
+ */
+int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
+                        int lnum, const void *buf, int len, int dtype,
+                        int used_ebs)
+{
+       int err, pnum, tries = 0, data_size = len, vol_id = vol->vol_id;
+       struct ubi_vid_hdr *vid_hdr;
+       uint32_t crc;
+
+       if (ubi->ro_mode)
+               return -EROFS;
+
+       if (lnum == used_ebs - 1)
+               /* If this is the last LEB @len may be unaligned */
+               len = ALIGN(data_size, ubi->min_io_size);
+       else
+               ubi_assert(!(len & (ubi->min_io_size - 1)));
+
+       vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
+       if (!vid_hdr)
+               return -ENOMEM;
+
+       err = leb_write_lock(ubi, vol_id, lnum);
+       if (err) {
+               ubi_free_vid_hdr(ubi, vid_hdr);
+               return err;
+       }
+
+       vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+       vid_hdr->vol_id = cpu_to_be32(vol_id);
+       vid_hdr->lnum = cpu_to_be32(lnum);
+       vid_hdr->compat = ubi_get_compat(ubi, vol_id);
+       vid_hdr->data_pad = cpu_to_be32(vol->data_pad);
+
+       crc = crc32(UBI_CRC32_INIT, buf, data_size);
+       vid_hdr->vol_type = UBI_VID_STATIC;
+       vid_hdr->data_size = cpu_to_be32(data_size);
+       vid_hdr->used_ebs = cpu_to_be32(used_ebs);
+       vid_hdr->data_crc = cpu_to_be32(crc);
+
+retry:
+       pnum = ubi_wl_get_peb(ubi, dtype);
+       if (pnum < 0) {
+               ubi_free_vid_hdr(ubi, vid_hdr);
+               leb_write_unlock(ubi, vol_id, lnum);
+               return pnum;
+       }
+
+       dbg_eba("write VID hdr and %d bytes at LEB %d:%d, PEB %d, used_ebs %d",
+               len, vol_id, lnum, pnum, used_ebs);
+
+       err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr);
+       if (err) {
+               ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
+                        vol_id, lnum, pnum);
+               goto write_error;
+       }
+
+       err = ubi_io_write_data(ubi, buf, pnum, 0, len);
+       if (err) {
+               ubi_warn("failed to write %d bytes of data to PEB %d",
+                        len, pnum);
+               goto write_error;
+       }
+
+       ubi_assert(vol->eba_tbl[lnum] < 0);
+       vol->eba_tbl[lnum] = pnum;
+
+       leb_write_unlock(ubi, vol_id, lnum);
+       ubi_free_vid_hdr(ubi, vid_hdr);
+       return 0;
+
+write_error:
+       if (err != -EIO || !ubi->bad_allowed) {
+               /*
+                * This flash device does not admit of bad eraseblocks or
+                * something nasty and unexpected happened. Switch to read-only
+                * mode just in case.
+                */
+               ubi_ro_mode(ubi);
+               leb_write_unlock(ubi, vol_id, lnum);
+               ubi_free_vid_hdr(ubi, vid_hdr);
+               return err;
+       }
+
+       err = ubi_wl_put_peb(ubi, pnum, 1);
+       if (err || ++tries > UBI_IO_RETRIES) {
+               ubi_ro_mode(ubi);
+               leb_write_unlock(ubi, vol_id, lnum);
+               ubi_free_vid_hdr(ubi, vid_hdr);
+               return err;
+       }
+
+       vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+       ubi_msg("try another PEB");
+       goto retry;
+}
+
+/*
+ * ubi_eba_atomic_leb_change - change logical eraseblock atomically.
+ * @ubi: UBI device description object
+ * @vol: volume description object
+ * @lnum: logical eraseblock number
+ * @buf: data to write
+ * @len: how many bytes to write
+ * @dtype: data type
+ *
+ * This function changes the contents of a logical eraseblock atomically. @buf
+ * has to contain new logical eraseblock data, and @len - the length of the
+ * data, which has to be aligned. This function guarantees that in case of an
+ * unclean reboot the old contents is preserved. Returns zero in case of
+ * success and a negative error code in case of failure.
+ *
+ * UBI reserves one LEB for the "atomic LEB change" operation, so only one
+ * LEB change may be done at a time. This is ensured by @ubi->alc_mutex.
+ */
+int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
+                             int lnum, const void *buf, int len, int dtype)
+{
+       int err, pnum, tries = 0, vol_id = vol->vol_id;
+       struct ubi_vid_hdr *vid_hdr;
+       uint32_t crc;
+
+       if (ubi->ro_mode)
+               return -EROFS;
+
+       if (len == 0) {
+               /*
+                * Special case when data length is zero. In this case the LEB
+                * has to be unmapped and mapped somewhere else.
+                */
+               err = ubi_eba_unmap_leb(ubi, vol, lnum);
+               if (err)
+                       return err;
+               return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype);
+       }
+
+       vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
+       if (!vid_hdr)
+               return -ENOMEM;
+
+       mutex_lock(&ubi->alc_mutex);
+       err = leb_write_lock(ubi, vol_id, lnum);
+       if (err)
+               goto out_mutex;
+
+       vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+       vid_hdr->vol_id = cpu_to_be32(vol_id);
+       vid_hdr->lnum = cpu_to_be32(lnum);
+       vid_hdr->compat = ubi_get_compat(ubi, vol_id);
+       vid_hdr->data_pad = cpu_to_be32(vol->data_pad);
+
+       crc = crc32(UBI_CRC32_INIT, buf, len);
+       vid_hdr->vol_type = UBI_VID_DYNAMIC;
+       vid_hdr->data_size = cpu_to_be32(len);
+       vid_hdr->copy_flag = 1;
+       vid_hdr->data_crc = cpu_to_be32(crc);
+
+retry:
+       pnum = ubi_wl_get_peb(ubi, dtype);
+       if (pnum < 0) {
+               err = pnum;
+               goto out_leb_unlock;
+       }
+
+       dbg_eba("change LEB %d:%d, PEB %d, write VID hdr to PEB %d",
+               vol_id, lnum, vol->eba_tbl[lnum], pnum);
+
+       err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr);
+       if (err) {
+               ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
+                        vol_id, lnum, pnum);
+               goto write_error;
+       }
+
+       err = ubi_io_write_data(ubi, buf, pnum, 0, len);
+       if (err) {
+               ubi_warn("failed to write %d bytes of data to PEB %d",
+                        len, pnum);
+               goto write_error;
+       }
+
+       if (vol->eba_tbl[lnum] >= 0) {
+               err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 1);
+               if (err)
+                       goto out_leb_unlock;
+       }
+
+       vol->eba_tbl[lnum] = pnum;
+
+out_leb_unlock:
+       leb_write_unlock(ubi, vol_id, lnum);
+out_mutex:
+       mutex_unlock(&ubi->alc_mutex);
+       ubi_free_vid_hdr(ubi, vid_hdr);
+       return err;
+
+write_error:
+       if (err != -EIO || !ubi->bad_allowed) {
+               /*
+                * This flash device does not admit of bad eraseblocks or
+                * something nasty and unexpected happened. Switch to read-only
+                * mode just in case.
+                */
+               ubi_ro_mode(ubi);
+               goto out_leb_unlock;
+       }
+
+       err = ubi_wl_put_peb(ubi, pnum, 1);
+       if (err || ++tries > UBI_IO_RETRIES) {
+               ubi_ro_mode(ubi);
+               goto out_leb_unlock;
+       }
+
+       vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+       ubi_msg("try another PEB");
+       goto retry;
+}
+
+/**
+ * ubi_eba_copy_leb - copy logical eraseblock.
+ * @ubi: UBI device description object
+ * @from: physical eraseblock number from where to copy
+ * @to: physical eraseblock number where to copy
+ * @vid_hdr: VID header of the @from physical eraseblock
+ *
+ * This function copies logical eraseblock from physical eraseblock @from to
+ * physical eraseblock @to. The @vid_hdr buffer may be changed by this
+ * function. Returns:
+ *   o %0  in case of success;
+ *   o %1 if the operation was canceled and should be tried later (e.g.,
+ *     because a bit-flip was detected at the target PEB);
+ *   o %2 if the volume is being deleted and this LEB should not be moved.
+ */
+int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
+                    struct ubi_vid_hdr *vid_hdr)
+{
+       int err, vol_id, lnum, data_size, aldata_size, idx;
+       struct ubi_volume *vol;
+       uint32_t crc;
+
+       vol_id = be32_to_cpu(vid_hdr->vol_id);
+       lnum = be32_to_cpu(vid_hdr->lnum);
+
+       dbg_eba("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to);
+
+       if (vid_hdr->vol_type == UBI_VID_STATIC) {
+               data_size = be32_to_cpu(vid_hdr->data_size);
+               aldata_size = ALIGN(data_size, ubi->min_io_size);
+       } else
+               data_size = aldata_size =
+                           ubi->leb_size - be32_to_cpu(vid_hdr->data_pad);
+
+       idx = vol_id2idx(ubi, vol_id);
+       spin_lock(&ubi->volumes_lock);
+       /*
+        * Note, we may race with volume deletion, which means that the volume
+        * this logical eraseblock belongs to might be being deleted. Since the
+        * volume deletion unmaps all the volume's logical eraseblocks, it will
+        * be locked in 'ubi_wl_put_peb()' and wait for the WL worker to finish.
+        */
+       vol = ubi->volumes[idx];
+       if (!vol) {
+               /* No need to do further work, cancel */
+               dbg_eba("volume %d is being removed, cancel", vol_id);
+               spin_unlock(&ubi->volumes_lock);
+               return 2;
+       }
+       spin_unlock(&ubi->volumes_lock);
+
+       /*
+        * We do not want anybody to write to this logical eraseblock while we
+        * are moving it, so lock it.
+        *
+        * Note, we are using non-waiting locking here, because we cannot sleep
+        * on the LEB, since it may cause deadlocks. Indeed, imagine a task is
+        * unmapping the LEB which is mapped to the PEB we are going to move
+        * (@from). This task locks the LEB and goes sleep in the
+        * 'ubi_wl_put_peb()' function on the @ubi->move_mutex. In turn, we are
+        * holding @ubi->move_mutex and go sleep on the LEB lock. So, if the
+        * LEB is already locked, we just do not move it and return %1.
+        */
+       err = leb_write_trylock(ubi, vol_id, lnum);
+       if (err) {
+               dbg_eba("contention on LEB %d:%d, cancel", vol_id, lnum);
+               return err;
+       }
+
+       /*
+        * The LEB might have been put meanwhile, and the task which put it is
+        * probably waiting on @ubi->move_mutex. No need to continue the work,
+        * cancel it.
+        */
+       if (vol->eba_tbl[lnum] != from) {
+               dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to "
+                       "PEB %d, cancel", vol_id, lnum, from,
+                       vol->eba_tbl[lnum]);
+               err = 1;
+               goto out_unlock_leb;
+       }
+
+       /*
+        * OK, now the LEB is locked and we can safely start moving iy. Since
+        * this function utilizes thie @ubi->peb1_buf buffer which is shared
+        * with some other functions, so lock the buffer by taking the
+        * @ubi->buf_mutex.
+        */
+       mutex_lock(&ubi->buf_mutex);
+       dbg_eba("read %d bytes of data", aldata_size);
+       err = ubi_io_read_data(ubi, ubi->peb_buf1, from, 0, aldata_size);
+       if (err && err != UBI_IO_BITFLIPS) {
+               ubi_warn("error %d while reading data from PEB %d",
+                        err, from);
+               goto out_unlock_buf;
+       }
+
+       /*
+        * 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 they may be filled
+        * by data.
+        */
+       if (vid_hdr->vol_type == UBI_VID_DYNAMIC)
+               aldata_size = data_size =
+                       ubi_calc_data_len(ubi, ubi->peb_buf1, data_size);
+
+       cond_resched();
+       crc = crc32(UBI_CRC32_INIT, ubi->peb_buf1, data_size);
+       cond_resched();
+
+       /*
+        * It may turn out to me that the whole @from 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 (data_size > 0) {
+               vid_hdr->copy_flag = 1;
+               vid_hdr->data_size = cpu_to_be32(data_size);
+               vid_hdr->data_crc = cpu_to_be32(crc);
+       }
+       vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+
+       err = ubi_io_write_vid_hdr(ubi, to, vid_hdr);
+       if (err)
+               goto out_unlock_buf;
+
+       cond_resched();
+
+       /* Read the VID header back and check if it was written correctly */
+       err = ubi_io_read_vid_hdr(ubi, to, vid_hdr, 1);
+       if (err) {
+               if (err != UBI_IO_BITFLIPS)
+                       ubi_warn("cannot read VID header back from PEB %d", to);
+               else
+                       err = 1;
+               goto out_unlock_buf;
+       }
+
+       if (data_size > 0) {
+               err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size);
+               if (err)
+                       goto out_unlock_buf;
+
+               cond_resched();
+
+               /*
+                * We've written the data and are going to read it back to make
+                * sure it was written correctly.
+                */
+
+               err = ubi_io_read_data(ubi, ubi->peb_buf2, to, 0, aldata_size);
+               if (err) {
+                       if (err != UBI_IO_BITFLIPS)
+                               ubi_warn("cannot read data back from PEB %d",
+                                        to);
+                       else
+                               err = 1;
+                       goto out_unlock_buf;
+               }
+
+               cond_resched();
+
+               if (memcmp(ubi->peb_buf1, ubi->peb_buf2, aldata_size)) {
+                       ubi_warn("read data back from PEB %d - it is different",
+                                to);
+                       goto out_unlock_buf;
+               }
+       }
+
+       ubi_assert(vol->eba_tbl[lnum] == from);
+       vol->eba_tbl[lnum] = to;
+
+out_unlock_buf:
+       mutex_unlock(&ubi->buf_mutex);
+out_unlock_leb:
+       leb_write_unlock(ubi, vol_id, lnum);
+       return err;
+}
+
+/**
+ * ubi_eba_init_scan - initialize the EBA unit using scanning information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
+{
+       int i, j, err, num_volumes;
+       struct ubi_scan_volume *sv;
+       struct ubi_volume *vol;
+       struct ubi_scan_leb *seb;
+       struct rb_node *rb;
+
+       dbg_eba("initialize EBA unit");
+
+       spin_lock_init(&ubi->ltree_lock);
+       mutex_init(&ubi->alc_mutex);
+       ubi->ltree = RB_ROOT;
+
+       ubi->global_sqnum = si->max_sqnum + 1;
+       num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT;
+
+       for (i = 0; i < num_volumes; i++) {
+               vol = ubi->volumes[i];
+               if (!vol)
+                       continue;
+
+               cond_resched();
+
+               vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int),
+                                      GFP_KERNEL);
+               if (!vol->eba_tbl) {
+                       err = -ENOMEM;
+                       goto out_free;
+               }
+
+               for (j = 0; j < vol->reserved_pebs; j++)
+                       vol->eba_tbl[j] = UBI_LEB_UNMAPPED;
+
+               sv = ubi_scan_find_sv(si, idx2vol_id(ubi, i));
+               if (!sv)
+                       continue;
+
+               ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) {
+                       if (seb->lnum >= vol->reserved_pebs)
+                               /*
+                                * This may happen in case of an unclean reboot
+                                * during re-size.
+                                */
+                               ubi_scan_move_to_list(sv, seb, &si->erase);
+                       vol->eba_tbl[seb->lnum] = seb->pnum;
+               }
+       }
+
+       if (ubi->avail_pebs < EBA_RESERVED_PEBS) {
+               ubi_err("no enough physical eraseblocks (%d, need %d)",
+                       ubi->avail_pebs, EBA_RESERVED_PEBS);
+               err = -ENOSPC;
+               goto out_free;
+       }
+       ubi->avail_pebs -= EBA_RESERVED_PEBS;
+       ubi->rsvd_pebs += EBA_RESERVED_PEBS;
+
+       if (ubi->bad_allowed) {
+               ubi_calculate_reserved(ubi);
+
+               if (ubi->avail_pebs < ubi->beb_rsvd_level) {
+                       /* No enough free physical eraseblocks */
+                       ubi->beb_rsvd_pebs = ubi->avail_pebs;
+                       ubi_warn("cannot reserve enough PEBs for bad PEB "
+                                "handling, reserved %d, need %d",
+                                ubi->beb_rsvd_pebs, ubi->beb_rsvd_level);
+               } else
+                       ubi->beb_rsvd_pebs = ubi->beb_rsvd_level;
+
+               ubi->avail_pebs -= ubi->beb_rsvd_pebs;
+               ubi->rsvd_pebs  += ubi->beb_rsvd_pebs;
+       }
+
+       dbg_eba("EBA unit is initialized");
+       return 0;
+
+out_free:
+       for (i = 0; i < num_volumes; i++) {
+               if (!ubi->volumes[i])
+                       continue;
+               kfree(ubi->volumes[i]->eba_tbl);
+       }
+       return err;
+}
+
+/**
+ * ubi_eba_close - close EBA unit.
+ * @ubi: UBI device description object
+ */
+void ubi_eba_close(const struct ubi_device *ubi)
+{
+       int i, num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT;
+
+       dbg_eba("close EBA unit");
+
+       for (i = 0; i < num_volumes; i++) {
+               if (!ubi->volumes[i])
+                       continue;
+               kfree(ubi->volumes[i]->eba_tbl);
+       }
+}