mtd, ubi, ubifs: resync with Linux-3.14
resync ubi subsystem with linux: commit 455c6fdbd219161bd09b1165f11699d6d73de11c Author: Linus Torvalds <torvalds@linux-foundation.org> Date: Sun Mar 30 20:40:15 2014 -0700 Linux 3.14 A nice side effect of this, is we introduce UBI Fastmap support to U-Boot. Signed-off-by: Heiko Schocher <hs@denx.de> Signed-off-by: Tom Rini <trini@ti.com> Cc: Marek Vasut <marex@denx.de> Cc: Sergey Lapin <slapin@ossfans.org> Cc: Scott Wood <scottwood@freescale.com> Cc: Joerg Krause <jkrause@posteo.de>
This commit is contained in:
parent
0c06db5983
commit
ff94bc40af
61
README
61
README
@ -3338,6 +3338,9 @@ FIT uImage format:
|
||||
Adds the MTD partitioning infrastructure from the Linux
|
||||
kernel. Needed for UBI support.
|
||||
|
||||
CONFIG_MTD_NAND_VERIFY_WRITE
|
||||
verify if the written data is correct reread.
|
||||
|
||||
- UBI support
|
||||
CONFIG_CMD_UBI
|
||||
|
||||
@ -3351,6 +3354,64 @@ FIT uImage format:
|
||||
Make the verbose messages from UBI stop printing. This leaves
|
||||
warnings and errors enabled.
|
||||
|
||||
|
||||
CONFIG_MTD_UBI_WL_THRESHOLD
|
||||
This parameter defines the maximum difference between the highest
|
||||
erase counter value and the lowest erase counter value of eraseblocks
|
||||
of UBI devices. When this threshold is exceeded, UBI starts performing
|
||||
wear leveling by means of moving data from eraseblock with low erase
|
||||
counter to eraseblocks with high erase counter.
|
||||
|
||||
The default value should be OK for SLC NAND flashes, NOR flashes and
|
||||
other flashes which have eraseblock life-cycle 100000 or more.
|
||||
However, in case of MLC NAND flashes which typically have eraseblock
|
||||
life-cycle less than 10000, the threshold should be lessened (e.g.,
|
||||
to 128 or 256, although it does not have to be power of 2).
|
||||
|
||||
default: 4096
|
||||
|
||||
CONFIG_MTD_UBI_BEB_LIMIT
|
||||
This option specifies the maximum bad physical eraseblocks UBI
|
||||
expects on the MTD device (per 1024 eraseblocks). If the
|
||||
underlying flash does not admit of bad eraseblocks (e.g. NOR
|
||||
flash), this value is ignored.
|
||||
|
||||
NAND datasheets often specify the minimum and maximum NVM
|
||||
(Number of Valid Blocks) for the flashes' endurance lifetime.
|
||||
The maximum expected bad eraseblocks per 1024 eraseblocks
|
||||
then can be calculated as "1024 * (1 - MinNVB / MaxNVB)",
|
||||
which gives 20 for most NANDs (MaxNVB is basically the total
|
||||
count of eraseblocks on the chip).
|
||||
|
||||
To put it differently, if this value is 20, UBI will try to
|
||||
reserve about 1.9% of physical eraseblocks for bad blocks
|
||||
handling. And that will be 1.9% of eraseblocks on the entire
|
||||
NAND chip, not just the MTD partition UBI attaches. This means
|
||||
that if you have, say, a NAND flash chip admits maximum 40 bad
|
||||
eraseblocks, and it is split on two MTD partitions of the same
|
||||
size, UBI will reserve 40 eraseblocks when attaching a
|
||||
partition.
|
||||
|
||||
default: 20
|
||||
|
||||
CONFIG_MTD_UBI_FASTMAP
|
||||
Fastmap is a mechanism which allows attaching an UBI device
|
||||
in nearly constant time. Instead of scanning the whole MTD device it
|
||||
only has to locate a checkpoint (called fastmap) on the device.
|
||||
The on-flash fastmap contains all information needed to attach
|
||||
the device. Using fastmap makes only sense on large devices where
|
||||
attaching by scanning takes long. UBI will not automatically install
|
||||
a fastmap on old images, but you can set the UBI parameter
|
||||
CONFIG_MTD_UBI_FASTMAP_AUTOCONVERT to 1 if you want so. Please note
|
||||
that fastmap-enabled images are still usable with UBI implementations
|
||||
without fastmap support. On typical flash devices the whole fastmap
|
||||
fits into one PEB. UBI will reserve PEBs to hold two fastmaps.
|
||||
|
||||
CONFIG_MTD_UBI_FASTMAP_AUTOCONVERT
|
||||
Set this parameter to enable fastmap automatically on images
|
||||
without a fastmap.
|
||||
default: 0
|
||||
|
||||
- UBIFS support
|
||||
CONFIG_CMD_UBIFS
|
||||
|
||||
|
@ -93,6 +93,7 @@ static void alpr_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
static int alpr_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
@ -103,6 +104,7 @@ static int alpr_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int alpr_nand_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
@ -128,7 +130,9 @@ int board_nand_init(struct nand_chip *nand)
|
||||
nand->read_byte = alpr_nand_read_byte;
|
||||
nand->write_buf = alpr_nand_write_buf;
|
||||
nand->read_buf = alpr_nand_read_buf;
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
nand->verify_buf = alpr_nand_verify_buf;
|
||||
#endif
|
||||
nand->dev_ready = alpr_nand_dev_ready;
|
||||
|
||||
return 0;
|
||||
|
@ -18,7 +18,9 @@ static void sc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len);
|
||||
static u_char sc_nand_read_byte(struct mtd_info *mtd);
|
||||
static u16 sc_nand_read_word(struct mtd_info *mtd);
|
||||
static void sc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len);
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
static int sc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len);
|
||||
#endif
|
||||
static int sc_nand_device_ready(struct mtd_info *mtdinfo);
|
||||
|
||||
#define FPGA_NAND_CMD_MASK (0x7 << 28)
|
||||
@ -100,6 +102,7 @@ static void sc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
/**
|
||||
* sc_nand_verify_buf - Verify chip data against buffer
|
||||
* @mtd: MTD device structure
|
||||
@ -116,6 +119,7 @@ static int sc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* sc_nand_device_ready - Check the NAND device is ready for next command.
|
||||
@ -174,7 +178,9 @@ int board_nand_init(struct nand_chip *nand)
|
||||
nand->read_word = sc_nand_read_word;
|
||||
nand->write_buf = sc_nand_write_buf;
|
||||
nand->read_buf = sc_nand_read_buf;
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
nand->verify_buf = sc_nand_verify_buf;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -188,6 +188,7 @@ static void tqm8272_write_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int
|
||||
*base = buf[i];
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
static int tqm8272_verify_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int len)
|
||||
{
|
||||
struct nand_chip *this = mtdinfo->priv;
|
||||
@ -199,6 +200,7 @@ static int tqm8272_verify_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif /* #ifndef CONFIG_NAND_SPL */
|
||||
|
||||
void board_nand_select_device(struct nand_chip *nand, int chip)
|
||||
@ -247,7 +249,9 @@ int board_nand_init(struct nand_chip *nand)
|
||||
#ifndef CONFIG_NAND_SPL
|
||||
nand->write_buf = tqm8272_write_buf;
|
||||
nand->read_buf = tqm8272_read_buf;
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
nand->verify_buf = tqm8272_verify_buf;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <onenand_uboot.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/err.h>
|
||||
#include <ubi_uboot.h>
|
||||
#include <asm/errno.h>
|
||||
#include <jffs2/load_kernel.h>
|
||||
@ -50,33 +51,6 @@ int ubifs_is_mounted(void);
|
||||
void cmd_ubifs_umount(void);
|
||||
#endif
|
||||
|
||||
static void ubi_dump_vol_info(const struct ubi_volume *vol)
|
||||
{
|
||||
ubi_msg("volume information dump:");
|
||||
ubi_msg("vol_id %d", vol->vol_id);
|
||||
ubi_msg("reserved_pebs %d", vol->reserved_pebs);
|
||||
ubi_msg("alignment %d", vol->alignment);
|
||||
ubi_msg("data_pad %d", vol->data_pad);
|
||||
ubi_msg("vol_type %d", vol->vol_type);
|
||||
ubi_msg("name_len %d", vol->name_len);
|
||||
ubi_msg("usable_leb_size %d", vol->usable_leb_size);
|
||||
ubi_msg("used_ebs %d", vol->used_ebs);
|
||||
ubi_msg("used_bytes %lld", vol->used_bytes);
|
||||
ubi_msg("last_eb_bytes %d", vol->last_eb_bytes);
|
||||
ubi_msg("corrupted %d", vol->corrupted);
|
||||
ubi_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) {
|
||||
ubi_msg("name %s", vol->name);
|
||||
} else {
|
||||
ubi_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]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void display_volume_info(struct ubi_device *ubi)
|
||||
{
|
||||
int i;
|
||||
|
@ -38,7 +38,7 @@ static int do_ubifs_mount(cmd_tbl_t *cmdtp, int flag, int argc,
|
||||
ubifs_initialized = 1;
|
||||
}
|
||||
|
||||
ret = ubifs_mount(vol_name);
|
||||
ret = uboot_ubifs_mount(vol_name);
|
||||
if (ret)
|
||||
return -1;
|
||||
|
||||
|
@ -1,16 +1,32 @@
|
||||
/*
|
||||
* MTD device concatenation layer
|
||||
*
|
||||
* (C) 2002 Robert Kaiser <rkaiser@sysgo.de>
|
||||
* Copyright © 2002 Robert Kaiser <rkaiser@sysgo.de>
|
||||
* Copyright © 2002-2010 David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* NAND support by Christian Gan <cgan@iders.ca>
|
||||
*
|
||||
* This code is GPL
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include <asm/div64.h>
|
||||
#else
|
||||
#include <div64.h>
|
||||
#include <linux/compat.h>
|
||||
#endif
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/concat.h>
|
||||
|
||||
#include <ubi_uboot.h>
|
||||
|
||||
/*
|
||||
@ -51,7 +67,9 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
int ret = 0, err;
|
||||
int i;
|
||||
|
||||
#ifdef __UBOOT__
|
||||
*retlen = 0;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
@ -105,7 +123,9 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
int err = -EINVAL;
|
||||
int i;
|
||||
|
||||
#ifdef __UBOOT__
|
||||
*retlen = 0;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
@ -137,6 +157,83 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
static int
|
||||
concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
|
||||
unsigned long count, loff_t to, size_t * retlen)
|
||||
{
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
struct kvec *vecs_copy;
|
||||
unsigned long entry_low, entry_high;
|
||||
size_t total_len = 0;
|
||||
int i;
|
||||
int err = -EINVAL;
|
||||
|
||||
/* Calculate total length of data */
|
||||
for (i = 0; i < count; i++)
|
||||
total_len += vecs[i].iov_len;
|
||||
|
||||
/* Check alignment */
|
||||
if (mtd->writesize > 1) {
|
||||
uint64_t __to = to;
|
||||
if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* make a copy of vecs */
|
||||
vecs_copy = kmemdup(vecs, sizeof(struct kvec) * count, GFP_KERNEL);
|
||||
if (!vecs_copy)
|
||||
return -ENOMEM;
|
||||
|
||||
entry_low = 0;
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
size_t size, wsize, retsize, old_iov_len;
|
||||
|
||||
if (to >= subdev->size) {
|
||||
to -= subdev->size;
|
||||
continue;
|
||||
}
|
||||
|
||||
size = min_t(uint64_t, total_len, subdev->size - to);
|
||||
wsize = size; /* store for future use */
|
||||
|
||||
entry_high = entry_low;
|
||||
while (entry_high < count) {
|
||||
if (size <= vecs_copy[entry_high].iov_len)
|
||||
break;
|
||||
size -= vecs_copy[entry_high++].iov_len;
|
||||
}
|
||||
|
||||
old_iov_len = vecs_copy[entry_high].iov_len;
|
||||
vecs_copy[entry_high].iov_len = size;
|
||||
|
||||
err = mtd_writev(subdev, &vecs_copy[entry_low],
|
||||
entry_high - entry_low + 1, to, &retsize);
|
||||
|
||||
vecs_copy[entry_high].iov_len = old_iov_len - size;
|
||||
vecs_copy[entry_high].iov_base += size;
|
||||
|
||||
entry_low = entry_high;
|
||||
|
||||
if (err)
|
||||
break;
|
||||
|
||||
*retlen += retsize;
|
||||
total_len -= wsize;
|
||||
|
||||
if (total_len == 0)
|
||||
break;
|
||||
|
||||
err = -EINVAL;
|
||||
to = 0;
|
||||
}
|
||||
|
||||
kfree(vecs_copy);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
|
||||
{
|
||||
@ -204,7 +301,7 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
|
||||
ops->retlen = 0;
|
||||
ops->retlen = ops->oobretlen = 0;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
@ -219,7 +316,7 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
|
||||
devops.len = subdev->size - to;
|
||||
|
||||
err = mtd_write_oob(subdev, to, &devops);
|
||||
ops->retlen += devops.retlen;
|
||||
ops->retlen += devops.oobretlen;
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -243,6 +340,9 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
|
||||
static void concat_erase_callback(struct erase_info *instr)
|
||||
{
|
||||
/* Nothing to do here in U-Boot */
|
||||
#ifndef __UBOOT__
|
||||
wake_up((wait_queue_head_t *) instr->priv);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
|
||||
@ -316,7 +416,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
* to-be-erased area begins. Verify that the starting
|
||||
* offset is aligned to this region's erase size:
|
||||
*/
|
||||
if (instr->addr & (erase_regions[i].erasesize - 1))
|
||||
if (i < 0 || instr->addr & (erase_regions[i].erasesize - 1))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
@ -329,8 +429,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
/*
|
||||
* check if the ending offset is aligned to this region's erase size
|
||||
*/
|
||||
if ((instr->addr + instr->len) & (erase_regions[i].erasesize -
|
||||
1))
|
||||
if (i < 0 || ((instr->addr + instr->len) &
|
||||
(erase_regions[i].erasesize - 1)))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -422,7 +522,6 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
size = len;
|
||||
|
||||
err = mtd_lock(subdev, ofs, size);
|
||||
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -457,7 +556,6 @@ static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
size = len;
|
||||
|
||||
err = mtd_unlock(subdev, ofs, size);
|
||||
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -483,6 +581,32 @@ static void concat_sync(struct mtd_info *mtd)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
static int concat_suspend(struct mtd_info *mtd)
|
||||
{
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
int i, rc = 0;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
if ((rc = mtd_suspend(subdev)) < 0)
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void concat_resume(struct mtd_info *mtd)
|
||||
{
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
mtd_resume(subdev);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
|
||||
{
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
@ -511,9 +635,6 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
int i, err = -EINVAL;
|
||||
|
||||
if (!mtd_can_have_bb(concat->subdev[0]))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
|
||||
@ -531,6 +652,32 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* try to support NOMMU mmaps on concatenated devices
|
||||
* - we don't support subdev spanning as we can't guarantee it'll work
|
||||
*/
|
||||
static unsigned long concat_get_unmapped_area(struct mtd_info *mtd,
|
||||
unsigned long len,
|
||||
unsigned long offset,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
|
||||
if (offset >= subdev->size) {
|
||||
offset -= subdev->size;
|
||||
continue;
|
||||
}
|
||||
|
||||
return mtd_get_unmapped_area(subdev, len, offset, flags);
|
||||
}
|
||||
|
||||
return (unsigned long) -ENOSYS;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function constructs a virtual MTD device by concatenating
|
||||
* num_devs MTD devices. A pointer to the new device object is
|
||||
@ -539,17 +686,22 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
*/
|
||||
struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */
|
||||
int num_devs, /* number of subdevices */
|
||||
#ifndef __UBOOT__
|
||||
const char *name)
|
||||
#else
|
||||
char *name)
|
||||
#endif
|
||||
{ /* name for the new device */
|
||||
int i;
|
||||
size_t size;
|
||||
struct mtd_concat *concat;
|
||||
uint32_t max_erasesize, curr_erasesize;
|
||||
int num_erase_region;
|
||||
int max_writebufsize = 0;
|
||||
|
||||
debug("Concatenating MTD devices:\n");
|
||||
for (i = 0; i < num_devs; i++)
|
||||
debug("(%d): \"%s\"\n", i, subdev[i]->name);
|
||||
printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name);
|
||||
debug("into device \"%s\"\n", name);
|
||||
|
||||
/* allocate the device structure */
|
||||
@ -565,16 +717,26 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
||||
|
||||
/*
|
||||
* Set up the new "super" device's MTD object structure, check for
|
||||
* incompatibilites between the subdevices.
|
||||
* incompatibilities between the subdevices.
|
||||
*/
|
||||
concat->mtd.type = subdev[0]->type;
|
||||
concat->mtd.flags = subdev[0]->flags;
|
||||
concat->mtd.size = subdev[0]->size;
|
||||
concat->mtd.erasesize = subdev[0]->erasesize;
|
||||
concat->mtd.writesize = subdev[0]->writesize;
|
||||
|
||||
for (i = 0; i < num_devs; i++)
|
||||
if (max_writebufsize < subdev[i]->writebufsize)
|
||||
max_writebufsize = subdev[i]->writebufsize;
|
||||
concat->mtd.writebufsize = max_writebufsize;
|
||||
|
||||
concat->mtd.subpage_sft = subdev[0]->subpage_sft;
|
||||
concat->mtd.oobsize = subdev[0]->oobsize;
|
||||
concat->mtd.oobavail = subdev[0]->oobavail;
|
||||
#ifndef __UBOOT__
|
||||
if (subdev[0]->_writev)
|
||||
concat->mtd._writev = concat_writev;
|
||||
#endif
|
||||
if (subdev[0]->_read_oob)
|
||||
concat->mtd._read_oob = concat_read_oob;
|
||||
if (subdev[0]->_write_oob)
|
||||
@ -586,6 +748,10 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
||||
|
||||
concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
|
||||
|
||||
#ifndef __UBOOT__
|
||||
concat->mtd.backing_dev_info = subdev[0]->backing_dev_info;
|
||||
#endif
|
||||
|
||||
concat->subdev[0] = subdev[0];
|
||||
|
||||
for (i = 1; i < num_devs; i++) {
|
||||
@ -613,6 +779,16 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
||||
subdev[i]->flags & MTD_WRITEABLE;
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/* only permit direct mapping if the BDIs are all the same
|
||||
* - copy-mapping is still permitted
|
||||
*/
|
||||
if (concat->mtd.backing_dev_info !=
|
||||
subdev[i]->backing_dev_info)
|
||||
concat->mtd.backing_dev_info =
|
||||
&default_backing_dev_info;
|
||||
#endif
|
||||
|
||||
concat->mtd.size += subdev[i]->size;
|
||||
concat->mtd.ecc_stats.badblocks +=
|
||||
subdev[i]->ecc_stats.badblocks;
|
||||
@ -641,6 +817,11 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
||||
concat->mtd._sync = concat_sync;
|
||||
concat->mtd._lock = concat_lock;
|
||||
concat->mtd._unlock = concat_unlock;
|
||||
#ifndef __UBOOT__
|
||||
concat->mtd._suspend = concat_suspend;
|
||||
concat->mtd._resume = concat_resume;
|
||||
#endif
|
||||
concat->mtd._get_unmapped_area = concat_get_unmapped_area;
|
||||
|
||||
/*
|
||||
* Combine the erase block size info of the subdevices:
|
||||
@ -771,3 +952,22 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
||||
|
||||
return &concat->mtd;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function destroys an MTD object obtained from concat_mtd_devs()
|
||||
*/
|
||||
|
||||
void mtd_concat_destroy(struct mtd_info *mtd)
|
||||
{
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
if (concat->mtd.numeraseregions)
|
||||
kfree(concat->mtd.eraseregions);
|
||||
kfree(concat);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(mtd_concat_create);
|
||||
EXPORT_SYMBOL(mtd_concat_destroy);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Robert Kaiser <rkaiser@sysgo.de>");
|
||||
MODULE_DESCRIPTION("Generic support for concatenating of MTD devices");
|
||||
|
File diff suppressed because it is too large
Load Diff
23
drivers/mtd/mtdcore.h
Normal file
23
drivers/mtd/mtdcore.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* These are exported solely for the purpose of mtd_blkdevs.c and mtdchar.c.
|
||||
* You should not use them for _anything_ else.
|
||||
*/
|
||||
|
||||
extern struct mutex mtd_table_mutex;
|
||||
|
||||
struct mtd_info *__mtd_next_device(int i);
|
||||
int add_mtd_device(struct mtd_info *mtd);
|
||||
int del_mtd_device(struct mtd_info *mtd);
|
||||
int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
|
||||
int del_mtd_partitions(struct mtd_info *);
|
||||
int parse_mtd_partitions(struct mtd_info *master, const char * const *types,
|
||||
struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data);
|
||||
|
||||
int __init init_mtdchar(void);
|
||||
void __exit cleanup_mtdchar(void);
|
||||
|
||||
#define mtd_for_each_device(mtd) \
|
||||
for ((mtd) = __mtd_next_device(0); \
|
||||
(mtd) != NULL; \
|
||||
(mtd) = __mtd_next_device(mtd->index + 1))
|
@ -1,35 +1,50 @@
|
||||
/*
|
||||
* Simple MTD partitioning layer
|
||||
*
|
||||
* (C) 2000 Nicolas Pitre <nico@cam.org>
|
||||
* Copyright © 2000 Nicolas Pitre <nico@fluxnic.net>
|
||||
* Copyright © 2002 Thomas Gleixner <gleixner@linutronix.de>
|
||||
* Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* This code is GPL
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* 02-21-2002 Thomas Gleixner <gleixner@autronix.de>
|
||||
* added support for read_oob, write_oob
|
||||
*/
|
||||
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/kmod.h>
|
||||
#endif
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/errno.h>
|
||||
#include <linux/compat.h>
|
||||
#include <ubi_uboot.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include "mtdcore.h"
|
||||
|
||||
/* Our partition linked list */
|
||||
struct list_head mtd_partitions;
|
||||
static LIST_HEAD(mtd_partitions);
|
||||
#ifndef __UBOOT__
|
||||
static DEFINE_MUTEX(mtd_partitions_mutex);
|
||||
#else
|
||||
DEFINE_MUTEX(mtd_partitions_mutex);
|
||||
#endif
|
||||
|
||||
/* Our partition node structure */
|
||||
struct mtd_part {
|
||||
struct mtd_info mtd;
|
||||
struct mtd_info *master;
|
||||
uint64_t offset;
|
||||
int index;
|
||||
struct list_head list;
|
||||
int registered;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -39,6 +54,30 @@ struct mtd_part {
|
||||
#define PART(x) ((struct mtd_part *)(x))
|
||||
|
||||
|
||||
#ifdef __UBOOT__
|
||||
/* from mm/util.c */
|
||||
|
||||
/**
|
||||
* kstrdup - allocate space for and copy an existing string
|
||||
* @s: the string to duplicate
|
||||
* @gfp: the GFP mask used in the kmalloc() call when allocating memory
|
||||
*/
|
||||
char *kstrdup(const char *s, gfp_t gfp)
|
||||
{
|
||||
size_t len;
|
||||
char *buf;
|
||||
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
len = strlen(s) + 1;
|
||||
buf = kmalloc(len, gfp);
|
||||
if (buf)
|
||||
memcpy(buf, s, len);
|
||||
return buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MTD methods which simply translate the effective address and pass through
|
||||
* to the _real_ device.
|
||||
@ -52,7 +91,8 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
int res;
|
||||
|
||||
stats = part->master->ecc_stats;
|
||||
res = mtd_read(part->master, from + part->offset, len, retlen, buf);
|
||||
res = part->master->_read(part->master, from + part->offset, len,
|
||||
retlen, buf);
|
||||
if (unlikely(mtd_is_eccerr(res)))
|
||||
mtd->ecc_stats.failed +=
|
||||
part->master->ecc_stats.failed - stats.failed;
|
||||
@ -62,6 +102,36 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, void **virt, resource_size_t *phys)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
|
||||
return part->master->_point(part->master, from + part->offset, len,
|
||||
retlen, virt, phys);
|
||||
}
|
||||
|
||||
static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
|
||||
return part->master->_unpoint(part->master, from + part->offset, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
|
||||
unsigned long len,
|
||||
unsigned long offset,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
|
||||
offset += part->offset;
|
||||
return part->master->_get_unmapped_area(part->master, len, offset,
|
||||
flags);
|
||||
}
|
||||
|
||||
static int part_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
@ -72,8 +142,25 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
return -EINVAL;
|
||||
if (ops->datbuf && from + ops->len > mtd->size)
|
||||
return -EINVAL;
|
||||
res = mtd_read_oob(part->master, from + part->offset, ops);
|
||||
|
||||
/*
|
||||
* If OOB is also requested, make sure that we do not read past the end
|
||||
* of this partition.
|
||||
*/
|
||||
if (ops->oobbuf) {
|
||||
size_t len, pages;
|
||||
|
||||
if (ops->mode == MTD_OPS_AUTO_OOB)
|
||||
len = mtd->oobavail;
|
||||
else
|
||||
len = mtd->oobsize;
|
||||
pages = mtd_div_by_ws(mtd->size, mtd);
|
||||
pages -= mtd_div_by_ws(from, mtd);
|
||||
if (ops->ooboffs + ops->ooblen > pages * len)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = part->master->_read_oob(part->master, from + part->offset, ops);
|
||||
if (unlikely(res)) {
|
||||
if (mtd_is_bitflip(res))
|
||||
mtd->ecc_stats.corrected++;
|
||||
@ -87,35 +174,46 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
|
||||
size_t len, size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_read_user_prot_reg(part->master, from, len, retlen, buf);
|
||||
return part->master->_read_user_prot_reg(part->master, from, len,
|
||||
retlen, buf);
|
||||
}
|
||||
|
||||
static int part_get_user_prot_info(struct mtd_info *mtd,
|
||||
struct otp_info *buf, size_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_get_user_prot_info(part->master, buf, len);
|
||||
return part->master->_get_user_prot_info(part->master, buf, len);
|
||||
}
|
||||
|
||||
static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
|
||||
size_t len, size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_read_fact_prot_reg(part->master, from, len, retlen, buf);
|
||||
return part->master->_read_fact_prot_reg(part->master, from, len,
|
||||
retlen, buf);
|
||||
}
|
||||
|
||||
static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_get_fact_prot_info(part->master, buf, len);
|
||||
return part->master->_get_fact_prot_info(part->master, buf, len);
|
||||
}
|
||||
|
||||
static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_write(part->master, to + part->offset, len, retlen, buf);
|
||||
return part->master->_write(part->master, to + part->offset, len,
|
||||
retlen, buf);
|
||||
}
|
||||
|
||||
static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return part->master->_panic_write(part->master, to + part->offset, len,
|
||||
retlen, buf);
|
||||
}
|
||||
|
||||
static int part_write_oob(struct mtd_info *mtd, loff_t to,
|
||||
@ -127,30 +225,41 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to,
|
||||
return -EINVAL;
|
||||
if (ops->datbuf && to + ops->len > mtd->size)
|
||||
return -EINVAL;
|
||||
return mtd_write_oob(part->master, to + part->offset, ops);
|
||||
return part->master->_write_oob(part->master, to + part->offset, ops);
|
||||
}
|
||||
|
||||
static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
|
||||
size_t len, size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_write_user_prot_reg(part->master, from, len, retlen, buf);
|
||||
return part->master->_write_user_prot_reg(part->master, from, len,
|
||||
retlen, buf);
|
||||
}
|
||||
|
||||
static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
|
||||
size_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_lock_user_prot_reg(part->master, from, len);
|
||||
return part->master->_lock_user_prot_reg(part->master, from, len);
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
|
||||
unsigned long count, loff_t to, size_t *retlen)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return part->master->_writev(part->master, vecs, count,
|
||||
to + part->offset, retlen);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
int ret;
|
||||
|
||||
instr->addr += part->offset;
|
||||
ret = mtd_erase(part->master, instr);
|
||||
ret = part->master->_erase(part->master, instr);
|
||||
if (ret) {
|
||||
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
|
||||
instr->fail_addr -= part->offset;
|
||||
@ -171,30 +280,51 @@ void mtd_erase_callback(struct erase_info *instr)
|
||||
if (instr->callback)
|
||||
instr->callback(instr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_erase_callback);
|
||||
|
||||
static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_lock(part->master, ofs + part->offset, len);
|
||||
return part->master->_lock(part->master, ofs + part->offset, len);
|
||||
}
|
||||
|
||||
static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_unlock(part->master, ofs + part->offset, len);
|
||||
return part->master->_unlock(part->master, ofs + part->offset, len);
|
||||
}
|
||||
|
||||
static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return part->master->_is_locked(part->master, ofs + part->offset, len);
|
||||
}
|
||||
|
||||
static void part_sync(struct mtd_info *mtd)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
mtd_sync(part->master);
|
||||
part->master->_sync(part->master);
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
static int part_suspend(struct mtd_info *mtd)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return part->master->_suspend(part->master);
|
||||
}
|
||||
|
||||
static void part_resume(struct mtd_info *mtd)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
part->master->_resume(part->master);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
ofs += part->offset;
|
||||
return mtd_block_isbad(part->master, ofs);
|
||||
return part->master->_block_isbad(part->master, ofs);
|
||||
}
|
||||
|
||||
static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
@ -203,12 +333,18 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
int res;
|
||||
|
||||
ofs += part->offset;
|
||||
res = mtd_block_markbad(part->master, ofs);
|
||||
res = part->master->_block_markbad(part->master, ofs);
|
||||
if (!res)
|
||||
mtd->ecc_stats.badblocks++;
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline void free_partition(struct mtd_part *p)
|
||||
{
|
||||
kfree(p->mtd.name);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function unregisters and destroy all slave MTD objects which are
|
||||
* attached to the given master MTD object.
|
||||
@ -217,49 +353,78 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
int del_mtd_partitions(struct mtd_info *master)
|
||||
{
|
||||
struct mtd_part *slave, *next;
|
||||
int ret, err = 0;
|
||||
|
||||
mutex_lock(&mtd_partitions_mutex);
|
||||
list_for_each_entry_safe(slave, next, &mtd_partitions, list)
|
||||
if (slave->master == master) {
|
||||
ret = del_mtd_device(&slave->mtd);
|
||||
if (ret < 0) {
|
||||
err = ret;
|
||||
continue;
|
||||
}
|
||||
list_del(&slave->list);
|
||||
if (slave->registered)
|
||||
del_mtd_device(&slave->mtd);
|
||||
kfree(slave);
|
||||
free_partition(slave);
|
||||
}
|
||||
mutex_unlock(&mtd_partitions_mutex);
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct mtd_part *add_one_partition(struct mtd_info *master,
|
||||
const struct mtd_partition *part, int partno,
|
||||
uint64_t cur_offset)
|
||||
static struct mtd_part *allocate_partition(struct mtd_info *master,
|
||||
const struct mtd_partition *part, int partno,
|
||||
uint64_t cur_offset)
|
||||
{
|
||||
struct mtd_part *slave;
|
||||
char *name;
|
||||
|
||||
/* allocate the partition structure */
|
||||
slave = kzalloc(sizeof(*slave), GFP_KERNEL);
|
||||
if (!slave) {
|
||||
name = kstrdup(part->name, GFP_KERNEL);
|
||||
if (!name || !slave) {
|
||||
printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
|
||||
master->name);
|
||||
del_mtd_partitions(master);
|
||||
return NULL;
|
||||
master->name);
|
||||
kfree(name);
|
||||
kfree(slave);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
list_add(&slave->list, &mtd_partitions);
|
||||
|
||||
/* set up the MTD object for this partition */
|
||||
slave->mtd.type = master->type;
|
||||
slave->mtd.flags = master->flags & ~part->mask_flags;
|
||||
slave->mtd.size = part->size;
|
||||
slave->mtd.writesize = master->writesize;
|
||||
slave->mtd.writebufsize = master->writebufsize;
|
||||
slave->mtd.oobsize = master->oobsize;
|
||||
slave->mtd.oobavail = master->oobavail;
|
||||
slave->mtd.subpage_sft = master->subpage_sft;
|
||||
|
||||
slave->mtd.name = part->name;
|
||||
slave->mtd.name = name;
|
||||
slave->mtd.owner = master->owner;
|
||||
#ifndef __UBOOT__
|
||||
slave->mtd.backing_dev_info = master->backing_dev_info;
|
||||
|
||||
/* NOTE: we don't arrange MTDs as a tree; it'd be error-prone
|
||||
* to have the same data be in two different partitions.
|
||||
*/
|
||||
slave->mtd.dev.parent = master->dev.parent;
|
||||
#endif
|
||||
|
||||
slave->mtd._read = part_read;
|
||||
slave->mtd._write = part_write;
|
||||
|
||||
if (master->_panic_write)
|
||||
slave->mtd._panic_write = part_panic_write;
|
||||
|
||||
#ifndef __UBOOT__
|
||||
if (master->_point && master->_unpoint) {
|
||||
slave->mtd._point = part_point;
|
||||
slave->mtd._unpoint = part_unpoint;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (master->_get_unmapped_area)
|
||||
slave->mtd._get_unmapped_area = part_get_unmapped_area;
|
||||
if (master->_read_oob)
|
||||
slave->mtd._read_oob = part_read_oob;
|
||||
if (master->_write_oob)
|
||||
@ -278,10 +443,21 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
|
||||
slave->mtd._get_fact_prot_info = part_get_fact_prot_info;
|
||||
if (master->_sync)
|
||||
slave->mtd._sync = part_sync;
|
||||
#ifndef __UBOOT__
|
||||
if (!partno && !master->dev.class && master->_suspend &&
|
||||
master->_resume) {
|
||||
slave->mtd._suspend = part_suspend;
|
||||
slave->mtd._resume = part_resume;
|
||||
}
|
||||
if (master->_writev)
|
||||
slave->mtd._writev = part_writev;
|
||||
#endif
|
||||
if (master->_lock)
|
||||
slave->mtd._lock = part_lock;
|
||||
if (master->_unlock)
|
||||
slave->mtd._unlock = part_unlock;
|
||||
if (master->_is_locked)
|
||||
slave->mtd._is_locked = part_is_locked;
|
||||
if (master->_block_isbad)
|
||||
slave->mtd._block_isbad = part_block_isbad;
|
||||
if (master->_block_markbad)
|
||||
@ -289,7 +465,6 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
|
||||
slave->mtd._erase = part_erase;
|
||||
slave->master = master;
|
||||
slave->offset = part->offset;
|
||||
slave->index = partno;
|
||||
|
||||
if (slave->offset == MTDPART_OFS_APPEND)
|
||||
slave->offset = cur_offset;
|
||||
@ -298,18 +473,29 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
|
||||
if (mtd_mod_by_eb(cur_offset, master) != 0) {
|
||||
/* Round up to next erasesize */
|
||||
slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
|
||||
debug("Moving partition %d: 0x%012llx -> 0x%012llx\n",
|
||||
partno, (unsigned long long)cur_offset,
|
||||
(unsigned long long)slave->offset);
|
||||
debug("Moving partition %d: "
|
||||
"0x%012llx -> 0x%012llx\n", partno,
|
||||
(unsigned long long)cur_offset, (unsigned long long)slave->offset);
|
||||
}
|
||||
}
|
||||
if (slave->offset == MTDPART_OFS_RETAIN) {
|
||||
slave->offset = cur_offset;
|
||||
if (master->size - slave->offset >= slave->mtd.size) {
|
||||
slave->mtd.size = master->size - slave->offset
|
||||
- slave->mtd.size;
|
||||
} else {
|
||||
debug("mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n",
|
||||
part->name, master->size - slave->offset,
|
||||
slave->mtd.size);
|
||||
/* register to preserve ordering */
|
||||
goto out_register;
|
||||
}
|
||||
}
|
||||
if (slave->mtd.size == MTDPART_SIZ_FULL)
|
||||
slave->mtd.size = master->size - slave->offset;
|
||||
|
||||
debug("0x%012llx-0x%012llx : \"%s\"\n",
|
||||
(unsigned long long)slave->offset,
|
||||
(unsigned long long)(slave->offset + slave->mtd.size),
|
||||
slave->mtd.name);
|
||||
debug("0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
|
||||
(unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
|
||||
|
||||
/* let's do some sanity checks */
|
||||
if (slave->offset >= master->size) {
|
||||
@ -336,7 +522,8 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
|
||||
for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
|
||||
;
|
||||
/* The loop searched for the region _behind_ the first one */
|
||||
i--;
|
||||
if (i > 0)
|
||||
i--;
|
||||
|
||||
/* Pick biggest erasesize */
|
||||
for (; i < max && regions[i].offset < end; i++) {
|
||||
@ -367,6 +554,10 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
|
||||
}
|
||||
|
||||
slave->mtd.ecclayout = master->ecclayout;
|
||||
slave->mtd.ecc_step_size = master->ecc_step_size;
|
||||
slave->mtd.ecc_strength = master->ecc_strength;
|
||||
slave->mtd.bitflip_threshold = master->bitflip_threshold;
|
||||
|
||||
if (master->_block_isbad) {
|
||||
uint64_t offs = 0;
|
||||
|
||||
@ -378,18 +569,89 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
|
||||
}
|
||||
|
||||
out_register:
|
||||
if (part->mtdp) {
|
||||
/* store the object pointer (caller may or may not register it*/
|
||||
*part->mtdp = &slave->mtd;
|
||||
slave->registered = 0;
|
||||
} else {
|
||||
/* register our partition */
|
||||
add_mtd_device(&slave->mtd);
|
||||
slave->registered = 1;
|
||||
}
|
||||
return slave;
|
||||
}
|
||||
|
||||
int mtd_add_partition(struct mtd_info *master, const char *name,
|
||||
long long offset, long long length)
|
||||
{
|
||||
struct mtd_partition part;
|
||||
struct mtd_part *p, *new;
|
||||
uint64_t start, end;
|
||||
int ret = 0;
|
||||
|
||||
/* the direct offset is expected */
|
||||
if (offset == MTDPART_OFS_APPEND ||
|
||||
offset == MTDPART_OFS_NXTBLK)
|
||||
return -EINVAL;
|
||||
|
||||
if (length == MTDPART_SIZ_FULL)
|
||||
length = master->size - offset;
|
||||
|
||||
if (length <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
part.name = name;
|
||||
part.size = length;
|
||||
part.offset = offset;
|
||||
part.mask_flags = 0;
|
||||
part.ecclayout = NULL;
|
||||
|
||||
new = allocate_partition(master, &part, -1, offset);
|
||||
if (IS_ERR(new))
|
||||
return PTR_ERR(new);
|
||||
|
||||
start = offset;
|
||||
end = offset + length;
|
||||
|
||||
mutex_lock(&mtd_partitions_mutex);
|
||||
list_for_each_entry(p, &mtd_partitions, list)
|
||||
if (p->master == master) {
|
||||
if ((start >= p->offset) &&
|
||||
(start < (p->offset + p->mtd.size)))
|
||||
goto err_inv;
|
||||
|
||||
if ((end >= p->offset) &&
|
||||
(end < (p->offset + p->mtd.size)))
|
||||
goto err_inv;
|
||||
}
|
||||
|
||||
list_add(&new->list, &mtd_partitions);
|
||||
mutex_unlock(&mtd_partitions_mutex);
|
||||
|
||||
add_mtd_device(&new->mtd);
|
||||
|
||||
return ret;
|
||||
err_inv:
|
||||
mutex_unlock(&mtd_partitions_mutex);
|
||||
free_partition(new);
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_add_partition);
|
||||
|
||||
int mtd_del_partition(struct mtd_info *master, int partno)
|
||||
{
|
||||
struct mtd_part *slave, *next;
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&mtd_partitions_mutex);
|
||||
list_for_each_entry_safe(slave, next, &mtd_partitions, list)
|
||||
if ((slave->master == master) &&
|
||||
(slave->mtd.index == partno)) {
|
||||
ret = del_mtd_device(&slave->mtd);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
list_del(&slave->list);
|
||||
free_partition(slave);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&mtd_partitions_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_del_partition);
|
||||
|
||||
/*
|
||||
* This function, given a master MTD object and a partition table, creates
|
||||
* and registers slave MTD objects which are bound to the master according to
|
||||
@ -407,6 +669,7 @@ int add_mtd_partitions(struct mtd_info *master,
|
||||
uint64_t cur_offset = 0;
|
||||
int i;
|
||||
|
||||
#ifdef __UBOOT__
|
||||
/*
|
||||
* Need to init the list here, since LIST_INIT() does not
|
||||
* work on platforms where relocation has problems (like MIPS
|
||||
@ -414,15 +677,147 @@ int add_mtd_partitions(struct mtd_info *master,
|
||||
*/
|
||||
if (mtd_partitions.next == NULL)
|
||||
INIT_LIST_HEAD(&mtd_partitions);
|
||||
#endif
|
||||
|
||||
debug("Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
|
||||
|
||||
for (i = 0; i < nbparts; i++) {
|
||||
slave = add_one_partition(master, parts + i, i, cur_offset);
|
||||
if (!slave)
|
||||
return -ENOMEM;
|
||||
slave = allocate_partition(master, parts + i, i, cur_offset);
|
||||
if (IS_ERR(slave))
|
||||
return PTR_ERR(slave);
|
||||
|
||||
mutex_lock(&mtd_partitions_mutex);
|
||||
list_add(&slave->list, &mtd_partitions);
|
||||
mutex_unlock(&mtd_partitions_mutex);
|
||||
|
||||
add_mtd_device(&slave->mtd);
|
||||
|
||||
cur_offset = slave->offset + slave->mtd.size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
static DEFINE_SPINLOCK(part_parser_lock);
|
||||
static LIST_HEAD(part_parsers);
|
||||
|
||||
static struct mtd_part_parser *get_partition_parser(const char *name)
|
||||
{
|
||||
struct mtd_part_parser *p, *ret = NULL;
|
||||
|
||||
spin_lock(&part_parser_lock);
|
||||
|
||||
list_for_each_entry(p, &part_parsers, list)
|
||||
if (!strcmp(p->name, name) && try_module_get(p->owner)) {
|
||||
ret = p;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock(&part_parser_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define put_partition_parser(p) do { module_put((p)->owner); } while (0)
|
||||
|
||||
void register_mtd_parser(struct mtd_part_parser *p)
|
||||
{
|
||||
spin_lock(&part_parser_lock);
|
||||
list_add(&p->list, &part_parsers);
|
||||
spin_unlock(&part_parser_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_mtd_parser);
|
||||
|
||||
void deregister_mtd_parser(struct mtd_part_parser *p)
|
||||
{
|
||||
spin_lock(&part_parser_lock);
|
||||
list_del(&p->list);
|
||||
spin_unlock(&part_parser_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(deregister_mtd_parser);
|
||||
|
||||
/*
|
||||
* Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you
|
||||
* are changing this array!
|
||||
*/
|
||||
static const char * const default_mtd_part_types[] = {
|
||||
"cmdlinepart",
|
||||
"ofpart",
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* parse_mtd_partitions - parse MTD partitions
|
||||
* @master: the master partition (describes whole MTD device)
|
||||
* @types: names of partition parsers to try or %NULL
|
||||
* @pparts: array of partitions found is returned here
|
||||
* @data: MTD partition parser-specific data
|
||||
*
|
||||
* This function tries to find partition on MTD device @master. It uses MTD
|
||||
* partition parsers, specified in @types. However, if @types is %NULL, then
|
||||
* the default list of parsers is used. The default list contains only the
|
||||
* "cmdlinepart" and "ofpart" parsers ATM.
|
||||
* Note: If there are more then one parser in @types, the kernel only takes the
|
||||
* partitions parsed out by the first parser.
|
||||
*
|
||||
* This function may return:
|
||||
* o a negative error code in case of failure
|
||||
* o zero if no partitions were found
|
||||
* o a positive number of found partitions, in which case on exit @pparts will
|
||||
* point to an array containing this number of &struct mtd_info objects.
|
||||
*/
|
||||
int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
|
||||
struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct mtd_part_parser *parser;
|
||||
int ret = 0;
|
||||
|
||||
if (!types)
|
||||
types = default_mtd_part_types;
|
||||
|
||||
for ( ; ret <= 0 && *types; types++) {
|
||||
parser = get_partition_parser(*types);
|
||||
if (!parser && !request_module("%s", *types))
|
||||
parser = get_partition_parser(*types);
|
||||
if (!parser)
|
||||
continue;
|
||||
ret = (*parser->parse_fn)(master, pparts, data);
|
||||
put_partition_parser(parser);
|
||||
if (ret > 0) {
|
||||
printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
|
||||
ret, parser->name, master->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
int mtd_is_partition(const struct mtd_info *mtd)
|
||||
{
|
||||
struct mtd_part *part;
|
||||
int ispart = 0;
|
||||
|
||||
mutex_lock(&mtd_partitions_mutex);
|
||||
list_for_each_entry(part, &mtd_partitions, list)
|
||||
if (&part->mtd == mtd) {
|
||||
ispart = 1;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&mtd_partitions_mutex);
|
||||
|
||||
return ispart;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_is_partition);
|
||||
|
||||
/* Returns the size of the entire flash chip */
|
||||
uint64_t mtd_get_device_size(const struct mtd_info *mtd)
|
||||
{
|
||||
if (!mtd_is_partition(mtd))
|
||||
return mtd->size;
|
||||
|
||||
return PART(mtd)->master->size;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_get_device_size);
|
||||
|
@ -561,6 +561,7 @@ static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
|
||||
len, avail);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
/*
|
||||
* Verify buffer against the FCM Controller Data Buffer
|
||||
*/
|
||||
@ -593,6 +594,7 @@ static int fsl_elbc_verify_buf(struct mtd_info *mtd,
|
||||
ctrl->index += len;
|
||||
return i == len && ctrl->status == LTESR_CC ? 0 : -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This function is called after Program and Erase Operations to
|
||||
* check for success or failure.
|
||||
@ -725,7 +727,9 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr)
|
||||
nand->read_byte = fsl_elbc_read_byte;
|
||||
nand->write_buf = fsl_elbc_write_buf;
|
||||
nand->read_buf = fsl_elbc_read_buf;
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
nand->verify_buf = fsl_elbc_verify_buf;
|
||||
#endif
|
||||
nand->select_chip = fsl_elbc_select_chip;
|
||||
nand->cmdfunc = fsl_elbc_cmdfunc;
|
||||
nand->waitfunc = fsl_elbc_wait;
|
||||
|
@ -684,6 +684,7 @@ static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
|
||||
__func__, len, avail);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
/*
|
||||
* Verify buffer against the IFC Controller Data Buffer
|
||||
*/
|
||||
@ -716,6 +717,7 @@ static int fsl_ifc_verify_buf(struct mtd_info *mtd,
|
||||
ctrl->index += len;
|
||||
return i == len && ctrl->status == IFC_NAND_EVTER_STAT_OPC ? 0 : -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This function is called after Program and Erase Operations to
|
||||
* check for success or failure.
|
||||
@ -939,7 +941,9 @@ static int fsl_ifc_chip_init(int devnum, u8 *addr)
|
||||
|
||||
nand->write_buf = fsl_ifc_write_buf;
|
||||
nand->read_buf = fsl_ifc_read_buf;
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
nand->verify_buf = fsl_ifc_verify_buf;
|
||||
#endif
|
||||
nand->select_chip = fsl_ifc_select_chip;
|
||||
nand->cmdfunc = fsl_ifc_cmdfunc;
|
||||
nand->waitfunc = fsl_ifc_wait;
|
||||
|
@ -153,6 +153,7 @@ static void upm_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
buf[i] = in_8(chip->IO_ADDR_R);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
static int upm_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
@ -165,6 +166,7 @@ static int upm_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int nand_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
@ -191,7 +193,9 @@ int fsl_upm_nand_init(struct nand_chip *chip, struct fsl_upm_nand *fun)
|
||||
chip->read_byte = upm_nand_read_byte;
|
||||
chip->read_buf = upm_nand_read_buf;
|
||||
chip->write_buf = upm_nand_write_buf;
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
chip->verify_buf = upm_nand_verify_buf;
|
||||
#endif
|
||||
if (fun->dev_ready)
|
||||
chip->dev_ready = nand_dev_ready;
|
||||
|
||||
|
@ -459,6 +459,7 @@ static void mpc5121_nfc_write_buf(struct mtd_info *mtd,
|
||||
mpc5121_nfc_buf_copy(mtd, (u_char *) buf, len, 1);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
/* Compare buffer with NAND flash */
|
||||
static int mpc5121_nfc_verify_buf(struct mtd_info *mtd,
|
||||
const u_char * buf, int len)
|
||||
@ -479,6 +480,7 @@ static int mpc5121_nfc_verify_buf(struct mtd_info *mtd,
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Read byte from NFC buffers */
|
||||
static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd)
|
||||
@ -607,7 +609,9 @@ int board_nand_init(struct nand_chip *chip)
|
||||
chip->read_word = mpc5121_nfc_read_word;
|
||||
chip->read_buf = mpc5121_nfc_read_buf;
|
||||
chip->write_buf = mpc5121_nfc_write_buf;
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
chip->verify_buf = mpc5121_nfc_verify_buf;
|
||||
#endif
|
||||
chip->select_chip = mpc5121_nfc_select_chip;
|
||||
chip->bbt_options = NAND_BBT_USE_FLASH;
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
|
@ -949,6 +949,8 @@ static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
host->col_addr = col;
|
||||
}
|
||||
|
||||
#ifdef __UBOOT__
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
/*
|
||||
* Used by the upper layer to verify the data in NAND Flash
|
||||
* with the data in the buf.
|
||||
@ -972,6 +974,8 @@ static int mxc_nand_verify_buf(struct mtd_info *mtd,
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This function is used by upper layer for select and
|
||||
@ -1203,7 +1207,11 @@ int board_nand_init(struct nand_chip *this)
|
||||
this->read_word = mxc_nand_read_word;
|
||||
this->write_buf = mxc_nand_write_buf;
|
||||
this->read_buf = mxc_nand_read_buf;
|
||||
#ifdef __UBOOT__
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
this->verify_buf = mxc_nand_verify_buf;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
host->regs = (struct mxc_nand_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE;
|
||||
#ifdef MXC_NFC_V3_2
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -59,17 +59,55 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <linux/compat.h>
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/bbm.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/string.h>
|
||||
#else
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include <asm/errno.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/bbm.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/string.h>
|
||||
#endif
|
||||
|
||||
#define BBT_BLOCK_GOOD 0x00
|
||||
#define BBT_BLOCK_WORN 0x01
|
||||
#define BBT_BLOCK_RESERVED 0x02
|
||||
#define BBT_BLOCK_FACTORY_BAD 0x03
|
||||
|
||||
#define BBT_ENTRY_MASK 0x03
|
||||
#define BBT_ENTRY_SHIFT 2
|
||||
|
||||
static int nand_update_bbt(struct mtd_info *mtd, loff_t offs);
|
||||
|
||||
static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block)
|
||||
{
|
||||
uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT];
|
||||
entry >>= (block & BBT_ENTRY_MASK) * 2;
|
||||
return entry & BBT_ENTRY_MASK;
|
||||
}
|
||||
|
||||
static inline void bbt_mark_entry(struct nand_chip *chip, int block,
|
||||
uint8_t mark)
|
||||
{
|
||||
uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2);
|
||||
chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk;
|
||||
}
|
||||
|
||||
static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
|
||||
{
|
||||
@ -86,33 +124,17 @@ static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
|
||||
* @td: search pattern descriptor
|
||||
*
|
||||
* Check for a pattern at the given place. Used to search bad block tables and
|
||||
* good / bad block identifiers. If the SCAN_EMPTY option is set then check, if
|
||||
* all bytes except the pattern area contain 0xff.
|
||||
* good / bad block identifiers.
|
||||
*/
|
||||
static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
|
||||
{
|
||||
int end = 0;
|
||||
uint8_t *p = buf;
|
||||
|
||||
if (td->options & NAND_BBT_NO_OOB)
|
||||
return check_pattern_no_oob(buf, td);
|
||||
|
||||
end = paglen + td->offs;
|
||||
if (td->options & NAND_BBT_SCANEMPTY)
|
||||
if (memchr_inv(p, 0xff, end))
|
||||
return -1;
|
||||
p += end;
|
||||
|
||||
/* Compare the pattern */
|
||||
if (memcmp(p, td->pattern, td->len))
|
||||
if (memcmp(buf + paglen + td->offs, td->pattern, td->len))
|
||||
return -1;
|
||||
|
||||
if (td->options & NAND_BBT_SCANEMPTY) {
|
||||
p += td->len;
|
||||
end += td->len;
|
||||
if (memchr_inv(p, 0xff, len - end))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -159,7 +181,7 @@ static u32 add_marker_len(struct nand_bbt_descr *td)
|
||||
* @page: the starting page
|
||||
* @num: the number of bbt descriptors to read
|
||||
* @td: the bbt describtion table
|
||||
* @offs: offset in the memory table
|
||||
* @offs: block number offset in the table
|
||||
*
|
||||
* Read the bad block table starting from page.
|
||||
*/
|
||||
@ -209,25 +231,33 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
|
||||
/* Analyse data */
|
||||
for (i = 0; i < len; i++) {
|
||||
uint8_t dat = buf[i];
|
||||
for (j = 0; j < 8; j += bits, act += 2) {
|
||||
for (j = 0; j < 8; j += bits, act++) {
|
||||
uint8_t tmp = (dat >> j) & msk;
|
||||
if (tmp == msk)
|
||||
continue;
|
||||
if (reserved_block_code && (tmp == reserved_block_code)) {
|
||||
pr_info("nand_read_bbt: reserved block at 0x%012llx\n",
|
||||
(loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
|
||||
this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
|
||||
(loff_t)(offs + act) <<
|
||||
this->bbt_erase_shift);
|
||||
bbt_mark_entry(this, offs + act,
|
||||
BBT_BLOCK_RESERVED);
|
||||
mtd->ecc_stats.bbtblocks++;
|
||||
continue;
|
||||
}
|
||||
pr_info("nand_read_bbt: Bad block at 0x%012llx\n",
|
||||
(loff_t)((offs << 2) + (act >> 1))
|
||||
<< this->bbt_erase_shift);
|
||||
/*
|
||||
* Leave it for now, if it's matured we can
|
||||
* move this message to pr_debug.
|
||||
*/
|
||||
pr_info("nand_read_bbt: bad block at 0x%012llx\n",
|
||||
(loff_t)(offs + act) <<
|
||||
this->bbt_erase_shift);
|
||||
/* Factory marked bad or worn out? */
|
||||
if (tmp == 0)
|
||||
this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
|
||||
bbt_mark_entry(this, offs + act,
|
||||
BBT_BLOCK_FACTORY_BAD);
|
||||
else
|
||||
this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
|
||||
bbt_mark_entry(this, offs + act,
|
||||
BBT_BLOCK_WORN);
|
||||
mtd->ecc_stats.badblocks++;
|
||||
}
|
||||
}
|
||||
@ -262,7 +292,7 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
|
||||
td, offs);
|
||||
if (res)
|
||||
return res;
|
||||
offs += this->chipsize >> (this->bbt_erase_shift + 2);
|
||||
offs += this->chipsize >> this->bbt_erase_shift;
|
||||
}
|
||||
} else {
|
||||
res = read_bbt(mtd, buf, td->pages[0],
|
||||
@ -396,25 +426,6 @@ static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
|
||||
}
|
||||
}
|
||||
|
||||
/* Scan a given block full */
|
||||
static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
|
||||
loff_t offs, uint8_t *buf, size_t readlen,
|
||||
int scanlen, int numpages)
|
||||
{
|
||||
int ret, j;
|
||||
|
||||
ret = scan_read_oob(mtd, buf, offs, readlen);
|
||||
/* Ignore ECC errors when checking for BBM */
|
||||
if (ret && !mtd_is_bitflip_or_eccerr(ret))
|
||||
return ret;
|
||||
|
||||
for (j = 0; j < numpages; j++, buf += scanlen) {
|
||||
if (check_pattern(buf, scanlen, mtd->writesize, bd))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Scan a given block partially */
|
||||
static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
|
||||
loff_t offs, uint8_t *buf, int numpages)
|
||||
@ -461,36 +472,19 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||
struct nand_bbt_descr *bd, int chip)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int i, numblocks, numpages, scanlen;
|
||||
int i, numblocks, numpages;
|
||||
int startblock;
|
||||
loff_t from;
|
||||
size_t readlen;
|
||||
|
||||
pr_info("Scanning device for bad blocks\n");
|
||||
|
||||
if (bd->options & NAND_BBT_SCANALLPAGES)
|
||||
numpages = 1 << (this->bbt_erase_shift - this->page_shift);
|
||||
else if (bd->options & NAND_BBT_SCAN2NDPAGE)
|
||||
if (bd->options & NAND_BBT_SCAN2NDPAGE)
|
||||
numpages = 2;
|
||||
else
|
||||
numpages = 1;
|
||||
|
||||
if (!(bd->options & NAND_BBT_SCANEMPTY)) {
|
||||
/* We need only read few bytes from the OOB area */
|
||||
scanlen = 0;
|
||||
readlen = bd->len;
|
||||
} else {
|
||||
/* Full page content should be read */
|
||||
scanlen = mtd->writesize + mtd->oobsize;
|
||||
readlen = numpages * mtd->writesize;
|
||||
}
|
||||
|
||||
if (chip == -1) {
|
||||
/*
|
||||
* Note that numblocks is 2 * (real numblocks) here, see i+=2
|
||||
* below as it makes shifting and masking less painful
|
||||
*/
|
||||
numblocks = mtd->size >> (this->bbt_erase_shift - 1);
|
||||
numblocks = mtd->size >> this->bbt_erase_shift;
|
||||
startblock = 0;
|
||||
from = 0;
|
||||
} else {
|
||||
@ -499,37 +493,31 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||
chip + 1, this->numchips);
|
||||
return -EINVAL;
|
||||
}
|
||||
numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
|
||||
numblocks = this->chipsize >> this->bbt_erase_shift;
|
||||
startblock = chip * numblocks;
|
||||
numblocks += startblock;
|
||||
from = (loff_t)startblock << (this->bbt_erase_shift - 1);
|
||||
from = (loff_t)startblock << this->bbt_erase_shift;
|
||||
}
|
||||
|
||||
if (this->bbt_options & NAND_BBT_SCANLASTPAGE)
|
||||
from += mtd->erasesize - (mtd->writesize * numpages);
|
||||
|
||||
for (i = startblock; i < numblocks;) {
|
||||
for (i = startblock; i < numblocks; i++) {
|
||||
int ret;
|
||||
|
||||
BUG_ON(bd->options & NAND_BBT_NO_OOB);
|
||||
|
||||
if (bd->options & NAND_BBT_SCANALLPAGES)
|
||||
ret = scan_block_full(mtd, bd, from, buf, readlen,
|
||||
scanlen, numpages);
|
||||
else
|
||||
ret = scan_block_fast(mtd, bd, from, buf, numpages);
|
||||
|
||||
ret = scan_block_fast(mtd, bd, from, buf, numpages);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret) {
|
||||
this->bbt[i >> 3] |= 0x03 << (i & 0x6);
|
||||
bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD);
|
||||
pr_warn("Bad eraseblock %d at 0x%012llx\n",
|
||||
i >> 1, (unsigned long long)from);
|
||||
i, (unsigned long long)from);
|
||||
mtd->ecc_stats.badblocks++;
|
||||
}
|
||||
|
||||
i += 2;
|
||||
from += (1 << this->bbt_erase_shift);
|
||||
}
|
||||
return 0;
|
||||
@ -554,7 +542,11 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int i, chips;
|
||||
#ifndef __UBOOT__
|
||||
int bits, startblock, block, dir;
|
||||
#else
|
||||
int startblock, block, dir;
|
||||
#endif
|
||||
int scanlen = mtd->writesize + mtd->oobsize;
|
||||
int bbtblocks;
|
||||
int blocktopage = this->bbt_erase_shift - this->page_shift;
|
||||
@ -578,6 +570,11 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
bbtblocks = mtd->size >> this->bbt_erase_shift;
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/* Number of bits for each erase block in the bbt */
|
||||
bits = td->options & NAND_BBT_NRBITS_MSK;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < chips; i++) {
|
||||
/* Reset version information */
|
||||
td->version[i] = 0;
|
||||
@ -606,8 +603,8 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
if (td->pages[i] == -1)
|
||||
pr_warn("Bad block table not found for chip %d\n", i);
|
||||
else
|
||||
pr_info("Bad block table found at page %d, version 0x%02X\n", td->pages[i],
|
||||
td->version[i]);
|
||||
pr_info("Bad block table found at page %d, version "
|
||||
"0x%02X\n", td->pages[i], td->version[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -649,9 +646,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
struct erase_info einfo;
|
||||
int i, j, res, chip = 0;
|
||||
int i, res, chip = 0;
|
||||
int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
|
||||
int nrchips, bbtoffs, pageoffs, ooboffs;
|
||||
int nrchips, pageoffs, ooboffs;
|
||||
uint8_t msk[4];
|
||||
uint8_t rcode = td->reserved_block_code;
|
||||
size_t retlen, len = 0;
|
||||
@ -707,10 +704,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||
for (i = 0; i < td->maxblocks; i++) {
|
||||
int block = startblock + dir * i;
|
||||
/* Check, if the block is bad */
|
||||
switch ((this->bbt[block >> 2] >>
|
||||
(2 * (block & 0x03))) & 0x03) {
|
||||
case 0x01:
|
||||
case 0x03:
|
||||
switch (bbt_get_entry(this, block)) {
|
||||
case BBT_BLOCK_WORN:
|
||||
case BBT_BLOCK_FACTORY_BAD:
|
||||
continue;
|
||||
}
|
||||
page = block <<
|
||||
@ -742,8 +738,6 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||
default: return -EINVAL;
|
||||
}
|
||||
|
||||
bbtoffs = chip * (numblocks >> 2);
|
||||
|
||||
to = ((loff_t)page) << this->page_shift;
|
||||
|
||||
/* Must we save the block contents? */
|
||||
@ -808,16 +802,12 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||
buf[ooboffs + td->veroffs] = td->version[chip];
|
||||
|
||||
/* Walk through the memory table */
|
||||
for (i = 0; i < numblocks;) {
|
||||
for (i = 0; i < numblocks; i++) {
|
||||
uint8_t dat;
|
||||
dat = this->bbt[bbtoffs + (i >> 2)];
|
||||
for (j = 0; j < 4; j++, i++) {
|
||||
int sftcnt = (i << (3 - sft)) & sftmsk;
|
||||
/* Do not store the reserved bbt blocks! */
|
||||
buf[offs + (i >> sft)] &=
|
||||
~(msk[dat & 0x03] << sftcnt);
|
||||
dat >>= 2;
|
||||
}
|
||||
int sftcnt = (i << (3 - sft)) & sftmsk;
|
||||
dat = bbt_get_entry(this, chip * numblocks + i);
|
||||
/* Do not store the reserved bbt blocks! */
|
||||
buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt);
|
||||
}
|
||||
|
||||
memset(&einfo, 0, sizeof(einfo));
|
||||
@ -859,7 +849,6 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
bd->options &= ~NAND_BBT_SCANEMPTY;
|
||||
return create_bbt(mtd, this->buffers->databuf, bd, -1);
|
||||
}
|
||||
|
||||
@ -1003,7 +992,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int i, j, chips, block, nrblocks, update;
|
||||
uint8_t oldval, newval;
|
||||
uint8_t oldval;
|
||||
|
||||
/* Do we have a bbt per chip? */
|
||||
if (td->options & NAND_BBT_PERCHIP) {
|
||||
@ -1020,12 +1009,12 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
|
||||
if (td->pages[i] == -1)
|
||||
continue;
|
||||
block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
|
||||
block <<= 1;
|
||||
oldval = this->bbt[(block >> 3)];
|
||||
newval = oldval | (0x2 << (block & 0x06));
|
||||
this->bbt[(block >> 3)] = newval;
|
||||
if ((oldval != newval) && td->reserved_block_code)
|
||||
nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1));
|
||||
oldval = bbt_get_entry(this, block);
|
||||
bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
|
||||
if ((oldval != BBT_BLOCK_RESERVED) &&
|
||||
td->reserved_block_code)
|
||||
nand_update_bbt(mtd, (loff_t)block <<
|
||||
this->bbt_erase_shift);
|
||||
continue;
|
||||
}
|
||||
update = 0;
|
||||
@ -1033,14 +1022,12 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
|
||||
block = ((i + 1) * nrblocks) - td->maxblocks;
|
||||
else
|
||||
block = i * nrblocks;
|
||||
block <<= 1;
|
||||
for (j = 0; j < td->maxblocks; j++) {
|
||||
oldval = this->bbt[(block >> 3)];
|
||||
newval = oldval | (0x2 << (block & 0x06));
|
||||
this->bbt[(block >> 3)] = newval;
|
||||
if (oldval != newval)
|
||||
oldval = bbt_get_entry(this, block);
|
||||
bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
|
||||
if (oldval != BBT_BLOCK_RESERVED)
|
||||
update = 1;
|
||||
block += 2;
|
||||
block++;
|
||||
}
|
||||
/*
|
||||
* If we want reserved blocks to be recorded to flash, and some
|
||||
@ -1048,7 +1035,8 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
|
||||
* bbts. This should only happen once.
|
||||
*/
|
||||
if (update && td->reserved_block_code)
|
||||
nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1));
|
||||
nand_update_bbt(mtd, (loff_t)(block - 1) <<
|
||||
this->bbt_erase_shift);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1174,13 +1162,13 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_update_bbt - [NAND Interface] update bad block table(s)
|
||||
* nand_update_bbt - update bad block table(s)
|
||||
* @mtd: MTD device structure
|
||||
* @offs: the offset of the newly marked block
|
||||
*
|
||||
* The function updates the bad block table(s).
|
||||
*/
|
||||
int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
|
||||
static int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int len, res = 0;
|
||||
@ -1234,15 +1222,6 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
|
||||
*/
|
||||
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
|
||||
|
||||
static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 };
|
||||
|
||||
static struct nand_bbt_descr agand_flashbased = {
|
||||
.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
|
||||
.offs = 0x20,
|
||||
.len = 6,
|
||||
.pattern = scan_agand_pattern
|
||||
};
|
||||
|
||||
/* Generic flash bbt descriptors */
|
||||
static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
|
||||
static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
|
||||
@ -1327,22 +1306,6 @@ int nand_default_bbt(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
/*
|
||||
* Default for AG-AND. We must use a flash based bad block table as the
|
||||
* devices have factory marked _good_ blocks. Erasing those blocks
|
||||
* leads to loss of the good / bad information, so we _must_ store this
|
||||
* information in a good / bad table during startup.
|
||||
*/
|
||||
if (this->options & NAND_IS_AND) {
|
||||
/* Use the default pattern descriptors */
|
||||
if (!this->bbt_td) {
|
||||
this->bbt_td = &bbt_main_descr;
|
||||
this->bbt_md = &bbt_mirror_descr;
|
||||
}
|
||||
this->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
return nand_scan_bbt(mtd, &agand_flashbased);
|
||||
}
|
||||
|
||||
/* Is a flash based bad block table requested? */
|
||||
if (this->bbt_options & NAND_BBT_USE_FLASH) {
|
||||
/* Use the default pattern descriptors */
|
||||
@ -1375,23 +1338,46 @@ int nand_default_bbt(struct mtd_info *mtd)
|
||||
int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int block;
|
||||
uint8_t res;
|
||||
int block, res;
|
||||
|
||||
/* Get block number * 2 */
|
||||
block = (int)(offs >> (this->bbt_erase_shift - 1));
|
||||
res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
|
||||
block = (int)(offs >> this->bbt_erase_shift);
|
||||
res = bbt_get_entry(this, block);
|
||||
|
||||
MTDDEBUG(MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
|
||||
(unsigned int)offs, block >> 1, res);
|
||||
pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: "
|
||||
"(block %d) 0x%02x\n",
|
||||
(unsigned int)offs, block, res);
|
||||
|
||||
switch ((int)res) {
|
||||
case 0x00:
|
||||
switch (res) {
|
||||
case BBT_BLOCK_GOOD:
|
||||
return 0;
|
||||
case 0x01:
|
||||
case BBT_BLOCK_WORN:
|
||||
return 1;
|
||||
case 0x02:
|
||||
case BBT_BLOCK_RESERVED:
|
||||
return allowbbt ? 0 : 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT
|
||||
* @mtd: MTD device structure
|
||||
* @offs: offset of the bad block
|
||||
*/
|
||||
int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int block, ret = 0;
|
||||
|
||||
block = (int)(offs >> this->bbt_erase_shift);
|
||||
|
||||
/* Mark bad block in memory */
|
||||
bbt_mark_entry(this, block, BBT_BLOCK_WORN);
|
||||
|
||||
/* Update flash-based bad block table */
|
||||
if (this->bbt_options & NAND_BBT_USE_FLASH)
|
||||
ret = nand_update_bbt(mtd, offs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(nand_scan_bbt);
|
||||
|
@ -8,165 +8,172 @@
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#else
|
||||
#include <common.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
/*
|
||||
* Chip ID list
|
||||
*
|
||||
* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size,
|
||||
* options
|
||||
*
|
||||
* Pagesize; 0, 256, 512
|
||||
* 0 get this information from the extended chip ID
|
||||
+ 256 256 Byte page size
|
||||
* 512 512 Byte page size
|
||||
*/
|
||||
const struct nand_flash_dev nand_flash_ids[] = {
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_MUSEUM_IDS
|
||||
{"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0},
|
||||
{"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0},
|
||||
{"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0},
|
||||
{"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0},
|
||||
{"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0},
|
||||
{"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0},
|
||||
{"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0},
|
||||
{"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0},
|
||||
{"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0},
|
||||
{"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0},
|
||||
|
||||
{"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0},
|
||||
{"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0},
|
||||
{"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
|
||||
{"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
|
||||
#endif
|
||||
#include <linux/sizes.h>
|
||||
|
||||
{"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0},
|
||||
{"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0},
|
||||
{"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16},
|
||||
|
||||
{"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0},
|
||||
{"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0},
|
||||
{"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16},
|
||||
|
||||
{"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0},
|
||||
{"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0},
|
||||
{"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},
|
||||
|
||||
{"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0},
|
||||
{"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0},
|
||||
{"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0},
|
||||
{"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16},
|
||||
|
||||
{"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0},
|
||||
|
||||
/*
|
||||
* These are the new chips with large page size. The pagesize and the
|
||||
* erasesize is determined from the extended id bytes
|
||||
*/
|
||||
#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS
|
||||
#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
|
||||
|
||||
/* 512 Megabit */
|
||||
{"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 1,8V 8-bit", 0xA0, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 3,3V 8-bit", 0xD0, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 3,3V 8-bit", 0xF0, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16},
|
||||
{"NAND 64MiB 1,8V 16-bit", 0xB0, 0, 64, 0, LP_OPTIONS16},
|
||||
{"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16},
|
||||
{"NAND 64MiB 3,3V 16-bit", 0xC0, 0, 64, 0, LP_OPTIONS16},
|
||||
|
||||
/* 1 Gigabit */
|
||||
{"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS},
|
||||
{"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS},
|
||||
{"NAND 128MiB 3,3V 8-bit", 0xD1, 0, 128, 0, LP_OPTIONS},
|
||||
{"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16},
|
||||
{"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16},
|
||||
{"NAND 128MiB 1,8V 16-bit", 0xAD, 0, 128, 0, LP_OPTIONS16},
|
||||
|
||||
/* 2 Gigabit */
|
||||
{"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS},
|
||||
{"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS},
|
||||
{"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16},
|
||||
{"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16},
|
||||
|
||||
/* 4 Gigabit */
|
||||
{"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS},
|
||||
{"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS},
|
||||
{"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16},
|
||||
{"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16},
|
||||
|
||||
/* 8 Gigabit */
|
||||
{"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS},
|
||||
{"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS},
|
||||
{"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16},
|
||||
{"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16},
|
||||
|
||||
/* 16 Gigabit */
|
||||
{"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS},
|
||||
{"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS},
|
||||
{"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16},
|
||||
{"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16},
|
||||
|
||||
/* 32 Gigabit */
|
||||
{"NAND 4GiB 1,8V 8-bit", 0xA7, 0, 4096, 0, LP_OPTIONS},
|
||||
{"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS},
|
||||
{"NAND 4GiB 1,8V 16-bit", 0xB7, 0, 4096, 0, LP_OPTIONS16},
|
||||
{"NAND 4GiB 3,3V 16-bit", 0xC7, 0, 4096, 0, LP_OPTIONS16},
|
||||
|
||||
/* 64 Gigabit */
|
||||
{"NAND 8GiB 1,8V 8-bit", 0xAE, 0, 8192, 0, LP_OPTIONS},
|
||||
{"NAND 8GiB 3,3V 8-bit", 0xDE, 0, 8192, 0, LP_OPTIONS},
|
||||
{"NAND 8GiB 1,8V 16-bit", 0xBE, 0, 8192, 0, LP_OPTIONS16},
|
||||
{"NAND 8GiB 3,3V 16-bit", 0xCE, 0, 8192, 0, LP_OPTIONS16},
|
||||
|
||||
/* 128 Gigabit */
|
||||
{"NAND 16GiB 1,8V 8-bit", 0x1A, 0, 16384, 0, LP_OPTIONS},
|
||||
{"NAND 16GiB 3,3V 8-bit", 0x3A, 0, 16384, 0, LP_OPTIONS},
|
||||
{"NAND 16GiB 1,8V 16-bit", 0x2A, 0, 16384, 0, LP_OPTIONS16},
|
||||
{"NAND 16GiB 3,3V 16-bit", 0x4A, 0, 16384, 0, LP_OPTIONS16},
|
||||
|
||||
/* 256 Gigabit */
|
||||
{"NAND 32GiB 1,8V 8-bit", 0x1C, 0, 32768, 0, LP_OPTIONS},
|
||||
{"NAND 32GiB 3,3V 8-bit", 0x3C, 0, 32768, 0, LP_OPTIONS},
|
||||
{"NAND 32GiB 1,8V 16-bit", 0x2C, 0, 32768, 0, LP_OPTIONS16},
|
||||
{"NAND 32GiB 3,3V 16-bit", 0x4C, 0, 32768, 0, LP_OPTIONS16},
|
||||
|
||||
/* 512 Gigabit */
|
||||
{"NAND 64GiB 1,8V 8-bit", 0x1E, 0, 65536, 0, LP_OPTIONS},
|
||||
{"NAND 64GiB 3,3V 8-bit", 0x3E, 0, 65536, 0, LP_OPTIONS},
|
||||
{"NAND 64GiB 1,8V 16-bit", 0x2E, 0, 65536, 0, LP_OPTIONS16},
|
||||
{"NAND 64GiB 3,3V 16-bit", 0x4E, 0, 65536, 0, LP_OPTIONS16},
|
||||
|
||||
/*
|
||||
* Renesas AND 1 Gigabit. Those chips do not support extended id and
|
||||
* have a strange page/block layout ! The chosen minimum erasesize is
|
||||
* 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page
|
||||
* planes 1 block = 2 pages, but due to plane arrangement the blocks
|
||||
* 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 Anyway JFFS2 would
|
||||
* increase the eraseblock size so we chose a combined one which can be
|
||||
* erased in one go There are more speed improvements for reads and
|
||||
* writes possible, but not implemented now
|
||||
*/
|
||||
{"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000,
|
||||
NAND_IS_AND | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH},
|
||||
|
||||
{NULL,}
|
||||
};
|
||||
#define SP_OPTIONS NAND_NEED_READRDY
|
||||
#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16)
|
||||
|
||||
/*
|
||||
* Manufacturer ID list
|
||||
*/
|
||||
const struct nand_manufacturers nand_manuf_ids[] = {
|
||||
* The chip ID list:
|
||||
* name, device ID, page size, chip size in MiB, eraseblock size, options
|
||||
*
|
||||
* If page size and eraseblock size are 0, the sizes are taken from the
|
||||
* extended chip ID.
|
||||
*/
|
||||
struct nand_flash_dev nand_flash_ids[] = {
|
||||
#ifdef CONFIG_MTD_NAND_MUSEUM_IDS
|
||||
LEGACY_ID_NAND("NAND 1MiB 5V 8-bit", 0x6e, 1, SZ_4K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 2MiB 5V 8-bit", 0x64, 2, SZ_4K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 1MiB 3,3V 8-bit", 0xe8, 1, SZ_4K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 1MiB 3,3V 8-bit", 0xec, 1, SZ_4K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 2MiB 3,3V 8-bit", 0xea, 2, SZ_4K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xd5, 4, SZ_8K, SP_OPTIONS),
|
||||
|
||||
LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xe6, 8, SZ_8K, SP_OPTIONS),
|
||||
#endif
|
||||
/*
|
||||
* Some incompatible NAND chips share device ID's and so must be
|
||||
* listed by full ID. We list them first so that we can easily identify
|
||||
* the most specific match.
|
||||
*/
|
||||
{"TC58NVG2S0F 4G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
|
||||
SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
|
||||
{"TC58NVG3S0F 8G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} },
|
||||
SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) },
|
||||
{"TC58NVG5D2 32G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} },
|
||||
SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
|
||||
{"TC58NVG6D2 64G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} },
|
||||
SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
|
||||
|
||||
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE5, 4, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xD6, 8, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xE6, 8, SZ_8K, SP_OPTIONS),
|
||||
|
||||
LEGACY_ID_NAND("NAND 16MiB 1,8V 8-bit", 0x33, 16, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 16MiB 3,3V 8-bit", 0x73, 16, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 16MiB 1,8V 16-bit", 0x43, 16, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 16MiB 3,3V 16-bit", 0x53, 16, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND("NAND 32MiB 1,8V 8-bit", 0x35, 32, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 32MiB 3,3V 8-bit", 0x75, 32, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 32MiB 1,8V 16-bit", 0x45, 32, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 32MiB 3,3V 16-bit", 0x55, 32, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND("NAND 64MiB 1,8V 8-bit", 0x36, 64, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 64MiB 3,3V 8-bit", 0x76, 64, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 64MiB 1,8V 16-bit", 0x46, 64, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 64MiB 3,3V 16-bit", 0x56, 64, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x78, 128, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x39, 128, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 128MiB 3,3V 8-bit", 0x79, 128, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x72, 128, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x49, 128, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x74, 128, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x59, 128, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND("NAND 256MiB 3,3V 8-bit", 0x71, 256, SZ_16K, SP_OPTIONS),
|
||||
|
||||
/*
|
||||
* These are the new chips with large page size. Their page size and
|
||||
* eraseblock size are determined from the extended ID bytes.
|
||||
*/
|
||||
|
||||
/* 512 Megabit */
|
||||
EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA2, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA0, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF2, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xD0, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF0, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB2, 64, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB0, 64, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC2, 64, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC0, 64, LP_OPTIONS16),
|
||||
|
||||
/* 1 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 128MiB 1,8V 8-bit", 0xA1, 128, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xF1, 128, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xD1, 128, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xB1, 128, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 128MiB 3,3V 16-bit", 0xC1, 128, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xAD, 128, LP_OPTIONS16),
|
||||
|
||||
/* 2 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 256MiB 1,8V 8-bit", 0xAA, 256, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit", 0xDA, 256, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 256MiB 1,8V 16-bit", 0xBA, 256, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 256MiB 3,3V 16-bit", 0xCA, 256, LP_OPTIONS16),
|
||||
|
||||
/* 4 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 512MiB 1,8V 8-bit", 0xAC, 512, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 512MiB 3,3V 8-bit", 0xDC, 512, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 512MiB 1,8V 16-bit", 0xBC, 512, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 512MiB 3,3V 16-bit", 0xCC, 512, LP_OPTIONS16),
|
||||
|
||||
/* 8 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 1GiB 1,8V 8-bit", 0xA3, 1024, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 1GiB 3,3V 8-bit", 0xD3, 1024, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 1GiB 1,8V 16-bit", 0xB3, 1024, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 1GiB 3,3V 16-bit", 0xC3, 1024, LP_OPTIONS16),
|
||||
|
||||
/* 16 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 2GiB 1,8V 8-bit", 0xA5, 2048, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 2GiB 3,3V 8-bit", 0xD5, 2048, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 2GiB 1,8V 16-bit", 0xB5, 2048, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 2GiB 3,3V 16-bit", 0xC5, 2048, LP_OPTIONS16),
|
||||
|
||||
/* 32 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 4GiB 1,8V 8-bit", 0xA7, 4096, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 4GiB 3,3V 8-bit", 0xD7, 4096, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 4GiB 1,8V 16-bit", 0xB7, 4096, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 4GiB 3,3V 16-bit", 0xC7, 4096, LP_OPTIONS16),
|
||||
|
||||
/* 64 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 8GiB 1,8V 8-bit", 0xAE, 8192, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 8GiB 3,3V 8-bit", 0xDE, 8192, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 8GiB 1,8V 16-bit", 0xBE, 8192, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 8GiB 3,3V 16-bit", 0xCE, 8192, LP_OPTIONS16),
|
||||
|
||||
/* 128 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 16GiB 1,8V 8-bit", 0x1A, 16384, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 16GiB 3,3V 8-bit", 0x3A, 16384, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 16GiB 1,8V 16-bit", 0x2A, 16384, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 16GiB 3,3V 16-bit", 0x4A, 16384, LP_OPTIONS16),
|
||||
|
||||
/* 256 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 32GiB 1,8V 8-bit", 0x1C, 32768, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 32GiB 3,3V 8-bit", 0x3C, 32768, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 32GiB 1,8V 16-bit", 0x2C, 32768, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 32GiB 3,3V 16-bit", 0x4C, 32768, LP_OPTIONS16),
|
||||
|
||||
/* 512 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 64GiB 1,8V 8-bit", 0x1E, 65536, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64GiB 3,3V 8-bit", 0x3E, 65536, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64GiB 1,8V 16-bit", 0x2E, 65536, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 64GiB 3,3V 16-bit", 0x4E, 65536, LP_OPTIONS16),
|
||||
|
||||
{NULL}
|
||||
};
|
||||
|
||||
/* Manufacturer IDs */
|
||||
struct nand_manufacturers nand_manuf_ids[] = {
|
||||
{NAND_MFR_TOSHIBA, "Toshiba"},
|
||||
{NAND_MFR_SAMSUNG, "Samsung"},
|
||||
{NAND_MFR_FUJITSU, "Fujitsu"},
|
||||
@ -178,5 +185,14 @@ const struct nand_manufacturers nand_manuf_ids[] = {
|
||||
{NAND_MFR_AMD, "AMD/Spansion"},
|
||||
{NAND_MFR_MACRONIX, "Macronix"},
|
||||
{NAND_MFR_EON, "Eon"},
|
||||
{NAND_MFR_SANDISK, "SanDisk"},
|
||||
{NAND_MFR_INTEL, "Intel"},
|
||||
{0x0, "Unknown"}
|
||||
};
|
||||
|
||||
EXPORT_SYMBOL(nand_manuf_ids);
|
||||
EXPORT_SYMBOL(nand_flash_ids);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
|
||||
MODULE_DESCRIPTION("Nand device & manufacturer IDs");
|
||||
|
@ -187,6 +187,9 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
|
||||
|
||||
#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK
|
||||
|
||||
#define NAND_CMD_LOCK_TIGHT 0x2c
|
||||
#define NAND_CMD_LOCK_STATUS 0x7a
|
||||
|
||||
/******************************************************************************
|
||||
* Support for locking / unlocking operations of some NAND devices
|
||||
*****************************************************************************/
|
||||
|
@ -118,6 +118,7 @@ static void ndfc_write_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int len
|
||||
out_be32((u32 *)(base + NDFC_DATA), *p++);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
static int ndfc_verify_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int len)
|
||||
{
|
||||
struct nand_chip *this = mtdinfo->priv;
|
||||
@ -130,6 +131,7 @@ static int ndfc_verify_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int len
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Read a byte from the NDFC.
|
||||
@ -205,7 +207,9 @@ int board_nand_init(struct nand_chip *nand)
|
||||
#endif
|
||||
|
||||
nand->write_buf = ndfc_write_buf;
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
nand->verify_buf = ndfc_verify_buf;
|
||||
#endif
|
||||
nand->read_byte = ndfc_read_byte;
|
||||
|
||||
chip++;
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <common.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include "linux/mtd/flashchip.h"
|
||||
#include <linux/mtd/onenand.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
@ -140,7 +140,6 @@ static inline int onenand_memory_bbt(struct mtd_info *mtd,
|
||||
{
|
||||
unsigned char data_buf[MAX_ONENAND_PAGESIZE];
|
||||
|
||||
bd->options &= ~NAND_BBT_SCANEMPTY;
|
||||
return create_bbt(mtd, data_buf, bd, -1);
|
||||
}
|
||||
|
||||
|
@ -15,20 +15,12 @@
|
||||
#include <linux/compat.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/onenand.h>
|
||||
#include <linux/mtd/flashchip.h>
|
||||
#include <linux/mtd/samsung_onenand.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/errno.h>
|
||||
|
||||
#ifdef ONENAND_DEBUG
|
||||
#define DPRINTK(format, args...) \
|
||||
do { \
|
||||
printf("%s[%d]: " format "\n", __func__, __LINE__, ##args); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DPRINTK(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#define ONENAND_ERASE_STATUS 0x00
|
||||
#define ONENAND_MULTI_ERASE_SET 0x01
|
||||
#define ONENAND_ERASE_START 0x03
|
||||
|
@ -5,6 +5,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
obj-y += build.o vtbl.o vmt.o upd.o kapi.o eba.o io.o wl.o scan.o crc32.o
|
||||
obj-y += attach.o build.o vtbl.o vmt.o upd.o kapi.o eba.o io.o wl.o crc32.o
|
||||
obj-$(CONFIG_MTD_UBI_FASTMAP) += fastmap.o
|
||||
obj-y += misc.o
|
||||
obj-y += debug.o
|
||||
|
1754
drivers/mtd/ubi/attach.c
Normal file
1754
drivers/mtd/ubi/attach.c
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -20,7 +20,8 @@
|
||||
* Version 2. See the file COPYING for more details.
|
||||
*/
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
@ -30,7 +31,7 @@
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/atomic.h>
|
||||
@ -46,7 +47,7 @@
|
||||
#define tobe(x) (x)
|
||||
#endif
|
||||
#include "crc32table.h"
|
||||
#ifdef UBI_LINUX
|
||||
#ifndef __UBOOT__
|
||||
MODULE_AUTHOR("Matt Domsch <Matt_Domsch@dell.com>");
|
||||
MODULE_DESCRIPTION("Ethernet CRC32 calculations");
|
||||
MODULE_LICENSE("GPL");
|
||||
@ -146,7 +147,7 @@ u32 crc32_le(u32 crc, unsigned char const *p, size_t len)
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
#ifdef UBI_LINUX
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32
|
||||
* @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for
|
||||
@ -379,7 +380,7 @@ EXPORT_SYMBOL(crc32_be);
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef UBI_LINUX /*Not used at present */
|
||||
#ifndef __UBOOT__
|
||||
static void
|
||||
buf_dump(char const *prefix, unsigned char const *buf, size_t len)
|
||||
{
|
||||
@ -405,7 +406,7 @@ static void random_garbage(unsigned char *buf, size_t len)
|
||||
*buf++ = (unsigned char) random();
|
||||
}
|
||||
|
||||
#ifdef UBI_LINUX /* Not used at present */
|
||||
#ifndef __UBOOT__
|
||||
static void store_le(u32 x, unsigned char *buf)
|
||||
{
|
||||
buf[0] = (unsigned char) x;
|
||||
|
@ -66,7 +66,7 @@ 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
|
||||
#ifndef __UBOOT__
|
||||
static const u32 crc32table_be[] = {
|
||||
tobe(0x00000000L), tobe(0x04c11db7L), tobe(0x09823b6eL), tobe(0x0d4326d9L),
|
||||
tobe(0x130476dcL), tobe(0x17c56b6bL), tobe(0x1a864db2L), tobe(0x1e475005L),
|
||||
|
@ -6,175 +6,455 @@
|
||||
* 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"
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/module.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ubi_dbg_dump_ec_hdr - dump an erase counter header.
|
||||
* ubi_dump_flash - dump a region of flash.
|
||||
* @ubi: UBI device description object
|
||||
* @pnum: the physical eraseblock number to dump
|
||||
* @offset: the starting offset within the physical eraseblock to dump
|
||||
* @len: the length of the region to dump
|
||||
*/
|
||||
void ubi_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len)
|
||||
{
|
||||
int err;
|
||||
size_t read;
|
||||
void *buf;
|
||||
loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
|
||||
|
||||
buf = vmalloc(len);
|
||||
if (!buf)
|
||||
return;
|
||||
err = mtd_read(ubi->mtd, addr, len, &read, buf);
|
||||
if (err && err != -EUCLEAN) {
|
||||
ubi_err("error %d while reading %d bytes from PEB %d:%d, read %zd bytes",
|
||||
err, len, pnum, offset, read);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ubi_msg("dumping %d bytes of data from PEB %d, offset %d",
|
||||
len, pnum, offset);
|
||||
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, buf, len, 1);
|
||||
out:
|
||||
vfree(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_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)
|
||||
void ubi_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:");
|
||||
pr_err("Erase counter header dump:\n");
|
||||
pr_err("\tmagic %#08x\n", be32_to_cpu(ec_hdr->magic));
|
||||
pr_err("\tversion %d\n", (int)ec_hdr->version);
|
||||
pr_err("\tec %llu\n", (long long)be64_to_cpu(ec_hdr->ec));
|
||||
pr_err("\tvid_hdr_offset %d\n", be32_to_cpu(ec_hdr->vid_hdr_offset));
|
||||
pr_err("\tdata_offset %d\n", be32_to_cpu(ec_hdr->data_offset));
|
||||
pr_err("\timage_seq %d\n", be32_to_cpu(ec_hdr->image_seq));
|
||||
pr_err("\thdr_crc %#08x\n", be32_to_cpu(ec_hdr->hdr_crc));
|
||||
pr_err("erase counter header hexdump:\n");
|
||||
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.
|
||||
* ubi_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)
|
||||
void ubi_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",
|
||||
pr_err("Volume identifier header dump:\n");
|
||||
pr_err("\tmagic %08x\n", be32_to_cpu(vid_hdr->magic));
|
||||
pr_err("\tversion %d\n", (int)vid_hdr->version);
|
||||
pr_err("\tvol_type %d\n", (int)vid_hdr->vol_type);
|
||||
pr_err("\tcopy_flag %d\n", (int)vid_hdr->copy_flag);
|
||||
pr_err("\tcompat %d\n", (int)vid_hdr->compat);
|
||||
pr_err("\tvol_id %d\n", be32_to_cpu(vid_hdr->vol_id));
|
||||
pr_err("\tlnum %d\n", be32_to_cpu(vid_hdr->lnum));
|
||||
pr_err("\tdata_size %d\n", be32_to_cpu(vid_hdr->data_size));
|
||||
pr_err("\tused_ebs %d\n", be32_to_cpu(vid_hdr->used_ebs));
|
||||
pr_err("\tdata_pad %d\n", be32_to_cpu(vid_hdr->data_pad));
|
||||
pr_err("\tsqnum %llu\n",
|
||||
(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:");
|
||||
pr_err("\thdr_crc %08x\n", be32_to_cpu(vid_hdr->hdr_crc));
|
||||
pr_err("Volume identifier header hexdump:\n");
|
||||
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
|
||||
vid_hdr, UBI_VID_HDR_SIZE, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_dbg_dump_vol_info- dump volume information.
|
||||
* ubi_dump_vol_info - dump volume information.
|
||||
* @vol: UBI volume description object
|
||||
*/
|
||||
void ubi_dbg_dump_vol_info(const struct ubi_volume *vol)
|
||||
void ubi_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);
|
||||
printf("Volume information dump:\n");
|
||||
printf("\tvol_id %d\n", vol->vol_id);
|
||||
printf("\treserved_pebs %d\n", vol->reserved_pebs);
|
||||
printf("\talignment %d\n", vol->alignment);
|
||||
printf("\tdata_pad %d\n", vol->data_pad);
|
||||
printf("\tvol_type %d\n", vol->vol_type);
|
||||
printf("\tname_len %d\n", vol->name_len);
|
||||
printf("\tusable_leb_size %d\n", vol->usable_leb_size);
|
||||
printf("\tused_ebs %d\n", vol->used_ebs);
|
||||
printf("\tused_bytes %lld\n", vol->used_bytes);
|
||||
printf("\tlast_eb_bytes %d\n", vol->last_eb_bytes);
|
||||
printf("\tcorrupted %d\n", vol->corrupted);
|
||||
printf("\tupd_marker %d\n", 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);
|
||||
printf("\tname %s\n", 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]);
|
||||
printf("\t1st 5 characters of name: %c%c%c%c%c\n",
|
||||
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.
|
||||
* ubi_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)
|
||||
void ubi_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);
|
||||
pr_err("Volume table record %d dump:\n", idx);
|
||||
pr_err("\treserved_pebs %d\n", be32_to_cpu(r->reserved_pebs));
|
||||
pr_err("\talignment %d\n", be32_to_cpu(r->alignment));
|
||||
pr_err("\tdata_pad %d\n", be32_to_cpu(r->data_pad));
|
||||
pr_err("\tvol_type %d\n", (int)r->vol_type);
|
||||
pr_err("\tupd_marker %d\n", (int)r->upd_marker);
|
||||
pr_err("\tname_len %d\n", name_len);
|
||||
|
||||
if (r->name[0] == '\0') {
|
||||
dbg_msg("name NULL");
|
||||
pr_err("\tname NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (name_len <= UBI_VOL_NAME_MAX &&
|
||||
strnlen(&r->name[0], name_len + 1) == name_len) {
|
||||
dbg_msg("name %s", &r->name[0]);
|
||||
pr_err("\tname %s\n", &r->name[0]);
|
||||
} else {
|
||||
dbg_msg("1st 5 characters of the name: %c%c%c%c%c",
|
||||
pr_err("\t1st 5 characters of name: %c%c%c%c%c\n",
|
||||
r->name[0], r->name[1], r->name[2], r->name[3],
|
||||
r->name[4]);
|
||||
}
|
||||
dbg_msg("crc %#08x", be32_to_cpu(r->crc));
|
||||
pr_err("\tcrc %#08x\n", be32_to_cpu(r->crc));
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_dbg_dump_sv - dump a &struct ubi_scan_volume object.
|
||||
* @sv: the object to dump
|
||||
* ubi_dump_av - dump a &struct ubi_ainf_volume object.
|
||||
* @av: the object to dump
|
||||
*/
|
||||
void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv)
|
||||
void ubi_dump_av(const struct ubi_ainf_volume *av)
|
||||
{
|
||||
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);
|
||||
pr_err("Volume attaching information dump:\n");
|
||||
pr_err("\tvol_id %d\n", av->vol_id);
|
||||
pr_err("\thighest_lnum %d\n", av->highest_lnum);
|
||||
pr_err("\tleb_count %d\n", av->leb_count);
|
||||
pr_err("\tcompat %d\n", av->compat);
|
||||
pr_err("\tvol_type %d\n", av->vol_type);
|
||||
pr_err("\tused_ebs %d\n", av->used_ebs);
|
||||
pr_err("\tlast_data_size %d\n", av->last_data_size);
|
||||
pr_err("\tdata_pad %d\n", av->data_pad);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_dbg_dump_seb - dump a &struct ubi_scan_leb object.
|
||||
* @seb: the object to dump
|
||||
* ubi_dump_aeb - dump a &struct ubi_ainf_peb object.
|
||||
* @aeb: 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)
|
||||
void ubi_dump_aeb(const struct ubi_ainf_peb *aeb, int type)
|
||||
{
|
||||
dbg_msg("eraseblock scanning information dump:");
|
||||
dbg_msg("ec %d", seb->ec);
|
||||
dbg_msg("pnum %d", seb->pnum);
|
||||
pr_err("eraseblock attaching information dump:\n");
|
||||
pr_err("\tec %d\n", aeb->ec);
|
||||
pr_err("\tpnum %d\n", aeb->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);
|
||||
pr_err("\tlnum %d\n", aeb->lnum);
|
||||
pr_err("\tscrub %d\n", aeb->scrub);
|
||||
pr_err("\tsqnum %llu\n", aeb->sqnum);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_dbg_dump_mkvol_req - dump a &struct ubi_mkvol_req object.
|
||||
* ubi_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)
|
||||
void ubi_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);
|
||||
pr_err("Volume creation request dump:\n");
|
||||
pr_err("\tvol_id %d\n", req->vol_id);
|
||||
pr_err("\talignment %d\n", req->alignment);
|
||||
pr_err("\tbytes %lld\n", (long long)req->bytes);
|
||||
pr_err("\tvol_type %d\n", req->vol_type);
|
||||
pr_err("\tname_len %d\n", req->name_len);
|
||||
|
||||
memcpy(nm, req->name, 16);
|
||||
nm[16] = 0;
|
||||
dbg_msg("the 1st 16 characters of the name: %s", nm);
|
||||
pr_err("\t1st 16 characters of name: %s\n", nm);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MTD_UBI_DEBUG_MSG */
|
||||
#ifndef __UBOOT__
|
||||
/*
|
||||
* Root directory for UBI stuff in debugfs. Contains sub-directories which
|
||||
* contain the stuff specific to particular UBI devices.
|
||||
*/
|
||||
static struct dentry *dfs_rootdir;
|
||||
|
||||
/**
|
||||
* ubi_debugfs_init - create UBI debugfs directory.
|
||||
*
|
||||
* Create UBI debugfs directory. Returns zero in case of success and a negative
|
||||
* error code in case of failure.
|
||||
*/
|
||||
int ubi_debugfs_init(void)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_DEBUG_FS))
|
||||
return 0;
|
||||
|
||||
dfs_rootdir = debugfs_create_dir("ubi", NULL);
|
||||
if (IS_ERR_OR_NULL(dfs_rootdir)) {
|
||||
int err = dfs_rootdir ? -ENODEV : PTR_ERR(dfs_rootdir);
|
||||
|
||||
ubi_err("cannot create \"ubi\" debugfs directory, error %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_debugfs_exit - remove UBI debugfs directory.
|
||||
*/
|
||||
void ubi_debugfs_exit(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_DEBUG_FS))
|
||||
debugfs_remove(dfs_rootdir);
|
||||
}
|
||||
|
||||
/* Read an UBI debugfs file */
|
||||
static ssize_t dfs_file_read(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
unsigned long ubi_num = (unsigned long)file->private_data;
|
||||
struct dentry *dent = file->f_path.dentry;
|
||||
struct ubi_device *ubi;
|
||||
struct ubi_debug_info *d;
|
||||
char buf[3];
|
||||
int val;
|
||||
|
||||
ubi = ubi_get_device(ubi_num);
|
||||
if (!ubi)
|
||||
return -ENODEV;
|
||||
d = &ubi->dbg;
|
||||
|
||||
if (dent == d->dfs_chk_gen)
|
||||
val = d->chk_gen;
|
||||
else if (dent == d->dfs_chk_io)
|
||||
val = d->chk_io;
|
||||
else if (dent == d->dfs_disable_bgt)
|
||||
val = d->disable_bgt;
|
||||
else if (dent == d->dfs_emulate_bitflips)
|
||||
val = d->emulate_bitflips;
|
||||
else if (dent == d->dfs_emulate_io_failures)
|
||||
val = d->emulate_io_failures;
|
||||
else {
|
||||
count = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (val)
|
||||
buf[0] = '1';
|
||||
else
|
||||
buf[0] = '0';
|
||||
buf[1] = '\n';
|
||||
buf[2] = 0x00;
|
||||
|
||||
count = simple_read_from_buffer(user_buf, count, ppos, buf, 2);
|
||||
|
||||
out:
|
||||
ubi_put_device(ubi);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Write an UBI debugfs file */
|
||||
static ssize_t dfs_file_write(struct file *file, const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
unsigned long ubi_num = (unsigned long)file->private_data;
|
||||
struct dentry *dent = file->f_path.dentry;
|
||||
struct ubi_device *ubi;
|
||||
struct ubi_debug_info *d;
|
||||
size_t buf_size;
|
||||
char buf[8];
|
||||
int val;
|
||||
|
||||
ubi = ubi_get_device(ubi_num);
|
||||
if (!ubi)
|
||||
return -ENODEV;
|
||||
d = &ubi->dbg;
|
||||
|
||||
buf_size = min_t(size_t, count, (sizeof(buf) - 1));
|
||||
if (copy_from_user(buf, user_buf, buf_size)) {
|
||||
count = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (buf[0] == '1')
|
||||
val = 1;
|
||||
else if (buf[0] == '0')
|
||||
val = 0;
|
||||
else {
|
||||
count = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dent == d->dfs_chk_gen)
|
||||
d->chk_gen = val;
|
||||
else if (dent == d->dfs_chk_io)
|
||||
d->chk_io = val;
|
||||
else if (dent == d->dfs_disable_bgt)
|
||||
d->disable_bgt = val;
|
||||
else if (dent == d->dfs_emulate_bitflips)
|
||||
d->emulate_bitflips = val;
|
||||
else if (dent == d->dfs_emulate_io_failures)
|
||||
d->emulate_io_failures = val;
|
||||
else
|
||||
count = -EINVAL;
|
||||
|
||||
out:
|
||||
ubi_put_device(ubi);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* File operations for all UBI debugfs files */
|
||||
static const struct file_operations dfs_fops = {
|
||||
.read = dfs_file_read,
|
||||
.write = dfs_file_write,
|
||||
.open = simple_open,
|
||||
.llseek = no_llseek,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/**
|
||||
* ubi_debugfs_init_dev - initialize debugfs for an UBI device.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* This function creates all debugfs files for UBI device @ubi. Returns zero in
|
||||
* case of success and a negative error code in case of failure.
|
||||
*/
|
||||
int ubi_debugfs_init_dev(struct ubi_device *ubi)
|
||||
{
|
||||
int err, n;
|
||||
unsigned long ubi_num = ubi->ubi_num;
|
||||
const char *fname;
|
||||
struct dentry *dent;
|
||||
struct ubi_debug_info *d = &ubi->dbg;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_DEBUG_FS))
|
||||
return 0;
|
||||
|
||||
n = snprintf(d->dfs_dir_name, UBI_DFS_DIR_LEN + 1, UBI_DFS_DIR_NAME,
|
||||
ubi->ubi_num);
|
||||
if (n == UBI_DFS_DIR_LEN) {
|
||||
/* The array size is too small */
|
||||
fname = UBI_DFS_DIR_NAME;
|
||||
dent = ERR_PTR(-EINVAL);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fname = d->dfs_dir_name;
|
||||
dent = debugfs_create_dir(fname, dfs_rootdir);
|
||||
if (IS_ERR_OR_NULL(dent))
|
||||
goto out;
|
||||
d->dfs_dir = dent;
|
||||
|
||||
fname = "chk_gen";
|
||||
dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
|
||||
&dfs_fops);
|
||||
if (IS_ERR_OR_NULL(dent))
|
||||
goto out_remove;
|
||||
d->dfs_chk_gen = dent;
|
||||
|
||||
fname = "chk_io";
|
||||
dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
|
||||
&dfs_fops);
|
||||
if (IS_ERR_OR_NULL(dent))
|
||||
goto out_remove;
|
||||
d->dfs_chk_io = dent;
|
||||
|
||||
fname = "tst_disable_bgt";
|
||||
dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
|
||||
&dfs_fops);
|
||||
if (IS_ERR_OR_NULL(dent))
|
||||
goto out_remove;
|
||||
d->dfs_disable_bgt = dent;
|
||||
|
||||
fname = "tst_emulate_bitflips";
|
||||
dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
|
||||
&dfs_fops);
|
||||
if (IS_ERR_OR_NULL(dent))
|
||||
goto out_remove;
|
||||
d->dfs_emulate_bitflips = dent;
|
||||
|
||||
fname = "tst_emulate_io_failures";
|
||||
dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
|
||||
&dfs_fops);
|
||||
if (IS_ERR_OR_NULL(dent))
|
||||
goto out_remove;
|
||||
d->dfs_emulate_io_failures = dent;
|
||||
|
||||
return 0;
|
||||
|
||||
out_remove:
|
||||
debugfs_remove_recursive(d->dfs_dir);
|
||||
out:
|
||||
err = dent ? PTR_ERR(dent) : -ENODEV;
|
||||
ubi_err("cannot create \"%s\" debugfs file or directory, error %d\n",
|
||||
fname, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_debug_exit_dev - free all debugfs files corresponding to device @ubi
|
||||
* @ubi: UBI device description object
|
||||
*/
|
||||
void ubi_debugfs_exit_dev(struct ubi_device *ubi)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_DEBUG_FS))
|
||||
debugfs_remove_recursive(ubi->dbg.dfs_dir);
|
||||
}
|
||||
#else
|
||||
int ubi_debugfs_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ubi_debugfs_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
int ubi_debugfs_init_dev(struct ubi_device *ubi)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ubi_debugfs_exit_dev(struct ubi_device *ubi)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
@ -9,132 +9,113 @@
|
||||
#ifndef __UBI_DEBUG_H__
|
||||
#define __UBI_DEBUG_H__
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG
|
||||
#ifdef UBI_LINUX
|
||||
void ubi_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len);
|
||||
void ubi_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr);
|
||||
void ubi_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr);
|
||||
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#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
|
||||
#define ubi_assert(expr) do { \
|
||||
if (unlikely(!(expr))) { \
|
||||
pr_crit("UBI assert failed in %s at %u (pid %d)\n", \
|
||||
__func__, __LINE__, current->pid); \
|
||||
dump_stack(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT
|
||||
#define DBG_DISABLE_BGT 1
|
||||
#else
|
||||
#define DBG_DISABLE_BGT 0
|
||||
#endif
|
||||
#define ubi_dbg_print_hex_dump(l, ps, pt, r, g, b, len, a) \
|
||||
print_hex_dump(l, ps, pt, r, g, b, len, a)
|
||||
|
||||
#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_msg(type, fmt, ...) \
|
||||
pr_debug("UBI DBG " type " (pid %d): " fmt "\n", current->pid, \
|
||||
##__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
|
||||
/* General debugging messages */
|
||||
#define dbg_gen(fmt, ...) ubi_dbg_msg("gen", fmt, ##__VA_ARGS__)
|
||||
/* Messages from the eraseblock association sub-system */
|
||||
#define dbg_eba(fmt, ...) ubi_dbg_msg("eba", fmt, ##__VA_ARGS__)
|
||||
/* Messages from the wear-leveling sub-system */
|
||||
#define dbg_wl(fmt, ...) ubi_dbg_msg("wl", fmt, ##__VA_ARGS__)
|
||||
/* Messages from the input/output sub-system */
|
||||
#define dbg_io(fmt, ...) ubi_dbg_msg("io", fmt, ##__VA_ARGS__)
|
||||
/* Initialization and build messages */
|
||||
#define dbg_bld(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_bld(fmt, ...) ({})
|
||||
#endif
|
||||
#define dbg_bld(fmt, ...) ubi_dbg_msg("bld", fmt, ##__VA_ARGS__)
|
||||
|
||||
void ubi_dump_vol_info(const struct ubi_volume *vol);
|
||||
void ubi_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx);
|
||||
void ubi_dump_av(const struct ubi_ainf_volume *av);
|
||||
void ubi_dump_aeb(const struct ubi_ainf_peb *aeb, int type);
|
||||
void ubi_dump_mkvol_req(const struct ubi_mkvol_req *req);
|
||||
int ubi_self_check_all_ff(struct ubi_device *ubi, int pnum, int offset,
|
||||
int len);
|
||||
int ubi_debugfs_init(void);
|
||||
void ubi_debugfs_exit(void);
|
||||
int ubi_debugfs_init_dev(struct ubi_device *ubi);
|
||||
void ubi_debugfs_exit_dev(struct ubi_device *ubi);
|
||||
|
||||
/**
|
||||
* ubi_dbg_is_bgt_disabled - if the background thread is disabled.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* Returns non-zero if the UBI background thread is disabled for testing
|
||||
* purposes.
|
||||
*/
|
||||
static inline int ubi_dbg_is_bgt_disabled(const struct ubi_device *ubi)
|
||||
{
|
||||
return ubi->dbg.disable_bgt;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS
|
||||
/**
|
||||
* ubi_dbg_is_bitflip - if it is time to emulate a bit-flip.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* Returns non-zero if a bit-flip should be emulated, otherwise returns zero.
|
||||
*/
|
||||
static inline int ubi_dbg_is_bitflip(void)
|
||||
static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi)
|
||||
{
|
||||
return !(random32() % 200);
|
||||
if (ubi->dbg.emulate_bitflips)
|
||||
return !(prandom_u32() % 200);
|
||||
return 0;
|
||||
}
|
||||
#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.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* Returns non-zero if a write failure should be emulated, otherwise returns
|
||||
* zero.
|
||||
*/
|
||||
static inline int ubi_dbg_is_write_failure(void)
|
||||
static inline int ubi_dbg_is_write_failure(const struct ubi_device *ubi)
|
||||
{
|
||||
return !(random32() % 500);
|
||||
if (ubi->dbg.emulate_io_failures)
|
||||
return !(prandom_u32() % 500);
|
||||
return 0;
|
||||
}
|
||||
#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.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* Returns non-zero if an erase failure should be emulated, otherwise returns
|
||||
* zero.
|
||||
*/
|
||||
static inline int ubi_dbg_is_erase_failure(void)
|
||||
static inline int ubi_dbg_is_erase_failure(const struct ubi_device *ubi)
|
||||
{
|
||||
return !(random32() % 400);
|
||||
if (ubi->dbg.emulate_io_failures)
|
||||
return !(prandom_u32() % 400);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ubi_dbg_is_erase_failure() 0
|
||||
#endif
|
||||
|
||||
static inline int ubi_dbg_chk_io(const struct ubi_device *ubi)
|
||||
{
|
||||
return ubi->dbg.chk_io;
|
||||
}
|
||||
|
||||
static inline int ubi_dbg_chk_gen(const struct ubi_device *ubi)
|
||||
{
|
||||
return ubi->dbg.chk_gen;
|
||||
}
|
||||
#endif /* !__UBI_DEBUG_H__ */
|
||||
|
@ -7,20 +7,20 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* The UBI Eraseblock Association (EBA) unit.
|
||||
* The UBI Eraseblock Association (EBA) sub-system.
|
||||
*
|
||||
* This unit is responsible for I/O to/from logical eraseblock.
|
||||
* This sub-system 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.
|
||||
* The EBA sub-system 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
|
||||
@ -29,13 +29,15 @@
|
||||
* 64 bits is enough to never overflow.
|
||||
*/
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/slab.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/err.h>
|
||||
#else
|
||||
#include <ubi_uboot.h>
|
||||
#endif
|
||||
|
||||
#include <ubi_uboot.h>
|
||||
#include <linux/err.h>
|
||||
#include "ubi.h"
|
||||
|
||||
/* Number of physical eraseblocks reserved for atomic LEB change operation */
|
||||
@ -49,7 +51,7 @@
|
||||
* global sequence counter value. It also increases the global sequence
|
||||
* counter.
|
||||
*/
|
||||
static unsigned long long next_sqnum(struct ubi_device *ubi)
|
||||
unsigned long long ubi_next_sqnum(struct ubi_device *ubi)
|
||||
{
|
||||
unsigned long long sqnum;
|
||||
|
||||
@ -181,9 +183,7 @@ static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi,
|
||||
le->users += 1;
|
||||
spin_unlock(&ubi->ltree_lock);
|
||||
|
||||
if (le_free)
|
||||
kfree(le_free);
|
||||
|
||||
kfree(le_free);
|
||||
return le;
|
||||
}
|
||||
|
||||
@ -215,22 +215,18 @@ static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum)
|
||||
*/
|
||||
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);
|
||||
up_read(&le->mutex);
|
||||
if (le->users == 0) {
|
||||
rb_erase(&le->rb, &ubi->ltree);
|
||||
_free = 1;
|
||||
kfree(le);
|
||||
}
|
||||
spin_unlock(&ubi->ltree_lock);
|
||||
|
||||
up_read(&le->mutex);
|
||||
if (_free)
|
||||
kfree(le);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -266,7 +262,6 @@ static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum)
|
||||
*/
|
||||
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);
|
||||
@ -281,12 +276,9 @@ static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum)
|
||||
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);
|
||||
}
|
||||
spin_unlock(&ubi->ltree_lock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -299,23 +291,18 @@ static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum)
|
||||
*/
|
||||
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);
|
||||
up_write(&le->mutex);
|
||||
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);
|
||||
}
|
||||
spin_unlock(&ubi->ltree_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -347,8 +334,10 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
|
||||
dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum);
|
||||
|
||||
down_read(&ubi->fm_sem);
|
||||
vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED;
|
||||
err = ubi_wl_put_peb(ubi, pnum, 0);
|
||||
up_read(&ubi->fm_sem);
|
||||
err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 0);
|
||||
|
||||
out_unlock:
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
@ -425,9 +414,10 @@ retry:
|
||||
* 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);
|
||||
if (err == UBI_IO_BAD_HDR_EBADMSG ||
|
||||
err == UBI_IO_BAD_HDR) {
|
||||
ubi_warn("corrupted VID header at PEB %d, LEB %d:%d",
|
||||
pnum, vol_id, lnum);
|
||||
err = -EBADMSG;
|
||||
} else
|
||||
ubi_ro_mode(ubi);
|
||||
@ -508,16 +498,12 @@ static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum,
|
||||
struct ubi_vid_hdr *vid_hdr;
|
||||
|
||||
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
|
||||
if (!vid_hdr) {
|
||||
if (!vid_hdr)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_lock(&ubi->buf_mutex);
|
||||
|
||||
retry:
|
||||
new_pnum = ubi_wl_get_peb(ubi, UBI_UNKNOWN);
|
||||
new_pnum = ubi_wl_get_peb(ubi);
|
||||
if (new_pnum < 0) {
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
return new_pnum;
|
||||
}
|
||||
@ -531,39 +517,45 @@ retry:
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_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);
|
||||
mutex_lock(&ubi->buf_mutex);
|
||||
memset(ubi->peb_buf + 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);
|
||||
err = ubi_io_read_data(ubi, ubi->peb_buf, pnum, 0, offset);
|
||||
if (err && err != UBI_IO_BITFLIPS)
|
||||
goto out_put;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
memcpy(ubi->peb_buf1 + offset, buf, len);
|
||||
memcpy(ubi->peb_buf + offset, buf, len);
|
||||
|
||||
err = ubi_io_write_data(ubi, ubi->peb_buf1, new_pnum, 0, data_size);
|
||||
if (err)
|
||||
err = ubi_io_write_data(ubi, ubi->peb_buf, new_pnum, 0, data_size);
|
||||
if (err) {
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
goto write_error;
|
||||
}
|
||||
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
|
||||
down_read(&ubi->fm_sem);
|
||||
vol->eba_tbl[lnum] = new_pnum;
|
||||
ubi_wl_put_peb(ubi, pnum, 1);
|
||||
up_read(&ubi->fm_sem);
|
||||
ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1);
|
||||
|
||||
ubi_msg("data was successfully recovered");
|
||||
return 0;
|
||||
|
||||
out_put:
|
||||
out_unlock:
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
ubi_wl_put_peb(ubi, new_pnum, 1);
|
||||
out_put:
|
||||
ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
return err;
|
||||
|
||||
@ -573,9 +565,8 @@ write_error:
|
||||
* get another one.
|
||||
*/
|
||||
ubi_warn("failed to write to PEB %d", new_pnum);
|
||||
ubi_wl_put_peb(ubi, new_pnum, 1);
|
||||
ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1);
|
||||
if (++tries > UBI_IO_RETRIES) {
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
return err;
|
||||
}
|
||||
@ -591,7 +582,6 @@ write_error:
|
||||
* @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
|
||||
@ -599,7 +589,7 @@ write_error:
|
||||
* 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)
|
||||
const void *buf, int offset, int len)
|
||||
{
|
||||
int err, pnum, tries = 0, vol_id = vol->vol_id;
|
||||
struct ubi_vid_hdr *vid_hdr;
|
||||
@ -640,14 +630,14 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
|
||||
}
|
||||
|
||||
vid_hdr->vol_type = UBI_VID_DYNAMIC;
|
||||
vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_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);
|
||||
pnum = ubi_wl_get_peb(ubi);
|
||||
if (pnum < 0) {
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
@ -667,14 +657,15 @@ retry:
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
down_read(&ubi->fm_sem);
|
||||
vol->eba_tbl[lnum] = pnum;
|
||||
up_read(&ubi->fm_sem);
|
||||
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
@ -693,7 +684,7 @@ write_error:
|
||||
* 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);
|
||||
err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1);
|
||||
if (err || ++tries > UBI_IO_RETRIES) {
|
||||
ubi_ro_mode(ubi);
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
@ -701,7 +692,7 @@ write_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
||||
ubi_msg("try another PEB");
|
||||
goto retry;
|
||||
}
|
||||
@ -713,7 +704,6 @@ write_error:
|
||||
* @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
|
||||
@ -725,13 +715,12 @@ write_error:
|
||||
* 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
|
||||
* It is prohibited to write more than 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 lnum, const void *buf, int len, int used_ebs)
|
||||
{
|
||||
int err, pnum, tries = 0, data_size = len, vol_id = vol->vol_id;
|
||||
struct ubi_vid_hdr *vid_hdr;
|
||||
@ -756,7 +745,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
return err;
|
||||
}
|
||||
|
||||
vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_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);
|
||||
@ -769,7 +758,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
vid_hdr->data_crc = cpu_to_be32(crc);
|
||||
|
||||
retry:
|
||||
pnum = ubi_wl_get_peb(ubi, dtype);
|
||||
pnum = ubi_wl_get_peb(ubi);
|
||||
if (pnum < 0) {
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
@ -794,7 +783,9 @@ retry:
|
||||
}
|
||||
|
||||
ubi_assert(vol->eba_tbl[lnum] < 0);
|
||||
down_read(&ubi->fm_sem);
|
||||
vol->eba_tbl[lnum] = pnum;
|
||||
up_read(&ubi->fm_sem);
|
||||
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
@ -813,7 +804,7 @@ write_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ubi_wl_put_peb(ubi, pnum, 1);
|
||||
err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1);
|
||||
if (err || ++tries > UBI_IO_RETRIES) {
|
||||
ubi_ro_mode(ubi);
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
@ -821,7 +812,7 @@ write_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
||||
ubi_msg("try another PEB");
|
||||
goto retry;
|
||||
}
|
||||
@ -833,7 +824,6 @@ write_error:
|
||||
* @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
|
||||
@ -845,7 +835,7 @@ write_error:
|
||||
* 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 lnum, const void *buf, int len)
|
||||
{
|
||||
int err, pnum, tries = 0, vol_id = vol->vol_id;
|
||||
struct ubi_vid_hdr *vid_hdr;
|
||||
@ -862,7 +852,7 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
err = ubi_eba_unmap_leb(ubi, vol, lnum);
|
||||
if (err)
|
||||
return err;
|
||||
return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype);
|
||||
return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0);
|
||||
}
|
||||
|
||||
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
|
||||
@ -874,7 +864,7 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
if (err)
|
||||
goto out_mutex;
|
||||
|
||||
vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_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);
|
||||
@ -887,7 +877,7 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
vid_hdr->data_crc = cpu_to_be32(crc);
|
||||
|
||||
retry:
|
||||
pnum = ubi_wl_get_peb(ubi, dtype);
|
||||
pnum = ubi_wl_get_peb(ubi);
|
||||
if (pnum < 0) {
|
||||
err = pnum;
|
||||
goto out_leb_unlock;
|
||||
@ -911,12 +901,14 @@ retry:
|
||||
}
|
||||
|
||||
if (vol->eba_tbl[lnum] >= 0) {
|
||||
err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 1);
|
||||
err = ubi_wl_put_peb(ubi, vol_id, lnum, vol->eba_tbl[lnum], 0);
|
||||
if (err)
|
||||
goto out_leb_unlock;
|
||||
}
|
||||
|
||||
down_read(&ubi->fm_sem);
|
||||
vol->eba_tbl[lnum] = pnum;
|
||||
up_read(&ubi->fm_sem);
|
||||
|
||||
out_leb_unlock:
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
@ -936,17 +928,44 @@ write_error:
|
||||
goto out_leb_unlock;
|
||||
}
|
||||
|
||||
err = ubi_wl_put_peb(ubi, pnum, 1);
|
||||
err = ubi_wl_put_peb(ubi, vol_id, lnum, 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));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
||||
ubi_msg("try another PEB");
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/**
|
||||
* is_error_sane - check whether a read error is sane.
|
||||
* @err: code of the error happened during reading
|
||||
*
|
||||
* This is a helper function for 'ubi_eba_copy_leb()' which is called when we
|
||||
* cannot read data from the target PEB (an error @err happened). If the error
|
||||
* code is sane, then we treat this error as non-fatal. Otherwise the error is
|
||||
* fatal and UBI will be switched to R/O mode later.
|
||||
*
|
||||
* The idea is that we try not to switch to R/O mode if the read error is
|
||||
* something which suggests there was a real read problem. E.g., %-EIO. Or a
|
||||
* memory allocation failed (-%ENOMEM). Otherwise, it is safer to switch to R/O
|
||||
* mode, simply because we do not know what happened at the MTD level, and we
|
||||
* cannot handle this. E.g., the underlying driver may have become crazy, and
|
||||
* it is safer to switch to R/O mode to preserve the data.
|
||||
*
|
||||
* And bear in mind, this is about reading from the target PEB, i.e. the PEB
|
||||
* which we have just written.
|
||||
*/
|
||||
static int is_error_sane(int err)
|
||||
{
|
||||
if (err == -EIO || err == -ENOMEM || err == UBI_IO_BAD_HDR ||
|
||||
err == UBI_IO_BAD_HDR_EBADMSG || err == -ETIMEDOUT)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_eba_copy_leb - copy logical eraseblock.
|
||||
* @ubi: UBI device description object
|
||||
@ -957,10 +976,9 @@ write_error:
|
||||
* 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.
|
||||
* o %0 in case of success;
|
||||
* o %MOVE_CANCEL_RACE, %MOVE_TARGET_WR_ERR, %MOVE_TARGET_BITFLIPS, etc;
|
||||
* o a negative error code in case of failure.
|
||||
*/
|
||||
int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
||||
struct ubi_vid_hdr *vid_hdr)
|
||||
@ -972,7 +990,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
||||
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);
|
||||
dbg_wl("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);
|
||||
@ -986,17 +1004,16 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
||||
/*
|
||||
* 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
|
||||
* volume deletion un-maps 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];
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
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;
|
||||
dbg_wl("volume %d is being removed, cancel", vol_id);
|
||||
return MOVE_CANCEL_RACE;
|
||||
}
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
|
||||
/*
|
||||
* We do not want anybody to write to this logical eraseblock while we
|
||||
@ -1008,12 +1025,15 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
||||
* (@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.
|
||||
* LEB is already locked, we just do not move it and return
|
||||
* %MOVE_RETRY. Note, we do not return %MOVE_CANCEL_RACE here because
|
||||
* we do not know the reasons of the contention - it may be just a
|
||||
* normal I/O on this LEB, so we want to re-try.
|
||||
*/
|
||||
err = leb_write_trylock(ubi, vol_id, lnum);
|
||||
if (err) {
|
||||
dbg_eba("contention on LEB %d:%d, cancel", vol_id, lnum);
|
||||
return err;
|
||||
dbg_wl("contention on LEB %d:%d, cancel", vol_id, lnum);
|
||||
return MOVE_RETRY;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1022,30 +1042,30 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
||||
* 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;
|
||||
dbg_wl("LEB %d:%d is no longer mapped to PEB %d, mapped to PEB %d, cancel",
|
||||
vol_id, lnum, from, vol->eba_tbl[lnum]);
|
||||
err = MOVE_CANCEL_RACE;
|
||||
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
|
||||
* OK, now the LEB is locked and we can safely start moving it. Since
|
||||
* this function utilizes the @ubi->peb_buf buffer which is shared
|
||||
* with some other functions - we 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);
|
||||
dbg_wl("read %d bytes of data", aldata_size);
|
||||
err = ubi_io_read_data(ubi, ubi->peb_buf, from, 0, aldata_size);
|
||||
if (err && err != UBI_IO_BITFLIPS) {
|
||||
ubi_warn("error %d while reading data from PEB %d",
|
||||
err, from);
|
||||
err = MOVE_SOURCE_RD_ERR;
|
||||
goto out_unlock_buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we have got to calculate how much data we have to to copy. In
|
||||
* Now we have got to calculate how much data we have 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
|
||||
@ -1056,14 +1076,14 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
||||
*/
|
||||
if (vid_hdr->vol_type == UBI_VID_DYNAMIC)
|
||||
aldata_size = data_size =
|
||||
ubi_calc_data_len(ubi, ubi->peb_buf1, data_size);
|
||||
ubi_calc_data_len(ubi, ubi->peb_buf, data_size);
|
||||
|
||||
cond_resched();
|
||||
crc = crc32(UBI_CRC32_INIT, ubi->peb_buf1, data_size);
|
||||
crc = crc32(UBI_CRC32_INIT, ubi->peb_buf, data_size);
|
||||
cond_resched();
|
||||
|
||||
/*
|
||||
* It may turn out to me that the whole @from physical eraseblock
|
||||
* It may turn out to be 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.
|
||||
@ -1073,28 +1093,37 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
||||
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));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
||||
|
||||
err = ubi_io_write_vid_hdr(ubi, to, vid_hdr);
|
||||
if (err)
|
||||
if (err) {
|
||||
if (err == -EIO)
|
||||
err = MOVE_TARGET_WR_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;
|
||||
if (err != UBI_IO_BITFLIPS) {
|
||||
ubi_warn("error %d while reading VID header back from PEB %d",
|
||||
err, to);
|
||||
if (is_error_sane(err))
|
||||
err = MOVE_TARGET_RD_ERR;
|
||||
} else
|
||||
err = MOVE_TARGET_BITFLIPS;
|
||||
goto out_unlock_buf;
|
||||
}
|
||||
|
||||
if (data_size > 0) {
|
||||
err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size);
|
||||
if (err)
|
||||
err = ubi_io_write_data(ubi, ubi->peb_buf, to, 0, aldata_size);
|
||||
if (err) {
|
||||
if (err == -EIO)
|
||||
err = MOVE_TARGET_WR_ERR;
|
||||
goto out_unlock_buf;
|
||||
}
|
||||
|
||||
cond_resched();
|
||||
|
||||
@ -1102,28 +1131,33 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
||||
* 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);
|
||||
memset(ubi->peb_buf, 0xFF, aldata_size);
|
||||
err = ubi_io_read_data(ubi, ubi->peb_buf, to, 0, aldata_size);
|
||||
if (err) {
|
||||
if (err != UBI_IO_BITFLIPS)
|
||||
ubi_warn("cannot read data back from PEB %d",
|
||||
to);
|
||||
else
|
||||
err = 1;
|
||||
if (err != UBI_IO_BITFLIPS) {
|
||||
ubi_warn("error %d while reading data back from PEB %d",
|
||||
err, to);
|
||||
if (is_error_sane(err))
|
||||
err = MOVE_TARGET_RD_ERR;
|
||||
} else
|
||||
err = MOVE_TARGET_BITFLIPS;
|
||||
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",
|
||||
if (crc != crc32(UBI_CRC32_INIT, ubi->peb_buf, data_size)) {
|
||||
ubi_warn("read data back from PEB %d and it is different",
|
||||
to);
|
||||
err = -EINVAL;
|
||||
goto out_unlock_buf;
|
||||
}
|
||||
}
|
||||
|
||||
ubi_assert(vol->eba_tbl[lnum] == from);
|
||||
down_read(&ubi->fm_sem);
|
||||
vol->eba_tbl[lnum] = to;
|
||||
up_read(&ubi->fm_sem);
|
||||
|
||||
out_unlock_buf:
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
@ -1133,28 +1167,165 @@ out_unlock_leb:
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_eba_init_scan - initialize the EBA unit using scanning information.
|
||||
* print_rsvd_warning - warn about not having enough reserved PEBs.
|
||||
* @ubi: UBI device description object
|
||||
* @si: scanning information
|
||||
*
|
||||
* This is a helper function for 'ubi_eba_init()' which is called when UBI
|
||||
* cannot reserve enough PEBs for bad block handling. This function makes a
|
||||
* decision whether we have to print a warning or not. The algorithm is as
|
||||
* follows:
|
||||
* o if this is a new UBI image, then just print the warning
|
||||
* o if this is an UBI image which has already been used for some time, print
|
||||
* a warning only if we can reserve less than 10% of the expected amount of
|
||||
* the reserved PEB.
|
||||
*
|
||||
* The idea is that when UBI is used, PEBs become bad, and the reserved pool
|
||||
* of PEBs becomes smaller, which is normal and we do not want to scare users
|
||||
* with a warning every time they attach the MTD device. This was an issue
|
||||
* reported by real users.
|
||||
*/
|
||||
static void print_rsvd_warning(struct ubi_device *ubi,
|
||||
struct ubi_attach_info *ai)
|
||||
{
|
||||
/*
|
||||
* The 1 << 18 (256KiB) number is picked randomly, just a reasonably
|
||||
* large number to distinguish between newly flashed and used images.
|
||||
*/
|
||||
if (ai->max_sqnum > (1 << 18)) {
|
||||
int min = ubi->beb_rsvd_level / 10;
|
||||
|
||||
if (!min)
|
||||
min = 1;
|
||||
if (ubi->beb_rsvd_pebs > min)
|
||||
return;
|
||||
}
|
||||
|
||||
ubi_warn("cannot reserve enough PEBs for bad PEB handling, reserved %d, need %d",
|
||||
ubi->beb_rsvd_pebs, ubi->beb_rsvd_level);
|
||||
if (ubi->corr_peb_count)
|
||||
ubi_warn("%d PEBs are corrupted and not used",
|
||||
ubi->corr_peb_count);
|
||||
}
|
||||
|
||||
/**
|
||||
* self_check_eba - run a self check on the EBA table constructed by fastmap.
|
||||
* @ubi: UBI device description object
|
||||
* @ai_fastmap: UBI attach info object created by fastmap
|
||||
* @ai_scan: UBI attach info object created by scanning
|
||||
*
|
||||
* Returns < 0 in case of an internal error, 0 otherwise.
|
||||
* If a bad EBA table entry was found it will be printed out and
|
||||
* ubi_assert() triggers.
|
||||
*/
|
||||
int self_check_eba(struct ubi_device *ubi, struct ubi_attach_info *ai_fastmap,
|
||||
struct ubi_attach_info *ai_scan)
|
||||
{
|
||||
int i, j, num_volumes, ret = 0;
|
||||
int **scan_eba, **fm_eba;
|
||||
struct ubi_ainf_volume *av;
|
||||
struct ubi_volume *vol;
|
||||
struct ubi_ainf_peb *aeb;
|
||||
struct rb_node *rb;
|
||||
|
||||
num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT;
|
||||
|
||||
scan_eba = kmalloc(sizeof(*scan_eba) * num_volumes, GFP_KERNEL);
|
||||
if (!scan_eba)
|
||||
return -ENOMEM;
|
||||
|
||||
fm_eba = kmalloc(sizeof(*fm_eba) * num_volumes, GFP_KERNEL);
|
||||
if (!fm_eba) {
|
||||
kfree(scan_eba);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_volumes; i++) {
|
||||
vol = ubi->volumes[i];
|
||||
if (!vol)
|
||||
continue;
|
||||
|
||||
scan_eba[i] = kmalloc(vol->reserved_pebs * sizeof(**scan_eba),
|
||||
GFP_KERNEL);
|
||||
if (!scan_eba[i]) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
fm_eba[i] = kmalloc(vol->reserved_pebs * sizeof(**fm_eba),
|
||||
GFP_KERNEL);
|
||||
if (!fm_eba[i]) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
for (j = 0; j < vol->reserved_pebs; j++)
|
||||
scan_eba[i][j] = fm_eba[i][j] = UBI_LEB_UNMAPPED;
|
||||
|
||||
av = ubi_find_av(ai_scan, idx2vol_id(ubi, i));
|
||||
if (!av)
|
||||
continue;
|
||||
|
||||
ubi_rb_for_each_entry(rb, aeb, &av->root, u.rb)
|
||||
scan_eba[i][aeb->lnum] = aeb->pnum;
|
||||
|
||||
av = ubi_find_av(ai_fastmap, idx2vol_id(ubi, i));
|
||||
if (!av)
|
||||
continue;
|
||||
|
||||
ubi_rb_for_each_entry(rb, aeb, &av->root, u.rb)
|
||||
fm_eba[i][aeb->lnum] = aeb->pnum;
|
||||
|
||||
for (j = 0; j < vol->reserved_pebs; j++) {
|
||||
if (scan_eba[i][j] != fm_eba[i][j]) {
|
||||
if (scan_eba[i][j] == UBI_LEB_UNMAPPED ||
|
||||
fm_eba[i][j] == UBI_LEB_UNMAPPED)
|
||||
continue;
|
||||
|
||||
ubi_err("LEB:%i:%i is PEB:%i instead of %i!",
|
||||
vol->vol_id, i, fm_eba[i][j],
|
||||
scan_eba[i][j]);
|
||||
ubi_assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out_free:
|
||||
for (i = 0; i < num_volumes; i++) {
|
||||
if (!ubi->volumes[i])
|
||||
continue;
|
||||
|
||||
kfree(scan_eba[i]);
|
||||
kfree(fm_eba[i]);
|
||||
}
|
||||
|
||||
kfree(scan_eba);
|
||||
kfree(fm_eba);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_eba_init - initialize the EBA sub-system using attaching information.
|
||||
* @ubi: UBI device description object
|
||||
* @ai: attaching 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 ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
|
||||
{
|
||||
int i, j, err, num_volumes;
|
||||
struct ubi_scan_volume *sv;
|
||||
struct ubi_ainf_volume *av;
|
||||
struct ubi_volume *vol;
|
||||
struct ubi_scan_leb *seb;
|
||||
struct ubi_ainf_peb *aeb;
|
||||
struct rb_node *rb;
|
||||
|
||||
dbg_eba("initialize EBA unit");
|
||||
dbg_eba("initialize EBA sub-system");
|
||||
|
||||
spin_lock_init(&ubi->ltree_lock);
|
||||
mutex_init(&ubi->alc_mutex);
|
||||
ubi->ltree = RB_ROOT;
|
||||
|
||||
ubi->global_sqnum = si->max_sqnum + 1;
|
||||
ubi->global_sqnum = ai->max_sqnum + 1;
|
||||
num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT;
|
||||
|
||||
for (i = 0; i < num_volumes; i++) {
|
||||
@ -1174,24 +1345,27 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
|
||||
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)
|
||||
av = ubi_find_av(ai, idx2vol_id(ubi, i));
|
||||
if (!av)
|
||||
continue;
|
||||
|
||||
ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) {
|
||||
if (seb->lnum >= vol->reserved_pebs)
|
||||
ubi_rb_for_each_entry(rb, aeb, &av->root, u.rb) {
|
||||
if (aeb->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;
|
||||
ubi_move_aeb_to_list(av, aeb, &ai->erase);
|
||||
vol->eba_tbl[aeb->lnum] = aeb->pnum;
|
||||
}
|
||||
}
|
||||
|
||||
if (ubi->avail_pebs < EBA_RESERVED_PEBS) {
|
||||
ubi_err("no enough physical eraseblocks (%d, need %d)",
|
||||
ubi->avail_pebs, EBA_RESERVED_PEBS);
|
||||
if (ubi->corr_peb_count)
|
||||
ubi_err("%d PEBs are corrupted and not used",
|
||||
ubi->corr_peb_count);
|
||||
err = -ENOSPC;
|
||||
goto out_free;
|
||||
}
|
||||
@ -1204,9 +1378,7 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
|
||||
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);
|
||||
print_rsvd_warning(ubi, ai);
|
||||
} else
|
||||
ubi->beb_rsvd_pebs = ubi->beb_rsvd_level;
|
||||
|
||||
@ -1214,7 +1386,7 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
|
||||
ubi->rsvd_pebs += ubi->beb_rsvd_pebs;
|
||||
}
|
||||
|
||||
dbg_eba("EBA unit is initialized");
|
||||
dbg_eba("EBA sub-system is initialized");
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
@ -1222,23 +1394,7 @@ out_free:
|
||||
if (!ubi->volumes[i])
|
||||
continue;
|
||||
kfree(ubi->volumes[i]->eba_tbl);
|
||||
ubi->volumes[i]->eba_tbl = NULL;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
1584
drivers/mtd/ubi/fastmap.c
Normal file
1584
drivers/mtd/ubi/fastmap.c
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -8,15 +8,42 @@
|
||||
|
||||
/* This file mostly implements UBI kernel API functions */
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/fs.h>
|
||||
#include <asm/div64.h>
|
||||
#endif
|
||||
|
||||
#else
|
||||
#include <ubi_uboot.h>
|
||||
#endif
|
||||
#include <linux/err.h>
|
||||
|
||||
#include "ubi.h"
|
||||
|
||||
/**
|
||||
* ubi_do_get_device_info - get information about UBI device.
|
||||
* @ubi: UBI device description object
|
||||
* @di: the information is stored here
|
||||
*
|
||||
* This function is the same as 'ubi_get_device_info()', but it assumes the UBI
|
||||
* device is locked and cannot disappear.
|
||||
*/
|
||||
void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di)
|
||||
{
|
||||
di->ubi_num = ubi->ubi_num;
|
||||
di->leb_size = ubi->leb_size;
|
||||
di->leb_start = ubi->leb_start;
|
||||
di->min_io_size = ubi->min_io_size;
|
||||
di->max_write_size = ubi->max_write_size;
|
||||
di->ro_mode = ubi->ro_mode;
|
||||
#ifndef __UBOOT__
|
||||
di->cdev = ubi->cdev.dev;
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_do_get_device_info);
|
||||
|
||||
/**
|
||||
* ubi_get_device_info - get information about UBI device.
|
||||
* @ubi_num: UBI device number
|
||||
@ -31,33 +58,24 @@ int ubi_get_device_info(int ubi_num, struct ubi_device_info *di)
|
||||
|
||||
if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES)
|
||||
return -EINVAL;
|
||||
|
||||
ubi = ubi_get_device(ubi_num);
|
||||
if (!ubi)
|
||||
return -ENODEV;
|
||||
|
||||
di->ubi_num = ubi->ubi_num;
|
||||
di->leb_size = ubi->leb_size;
|
||||
di->min_io_size = ubi->min_io_size;
|
||||
di->ro_mode = ubi->ro_mode;
|
||||
di->cdev = ubi->cdev.dev;
|
||||
|
||||
ubi_do_get_device_info(ubi, di);
|
||||
ubi_put_device(ubi);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_get_device_info);
|
||||
|
||||
/**
|
||||
* ubi_get_volume_info - get information about UBI volume.
|
||||
* @desc: volume descriptor
|
||||
* ubi_do_get_volume_info - get information about UBI volume.
|
||||
* @ubi: UBI device description object
|
||||
* @vol: volume description object
|
||||
* @vi: the information is stored here
|
||||
*/
|
||||
void ubi_get_volume_info(struct ubi_volume_desc *desc,
|
||||
struct ubi_volume_info *vi)
|
||||
void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
struct ubi_volume_info *vi)
|
||||
{
|
||||
const struct ubi_volume *vol = desc->vol;
|
||||
const struct ubi_device *ubi = vol->ubi;
|
||||
|
||||
vi->vol_id = vol->vol_id;
|
||||
vi->ubi_num = ubi->ubi_num;
|
||||
vi->size = vol->reserved_pebs;
|
||||
@ -71,6 +89,17 @@ void ubi_get_volume_info(struct ubi_volume_desc *desc,
|
||||
vi->name = vol->name;
|
||||
vi->cdev = vol->cdev.dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_get_volume_info - get information about UBI volume.
|
||||
* @desc: volume descriptor
|
||||
* @vi: the information is stored here
|
||||
*/
|
||||
void ubi_get_volume_info(struct ubi_volume_desc *desc,
|
||||
struct ubi_volume_info *vi)
|
||||
{
|
||||
ubi_do_get_volume_info(desc->vol->ubi, desc->vol, vi);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_get_volume_info);
|
||||
|
||||
/**
|
||||
@ -98,7 +127,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode)
|
||||
struct ubi_device *ubi;
|
||||
struct ubi_volume *vol;
|
||||
|
||||
dbg_msg("open device %d volume %d, mode %d", ubi_num, vol_id, mode);
|
||||
dbg_gen("open device %d, volume %d, mode %d", ubi_num, vol_id, mode);
|
||||
|
||||
if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES)
|
||||
return ERR_PTR(-EINVAL);
|
||||
@ -188,6 +217,8 @@ out_free:
|
||||
kfree(desc);
|
||||
out_put_ubi:
|
||||
ubi_put_device(ubi);
|
||||
ubi_err("cannot open device %d, volume %d, error %d",
|
||||
ubi_num, vol_id, err);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_open_volume);
|
||||
@ -207,7 +238,7 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
|
||||
struct ubi_device *ubi;
|
||||
struct ubi_volume_desc *ret;
|
||||
|
||||
dbg_msg("open volume %s, mode %d", name, mode);
|
||||
dbg_gen("open device %d, volume %s, mode %d", ubi_num, name, mode);
|
||||
|
||||
if (!name)
|
||||
return ERR_PTR(-EINVAL);
|
||||
@ -249,6 +280,45 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_open_volume_nm);
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* ubi_open_volume_path - open UBI volume by its character device node path.
|
||||
* @pathname: volume character device node path
|
||||
* @mode: open mode
|
||||
*
|
||||
* This function is similar to 'ubi_open_volume()', but opens a volume the path
|
||||
* to its character device node.
|
||||
*/
|
||||
struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode)
|
||||
{
|
||||
int error, ubi_num, vol_id, mod;
|
||||
struct inode *inode;
|
||||
struct path path;
|
||||
|
||||
dbg_gen("open volume %s, mode %d", pathname, mode);
|
||||
|
||||
if (!pathname || !*pathname)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
error = kern_path(pathname, LOOKUP_FOLLOW, &path);
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
|
||||
inode = path.dentry->d_inode;
|
||||
mod = inode->i_mode;
|
||||
ubi_num = ubi_major2num(imajor(inode));
|
||||
vol_id = iminor(inode) - 1;
|
||||
path_put(&path);
|
||||
|
||||
if (!S_ISCHR(mod))
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (vol_id >= 0 && ubi_num >= 0)
|
||||
return ubi_open_volume(ubi_num, vol_id, mode);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_open_volume_path);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ubi_close_volume - close UBI volume.
|
||||
* @desc: volume descriptor
|
||||
@ -258,7 +328,8 @@ void ubi_close_volume(struct ubi_volume_desc *desc)
|
||||
struct ubi_volume *vol = desc->vol;
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
|
||||
dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode);
|
||||
dbg_gen("close device %d, volume %d, mode %d",
|
||||
ubi->ubi_num, vol->vol_id, desc->mode);
|
||||
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
switch (desc->mode) {
|
||||
@ -315,7 +386,7 @@ int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
int err, vol_id = vol->vol_id;
|
||||
|
||||
dbg_msg("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset);
|
||||
dbg_gen("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset);
|
||||
|
||||
if (vol_id < 0 || vol_id >= ubi->vtbl_slots || lnum < 0 ||
|
||||
lnum >= vol->used_ebs || offset < 0 || len < 0 ||
|
||||
@ -353,11 +424,9 @@ EXPORT_SYMBOL_GPL(ubi_leb_read);
|
||||
* @buf: data to write
|
||||
* @offset: offset within the logical eraseblock where to write
|
||||
* @len: how many bytes to write
|
||||
* @dtype: expected data type
|
||||
*
|
||||
* This function writes @len bytes of data from @buf to offset @offset of
|
||||
* logical eraseblock @lnum. The @dtype argument describes expected lifetime of
|
||||
* the data.
|
||||
* logical eraseblock @lnum.
|
||||
*
|
||||
* This function takes care of physical eraseblock write failures. If write to
|
||||
* the physical eraseblock write operation fails, the logical eraseblock is
|
||||
@ -374,13 +443,13 @@ EXPORT_SYMBOL_GPL(ubi_leb_read);
|
||||
* returns immediately with %-EBADF code.
|
||||
*/
|
||||
int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
||||
int offset, int len, int dtype)
|
||||
int offset, int len)
|
||||
{
|
||||
struct ubi_volume *vol = desc->vol;
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
int vol_id = vol->vol_id;
|
||||
|
||||
dbg_msg("write %d bytes to LEB %d:%d:%d", len, vol_id, lnum, offset);
|
||||
dbg_gen("write %d bytes to LEB %d:%d:%d", len, vol_id, lnum, offset);
|
||||
|
||||
if (vol_id < 0 || vol_id >= ubi->vtbl_slots)
|
||||
return -EINVAL;
|
||||
@ -393,17 +462,13 @@ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
||||
offset & (ubi->min_io_size - 1) || len & (ubi->min_io_size - 1))
|
||||
return -EINVAL;
|
||||
|
||||
if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
|
||||
dtype != UBI_UNKNOWN)
|
||||
return -EINVAL;
|
||||
|
||||
if (vol->upd_marker)
|
||||
return -EBADF;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
return ubi_eba_write_leb(ubi, vol, lnum, buf, offset, len, dtype);
|
||||
return ubi_eba_write_leb(ubi, vol, lnum, buf, offset, len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_leb_write);
|
||||
|
||||
@ -413,24 +478,23 @@ EXPORT_SYMBOL_GPL(ubi_leb_write);
|
||||
* @lnum: logical eraseblock number to change
|
||||
* @buf: data to write
|
||||
* @len: how many bytes to write
|
||||
* @dtype: expected 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. The length may be shorter then the logical
|
||||
* data, which has to be aligned. The length may be shorter than the logical
|
||||
* eraseblock size, ant the logical eraseblock may be appended to more times
|
||||
* later on. 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.
|
||||
*/
|
||||
int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
||||
int len, int dtype)
|
||||
int len)
|
||||
{
|
||||
struct ubi_volume *vol = desc->vol;
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
int vol_id = vol->vol_id;
|
||||
|
||||
dbg_msg("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum);
|
||||
dbg_gen("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum);
|
||||
|
||||
if (vol_id < 0 || vol_id >= ubi->vtbl_slots)
|
||||
return -EINVAL;
|
||||
@ -442,17 +506,13 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
||||
len > vol->usable_leb_size || len & (ubi->min_io_size - 1))
|
||||
return -EINVAL;
|
||||
|
||||
if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
|
||||
dtype != UBI_UNKNOWN)
|
||||
return -EINVAL;
|
||||
|
||||
if (vol->upd_marker)
|
||||
return -EBADF;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
return ubi_eba_atomic_leb_change(ubi, vol, lnum, buf, len, dtype);
|
||||
return ubi_eba_atomic_leb_change(ubi, vol, lnum, buf, len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_leb_change);
|
||||
|
||||
@ -474,7 +534,7 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum)
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
int err;
|
||||
|
||||
dbg_msg("erase LEB %d:%d", vol->vol_id, lnum);
|
||||
dbg_gen("erase LEB %d:%d", vol->vol_id, lnum);
|
||||
|
||||
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
|
||||
return -EROFS;
|
||||
@ -489,7 +549,7 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return ubi_wl_flush(ubi);
|
||||
return ubi_wl_flush(ubi, vol->vol_id, lnum);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_leb_erase);
|
||||
|
||||
@ -500,7 +560,7 @@ EXPORT_SYMBOL_GPL(ubi_leb_erase);
|
||||
*
|
||||
* This function un-maps logical eraseblock @lnum and schedules the
|
||||
* corresponding physical eraseblock for erasure, so that it will eventually be
|
||||
* physically erased in background. This operation is much faster then the
|
||||
* physically erased in background. This operation is much faster than the
|
||||
* erase operation.
|
||||
*
|
||||
* Unlike erase, the un-map operation does not guarantee that the logical
|
||||
@ -519,7 +579,7 @@ EXPORT_SYMBOL_GPL(ubi_leb_erase);
|
||||
*
|
||||
* The main and obvious use-case of this function is when the contents of a
|
||||
* logical eraseblock has to be re-written. Then it is much more efficient to
|
||||
* first un-map it, then write new data, rather then first erase it, then write
|
||||
* first un-map it, then write new data, rather than first erase it, then write
|
||||
* new data. Note, once new data has been written to the logical eraseblock,
|
||||
* UBI guarantees that the old contents has gone forever. In other words, if an
|
||||
* unclean reboot happens after the logical eraseblock has been un-mapped and
|
||||
@ -534,7 +594,7 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum)
|
||||
struct ubi_volume *vol = desc->vol;
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
|
||||
dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum);
|
||||
dbg_gen("unmap LEB %d:%d", vol->vol_id, lnum);
|
||||
|
||||
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
|
||||
return -EROFS;
|
||||
@ -550,13 +610,12 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum)
|
||||
EXPORT_SYMBOL_GPL(ubi_leb_unmap);
|
||||
|
||||
/**
|
||||
* ubi_leb_map - map logical erasblock to a physical eraseblock.
|
||||
* ubi_leb_map - map logical eraseblock to a physical eraseblock.
|
||||
* @desc: volume descriptor
|
||||
* @lnum: logical eraseblock number
|
||||
* @dtype: expected data type
|
||||
*
|
||||
* This function maps an un-mapped logical eraseblock @lnum to a physical
|
||||
* eraseblock. This means, that after a successfull invocation of this
|
||||
* eraseblock. This means, that after a successful invocation of this
|
||||
* function the logical eraseblock @lnum will be empty (contain only %0xFF
|
||||
* bytes) and be mapped to a physical eraseblock, even if an unclean reboot
|
||||
* happens.
|
||||
@ -566,12 +625,12 @@ EXPORT_SYMBOL_GPL(ubi_leb_unmap);
|
||||
* eraseblock is already mapped, and other negative error codes in case of
|
||||
* other failures.
|
||||
*/
|
||||
int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype)
|
||||
int ubi_leb_map(struct ubi_volume_desc *desc, int lnum)
|
||||
{
|
||||
struct ubi_volume *vol = desc->vol;
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
|
||||
dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum);
|
||||
dbg_gen("unmap LEB %d:%d", vol->vol_id, lnum);
|
||||
|
||||
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
|
||||
return -EROFS;
|
||||
@ -579,17 +638,13 @@ int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype)
|
||||
if (lnum < 0 || lnum >= vol->reserved_pebs)
|
||||
return -EINVAL;
|
||||
|
||||
if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
|
||||
dtype != UBI_UNKNOWN)
|
||||
return -EINVAL;
|
||||
|
||||
if (vol->upd_marker)
|
||||
return -EBADF;
|
||||
|
||||
if (vol->eba_tbl[lnum] >= 0)
|
||||
return -EBADMSG;
|
||||
|
||||
return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype);
|
||||
return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_leb_map);
|
||||
|
||||
@ -613,7 +668,7 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum)
|
||||
{
|
||||
struct ubi_volume *vol = desc->vol;
|
||||
|
||||
dbg_msg("test LEB %d:%d", vol->vol_id, lnum);
|
||||
dbg_gen("test LEB %d:%d", vol->vol_id, lnum);
|
||||
|
||||
if (lnum < 0 || lnum >= vol->reserved_pebs)
|
||||
return -EINVAL;
|
||||
@ -624,3 +679,110 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum)
|
||||
return vol->eba_tbl[lnum] >= 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_is_mapped);
|
||||
|
||||
/**
|
||||
* ubi_sync - synchronize UBI device buffers.
|
||||
* @ubi_num: UBI device to synchronize
|
||||
*
|
||||
* The underlying MTD device may cache data in hardware or in software. This
|
||||
* function ensures the caches are flushed. Returns zero in case of success and
|
||||
* a negative error code in case of failure.
|
||||
*/
|
||||
int ubi_sync(int ubi_num)
|
||||
{
|
||||
struct ubi_device *ubi;
|
||||
|
||||
ubi = ubi_get_device(ubi_num);
|
||||
if (!ubi)
|
||||
return -ENODEV;
|
||||
|
||||
mtd_sync(ubi->mtd);
|
||||
ubi_put_device(ubi);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_sync);
|
||||
|
||||
/**
|
||||
* ubi_flush - flush UBI work queue.
|
||||
* @ubi_num: UBI device to flush work queue
|
||||
* @vol_id: volume id to flush for
|
||||
* @lnum: logical eraseblock number to flush for
|
||||
*
|
||||
* This function executes all pending works for a particular volume id / logical
|
||||
* eraseblock number pair. If either value is set to %UBI_ALL, then it acts as
|
||||
* a wildcard for all of the corresponding volume numbers or logical
|
||||
* eraseblock numbers. It returns zero in case of success and a negative error
|
||||
* code in case of failure.
|
||||
*/
|
||||
int ubi_flush(int ubi_num, int vol_id, int lnum)
|
||||
{
|
||||
struct ubi_device *ubi;
|
||||
int err = 0;
|
||||
|
||||
ubi = ubi_get_device(ubi_num);
|
||||
if (!ubi)
|
||||
return -ENODEV;
|
||||
|
||||
err = ubi_wl_flush(ubi, vol_id, lnum);
|
||||
ubi_put_device(ubi);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_flush);
|
||||
|
||||
#ifndef __UBOOT__
|
||||
BLOCKING_NOTIFIER_HEAD(ubi_notifiers);
|
||||
|
||||
/**
|
||||
* ubi_register_volume_notifier - register a volume notifier.
|
||||
* @nb: the notifier description object
|
||||
* @ignore_existing: if non-zero, do not send "added" notification for all
|
||||
* already existing volumes
|
||||
*
|
||||
* This function registers a volume notifier, which means that
|
||||
* 'nb->notifier_call()' will be invoked when an UBI volume is created,
|
||||
* removed, re-sized, re-named, or updated. The first argument of the function
|
||||
* is the notification type. The second argument is pointer to a
|
||||
* &struct ubi_notification object which describes the notification event.
|
||||
* Using UBI API from the volume notifier is prohibited.
|
||||
*
|
||||
* This function returns zero in case of success and a negative error code
|
||||
* in case of failure.
|
||||
*/
|
||||
int ubi_register_volume_notifier(struct notifier_block *nb,
|
||||
int ignore_existing)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = blocking_notifier_chain_register(&ubi_notifiers, nb);
|
||||
if (err != 0)
|
||||
return err;
|
||||
if (ignore_existing)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* We are going to walk all UBI devices and all volumes, and
|
||||
* notify the user about existing volumes by the %UBI_VOLUME_ADDED
|
||||
* event. We have to lock the @ubi_devices_mutex to make sure UBI
|
||||
* devices do not disappear.
|
||||
*/
|
||||
mutex_lock(&ubi_devices_mutex);
|
||||
ubi_enumerate_volumes(nb);
|
||||
mutex_unlock(&ubi_devices_mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_register_volume_notifier);
|
||||
|
||||
/**
|
||||
* ubi_unregister_volume_notifier - unregister the volume notifier.
|
||||
* @nb: the notifier description object
|
||||
*
|
||||
* This function unregisters volume notifier @nm and returns zero in case of
|
||||
* success and a negative error code in case of failure.
|
||||
*/
|
||||
int ubi_unregister_volume_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_unregister(&ubi_notifiers, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_unregister_volume_notifier);
|
||||
#endif
|
||||
|
@ -81,14 +81,62 @@ int ubi_check_volume(struct ubi_device *ubi, int vol_id)
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_calculate_rsvd_pool - calculate how many PEBs must be reserved for bad
|
||||
* ubi_update_reserved - update bad eraseblock handling accounting data.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* This function calculates the gap between current number of PEBs reserved for
|
||||
* bad eraseblock handling and the required level of PEBs that must be
|
||||
* reserved, and if necessary, reserves more PEBs to fill that gap, according
|
||||
* to availability. Should be called with ubi->volumes_lock held.
|
||||
*/
|
||||
void ubi_update_reserved(struct ubi_device *ubi)
|
||||
{
|
||||
int need = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs;
|
||||
|
||||
if (need <= 0 || ubi->avail_pebs == 0)
|
||||
return;
|
||||
|
||||
need = min_t(int, need, ubi->avail_pebs);
|
||||
ubi->avail_pebs -= need;
|
||||
ubi->rsvd_pebs += need;
|
||||
ubi->beb_rsvd_pebs += need;
|
||||
ubi_msg("reserved more %d PEBs for bad PEB handling", need);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_calculate_reserved - calculate how many PEBs must be reserved for bad
|
||||
* eraseblock handling.
|
||||
* @ubi: UBI device description object
|
||||
*/
|
||||
void ubi_calculate_reserved(struct ubi_device *ubi)
|
||||
{
|
||||
ubi->beb_rsvd_level = ubi->good_peb_count/100;
|
||||
ubi->beb_rsvd_level *= CONFIG_MTD_UBI_BEB_RESERVE;
|
||||
if (ubi->beb_rsvd_level < MIN_RESEVED_PEBS)
|
||||
ubi->beb_rsvd_level = MIN_RESEVED_PEBS;
|
||||
/*
|
||||
* Calculate the actual number of PEBs currently needed to be reserved
|
||||
* for future bad eraseblock handling.
|
||||
*/
|
||||
ubi->beb_rsvd_level = ubi->bad_peb_limit - ubi->bad_peb_count;
|
||||
if (ubi->beb_rsvd_level < 0) {
|
||||
ubi->beb_rsvd_level = 0;
|
||||
ubi_warn("number of bad PEBs (%d) is above the expected limit (%d), not reserving any PEBs for bad PEB handling, will use available PEBs (if any)",
|
||||
ubi->bad_peb_count, ubi->bad_peb_limit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_check_pattern - 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 %1 in there are only @patt bytes in @buf, and %0 if
|
||||
* something else was also found.
|
||||
*/
|
||||
int ubi_check_pattern(const void *buf, uint8_t patt, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
if (((const uint8_t *)buf)[i] != patt)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,153 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) International Business Machines Corp., 2006
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Author: Artem Bityutskiy (Битюцкий Артём)
|
||||
*/
|
||||
|
||||
#ifndef __UBI_SCAN_H__
|
||||
#define __UBI_SCAN_H__
|
||||
|
||||
/* The erase counter value for this physical eraseblock is unknown */
|
||||
#define UBI_SCAN_UNKNOWN_EC (-1)
|
||||
|
||||
/**
|
||||
* struct ubi_scan_leb - scanning information about a physical eraseblock.
|
||||
* @ec: erase counter (%UBI_SCAN_UNKNOWN_EC if it is unknown)
|
||||
* @pnum: physical eraseblock number
|
||||
* @lnum: logical eraseblock number
|
||||
* @scrub: if this physical eraseblock needs scrubbing
|
||||
* @sqnum: sequence number
|
||||
* @u: unions RB-tree or @list links
|
||||
* @u.rb: link in the per-volume RB-tree of &struct ubi_scan_leb objects
|
||||
* @u.list: link in one of the eraseblock lists
|
||||
* @leb_ver: logical eraseblock version (obsolete)
|
||||
*
|
||||
* 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;
|
||||
unsigned long long sqnum;
|
||||
union {
|
||||
struct rb_node rb;
|
||||
struct list_head list;
|
||||
} u;
|
||||
uint32_t leb_ver;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_scan_volume - scanning information about a volume.
|
||||
* @vol_id: volume ID
|
||||
* @highest_lnum: highest logical eraseblock number in this volume
|
||||
* @leb_count: number of logical eraseblocks in this volume
|
||||
* @vol_type: volume type
|
||||
* @used_ebs: number of used logical eraseblocks in this volume (only for
|
||||
* static volumes)
|
||||
* @last_data_size: amount of data in the last logical eraseblock of this
|
||||
* volume (always equivalent to the usable logical eraseblock size in case of
|
||||
* dynamic volumes)
|
||||
* @data_pad: how many bytes at the end of logical eraseblocks of this volume
|
||||
* are not used (due to volume alignment)
|
||||
* @compat: compatibility flags of this volume
|
||||
* @rb: link in the volume RB-tree
|
||||
* @root: root of the RB-tree containing all the eraseblock belonging to this
|
||||
* volume (&struct ubi_scan_leb objects)
|
||||
*
|
||||
* One object of this type is allocated for each volume during scanning.
|
||||
*/
|
||||
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_info - UBI scanning information.
|
||||
* @volumes: root of the volume RB-tree
|
||||
* @corr: list of corrupted physical eraseblocks
|
||||
* @free: list of free physical eraseblocks
|
||||
* @erase: list of physical eraseblocks which have to be erased
|
||||
* @alien: list of physical eraseblocks which should not be used by UBI (e.g.,
|
||||
* @bad_peb_count: count of bad physical eraseblocks
|
||||
* those belonging to "preserve"-compatible internal volumes)
|
||||
* @vols_found: number of volumes found during scanning
|
||||
* @highest_vol_id: highest volume ID
|
||||
* @alien_peb_count: count of physical eraseblocks in the @alien list
|
||||
* @is_empty: flag indicating whether the MTD device is empty or not
|
||||
* @min_ec: lowest erase counter value
|
||||
* @max_ec: highest erase counter value
|
||||
* @max_sqnum: highest sequence number 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.
|
||||
*/
|
||||
struct ubi_scan_info {
|
||||
struct rb_root volumes;
|
||||
struct list_head corr;
|
||||
struct list_head free;
|
||||
struct list_head erase;
|
||||
struct list_head alien;
|
||||
int bad_peb_count;
|
||||
int vols_found;
|
||||
int highest_vol_id;
|
||||
int alien_peb_count;
|
||||
int is_empty;
|
||||
int min_ec;
|
||||
int max_ec;
|
||||
unsigned long long max_sqnum;
|
||||
int mean_ec;
|
||||
uint64_t ec_sum;
|
||||
int ec_count;
|
||||
};
|
||||
|
||||
struct ubi_device;
|
||||
struct ubi_vid_hdr;
|
||||
|
||||
/*
|
||||
* ubi_scan_move_to_list - move a physical eraseblock from the volume tree to a
|
||||
* list.
|
||||
*
|
||||
* @sv: volume scanning information
|
||||
* @seb: scanning eraseblock infprmation
|
||||
* @list: the list to move to
|
||||
*/
|
||||
static inline void ubi_scan_move_to_list(struct ubi_scan_volume *sv,
|
||||
struct ubi_scan_leb *seb,
|
||||
struct list_head *list)
|
||||
{
|
||||
rb_erase(&seb->u.rb, &sv->root);
|
||||
list_add_tail(&seb->u.list, list);
|
||||
}
|
||||
|
||||
int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si,
|
||||
int pnum, int ec, const struct ubi_vid_hdr *vid_hdr,
|
||||
int bitflips);
|
||||
struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si,
|
||||
int vol_id);
|
||||
struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv,
|
||||
int lnum);
|
||||
void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv);
|
||||
struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi,
|
||||
struct ubi_scan_info *si);
|
||||
int ubi_scan_erase_peb(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
||||
int pnum, int ec);
|
||||
struct ubi_scan_info *ubi_scan(struct ubi_device *ubi);
|
||||
void ubi_scan_destroy_si(struct ubi_scan_info *si);
|
||||
|
||||
#endif /* !__UBI_SCAN_H__ */
|
@ -86,10 +86,11 @@ enum {
|
||||
* Compatibility constants used by internal volumes.
|
||||
*
|
||||
* @UBI_COMPAT_DELETE: delete this internal volume before anything is written
|
||||
* to the flash
|
||||
* 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
|
||||
* physical eraseblocks, don't allow the wear-leveling
|
||||
* sub-system to move them
|
||||
* @UBI_COMPAT_REJECT: reject this UBI image
|
||||
*/
|
||||
enum {
|
||||
@ -111,18 +112,19 @@ enum {
|
||||
* struct ubi_ec_hdr - UBI erase counter header.
|
||||
* @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC)
|
||||
* @version: version of UBI implementation which is supposed to accept this
|
||||
* UBI image
|
||||
* UBI image
|
||||
* @padding1: reserved for future, zeroes
|
||||
* @ec: the erase counter
|
||||
* @vid_hdr_offset: where the VID header starts
|
||||
* @data_offset: where the user data start
|
||||
* @image_seq: image sequence number
|
||||
* @padding2: reserved for future, zeroes
|
||||
* @hdr_crc: 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
|
||||
* work with this UBI image. If @version is greater than the current UBI
|
||||
* version, the image is rejected. This may be useful in future if something
|
||||
* is changed radically. This field is duplicated in the volume identifier
|
||||
* header.
|
||||
@ -131,6 +133,14 @@ enum {
|
||||
* volume identifier header and user data, relative to the beginning of the
|
||||
* physical eraseblock. These values have to be the same for all physical
|
||||
* eraseblocks.
|
||||
*
|
||||
* The @image_seq field is used to validate a UBI image that has been prepared
|
||||
* for a UBI device. The @image_seq value can be any value, but it must be the
|
||||
* same on all eraseblocks. UBI will ensure that all new erase counter headers
|
||||
* also contain this value, and will check the value when attaching the flash.
|
||||
* One way to make use of @image_seq is to increase its value by one every time
|
||||
* an image is flashed over an existing image, then, if the flashing does not
|
||||
* complete, UBI will detect the error when attaching the media.
|
||||
*/
|
||||
struct ubi_ec_hdr {
|
||||
__be32 magic;
|
||||
@ -139,32 +149,32 @@ struct ubi_ec_hdr {
|
||||
__be64 ec; /* Warning: the current limit is 31-bit anyway! */
|
||||
__be32 vid_hdr_offset;
|
||||
__be32 data_offset;
|
||||
__u8 padding2[36];
|
||||
__be32 image_seq;
|
||||
__u8 padding2[32];
|
||||
__be32 hdr_crc;
|
||||
} __attribute__ ((packed));
|
||||
} __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)
|
||||
* image (%UBI_VERSION)
|
||||
* @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC)
|
||||
* @copy_flag: if this logical eraseblock was copied from another physical
|
||||
* eraseblock (for wear-leveling reasons)
|
||||
* eraseblock (for wear-leveling reasons)
|
||||
* @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE,
|
||||
* %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT)
|
||||
* %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT)
|
||||
* @vol_id: ID of this volume
|
||||
* @lnum: logical eraseblock number
|
||||
* @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be
|
||||
* removed, kept only for not breaking older UBI users)
|
||||
* @padding1: reserved for future, zeroes
|
||||
* @data_size: how many bytes of data this logical eraseblock contains
|
||||
* @used_ebs: total number of used logical eraseblocks in this volume
|
||||
* @data_pad: how many bytes at the end of this physical eraseblock are not
|
||||
* used
|
||||
* used
|
||||
* @data_crc: CRC checksum of the data stored in this logical eraseblock
|
||||
* @padding1: reserved for future, zeroes
|
||||
* @sqnum: sequence number
|
||||
* @padding2: reserved for future, zeroes
|
||||
* @sqnum: sequence number
|
||||
* @padding3: reserved for future, zeroes
|
||||
* @hdr_crc: volume identifier header CRC checksum
|
||||
*
|
||||
* The @sqnum is the value of the global sequence counter at the time when this
|
||||
@ -175,7 +185,7 @@ struct ubi_ec_hdr {
|
||||
* (sequence number) is used to distinguish between older and newer versions of
|
||||
* logical eraseblocks.
|
||||
*
|
||||
* There are 2 situations when there may be more then one physical eraseblock
|
||||
* There are 2 situations when there may be more than one physical eraseblock
|
||||
* corresponding to the same logical eraseblock, i.e., having the same @vol_id
|
||||
* and @lnum values in the volume identifier header. Suppose we have a logical
|
||||
* eraseblock L and it is mapped to the physical eraseblock P.
|
||||
@ -212,10 +222,6 @@ struct ubi_ec_hdr {
|
||||
* checksum is correct, this physical eraseblock is selected (P1). Otherwise
|
||||
* the older one (P) is selected.
|
||||
*
|
||||
* Note, there is an obsolete @leb_ver field which was used instead of @sqnum
|
||||
* in the past. But it is not used anymore and we keep it in order to be able
|
||||
* to deal with old UBI images. It will be removed at some point.
|
||||
*
|
||||
* There are 2 sorts of volumes in UBI: user volumes and internal volumes.
|
||||
* Internal volumes are not seen from outside and are used for various internal
|
||||
* UBI purposes. In this implementation there is only one internal volume - the
|
||||
@ -236,9 +242,9 @@ struct ubi_ec_hdr {
|
||||
* 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
|
||||
* data of the physical eraseblock was moved by the wear-leveling unit, then
|
||||
* the wear-leveling unit calculates the data CRC and stores it in the
|
||||
* @data_crc field. And of course, the @copy_flag is %in this case.
|
||||
* data of the physical eraseblock was moved by the wear-leveling sub-system,
|
||||
* then the wear-leveling sub-system calculates the data CRC and stores it in
|
||||
* the @data_crc field. And of course, the @copy_flag is %in this case.
|
||||
*
|
||||
* The @data_size field is used only for static volumes because UBI has to know
|
||||
* how many bytes of data are stored in this eraseblock. For dynamic volumes,
|
||||
@ -265,23 +271,23 @@ struct ubi_vid_hdr {
|
||||
__u8 compat;
|
||||
__be32 vol_id;
|
||||
__be32 lnum;
|
||||
__be32 leb_ver; /* obsolete, to be removed, don't use */
|
||||
__u8 padding1[4];
|
||||
__be32 data_size;
|
||||
__be32 used_ebs;
|
||||
__be32 data_pad;
|
||||
__be32 data_crc;
|
||||
__u8 padding1[4];
|
||||
__u8 padding2[4];
|
||||
__be64 sqnum;
|
||||
__u8 padding2[12];
|
||||
__u8 padding3[12];
|
||||
__be32 hdr_crc;
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/* Internal UBI volumes count */
|
||||
#define UBI_INT_VOL_COUNT 1
|
||||
|
||||
/*
|
||||
* Starting ID of internal volumes. There is reserved room for 4096 internal
|
||||
* volumes.
|
||||
* Starting ID of internal volumes: 0x7fffefff.
|
||||
* There is reserved room for 4096 internal volumes.
|
||||
*/
|
||||
#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096)
|
||||
|
||||
@ -351,10 +357,151 @@ struct ubi_vtbl_record {
|
||||
__u8 vol_type;
|
||||
__u8 upd_marker;
|
||||
__be16 name_len;
|
||||
#ifndef __UBOOT__
|
||||
__u8 name[UBI_VOL_NAME_MAX+1];
|
||||
#else
|
||||
char name[UBI_VOL_NAME_MAX+1];
|
||||
#endif
|
||||
__u8 flags;
|
||||
__u8 padding[23];
|
||||
__be32 crc;
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/* UBI fastmap on-flash data structures */
|
||||
|
||||
#define UBI_FM_SB_VOLUME_ID (UBI_LAYOUT_VOLUME_ID + 1)
|
||||
#define UBI_FM_DATA_VOLUME_ID (UBI_LAYOUT_VOLUME_ID + 2)
|
||||
|
||||
/* fastmap on-flash data structure format version */
|
||||
#define UBI_FM_FMT_VERSION 1
|
||||
|
||||
#define UBI_FM_SB_MAGIC 0x7B11D69F
|
||||
#define UBI_FM_HDR_MAGIC 0xD4B82EF7
|
||||
#define UBI_FM_VHDR_MAGIC 0xFA370ED1
|
||||
#define UBI_FM_POOL_MAGIC 0x67AF4D08
|
||||
#define UBI_FM_EBA_MAGIC 0xf0c040a8
|
||||
|
||||
/* A fastmap supber block can be located between PEB 0 and
|
||||
* UBI_FM_MAX_START */
|
||||
#define UBI_FM_MAX_START 64
|
||||
|
||||
/* A fastmap can use up to UBI_FM_MAX_BLOCKS PEBs */
|
||||
#define UBI_FM_MAX_BLOCKS 32
|
||||
|
||||
/* 5% of the total number of PEBs have to be scanned while attaching
|
||||
* from a fastmap.
|
||||
* But the size of this pool is limited to be between UBI_FM_MIN_POOL_SIZE and
|
||||
* UBI_FM_MAX_POOL_SIZE */
|
||||
#define UBI_FM_MIN_POOL_SIZE 8
|
||||
#define UBI_FM_MAX_POOL_SIZE 256
|
||||
|
||||
#define UBI_FM_WL_POOL_SIZE 25
|
||||
|
||||
/**
|
||||
* struct ubi_fm_sb - UBI fastmap super block
|
||||
* @magic: fastmap super block magic number (%UBI_FM_SB_MAGIC)
|
||||
* @version: format version of this fastmap
|
||||
* @data_crc: CRC over the fastmap data
|
||||
* @used_blocks: number of PEBs used by this fastmap
|
||||
* @block_loc: an array containing the location of all PEBs of the fastmap
|
||||
* @block_ec: the erase counter of each used PEB
|
||||
* @sqnum: highest sequence number value at the time while taking the fastmap
|
||||
*
|
||||
*/
|
||||
struct ubi_fm_sb {
|
||||
__be32 magic;
|
||||
__u8 version;
|
||||
__u8 padding1[3];
|
||||
__be32 data_crc;
|
||||
__be32 used_blocks;
|
||||
__be32 block_loc[UBI_FM_MAX_BLOCKS];
|
||||
__be32 block_ec[UBI_FM_MAX_BLOCKS];
|
||||
__be64 sqnum;
|
||||
__u8 padding2[32];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubi_fm_hdr - header of the fastmap data set
|
||||
* @magic: fastmap header magic number (%UBI_FM_HDR_MAGIC)
|
||||
* @free_peb_count: number of free PEBs known by this fastmap
|
||||
* @used_peb_count: number of used PEBs known by this fastmap
|
||||
* @scrub_peb_count: number of to be scrubbed PEBs known by this fastmap
|
||||
* @bad_peb_count: number of bad PEBs known by this fastmap
|
||||
* @erase_peb_count: number of bad PEBs which have to be erased
|
||||
* @vol_count: number of UBI volumes known by this fastmap
|
||||
*/
|
||||
struct ubi_fm_hdr {
|
||||
__be32 magic;
|
||||
__be32 free_peb_count;
|
||||
__be32 used_peb_count;
|
||||
__be32 scrub_peb_count;
|
||||
__be32 bad_peb_count;
|
||||
__be32 erase_peb_count;
|
||||
__be32 vol_count;
|
||||
__u8 padding[4];
|
||||
} __packed;
|
||||
|
||||
/* struct ubi_fm_hdr is followed by two struct ubi_fm_scan_pool */
|
||||
|
||||
/**
|
||||
* struct ubi_fm_scan_pool - Fastmap pool PEBs to be scanned while attaching
|
||||
* @magic: pool magic numer (%UBI_FM_POOL_MAGIC)
|
||||
* @size: current pool size
|
||||
* @max_size: maximal pool size
|
||||
* @pebs: an array containing the location of all PEBs in this pool
|
||||
*/
|
||||
struct ubi_fm_scan_pool {
|
||||
__be32 magic;
|
||||
__be16 size;
|
||||
__be16 max_size;
|
||||
__be32 pebs[UBI_FM_MAX_POOL_SIZE];
|
||||
__be32 padding[4];
|
||||
} __packed;
|
||||
|
||||
/* ubi_fm_scan_pool is followed by nfree+nused struct ubi_fm_ec records */
|
||||
|
||||
/**
|
||||
* struct ubi_fm_ec - stores the erase counter of a PEB
|
||||
* @pnum: PEB number
|
||||
* @ec: ec of this PEB
|
||||
*/
|
||||
struct ubi_fm_ec {
|
||||
__be32 pnum;
|
||||
__be32 ec;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubi_fm_volhdr - Fastmap volume header
|
||||
* it identifies the start of an eba table
|
||||
* @magic: Fastmap volume header magic number (%UBI_FM_VHDR_MAGIC)
|
||||
* @vol_id: volume id of the fastmapped volume
|
||||
* @vol_type: type of the fastmapped volume
|
||||
* @data_pad: data_pad value of the fastmapped volume
|
||||
* @used_ebs: number of used LEBs within this volume
|
||||
* @last_eb_bytes: number of bytes used in the last LEB
|
||||
*/
|
||||
struct ubi_fm_volhdr {
|
||||
__be32 magic;
|
||||
__be32 vol_id;
|
||||
__u8 vol_type;
|
||||
__u8 padding1[3];
|
||||
__be32 data_pad;
|
||||
__be32 used_ebs;
|
||||
__be32 last_eb_bytes;
|
||||
__u8 padding2[8];
|
||||
} __packed;
|
||||
|
||||
/* struct ubi_fm_volhdr is followed by one struct ubi_fm_eba records */
|
||||
|
||||
/**
|
||||
* struct ubi_fm_eba - denotes an association beween a PEB and LEB
|
||||
* @magic: EBA table magic number
|
||||
* @reserved_pebs: number of table entries
|
||||
* @pnum: PEB number of LEB (LEB is the index)
|
||||
*/
|
||||
struct ubi_fm_eba {
|
||||
__be32 magic;
|
||||
__be32 reserved_pebs;
|
||||
__be32 pnum[0];
|
||||
} __packed;
|
||||
#endif /* !__UBI_MEDIA_H__ */
|
||||
|
@ -10,7 +10,8 @@
|
||||
#ifndef __UBI_UBI_H__
|
||||
#define __UBI_UBI_H__
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
@ -23,22 +24,18 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/ubi.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <asm/pgtable.h>
|
||||
#else
|
||||
#include <ubi_uboot.h>
|
||||
#endif
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/ubi.h>
|
||||
|
||||
#include "ubi-media.h"
|
||||
#include "scan.h"
|
||||
#include "debug.h"
|
||||
#include <mtd/ubi-user.h>
|
||||
|
||||
/* Maximum number of supported UBI devices */
|
||||
#define UBI_MAX_DEVICES 32
|
||||
@ -52,20 +49,21 @@
|
||||
#else
|
||||
#define ubi_msg(fmt, ...) printk(KERN_NOTICE "UBI: " fmt "\n", ##__VA_ARGS__)
|
||||
#endif
|
||||
/* UBI warning messages */
|
||||
#define ubi_warn(fmt, ...) printk(KERN_WARNING "UBI warning: %s: " fmt "\n", \
|
||||
__func__, ##__VA_ARGS__)
|
||||
/* UBI error messages */
|
||||
#define ubi_err(fmt, ...) printk(KERN_ERR "UBI error: %s: " fmt "\n", \
|
||||
__func__, ##__VA_ARGS__)
|
||||
|
||||
/* Lowest number PEBs reserved for bad PEB handling */
|
||||
#define MIN_RESEVED_PEBS 2
|
||||
/* UBI warning messages */
|
||||
#define ubi_warn(fmt, ...) pr_warn("UBI warning: %s: " fmt "\n", \
|
||||
__func__, ##__VA_ARGS__)
|
||||
/* UBI error messages */
|
||||
#define ubi_err(fmt, ...) pr_err("UBI error: %s: " fmt "\n", \
|
||||
__func__, ##__VA_ARGS__)
|
||||
|
||||
/* Background thread name pattern */
|
||||
#define UBI_BGT_NAME_PATTERN "ubi_bgt%dd"
|
||||
|
||||
/* This marker in the EBA table means that the LEB is um-mapped */
|
||||
/*
|
||||
* This marker in the EBA table means that the LEB is um-mapped.
|
||||
* NOTE! It has to have the same value as %UBI_ALL.
|
||||
*/
|
||||
#define UBI_LEB_UNMAPPED -1
|
||||
|
||||
/*
|
||||
@ -75,37 +73,98 @@
|
||||
#define UBI_IO_RETRIES 3
|
||||
|
||||
/*
|
||||
* Error codes returned by the I/O unit.
|
||||
* Length of the protection queue. The length is effectively equivalent to the
|
||||
* number of (global) erase cycles PEBs are protected from the wear-leveling
|
||||
* worker.
|
||||
*/
|
||||
#define UBI_PROT_QUEUE_LEN 10
|
||||
|
||||
/* The volume ID/LEB number/erase counter is unknown */
|
||||
#define UBI_UNKNOWN -1
|
||||
|
||||
/*
|
||||
* The UBI debugfs directory name pattern and maximum name length (3 for "ubi"
|
||||
* + 2 for the number plus 1 for the trailing zero byte.
|
||||
*/
|
||||
#define UBI_DFS_DIR_NAME "ubi%d"
|
||||
#define UBI_DFS_DIR_LEN (3 + 2 + 1)
|
||||
|
||||
/*
|
||||
* Error codes returned by the I/O sub-system.
|
||||
*
|
||||
* 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 contains only 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_FF: the read region of flash contains only 0xFFs
|
||||
* UBI_IO_FF_BITFLIPS: the same as %UBI_IO_FF, but also also there was a data
|
||||
* integrity error reported by the MTD driver
|
||||
* (uncorrectable ECC error in case of NAND)
|
||||
* UBI_IO_BAD_HDR: the EC or VID header is corrupted (bad magic or CRC)
|
||||
* UBI_IO_BAD_HDR_EBADMSG: the same as %UBI_IO_BAD_HDR, but also there was a
|
||||
* data integrity error reported by the MTD driver
|
||||
* (uncorrectable ECC error in case of NAND)
|
||||
* UBI_IO_BITFLIPS: bit-flips were detected and corrected
|
||||
*
|
||||
* Note, it is probably better to have bit-flip and ebadmsg as flags which can
|
||||
* be or'ed with other error code. But this is a big change because there are
|
||||
* may callers, so it does not worth the risk of introducing a bug
|
||||
*/
|
||||
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_FF = 1,
|
||||
UBI_IO_FF_BITFLIPS,
|
||||
UBI_IO_BAD_HDR,
|
||||
UBI_IO_BAD_HDR_EBADMSG,
|
||||
UBI_IO_BITFLIPS,
|
||||
};
|
||||
|
||||
/*
|
||||
* Return codes of the 'ubi_eba_copy_leb()' function.
|
||||
*
|
||||
* MOVE_CANCEL_RACE: canceled because the volume is being deleted, the source
|
||||
* PEB was put meanwhile, or there is I/O on the source PEB
|
||||
* MOVE_SOURCE_RD_ERR: canceled because there was a read error from the source
|
||||
* PEB
|
||||
* MOVE_TARGET_RD_ERR: canceled because there was a read error from the target
|
||||
* PEB
|
||||
* MOVE_TARGET_WR_ERR: canceled because there was a write error to the target
|
||||
* PEB
|
||||
* MOVE_TARGET_BITFLIPS: canceled because a bit-flip was detected in the
|
||||
* target PEB
|
||||
* MOVE_RETRY: retry scrubbing the PEB
|
||||
*/
|
||||
enum {
|
||||
MOVE_CANCEL_RACE = 1,
|
||||
MOVE_SOURCE_RD_ERR,
|
||||
MOVE_TARGET_RD_ERR,
|
||||
MOVE_TARGET_WR_ERR,
|
||||
MOVE_TARGET_BITFLIPS,
|
||||
MOVE_RETRY,
|
||||
};
|
||||
|
||||
/*
|
||||
* Return codes of the fastmap sub-system
|
||||
*
|
||||
* UBI_NO_FASTMAP: No fastmap super block was found
|
||||
* UBI_BAD_FASTMAP: A fastmap was found but it's unusable
|
||||
*/
|
||||
enum {
|
||||
UBI_NO_FASTMAP = 1,
|
||||
UBI_BAD_FASTMAP,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_wl_entry - wear-leveling entry.
|
||||
* @rb: link in the corresponding RB-tree
|
||||
* @u.rb: link in the corresponding (free/used) RB-tree
|
||||
* @u.list: link in the protection queue
|
||||
* @ec: erase counter
|
||||
* @pnum: physical eraseblock number
|
||||
*
|
||||
* This data structure is used in the WL unit. Each physical eraseblock has a
|
||||
* corresponding &struct wl_entry object which may be kept in different
|
||||
* RB-trees. See WL unit for details.
|
||||
* This data structure is used in the WL sub-system. Each physical eraseblock
|
||||
* has a corresponding &struct wl_entry object which may be kept in different
|
||||
* RB-trees. See WL sub-system for details.
|
||||
*/
|
||||
struct ubi_wl_entry {
|
||||
struct rb_node rb;
|
||||
union {
|
||||
struct rb_node rb;
|
||||
struct list_head list;
|
||||
} u;
|
||||
int ec;
|
||||
int pnum;
|
||||
};
|
||||
@ -119,10 +178,10 @@ struct ubi_wl_entry {
|
||||
* @mutex: read/write mutex to implement read/write access serialization to
|
||||
* the (@vol_id, @lnum) logical eraseblock
|
||||
*
|
||||
* This data structure is used in the EBA unit to implement per-LEB locking.
|
||||
* When a logical eraseblock is being locked - corresponding
|
||||
* This data structure is used in the EBA sub-system to implement per-LEB
|
||||
* locking. When a logical eraseblock is being locked - corresponding
|
||||
* &struct ubi_ltree_entry object is inserted to the lock tree (@ubi->ltree).
|
||||
* See EBA unit for details.
|
||||
* See EBA sub-system for details.
|
||||
*/
|
||||
struct ubi_ltree_entry {
|
||||
struct rb_node rb;
|
||||
@ -132,8 +191,64 @@ struct ubi_ltree_entry {
|
||||
struct rw_semaphore mutex;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_rename_entry - volume re-name description data structure.
|
||||
* @new_name_len: new volume name length
|
||||
* @new_name: new volume name
|
||||
* @remove: if not zero, this volume should be removed, not re-named
|
||||
* @desc: descriptor of the volume
|
||||
* @list: links re-name entries into a list
|
||||
*
|
||||
* This data structure is utilized in the multiple volume re-name code. Namely,
|
||||
* UBI first creates a list of &struct ubi_rename_entry objects from the
|
||||
* &struct ubi_rnvol_req request object, and then utilizes this list to do all
|
||||
* the job.
|
||||
*/
|
||||
struct ubi_rename_entry {
|
||||
int new_name_len;
|
||||
char new_name[UBI_VOL_NAME_MAX + 1];
|
||||
int remove;
|
||||
struct ubi_volume_desc *desc;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct ubi_volume_desc;
|
||||
|
||||
/**
|
||||
* struct ubi_fastmap_layout - in-memory fastmap data structure.
|
||||
* @e: PEBs used by the current fastmap
|
||||
* @to_be_tortured: if non-zero tortured this PEB
|
||||
* @used_blocks: number of used PEBs
|
||||
* @max_pool_size: maximal size of the user pool
|
||||
* @max_wl_pool_size: maximal size of the pool used by the WL sub-system
|
||||
*/
|
||||
struct ubi_fastmap_layout {
|
||||
struct ubi_wl_entry *e[UBI_FM_MAX_BLOCKS];
|
||||
int to_be_tortured[UBI_FM_MAX_BLOCKS];
|
||||
int used_blocks;
|
||||
int max_pool_size;
|
||||
int max_wl_pool_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_fm_pool - in-memory fastmap pool
|
||||
* @pebs: PEBs in this pool
|
||||
* @used: number of used PEBs
|
||||
* @size: total number of PEBs in this pool
|
||||
* @max_size: maximal size of the pool
|
||||
*
|
||||
* A pool gets filled with up to max_size.
|
||||
* If all PEBs within the pool are used a new fastmap will be written
|
||||
* to the flash and the pool gets refilled with empty PEBs.
|
||||
*
|
||||
*/
|
||||
struct ubi_fm_pool {
|
||||
int pebs[UBI_FM_MAX_POOL_SIZE];
|
||||
int used;
|
||||
int size;
|
||||
int max_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_volume - UBI volume description data structure.
|
||||
* @dev: device object to make use of the the Linux device model
|
||||
@ -160,8 +275,6 @@ struct ubi_volume_desc;
|
||||
* @upd_ebs: how many eraseblocks are expected to be updated
|
||||
* @ch_lnum: LEB number which is being changing by the atomic LEB change
|
||||
* operation
|
||||
* @ch_dtype: data persistency type which is being changing by the atomic LEB
|
||||
* change operation
|
||||
* @upd_bytes: how many bytes are expected to be received for volume update or
|
||||
* atomic LEB change
|
||||
* @upd_received: how many bytes were already received for volume update or
|
||||
@ -175,10 +288,7 @@ struct ubi_volume_desc;
|
||||
* @upd_marker: %1 if the update marker is set for this volume
|
||||
* @updating: %1 if the volume is being updated
|
||||
* @changing_leb: %1 if the atomic LEB change ioctl command is in progress
|
||||
*
|
||||
* @gluebi_desc: gluebi UBI volume descriptor
|
||||
* @gluebi_refcount: reference count of the gluebi MTD device
|
||||
* @gluebi_mtd: MTD device description object of the gluebi MTD device
|
||||
* @direct_writes: %1 if direct writes are enabled for this volume
|
||||
*
|
||||
* The @corrupted field indicates that the volume's contents is corrupted.
|
||||
* Since UBI protects only static volumes, this field is not relevant to
|
||||
@ -202,16 +312,19 @@ struct ubi_volume {
|
||||
int vol_type;
|
||||
int usable_leb_size;
|
||||
int used_ebs;
|
||||
#ifndef __UBOOT__
|
||||
int last_eb_bytes;
|
||||
#else
|
||||
u32 last_eb_bytes;
|
||||
#endif
|
||||
long long used_bytes;
|
||||
int alignment;
|
||||
int data_pad;
|
||||
int name_len;
|
||||
char name[UBI_VOL_NAME_MAX+1];
|
||||
char name[UBI_VOL_NAME_MAX + 1];
|
||||
|
||||
int upd_ebs;
|
||||
int ch_lnum;
|
||||
int ch_dtype;
|
||||
long long upd_bytes;
|
||||
long long upd_received;
|
||||
void *upd_buf;
|
||||
@ -222,22 +335,11 @@ struct ubi_volume {
|
||||
unsigned int upd_marker:1;
|
||||
unsigned int updating:1;
|
||||
unsigned int changing_leb:1;
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_GLUEBI
|
||||
/*
|
||||
* Gluebi-related stuff may be compiled out.
|
||||
* TODO: this should not be built into UBI but should be a separate
|
||||
* ubimtd driver which works on top of UBI and emulates MTD devices.
|
||||
*/
|
||||
struct ubi_volume_desc *gluebi_desc;
|
||||
int gluebi_refcount;
|
||||
struct mtd_info gluebi_mtd;
|
||||
#endif
|
||||
unsigned int direct_writes:1;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_volume_desc - descriptor of the UBI volume returned when it is
|
||||
* opened.
|
||||
* struct ubi_volume_desc - UBI volume descriptor returned when it is opened.
|
||||
* @vol: reference to the corresponding volume description object
|
||||
* @mode: open mode (%UBI_READONLY, %UBI_READWRITE, or %UBI_EXCLUSIVE)
|
||||
*/
|
||||
@ -248,6 +350,37 @@ struct ubi_volume_desc {
|
||||
|
||||
struct ubi_wl_entry;
|
||||
|
||||
/**
|
||||
* struct ubi_debug_info - debugging information for an UBI device.
|
||||
*
|
||||
* @chk_gen: if UBI general extra checks are enabled
|
||||
* @chk_io: if UBI I/O extra checks are enabled
|
||||
* @disable_bgt: disable the background task for testing purposes
|
||||
* @emulate_bitflips: emulate bit-flips for testing purposes
|
||||
* @emulate_io_failures: emulate write/erase failures for testing purposes
|
||||
* @dfs_dir_name: name of debugfs directory containing files of this UBI device
|
||||
* @dfs_dir: direntry object of the UBI device debugfs directory
|
||||
* @dfs_chk_gen: debugfs knob to enable UBI general extra checks
|
||||
* @dfs_chk_io: debugfs knob to enable UBI I/O extra checks
|
||||
* @dfs_disable_bgt: debugfs knob to disable the background task
|
||||
* @dfs_emulate_bitflips: debugfs knob to emulate bit-flips
|
||||
* @dfs_emulate_io_failures: debugfs knob to emulate write/erase failures
|
||||
*/
|
||||
struct ubi_debug_info {
|
||||
unsigned int chk_gen:1;
|
||||
unsigned int chk_io:1;
|
||||
unsigned int disable_bgt:1;
|
||||
unsigned int emulate_bitflips:1;
|
||||
unsigned int emulate_io_failures:1;
|
||||
char dfs_dir_name[UBI_DFS_DIR_LEN + 1];
|
||||
struct dentry *dfs_dir;
|
||||
struct dentry *dfs_chk_gen;
|
||||
struct dentry *dfs_chk_io;
|
||||
struct dentry *dfs_disable_bgt;
|
||||
struct dentry *dfs_emulate_bitflips;
|
||||
struct dentry *dfs_emulate_io_failures;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_device - UBI device description structure
|
||||
* @dev: UBI device object to use the the Linux device model
|
||||
@ -261,6 +394,7 @@ struct ubi_wl_entry;
|
||||
* @vol->readers, @vol->writers, @vol->exclusive,
|
||||
* @vol->ref_count, @vol->mapping and @vol->eba_tbl.
|
||||
* @ref_count: count of references on the UBI device
|
||||
* @image_seq: image sequence number recorded on EC headers
|
||||
*
|
||||
* @rsvd_pebs: count of reserved physical eraseblocks
|
||||
* @avail_pebs: count of available physical eraseblocks
|
||||
@ -269,12 +403,13 @@ struct ubi_wl_entry;
|
||||
* @beb_rsvd_level: normal level of PEBs reserved for bad PEB handling
|
||||
*
|
||||
* @autoresize_vol_id: ID of the volume which has to be auto-resized at the end
|
||||
* of UBI ititializetion
|
||||
* of UBI initialization
|
||||
* @vtbl_slots: how many slots are available in the volume table
|
||||
* @vtbl_size: size of the volume table in bytes
|
||||
* @vtbl: in-RAM volume table copy
|
||||
* @volumes_mutex: protects on-flash volume table and serializes volume
|
||||
* changes, like creation, deletion, update, resize
|
||||
* @device_mutex: protects on-flash volume table and serializes volume
|
||||
* creation, deletion, update, re-size, re-name and set
|
||||
* property
|
||||
*
|
||||
* @max_ec: current highest erase counter value
|
||||
* @mean_ec: current mean erase counter value
|
||||
@ -284,20 +419,33 @@ struct ubi_wl_entry;
|
||||
* @ltree: the lock tree
|
||||
* @alc_mutex: serializes "atomic LEB change" operations
|
||||
*
|
||||
* @fm_disabled: non-zero if fastmap is disabled (default)
|
||||
* @fm: in-memory data structure of the currently used fastmap
|
||||
* @fm_pool: in-memory data structure of the fastmap pool
|
||||
* @fm_wl_pool: in-memory data structure of the fastmap pool used by the WL
|
||||
* sub-system
|
||||
* @fm_mutex: serializes ubi_update_fastmap() and protects @fm_buf
|
||||
* @fm_buf: vmalloc()'d buffer which holds the raw fastmap
|
||||
* @fm_size: fastmap size in bytes
|
||||
* @fm_sem: allows ubi_update_fastmap() to block EBA table changes
|
||||
* @fm_work: fastmap work queue
|
||||
*
|
||||
* @used: RB-tree of used physical eraseblocks
|
||||
* @erroneous: RB-tree of erroneous used physical eraseblocks
|
||||
* @free: RB-tree of free physical eraseblocks
|
||||
* @free_count: Contains the number of elements in @free
|
||||
* @scrub: RB-tree of physical eraseblocks which need scrubbing
|
||||
* @prot: protection trees
|
||||
* @prot.pnum: protection tree indexed by physical eraseblock numbers
|
||||
* @prot.aec: protection tree indexed by absolute erase counter value
|
||||
* @wl_lock: protects the @used, @free, @prot, @lookuptbl, @abs_ec, @move_from,
|
||||
* @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works
|
||||
* fields
|
||||
* @pq: protection queue (contain physical eraseblocks which are temporarily
|
||||
* protected from the wear-leveling worker)
|
||||
* @pq_head: protection queue head
|
||||
* @wl_lock: protects the @used, @free, @pq, @pq_head, @lookuptbl, @move_from,
|
||||
* @move_to, @move_to_put @erase_pending, @wl_scheduled, @works,
|
||||
* @erroneous, and @erroneous_peb_count fields
|
||||
* @move_mutex: serializes eraseblock moves
|
||||
* @work_sem: synchronizes the WL worker with use tasks
|
||||
* @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
|
||||
* @abs_ec: absolute erase counter
|
||||
* @move_from: physical eraseblock from where the data is being moved
|
||||
* @move_to: physical eraseblock where the data is being moved to
|
||||
* @move_to_put: if the "to" PEB was put
|
||||
@ -310,30 +458,38 @@ struct ubi_wl_entry;
|
||||
* @flash_size: underlying MTD device size (in bytes)
|
||||
* @peb_count: count of physical eraseblocks on the MTD device
|
||||
* @peb_size: physical eraseblock size
|
||||
* @bad_peb_limit: top limit of expected bad physical eraseblocks
|
||||
* @bad_peb_count: count of bad physical eraseblocks
|
||||
* @good_peb_count: count of good physical eraseblocks
|
||||
* @corr_peb_count: count of corrupted physical eraseblocks (preserved and not
|
||||
* used by UBI)
|
||||
* @erroneous_peb_count: count of erroneous physical eraseblocks in @erroneous
|
||||
* @max_erroneous: maximum allowed amount of erroneous physical eraseblocks
|
||||
* @min_io_size: 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
|
||||
* eraseblocks
|
||||
* @ec_hdr_alsize: size of the EC header aligned to @hdrs_min_io_size
|
||||
* @vid_hdr_alsize: size of the VID header aligned to @hdrs_min_io_size
|
||||
* @vid_hdr_offset: starting offset of the volume identifier header (might be
|
||||
* unaligned)
|
||||
* unaligned)
|
||||
* @vid_hdr_aloffset: starting offset of the VID header aligned to
|
||||
* @hdrs_min_io_size
|
||||
* @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset
|
||||
* @bad_allowed: whether the MTD device admits of bad physical eraseblocks or
|
||||
* not
|
||||
* @nor_flash: non-zero if working on top of NOR flash
|
||||
* @max_write_size: maximum amount of bytes the underlying flash can write at a
|
||||
* time (MTD write buffer size)
|
||||
* @mtd: MTD device descriptor
|
||||
*
|
||||
* @peb_buf1: a buffer of PEB size used for different purposes
|
||||
* @peb_buf2: another buffer of PEB size used for different purposes
|
||||
* @buf_mutex: proptects @peb_buf1 and @peb_buf2
|
||||
* @dbg_peb_buf: buffer of PEB size used for debugging
|
||||
* @dbg_buf_mutex: proptects @dbg_peb_buf
|
||||
* @peb_buf: a buffer of PEB size used for different purposes
|
||||
* @buf_mutex: protects @peb_buf
|
||||
* @ckvol_mutex: serializes static volume checking when opening
|
||||
*
|
||||
* @dbg: debugging information for this UBI device
|
||||
*/
|
||||
struct ubi_device {
|
||||
struct cdev cdev;
|
||||
@ -344,42 +500,56 @@ struct ubi_device {
|
||||
struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT];
|
||||
spinlock_t volumes_lock;
|
||||
int ref_count;
|
||||
int image_seq;
|
||||
|
||||
int rsvd_pebs;
|
||||
int avail_pebs;
|
||||
int beb_rsvd_pebs;
|
||||
int beb_rsvd_level;
|
||||
int bad_peb_limit;
|
||||
|
||||
int autoresize_vol_id;
|
||||
int vtbl_slots;
|
||||
int vtbl_size;
|
||||
struct ubi_vtbl_record *vtbl;
|
||||
struct mutex volumes_mutex;
|
||||
struct mutex device_mutex;
|
||||
|
||||
int max_ec;
|
||||
/* TODO: mean_ec is not updated run-time, fix */
|
||||
/* Note, mean_ec is not updated run-time - should be fixed */
|
||||
int mean_ec;
|
||||
|
||||
/* EBA unit's stuff */
|
||||
/* EBA sub-system's stuff */
|
||||
unsigned long long global_sqnum;
|
||||
spinlock_t ltree_lock;
|
||||
struct rb_root ltree;
|
||||
struct mutex alc_mutex;
|
||||
|
||||
/* Wear-leveling unit's stuff */
|
||||
/* Fastmap stuff */
|
||||
int fm_disabled;
|
||||
struct ubi_fastmap_layout *fm;
|
||||
struct ubi_fm_pool fm_pool;
|
||||
struct ubi_fm_pool fm_wl_pool;
|
||||
struct rw_semaphore fm_sem;
|
||||
struct mutex fm_mutex;
|
||||
void *fm_buf;
|
||||
size_t fm_size;
|
||||
#ifndef __UBOOT__
|
||||
struct work_struct fm_work;
|
||||
#endif
|
||||
|
||||
/* Wear-leveling sub-system's stuff */
|
||||
struct rb_root used;
|
||||
struct rb_root erroneous;
|
||||
struct rb_root free;
|
||||
int free_count;
|
||||
struct rb_root scrub;
|
||||
struct {
|
||||
struct rb_root pnum;
|
||||
struct rb_root aec;
|
||||
} prot;
|
||||
struct list_head pq[UBI_PROT_QUEUE_LEN];
|
||||
int pq_head;
|
||||
spinlock_t wl_lock;
|
||||
struct mutex move_mutex;
|
||||
struct rw_semaphore work_sem;
|
||||
int wl_scheduled;
|
||||
struct ubi_wl_entry **lookuptbl;
|
||||
unsigned long long abs_ec;
|
||||
struct ubi_wl_entry *move_from;
|
||||
struct ubi_wl_entry *move_to;
|
||||
int move_to_put;
|
||||
@ -389,12 +559,15 @@ struct ubi_device {
|
||||
int thread_enabled;
|
||||
char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2];
|
||||
|
||||
/* I/O unit's stuff */
|
||||
/* I/O sub-system's stuff */
|
||||
long long flash_size;
|
||||
int peb_count;
|
||||
int peb_size;
|
||||
int bad_peb_count;
|
||||
int good_peb_count;
|
||||
int corr_peb_count;
|
||||
int erroneous_peb_count;
|
||||
int max_erroneous;
|
||||
int min_io_size;
|
||||
int hdrs_min_io_size;
|
||||
int ro_mode;
|
||||
@ -405,35 +578,195 @@ struct ubi_device {
|
||||
int vid_hdr_offset;
|
||||
int vid_hdr_aloffset;
|
||||
int vid_hdr_shift;
|
||||
int bad_allowed;
|
||||
unsigned int bad_allowed:1;
|
||||
unsigned int nor_flash:1;
|
||||
int max_write_size;
|
||||
struct mtd_info *mtd;
|
||||
|
||||
void *peb_buf1;
|
||||
void *peb_buf2;
|
||||
void *peb_buf;
|
||||
struct mutex buf_mutex;
|
||||
struct mutex ckvol_mutex;
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG
|
||||
void *dbg_peb_buf;
|
||||
struct mutex dbg_buf_mutex;
|
||||
#endif
|
||||
|
||||
struct ubi_debug_info dbg;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_ainf_peb - attach information about a physical eraseblock.
|
||||
* @ec: erase counter (%UBI_UNKNOWN if it is unknown)
|
||||
* @pnum: physical eraseblock number
|
||||
* @vol_id: ID of the volume this LEB belongs to
|
||||
* @lnum: logical eraseblock number
|
||||
* @scrub: if this physical eraseblock needs scrubbing
|
||||
* @copy_flag: this LEB is a copy (@copy_flag is set in VID header of this LEB)
|
||||
* @sqnum: sequence number
|
||||
* @u: unions RB-tree or @list links
|
||||
* @u.rb: link in the per-volume RB-tree of &struct ubi_ainf_peb objects
|
||||
* @u.list: link in one of the eraseblock lists
|
||||
*
|
||||
* One object of this type is allocated for each physical eraseblock when
|
||||
* attaching an MTD device. Note, if this PEB does not belong to any LEB /
|
||||
* volume, the @vol_id and @lnum fields are initialized to %UBI_UNKNOWN.
|
||||
*/
|
||||
struct ubi_ainf_peb {
|
||||
int ec;
|
||||
int pnum;
|
||||
int vol_id;
|
||||
int lnum;
|
||||
unsigned int scrub:1;
|
||||
unsigned int copy_flag:1;
|
||||
unsigned long long sqnum;
|
||||
union {
|
||||
struct rb_node rb;
|
||||
struct list_head list;
|
||||
} u;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_ainf_volume - attaching information about a volume.
|
||||
* @vol_id: volume ID
|
||||
* @highest_lnum: highest logical eraseblock number in this volume
|
||||
* @leb_count: number of logical eraseblocks in this volume
|
||||
* @vol_type: volume type
|
||||
* @used_ebs: number of used logical eraseblocks in this volume (only for
|
||||
* static volumes)
|
||||
* @last_data_size: amount of data in the last logical eraseblock of this
|
||||
* volume (always equivalent to the usable logical eraseblock
|
||||
* size in case of dynamic volumes)
|
||||
* @data_pad: how many bytes at the end of logical eraseblocks of this volume
|
||||
* are not used (due to volume alignment)
|
||||
* @compat: compatibility flags of this volume
|
||||
* @rb: link in the volume RB-tree
|
||||
* @root: root of the RB-tree containing all the eraseblock belonging to this
|
||||
* volume (&struct ubi_ainf_peb objects)
|
||||
*
|
||||
* One object of this type is allocated for each volume when attaching an MTD
|
||||
* device.
|
||||
*/
|
||||
struct ubi_ainf_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_attach_info - MTD device attaching information.
|
||||
* @volumes: root of the volume RB-tree
|
||||
* @corr: list of corrupted physical eraseblocks
|
||||
* @free: list of free physical eraseblocks
|
||||
* @erase: list of physical eraseblocks which have to be erased
|
||||
* @alien: list of physical eraseblocks which should not be used by UBI (e.g.,
|
||||
* those belonging to "preserve"-compatible internal volumes)
|
||||
* @corr_peb_count: count of PEBs in the @corr list
|
||||
* @empty_peb_count: count of PEBs which are presumably empty (contain only
|
||||
* 0xFF bytes)
|
||||
* @alien_peb_count: count of PEBs in the @alien list
|
||||
* @bad_peb_count: count of bad physical eraseblocks
|
||||
* @maybe_bad_peb_count: count of bad physical eraseblocks which are not marked
|
||||
* as bad yet, but which look like bad
|
||||
* @vols_found: number of volumes found
|
||||
* @highest_vol_id: highest volume ID
|
||||
* @is_empty: flag indicating whether the MTD device is empty or not
|
||||
* @min_ec: lowest erase counter value
|
||||
* @max_ec: highest erase counter value
|
||||
* @max_sqnum: highest sequence number 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
|
||||
* @aeb_slab_cache: slab cache for &struct ubi_ainf_peb objects
|
||||
*
|
||||
* This data structure contains the result of attaching an MTD device and may
|
||||
* be used by other UBI sub-systems to build final UBI data structures, further
|
||||
* error-recovery and so on.
|
||||
*/
|
||||
struct ubi_attach_info {
|
||||
struct rb_root volumes;
|
||||
struct list_head corr;
|
||||
struct list_head free;
|
||||
struct list_head erase;
|
||||
struct list_head alien;
|
||||
int corr_peb_count;
|
||||
int empty_peb_count;
|
||||
int alien_peb_count;
|
||||
int bad_peb_count;
|
||||
int maybe_bad_peb_count;
|
||||
int vols_found;
|
||||
int highest_vol_id;
|
||||
int is_empty;
|
||||
int min_ec;
|
||||
int max_ec;
|
||||
unsigned long long max_sqnum;
|
||||
int mean_ec;
|
||||
uint64_t ec_sum;
|
||||
int ec_count;
|
||||
struct kmem_cache *aeb_slab_cache;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_work - UBI work description data structure.
|
||||
* @list: a link in the list of pending works
|
||||
* @func: worker function
|
||||
* @e: physical eraseblock to erase
|
||||
* @vol_id: the volume ID on which this erasure is being performed
|
||||
* @lnum: the logical eraseblock number
|
||||
* @torture: if the physical eraseblock has to be tortured
|
||||
* @anchor: produce a anchor PEB to by used by fastmap
|
||||
*
|
||||
* The @func pointer points to the worker function. If the @cancel argument is
|
||||
* not zero, the worker has to free the resources and exit immediately. The
|
||||
* worker has to return zero in case of success and a negative error code in
|
||||
* case of failure.
|
||||
*/
|
||||
struct ubi_work {
|
||||
struct list_head list;
|
||||
int (*func)(struct ubi_device *ubi, struct ubi_work *wrk, int cancel);
|
||||
/* The below fields are only relevant to erasure works */
|
||||
struct ubi_wl_entry *e;
|
||||
int vol_id;
|
||||
int lnum;
|
||||
int torture;
|
||||
int anchor;
|
||||
};
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
extern struct kmem_cache *ubi_wl_entry_slab;
|
||||
extern struct file_operations ubi_ctrl_cdev_operations;
|
||||
extern struct file_operations ubi_cdev_operations;
|
||||
extern struct file_operations ubi_vol_cdev_operations;
|
||||
extern const struct file_operations ubi_ctrl_cdev_operations;
|
||||
extern const struct file_operations ubi_cdev_operations;
|
||||
extern const struct file_operations ubi_vol_cdev_operations;
|
||||
extern struct class *ubi_class;
|
||||
extern struct mutex ubi_devices_mutex;
|
||||
extern struct blocking_notifier_head ubi_notifiers;
|
||||
|
||||
/* attach.c */
|
||||
int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum,
|
||||
int ec, const struct ubi_vid_hdr *vid_hdr, int bitflips);
|
||||
struct ubi_ainf_volume *ubi_find_av(const struct ubi_attach_info *ai,
|
||||
int vol_id);
|
||||
void ubi_remove_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av);
|
||||
struct ubi_ainf_peb *ubi_early_get_peb(struct ubi_device *ubi,
|
||||
struct ubi_attach_info *ai);
|
||||
int ubi_attach(struct ubi_device *ubi, int force_scan);
|
||||
void ubi_destroy_ai(struct ubi_attach_info *ai);
|
||||
|
||||
/* vtbl.c */
|
||||
int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
|
||||
struct ubi_vtbl_record *vtbl_rec);
|
||||
int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si);
|
||||
int ubi_vtbl_rename_volumes(struct ubi_device *ubi,
|
||||
struct list_head *rename_list);
|
||||
int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_attach_info *ai);
|
||||
|
||||
/* vmt.c */
|
||||
int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req);
|
||||
int ubi_remove_volume(struct ubi_volume_desc *desc);
|
||||
int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl);
|
||||
int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs);
|
||||
int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list);
|
||||
int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol);
|
||||
void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol);
|
||||
|
||||
@ -448,9 +781,12 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
const void __user *buf, int count);
|
||||
|
||||
/* misc.c */
|
||||
int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length);
|
||||
int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf,
|
||||
int length);
|
||||
int ubi_check_volume(struct ubi_device *ubi, int vol_id);
|
||||
void ubi_update_reserved(struct ubi_device *ubi);
|
||||
void ubi_calculate_reserved(struct ubi_device *ubi);
|
||||
int ubi_check_pattern(const void *buf, uint8_t patt, int size);
|
||||
|
||||
/* gluebi.c */
|
||||
#ifdef CONFIG_MTD_UBI_GLUEBI
|
||||
@ -474,25 +810,33 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
|
||||
void *buf, int offset, int len, int check);
|
||||
int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
|
||||
const void *buf, int offset, int len, int dtype);
|
||||
const void *buf, int offset, int len);
|
||||
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 lnum, const void *buf, int len, int used_ebs);
|
||||
int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
int lnum, const void *buf, int len, int dtype);
|
||||
int lnum, const void *buf, int len);
|
||||
int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
||||
struct ubi_vid_hdr *vid_hdr);
|
||||
int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si);
|
||||
void ubi_eba_close(const struct ubi_device *ubi);
|
||||
int ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai);
|
||||
unsigned long long ubi_next_sqnum(struct ubi_device *ubi);
|
||||
int self_check_eba(struct ubi_device *ubi, struct ubi_attach_info *ai_fastmap,
|
||||
struct ubi_attach_info *ai_scan);
|
||||
|
||||
/* wl.c */
|
||||
int ubi_wl_get_peb(struct ubi_device *ubi, int dtype);
|
||||
int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture);
|
||||
int ubi_wl_flush(struct ubi_device *ubi);
|
||||
int ubi_wl_get_peb(struct ubi_device *ubi);
|
||||
int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum,
|
||||
int pnum, int torture);
|
||||
int ubi_wl_flush(struct ubi_device *ubi, int vol_id, int lnum);
|
||||
int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum);
|
||||
int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si);
|
||||
int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai);
|
||||
void ubi_wl_close(struct ubi_device *ubi);
|
||||
int ubi_thread(void *u);
|
||||
struct ubi_wl_entry *ubi_wl_get_fm_peb(struct ubi_device *ubi, int anchor);
|
||||
int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *used_e,
|
||||
int lnum, int torture);
|
||||
int ubi_is_erase_work(struct ubi_work *wrk);
|
||||
void ubi_refill_pools(struct ubi_device *ubi);
|
||||
int ubi_ensure_anchor_pebs(struct ubi_device *ubi);
|
||||
|
||||
/* io.c */
|
||||
int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
|
||||
@ -512,16 +856,37 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
|
||||
struct ubi_vid_hdr *vid_hdr);
|
||||
|
||||
/* build.c */
|
||||
int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset);
|
||||
int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
|
||||
int vid_hdr_offset, int max_beb_per1024);
|
||||
int ubi_detach_mtd_dev(int ubi_num, int anyway);
|
||||
struct ubi_device *ubi_get_device(int ubi_num);
|
||||
void ubi_put_device(struct ubi_device *ubi);
|
||||
struct ubi_device *ubi_get_by_major(int major);
|
||||
int ubi_major2num(int major);
|
||||
int ubi_volume_notify(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
int ntype);
|
||||
int ubi_notify_all(struct ubi_device *ubi, int ntype,
|
||||
struct notifier_block *nb);
|
||||
int ubi_enumerate_volumes(struct notifier_block *nb);
|
||||
void ubi_free_internal_volumes(struct ubi_device *ubi);
|
||||
|
||||
/* kapi.c */
|
||||
void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di);
|
||||
void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
struct ubi_volume_info *vi);
|
||||
/* scan.c */
|
||||
int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
|
||||
int pnum, const struct ubi_vid_hdr *vid_hdr);
|
||||
|
||||
/* fastmap.c */
|
||||
size_t ubi_calc_fm_size(struct ubi_device *ubi);
|
||||
int ubi_update_fastmap(struct ubi_device *ubi);
|
||||
int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
||||
int fm_anchor);
|
||||
|
||||
/*
|
||||
* ubi_rb_for_each_entry - walk an RB-tree.
|
||||
* @rb: a pointer to type 'struct rb_node' to to use as a loop counter
|
||||
* @rb: a pointer to type 'struct rb_node' 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
|
||||
@ -530,7 +895,23 @@ int ubi_major2num(int major);
|
||||
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))
|
||||
rb = rb_next(rb), \
|
||||
pos = (rb ? container_of(rb, typeof(*pos), member) : NULL))
|
||||
|
||||
/*
|
||||
* ubi_move_aeb_to_list - move a PEB from the volume tree to a list.
|
||||
*
|
||||
* @av: volume attaching information
|
||||
* @aeb: attaching eraseblock information
|
||||
* @list: the list to move to
|
||||
*/
|
||||
static inline void ubi_move_aeb_to_list(struct ubi_ainf_volume *av,
|
||||
struct ubi_ainf_peb *aeb,
|
||||
struct list_head *list)
|
||||
{
|
||||
rb_erase(&aeb->u.rb, &av->root);
|
||||
list_add_tail(&aeb->u.list, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_zalloc_vid_hdr - allocate a volume identifier header object.
|
||||
@ -606,6 +987,7 @@ static inline void ubi_ro_mode(struct ubi_device *ubi)
|
||||
if (!ubi->ro_mode) {
|
||||
ubi->ro_mode = 1;
|
||||
ubi_warn("switch to read-only mode");
|
||||
dump_stack();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,13 +26,16 @@
|
||||
* transaction with a roll-back capability.
|
||||
*/
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#include <linux/err.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/div64.h>
|
||||
#endif
|
||||
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/uaccess.h>
|
||||
#else
|
||||
#include <div64.h>
|
||||
#include <ubi_uboot.h>
|
||||
#endif
|
||||
#include <linux/err.h>
|
||||
#include <linux/math64.h>
|
||||
|
||||
#include "ubi.h"
|
||||
|
||||
/**
|
||||
@ -48,22 +51,21 @@ static int set_update_marker(struct ubi_device *ubi, struct ubi_volume *vol)
|
||||
int err;
|
||||
struct ubi_vtbl_record vtbl_rec;
|
||||
|
||||
dbg_msg("set update marker for volume %d", vol->vol_id);
|
||||
dbg_gen("set update marker for volume %d", vol->vol_id);
|
||||
|
||||
if (vol->upd_marker) {
|
||||
ubi_assert(ubi->vtbl[vol->vol_id].upd_marker);
|
||||
dbg_msg("already set");
|
||||
dbg_gen("already set");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id],
|
||||
sizeof(struct ubi_vtbl_record));
|
||||
vtbl_rec = ubi->vtbl[vol->vol_id];
|
||||
vtbl_rec.upd_marker = 1;
|
||||
|
||||
mutex_lock(&ubi->volumes_mutex);
|
||||
mutex_lock(&ubi->device_mutex);
|
||||
err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec);
|
||||
mutex_unlock(&ubi->volumes_mutex);
|
||||
vol->upd_marker = 1;
|
||||
mutex_unlock(&ubi->device_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -81,31 +83,29 @@ static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
long long bytes)
|
||||
{
|
||||
int err;
|
||||
uint64_t tmp;
|
||||
struct ubi_vtbl_record vtbl_rec;
|
||||
|
||||
dbg_msg("clear update marker for volume %d", vol->vol_id);
|
||||
dbg_gen("clear update marker for volume %d", vol->vol_id);
|
||||
|
||||
memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id],
|
||||
sizeof(struct ubi_vtbl_record));
|
||||
vtbl_rec = ubi->vtbl[vol->vol_id];
|
||||
ubi_assert(vol->upd_marker && vtbl_rec.upd_marker);
|
||||
vtbl_rec.upd_marker = 0;
|
||||
|
||||
if (vol->vol_type == UBI_STATIC_VOLUME) {
|
||||
vol->corrupted = 0;
|
||||
vol->used_bytes = tmp = bytes;
|
||||
vol->last_eb_bytes = do_div(tmp, vol->usable_leb_size);
|
||||
vol->used_ebs = tmp;
|
||||
vol->used_bytes = bytes;
|
||||
vol->used_ebs = div_u64_rem(bytes, vol->usable_leb_size,
|
||||
&vol->last_eb_bytes);
|
||||
if (vol->last_eb_bytes)
|
||||
vol->used_ebs += 1;
|
||||
else
|
||||
vol->last_eb_bytes = vol->usable_leb_size;
|
||||
}
|
||||
|
||||
mutex_lock(&ubi->volumes_mutex);
|
||||
mutex_lock(&ubi->device_mutex);
|
||||
err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec);
|
||||
mutex_unlock(&ubi->volumes_mutex);
|
||||
vol->upd_marker = 0;
|
||||
mutex_unlock(&ubi->device_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -123,9 +123,8 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
long long bytes)
|
||||
{
|
||||
int i, err;
|
||||
uint64_t tmp;
|
||||
|
||||
dbg_msg("start update of volume %d, %llu bytes", vol->vol_id, bytes);
|
||||
dbg_gen("start update of volume %d, %llu bytes", vol->vol_id, bytes);
|
||||
ubi_assert(!vol->updating && !vol->changing_leb);
|
||||
vol->updating = 1;
|
||||
|
||||
@ -141,21 +140,23 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
}
|
||||
|
||||
if (bytes == 0) {
|
||||
err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = clear_update_marker(ubi, vol, 0);
|
||||
if (err)
|
||||
return err;
|
||||
err = ubi_wl_flush(ubi);
|
||||
if (!err)
|
||||
vol->updating = 0;
|
||||
vol->updating = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
vol->upd_buf = vmalloc(ubi->leb_size);
|
||||
if (!vol->upd_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
tmp = bytes;
|
||||
vol->upd_ebs = !!do_div(tmp, vol->usable_leb_size);
|
||||
vol->upd_ebs += tmp;
|
||||
vol->upd_ebs = div_u64(bytes + vol->usable_leb_size - 1,
|
||||
vol->usable_leb_size);
|
||||
vol->upd_bytes = bytes;
|
||||
vol->upd_received = 0;
|
||||
return 0;
|
||||
@ -175,17 +176,15 @@ int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
{
|
||||
ubi_assert(!vol->updating && !vol->changing_leb);
|
||||
|
||||
dbg_msg("start changing LEB %d:%d, %u bytes",
|
||||
dbg_gen("start changing LEB %d:%d, %u bytes",
|
||||
vol->vol_id, req->lnum, req->bytes);
|
||||
if (req->bytes == 0)
|
||||
return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0,
|
||||
req->dtype);
|
||||
return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0);
|
||||
|
||||
vol->upd_bytes = req->bytes;
|
||||
vol->upd_received = 0;
|
||||
vol->changing_leb = 1;
|
||||
vol->ch_lnum = req->lnum;
|
||||
vol->ch_dtype = req->dtype;
|
||||
|
||||
vol->upd_buf = vmalloc(req->bytes);
|
||||
if (!vol->upd_buf)
|
||||
@ -234,11 +233,11 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
|
||||
memset(buf + len, 0xFF, l - len);
|
||||
len = ubi_calc_data_len(ubi, buf, l);
|
||||
if (len == 0) {
|
||||
dbg_msg("all %d bytes contain 0xFF - skip", len);
|
||||
dbg_gen("all %d bytes contain 0xFF - skip", len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, UBI_UNKNOWN);
|
||||
err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len);
|
||||
} else {
|
||||
/*
|
||||
* When writing static volume, and this is the last logical
|
||||
@ -250,8 +249,7 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
|
||||
* contain zeros, not random trash.
|
||||
*/
|
||||
memset(buf + len, 0, vol->usable_leb_size - len);
|
||||
err = ubi_eba_write_leb_st(ubi, vol, lnum, buf, len,
|
||||
UBI_UNKNOWN, used_ebs);
|
||||
err = ubi_eba_write_leb_st(ubi, vol, lnum, buf, len, used_ebs);
|
||||
}
|
||||
|
||||
return err;
|
||||
@ -259,6 +257,7 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
|
||||
|
||||
/**
|
||||
* ubi_more_update_data - write more update data.
|
||||
* @ubi: UBI device description object
|
||||
* @vol: volume description object
|
||||
* @buf: write data (user-space memory buffer)
|
||||
* @count: how much bytes to write
|
||||
@ -272,19 +271,20 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
|
||||
int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
const void __user *buf, int count)
|
||||
{
|
||||
uint64_t tmp;
|
||||
#ifndef __UBOOT__
|
||||
int lnum, offs, err = 0, len, to_write = count;
|
||||
#else
|
||||
int lnum, err = 0, len, to_write = count;
|
||||
u32 offs;
|
||||
#endif
|
||||
|
||||
dbg_msg("write %d of %lld bytes, %lld already passed",
|
||||
dbg_gen("write %d of %lld bytes, %lld already passed",
|
||||
count, vol->upd_bytes, vol->upd_received);
|
||||
|
||||
if (ubi->ro_mode)
|
||||
return -EROFS;
|
||||
|
||||
tmp = vol->upd_received;
|
||||
offs = do_div(tmp, vol->usable_leb_size);
|
||||
lnum = tmp;
|
||||
|
||||
lnum = div_u64_rem(vol->upd_received, vol->usable_leb_size, &offs);
|
||||
if (vol->upd_received + count > vol->upd_bytes)
|
||||
to_write = count = vol->upd_bytes - vol->upd_received;
|
||||
|
||||
@ -359,16 +359,16 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
|
||||
ubi_assert(vol->upd_received <= vol->upd_bytes);
|
||||
if (vol->upd_received == vol->upd_bytes) {
|
||||
err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL);
|
||||
if (err)
|
||||
return err;
|
||||
/* The update is finished, clear the update marker */
|
||||
err = clear_update_marker(ubi, vol, vol->upd_bytes);
|
||||
if (err)
|
||||
return err;
|
||||
err = ubi_wl_flush(ubi);
|
||||
if (err == 0) {
|
||||
vol->updating = 0;
|
||||
err = to_write;
|
||||
vfree(vol->upd_buf);
|
||||
}
|
||||
vol->updating = 0;
|
||||
err = to_write;
|
||||
vfree(vol->upd_buf);
|
||||
}
|
||||
|
||||
return err;
|
||||
@ -376,6 +376,7 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
|
||||
/**
|
||||
* ubi_more_leb_change_data - accept more data for atomic LEB change.
|
||||
* @ubi: UBI device description object
|
||||
* @vol: volume description object
|
||||
* @buf: write data (user-space memory buffer)
|
||||
* @count: how much bytes to write
|
||||
@ -392,7 +393,7 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
{
|
||||
int err;
|
||||
|
||||
dbg_msg("write %d of %lld bytes, %lld already passed",
|
||||
dbg_gen("write %d of %lld bytes, %lld already passed",
|
||||
count, vol->upd_bytes, vol->upd_received);
|
||||
|
||||
if (ubi->ro_mode)
|
||||
@ -410,10 +411,11 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
if (vol->upd_received == vol->upd_bytes) {
|
||||
int len = ALIGN((int)vol->upd_bytes, ubi->min_io_size);
|
||||
|
||||
memset(vol->upd_buf + vol->upd_bytes, 0xFF, len - vol->upd_bytes);
|
||||
memset(vol->upd_buf + vol->upd_bytes, 0xFF,
|
||||
len - vol->upd_bytes);
|
||||
len = ubi_calc_data_len(ubi, vol->upd_buf, len);
|
||||
err = ubi_eba_atomic_leb_change(ubi, vol, vol->ch_lnum,
|
||||
vol->upd_buf, len, UBI_UNKNOWN);
|
||||
vol->upd_buf, len);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
@ -11,21 +11,22 @@
|
||||
* resizing.
|
||||
*/
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/err.h>
|
||||
#include <asm/div64.h>
|
||||
#endif
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#else
|
||||
#include <div64.h>
|
||||
#include <ubi_uboot.h>
|
||||
#endif
|
||||
#include <linux/math64.h>
|
||||
|
||||
#include "ubi.h"
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
|
||||
static void paranoid_check_volumes(struct ubi_device *ubi);
|
||||
#else
|
||||
#define paranoid_check_volumes(ubi)
|
||||
#endif
|
||||
static int self_check_volumes(struct ubi_device *ubi);
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#ifndef __UBOOT__
|
||||
static ssize_t vol_attribute_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
@ -121,10 +122,11 @@ static void vol_release(struct device *dev)
|
||||
{
|
||||
struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
|
||||
|
||||
kfree(vol->eba_tbl);
|
||||
kfree(vol);
|
||||
}
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* volume_sysfs_init - initialize sysfs for new volume.
|
||||
* @ubi: UBI device description object
|
||||
@ -193,14 +195,13 @@ static void volume_sysfs_close(struct ubi_volume *vol)
|
||||
* %UBI_VOL_NUM_AUTO, this function automatically assign ID to the new volume
|
||||
* and saves it in @req->vol_id. Returns zero in case of success and a negative
|
||||
* error code in case of failure. Note, the caller has to have the
|
||||
* @ubi->volumes_mutex locked.
|
||||
* @ubi->device_mutex locked.
|
||||
*/
|
||||
int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
|
||||
{
|
||||
int i, err, vol_id = req->vol_id, dont_free = 0;
|
||||
int i, err, vol_id = req->vol_id, do_free = 1;
|
||||
struct ubi_volume *vol;
|
||||
struct ubi_vtbl_record vtbl_rec;
|
||||
uint64_t bytes;
|
||||
dev_t dev;
|
||||
|
||||
if (ubi->ro_mode)
|
||||
@ -213,7 +214,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
if (vol_id == UBI_VOL_NUM_AUTO) {
|
||||
/* Find unused volume ID */
|
||||
dbg_msg("search for vacant volume ID");
|
||||
dbg_gen("search for vacant volume ID");
|
||||
for (i = 0; i < ubi->vtbl_slots; i++)
|
||||
if (!ubi->volumes[i]) {
|
||||
vol_id = i;
|
||||
@ -221,21 +222,21 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
|
||||
}
|
||||
|
||||
if (vol_id == UBI_VOL_NUM_AUTO) {
|
||||
dbg_err("out of volume IDs");
|
||||
ubi_err("out of volume IDs");
|
||||
err = -ENFILE;
|
||||
goto out_unlock;
|
||||
}
|
||||
req->vol_id = vol_id;
|
||||
}
|
||||
|
||||
dbg_msg("volume ID %d, %llu bytes, type %d, name %s",
|
||||
vol_id, (unsigned long long)req->bytes,
|
||||
dbg_gen("create device %d, volume %d, %llu bytes, type %d, name %s",
|
||||
ubi->ubi_num, vol_id, (unsigned long long)req->bytes,
|
||||
(int)req->vol_type, req->name);
|
||||
|
||||
/* Ensure that this volume does not exist */
|
||||
err = -EEXIST;
|
||||
if (ubi->volumes[vol_id]) {
|
||||
dbg_err("volume %d already exists", vol_id);
|
||||
ubi_err("volume %d already exists", vol_id);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
@ -244,20 +245,21 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
|
||||
if (ubi->volumes[i] &&
|
||||
ubi->volumes[i]->name_len == req->name_len &&
|
||||
!strcmp(ubi->volumes[i]->name, req->name)) {
|
||||
dbg_err("volume \"%s\" exists (ID %d)", req->name, i);
|
||||
ubi_err("volume \"%s\" exists (ID %d)", req->name, i);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* Calculate how many eraseblocks are requested */
|
||||
vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment;
|
||||
bytes = req->bytes;
|
||||
if (do_div(bytes, vol->usable_leb_size))
|
||||
vol->reserved_pebs = 1;
|
||||
vol->reserved_pebs += bytes;
|
||||
vol->reserved_pebs += div_u64(req->bytes + vol->usable_leb_size - 1,
|
||||
vol->usable_leb_size);
|
||||
|
||||
/* Reserve physical eraseblocks */
|
||||
if (vol->reserved_pebs > ubi->avail_pebs) {
|
||||
dbg_err("not enough PEBs, only %d available", ubi->avail_pebs);
|
||||
ubi_err("not enough PEBs, only %d available", ubi->avail_pebs);
|
||||
if (ubi->corr_peb_count)
|
||||
ubi_err("%d PEBs are corrupted and not used",
|
||||
ubi->corr_peb_count);
|
||||
err = -ENOSPC;
|
||||
goto out_unlock;
|
||||
}
|
||||
@ -270,14 +272,14 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
|
||||
vol->data_pad = ubi->leb_size % vol->alignment;
|
||||
vol->vol_type = req->vol_type;
|
||||
vol->name_len = req->name_len;
|
||||
memcpy(vol->name, req->name, vol->name_len + 1);
|
||||
memcpy(vol->name, req->name, vol->name_len);
|
||||
vol->ubi = ubi;
|
||||
|
||||
/*
|
||||
* Finish all pending erases because there may be some LEBs belonging
|
||||
* to the same volume ID.
|
||||
*/
|
||||
err = ubi_wl_flush(ubi);
|
||||
err = ubi_wl_flush(ubi, vol_id, UBI_ALL);
|
||||
if (err)
|
||||
goto out_acc;
|
||||
|
||||
@ -296,10 +298,10 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
|
||||
vol->used_bytes =
|
||||
(long long)vol->used_ebs * vol->usable_leb_size;
|
||||
} else {
|
||||
bytes = vol->used_bytes;
|
||||
vol->last_eb_bytes = do_div(bytes, vol->usable_leb_size);
|
||||
vol->used_ebs = bytes;
|
||||
if (vol->last_eb_bytes)
|
||||
vol->used_ebs = div_u64_rem(vol->used_bytes,
|
||||
vol->usable_leb_size,
|
||||
&vol->last_eb_bytes);
|
||||
if (vol->last_eb_bytes != 0)
|
||||
vol->used_ebs += 1;
|
||||
else
|
||||
vol->last_eb_bytes = vol->usable_leb_size;
|
||||
@ -315,20 +317,16 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
|
||||
goto out_mapping;
|
||||
}
|
||||
|
||||
err = ubi_create_gluebi(ubi, vol);
|
||||
if (err)
|
||||
goto out_cdev;
|
||||
|
||||
vol->dev.release = vol_release;
|
||||
vol->dev.parent = &ubi->dev;
|
||||
vol->dev.devt = dev;
|
||||
vol->dev.class = ubi_class;
|
||||
|
||||
sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id);
|
||||
dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id);
|
||||
err = device_register(&vol->dev);
|
||||
if (err) {
|
||||
ubi_err("cannot register device");
|
||||
goto out_gluebi;
|
||||
goto out_cdev;
|
||||
}
|
||||
|
||||
err = volume_sysfs_init(ubi, vol);
|
||||
@ -345,7 +343,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
|
||||
vtbl_rec.vol_type = UBI_VID_DYNAMIC;
|
||||
else
|
||||
vtbl_rec.vol_type = UBI_VID_STATIC;
|
||||
memcpy(vtbl_rec.name, vol->name, vol->name_len + 1);
|
||||
memcpy(vtbl_rec.name, vol->name, vol->name_len);
|
||||
|
||||
err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
|
||||
if (err)
|
||||
@ -356,39 +354,37 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
|
||||
ubi->vol_count += 1;
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
|
||||
paranoid_check_volumes(ubi);
|
||||
return 0;
|
||||
ubi_volume_notify(ubi, vol, UBI_VOLUME_ADDED);
|
||||
self_check_volumes(ubi);
|
||||
return err;
|
||||
|
||||
out_sysfs:
|
||||
/*
|
||||
* We have registered our device, we should not free the volume*
|
||||
* We have registered our device, we should not free the volume
|
||||
* description object in this function in case of an error - it is
|
||||
* freed by the release function.
|
||||
*
|
||||
* Get device reference to prevent the release function from being
|
||||
* called just after sysfs has been closed.
|
||||
*/
|
||||
dont_free = 1;
|
||||
do_free = 0;
|
||||
get_device(&vol->dev);
|
||||
volume_sysfs_close(vol);
|
||||
out_gluebi:
|
||||
if (ubi_destroy_gluebi(vol))
|
||||
dbg_err("cannot destroy gluebi for volume %d:%d",
|
||||
ubi->ubi_num, vol_id);
|
||||
out_cdev:
|
||||
cdev_del(&vol->cdev);
|
||||
out_mapping:
|
||||
kfree(vol->eba_tbl);
|
||||
if (do_free)
|
||||
kfree(vol->eba_tbl);
|
||||
out_acc:
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
ubi->rsvd_pebs -= vol->reserved_pebs;
|
||||
ubi->avail_pebs += vol->reserved_pebs;
|
||||
out_unlock:
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
if (dont_free)
|
||||
put_device(&vol->dev);
|
||||
else
|
||||
if (do_free)
|
||||
kfree(vol);
|
||||
else
|
||||
put_device(&vol->dev);
|
||||
ubi_err("cannot create volume %d, error %d", vol_id, err);
|
||||
return err;
|
||||
}
|
||||
@ -396,19 +392,20 @@ out_unlock:
|
||||
/**
|
||||
* ubi_remove_volume - remove volume.
|
||||
* @desc: volume descriptor
|
||||
* @no_vtbl: do not change volume table if not zero
|
||||
*
|
||||
* This function removes volume described by @desc. The volume has to be opened
|
||||
* in "exclusive" mode. Returns zero in case of success and a negative error
|
||||
* code in case of failure. The caller has to have the @ubi->volumes_mutex
|
||||
* code in case of failure. The caller has to have the @ubi->device_mutex
|
||||
* locked.
|
||||
*/
|
||||
int ubi_remove_volume(struct ubi_volume_desc *desc)
|
||||
int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
|
||||
{
|
||||
struct ubi_volume *vol = desc->vol;
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs;
|
||||
|
||||
dbg_msg("remove UBI volume %d", vol_id);
|
||||
dbg_gen("remove device %d, volume %d", ubi->ubi_num, vol_id);
|
||||
ubi_assert(desc->mode == UBI_EXCLUSIVE);
|
||||
ubi_assert(vol == ubi->volumes[vol_id]);
|
||||
|
||||
@ -427,13 +424,11 @@ int ubi_remove_volume(struct ubi_volume_desc *desc)
|
||||
ubi->volumes[vol_id] = NULL;
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
|
||||
err = ubi_destroy_gluebi(vol);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
err = ubi_change_vtbl_record(ubi, vol_id, NULL);
|
||||
if (err)
|
||||
goto out_err;
|
||||
if (!no_vtbl) {
|
||||
err = ubi_change_vtbl_record(ubi, vol_id, NULL);
|
||||
if (err)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
for (i = 0; i < vol->reserved_pebs; i++) {
|
||||
err = ubi_eba_unmap_leb(ubi, vol, i);
|
||||
@ -441,28 +436,21 @@ int ubi_remove_volume(struct ubi_volume_desc *desc)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
kfree(vol->eba_tbl);
|
||||
vol->eba_tbl = NULL;
|
||||
cdev_del(&vol->cdev);
|
||||
volume_sysfs_close(vol);
|
||||
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
ubi->rsvd_pebs -= reserved_pebs;
|
||||
ubi->avail_pebs += reserved_pebs;
|
||||
i = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs;
|
||||
if (i > 0) {
|
||||
i = ubi->avail_pebs >= i ? i : ubi->avail_pebs;
|
||||
ubi->avail_pebs -= i;
|
||||
ubi->rsvd_pebs += i;
|
||||
ubi->beb_rsvd_pebs += i;
|
||||
if (i > 0)
|
||||
ubi_msg("reserve more %d PEBs", i);
|
||||
}
|
||||
ubi_update_reserved(ubi);
|
||||
ubi->vol_count -= 1;
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
|
||||
paranoid_check_volumes(ubi);
|
||||
return 0;
|
||||
ubi_volume_notify(ubi, vol, UBI_VOLUME_REMOVED);
|
||||
if (!no_vtbl)
|
||||
self_check_volumes(ubi);
|
||||
|
||||
return err;
|
||||
|
||||
out_err:
|
||||
ubi_err("cannot remove volume %d, error %d", vol_id, err);
|
||||
@ -480,7 +468,7 @@ out_unlock:
|
||||
*
|
||||
* This function re-sizes the volume and returns zero in case of success, and a
|
||||
* negative error code in case of failure. The caller has to have the
|
||||
* @ubi->volumes_mutex locked.
|
||||
* @ubi->device_mutex locked.
|
||||
*/
|
||||
int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
|
||||
{
|
||||
@ -493,12 +481,12 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
|
||||
if (ubi->ro_mode)
|
||||
return -EROFS;
|
||||
|
||||
dbg_msg("re-size volume %d to from %d to %d PEBs",
|
||||
vol_id, vol->reserved_pebs, reserved_pebs);
|
||||
dbg_gen("re-size device %d, volume %d to from %d to %d PEBs",
|
||||
ubi->ubi_num, vol_id, vol->reserved_pebs, reserved_pebs);
|
||||
|
||||
if (vol->vol_type == UBI_STATIC_VOLUME &&
|
||||
reserved_pebs < vol->used_ebs) {
|
||||
dbg_err("too small size %d, %d LEBs contain data",
|
||||
ubi_err("too small size %d, %d LEBs contain data",
|
||||
reserved_pebs, vol->used_ebs);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -527,8 +515,11 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
|
||||
if (pebs > 0) {
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
if (pebs > ubi->avail_pebs) {
|
||||
dbg_err("not enough PEBs: requested %d, available %d",
|
||||
ubi_err("not enough PEBs: requested %d, available %d",
|
||||
pebs, ubi->avail_pebs);
|
||||
if (ubi->corr_peb_count)
|
||||
ubi_err("%d PEBs are corrupted and not used",
|
||||
ubi->corr_peb_count);
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
err = -ENOSPC;
|
||||
goto out_free;
|
||||
@ -543,7 +534,7 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
|
||||
}
|
||||
|
||||
/* Change volume table record */
|
||||
memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record));
|
||||
vtbl_rec = ubi->vtbl[vol_id];
|
||||
vtbl_rec.reserved_pebs = cpu_to_be32(reserved_pebs);
|
||||
err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
|
||||
if (err)
|
||||
@ -558,15 +549,7 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
ubi->rsvd_pebs += pebs;
|
||||
ubi->avail_pebs -= pebs;
|
||||
pebs = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs;
|
||||
if (pebs > 0) {
|
||||
pebs = ubi->avail_pebs >= pebs ? pebs : ubi->avail_pebs;
|
||||
ubi->avail_pebs -= pebs;
|
||||
ubi->rsvd_pebs += pebs;
|
||||
ubi->beb_rsvd_pebs += pebs;
|
||||
if (pebs > 0)
|
||||
ubi_msg("reserve more %d PEBs", pebs);
|
||||
}
|
||||
ubi_update_reserved(ubi);
|
||||
for (i = 0; i < reserved_pebs; i++)
|
||||
new_mapping[i] = vol->eba_tbl[i];
|
||||
kfree(vol->eba_tbl);
|
||||
@ -582,8 +565,9 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
|
||||
(long long)vol->used_ebs * vol->usable_leb_size;
|
||||
}
|
||||
|
||||
paranoid_check_volumes(ubi);
|
||||
return 0;
|
||||
ubi_volume_notify(ubi, vol, UBI_VOLUME_RESIZED);
|
||||
self_check_volumes(ubi);
|
||||
return err;
|
||||
|
||||
out_acc:
|
||||
if (pebs > 0) {
|
||||
@ -597,6 +581,45 @@ out_free:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_rename_volumes - re-name UBI volumes.
|
||||
* @ubi: UBI device description object
|
||||
* @rename_list: list of &struct ubi_rename_entry objects
|
||||
*
|
||||
* This function re-names or removes volumes specified in the re-name list.
|
||||
* Returns zero in case of success and a negative error code in case of
|
||||
* failure.
|
||||
*/
|
||||
int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list)
|
||||
{
|
||||
int err;
|
||||
struct ubi_rename_entry *re;
|
||||
|
||||
err = ubi_vtbl_rename_volumes(ubi, rename_list);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
list_for_each_entry(re, rename_list, list) {
|
||||
if (re->remove) {
|
||||
err = ubi_remove_volume(re->desc, 1);
|
||||
if (err)
|
||||
break;
|
||||
} else {
|
||||
struct ubi_volume *vol = re->desc->vol;
|
||||
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
vol->name_len = re->new_name_len;
|
||||
memcpy(vol->name, re->new_name, re->new_name_len + 1);
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
ubi_volume_notify(ubi, vol, UBI_VOLUME_RENAMED);
|
||||
}
|
||||
}
|
||||
|
||||
if (!err)
|
||||
self_check_volumes(ubi);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_add_volume - add volume.
|
||||
* @ubi: UBI device description object
|
||||
@ -611,8 +634,7 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol)
|
||||
int err, vol_id = vol->vol_id;
|
||||
dev_t dev;
|
||||
|
||||
dbg_msg("add volume %d", vol_id);
|
||||
ubi_dbg_dump_vol_info(vol);
|
||||
dbg_gen("add volume %d", vol_id);
|
||||
|
||||
/* Register character device for the volume */
|
||||
cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
|
||||
@ -625,32 +647,25 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ubi_create_gluebi(ubi, vol);
|
||||
if (err)
|
||||
goto out_cdev;
|
||||
|
||||
vol->dev.release = vol_release;
|
||||
vol->dev.parent = &ubi->dev;
|
||||
vol->dev.devt = dev;
|
||||
vol->dev.class = ubi_class;
|
||||
sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id);
|
||||
dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id);
|
||||
err = device_register(&vol->dev);
|
||||
if (err)
|
||||
goto out_gluebi;
|
||||
goto out_cdev;
|
||||
|
||||
err = volume_sysfs_init(ubi, vol);
|
||||
if (err) {
|
||||
cdev_del(&vol->cdev);
|
||||
err = ubi_destroy_gluebi(vol);
|
||||
volume_sysfs_close(vol);
|
||||
return err;
|
||||
}
|
||||
|
||||
paranoid_check_volumes(ubi);
|
||||
return 0;
|
||||
self_check_volumes(ubi);
|
||||
return err;
|
||||
|
||||
out_gluebi:
|
||||
err = ubi_destroy_gluebi(vol);
|
||||
out_cdev:
|
||||
cdev_del(&vol->cdev);
|
||||
return err;
|
||||
@ -666,22 +681,21 @@ out_cdev:
|
||||
*/
|
||||
void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol)
|
||||
{
|
||||
dbg_msg("free volume %d", vol->vol_id);
|
||||
dbg_gen("free volume %d", vol->vol_id);
|
||||
|
||||
ubi->volumes[vol->vol_id] = NULL;
|
||||
ubi_destroy_gluebi(vol);
|
||||
cdev_del(&vol->cdev);
|
||||
volume_sysfs_close(vol);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
|
||||
|
||||
/**
|
||||
* paranoid_check_volume - check volume information.
|
||||
* self_check_volume - check volume information.
|
||||
* @ubi: UBI device description object
|
||||
* @vol_id: volume ID
|
||||
*
|
||||
* Returns zero if volume is all right and a a negative error code if not.
|
||||
*/
|
||||
static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
|
||||
static int self_check_volume(struct ubi_device *ubi, int vol_id)
|
||||
{
|
||||
int idx = vol_id2idx(ubi, vol_id);
|
||||
int reserved_pebs, alignment, data_pad, vol_type, name_len, upd_marker;
|
||||
@ -699,16 +713,7 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
|
||||
goto fail;
|
||||
}
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vol->exclusive) {
|
||||
/*
|
||||
* The volume may be being created at the moment, do not check
|
||||
* it (e.g., it may be in the middle of ubi_create_volume().
|
||||
*/
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vol->reserved_pebs < 0 || vol->alignment < 0 || vol->data_pad < 0 ||
|
||||
@ -740,7 +745,7 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
|
||||
}
|
||||
|
||||
if (vol->upd_marker && vol->corrupted) {
|
||||
dbg_err("update marker and corrupted simultaneously");
|
||||
ubi_err("update marker and corrupted simultaneously");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -760,11 +765,6 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vol->name) {
|
||||
ubi_err("NULL volume name");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
n = strnlen(vol->name, vol->name_len + 1);
|
||||
if (n != vol->name_len) {
|
||||
ubi_err("bad name_len %lld", n);
|
||||
@ -818,31 +818,42 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
|
||||
|
||||
if (alignment != vol->alignment || data_pad != vol->data_pad ||
|
||||
upd_marker != vol->upd_marker || vol_type != vol->vol_type ||
|
||||
name_len!= vol->name_len || strncmp(name, vol->name, name_len)) {
|
||||
name_len != vol->name_len || strncmp(name, vol->name, name_len)) {
|
||||
ubi_err("volume info is different");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
return;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
ubi_err("paranoid check failed for volume %d", vol_id);
|
||||
ubi_dbg_dump_vol_info(vol);
|
||||
ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id);
|
||||
ubi_err("self-check failed for volume %d", vol_id);
|
||||
if (vol)
|
||||
ubi_dump_vol_info(vol);
|
||||
ubi_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id);
|
||||
dump_stack();
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
BUG();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* paranoid_check_volumes - check information about all volumes.
|
||||
* self_check_volumes - check information about all volumes.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* Returns zero if volumes are all right and a a negative error code if not.
|
||||
*/
|
||||
static void paranoid_check_volumes(struct ubi_device *ubi)
|
||||
static int self_check_volumes(struct ubi_device *ubi)
|
||||
{
|
||||
int i;
|
||||
int i, err = 0;
|
||||
|
||||
for (i = 0; i < ubi->vtbl_slots; i++)
|
||||
paranoid_check_volume(ubi, i);
|
||||
if (!ubi_dbg_chk_gen(ubi))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ubi->vtbl_slots; i++) {
|
||||
err = self_check_volume(ubi, i);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
@ -25,16 +25,15 @@
|
||||
* LEB 1. This scheme guarantees recoverability from unclean reboots.
|
||||
*
|
||||
* In this UBI implementation the on-flash volume table does not contain any
|
||||
* information about how many data static volumes contain. This information may
|
||||
* be found from the scanning data.
|
||||
* information about how much data static volumes contain.
|
||||
*
|
||||
* But it would still be beneficial to store this information in the volume
|
||||
* table. For example, suppose we have a static volume X, and all its physical
|
||||
* eraseblocks became bad for some reasons. Suppose we are attaching the
|
||||
* corresponding MTD device, the scanning has found no logical eraseblocks
|
||||
* corresponding MTD device, for some reason we find no logical eraseblocks
|
||||
* corresponding to 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. So we cannot alarm the user about this corruption.
|
||||
* eraseblocks went bad. So we cannot alarm the user properly.
|
||||
*
|
||||
* The volume table also stores so-called "update marker", which is used for
|
||||
* volume updates. Before updating the volume, the update marker is set, and
|
||||
@ -44,20 +43,20 @@
|
||||
* damaged.
|
||||
*/
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/div64.h>
|
||||
#else
|
||||
#include <ubi_uboot.h>
|
||||
#endif
|
||||
|
||||
#include <ubi_uboot.h>
|
||||
#include <linux/err.h>
|
||||
#include "ubi.h"
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
|
||||
static void paranoid_vtbl_check(const struct ubi_device *ubi);
|
||||
#else
|
||||
#define paranoid_vtbl_check(ubi)
|
||||
#endif
|
||||
static void self_vtbl_check(const struct ubi_device *ubi);
|
||||
|
||||
/* Empty volume table record */
|
||||
static struct ubi_vtbl_record empty_vtbl_record;
|
||||
@ -97,18 +96,68 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
|
||||
return err;
|
||||
|
||||
err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0,
|
||||
ubi->vtbl_size, UBI_LONGTERM);
|
||||
ubi->vtbl_size);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
paranoid_vtbl_check(ubi);
|
||||
self_vtbl_check(ubi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* vtbl_check - check if volume table is not corrupted and contains sensible
|
||||
* data.
|
||||
* ubi_vtbl_rename_volumes - rename UBI volumes in the volume table.
|
||||
* @ubi: UBI device description object
|
||||
* @rename_list: list of &struct ubi_rename_entry objects
|
||||
*
|
||||
* This function re-names multiple volumes specified in @req in the volume
|
||||
* table. Returns zero in case of success and a negative error code in case of
|
||||
* failure.
|
||||
*/
|
||||
int ubi_vtbl_rename_volumes(struct ubi_device *ubi,
|
||||
struct list_head *rename_list)
|
||||
{
|
||||
int i, err;
|
||||
struct ubi_rename_entry *re;
|
||||
struct ubi_volume *layout_vol;
|
||||
|
||||
list_for_each_entry(re, rename_list, list) {
|
||||
uint32_t crc;
|
||||
struct ubi_volume *vol = re->desc->vol;
|
||||
struct ubi_vtbl_record *vtbl_rec = &ubi->vtbl[vol->vol_id];
|
||||
|
||||
if (re->remove) {
|
||||
memcpy(vtbl_rec, &empty_vtbl_record,
|
||||
sizeof(struct ubi_vtbl_record));
|
||||
continue;
|
||||
}
|
||||
|
||||
vtbl_rec->name_len = cpu_to_be16(re->new_name_len);
|
||||
memcpy(vtbl_rec->name, re->new_name, re->new_name_len);
|
||||
memset(vtbl_rec->name + re->new_name_len, 0,
|
||||
UBI_VOL_NAME_MAX + 1 - re->new_name_len);
|
||||
crc = crc32(UBI_CRC32_INIT, vtbl_rec,
|
||||
UBI_VTBL_RECORD_SIZE_CRC);
|
||||
vtbl_rec->crc = cpu_to_be32(crc);
|
||||
}
|
||||
|
||||
layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)];
|
||||
for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
|
||||
err = ubi_eba_unmap_leb(ubi, layout_vol, i);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0,
|
||||
ubi->vtbl_size);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* vtbl_check - check if volume table is not corrupted and sensible.
|
||||
* @ubi: UBI device description object
|
||||
* @vtbl: volume table
|
||||
*
|
||||
@ -132,13 +181,13 @@ static int vtbl_check(const struct ubi_device *ubi,
|
||||
upd_marker = vtbl[i].upd_marker;
|
||||
vol_type = vtbl[i].vol_type;
|
||||
name_len = be16_to_cpu(vtbl[i].name_len);
|
||||
name = (const char *) &vtbl[i].name[0];
|
||||
name = &vtbl[i].name[0];
|
||||
|
||||
crc = crc32(UBI_CRC32_INIT, &vtbl[i], UBI_VTBL_RECORD_SIZE_CRC);
|
||||
if (be32_to_cpu(vtbl[i].crc) != crc) {
|
||||
ubi_err("bad CRC at record %u: %#08x, not %#08x",
|
||||
i, crc, be32_to_cpu(vtbl[i].crc));
|
||||
ubi_dbg_dump_vtbl_record(&vtbl[i], i);
|
||||
ubi_dump_vtbl_record(&vtbl[i], i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -170,7 +219,7 @@ static int vtbl_check(const struct ubi_device *ubi,
|
||||
|
||||
n = ubi->leb_size % alignment;
|
||||
if (data_pad != n) {
|
||||
dbg_err("bad data_pad, has to be %d", n);
|
||||
ubi_err("bad data_pad, has to be %d", n);
|
||||
err = 6;
|
||||
goto bad;
|
||||
}
|
||||
@ -186,8 +235,8 @@ static int vtbl_check(const struct ubi_device *ubi,
|
||||
}
|
||||
|
||||
if (reserved_pebs > ubi->good_peb_count) {
|
||||
dbg_err("too large reserved_pebs, good PEBs %d",
|
||||
ubi->good_peb_count);
|
||||
ubi_err("too large reserved_pebs %d, good PEBs %d",
|
||||
reserved_pebs, ubi->good_peb_count);
|
||||
err = 9;
|
||||
goto bad;
|
||||
}
|
||||
@ -215,11 +264,15 @@ static int vtbl_check(const struct ubi_device *ubi,
|
||||
int len2 = be16_to_cpu(vtbl[n].name_len);
|
||||
|
||||
if (len1 > 0 && len1 == len2 &&
|
||||
!strncmp((char *)vtbl[i].name, (char *)vtbl[n].name, len1)) {
|
||||
ubi_err("volumes %d and %d have the same name"
|
||||
" \"%s\"", i, n, vtbl[i].name);
|
||||
ubi_dbg_dump_vtbl_record(&vtbl[i], i);
|
||||
ubi_dbg_dump_vtbl_record(&vtbl[n], n);
|
||||
#ifndef __UBOOT__
|
||||
!strncmp(vtbl[i].name, vtbl[n].name, len1)) {
|
||||
#else
|
||||
!strncmp((char *)vtbl[i].name, vtbl[n].name, len1)) {
|
||||
#endif
|
||||
ubi_err("volumes %d and %d have the same name \"%s\"",
|
||||
i, n, vtbl[i].name);
|
||||
ubi_dump_vtbl_record(&vtbl[i], i);
|
||||
ubi_dump_vtbl_record(&vtbl[n], n);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
@ -229,76 +282,64 @@ static int vtbl_check(const struct ubi_device *ubi,
|
||||
|
||||
bad:
|
||||
ubi_err("volume table check failed: record %d, error %d", i, err);
|
||||
ubi_dbg_dump_vtbl_record(&vtbl[i], i);
|
||||
ubi_dump_vtbl_record(&vtbl[i], i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* create_vtbl - create a copy of volume table.
|
||||
* @ubi: UBI device description object
|
||||
* @si: scanning information
|
||||
* @ai: attaching information
|
||||
* @copy: number of the volume table copy
|
||||
* @vtbl: contents of the volume table
|
||||
*
|
||||
* This function returns zero in case of success and a negative error code in
|
||||
* case of failure.
|
||||
*/
|
||||
static int create_vtbl(struct ubi_device *ubi, struct ubi_scan_info *si,
|
||||
static int create_vtbl(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
||||
int copy, void *vtbl)
|
||||
{
|
||||
int err, tries = 0;
|
||||
static struct ubi_vid_hdr *vid_hdr;
|
||||
struct ubi_scan_volume *sv;
|
||||
struct ubi_scan_leb *new_seb, *old_seb = NULL;
|
||||
struct ubi_vid_hdr *vid_hdr;
|
||||
struct ubi_ainf_peb *new_aeb;
|
||||
|
||||
ubi_msg("create volume table (copy #%d)", copy + 1);
|
||||
dbg_gen("create volume table (copy #%d)", copy + 1);
|
||||
|
||||
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
|
||||
if (!vid_hdr)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Check if there is a logical eraseblock which would have to contain
|
||||
* this volume table copy was found during scanning. It has to be wiped
|
||||
* out.
|
||||
*/
|
||||
sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID);
|
||||
if (sv)
|
||||
old_seb = ubi_scan_find_seb(sv, copy);
|
||||
|
||||
retry:
|
||||
new_seb = ubi_scan_get_free_peb(ubi, si);
|
||||
if (IS_ERR(new_seb)) {
|
||||
err = PTR_ERR(new_seb);
|
||||
new_aeb = ubi_early_get_peb(ubi, ai);
|
||||
if (IS_ERR(new_aeb)) {
|
||||
err = PTR_ERR(new_aeb);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
vid_hdr->vol_type = UBI_VID_DYNAMIC;
|
||||
vid_hdr->vol_type = UBI_LAYOUT_VOLUME_TYPE;
|
||||
vid_hdr->vol_id = cpu_to_be32(UBI_LAYOUT_VOLUME_ID);
|
||||
vid_hdr->compat = UBI_LAYOUT_VOLUME_COMPAT;
|
||||
vid_hdr->data_size = vid_hdr->used_ebs =
|
||||
vid_hdr->data_pad = cpu_to_be32(0);
|
||||
vid_hdr->lnum = cpu_to_be32(copy);
|
||||
vid_hdr->sqnum = cpu_to_be64(++si->max_sqnum);
|
||||
vid_hdr->leb_ver = cpu_to_be32(old_seb ? old_seb->leb_ver + 1: 0);
|
||||
vid_hdr->sqnum = cpu_to_be64(++ai->max_sqnum);
|
||||
|
||||
/* The EC header is already there, write the VID header */
|
||||
err = ubi_io_write_vid_hdr(ubi, new_seb->pnum, vid_hdr);
|
||||
err = ubi_io_write_vid_hdr(ubi, new_aeb->pnum, vid_hdr);
|
||||
if (err)
|
||||
goto write_error;
|
||||
|
||||
/* Write the layout volume contents */
|
||||
err = ubi_io_write_data(ubi, vtbl, new_seb->pnum, 0, ubi->vtbl_size);
|
||||
err = ubi_io_write_data(ubi, vtbl, new_aeb->pnum, 0, ubi->vtbl_size);
|
||||
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_used()'.
|
||||
* And add it to the attaching information. Don't delete the old version
|
||||
* of this LEB as it will be deleted and freed in 'ubi_add_to_av()'.
|
||||
*/
|
||||
err = ubi_scan_add_used(ubi, si, new_seb->pnum, new_seb->ec,
|
||||
vid_hdr, 0);
|
||||
kfree(new_seb);
|
||||
err = ubi_add_to_av(ubi, ai, new_aeb->pnum, new_aeb->ec, vid_hdr, 0);
|
||||
kmem_cache_free(ai->aeb_slab_cache, new_aeb);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
return err;
|
||||
|
||||
@ -308,10 +349,10 @@ write_error:
|
||||
* Probably this physical eraseblock went bad, try to pick
|
||||
* another one.
|
||||
*/
|
||||
list_add_tail(&new_seb->u.list, &si->corr);
|
||||
list_add(&new_aeb->u.list, &ai->erase);
|
||||
goto retry;
|
||||
}
|
||||
kfree(new_seb);
|
||||
kmem_cache_free(ai->aeb_slab_cache, new_aeb);
|
||||
out_free:
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
return err;
|
||||
@ -321,20 +362,20 @@ out_free:
|
||||
/**
|
||||
* process_lvol - process the layout volume.
|
||||
* @ubi: UBI device description object
|
||||
* @si: scanning information
|
||||
* @sv: layout volume scanning information
|
||||
* @ai: attaching information
|
||||
* @av: layout volume attaching information
|
||||
*
|
||||
* This function is responsible for reading the layout volume, ensuring it is
|
||||
* not corrupted, and recovering from corruptions if needed. Returns volume
|
||||
* table in case of success and a negative error code in case of failure.
|
||||
*/
|
||||
static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
|
||||
struct ubi_scan_info *si,
|
||||
struct ubi_scan_volume *sv)
|
||||
struct ubi_attach_info *ai,
|
||||
struct ubi_ainf_volume *av)
|
||||
{
|
||||
int err;
|
||||
struct rb_node *rb;
|
||||
struct ubi_scan_leb *seb;
|
||||
struct ubi_ainf_peb *aeb;
|
||||
struct ubi_vtbl_record *leb[UBI_LAYOUT_VOLUME_EBS] = { NULL, NULL };
|
||||
int leb_corrupted[UBI_LAYOUT_VOLUME_EBS] = {1, 1};
|
||||
|
||||
@ -356,25 +397,24 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
|
||||
* 0 contains more recent information.
|
||||
*
|
||||
* So the plan is to first check LEB 0. Then
|
||||
* a. if LEB 0 is OK, it must be containing the most resent data; then
|
||||
* a. if LEB 0 is OK, it must be containing the most recent data; then
|
||||
* we compare it with LEB 1, and if they are different, we copy LEB
|
||||
* 0 to LEB 1;
|
||||
* b. if LEB 0 is corrupted, but LEB 1 has to be OK, and we copy LEB 1
|
||||
* to LEB 0.
|
||||
*/
|
||||
|
||||
dbg_msg("check layout volume");
|
||||
dbg_gen("check layout volume");
|
||||
|
||||
/* Read both LEB 0 and LEB 1 into memory */
|
||||
ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) {
|
||||
leb[seb->lnum] = vmalloc(ubi->vtbl_size);
|
||||
if (!leb[seb->lnum]) {
|
||||
ubi_rb_for_each_entry(rb, aeb, &av->root, u.rb) {
|
||||
leb[aeb->lnum] = vzalloc(ubi->vtbl_size);
|
||||
if (!leb[aeb->lnum]) {
|
||||
err = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
memset(leb[seb->lnum], 0, ubi->vtbl_size);
|
||||
|
||||
err = ubi_io_read_data(ubi, leb[seb->lnum], seb->pnum, 0,
|
||||
err = ubi_io_read_data(ubi, leb[aeb->lnum], aeb->pnum, 0,
|
||||
ubi->vtbl_size);
|
||||
if (err == UBI_IO_BITFLIPS || mtd_is_eccerr(err))
|
||||
/*
|
||||
@ -382,12 +422,12 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
|
||||
* uncorrectable ECC error, but we have our own CRC and
|
||||
* the data will be checked later. If the data is OK,
|
||||
* the PEB will be scrubbed (because we set
|
||||
* seb->scrub). If the data is not OK, the contents of
|
||||
* aeb->scrub). If the data is not OK, the contents of
|
||||
* the PEB will be recovered from the second copy, and
|
||||
* seb->scrub will be cleared in
|
||||
* 'ubi_scan_add_used()'.
|
||||
* aeb->scrub will be cleared in
|
||||
* 'ubi_add_to_av()'.
|
||||
*/
|
||||
seb->scrub = 1;
|
||||
aeb->scrub = 1;
|
||||
else if (err)
|
||||
goto out_free;
|
||||
}
|
||||
@ -402,10 +442,11 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
|
||||
if (!leb_corrupted[0]) {
|
||||
/* LEB 0 is OK */
|
||||
if (leb[1])
|
||||
leb_corrupted[1] = memcmp(leb[0], leb[1], ubi->vtbl_size);
|
||||
leb_corrupted[1] = memcmp(leb[0], leb[1],
|
||||
ubi->vtbl_size);
|
||||
if (leb_corrupted[1]) {
|
||||
ubi_warn("volume table copy #2 is corrupted");
|
||||
err = create_vtbl(ubi, si, 1, leb[0]);
|
||||
err = create_vtbl(ubi, ai, 1, leb[0]);
|
||||
if (err)
|
||||
goto out_free;
|
||||
ubi_msg("volume table was restored");
|
||||
@ -428,7 +469,7 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
|
||||
}
|
||||
|
||||
ubi_warn("volume table copy #1 is corrupted");
|
||||
err = create_vtbl(ubi, si, 0, leb[1]);
|
||||
err = create_vtbl(ubi, ai, 0, leb[1]);
|
||||
if (err)
|
||||
goto out_free;
|
||||
ubi_msg("volume table was restored");
|
||||
@ -446,21 +487,20 @@ out_free:
|
||||
/**
|
||||
* create_empty_lvol - create empty layout volume.
|
||||
* @ubi: UBI device description object
|
||||
* @si: scanning information
|
||||
* @ai: attaching information
|
||||
*
|
||||
* This function returns volume table contents in case of success and a
|
||||
* negative error code in case of failure.
|
||||
*/
|
||||
static struct ubi_vtbl_record *create_empty_lvol(struct ubi_device *ubi,
|
||||
struct ubi_scan_info *si)
|
||||
struct ubi_attach_info *ai)
|
||||
{
|
||||
int i;
|
||||
struct ubi_vtbl_record *vtbl;
|
||||
|
||||
vtbl = vmalloc(ubi->vtbl_size);
|
||||
vtbl = vzalloc(ubi->vtbl_size);
|
||||
if (!vtbl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
memset(vtbl, 0, ubi->vtbl_size);
|
||||
|
||||
for (i = 0; i < ubi->vtbl_slots; i++)
|
||||
memcpy(&vtbl[i], &empty_vtbl_record, UBI_VTBL_RECORD_SIZE);
|
||||
@ -468,7 +508,7 @@ static struct ubi_vtbl_record *create_empty_lvol(struct ubi_device *ubi,
|
||||
for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
|
||||
int err;
|
||||
|
||||
err = create_vtbl(ubi, si, i, vtbl);
|
||||
err = create_vtbl(ubi, ai, i, vtbl);
|
||||
if (err) {
|
||||
vfree(vtbl);
|
||||
return ERR_PTR(err);
|
||||
@ -481,18 +521,19 @@ static struct ubi_vtbl_record *create_empty_lvol(struct ubi_device *ubi,
|
||||
/**
|
||||
* init_volumes - initialize volume information for existing volumes.
|
||||
* @ubi: UBI device description object
|
||||
* @si: scanning information
|
||||
* @ai: scanning information
|
||||
* @vtbl: volume table
|
||||
*
|
||||
* This function allocates volume description objects for existing volumes.
|
||||
* Returns zero in case of success and a negative error code in case of
|
||||
* failure.
|
||||
*/
|
||||
static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
||||
static int init_volumes(struct ubi_device *ubi,
|
||||
const struct ubi_attach_info *ai,
|
||||
const struct ubi_vtbl_record *vtbl)
|
||||
{
|
||||
int i, reserved_pebs = 0;
|
||||
struct ubi_scan_volume *sv;
|
||||
struct ubi_ainf_volume *av;
|
||||
struct ubi_volume *vol;
|
||||
|
||||
for (i = 0; i < ubi->vtbl_slots; i++) {
|
||||
@ -520,8 +561,8 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
||||
if (vtbl[i].flags & UBI_VTBL_AUTORESIZE_FLG) {
|
||||
/* Auto re-size flag may be set only for one volume */
|
||||
if (ubi->autoresize_vol_id != -1) {
|
||||
ubi_err("more then one auto-resize volume (%d "
|
||||
"and %d)", ubi->autoresize_vol_id, i);
|
||||
ubi_err("more than one auto-resize volume (%d and %d)",
|
||||
ubi->autoresize_vol_id, i);
|
||||
kfree(vol);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -548,8 +589,8 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
||||
}
|
||||
|
||||
/* Static volumes only */
|
||||
sv = ubi_scan_find_sv(si, i);
|
||||
if (!sv) {
|
||||
av = ubi_find_av(ai, i);
|
||||
if (!av) {
|
||||
/*
|
||||
* No eraseblocks belonging to this volume found. We
|
||||
* don't actually know whether this static volume is
|
||||
@ -561,22 +602,22 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sv->leb_count != sv->used_ebs) {
|
||||
if (av->leb_count != av->used_ebs) {
|
||||
/*
|
||||
* We found a static volume which misses several
|
||||
* eraseblocks. Treat it as corrupted.
|
||||
*/
|
||||
ubi_warn("static volume %d misses %d LEBs - corrupted",
|
||||
sv->vol_id, sv->used_ebs - sv->leb_count);
|
||||
av->vol_id, av->used_ebs - av->leb_count);
|
||||
vol->corrupted = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
vol->used_ebs = sv->used_ebs;
|
||||
vol->used_ebs = av->used_ebs;
|
||||
vol->used_bytes =
|
||||
(long long)(vol->used_ebs - 1) * vol->usable_leb_size;
|
||||
vol->used_bytes += sv->last_data_size;
|
||||
vol->last_eb_bytes = sv->last_data_size;
|
||||
vol->used_bytes += av->last_data_size;
|
||||
vol->last_eb_bytes = av->last_data_size;
|
||||
}
|
||||
|
||||
/* And add the layout volume */
|
||||
@ -585,7 +626,7 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
||||
return -ENOMEM;
|
||||
|
||||
vol->reserved_pebs = UBI_LAYOUT_VOLUME_EBS;
|
||||
vol->alignment = 1;
|
||||
vol->alignment = UBI_LAYOUT_VOLUME_ALIGN;
|
||||
vol->vol_type = UBI_DYNAMIC_VOLUME;
|
||||
vol->name_len = sizeof(UBI_LAYOUT_VOLUME_NAME) - 1;
|
||||
memcpy(vol->name, UBI_LAYOUT_VOLUME_NAME, vol->name_len + 1);
|
||||
@ -603,9 +644,13 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
||||
ubi->vol_count += 1;
|
||||
vol->ubi = ubi;
|
||||
|
||||
if (reserved_pebs > ubi->avail_pebs)
|
||||
if (reserved_pebs > ubi->avail_pebs) {
|
||||
ubi_err("not enough PEBs, required %d, available %d",
|
||||
reserved_pebs, ubi->avail_pebs);
|
||||
if (ubi->corr_peb_count)
|
||||
ubi_err("%d PEBs are corrupted and not used",
|
||||
ubi->corr_peb_count);
|
||||
}
|
||||
ubi->rsvd_pebs += reserved_pebs;
|
||||
ubi->avail_pebs -= reserved_pebs;
|
||||
|
||||
@ -613,105 +658,104 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
||||
}
|
||||
|
||||
/**
|
||||
* check_sv - check volume scanning information.
|
||||
* check_av - check volume attaching information.
|
||||
* @vol: UBI volume description object
|
||||
* @sv: volume scanning information
|
||||
* @av: volume attaching information
|
||||
*
|
||||
* This function returns zero if the volume scanning information is consistent
|
||||
* This function returns zero if the volume attaching information is consistent
|
||||
* to the data read from the volume tabla, and %-EINVAL if not.
|
||||
*/
|
||||
static int check_sv(const struct ubi_volume *vol,
|
||||
const struct ubi_scan_volume *sv)
|
||||
static int check_av(const struct ubi_volume *vol,
|
||||
const struct ubi_ainf_volume *av)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (sv->highest_lnum >= vol->reserved_pebs) {
|
||||
if (av->highest_lnum >= vol->reserved_pebs) {
|
||||
err = 1;
|
||||
goto bad;
|
||||
}
|
||||
if (sv->leb_count > vol->reserved_pebs) {
|
||||
if (av->leb_count > vol->reserved_pebs) {
|
||||
err = 2;
|
||||
goto bad;
|
||||
}
|
||||
if (sv->vol_type != vol->vol_type) {
|
||||
if (av->vol_type != vol->vol_type) {
|
||||
err = 3;
|
||||
goto bad;
|
||||
}
|
||||
if (sv->used_ebs > vol->reserved_pebs) {
|
||||
if (av->used_ebs > vol->reserved_pebs) {
|
||||
err = 4;
|
||||
goto bad;
|
||||
}
|
||||
if (sv->data_pad != vol->data_pad) {
|
||||
if (av->data_pad != vol->data_pad) {
|
||||
err = 5;
|
||||
goto bad;
|
||||
}
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
ubi_err("bad scanning information, error %d", err);
|
||||
ubi_dbg_dump_sv(sv);
|
||||
ubi_dbg_dump_vol_info(vol);
|
||||
ubi_err("bad attaching information, error %d", err);
|
||||
ubi_dump_av(av);
|
||||
ubi_dump_vol_info(vol);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* check_scanning_info - check that scanning information.
|
||||
* check_attaching_info - check that attaching information.
|
||||
* @ubi: UBI device description object
|
||||
* @si: scanning information
|
||||
* @ai: attaching information
|
||||
*
|
||||
* Even though we protect on-flash data by CRC checksums, we still don't trust
|
||||
* the media. This function ensures that scanning information is consistent to
|
||||
* the information read from the volume table. Returns zero if the scanning
|
||||
* the media. This function ensures that attaching information is consistent to
|
||||
* the information read from the volume table. Returns zero if the attaching
|
||||
* information is OK and %-EINVAL if it is not.
|
||||
*/
|
||||
static int check_scanning_info(const struct ubi_device *ubi,
|
||||
struct ubi_scan_info *si)
|
||||
static int check_attaching_info(const struct ubi_device *ubi,
|
||||
struct ubi_attach_info *ai)
|
||||
{
|
||||
int err, i;
|
||||
struct ubi_scan_volume *sv;
|
||||
struct ubi_ainf_volume *av;
|
||||
struct ubi_volume *vol;
|
||||
|
||||
if (si->vols_found > UBI_INT_VOL_COUNT + ubi->vtbl_slots) {
|
||||
ubi_err("scanning found %d volumes, maximum is %d + %d",
|
||||
si->vols_found, UBI_INT_VOL_COUNT, ubi->vtbl_slots);
|
||||
if (ai->vols_found > UBI_INT_VOL_COUNT + ubi->vtbl_slots) {
|
||||
ubi_err("found %d volumes while attaching, maximum is %d + %d",
|
||||
ai->vols_found, UBI_INT_VOL_COUNT, ubi->vtbl_slots);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (si->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT &&
|
||||
si->highest_vol_id < UBI_INTERNAL_VOL_START) {
|
||||
ubi_err("too large volume ID %d found by scanning",
|
||||
si->highest_vol_id);
|
||||
if (ai->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT &&
|
||||
ai->highest_vol_id < UBI_INTERNAL_VOL_START) {
|
||||
ubi_err("too large volume ID %d found", ai->highest_vol_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
|
||||
cond_resched();
|
||||
|
||||
sv = ubi_scan_find_sv(si, i);
|
||||
av = ubi_find_av(ai, i);
|
||||
vol = ubi->volumes[i];
|
||||
if (!vol) {
|
||||
if (sv)
|
||||
ubi_scan_rm_volume(si, sv);
|
||||
if (av)
|
||||
ubi_remove_av(ai, av);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vol->reserved_pebs == 0) {
|
||||
ubi_assert(i < ubi->vtbl_slots);
|
||||
|
||||
if (!sv)
|
||||
if (!av)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* During scanning we found a volume which does not
|
||||
* During attaching we 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. Discard
|
||||
* these eraseblocks.
|
||||
*/
|
||||
ubi_msg("finish volume %d removal", sv->vol_id);
|
||||
ubi_scan_rm_volume(si, sv);
|
||||
} else if (sv) {
|
||||
err = check_sv(vol, sv);
|
||||
ubi_msg("finish volume %d removal", av->vol_id);
|
||||
ubi_remove_av(ai, av);
|
||||
} else if (av) {
|
||||
err = check_av(vol, av);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -721,19 +765,18 @@ static int check_scanning_info(const struct ubi_device *ubi,
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_read_volume_table - read volume table.
|
||||
* information.
|
||||
* ubi_read_volume_table - read the volume table.
|
||||
* @ubi: UBI device description object
|
||||
* @si: scanning information
|
||||
* @ai: attaching information
|
||||
*
|
||||
* This function reads volume table, checks it, recover from errors if needed,
|
||||
* or creates it if needed. Returns zero in case of success and a negative
|
||||
* error code in case of failure.
|
||||
*/
|
||||
int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si)
|
||||
int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_attach_info *ai)
|
||||
{
|
||||
int i, err;
|
||||
struct ubi_scan_volume *sv;
|
||||
struct ubi_ainf_volume *av;
|
||||
|
||||
empty_vtbl_record.crc = cpu_to_be32(0xf116c36b);
|
||||
|
||||
@ -748,8 +791,8 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si)
|
||||
ubi->vtbl_size = ubi->vtbl_slots * UBI_VTBL_RECORD_SIZE;
|
||||
ubi->vtbl_size = ALIGN(ubi->vtbl_size, ubi->min_io_size);
|
||||
|
||||
sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID);
|
||||
if (!sv) {
|
||||
av = ubi_find_av(ai, UBI_LAYOUT_VOLUME_ID);
|
||||
if (!av) {
|
||||
/*
|
||||
* No logical eraseblocks belonging to the layout volume were
|
||||
* found. This could mean that the flash is just empty. In
|
||||
@ -758,8 +801,8 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si)
|
||||
* But if flash is not empty this must be a corruption or the
|
||||
* MTD device just contains garbage.
|
||||
*/
|
||||
if (si->is_empty) {
|
||||
ubi->vtbl = create_empty_lvol(ubi, si);
|
||||
if (ai->is_empty) {
|
||||
ubi->vtbl = create_empty_lvol(ubi, ai);
|
||||
if (IS_ERR(ubi->vtbl))
|
||||
return PTR_ERR(ubi->vtbl);
|
||||
} else {
|
||||
@ -767,33 +810,33 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si)
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
if (sv->leb_count > UBI_LAYOUT_VOLUME_EBS) {
|
||||
if (av->leb_count > UBI_LAYOUT_VOLUME_EBS) {
|
||||
/* This must not happen with proper UBI images */
|
||||
dbg_err("too many LEBs (%d) in layout volume",
|
||||
sv->leb_count);
|
||||
ubi_err("too many LEBs (%d) in layout volume",
|
||||
av->leb_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ubi->vtbl = process_lvol(ubi, si, sv);
|
||||
ubi->vtbl = process_lvol(ubi, ai, av);
|
||||
if (IS_ERR(ubi->vtbl))
|
||||
return PTR_ERR(ubi->vtbl);
|
||||
}
|
||||
|
||||
ubi->avail_pebs = ubi->good_peb_count;
|
||||
ubi->avail_pebs = ubi->good_peb_count - ubi->corr_peb_count;
|
||||
|
||||
/*
|
||||
* The layout volume is OK, initialize the corresponding in-RAM data
|
||||
* structures.
|
||||
*/
|
||||
err = init_volumes(ubi, si, ubi->vtbl);
|
||||
err = init_volumes(ubi, ai, ubi->vtbl);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
/*
|
||||
* Get sure that the scanning information is consistent to the
|
||||
* Make sure that the attaching information is consistent to the
|
||||
* information stored in the volume table.
|
||||
*/
|
||||
err = check_scanning_info(ubi, si);
|
||||
err = check_attaching_info(ubi, ai);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
@ -801,26 +844,24 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si)
|
||||
|
||||
out_free:
|
||||
vfree(ubi->vtbl);
|
||||
for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++)
|
||||
if (ubi->volumes[i]) {
|
||||
kfree(ubi->volumes[i]);
|
||||
ubi->volumes[i] = NULL;
|
||||
}
|
||||
for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
|
||||
kfree(ubi->volumes[i]);
|
||||
ubi->volumes[i] = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
|
||||
|
||||
/**
|
||||
* paranoid_vtbl_check - check volume table.
|
||||
* self_vtbl_check - check volume table.
|
||||
* @ubi: UBI device description object
|
||||
*/
|
||||
static void paranoid_vtbl_check(const struct ubi_device *ubi)
|
||||
static void self_vtbl_check(const struct ubi_device *ubi)
|
||||
{
|
||||
if (!ubi_dbg_chk_gen(ubi))
|
||||
return;
|
||||
|
||||
if (vtbl_check(ubi, ubi->vtbl)) {
|
||||
ubi_err("paranoid check failed");
|
||||
ubi_err("self-check failed");
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
|
||||
|
1620
drivers/mtd/ubi/wl.c
1620
drivers/mtd/ubi/wl.c
File diff suppressed because it is too large
Load Diff
@ -3,18 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2006-2008 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Authors: Adrian Hunter
|
||||
* Artem Bityutskiy (Битюцкий Артём)
|
||||
@ -31,32 +20,171 @@
|
||||
*/
|
||||
|
||||
#include "ubifs.h"
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/writeback.h>
|
||||
#else
|
||||
#include <linux/err.h>
|
||||
#endif
|
||||
#include <linux/math64.h>
|
||||
|
||||
/*
|
||||
* When pessimistic budget calculations say that there is no enough space,
|
||||
* UBIFS starts writing back dirty inodes and pages, doing garbage collection,
|
||||
* or committing. The below constant defines maximum number of times UBIFS
|
||||
* repeats the operations.
|
||||
*/
|
||||
#define MAX_MKSPC_RETRIES 3
|
||||
|
||||
/*
|
||||
* The below constant defines amount of dirty pages which should be written
|
||||
* back at when trying to shrink the liability.
|
||||
*/
|
||||
#define NR_TO_WRITE 16
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* ubifs_calc_min_idx_lebs - calculate amount of eraseblocks for the index.
|
||||
* shrink_liability - write-back some dirty pages/inodes.
|
||||
* @c: UBIFS file-system description object
|
||||
* @nr_to_write: how many dirty pages to write-back
|
||||
*
|
||||
* This function shrinks UBIFS liability by means of writing back some amount
|
||||
* of dirty inodes and their pages.
|
||||
*
|
||||
* Note, this function synchronizes even VFS inodes which are locked
|
||||
* (@i_mutex) by the caller of the budgeting function, because write-back does
|
||||
* not touch @i_mutex.
|
||||
*/
|
||||
static void shrink_liability(struct ubifs_info *c, int nr_to_write)
|
||||
{
|
||||
down_read(&c->vfs_sb->s_umount);
|
||||
writeback_inodes_sb(c->vfs_sb, WB_REASON_FS_FREE_SPACE);
|
||||
up_read(&c->vfs_sb->s_umount);
|
||||
}
|
||||
|
||||
/**
|
||||
* run_gc - run garbage collector.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function calculates and returns the number of eraseblocks which should
|
||||
* be kept for index usage.
|
||||
* This function runs garbage collector to make some more free space. Returns
|
||||
* zero if a free LEB has been produced, %-EAGAIN if commit is required, and a
|
||||
* negative error code in case of failure.
|
||||
*/
|
||||
static int run_gc(struct ubifs_info *c)
|
||||
{
|
||||
int err, lnum;
|
||||
|
||||
/* Make some free space by garbage-collecting dirty space */
|
||||
down_read(&c->commit_sem);
|
||||
lnum = ubifs_garbage_collect(c, 1);
|
||||
up_read(&c->commit_sem);
|
||||
if (lnum < 0)
|
||||
return lnum;
|
||||
|
||||
/* GC freed one LEB, return it to lprops */
|
||||
dbg_budg("GC freed LEB %d", lnum);
|
||||
err = ubifs_return_leb(c, lnum);
|
||||
if (err)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_liability - calculate current liability.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function calculates and returns current UBIFS liability, i.e. the
|
||||
* amount of bytes UBIFS has "promised" to write to the media.
|
||||
*/
|
||||
static long long get_liability(struct ubifs_info *c)
|
||||
{
|
||||
long long liab;
|
||||
|
||||
spin_lock(&c->space_lock);
|
||||
liab = c->bi.idx_growth + c->bi.data_growth + c->bi.dd_growth;
|
||||
spin_unlock(&c->space_lock);
|
||||
return liab;
|
||||
}
|
||||
|
||||
/**
|
||||
* make_free_space - make more free space on the file-system.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function is called when an operation cannot be budgeted because there
|
||||
* is supposedly no free space. But in most cases there is some free space:
|
||||
* o budgeting is pessimistic, so it always budgets more than it is actually
|
||||
* needed, so shrinking the liability is one way to make free space - the
|
||||
* cached data will take less space then it was budgeted for;
|
||||
* o GC may turn some dark space into free space (budgeting treats dark space
|
||||
* as not available);
|
||||
* o commit may free some LEB, i.e., turn freeable LEBs into free LEBs.
|
||||
*
|
||||
* So this function tries to do the above. Returns %-EAGAIN if some free space
|
||||
* was presumably made and the caller has to re-try budgeting the operation.
|
||||
* Returns %-ENOSPC if it couldn't do more free space, and other negative error
|
||||
* codes on failures.
|
||||
*/
|
||||
static int make_free_space(struct ubifs_info *c)
|
||||
{
|
||||
int err, retries = 0;
|
||||
long long liab1, liab2;
|
||||
|
||||
do {
|
||||
liab1 = get_liability(c);
|
||||
/*
|
||||
* We probably have some dirty pages or inodes (liability), try
|
||||
* to write them back.
|
||||
*/
|
||||
dbg_budg("liability %lld, run write-back", liab1);
|
||||
shrink_liability(c, NR_TO_WRITE);
|
||||
|
||||
liab2 = get_liability(c);
|
||||
if (liab2 < liab1)
|
||||
return -EAGAIN;
|
||||
|
||||
dbg_budg("new liability %lld (not shrunk)", liab2);
|
||||
|
||||
/* Liability did not shrink again, try GC */
|
||||
dbg_budg("Run GC");
|
||||
err = run_gc(c);
|
||||
if (!err)
|
||||
return -EAGAIN;
|
||||
|
||||
if (err != -EAGAIN && err != -ENOSPC)
|
||||
/* Some real error happened */
|
||||
return err;
|
||||
|
||||
dbg_budg("Run commit (retries %d)", retries);
|
||||
err = ubifs_run_commit(c);
|
||||
if (err)
|
||||
return err;
|
||||
} while (retries++ < MAX_MKSPC_RETRIES);
|
||||
|
||||
return -ENOSPC;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ubifs_calc_min_idx_lebs - calculate amount of LEBs for the index.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function calculates and returns the number of LEBs which should be kept
|
||||
* for index usage.
|
||||
*/
|
||||
int ubifs_calc_min_idx_lebs(struct ubifs_info *c)
|
||||
{
|
||||
int idx_lebs, eff_leb_size = c->leb_size - c->max_idx_node_sz;
|
||||
int idx_lebs;
|
||||
long long idx_size;
|
||||
|
||||
idx_size = c->old_idx_sz + c->budg_idx_growth + c->budg_uncommitted_idx;
|
||||
|
||||
idx_size = c->bi.old_idx_sz + c->bi.idx_growth + c->bi.uncommitted_idx;
|
||||
/* And make sure we have thrice the index size of space reserved */
|
||||
idx_size = idx_size + (idx_size << 1);
|
||||
|
||||
idx_size += idx_size << 1;
|
||||
/*
|
||||
* We do not maintain 'old_idx_size' as 'old_idx_lebs'/'old_idx_bytes'
|
||||
* pair, nor similarly the two variables for the new index size, so we
|
||||
* have to do this costly 64-bit division on fast-path.
|
||||
*/
|
||||
idx_size += eff_leb_size - 1;
|
||||
idx_lebs = div_u64(idx_size, eff_leb_size);
|
||||
idx_lebs = div_u64(idx_size + c->idx_leb_size - 1, c->idx_leb_size);
|
||||
/*
|
||||
* The index head is not available for the in-the-gaps method, so add an
|
||||
* extra LEB to compensate.
|
||||
@ -67,6 +195,424 @@ int ubifs_calc_min_idx_lebs(struct ubifs_info *c)
|
||||
return idx_lebs;
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* ubifs_calc_available - calculate available FS space.
|
||||
* @c: UBIFS file-system description object
|
||||
* @min_idx_lebs: minimum number of LEBs reserved for the index
|
||||
*
|
||||
* This function calculates and returns amount of FS space available for use.
|
||||
*/
|
||||
long long ubifs_calc_available(const struct ubifs_info *c, int min_idx_lebs)
|
||||
{
|
||||
int subtract_lebs;
|
||||
long long available;
|
||||
|
||||
available = c->main_bytes - c->lst.total_used;
|
||||
|
||||
/*
|
||||
* Now 'available' contains theoretically available flash space
|
||||
* assuming there is no index, so we have to subtract the space which
|
||||
* is reserved for the index.
|
||||
*/
|
||||
subtract_lebs = min_idx_lebs;
|
||||
|
||||
/* Take into account that GC reserves one LEB for its own needs */
|
||||
subtract_lebs += 1;
|
||||
|
||||
/*
|
||||
* The GC journal head LEB is not really accessible. And since
|
||||
* different write types go to different heads, we may count only on
|
||||
* one head's space.
|
||||
*/
|
||||
subtract_lebs += c->jhead_cnt - 1;
|
||||
|
||||
/* We also reserve one LEB for deletions, which bypass budgeting */
|
||||
subtract_lebs += 1;
|
||||
|
||||
available -= (long long)subtract_lebs * c->leb_size;
|
||||
|
||||
/* Subtract the dead space which is not available for use */
|
||||
available -= c->lst.total_dead;
|
||||
|
||||
/*
|
||||
* Subtract dark space, which might or might not be usable - it depends
|
||||
* on the data which we have on the media and which will be written. If
|
||||
* this is a lot of uncompressed or not-compressible data, the dark
|
||||
* space cannot be used.
|
||||
*/
|
||||
available -= c->lst.total_dark;
|
||||
|
||||
/*
|
||||
* However, there is more dark space. The index may be bigger than
|
||||
* @min_idx_lebs. Those extra LEBs are assumed to be available, but
|
||||
* their dark space is not included in total_dark, so it is subtracted
|
||||
* here.
|
||||
*/
|
||||
if (c->lst.idx_lebs > min_idx_lebs) {
|
||||
subtract_lebs = c->lst.idx_lebs - min_idx_lebs;
|
||||
available -= subtract_lebs * c->dark_wm;
|
||||
}
|
||||
|
||||
/* The calculations are rough and may end up with a negative number */
|
||||
return available > 0 ? available : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* can_use_rp - check whether the user is allowed to use reserved pool.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* UBIFS has so-called "reserved pool" which is flash space reserved
|
||||
* for the superuser and for uses whose UID/GID is recorded in UBIFS superblock.
|
||||
* This function checks whether current user is allowed to use reserved pool.
|
||||
* Returns %1 current user is allowed to use reserved pool and %0 otherwise.
|
||||
*/
|
||||
static int can_use_rp(struct ubifs_info *c)
|
||||
{
|
||||
if (uid_eq(current_fsuid(), c->rp_uid) || capable(CAP_SYS_RESOURCE) ||
|
||||
(!gid_eq(c->rp_gid, GLOBAL_ROOT_GID) && in_group_p(c->rp_gid)))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* do_budget_space - reserve flash space for index and data growth.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function makes sure UBIFS has enough free LEBs for index growth and
|
||||
* data.
|
||||
*
|
||||
* When budgeting index space, UBIFS reserves thrice as many LEBs as the index
|
||||
* would take if it was consolidated and written to the flash. This guarantees
|
||||
* that the "in-the-gaps" commit method always succeeds and UBIFS will always
|
||||
* be able to commit dirty index. So this function basically adds amount of
|
||||
* budgeted index space to the size of the current index, multiplies this by 3,
|
||||
* and makes sure this does not exceed the amount of free LEBs.
|
||||
*
|
||||
* Notes about @c->bi.min_idx_lebs and @c->lst.idx_lebs variables:
|
||||
* o @c->lst.idx_lebs is the number of LEBs the index currently uses. It might
|
||||
* be large, because UBIFS does not do any index consolidation as long as
|
||||
* there is free space. IOW, the index may take a lot of LEBs, but the LEBs
|
||||
* will contain a lot of dirt.
|
||||
* o @c->bi.min_idx_lebs is the number of LEBS the index presumably takes. IOW,
|
||||
* the index may be consolidated to take up to @c->bi.min_idx_lebs LEBs.
|
||||
*
|
||||
* This function returns zero in case of success, and %-ENOSPC in case of
|
||||
* failure.
|
||||
*/
|
||||
static int do_budget_space(struct ubifs_info *c)
|
||||
{
|
||||
long long outstanding, available;
|
||||
int lebs, rsvd_idx_lebs, min_idx_lebs;
|
||||
|
||||
/* First budget index space */
|
||||
min_idx_lebs = ubifs_calc_min_idx_lebs(c);
|
||||
|
||||
/* Now 'min_idx_lebs' contains number of LEBs to reserve */
|
||||
if (min_idx_lebs > c->lst.idx_lebs)
|
||||
rsvd_idx_lebs = min_idx_lebs - c->lst.idx_lebs;
|
||||
else
|
||||
rsvd_idx_lebs = 0;
|
||||
|
||||
/*
|
||||
* The number of LEBs that are available to be used by the index is:
|
||||
*
|
||||
* @c->lst.empty_lebs + @c->freeable_cnt + @c->idx_gc_cnt -
|
||||
* @c->lst.taken_empty_lebs
|
||||
*
|
||||
* @c->lst.empty_lebs are available because they are empty.
|
||||
* @c->freeable_cnt are available because they contain only free and
|
||||
* dirty space, @c->idx_gc_cnt are available because they are index
|
||||
* LEBs that have been garbage collected and are awaiting the commit
|
||||
* before they can be used. And the in-the-gaps method will grab these
|
||||
* if it needs them. @c->lst.taken_empty_lebs are empty LEBs that have
|
||||
* already been allocated for some purpose.
|
||||
*
|
||||
* Note, @c->idx_gc_cnt is included to both @c->lst.empty_lebs (because
|
||||
* these LEBs are empty) and to @c->lst.taken_empty_lebs (because they
|
||||
* are taken until after the commit).
|
||||
*
|
||||
* Note, @c->lst.taken_empty_lebs may temporarily be higher by one
|
||||
* because of the way we serialize LEB allocations and budgeting. See a
|
||||
* comment in 'ubifs_find_free_space()'.
|
||||
*/
|
||||
lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt -
|
||||
c->lst.taken_empty_lebs;
|
||||
if (unlikely(rsvd_idx_lebs > lebs)) {
|
||||
dbg_budg("out of indexing space: min_idx_lebs %d (old %d), rsvd_idx_lebs %d",
|
||||
min_idx_lebs, c->bi.min_idx_lebs, rsvd_idx_lebs);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
available = ubifs_calc_available(c, min_idx_lebs);
|
||||
outstanding = c->bi.data_growth + c->bi.dd_growth;
|
||||
|
||||
if (unlikely(available < outstanding)) {
|
||||
dbg_budg("out of data space: available %lld, outstanding %lld",
|
||||
available, outstanding);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (available - outstanding <= c->rp_size && !can_use_rp(c))
|
||||
return -ENOSPC;
|
||||
|
||||
c->bi.min_idx_lebs = min_idx_lebs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* calc_idx_growth - calculate approximate index growth from budgeting request.
|
||||
* @c: UBIFS file-system description object
|
||||
* @req: budgeting request
|
||||
*
|
||||
* For now we assume each new node adds one znode. But this is rather poor
|
||||
* approximation, though.
|
||||
*/
|
||||
static int calc_idx_growth(const struct ubifs_info *c,
|
||||
const struct ubifs_budget_req *req)
|
||||
{
|
||||
int znodes;
|
||||
|
||||
znodes = req->new_ino + (req->new_page << UBIFS_BLOCKS_PER_PAGE_SHIFT) +
|
||||
req->new_dent;
|
||||
return znodes * c->max_idx_node_sz;
|
||||
}
|
||||
|
||||
/**
|
||||
* calc_data_growth - calculate approximate amount of new data from budgeting
|
||||
* request.
|
||||
* @c: UBIFS file-system description object
|
||||
* @req: budgeting request
|
||||
*/
|
||||
static int calc_data_growth(const struct ubifs_info *c,
|
||||
const struct ubifs_budget_req *req)
|
||||
{
|
||||
int data_growth;
|
||||
|
||||
data_growth = req->new_ino ? c->bi.inode_budget : 0;
|
||||
if (req->new_page)
|
||||
data_growth += c->bi.page_budget;
|
||||
if (req->new_dent)
|
||||
data_growth += c->bi.dent_budget;
|
||||
data_growth += req->new_ino_d;
|
||||
return data_growth;
|
||||
}
|
||||
|
||||
/**
|
||||
* calc_dd_growth - calculate approximate amount of data which makes other data
|
||||
* dirty from budgeting request.
|
||||
* @c: UBIFS file-system description object
|
||||
* @req: budgeting request
|
||||
*/
|
||||
static int calc_dd_growth(const struct ubifs_info *c,
|
||||
const struct ubifs_budget_req *req)
|
||||
{
|
||||
int dd_growth;
|
||||
|
||||
dd_growth = req->dirtied_page ? c->bi.page_budget : 0;
|
||||
|
||||
if (req->dirtied_ino)
|
||||
dd_growth += c->bi.inode_budget << (req->dirtied_ino - 1);
|
||||
if (req->mod_dent)
|
||||
dd_growth += c->bi.dent_budget;
|
||||
dd_growth += req->dirtied_ino_d;
|
||||
return dd_growth;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_budget_space - ensure there is enough space to complete an operation.
|
||||
* @c: UBIFS file-system description object
|
||||
* @req: budget request
|
||||
*
|
||||
* This function allocates budget for an operation. It uses pessimistic
|
||||
* approximation of how much flash space the operation needs. The goal of this
|
||||
* function is to make sure UBIFS always has flash space to flush all dirty
|
||||
* pages, dirty inodes, and dirty znodes (liability). This function may force
|
||||
* commit, garbage-collection or write-back. Returns zero in case of success,
|
||||
* %-ENOSPC if there is no free space and other negative error codes in case of
|
||||
* failures.
|
||||
*/
|
||||
int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req)
|
||||
{
|
||||
int uninitialized_var(cmt_retries), uninitialized_var(wb_retries);
|
||||
int err, idx_growth, data_growth, dd_growth, retried = 0;
|
||||
|
||||
ubifs_assert(req->new_page <= 1);
|
||||
ubifs_assert(req->dirtied_page <= 1);
|
||||
ubifs_assert(req->new_dent <= 1);
|
||||
ubifs_assert(req->mod_dent <= 1);
|
||||
ubifs_assert(req->new_ino <= 1);
|
||||
ubifs_assert(req->new_ino_d <= UBIFS_MAX_INO_DATA);
|
||||
ubifs_assert(req->dirtied_ino <= 4);
|
||||
ubifs_assert(req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 4);
|
||||
ubifs_assert(!(req->new_ino_d & 7));
|
||||
ubifs_assert(!(req->dirtied_ino_d & 7));
|
||||
|
||||
data_growth = calc_data_growth(c, req);
|
||||
dd_growth = calc_dd_growth(c, req);
|
||||
if (!data_growth && !dd_growth)
|
||||
return 0;
|
||||
idx_growth = calc_idx_growth(c, req);
|
||||
|
||||
again:
|
||||
spin_lock(&c->space_lock);
|
||||
ubifs_assert(c->bi.idx_growth >= 0);
|
||||
ubifs_assert(c->bi.data_growth >= 0);
|
||||
ubifs_assert(c->bi.dd_growth >= 0);
|
||||
|
||||
if (unlikely(c->bi.nospace) && (c->bi.nospace_rp || !can_use_rp(c))) {
|
||||
dbg_budg("no space");
|
||||
spin_unlock(&c->space_lock);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
c->bi.idx_growth += idx_growth;
|
||||
c->bi.data_growth += data_growth;
|
||||
c->bi.dd_growth += dd_growth;
|
||||
|
||||
err = do_budget_space(c);
|
||||
if (likely(!err)) {
|
||||
req->idx_growth = idx_growth;
|
||||
req->data_growth = data_growth;
|
||||
req->dd_growth = dd_growth;
|
||||
spin_unlock(&c->space_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Restore the old values */
|
||||
c->bi.idx_growth -= idx_growth;
|
||||
c->bi.data_growth -= data_growth;
|
||||
c->bi.dd_growth -= dd_growth;
|
||||
spin_unlock(&c->space_lock);
|
||||
|
||||
if (req->fast) {
|
||||
dbg_budg("no space for fast budgeting");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = make_free_space(c);
|
||||
cond_resched();
|
||||
if (err == -EAGAIN) {
|
||||
dbg_budg("try again");
|
||||
goto again;
|
||||
} else if (err == -ENOSPC) {
|
||||
if (!retried) {
|
||||
retried = 1;
|
||||
dbg_budg("-ENOSPC, but anyway try once again");
|
||||
goto again;
|
||||
}
|
||||
dbg_budg("FS is full, -ENOSPC");
|
||||
c->bi.nospace = 1;
|
||||
if (can_use_rp(c) || c->rp_size == 0)
|
||||
c->bi.nospace_rp = 1;
|
||||
smp_wmb();
|
||||
} else
|
||||
ubifs_err("cannot budget space, error %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_release_budget - release budgeted free space.
|
||||
* @c: UBIFS file-system description object
|
||||
* @req: budget request
|
||||
*
|
||||
* This function releases the space budgeted by 'ubifs_budget_space()'. Note,
|
||||
* since the index changes (which were budgeted for in @req->idx_growth) will
|
||||
* only be written to the media on commit, this function moves the index budget
|
||||
* from @c->bi.idx_growth to @c->bi.uncommitted_idx. The latter will be zeroed
|
||||
* by the commit operation.
|
||||
*/
|
||||
void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req)
|
||||
{
|
||||
ubifs_assert(req->new_page <= 1);
|
||||
ubifs_assert(req->dirtied_page <= 1);
|
||||
ubifs_assert(req->new_dent <= 1);
|
||||
ubifs_assert(req->mod_dent <= 1);
|
||||
ubifs_assert(req->new_ino <= 1);
|
||||
ubifs_assert(req->new_ino_d <= UBIFS_MAX_INO_DATA);
|
||||
ubifs_assert(req->dirtied_ino <= 4);
|
||||
ubifs_assert(req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 4);
|
||||
ubifs_assert(!(req->new_ino_d & 7));
|
||||
ubifs_assert(!(req->dirtied_ino_d & 7));
|
||||
if (!req->recalculate) {
|
||||
ubifs_assert(req->idx_growth >= 0);
|
||||
ubifs_assert(req->data_growth >= 0);
|
||||
ubifs_assert(req->dd_growth >= 0);
|
||||
}
|
||||
|
||||
if (req->recalculate) {
|
||||
req->data_growth = calc_data_growth(c, req);
|
||||
req->dd_growth = calc_dd_growth(c, req);
|
||||
req->idx_growth = calc_idx_growth(c, req);
|
||||
}
|
||||
|
||||
if (!req->data_growth && !req->dd_growth)
|
||||
return;
|
||||
|
||||
c->bi.nospace = c->bi.nospace_rp = 0;
|
||||
smp_wmb();
|
||||
|
||||
spin_lock(&c->space_lock);
|
||||
c->bi.idx_growth -= req->idx_growth;
|
||||
c->bi.uncommitted_idx += req->idx_growth;
|
||||
c->bi.data_growth -= req->data_growth;
|
||||
c->bi.dd_growth -= req->dd_growth;
|
||||
c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
|
||||
|
||||
ubifs_assert(c->bi.idx_growth >= 0);
|
||||
ubifs_assert(c->bi.data_growth >= 0);
|
||||
ubifs_assert(c->bi.dd_growth >= 0);
|
||||
ubifs_assert(c->bi.min_idx_lebs < c->main_lebs);
|
||||
ubifs_assert(!(c->bi.idx_growth & 7));
|
||||
ubifs_assert(!(c->bi.data_growth & 7));
|
||||
ubifs_assert(!(c->bi.dd_growth & 7));
|
||||
spin_unlock(&c->space_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_convert_page_budget - convert budget of a new page.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function converts budget which was allocated for a new page of data to
|
||||
* the budget of changing an existing page of data. The latter is smaller than
|
||||
* the former, so this function only does simple re-calculation and does not
|
||||
* involve any write-back.
|
||||
*/
|
||||
void ubifs_convert_page_budget(struct ubifs_info *c)
|
||||
{
|
||||
spin_lock(&c->space_lock);
|
||||
/* Release the index growth reservation */
|
||||
c->bi.idx_growth -= c->max_idx_node_sz << UBIFS_BLOCKS_PER_PAGE_SHIFT;
|
||||
/* Release the data growth reservation */
|
||||
c->bi.data_growth -= c->bi.page_budget;
|
||||
/* Increase the dirty data growth reservation instead */
|
||||
c->bi.dd_growth += c->bi.page_budget;
|
||||
/* And re-calculate the indexing space reservation */
|
||||
c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
|
||||
spin_unlock(&c->space_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_release_dirty_inode_budget - release dirty inode budget.
|
||||
* @c: UBIFS file-system description object
|
||||
* @ui: UBIFS inode to release the budget for
|
||||
*
|
||||
* This function releases budget corresponding to a dirty inode. It is usually
|
||||
* called when after the inode has been written to the media and marked as
|
||||
* clean. It also causes the "no space" flags to be cleared.
|
||||
*/
|
||||
void ubifs_release_dirty_inode_budget(struct ubifs_info *c,
|
||||
struct ubifs_inode *ui)
|
||||
{
|
||||
struct ubifs_budget_req req;
|
||||
|
||||
memset(&req, 0, sizeof(struct ubifs_budget_req));
|
||||
/* The "no space" flags will be cleared because dd_growth is > 0 */
|
||||
req.dd_growth = c->bi.inode_budget + ALIGN(ui->data_len, 8);
|
||||
ubifs_release_budget(c, &req);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ubifs_reported_space - calculate reported free space.
|
||||
* @c: the UBIFS file-system description object
|
||||
@ -111,3 +657,75 @@ long long ubifs_reported_space(const struct ubifs_info *c, long long free)
|
||||
free *= factor;
|
||||
return div_u64(free, divisor);
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* ubifs_get_free_space_nolock - return amount of free space.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function calculates amount of free space to report to user-space.
|
||||
*
|
||||
* Because UBIFS may introduce substantial overhead (the index, node headers,
|
||||
* alignment, wastage at the end of LEBs, etc), it cannot report real amount of
|
||||
* free flash space it has (well, because not all dirty space is reclaimable,
|
||||
* UBIFS does not actually know the real amount). If UBIFS did so, it would
|
||||
* bread user expectations about what free space is. Users seem to accustomed
|
||||
* to assume that if the file-system reports N bytes of free space, they would
|
||||
* be able to fit a file of N bytes to the FS. This almost works for
|
||||
* traditional file-systems, because they have way less overhead than UBIFS.
|
||||
* So, to keep users happy, UBIFS tries to take the overhead into account.
|
||||
*/
|
||||
long long ubifs_get_free_space_nolock(struct ubifs_info *c)
|
||||
{
|
||||
int rsvd_idx_lebs, lebs;
|
||||
long long available, outstanding, free;
|
||||
|
||||
ubifs_assert(c->bi.min_idx_lebs == ubifs_calc_min_idx_lebs(c));
|
||||
outstanding = c->bi.data_growth + c->bi.dd_growth;
|
||||
available = ubifs_calc_available(c, c->bi.min_idx_lebs);
|
||||
|
||||
/*
|
||||
* When reporting free space to user-space, UBIFS guarantees that it is
|
||||
* possible to write a file of free space size. This means that for
|
||||
* empty LEBs we may use more precise calculations than
|
||||
* 'ubifs_calc_available()' is using. Namely, we know that in empty
|
||||
* LEBs we would waste only @c->leb_overhead bytes, not @c->dark_wm.
|
||||
* Thus, amend the available space.
|
||||
*
|
||||
* Note, the calculations below are similar to what we have in
|
||||
* 'do_budget_space()', so refer there for comments.
|
||||
*/
|
||||
if (c->bi.min_idx_lebs > c->lst.idx_lebs)
|
||||
rsvd_idx_lebs = c->bi.min_idx_lebs - c->lst.idx_lebs;
|
||||
else
|
||||
rsvd_idx_lebs = 0;
|
||||
lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt -
|
||||
c->lst.taken_empty_lebs;
|
||||
lebs -= rsvd_idx_lebs;
|
||||
available += lebs * (c->dark_wm - c->leb_overhead);
|
||||
|
||||
if (available > outstanding)
|
||||
free = ubifs_reported_space(c, available - outstanding);
|
||||
else
|
||||
free = 0;
|
||||
return free;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_get_free_space - return amount of free space.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function calculates and returns amount of free space to report to
|
||||
* user-space.
|
||||
*/
|
||||
long long ubifs_get_free_space(struct ubifs_info *c)
|
||||
{
|
||||
long long free;
|
||||
|
||||
spin_lock(&c->space_lock);
|
||||
free = ubifs_get_free_space_nolock(c);
|
||||
spin_unlock(&c->space_lock);
|
||||
|
||||
return free;
|
||||
}
|
||||
#endif
|
||||
|
3117
fs/ubifs/debug.c
3117
fs/ubifs/debug.c
File diff suppressed because it is too large
Load Diff
568
fs/ubifs/debug.h
568
fs/ubifs/debug.h
@ -3,18 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2006-2008 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Authors: Artem Bityutskiy (Битюцкий Артём)
|
||||
* Adrian Hunter
|
||||
@ -23,19 +12,32 @@
|
||||
#ifndef __UBIFS_DEBUG_H__
|
||||
#define __UBIFS_DEBUG_H__
|
||||
|
||||
#ifdef CONFIG_UBIFS_FS_DEBUG
|
||||
#define __UBOOT__
|
||||
/* Checking helper functions */
|
||||
typedef int (*dbg_leaf_callback)(struct ubifs_info *c,
|
||||
struct ubifs_zbranch *zbr, void *priv);
|
||||
typedef int (*dbg_znode_callback)(struct ubifs_info *c,
|
||||
struct ubifs_znode *znode, void *priv);
|
||||
|
||||
/*
|
||||
* The UBIFS debugfs directory name pattern and maximum name length (3 for "ubi"
|
||||
* + 1 for "_" and plus 2x2 for 2 UBI numbers and 1 for the trailing zero byte.
|
||||
*/
|
||||
#define UBIFS_DFS_DIR_NAME "ubi%d_%d"
|
||||
#define UBIFS_DFS_DIR_LEN (3 + 1 + 2*2 + 1)
|
||||
|
||||
/**
|
||||
* ubifs_debug_info - per-FS debugging information.
|
||||
* @buf: a buffer of LEB size, used for various purposes
|
||||
* @old_zroot: old index root - used by 'dbg_check_old_index()'
|
||||
* @old_zroot_level: old index root level - used by 'dbg_check_old_index()'
|
||||
* @old_zroot_sqnum: old index root sqnum - used by 'dbg_check_old_index()'
|
||||
* @failure_mode: failure mode for recovery testing
|
||||
* @fail_delay: 0=>don't delay, 1=>delay a time, 2=>delay a number of calls
|
||||
* @fail_timeout: time in jiffies when delay of failure mode expires
|
||||
* @fail_cnt: current number of calls to failure mode I/O functions
|
||||
* @fail_cnt_max: number of calls by which to delay failure mode
|
||||
*
|
||||
* @pc_happened: non-zero if an emulated power cut happened
|
||||
* @pc_delay: 0=>don't delay, 1=>delay a time, 2=>delay a number of calls
|
||||
* @pc_timeout: time in jiffies when delay of failure mode expires
|
||||
* @pc_cnt: current number of calls to failure mode I/O functions
|
||||
* @pc_cnt_max: number of calls by which to delay failure mode
|
||||
*
|
||||
* @chk_lpt_sz: used by LPT tree size checker
|
||||
* @chk_lpt_sz2: used by LPT tree size checker
|
||||
* @chk_lpt_wastage: used by LPT tree size checker
|
||||
@ -45,24 +47,44 @@
|
||||
* @new_ihead_offs: used by debugging to check @c->ihead_offs
|
||||
*
|
||||
* @saved_lst: saved lprops statistics (used by 'dbg_save_space_info()')
|
||||
* @saved_free: saved free space (used by 'dbg_save_space_info()')
|
||||
* @saved_bi: saved budgeting information
|
||||
* @saved_free: saved amount of free space
|
||||
* @saved_idx_gc_cnt: saved value of @c->idx_gc_cnt
|
||||
*
|
||||
* dfs_dir_name: name of debugfs directory containing this file-system's files
|
||||
* dfs_dir: direntry object of the file-system debugfs directory
|
||||
* dfs_dump_lprops: "dump lprops" debugfs knob
|
||||
* dfs_dump_budg: "dump budgeting information" debugfs knob
|
||||
* dfs_dump_tnc: "dump TNC" debugfs knob
|
||||
* @chk_gen: if general extra checks are enabled
|
||||
* @chk_index: if index xtra checks are enabled
|
||||
* @chk_orph: if orphans extra checks are enabled
|
||||
* @chk_lprops: if lprops extra checks are enabled
|
||||
* @chk_fs: if UBIFS contents extra checks are enabled
|
||||
* @tst_rcvry: if UBIFS recovery testing mode enabled
|
||||
*
|
||||
* @dfs_dir_name: name of debugfs directory containing this file-system's files
|
||||
* @dfs_dir: direntry object of the file-system debugfs directory
|
||||
* @dfs_dump_lprops: "dump lprops" debugfs knob
|
||||
* @dfs_dump_budg: "dump budgeting information" debugfs knob
|
||||
* @dfs_dump_tnc: "dump TNC" debugfs knob
|
||||
* @dfs_chk_gen: debugfs knob to enable UBIFS general extra checks
|
||||
* @dfs_chk_index: debugfs knob to enable UBIFS index extra checks
|
||||
* @dfs_chk_orph: debugfs knob to enable UBIFS orphans extra checks
|
||||
* @dfs_chk_lprops: debugfs knob to enable UBIFS LEP properties extra checks
|
||||
* @dfs_chk_fs: debugfs knob to enable UBIFS contents extra checks
|
||||
* @dfs_tst_rcvry: debugfs knob to enable UBIFS recovery testing
|
||||
* @dfs_ro_error: debugfs knob to switch UBIFS to R/O mode (different to
|
||||
* re-mounting to R/O mode because it does not flush any buffers
|
||||
* and UBIFS just starts returning -EROFS on all write
|
||||
* operations)
|
||||
*/
|
||||
struct ubifs_debug_info {
|
||||
void *buf;
|
||||
struct ubifs_zbranch old_zroot;
|
||||
int old_zroot_level;
|
||||
unsigned long long old_zroot_sqnum;
|
||||
int failure_mode;
|
||||
int fail_delay;
|
||||
unsigned long fail_timeout;
|
||||
unsigned int fail_cnt;
|
||||
unsigned int fail_cnt_max;
|
||||
|
||||
int pc_happened;
|
||||
int pc_delay;
|
||||
unsigned long pc_timeout;
|
||||
unsigned int pc_cnt;
|
||||
unsigned int pc_cnt_max;
|
||||
|
||||
long long chk_lpt_sz;
|
||||
long long chk_lpt_sz2;
|
||||
long long chk_lpt_wastage;
|
||||
@ -72,321 +94,285 @@ struct ubifs_debug_info {
|
||||
int new_ihead_offs;
|
||||
|
||||
struct ubifs_lp_stats saved_lst;
|
||||
struct ubifs_budg_info saved_bi;
|
||||
long long saved_free;
|
||||
int saved_idx_gc_cnt;
|
||||
|
||||
char dfs_dir_name[100];
|
||||
unsigned int chk_gen:1;
|
||||
unsigned int chk_index:1;
|
||||
unsigned int chk_orph:1;
|
||||
unsigned int chk_lprops:1;
|
||||
unsigned int chk_fs:1;
|
||||
unsigned int tst_rcvry:1;
|
||||
|
||||
char dfs_dir_name[UBIFS_DFS_DIR_LEN + 1];
|
||||
struct dentry *dfs_dir;
|
||||
struct dentry *dfs_dump_lprops;
|
||||
struct dentry *dfs_dump_budg;
|
||||
struct dentry *dfs_dump_tnc;
|
||||
struct dentry *dfs_chk_gen;
|
||||
struct dentry *dfs_chk_index;
|
||||
struct dentry *dfs_chk_orph;
|
||||
struct dentry *dfs_chk_lprops;
|
||||
struct dentry *dfs_chk_fs;
|
||||
struct dentry *dfs_tst_rcvry;
|
||||
struct dentry *dfs_ro_error;
|
||||
};
|
||||
|
||||
#define UBIFS_DBG(op) op
|
||||
/**
|
||||
* ubifs_global_debug_info - global (not per-FS) UBIFS debugging information.
|
||||
*
|
||||
* @chk_gen: if general extra checks are enabled
|
||||
* @chk_index: if index xtra checks are enabled
|
||||
* @chk_orph: if orphans extra checks are enabled
|
||||
* @chk_lprops: if lprops extra checks are enabled
|
||||
* @chk_fs: if UBIFS contents extra checks are enabled
|
||||
* @tst_rcvry: if UBIFS recovery testing mode enabled
|
||||
*/
|
||||
struct ubifs_global_debug_info {
|
||||
unsigned int chk_gen:1;
|
||||
unsigned int chk_index:1;
|
||||
unsigned int chk_orph:1;
|
||||
unsigned int chk_lprops:1;
|
||||
unsigned int chk_fs:1;
|
||||
unsigned int tst_rcvry:1;
|
||||
};
|
||||
|
||||
#ifndef __UBOOT__
|
||||
#define ubifs_assert(expr) do { \
|
||||
if (unlikely(!(expr))) { \
|
||||
printk(KERN_CRIT "UBIFS assert failed in %s at %u (pid %d)\n", \
|
||||
__func__, __LINE__, 0); \
|
||||
dbg_dump_stack(); \
|
||||
pr_crit("UBIFS assert failed in %s at %u (pid %d)\n", \
|
||||
__func__, __LINE__, current->pid); \
|
||||
dump_stack(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ubifs_assert_cmt_locked(c) do { \
|
||||
if (unlikely(down_write_trylock(&(c)->commit_sem))) { \
|
||||
up_write(&(c)->commit_sem); \
|
||||
printk(KERN_CRIT "commit lock is not locked!\n"); \
|
||||
pr_crit("commit lock is not locked!\n"); \
|
||||
ubifs_assert(0); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define dbg_dump_stack() do { \
|
||||
if (!dbg_failure_mode) \
|
||||
#define ubifs_dbg_msg(type, fmt, ...) \
|
||||
pr_debug("UBIFS DBG " type " (pid %d): " fmt "\n", current->pid, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define DBG_KEY_BUF_LEN 48
|
||||
#define ubifs_dbg_msg_key(type, key, fmt, ...) do { \
|
||||
char __tmp_key_buf[DBG_KEY_BUF_LEN]; \
|
||||
pr_debug("UBIFS DBG " type " (pid %d): " fmt "%s\n", current->pid, \
|
||||
##__VA_ARGS__, \
|
||||
dbg_snprintf_key(c, key, __tmp_key_buf, DBG_KEY_BUF_LEN)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define ubifs_assert(expr) do { \
|
||||
if (unlikely(!(expr))) { \
|
||||
pr_crit("UBIFS assert failed in %s at %u\n", \
|
||||
__func__, __LINE__); \
|
||||
dump_stack(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Generic debugging messages */
|
||||
#define dbg_msg(fmt, ...) do { \
|
||||
spin_lock(&dbg_lock); \
|
||||
printk(KERN_DEBUG "UBIFS DBG (pid %d): %s: " fmt "\n", 0, \
|
||||
__func__, ##__VA_ARGS__); \
|
||||
spin_unlock(&dbg_lock); \
|
||||
#define ubifs_assert_cmt_locked(c) do { \
|
||||
if (unlikely(down_write_trylock(&(c)->commit_sem))) { \
|
||||
up_write(&(c)->commit_sem); \
|
||||
pr_crit("commit lock is not locked!\n"); \
|
||||
ubifs_assert(0); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define dbg_do_msg(typ, fmt, ...) do { \
|
||||
if (ubifs_msg_flags & typ) \
|
||||
dbg_msg(fmt, ##__VA_ARGS__); \
|
||||
#define ubifs_dbg_msg(type, fmt, ...) \
|
||||
pr_debug("UBIFS DBG " type ": " fmt "\n", \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define DBG_KEY_BUF_LEN 48
|
||||
#if defined CONFIG_MTD_DEBUG
|
||||
#define ubifs_dbg_msg_key(type, key, fmt, ...) do { \
|
||||
char __tmp_key_buf[DBG_KEY_BUF_LEN]; \
|
||||
pr_debug("UBIFS DBG " type ": " fmt "%s\n", \
|
||||
##__VA_ARGS__, \
|
||||
dbg_snprintf_key(c, key, __tmp_key_buf, DBG_KEY_BUF_LEN)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define ubifs_dbg_msg_key(type, key, fmt, ...) do { \
|
||||
pr_debug("UBIFS DBG\n"); \
|
||||
} while (0)
|
||||
|
||||
#define dbg_err(fmt, ...) do { \
|
||||
spin_lock(&dbg_lock); \
|
||||
ubifs_err(fmt, ##__VA_ARGS__); \
|
||||
spin_unlock(&dbg_lock); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
const char *dbg_key_str0(const struct ubifs_info *c,
|
||||
const union ubifs_key *key);
|
||||
const char *dbg_key_str1(const struct ubifs_info *c,
|
||||
const union ubifs_key *key);
|
||||
|
||||
/*
|
||||
* DBGKEY macros require @dbg_lock to be held, which it is in the dbg message
|
||||
* macros.
|
||||
*/
|
||||
#define DBGKEY(key) dbg_key_str0(c, (key))
|
||||
#define DBGKEY1(key) dbg_key_str1(c, (key))
|
||||
#endif
|
||||
|
||||
/* General messages */
|
||||
#define dbg_gen(fmt, ...) dbg_do_msg(UBIFS_MSG_GEN, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define dbg_gen(fmt, ...) ubifs_dbg_msg("gen", fmt, ##__VA_ARGS__)
|
||||
/* Additional journal messages */
|
||||
#define dbg_jnl(fmt, ...) dbg_do_msg(UBIFS_MSG_JNL, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define dbg_jnl(fmt, ...) ubifs_dbg_msg("jnl", fmt, ##__VA_ARGS__)
|
||||
#define dbg_jnlk(key, fmt, ...) \
|
||||
ubifs_dbg_msg_key("jnl", key, fmt, ##__VA_ARGS__)
|
||||
/* Additional TNC messages */
|
||||
#define dbg_tnc(fmt, ...) dbg_do_msg(UBIFS_MSG_TNC, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define dbg_tnc(fmt, ...) ubifs_dbg_msg("tnc", fmt, ##__VA_ARGS__)
|
||||
#define dbg_tnck(key, fmt, ...) \
|
||||
ubifs_dbg_msg_key("tnc", key, fmt, ##__VA_ARGS__)
|
||||
/* Additional lprops messages */
|
||||
#define dbg_lp(fmt, ...) dbg_do_msg(UBIFS_MSG_LP, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define dbg_lp(fmt, ...) ubifs_dbg_msg("lp", fmt, ##__VA_ARGS__)
|
||||
/* Additional LEB find messages */
|
||||
#define dbg_find(fmt, ...) dbg_do_msg(UBIFS_MSG_FIND, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define dbg_find(fmt, ...) ubifs_dbg_msg("find", fmt, ##__VA_ARGS__)
|
||||
/* Additional mount messages */
|
||||
#define dbg_mnt(fmt, ...) dbg_do_msg(UBIFS_MSG_MNT, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define dbg_mnt(fmt, ...) ubifs_dbg_msg("mnt", fmt, ##__VA_ARGS__)
|
||||
#define dbg_mntk(key, fmt, ...) \
|
||||
ubifs_dbg_msg_key("mnt", key, fmt, ##__VA_ARGS__)
|
||||
/* Additional I/O messages */
|
||||
#define dbg_io(fmt, ...) dbg_do_msg(UBIFS_MSG_IO, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define dbg_io(fmt, ...) ubifs_dbg_msg("io", fmt, ##__VA_ARGS__)
|
||||
/* Additional commit messages */
|
||||
#define dbg_cmt(fmt, ...) dbg_do_msg(UBIFS_MSG_CMT, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define dbg_cmt(fmt, ...) ubifs_dbg_msg("cmt", fmt, ##__VA_ARGS__)
|
||||
/* Additional budgeting messages */
|
||||
#define dbg_budg(fmt, ...) dbg_do_msg(UBIFS_MSG_BUDG, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define dbg_budg(fmt, ...) ubifs_dbg_msg("budg", fmt, ##__VA_ARGS__)
|
||||
/* Additional log messages */
|
||||
#define dbg_log(fmt, ...) dbg_do_msg(UBIFS_MSG_LOG, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define dbg_log(fmt, ...) ubifs_dbg_msg("log", fmt, ##__VA_ARGS__)
|
||||
/* Additional gc messages */
|
||||
#define dbg_gc(fmt, ...) dbg_do_msg(UBIFS_MSG_GC, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define dbg_gc(fmt, ...) ubifs_dbg_msg("gc", fmt, ##__VA_ARGS__)
|
||||
/* Additional scan messages */
|
||||
#define dbg_scan(fmt, ...) dbg_do_msg(UBIFS_MSG_SCAN, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define dbg_scan(fmt, ...) ubifs_dbg_msg("scan", fmt, ##__VA_ARGS__)
|
||||
/* Additional recovery messages */
|
||||
#define dbg_rcvry(fmt, ...) dbg_do_msg(UBIFS_MSG_RCVRY, fmt, ##__VA_ARGS__)
|
||||
#define dbg_rcvry(fmt, ...) ubifs_dbg_msg("rcvry", fmt, ##__VA_ARGS__)
|
||||
|
||||
/*
|
||||
* Debugging message type flags (must match msg_type_names in debug.c).
|
||||
*
|
||||
* UBIFS_MSG_GEN: general messages
|
||||
* UBIFS_MSG_JNL: journal messages
|
||||
* UBIFS_MSG_MNT: mount messages
|
||||
* UBIFS_MSG_CMT: commit messages
|
||||
* UBIFS_MSG_FIND: LEB find messages
|
||||
* UBIFS_MSG_BUDG: budgeting messages
|
||||
* UBIFS_MSG_GC: garbage collection messages
|
||||
* UBIFS_MSG_TNC: TNC messages
|
||||
* UBIFS_MSG_LP: lprops messages
|
||||
* UBIFS_MSG_IO: I/O messages
|
||||
* UBIFS_MSG_LOG: log messages
|
||||
* UBIFS_MSG_SCAN: scan messages
|
||||
* UBIFS_MSG_RCVRY: recovery messages
|
||||
*/
|
||||
enum {
|
||||
UBIFS_MSG_GEN = 0x1,
|
||||
UBIFS_MSG_JNL = 0x2,
|
||||
UBIFS_MSG_MNT = 0x4,
|
||||
UBIFS_MSG_CMT = 0x8,
|
||||
UBIFS_MSG_FIND = 0x10,
|
||||
UBIFS_MSG_BUDG = 0x20,
|
||||
UBIFS_MSG_GC = 0x40,
|
||||
UBIFS_MSG_TNC = 0x80,
|
||||
UBIFS_MSG_LP = 0x100,
|
||||
UBIFS_MSG_IO = 0x200,
|
||||
UBIFS_MSG_LOG = 0x400,
|
||||
UBIFS_MSG_SCAN = 0x800,
|
||||
UBIFS_MSG_RCVRY = 0x1000,
|
||||
};
|
||||
#ifndef __UBOOT__
|
||||
extern struct ubifs_global_debug_info ubifs_dbg;
|
||||
|
||||
/* Debugging message type flags for each default debug message level */
|
||||
#define UBIFS_MSG_LVL_0 0
|
||||
#define UBIFS_MSG_LVL_1 0x1
|
||||
#define UBIFS_MSG_LVL_2 0x7f
|
||||
#define UBIFS_MSG_LVL_3 0xffff
|
||||
static inline int dbg_is_chk_gen(const struct ubifs_info *c)
|
||||
{
|
||||
return !!(ubifs_dbg.chk_gen || c->dbg->chk_gen);
|
||||
}
|
||||
static inline int dbg_is_chk_index(const struct ubifs_info *c)
|
||||
{
|
||||
return !!(ubifs_dbg.chk_index || c->dbg->chk_index);
|
||||
}
|
||||
static inline int dbg_is_chk_orph(const struct ubifs_info *c)
|
||||
{
|
||||
return !!(ubifs_dbg.chk_orph || c->dbg->chk_orph);
|
||||
}
|
||||
static inline int dbg_is_chk_lprops(const struct ubifs_info *c)
|
||||
{
|
||||
return !!(ubifs_dbg.chk_lprops || c->dbg->chk_lprops);
|
||||
}
|
||||
static inline int dbg_is_chk_fs(const struct ubifs_info *c)
|
||||
{
|
||||
return !!(ubifs_dbg.chk_fs || c->dbg->chk_fs);
|
||||
}
|
||||
static inline int dbg_is_tst_rcvry(const struct ubifs_info *c)
|
||||
{
|
||||
return !!(ubifs_dbg.tst_rcvry || c->dbg->tst_rcvry);
|
||||
}
|
||||
static inline int dbg_is_power_cut(const struct ubifs_info *c)
|
||||
{
|
||||
return !!c->dbg->pc_happened;
|
||||
}
|
||||
|
||||
/*
|
||||
* Debugging check flags (must match chk_names in debug.c).
|
||||
*
|
||||
* UBIFS_CHK_GEN: general checks
|
||||
* UBIFS_CHK_TNC: check TNC
|
||||
* UBIFS_CHK_IDX_SZ: check index size
|
||||
* UBIFS_CHK_ORPH: check orphans
|
||||
* UBIFS_CHK_OLD_IDX: check the old index
|
||||
* UBIFS_CHK_LPROPS: check lprops
|
||||
* UBIFS_CHK_FS: check the file-system
|
||||
*/
|
||||
enum {
|
||||
UBIFS_CHK_GEN = 0x1,
|
||||
UBIFS_CHK_TNC = 0x2,
|
||||
UBIFS_CHK_IDX_SZ = 0x4,
|
||||
UBIFS_CHK_ORPH = 0x8,
|
||||
UBIFS_CHK_OLD_IDX = 0x10,
|
||||
UBIFS_CHK_LPROPS = 0x20,
|
||||
UBIFS_CHK_FS = 0x40,
|
||||
};
|
||||
|
||||
/*
|
||||
* Special testing flags (must match tst_names in debug.c).
|
||||
*
|
||||
* UBIFS_TST_FORCE_IN_THE_GAPS: force the use of in-the-gaps method
|
||||
* UBIFS_TST_RCVRY: failure mode for recovery testing
|
||||
*/
|
||||
enum {
|
||||
UBIFS_TST_FORCE_IN_THE_GAPS = 0x2,
|
||||
UBIFS_TST_RCVRY = 0x4,
|
||||
};
|
||||
|
||||
#if CONFIG_UBIFS_FS_DEBUG_MSG_LVL == 1
|
||||
#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_1
|
||||
#elif CONFIG_UBIFS_FS_DEBUG_MSG_LVL == 2
|
||||
#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_2
|
||||
#elif CONFIG_UBIFS_FS_DEBUG_MSG_LVL == 3
|
||||
#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_3
|
||||
int ubifs_debugging_init(struct ubifs_info *c);
|
||||
void ubifs_debugging_exit(struct ubifs_info *c);
|
||||
#else
|
||||
#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_UBIFS_FS_DEBUG_CHKS
|
||||
#define UBIFS_CHK_FLAGS_DEFAULT 0xffffffff
|
||||
#else
|
||||
#define UBIFS_CHK_FLAGS_DEFAULT 0
|
||||
#endif
|
||||
|
||||
#define dbg_ntype(type) ""
|
||||
#define dbg_cstate(cmt_state) ""
|
||||
#define dbg_get_key_dump(c, key) ({})
|
||||
#define dbg_dump_inode(c, inode) ({})
|
||||
#define dbg_dump_node(c, node) ({})
|
||||
#define dbg_dump_budget_req(req) ({})
|
||||
#define dbg_dump_lstats(lst) ({})
|
||||
#define dbg_dump_budg(c) ({})
|
||||
#define dbg_dump_lprop(c, lp) ({})
|
||||
#define dbg_dump_lprops(c) ({})
|
||||
#define dbg_dump_lpt_info(c) ({})
|
||||
#define dbg_dump_leb(c, lnum) ({})
|
||||
#define dbg_dump_znode(c, znode) ({})
|
||||
#define dbg_dump_heap(c, heap, cat) ({})
|
||||
#define dbg_dump_pnode(c, pnode, parent, iip) ({})
|
||||
#define dbg_dump_tnc(c) ({})
|
||||
#define dbg_dump_index(c) ({})
|
||||
|
||||
#define dbg_walk_index(c, leaf_cb, znode_cb, priv) 0
|
||||
#define dbg_old_index_check_init(c, zroot) 0
|
||||
#define dbg_check_old_index(c, zroot) 0
|
||||
#define dbg_check_cats(c) 0
|
||||
#define dbg_check_ltab(c) 0
|
||||
#define dbg_chk_lpt_free_spc(c) 0
|
||||
#define dbg_chk_lpt_sz(c, action, len) 0
|
||||
#define dbg_check_synced_i_size(inode) 0
|
||||
#define dbg_check_dir_size(c, dir) 0
|
||||
#define dbg_check_tnc(c, x) 0
|
||||
#define dbg_check_idx_size(c, idx_size) 0
|
||||
#define dbg_check_filesystem(c) 0
|
||||
#define dbg_check_heap(c, heap, cat, add_pos) ({})
|
||||
#define dbg_check_lprops(c) 0
|
||||
#define dbg_check_lpt_nodes(c, cnode, row, col) 0
|
||||
#define dbg_force_in_the_gaps_enabled 0
|
||||
#define dbg_force_in_the_gaps() 0
|
||||
#define dbg_failure_mode 0
|
||||
#define dbg_failure_mode_registration(c) ({})
|
||||
#define dbg_failure_mode_deregistration(c) ({})
|
||||
static inline int dbg_is_chk_gen(const struct ubifs_info *c)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int dbg_is_chk_index(const struct ubifs_info *c)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int dbg_is_chk_orph(const struct ubifs_info *c)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int dbg_is_chk_lprops(const struct ubifs_info *c)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int dbg_is_chk_fs(const struct ubifs_info *c)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int dbg_is_tst_rcvry(const struct ubifs_info *c)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int dbg_is_power_cut(const struct ubifs_info *c)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ubifs_debugging_init(struct ubifs_info *c);
|
||||
void ubifs_debugging_exit(struct ubifs_info *c);
|
||||
|
||||
#else /* !CONFIG_UBIFS_FS_DEBUG */
|
||||
#endif
|
||||
|
||||
#define UBIFS_DBG(op)
|
||||
/* Dump functions */
|
||||
const char *dbg_ntype(int type);
|
||||
const char *dbg_cstate(int cmt_state);
|
||||
const char *dbg_jhead(int jhead);
|
||||
const char *dbg_get_key_dump(const struct ubifs_info *c,
|
||||
const union ubifs_key *key);
|
||||
const char *dbg_snprintf_key(const struct ubifs_info *c,
|
||||
const union ubifs_key *key, char *buffer, int len);
|
||||
void ubifs_dump_inode(struct ubifs_info *c, const struct inode *inode);
|
||||
void ubifs_dump_node(const struct ubifs_info *c, const void *node);
|
||||
void ubifs_dump_budget_req(const struct ubifs_budget_req *req);
|
||||
void ubifs_dump_lstats(const struct ubifs_lp_stats *lst);
|
||||
void ubifs_dump_budg(struct ubifs_info *c, const struct ubifs_budg_info *bi);
|
||||
void ubifs_dump_lprop(const struct ubifs_info *c,
|
||||
const struct ubifs_lprops *lp);
|
||||
void ubifs_dump_lprops(struct ubifs_info *c);
|
||||
void ubifs_dump_lpt_info(struct ubifs_info *c);
|
||||
void ubifs_dump_leb(const struct ubifs_info *c, int lnum);
|
||||
void ubifs_dump_sleb(const struct ubifs_info *c,
|
||||
const struct ubifs_scan_leb *sleb, int offs);
|
||||
void ubifs_dump_znode(const struct ubifs_info *c,
|
||||
const struct ubifs_znode *znode);
|
||||
void ubifs_dump_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
|
||||
int cat);
|
||||
void ubifs_dump_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode,
|
||||
struct ubifs_nnode *parent, int iip);
|
||||
void ubifs_dump_tnc(struct ubifs_info *c);
|
||||
void ubifs_dump_index(struct ubifs_info *c);
|
||||
void ubifs_dump_lpt_lebs(const struct ubifs_info *c);
|
||||
|
||||
/* Use "if (0)" to make compiler check arguments even if debugging is off */
|
||||
#define ubifs_assert(expr) do { \
|
||||
if (0 && (expr)) \
|
||||
printk(KERN_CRIT "UBIFS assert failed in %s at %u (pid %d)\n", \
|
||||
__func__, __LINE__, 0); \
|
||||
} while (0)
|
||||
int dbg_walk_index(struct ubifs_info *c, dbg_leaf_callback leaf_cb,
|
||||
dbg_znode_callback znode_cb, void *priv);
|
||||
|
||||
#define dbg_err(fmt, ...) do { \
|
||||
if (0) \
|
||||
ubifs_err(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
/* Checking functions */
|
||||
void dbg_save_space_info(struct ubifs_info *c);
|
||||
int dbg_check_space_info(struct ubifs_info *c);
|
||||
int dbg_check_lprops(struct ubifs_info *c);
|
||||
int dbg_old_index_check_init(struct ubifs_info *c, struct ubifs_zbranch *zroot);
|
||||
int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot);
|
||||
int dbg_check_cats(struct ubifs_info *c);
|
||||
int dbg_check_ltab(struct ubifs_info *c);
|
||||
int dbg_chk_lpt_free_spc(struct ubifs_info *c);
|
||||
int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len);
|
||||
int dbg_check_synced_i_size(const struct ubifs_info *c, struct inode *inode);
|
||||
int dbg_check_dir(struct ubifs_info *c, const struct inode *dir);
|
||||
int dbg_check_tnc(struct ubifs_info *c, int extra);
|
||||
int dbg_check_idx_size(struct ubifs_info *c, long long idx_size);
|
||||
int dbg_check_filesystem(struct ubifs_info *c);
|
||||
void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat,
|
||||
int add_pos);
|
||||
int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode,
|
||||
int row, int col);
|
||||
int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode,
|
||||
loff_t size);
|
||||
int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head);
|
||||
int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head);
|
||||
|
||||
#define dbg_msg(fmt, ...) do { \
|
||||
if (0) \
|
||||
printk(KERN_DEBUG "UBIFS DBG (pid %d): %s: " fmt "\n", \
|
||||
0, __func__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
int dbg_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs,
|
||||
int len);
|
||||
int dbg_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len);
|
||||
int dbg_leb_unmap(struct ubifs_info *c, int lnum);
|
||||
int dbg_leb_map(struct ubifs_info *c, int lnum);
|
||||
|
||||
#define dbg_dump_stack()
|
||||
#define ubifs_assert_cmt_locked(c)
|
||||
|
||||
#define dbg_gen(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#define dbg_jnl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#define dbg_tnc(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#define dbg_lp(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#define dbg_find(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#define dbg_mnt(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#define dbg_cmt(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#define dbg_budg(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#define dbg_log(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#define dbg_gc(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#define dbg_scan(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#define dbg_rcvry(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
|
||||
#define DBGKEY(key) ((char *)(key))
|
||||
#define DBGKEY1(key) ((char *)(key))
|
||||
|
||||
#define ubifs_debugging_init(c) 0
|
||||
#define ubifs_debugging_exit(c) ({})
|
||||
|
||||
#define dbg_ntype(type) ""
|
||||
#define dbg_cstate(cmt_state) ""
|
||||
#define dbg_get_key_dump(c, key) ({})
|
||||
#define dbg_dump_inode(c, inode) ({})
|
||||
#define dbg_dump_node(c, node) ({})
|
||||
#define dbg_dump_budget_req(req) ({})
|
||||
#define dbg_dump_lstats(lst) ({})
|
||||
#define dbg_dump_budg(c) ({})
|
||||
#define dbg_dump_lprop(c, lp) ({})
|
||||
#define dbg_dump_lprops(c) ({})
|
||||
#define dbg_dump_lpt_info(c) ({})
|
||||
#define dbg_dump_leb(c, lnum) ({})
|
||||
#define dbg_dump_znode(c, znode) ({})
|
||||
#define dbg_dump_heap(c, heap, cat) ({})
|
||||
#define dbg_dump_pnode(c, pnode, parent, iip) ({})
|
||||
#define dbg_dump_tnc(c) ({})
|
||||
#define dbg_dump_index(c) ({})
|
||||
|
||||
#define dbg_walk_index(c, leaf_cb, znode_cb, priv) 0
|
||||
#define dbg_old_index_check_init(c, zroot) 0
|
||||
#define dbg_check_old_index(c, zroot) 0
|
||||
#define dbg_check_cats(c) 0
|
||||
#define dbg_check_ltab(c) 0
|
||||
#define dbg_chk_lpt_free_spc(c) 0
|
||||
#define dbg_chk_lpt_sz(c, action, len) 0
|
||||
#define dbg_check_synced_i_size(inode) 0
|
||||
#define dbg_check_dir_size(c, dir) 0
|
||||
#define dbg_check_tnc(c, x) 0
|
||||
#define dbg_check_idx_size(c, idx_size) 0
|
||||
#define dbg_check_filesystem(c) 0
|
||||
#define dbg_check_heap(c, heap, cat, add_pos) ({})
|
||||
#define dbg_check_lprops(c) 0
|
||||
#define dbg_check_lpt_nodes(c, cnode, row, col) 0
|
||||
#define dbg_force_in_the_gaps_enabled 0
|
||||
#define dbg_force_in_the_gaps() 0
|
||||
#define dbg_failure_mode 0
|
||||
#define dbg_failure_mode_registration(c) ({})
|
||||
#define dbg_failure_mode_deregistration(c) ({})
|
||||
|
||||
#endif /* !CONFIG_UBIFS_FS_DEBUG */
|
||||
/* Debugfs-related stuff */
|
||||
int dbg_debugfs_init(void);
|
||||
void dbg_debugfs_exit(void);
|
||||
int dbg_debugfs_init_fs(struct ubifs_info *c);
|
||||
void dbg_debugfs_exit_fs(struct ubifs_info *c);
|
||||
|
||||
#endif /* !__UBIFS_DEBUG_H__ */
|
||||
|
897
fs/ubifs/io.c
897
fs/ubifs/io.c
@ -4,18 +4,7 @@
|
||||
* Copyright (C) 2006-2008 Nokia Corporation.
|
||||
* Copyright (C) 2006, 2007 University of Szeged, Hungary
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Authors: Artem Bityutskiy (Битюцкий Артём)
|
||||
* Adrian Hunter
|
||||
@ -31,6 +20,26 @@
|
||||
* buffer is full or when it is not used for some time (by timer). This is
|
||||
* similar to the mechanism is used by JFFS2.
|
||||
*
|
||||
* UBIFS distinguishes between minimum write size (@c->min_io_size) and maximum
|
||||
* write size (@c->max_write_size). The latter is the maximum amount of bytes
|
||||
* the underlying flash is able to program at a time, and writing in
|
||||
* @c->max_write_size units should presumably be faster. Obviously,
|
||||
* @c->min_io_size <= @c->max_write_size. Write-buffers are of
|
||||
* @c->max_write_size bytes in size for maximum performance. However, when a
|
||||
* write-buffer is flushed, only the portion of it (aligned to @c->min_io_size
|
||||
* boundary) which contains data is written, not the whole write-buffer,
|
||||
* because this is more space-efficient.
|
||||
*
|
||||
* This optimization adds few complications to the code. Indeed, on the one
|
||||
* hand, we want to write in optimal @c->max_write_size bytes chunks, which
|
||||
* also means aligning writes at the @c->max_write_size bytes offsets. On the
|
||||
* other hand, we do not want to waste space when synchronizing the write
|
||||
* buffer, so during synchronization we writes in smaller chunks. And this makes
|
||||
* the next write offset to be not aligned to @c->max_write_size bytes. So the
|
||||
* have to make sure that the write-buffer offset (@wbuf->offs) becomes aligned
|
||||
* to @c->max_write_size bytes again. We do this by temporarily shrinking
|
||||
* write-buffer size (@wbuf->size).
|
||||
*
|
||||
* Write-buffers are defined by 'struct ubifs_wbuf' objects and protected by
|
||||
* mutexes defined inside these objects. Since sometimes upper-level code
|
||||
* has to lock the write-buffer (e.g. journal space reservation code), many
|
||||
@ -46,10 +55,18 @@
|
||||
* UBIFS uses padding when it pads to the next min. I/O unit. In this case it
|
||||
* uses padding nodes or padding bytes, if the padding node does not fit.
|
||||
*
|
||||
* All UBIFS nodes are protected by CRC checksums and UBIFS checks all nodes
|
||||
* every time they are read from the flash media.
|
||||
* All UBIFS nodes are protected by CRC checksums and UBIFS checks CRC when
|
||||
* they are read from the flash media.
|
||||
*/
|
||||
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/slab.h>
|
||||
#else
|
||||
#include <linux/compat.h>
|
||||
#include <linux/err.h>
|
||||
#endif
|
||||
#include "ubifs.h"
|
||||
|
||||
/**
|
||||
@ -59,14 +76,131 @@
|
||||
*/
|
||||
void ubifs_ro_mode(struct ubifs_info *c, int err)
|
||||
{
|
||||
if (!c->ro_media) {
|
||||
c->ro_media = 1;
|
||||
if (!c->ro_error) {
|
||||
c->ro_error = 1;
|
||||
c->no_chk_data_crc = 0;
|
||||
c->vfs_sb->s_flags |= MS_RDONLY;
|
||||
ubifs_warn("switched to read-only mode, error %d", err);
|
||||
dbg_dump_stack();
|
||||
dump_stack();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Below are simple wrappers over UBI I/O functions which include some
|
||||
* additional checks and UBIFS debugging stuff. See corresponding UBI function
|
||||
* for more information.
|
||||
*/
|
||||
|
||||
int ubifs_leb_read(const struct ubifs_info *c, int lnum, void *buf, int offs,
|
||||
int len, int even_ebadmsg)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ubi_read(c->ubi, lnum, buf, offs, len);
|
||||
/*
|
||||
* In case of %-EBADMSG print the error message only if the
|
||||
* @even_ebadmsg is true.
|
||||
*/
|
||||
if (err && (err != -EBADMSG || even_ebadmsg)) {
|
||||
ubifs_err("reading %d bytes from LEB %d:%d failed, error %d",
|
||||
len, lnum, offs, err);
|
||||
dump_stack();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int ubifs_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs,
|
||||
int len)
|
||||
{
|
||||
int err;
|
||||
|
||||
ubifs_assert(!c->ro_media && !c->ro_mount);
|
||||
if (c->ro_error)
|
||||
return -EROFS;
|
||||
if (!dbg_is_tst_rcvry(c))
|
||||
err = ubi_leb_write(c->ubi, lnum, buf, offs, len);
|
||||
else
|
||||
err = dbg_leb_write(c, lnum, buf, offs, len);
|
||||
if (err) {
|
||||
ubifs_err("writing %d bytes to LEB %d:%d failed, error %d",
|
||||
len, lnum, offs, err);
|
||||
ubifs_ro_mode(c, err);
|
||||
dump_stack();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int ubifs_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len)
|
||||
{
|
||||
int err;
|
||||
|
||||
ubifs_assert(!c->ro_media && !c->ro_mount);
|
||||
if (c->ro_error)
|
||||
return -EROFS;
|
||||
if (!dbg_is_tst_rcvry(c))
|
||||
err = ubi_leb_change(c->ubi, lnum, buf, len);
|
||||
else
|
||||
err = dbg_leb_change(c, lnum, buf, len);
|
||||
if (err) {
|
||||
ubifs_err("changing %d bytes in LEB %d failed, error %d",
|
||||
len, lnum, err);
|
||||
ubifs_ro_mode(c, err);
|
||||
dump_stack();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int ubifs_leb_unmap(struct ubifs_info *c, int lnum)
|
||||
{
|
||||
int err;
|
||||
|
||||
ubifs_assert(!c->ro_media && !c->ro_mount);
|
||||
if (c->ro_error)
|
||||
return -EROFS;
|
||||
if (!dbg_is_tst_rcvry(c))
|
||||
err = ubi_leb_unmap(c->ubi, lnum);
|
||||
else
|
||||
err = dbg_leb_unmap(c, lnum);
|
||||
if (err) {
|
||||
ubifs_err("unmap LEB %d failed, error %d", lnum, err);
|
||||
ubifs_ro_mode(c, err);
|
||||
dump_stack();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int ubifs_leb_map(struct ubifs_info *c, int lnum)
|
||||
{
|
||||
int err;
|
||||
|
||||
ubifs_assert(!c->ro_media && !c->ro_mount);
|
||||
if (c->ro_error)
|
||||
return -EROFS;
|
||||
if (!dbg_is_tst_rcvry(c))
|
||||
err = ubi_leb_map(c->ubi, lnum);
|
||||
else
|
||||
err = dbg_leb_map(c, lnum);
|
||||
if (err) {
|
||||
ubifs_err("mapping LEB %d failed, error %d", lnum, err);
|
||||
ubifs_ro_mode(c, err);
|
||||
dump_stack();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int ubifs_is_mapped(const struct ubifs_info *c, int lnum)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ubi_is_mapped(c->ubi, lnum);
|
||||
if (err < 0) {
|
||||
ubifs_err("ubi_is_mapped failed for LEB %d, error %d",
|
||||
lnum, err);
|
||||
dump_stack();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_check_node - check node.
|
||||
* @c: UBIFS file-system description object
|
||||
@ -85,8 +219,12 @@ void ubifs_ro_mode(struct ubifs_info *c, int err)
|
||||
* This function may skip data nodes CRC checking if @c->no_chk_data_crc is
|
||||
* true, which is controlled by corresponding UBIFS mount option. However, if
|
||||
* @must_chk_crc is true, then @c->no_chk_data_crc is ignored and CRC is
|
||||
* checked. Similarly, if @c->always_chk_crc is true, @c->no_chk_data_crc is
|
||||
* ignored and CRC is checked.
|
||||
* checked. Similarly, if @c->mounting or @c->remounting_rw is true (we are
|
||||
* mounting or re-mounting to R/W mode), @c->no_chk_data_crc is ignored and CRC
|
||||
* is checked. This is because during mounting or re-mounting from R/O mode to
|
||||
* R/W mode we may read journal nodes (when replying the journal or doing the
|
||||
* recovery) and the journal nodes may potentially be corrupted, so checking is
|
||||
* required.
|
||||
*
|
||||
* This function returns zero in case of success and %-EUCLEAN in case of bad
|
||||
* CRC or magic.
|
||||
@ -128,8 +266,8 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum,
|
||||
node_len > c->ranges[type].max_len)
|
||||
goto out_len;
|
||||
|
||||
if (!must_chk_crc && type == UBIFS_DATA_NODE && !c->always_chk_crc &&
|
||||
c->no_chk_data_crc)
|
||||
if (!must_chk_crc && type == UBIFS_DATA_NODE && !c->mounting &&
|
||||
!c->remounting_rw && c->no_chk_data_crc)
|
||||
return 0;
|
||||
|
||||
crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8);
|
||||
@ -150,8 +288,8 @@ out_len:
|
||||
out:
|
||||
if (!quiet) {
|
||||
ubifs_err("bad node at LEB %d:%d", lnum, offs);
|
||||
dbg_dump_node(c, buf);
|
||||
dbg_dump_stack();
|
||||
ubifs_dump_node(c, buf);
|
||||
dump_stack();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
@ -256,6 +394,571 @@ void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_prep_grp_node - prepare node of a group to be written to flash.
|
||||
* @c: UBIFS file-system description object
|
||||
* @node: the node to pad
|
||||
* @len: node length
|
||||
* @last: indicates the last node of the group
|
||||
*
|
||||
* This function prepares node at @node to be written to the media - it
|
||||
* calculates node CRC and fills the common header.
|
||||
*/
|
||||
void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last)
|
||||
{
|
||||
uint32_t crc;
|
||||
struct ubifs_ch *ch = node;
|
||||
unsigned long long sqnum = next_sqnum(c);
|
||||
|
||||
ubifs_assert(len >= UBIFS_CH_SZ);
|
||||
|
||||
ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC);
|
||||
ch->len = cpu_to_le32(len);
|
||||
if (last)
|
||||
ch->group_type = UBIFS_LAST_OF_NODE_GROUP;
|
||||
else
|
||||
ch->group_type = UBIFS_IN_NODE_GROUP;
|
||||
ch->sqnum = cpu_to_le64(sqnum);
|
||||
ch->padding[0] = ch->padding[1] = 0;
|
||||
crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
|
||||
ch->crc = cpu_to_le32(crc);
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* wbuf_timer_callback - write-buffer timer callback function.
|
||||
* @data: timer data (write-buffer descriptor)
|
||||
*
|
||||
* This function is called when the write-buffer timer expires.
|
||||
*/
|
||||
static enum hrtimer_restart wbuf_timer_callback_nolock(struct hrtimer *timer)
|
||||
{
|
||||
struct ubifs_wbuf *wbuf = container_of(timer, struct ubifs_wbuf, timer);
|
||||
|
||||
dbg_io("jhead %s", dbg_jhead(wbuf->jhead));
|
||||
wbuf->need_sync = 1;
|
||||
wbuf->c->need_wbuf_sync = 1;
|
||||
ubifs_wake_up_bgt(wbuf->c);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
/**
|
||||
* new_wbuf_timer - start new write-buffer timer.
|
||||
* @wbuf: write-buffer descriptor
|
||||
*/
|
||||
static void new_wbuf_timer_nolock(struct ubifs_wbuf *wbuf)
|
||||
{
|
||||
ubifs_assert(!hrtimer_active(&wbuf->timer));
|
||||
|
||||
if (wbuf->no_timer)
|
||||
return;
|
||||
dbg_io("set timer for jhead %s, %llu-%llu millisecs",
|
||||
dbg_jhead(wbuf->jhead),
|
||||
div_u64(ktime_to_ns(wbuf->softlimit), USEC_PER_SEC),
|
||||
div_u64(ktime_to_ns(wbuf->softlimit) + wbuf->delta,
|
||||
USEC_PER_SEC));
|
||||
hrtimer_start_range_ns(&wbuf->timer, wbuf->softlimit, wbuf->delta,
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* cancel_wbuf_timer - cancel write-buffer timer.
|
||||
* @wbuf: write-buffer descriptor
|
||||
*/
|
||||
static void cancel_wbuf_timer_nolock(struct ubifs_wbuf *wbuf)
|
||||
{
|
||||
if (wbuf->no_timer)
|
||||
return;
|
||||
wbuf->need_sync = 0;
|
||||
#ifndef __UBOOT__
|
||||
hrtimer_cancel(&wbuf->timer);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_wbuf_sync_nolock - synchronize write-buffer.
|
||||
* @wbuf: write-buffer to synchronize
|
||||
*
|
||||
* This function synchronizes write-buffer @buf and returns zero in case of
|
||||
* success or a negative error code in case of failure.
|
||||
*
|
||||
* Note, although write-buffers are of @c->max_write_size, this function does
|
||||
* not necessarily writes all @c->max_write_size bytes to the flash. Instead,
|
||||
* if the write-buffer is only partially filled with data, only the used part
|
||||
* of the write-buffer (aligned on @c->min_io_size boundary) is synchronized.
|
||||
* This way we waste less space.
|
||||
*/
|
||||
int ubifs_wbuf_sync_nolock(struct ubifs_wbuf *wbuf)
|
||||
{
|
||||
struct ubifs_info *c = wbuf->c;
|
||||
int err, dirt, sync_len;
|
||||
|
||||
cancel_wbuf_timer_nolock(wbuf);
|
||||
if (!wbuf->used || wbuf->lnum == -1)
|
||||
/* Write-buffer is empty or not seeked */
|
||||
return 0;
|
||||
|
||||
dbg_io("LEB %d:%d, %d bytes, jhead %s",
|
||||
wbuf->lnum, wbuf->offs, wbuf->used, dbg_jhead(wbuf->jhead));
|
||||
ubifs_assert(!(wbuf->avail & 7));
|
||||
ubifs_assert(wbuf->offs + wbuf->size <= c->leb_size);
|
||||
ubifs_assert(wbuf->size >= c->min_io_size);
|
||||
ubifs_assert(wbuf->size <= c->max_write_size);
|
||||
ubifs_assert(wbuf->size % c->min_io_size == 0);
|
||||
ubifs_assert(!c->ro_media && !c->ro_mount);
|
||||
if (c->leb_size - wbuf->offs >= c->max_write_size)
|
||||
ubifs_assert(!((wbuf->offs + wbuf->size) % c->max_write_size));
|
||||
|
||||
if (c->ro_error)
|
||||
return -EROFS;
|
||||
|
||||
/*
|
||||
* Do not write whole write buffer but write only the minimum necessary
|
||||
* amount of min. I/O units.
|
||||
*/
|
||||
sync_len = ALIGN(wbuf->used, c->min_io_size);
|
||||
dirt = sync_len - wbuf->used;
|
||||
if (dirt)
|
||||
ubifs_pad(c, wbuf->buf + wbuf->used, dirt);
|
||||
err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf, wbuf->offs, sync_len);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
spin_lock(&wbuf->lock);
|
||||
wbuf->offs += sync_len;
|
||||
/*
|
||||
* Now @wbuf->offs is not necessarily aligned to @c->max_write_size.
|
||||
* But our goal is to optimize writes and make sure we write in
|
||||
* @c->max_write_size chunks and to @c->max_write_size-aligned offset.
|
||||
* Thus, if @wbuf->offs is not aligned to @c->max_write_size now, make
|
||||
* sure that @wbuf->offs + @wbuf->size is aligned to
|
||||
* @c->max_write_size. This way we make sure that after next
|
||||
* write-buffer flush we are again at the optimal offset (aligned to
|
||||
* @c->max_write_size).
|
||||
*/
|
||||
if (c->leb_size - wbuf->offs < c->max_write_size)
|
||||
wbuf->size = c->leb_size - wbuf->offs;
|
||||
else if (wbuf->offs & (c->max_write_size - 1))
|
||||
wbuf->size = ALIGN(wbuf->offs, c->max_write_size) - wbuf->offs;
|
||||
else
|
||||
wbuf->size = c->max_write_size;
|
||||
wbuf->avail = wbuf->size;
|
||||
wbuf->used = 0;
|
||||
wbuf->next_ino = 0;
|
||||
spin_unlock(&wbuf->lock);
|
||||
|
||||
if (wbuf->sync_callback)
|
||||
err = wbuf->sync_callback(c, wbuf->lnum,
|
||||
c->leb_size - wbuf->offs, dirt);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_wbuf_seek_nolock - seek write-buffer.
|
||||
* @wbuf: write-buffer
|
||||
* @lnum: logical eraseblock number to seek to
|
||||
* @offs: logical eraseblock offset to seek to
|
||||
*
|
||||
* This function targets the write-buffer to logical eraseblock @lnum:@offs.
|
||||
* The write-buffer has to be empty. Returns zero in case of success and a
|
||||
* negative error code in case of failure.
|
||||
*/
|
||||
int ubifs_wbuf_seek_nolock(struct ubifs_wbuf *wbuf, int lnum, int offs)
|
||||
{
|
||||
const struct ubifs_info *c = wbuf->c;
|
||||
|
||||
dbg_io("LEB %d:%d, jhead %s", lnum, offs, dbg_jhead(wbuf->jhead));
|
||||
ubifs_assert(lnum >= 0 && lnum < c->leb_cnt);
|
||||
ubifs_assert(offs >= 0 && offs <= c->leb_size);
|
||||
ubifs_assert(offs % c->min_io_size == 0 && !(offs & 7));
|
||||
ubifs_assert(lnum != wbuf->lnum);
|
||||
ubifs_assert(wbuf->used == 0);
|
||||
|
||||
spin_lock(&wbuf->lock);
|
||||
wbuf->lnum = lnum;
|
||||
wbuf->offs = offs;
|
||||
if (c->leb_size - wbuf->offs < c->max_write_size)
|
||||
wbuf->size = c->leb_size - wbuf->offs;
|
||||
else if (wbuf->offs & (c->max_write_size - 1))
|
||||
wbuf->size = ALIGN(wbuf->offs, c->max_write_size) - wbuf->offs;
|
||||
else
|
||||
wbuf->size = c->max_write_size;
|
||||
wbuf->avail = wbuf->size;
|
||||
wbuf->used = 0;
|
||||
spin_unlock(&wbuf->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* ubifs_bg_wbufs_sync - synchronize write-buffers.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function is called by background thread to synchronize write-buffers.
|
||||
* Returns zero in case of success and a negative error code in case of
|
||||
* failure.
|
||||
*/
|
||||
int ubifs_bg_wbufs_sync(struct ubifs_info *c)
|
||||
{
|
||||
int err, i;
|
||||
|
||||
ubifs_assert(!c->ro_media && !c->ro_mount);
|
||||
if (!c->need_wbuf_sync)
|
||||
return 0;
|
||||
c->need_wbuf_sync = 0;
|
||||
|
||||
if (c->ro_error) {
|
||||
err = -EROFS;
|
||||
goto out_timers;
|
||||
}
|
||||
|
||||
dbg_io("synchronize");
|
||||
for (i = 0; i < c->jhead_cnt; i++) {
|
||||
struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf;
|
||||
|
||||
cond_resched();
|
||||
|
||||
/*
|
||||
* If the mutex is locked then wbuf is being changed, so
|
||||
* synchronization is not necessary.
|
||||
*/
|
||||
if (mutex_is_locked(&wbuf->io_mutex))
|
||||
continue;
|
||||
|
||||
mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
|
||||
if (!wbuf->need_sync) {
|
||||
mutex_unlock(&wbuf->io_mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
err = ubifs_wbuf_sync_nolock(wbuf);
|
||||
mutex_unlock(&wbuf->io_mutex);
|
||||
if (err) {
|
||||
ubifs_err("cannot sync write-buffer, error %d", err);
|
||||
ubifs_ro_mode(c, err);
|
||||
goto out_timers;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_timers:
|
||||
/* Cancel all timers to prevent repeated errors */
|
||||
for (i = 0; i < c->jhead_cnt; i++) {
|
||||
struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf;
|
||||
|
||||
mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
|
||||
cancel_wbuf_timer_nolock(wbuf);
|
||||
mutex_unlock(&wbuf->io_mutex);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_wbuf_write_nolock - write data to flash via write-buffer.
|
||||
* @wbuf: write-buffer
|
||||
* @buf: node to write
|
||||
* @len: node length
|
||||
*
|
||||
* This function writes data to flash via write-buffer @wbuf. This means that
|
||||
* the last piece of the node won't reach the flash media immediately if it
|
||||
* does not take whole max. write unit (@c->max_write_size). Instead, the node
|
||||
* will sit in RAM until the write-buffer is synchronized (e.g., by timer, or
|
||||
* because more data are appended to the write-buffer).
|
||||
*
|
||||
* This function returns zero in case of success and a negative error code in
|
||||
* case of failure. If the node cannot be written because there is no more
|
||||
* space in this logical eraseblock, %-ENOSPC is returned.
|
||||
*/
|
||||
int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
|
||||
{
|
||||
struct ubifs_info *c = wbuf->c;
|
||||
int err, written, n, aligned_len = ALIGN(len, 8);
|
||||
|
||||
dbg_io("%d bytes (%s) to jhead %s wbuf at LEB %d:%d", len,
|
||||
dbg_ntype(((struct ubifs_ch *)buf)->node_type),
|
||||
dbg_jhead(wbuf->jhead), wbuf->lnum, wbuf->offs + wbuf->used);
|
||||
ubifs_assert(len > 0 && wbuf->lnum >= 0 && wbuf->lnum < c->leb_cnt);
|
||||
ubifs_assert(wbuf->offs >= 0 && wbuf->offs % c->min_io_size == 0);
|
||||
ubifs_assert(!(wbuf->offs & 7) && wbuf->offs <= c->leb_size);
|
||||
ubifs_assert(wbuf->avail > 0 && wbuf->avail <= wbuf->size);
|
||||
ubifs_assert(wbuf->size >= c->min_io_size);
|
||||
ubifs_assert(wbuf->size <= c->max_write_size);
|
||||
ubifs_assert(wbuf->size % c->min_io_size == 0);
|
||||
ubifs_assert(mutex_is_locked(&wbuf->io_mutex));
|
||||
ubifs_assert(!c->ro_media && !c->ro_mount);
|
||||
ubifs_assert(!c->space_fixup);
|
||||
if (c->leb_size - wbuf->offs >= c->max_write_size)
|
||||
ubifs_assert(!((wbuf->offs + wbuf->size) % c->max_write_size));
|
||||
|
||||
if (c->leb_size - wbuf->offs - wbuf->used < aligned_len) {
|
||||
err = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cancel_wbuf_timer_nolock(wbuf);
|
||||
|
||||
if (c->ro_error)
|
||||
return -EROFS;
|
||||
|
||||
if (aligned_len <= wbuf->avail) {
|
||||
/*
|
||||
* The node is not very large and fits entirely within
|
||||
* write-buffer.
|
||||
*/
|
||||
memcpy(wbuf->buf + wbuf->used, buf, len);
|
||||
|
||||
if (aligned_len == wbuf->avail) {
|
||||
dbg_io("flush jhead %s wbuf to LEB %d:%d",
|
||||
dbg_jhead(wbuf->jhead), wbuf->lnum, wbuf->offs);
|
||||
err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf,
|
||||
wbuf->offs, wbuf->size);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
spin_lock(&wbuf->lock);
|
||||
wbuf->offs += wbuf->size;
|
||||
if (c->leb_size - wbuf->offs >= c->max_write_size)
|
||||
wbuf->size = c->max_write_size;
|
||||
else
|
||||
wbuf->size = c->leb_size - wbuf->offs;
|
||||
wbuf->avail = wbuf->size;
|
||||
wbuf->used = 0;
|
||||
wbuf->next_ino = 0;
|
||||
spin_unlock(&wbuf->lock);
|
||||
} else {
|
||||
spin_lock(&wbuf->lock);
|
||||
wbuf->avail -= aligned_len;
|
||||
wbuf->used += aligned_len;
|
||||
spin_unlock(&wbuf->lock);
|
||||
}
|
||||
|
||||
goto exit;
|
||||
}
|
||||
|
||||
written = 0;
|
||||
|
||||
if (wbuf->used) {
|
||||
/*
|
||||
* The node is large enough and does not fit entirely within
|
||||
* current available space. We have to fill and flush
|
||||
* write-buffer and switch to the next max. write unit.
|
||||
*/
|
||||
dbg_io("flush jhead %s wbuf to LEB %d:%d",
|
||||
dbg_jhead(wbuf->jhead), wbuf->lnum, wbuf->offs);
|
||||
memcpy(wbuf->buf + wbuf->used, buf, wbuf->avail);
|
||||
err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf, wbuf->offs,
|
||||
wbuf->size);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
wbuf->offs += wbuf->size;
|
||||
len -= wbuf->avail;
|
||||
aligned_len -= wbuf->avail;
|
||||
written += wbuf->avail;
|
||||
} else if (wbuf->offs & (c->max_write_size - 1)) {
|
||||
/*
|
||||
* The write-buffer offset is not aligned to
|
||||
* @c->max_write_size and @wbuf->size is less than
|
||||
* @c->max_write_size. Write @wbuf->size bytes to make sure the
|
||||
* following writes are done in optimal @c->max_write_size
|
||||
* chunks.
|
||||
*/
|
||||
dbg_io("write %d bytes to LEB %d:%d",
|
||||
wbuf->size, wbuf->lnum, wbuf->offs);
|
||||
err = ubifs_leb_write(c, wbuf->lnum, buf, wbuf->offs,
|
||||
wbuf->size);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
wbuf->offs += wbuf->size;
|
||||
len -= wbuf->size;
|
||||
aligned_len -= wbuf->size;
|
||||
written += wbuf->size;
|
||||
}
|
||||
|
||||
/*
|
||||
* The remaining data may take more whole max. write units, so write the
|
||||
* remains multiple to max. write unit size directly to the flash media.
|
||||
* We align node length to 8-byte boundary because we anyway flash wbuf
|
||||
* if the remaining space is less than 8 bytes.
|
||||
*/
|
||||
n = aligned_len >> c->max_write_shift;
|
||||
if (n) {
|
||||
n <<= c->max_write_shift;
|
||||
dbg_io("write %d bytes to LEB %d:%d", n, wbuf->lnum,
|
||||
wbuf->offs);
|
||||
err = ubifs_leb_write(c, wbuf->lnum, buf + written,
|
||||
wbuf->offs, n);
|
||||
if (err)
|
||||
goto out;
|
||||
wbuf->offs += n;
|
||||
aligned_len -= n;
|
||||
len -= n;
|
||||
written += n;
|
||||
}
|
||||
|
||||
spin_lock(&wbuf->lock);
|
||||
if (aligned_len)
|
||||
/*
|
||||
* And now we have what's left and what does not take whole
|
||||
* max. write unit, so write it to the write-buffer and we are
|
||||
* done.
|
||||
*/
|
||||
memcpy(wbuf->buf, buf + written, len);
|
||||
|
||||
if (c->leb_size - wbuf->offs >= c->max_write_size)
|
||||
wbuf->size = c->max_write_size;
|
||||
else
|
||||
wbuf->size = c->leb_size - wbuf->offs;
|
||||
wbuf->avail = wbuf->size - aligned_len;
|
||||
wbuf->used = aligned_len;
|
||||
wbuf->next_ino = 0;
|
||||
spin_unlock(&wbuf->lock);
|
||||
|
||||
exit:
|
||||
if (wbuf->sync_callback) {
|
||||
int free = c->leb_size - wbuf->offs - wbuf->used;
|
||||
|
||||
err = wbuf->sync_callback(c, wbuf->lnum, free, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (wbuf->used)
|
||||
new_wbuf_timer_nolock(wbuf);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
ubifs_err("cannot write %d bytes to LEB %d:%d, error %d",
|
||||
len, wbuf->lnum, wbuf->offs, err);
|
||||
ubifs_dump_node(c, buf);
|
||||
dump_stack();
|
||||
ubifs_dump_leb(c, wbuf->lnum);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_write_node - write node to the media.
|
||||
* @c: UBIFS file-system description object
|
||||
* @buf: the node to write
|
||||
* @len: node length
|
||||
* @lnum: logical eraseblock number
|
||||
* @offs: offset within the logical eraseblock
|
||||
*
|
||||
* This function automatically fills node magic number, assigns sequence
|
||||
* number, and calculates node CRC checksum. The length of the @buf buffer has
|
||||
* to be aligned to the minimal I/O unit size. This function automatically
|
||||
* appends padding node and padding bytes if needed. Returns zero in case of
|
||||
* success and a negative error code in case of failure.
|
||||
*/
|
||||
int ubifs_write_node(struct ubifs_info *c, void *buf, int len, int lnum,
|
||||
int offs)
|
||||
{
|
||||
int err, buf_len = ALIGN(len, c->min_io_size);
|
||||
|
||||
dbg_io("LEB %d:%d, %s, length %d (aligned %d)",
|
||||
lnum, offs, dbg_ntype(((struct ubifs_ch *)buf)->node_type), len,
|
||||
buf_len);
|
||||
ubifs_assert(lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
|
||||
ubifs_assert(offs % c->min_io_size == 0 && offs < c->leb_size);
|
||||
ubifs_assert(!c->ro_media && !c->ro_mount);
|
||||
ubifs_assert(!c->space_fixup);
|
||||
|
||||
if (c->ro_error)
|
||||
return -EROFS;
|
||||
|
||||
ubifs_prepare_node(c, buf, len, 1);
|
||||
err = ubifs_leb_write(c, lnum, buf, offs, buf_len);
|
||||
if (err)
|
||||
ubifs_dump_node(c, buf);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ubifs_read_node_wbuf - read node from the media or write-buffer.
|
||||
* @wbuf: wbuf to check for un-written data
|
||||
* @buf: buffer to read to
|
||||
* @type: node type
|
||||
* @len: node length
|
||||
* @lnum: logical eraseblock number
|
||||
* @offs: offset within the logical eraseblock
|
||||
*
|
||||
* This function reads a node of known type and length, checks it and stores
|
||||
* in @buf. If the node partially or fully sits in the write-buffer, this
|
||||
* function takes data from the buffer, otherwise it reads the flash media.
|
||||
* Returns zero in case of success, %-EUCLEAN if CRC mismatched and a negative
|
||||
* error code in case of failure.
|
||||
*/
|
||||
int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len,
|
||||
int lnum, int offs)
|
||||
{
|
||||
const struct ubifs_info *c = wbuf->c;
|
||||
int err, rlen, overlap;
|
||||
struct ubifs_ch *ch = buf;
|
||||
|
||||
dbg_io("LEB %d:%d, %s, length %d, jhead %s", lnum, offs,
|
||||
dbg_ntype(type), len, dbg_jhead(wbuf->jhead));
|
||||
ubifs_assert(wbuf && lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
|
||||
ubifs_assert(!(offs & 7) && offs < c->leb_size);
|
||||
ubifs_assert(type >= 0 && type < UBIFS_NODE_TYPES_CNT);
|
||||
|
||||
spin_lock(&wbuf->lock);
|
||||
overlap = (lnum == wbuf->lnum && offs + len > wbuf->offs);
|
||||
if (!overlap) {
|
||||
/* We may safely unlock the write-buffer and read the data */
|
||||
spin_unlock(&wbuf->lock);
|
||||
return ubifs_read_node(c, buf, type, len, lnum, offs);
|
||||
}
|
||||
|
||||
/* Don't read under wbuf */
|
||||
rlen = wbuf->offs - offs;
|
||||
if (rlen < 0)
|
||||
rlen = 0;
|
||||
|
||||
/* Copy the rest from the write-buffer */
|
||||
memcpy(buf + rlen, wbuf->buf + offs + rlen - wbuf->offs, len - rlen);
|
||||
spin_unlock(&wbuf->lock);
|
||||
|
||||
if (rlen > 0) {
|
||||
/* Read everything that goes before write-buffer */
|
||||
err = ubifs_leb_read(c, lnum, buf, offs, rlen, 0);
|
||||
if (err && err != -EBADMSG)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (type != ch->node_type) {
|
||||
ubifs_err("bad node type (%d but expected %d)",
|
||||
ch->node_type, type);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ubifs_check_node(c, buf, lnum, offs, 0, 0);
|
||||
if (err) {
|
||||
ubifs_err("expected node type %d", type);
|
||||
return err;
|
||||
}
|
||||
|
||||
rlen = le32_to_cpu(ch->len);
|
||||
if (rlen != len) {
|
||||
ubifs_err("bad node length %d, expected %d", rlen, len);
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
ubifs_err("bad node at LEB %d:%d", lnum, offs);
|
||||
ubifs_dump_node(c, buf);
|
||||
dump_stack();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_read_node - read node.
|
||||
* @c: UBIFS file-system description object
|
||||
@ -281,12 +984,9 @@ int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len,
|
||||
ubifs_assert(!(offs & 7) && offs < c->leb_size);
|
||||
ubifs_assert(type >= 0 && type < UBIFS_NODE_TYPES_CNT);
|
||||
|
||||
err = ubi_read(c->ubi, lnum, buf, offs, len);
|
||||
if (err && err != -EBADMSG) {
|
||||
ubifs_err("cannot read node %d from LEB %d:%d, error %d",
|
||||
type, lnum, offs, err);
|
||||
err = ubifs_leb_read(c, lnum, buf, offs, len, 0);
|
||||
if (err && err != -EBADMSG)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (type != ch->node_type) {
|
||||
ubifs_err("bad node type (%d but expected %d)",
|
||||
@ -309,8 +1009,143 @@ int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len,
|
||||
return 0;
|
||||
|
||||
out:
|
||||
ubifs_err("bad node at LEB %d:%d", lnum, offs);
|
||||
dbg_dump_node(c, buf);
|
||||
dbg_dump_stack();
|
||||
ubifs_err("bad node at LEB %d:%d, LEB mapping status %d", lnum, offs,
|
||||
ubi_is_mapped(c->ubi, lnum));
|
||||
ubifs_dump_node(c, buf);
|
||||
dump_stack();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_wbuf_init - initialize write-buffer.
|
||||
* @c: UBIFS file-system description object
|
||||
* @wbuf: write-buffer to initialize
|
||||
*
|
||||
* This function initializes write-buffer. Returns zero in case of success
|
||||
* %-ENOMEM in case of failure.
|
||||
*/
|
||||
int ubifs_wbuf_init(struct ubifs_info *c, struct ubifs_wbuf *wbuf)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
wbuf->buf = kmalloc(c->max_write_size, GFP_KERNEL);
|
||||
if (!wbuf->buf)
|
||||
return -ENOMEM;
|
||||
|
||||
size = (c->max_write_size / UBIFS_CH_SZ + 1) * sizeof(ino_t);
|
||||
wbuf->inodes = kmalloc(size, GFP_KERNEL);
|
||||
if (!wbuf->inodes) {
|
||||
kfree(wbuf->buf);
|
||||
wbuf->buf = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
wbuf->used = 0;
|
||||
wbuf->lnum = wbuf->offs = -1;
|
||||
/*
|
||||
* If the LEB starts at the max. write size aligned address, then
|
||||
* write-buffer size has to be set to @c->max_write_size. Otherwise,
|
||||
* set it to something smaller so that it ends at the closest max.
|
||||
* write size boundary.
|
||||
*/
|
||||
size = c->max_write_size - (c->leb_start % c->max_write_size);
|
||||
wbuf->avail = wbuf->size = size;
|
||||
wbuf->sync_callback = NULL;
|
||||
mutex_init(&wbuf->io_mutex);
|
||||
spin_lock_init(&wbuf->lock);
|
||||
wbuf->c = c;
|
||||
wbuf->next_ino = 0;
|
||||
|
||||
#ifndef __UBOOT__
|
||||
hrtimer_init(&wbuf->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
wbuf->timer.function = wbuf_timer_callback_nolock;
|
||||
wbuf->softlimit = ktime_set(WBUF_TIMEOUT_SOFTLIMIT, 0);
|
||||
wbuf->delta = WBUF_TIMEOUT_HARDLIMIT - WBUF_TIMEOUT_SOFTLIMIT;
|
||||
wbuf->delta *= 1000000000ULL;
|
||||
ubifs_assert(wbuf->delta <= ULONG_MAX);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_wbuf_add_ino_nolock - add an inode number into the wbuf inode array.
|
||||
* @wbuf: the write-buffer where to add
|
||||
* @inum: the inode number
|
||||
*
|
||||
* This function adds an inode number to the inode array of the write-buffer.
|
||||
*/
|
||||
void ubifs_wbuf_add_ino_nolock(struct ubifs_wbuf *wbuf, ino_t inum)
|
||||
{
|
||||
if (!wbuf->buf)
|
||||
/* NOR flash or something similar */
|
||||
return;
|
||||
|
||||
spin_lock(&wbuf->lock);
|
||||
if (wbuf->used)
|
||||
wbuf->inodes[wbuf->next_ino++] = inum;
|
||||
spin_unlock(&wbuf->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* wbuf_has_ino - returns if the wbuf contains data from the inode.
|
||||
* @wbuf: the write-buffer
|
||||
* @inum: the inode number
|
||||
*
|
||||
* This function returns with %1 if the write-buffer contains some data from the
|
||||
* given inode otherwise it returns with %0.
|
||||
*/
|
||||
static int wbuf_has_ino(struct ubifs_wbuf *wbuf, ino_t inum)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
spin_lock(&wbuf->lock);
|
||||
for (i = 0; i < wbuf->next_ino; i++)
|
||||
if (inum == wbuf->inodes[i]) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
spin_unlock(&wbuf->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_sync_wbufs_by_inode - synchronize write-buffers for an inode.
|
||||
* @c: UBIFS file-system description object
|
||||
* @inode: inode to synchronize
|
||||
*
|
||||
* This function synchronizes write-buffers which contain nodes belonging to
|
||||
* @inode. Returns zero in case of success and a negative error code in case of
|
||||
* failure.
|
||||
*/
|
||||
int ubifs_sync_wbufs_by_inode(struct ubifs_info *c, struct inode *inode)
|
||||
{
|
||||
int i, err = 0;
|
||||
|
||||
for (i = 0; i < c->jhead_cnt; i++) {
|
||||
struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf;
|
||||
|
||||
if (i == GCHD)
|
||||
/*
|
||||
* GC head is special, do not look at it. Even if the
|
||||
* head contains something related to this inode, it is
|
||||
* a _copy_ of corresponding on-flash node which sits
|
||||
* somewhere else.
|
||||
*/
|
||||
continue;
|
||||
|
||||
if (!wbuf_has_ino(wbuf, inode->i_ino))
|
||||
continue;
|
||||
|
||||
mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
|
||||
if (wbuf_has_ino(wbuf, inode->i_ino))
|
||||
err = ubifs_wbuf_sync_nolock(wbuf);
|
||||
mutex_unlock(&wbuf->io_mutex);
|
||||
|
||||
if (err) {
|
||||
ubifs_ro_mode(c, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -3,18 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2006-2008 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Authors: Artem Bityutskiy (Битюцкий Артём)
|
||||
* Adrian Hunter
|
||||
@ -228,23 +217,6 @@ static inline void xent_key_init(const struct ubifs_info *c,
|
||||
key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS);
|
||||
}
|
||||
|
||||
/**
|
||||
* xent_key_init_hash - initialize extended attribute entry key without
|
||||
* re-calculating hash function.
|
||||
* @c: UBIFS file-system description object
|
||||
* @key: key to initialize
|
||||
* @inum: host inode number
|
||||
* @hash: extended attribute entry name hash
|
||||
*/
|
||||
static inline void xent_key_init_hash(const struct ubifs_info *c,
|
||||
union ubifs_key *key, ino_t inum,
|
||||
uint32_t hash)
|
||||
{
|
||||
ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
|
||||
key->u32[0] = inum;
|
||||
key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS);
|
||||
}
|
||||
|
||||
/**
|
||||
* xent_key_init_flash - initialize on-flash extended attribute entry key.
|
||||
* @c: UBIFS file-system description object
|
||||
@ -295,22 +267,15 @@ static inline void data_key_init(const struct ubifs_info *c,
|
||||
}
|
||||
|
||||
/**
|
||||
* data_key_init_flash - initialize on-flash data key.
|
||||
* highest_data_key - get the highest possible data key for an inode.
|
||||
* @c: UBIFS file-system description object
|
||||
* @k: key to initialize
|
||||
* @key: key to initialize
|
||||
* @inum: inode number
|
||||
* @block: block number
|
||||
*/
|
||||
static inline void data_key_init_flash(const struct ubifs_info *c, void *k,
|
||||
ino_t inum, unsigned int block)
|
||||
static inline void highest_data_key(const struct ubifs_info *c,
|
||||
union ubifs_key *key, ino_t inum)
|
||||
{
|
||||
union ubifs_key *key = k;
|
||||
|
||||
ubifs_assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK));
|
||||
key->j32[0] = cpu_to_le32(inum);
|
||||
key->j32[1] = cpu_to_le32(block |
|
||||
(UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS));
|
||||
memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
|
||||
data_key_init(c, key, inum, UBIFS_S_KEY_BLOCK_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -329,6 +294,20 @@ static inline void trun_key_init(const struct ubifs_info *c,
|
||||
key->u32[1] = UBIFS_TRUN_KEY << UBIFS_S_KEY_BLOCK_BITS;
|
||||
}
|
||||
|
||||
/**
|
||||
* invalid_key_init - initialize invalid node key.
|
||||
* @c: UBIFS file-system description object
|
||||
* @key: key to initialize
|
||||
*
|
||||
* This is a helper function which marks a @key object as invalid.
|
||||
*/
|
||||
static inline void invalid_key_init(const struct ubifs_info *c,
|
||||
union ubifs_key *key)
|
||||
{
|
||||
key->u32[0] = 0xDEADBEAF;
|
||||
key->u32[1] = UBIFS_INVALID_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* key_type - get key type.
|
||||
* @c: UBIFS file-system description object
|
||||
@ -381,8 +360,8 @@ static inline ino_t key_inum_flash(const struct ubifs_info *c, const void *k)
|
||||
* @c: UBIFS file-system description object
|
||||
* @key: the key to get hash from
|
||||
*/
|
||||
static inline int key_hash(const struct ubifs_info *c,
|
||||
const union ubifs_key *key)
|
||||
static inline uint32_t key_hash(const struct ubifs_info *c,
|
||||
const union ubifs_key *key)
|
||||
{
|
||||
return key->u32[1] & UBIFS_S_KEY_HASH_MASK;
|
||||
}
|
||||
@ -392,7 +371,7 @@ static inline int key_hash(const struct ubifs_info *c,
|
||||
* @c: UBIFS file-system description object
|
||||
* @k: the key to get hash from
|
||||
*/
|
||||
static inline int key_hash_flash(const struct ubifs_info *c, const void *k)
|
||||
static inline uint32_t key_hash_flash(const struct ubifs_info *c, const void *k)
|
||||
{
|
||||
const union ubifs_key *key = k;
|
||||
|
||||
@ -554,4 +533,5 @@ static inline unsigned long long key_max_inode_size(const struct ubifs_info *c)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* !__UBIFS_KEY_H__ */
|
||||
|
663
fs/ubifs/log.c
663
fs/ubifs/log.c
@ -3,18 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2006-2008 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Authors: Artem Bityutskiy (Битюцкий Артём)
|
||||
* Adrian Hunter
|
||||
@ -27,8 +16,14 @@
|
||||
* journal.
|
||||
*/
|
||||
|
||||
#define __UBOOT__
|
||||
#ifdef __UBOOT__
|
||||
#include <linux/err.h>
|
||||
#endif
|
||||
#include "ubifs.h"
|
||||
|
||||
static int dbg_check_bud_bytes(struct ubifs_info *c);
|
||||
|
||||
/**
|
||||
* ubifs_search_bud - search bud LEB.
|
||||
* @c: UBIFS file-system description object
|
||||
@ -59,6 +54,57 @@ struct ubifs_bud *ubifs_search_bud(struct ubifs_info *c, int lnum)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_get_wbuf - get the wbuf associated with a LEB, if there is one.
|
||||
* @c: UBIFS file-system description object
|
||||
* @lnum: logical eraseblock number to search
|
||||
*
|
||||
* This functions returns the wbuf for @lnum or %NULL if there is not one.
|
||||
*/
|
||||
struct ubifs_wbuf *ubifs_get_wbuf(struct ubifs_info *c, int lnum)
|
||||
{
|
||||
struct rb_node *p;
|
||||
struct ubifs_bud *bud;
|
||||
int jhead;
|
||||
|
||||
if (!c->jheads)
|
||||
return NULL;
|
||||
|
||||
spin_lock(&c->buds_lock);
|
||||
p = c->buds.rb_node;
|
||||
while (p) {
|
||||
bud = rb_entry(p, struct ubifs_bud, rb);
|
||||
if (lnum < bud->lnum)
|
||||
p = p->rb_left;
|
||||
else if (lnum > bud->lnum)
|
||||
p = p->rb_right;
|
||||
else {
|
||||
jhead = bud->jhead;
|
||||
spin_unlock(&c->buds_lock);
|
||||
return &c->jheads[jhead].wbuf;
|
||||
}
|
||||
}
|
||||
spin_unlock(&c->buds_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* empty_log_bytes - calculate amount of empty space in the log.
|
||||
* @c: UBIFS file-system description object
|
||||
*/
|
||||
static inline long long empty_log_bytes(const struct ubifs_info *c)
|
||||
{
|
||||
long long h, t;
|
||||
|
||||
h = (long long)c->lhead_lnum * c->leb_size + c->lhead_offs;
|
||||
t = (long long)c->ltail_lnum * c->leb_size;
|
||||
|
||||
if (h >= t)
|
||||
return c->log_bytes - h + t;
|
||||
else
|
||||
return t - h;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_add_bud - add bud LEB to the tree of buds and its journal head list.
|
||||
* @c: UBIFS file-system description object
|
||||
@ -88,7 +134,7 @@ void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud)
|
||||
jhead = &c->jheads[bud->jhead];
|
||||
list_add_tail(&bud->list, &jhead->buds_list);
|
||||
} else
|
||||
ubifs_assert(c->replaying && (c->vfs_sb->s_flags & MS_RDONLY));
|
||||
ubifs_assert(c->replaying && c->ro_mount);
|
||||
|
||||
/*
|
||||
* Note, although this is a new bud, we anyway account this space now,
|
||||
@ -98,7 +144,594 @@ void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud)
|
||||
*/
|
||||
c->bud_bytes += c->leb_size - bud->start;
|
||||
|
||||
dbg_log("LEB %d:%d, jhead %d, bud_bytes %lld", bud->lnum,
|
||||
bud->start, bud->jhead, c->bud_bytes);
|
||||
dbg_log("LEB %d:%d, jhead %s, bud_bytes %lld", bud->lnum,
|
||||
bud->start, dbg_jhead(bud->jhead), c->bud_bytes);
|
||||
spin_unlock(&c->buds_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_add_bud_to_log - add a new bud to the log.
|
||||
* @c: UBIFS file-system description object
|
||||
* @jhead: journal head the bud belongs to
|
||||
* @lnum: LEB number of the bud
|
||||
* @offs: starting offset of the bud
|
||||
*
|
||||
* This function writes reference node for the new bud LEB @lnum it to the log,
|
||||
* and adds it to the buds tress. It also makes sure that log size does not
|
||||
* exceed the 'c->max_bud_bytes' limit. Returns zero in case of success,
|
||||
* %-EAGAIN if commit is required, and a negative error codes in case of
|
||||
* failure.
|
||||
*/
|
||||
int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs)
|
||||
{
|
||||
int err;
|
||||
struct ubifs_bud *bud;
|
||||
struct ubifs_ref_node *ref;
|
||||
|
||||
bud = kmalloc(sizeof(struct ubifs_bud), GFP_NOFS);
|
||||
if (!bud)
|
||||
return -ENOMEM;
|
||||
ref = kzalloc(c->ref_node_alsz, GFP_NOFS);
|
||||
if (!ref) {
|
||||
kfree(bud);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_lock(&c->log_mutex);
|
||||
ubifs_assert(!c->ro_media && !c->ro_mount);
|
||||
if (c->ro_error) {
|
||||
err = -EROFS;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* Make sure we have enough space in the log */
|
||||
if (empty_log_bytes(c) - c->ref_node_alsz < c->min_log_bytes) {
|
||||
dbg_log("not enough log space - %lld, required %d",
|
||||
empty_log_bytes(c), c->min_log_bytes);
|
||||
ubifs_commit_required(c);
|
||||
err = -EAGAIN;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the amount of space in buds will not exceed the
|
||||
* 'c->max_bud_bytes' limit, because we want to guarantee mount time
|
||||
* limits.
|
||||
*
|
||||
* It is not necessary to hold @c->buds_lock when reading @c->bud_bytes
|
||||
* because we are holding @c->log_mutex. All @c->bud_bytes take place
|
||||
* when both @c->log_mutex and @c->bud_bytes are locked.
|
||||
*/
|
||||
if (c->bud_bytes + c->leb_size - offs > c->max_bud_bytes) {
|
||||
dbg_log("bud bytes %lld (%lld max), require commit",
|
||||
c->bud_bytes, c->max_bud_bytes);
|
||||
ubifs_commit_required(c);
|
||||
err = -EAGAIN;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the journal is full enough - start background commit. Note, it is
|
||||
* OK to read 'c->cmt_state' without spinlock because integer reads
|
||||
* are atomic in the kernel.
|
||||
*/
|
||||
if (c->bud_bytes >= c->bg_bud_bytes &&
|
||||
c->cmt_state == COMMIT_RESTING) {
|
||||
dbg_log("bud bytes %lld (%lld max), initiate BG commit",
|
||||
c->bud_bytes, c->max_bud_bytes);
|
||||
ubifs_request_bg_commit(c);
|
||||
}
|
||||
|
||||
bud->lnum = lnum;
|
||||
bud->start = offs;
|
||||
bud->jhead = jhead;
|
||||
|
||||
ref->ch.node_type = UBIFS_REF_NODE;
|
||||
ref->lnum = cpu_to_le32(bud->lnum);
|
||||
ref->offs = cpu_to_le32(bud->start);
|
||||
ref->jhead = cpu_to_le32(jhead);
|
||||
|
||||
if (c->lhead_offs > c->leb_size - c->ref_node_alsz) {
|
||||
c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
|
||||
c->lhead_offs = 0;
|
||||
}
|
||||
|
||||
if (c->lhead_offs == 0) {
|
||||
/* Must ensure next log LEB has been unmapped */
|
||||
err = ubifs_leb_unmap(c, c->lhead_lnum);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (bud->start == 0) {
|
||||
/*
|
||||
* Before writing the LEB reference which refers an empty LEB
|
||||
* to the log, we have to make sure it is mapped, because
|
||||
* otherwise we'd risk to refer an LEB with garbage in case of
|
||||
* an unclean reboot, because the target LEB might have been
|
||||
* unmapped, but not yet physically erased.
|
||||
*/
|
||||
err = ubifs_leb_map(c, bud->lnum);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
dbg_log("write ref LEB %d:%d",
|
||||
c->lhead_lnum, c->lhead_offs);
|
||||
err = ubifs_write_node(c, ref, UBIFS_REF_NODE_SZ, c->lhead_lnum,
|
||||
c->lhead_offs);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
c->lhead_offs += c->ref_node_alsz;
|
||||
|
||||
ubifs_add_bud(c, bud);
|
||||
|
||||
mutex_unlock(&c->log_mutex);
|
||||
kfree(ref);
|
||||
return 0;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&c->log_mutex);
|
||||
kfree(ref);
|
||||
kfree(bud);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove_buds - remove used buds.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function removes use buds from the buds tree. It does not remove the
|
||||
* buds which are pointed to by journal heads.
|
||||
*/
|
||||
static void remove_buds(struct ubifs_info *c)
|
||||
{
|
||||
struct rb_node *p;
|
||||
|
||||
ubifs_assert(list_empty(&c->old_buds));
|
||||
c->cmt_bud_bytes = 0;
|
||||
spin_lock(&c->buds_lock);
|
||||
p = rb_first(&c->buds);
|
||||
while (p) {
|
||||
struct rb_node *p1 = p;
|
||||
struct ubifs_bud *bud;
|
||||
struct ubifs_wbuf *wbuf;
|
||||
|
||||
p = rb_next(p);
|
||||
bud = rb_entry(p1, struct ubifs_bud, rb);
|
||||
wbuf = &c->jheads[bud->jhead].wbuf;
|
||||
|
||||
if (wbuf->lnum == bud->lnum) {
|
||||
/*
|
||||
* Do not remove buds which are pointed to by journal
|
||||
* heads (non-closed buds).
|
||||
*/
|
||||
c->cmt_bud_bytes += wbuf->offs - bud->start;
|
||||
dbg_log("preserve %d:%d, jhead %s, bud bytes %d, cmt_bud_bytes %lld",
|
||||
bud->lnum, bud->start, dbg_jhead(bud->jhead),
|
||||
wbuf->offs - bud->start, c->cmt_bud_bytes);
|
||||
bud->start = wbuf->offs;
|
||||
} else {
|
||||
c->cmt_bud_bytes += c->leb_size - bud->start;
|
||||
dbg_log("remove %d:%d, jhead %s, bud bytes %d, cmt_bud_bytes %lld",
|
||||
bud->lnum, bud->start, dbg_jhead(bud->jhead),
|
||||
c->leb_size - bud->start, c->cmt_bud_bytes);
|
||||
rb_erase(p1, &c->buds);
|
||||
/*
|
||||
* If the commit does not finish, the recovery will need
|
||||
* to replay the journal, in which case the old buds
|
||||
* must be unchanged. Do not release them until post
|
||||
* commit i.e. do not allow them to be garbage
|
||||
* collected.
|
||||
*/
|
||||
list_move(&bud->list, &c->old_buds);
|
||||
}
|
||||
}
|
||||
spin_unlock(&c->buds_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_log_start_commit - start commit.
|
||||
* @c: UBIFS file-system description object
|
||||
* @ltail_lnum: return new log tail LEB number
|
||||
*
|
||||
* The commit operation starts with writing "commit start" node to the log and
|
||||
* reference nodes for all journal heads which will define new journal after
|
||||
* the commit has been finished. The commit start and reference nodes are
|
||||
* written in one go to the nearest empty log LEB (hence, when commit is
|
||||
* finished UBIFS may safely unmap all the previous log LEBs). This function
|
||||
* returns zero in case of success and a negative error code in case of
|
||||
* failure.
|
||||
*/
|
||||
int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum)
|
||||
{
|
||||
void *buf;
|
||||
struct ubifs_cs_node *cs;
|
||||
struct ubifs_ref_node *ref;
|
||||
int err, i, max_len, len;
|
||||
|
||||
err = dbg_check_bud_bytes(c);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
max_len = UBIFS_CS_NODE_SZ + c->jhead_cnt * UBIFS_REF_NODE_SZ;
|
||||
max_len = ALIGN(max_len, c->min_io_size);
|
||||
buf = cs = kmalloc(max_len, GFP_NOFS);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
cs->ch.node_type = UBIFS_CS_NODE;
|
||||
cs->cmt_no = cpu_to_le64(c->cmt_no);
|
||||
ubifs_prepare_node(c, cs, UBIFS_CS_NODE_SZ, 0);
|
||||
|
||||
/*
|
||||
* Note, we do not lock 'c->log_mutex' because this is the commit start
|
||||
* phase and we are exclusively using the log. And we do not lock
|
||||
* write-buffer because nobody can write to the file-system at this
|
||||
* phase.
|
||||
*/
|
||||
|
||||
len = UBIFS_CS_NODE_SZ;
|
||||
for (i = 0; i < c->jhead_cnt; i++) {
|
||||
int lnum = c->jheads[i].wbuf.lnum;
|
||||
int offs = c->jheads[i].wbuf.offs;
|
||||
|
||||
if (lnum == -1 || offs == c->leb_size)
|
||||
continue;
|
||||
|
||||
dbg_log("add ref to LEB %d:%d for jhead %s",
|
||||
lnum, offs, dbg_jhead(i));
|
||||
ref = buf + len;
|
||||
ref->ch.node_type = UBIFS_REF_NODE;
|
||||
ref->lnum = cpu_to_le32(lnum);
|
||||
ref->offs = cpu_to_le32(offs);
|
||||
ref->jhead = cpu_to_le32(i);
|
||||
|
||||
ubifs_prepare_node(c, ref, UBIFS_REF_NODE_SZ, 0);
|
||||
len += UBIFS_REF_NODE_SZ;
|
||||
}
|
||||
|
||||
ubifs_pad(c, buf + len, ALIGN(len, c->min_io_size) - len);
|
||||
|
||||
/* Switch to the next log LEB */
|
||||
if (c->lhead_offs) {
|
||||
c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
|
||||
c->lhead_offs = 0;
|
||||
}
|
||||
|
||||
if (c->lhead_offs == 0) {
|
||||
/* Must ensure next LEB has been unmapped */
|
||||
err = ubifs_leb_unmap(c, c->lhead_lnum);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = ALIGN(len, c->min_io_size);
|
||||
dbg_log("writing commit start at LEB %d:0, len %d", c->lhead_lnum, len);
|
||||
err = ubifs_leb_write(c, c->lhead_lnum, cs, 0, len);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
*ltail_lnum = c->lhead_lnum;
|
||||
|
||||
c->lhead_offs += len;
|
||||
if (c->lhead_offs == c->leb_size) {
|
||||
c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
|
||||
c->lhead_offs = 0;
|
||||
}
|
||||
|
||||
remove_buds(c);
|
||||
|
||||
/*
|
||||
* We have started the commit and now users may use the rest of the log
|
||||
* for new writes.
|
||||
*/
|
||||
c->min_log_bytes = 0;
|
||||
|
||||
out:
|
||||
kfree(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_log_end_commit - end commit.
|
||||
* @c: UBIFS file-system description object
|
||||
* @ltail_lnum: new log tail LEB number
|
||||
*
|
||||
* This function is called on when the commit operation was finished. It
|
||||
* moves log tail to new position and unmaps LEBs which contain obsolete data.
|
||||
* Returns zero in case of success and a negative error code in case of
|
||||
* failure.
|
||||
*/
|
||||
int ubifs_log_end_commit(struct ubifs_info *c, int ltail_lnum)
|
||||
{
|
||||
int err;
|
||||
|
||||
/*
|
||||
* At this phase we have to lock 'c->log_mutex' because UBIFS allows FS
|
||||
* writes during commit. Its only short "commit" start phase when
|
||||
* writers are blocked.
|
||||
*/
|
||||
mutex_lock(&c->log_mutex);
|
||||
|
||||
dbg_log("old tail was LEB %d:0, new tail is LEB %d:0",
|
||||
c->ltail_lnum, ltail_lnum);
|
||||
|
||||
c->ltail_lnum = ltail_lnum;
|
||||
/*
|
||||
* The commit is finished and from now on it must be guaranteed that
|
||||
* there is always enough space for the next commit.
|
||||
*/
|
||||
c->min_log_bytes = c->leb_size;
|
||||
|
||||
spin_lock(&c->buds_lock);
|
||||
c->bud_bytes -= c->cmt_bud_bytes;
|
||||
spin_unlock(&c->buds_lock);
|
||||
|
||||
err = dbg_check_bud_bytes(c);
|
||||
|
||||
mutex_unlock(&c->log_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_log_post_commit - things to do after commit is completed.
|
||||
* @c: UBIFS file-system description object
|
||||
* @old_ltail_lnum: old log tail LEB number
|
||||
*
|
||||
* Release buds only after commit is completed, because they must be unchanged
|
||||
* if recovery is needed.
|
||||
*
|
||||
* Unmap log LEBs only after commit is completed, because they may be needed for
|
||||
* recovery.
|
||||
*
|
||||
* This function returns %0 on success and a negative error code on failure.
|
||||
*/
|
||||
int ubifs_log_post_commit(struct ubifs_info *c, int old_ltail_lnum)
|
||||
{
|
||||
int lnum, err = 0;
|
||||
|
||||
while (!list_empty(&c->old_buds)) {
|
||||
struct ubifs_bud *bud;
|
||||
|
||||
bud = list_entry(c->old_buds.next, struct ubifs_bud, list);
|
||||
err = ubifs_return_leb(c, bud->lnum);
|
||||
if (err)
|
||||
return err;
|
||||
list_del(&bud->list);
|
||||
kfree(bud);
|
||||
}
|
||||
mutex_lock(&c->log_mutex);
|
||||
for (lnum = old_ltail_lnum; lnum != c->ltail_lnum;
|
||||
lnum = ubifs_next_log_lnum(c, lnum)) {
|
||||
dbg_log("unmap log LEB %d", lnum);
|
||||
err = ubifs_leb_unmap(c, lnum);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&c->log_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct done_ref - references that have been done.
|
||||
* @rb: rb-tree node
|
||||
* @lnum: LEB number
|
||||
*/
|
||||
struct done_ref {
|
||||
struct rb_node rb;
|
||||
int lnum;
|
||||
};
|
||||
|
||||
/**
|
||||
* done_already - determine if a reference has been done already.
|
||||
* @done_tree: rb-tree to store references that have been done
|
||||
* @lnum: LEB number of reference
|
||||
*
|
||||
* This function returns %1 if the reference has been done, %0 if not, otherwise
|
||||
* a negative error code is returned.
|
||||
*/
|
||||
static int done_already(struct rb_root *done_tree, int lnum)
|
||||
{
|
||||
struct rb_node **p = &done_tree->rb_node, *parent = NULL;
|
||||
struct done_ref *dr;
|
||||
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
dr = rb_entry(parent, struct done_ref, rb);
|
||||
if (lnum < dr->lnum)
|
||||
p = &(*p)->rb_left;
|
||||
else if (lnum > dr->lnum)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
dr = kzalloc(sizeof(struct done_ref), GFP_NOFS);
|
||||
if (!dr)
|
||||
return -ENOMEM;
|
||||
|
||||
dr->lnum = lnum;
|
||||
|
||||
rb_link_node(&dr->rb, parent, p);
|
||||
rb_insert_color(&dr->rb, done_tree);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* destroy_done_tree - destroy the done tree.
|
||||
* @done_tree: done tree to destroy
|
||||
*/
|
||||
static void destroy_done_tree(struct rb_root *done_tree)
|
||||
{
|
||||
struct done_ref *dr, *n;
|
||||
|
||||
rbtree_postorder_for_each_entry_safe(dr, n, done_tree, rb)
|
||||
kfree(dr);
|
||||
}
|
||||
|
||||
/**
|
||||
* add_node - add a node to the consolidated log.
|
||||
* @c: UBIFS file-system description object
|
||||
* @buf: buffer to which to add
|
||||
* @lnum: LEB number to which to write is passed and returned here
|
||||
* @offs: offset to where to write is passed and returned here
|
||||
* @node: node to add
|
||||
*
|
||||
* This function returns %0 on success and a negative error code on failure.
|
||||
*/
|
||||
static int add_node(struct ubifs_info *c, void *buf, int *lnum, int *offs,
|
||||
void *node)
|
||||
{
|
||||
struct ubifs_ch *ch = node;
|
||||
int len = le32_to_cpu(ch->len), remains = c->leb_size - *offs;
|
||||
|
||||
if (len > remains) {
|
||||
int sz = ALIGN(*offs, c->min_io_size), err;
|
||||
|
||||
ubifs_pad(c, buf + *offs, sz - *offs);
|
||||
err = ubifs_leb_change(c, *lnum, buf, sz);
|
||||
if (err)
|
||||
return err;
|
||||
*lnum = ubifs_next_log_lnum(c, *lnum);
|
||||
*offs = 0;
|
||||
}
|
||||
memcpy(buf + *offs, node, len);
|
||||
*offs += ALIGN(len, 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_consolidate_log - consolidate the log.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* Repeated failed commits could cause the log to be full, but at least 1 LEB is
|
||||
* needed for commit. This function rewrites the reference nodes in the log
|
||||
* omitting duplicates, and failed CS nodes, and leaving no gaps.
|
||||
*
|
||||
* This function returns %0 on success and a negative error code on failure.
|
||||
*/
|
||||
int ubifs_consolidate_log(struct ubifs_info *c)
|
||||
{
|
||||
struct ubifs_scan_leb *sleb;
|
||||
struct ubifs_scan_node *snod;
|
||||
struct rb_root done_tree = RB_ROOT;
|
||||
int lnum, err, first = 1, write_lnum, offs = 0;
|
||||
void *buf;
|
||||
|
||||
dbg_rcvry("log tail LEB %d, log head LEB %d", c->ltail_lnum,
|
||||
c->lhead_lnum);
|
||||
buf = vmalloc(c->leb_size);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
lnum = c->ltail_lnum;
|
||||
write_lnum = lnum;
|
||||
while (1) {
|
||||
sleb = ubifs_scan(c, lnum, 0, c->sbuf, 0);
|
||||
if (IS_ERR(sleb)) {
|
||||
err = PTR_ERR(sleb);
|
||||
goto out_free;
|
||||
}
|
||||
list_for_each_entry(snod, &sleb->nodes, list) {
|
||||
switch (snod->type) {
|
||||
case UBIFS_REF_NODE: {
|
||||
struct ubifs_ref_node *ref = snod->node;
|
||||
int ref_lnum = le32_to_cpu(ref->lnum);
|
||||
|
||||
err = done_already(&done_tree, ref_lnum);
|
||||
if (err < 0)
|
||||
goto out_scan;
|
||||
if (err != 1) {
|
||||
err = add_node(c, buf, &write_lnum,
|
||||
&offs, snod->node);
|
||||
if (err)
|
||||
goto out_scan;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case UBIFS_CS_NODE:
|
||||
if (!first)
|
||||
break;
|
||||
err = add_node(c, buf, &write_lnum, &offs,
|
||||
snod->node);
|
||||
if (err)
|
||||
goto out_scan;
|
||||
first = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ubifs_scan_destroy(sleb);
|
||||
if (lnum == c->lhead_lnum)
|
||||
break;
|
||||
lnum = ubifs_next_log_lnum(c, lnum);
|
||||
}
|
||||
if (offs) {
|
||||
int sz = ALIGN(offs, c->min_io_size);
|
||||
|
||||
ubifs_pad(c, buf + offs, sz - offs);
|
||||
err = ubifs_leb_change(c, write_lnum, buf, sz);
|
||||
if (err)
|
||||
goto out_free;
|
||||
offs = ALIGN(offs, c->min_io_size);
|
||||
}
|
||||
destroy_done_tree(&done_tree);
|
||||
vfree(buf);
|
||||
if (write_lnum == c->lhead_lnum) {
|
||||
ubifs_err("log is too full");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Unmap remaining LEBs */
|
||||
lnum = write_lnum;
|
||||
do {
|
||||
lnum = ubifs_next_log_lnum(c, lnum);
|
||||
err = ubifs_leb_unmap(c, lnum);
|
||||
if (err)
|
||||
return err;
|
||||
} while (lnum != c->lhead_lnum);
|
||||
c->lhead_lnum = write_lnum;
|
||||
c->lhead_offs = offs;
|
||||
dbg_rcvry("new log head at %d:%d", c->lhead_lnum, c->lhead_offs);
|
||||
return 0;
|
||||
|
||||
out_scan:
|
||||
ubifs_scan_destroy(sleb);
|
||||
out_free:
|
||||
destroy_done_tree(&done_tree);
|
||||
vfree(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_check_bud_bytes - make sure bud bytes calculation are all right.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function makes sure the amount of flash space used by closed buds
|
||||
* ('c->bud_bytes' is correct). Returns zero in case of success and %-EINVAL in
|
||||
* case of failure.
|
||||
*/
|
||||
static int dbg_check_bud_bytes(struct ubifs_info *c)
|
||||
{
|
||||
int i, err = 0;
|
||||
struct ubifs_bud *bud;
|
||||
long long bud_bytes = 0;
|
||||
|
||||
if (!dbg_is_chk_gen(c))
|
||||
return 0;
|
||||
|
||||
spin_lock(&c->buds_lock);
|
||||
for (i = 0; i < c->jhead_cnt; i++)
|
||||
list_for_each_entry(bud, &c->jheads[i].buds_list, list)
|
||||
bud_bytes += c->leb_size - bud->start;
|
||||
|
||||
if (c->bud_bytes != bud_bytes) {
|
||||
ubifs_err("bad bud_bytes %lld, calculated %lld",
|
||||
c->bud_bytes, bud_bytes);
|
||||
err = -EINVAL;
|
||||
}
|
||||
spin_unlock(&c->buds_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -3,18 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2006-2008 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Authors: Adrian Hunter
|
||||
* Artem Bityutskiy (Битюцкий Артём)
|
||||
@ -28,6 +17,10 @@
|
||||
* an empty LEB for the journal, or a very dirty LEB for garbage collection.
|
||||
*/
|
||||
|
||||
#define __UBOOT__
|
||||
#ifdef __UBOOT__
|
||||
#include <linux/err.h>
|
||||
#endif
|
||||
#include "ubifs.h"
|
||||
|
||||
/**
|
||||
@ -281,7 +274,7 @@ void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops,
|
||||
case LPROPS_FREE:
|
||||
if (add_to_lpt_heap(c, lprops, cat))
|
||||
break;
|
||||
/* No more room on heap so make it uncategorized */
|
||||
/* No more room on heap so make it un-categorized */
|
||||
cat = LPROPS_UNCAT;
|
||||
/* Fall through */
|
||||
case LPROPS_UNCAT:
|
||||
@ -300,8 +293,11 @@ void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops,
|
||||
default:
|
||||
ubifs_assert(0);
|
||||
}
|
||||
|
||||
lprops->flags &= ~LPROPS_CAT_MASK;
|
||||
lprops->flags |= cat;
|
||||
c->in_a_category_cnt += 1;
|
||||
ubifs_assert(c->in_a_category_cnt <= c->main_lebs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -334,6 +330,9 @@ static void ubifs_remove_from_cat(struct ubifs_info *c,
|
||||
default:
|
||||
ubifs_assert(0);
|
||||
}
|
||||
|
||||
c->in_a_category_cnt -= 1;
|
||||
ubifs_assert(c->in_a_category_cnt >= 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -375,8 +374,8 @@ void ubifs_replace_cat(struct ubifs_info *c, struct ubifs_lprops *old_lprops,
|
||||
* @lprops: LEB properties
|
||||
*
|
||||
* A LEB may have fallen off of the bottom of a heap, and ended up as
|
||||
* uncategorized even though it has enough space for us now. If that is the case
|
||||
* this function will put the LEB back onto a heap.
|
||||
* un-categorized even though it has enough space for us now. If that is the
|
||||
* case this function will put the LEB back onto a heap.
|
||||
*/
|
||||
void ubifs_ensure_cat(struct ubifs_info *c, struct ubifs_lprops *lprops)
|
||||
{
|
||||
@ -436,10 +435,10 @@ int ubifs_categorize_lprops(const struct ubifs_info *c,
|
||||
/**
|
||||
* change_category - change LEB properties category.
|
||||
* @c: UBIFS file-system description object
|
||||
* @lprops: LEB properties to recategorize
|
||||
* @lprops: LEB properties to re-categorize
|
||||
*
|
||||
* LEB properties are categorized to enable fast find operations. When the LEB
|
||||
* properties change they must be recategorized.
|
||||
* properties change they must be re-categorized.
|
||||
*/
|
||||
static void change_category(struct ubifs_info *c, struct ubifs_lprops *lprops)
|
||||
{
|
||||
@ -447,7 +446,7 @@ static void change_category(struct ubifs_info *c, struct ubifs_lprops *lprops)
|
||||
int new_cat = ubifs_categorize_lprops(c, lprops);
|
||||
|
||||
if (old_cat == new_cat) {
|
||||
struct ubifs_lpt_heap *heap = &c->lpt_heap[new_cat - 1];
|
||||
struct ubifs_lpt_heap *heap;
|
||||
|
||||
/* lprops on a heap now must be moved up or down */
|
||||
if (new_cat < 1 || new_cat > LPROPS_HEAP_CNT)
|
||||
@ -461,21 +460,18 @@ static void change_category(struct ubifs_info *c, struct ubifs_lprops *lprops)
|
||||
}
|
||||
|
||||
/**
|
||||
* calc_dark - calculate LEB dark space size.
|
||||
* ubifs_calc_dark - calculate LEB dark space size.
|
||||
* @c: the UBIFS file-system description object
|
||||
* @spc: amount of free and dirty space in the LEB
|
||||
*
|
||||
* This function calculates amount of dark space in an LEB which has @spc bytes
|
||||
* of free and dirty space. Returns the calculations result.
|
||||
* This function calculates and returns amount of dark space in an LEB which
|
||||
* has @spc bytes of free and dirty space.
|
||||
*
|
||||
* Dark space is the space which is not always usable - it depends on which
|
||||
* nodes are written in which order. E.g., if an LEB has only 512 free bytes,
|
||||
* it is dark space, because it cannot fit a large data node. So UBIFS cannot
|
||||
* count on this LEB and treat these 512 bytes as usable because it is not true
|
||||
* if, for example, only big chunks of uncompressible data will be written to
|
||||
* the FS.
|
||||
* UBIFS is trying to account the space which might not be usable, and this
|
||||
* space is called "dark space". For example, if an LEB has only %512 free
|
||||
* bytes, it is dark space, because it cannot fit a large data node.
|
||||
*/
|
||||
static int calc_dark(struct ubifs_info *c, int spc)
|
||||
int ubifs_calc_dark(const struct ubifs_info *c, int spc)
|
||||
{
|
||||
ubifs_assert(!(spc & 7));
|
||||
|
||||
@ -507,7 +503,7 @@ static int is_lprops_dirty(struct ubifs_info *c, struct ubifs_lprops *lprops)
|
||||
pnode = (struct ubifs_pnode *)container_of(lprops - pos,
|
||||
struct ubifs_pnode,
|
||||
lprops[0]);
|
||||
return !test_bit(COW_ZNODE, &pnode->flags) &&
|
||||
return !test_bit(COW_CNODE, &pnode->flags) &&
|
||||
test_bit(DIRTY_CNODE, &pnode->flags);
|
||||
}
|
||||
|
||||
@ -518,7 +514,7 @@ static int is_lprops_dirty(struct ubifs_info *c, struct ubifs_lprops *lprops)
|
||||
* @free: new free space amount
|
||||
* @dirty: new dirty space amount
|
||||
* @flags: new flags
|
||||
* @idx_gc_cnt: change to the count of idx_gc list
|
||||
* @idx_gc_cnt: change to the count of @idx_gc list
|
||||
*
|
||||
* This function changes LEB properties (@free, @dirty or @flag). However, the
|
||||
* property which has the %LPROPS_NC value is not changed. Returns a pointer to
|
||||
@ -535,7 +531,7 @@ const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
|
||||
{
|
||||
/*
|
||||
* This is the only function that is allowed to change lprops, so we
|
||||
* discard the const qualifier.
|
||||
* discard the "const" qualifier.
|
||||
*/
|
||||
struct ubifs_lprops *lprops = (struct ubifs_lprops *)lp;
|
||||
|
||||
@ -575,7 +571,7 @@ const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
|
||||
if (old_spc < c->dead_wm)
|
||||
c->lst.total_dead -= old_spc;
|
||||
else
|
||||
c->lst.total_dark -= calc_dark(c, old_spc);
|
||||
c->lst.total_dark -= ubifs_calc_dark(c, old_spc);
|
||||
|
||||
c->lst.total_used -= c->leb_size - old_spc;
|
||||
}
|
||||
@ -616,7 +612,7 @@ const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
|
||||
if (new_spc < c->dead_wm)
|
||||
c->lst.total_dead += new_spc;
|
||||
else
|
||||
c->lst.total_dark += calc_dark(c, new_spc);
|
||||
c->lst.total_dark += ubifs_calc_dark(c, new_spc);
|
||||
|
||||
c->lst.total_used += c->leb_size - new_spc;
|
||||
}
|
||||
@ -678,6 +674,9 @@ int ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
|
||||
|
||||
out:
|
||||
ubifs_release_lprops(c);
|
||||
if (err)
|
||||
ubifs_err("cannot change properties of LEB %d, error %d",
|
||||
lnum, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -714,6 +713,9 @@ int ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
|
||||
|
||||
out:
|
||||
ubifs_release_lprops(c);
|
||||
if (err)
|
||||
ubifs_err("cannot update properties of LEB %d, error %d",
|
||||
lnum, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -737,6 +739,8 @@ int ubifs_read_one_lp(struct ubifs_info *c, int lnum, struct ubifs_lprops *lp)
|
||||
lpp = ubifs_lpt_lookup(c, lnum);
|
||||
if (IS_ERR(lpp)) {
|
||||
err = PTR_ERR(lpp);
|
||||
ubifs_err("cannot read properties of LEB %d, error %d",
|
||||
lnum, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -840,3 +844,471 @@ const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c)
|
||||
ubifs_assert(lprops->free + lprops->dirty == c->leb_size);
|
||||
return lprops;
|
||||
}
|
||||
|
||||
/*
|
||||
* Everything below is related to debugging.
|
||||
*/
|
||||
|
||||
/**
|
||||
* dbg_check_cats - check category heaps and lists.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function returns %0 on success and a negative error code on failure.
|
||||
*/
|
||||
int dbg_check_cats(struct ubifs_info *c)
|
||||
{
|
||||
struct ubifs_lprops *lprops;
|
||||
struct list_head *pos;
|
||||
int i, cat;
|
||||
|
||||
if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c))
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(lprops, &c->empty_list, list) {
|
||||
if (lprops->free != c->leb_size) {
|
||||
ubifs_err("non-empty LEB %d on empty list (free %d dirty %d flags %d)",
|
||||
lprops->lnum, lprops->free, lprops->dirty,
|
||||
lprops->flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (lprops->flags & LPROPS_TAKEN) {
|
||||
ubifs_err("taken LEB %d on empty list (free %d dirty %d flags %d)",
|
||||
lprops->lnum, lprops->free, lprops->dirty,
|
||||
lprops->flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry(lprops, &c->freeable_list, list) {
|
||||
if (lprops->free + lprops->dirty != c->leb_size) {
|
||||
ubifs_err("non-freeable LEB %d on freeable list (free %d dirty %d flags %d)",
|
||||
lprops->lnum, lprops->free, lprops->dirty,
|
||||
lprops->flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (lprops->flags & LPROPS_TAKEN) {
|
||||
ubifs_err("taken LEB %d on freeable list (free %d dirty %d flags %d)",
|
||||
lprops->lnum, lprops->free, lprops->dirty,
|
||||
lprops->flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
if (i != c->freeable_cnt) {
|
||||
ubifs_err("freeable list count %d expected %d", i,
|
||||
c->freeable_cnt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
list_for_each(pos, &c->idx_gc)
|
||||
i += 1;
|
||||
if (i != c->idx_gc_cnt) {
|
||||
ubifs_err("idx_gc list count %d expected %d", i,
|
||||
c->idx_gc_cnt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
list_for_each_entry(lprops, &c->frdi_idx_list, list) {
|
||||
if (lprops->free + lprops->dirty != c->leb_size) {
|
||||
ubifs_err("non-freeable LEB %d on frdi_idx list (free %d dirty %d flags %d)",
|
||||
lprops->lnum, lprops->free, lprops->dirty,
|
||||
lprops->flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (lprops->flags & LPROPS_TAKEN) {
|
||||
ubifs_err("taken LEB %d on frdi_idx list (free %d dirty %d flags %d)",
|
||||
lprops->lnum, lprops->free, lprops->dirty,
|
||||
lprops->flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!(lprops->flags & LPROPS_INDEX)) {
|
||||
ubifs_err("non-index LEB %d on frdi_idx list (free %d dirty %d flags %d)",
|
||||
lprops->lnum, lprops->free, lprops->dirty,
|
||||
lprops->flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
for (cat = 1; cat <= LPROPS_HEAP_CNT; cat++) {
|
||||
struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
|
||||
|
||||
for (i = 0; i < heap->cnt; i++) {
|
||||
lprops = heap->arr[i];
|
||||
if (!lprops) {
|
||||
ubifs_err("null ptr in LPT heap cat %d", cat);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (lprops->hpos != i) {
|
||||
ubifs_err("bad ptr in LPT heap cat %d", cat);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (lprops->flags & LPROPS_TAKEN) {
|
||||
ubifs_err("taken LEB in LPT heap cat %d", cat);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat,
|
||||
int add_pos)
|
||||
{
|
||||
int i = 0, j, err = 0;
|
||||
|
||||
if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c))
|
||||
return;
|
||||
|
||||
for (i = 0; i < heap->cnt; i++) {
|
||||
struct ubifs_lprops *lprops = heap->arr[i];
|
||||
struct ubifs_lprops *lp;
|
||||
|
||||
if (i != add_pos)
|
||||
if ((lprops->flags & LPROPS_CAT_MASK) != cat) {
|
||||
err = 1;
|
||||
goto out;
|
||||
}
|
||||
if (lprops->hpos != i) {
|
||||
err = 2;
|
||||
goto out;
|
||||
}
|
||||
lp = ubifs_lpt_lookup(c, lprops->lnum);
|
||||
if (IS_ERR(lp)) {
|
||||
err = 3;
|
||||
goto out;
|
||||
}
|
||||
if (lprops != lp) {
|
||||
ubifs_err("lprops %zx lp %zx lprops->lnum %d lp->lnum %d",
|
||||
(size_t)lprops, (size_t)lp, lprops->lnum,
|
||||
lp->lnum);
|
||||
err = 4;
|
||||
goto out;
|
||||
}
|
||||
for (j = 0; j < i; j++) {
|
||||
lp = heap->arr[j];
|
||||
if (lp == lprops) {
|
||||
err = 5;
|
||||
goto out;
|
||||
}
|
||||
if (lp->lnum == lprops->lnum) {
|
||||
err = 6;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (err) {
|
||||
ubifs_err("failed cat %d hpos %d err %d", cat, i, err);
|
||||
dump_stack();
|
||||
ubifs_dump_heap(c, heap, cat);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* scan_check_cb - scan callback.
|
||||
* @c: the UBIFS file-system description object
|
||||
* @lp: LEB properties to scan
|
||||
* @in_tree: whether the LEB properties are in main memory
|
||||
* @lst: lprops statistics to update
|
||||
*
|
||||
* This function returns a code that indicates whether the scan should continue
|
||||
* (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
|
||||
* in main memory (%LPT_SCAN_ADD), or whether the scan should stop
|
||||
* (%LPT_SCAN_STOP).
|
||||
*/
|
||||
static int scan_check_cb(struct ubifs_info *c,
|
||||
const struct ubifs_lprops *lp, int in_tree,
|
||||
struct ubifs_lp_stats *lst)
|
||||
{
|
||||
struct ubifs_scan_leb *sleb;
|
||||
struct ubifs_scan_node *snod;
|
||||
int cat, lnum = lp->lnum, is_idx = 0, used = 0, freef, dirty, ret;
|
||||
void *buf = NULL;
|
||||
|
||||
cat = lp->flags & LPROPS_CAT_MASK;
|
||||
if (cat != LPROPS_UNCAT) {
|
||||
cat = ubifs_categorize_lprops(c, lp);
|
||||
if (cat != (lp->flags & LPROPS_CAT_MASK)) {
|
||||
ubifs_err("bad LEB category %d expected %d",
|
||||
(lp->flags & LPROPS_CAT_MASK), cat);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check lp is on its category list (if it has one) */
|
||||
if (in_tree) {
|
||||
struct list_head *list = NULL;
|
||||
|
||||
switch (cat) {
|
||||
case LPROPS_EMPTY:
|
||||
list = &c->empty_list;
|
||||
break;
|
||||
case LPROPS_FREEABLE:
|
||||
list = &c->freeable_list;
|
||||
break;
|
||||
case LPROPS_FRDI_IDX:
|
||||
list = &c->frdi_idx_list;
|
||||
break;
|
||||
case LPROPS_UNCAT:
|
||||
list = &c->uncat_list;
|
||||
break;
|
||||
}
|
||||
if (list) {
|
||||
struct ubifs_lprops *lprops;
|
||||
int found = 0;
|
||||
|
||||
list_for_each_entry(lprops, list, list) {
|
||||
if (lprops == lp) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
ubifs_err("bad LPT list (category %d)", cat);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check lp is on its category heap (if it has one) */
|
||||
if (in_tree && cat > 0 && cat <= LPROPS_HEAP_CNT) {
|
||||
struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
|
||||
|
||||
if ((lp->hpos != -1 && heap->arr[lp->hpos]->lnum != lnum) ||
|
||||
lp != heap->arr[lp->hpos]) {
|
||||
ubifs_err("bad LPT heap (category %d)", cat);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* After an unclean unmount, empty and freeable LEBs
|
||||
* may contain garbage - do not scan them.
|
||||
*/
|
||||
if (lp->free == c->leb_size) {
|
||||
lst->empty_lebs += 1;
|
||||
lst->total_free += c->leb_size;
|
||||
lst->total_dark += ubifs_calc_dark(c, c->leb_size);
|
||||
return LPT_SCAN_CONTINUE;
|
||||
}
|
||||
if (lp->free + lp->dirty == c->leb_size &&
|
||||
!(lp->flags & LPROPS_INDEX)) {
|
||||
lst->total_free += lp->free;
|
||||
lst->total_dirty += lp->dirty;
|
||||
lst->total_dark += ubifs_calc_dark(c, c->leb_size);
|
||||
return LPT_SCAN_CONTINUE;
|
||||
}
|
||||
|
||||
sleb = ubifs_scan(c, lnum, 0, buf, 0);
|
||||
if (IS_ERR(sleb)) {
|
||||
ret = PTR_ERR(sleb);
|
||||
if (ret == -EUCLEAN) {
|
||||
ubifs_dump_lprops(c);
|
||||
ubifs_dump_budg(c, &c->bi);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
is_idx = -1;
|
||||
list_for_each_entry(snod, &sleb->nodes, list) {
|
||||
int found, level = 0;
|
||||
|
||||
cond_resched();
|
||||
|
||||
if (is_idx == -1)
|
||||
is_idx = (snod->type == UBIFS_IDX_NODE) ? 1 : 0;
|
||||
|
||||
if (is_idx && snod->type != UBIFS_IDX_NODE) {
|
||||
ubifs_err("indexing node in data LEB %d:%d",
|
||||
lnum, snod->offs);
|
||||
goto out_destroy;
|
||||
}
|
||||
|
||||
if (snod->type == UBIFS_IDX_NODE) {
|
||||
struct ubifs_idx_node *idx = snod->node;
|
||||
|
||||
key_read(c, ubifs_idx_key(c, idx), &snod->key);
|
||||
level = le16_to_cpu(idx->level);
|
||||
}
|
||||
|
||||
found = ubifs_tnc_has_node(c, &snod->key, level, lnum,
|
||||
snod->offs, is_idx);
|
||||
if (found) {
|
||||
if (found < 0)
|
||||
goto out_destroy;
|
||||
used += ALIGN(snod->len, 8);
|
||||
}
|
||||
}
|
||||
|
||||
freef = c->leb_size - sleb->endpt;
|
||||
dirty = sleb->endpt - used;
|
||||
|
||||
if (freef > c->leb_size || freef < 0 || dirty > c->leb_size ||
|
||||
dirty < 0) {
|
||||
ubifs_err("bad calculated accounting for LEB %d: free %d, dirty %d",
|
||||
lnum, freef, dirty);
|
||||
goto out_destroy;
|
||||
}
|
||||
|
||||
if (lp->free + lp->dirty == c->leb_size &&
|
||||
freef + dirty == c->leb_size)
|
||||
if ((is_idx && !(lp->flags & LPROPS_INDEX)) ||
|
||||
(!is_idx && freef == c->leb_size) ||
|
||||
lp->free == c->leb_size) {
|
||||
/*
|
||||
* Empty or freeable LEBs could contain index
|
||||
* nodes from an uncompleted commit due to an
|
||||
* unclean unmount. Or they could be empty for
|
||||
* the same reason. Or it may simply not have been
|
||||
* unmapped.
|
||||
*/
|
||||
freef = lp->free;
|
||||
dirty = lp->dirty;
|
||||
is_idx = 0;
|
||||
}
|
||||
|
||||
if (is_idx && lp->free + lp->dirty == freef + dirty &&
|
||||
lnum != c->ihead_lnum) {
|
||||
/*
|
||||
* After an unclean unmount, an index LEB could have a different
|
||||
* amount of free space than the value recorded by lprops. That
|
||||
* is because the in-the-gaps method may use free space or
|
||||
* create free space (as a side-effect of using ubi_leb_change
|
||||
* and not writing the whole LEB). The incorrect free space
|
||||
* value is not a problem because the index is only ever
|
||||
* allocated empty LEBs, so there will never be an attempt to
|
||||
* write to the free space at the end of an index LEB - except
|
||||
* by the in-the-gaps method for which it is not a problem.
|
||||
*/
|
||||
freef = lp->free;
|
||||
dirty = lp->dirty;
|
||||
}
|
||||
|
||||
if (lp->free != freef || lp->dirty != dirty)
|
||||
goto out_print;
|
||||
|
||||
if (is_idx && !(lp->flags & LPROPS_INDEX)) {
|
||||
if (freef == c->leb_size)
|
||||
/* Free but not unmapped LEB, it's fine */
|
||||
is_idx = 0;
|
||||
else {
|
||||
ubifs_err("indexing node without indexing flag");
|
||||
goto out_print;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_idx && (lp->flags & LPROPS_INDEX)) {
|
||||
ubifs_err("data node with indexing flag");
|
||||
goto out_print;
|
||||
}
|
||||
|
||||
if (freef == c->leb_size)
|
||||
lst->empty_lebs += 1;
|
||||
|
||||
if (is_idx)
|
||||
lst->idx_lebs += 1;
|
||||
|
||||
if (!(lp->flags & LPROPS_INDEX))
|
||||
lst->total_used += c->leb_size - freef - dirty;
|
||||
lst->total_free += freef;
|
||||
lst->total_dirty += dirty;
|
||||
|
||||
if (!(lp->flags & LPROPS_INDEX)) {
|
||||
int spc = freef + dirty;
|
||||
|
||||
if (spc < c->dead_wm)
|
||||
lst->total_dead += spc;
|
||||
else
|
||||
lst->total_dark += ubifs_calc_dark(c, spc);
|
||||
}
|
||||
|
||||
ubifs_scan_destroy(sleb);
|
||||
vfree(buf);
|
||||
return LPT_SCAN_CONTINUE;
|
||||
|
||||
out_print:
|
||||
ubifs_err("bad accounting of LEB %d: free %d, dirty %d flags %#x, should be free %d, dirty %d",
|
||||
lnum, lp->free, lp->dirty, lp->flags, freef, dirty);
|
||||
ubifs_dump_leb(c, lnum);
|
||||
out_destroy:
|
||||
ubifs_scan_destroy(sleb);
|
||||
ret = -EINVAL;
|
||||
out:
|
||||
vfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_check_lprops - check all LEB properties.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function checks all LEB properties and makes sure they are all correct.
|
||||
* It returns zero if everything is fine, %-EINVAL if there is an inconsistency
|
||||
* and other negative error codes in case of other errors. This function is
|
||||
* called while the file system is locked (because of commit start), so no
|
||||
* additional locking is required. Note that locking the LPT mutex would cause
|
||||
* a circular lock dependency with the TNC mutex.
|
||||
*/
|
||||
int dbg_check_lprops(struct ubifs_info *c)
|
||||
{
|
||||
int i, err;
|
||||
struct ubifs_lp_stats lst;
|
||||
|
||||
if (!dbg_is_chk_lprops(c))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* As we are going to scan the media, the write buffers have to be
|
||||
* synchronized.
|
||||
*/
|
||||
for (i = 0; i < c->jhead_cnt; i++) {
|
||||
err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
memset(&lst, 0, sizeof(struct ubifs_lp_stats));
|
||||
err = ubifs_lpt_scan_nolock(c, c->main_first, c->leb_cnt - 1,
|
||||
(ubifs_lpt_scan_callback)scan_check_cb,
|
||||
&lst);
|
||||
if (err && err != -ENOSPC)
|
||||
goto out;
|
||||
|
||||
if (lst.empty_lebs != c->lst.empty_lebs ||
|
||||
lst.idx_lebs != c->lst.idx_lebs ||
|
||||
lst.total_free != c->lst.total_free ||
|
||||
lst.total_dirty != c->lst.total_dirty ||
|
||||
lst.total_used != c->lst.total_used) {
|
||||
ubifs_err("bad overall accounting");
|
||||
ubifs_err("calculated: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld",
|
||||
lst.empty_lebs, lst.idx_lebs, lst.total_free,
|
||||
lst.total_dirty, lst.total_used);
|
||||
ubifs_err("read from lprops: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld",
|
||||
c->lst.empty_lebs, c->lst.idx_lebs, c->lst.total_free,
|
||||
c->lst.total_dirty, c->lst.total_used);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (lst.total_dead != c->lst.total_dead ||
|
||||
lst.total_dark != c->lst.total_dark) {
|
||||
ubifs_err("bad dead/dark space accounting");
|
||||
ubifs_err("calculated: total_dead %lld, total_dark %lld",
|
||||
lst.total_dead, lst.total_dark);
|
||||
ubifs_err("read from lprops: total_dead %lld, total_dark %lld",
|
||||
c->lst.total_dead, c->lst.total_dark);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = dbg_check_cats(c);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
1242
fs/ubifs/lpt.c
1242
fs/ubifs/lpt.c
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -3,18 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2006-2008 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Authors: Artem Bityutskiy (Битюцкий Артём)
|
||||
* Adrian Hunter
|
||||
@ -22,14 +11,21 @@
|
||||
|
||||
/* This file implements reading and writing the master node */
|
||||
|
||||
#define __UBOOT__
|
||||
#include "ubifs.h"
|
||||
#ifdef __UBOOT__
|
||||
#include <linux/compat.h>
|
||||
#include <linux/err.h>
|
||||
#include <ubi_uboot.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* scan_for_master - search the valid master node.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function scans the master node LEBs and search for the latest master
|
||||
* node. Returns zero in case of success and a negative error code in case of
|
||||
* node. Returns zero in case of success, %-EUCLEAN if there master area is
|
||||
* corrupted and requires recovery, and a negative error code in case of
|
||||
* failure.
|
||||
*/
|
||||
static int scan_for_master(struct ubifs_info *c)
|
||||
@ -40,7 +36,7 @@ static int scan_for_master(struct ubifs_info *c)
|
||||
|
||||
lnum = UBIFS_MST_LNUM;
|
||||
|
||||
sleb = ubifs_scan(c, lnum, 0, c->sbuf);
|
||||
sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
|
||||
if (IS_ERR(sleb))
|
||||
return PTR_ERR(sleb);
|
||||
nodes_cnt = sleb->nodes_cnt;
|
||||
@ -48,7 +44,7 @@ static int scan_for_master(struct ubifs_info *c)
|
||||
snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node,
|
||||
list);
|
||||
if (snod->type != UBIFS_MST_NODE)
|
||||
goto out;
|
||||
goto out_dump;
|
||||
memcpy(c->mst_node, snod->node, snod->len);
|
||||
offs = snod->offs;
|
||||
}
|
||||
@ -56,7 +52,7 @@ static int scan_for_master(struct ubifs_info *c)
|
||||
|
||||
lnum += 1;
|
||||
|
||||
sleb = ubifs_scan(c, lnum, 0, c->sbuf);
|
||||
sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
|
||||
if (IS_ERR(sleb))
|
||||
return PTR_ERR(sleb);
|
||||
if (sleb->nodes_cnt != nodes_cnt)
|
||||
@ -65,7 +61,7 @@ static int scan_for_master(struct ubifs_info *c)
|
||||
goto out;
|
||||
snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, list);
|
||||
if (snod->type != UBIFS_MST_NODE)
|
||||
goto out;
|
||||
goto out_dump;
|
||||
if (snod->offs != offs)
|
||||
goto out;
|
||||
if (memcmp((void *)c->mst_node + UBIFS_CH_SZ,
|
||||
@ -78,6 +74,12 @@ static int scan_for_master(struct ubifs_info *c)
|
||||
|
||||
out:
|
||||
ubifs_scan_destroy(sleb);
|
||||
return -EUCLEAN;
|
||||
|
||||
out_dump:
|
||||
ubifs_err("unexpected node type %d master LEB %d:%d",
|
||||
snod->type, lnum, snod->offs);
|
||||
ubifs_scan_destroy(sleb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -141,7 +143,7 @@ static int validate_master(const struct ubifs_info *c)
|
||||
}
|
||||
|
||||
main_sz = (long long)c->main_lebs * c->leb_size;
|
||||
if (c->old_idx_sz & 7 || c->old_idx_sz >= main_sz) {
|
||||
if (c->bi.old_idx_sz & 7 || c->bi.old_idx_sz >= main_sz) {
|
||||
err = 9;
|
||||
goto out;
|
||||
}
|
||||
@ -211,7 +213,7 @@ static int validate_master(const struct ubifs_info *c)
|
||||
}
|
||||
|
||||
if (c->lst.total_dead + c->lst.total_dark +
|
||||
c->lst.total_used + c->old_idx_sz > main_sz) {
|
||||
c->lst.total_used + c->bi.old_idx_sz > main_sz) {
|
||||
err = 21;
|
||||
goto out;
|
||||
}
|
||||
@ -234,7 +236,7 @@ static int validate_master(const struct ubifs_info *c)
|
||||
|
||||
out:
|
||||
ubifs_err("bad master node at offset %d error %d", c->mst_offs, err);
|
||||
dbg_dump_node(c, c->mst_node);
|
||||
ubifs_dump_node(c, c->mst_node);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -256,7 +258,8 @@ int ubifs_read_master(struct ubifs_info *c)
|
||||
|
||||
err = scan_for_master(c);
|
||||
if (err) {
|
||||
err = ubifs_recover_master_node(c);
|
||||
if (err == -EUCLEAN)
|
||||
err = ubifs_recover_master_node(c);
|
||||
if (err)
|
||||
/*
|
||||
* Note, we do not free 'c->mst_node' here because the
|
||||
@ -278,7 +281,7 @@ int ubifs_read_master(struct ubifs_info *c)
|
||||
c->gc_lnum = le32_to_cpu(c->mst_node->gc_lnum);
|
||||
c->ihead_lnum = le32_to_cpu(c->mst_node->ihead_lnum);
|
||||
c->ihead_offs = le32_to_cpu(c->mst_node->ihead_offs);
|
||||
c->old_idx_sz = le64_to_cpu(c->mst_node->index_size);
|
||||
c->bi.old_idx_sz = le64_to_cpu(c->mst_node->index_size);
|
||||
c->lpt_lnum = le32_to_cpu(c->mst_node->lpt_lnum);
|
||||
c->lpt_offs = le32_to_cpu(c->mst_node->lpt_offs);
|
||||
c->nhead_lnum = le32_to_cpu(c->mst_node->nhead_lnum);
|
||||
@ -297,7 +300,7 @@ int ubifs_read_master(struct ubifs_info *c)
|
||||
c->lst.total_dead = le64_to_cpu(c->mst_node->total_dead);
|
||||
c->lst.total_dark = le64_to_cpu(c->mst_node->total_dark);
|
||||
|
||||
c->calc_idx_sz = c->old_idx_sz;
|
||||
c->calc_idx_sz = c->bi.old_idx_sz;
|
||||
|
||||
if (c->mst_node->flags & cpu_to_le32(UBIFS_MST_NO_ORPHS))
|
||||
c->no_orphs = 1;
|
||||
@ -309,7 +312,7 @@ int ubifs_read_master(struct ubifs_info *c)
|
||||
if (c->leb_cnt < old_leb_cnt ||
|
||||
c->leb_cnt < UBIFS_MIN_LEB_CNT) {
|
||||
ubifs_err("bad leb_cnt on master node");
|
||||
dbg_dump_node(c, c->mst_node);
|
||||
ubifs_dump_node(c, c->mst_node);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -335,7 +338,58 @@ int ubifs_read_master(struct ubifs_info *c)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
#ifndef __UBOOT__
|
||||
err = dbg_old_index_check_init(c, &c->zroot);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* ubifs_write_master - write master node.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function writes the master node. The caller has to take the
|
||||
* @c->mst_mutex lock before calling this function. Returns zero in case of
|
||||
* success and a negative error code in case of failure. The master node is
|
||||
* written twice to enable recovery.
|
||||
*/
|
||||
int ubifs_write_master(struct ubifs_info *c)
|
||||
{
|
||||
int err, lnum, offs, len;
|
||||
|
||||
ubifs_assert(!c->ro_media && !c->ro_mount);
|
||||
if (c->ro_error)
|
||||
return -EROFS;
|
||||
|
||||
lnum = UBIFS_MST_LNUM;
|
||||
offs = c->mst_offs + c->mst_node_alsz;
|
||||
len = UBIFS_MST_NODE_SZ;
|
||||
|
||||
if (offs + UBIFS_MST_NODE_SZ > c->leb_size) {
|
||||
err = ubifs_leb_unmap(c, lnum);
|
||||
if (err)
|
||||
return err;
|
||||
offs = 0;
|
||||
}
|
||||
|
||||
c->mst_offs = offs;
|
||||
c->mst_node->highest_inum = cpu_to_le64(c->highest_inum);
|
||||
|
||||
err = ubifs_write_node(c, c->mst_node, len, lnum, offs);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
lnum += 1;
|
||||
|
||||
if (offs == 0) {
|
||||
err = ubifs_leb_unmap(c, lnum);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
err = ubifs_write_node(c, c->mst_node, len, lnum, offs);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
159
fs/ubifs/misc.h
159
fs/ubifs/misc.h
@ -3,18 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2006-2008 Nokia Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Authors: Artem Bityutskiy (Битюцкий Артём)
|
||||
* Adrian Hunter
|
||||
@ -27,6 +16,7 @@
|
||||
#ifndef __UBIFS_MISC_H__
|
||||
#define __UBIFS_MISC_H__
|
||||
|
||||
#define __UBOOT__
|
||||
/**
|
||||
* ubifs_zn_dirty - check if znode is dirty.
|
||||
* @znode: znode to check
|
||||
@ -38,6 +28,29 @@ static inline int ubifs_zn_dirty(const struct ubifs_znode *znode)
|
||||
return !!test_bit(DIRTY_ZNODE, &znode->flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_zn_obsolete - check if znode is obsolete.
|
||||
* @znode: znode to check
|
||||
*
|
||||
* This helper function returns %1 if @znode is obsolete and %0 otherwise.
|
||||
*/
|
||||
static inline int ubifs_zn_obsolete(const struct ubifs_znode *znode)
|
||||
{
|
||||
return !!test_bit(OBSOLETE_ZNODE, &znode->flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_zn_cow - check if znode has to be copied on write.
|
||||
* @znode: znode to check
|
||||
*
|
||||
* This helper function returns %1 if @znode is has COW flag set and %0
|
||||
* otherwise.
|
||||
*/
|
||||
static inline int ubifs_zn_cow(const struct ubifs_znode *znode)
|
||||
{
|
||||
return !!test_bit(COW_ZNODE, &znode->flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_wake_up_bgt - wake up background thread.
|
||||
* @c: UBIFS file-system description object
|
||||
@ -121,82 +134,27 @@ static inline int ubifs_wbuf_sync(struct ubifs_wbuf *wbuf)
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* ubifs_leb_unmap - unmap an LEB.
|
||||
* @c: UBIFS file-system description object
|
||||
* @lnum: LEB number to unmap
|
||||
* ubifs_encode_dev - encode device node IDs.
|
||||
* @dev: UBIFS device node information
|
||||
* @rdev: device IDs to encode
|
||||
*
|
||||
* This function returns %0 on success and a negative error code on failure.
|
||||
* This is a helper function which encodes major/minor numbers of a device node
|
||||
* into UBIFS device node description. We use standard Linux "new" and "huge"
|
||||
* encodings.
|
||||
*/
|
||||
static inline int ubifs_leb_unmap(const struct ubifs_info *c, int lnum)
|
||||
static inline int ubifs_encode_dev(union ubifs_dev_desc *dev, dev_t rdev)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (c->ro_media)
|
||||
return -EROFS;
|
||||
err = ubi_leb_unmap(c->ubi, lnum);
|
||||
if (err) {
|
||||
ubifs_err("unmap LEB %d failed, error %d", lnum, err);
|
||||
return err;
|
||||
if (new_valid_dev(rdev)) {
|
||||
dev->new = cpu_to_le32(new_encode_dev(rdev));
|
||||
return sizeof(dev->new);
|
||||
} else {
|
||||
dev->huge = cpu_to_le64(huge_encode_dev(rdev));
|
||||
return sizeof(dev->huge);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_leb_write - write to a LEB.
|
||||
* @c: UBIFS file-system description object
|
||||
* @lnum: LEB number to write
|
||||
* @buf: buffer to write from
|
||||
* @offs: offset within LEB to write to
|
||||
* @len: length to write
|
||||
* @dtype: data type
|
||||
*
|
||||
* This function returns %0 on success and a negative error code on failure.
|
||||
*/
|
||||
static inline int ubifs_leb_write(const struct ubifs_info *c, int lnum,
|
||||
const void *buf, int offs, int len, int dtype)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (c->ro_media)
|
||||
return -EROFS;
|
||||
err = ubi_leb_write(c->ubi, lnum, buf, offs, len, dtype);
|
||||
if (err) {
|
||||
ubifs_err("writing %d bytes at %d:%d, error %d",
|
||||
len, lnum, offs, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_leb_change - atomic LEB change.
|
||||
* @c: UBIFS file-system description object
|
||||
* @lnum: LEB number to write
|
||||
* @buf: buffer to write from
|
||||
* @len: length to write
|
||||
* @dtype: data type
|
||||
*
|
||||
* This function returns %0 on success and a negative error code on failure.
|
||||
*/
|
||||
static inline int ubifs_leb_change(const struct ubifs_info *c, int lnum,
|
||||
const void *buf, int len, int dtype)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (c->ro_media)
|
||||
return -EROFS;
|
||||
err = ubi_leb_change(c->ubi, lnum, buf, len, dtype);
|
||||
if (err) {
|
||||
ubifs_err("changing %d bytes in LEB %d, error %d",
|
||||
len, lnum, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ubifs_add_dirt - add dirty space to LEB properties.
|
||||
@ -260,8 +218,24 @@ struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c,
|
||||
static inline void *ubifs_idx_key(const struct ubifs_info *c,
|
||||
const struct ubifs_idx_node *idx)
|
||||
{
|
||||
const __u8 *branch = idx->branches;
|
||||
return (void *)((struct ubifs_branch *)branch)->key;
|
||||
#ifndef __UBOOT__
|
||||
return (void *)((struct ubifs_branch *)idx->branches)->key;
|
||||
#else
|
||||
struct ubifs_branch *tmp;
|
||||
|
||||
tmp = (struct ubifs_branch *)idx->branches;
|
||||
return (void *)tmp->key;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_current_time - round current time to time granularity.
|
||||
* @inode: inode
|
||||
*/
|
||||
static inline struct timespec ubifs_current_time(struct inode *inode)
|
||||
{
|
||||
return (inode->i_sb->s_time_gran < NSEC_PER_SEC) ?
|
||||
current_fs_time(inode->i_sb) : CURRENT_TIME_SEC;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -308,4 +282,21 @@ static inline void ubifs_release_lprops(struct ubifs_info *c)
|
||||
mutex_unlock(&c->lp_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_next_log_lnum - switch to the next log LEB.
|
||||
* @c: UBIFS file-system description object
|
||||
* @lnum: current log LEB
|
||||
*
|
||||
* This helper function returns the log LEB number which goes next after LEB
|
||||
* 'lnum'.
|
||||
*/
|
||||
static inline int ubifs_next_log_lnum(const struct ubifs_info *c, int lnum)
|
||||
{
|
||||
lnum += 1;
|
||||
if (lnum > c->log_last)
|
||||
lnum = UBIFS_LOG_LNUM;
|
||||
|
||||
return lnum;
|
||||
}
|
||||
|
||||
#endif /* __UBIFS_MISC_H__ */
|
||||
|
@ -3,22 +3,12 @@
|
||||
*
|
||||
* Copyright (C) 2006-2008 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Author: Adrian Hunter
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include "ubifs.h"
|
||||
|
||||
/*
|
||||
@ -52,6 +42,166 @@
|
||||
* than the maximum number of orphans allowed.
|
||||
*/
|
||||
|
||||
static int dbg_check_orphans(struct ubifs_info *c);
|
||||
|
||||
/**
|
||||
* ubifs_add_orphan - add an orphan.
|
||||
* @c: UBIFS file-system description object
|
||||
* @inum: orphan inode number
|
||||
*
|
||||
* Add an orphan. This function is called when an inodes link count drops to
|
||||
* zero.
|
||||
*/
|
||||
int ubifs_add_orphan(struct ubifs_info *c, ino_t inum)
|
||||
{
|
||||
struct ubifs_orphan *orphan, *o;
|
||||
struct rb_node **p, *parent = NULL;
|
||||
|
||||
orphan = kzalloc(sizeof(struct ubifs_orphan), GFP_NOFS);
|
||||
if (!orphan)
|
||||
return -ENOMEM;
|
||||
orphan->inum = inum;
|
||||
orphan->new = 1;
|
||||
|
||||
spin_lock(&c->orphan_lock);
|
||||
if (c->tot_orphans >= c->max_orphans) {
|
||||
spin_unlock(&c->orphan_lock);
|
||||
kfree(orphan);
|
||||
return -ENFILE;
|
||||
}
|
||||
p = &c->orph_tree.rb_node;
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
o = rb_entry(parent, struct ubifs_orphan, rb);
|
||||
if (inum < o->inum)
|
||||
p = &(*p)->rb_left;
|
||||
else if (inum > o->inum)
|
||||
p = &(*p)->rb_right;
|
||||
else {
|
||||
ubifs_err("orphaned twice");
|
||||
spin_unlock(&c->orphan_lock);
|
||||
kfree(orphan);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
c->tot_orphans += 1;
|
||||
c->new_orphans += 1;
|
||||
rb_link_node(&orphan->rb, parent, p);
|
||||
rb_insert_color(&orphan->rb, &c->orph_tree);
|
||||
list_add_tail(&orphan->list, &c->orph_list);
|
||||
list_add_tail(&orphan->new_list, &c->orph_new);
|
||||
spin_unlock(&c->orphan_lock);
|
||||
dbg_gen("ino %lu", (unsigned long)inum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_delete_orphan - delete an orphan.
|
||||
* @c: UBIFS file-system description object
|
||||
* @inum: orphan inode number
|
||||
*
|
||||
* Delete an orphan. This function is called when an inode is deleted.
|
||||
*/
|
||||
void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum)
|
||||
{
|
||||
struct ubifs_orphan *o;
|
||||
struct rb_node *p;
|
||||
|
||||
spin_lock(&c->orphan_lock);
|
||||
p = c->orph_tree.rb_node;
|
||||
while (p) {
|
||||
o = rb_entry(p, struct ubifs_orphan, rb);
|
||||
if (inum < o->inum)
|
||||
p = p->rb_left;
|
||||
else if (inum > o->inum)
|
||||
p = p->rb_right;
|
||||
else {
|
||||
if (o->del) {
|
||||
spin_unlock(&c->orphan_lock);
|
||||
dbg_gen("deleted twice ino %lu",
|
||||
(unsigned long)inum);
|
||||
return;
|
||||
}
|
||||
if (o->cmt) {
|
||||
o->del = 1;
|
||||
o->dnext = c->orph_dnext;
|
||||
c->orph_dnext = o;
|
||||
spin_unlock(&c->orphan_lock);
|
||||
dbg_gen("delete later ino %lu",
|
||||
(unsigned long)inum);
|
||||
return;
|
||||
}
|
||||
rb_erase(p, &c->orph_tree);
|
||||
list_del(&o->list);
|
||||
c->tot_orphans -= 1;
|
||||
if (o->new) {
|
||||
list_del(&o->new_list);
|
||||
c->new_orphans -= 1;
|
||||
}
|
||||
spin_unlock(&c->orphan_lock);
|
||||
kfree(o);
|
||||
dbg_gen("inum %lu", (unsigned long)inum);
|
||||
return;
|
||||
}
|
||||
}
|
||||
spin_unlock(&c->orphan_lock);
|
||||
ubifs_err("missing orphan ino %lu", (unsigned long)inum);
|
||||
dump_stack();
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_orphan_start_commit - start commit of orphans.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* Start commit of orphans.
|
||||
*/
|
||||
int ubifs_orphan_start_commit(struct ubifs_info *c)
|
||||
{
|
||||
struct ubifs_orphan *orphan, **last;
|
||||
|
||||
spin_lock(&c->orphan_lock);
|
||||
last = &c->orph_cnext;
|
||||
list_for_each_entry(orphan, &c->orph_new, new_list) {
|
||||
ubifs_assert(orphan->new);
|
||||
ubifs_assert(!orphan->cmt);
|
||||
orphan->new = 0;
|
||||
orphan->cmt = 1;
|
||||
*last = orphan;
|
||||
last = &orphan->cnext;
|
||||
}
|
||||
*last = NULL;
|
||||
c->cmt_orphans = c->new_orphans;
|
||||
c->new_orphans = 0;
|
||||
dbg_cmt("%d orphans to commit", c->cmt_orphans);
|
||||
INIT_LIST_HEAD(&c->orph_new);
|
||||
if (c->tot_orphans == 0)
|
||||
c->no_orphs = 1;
|
||||
else
|
||||
c->no_orphs = 0;
|
||||
spin_unlock(&c->orphan_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* avail_orphs - calculate available space.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function returns the number of orphans that can be written in the
|
||||
* available space.
|
||||
*/
|
||||
static int avail_orphs(struct ubifs_info *c)
|
||||
{
|
||||
int avail_lebs, avail, gap;
|
||||
|
||||
avail_lebs = c->orph_lebs - (c->ohead_lnum - c->orph_first) - 1;
|
||||
avail = avail_lebs *
|
||||
((c->leb_size - UBIFS_ORPH_NODE_SZ) / sizeof(__le64));
|
||||
gap = c->leb_size - c->ohead_offs;
|
||||
if (gap >= UBIFS_ORPH_NODE_SZ + sizeof(__le64))
|
||||
avail += (gap - UBIFS_ORPH_NODE_SZ) / sizeof(__le64);
|
||||
return avail;
|
||||
}
|
||||
|
||||
/**
|
||||
* tot_avail_orphs - calculate total space.
|
||||
* @c: UBIFS file-system description object
|
||||
@ -69,6 +219,256 @@ static int tot_avail_orphs(struct ubifs_info *c)
|
||||
return avail / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* do_write_orph_node - write a node to the orphan head.
|
||||
* @c: UBIFS file-system description object
|
||||
* @len: length of node
|
||||
* @atomic: write atomically
|
||||
*
|
||||
* This function writes a node to the orphan head from the orphan buffer. If
|
||||
* %atomic is not zero, then the write is done atomically. On success, %0 is
|
||||
* returned, otherwise a negative error code is returned.
|
||||
*/
|
||||
static int do_write_orph_node(struct ubifs_info *c, int len, int atomic)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (atomic) {
|
||||
ubifs_assert(c->ohead_offs == 0);
|
||||
ubifs_prepare_node(c, c->orph_buf, len, 1);
|
||||
len = ALIGN(len, c->min_io_size);
|
||||
err = ubifs_leb_change(c, c->ohead_lnum, c->orph_buf, len);
|
||||
} else {
|
||||
if (c->ohead_offs == 0) {
|
||||
/* Ensure LEB has been unmapped */
|
||||
err = ubifs_leb_unmap(c, c->ohead_lnum);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
err = ubifs_write_node(c, c->orph_buf, len, c->ohead_lnum,
|
||||
c->ohead_offs);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* write_orph_node - write an orphan node.
|
||||
* @c: UBIFS file-system description object
|
||||
* @atomic: write atomically
|
||||
*
|
||||
* This function builds an orphan node from the cnext list and writes it to the
|
||||
* orphan head. On success, %0 is returned, otherwise a negative error code
|
||||
* is returned.
|
||||
*/
|
||||
static int write_orph_node(struct ubifs_info *c, int atomic)
|
||||
{
|
||||
struct ubifs_orphan *orphan, *cnext;
|
||||
struct ubifs_orph_node *orph;
|
||||
int gap, err, len, cnt, i;
|
||||
|
||||
ubifs_assert(c->cmt_orphans > 0);
|
||||
gap = c->leb_size - c->ohead_offs;
|
||||
if (gap < UBIFS_ORPH_NODE_SZ + sizeof(__le64)) {
|
||||
c->ohead_lnum += 1;
|
||||
c->ohead_offs = 0;
|
||||
gap = c->leb_size;
|
||||
if (c->ohead_lnum > c->orph_last) {
|
||||
/*
|
||||
* We limit the number of orphans so that this should
|
||||
* never happen.
|
||||
*/
|
||||
ubifs_err("out of space in orphan area");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
cnt = (gap - UBIFS_ORPH_NODE_SZ) / sizeof(__le64);
|
||||
if (cnt > c->cmt_orphans)
|
||||
cnt = c->cmt_orphans;
|
||||
len = UBIFS_ORPH_NODE_SZ + cnt * sizeof(__le64);
|
||||
ubifs_assert(c->orph_buf);
|
||||
orph = c->orph_buf;
|
||||
orph->ch.node_type = UBIFS_ORPH_NODE;
|
||||
spin_lock(&c->orphan_lock);
|
||||
cnext = c->orph_cnext;
|
||||
for (i = 0; i < cnt; i++) {
|
||||
orphan = cnext;
|
||||
ubifs_assert(orphan->cmt);
|
||||
orph->inos[i] = cpu_to_le64(orphan->inum);
|
||||
orphan->cmt = 0;
|
||||
cnext = orphan->cnext;
|
||||
orphan->cnext = NULL;
|
||||
}
|
||||
c->orph_cnext = cnext;
|
||||
c->cmt_orphans -= cnt;
|
||||
spin_unlock(&c->orphan_lock);
|
||||
if (c->cmt_orphans)
|
||||
orph->cmt_no = cpu_to_le64(c->cmt_no);
|
||||
else
|
||||
/* Mark the last node of the commit */
|
||||
orph->cmt_no = cpu_to_le64((c->cmt_no) | (1ULL << 63));
|
||||
ubifs_assert(c->ohead_offs + len <= c->leb_size);
|
||||
ubifs_assert(c->ohead_lnum >= c->orph_first);
|
||||
ubifs_assert(c->ohead_lnum <= c->orph_last);
|
||||
err = do_write_orph_node(c, len, atomic);
|
||||
c->ohead_offs += ALIGN(len, c->min_io_size);
|
||||
c->ohead_offs = ALIGN(c->ohead_offs, 8);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* write_orph_nodes - write orphan nodes until there are no more to commit.
|
||||
* @c: UBIFS file-system description object
|
||||
* @atomic: write atomically
|
||||
*
|
||||
* This function writes orphan nodes for all the orphans to commit. On success,
|
||||
* %0 is returned, otherwise a negative error code is returned.
|
||||
*/
|
||||
static int write_orph_nodes(struct ubifs_info *c, int atomic)
|
||||
{
|
||||
int err;
|
||||
|
||||
while (c->cmt_orphans > 0) {
|
||||
err = write_orph_node(c, atomic);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
if (atomic) {
|
||||
int lnum;
|
||||
|
||||
/* Unmap any unused LEBs after consolidation */
|
||||
lnum = c->ohead_lnum + 1;
|
||||
for (lnum = c->ohead_lnum + 1; lnum <= c->orph_last; lnum++) {
|
||||
err = ubifs_leb_unmap(c, lnum);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* consolidate - consolidate the orphan area.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function enables consolidation by putting all the orphans into the list
|
||||
* to commit. The list is in the order that the orphans were added, and the
|
||||
* LEBs are written atomically in order, so at no time can orphans be lost by
|
||||
* an unclean unmount.
|
||||
*
|
||||
* This function returns %0 on success and a negative error code on failure.
|
||||
*/
|
||||
static int consolidate(struct ubifs_info *c)
|
||||
{
|
||||
int tot_avail = tot_avail_orphs(c), err = 0;
|
||||
|
||||
spin_lock(&c->orphan_lock);
|
||||
dbg_cmt("there is space for %d orphans and there are %d",
|
||||
tot_avail, c->tot_orphans);
|
||||
if (c->tot_orphans - c->new_orphans <= tot_avail) {
|
||||
struct ubifs_orphan *orphan, **last;
|
||||
int cnt = 0;
|
||||
|
||||
/* Change the cnext list to include all non-new orphans */
|
||||
last = &c->orph_cnext;
|
||||
list_for_each_entry(orphan, &c->orph_list, list) {
|
||||
if (orphan->new)
|
||||
continue;
|
||||
orphan->cmt = 1;
|
||||
*last = orphan;
|
||||
last = &orphan->cnext;
|
||||
cnt += 1;
|
||||
}
|
||||
*last = NULL;
|
||||
ubifs_assert(cnt == c->tot_orphans - c->new_orphans);
|
||||
c->cmt_orphans = cnt;
|
||||
c->ohead_lnum = c->orph_first;
|
||||
c->ohead_offs = 0;
|
||||
} else {
|
||||
/*
|
||||
* We limit the number of orphans so that this should
|
||||
* never happen.
|
||||
*/
|
||||
ubifs_err("out of space in orphan area");
|
||||
err = -EINVAL;
|
||||
}
|
||||
spin_unlock(&c->orphan_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* commit_orphans - commit orphans.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function commits orphans to flash. On success, %0 is returned,
|
||||
* otherwise a negative error code is returned.
|
||||
*/
|
||||
static int commit_orphans(struct ubifs_info *c)
|
||||
{
|
||||
int avail, atomic = 0, err;
|
||||
|
||||
ubifs_assert(c->cmt_orphans > 0);
|
||||
avail = avail_orphs(c);
|
||||
if (avail < c->cmt_orphans) {
|
||||
/* Not enough space to write new orphans, so consolidate */
|
||||
err = consolidate(c);
|
||||
if (err)
|
||||
return err;
|
||||
atomic = 1;
|
||||
}
|
||||
err = write_orph_nodes(c, atomic);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* erase_deleted - erase the orphans marked for deletion.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* During commit, the orphans being committed cannot be deleted, so they are
|
||||
* marked for deletion and deleted by this function. Also, the recovery
|
||||
* adds killed orphans to the deletion list, and therefore they are deleted
|
||||
* here too.
|
||||
*/
|
||||
static void erase_deleted(struct ubifs_info *c)
|
||||
{
|
||||
struct ubifs_orphan *orphan, *dnext;
|
||||
|
||||
spin_lock(&c->orphan_lock);
|
||||
dnext = c->orph_dnext;
|
||||
while (dnext) {
|
||||
orphan = dnext;
|
||||
dnext = orphan->dnext;
|
||||
ubifs_assert(!orphan->new);
|
||||
ubifs_assert(orphan->del);
|
||||
rb_erase(&orphan->rb, &c->orph_tree);
|
||||
list_del(&orphan->list);
|
||||
c->tot_orphans -= 1;
|
||||
dbg_gen("deleting orphan ino %lu", (unsigned long)orphan->inum);
|
||||
kfree(orphan);
|
||||
}
|
||||
c->orph_dnext = NULL;
|
||||
spin_unlock(&c->orphan_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_orphan_end_commit - end commit of orphans.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* End commit of orphans.
|
||||
*/
|
||||
int ubifs_orphan_end_commit(struct ubifs_info *c)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (c->cmt_orphans != 0) {
|
||||
err = commit_orphans(c);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
erase_deleted(c);
|
||||
err = dbg_check_orphans(c);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_clear_orphans - erase all LEBs used for orphans.
|
||||
* @c: UBIFS file-system description object
|
||||
@ -128,6 +528,7 @@ static int insert_dead_orphan(struct ubifs_info *c, ino_t inum)
|
||||
rb_link_node(&orphan->rb, parent, p);
|
||||
rb_insert_color(&orphan->rb, &c->orph_tree);
|
||||
list_add_tail(&orphan->list, &c->orph_list);
|
||||
orphan->del = 1;
|
||||
orphan->dnext = c->orph_dnext;
|
||||
c->orph_dnext = orphan;
|
||||
dbg_mnt("ino %lu, new %d, tot %d", (unsigned long)inum,
|
||||
@ -159,9 +560,9 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
|
||||
|
||||
list_for_each_entry(snod, &sleb->nodes, list) {
|
||||
if (snod->type != UBIFS_ORPH_NODE) {
|
||||
ubifs_err("invalid node type %d in orphan area at "
|
||||
"%d:%d", snod->type, sleb->lnum, snod->offs);
|
||||
dbg_dump_node(c, snod->node);
|
||||
ubifs_err("invalid node type %d in orphan area at %d:%d",
|
||||
snod->type, sleb->lnum, snod->offs);
|
||||
ubifs_dump_node(c, snod->node);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -186,10 +587,9 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
|
||||
* number. That makes this orphan node, out of date.
|
||||
*/
|
||||
if (!first) {
|
||||
ubifs_err("out of order commit number %llu in "
|
||||
"orphan node at %d:%d",
|
||||
ubifs_err("out of order commit number %llu in orphan node at %d:%d",
|
||||
cmt_no, sleb->lnum, snod->offs);
|
||||
dbg_dump_node(c, snod->node);
|
||||
ubifs_dump_node(c, snod->node);
|
||||
return -EINVAL;
|
||||
}
|
||||
dbg_rcvry("out of date LEB %d", sleb->lnum);
|
||||
@ -262,9 +662,11 @@ static int kill_orphans(struct ubifs_info *c)
|
||||
struct ubifs_scan_leb *sleb;
|
||||
|
||||
dbg_rcvry("LEB %d", lnum);
|
||||
sleb = ubifs_scan(c, lnum, 0, c->sbuf);
|
||||
sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
|
||||
if (IS_ERR(sleb)) {
|
||||
sleb = ubifs_recover_leb(c, lnum, 0, c->sbuf, 0);
|
||||
if (PTR_ERR(sleb) == -EUCLEAN)
|
||||
sleb = ubifs_recover_leb(c, lnum, 0,
|
||||
c->sbuf, -1);
|
||||
if (IS_ERR(sleb)) {
|
||||
err = PTR_ERR(sleb);
|
||||
break;
|
||||
@ -314,3 +716,232 @@ int ubifs_mount_orphans(struct ubifs_info *c, int unclean, int read_only)
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Everything below is related to debugging.
|
||||
*/
|
||||
|
||||
struct check_orphan {
|
||||
struct rb_node rb;
|
||||
ino_t inum;
|
||||
};
|
||||
|
||||
struct check_info {
|
||||
unsigned long last_ino;
|
||||
unsigned long tot_inos;
|
||||
unsigned long missing;
|
||||
unsigned long long leaf_cnt;
|
||||
struct ubifs_ino_node *node;
|
||||
struct rb_root root;
|
||||
};
|
||||
|
||||
static int dbg_find_orphan(struct ubifs_info *c, ino_t inum)
|
||||
{
|
||||
struct ubifs_orphan *o;
|
||||
struct rb_node *p;
|
||||
|
||||
spin_lock(&c->orphan_lock);
|
||||
p = c->orph_tree.rb_node;
|
||||
while (p) {
|
||||
o = rb_entry(p, struct ubifs_orphan, rb);
|
||||
if (inum < o->inum)
|
||||
p = p->rb_left;
|
||||
else if (inum > o->inum)
|
||||
p = p->rb_right;
|
||||
else {
|
||||
spin_unlock(&c->orphan_lock);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
spin_unlock(&c->orphan_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dbg_ins_check_orphan(struct rb_root *root, ino_t inum)
|
||||
{
|
||||
struct check_orphan *orphan, *o;
|
||||
struct rb_node **p, *parent = NULL;
|
||||
|
||||
orphan = kzalloc(sizeof(struct check_orphan), GFP_NOFS);
|
||||
if (!orphan)
|
||||
return -ENOMEM;
|
||||
orphan->inum = inum;
|
||||
|
||||
p = &root->rb_node;
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
o = rb_entry(parent, struct check_orphan, rb);
|
||||
if (inum < o->inum)
|
||||
p = &(*p)->rb_left;
|
||||
else if (inum > o->inum)
|
||||
p = &(*p)->rb_right;
|
||||
else {
|
||||
kfree(orphan);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
rb_link_node(&orphan->rb, parent, p);
|
||||
rb_insert_color(&orphan->rb, root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dbg_find_check_orphan(struct rb_root *root, ino_t inum)
|
||||
{
|
||||
struct check_orphan *o;
|
||||
struct rb_node *p;
|
||||
|
||||
p = root->rb_node;
|
||||
while (p) {
|
||||
o = rb_entry(p, struct check_orphan, rb);
|
||||
if (inum < o->inum)
|
||||
p = p->rb_left;
|
||||
else if (inum > o->inum)
|
||||
p = p->rb_right;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dbg_free_check_tree(struct rb_root *root)
|
||||
{
|
||||
struct check_orphan *o, *n;
|
||||
|
||||
rbtree_postorder_for_each_entry_safe(o, n, root, rb)
|
||||
kfree(o);
|
||||
}
|
||||
|
||||
static int dbg_orphan_check(struct ubifs_info *c, struct ubifs_zbranch *zbr,
|
||||
void *priv)
|
||||
{
|
||||
struct check_info *ci = priv;
|
||||
ino_t inum;
|
||||
int err;
|
||||
|
||||
inum = key_inum(c, &zbr->key);
|
||||
if (inum != ci->last_ino) {
|
||||
/* Lowest node type is the inode node, so it comes first */
|
||||
if (key_type(c, &zbr->key) != UBIFS_INO_KEY)
|
||||
ubifs_err("found orphan node ino %lu, type %d",
|
||||
(unsigned long)inum, key_type(c, &zbr->key));
|
||||
ci->last_ino = inum;
|
||||
ci->tot_inos += 1;
|
||||
err = ubifs_tnc_read_node(c, zbr, ci->node);
|
||||
if (err) {
|
||||
ubifs_err("node read failed, error %d", err);
|
||||
return err;
|
||||
}
|
||||
if (ci->node->nlink == 0)
|
||||
/* Must be recorded as an orphan */
|
||||
if (!dbg_find_check_orphan(&ci->root, inum) &&
|
||||
!dbg_find_orphan(c, inum)) {
|
||||
ubifs_err("missing orphan, ino %lu",
|
||||
(unsigned long)inum);
|
||||
ci->missing += 1;
|
||||
}
|
||||
}
|
||||
ci->leaf_cnt += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dbg_read_orphans(struct check_info *ci, struct ubifs_scan_leb *sleb)
|
||||
{
|
||||
struct ubifs_scan_node *snod;
|
||||
struct ubifs_orph_node *orph;
|
||||
ino_t inum;
|
||||
int i, n, err;
|
||||
|
||||
list_for_each_entry(snod, &sleb->nodes, list) {
|
||||
cond_resched();
|
||||
if (snod->type != UBIFS_ORPH_NODE)
|
||||
continue;
|
||||
orph = snod->node;
|
||||
n = (le32_to_cpu(orph->ch.len) - UBIFS_ORPH_NODE_SZ) >> 3;
|
||||
for (i = 0; i < n; i++) {
|
||||
inum = le64_to_cpu(orph->inos[i]);
|
||||
err = dbg_ins_check_orphan(&ci->root, inum);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dbg_scan_orphans(struct ubifs_info *c, struct check_info *ci)
|
||||
{
|
||||
int lnum, err = 0;
|
||||
void *buf;
|
||||
|
||||
/* Check no-orphans flag and skip this if no orphans */
|
||||
if (c->no_orphs)
|
||||
return 0;
|
||||
|
||||
buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL);
|
||||
if (!buf) {
|
||||
ubifs_err("cannot allocate memory to check orphans");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) {
|
||||
struct ubifs_scan_leb *sleb;
|
||||
|
||||
sleb = ubifs_scan(c, lnum, 0, buf, 0);
|
||||
if (IS_ERR(sleb)) {
|
||||
err = PTR_ERR(sleb);
|
||||
break;
|
||||
}
|
||||
|
||||
err = dbg_read_orphans(ci, sleb);
|
||||
ubifs_scan_destroy(sleb);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
vfree(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dbg_check_orphans(struct ubifs_info *c)
|
||||
{
|
||||
struct check_info ci;
|
||||
int err;
|
||||
|
||||
if (!dbg_is_chk_orph(c))
|
||||
return 0;
|
||||
|
||||
ci.last_ino = 0;
|
||||
ci.tot_inos = 0;
|
||||
ci.missing = 0;
|
||||
ci.leaf_cnt = 0;
|
||||
ci.root = RB_ROOT;
|
||||
ci.node = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS);
|
||||
if (!ci.node) {
|
||||
ubifs_err("out of memory");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = dbg_scan_orphans(c, &ci);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = dbg_walk_index(c, &dbg_orphan_check, NULL, &ci);
|
||||
if (err) {
|
||||
ubifs_err("cannot scan TNC, error %d", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ci.missing) {
|
||||
ubifs_err("%lu missing orphan(s)", ci.missing);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dbg_cmt("last inode number is %lu", ci.last_ino);
|
||||
dbg_cmt("total number of inodes is %lu", ci.tot_inos);
|
||||
dbg_cmt("total number of leaf nodes is %llu", ci.leaf_cnt);
|
||||
|
||||
out:
|
||||
dbg_free_check_tree(&ci.root);
|
||||
kfree(ci.node);
|
||||
return err;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,18 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2006-2008 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Authors: Adrian Hunter
|
||||
* Artem Bityutskiy (Битюцкий Артём)
|
||||
@ -32,44 +21,38 @@
|
||||
* larger is the journal, the more memory its index may consume.
|
||||
*/
|
||||
|
||||
#define __UBOOT__
|
||||
#ifdef __UBOOT__
|
||||
#include <linux/compat.h>
|
||||
#include <linux/err.h>
|
||||
#endif
|
||||
#include "ubifs.h"
|
||||
|
||||
/*
|
||||
* Replay flags.
|
||||
*
|
||||
* REPLAY_DELETION: node was deleted
|
||||
* REPLAY_REF: node is a reference node
|
||||
*/
|
||||
enum {
|
||||
REPLAY_DELETION = 1,
|
||||
REPLAY_REF = 2,
|
||||
};
|
||||
#include <linux/list_sort.h>
|
||||
|
||||
/**
|
||||
* struct replay_entry - replay tree entry.
|
||||
* struct replay_entry - replay list entry.
|
||||
* @lnum: logical eraseblock number of the node
|
||||
* @offs: node offset
|
||||
* @len: node length
|
||||
* @deletion: non-zero if this entry corresponds to a node deletion
|
||||
* @sqnum: node sequence number
|
||||
* @flags: replay flags
|
||||
* @rb: links the replay tree
|
||||
* @list: links the replay list
|
||||
* @key: node key
|
||||
* @nm: directory entry name
|
||||
* @old_size: truncation old size
|
||||
* @new_size: truncation new size
|
||||
* @free: amount of free space in a bud
|
||||
* @dirty: amount of dirty space in a bud from padding and deletion nodes
|
||||
*
|
||||
* UBIFS journal replay must compare node sequence numbers, which means it must
|
||||
* build a tree of node information to insert into the TNC.
|
||||
* The replay process first scans all buds and builds the replay list, then
|
||||
* sorts the replay list in nodes sequence number order, and then inserts all
|
||||
* the replay entries to the TNC.
|
||||
*/
|
||||
struct replay_entry {
|
||||
int lnum;
|
||||
int offs;
|
||||
int len;
|
||||
unsigned int deletion:1;
|
||||
unsigned long long sqnum;
|
||||
int flags;
|
||||
struct rb_node rb;
|
||||
struct list_head list;
|
||||
union ubifs_key key;
|
||||
union {
|
||||
struct qstr nm;
|
||||
@ -77,10 +60,6 @@ struct replay_entry {
|
||||
loff_t old_size;
|
||||
loff_t new_size;
|
||||
};
|
||||
struct {
|
||||
int free;
|
||||
int dirty;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@ -88,82 +67,116 @@ struct replay_entry {
|
||||
* struct bud_entry - entry in the list of buds to replay.
|
||||
* @list: next bud in the list
|
||||
* @bud: bud description object
|
||||
* @free: free bytes in the bud
|
||||
* @sqnum: reference node sequence number
|
||||
* @free: free bytes in the bud
|
||||
* @dirty: dirty bytes in the bud
|
||||
*/
|
||||
struct bud_entry {
|
||||
struct list_head list;
|
||||
struct ubifs_bud *bud;
|
||||
int free;
|
||||
unsigned long long sqnum;
|
||||
int free;
|
||||
int dirty;
|
||||
};
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* set_bud_lprops - set free and dirty space used by a bud.
|
||||
* @c: UBIFS file-system description object
|
||||
* @r: replay entry of bud
|
||||
* @b: bud entry which describes the bud
|
||||
*
|
||||
* This function makes sure the LEB properties of bud @b are set correctly
|
||||
* after the replay. Returns zero in case of success and a negative error code
|
||||
* in case of failure.
|
||||
*/
|
||||
static int set_bud_lprops(struct ubifs_info *c, struct replay_entry *r)
|
||||
static int set_bud_lprops(struct ubifs_info *c, struct bud_entry *b)
|
||||
{
|
||||
const struct ubifs_lprops *lp;
|
||||
int err = 0, dirty;
|
||||
|
||||
ubifs_get_lprops(c);
|
||||
|
||||
lp = ubifs_lpt_lookup_dirty(c, r->lnum);
|
||||
lp = ubifs_lpt_lookup_dirty(c, b->bud->lnum);
|
||||
if (IS_ERR(lp)) {
|
||||
err = PTR_ERR(lp);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dirty = lp->dirty;
|
||||
if (r->offs == 0 && (lp->free != c->leb_size || lp->dirty != 0)) {
|
||||
if (b->bud->start == 0 && (lp->free != c->leb_size || lp->dirty != 0)) {
|
||||
/*
|
||||
* The LEB was added to the journal with a starting offset of
|
||||
* zero which means the LEB must have been empty. The LEB
|
||||
* property values should be lp->free == c->leb_size and
|
||||
* lp->dirty == 0, but that is not the case. The reason is that
|
||||
* the LEB was garbage collected. The garbage collector resets
|
||||
* the free and dirty space without recording it anywhere except
|
||||
* lprops, so if there is not a commit then lprops does not have
|
||||
* that information next time the file system is mounted.
|
||||
* property values should be @lp->free == @c->leb_size and
|
||||
* @lp->dirty == 0, but that is not the case. The reason is that
|
||||
* the LEB had been garbage collected before it became the bud,
|
||||
* and there was not commit inbetween. The garbage collector
|
||||
* resets the free and dirty space without recording it
|
||||
* anywhere except lprops, so if there was no commit then
|
||||
* lprops does not have that information.
|
||||
*
|
||||
* We do not need to adjust free space because the scan has told
|
||||
* us the exact value which is recorded in the replay entry as
|
||||
* r->free.
|
||||
* @b->free.
|
||||
*
|
||||
* However we do need to subtract from the dirty space the
|
||||
* amount of space that the garbage collector reclaimed, which
|
||||
* is the whole LEB minus the amount of space that was free.
|
||||
*/
|
||||
dbg_mnt("bud LEB %d was GC'd (%d free, %d dirty)", r->lnum,
|
||||
dbg_mnt("bud LEB %d was GC'd (%d free, %d dirty)", b->bud->lnum,
|
||||
lp->free, lp->dirty);
|
||||
dbg_gc("bud LEB %d was GC'd (%d free, %d dirty)", r->lnum,
|
||||
dbg_gc("bud LEB %d was GC'd (%d free, %d dirty)", b->bud->lnum,
|
||||
lp->free, lp->dirty);
|
||||
dirty -= c->leb_size - lp->free;
|
||||
/*
|
||||
* If the replay order was perfect the dirty space would now be
|
||||
* zero. The order is not perfect because the the journal heads
|
||||
* zero. The order is not perfect because the journal heads
|
||||
* race with each other. This is not a problem but is does mean
|
||||
* that the dirty space may temporarily exceed c->leb_size
|
||||
* during the replay.
|
||||
*/
|
||||
if (dirty != 0)
|
||||
dbg_msg("LEB %d lp: %d free %d dirty "
|
||||
"replay: %d free %d dirty", r->lnum, lp->free,
|
||||
lp->dirty, r->free, r->dirty);
|
||||
dbg_mnt("LEB %d lp: %d free %d dirty replay: %d free %d dirty",
|
||||
b->bud->lnum, lp->free, lp->dirty, b->free,
|
||||
b->dirty);
|
||||
}
|
||||
lp = ubifs_change_lp(c, lp, r->free, dirty + r->dirty,
|
||||
lp = ubifs_change_lp(c, lp, b->free, dirty + b->dirty,
|
||||
lp->flags | LPROPS_TAKEN, 0);
|
||||
if (IS_ERR(lp)) {
|
||||
err = PTR_ERR(lp);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Make sure the journal head points to the latest bud */
|
||||
err = ubifs_wbuf_seek_nolock(&c->jheads[b->bud->jhead].wbuf,
|
||||
b->bud->lnum, c->leb_size - b->free);
|
||||
|
||||
out:
|
||||
ubifs_release_lprops(c);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* set_buds_lprops - set free and dirty space for all replayed buds.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function sets LEB properties for all replayed buds. Returns zero in
|
||||
* case of success and a negative error code in case of failure.
|
||||
*/
|
||||
static int set_buds_lprops(struct ubifs_info *c)
|
||||
{
|
||||
struct bud_entry *b;
|
||||
int err;
|
||||
|
||||
list_for_each_entry(b, &c->replay_buds, list) {
|
||||
err = set_bud_lprops(c, b);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* trun_remove_range - apply a replay entry for a truncation to the TNC.
|
||||
* @c: UBIFS file-system description object
|
||||
@ -200,24 +213,22 @@ static int trun_remove_range(struct ubifs_info *c, struct replay_entry *r)
|
||||
*/
|
||||
static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r)
|
||||
{
|
||||
int err, deletion = ((r->flags & REPLAY_DELETION) != 0);
|
||||
int err;
|
||||
|
||||
dbg_mnt("LEB %d:%d len %d flgs %d sqnum %llu %s", r->lnum,
|
||||
r->offs, r->len, r->flags, r->sqnum, DBGKEY(&r->key));
|
||||
dbg_mntk(&r->key, "LEB %d:%d len %d deletion %d sqnum %llu key ",
|
||||
r->lnum, r->offs, r->len, r->deletion, r->sqnum);
|
||||
|
||||
/* Set c->replay_sqnum to help deal with dangling branches. */
|
||||
c->replay_sqnum = r->sqnum;
|
||||
|
||||
if (r->flags & REPLAY_REF)
|
||||
err = set_bud_lprops(c, r);
|
||||
else if (is_hash_key(c, &r->key)) {
|
||||
if (deletion)
|
||||
if (is_hash_key(c, &r->key)) {
|
||||
if (r->deletion)
|
||||
err = ubifs_tnc_remove_nm(c, &r->key, &r->nm);
|
||||
else
|
||||
err = ubifs_tnc_add_nm(c, &r->key, r->lnum, r->offs,
|
||||
r->len, &r->nm);
|
||||
} else {
|
||||
if (deletion)
|
||||
if (r->deletion)
|
||||
switch (key_type(c, &r->key)) {
|
||||
case UBIFS_INO_KEY:
|
||||
{
|
||||
@ -240,7 +251,7 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r)
|
||||
return err;
|
||||
|
||||
if (c->need_recovery)
|
||||
err = ubifs_recover_size_accum(c, &r->key, deletion,
|
||||
err = ubifs_recover_size_accum(c, &r->key, r->deletion,
|
||||
r->new_size);
|
||||
}
|
||||
|
||||
@ -248,68 +259,77 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r)
|
||||
}
|
||||
|
||||
/**
|
||||
* destroy_replay_tree - destroy the replay.
|
||||
* @c: UBIFS file-system description object
|
||||
* replay_entries_cmp - compare 2 replay entries.
|
||||
* @priv: UBIFS file-system description object
|
||||
* @a: first replay entry
|
||||
* @a: second replay entry
|
||||
*
|
||||
* Destroy the replay tree.
|
||||
* This is a comparios function for 'list_sort()' which compares 2 replay
|
||||
* entries @a and @b by comparing their sequence numer. Returns %1 if @a has
|
||||
* greater sequence number and %-1 otherwise.
|
||||
*/
|
||||
static void destroy_replay_tree(struct ubifs_info *c)
|
||||
static int replay_entries_cmp(void *priv, struct list_head *a,
|
||||
struct list_head *b)
|
||||
{
|
||||
struct rb_node *this = c->replay_tree.rb_node;
|
||||
struct replay_entry *r;
|
||||
struct replay_entry *ra, *rb;
|
||||
|
||||
while (this) {
|
||||
if (this->rb_left) {
|
||||
this = this->rb_left;
|
||||
continue;
|
||||
} else if (this->rb_right) {
|
||||
this = this->rb_right;
|
||||
continue;
|
||||
}
|
||||
r = rb_entry(this, struct replay_entry, rb);
|
||||
this = rb_parent(this);
|
||||
if (this) {
|
||||
if (this->rb_left == &r->rb)
|
||||
this->rb_left = NULL;
|
||||
else
|
||||
this->rb_right = NULL;
|
||||
}
|
||||
if (is_hash_key(c, &r->key))
|
||||
kfree((void *)r->nm.name);
|
||||
kfree(r);
|
||||
}
|
||||
c->replay_tree = RB_ROOT;
|
||||
cond_resched();
|
||||
if (a == b)
|
||||
return 0;
|
||||
|
||||
ra = list_entry(a, struct replay_entry, list);
|
||||
rb = list_entry(b, struct replay_entry, list);
|
||||
ubifs_assert(ra->sqnum != rb->sqnum);
|
||||
if (ra->sqnum > rb->sqnum)
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* apply_replay_tree - apply the replay tree to the TNC.
|
||||
* apply_replay_list - apply the replay list to the TNC.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* Apply the replay tree.
|
||||
* Returns zero in case of success and a negative error code in case of
|
||||
* failure.
|
||||
* Apply all entries in the replay list to the TNC. Returns zero in case of
|
||||
* success and a negative error code in case of failure.
|
||||
*/
|
||||
static int apply_replay_tree(struct ubifs_info *c)
|
||||
static int apply_replay_list(struct ubifs_info *c)
|
||||
{
|
||||
struct rb_node *this = rb_first(&c->replay_tree);
|
||||
struct replay_entry *r;
|
||||
int err;
|
||||
|
||||
while (this) {
|
||||
struct replay_entry *r;
|
||||
int err;
|
||||
list_sort(c, &c->replay_list, &replay_entries_cmp);
|
||||
|
||||
list_for_each_entry(r, &c->replay_list, list) {
|
||||
cond_resched();
|
||||
|
||||
r = rb_entry(this, struct replay_entry, rb);
|
||||
err = apply_replay_entry(c, r);
|
||||
if (err)
|
||||
return err;
|
||||
this = rb_next(this);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert_node - insert a node to the replay tree.
|
||||
* destroy_replay_list - destroy the replay.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* Destroy the replay list.
|
||||
*/
|
||||
static void destroy_replay_list(struct ubifs_info *c)
|
||||
{
|
||||
struct replay_entry *r, *tmp;
|
||||
|
||||
list_for_each_entry_safe(r, tmp, &c->replay_list, list) {
|
||||
if (is_hash_key(c, &r->key))
|
||||
kfree(r->nm.name);
|
||||
list_del(&r->list);
|
||||
kfree(r);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* insert_node - insert a node to the replay list
|
||||
* @c: UBIFS file-system description object
|
||||
* @lnum: node logical eraseblock number
|
||||
* @offs: node offset
|
||||
@ -321,39 +341,25 @@ static int apply_replay_tree(struct ubifs_info *c)
|
||||
* @old_size: truncation old size
|
||||
* @new_size: truncation new size
|
||||
*
|
||||
* This function inserts a scanned non-direntry node to the replay tree. The
|
||||
* replay tree is an RB-tree containing @struct replay_entry elements which are
|
||||
* indexed by the sequence number. The replay tree is applied at the very end
|
||||
* of the replay process. Since the tree is sorted in sequence number order,
|
||||
* the older modifications are applied first. This function returns zero in
|
||||
* case of success and a negative error code in case of failure.
|
||||
* This function inserts a scanned non-direntry node to the replay list. The
|
||||
* replay list contains @struct replay_entry elements, and we sort this list in
|
||||
* sequence number order before applying it. The replay list is applied at the
|
||||
* very end of the replay process. Since the list is sorted in sequence number
|
||||
* order, the older modifications are applied first. This function returns zero
|
||||
* in case of success and a negative error code in case of failure.
|
||||
*/
|
||||
static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
|
||||
union ubifs_key *key, unsigned long long sqnum,
|
||||
int deletion, int *used, loff_t old_size,
|
||||
loff_t new_size)
|
||||
{
|
||||
struct rb_node **p = &c->replay_tree.rb_node, *parent = NULL;
|
||||
struct replay_entry *r;
|
||||
|
||||
dbg_mntk(key, "add LEB %d:%d, key ", lnum, offs);
|
||||
|
||||
if (key_inum(c, key) >= c->highest_inum)
|
||||
c->highest_inum = key_inum(c, key);
|
||||
|
||||
dbg_mnt("add LEB %d:%d, key %s", lnum, offs, DBGKEY(key));
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
r = rb_entry(parent, struct replay_entry, rb);
|
||||
if (sqnum < r->sqnum) {
|
||||
p = &(*p)->rb_left;
|
||||
continue;
|
||||
} else if (sqnum > r->sqnum) {
|
||||
p = &(*p)->rb_right;
|
||||
continue;
|
||||
}
|
||||
ubifs_err("duplicate sqnum in replay");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL);
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
@ -363,19 +369,18 @@ static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
|
||||
r->lnum = lnum;
|
||||
r->offs = offs;
|
||||
r->len = len;
|
||||
r->deletion = !!deletion;
|
||||
r->sqnum = sqnum;
|
||||
r->flags = (deletion ? REPLAY_DELETION : 0);
|
||||
key_copy(c, key, &r->key);
|
||||
r->old_size = old_size;
|
||||
r->new_size = new_size;
|
||||
key_copy(c, key, &r->key);
|
||||
|
||||
rb_link_node(&r->rb, parent, p);
|
||||
rb_insert_color(&r->rb, &c->replay_tree);
|
||||
list_add_tail(&r->list, &c->replay_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert_dent - insert a directory entry node into the replay tree.
|
||||
* insert_dent - insert a directory entry node into the replay list.
|
||||
* @c: UBIFS file-system description object
|
||||
* @lnum: node logical eraseblock number
|
||||
* @offs: node offset
|
||||
@ -387,43 +392,25 @@ static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
|
||||
* @deletion: non-zero if this is a deletion
|
||||
* @used: number of bytes in use in a LEB
|
||||
*
|
||||
* This function inserts a scanned directory entry node to the replay tree.
|
||||
* Returns zero in case of success and a negative error code in case of
|
||||
* failure.
|
||||
*
|
||||
* This function is also used for extended attribute entries because they are
|
||||
* implemented as directory entry nodes.
|
||||
* This function inserts a scanned directory entry node or an extended
|
||||
* attribute entry to the replay list. Returns zero in case of success and a
|
||||
* negative error code in case of failure.
|
||||
*/
|
||||
static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len,
|
||||
union ubifs_key *key, const char *name, int nlen,
|
||||
unsigned long long sqnum, int deletion, int *used)
|
||||
{
|
||||
struct rb_node **p = &c->replay_tree.rb_node, *parent = NULL;
|
||||
struct replay_entry *r;
|
||||
char *nbuf;
|
||||
|
||||
dbg_mntk(key, "add LEB %d:%d, key ", lnum, offs);
|
||||
if (key_inum(c, key) >= c->highest_inum)
|
||||
c->highest_inum = key_inum(c, key);
|
||||
|
||||
dbg_mnt("add LEB %d:%d, key %s", lnum, offs, DBGKEY(key));
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
r = rb_entry(parent, struct replay_entry, rb);
|
||||
if (sqnum < r->sqnum) {
|
||||
p = &(*p)->rb_left;
|
||||
continue;
|
||||
}
|
||||
if (sqnum > r->sqnum) {
|
||||
p = &(*p)->rb_right;
|
||||
continue;
|
||||
}
|
||||
ubifs_err("duplicate sqnum in replay");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL);
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
|
||||
nbuf = kmalloc(nlen + 1, GFP_KERNEL);
|
||||
if (!nbuf) {
|
||||
kfree(r);
|
||||
@ -435,19 +422,18 @@ static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len,
|
||||
r->lnum = lnum;
|
||||
r->offs = offs;
|
||||
r->len = len;
|
||||
r->deletion = !!deletion;
|
||||
r->sqnum = sqnum;
|
||||
key_copy(c, key, &r->key);
|
||||
r->nm.len = nlen;
|
||||
memcpy(nbuf, name, nlen);
|
||||
nbuf[nlen] = '\0';
|
||||
r->nm.name = nbuf;
|
||||
r->flags = (deletion ? REPLAY_DELETION : 0);
|
||||
key_copy(c, key, &r->key);
|
||||
|
||||
ubifs_assert(!*p);
|
||||
rb_link_node(&r->rb, parent, p);
|
||||
rb_insert_color(&r->rb, &c->replay_tree);
|
||||
list_add_tail(&r->list, &c->replay_list);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ubifs_validate_entry - validate directory or extended attribute entry node.
|
||||
@ -466,7 +452,7 @@ int ubifs_validate_entry(struct ubifs_info *c,
|
||||
if (le32_to_cpu(dent->ch.len) != nlen + UBIFS_DENT_NODE_SZ + 1 ||
|
||||
dent->type >= UBIFS_ITYPES_CNT ||
|
||||
nlen > UBIFS_MAX_NLEN || dent->name[nlen] != 0 ||
|
||||
strnlen((char *)dent->name, nlen) != nlen ||
|
||||
strnlen(dent->name, nlen) != nlen ||
|
||||
le64_to_cpu(dent->inum) > MAX_INUM) {
|
||||
ubifs_err("bad %s node", key_type == UBIFS_DENT_KEY ?
|
||||
"directory entry" : "extended attribute entry");
|
||||
@ -481,32 +467,94 @@ int ubifs_validate_entry(struct ubifs_info *c,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* is_last_bud - check if the bud is the last in the journal head.
|
||||
* @c: UBIFS file-system description object
|
||||
* @bud: bud description object
|
||||
*
|
||||
* This function checks if bud @bud is the last bud in its journal head. This
|
||||
* information is then used by 'replay_bud()' to decide whether the bud can
|
||||
* have corruptions or not. Indeed, only last buds can be corrupted by power
|
||||
* cuts. Returns %1 if this is the last bud, and %0 if not.
|
||||
*/
|
||||
static int is_last_bud(struct ubifs_info *c, struct ubifs_bud *bud)
|
||||
{
|
||||
struct ubifs_jhead *jh = &c->jheads[bud->jhead];
|
||||
struct ubifs_bud *next;
|
||||
uint32_t data;
|
||||
int err;
|
||||
|
||||
if (list_is_last(&bud->list, &jh->buds_list))
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* The following is a quirk to make sure we work correctly with UBIFS
|
||||
* images used with older UBIFS.
|
||||
*
|
||||
* Normally, the last bud will be the last in the journal head's list
|
||||
* of bud. However, there is one exception if the UBIFS image belongs
|
||||
* to older UBIFS. This is fairly unlikely: one would need to use old
|
||||
* UBIFS, then have a power cut exactly at the right point, and then
|
||||
* try to mount this image with new UBIFS.
|
||||
*
|
||||
* The exception is: it is possible to have 2 buds A and B, A goes
|
||||
* before B, and B is the last, bud B is contains no data, and bud A is
|
||||
* corrupted at the end. The reason is that in older versions when the
|
||||
* journal code switched the next bud (from A to B), it first added a
|
||||
* log reference node for the new bud (B), and only after this it
|
||||
* synchronized the write-buffer of current bud (A). But later this was
|
||||
* changed and UBIFS started to always synchronize the write-buffer of
|
||||
* the bud (A) before writing the log reference for the new bud (B).
|
||||
*
|
||||
* But because older UBIFS always synchronized A's write-buffer before
|
||||
* writing to B, we can recognize this exceptional situation but
|
||||
* checking the contents of bud B - if it is empty, then A can be
|
||||
* treated as the last and we can recover it.
|
||||
*
|
||||
* TODO: remove this piece of code in a couple of years (today it is
|
||||
* 16.05.2011).
|
||||
*/
|
||||
next = list_entry(bud->list.next, struct ubifs_bud, list);
|
||||
if (!list_is_last(&next->list, &jh->buds_list))
|
||||
return 0;
|
||||
|
||||
err = ubifs_leb_read(c, next->lnum, (char *)&data, next->start, 4, 1);
|
||||
if (err)
|
||||
return 0;
|
||||
|
||||
return data == 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* replay_bud - replay a bud logical eraseblock.
|
||||
* @c: UBIFS file-system description object
|
||||
* @lnum: bud logical eraseblock number to replay
|
||||
* @offs: bud start offset
|
||||
* @jhead: journal head to which this bud belongs
|
||||
* @free: amount of free space in the bud is returned here
|
||||
* @dirty: amount of dirty space from padding and deletion nodes is returned
|
||||
* here
|
||||
* @b: bud entry which describes the bud
|
||||
*
|
||||
* This function returns zero in case of success and a negative error code in
|
||||
* case of failure.
|
||||
* This function replays bud @bud, recovers it if needed, and adds all nodes
|
||||
* from this bud to the replay list. Returns zero in case of success and a
|
||||
* negative error code in case of failure.
|
||||
*/
|
||||
static int replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead,
|
||||
int *free, int *dirty)
|
||||
static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
|
||||
{
|
||||
int err = 0, used = 0;
|
||||
int is_last = is_last_bud(c, b->bud);
|
||||
int err = 0, used = 0, lnum = b->bud->lnum, offs = b->bud->start;
|
||||
struct ubifs_scan_leb *sleb;
|
||||
struct ubifs_scan_node *snod;
|
||||
struct ubifs_bud *bud;
|
||||
|
||||
dbg_mnt("replay bud LEB %d, head %d", lnum, jhead);
|
||||
if (c->need_recovery)
|
||||
sleb = ubifs_recover_leb(c, lnum, offs, c->sbuf, jhead != GCHD);
|
||||
dbg_mnt("replay bud LEB %d, head %d, offs %d, is_last %d",
|
||||
lnum, b->bud->jhead, offs, is_last);
|
||||
|
||||
if (c->need_recovery && is_last)
|
||||
/*
|
||||
* Recover only last LEBs in the journal heads, because power
|
||||
* cuts may cause corruptions only in these LEBs, because only
|
||||
* these LEBs could possibly be written to at the power cut
|
||||
* time.
|
||||
*/
|
||||
sleb = ubifs_recover_leb(c, lnum, offs, c->sbuf, b->bud->jhead);
|
||||
else
|
||||
sleb = ubifs_scan(c, lnum, offs, c->sbuf);
|
||||
sleb = ubifs_scan(c, lnum, offs, c->sbuf, 0);
|
||||
if (IS_ERR(sleb))
|
||||
return PTR_ERR(sleb);
|
||||
|
||||
@ -580,7 +628,7 @@ static int replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead,
|
||||
goto out_dump;
|
||||
|
||||
err = insert_dent(c, lnum, snod->offs, snod->len,
|
||||
&snod->key, (char *)dent->name,
|
||||
&snod->key, dent->name,
|
||||
le16_to_cpu(dent->nlen), snod->sqnum,
|
||||
!le64_to_cpu(dent->inum), &used);
|
||||
break;
|
||||
@ -620,15 +668,14 @@ static int replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead,
|
||||
goto out;
|
||||
}
|
||||
|
||||
bud = ubifs_search_bud(c, lnum);
|
||||
if (!bud)
|
||||
BUG();
|
||||
|
||||
ubifs_assert(ubifs_search_bud(c, lnum));
|
||||
ubifs_assert(sleb->endpt - offs >= used);
|
||||
ubifs_assert(sleb->endpt % c->min_io_size == 0);
|
||||
|
||||
*dirty = sleb->endpt - offs - used;
|
||||
*free = c->leb_size - sleb->endpt;
|
||||
b->dirty = sleb->endpt - offs - used;
|
||||
b->free = c->leb_size - sleb->endpt;
|
||||
dbg_mnt("bud LEB %d replied: dirty %d, free %d",
|
||||
lnum, b->dirty, b->free);
|
||||
|
||||
out:
|
||||
ubifs_scan_destroy(sleb);
|
||||
@ -636,60 +683,11 @@ out:
|
||||
|
||||
out_dump:
|
||||
ubifs_err("bad node is at LEB %d:%d", lnum, snod->offs);
|
||||
dbg_dump_node(c, snod->node);
|
||||
ubifs_dump_node(c, snod->node);
|
||||
ubifs_scan_destroy(sleb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert_ref_node - insert a reference node to the replay tree.
|
||||
* @c: UBIFS file-system description object
|
||||
* @lnum: node logical eraseblock number
|
||||
* @offs: node offset
|
||||
* @sqnum: sequence number
|
||||
* @free: amount of free space in bud
|
||||
* @dirty: amount of dirty space from padding and deletion nodes
|
||||
*
|
||||
* This function inserts a reference node to the replay tree and returns zero
|
||||
* in case of success or a negative error code in case of failure.
|
||||
*/
|
||||
static int insert_ref_node(struct ubifs_info *c, int lnum, int offs,
|
||||
unsigned long long sqnum, int free, int dirty)
|
||||
{
|
||||
struct rb_node **p = &c->replay_tree.rb_node, *parent = NULL;
|
||||
struct replay_entry *r;
|
||||
|
||||
dbg_mnt("add ref LEB %d:%d", lnum, offs);
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
r = rb_entry(parent, struct replay_entry, rb);
|
||||
if (sqnum < r->sqnum) {
|
||||
p = &(*p)->rb_left;
|
||||
continue;
|
||||
} else if (sqnum > r->sqnum) {
|
||||
p = &(*p)->rb_right;
|
||||
continue;
|
||||
}
|
||||
ubifs_err("duplicate sqnum in replay tree");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL);
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
|
||||
r->lnum = lnum;
|
||||
r->offs = offs;
|
||||
r->sqnum = sqnum;
|
||||
r->flags = REPLAY_REF;
|
||||
r->free = free;
|
||||
r->dirty = dirty;
|
||||
|
||||
rb_link_node(&r->rb, parent, p);
|
||||
rb_insert_color(&r->rb, &c->replay_tree);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* replay_buds - replay all buds.
|
||||
* @c: UBIFS file-system description object
|
||||
@ -700,17 +698,16 @@ static int insert_ref_node(struct ubifs_info *c, int lnum, int offs,
|
||||
static int replay_buds(struct ubifs_info *c)
|
||||
{
|
||||
struct bud_entry *b;
|
||||
int err, uninitialized_var(free), uninitialized_var(dirty);
|
||||
int err;
|
||||
unsigned long long prev_sqnum = 0;
|
||||
|
||||
list_for_each_entry(b, &c->replay_buds, list) {
|
||||
err = replay_bud(c, b->bud->lnum, b->bud->start, b->bud->jhead,
|
||||
&free, &dirty);
|
||||
if (err)
|
||||
return err;
|
||||
err = insert_ref_node(c, b->bud->lnum, b->bud->start, b->sqnum,
|
||||
free, dirty);
|
||||
err = replay_bud(c, b);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ubifs_assert(b->sqnum > prev_sqnum);
|
||||
prev_sqnum = b->sqnum;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -831,10 +828,16 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf)
|
||||
const struct ubifs_cs_node *node;
|
||||
|
||||
dbg_mnt("replay log LEB %d:%d", lnum, offs);
|
||||
sleb = ubifs_scan(c, lnum, offs, sbuf);
|
||||
sleb = ubifs_scan(c, lnum, offs, sbuf, c->need_recovery);
|
||||
if (IS_ERR(sleb)) {
|
||||
if (c->need_recovery)
|
||||
sleb = ubifs_recover_log_leb(c, lnum, offs, sbuf);
|
||||
if (PTR_ERR(sleb) != -EUCLEAN || !c->need_recovery)
|
||||
return PTR_ERR(sleb);
|
||||
/*
|
||||
* Note, the below function will recover this log LEB only if
|
||||
* it is the last, because unclean reboots can possibly corrupt
|
||||
* only the tail of the log.
|
||||
*/
|
||||
sleb = ubifs_recover_log_leb(c, lnum, offs, sbuf);
|
||||
if (IS_ERR(sleb))
|
||||
return PTR_ERR(sleb);
|
||||
}
|
||||
@ -845,7 +848,6 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf)
|
||||
}
|
||||
|
||||
node = sleb->buf;
|
||||
|
||||
snod = list_entry(sleb->nodes.next, struct ubifs_scan_node, list);
|
||||
if (c->cs_sqnum == 0) {
|
||||
/*
|
||||
@ -856,16 +858,15 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf)
|
||||
* numbers.
|
||||
*/
|
||||
if (snod->type != UBIFS_CS_NODE) {
|
||||
dbg_err("first log node at LEB %d:%d is not CS node",
|
||||
lnum, offs);
|
||||
ubifs_err("first log node at LEB %d:%d is not CS node",
|
||||
lnum, offs);
|
||||
goto out_dump;
|
||||
}
|
||||
if (le64_to_cpu(node->cmt_no) != c->cmt_no) {
|
||||
dbg_err("first CS node at LEB %d:%d has wrong "
|
||||
"commit number %llu expected %llu",
|
||||
lnum, offs,
|
||||
(unsigned long long)le64_to_cpu(node->cmt_no),
|
||||
c->cmt_no);
|
||||
ubifs_err("first CS node at LEB %d:%d has wrong commit number %llu expected %llu",
|
||||
lnum, offs,
|
||||
(unsigned long long)le64_to_cpu(node->cmt_no),
|
||||
c->cmt_no);
|
||||
goto out_dump;
|
||||
}
|
||||
|
||||
@ -887,12 +888,11 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf)
|
||||
|
||||
/* Make sure the first node sits at offset zero of the LEB */
|
||||
if (snod->offs != 0) {
|
||||
dbg_err("first node is not at zero offset");
|
||||
ubifs_err("first node is not at zero offset");
|
||||
goto out_dump;
|
||||
}
|
||||
|
||||
list_for_each_entry(snod, &sleb->nodes, list) {
|
||||
|
||||
cond_resched();
|
||||
|
||||
if (snod->sqnum >= SQNUM_WATERMARK) {
|
||||
@ -901,8 +901,8 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf)
|
||||
}
|
||||
|
||||
if (snod->sqnum < c->cs_sqnum) {
|
||||
dbg_err("bad sqnum %llu, commit sqnum %llu",
|
||||
snod->sqnum, c->cs_sqnum);
|
||||
ubifs_err("bad sqnum %llu, commit sqnum %llu",
|
||||
snod->sqnum, c->cs_sqnum);
|
||||
goto out_dump;
|
||||
}
|
||||
|
||||
@ -952,9 +952,9 @@ out:
|
||||
return err;
|
||||
|
||||
out_dump:
|
||||
ubifs_err("log error detected while replying the log at LEB %d:%d",
|
||||
ubifs_err("log error detected while replaying the log at LEB %d:%d",
|
||||
lnum, offs + snod->offs);
|
||||
dbg_dump_node(c, snod->node);
|
||||
ubifs_dump_node(c, snod->node);
|
||||
ubifs_scan_destroy(sleb);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1004,67 +1004,64 @@ out:
|
||||
*/
|
||||
int ubifs_replay_journal(struct ubifs_info *c)
|
||||
{
|
||||
int err, i, lnum, offs, _free;
|
||||
void *sbuf = NULL;
|
||||
int err, lnum, free;
|
||||
|
||||
BUILD_BUG_ON(UBIFS_TRUN_KEY > 5);
|
||||
|
||||
/* Update the status of the index head in lprops to 'taken' */
|
||||
_free = take_ihead(c);
|
||||
if (_free < 0)
|
||||
return _free; /* Error code */
|
||||
free = take_ihead(c);
|
||||
if (free < 0)
|
||||
return free; /* Error code */
|
||||
|
||||
if (c->ihead_offs != c->leb_size - _free) {
|
||||
if (c->ihead_offs != c->leb_size - free) {
|
||||
ubifs_err("bad index head LEB %d:%d", c->ihead_lnum,
|
||||
c->ihead_offs);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sbuf = vmalloc(c->leb_size);
|
||||
if (!sbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
dbg_mnt("start replaying the journal");
|
||||
|
||||
c->replaying = 1;
|
||||
|
||||
lnum = c->ltail_lnum = c->lhead_lnum;
|
||||
offs = c->lhead_offs;
|
||||
|
||||
for (i = 0; i < c->log_lebs; i++, lnum++) {
|
||||
if (lnum >= UBIFS_LOG_LNUM + c->log_lebs) {
|
||||
/*
|
||||
* The log is logically circular, we reached the last
|
||||
* LEB, switch to the first one.
|
||||
*/
|
||||
lnum = UBIFS_LOG_LNUM;
|
||||
offs = 0;
|
||||
}
|
||||
err = replay_log_leb(c, lnum, offs, sbuf);
|
||||
do {
|
||||
err = replay_log_leb(c, lnum, 0, c->sbuf);
|
||||
if (err == 1)
|
||||
/* We hit the end of the log */
|
||||
break;
|
||||
if (err)
|
||||
goto out;
|
||||
offs = 0;
|
||||
}
|
||||
lnum = ubifs_next_log_lnum(c, lnum);
|
||||
} while (lnum != c->ltail_lnum);
|
||||
|
||||
err = replay_buds(c);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = apply_replay_tree(c);
|
||||
err = apply_replay_list(c);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = set_buds_lprops(c);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* UBIFS budgeting calculations use @c->bi.uncommitted_idx variable
|
||||
* to roughly estimate index growth. Things like @c->bi.min_idx_lebs
|
||||
* depend on it. This means we have to initialize it to make sure
|
||||
* budgeting works properly.
|
||||
*/
|
||||
c->bi.uncommitted_idx = atomic_long_read(&c->dirty_zn_cnt);
|
||||
c->bi.uncommitted_idx *= c->max_idx_node_sz;
|
||||
|
||||
ubifs_assert(c->bud_bytes <= c->max_bud_bytes || c->need_recovery);
|
||||
dbg_mnt("finished, log head LEB %d:%d, max_sqnum %llu, "
|
||||
"highest_inum %lu", c->lhead_lnum, c->lhead_offs, c->max_sqnum,
|
||||
dbg_mnt("finished, log head LEB %d:%d, max_sqnum %llu, highest_inum %lu",
|
||||
c->lhead_lnum, c->lhead_offs, c->max_sqnum,
|
||||
(unsigned long)c->highest_inum);
|
||||
out:
|
||||
destroy_replay_tree(c);
|
||||
destroy_replay_list(c);
|
||||
destroy_bud_list(c);
|
||||
vfree(sbuf);
|
||||
c->replaying = 0;
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
547
fs/ubifs/sb.c
547
fs/ubifs/sb.c
@ -3,18 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2006-2008 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Authors: Artem Bityutskiy (Битюцкий Артём)
|
||||
* Adrian Hunter
|
||||
@ -27,6 +16,18 @@
|
||||
*/
|
||||
|
||||
#include "ubifs.h"
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/math64.h>
|
||||
#else
|
||||
|
||||
#include <linux/compat.h>
|
||||
#include <linux/err.h>
|
||||
#include <ubi_uboot.h>
|
||||
#include <linux/stat.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Default journal size in logical eraseblocks as a percent of total
|
||||
@ -60,6 +61,282 @@
|
||||
/* Default time granularity in nanoseconds */
|
||||
#define DEFAULT_TIME_GRAN 1000000000
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* create_default_filesystem - format empty UBI volume.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function creates default empty file-system. Returns zero in case of
|
||||
* success and a negative error code in case of failure.
|
||||
*/
|
||||
static int create_default_filesystem(struct ubifs_info *c)
|
||||
{
|
||||
struct ubifs_sb_node *sup;
|
||||
struct ubifs_mst_node *mst;
|
||||
struct ubifs_idx_node *idx;
|
||||
struct ubifs_branch *br;
|
||||
struct ubifs_ino_node *ino;
|
||||
struct ubifs_cs_node *cs;
|
||||
union ubifs_key key;
|
||||
int err, tmp, jnl_lebs, log_lebs, max_buds, main_lebs, main_first;
|
||||
int lpt_lebs, lpt_first, orph_lebs, big_lpt, ino_waste, sup_flags = 0;
|
||||
int min_leb_cnt = UBIFS_MIN_LEB_CNT;
|
||||
long long tmp64, main_bytes;
|
||||
__le64 tmp_le64;
|
||||
|
||||
/* Some functions called from here depend on the @c->key_len filed */
|
||||
c->key_len = UBIFS_SK_LEN;
|
||||
|
||||
/*
|
||||
* First of all, we have to calculate default file-system geometry -
|
||||
* log size, journal size, etc.
|
||||
*/
|
||||
if (c->leb_cnt < 0x7FFFFFFF / DEFAULT_JNL_PERCENT)
|
||||
/* We can first multiply then divide and have no overflow */
|
||||
jnl_lebs = c->leb_cnt * DEFAULT_JNL_PERCENT / 100;
|
||||
else
|
||||
jnl_lebs = (c->leb_cnt / 100) * DEFAULT_JNL_PERCENT;
|
||||
|
||||
if (jnl_lebs < UBIFS_MIN_JNL_LEBS)
|
||||
jnl_lebs = UBIFS_MIN_JNL_LEBS;
|
||||
if (jnl_lebs * c->leb_size > DEFAULT_MAX_JNL)
|
||||
jnl_lebs = DEFAULT_MAX_JNL / c->leb_size;
|
||||
|
||||
/*
|
||||
* The log should be large enough to fit reference nodes for all bud
|
||||
* LEBs. Because buds do not have to start from the beginning of LEBs
|
||||
* (half of the LEB may contain committed data), the log should
|
||||
* generally be larger, make it twice as large.
|
||||
*/
|
||||
tmp = 2 * (c->ref_node_alsz * jnl_lebs) + c->leb_size - 1;
|
||||
log_lebs = tmp / c->leb_size;
|
||||
/* Plus one LEB reserved for commit */
|
||||
log_lebs += 1;
|
||||
if (c->leb_cnt - min_leb_cnt > 8) {
|
||||
/* And some extra space to allow writes while committing */
|
||||
log_lebs += 1;
|
||||
min_leb_cnt += 1;
|
||||
}
|
||||
|
||||
max_buds = jnl_lebs - log_lebs;
|
||||
if (max_buds < UBIFS_MIN_BUD_LEBS)
|
||||
max_buds = UBIFS_MIN_BUD_LEBS;
|
||||
|
||||
/*
|
||||
* Orphan nodes are stored in a separate area. One node can store a lot
|
||||
* of orphan inode numbers, but when new orphan comes we just add a new
|
||||
* orphan node. At some point the nodes are consolidated into one
|
||||
* orphan node.
|
||||
*/
|
||||
orph_lebs = UBIFS_MIN_ORPH_LEBS;
|
||||
if (c->leb_cnt - min_leb_cnt > 1)
|
||||
/*
|
||||
* For debugging purposes it is better to have at least 2
|
||||
* orphan LEBs, because the orphan subsystem would need to do
|
||||
* consolidations and would be stressed more.
|
||||
*/
|
||||
orph_lebs += 1;
|
||||
|
||||
main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS - log_lebs;
|
||||
main_lebs -= orph_lebs;
|
||||
|
||||
lpt_first = UBIFS_LOG_LNUM + log_lebs;
|
||||
c->lsave_cnt = DEFAULT_LSAVE_CNT;
|
||||
c->max_leb_cnt = c->leb_cnt;
|
||||
err = ubifs_create_dflt_lpt(c, &main_lebs, lpt_first, &lpt_lebs,
|
||||
&big_lpt);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dbg_gen("LEB Properties Tree created (LEBs %d-%d)", lpt_first,
|
||||
lpt_first + lpt_lebs - 1);
|
||||
|
||||
main_first = c->leb_cnt - main_lebs;
|
||||
|
||||
/* Create default superblock */
|
||||
tmp = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size);
|
||||
sup = kzalloc(tmp, GFP_KERNEL);
|
||||
if (!sup)
|
||||
return -ENOMEM;
|
||||
|
||||
tmp64 = (long long)max_buds * c->leb_size;
|
||||
if (big_lpt)
|
||||
sup_flags |= UBIFS_FLG_BIGLPT;
|
||||
|
||||
sup->ch.node_type = UBIFS_SB_NODE;
|
||||
sup->key_hash = UBIFS_KEY_HASH_R5;
|
||||
sup->flags = cpu_to_le32(sup_flags);
|
||||
sup->min_io_size = cpu_to_le32(c->min_io_size);
|
||||
sup->leb_size = cpu_to_le32(c->leb_size);
|
||||
sup->leb_cnt = cpu_to_le32(c->leb_cnt);
|
||||
sup->max_leb_cnt = cpu_to_le32(c->max_leb_cnt);
|
||||
sup->max_bud_bytes = cpu_to_le64(tmp64);
|
||||
sup->log_lebs = cpu_to_le32(log_lebs);
|
||||
sup->lpt_lebs = cpu_to_le32(lpt_lebs);
|
||||
sup->orph_lebs = cpu_to_le32(orph_lebs);
|
||||
sup->jhead_cnt = cpu_to_le32(DEFAULT_JHEADS_CNT);
|
||||
sup->fanout = cpu_to_le32(DEFAULT_FANOUT);
|
||||
sup->lsave_cnt = cpu_to_le32(c->lsave_cnt);
|
||||
sup->fmt_version = cpu_to_le32(UBIFS_FORMAT_VERSION);
|
||||
sup->time_gran = cpu_to_le32(DEFAULT_TIME_GRAN);
|
||||
if (c->mount_opts.override_compr)
|
||||
sup->default_compr = cpu_to_le16(c->mount_opts.compr_type);
|
||||
else
|
||||
sup->default_compr = cpu_to_le16(UBIFS_COMPR_LZO);
|
||||
|
||||
generate_random_uuid(sup->uuid);
|
||||
|
||||
main_bytes = (long long)main_lebs * c->leb_size;
|
||||
tmp64 = div_u64(main_bytes * DEFAULT_RP_PERCENT, 100);
|
||||
if (tmp64 > DEFAULT_MAX_RP_SIZE)
|
||||
tmp64 = DEFAULT_MAX_RP_SIZE;
|
||||
sup->rp_size = cpu_to_le64(tmp64);
|
||||
sup->ro_compat_version = cpu_to_le32(UBIFS_RO_COMPAT_VERSION);
|
||||
|
||||
err = ubifs_write_node(c, sup, UBIFS_SB_NODE_SZ, 0, 0);
|
||||
kfree(sup);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dbg_gen("default superblock created at LEB 0:0");
|
||||
|
||||
/* Create default master node */
|
||||
mst = kzalloc(c->mst_node_alsz, GFP_KERNEL);
|
||||
if (!mst)
|
||||
return -ENOMEM;
|
||||
|
||||
mst->ch.node_type = UBIFS_MST_NODE;
|
||||
mst->log_lnum = cpu_to_le32(UBIFS_LOG_LNUM);
|
||||
mst->highest_inum = cpu_to_le64(UBIFS_FIRST_INO);
|
||||
mst->cmt_no = 0;
|
||||
mst->root_lnum = cpu_to_le32(main_first + DEFAULT_IDX_LEB);
|
||||
mst->root_offs = 0;
|
||||
tmp = ubifs_idx_node_sz(c, 1);
|
||||
mst->root_len = cpu_to_le32(tmp);
|
||||
mst->gc_lnum = cpu_to_le32(main_first + DEFAULT_GC_LEB);
|
||||
mst->ihead_lnum = cpu_to_le32(main_first + DEFAULT_IDX_LEB);
|
||||
mst->ihead_offs = cpu_to_le32(ALIGN(tmp, c->min_io_size));
|
||||
mst->index_size = cpu_to_le64(ALIGN(tmp, 8));
|
||||
mst->lpt_lnum = cpu_to_le32(c->lpt_lnum);
|
||||
mst->lpt_offs = cpu_to_le32(c->lpt_offs);
|
||||
mst->nhead_lnum = cpu_to_le32(c->nhead_lnum);
|
||||
mst->nhead_offs = cpu_to_le32(c->nhead_offs);
|
||||
mst->ltab_lnum = cpu_to_le32(c->ltab_lnum);
|
||||
mst->ltab_offs = cpu_to_le32(c->ltab_offs);
|
||||
mst->lsave_lnum = cpu_to_le32(c->lsave_lnum);
|
||||
mst->lsave_offs = cpu_to_le32(c->lsave_offs);
|
||||
mst->lscan_lnum = cpu_to_le32(main_first);
|
||||
mst->empty_lebs = cpu_to_le32(main_lebs - 2);
|
||||
mst->idx_lebs = cpu_to_le32(1);
|
||||
mst->leb_cnt = cpu_to_le32(c->leb_cnt);
|
||||
|
||||
/* Calculate lprops statistics */
|
||||
tmp64 = main_bytes;
|
||||
tmp64 -= ALIGN(ubifs_idx_node_sz(c, 1), c->min_io_size);
|
||||
tmp64 -= ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size);
|
||||
mst->total_free = cpu_to_le64(tmp64);
|
||||
|
||||
tmp64 = ALIGN(ubifs_idx_node_sz(c, 1), c->min_io_size);
|
||||
ino_waste = ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size) -
|
||||
UBIFS_INO_NODE_SZ;
|
||||
tmp64 += ino_waste;
|
||||
tmp64 -= ALIGN(ubifs_idx_node_sz(c, 1), 8);
|
||||
mst->total_dirty = cpu_to_le64(tmp64);
|
||||
|
||||
/* The indexing LEB does not contribute to dark space */
|
||||
tmp64 = ((long long)(c->main_lebs - 1) * c->dark_wm);
|
||||
mst->total_dark = cpu_to_le64(tmp64);
|
||||
|
||||
mst->total_used = cpu_to_le64(UBIFS_INO_NODE_SZ);
|
||||
|
||||
err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM, 0);
|
||||
if (err) {
|
||||
kfree(mst);
|
||||
return err;
|
||||
}
|
||||
err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1,
|
||||
0);
|
||||
kfree(mst);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dbg_gen("default master node created at LEB %d:0", UBIFS_MST_LNUM);
|
||||
|
||||
/* Create the root indexing node */
|
||||
tmp = ubifs_idx_node_sz(c, 1);
|
||||
idx = kzalloc(ALIGN(tmp, c->min_io_size), GFP_KERNEL);
|
||||
if (!idx)
|
||||
return -ENOMEM;
|
||||
|
||||
c->key_fmt = UBIFS_SIMPLE_KEY_FMT;
|
||||
c->key_hash = key_r5_hash;
|
||||
|
||||
idx->ch.node_type = UBIFS_IDX_NODE;
|
||||
idx->child_cnt = cpu_to_le16(1);
|
||||
ino_key_init(c, &key, UBIFS_ROOT_INO);
|
||||
br = ubifs_idx_branch(c, idx, 0);
|
||||
key_write_idx(c, &key, &br->key);
|
||||
br->lnum = cpu_to_le32(main_first + DEFAULT_DATA_LEB);
|
||||
br->len = cpu_to_le32(UBIFS_INO_NODE_SZ);
|
||||
err = ubifs_write_node(c, idx, tmp, main_first + DEFAULT_IDX_LEB, 0);
|
||||
kfree(idx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dbg_gen("default root indexing node created LEB %d:0",
|
||||
main_first + DEFAULT_IDX_LEB);
|
||||
|
||||
/* Create default root inode */
|
||||
tmp = ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size);
|
||||
ino = kzalloc(tmp, GFP_KERNEL);
|
||||
if (!ino)
|
||||
return -ENOMEM;
|
||||
|
||||
ino_key_init_flash(c, &ino->key, UBIFS_ROOT_INO);
|
||||
ino->ch.node_type = UBIFS_INO_NODE;
|
||||
ino->creat_sqnum = cpu_to_le64(++c->max_sqnum);
|
||||
ino->nlink = cpu_to_le32(2);
|
||||
tmp_le64 = cpu_to_le64(CURRENT_TIME_SEC.tv_sec);
|
||||
ino->atime_sec = tmp_le64;
|
||||
ino->ctime_sec = tmp_le64;
|
||||
ino->mtime_sec = tmp_le64;
|
||||
ino->atime_nsec = 0;
|
||||
ino->ctime_nsec = 0;
|
||||
ino->mtime_nsec = 0;
|
||||
ino->mode = cpu_to_le32(S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO);
|
||||
ino->size = cpu_to_le64(UBIFS_INO_NODE_SZ);
|
||||
|
||||
/* Set compression enabled by default */
|
||||
ino->flags = cpu_to_le32(UBIFS_COMPR_FL);
|
||||
|
||||
err = ubifs_write_node(c, ino, UBIFS_INO_NODE_SZ,
|
||||
main_first + DEFAULT_DATA_LEB, 0);
|
||||
kfree(ino);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dbg_gen("root inode created at LEB %d:0",
|
||||
main_first + DEFAULT_DATA_LEB);
|
||||
|
||||
/*
|
||||
* The first node in the log has to be the commit start node. This is
|
||||
* always the case during normal file-system operation. Write a fake
|
||||
* commit start node to the log.
|
||||
*/
|
||||
tmp = ALIGN(UBIFS_CS_NODE_SZ, c->min_io_size);
|
||||
cs = kzalloc(tmp, GFP_KERNEL);
|
||||
if (!cs)
|
||||
return -ENOMEM;
|
||||
|
||||
cs->ch.node_type = UBIFS_CS_NODE;
|
||||
err = ubifs_write_node(c, cs, UBIFS_CS_NODE_SZ, UBIFS_LOG_LNUM, 0);
|
||||
kfree(cs);
|
||||
|
||||
ubifs_msg("default file-system created");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* validate_sb - validate superblock node.
|
||||
* @c: UBIFS file-system description object
|
||||
@ -114,9 +391,8 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup)
|
||||
min_leb_cnt += c->lpt_lebs + c->orph_lebs + c->jhead_cnt + 6;
|
||||
|
||||
if (c->leb_cnt < min_leb_cnt || c->leb_cnt > c->vi.size) {
|
||||
ubifs_err("bad LEB count: %d in superblock, %d on UBI volume, "
|
||||
"%d minimum required", c->leb_cnt, c->vi.size,
|
||||
min_leb_cnt);
|
||||
ubifs_err("bad LEB count: %d in superblock, %d on UBI volume, %d minimum required",
|
||||
c->leb_cnt, c->vi.size, min_leb_cnt);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
@ -127,13 +403,22 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup)
|
||||
}
|
||||
|
||||
if (c->main_lebs < UBIFS_MIN_MAIN_LEBS) {
|
||||
err = 7;
|
||||
ubifs_err("too few main LEBs count %d, must be at least %d",
|
||||
c->main_lebs, UBIFS_MIN_MAIN_LEBS);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (c->max_bud_bytes < (long long)c->leb_size * UBIFS_MIN_BUD_LEBS ||
|
||||
c->max_bud_bytes > (long long)c->leb_size * c->main_lebs) {
|
||||
err = 8;
|
||||
max_bytes = (long long)c->leb_size * UBIFS_MIN_BUD_LEBS;
|
||||
if (c->max_bud_bytes < max_bytes) {
|
||||
ubifs_err("too small journal (%lld bytes), must be at least %lld bytes",
|
||||
c->max_bud_bytes, max_bytes);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
max_bytes = (long long)c->leb_size * c->main_lebs;
|
||||
if (c->max_bud_bytes > max_bytes) {
|
||||
ubifs_err("too large journal size (%lld bytes), only %lld bytes available in the main area",
|
||||
c->max_bud_bytes, max_bytes);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
@ -167,7 +452,6 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
max_bytes = c->main_lebs * (long long)c->leb_size;
|
||||
if (c->rp_size < 0 || max_bytes < c->rp_size) {
|
||||
err = 14;
|
||||
goto failed;
|
||||
@ -183,7 +467,7 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup)
|
||||
|
||||
failed:
|
||||
ubifs_err("bad superblock, error %d", err);
|
||||
dbg_dump_node(c, sup);
|
||||
ubifs_dump_node(c, sup);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -192,7 +476,8 @@ failed:
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function returns a pointer to the superblock node or a negative error
|
||||
* code.
|
||||
* code. Note, the user of this function is responsible of kfree()'ing the
|
||||
* returned superblock buffer.
|
||||
*/
|
||||
struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
|
||||
{
|
||||
@ -213,6 +498,21 @@ struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
|
||||
return sup;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_write_sb_node - write superblock node.
|
||||
* @c: UBIFS file-system description object
|
||||
* @sup: superblock node read with 'ubifs_read_sb_node()'
|
||||
*
|
||||
* This function returns %0 on success and a negative error code on failure.
|
||||
*/
|
||||
int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup)
|
||||
{
|
||||
int len = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size);
|
||||
|
||||
ubifs_prepare_node(c, sup, UBIFS_SB_NODE_SZ, 1);
|
||||
return ubifs_leb_change(c, UBIFS_SB_LNUM, sup, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_read_superblock - read superblock.
|
||||
* @c: UBIFS file-system description object
|
||||
@ -227,8 +527,14 @@ int ubifs_read_superblock(struct ubifs_info *c)
|
||||
struct ubifs_sb_node *sup;
|
||||
|
||||
if (c->empty) {
|
||||
#ifndef __UBOOT__
|
||||
err = create_default_filesystem(c);
|
||||
if (err)
|
||||
return err;
|
||||
#else
|
||||
printf("No UBIFS filesystem found!\n");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
sup = ubifs_read_sb_node(c);
|
||||
@ -243,16 +549,12 @@ int ubifs_read_superblock(struct ubifs_info *c)
|
||||
* due to the unavailability of time-travelling equipment.
|
||||
*/
|
||||
if (c->fmt_version > UBIFS_FORMAT_VERSION) {
|
||||
struct super_block *sb = c->vfs_sb;
|
||||
int mounting_ro = sb->s_flags & MS_RDONLY;
|
||||
|
||||
ubifs_assert(!c->ro_media || mounting_ro);
|
||||
if (!mounting_ro ||
|
||||
ubifs_assert(!c->ro_media || c->ro_mount);
|
||||
if (!c->ro_mount ||
|
||||
c->ro_compat_version > UBIFS_RO_COMPAT_VERSION) {
|
||||
ubifs_err("on-flash format version is w%d/r%d, but "
|
||||
"software only supports up to version "
|
||||
"w%d/r%d", c->fmt_version,
|
||||
c->ro_compat_version, UBIFS_FORMAT_VERSION,
|
||||
ubifs_err("on-flash format version is w%d/r%d, but software only supports up to version w%d/r%d",
|
||||
c->fmt_version, c->ro_compat_version,
|
||||
UBIFS_FORMAT_VERSION,
|
||||
UBIFS_RO_COMPAT_VERSION);
|
||||
if (c->ro_compat_version <= UBIFS_RO_COMPAT_VERSION) {
|
||||
ubifs_msg("only R/O mounting is possible");
|
||||
@ -310,22 +612,41 @@ int ubifs_read_superblock(struct ubifs_info *c)
|
||||
c->jhead_cnt = le32_to_cpu(sup->jhead_cnt) + NONDATA_JHEADS_CNT;
|
||||
c->fanout = le32_to_cpu(sup->fanout);
|
||||
c->lsave_cnt = le32_to_cpu(sup->lsave_cnt);
|
||||
c->default_compr = le16_to_cpu(sup->default_compr);
|
||||
c->rp_size = le64_to_cpu(sup->rp_size);
|
||||
c->rp_uid = le32_to_cpu(sup->rp_uid);
|
||||
c->rp_gid = le32_to_cpu(sup->rp_gid);
|
||||
#ifndef __UBOOT__
|
||||
c->rp_uid = make_kuid(&init_user_ns, le32_to_cpu(sup->rp_uid));
|
||||
c->rp_gid = make_kgid(&init_user_ns, le32_to_cpu(sup->rp_gid));
|
||||
#else
|
||||
c->rp_uid.val = le32_to_cpu(sup->rp_uid);
|
||||
c->rp_gid.val = le32_to_cpu(sup->rp_gid);
|
||||
#endif
|
||||
sup_flags = le32_to_cpu(sup->flags);
|
||||
if (!c->mount_opts.override_compr)
|
||||
c->default_compr = le16_to_cpu(sup->default_compr);
|
||||
|
||||
c->vfs_sb->s_time_gran = le32_to_cpu(sup->time_gran);
|
||||
memcpy(&c->uuid, &sup->uuid, 16);
|
||||
c->big_lpt = !!(sup_flags & UBIFS_FLG_BIGLPT);
|
||||
c->space_fixup = !!(sup_flags & UBIFS_FLG_SPACE_FIXUP);
|
||||
|
||||
/* Automatically increase file system size to the maximum size */
|
||||
c->old_leb_cnt = c->leb_cnt;
|
||||
if (c->leb_cnt < c->vi.size && c->leb_cnt < c->max_leb_cnt) {
|
||||
c->leb_cnt = min_t(int, c->max_leb_cnt, c->vi.size);
|
||||
dbg_mnt("Auto resizing (ro) from %d LEBs to %d LEBs",
|
||||
c->old_leb_cnt, c->leb_cnt);
|
||||
if (c->ro_mount)
|
||||
dbg_mnt("Auto resizing (ro) from %d LEBs to %d LEBs",
|
||||
c->old_leb_cnt, c->leb_cnt);
|
||||
#ifndef __UBOOT__
|
||||
else {
|
||||
dbg_mnt("Auto resizing (sb) from %d LEBs to %d LEBs",
|
||||
c->old_leb_cnt, c->leb_cnt);
|
||||
sup->leb_cnt = cpu_to_le32(c->leb_cnt);
|
||||
err = ubifs_write_sb_node(c, sup);
|
||||
if (err)
|
||||
goto out;
|
||||
c->old_leb_cnt = c->leb_cnt;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
c->log_bytes = (long long)c->log_lebs * c->leb_size;
|
||||
@ -337,10 +658,162 @@ int ubifs_read_superblock(struct ubifs_info *c)
|
||||
c->main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS;
|
||||
c->main_lebs -= c->log_lebs + c->lpt_lebs + c->orph_lebs;
|
||||
c->main_first = c->leb_cnt - c->main_lebs;
|
||||
c->report_rp_size = ubifs_reported_space(c, c->rp_size);
|
||||
|
||||
err = validate_sb(c, sup);
|
||||
out:
|
||||
kfree(sup);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* fixup_leb - fixup/unmap an LEB containing free space.
|
||||
* @c: UBIFS file-system description object
|
||||
* @lnum: the LEB number to fix up
|
||||
* @len: number of used bytes in LEB (starting at offset 0)
|
||||
*
|
||||
* This function reads the contents of the given LEB number @lnum, then fixes
|
||||
* it up, so that empty min. I/O units in the end of LEB are actually erased on
|
||||
* flash (rather than being just all-0xff real data). If the LEB is completely
|
||||
* empty, it is simply unmapped.
|
||||
*/
|
||||
static int fixup_leb(struct ubifs_info *c, int lnum, int len)
|
||||
{
|
||||
int err;
|
||||
|
||||
ubifs_assert(len >= 0);
|
||||
ubifs_assert(len % c->min_io_size == 0);
|
||||
ubifs_assert(len < c->leb_size);
|
||||
|
||||
if (len == 0) {
|
||||
dbg_mnt("unmap empty LEB %d", lnum);
|
||||
return ubifs_leb_unmap(c, lnum);
|
||||
}
|
||||
|
||||
dbg_mnt("fixup LEB %d, data len %d", lnum, len);
|
||||
err = ubifs_leb_read(c, lnum, c->sbuf, 0, len, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return ubifs_leb_change(c, lnum, c->sbuf, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* fixup_free_space - find & remap all LEBs containing free space.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function walks through all LEBs in the filesystem and fiexes up those
|
||||
* containing free/empty space.
|
||||
*/
|
||||
static int fixup_free_space(struct ubifs_info *c)
|
||||
{
|
||||
int lnum, err = 0;
|
||||
struct ubifs_lprops *lprops;
|
||||
|
||||
ubifs_get_lprops(c);
|
||||
|
||||
/* Fixup LEBs in the master area */
|
||||
for (lnum = UBIFS_MST_LNUM; lnum < UBIFS_LOG_LNUM; lnum++) {
|
||||
err = fixup_leb(c, lnum, c->mst_offs + c->mst_node_alsz);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Unmap unused log LEBs */
|
||||
lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
|
||||
while (lnum != c->ltail_lnum) {
|
||||
err = fixup_leb(c, lnum, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
lnum = ubifs_next_log_lnum(c, lnum);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fixup the log head which contains the only a CS node at the
|
||||
* beginning.
|
||||
*/
|
||||
err = fixup_leb(c, c->lhead_lnum,
|
||||
ALIGN(UBIFS_CS_NODE_SZ, c->min_io_size));
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* Fixup LEBs in the LPT area */
|
||||
for (lnum = c->lpt_first; lnum <= c->lpt_last; lnum++) {
|
||||
int free = c->ltab[lnum - c->lpt_first].free;
|
||||
|
||||
if (free > 0) {
|
||||
err = fixup_leb(c, lnum, c->leb_size - free);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unmap LEBs in the orphans area */
|
||||
for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) {
|
||||
err = fixup_leb(c, lnum, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Fixup LEBs in the main area */
|
||||
for (lnum = c->main_first; lnum < c->leb_cnt; lnum++) {
|
||||
lprops = ubifs_lpt_lookup(c, lnum);
|
||||
if (IS_ERR(lprops)) {
|
||||
err = PTR_ERR(lprops);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (lprops->free > 0) {
|
||||
err = fixup_leb(c, lnum, c->leb_size - lprops->free);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
ubifs_release_lprops(c);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_fixup_free_space - find & fix all LEBs with free space.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function fixes up LEBs containing free space on first mount, if the
|
||||
* appropriate flag was set when the FS was created. Each LEB with one or more
|
||||
* empty min. I/O unit (i.e. free-space-count > 0) is re-written, to make sure
|
||||
* the free space is actually erased. E.g., this is necessary for some NAND
|
||||
* chips, since the free space may have been programmed like real "0xff" data
|
||||
* (generating a non-0xff ECC), causing future writes to the not-really-erased
|
||||
* NAND pages to behave badly. After the space is fixed up, the superblock flag
|
||||
* is cleared, so that this is skipped for all future mounts.
|
||||
*/
|
||||
int ubifs_fixup_free_space(struct ubifs_info *c)
|
||||
{
|
||||
int err;
|
||||
struct ubifs_sb_node *sup;
|
||||
|
||||
ubifs_assert(c->space_fixup);
|
||||
ubifs_assert(!c->ro_mount);
|
||||
|
||||
ubifs_msg("start fixing up free space");
|
||||
|
||||
err = fixup_free_space(c);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
sup = ubifs_read_sb_node(c);
|
||||
if (IS_ERR(sup))
|
||||
return PTR_ERR(sup);
|
||||
|
||||
/* Free-space fixup is no longer required */
|
||||
c->space_fixup = 0;
|
||||
sup->flags &= cpu_to_le32(~UBIFS_FLG_SPACE_FIXUP);
|
||||
|
||||
err = ubifs_write_sb_node(c, sup);
|
||||
kfree(sup);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ubifs_msg("free space fixup complete");
|
||||
return err;
|
||||
}
|
||||
|
102
fs/ubifs/scan.c
102
fs/ubifs/scan.c
@ -3,18 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2006-2008 Nokia Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Authors: Adrian Hunter
|
||||
* Artem Bityutskiy (Битюцкий Артём)
|
||||
@ -27,6 +16,10 @@
|
||||
* debugging functions.
|
||||
*/
|
||||
|
||||
#define __UBOOT__
|
||||
#ifdef __UBOOT__
|
||||
#include <linux/err.h>
|
||||
#endif
|
||||
#include "ubifs.h"
|
||||
|
||||
/**
|
||||
@ -75,7 +68,7 @@ int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum,
|
||||
magic = le32_to_cpu(ch->magic);
|
||||
|
||||
if (magic == 0xFFFFFFFF) {
|
||||
dbg_scan("hit empty space");
|
||||
dbg_scan("hit empty space at LEB %d:%d", lnum, offs);
|
||||
return SCANNED_EMPTY_SPACE;
|
||||
}
|
||||
|
||||
@ -85,7 +78,8 @@ int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum,
|
||||
if (len < UBIFS_CH_SZ)
|
||||
return SCANNED_GARBAGE;
|
||||
|
||||
dbg_scan("scanning %s", dbg_ntype(ch->node_type));
|
||||
dbg_scan("scanning %s at LEB %d:%d",
|
||||
dbg_ntype(ch->node_type), lnum, offs);
|
||||
|
||||
if (ubifs_check_node(c, buf, lnum, offs, quiet, 1))
|
||||
return SCANNED_A_CORRUPT_NODE;
|
||||
@ -101,22 +95,21 @@ int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum,
|
||||
if (!quiet) {
|
||||
ubifs_err("bad pad node at LEB %d:%d",
|
||||
lnum, offs);
|
||||
dbg_dump_node(c, pad);
|
||||
ubifs_dump_node(c, pad);
|
||||
}
|
||||
return SCANNED_A_BAD_PAD_NODE;
|
||||
}
|
||||
|
||||
/* Make the node pads to 8-byte boundary */
|
||||
if ((node_len + pad_len) & 7) {
|
||||
if (!quiet) {
|
||||
dbg_err("bad padding length %d - %d",
|
||||
offs, offs + node_len + pad_len);
|
||||
}
|
||||
if (!quiet)
|
||||
ubifs_err("bad padding length %d - %d",
|
||||
offs, offs + node_len + pad_len);
|
||||
return SCANNED_A_BAD_PAD_NODE;
|
||||
}
|
||||
|
||||
dbg_scan("%d bytes padded, offset now %d",
|
||||
pad_len, ALIGN(offs + node_len + pad_len, 8));
|
||||
dbg_scan("%d bytes padded at LEB %d:%d, offset now %d", pad_len,
|
||||
lnum, offs, ALIGN(offs + node_len + pad_len, 8));
|
||||
|
||||
return node_len + pad_len;
|
||||
}
|
||||
@ -149,10 +142,10 @@ struct ubifs_scan_leb *ubifs_start_scan(const struct ubifs_info *c, int lnum,
|
||||
INIT_LIST_HEAD(&sleb->nodes);
|
||||
sleb->buf = sbuf;
|
||||
|
||||
err = ubi_read(c->ubi, lnum, sbuf + offs, offs, c->leb_size - offs);
|
||||
err = ubifs_leb_read(c, lnum, sbuf + offs, offs, c->leb_size - offs, 0);
|
||||
if (err && err != -EBADMSG) {
|
||||
ubifs_err("cannot read %d bytes from LEB %d:%d,"
|
||||
" error %d", c->leb_size - offs, lnum, offs, err);
|
||||
ubifs_err("cannot read %d bytes from LEB %d:%d, error %d",
|
||||
c->leb_size - offs, lnum, offs, err);
|
||||
kfree(sleb);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
@ -198,7 +191,7 @@ int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb,
|
||||
struct ubifs_ino_node *ino = buf;
|
||||
struct ubifs_scan_node *snod;
|
||||
|
||||
snod = kzalloc(sizeof(struct ubifs_scan_node), GFP_NOFS);
|
||||
snod = kmalloc(sizeof(struct ubifs_scan_node), GFP_NOFS);
|
||||
if (!snod)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -213,13 +206,15 @@ int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb,
|
||||
case UBIFS_DENT_NODE:
|
||||
case UBIFS_XENT_NODE:
|
||||
case UBIFS_DATA_NODE:
|
||||
case UBIFS_TRUN_NODE:
|
||||
/*
|
||||
* The key is in the same place in all keyed
|
||||
* nodes.
|
||||
*/
|
||||
key_read(c, &ino->key, &snod->key);
|
||||
break;
|
||||
default:
|
||||
invalid_key_init(c, &snod->key);
|
||||
break;
|
||||
}
|
||||
list_add_tail(&snod->list, &sleb->nodes);
|
||||
sleb->nodes_cnt += 1;
|
||||
@ -238,13 +233,11 @@ void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs,
|
||||
{
|
||||
int len;
|
||||
|
||||
ubifs_err("corrupted data at LEB %d:%d", lnum, offs);
|
||||
if (dbg_failure_mode)
|
||||
return;
|
||||
ubifs_err("corruption at LEB %d:%d", lnum, offs);
|
||||
len = c->leb_size - offs;
|
||||
if (len > 4096)
|
||||
len = 4096;
|
||||
dbg_err("first %d bytes from LEB %d:%d", len, lnum, offs);
|
||||
if (len > 8192)
|
||||
len = 8192;
|
||||
ubifs_err("first %d bytes from LEB %d:%d", len, lnum, offs);
|
||||
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 4, buf, len, 1);
|
||||
}
|
||||
|
||||
@ -253,13 +246,19 @@ void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs,
|
||||
* @c: UBIFS file-system description object
|
||||
* @lnum: logical eraseblock number
|
||||
* @offs: offset to start at (usually zero)
|
||||
* @sbuf: scan buffer (must be c->leb_size)
|
||||
* @sbuf: scan buffer (must be of @c->leb_size bytes in size)
|
||||
* @quiet: print no messages
|
||||
*
|
||||
* This function scans LEB number @lnum and returns complete information about
|
||||
* its contents. Returns an error code in case of failure.
|
||||
* its contents. Returns the scaned information in case of success and,
|
||||
* %-EUCLEAN if the LEB neads recovery, and other negative error codes in case
|
||||
* of failure.
|
||||
*
|
||||
* If @quiet is non-zero, this function does not print large and scary
|
||||
* error messages and flash dumps in case of errors.
|
||||
*/
|
||||
struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum,
|
||||
int offs, void *sbuf)
|
||||
int offs, void *sbuf, int quiet)
|
||||
{
|
||||
void *buf = sbuf + offs;
|
||||
int err, len = c->leb_size - offs;
|
||||
@ -278,8 +277,7 @@ struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum,
|
||||
|
||||
cond_resched();
|
||||
|
||||
ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 0);
|
||||
|
||||
ret = ubifs_scan_a_node(c, buf, len, lnum, offs, quiet);
|
||||
if (ret > 0) {
|
||||
/* Padding bytes or a valid padding node */
|
||||
offs += ret;
|
||||
@ -294,17 +292,18 @@ struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum,
|
||||
|
||||
switch (ret) {
|
||||
case SCANNED_GARBAGE:
|
||||
dbg_err("garbage");
|
||||
ubifs_err("garbage");
|
||||
goto corrupted;
|
||||
case SCANNED_A_NODE:
|
||||
break;
|
||||
case SCANNED_A_CORRUPT_NODE:
|
||||
case SCANNED_A_BAD_PAD_NODE:
|
||||
dbg_err("bad node");
|
||||
ubifs_err("bad node");
|
||||
goto corrupted;
|
||||
default:
|
||||
dbg_err("unknown");
|
||||
goto corrupted;
|
||||
ubifs_err("unknown");
|
||||
err = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = ubifs_add_snod(c, sleb, buf, offs);
|
||||
@ -317,8 +316,12 @@ struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum,
|
||||
len -= node_len;
|
||||
}
|
||||
|
||||
if (offs % c->min_io_size)
|
||||
if (offs % c->min_io_size) {
|
||||
if (!quiet)
|
||||
ubifs_err("empty space starts at non-aligned offset %d",
|
||||
offs);
|
||||
goto corrupted;
|
||||
}
|
||||
|
||||
ubifs_end_scan(c, sleb, lnum, offs);
|
||||
|
||||
@ -327,18 +330,25 @@ struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum,
|
||||
break;
|
||||
for (; len; offs++, buf++, len--)
|
||||
if (*(uint8_t *)buf != 0xff) {
|
||||
ubifs_err("corrupt empty space at LEB %d:%d",
|
||||
lnum, offs);
|
||||
if (!quiet)
|
||||
ubifs_err("corrupt empty space at LEB %d:%d",
|
||||
lnum, offs);
|
||||
goto corrupted;
|
||||
}
|
||||
|
||||
return sleb;
|
||||
|
||||
corrupted:
|
||||
ubifs_scanned_corruption(c, lnum, offs, buf);
|
||||
if (!quiet) {
|
||||
ubifs_scanned_corruption(c, lnum, offs, buf);
|
||||
ubifs_err("LEB %d scanning failed", lnum);
|
||||
}
|
||||
err = -EUCLEAN;
|
||||
ubifs_scan_destroy(sleb);
|
||||
return ERR_PTR(err);
|
||||
|
||||
error:
|
||||
ubifs_err("LEB %d scanning failed", lnum);
|
||||
ubifs_err("LEB %d scanning failed, error %d", lnum, err);
|
||||
ubifs_scan_destroy(sleb);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
2097
fs/ubifs/super.c
2097
fs/ubifs/super.c
File diff suppressed because it is too large
Load Diff
742
fs/ubifs/tnc.c
742
fs/ubifs/tnc.c
File diff suppressed because it is too large
Load Diff
@ -3,18 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2006-2008 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Authors: Adrian Hunter
|
||||
* Artem Bityutskiy (Битюцкий Артём)
|
||||
@ -27,6 +16,10 @@
|
||||
* putting it all in one file would make that file too big and unreadable.
|
||||
*/
|
||||
|
||||
#define __UBOOT__
|
||||
#ifdef __UBOOT__
|
||||
#include <linux/err.h>
|
||||
#endif
|
||||
#include "ubifs.h"
|
||||
|
||||
/**
|
||||
@ -218,6 +211,44 @@ struct ubifs_znode *ubifs_tnc_postorder_next(struct ubifs_znode *znode)
|
||||
return ubifs_tnc_postorder_first(zn);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_destroy_tnc_subtree - destroy all znodes connected to a subtree.
|
||||
* @znode: znode defining subtree to destroy
|
||||
*
|
||||
* This function destroys subtree of the TNC tree. Returns number of clean
|
||||
* znodes in the subtree.
|
||||
*/
|
||||
long ubifs_destroy_tnc_subtree(struct ubifs_znode *znode)
|
||||
{
|
||||
struct ubifs_znode *zn = ubifs_tnc_postorder_first(znode);
|
||||
long clean_freed = 0;
|
||||
int n;
|
||||
|
||||
ubifs_assert(zn);
|
||||
while (1) {
|
||||
for (n = 0; n < zn->child_cnt; n++) {
|
||||
if (!zn->zbranch[n].znode)
|
||||
continue;
|
||||
|
||||
if (zn->level > 0 &&
|
||||
!ubifs_zn_dirty(zn->zbranch[n].znode))
|
||||
clean_freed += 1;
|
||||
|
||||
cond_resched();
|
||||
kfree(zn->zbranch[n].znode);
|
||||
}
|
||||
|
||||
if (zn == znode) {
|
||||
if (!ubifs_zn_dirty(zn))
|
||||
clean_freed += 1;
|
||||
kfree(zn);
|
||||
return clean_freed;
|
||||
}
|
||||
|
||||
zn = ubifs_tnc_postorder_next(zn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* read_znode - read an indexing node from flash and fill znode.
|
||||
* @c: UBIFS file-system description object
|
||||
@ -255,10 +286,10 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len,
|
||||
lnum, offs, znode->level, znode->child_cnt);
|
||||
|
||||
if (znode->child_cnt > c->fanout || znode->level > UBIFS_MAX_LEVELS) {
|
||||
dbg_err("current fanout %d, branch count %d",
|
||||
c->fanout, znode->child_cnt);
|
||||
dbg_err("max levels %d, znode level %d",
|
||||
UBIFS_MAX_LEVELS, znode->level);
|
||||
ubifs_err("current fanout %d, branch count %d",
|
||||
c->fanout, znode->child_cnt);
|
||||
ubifs_err("max levels %d, znode level %d",
|
||||
UBIFS_MAX_LEVELS, znode->level);
|
||||
err = 1;
|
||||
goto out_dump;
|
||||
}
|
||||
@ -278,7 +309,7 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len,
|
||||
if (zbr->lnum < c->main_first ||
|
||||
zbr->lnum >= c->leb_cnt || zbr->offs < 0 ||
|
||||
zbr->offs + zbr->len > c->leb_size || zbr->offs & 7) {
|
||||
dbg_err("bad branch %d", i);
|
||||
ubifs_err("bad branch %d", i);
|
||||
err = 2;
|
||||
goto out_dump;
|
||||
}
|
||||
@ -290,8 +321,8 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len,
|
||||
case UBIFS_XENT_KEY:
|
||||
break;
|
||||
default:
|
||||
dbg_msg("bad key type at slot %d: %s", i,
|
||||
DBGKEY(&zbr->key));
|
||||
ubifs_err("bad key type at slot %d: %d",
|
||||
i, key_type(c, &zbr->key));
|
||||
err = 3;
|
||||
goto out_dump;
|
||||
}
|
||||
@ -302,19 +333,19 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len,
|
||||
type = key_type(c, &zbr->key);
|
||||
if (c->ranges[type].max_len == 0) {
|
||||
if (zbr->len != c->ranges[type].len) {
|
||||
dbg_err("bad target node (type %d) length (%d)",
|
||||
type, zbr->len);
|
||||
dbg_err("have to be %d", c->ranges[type].len);
|
||||
ubifs_err("bad target node (type %d) length (%d)",
|
||||
type, zbr->len);
|
||||
ubifs_err("have to be %d", c->ranges[type].len);
|
||||
err = 4;
|
||||
goto out_dump;
|
||||
}
|
||||
} else if (zbr->len < c->ranges[type].min_len ||
|
||||
zbr->len > c->ranges[type].max_len) {
|
||||
dbg_err("bad target node (type %d) length (%d)",
|
||||
type, zbr->len);
|
||||
dbg_err("have to be in range of %d-%d",
|
||||
c->ranges[type].min_len,
|
||||
c->ranges[type].max_len);
|
||||
ubifs_err("bad target node (type %d) length (%d)",
|
||||
type, zbr->len);
|
||||
ubifs_err("have to be in range of %d-%d",
|
||||
c->ranges[type].min_len,
|
||||
c->ranges[type].max_len);
|
||||
err = 5;
|
||||
goto out_dump;
|
||||
}
|
||||
@ -332,13 +363,13 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len,
|
||||
|
||||
cmp = keys_cmp(c, key1, key2);
|
||||
if (cmp > 0) {
|
||||
dbg_err("bad key order (keys %d and %d)", i, i + 1);
|
||||
ubifs_err("bad key order (keys %d and %d)", i, i + 1);
|
||||
err = 6;
|
||||
goto out_dump;
|
||||
} else if (cmp == 0 && !is_hash_key(c, key1)) {
|
||||
/* These can only be keys with colliding hash */
|
||||
dbg_err("keys %d and %d are not hashed but equivalent",
|
||||
i, i + 1);
|
||||
ubifs_err("keys %d and %d are not hashed but equivalent",
|
||||
i, i + 1);
|
||||
err = 7;
|
||||
goto out_dump;
|
||||
}
|
||||
@ -349,7 +380,7 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len,
|
||||
|
||||
out_dump:
|
||||
ubifs_err("bad indexing node at LEB %d:%d, error %d", lnum, offs, err);
|
||||
dbg_dump_node(c, idx);
|
||||
ubifs_dump_node(c, idx);
|
||||
kfree(idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -385,6 +416,16 @@ struct ubifs_znode *ubifs_load_znode(struct ubifs_info *c,
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
atomic_long_inc(&c->clean_zn_cnt);
|
||||
|
||||
/*
|
||||
* Increment the global clean znode counter as well. It is OK that
|
||||
* global and per-FS clean znode counters may be inconsistent for some
|
||||
* short time (because we might be preempted at this point), the global
|
||||
* one is only used in shrinker.
|
||||
*/
|
||||
atomic_long_inc(&ubifs_clean_zn_cnt);
|
||||
|
||||
zbr->znode = znode;
|
||||
znode->parent = parent;
|
||||
znode->time = get_seconds();
|
||||
@ -412,11 +453,22 @@ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
|
||||
{
|
||||
union ubifs_key key1, *key = &zbr->key;
|
||||
int err, type = key_type(c, key);
|
||||
struct ubifs_wbuf *wbuf;
|
||||
|
||||
err = ubifs_read_node(c, node, type, zbr->len, zbr->lnum, zbr->offs);
|
||||
/*
|
||||
* 'zbr' has to point to on-flash node. The node may sit in a bud and
|
||||
* may even be in a write buffer, so we have to take care about this.
|
||||
*/
|
||||
wbuf = ubifs_get_wbuf(c, zbr->lnum);
|
||||
if (wbuf)
|
||||
err = ubifs_read_node_wbuf(wbuf, node, type, zbr->len,
|
||||
zbr->lnum, zbr->offs);
|
||||
else
|
||||
err = ubifs_read_node(c, node, type, zbr->len, zbr->lnum,
|
||||
zbr->offs);
|
||||
|
||||
if (err) {
|
||||
dbg_tnc("key %s", DBGKEY(key));
|
||||
dbg_tnck(key, "key ");
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -425,9 +477,9 @@ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
|
||||
if (!keys_eq(c, key, &key1)) {
|
||||
ubifs_err("bad key in node at LEB %d:%d",
|
||||
zbr->lnum, zbr->offs);
|
||||
dbg_tnc("looked for key %s found node's key %s",
|
||||
DBGKEY(key), DBGKEY1(&key1));
|
||||
dbg_dump_node(c, node);
|
||||
dbg_tnck(key, "looked for key ");
|
||||
dbg_tnck(&key1, "but found node's key ");
|
||||
ubifs_dump_node(c, node);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -3,18 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2006-2008 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Authors: Artem Bityutskiy (Битюцкий Артём)
|
||||
* Adrian Hunter
|
||||
@ -135,6 +124,13 @@
|
||||
/* The key is always at the same position in all keyed nodes */
|
||||
#define UBIFS_KEY_OFFSET offsetof(struct ubifs_ino_node, key)
|
||||
|
||||
/* Garbage collector journal head number */
|
||||
#define UBIFS_GC_HEAD 0
|
||||
/* Base journal head number */
|
||||
#define UBIFS_BASE_HEAD 1
|
||||
/* Data journal head number */
|
||||
#define UBIFS_DATA_HEAD 2
|
||||
|
||||
/*
|
||||
* LEB Properties Tree node types.
|
||||
*
|
||||
@ -401,9 +397,11 @@ enum {
|
||||
* Superblock flags.
|
||||
*
|
||||
* UBIFS_FLG_BIGLPT: if "big" LPT model is used if set
|
||||
* UBIFS_FLG_SPACE_FIXUP: first-mount "fixup" of free space within LEBs needed
|
||||
*/
|
||||
enum {
|
||||
UBIFS_FLG_BIGLPT = 0x02,
|
||||
UBIFS_FLG_SPACE_FIXUP = 0x04,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -427,7 +425,7 @@ struct ubifs_ch {
|
||||
__u8 node_type;
|
||||
__u8 group_type;
|
||||
__u8 padding[2];
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* union ubifs_dev_desc - device node descriptor.
|
||||
@ -441,7 +439,7 @@ struct ubifs_ch {
|
||||
union ubifs_dev_desc {
|
||||
__le32 new;
|
||||
__le64 huge;
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubifs_ino_node - inode node.
|
||||
@ -502,7 +500,7 @@ struct ubifs_ino_node {
|
||||
__le16 compr_type;
|
||||
__u8 padding2[26]; /* Watch 'zero_ino_node_unused()' if changing! */
|
||||
__u8 data[];
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubifs_dent_node - directory entry node.
|
||||
@ -526,8 +524,12 @@ struct ubifs_dent_node {
|
||||
__u8 type;
|
||||
__le16 nlen;
|
||||
__u8 padding2[4]; /* Watch 'zero_dent_node_unused()' if changing! */
|
||||
#ifndef __UBOOT__
|
||||
__u8 name[];
|
||||
} __attribute__ ((packed));
|
||||
#else
|
||||
char name[];
|
||||
#endif
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubifs_data_node - data node.
|
||||
@ -548,7 +550,7 @@ struct ubifs_data_node {
|
||||
__le16 compr_type;
|
||||
__u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! */
|
||||
__u8 data[];
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubifs_trun_node - truncation node.
|
||||
@ -568,7 +570,7 @@ struct ubifs_trun_node {
|
||||
__u8 padding[12]; /* Watch 'zero_trun_node_unused()' if changing! */
|
||||
__le64 old_size;
|
||||
__le64 new_size;
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubifs_pad_node - padding node.
|
||||
@ -579,7 +581,7 @@ struct ubifs_trun_node {
|
||||
struct ubifs_pad_node {
|
||||
struct ubifs_ch ch;
|
||||
__le32 pad_len;
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubifs_sb_node - superblock node.
|
||||
@ -637,7 +639,7 @@ struct ubifs_sb_node {
|
||||
__u8 uuid[16];
|
||||
__le32 ro_compat_version;
|
||||
__u8 padding2[3968];
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubifs_mst_node - master node.
|
||||
@ -704,7 +706,7 @@ struct ubifs_mst_node {
|
||||
__le32 idx_lebs;
|
||||
__le32 leb_cnt;
|
||||
__u8 padding[344];
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubifs_ref_node - logical eraseblock reference node.
|
||||
@ -720,7 +722,7 @@ struct ubifs_ref_node {
|
||||
__le32 offs;
|
||||
__le32 jhead;
|
||||
__u8 padding[28];
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubifs_branch - key/reference/length branch
|
||||
@ -733,8 +735,12 @@ struct ubifs_branch {
|
||||
__le32 lnum;
|
||||
__le32 offs;
|
||||
__le32 len;
|
||||
#ifndef __UBOOT__
|
||||
__u8 key[];
|
||||
} __attribute__ ((packed));
|
||||
#else
|
||||
char key[];
|
||||
#endif
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubifs_idx_node - indexing node.
|
||||
@ -747,8 +753,12 @@ struct ubifs_idx_node {
|
||||
struct ubifs_ch ch;
|
||||
__le16 child_cnt;
|
||||
__le16 level;
|
||||
#ifndef __UBOOT__
|
||||
__u8 branches[];
|
||||
} __attribute__ ((packed));
|
||||
#else
|
||||
char branches[];
|
||||
#endif
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubifs_cs_node - commit start node.
|
||||
@ -758,7 +768,7 @@ struct ubifs_idx_node {
|
||||
struct ubifs_cs_node {
|
||||
struct ubifs_ch ch;
|
||||
__le64 cmt_no;
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubifs_orph_node - orphan node.
|
||||
@ -770,6 +780,6 @@ struct ubifs_orph_node {
|
||||
struct ubifs_ch ch;
|
||||
__le64 cmt_no;
|
||||
__le64 inos[];
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
#endif /* __UBIFS_MEDIA_H__ */
|
||||
|
115
fs/ubifs/ubifs.c
115
fs/ubifs/ubifs.c
@ -26,6 +26,10 @@
|
||||
#include "ubifs.h"
|
||||
#include <u-boot/zlib.h>
|
||||
|
||||
#define __UBOOT__
|
||||
#include <linux/err.h>
|
||||
#include <linux/lzo.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* compress.c */
|
||||
@ -44,20 +48,27 @@ static int gzip_decompress(const unsigned char *in, size_t in_len,
|
||||
/* Fake description object for the "none" compressor */
|
||||
static struct ubifs_compressor none_compr = {
|
||||
.compr_type = UBIFS_COMPR_NONE,
|
||||
.name = "no compression",
|
||||
.name = "none",
|
||||
.capi_name = "",
|
||||
.decompress = NULL,
|
||||
};
|
||||
|
||||
static struct ubifs_compressor lzo_compr = {
|
||||
.compr_type = UBIFS_COMPR_LZO,
|
||||
.name = "LZO",
|
||||
#ifndef __UBOOT__
|
||||
.comp_mutex = &lzo_mutex,
|
||||
#endif
|
||||
.name = "lzo",
|
||||
.capi_name = "lzo",
|
||||
.decompress = lzo1x_decompress_safe,
|
||||
};
|
||||
|
||||
static struct ubifs_compressor zlib_compr = {
|
||||
.compr_type = UBIFS_COMPR_ZLIB,
|
||||
#ifndef __UBOOT__
|
||||
.comp_mutex = &deflate_mutex,
|
||||
.decomp_mutex = &inflate_mutex,
|
||||
#endif
|
||||
.name = "zlib",
|
||||
.capi_name = "deflate",
|
||||
.decompress = gzip_decompress,
|
||||
@ -66,6 +77,82 @@ static struct ubifs_compressor zlib_compr = {
|
||||
/* All UBIFS compressors */
|
||||
struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
|
||||
|
||||
|
||||
#ifdef __UBOOT__
|
||||
/* from mm/util.c */
|
||||
|
||||
/**
|
||||
* kmemdup - duplicate region of memory
|
||||
*
|
||||
* @src: memory region to duplicate
|
||||
* @len: memory region length
|
||||
* @gfp: GFP mask to use
|
||||
*/
|
||||
void *kmemdup(const void *src, size_t len, gfp_t gfp)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = kmalloc(len, gfp);
|
||||
if (p)
|
||||
memcpy(p, src, len);
|
||||
return p;
|
||||
}
|
||||
|
||||
struct crypto_comp {
|
||||
int compressor;
|
||||
};
|
||||
|
||||
static inline struct crypto_comp *crypto_alloc_comp(const char *alg_name,
|
||||
u32 type, u32 mask)
|
||||
{
|
||||
struct ubifs_compressor *comp;
|
||||
struct crypto_comp *ptr;
|
||||
int i = 0;
|
||||
|
||||
ptr = malloc(sizeof(struct crypto_comp));
|
||||
while (i < UBIFS_COMPR_TYPES_CNT) {
|
||||
comp = ubifs_compressors[i];
|
||||
if (!comp) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (strncmp(alg_name, comp->capi_name, strlen(alg_name)) == 0) {
|
||||
ptr->compressor = i;
|
||||
return ptr;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (i >= UBIFS_COMPR_TYPES_CNT) {
|
||||
ubifs_err("invalid compression type %s", alg_name);
|
||||
free (ptr);
|
||||
return NULL;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
static inline int crypto_comp_decompress(struct crypto_comp *tfm,
|
||||
const u8 *src, unsigned int slen,
|
||||
u8 *dst, unsigned int *dlen)
|
||||
{
|
||||
struct ubifs_compressor *compr = ubifs_compressors[tfm->compressor];
|
||||
int err;
|
||||
|
||||
if (compr->compr_type == UBIFS_COMPR_NONE) {
|
||||
memcpy(dst, src, slen);
|
||||
*dlen = slen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = compr->decompress(src, slen, dst, (size_t *)dlen);
|
||||
if (err)
|
||||
ubifs_err("cannot decompress %d bytes, compressor %s, "
|
||||
"error %d", slen, compr->name, err);
|
||||
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ubifs_decompress - decompress data.
|
||||
* @in_buf: data to decompress
|
||||
@ -102,10 +189,15 @@ int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = compr->decompress(in_buf, in_len, out_buf, (size_t *)out_len);
|
||||
if (compr->decomp_mutex)
|
||||
mutex_lock(compr->decomp_mutex);
|
||||
err = crypto_comp_decompress(compr->cc, in_buf, in_len, out_buf,
|
||||
(unsigned int *)out_len);
|
||||
if (compr->decomp_mutex)
|
||||
mutex_unlock(compr->decomp_mutex);
|
||||
if (err)
|
||||
ubifs_err("cannot decompress %d bytes, compressor %s, "
|
||||
"error %d", in_len, compr->name, err);
|
||||
ubifs_err("cannot decompress %d bytes, compressor %s, error %d",
|
||||
in_len, compr->name, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -127,6 +219,15 @@ static int __init compr_init(struct ubifs_compressor *compr)
|
||||
ubifs_compressors[compr->compr_type]->decompress += gd->reloc_off;
|
||||
#endif
|
||||
|
||||
if (compr->capi_name) {
|
||||
compr->cc = crypto_alloc_comp(compr->capi_name, 0, 0);
|
||||
if (IS_ERR(compr->cc)) {
|
||||
ubifs_err("cannot initialize compressor %s, error %ld",
|
||||
compr->name, PTR_ERR(compr->cc));
|
||||
return PTR_ERR(compr->cc);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -188,7 +289,9 @@ static int filldir(struct ubifs_info *c, const char *name, int namlen,
|
||||
}
|
||||
ctime_r((time_t *)&inode->i_mtime, filetime);
|
||||
printf("%9lld %24.24s ", inode->i_size, filetime);
|
||||
#ifndef __UBOOT__
|
||||
ubifs_iput(inode);
|
||||
#endif
|
||||
|
||||
printf("%s\n", name);
|
||||
|
||||
@ -562,7 +665,7 @@ static int read_block(struct inode *inode, void *addr, unsigned int block,
|
||||
dump:
|
||||
ubifs_err("bad data node (block %u, inode %lu)",
|
||||
block, inode->i_ino);
|
||||
dbg_dump_node(c, dn);
|
||||
ubifs_dump_node(c, dn);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
740
fs/ubifs/ubifs.h
740
fs/ubifs/ubifs.h
File diff suppressed because it is too large
Load Diff
@ -4,13 +4,14 @@
|
||||
* NAND family Bad Block Management (BBM) header file
|
||||
* - Bad Block Table (BBT) implementation
|
||||
*
|
||||
* Copyright (c) 2005-2007 Samsung Electronics
|
||||
* Copyright © 2005 Samsung Electronics
|
||||
* Kyungmin Park <kyungmin.park@samsung.com>
|
||||
*
|
||||
* Copyright (c) 2000-2005
|
||||
* Copyright © 2000-2005
|
||||
* Thomas Gleixner <tglx@linuxtronix.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
*/
|
||||
#ifndef __LINUX_MTD_BBM_H
|
||||
#define __LINUX_MTD_BBM_H
|
||||
@ -22,22 +23,21 @@
|
||||
|
||||
/**
|
||||
* struct nand_bbt_descr - bad block table descriptor
|
||||
* @param options options for this descriptor
|
||||
* @param pages the page(s) where we find the bbt, used with
|
||||
* option BBT_ABSPAGE when bbt is searched,
|
||||
* then we store the found bbts pages here.
|
||||
* Its an array and supports up to 8 chips now
|
||||
* @param offs offset of the pattern in the oob area of the page
|
||||
* @param veroffs offset of the bbt version counter in the oob are of the page
|
||||
* @param version version read from the bbt page during scan
|
||||
* @param len length of the pattern, if 0 no pattern check is performed
|
||||
* @param maxblocks maximum number of blocks to search for a bbt. This number of
|
||||
* blocks is reserved at the end of the device
|
||||
* where the tables are written.
|
||||
* @param reserved_block_code if non-0, this pattern denotes a reserved
|
||||
* (rather than bad) block in the stored bbt
|
||||
* @param pattern pattern to identify bad block table or factory marked
|
||||
* good / bad blocks, can be NULL, if len = 0
|
||||
* @options: options for this descriptor
|
||||
* @pages: the page(s) where we find the bbt, used with option BBT_ABSPAGE
|
||||
* when bbt is searched, then we store the found bbts pages here.
|
||||
* Its an array and supports up to 8 chips now
|
||||
* @offs: offset of the pattern in the oob area of the page
|
||||
* @veroffs: offset of the bbt version counter in the oob are of the page
|
||||
* @version: version read from the bbt page during scan
|
||||
* @len: length of the pattern, if 0 no pattern check is performed
|
||||
* @maxblocks: maximum number of blocks to search for a bbt. This number of
|
||||
* blocks is reserved at the end of the device where the tables are
|
||||
* written.
|
||||
* @reserved_block_code: if non-0, this pattern denotes a reserved (rather than
|
||||
* bad) block in the stored bbt
|
||||
* @pattern: pattern to identify bad block table or factory marked good /
|
||||
* bad blocks, can be NULL, if len = 0
|
||||
*
|
||||
* Descriptor for the bad block table marker and the descriptor for the
|
||||
* pattern which identifies good and bad blocks. The assumption is made
|
||||
@ -81,10 +81,6 @@ struct nand_bbt_descr {
|
||||
* with NAND_BBT_CREATE.
|
||||
*/
|
||||
#define NAND_BBT_CREATE_EMPTY 0x00000400
|
||||
/* Search good / bad pattern through all pages of a block */
|
||||
#define NAND_BBT_SCANALLPAGES 0x00000800
|
||||
/* Scan block empty during good / bad block scan */
|
||||
#define NAND_BBT_SCANEMPTY 0x00001000
|
||||
/* Write bbt if neccecary */
|
||||
#define NAND_BBT_WRITE 0x00002000
|
||||
/* Read and write back block contents when writing bbt */
|
||||
@ -122,22 +118,27 @@ struct nand_bbt_descr {
|
||||
/*
|
||||
* Constants for oob configuration
|
||||
*/
|
||||
#define ONENAND_BADBLOCK_POS 0
|
||||
#define NAND_SMALL_BADBLOCK_POS 5
|
||||
#define NAND_LARGE_BADBLOCK_POS 0
|
||||
#define ONENAND_BADBLOCK_POS 0
|
||||
|
||||
/*
|
||||
* Bad block scanning errors
|
||||
*/
|
||||
#define ONENAND_BBT_READ_ERROR 1
|
||||
#define ONENAND_BBT_READ_ECC_ERROR 2
|
||||
#define ONENAND_BBT_READ_FATAL_ERROR 4
|
||||
#define ONENAND_BBT_READ_ERROR 1
|
||||
#define ONENAND_BBT_READ_ECC_ERROR 2
|
||||
#define ONENAND_BBT_READ_FATAL_ERROR 4
|
||||
|
||||
/**
|
||||
* struct bbt_info - [GENERIC] Bad Block Table data structure
|
||||
* @param bbt_erase_shift [INTERN] number of address bits in a bbt entry
|
||||
* @param badblockpos [INTERN] position of the bad block marker in the oob area
|
||||
* @param bbt [INTERN] bad block table pointer
|
||||
* @param badblock_pattern [REPLACEABLE] bad block scan pattern used for initial bad block scan
|
||||
* @param priv [OPTIONAL] pointer to private bbm date
|
||||
* struct bbm_info - [GENERIC] Bad Block Table data structure
|
||||
* @bbt_erase_shift: [INTERN] number of address bits in a bbt entry
|
||||
* @badblockpos: [INTERN] position of the bad block marker in the oob area
|
||||
* @options: options for this descriptor
|
||||
* @bbt: [INTERN] bad block table pointer
|
||||
* @isbad_bbt: function to determine if a block is bad
|
||||
* @badblock_pattern: [REPLACEABLE] bad block scan pattern used for
|
||||
* initial bad block scan
|
||||
* @priv: [OPTIONAL] pointer to private bbm date
|
||||
*/
|
||||
struct bbm_info {
|
||||
int bbt_erase_shift;
|
||||
@ -146,7 +147,7 @@ struct bbm_info {
|
||||
|
||||
uint8_t *bbt;
|
||||
|
||||
int (*isbad_bbt) (struct mtd_info * mtd, loff_t ofs, int allowbbt);
|
||||
int (*isbad_bbt)(struct mtd_info *mtd, loff_t ofs, int allowbbt);
|
||||
|
||||
/* TODO Add more NAND specific fileds */
|
||||
struct nand_bbt_descr *badblock_pattern;
|
||||
@ -155,7 +156,7 @@ struct bbm_info {
|
||||
};
|
||||
|
||||
/* OneNAND BBT interface */
|
||||
extern int onenand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd);
|
||||
extern int onenand_default_bbt (struct mtd_info *mtd);
|
||||
extern int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
|
||||
extern int onenand_default_bbt(struct mtd_info *mtd);
|
||||
|
||||
#endif /* __LINUX_MTD_BBM_H */
|
||||
#endif /* __LINUX_MTD_BBM_H */
|
||||
|
@ -12,7 +12,11 @@
|
||||
struct mtd_info *mtd_concat_create(
|
||||
struct mtd_info *subdev[], /* subdevices to concatenate */
|
||||
int num_devs, /* number of subdevices */
|
||||
#ifndef __UBOOT__
|
||||
const char *name); /* name for the new device */
|
||||
#else
|
||||
char *name); /* name for the new device */
|
||||
#endif
|
||||
|
||||
void mtd_concat_destroy(struct mtd_info *mtd);
|
||||
|
||||
|
105
include/linux/mtd/flashchip.h
Normal file
105
include/linux/mtd/flashchip.h
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright © 2000 Red Hat UK Limited
|
||||
* Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MTD_FLASHCHIP_H__
|
||||
#define __MTD_FLASHCHIP_H__
|
||||
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
/* For spinlocks. sched.h includes spinlock.h from whichever directory it
|
||||
* happens to be in - so we don't have to care whether we're on 2.2, which
|
||||
* has asm/spinlock.h, or 2.4, which has linux/spinlock.h
|
||||
*/
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mutex.h>
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
FL_READY,
|
||||
FL_STATUS,
|
||||
FL_CFI_QUERY,
|
||||
FL_JEDEC_QUERY,
|
||||
FL_ERASING,
|
||||
FL_ERASE_SUSPENDING,
|
||||
FL_ERASE_SUSPENDED,
|
||||
FL_WRITING,
|
||||
FL_WRITING_TO_BUFFER,
|
||||
FL_OTP_WRITE,
|
||||
FL_WRITE_SUSPENDING,
|
||||
FL_WRITE_SUSPENDED,
|
||||
FL_PM_SUSPENDED,
|
||||
FL_SYNCING,
|
||||
FL_UNLOADING,
|
||||
FL_LOCKING,
|
||||
FL_UNLOCKING,
|
||||
FL_POINT,
|
||||
FL_XIP_WHILE_ERASING,
|
||||
FL_XIP_WHILE_WRITING,
|
||||
FL_SHUTDOWN,
|
||||
/* These 2 come from nand_state_t, which has been unified here */
|
||||
FL_READING,
|
||||
FL_CACHEDPRG,
|
||||
/* These 4 come from onenand_state_t, which has been unified here */
|
||||
FL_RESETING,
|
||||
FL_OTPING,
|
||||
FL_PREPARING_ERASE,
|
||||
FL_VERIFYING_ERASE,
|
||||
|
||||
FL_UNKNOWN
|
||||
} flstate_t;
|
||||
|
||||
|
||||
|
||||
/* NOTE: confusingly, this can be used to refer to more than one chip at a time,
|
||||
if they're interleaved. This can even refer to individual partitions on
|
||||
the same physical chip when present. */
|
||||
|
||||
struct flchip {
|
||||
unsigned long start; /* Offset within the map */
|
||||
// unsigned long len;
|
||||
/* We omit len for now, because when we group them together
|
||||
we insist that they're all of the same size, and the chip size
|
||||
is held in the next level up. If we get more versatile later,
|
||||
it'll make it a damn sight harder to find which chip we want from
|
||||
a given offset, and we'll want to add the per-chip length field
|
||||
back in.
|
||||
*/
|
||||
int ref_point_counter;
|
||||
flstate_t state;
|
||||
flstate_t oldstate;
|
||||
|
||||
unsigned int write_suspended:1;
|
||||
unsigned int erase_suspended:1;
|
||||
unsigned long in_progress_block_addr;
|
||||
|
||||
struct mutex mutex;
|
||||
#ifndef __UBOOT__
|
||||
wait_queue_head_t wq; /* Wait on here when we're waiting for the chip
|
||||
to be ready */
|
||||
#endif
|
||||
int word_write_time;
|
||||
int buffer_write_time;
|
||||
int erase_time;
|
||||
|
||||
int word_write_time_max;
|
||||
int buffer_write_time_max;
|
||||
int erase_time_max;
|
||||
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/* This is used to handle contention on write/erase operations
|
||||
between partitions of the same physical chip. */
|
||||
struct flchip_shared {
|
||||
struct mutex lock;
|
||||
struct flchip *writing;
|
||||
struct flchip *erasing;
|
||||
};
|
||||
|
||||
|
||||
#endif /* __MTD_FLASHCHIP_H__ */
|
@ -1,48 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
|
||||
* Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org> et al.
|
||||
*
|
||||
* Released under GPL
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MTD_MTD_H__
|
||||
#define __MTD_MTD_H__
|
||||
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/types.h>
|
||||
#include <div64.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <mtd/mtd-abi.h>
|
||||
|
||||
#include <asm/div64.h>
|
||||
#else
|
||||
#include <linux/compat.h>
|
||||
#include <mtd/mtd-abi.h>
|
||||
#include <asm/errno.h>
|
||||
#include <div64.h>
|
||||
|
||||
#define MTD_CHAR_MAJOR 90
|
||||
#define MTD_BLOCK_MAJOR 31
|
||||
#define MAX_MTD_DEVICES 32
|
||||
#endif
|
||||
|
||||
#define MTD_ERASE_PENDING 0x01
|
||||
#define MTD_ERASING 0x02
|
||||
#define MTD_ERASE_SUSPEND 0x04
|
||||
#define MTD_ERASE_DONE 0x08
|
||||
#define MTD_ERASE_FAILED 0x10
|
||||
#define MTD_ERASE_DONE 0x08
|
||||
#define MTD_ERASE_FAILED 0x10
|
||||
|
||||
#define MTD_FAIL_ADDR_UNKNOWN -1LL
|
||||
#define MTD_FAIL_ADDR_UNKNOWN -1LL
|
||||
|
||||
/*
|
||||
* Enumeration for NAND/OneNAND flash chip state
|
||||
* If the erase fails, fail_addr might indicate exactly which block failed. If
|
||||
* fail_addr = MTD_FAIL_ADDR_UNKNOWN, the failure was not at the device level
|
||||
* or was not specific to any particular block.
|
||||
*/
|
||||
enum {
|
||||
FL_READY,
|
||||
FL_READING,
|
||||
FL_WRITING,
|
||||
FL_ERASING,
|
||||
FL_SYNCING,
|
||||
FL_CACHEDPRG,
|
||||
FL_RESETING,
|
||||
FL_UNLOCKING,
|
||||
FL_LOCKING,
|
||||
FL_PM_SUSPENDED,
|
||||
};
|
||||
|
||||
/* If the erase fails, fail_addr might indicate exactly which block failed. If
|
||||
fail_addr = MTD_FAIL_ADDR_UNKNOWN, the failure was not at the device level or was not
|
||||
specific to any particular block. */
|
||||
struct erase_info {
|
||||
struct mtd_info *mtd;
|
||||
uint64_t addr;
|
||||
@ -50,8 +47,8 @@ struct erase_info {
|
||||
uint64_t fail_addr;
|
||||
u_long time;
|
||||
u_long retries;
|
||||
u_int dev;
|
||||
u_int cell;
|
||||
unsigned dev;
|
||||
unsigned cell;
|
||||
void (*callback) (struct erase_info *self);
|
||||
u_long priv;
|
||||
u_char state;
|
||||
@ -60,9 +57,9 @@ struct erase_info {
|
||||
};
|
||||
|
||||
struct mtd_erase_region_info {
|
||||
uint64_t offset; /* At which this region starts, from the beginning of the MTD */
|
||||
u_int32_t erasesize; /* For this region */
|
||||
u_int32_t numblocks; /* Number of blocks of erasesize in this region */
|
||||
uint64_t offset; /* At which this region starts, from the beginning of the MTD */
|
||||
uint32_t erasesize; /* For this region */
|
||||
uint32_t numblocks; /* Number of blocks of erasesize in this region */
|
||||
unsigned long *lockmap; /* If keeping bitmap of locks */
|
||||
};
|
||||
|
||||
@ -81,7 +78,7 @@ struct mtd_erase_region_info {
|
||||
* @datbuf: data buffer - if NULL only oob data are read/written
|
||||
* @oobbuf: oob data buffer
|
||||
*
|
||||
* Note, it is allowed to read more then one OOB area at one go, but not write.
|
||||
* Note, it is allowed to read more than one OOB area at one go, but not write.
|
||||
* The interface assumes that the OOB write requests program only one page's
|
||||
* OOB area.
|
||||
*/
|
||||
@ -109,26 +106,30 @@ struct mtd_oob_ops {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ECC layout control structure. Exported to userspace for
|
||||
* diagnosis and to allow creation of raw images
|
||||
* Internal ECC layout control structure. For historical reasons, there is a
|
||||
* similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained
|
||||
* for export to user-space via the ECCGETLAYOUT ioctl.
|
||||
* nand_ecclayout should be expandable in the future simply by the above macros.
|
||||
*/
|
||||
struct nand_ecclayout {
|
||||
uint32_t eccbytes;
|
||||
uint32_t eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE];
|
||||
uint32_t oobavail;
|
||||
__u32 eccbytes;
|
||||
__u32 eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE];
|
||||
__u32 oobavail;
|
||||
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE];
|
||||
};
|
||||
|
||||
struct module; /* only needed for owner field in mtd_info */
|
||||
|
||||
struct mtd_info {
|
||||
u_char type;
|
||||
u_int32_t flags;
|
||||
uint64_t size; /* Total size of the MTD */
|
||||
uint32_t flags;
|
||||
uint64_t size; // Total size of the MTD
|
||||
|
||||
/* "Major" erase size for the device. Naïve users may take this
|
||||
* to be the only erase size available, or may use the more detailed
|
||||
* information below if they desire
|
||||
*/
|
||||
u_int32_t erasesize;
|
||||
uint32_t erasesize;
|
||||
/* Minimal writable flash unit size. In case of NOR flash it is 1 (even
|
||||
* though individual bits can be cleared), in case of NAND flash it is
|
||||
* one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
|
||||
@ -136,10 +137,31 @@ struct mtd_info {
|
||||
* Any driver registering a struct mtd_info must ensure a writesize of
|
||||
* 1 or larger.
|
||||
*/
|
||||
u_int32_t writesize;
|
||||
uint32_t writesize;
|
||||
|
||||
u_int32_t oobsize; /* Amount of OOB data per block (e.g. 16) */
|
||||
u_int32_t oobavail; /* Available OOB bytes per block */
|
||||
/*
|
||||
* Size of the write buffer used by the MTD. MTD devices having a write
|
||||
* buffer can write multiple writesize chunks at a time. E.g. while
|
||||
* writing 4 * writesize bytes to a device with 2 * writesize bytes
|
||||
* buffer the MTD driver can (but doesn't have to) do 2 writesize
|
||||
* operations, but not 4. Currently, all NANDs have writebufsize
|
||||
* equivalent to writesize (NAND page size). Some NOR flashes do have
|
||||
* writebufsize greater than writesize.
|
||||
*/
|
||||
uint32_t writebufsize;
|
||||
|
||||
uint32_t oobsize; // Amount of OOB data per block (e.g. 16)
|
||||
uint32_t oobavail; // Available OOB bytes per block
|
||||
|
||||
/*
|
||||
* If erasesize is a power of 2 then the shift is stored in
|
||||
* erasesize_shift otherwise erasesize_shift is zero. Ditto writesize.
|
||||
*/
|
||||
unsigned int erasesize_shift;
|
||||
unsigned int writesize_shift;
|
||||
/* Masks based on erasesize_shift and writesize_shift */
|
||||
unsigned int erasesize_mask;
|
||||
unsigned int writesize_mask;
|
||||
|
||||
/*
|
||||
* read ops return -EUCLEAN if max number of bitflips corrected on any
|
||||
@ -150,13 +172,20 @@ struct mtd_info {
|
||||
*/
|
||||
unsigned int bitflip_threshold;
|
||||
|
||||
/* Kernel-only stuff starts here. */
|
||||
// Kernel-only stuff starts here.
|
||||
#ifndef __UBOOT__
|
||||
const char *name;
|
||||
#else
|
||||
char *name;
|
||||
#endif
|
||||
int index;
|
||||
|
||||
/* ECC layout structure pointer - read only! */
|
||||
struct nand_ecclayout *ecclayout;
|
||||
|
||||
/* the ecc step size. */
|
||||
unsigned int ecc_step_size;
|
||||
|
||||
/* max number of correctible bit errors per ecc step */
|
||||
unsigned int ecc_strength;
|
||||
|
||||
@ -171,44 +200,51 @@ struct mtd_info {
|
||||
* wrappers instead.
|
||||
*/
|
||||
int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
|
||||
#ifndef __UBOOT__
|
||||
int (*_point) (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, void **virt, phys_addr_t *phys);
|
||||
void (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
|
||||
size_t *retlen, void **virt, resource_size_t *phys);
|
||||
int (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
|
||||
#endif
|
||||
unsigned long (*_get_unmapped_area) (struct mtd_info *mtd,
|
||||
unsigned long len,
|
||||
unsigned long offset,
|
||||
unsigned long flags);
|
||||
int (*_read) (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf);
|
||||
size_t *retlen, u_char *buf);
|
||||
int (*_write) (struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf);
|
||||
|
||||
/* In blackbox flight recorder like scenarios we want to make successful
|
||||
writes in interrupt context. panic_write() is only intended to be
|
||||
called when its known the kernel is about to panic and we need the
|
||||
write to succeed. Since the kernel is not going to be running for much
|
||||
longer, this function can break locks and delay to ensure the write
|
||||
succeeds (but not sleep). */
|
||||
|
||||
int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
|
||||
|
||||
size_t *retlen, const u_char *buf);
|
||||
int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf);
|
||||
int (*_read_oob) (struct mtd_info *mtd, loff_t from,
|
||||
struct mtd_oob_ops *ops);
|
||||
struct mtd_oob_ops *ops);
|
||||
int (*_write_oob) (struct mtd_info *mtd, loff_t to,
|
||||
struct mtd_oob_ops *ops);
|
||||
struct mtd_oob_ops *ops);
|
||||
int (*_get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
|
||||
size_t len);
|
||||
size_t len);
|
||||
int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from,
|
||||
size_t len, size_t *retlen, u_char *buf);
|
||||
size_t len, size_t *retlen, u_char *buf);
|
||||
int (*_get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
|
||||
size_t len);
|
||||
size_t len);
|
||||
int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from,
|
||||
size_t len, size_t *retlen, u_char *buf);
|
||||
int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, u_char *buf);
|
||||
size_t len, size_t *retlen, u_char *buf);
|
||||
int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to,
|
||||
size_t len, size_t *retlen, u_char *buf);
|
||||
int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,
|
||||
size_t len);
|
||||
size_t len);
|
||||
#ifndef __UBOOT__
|
||||
int (*_writev) (struct mtd_info *mtd, const struct kvec *vecs,
|
||||
unsigned long count, loff_t to, size_t *retlen);
|
||||
#endif
|
||||
void (*_sync) (struct mtd_info *mtd);
|
||||
int (*_lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||
int (*_unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||
int (*_is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||
int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
|
||||
int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
|
||||
#ifndef __UBOOT__
|
||||
int (*_suspend) (struct mtd_info *mtd);
|
||||
void (*_resume) (struct mtd_info *mtd);
|
||||
#endif
|
||||
/*
|
||||
* If the driver is something smart, like UBI, it may need to maintain
|
||||
* its own reference counting. The below functions are only for driver.
|
||||
@ -216,16 +252,12 @@ struct mtd_info {
|
||||
int (*_get_device) (struct mtd_info *mtd);
|
||||
void (*_put_device) (struct mtd_info *mtd);
|
||||
|
||||
/* XXX U-BOOT XXX */
|
||||
#if 0
|
||||
/* kvec-based read/write methods.
|
||||
NB: The 'count' parameter is the number of _vectors_, each of
|
||||
which contains an (ofs, len) tuple.
|
||||
*/
|
||||
int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
|
||||
#endif
|
||||
/* XXX U-BOOT XXX */
|
||||
#if 0
|
||||
#ifndef __UBOOT__
|
||||
/* Backing device capabilities for this device
|
||||
* - provides mmap capabilities
|
||||
*/
|
||||
struct backing_dev_info *backing_dev_info;
|
||||
|
||||
struct notifier_block reboot_notifier; /* default mode before reboot */
|
||||
#endif
|
||||
|
||||
@ -237,10 +269,20 @@ struct mtd_info {
|
||||
void *priv;
|
||||
|
||||
struct module *owner;
|
||||
#ifndef __UBOOT__
|
||||
struct device dev;
|
||||
#endif
|
||||
int usecount;
|
||||
};
|
||||
|
||||
int mtd_erase(struct mtd_info *mtd, struct erase_info *instr);
|
||||
#ifndef __UBOOT__
|
||||
int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
|
||||
void **virt, resource_size_t *phys);
|
||||
int mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len);
|
||||
#endif
|
||||
unsigned long mtd_get_unmapped_area(struct mtd_info *mtd, unsigned long len,
|
||||
unsigned long offset, unsigned long flags);
|
||||
int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
|
||||
u_char *buf);
|
||||
int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
|
||||
@ -273,8 +315,7 @@ int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, u_char *buf);
|
||||
int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len);
|
||||
|
||||
/* XXX U-BOOT XXX */
|
||||
#if 0
|
||||
#ifndef __UBOOT__
|
||||
int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
|
||||
unsigned long count, loff_t to, size_t *retlen);
|
||||
#endif
|
||||
@ -291,22 +332,59 @@ int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||
int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs);
|
||||
int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs);
|
||||
|
||||
#ifndef __UBOOT__
|
||||
static inline int mtd_suspend(struct mtd_info *mtd)
|
||||
{
|
||||
return mtd->_suspend ? mtd->_suspend(mtd) : 0;
|
||||
}
|
||||
|
||||
static inline void mtd_resume(struct mtd_info *mtd)
|
||||
{
|
||||
if (mtd->_resume)
|
||||
mtd->_resume(mtd);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd)
|
||||
{
|
||||
if (mtd->erasesize_shift)
|
||||
return sz >> mtd->erasesize_shift;
|
||||
do_div(sz, mtd->erasesize);
|
||||
return sz;
|
||||
}
|
||||
|
||||
static inline uint32_t mtd_mod_by_eb(uint64_t sz, struct mtd_info *mtd)
|
||||
{
|
||||
if (mtd->erasesize_shift)
|
||||
return sz & mtd->erasesize_mask;
|
||||
return do_div(sz, mtd->erasesize);
|
||||
}
|
||||
|
||||
static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd)
|
||||
{
|
||||
if (mtd->writesize_shift)
|
||||
return sz >> mtd->writesize_shift;
|
||||
do_div(sz, mtd->writesize);
|
||||
return sz;
|
||||
}
|
||||
|
||||
static inline uint32_t mtd_mod_by_ws(uint64_t sz, struct mtd_info *mtd)
|
||||
{
|
||||
if (mtd->writesize_shift)
|
||||
return sz & mtd->writesize_mask;
|
||||
return do_div(sz, mtd->writesize);
|
||||
}
|
||||
|
||||
static inline int mtd_has_oob(const struct mtd_info *mtd)
|
||||
{
|
||||
return mtd->_read_oob && mtd->_write_oob;
|
||||
}
|
||||
|
||||
static inline int mtd_type_is_nand(const struct mtd_info *mtd)
|
||||
{
|
||||
return mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH;
|
||||
}
|
||||
|
||||
static inline int mtd_can_have_bb(const struct mtd_info *mtd)
|
||||
{
|
||||
return !!mtd->_block_isbad;
|
||||
@ -314,27 +392,36 @@ static inline int mtd_can_have_bb(const struct mtd_info *mtd)
|
||||
|
||||
/* Kernel-side ioctl definitions */
|
||||
|
||||
extern int add_mtd_device(struct mtd_info *mtd);
|
||||
extern int del_mtd_device (struct mtd_info *mtd);
|
||||
struct mtd_partition;
|
||||
struct mtd_part_parser_data;
|
||||
|
||||
extern int mtd_device_parse_register(struct mtd_info *mtd,
|
||||
const char * const *part_probe_types,
|
||||
struct mtd_part_parser_data *parser_data,
|
||||
const struct mtd_partition *defparts,
|
||||
int defnr_parts);
|
||||
#define mtd_device_register(master, parts, nr_parts) \
|
||||
mtd_device_parse_register(master, NULL, NULL, parts, nr_parts)
|
||||
extern int mtd_device_unregister(struct mtd_info *master);
|
||||
extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
|
||||
extern int __get_mtd_device(struct mtd_info *mtd);
|
||||
extern void __put_mtd_device(struct mtd_info *mtd);
|
||||
extern struct mtd_info *get_mtd_device_nm(const char *name);
|
||||
|
||||
extern void put_mtd_device(struct mtd_info *mtd);
|
||||
extern void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset,
|
||||
const uint64_t length, uint64_t *len_incl_bad,
|
||||
int *truncated);
|
||||
/* XXX U-BOOT XXX */
|
||||
#if 0
|
||||
|
||||
|
||||
#ifndef __UBOOT__
|
||||
struct mtd_notifier {
|
||||
void (*add)(struct mtd_info *mtd);
|
||||
void (*remove)(struct mtd_info *mtd);
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
|
||||
extern void register_mtd_user (struct mtd_notifier *new);
|
||||
extern int unregister_mtd_user (struct mtd_notifier *old);
|
||||
#endif
|
||||
void *mtd_kmalloc_up_to(const struct mtd_info *mtd, size_t *size);
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
void mtd_erase_callback(struct erase_info *instr);
|
||||
@ -346,6 +433,7 @@ static inline void mtd_erase_callback(struct erase_info *instr)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __UBOOT__
|
||||
/*
|
||||
* Debugging macro and defines
|
||||
*/
|
||||
@ -372,7 +460,11 @@ static inline void mtd_erase_callback(struct erase_info *instr)
|
||||
#define pr_info(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args)
|
||||
#define pr_warn(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args)
|
||||
#define pr_err(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args)
|
||||
|
||||
#define pr_crit(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args)
|
||||
#define pr_cont(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args)
|
||||
#define pr_notice(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args)
|
||||
#endif
|
||||
|
||||
static inline int mtd_is_bitflip(int err) {
|
||||
return err == -EUCLEAN;
|
||||
}
|
||||
@ -385,4 +477,10 @@ static inline int mtd_is_bitflip_or_eccerr(int err) {
|
||||
return mtd_is_bitflip(err) || mtd_is_eccerr(err);
|
||||
}
|
||||
|
||||
#ifdef __UBOOT__
|
||||
/* drivers/mtd/mtdcore.h */
|
||||
int add_mtd_device(struct mtd_info *mtd);
|
||||
int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
|
||||
int del_mtd_partitions(struct mtd_info *);
|
||||
#endif
|
||||
#endif /* __MTD_MTD_H__ */
|
||||
|
@ -5,9 +5,7 @@
|
||||
* Steven J. Hill <sjhill@realitydiluted.com>
|
||||
* Thomas Gleixner <tglx@linutronix.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Info:
|
||||
* Contains standard defines and IDs for NAND flash devices
|
||||
@ -18,21 +16,32 @@
|
||||
#ifndef __LINUX_MTD_NAND_H
|
||||
#define __LINUX_MTD_NAND_H
|
||||
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/wait.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/flashchip.h>
|
||||
#include <linux/mtd/bbm.h>
|
||||
#else
|
||||
#include "config.h"
|
||||
|
||||
#include "linux/compat.h"
|
||||
#include "linux/mtd/mtd.h"
|
||||
#include "linux/mtd/flashchip.h"
|
||||
#include "linux/mtd/bbm.h"
|
||||
|
||||
#endif
|
||||
|
||||
struct mtd_info;
|
||||
struct nand_flash_dev;
|
||||
/* Scan and identify a NAND device */
|
||||
extern int nand_scan (struct mtd_info *mtd, int max_chips);
|
||||
/* Separate phases of nand_scan(), allowing board driver to intervene
|
||||
* and override command or ECC setup according to flash type */
|
||||
extern int nand_scan(struct mtd_info *mtd, int max_chips);
|
||||
/*
|
||||
* Separate phases of nand_scan(), allowing board driver to intervene
|
||||
* and override command or ECC setup according to flash type.
|
||||
*/
|
||||
extern int nand_scan_ident(struct mtd_info *mtd, int max_chips,
|
||||
const struct nand_flash_dev *table);
|
||||
struct nand_flash_dev *table);
|
||||
extern int nand_scan_tail(struct mtd_info *mtd);
|
||||
|
||||
/* Free resources held by the NAND device */
|
||||
@ -41,12 +50,23 @@ extern void nand_release(struct mtd_info *mtd);
|
||||
/* Internal helper for board drivers which need to override command function */
|
||||
extern void nand_wait_ready(struct mtd_info *mtd);
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/* locks all blocks present in the device */
|
||||
extern int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||
|
||||
/* unlocks specified locked blocks */
|
||||
extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||
|
||||
/* The maximum number of NAND chips in an array */
|
||||
#define NAND_MAX_CHIPS 8
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This constant declares the max. oobsize / page, which
|
||||
* is supported now. If you add a chip with bigger oobsize/page
|
||||
* adjust this accordingly.
|
||||
*/
|
||||
#define NAND_MAX_OOBSIZE 640
|
||||
#define NAND_MAX_OOBSIZE 744
|
||||
#define NAND_MAX_PAGESIZE 8192
|
||||
|
||||
/*
|
||||
@ -76,7 +96,6 @@ extern void nand_wait_ready(struct mtd_info *mtd);
|
||||
#define NAND_CMD_READOOB 0x50
|
||||
#define NAND_CMD_ERASE1 0x60
|
||||
#define NAND_CMD_STATUS 0x70
|
||||
#define NAND_CMD_STATUS_MULTI 0x71
|
||||
#define NAND_CMD_SEQIN 0x80
|
||||
#define NAND_CMD_RNDIN 0x85
|
||||
#define NAND_CMD_READID 0x90
|
||||
@ -87,10 +106,8 @@ extern void nand_wait_ready(struct mtd_info *mtd);
|
||||
#define NAND_CMD_RESET 0xff
|
||||
|
||||
#define NAND_CMD_LOCK 0x2a
|
||||
#define NAND_CMD_LOCK_TIGHT 0x2c
|
||||
#define NAND_CMD_UNLOCK1 0x23
|
||||
#define NAND_CMD_UNLOCK2 0x24
|
||||
#define NAND_CMD_LOCK_STATUS 0x7a
|
||||
|
||||
/* Extended commands for large page devices */
|
||||
#define NAND_CMD_READSTART 0x30
|
||||
@ -164,21 +181,12 @@ typedef enum {
|
||||
/* Chip has copy back function */
|
||||
#define NAND_COPYBACK 0x00000010
|
||||
/*
|
||||
* AND Chip which has 4 banks and a confusing page / block
|
||||
* assignment. See Renesas datasheet for further information.
|
||||
* Chip requires ready check on read (for auto-incremented sequential read).
|
||||
* True only for small page devices; large page devices do not support
|
||||
* autoincrement.
|
||||
*/
|
||||
#define NAND_IS_AND 0x00000020
|
||||
/*
|
||||
* Chip has a array of 4 pages which can be read without
|
||||
* additional ready /busy waits.
|
||||
*/
|
||||
#define NAND_4PAGE_ARRAY 0x00000040
|
||||
/*
|
||||
* Chip requires that BBT is periodically rewritten to prevent
|
||||
* bits from adjacent blocks from 'leaking' in altering data.
|
||||
* This happens with the Renesas AG-AND chips, possibly others.
|
||||
*/
|
||||
#define BBT_AUTO_REFRESH 0x00000080
|
||||
#define NAND_NEED_READRDY 0x00000100
|
||||
|
||||
/* Chip does not allow subpage writes */
|
||||
#define NAND_NO_SUBPAGE_WRITE 0x00000200
|
||||
|
||||
@ -189,16 +197,13 @@ typedef enum {
|
||||
#define NAND_ROM 0x00000800
|
||||
|
||||
/* Device supports subpage reads */
|
||||
#define NAND_SUBPAGE_READ 0x00001000
|
||||
#define NAND_SUBPAGE_READ 0x00001000
|
||||
|
||||
/* Options valid for Samsung large page devices */
|
||||
#define NAND_SAMSUNG_LP_OPTIONS \
|
||||
(NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
|
||||
#define NAND_SAMSUNG_LP_OPTIONS NAND_CACHEPRG
|
||||
|
||||
/* Macros to identify the above */
|
||||
#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
|
||||
#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
|
||||
#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
|
||||
#define NAND_HAS_SUBPAGE_READ(chip) ((chip->options & NAND_SUBPAGE_READ))
|
||||
|
||||
/* Non chip related options */
|
||||
@ -211,6 +216,13 @@ typedef enum {
|
||||
#define NAND_OWN_BUFFERS 0x00020000
|
||||
/* Chip may not exist, so silence any errors in scan */
|
||||
#define NAND_SCAN_SILENT_NODEV 0x00040000
|
||||
/*
|
||||
* Autodetect nand buswidth with readid/onfi.
|
||||
* This suppose the driver will configure the hardware in 8 bits mode
|
||||
* when calling nand_scan_ident, and update its configuration
|
||||
* before calling nand_scan_tail.
|
||||
*/
|
||||
#define NAND_BUSWIDTH_AUTO 0x00080000
|
||||
|
||||
/* Options set by nand scan */
|
||||
/* bbt has already been read */
|
||||
@ -221,10 +233,15 @@ typedef enum {
|
||||
/* Cell info constants */
|
||||
#define NAND_CI_CHIPNR_MSK 0x03
|
||||
#define NAND_CI_CELLTYPE_MSK 0x0C
|
||||
#define NAND_CI_CELLTYPE_SHIFT 2
|
||||
|
||||
/* Keep gcc happy */
|
||||
struct nand_chip;
|
||||
|
||||
/* ONFI features */
|
||||
#define ONFI_FEATURE_16_BIT_BUS (1 << 0)
|
||||
#define ONFI_FEATURE_EXT_PARAM_PAGE (1 << 7)
|
||||
|
||||
/* ONFI timing mode, used in both asynchronous and synchronous mode */
|
||||
#define ONFI_TIMING_MODE_0 (1 << 0)
|
||||
#define ONFI_TIMING_MODE_1 (1 << 1)
|
||||
@ -237,9 +254,15 @@ struct nand_chip;
|
||||
/* ONFI feature address */
|
||||
#define ONFI_FEATURE_ADDR_TIMING_MODE 0x1
|
||||
|
||||
/* Vendor-specific feature address (Micron) */
|
||||
#define ONFI_FEATURE_ADDR_READ_RETRY 0x89
|
||||
|
||||
/* ONFI subfeature parameters length */
|
||||
#define ONFI_SUBFEATURE_PARAM_LEN 4
|
||||
|
||||
/* ONFI optional commands SET/GET FEATURES supported? */
|
||||
#define ONFI_OPT_CMD_SET_GET_FEATURES (1 << 2)
|
||||
|
||||
struct nand_onfi_params {
|
||||
/* rev info and features block */
|
||||
/* 'O' 'N' 'F' 'I' */
|
||||
@ -247,7 +270,10 @@ struct nand_onfi_params {
|
||||
__le16 revision;
|
||||
__le16 features;
|
||||
__le16 opt_cmd;
|
||||
u8 reserved[22];
|
||||
u8 reserved0[2];
|
||||
__le16 ext_param_page_length; /* since ONFI 2.1 */
|
||||
u8 num_of_param_pages; /* since ONFI 2.1 */
|
||||
u8 reserved1[17];
|
||||
|
||||
/* manufacturer information block */
|
||||
char manufacturer[12];
|
||||
@ -291,19 +317,74 @@ struct nand_onfi_params {
|
||||
__le16 io_pin_capacitance_typ;
|
||||
__le16 input_pin_capacitance_typ;
|
||||
u8 input_pin_capacitance_max;
|
||||
u8 driver_strenght_support;
|
||||
u8 driver_strength_support;
|
||||
__le16 t_int_r;
|
||||
__le16 t_ald;
|
||||
u8 reserved4[7];
|
||||
|
||||
/* vendor */
|
||||
u8 reserved5[90];
|
||||
__le16 vendor_revision;
|
||||
u8 vendor[88];
|
||||
|
||||
__le16 crc;
|
||||
} __attribute__((packed));
|
||||
} __packed;
|
||||
|
||||
#define ONFI_CRC_BASE 0x4F4E
|
||||
|
||||
/* Extended ECC information Block Definition (since ONFI 2.1) */
|
||||
struct onfi_ext_ecc_info {
|
||||
u8 ecc_bits;
|
||||
u8 codeword_size;
|
||||
__le16 bb_per_lun;
|
||||
__le16 block_endurance;
|
||||
u8 reserved[2];
|
||||
} __packed;
|
||||
|
||||
#define ONFI_SECTION_TYPE_0 0 /* Unused section. */
|
||||
#define ONFI_SECTION_TYPE_1 1 /* for additional sections. */
|
||||
#define ONFI_SECTION_TYPE_2 2 /* for ECC information. */
|
||||
struct onfi_ext_section {
|
||||
u8 type;
|
||||
u8 length;
|
||||
} __packed;
|
||||
|
||||
#define ONFI_EXT_SECTION_MAX 8
|
||||
|
||||
/* Extended Parameter Page Definition (since ONFI 2.1) */
|
||||
struct onfi_ext_param_page {
|
||||
__le16 crc;
|
||||
u8 sig[4]; /* 'E' 'P' 'P' 'S' */
|
||||
u8 reserved0[10];
|
||||
struct onfi_ext_section sections[ONFI_EXT_SECTION_MAX];
|
||||
|
||||
/*
|
||||
* The actual size of the Extended Parameter Page is in
|
||||
* @ext_param_page_length of nand_onfi_params{}.
|
||||
* The following are the variable length sections.
|
||||
* So we do not add any fields below. Please see the ONFI spec.
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct nand_onfi_vendor_micron {
|
||||
u8 two_plane_read;
|
||||
u8 read_cache;
|
||||
u8 read_unique_id;
|
||||
u8 dq_imped;
|
||||
u8 dq_imped_num_settings;
|
||||
u8 dq_imped_feat_addr;
|
||||
u8 rb_pulldown_strength;
|
||||
u8 rb_pulldown_strength_feat_addr;
|
||||
u8 rb_pulldown_strength_num_settings;
|
||||
u8 otp_mode;
|
||||
u8 otp_page_start;
|
||||
u8 otp_data_prot_addr;
|
||||
u8 otp_num_pages;
|
||||
u8 otp_feat_addr;
|
||||
u8 read_retry_options;
|
||||
u8 reserved[72];
|
||||
u8 param_revision;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
|
||||
* @lock: protection lock
|
||||
@ -313,12 +394,11 @@ struct nand_onfi_params {
|
||||
* when a hw controller is available.
|
||||
*/
|
||||
struct nand_hw_control {
|
||||
/* XXX U-BOOT XXX */
|
||||
#if 0
|
||||
spinlock_t lock;
|
||||
spinlock_t lock;
|
||||
struct nand_chip *active;
|
||||
#ifndef __UBOOT__
|
||||
wait_queue_head_t wq;
|
||||
#endif
|
||||
struct nand_chip *active;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -344,6 +424,7 @@ struct nand_hw_control {
|
||||
* any single ECC step, 0 if bitflips uncorrectable, -EIO hw error
|
||||
* @read_subpage: function to read parts of the page covered by ECC;
|
||||
* returns same as read_page()
|
||||
* @write_subpage: function to write parts of the page covered by ECC.
|
||||
* @write_page: function to write a page according to the ECC generator
|
||||
* requirements.
|
||||
* @write_oob_raw: function to write chip OOB data without ECC
|
||||
@ -375,6 +456,9 @@ struct nand_ecc_ctrl {
|
||||
uint8_t *buf, int oob_required, int page);
|
||||
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offs, uint32_t len, uint8_t *buf);
|
||||
int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offset, uint32_t data_len,
|
||||
const uint8_t *data_buf, int oob_required);
|
||||
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required);
|
||||
int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
@ -396,10 +480,16 @@ struct nand_ecc_ctrl {
|
||||
* consecutive order.
|
||||
*/
|
||||
struct nand_buffers {
|
||||
#ifndef __UBOOT__
|
||||
uint8_t *ecccalc;
|
||||
uint8_t *ecccode;
|
||||
uint8_t *databuf;
|
||||
#else
|
||||
uint8_t ecccalc[ALIGN(NAND_MAX_OOBSIZE, ARCH_DMA_MINALIGN)];
|
||||
uint8_t ecccode[ALIGN(NAND_MAX_OOBSIZE, ARCH_DMA_MINALIGN)];
|
||||
uint8_t databuf[ALIGN(NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE,
|
||||
ARCH_DMA_MINALIGN)];
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
@ -410,13 +500,13 @@ struct nand_buffers {
|
||||
* flash device.
|
||||
* @read_byte: [REPLACEABLE] read one byte from the chip
|
||||
* @read_word: [REPLACEABLE] read one word from the chip
|
||||
* @write_byte: [REPLACEABLE] write a single byte to the chip on the
|
||||
* low 8 I/O lines
|
||||
* @write_buf: [REPLACEABLE] write data from the buffer to the chip
|
||||
* @read_buf: [REPLACEABLE] read data from the chip into the buffer
|
||||
* @verify_buf: [REPLACEABLE] verify buffer contents against the chip
|
||||
* data.
|
||||
* @select_chip: [REPLACEABLE] select chip nr
|
||||
* @block_bad: [REPLACEABLE] check, if the block is bad
|
||||
* @block_markbad: [REPLACEABLE] mark the block bad
|
||||
* @block_bad: [REPLACEABLE] check if a block is bad, using OOB markers
|
||||
* @block_markbad: [REPLACEABLE] mark a block bad
|
||||
* @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling
|
||||
* ALE/CLE/nCE. Also used to write command and address
|
||||
* @init_size: [BOARDSPECIFIC] hardwarespecific function for setting
|
||||
@ -431,6 +521,8 @@ struct nand_buffers {
|
||||
* commands to the chip.
|
||||
* @waitfunc: [REPLACEABLE] hardwarespecific function for wait on
|
||||
* ready.
|
||||
* @setup_read_retry: [FLASHSPECIFIC] flash (vendor) specific function for
|
||||
* setting the read-retry mode. Mostly needed for MLC NAND.
|
||||
* @ecc: [BOARDSPECIFIC] ECC control structure
|
||||
* @buffers: buffer structure for read/write
|
||||
* @hwcontrol: platform-specific hardware control structure
|
||||
@ -458,7 +550,13 @@ struct nand_buffers {
|
||||
* @badblockbits: [INTERN] minimum number of set bits in a good block's
|
||||
* bad block marker position; i.e., BBM == 11110111b is
|
||||
* not bad when badblockbits == 7
|
||||
* @cellinfo: [INTERN] MLC/multichip data from chip ident
|
||||
* @bits_per_cell: [INTERN] number of bits per cell. i.e., 1 means SLC.
|
||||
* @ecc_strength_ds: [INTERN] ECC correctability from the datasheet.
|
||||
* Minimum amount of bit errors per @ecc_step_ds guaranteed
|
||||
* to be correctable. If unknown, set to zero.
|
||||
* @ecc_step_ds: [INTERN] ECC step required by the @ecc_strength_ds,
|
||||
* also from the datasheet. It is the recommended ECC step
|
||||
* size, if known; if unknown, set to zero.
|
||||
* @numchips: [INTERN] number of physical chips
|
||||
* @chipsize: [INTERN] the size of one chip for multichip arrays
|
||||
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
|
||||
@ -471,9 +569,9 @@ struct nand_buffers {
|
||||
* non 0 if ONFI supported.
|
||||
* @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is
|
||||
* supported, 0 otherwise.
|
||||
* @onfi_set_features [REPLACEABLE] set the features for ONFI nand
|
||||
* @onfi_get_features [REPLACEABLE] get the features for ONFI nand
|
||||
* @ecclayout: [REPLACEABLE] the default ECC placement scheme
|
||||
* @read_retries: [INTERN] the number of read retry modes supported
|
||||
* @onfi_set_features: [REPLACEABLE] set the features for ONFI nand
|
||||
* @onfi_get_features: [REPLACEABLE] get the features for ONFI nand
|
||||
* @bbt: [INTERN] bad block table pointer
|
||||
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash
|
||||
* lookup.
|
||||
@ -496,9 +594,14 @@ struct nand_chip {
|
||||
|
||||
uint8_t (*read_byte)(struct mtd_info *mtd);
|
||||
u16 (*read_word)(struct mtd_info *mtd);
|
||||
void (*write_byte)(struct mtd_info *mtd, uint8_t byte);
|
||||
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
|
||||
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
#ifdef __UBOOT__
|
||||
#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
|
||||
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
#endif
|
||||
#endif
|
||||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
|
||||
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
|
||||
@ -514,12 +617,13 @@ struct nand_chip {
|
||||
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
|
||||
int status, int page);
|
||||
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required, int page,
|
||||
int cached, int raw);
|
||||
uint32_t offset, int data_len, const uint8_t *buf,
|
||||
int oob_required, int page, int cached, int raw);
|
||||
int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int feature_addr, uint8_t *subfeature_para);
|
||||
int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int feature_addr, uint8_t *subfeature_para);
|
||||
int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
|
||||
|
||||
int chip_delay;
|
||||
unsigned int options;
|
||||
@ -535,20 +639,26 @@ struct nand_chip {
|
||||
int pagebuf;
|
||||
unsigned int pagebuf_bitflips;
|
||||
int subpagesize;
|
||||
uint8_t cellinfo;
|
||||
uint8_t bits_per_cell;
|
||||
uint16_t ecc_strength_ds;
|
||||
uint16_t ecc_step_ds;
|
||||
int badblockpos;
|
||||
int badblockbits;
|
||||
|
||||
int onfi_version;
|
||||
#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
|
||||
struct nand_onfi_params onfi_params;
|
||||
struct nand_onfi_params onfi_params;
|
||||
#endif
|
||||
|
||||
int state;
|
||||
int read_retries;
|
||||
|
||||
flstate_t state;
|
||||
|
||||
uint8_t *oob_poi;
|
||||
struct nand_hw_control *controller;
|
||||
#ifdef __UBOOT__
|
||||
struct nand_ecclayout *ecclayout;
|
||||
#endif
|
||||
|
||||
struct nand_ecc_ctrl ecc;
|
||||
struct nand_buffers *buffers;
|
||||
@ -577,26 +687,83 @@ struct nand_chip {
|
||||
#define NAND_MFR_AMD 0x01
|
||||
#define NAND_MFR_MACRONIX 0xc2
|
||||
#define NAND_MFR_EON 0x92
|
||||
#define NAND_MFR_SANDISK 0x45
|
||||
#define NAND_MFR_INTEL 0x89
|
||||
|
||||
/* The maximum expected count of bytes in the NAND ID sequence */
|
||||
#define NAND_MAX_ID_LEN 8
|
||||
|
||||
/*
|
||||
* A helper for defining older NAND chips where the second ID byte fully
|
||||
* defined the chip, including the geometry (chip size, eraseblock size, page
|
||||
* size). All these chips have 512 bytes NAND page size.
|
||||
*/
|
||||
#define LEGACY_ID_NAND(nm, devid, chipsz, erasesz, opts) \
|
||||
{ .name = (nm), {{ .dev_id = (devid) }}, .pagesize = 512, \
|
||||
.chipsize = (chipsz), .erasesize = (erasesz), .options = (opts) }
|
||||
|
||||
/*
|
||||
* A helper for defining newer chips which report their page size and
|
||||
* eraseblock size via the extended ID bytes.
|
||||
*
|
||||
* The real difference between LEGACY_ID_NAND and EXTENDED_ID_NAND is that with
|
||||
* EXTENDED_ID_NAND, manufacturers overloaded the same device ID so that the
|
||||
* device ID now only represented a particular total chip size (and voltage,
|
||||
* buswidth), and the page size, eraseblock size, and OOB size could vary while
|
||||
* using the same device ID.
|
||||
*/
|
||||
#define EXTENDED_ID_NAND(nm, devid, chipsz, opts) \
|
||||
{ .name = (nm), {{ .dev_id = (devid) }}, .chipsize = (chipsz), \
|
||||
.options = (opts) }
|
||||
|
||||
#define NAND_ECC_INFO(_strength, _step) \
|
||||
{ .strength_ds = (_strength), .step_ds = (_step) }
|
||||
#define NAND_ECC_STRENGTH(type) ((type)->ecc.strength_ds)
|
||||
#define NAND_ECC_STEP(type) ((type)->ecc.step_ds)
|
||||
|
||||
/**
|
||||
* struct nand_flash_dev - NAND Flash Device ID Structure
|
||||
* @name: Identify the device type
|
||||
* @id: device ID code
|
||||
* @pagesize: Pagesize in bytes. Either 256 or 512 or 0
|
||||
* If the pagesize is 0, then the real pagesize
|
||||
* and the eraseize are determined from the
|
||||
* extended id bytes in the chip
|
||||
* @erasesize: Size of an erase block in the flash device.
|
||||
* @chipsize: Total chipsize in Mega Bytes
|
||||
* @options: Bitfield to store chip relevant options
|
||||
* @name: a human-readable name of the NAND chip
|
||||
* @dev_id: the device ID (the second byte of the full chip ID array)
|
||||
* @mfr_id: manufecturer ID part of the full chip ID array (refers the same
|
||||
* memory address as @id[0])
|
||||
* @dev_id: device ID part of the full chip ID array (refers the same memory
|
||||
* address as @id[1])
|
||||
* @id: full device ID array
|
||||
* @pagesize: size of the NAND page in bytes; if 0, then the real page size (as
|
||||
* well as the eraseblock size) is determined from the extended NAND
|
||||
* chip ID array)
|
||||
* @chipsize: total chip size in MiB
|
||||
* @erasesize: eraseblock size in bytes (determined from the extended ID if 0)
|
||||
* @options: stores various chip bit options
|
||||
* @id_len: The valid length of the @id.
|
||||
* @oobsize: OOB size
|
||||
* @ecc.strength_ds: The ECC correctability from the datasheet, same as the
|
||||
* @ecc_strength_ds in nand_chip{}.
|
||||
* @ecc.step_ds: The ECC step required by the @ecc.strength_ds, same as the
|
||||
* @ecc_step_ds in nand_chip{}, also from the datasheet.
|
||||
* For example, the "4bit ECC for each 512Byte" can be set with
|
||||
* NAND_ECC_INFO(4, 512).
|
||||
*/
|
||||
struct nand_flash_dev {
|
||||
char *name;
|
||||
int id;
|
||||
unsigned long pagesize;
|
||||
unsigned long chipsize;
|
||||
unsigned long erasesize;
|
||||
unsigned long options;
|
||||
union {
|
||||
struct {
|
||||
uint8_t mfr_id;
|
||||
uint8_t dev_id;
|
||||
};
|
||||
uint8_t id[NAND_MAX_ID_LEN];
|
||||
};
|
||||
unsigned int pagesize;
|
||||
unsigned int chipsize;
|
||||
unsigned int erasesize;
|
||||
unsigned int options;
|
||||
uint16_t id_len;
|
||||
uint16_t oobsize;
|
||||
struct {
|
||||
uint16_t strength_ds;
|
||||
uint16_t step_ds;
|
||||
} ecc;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -609,23 +776,25 @@ struct nand_manufacturers {
|
||||
char *name;
|
||||
};
|
||||
|
||||
extern const struct nand_flash_dev nand_flash_ids[];
|
||||
extern const struct nand_manufacturers nand_manuf_ids[];
|
||||
extern struct nand_flash_dev nand_flash_ids[];
|
||||
extern struct nand_manufacturers nand_manuf_ids[];
|
||||
|
||||
extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
|
||||
extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs);
|
||||
extern int nand_default_bbt(struct mtd_info *mtd);
|
||||
extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs);
|
||||
extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);
|
||||
extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
||||
int allowbbt);
|
||||
extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, uint8_t *buf);
|
||||
|
||||
#ifdef __UBOOT__
|
||||
/*
|
||||
* Constants for oob configuration
|
||||
*/
|
||||
#define NAND_SMALL_BADBLOCK_POS 5
|
||||
#define NAND_LARGE_BADBLOCK_POS 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct platform_nand_chip - chip level device structure
|
||||
@ -656,20 +825,29 @@ struct platform_device;
|
||||
|
||||
/**
|
||||
* struct platform_nand_ctrl - controller level device structure
|
||||
* @probe: platform specific function to probe/setup hardware
|
||||
* @remove: platform specific function to remove/teardown hardware
|
||||
* @hwcontrol: platform specific hardware control structure
|
||||
* @dev_ready: platform specific function to read ready/busy pin
|
||||
* @select_chip: platform specific chip select function
|
||||
* @cmd_ctrl: platform specific function for controlling
|
||||
* ALE/CLE/nCE. Also used to write command and address
|
||||
* @write_buf: platform specific function for write buffer
|
||||
* @read_buf: platform specific function for read buffer
|
||||
* @read_byte: platform specific function to read one byte from chip
|
||||
* @priv: private data to transport driver specific settings
|
||||
*
|
||||
* All fields are optional and depend on the hardware driver requirements
|
||||
*/
|
||||
struct platform_nand_ctrl {
|
||||
int (*probe)(struct platform_device *pdev);
|
||||
void (*remove)(struct platform_device *pdev);
|
||||
void (*hwcontrol)(struct mtd_info *mtd, int cmd);
|
||||
int (*dev_ready)(struct mtd_info *mtd);
|
||||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
|
||||
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
|
||||
unsigned char (*read_byte)(struct mtd_info *mtd);
|
||||
void *priv;
|
||||
};
|
||||
@ -693,16 +871,14 @@ struct platform_nand_chip *get_platform_nandchip(struct mtd_info *mtd)
|
||||
return chip->priv;
|
||||
}
|
||||
|
||||
/* Standard NAND functions from nand_base.c */
|
||||
void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len);
|
||||
void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len);
|
||||
uint8_t nand_read_byte(struct mtd_info *mtd);
|
||||
#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
|
||||
/* return the supported features. */
|
||||
static inline int onfi_feature(struct nand_chip *chip)
|
||||
{
|
||||
return chip->onfi_version ? le16_to_cpu(chip->onfi_params.features) : 0;
|
||||
}
|
||||
|
||||
/* return the supported asynchronous timing mode. */
|
||||
|
||||
#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
|
||||
static inline int onfi_get_async_timing_mode(struct nand_chip *chip)
|
||||
{
|
||||
if (!chip->onfi_version)
|
||||
@ -719,6 +895,16 @@ static inline int onfi_get_sync_timing_mode(struct nand_chip *chip)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check if it is a SLC nand.
|
||||
* The !nand_is_slc() can be used to check the MLC/TLC nand chips.
|
||||
* We do not distinguish the MLC and TLC now.
|
||||
*/
|
||||
static inline bool nand_is_slc(struct nand_chip *chip)
|
||||
{
|
||||
return chip->bits_per_cell == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the opcode's address should be sent only on the lower 8 bits
|
||||
* @command: opcode to check
|
||||
@ -737,5 +923,12 @@ static inline int nand_opcode_8bits(unsigned int command)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __UBOOT__
|
||||
/* Standard NAND functions from nand_base.c */
|
||||
void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len);
|
||||
void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len);
|
||||
uint8_t nand_read_byte(struct mtd_info *mtd);
|
||||
#endif
|
||||
#endif /* __LINUX_MTD_NAND_H */
|
||||
|
@ -1,11 +1,9 @@
|
||||
/*
|
||||
* MTD partitioning layer definitions
|
||||
*
|
||||
* (C) 2000 Nicolas Pitre <nico@cam.org>
|
||||
* (C) 2000 Nicolas Pitre <nico@fluxnic.net>
|
||||
*
|
||||
* This code is GPL
|
||||
*
|
||||
* $Id: partitions.h,v 1.17 2005/11/07 11:14:55 gleixner Exp $
|
||||
*/
|
||||
|
||||
#ifndef MTD_PARTITIONS_H
|
||||
@ -18,7 +16,7 @@
|
||||
* Partition definition structure:
|
||||
*
|
||||
* An array of struct partition is passed along with a MTD object to
|
||||
* add_mtd_partitions() to create them.
|
||||
* mtd_device_register() to create them.
|
||||
*
|
||||
* For each partition, these fields are available:
|
||||
* name: string that will be used to label the partition's MTD device.
|
||||
@ -26,7 +24,9 @@
|
||||
* will extend to the end of the master MTD device.
|
||||
* offset: absolute starting position within the master MTD device; if
|
||||
* defined as MTDPART_OFS_APPEND, the partition will start where the
|
||||
* previous one ended; if MTDPART_OFS_NXTBLK, at the next erase block.
|
||||
* previous one ended; if MTDPART_OFS_NXTBLK, at the next erase block;
|
||||
* if MTDPART_OFS_RETAIN, consume as much as possible, leaving size
|
||||
* after the end of partition.
|
||||
* mask_flags: contains flags that have to be masked (removed) from the
|
||||
* master MTD flag set for the corresponding MTD partition.
|
||||
* For example, to force a read-only partition, simply adding
|
||||
@ -37,23 +37,34 @@
|
||||
*/
|
||||
|
||||
struct mtd_partition {
|
||||
char *name; /* identifier string */
|
||||
const char *name; /* identifier string */
|
||||
uint64_t size; /* partition size */
|
||||
uint64_t offset; /* offset within the master MTD space */
|
||||
u_int32_t mask_flags; /* master MTD flags to mask out for this partition */
|
||||
struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/
|
||||
struct mtd_info **mtdp; /* pointer to store the MTD object */
|
||||
uint32_t mask_flags; /* master MTD flags to mask out for this partition */
|
||||
struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only) */
|
||||
};
|
||||
|
||||
#define MTDPART_OFS_RETAIN (-3)
|
||||
#define MTDPART_OFS_NXTBLK (-2)
|
||||
#define MTDPART_OFS_APPEND (-1)
|
||||
#define MTDPART_SIZ_FULL (0)
|
||||
|
||||
|
||||
int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
|
||||
int del_mtd_partitions(struct mtd_info *);
|
||||
struct mtd_info;
|
||||
struct device_node;
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* struct mtd_part_parser_data - used to pass data to MTD partition parsers.
|
||||
* @origin: for RedBoot, start address of MTD device
|
||||
* @of_node: for OF parsers, device node containing partitioning information
|
||||
*/
|
||||
struct mtd_part_parser_data {
|
||||
unsigned long origin;
|
||||
struct device_node *of_node;
|
||||
};
|
||||
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Functions dealing with the various ways of partitioning the space
|
||||
*/
|
||||
@ -62,23 +73,18 @@ struct mtd_part_parser {
|
||||
struct list_head list;
|
||||
struct module *owner;
|
||||
const char *name;
|
||||
int (*parse_fn)(struct mtd_info *, struct mtd_partition **, unsigned long);
|
||||
int (*parse_fn)(struct mtd_info *, struct mtd_partition **,
|
||||
struct mtd_part_parser_data *);
|
||||
};
|
||||
|
||||
extern int register_mtd_parser(struct mtd_part_parser *parser);
|
||||
extern int deregister_mtd_parser(struct mtd_part_parser *parser);
|
||||
extern int parse_mtd_partitions(struct mtd_info *master, const char **types,
|
||||
struct mtd_partition **pparts, unsigned long origin);
|
||||
|
||||
#define put_partition_parser(p) do { module_put((p)->owner); } while(0)
|
||||
|
||||
struct device;
|
||||
struct device_node;
|
||||
|
||||
int __devinit of_mtd_parse_partitions(struct device *dev,
|
||||
struct mtd_info *mtd,
|
||||
struct device_node *node,
|
||||
struct mtd_partition **pparts);
|
||||
extern void register_mtd_parser(struct mtd_part_parser *parser);
|
||||
extern void deregister_mtd_parser(struct mtd_part_parser *parser);
|
||||
#endif
|
||||
|
||||
int mtd_is_partition(const struct mtd_info *mtd);
|
||||
int mtd_add_partition(struct mtd_info *master, const char *name,
|
||||
long long offset, long long length);
|
||||
int mtd_del_partition(struct mtd_info *master, int partno);
|
||||
uint64_t mtd_get_device_size(const struct mtd_info *mtd);
|
||||
|
||||
#endif
|
||||
|
@ -9,9 +9,15 @@
|
||||
#ifndef __LINUX_UBI_H__
|
||||
#define __LINUX_UBI_H__
|
||||
|
||||
/* #include <asm/ioctl.h> */
|
||||
#include <linux/types.h>
|
||||
#define __UBOOT__
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/ioctl.h>
|
||||
#include <mtd/ubi-user.h>
|
||||
#endif
|
||||
|
||||
/* All voumes/LEBs */
|
||||
#define UBI_ALL -1
|
||||
|
||||
/*
|
||||
* enum ubi_open_mode - UBI volume open mode constants.
|
||||
@ -33,13 +39,13 @@ enum {
|
||||
* @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
|
||||
* data
|
||||
* @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
|
||||
* @corrupted: non-zero if the volume is corrupted (static volumes only)
|
||||
* @upd_marker: non-zero if the volume has update marker set
|
||||
* @alignment: volume alignment
|
||||
* @usable_leb_size: how many bytes are available in logical eraseblocks of
|
||||
* this volume
|
||||
* this volume
|
||||
* @name_len: volume name length
|
||||
* @name: volume name
|
||||
* @cdev: UBI volume character device major and minor numbers
|
||||
@ -75,7 +81,7 @@ enum {
|
||||
* 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),
|
||||
* @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
|
||||
@ -104,20 +110,79 @@ struct ubi_volume_info {
|
||||
* struct ubi_device_info - UBI device description data structure.
|
||||
* @ubi_num: ubi device number
|
||||
* @leb_size: logical eraseblock size on this UBI device
|
||||
* @leb_start: starting offset of logical eraseblocks within physical
|
||||
* eraseblocks
|
||||
* @min_io_size: minimal I/O unit size
|
||||
* @max_write_size: maximum amount of bytes the underlying flash can write at a
|
||||
* time (MTD write buffer 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.
|
||||
*
|
||||
* The @max_write_size field describes flash write maximum write unit. For
|
||||
* example, NOR flash allows for changing individual bytes, so @min_io_size is
|
||||
* %1. However, it does not mean than NOR flash has to write data byte-by-byte.
|
||||
* Instead, CFI NOR flashes have a write-buffer of, e.g., 64 bytes, and when
|
||||
* writing large chunks of data, they write 64-bytes at a time. Obviously, this
|
||||
* improves write throughput.
|
||||
*
|
||||
* Also, the MTD device may have N interleaved (striped) flash chips
|
||||
* underneath, in which case @min_io_size can be physical min. I/O size of
|
||||
* single flash chip, while @max_write_size can be N * @min_io_size.
|
||||
*
|
||||
* The @max_write_size field is always greater or equivalent to @min_io_size.
|
||||
* E.g., some NOR flashes may have (@min_io_size = 1, @max_write_size = 64). In
|
||||
* contrast, NAND flashes usually have @min_io_size = @max_write_size = NAND
|
||||
* page size.
|
||||
*/
|
||||
struct ubi_device_info {
|
||||
int ubi_num;
|
||||
int leb_size;
|
||||
int leb_start;
|
||||
int min_io_size;
|
||||
int max_write_size;
|
||||
int ro_mode;
|
||||
#ifndef __UBOOT__
|
||||
dev_t cdev;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Volume notification types.
|
||||
* @UBI_VOLUME_ADDED: a volume has been added (an UBI device was attached or a
|
||||
* volume was created)
|
||||
* @UBI_VOLUME_REMOVED: a volume has been removed (an UBI device was detached
|
||||
* or a volume was removed)
|
||||
* @UBI_VOLUME_RESIZED: a volume has been re-sized
|
||||
* @UBI_VOLUME_RENAMED: a volume has been re-named
|
||||
* @UBI_VOLUME_UPDATED: data has been written to a volume
|
||||
*
|
||||
* These constants define which type of event has happened when a volume
|
||||
* notification function is invoked.
|
||||
*/
|
||||
enum {
|
||||
UBI_VOLUME_ADDED,
|
||||
UBI_VOLUME_REMOVED,
|
||||
UBI_VOLUME_RESIZED,
|
||||
UBI_VOLUME_RENAMED,
|
||||
UBI_VOLUME_UPDATED,
|
||||
};
|
||||
|
||||
/*
|
||||
* struct ubi_notification - UBI notification description structure.
|
||||
* @di: UBI device description object
|
||||
* @vi: UBI volume description object
|
||||
*
|
||||
* UBI notifiers are called with a pointer to an object of this type. The
|
||||
* object describes the notification. Namely, it provides a description of the
|
||||
* UBI device and UBI volume the notification informs about.
|
||||
*/
|
||||
struct ubi_notification {
|
||||
struct ubi_device_info di;
|
||||
struct ubi_volume_info vi;
|
||||
};
|
||||
|
||||
/* UBI descriptor given to users when they open UBI volumes */
|
||||
@ -129,17 +194,37 @@ void ubi_get_volume_info(struct ubi_volume_desc *desc,
|
||||
struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode);
|
||||
struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
|
||||
int mode);
|
||||
struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode);
|
||||
|
||||
#ifndef __UBOOT__
|
||||
typedef int (*notifier_fn_t)(void *nb,
|
||||
unsigned long action, void *data);
|
||||
|
||||
struct notifier_block {
|
||||
notifier_fn_t notifier_call;
|
||||
struct notifier_block *next;
|
||||
void *next;
|
||||
int priority;
|
||||
};
|
||||
|
||||
int ubi_register_volume_notifier(struct notifier_block *nb,
|
||||
int ignore_existing);
|
||||
int ubi_unregister_volume_notifier(struct notifier_block *nb);
|
||||
#endif
|
||||
|
||||
void ubi_close_volume(struct ubi_volume_desc *desc);
|
||||
int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
|
||||
int len, int check);
|
||||
int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
||||
int offset, int len, int dtype);
|
||||
int offset, int len);
|
||||
int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
||||
int len, int dtype);
|
||||
int len);
|
||||
int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum);
|
||||
int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum);
|
||||
int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype);
|
||||
int ubi_leb_map(struct ubi_volume_desc *desc, int lnum);
|
||||
int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum);
|
||||
int ubi_sync(int ubi_num);
|
||||
int ubi_flush(int ubi_num, int vol_id, int lnum);
|
||||
|
||||
/*
|
||||
* This function is the same as the 'ubi_leb_read()' function, but it does not
|
||||
@ -150,25 +235,4 @@ static inline int ubi_read(struct ubi_volume_desc *desc, int lnum, char *buf,
|
||||
{
|
||||
return ubi_leb_read(desc, lnum, buf, offset, len, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is the same as the 'ubi_leb_write()' functions, but it does
|
||||
* not have the data type argument.
|
||||
*/
|
||||
static inline int ubi_write(struct ubi_volume_desc *desc, int lnum,
|
||||
const void *buf, int offset, int len)
|
||||
{
|
||||
return ubi_leb_write(desc, lnum, buf, offset, len, UBI_UNKNOWN);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is the same as the 'ubi_leb_change()' functions, but it does
|
||||
* not have the data type argument.
|
||||
*/
|
||||
static inline int ubi_change(struct ubi_volume_desc *desc, int lnum,
|
||||
const void *buf, int len)
|
||||
{
|
||||
return ubi_leb_change(desc, lnum, buf, len, UBI_UNKNOWN);
|
||||
}
|
||||
|
||||
#endif /* !__LINUX_UBI_H__ */
|
||||
|
@ -1,30 +1,44 @@
|
||||
/*
|
||||
* $Id: mtd-abi.h,v 1.13 2005/11/07 11:14:56 gleixner Exp $
|
||||
* Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org> et al.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Portions of MTD ABI definition which are shared by kernel and user space
|
||||
*/
|
||||
|
||||
#ifndef __MTD_ABI_H__
|
||||
#define __MTD_ABI_H__
|
||||
|
||||
#if 1
|
||||
#define __UBOOT__
|
||||
#ifdef __UBOOT__
|
||||
#include <linux/compat.h>
|
||||
#endif
|
||||
|
||||
#include <linux/compiler.h>
|
||||
|
||||
struct erase_info_user {
|
||||
uint32_t start;
|
||||
uint32_t length;
|
||||
__u32 start;
|
||||
__u32 length;
|
||||
};
|
||||
|
||||
struct erase_info_user64 {
|
||||
__u64 start;
|
||||
__u64 length;
|
||||
};
|
||||
|
||||
struct mtd_oob_buf {
|
||||
uint32_t start;
|
||||
uint32_t length;
|
||||
__u32 start;
|
||||
__u32 length;
|
||||
unsigned char __user *ptr;
|
||||
};
|
||||
|
||||
/*
|
||||
struct mtd_oob_buf64 {
|
||||
__u64 start;
|
||||
__u32 pad;
|
||||
__u32 length;
|
||||
__u64 usr_ptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* MTD operation modes
|
||||
*
|
||||
* @MTD_OPS_PLACE_OOB: OOB data are placed at the given offset (default)
|
||||
@ -43,18 +57,45 @@ enum {
|
||||
MTD_OPS_RAW = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mtd_write_req - data structure for requesting a write operation
|
||||
*
|
||||
* @start: start address
|
||||
* @len: length of data buffer
|
||||
* @ooblen: length of OOB buffer
|
||||
* @usr_data: user-provided data buffer
|
||||
* @usr_oob: user-provided OOB buffer
|
||||
* @mode: MTD mode (see "MTD operation modes")
|
||||
* @padding: reserved, must be set to 0
|
||||
*
|
||||
* This structure supports ioctl(MEMWRITE) operations, allowing data and/or OOB
|
||||
* writes in various modes. To write to OOB-only, set @usr_data == NULL, and to
|
||||
* write data-only, set @usr_oob == NULL. However, setting both @usr_data and
|
||||
* @usr_oob to NULL is not allowed.
|
||||
*/
|
||||
struct mtd_write_req {
|
||||
__u64 start;
|
||||
__u64 len;
|
||||
__u64 ooblen;
|
||||
__u64 usr_data;
|
||||
__u64 usr_oob;
|
||||
__u8 mode;
|
||||
__u8 padding[7];
|
||||
};
|
||||
|
||||
#define MTD_ABSENT 0
|
||||
#define MTD_RAM 1
|
||||
#define MTD_ROM 2
|
||||
#define MTD_NORFLASH 3
|
||||
#define MTD_NANDFLASH 4
|
||||
#define MTD_NANDFLASH 4 /* SLC NAND */
|
||||
#define MTD_DATAFLASH 6
|
||||
#define MTD_UBIVOLUME 7
|
||||
#define MTD_MLCNANDFLASH 8 /* MLC NAND (including TLC) */
|
||||
|
||||
#define MTD_WRITEABLE 0x400 /* Device is writeable */
|
||||
#define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */
|
||||
#define MTD_NO_ERASE 0x1000 /* No erase necessary */
|
||||
#define MTD_STUPID_LOCK 0x2000 /* Always locked after reset */
|
||||
#define MTD_POWERUP_LOCK 0x2000 /* Always locked after reset */
|
||||
|
||||
/* Some common devices / combinations of capabilities */
|
||||
#define MTD_CAP_ROM 0
|
||||
@ -62,12 +103,12 @@ enum {
|
||||
#define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE)
|
||||
#define MTD_CAP_NANDFLASH (MTD_WRITEABLE)
|
||||
|
||||
/* ECC byte placement */
|
||||
#define MTD_NANDECC_OFF 0 /* Switch off ECC (Not recommended) */
|
||||
#define MTD_NANDECC_PLACE 1 /* Use the given placement in the structure (YAFFS1 legacy mode) */
|
||||
#define MTD_NANDECC_AUTOPLACE 2 /* Use the default placement scheme */
|
||||
#define MTD_NANDECC_PLACEONLY 3 /* Use the given placement in the structure (Do not store ecc result on read) */
|
||||
#define MTD_NANDECC_AUTOPL_USR 4 /* Use the given autoplacement scheme rather than using the default */
|
||||
/* Obsolete ECC byte placement modes (used with obsolete MEMGETOOBSEL) */
|
||||
#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended)
|
||||
#define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode)
|
||||
#define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme
|
||||
#define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read)
|
||||
#define MTD_NANDECC_AUTOPL_USR 4 // Use the given autoplacement scheme rather than using the default
|
||||
|
||||
/* OTP mode selection */
|
||||
#define MTD_OTP_OFF 0
|
||||
@ -75,32 +116,35 @@ enum {
|
||||
#define MTD_OTP_USER 2
|
||||
|
||||
struct mtd_info_user {
|
||||
uint8_t type;
|
||||
uint32_t flags;
|
||||
uint32_t size; /* Total size of the MTD */
|
||||
uint32_t erasesize;
|
||||
uint32_t writesize;
|
||||
uint32_t oobsize; /* Amount of OOB data per block (e.g. 16) */
|
||||
/* The below two fields are obsolete and broken, do not use them
|
||||
* (TODO: remove at some point) */
|
||||
uint32_t ecctype;
|
||||
uint32_t eccsize;
|
||||
__u8 type;
|
||||
__u32 flags;
|
||||
__u32 size; /* Total size of the MTD */
|
||||
__u32 erasesize;
|
||||
__u32 writesize;
|
||||
__u32 oobsize; /* Amount of OOB data per block (e.g. 16) */
|
||||
__u64 padding; /* Old obsolete field; do not use */
|
||||
};
|
||||
|
||||
struct region_info_user {
|
||||
uint32_t offset; /* At which this region starts,
|
||||
* from the beginning of the MTD */
|
||||
uint32_t erasesize; /* For this region */
|
||||
uint32_t numblocks; /* Number of blocks in this region */
|
||||
uint32_t regionindex;
|
||||
__u32 offset; /* At which this region starts,
|
||||
* from the beginning of the MTD */
|
||||
__u32 erasesize; /* For this region */
|
||||
__u32 numblocks; /* Number of blocks in this region */
|
||||
__u32 regionindex;
|
||||
};
|
||||
|
||||
struct otp_info {
|
||||
uint32_t start;
|
||||
uint32_t length;
|
||||
uint32_t locked;
|
||||
__u32 start;
|
||||
__u32 length;
|
||||
__u32 locked;
|
||||
};
|
||||
|
||||
/*
|
||||
* Note, the following ioctl existed in the past and was removed:
|
||||
* #define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo)
|
||||
* Try to avoid adding a new ioctl with the same ioctl number.
|
||||
*/
|
||||
|
||||
/* Get basic MTD characteristics info (better to use sysfs) */
|
||||
#define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
|
||||
/* Erase segment of MTD */
|
||||
@ -118,12 +162,11 @@ struct otp_info {
|
||||
/* Get information about the erase region for a specific index */
|
||||
#define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user)
|
||||
/* Get info about OOB modes (e.g., RAW, PLACE, AUTO) - legacy interface */
|
||||
#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo)
|
||||
#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo)
|
||||
/* Check if an eraseblock is bad */
|
||||
#define MEMGETBADBLOCK _IOW('M', 11, loff_t)
|
||||
#define MEMGETBADBLOCK _IOW('M', 11, __kernel_loff_t)
|
||||
/* Mark an eraseblock as bad */
|
||||
#define MEMSETBADBLOCK _IOW('M', 12, loff_t)
|
||||
#define MEMSETBADBLOCK _IOW('M', 12, __kernel_loff_t)
|
||||
/* Set OTP (One-Time Programmable) mode (factory vs. user) */
|
||||
#define OTPSELECT _IOR('M', 13, int)
|
||||
/* Get number of OTP (One-Time Programmable) regions */
|
||||
@ -133,26 +176,57 @@ struct otp_info {
|
||||
/* Lock a given range of user data (must be in mode %MTD_FILE_MODE_OTP_USER) */
|
||||
#define OTPLOCK _IOR('M', 16, struct otp_info)
|
||||
/* Get ECC layout (deprecated) */
|
||||
#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout)
|
||||
#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout_user)
|
||||
/* Get statistics about corrected/uncorrected errors */
|
||||
#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
|
||||
/* Set MTD mode on a per-file-descriptor basis (see "MTD file modes") */
|
||||
#define MTDFILEMODE _IO('M', 19)
|
||||
/* Erase segment of MTD (supports 64-bit address) */
|
||||
#define MEMERASE64 _IOW('M', 20, struct erase_info_user64)
|
||||
/* Write data to OOB (64-bit version) */
|
||||
#define MEMWRITEOOB64 _IOWR('M', 21, struct mtd_oob_buf64)
|
||||
/* Read data from OOB (64-bit version) */
|
||||
#define MEMREADOOB64 _IOWR('M', 22, struct mtd_oob_buf64)
|
||||
/* Check if chip is locked (for MTD that supports it) */
|
||||
#define MEMISLOCKED _IOR('M', 23, struct erase_info_user)
|
||||
/*
|
||||
* Most generic write interface; can write in-band and/or out-of-band in various
|
||||
* modes (see "struct mtd_write_req"). This ioctl is not supported for flashes
|
||||
* without OOB, e.g., NOR flash.
|
||||
*/
|
||||
#define MEMWRITE _IOWR('M', 24, struct mtd_write_req)
|
||||
|
||||
/*
|
||||
* Obsolete legacy interface. Keep it in order not to break userspace
|
||||
* interfaces
|
||||
*/
|
||||
struct nand_oobinfo {
|
||||
uint32_t useecc;
|
||||
uint32_t eccbytes;
|
||||
uint32_t oobfree[8][2];
|
||||
uint32_t eccpos[48];
|
||||
__u32 useecc;
|
||||
__u32 eccbytes;
|
||||
__u32 oobfree[8][2];
|
||||
__u32 eccpos[32];
|
||||
};
|
||||
|
||||
struct nand_oobfree {
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
__u32 offset;
|
||||
__u32 length;
|
||||
};
|
||||
|
||||
#define MTD_MAX_OOBFREE_ENTRIES 8
|
||||
#define MTD_MAX_ECCPOS_ENTRIES 64
|
||||
/*
|
||||
* OBSOLETE: ECC layout control structure. Exported to user-space via ioctl
|
||||
* ECCGETLAYOUT for backwards compatbility and should not be mistaken as a
|
||||
* complete set of ECC information. The ioctl truncates the larger internal
|
||||
* structure to retain binary compatibility with the static declaration of the
|
||||
* ioctl. Note that the "MTD_MAX_..._ENTRIES" macros represent the max size of
|
||||
* the user struct, not the MAX size of the internal struct nand_ecclayout.
|
||||
*/
|
||||
struct nand_ecclayout_user {
|
||||
__u32 eccbytes;
|
||||
__u32 eccpos[MTD_MAX_ECCPOS_ENTRIES];
|
||||
__u32 oobavail;
|
||||
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -164,10 +238,10 @@ struct nand_oobfree {
|
||||
* @bbtblocks: number of blocks reserved for bad block tables
|
||||
*/
|
||||
struct mtd_ecc_stats {
|
||||
uint32_t corrected;
|
||||
uint32_t failed;
|
||||
uint32_t badblocks;
|
||||
uint32_t bbtblocks;
|
||||
__u32 corrected;
|
||||
__u32 failed;
|
||||
__u32 badblocks;
|
||||
__u32 bbtblocks;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -188,10 +262,15 @@ struct mtd_ecc_stats {
|
||||
* used out of necessity (e.g., `write()', ioctl(MEMWRITEOOB64)).
|
||||
*/
|
||||
enum mtd_file_modes {
|
||||
MTD_MODE_NORMAL = MTD_OTP_OFF,
|
||||
MTD_MODE_OTP_FACTORY = MTD_OTP_FACTORY,
|
||||
MTD_MODE_OTP_USER = MTD_OTP_USER,
|
||||
MTD_MODE_RAW,
|
||||
MTD_FILE_MODE_NORMAL = MTD_OTP_OFF,
|
||||
MTD_FILE_MODE_OTP_FACTORY = MTD_OTP_FACTORY,
|
||||
MTD_FILE_MODE_OTP_USER = MTD_OTP_USER,
|
||||
MTD_FILE_MODE_RAW,
|
||||
};
|
||||
|
||||
static inline int mtd_type_is_nand_user(const struct mtd_info_user *mtd)
|
||||
{
|
||||
return mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH;
|
||||
}
|
||||
|
||||
#endif /* __MTD_ABI_H__ */
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) International Business Machines Corp., 2006
|
||||
* Copyright © International Business Machines Corp., 2006
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Author: Artem Bityutskiy (Битюцкий Артём)
|
||||
*/
|
||||
@ -9,6 +9,8 @@
|
||||
#ifndef __UBI_USER_H__
|
||||
#define __UBI_USER_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* UBI device creation (the same as MTD device attachment)
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -28,30 +30,37 @@
|
||||
* UBI volume creation
|
||||
* ~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* UBI volumes are created via the %UBI_IOCMKVOL IOCTL command of UBI character
|
||||
* 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.
|
||||
* 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
|
||||
* 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.
|
||||
* to the ioctl.
|
||||
*
|
||||
* UBI volume re-size
|
||||
* ~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* To re-size a volume, the %UBI_IOCRSVOL IOCTL command of the UBI character
|
||||
* 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.
|
||||
* filled and a pointer to it has to be passed to the ioctl.
|
||||
*
|
||||
* UBI volumes re-name
|
||||
* ~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* To re-name several volumes atomically at one go, the %UBI_IOCRNVOL command
|
||||
* of the UBI character device should be used. A &struct ubi_rnvol_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
|
||||
* 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 this, UBI expects user to write
|
||||
* size should be passed to the ioctl. After this, 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:
|
||||
@ -61,14 +70,58 @@
|
||||
* write(fd, buf, image_size);
|
||||
* close(fd);
|
||||
*
|
||||
* Atomic eraseblock change
|
||||
* Logical eraseblock erase
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Atomic eraseblock change operation is done via the %UBI_IOCEBCH IOCTL
|
||||
* command of the corresponding UBI volume character device. A pointer to
|
||||
* &struct ubi_leb_change_req has to be passed to the IOCTL. Then the user is
|
||||
* expected to write the requested amount of bytes. This is similar to the
|
||||
* "volume update" IOCTL.
|
||||
* To erase a logical eraseblock, the %UBI_IOCEBER ioctl command of the
|
||||
* corresponding UBI volume character device should be used. This command
|
||||
* unmaps the requested logical eraseblock, makes sure the corresponding
|
||||
* physical eraseblock is successfully erased, and returns.
|
||||
*
|
||||
* Atomic logical eraseblock change
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Atomic logical eraseblock change operation is called using the %UBI_IOCEBCH
|
||||
* ioctl command of the corresponding UBI volume character device. A pointer to
|
||||
* a &struct ubi_leb_change_req object has to be passed to the ioctl. Then the
|
||||
* user is expected to write the requested amount of bytes (similarly to what
|
||||
* should be done in case of the "volume update" ioctl).
|
||||
*
|
||||
* Logical eraseblock map
|
||||
* ~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* To map a logical eraseblock to a physical eraseblock, the %UBI_IOCEBMAP
|
||||
* ioctl command should be used. A pointer to a &struct ubi_map_req object is
|
||||
* expected to be passed. The ioctl maps the requested logical eraseblock to
|
||||
* a physical eraseblock and returns. Only non-mapped logical eraseblocks can
|
||||
* be mapped. If the logical eraseblock specified in the request is already
|
||||
* mapped to a physical eraseblock, the ioctl fails and returns error.
|
||||
*
|
||||
* Logical eraseblock unmap
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* To unmap a logical eraseblock to a physical eraseblock, the %UBI_IOCEBUNMAP
|
||||
* ioctl command should be used. The ioctl unmaps the logical eraseblocks,
|
||||
* schedules corresponding physical eraseblock for erasure, and returns. Unlike
|
||||
* the "LEB erase" command, it does not wait for the physical eraseblock being
|
||||
* erased. Note, the side effect of this is that if an unclean reboot happens
|
||||
* after the unmap ioctl returns, you may find the LEB mapped again to the same
|
||||
* physical eraseblock after the UBI is run again.
|
||||
*
|
||||
* Check if logical eraseblock is mapped
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* To check if a logical eraseblock is mapped to a physical eraseblock, the
|
||||
* %UBI_IOCEBISMAP ioctl command should be used. It returns %0 if the LEB is
|
||||
* not mapped, and %1 if it is mapped.
|
||||
*
|
||||
* Set an UBI volume property
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* To set an UBI volume property the %UBI_IOCSETPROP ioctl command should be
|
||||
* used. A pointer to a &struct ubi_set_vol_prop_req object is expected to be
|
||||
* passed. The object describes which property should be set, and to which value
|
||||
* it should be set.
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -82,56 +135,56 @@
|
||||
/* Maximum volume name length */
|
||||
#define UBI_MAX_VOLUME_NAME 127
|
||||
|
||||
/* IOCTL commands of UBI character devices */
|
||||
/* 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)
|
||||
#define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, __s32)
|
||||
/* Re-size an UBI volume */
|
||||
#define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req)
|
||||
/* Re-name volumes */
|
||||
#define UBI_IOCRNVOL _IOW(UBI_IOC_MAGIC, 3, struct ubi_rnvol_req)
|
||||
|
||||
/* IOCTL commands of the UBI control character device */
|
||||
/* ioctl commands of the UBI control character device */
|
||||
|
||||
#define UBI_CTRL_IOC_MAGIC 'o'
|
||||
|
||||
/* Attach an MTD device */
|
||||
#define UBI_IOCATT _IOW(UBI_CTRL_IOC_MAGIC, 64, struct ubi_attach_req)
|
||||
/* Detach an MTD device */
|
||||
#define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, int32_t)
|
||||
#define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, __s32)
|
||||
|
||||
/* IOCTL commands of UBI volume character devices */
|
||||
/* 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)
|
||||
/* An atomic eraseblock change command */
|
||||
#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, int32_t)
|
||||
/* Start UBI volume update
|
||||
* Note: This actually takes a pointer (__s64*), but we can't change
|
||||
* that without breaking the ABI on 32bit systems
|
||||
*/
|
||||
#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, __s64)
|
||||
/* LEB erasure command, used for debugging, disabled by default */
|
||||
#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, __s32)
|
||||
/* Atomic LEB change command */
|
||||
#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, __s32)
|
||||
/* Map LEB command */
|
||||
#define UBI_IOCEBMAP _IOW(UBI_VOL_IOC_MAGIC, 3, struct ubi_map_req)
|
||||
/* Unmap LEB command */
|
||||
#define UBI_IOCEBUNMAP _IOW(UBI_VOL_IOC_MAGIC, 4, __s32)
|
||||
/* Check if LEB is mapped command */
|
||||
#define UBI_IOCEBISMAP _IOR(UBI_VOL_IOC_MAGIC, 5, __s32)
|
||||
/* Set an UBI volume property */
|
||||
#define UBI_IOCSETVOLPROP _IOW(UBI_VOL_IOC_MAGIC, 6, \
|
||||
struct ubi_set_vol_prop_req)
|
||||
|
||||
/* Maximum MTD device name length supported by UBI */
|
||||
#define MAX_UBI_MTD_NAME_LEN 127
|
||||
|
||||
/*
|
||||
* UBI data type hint constants.
|
||||
*
|
||||
* UBI_LONGTERM: long-term data
|
||||
* UBI_SHORTTERM: short-term data
|
||||
* UBI_UNKNOWN: data persistence 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_LONGTERM = 1,
|
||||
UBI_SHORTTERM = 2,
|
||||
UBI_UNKNOWN = 3,
|
||||
};
|
||||
/* Maximum amount of UBI volumes that can be re-named at one go */
|
||||
#define UBI_MAX_RNVOL 32
|
||||
|
||||
/*
|
||||
* UBI volume type constants.
|
||||
@ -144,11 +197,23 @@ enum {
|
||||
UBI_STATIC_VOLUME = 4,
|
||||
};
|
||||
|
||||
/*
|
||||
* UBI set volume property ioctl constants.
|
||||
*
|
||||
* @UBI_VOL_PROP_DIRECT_WRITE: allow (any non-zero value) or disallow (value 0)
|
||||
* user to directly write and erase individual
|
||||
* eraseblocks on dynamic volumes
|
||||
*/
|
||||
enum {
|
||||
UBI_VOL_PROP_DIRECT_WRITE = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_attach_req - attach MTD device request.
|
||||
* @ubi_num: UBI device number to create
|
||||
* @mtd_num: MTD device number to attach
|
||||
* @vid_hdr_offset: VID header offset (use defaults if %0)
|
||||
* @max_beb_per1024: maximum expected number of bad PEB per 1024 PEBs
|
||||
* @padding: reserved for future, not used, has to be zeroed
|
||||
*
|
||||
* This data structure is used to specify MTD device UBI has to attach and the
|
||||
@ -164,20 +229,33 @@ enum {
|
||||
* it will be 512 in case of a 2KiB page NAND flash with 4 512-byte sub-pages.
|
||||
*
|
||||
* But in rare cases, if this optimizes things, the VID header may be placed to
|
||||
* a different offset. For example, the boot-loader might do things faster if the
|
||||
* VID header sits at the end of the first 2KiB NAND page with 4 sub-pages. As
|
||||
* the boot-loader would not normally need to read EC headers (unless it needs
|
||||
* UBI in RW mode), it might be faster to calculate ECC. This is weird example,
|
||||
* but it real-life example. So, in this example, @vid_hdr_offer would be
|
||||
* 2KiB-64 bytes = 1984. Note, that this position is not even 512-bytes
|
||||
* aligned, which is OK, as UBI is clever enough to realize this is 4th sub-page
|
||||
* of the first page and add needed padding.
|
||||
* a different offset. For example, the boot-loader might do things faster if
|
||||
* the VID header sits at the end of the first 2KiB NAND page with 4 sub-pages.
|
||||
* As the boot-loader would not normally need to read EC headers (unless it
|
||||
* needs UBI in RW mode), it might be faster to calculate ECC. This is weird
|
||||
* example, but it real-life example. So, in this example, @vid_hdr_offer would
|
||||
* be 2KiB-64 bytes = 1984. Note, that this position is not even 512-bytes
|
||||
* aligned, which is OK, as UBI is clever enough to realize this is 4th
|
||||
* sub-page of the first page and add needed padding.
|
||||
*
|
||||
* The @max_beb_per1024 is the maximum amount of bad PEBs UBI expects on the
|
||||
* UBI device per 1024 eraseblocks. This value is often given in an other form
|
||||
* in the NAND datasheet (min NVB i.e. minimal number of valid blocks). The
|
||||
* maximum expected bad eraseblocks per 1024 is then:
|
||||
* 1024 * (1 - MinNVB / MaxNVB)
|
||||
* Which gives 20 for most NAND devices. This limit is used in order to derive
|
||||
* amount of eraseblock UBI reserves for handling new bad blocks. If the device
|
||||
* has more bad eraseblocks than this limit, UBI does not reserve any physical
|
||||
* eraseblocks for new bad eraseblocks, but attempts to use available
|
||||
* eraseblocks (if any). The accepted range is 0-768. If 0 is given, the
|
||||
* default kernel value of %CONFIG_MTD_UBI_BEB_LIMIT will be used.
|
||||
*/
|
||||
struct ubi_attach_req {
|
||||
int32_t ubi_num;
|
||||
int32_t mtd_num;
|
||||
int32_t vid_hdr_offset;
|
||||
uint8_t padding[12];
|
||||
__s32 ubi_num;
|
||||
__s32 mtd_num;
|
||||
__s32 vid_hdr_offset;
|
||||
__s16 max_beb_per1024;
|
||||
__s8 padding[10];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -212,15 +290,15 @@ struct ubi_attach_req {
|
||||
* BLOBs, without caring about how to properly align them.
|
||||
*/
|
||||
struct ubi_mkvol_req {
|
||||
int32_t vol_id;
|
||||
int32_t alignment;
|
||||
int64_t bytes;
|
||||
int8_t vol_type;
|
||||
int8_t padding1;
|
||||
int16_t name_len;
|
||||
int8_t padding2[4];
|
||||
__s32 vol_id;
|
||||
__s32 alignment;
|
||||
__s64 bytes;
|
||||
__s8 vol_type;
|
||||
__s8 padding1;
|
||||
__s16 name_len;
|
||||
__s8 padding2[4];
|
||||
char name[UBI_MAX_VOLUME_NAME + 1];
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubi_rsvol_req - a data structure used in volume re-size requests.
|
||||
@ -229,28 +307,105 @@ struct ubi_mkvol_req {
|
||||
*
|
||||
* 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
|
||||
* smaller than 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));
|
||||
__s64 bytes;
|
||||
__s32 vol_id;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubi_leb_change_req - a data structure used in atomic logical
|
||||
* eraseblock change requests.
|
||||
* struct ubi_rnvol_req - volumes re-name request.
|
||||
* @count: count of volumes to re-name
|
||||
* @padding1: reserved for future, not used, has to be zeroed
|
||||
* @vol_id: ID of the volume to re-name
|
||||
* @name_len: name length
|
||||
* @padding2: reserved for future, not used, has to be zeroed
|
||||
* @name: new volume name
|
||||
*
|
||||
* UBI allows to re-name up to %32 volumes at one go. The count of volumes to
|
||||
* re-name is specified in the @count field. The ID of the volumes to re-name
|
||||
* and the new names are specified in the @vol_id and @name fields.
|
||||
*
|
||||
* The UBI volume re-name operation is atomic, which means that should power cut
|
||||
* happen, the volumes will have either old name or new name. So the possible
|
||||
* use-cases of this command is atomic upgrade. Indeed, to upgrade, say, volumes
|
||||
* A and B one may create temporary volumes %A1 and %B1 with the new contents,
|
||||
* then atomically re-name A1->A and B1->B, in which case old %A and %B will
|
||||
* be removed.
|
||||
*
|
||||
* If it is not desirable to remove old A and B, the re-name request has to
|
||||
* contain 4 entries: A1->A, A->A1, B1->B, B->B1, in which case old A1 and B1
|
||||
* become A and B, and old A and B will become A1 and B1.
|
||||
*
|
||||
* It is also OK to request: A1->A, A1->X, B1->B, B->Y, in which case old A1
|
||||
* and B1 become A and B, and old A and B become X and Y.
|
||||
*
|
||||
* In other words, in case of re-naming into an existing volume name, the
|
||||
* existing volume is removed, unless it is re-named as well at the same
|
||||
* re-name request.
|
||||
*/
|
||||
struct ubi_rnvol_req {
|
||||
__s32 count;
|
||||
__s8 padding1[12];
|
||||
struct {
|
||||
__s32 vol_id;
|
||||
__s16 name_len;
|
||||
__s8 padding2[2];
|
||||
char name[UBI_MAX_VOLUME_NAME + 1];
|
||||
} ents[UBI_MAX_RNVOL];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubi_leb_change_req - a data structure used in atomic LEB change
|
||||
* requests.
|
||||
* @lnum: logical eraseblock number to change
|
||||
* @bytes: how many bytes will be written to the logical eraseblock
|
||||
* @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN)
|
||||
* @dtype: pass "3" for better compatibility with old kernels
|
||||
* @padding: reserved for future, not used, has to be zeroed
|
||||
*
|
||||
* The @dtype field used to inform UBI about what kind of data will be written
|
||||
* to the LEB: long term (value 1), short term (value 2), unknown (value 3).
|
||||
* UBI tried to pick a PEB with lower erase counter for short term data and a
|
||||
* PEB with higher erase counter for long term data. But this was not really
|
||||
* used because users usually do not know this and could easily mislead UBI. We
|
||||
* removed this feature in May 2012. UBI currently just ignores the @dtype
|
||||
* field. But for better compatibility with older kernels it is recommended to
|
||||
* set @dtype to 3 (unknown).
|
||||
*/
|
||||
struct ubi_leb_change_req {
|
||||
int32_t lnum;
|
||||
int32_t bytes;
|
||||
uint8_t dtype;
|
||||
uint8_t padding[7];
|
||||
} __attribute__ ((packed));
|
||||
__s32 lnum;
|
||||
__s32 bytes;
|
||||
__s8 dtype; /* obsolete, do not use! */
|
||||
__s8 padding[7];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubi_map_req - a data structure used in map LEB requests.
|
||||
* @dtype: pass "3" for better compatibility with old kernels
|
||||
* @lnum: logical eraseblock number to unmap
|
||||
* @padding: reserved for future, not used, has to be zeroed
|
||||
*/
|
||||
struct ubi_map_req {
|
||||
__s32 lnum;
|
||||
__s8 dtype; /* obsolete, do not use! */
|
||||
__s8 padding[3];
|
||||
} __packed;
|
||||
|
||||
|
||||
/**
|
||||
* struct ubi_set_vol_prop_req - a data structure used to set an UBI volume
|
||||
* property.
|
||||
* @property: property to set (%UBI_VOL_PROP_DIRECT_WRITE)
|
||||
* @padding: reserved for future, not used, has to be zeroed
|
||||
* @value: value to set
|
||||
*/
|
||||
struct ubi_set_vol_prop_req {
|
||||
__u8 property;
|
||||
__u8 padding[7];
|
||||
__u64 value;
|
||||
} __packed;
|
||||
|
||||
#endif /* __UBI_USER_H__ */
|
||||
|
@ -13,22 +13,6 @@
|
||||
#include <linux/compat.h>
|
||||
|
||||
/* common */
|
||||
#define spin_lock_init(...)
|
||||
#define spin_lock(...)
|
||||
#define spin_lock_irqsave(lock, flags) do { debug("%lu\n", flags); } while (0)
|
||||
#define spin_unlock(...)
|
||||
#define spin_unlock_irqrestore(lock, flags) do {flags = 0; } while (0)
|
||||
#define disable_irq(...)
|
||||
#define enable_irq(...)
|
||||
|
||||
#define mutex_init(...)
|
||||
#define mutex_lock(...)
|
||||
#define mutex_unlock(...)
|
||||
|
||||
#define GFP_KERNEL 0
|
||||
|
||||
#define IRQ_HANDLED 1
|
||||
|
||||
#define ENOTSUPP 524 /* Operation is not supported */
|
||||
|
||||
#define BITS_PER_BYTE 8
|
||||
|
Loading…
Reference in New Issue
Block a user