1
0
Fork 0

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>
utp
Heiko Schocher 2014-06-24 10:10:04 +02:00 committed by Tom Rini
parent 0c06db5983
commit ff94bc40af
75 changed files with 26666 additions and 6883 deletions

61
README
View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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
/*

View File

@ -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;

View File

@ -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;

View File

@ -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

View 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))

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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");

View File

@ -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
*****************************************************************************/

View File

@ -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++;

View File

@ -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>

View File

@ -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);
}

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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),

View File

@ -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

View File

@ -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__ */

View File

@ -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);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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 */

File diff suppressed because it is too large Load Diff

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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__ */

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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;
}

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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__ */

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -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);

View 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__ */

View File

@ -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__ */

View File

@ -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 */

View File

@ -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

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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