kernel: remove yaffs2 support, it is no longer needed
Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
parent
21591a3361
commit
8ab057f5e4
56 changed files with 2 additions and 17333 deletions
|
@ -4621,8 +4621,6 @@ CONFIG_XZ_DEC=y
|
||||||
# CONFIG_XZ_DEC_SPARC is not set
|
# CONFIG_XZ_DEC_SPARC is not set
|
||||||
# CONFIG_XZ_DEC_TEST is not set
|
# CONFIG_XZ_DEC_TEST is not set
|
||||||
# CONFIG_XZ_DEC_X86 is not set
|
# CONFIG_XZ_DEC_X86 is not set
|
||||||
# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set
|
|
||||||
# CONFIG_YAFFS_FS is not set
|
|
||||||
# CONFIG_YAM is not set
|
# CONFIG_YAM is not set
|
||||||
# CONFIG_YELLOWFIN is not set
|
# CONFIG_YELLOWFIN is not set
|
||||||
# CONFIG_YENTA is not set
|
# CONFIG_YENTA is not set
|
||||||
|
|
|
@ -4806,8 +4806,6 @@ CONFIG_XZ_DEC=y
|
||||||
# CONFIG_XZ_DEC_SPARC is not set
|
# CONFIG_XZ_DEC_SPARC is not set
|
||||||
# CONFIG_XZ_DEC_TEST is not set
|
# CONFIG_XZ_DEC_TEST is not set
|
||||||
# CONFIG_XZ_DEC_X86 is not set
|
# CONFIG_XZ_DEC_X86 is not set
|
||||||
# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set
|
|
||||||
# CONFIG_YAFFS_FS is not set
|
|
||||||
# CONFIG_YAM is not set
|
# CONFIG_YAM is not set
|
||||||
# CONFIG_YELLOWFIN is not set
|
# CONFIG_YELLOWFIN is not set
|
||||||
# CONFIG_YENTA is not set
|
# CONFIG_YENTA is not set
|
||||||
|
|
|
@ -1,171 +0,0 @@
|
||||||
#
|
|
||||||
# yaffs file system configurations
|
|
||||||
#
|
|
||||||
|
|
||||||
config YAFFS_FS
|
|
||||||
tristate "yaffs2 file system support"
|
|
||||||
default n
|
|
||||||
depends on MTD_BLOCK
|
|
||||||
select YAFFS_YAFFS1
|
|
||||||
select YAFFS_YAFFS2
|
|
||||||
help
|
|
||||||
yaffs2, or Yet Another Flash File System, is a file system
|
|
||||||
optimised for NAND Flash chips.
|
|
||||||
|
|
||||||
To compile the yaffs2 file system support as a module, choose M
|
|
||||||
here: the module will be called yaffs2.
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
Further information on yaffs2 is available at
|
|
||||||
<http://www.aleph1.co.uk/yaffs/>.
|
|
||||||
|
|
||||||
config YAFFS_YAFFS1
|
|
||||||
bool "512 byte / page devices"
|
|
||||||
depends on YAFFS_FS
|
|
||||||
default y
|
|
||||||
help
|
|
||||||
Enable yaffs1 support -- yaffs for 512 byte / page devices
|
|
||||||
|
|
||||||
Not needed for 2K-page devices.
|
|
||||||
|
|
||||||
If unsure, say Y.
|
|
||||||
|
|
||||||
config YAFFS_9BYTE_TAGS
|
|
||||||
bool "Use older-style on-NAND data format with pageStatus byte"
|
|
||||||
depends on YAFFS_YAFFS1
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
|
|
||||||
Older-style on-NAND data format has a "pageStatus" byte to record
|
|
||||||
chunk/page state. This byte is zero when the page is discarded.
|
|
||||||
Choose this option if you have existing on-NAND data using this
|
|
||||||
format that you need to continue to support. New data written
|
|
||||||
also uses the older-style format. Note: Use of this option
|
|
||||||
generally requires that MTD's oob layout be adjusted to use the
|
|
||||||
older-style format. See notes on tags formats and MTD versions
|
|
||||||
in yaffs_mtdif1.c.
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
config YAFFS_DOES_ECC
|
|
||||||
bool "Lets yaffs do its own ECC"
|
|
||||||
depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
This enables yaffs to use its own ECC functions instead of using
|
|
||||||
the ones from the generic MTD-NAND driver.
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
config YAFFS_ECC_WRONG_ORDER
|
|
||||||
bool "Use the same ecc byte order as Steven Hill's nand_ecc.c"
|
|
||||||
depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
This makes yaffs_ecc.c use the same ecc byte order as Steven
|
|
||||||
Hill's nand_ecc.c. If not set, then you get the same ecc byte
|
|
||||||
order as SmartMedia.
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
config YAFFS_YAFFS2
|
|
||||||
bool "2048 byte (or larger) / page devices"
|
|
||||||
depends on YAFFS_FS
|
|
||||||
default y
|
|
||||||
help
|
|
||||||
Enable yaffs2 support -- yaffs for >= 2K bytes per page devices
|
|
||||||
|
|
||||||
If unsure, say Y.
|
|
||||||
|
|
||||||
config YAFFS_AUTO_YAFFS2
|
|
||||||
bool "Autoselect yaffs2 format"
|
|
||||||
depends on YAFFS_YAFFS2
|
|
||||||
default y
|
|
||||||
help
|
|
||||||
Without this, you need to explicitely use yaffs2 as the file
|
|
||||||
system type. With this, you can say "yaffs" and yaffs or yaffs2
|
|
||||||
will be used depending on the device page size (yaffs on
|
|
||||||
512-byte page devices, yaffs2 on 2K page devices).
|
|
||||||
|
|
||||||
If unsure, say Y.
|
|
||||||
|
|
||||||
config YAFFS_DISABLE_TAGS_ECC
|
|
||||||
bool "Disable yaffs from doing ECC on tags by default"
|
|
||||||
depends on YAFFS_FS && YAFFS_YAFFS2
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
This defaults yaffs to using its own ECC calculations on tags instead of
|
|
||||||
just relying on the MTD.
|
|
||||||
This behavior can also be overridden with tags_ecc_on and
|
|
||||||
tags_ecc_off mount options.
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
config YAFFS_ALWAYS_CHECK_CHUNK_ERASED
|
|
||||||
bool "Force chunk erase check"
|
|
||||||
depends on YAFFS_FS
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
Normally yaffs only checks chunks before writing until an erased
|
|
||||||
chunk is found. This helps to detect any partially written
|
|
||||||
chunks that might have happened due to power loss.
|
|
||||||
|
|
||||||
Enabling this forces on the test that chunks are erased in flash
|
|
||||||
before writing to them. This takes more time but is potentially
|
|
||||||
a bit more secure.
|
|
||||||
|
|
||||||
Suggest setting Y during development and ironing out driver
|
|
||||||
issues etc. Suggest setting to N if you want faster writing.
|
|
||||||
|
|
||||||
If unsure, say Y.
|
|
||||||
|
|
||||||
config YAFFS_EMPTY_LOST_AND_FOUND
|
|
||||||
bool "Empty lost and found on boot"
|
|
||||||
depends on YAFFS_FS
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
If this is enabled then the contents of lost and found is
|
|
||||||
automatically dumped at mount.
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
config YAFFS_DISABLE_BLOCK_REFRESHING
|
|
||||||
bool "Disable yaffs2 block refreshing"
|
|
||||||
depends on YAFFS_FS
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
If this is set, then block refreshing is disabled.
|
|
||||||
Block refreshing infrequently refreshes the oldest block in
|
|
||||||
a yaffs2 file system. This mechanism helps to refresh flash to
|
|
||||||
mitigate against data loss. This is particularly useful for MLC.
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
config YAFFS_DISABLE_BACKGROUND
|
|
||||||
bool "Disable yaffs2 background processing"
|
|
||||||
depends on YAFFS_FS
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
If this is set, then background processing is disabled.
|
|
||||||
Background processing makes many foreground activities faster.
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
config YAFFS_DISABLE_BAD_BLOCK_MARKING
|
|
||||||
bool "Disable yaffs2 bad block marking"
|
|
||||||
depends on YAFFS_FS
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
Useful during early flash bring up to prevent problems causing
|
|
||||||
lots of bad block marking.
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
config YAFFS_XATTR
|
|
||||||
bool "Enable yaffs2 xattr support"
|
|
||||||
depends on YAFFS_FS
|
|
||||||
default y
|
|
||||||
help
|
|
||||||
If this is set then yaffs2 will provide xattr support.
|
|
||||||
If unsure, say Y.
|
|
|
@ -1,18 +0,0 @@
|
||||||
#
|
|
||||||
# Makefile for the linux YAFFS filesystem routines.
|
|
||||||
#
|
|
||||||
|
|
||||||
obj-$(CONFIG_YAFFS_FS) += yaffs.o
|
|
||||||
|
|
||||||
yaffs-y := yaffs_ecc.o yaffs_vfs.o yaffs_guts.o yaffs_checkptrw.o
|
|
||||||
yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o
|
|
||||||
yaffs-y += yaffs_tagscompat.o yaffs_tagsmarshall.o
|
|
||||||
yaffs-y += yaffs_mtdif.o
|
|
||||||
yaffs-y += yaffs_nameval.o yaffs_attribs.o
|
|
||||||
yaffs-y += yaffs_allocator.o
|
|
||||||
yaffs-y += yaffs_yaffs1.o
|
|
||||||
yaffs-y += yaffs_yaffs2.o
|
|
||||||
yaffs-y += yaffs_bitmap.o
|
|
||||||
yaffs-y += yaffs_summary.o
|
|
||||||
yaffs-y += yaffs_verify.o
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
The yaffs2 source has been fetched from the yaffs2 GIT tree.
|
|
||||||
|
|
||||||
URL: git://www.aleph1.co.uk/yaffs2
|
|
||||||
Version: 583dbd9cc2668870cb013f051ba59f7d3e513dae (2015-06-02)
|
|
|
@ -1,357 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "yaffs_allocator.h"
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
#include "yaffs_trace.h"
|
|
||||||
#include "yportenv.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks
|
|
||||||
* of approx 100 objects that are themn allocated singly.
|
|
||||||
* This is basically a simplified slab allocator.
|
|
||||||
*
|
|
||||||
* We don't use the Linux slab allocator because slab does not allow
|
|
||||||
* us to dump all the objects in one hit when we do a umount and tear
|
|
||||||
* down all the tnodes and objects. slab requires that we first free
|
|
||||||
* the individual objects.
|
|
||||||
*
|
|
||||||
* Once yaffs has been mainlined I shall try to motivate for a change
|
|
||||||
* to slab to provide the extra features we need here.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct yaffs_tnode_list {
|
|
||||||
struct yaffs_tnode_list *next;
|
|
||||||
struct yaffs_tnode *tnodes;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct yaffs_obj_list {
|
|
||||||
struct yaffs_obj_list *next;
|
|
||||||
struct yaffs_obj *objects;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct yaffs_allocator {
|
|
||||||
int n_tnodes_created;
|
|
||||||
struct yaffs_tnode *free_tnodes;
|
|
||||||
int n_free_tnodes;
|
|
||||||
struct yaffs_tnode_list *alloc_tnode_list;
|
|
||||||
|
|
||||||
int n_obj_created;
|
|
||||||
struct list_head free_objs;
|
|
||||||
int n_free_objects;
|
|
||||||
|
|
||||||
struct yaffs_obj_list *allocated_obj_list;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
struct yaffs_allocator *allocator =
|
|
||||||
(struct yaffs_allocator *)dev->allocator;
|
|
||||||
struct yaffs_tnode_list *tmp;
|
|
||||||
|
|
||||||
if (!allocator) {
|
|
||||||
BUG();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (allocator->alloc_tnode_list) {
|
|
||||||
tmp = allocator->alloc_tnode_list->next;
|
|
||||||
|
|
||||||
kfree(allocator->alloc_tnode_list->tnodes);
|
|
||||||
kfree(allocator->alloc_tnode_list);
|
|
||||||
allocator->alloc_tnode_list = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
allocator->free_tnodes = NULL;
|
|
||||||
allocator->n_free_tnodes = 0;
|
|
||||||
allocator->n_tnodes_created = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void yaffs_init_raw_tnodes(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
struct yaffs_allocator *allocator = dev->allocator;
|
|
||||||
|
|
||||||
if (!allocator) {
|
|
||||||
BUG();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
allocator->alloc_tnode_list = NULL;
|
|
||||||
allocator->free_tnodes = NULL;
|
|
||||||
allocator->n_free_tnodes = 0;
|
|
||||||
allocator->n_tnodes_created = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes)
|
|
||||||
{
|
|
||||||
struct yaffs_allocator *allocator =
|
|
||||||
(struct yaffs_allocator *)dev->allocator;
|
|
||||||
int i;
|
|
||||||
struct yaffs_tnode *new_tnodes;
|
|
||||||
u8 *mem;
|
|
||||||
struct yaffs_tnode *curr;
|
|
||||||
struct yaffs_tnode *next;
|
|
||||||
struct yaffs_tnode_list *tnl;
|
|
||||||
|
|
||||||
if (!allocator) {
|
|
||||||
BUG();
|
|
||||||
return YAFFS_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n_tnodes < 1)
|
|
||||||
return YAFFS_OK;
|
|
||||||
|
|
||||||
/* make these things */
|
|
||||||
new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS);
|
|
||||||
mem = (u8 *) new_tnodes;
|
|
||||||
|
|
||||||
if (!new_tnodes) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
|
||||||
"yaffs: Could not allocate Tnodes");
|
|
||||||
return YAFFS_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* New hookup for wide tnodes */
|
|
||||||
for (i = 0; i < n_tnodes - 1; i++) {
|
|
||||||
curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size];
|
|
||||||
next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size];
|
|
||||||
curr->internal[0] = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size];
|
|
||||||
curr->internal[0] = allocator->free_tnodes;
|
|
||||||
allocator->free_tnodes = (struct yaffs_tnode *)mem;
|
|
||||||
|
|
||||||
allocator->n_free_tnodes += n_tnodes;
|
|
||||||
allocator->n_tnodes_created += n_tnodes;
|
|
||||||
|
|
||||||
/* Now add this bunch of tnodes to a list for freeing up.
|
|
||||||
* NB If we can't add this to the management list it isn't fatal
|
|
||||||
* but it just means we can't free this bunch of tnodes later.
|
|
||||||
*/
|
|
||||||
tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS);
|
|
||||||
if (!tnl) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
|
||||||
"Could not add tnodes to management list");
|
|
||||||
return YAFFS_FAIL;
|
|
||||||
} else {
|
|
||||||
tnl->tnodes = new_tnodes;
|
|
||||||
tnl->next = allocator->alloc_tnode_list;
|
|
||||||
allocator->alloc_tnode_list = tnl;
|
|
||||||
}
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added");
|
|
||||||
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
struct yaffs_allocator *allocator =
|
|
||||||
(struct yaffs_allocator *)dev->allocator;
|
|
||||||
struct yaffs_tnode *tn = NULL;
|
|
||||||
|
|
||||||
if (!allocator) {
|
|
||||||
BUG();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If there are none left make more */
|
|
||||||
if (!allocator->free_tnodes)
|
|
||||||
yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES);
|
|
||||||
|
|
||||||
if (allocator->free_tnodes) {
|
|
||||||
tn = allocator->free_tnodes;
|
|
||||||
allocator->free_tnodes = allocator->free_tnodes->internal[0];
|
|
||||||
allocator->n_free_tnodes--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FreeTnode frees up a tnode and puts it back on the free list */
|
|
||||||
void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn)
|
|
||||||
{
|
|
||||||
struct yaffs_allocator *allocator = dev->allocator;
|
|
||||||
|
|
||||||
if (!allocator) {
|
|
||||||
BUG();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tn) {
|
|
||||||
tn->internal[0] = allocator->free_tnodes;
|
|
||||||
allocator->free_tnodes = tn;
|
|
||||||
allocator->n_free_tnodes++;
|
|
||||||
}
|
|
||||||
dev->checkpoint_blocks_required = 0; /* force recalculation */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*--------------- yaffs_obj alloaction ------------------------
|
|
||||||
*
|
|
||||||
* Free yaffs_objs are stored in a list using obj->siblings.
|
|
||||||
* The blocks of allocated objects are stored in a linked list.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void yaffs_init_raw_objs(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
struct yaffs_allocator *allocator = dev->allocator;
|
|
||||||
|
|
||||||
if (!allocator) {
|
|
||||||
BUG();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
allocator->allocated_obj_list = NULL;
|
|
||||||
INIT_LIST_HEAD(&allocator->free_objs);
|
|
||||||
allocator->n_free_objects = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void yaffs_deinit_raw_objs(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
struct yaffs_allocator *allocator = dev->allocator;
|
|
||||||
struct yaffs_obj_list *tmp;
|
|
||||||
|
|
||||||
if (!allocator) {
|
|
||||||
BUG();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (allocator->allocated_obj_list) {
|
|
||||||
tmp = allocator->allocated_obj_list->next;
|
|
||||||
kfree(allocator->allocated_obj_list->objects);
|
|
||||||
kfree(allocator->allocated_obj_list);
|
|
||||||
allocator->allocated_obj_list = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
INIT_LIST_HEAD(&allocator->free_objs);
|
|
||||||
allocator->n_free_objects = 0;
|
|
||||||
allocator->n_obj_created = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj)
|
|
||||||
{
|
|
||||||
struct yaffs_allocator *allocator = dev->allocator;
|
|
||||||
int i;
|
|
||||||
struct yaffs_obj *new_objs;
|
|
||||||
struct yaffs_obj_list *list;
|
|
||||||
|
|
||||||
if (!allocator) {
|
|
||||||
BUG();
|
|
||||||
return YAFFS_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n_obj < 1)
|
|
||||||
return YAFFS_OK;
|
|
||||||
|
|
||||||
/* make these things */
|
|
||||||
new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS);
|
|
||||||
list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS);
|
|
||||||
|
|
||||||
if (!new_objs || !list) {
|
|
||||||
kfree(new_objs);
|
|
||||||
new_objs = NULL;
|
|
||||||
kfree(list);
|
|
||||||
list = NULL;
|
|
||||||
yaffs_trace(YAFFS_TRACE_ALLOCATE,
|
|
||||||
"Could not allocate more objects");
|
|
||||||
return YAFFS_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hook them into the free list */
|
|
||||||
for (i = 0; i < n_obj; i++)
|
|
||||||
list_add(&new_objs[i].siblings, &allocator->free_objs);
|
|
||||||
|
|
||||||
allocator->n_free_objects += n_obj;
|
|
||||||
allocator->n_obj_created += n_obj;
|
|
||||||
|
|
||||||
/* Now add this bunch of Objects to a list for freeing up. */
|
|
||||||
|
|
||||||
list->objects = new_objs;
|
|
||||||
list->next = allocator->allocated_obj_list;
|
|
||||||
allocator->allocated_obj_list = list;
|
|
||||||
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
struct yaffs_obj *obj = NULL;
|
|
||||||
struct list_head *lh;
|
|
||||||
struct yaffs_allocator *allocator = dev->allocator;
|
|
||||||
|
|
||||||
if (!allocator) {
|
|
||||||
BUG();
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If there are none left make more */
|
|
||||||
if (list_empty(&allocator->free_objs))
|
|
||||||
yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS);
|
|
||||||
|
|
||||||
if (!list_empty(&allocator->free_objs)) {
|
|
||||||
lh = allocator->free_objs.next;
|
|
||||||
obj = list_entry(lh, struct yaffs_obj, siblings);
|
|
||||||
list_del_init(lh);
|
|
||||||
allocator->n_free_objects--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj)
|
|
||||||
{
|
|
||||||
|
|
||||||
struct yaffs_allocator *allocator = dev->allocator;
|
|
||||||
|
|
||||||
if (!allocator) {
|
|
||||||
BUG();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Link into the free list. */
|
|
||||||
list_add(&obj->siblings, &allocator->free_objs);
|
|
||||||
allocator->n_free_objects++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (!dev->allocator) {
|
|
||||||
BUG();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
yaffs_deinit_raw_tnodes(dev);
|
|
||||||
yaffs_deinit_raw_objs(dev);
|
|
||||||
kfree(dev->allocator);
|
|
||||||
dev->allocator = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
struct yaffs_allocator *allocator;
|
|
||||||
|
|
||||||
if (dev->allocator) {
|
|
||||||
BUG();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS);
|
|
||||||
if (allocator) {
|
|
||||||
dev->allocator = allocator;
|
|
||||||
yaffs_init_raw_tnodes(dev);
|
|
||||||
yaffs_init_raw_objs(dev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YAFFS_ALLOCATOR_H__
|
|
||||||
#define __YAFFS_ALLOCATOR_H__
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
|
|
||||||
void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev);
|
|
||||||
void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev);
|
|
||||||
|
|
||||||
struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev);
|
|
||||||
void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn);
|
|
||||||
|
|
||||||
struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev);
|
|
||||||
void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,132 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
#include "yaffs_attribs.h"
|
|
||||||
|
|
||||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0))
|
|
||||||
#define IATTR_UID ia_uid
|
|
||||||
#define IATTR_GID ia_gid
|
|
||||||
#else
|
|
||||||
#define IATTR_UID ia_uid.val
|
|
||||||
#define IATTR_GID ia_gid.val
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh)
|
|
||||||
{
|
|
||||||
obj->yst_uid = oh->yst_uid;
|
|
||||||
obj->yst_gid = oh->yst_gid;
|
|
||||||
obj->yst_atime = oh->yst_atime;
|
|
||||||
obj->yst_mtime = oh->yst_mtime;
|
|
||||||
obj->yst_ctime = oh->yst_ctime;
|
|
||||||
obj->yst_rdev = oh->yst_rdev;
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj)
|
|
||||||
{
|
|
||||||
oh->yst_uid = obj->yst_uid;
|
|
||||||
oh->yst_gid = obj->yst_gid;
|
|
||||||
oh->yst_atime = obj->yst_atime;
|
|
||||||
oh->yst_mtime = obj->yst_mtime;
|
|
||||||
oh->yst_ctime = obj->yst_ctime;
|
|
||||||
oh->yst_rdev = obj->yst_rdev;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c)
|
|
||||||
{
|
|
||||||
obj->yst_mtime = Y_CURRENT_TIME;
|
|
||||||
if (do_a)
|
|
||||||
obj->yst_atime = obj->yst_mtime;
|
|
||||||
if (do_c)
|
|
||||||
obj->yst_ctime = obj->yst_mtime;
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev)
|
|
||||||
{
|
|
||||||
yaffs_load_current_time(obj, 1, 1);
|
|
||||||
obj->yst_rdev = rdev;
|
|
||||||
obj->yst_uid = uid;
|
|
||||||
obj->yst_gid = gid;
|
|
||||||
}
|
|
||||||
|
|
||||||
static loff_t yaffs_get_file_size(struct yaffs_obj *obj)
|
|
||||||
{
|
|
||||||
YCHAR *alias = NULL;
|
|
||||||
obj = yaffs_get_equivalent_obj(obj);
|
|
||||||
|
|
||||||
switch (obj->variant_type) {
|
|
||||||
case YAFFS_OBJECT_TYPE_FILE:
|
|
||||||
return obj->variant.file_variant.file_size;
|
|
||||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
|
||||||
alias = obj->variant.symlink_variant.alias;
|
|
||||||
if (!alias)
|
|
||||||
return 0;
|
|
||||||
return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH);
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr)
|
|
||||||
{
|
|
||||||
unsigned int valid = attr->ia_valid;
|
|
||||||
|
|
||||||
if (valid & ATTR_MODE)
|
|
||||||
obj->yst_mode = attr->ia_mode;
|
|
||||||
if (valid & ATTR_UID)
|
|
||||||
obj->yst_uid = attr->IATTR_UID;
|
|
||||||
if (valid & ATTR_GID)
|
|
||||||
obj->yst_gid = attr->IATTR_GID;
|
|
||||||
|
|
||||||
if (valid & ATTR_ATIME)
|
|
||||||
obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime);
|
|
||||||
if (valid & ATTR_CTIME)
|
|
||||||
obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime);
|
|
||||||
if (valid & ATTR_MTIME)
|
|
||||||
obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime);
|
|
||||||
|
|
||||||
if (valid & ATTR_SIZE)
|
|
||||||
yaffs_resize_file(obj, attr->ia_size);
|
|
||||||
|
|
||||||
yaffs_update_oh(obj, NULL, 1, 0, 0, NULL);
|
|
||||||
|
|
||||||
return YAFFS_OK;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr)
|
|
||||||
{
|
|
||||||
unsigned int valid = 0;
|
|
||||||
|
|
||||||
attr->ia_mode = obj->yst_mode;
|
|
||||||
valid |= ATTR_MODE;
|
|
||||||
attr->IATTR_UID = obj->yst_uid;
|
|
||||||
valid |= ATTR_UID;
|
|
||||||
attr->IATTR_GID = obj->yst_gid;
|
|
||||||
valid |= ATTR_GID;
|
|
||||||
|
|
||||||
Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime;
|
|
||||||
valid |= ATTR_ATIME;
|
|
||||||
Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime;
|
|
||||||
valid |= ATTR_CTIME;
|
|
||||||
Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime;
|
|
||||||
valid |= ATTR_MTIME;
|
|
||||||
|
|
||||||
attr->ia_size = yaffs_get_file_size(obj);
|
|
||||||
valid |= ATTR_SIZE;
|
|
||||||
|
|
||||||
attr->ia_valid = valid;
|
|
||||||
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YAFFS_ATTRIBS_H__
|
|
||||||
#define __YAFFS_ATTRIBS_H__
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
|
|
||||||
void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh);
|
|
||||||
void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj);
|
|
||||||
void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev);
|
|
||||||
void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c);
|
|
||||||
int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr);
|
|
||||||
int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,97 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "yaffs_bitmap.h"
|
|
||||||
#include "yaffs_trace.h"
|
|
||||||
/*
|
|
||||||
* Chunk bitmap manipulations
|
|
||||||
*/
|
|
||||||
|
|
||||||
static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk)
|
|
||||||
{
|
|
||||||
if (blk < dev->internal_start_block || blk > dev->internal_end_block) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
|
||||||
"BlockBits block %d is not valid",
|
|
||||||
blk);
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
return dev->chunk_bits +
|
|
||||||
(dev->chunk_bit_stride * (blk - dev->internal_start_block));
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk)
|
|
||||||
{
|
|
||||||
if (blk < dev->internal_start_block || blk > dev->internal_end_block ||
|
|
||||||
chunk < 0 || chunk >= dev->param.chunks_per_block) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
|
||||||
"Chunk Id (%d:%d) invalid",
|
|
||||||
blk, chunk);
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk)
|
|
||||||
{
|
|
||||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
|
||||||
|
|
||||||
memset(blk_bits, 0, dev->chunk_bit_stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)
|
|
||||||
{
|
|
||||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
|
||||||
|
|
||||||
yaffs_verify_chunk_bit_id(dev, blk, chunk);
|
|
||||||
blk_bits[chunk / 8] &= ~(1 << (chunk & 7));
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)
|
|
||||||
{
|
|
||||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
|
||||||
|
|
||||||
yaffs_verify_chunk_bit_id(dev, blk, chunk);
|
|
||||||
blk_bits[chunk / 8] |= (1 << (chunk & 7));
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)
|
|
||||||
{
|
|
||||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
|
||||||
|
|
||||||
yaffs_verify_chunk_bit_id(dev, blk, chunk);
|
|
||||||
return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk)
|
|
||||||
{
|
|
||||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < dev->chunk_bit_stride; i++) {
|
|
||||||
if (*blk_bits)
|
|
||||||
return 1;
|
|
||||||
blk_bits++;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk)
|
|
||||||
{
|
|
||||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
|
||||||
int i;
|
|
||||||
int n = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++)
|
|
||||||
n += hweight8(*blk_bits);
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Chunk bitmap manipulations
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YAFFS_BITMAP_H__
|
|
||||||
#define __YAFFS_BITMAP_H__
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
|
|
||||||
void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk);
|
|
||||||
void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk);
|
|
||||||
void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk);
|
|
||||||
void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk);
|
|
||||||
int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk);
|
|
||||||
int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk);
|
|
||||||
int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,466 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "yaffs_checkptrw.h"
|
|
||||||
#include "yaffs_getblockinfo.h"
|
|
||||||
|
|
||||||
struct yaffs_checkpt_chunk_hdr {
|
|
||||||
int version;
|
|
||||||
int seq;
|
|
||||||
u32 sum;
|
|
||||||
u32 xor;
|
|
||||||
} ;
|
|
||||||
|
|
||||||
|
|
||||||
static int apply_chunk_offset(struct yaffs_dev *dev, int chunk)
|
|
||||||
{
|
|
||||||
return chunk - dev->chunk_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int apply_block_offset(struct yaffs_dev *dev, int block)
|
|
||||||
{
|
|
||||||
return block - dev->block_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
struct yaffs_checkpt_chunk_hdr hdr;
|
|
||||||
|
|
||||||
hdr.version = YAFFS_CHECKPOINT_VERSION;
|
|
||||||
hdr.seq = dev->checkpt_page_seq;
|
|
||||||
hdr.sum = dev->checkpt_sum;
|
|
||||||
hdr.xor = dev->checkpt_xor;
|
|
||||||
|
|
||||||
dev->checkpt_byte_offs = sizeof(hdr);
|
|
||||||
|
|
||||||
memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
struct yaffs_checkpt_chunk_hdr hdr;
|
|
||||||
|
|
||||||
memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr));
|
|
||||||
|
|
||||||
dev->checkpt_byte_offs = sizeof(hdr);
|
|
||||||
|
|
||||||
return hdr.version == YAFFS_CHECKPOINT_VERSION &&
|
|
||||||
hdr.seq == dev->checkpt_page_seq &&
|
|
||||||
hdr.sum == dev->checkpt_sum &&
|
|
||||||
hdr.xor == dev->checkpt_xor;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
|
||||||
"checkpt blocks_avail = %d", blocks_avail);
|
|
||||||
|
|
||||||
return (blocks_avail <= 0) ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_checkpt_erase(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!dev->drv.drv_erase_fn)
|
|
||||||
return 0;
|
|
||||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
|
||||||
"checking blocks %d to %d",
|
|
||||||
dev->internal_start_block, dev->internal_end_block);
|
|
||||||
|
|
||||||
for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
|
|
||||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, i);
|
|
||||||
int offset_i = apply_block_offset(dev, i);
|
|
||||||
int result;
|
|
||||||
|
|
||||||
if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
|
||||||
"erasing checkpt block %d", i);
|
|
||||||
|
|
||||||
dev->n_erasures++;
|
|
||||||
|
|
||||||
result = dev->drv.drv_erase_fn(dev, offset_i);
|
|
||||||
if(result) {
|
|
||||||
bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
|
|
||||||
dev->n_erased_blocks++;
|
|
||||||
dev->n_free_chunks +=
|
|
||||||
dev->param.chunks_per_block;
|
|
||||||
} else {
|
|
||||||
dev->drv.drv_mark_bad_fn(dev, offset_i);
|
|
||||||
bi->block_state = YAFFS_BLOCK_STATE_DEAD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->blocks_in_checkpt = 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
|
||||||
"allocating checkpt block: erased %d reserved %d avail %d next %d ",
|
|
||||||
dev->n_erased_blocks, dev->param.n_reserved_blocks,
|
|
||||||
blocks_avail, dev->checkpt_next_block);
|
|
||||||
|
|
||||||
if (dev->checkpt_next_block >= 0 &&
|
|
||||||
dev->checkpt_next_block <= dev->internal_end_block &&
|
|
||||||
blocks_avail > 0) {
|
|
||||||
|
|
||||||
for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
|
|
||||||
i++) {
|
|
||||||
struct yaffs_block_info *bi;
|
|
||||||
|
|
||||||
bi = yaffs_get_block_info(dev, i);
|
|
||||||
if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {
|
|
||||||
dev->checkpt_next_block = i + 1;
|
|
||||||
dev->checkpt_cur_block = i;
|
|
||||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
|
||||||
"allocating checkpt block %d", i);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks");
|
|
||||||
|
|
||||||
dev->checkpt_next_block = -1;
|
|
||||||
dev->checkpt_cur_block = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void yaffs2_checkpt_find_block(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct yaffs_ext_tags tags;
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
|
||||||
"find next checkpt block: start: blocks %d next %d",
|
|
||||||
dev->blocks_in_checkpt, dev->checkpt_next_block);
|
|
||||||
|
|
||||||
if (dev->blocks_in_checkpt < dev->checkpt_max_blocks)
|
|
||||||
for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
|
|
||||||
i++) {
|
|
||||||
int chunk = i * dev->param.chunks_per_block;
|
|
||||||
enum yaffs_block_state state;
|
|
||||||
u32 seq;
|
|
||||||
|
|
||||||
dev->tagger.read_chunk_tags_fn(dev,
|
|
||||||
apply_chunk_offset(dev, chunk),
|
|
||||||
NULL, &tags);
|
|
||||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
|
||||||
"find next checkpt block: search: block %d state %d oid %d seq %d eccr %d",
|
|
||||||
i, (int) state,
|
|
||||||
tags.obj_id, tags.seq_number,
|
|
||||||
tags.ecc_result);
|
|
||||||
|
|
||||||
if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
dev->tagger.query_block_fn(dev,
|
|
||||||
apply_block_offset(dev, i),
|
|
||||||
&state, &seq);
|
|
||||||
if (state == YAFFS_BLOCK_STATE_DEAD)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Right kind of block */
|
|
||||||
dev->checkpt_next_block = tags.obj_id;
|
|
||||||
dev->checkpt_cur_block = i;
|
|
||||||
dev->checkpt_block_list[dev->blocks_in_checkpt] = i;
|
|
||||||
dev->blocks_in_checkpt++;
|
|
||||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
|
||||||
"found checkpt block %d", i);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks");
|
|
||||||
|
|
||||||
dev->checkpt_next_block = -1;
|
|
||||||
dev->checkpt_cur_block = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
dev->checkpt_open_write = writing;
|
|
||||||
|
|
||||||
/* Got the functions we need? */
|
|
||||||
if (!dev->tagger.write_chunk_tags_fn ||
|
|
||||||
!dev->tagger.read_chunk_tags_fn ||
|
|
||||||
!dev->drv.drv_erase_fn ||
|
|
||||||
!dev->drv.drv_mark_bad_fn)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (writing && !yaffs2_checkpt_space_ok(dev))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!dev->checkpt_buffer)
|
|
||||||
dev->checkpt_buffer =
|
|
||||||
kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
|
|
||||||
if (!dev->checkpt_buffer)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
dev->checkpt_page_seq = 0;
|
|
||||||
dev->checkpt_byte_count = 0;
|
|
||||||
dev->checkpt_sum = 0;
|
|
||||||
dev->checkpt_xor = 0;
|
|
||||||
dev->checkpt_cur_block = -1;
|
|
||||||
dev->checkpt_cur_chunk = -1;
|
|
||||||
dev->checkpt_next_block = dev->internal_start_block;
|
|
||||||
|
|
||||||
if (writing) {
|
|
||||||
memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
|
|
||||||
yaffs2_checkpt_init_chunk_hdr(dev);
|
|
||||||
return yaffs_checkpt_erase(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Opening for a read */
|
|
||||||
/* Set to a value that will kick off a read */
|
|
||||||
dev->checkpt_byte_offs = dev->data_bytes_per_chunk;
|
|
||||||
/* A checkpoint block list of 1 checkpoint block per 16 block is
|
|
||||||
* (hopefully) going to be way more than we need */
|
|
||||||
dev->blocks_in_checkpt = 0;
|
|
||||||
dev->checkpt_max_blocks =
|
|
||||||
(dev->internal_end_block - dev->internal_start_block) / 16 + 2;
|
|
||||||
if (!dev->checkpt_block_list)
|
|
||||||
dev->checkpt_block_list =
|
|
||||||
kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS);
|
|
||||||
|
|
||||||
if (!dev->checkpt_block_list)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (i = 0; i < dev->checkpt_max_blocks; i++)
|
|
||||||
dev->checkpt_block_list[i] = -1;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum)
|
|
||||||
{
|
|
||||||
u32 composite_sum;
|
|
||||||
|
|
||||||
composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff);
|
|
||||||
*sum = composite_sum;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
int chunk;
|
|
||||||
int offset_chunk;
|
|
||||||
struct yaffs_ext_tags tags;
|
|
||||||
|
|
||||||
if (dev->checkpt_cur_block < 0) {
|
|
||||||
yaffs2_checkpt_find_erased_block(dev);
|
|
||||||
dev->checkpt_cur_chunk = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev->checkpt_cur_block < 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
tags.is_deleted = 0;
|
|
||||||
tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */
|
|
||||||
tags.chunk_id = dev->checkpt_page_seq + 1;
|
|
||||||
tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA;
|
|
||||||
tags.n_bytes = dev->data_bytes_per_chunk;
|
|
||||||
if (dev->checkpt_cur_chunk == 0) {
|
|
||||||
/* First chunk we write for the block? Set block state to
|
|
||||||
checkpoint */
|
|
||||||
struct yaffs_block_info *bi =
|
|
||||||
yaffs_get_block_info(dev, dev->checkpt_cur_block);
|
|
||||||
bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
|
|
||||||
dev->blocks_in_checkpt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk =
|
|
||||||
dev->checkpt_cur_block * dev->param.chunks_per_block +
|
|
||||||
dev->checkpt_cur_chunk;
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
|
||||||
"checkpoint wite buffer nand %d(%d:%d) objid %d chId %d",
|
|
||||||
chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk,
|
|
||||||
tags.obj_id, tags.chunk_id);
|
|
||||||
|
|
||||||
offset_chunk = apply_chunk_offset(dev, chunk);
|
|
||||||
|
|
||||||
dev->n_page_writes++;
|
|
||||||
|
|
||||||
dev->tagger.write_chunk_tags_fn(dev, offset_chunk,
|
|
||||||
dev->checkpt_buffer, &tags);
|
|
||||||
dev->checkpt_page_seq++;
|
|
||||||
dev->checkpt_cur_chunk++;
|
|
||||||
if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) {
|
|
||||||
dev->checkpt_cur_chunk = 0;
|
|
||||||
dev->checkpt_cur_block = -1;
|
|
||||||
}
|
|
||||||
memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
|
|
||||||
|
|
||||||
yaffs2_checkpt_init_chunk_hdr(dev);
|
|
||||||
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
int ok = 1;
|
|
||||||
u8 *data_bytes = (u8 *) data;
|
|
||||||
|
|
||||||
if (!dev->checkpt_buffer)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!dev->checkpt_open_write)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
while (i < n_bytes && ok) {
|
|
||||||
dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes;
|
|
||||||
dev->checkpt_sum += *data_bytes;
|
|
||||||
dev->checkpt_xor ^= *data_bytes;
|
|
||||||
|
|
||||||
dev->checkpt_byte_offs++;
|
|
||||||
i++;
|
|
||||||
data_bytes++;
|
|
||||||
dev->checkpt_byte_count++;
|
|
||||||
|
|
||||||
if (dev->checkpt_byte_offs < 0 ||
|
|
||||||
dev->checkpt_byte_offs >= dev->data_bytes_per_chunk)
|
|
||||||
ok = yaffs2_checkpt_flush_buffer(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
struct yaffs_ext_tags tags;
|
|
||||||
int chunk;
|
|
||||||
int offset_chunk;
|
|
||||||
u8 *data_bytes = (u8 *) data;
|
|
||||||
|
|
||||||
if (!dev->checkpt_buffer)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (dev->checkpt_open_write)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
while (i < n_bytes) {
|
|
||||||
|
|
||||||
if (dev->checkpt_byte_offs < 0 ||
|
|
||||||
dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) {
|
|
||||||
|
|
||||||
if (dev->checkpt_cur_block < 0) {
|
|
||||||
yaffs2_checkpt_find_block(dev);
|
|
||||||
dev->checkpt_cur_chunk = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bail out if we can't find a checpoint block */
|
|
||||||
if (dev->checkpt_cur_block < 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
chunk = dev->checkpt_cur_block *
|
|
||||||
dev->param.chunks_per_block +
|
|
||||||
dev->checkpt_cur_chunk;
|
|
||||||
|
|
||||||
offset_chunk = apply_chunk_offset(dev, chunk);
|
|
||||||
dev->n_page_reads++;
|
|
||||||
|
|
||||||
/* Read in the next chunk */
|
|
||||||
dev->tagger.read_chunk_tags_fn(dev,
|
|
||||||
offset_chunk,
|
|
||||||
dev->checkpt_buffer,
|
|
||||||
&tags);
|
|
||||||
|
|
||||||
/* Bail out if the chunk is corrupted. */
|
|
||||||
if (tags.chunk_id != (dev->checkpt_page_seq + 1) ||
|
|
||||||
tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||
|
|
||||||
tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Bail out if it is not a checkpoint chunk. */
|
|
||||||
if(!yaffs2_checkpt_check_chunk_hdr(dev))
|
|
||||||
break;
|
|
||||||
|
|
||||||
dev->checkpt_page_seq++;
|
|
||||||
dev->checkpt_cur_chunk++;
|
|
||||||
|
|
||||||
if (dev->checkpt_cur_chunk >=
|
|
||||||
dev->param.chunks_per_block)
|
|
||||||
dev->checkpt_cur_block = -1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
*data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs];
|
|
||||||
dev->checkpt_sum += *data_bytes;
|
|
||||||
dev->checkpt_xor ^= *data_bytes;
|
|
||||||
dev->checkpt_byte_offs++;
|
|
||||||
i++;
|
|
||||||
data_bytes++;
|
|
||||||
dev->checkpt_byte_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return i; /* Number of bytes read */
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_checkpt_close(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (dev->checkpt_open_write) {
|
|
||||||
if (dev->checkpt_byte_offs !=
|
|
||||||
sizeof(sizeof(struct yaffs_checkpt_chunk_hdr)))
|
|
||||||
yaffs2_checkpt_flush_buffer(dev);
|
|
||||||
} else if (dev->checkpt_block_list) {
|
|
||||||
for (i = 0;
|
|
||||||
i < dev->blocks_in_checkpt &&
|
|
||||||
dev->checkpt_block_list[i] >= 0; i++) {
|
|
||||||
int blk = dev->checkpt_block_list[i];
|
|
||||||
struct yaffs_block_info *bi = NULL;
|
|
||||||
|
|
||||||
if (dev->internal_start_block <= blk &&
|
|
||||||
blk <= dev->internal_end_block)
|
|
||||||
bi = yaffs_get_block_info(dev, blk);
|
|
||||||
if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY)
|
|
||||||
bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->n_free_chunks -=
|
|
||||||
dev->blocks_in_checkpt * dev->param.chunks_per_block;
|
|
||||||
dev->n_erased_blocks -= dev->blocks_in_checkpt;
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d",
|
|
||||||
dev->checkpt_byte_count);
|
|
||||||
|
|
||||||
if (dev->checkpt_buffer)
|
|
||||||
return 1;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
/* Erase the checkpoint data */
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
|
||||||
"checkpoint invalidate of %d blocks",
|
|
||||||
dev->blocks_in_checkpt);
|
|
||||||
|
|
||||||
return yaffs_checkpt_erase(dev);
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YAFFS_CHECKPTRW_H__
|
|
||||||
#define __YAFFS_CHECKPTRW_H__
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
|
|
||||||
int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing);
|
|
||||||
|
|
||||||
int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes);
|
|
||||||
|
|
||||||
int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes);
|
|
||||||
|
|
||||||
int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum);
|
|
||||||
|
|
||||||
int yaffs_checkpt_close(struct yaffs_dev *dev);
|
|
||||||
|
|
||||||
int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,281 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* 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 code implements the ECC algorithm used in SmartMedia.
|
|
||||||
*
|
|
||||||
* The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
|
|
||||||
* The two unused bit are set to 1.
|
|
||||||
* The ECC can correct single bit errors in a 256-byte page of data. Thus, two
|
|
||||||
* such ECC blocks are used on a 512-byte NAND page.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "yportenv.h"
|
|
||||||
|
|
||||||
#include "yaffs_ecc.h"
|
|
||||||
|
|
||||||
/* Table generated by gen-ecc.c
|
|
||||||
* Using a table means we do not have to calculate p1..p4 and p1'..p4'
|
|
||||||
* for each byte of data. These are instead provided in a table in bits7..2.
|
|
||||||
* Bit 0 of each entry indicates whether the entry has an odd or even parity,
|
|
||||||
* and therefore this bytes influence on the line parity.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static const unsigned char column_parity_table[] = {
|
|
||||||
0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
|
|
||||||
0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
|
|
||||||
0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
|
|
||||||
0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
|
|
||||||
0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
|
|
||||||
0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
|
|
||||||
0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
|
|
||||||
0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
|
|
||||||
0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
|
|
||||||
0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
|
|
||||||
0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
|
|
||||||
0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
|
|
||||||
0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
|
|
||||||
0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
|
|
||||||
0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
|
|
||||||
0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
|
|
||||||
0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
|
|
||||||
0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
|
|
||||||
0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
|
|
||||||
0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
|
|
||||||
0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
|
|
||||||
0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
|
|
||||||
0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
|
|
||||||
0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
|
|
||||||
0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
|
|
||||||
0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
|
|
||||||
0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
|
|
||||||
0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
|
|
||||||
0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
|
|
||||||
0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
|
|
||||||
0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
|
|
||||||
0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Calculate the ECC for a 256-byte block of data */
|
|
||||||
void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
unsigned char col_parity = 0;
|
|
||||||
unsigned char line_parity = 0;
|
|
||||||
unsigned char line_parity_prime = 0;
|
|
||||||
unsigned char t;
|
|
||||||
unsigned char b;
|
|
||||||
|
|
||||||
for (i = 0; i < 256; i++) {
|
|
||||||
b = column_parity_table[*data++];
|
|
||||||
col_parity ^= b;
|
|
||||||
|
|
||||||
if (b & 0x01) { /* odd number of bits in the byte */
|
|
||||||
line_parity ^= i;
|
|
||||||
line_parity_prime ^= ~i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ecc[2] = (~col_parity) | 0x03;
|
|
||||||
|
|
||||||
t = 0;
|
|
||||||
if (line_parity & 0x80)
|
|
||||||
t |= 0x80;
|
|
||||||
if (line_parity_prime & 0x80)
|
|
||||||
t |= 0x40;
|
|
||||||
if (line_parity & 0x40)
|
|
||||||
t |= 0x20;
|
|
||||||
if (line_parity_prime & 0x40)
|
|
||||||
t |= 0x10;
|
|
||||||
if (line_parity & 0x20)
|
|
||||||
t |= 0x08;
|
|
||||||
if (line_parity_prime & 0x20)
|
|
||||||
t |= 0x04;
|
|
||||||
if (line_parity & 0x10)
|
|
||||||
t |= 0x02;
|
|
||||||
if (line_parity_prime & 0x10)
|
|
||||||
t |= 0x01;
|
|
||||||
ecc[1] = ~t;
|
|
||||||
|
|
||||||
t = 0;
|
|
||||||
if (line_parity & 0x08)
|
|
||||||
t |= 0x80;
|
|
||||||
if (line_parity_prime & 0x08)
|
|
||||||
t |= 0x40;
|
|
||||||
if (line_parity & 0x04)
|
|
||||||
t |= 0x20;
|
|
||||||
if (line_parity_prime & 0x04)
|
|
||||||
t |= 0x10;
|
|
||||||
if (line_parity & 0x02)
|
|
||||||
t |= 0x08;
|
|
||||||
if (line_parity_prime & 0x02)
|
|
||||||
t |= 0x04;
|
|
||||||
if (line_parity & 0x01)
|
|
||||||
t |= 0x02;
|
|
||||||
if (line_parity_prime & 0x01)
|
|
||||||
t |= 0x01;
|
|
||||||
ecc[0] = ~t;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Correct the ECC on a 256 byte block of data */
|
|
||||||
|
|
||||||
int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc,
|
|
||||||
const unsigned char *test_ecc)
|
|
||||||
{
|
|
||||||
unsigned char d0, d1, d2; /* deltas */
|
|
||||||
|
|
||||||
d0 = read_ecc[0] ^ test_ecc[0];
|
|
||||||
d1 = read_ecc[1] ^ test_ecc[1];
|
|
||||||
d2 = read_ecc[2] ^ test_ecc[2];
|
|
||||||
|
|
||||||
if ((d0 | d1 | d2) == 0)
|
|
||||||
return 0; /* no error */
|
|
||||||
|
|
||||||
if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 &&
|
|
||||||
((d1 ^ (d1 >> 1)) & 0x55) == 0x55 &&
|
|
||||||
((d2 ^ (d2 >> 1)) & 0x54) == 0x54) {
|
|
||||||
/* Single bit (recoverable) error in data */
|
|
||||||
|
|
||||||
unsigned byte;
|
|
||||||
unsigned bit;
|
|
||||||
|
|
||||||
bit = byte = 0;
|
|
||||||
|
|
||||||
if (d1 & 0x80)
|
|
||||||
byte |= 0x80;
|
|
||||||
if (d1 & 0x20)
|
|
||||||
byte |= 0x40;
|
|
||||||
if (d1 & 0x08)
|
|
||||||
byte |= 0x20;
|
|
||||||
if (d1 & 0x02)
|
|
||||||
byte |= 0x10;
|
|
||||||
if (d0 & 0x80)
|
|
||||||
byte |= 0x08;
|
|
||||||
if (d0 & 0x20)
|
|
||||||
byte |= 0x04;
|
|
||||||
if (d0 & 0x08)
|
|
||||||
byte |= 0x02;
|
|
||||||
if (d0 & 0x02)
|
|
||||||
byte |= 0x01;
|
|
||||||
|
|
||||||
if (d2 & 0x80)
|
|
||||||
bit |= 0x04;
|
|
||||||
if (d2 & 0x20)
|
|
||||||
bit |= 0x02;
|
|
||||||
if (d2 & 0x08)
|
|
||||||
bit |= 0x01;
|
|
||||||
|
|
||||||
data[byte] ^= (1 << bit);
|
|
||||||
|
|
||||||
return 1; /* Corrected the error */
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) {
|
|
||||||
/* Reccoverable error in ecc */
|
|
||||||
|
|
||||||
read_ecc[0] = test_ecc[0];
|
|
||||||
read_ecc[1] = test_ecc[1];
|
|
||||||
read_ecc[2] = test_ecc[2];
|
|
||||||
|
|
||||||
return 1; /* Corrected the error */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unrecoverable error */
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ECCxxxOther does ECC calcs on arbitrary n bytes of data
|
|
||||||
*/
|
|
||||||
void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes,
|
|
||||||
struct yaffs_ecc_other *ecc_other)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
unsigned char col_parity = 0;
|
|
||||||
unsigned line_parity = 0;
|
|
||||||
unsigned line_parity_prime = 0;
|
|
||||||
unsigned char b;
|
|
||||||
|
|
||||||
for (i = 0; i < n_bytes; i++) {
|
|
||||||
b = column_parity_table[*data++];
|
|
||||||
col_parity ^= b;
|
|
||||||
|
|
||||||
if (b & 0x01) {
|
|
||||||
/* odd number of bits in the byte */
|
|
||||||
line_parity ^= i;
|
|
||||||
line_parity_prime ^= ~i;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ecc_other->col_parity = (col_parity >> 2) & 0x3f;
|
|
||||||
ecc_other->line_parity = line_parity;
|
|
||||||
ecc_other->line_parity_prime = line_parity_prime;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes,
|
|
||||||
struct yaffs_ecc_other *read_ecc,
|
|
||||||
const struct yaffs_ecc_other *test_ecc)
|
|
||||||
{
|
|
||||||
unsigned char delta_col; /* column parity delta */
|
|
||||||
unsigned delta_line; /* line parity delta */
|
|
||||||
unsigned delta_line_prime; /* line parity delta */
|
|
||||||
unsigned bit;
|
|
||||||
|
|
||||||
delta_col = read_ecc->col_parity ^ test_ecc->col_parity;
|
|
||||||
delta_line = read_ecc->line_parity ^ test_ecc->line_parity;
|
|
||||||
delta_line_prime =
|
|
||||||
read_ecc->line_parity_prime ^ test_ecc->line_parity_prime;
|
|
||||||
|
|
||||||
if ((delta_col | delta_line | delta_line_prime) == 0)
|
|
||||||
return 0; /* no error */
|
|
||||||
|
|
||||||
if (delta_line == ~delta_line_prime &&
|
|
||||||
(((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) {
|
|
||||||
/* Single bit (recoverable) error in data */
|
|
||||||
|
|
||||||
bit = 0;
|
|
||||||
|
|
||||||
if (delta_col & 0x20)
|
|
||||||
bit |= 0x04;
|
|
||||||
if (delta_col & 0x08)
|
|
||||||
bit |= 0x02;
|
|
||||||
if (delta_col & 0x02)
|
|
||||||
bit |= 0x01;
|
|
||||||
|
|
||||||
if (delta_line >= n_bytes)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
data[delta_line] ^= (1 << bit);
|
|
||||||
|
|
||||||
return 1; /* corrected */
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((hweight32(delta_line) +
|
|
||||||
hweight32(delta_line_prime) +
|
|
||||||
hweight8(delta_col)) == 1) {
|
|
||||||
/* Reccoverable error in ecc */
|
|
||||||
|
|
||||||
*read_ecc = *test_ecc;
|
|
||||||
return 1; /* corrected */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unrecoverable error */
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This code implements the ECC algorithm used in SmartMedia.
|
|
||||||
*
|
|
||||||
* The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
|
|
||||||
* The two unused bit are set to 1.
|
|
||||||
* The ECC can correct single bit errors in a 256-byte page of data.
|
|
||||||
* Thus, two such ECC blocks are used on a 512-byte NAND page.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YAFFS_ECC_H__
|
|
||||||
#define __YAFFS_ECC_H__
|
|
||||||
|
|
||||||
struct yaffs_ecc_other {
|
|
||||||
unsigned char col_parity;
|
|
||||||
unsigned line_parity;
|
|
||||||
unsigned line_parity_prime;
|
|
||||||
};
|
|
||||||
|
|
||||||
void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc);
|
|
||||||
int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc,
|
|
||||||
const unsigned char *test_ecc);
|
|
||||||
|
|
||||||
void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes,
|
|
||||||
struct yaffs_ecc_other *ecc);
|
|
||||||
int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes,
|
|
||||||
struct yaffs_ecc_other *read_ecc,
|
|
||||||
const struct yaffs_ecc_other *test_ecc);
|
|
||||||
#endif
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YAFFS_GETBLOCKINFO_H__
|
|
||||||
#define __YAFFS_GETBLOCKINFO_H__
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
#include "yaffs_trace.h"
|
|
||||||
|
|
||||||
/* Function to manipulate block info */
|
|
||||||
static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev
|
|
||||||
*dev, int blk)
|
|
||||||
{
|
|
||||||
if (blk < dev->internal_start_block || blk > dev->internal_end_block) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
|
||||||
"**>> yaffs: get_block_info block %d is not valid",
|
|
||||||
blk);
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
return &dev->block_info[blk - dev->internal_start_block];
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YAFFS_LINUX_H__
|
|
||||||
#define __YAFFS_LINUX_H__
|
|
||||||
|
|
||||||
#include "yportenv.h"
|
|
||||||
|
|
||||||
struct yaffs_linux_context {
|
|
||||||
struct list_head context_list; /* List of these we have mounted */
|
|
||||||
struct yaffs_dev *dev;
|
|
||||||
struct super_block *super;
|
|
||||||
struct task_struct *bg_thread; /* Background thread for this device */
|
|
||||||
int bg_running;
|
|
||||||
struct mutex gross_lock; /* Gross locking mutex*/
|
|
||||||
u8 *spare_buffer; /* For mtdif2 use. Don't know the buffer size
|
|
||||||
* at compile time so we have to allocate it.
|
|
||||||
*/
|
|
||||||
struct list_head search_contexts;
|
|
||||||
struct task_struct *readdir_process;
|
|
||||||
unsigned mount_id;
|
|
||||||
int dirty;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context))
|
|
||||||
#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context))
|
|
||||||
|
|
||||||
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))
|
|
||||||
#define WRITE_SIZE_STR "writesize"
|
|
||||||
#define WRITE_SIZE(mtd) ((mtd)->writesize)
|
|
||||||
#else
|
|
||||||
#define WRITE_SIZE_STR "oobblock"
|
|
||||||
#define WRITE_SIZE(mtd) ((mtd)->oobblock)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,310 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "yportenv.h"
|
|
||||||
|
|
||||||
#include "yaffs_mtdif.h"
|
|
||||||
|
|
||||||
#include "linux/mtd/mtd.h"
|
|
||||||
#include "linux/types.h"
|
|
||||||
#include "linux/time.h"
|
|
||||||
#include "linux/mtd/nand.h"
|
|
||||||
#include "linux/kernel.h"
|
|
||||||
#include "linux/version.h"
|
|
||||||
#include "linux/types.h"
|
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
|
|
||||||
#include "uapi/linux/major.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "yaffs_trace.h"
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
#include "yaffs_linux.h"
|
|
||||||
|
|
||||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0))
|
|
||||||
#define MTD_OPS_AUTO_OOB MTD_OOB_AUTO
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0))
|
|
||||||
#define mtd_erase(m, ei) (m)->erase(m, ei)
|
|
||||||
#define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops)
|
|
||||||
#define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops)
|
|
||||||
#define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs)
|
|
||||||
#define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int nandmtd_erase_block(struct yaffs_dev *dev, int block_no)
|
|
||||||
{
|
|
||||||
struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
|
|
||||||
u32 addr =
|
|
||||||
((loff_t) block_no) * dev->param.total_bytes_per_chunk *
|
|
||||||
dev->param.chunks_per_block;
|
|
||||||
struct erase_info ei;
|
|
||||||
int retval = 0;
|
|
||||||
|
|
||||||
ei.mtd = mtd;
|
|
||||||
ei.addr = addr;
|
|
||||||
ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block;
|
|
||||||
ei.time = 1000;
|
|
||||||
ei.retries = 2;
|
|
||||||
ei.callback = NULL;
|
|
||||||
ei.priv = (u_long) dev;
|
|
||||||
|
|
||||||
retval = mtd_erase(mtd, &ei);
|
|
||||||
|
|
||||||
if (retval == 0)
|
|
||||||
return YAFFS_OK;
|
|
||||||
|
|
||||||
return YAFFS_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk,
|
|
||||||
const u8 *data, int data_len,
|
|
||||||
const u8 *oob, int oob_len)
|
|
||||||
{
|
|
||||||
struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
|
|
||||||
loff_t addr;
|
|
||||||
struct mtd_oob_ops ops;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_MTD,
|
|
||||||
"yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n",
|
|
||||||
dev, nand_chunk, data, data_len, oob, oob_len);
|
|
||||||
|
|
||||||
if (!data || !data_len) {
|
|
||||||
data = NULL;
|
|
||||||
data_len = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!oob || !oob_len) {
|
|
||||||
oob = NULL;
|
|
||||||
oob_len = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
|
|
||||||
memset(&ops, 0, sizeof(ops));
|
|
||||||
ops.mode = MTD_OPS_AUTO_OOB;
|
|
||||||
ops.len = (data) ? data_len : 0;
|
|
||||||
ops.ooblen = oob_len;
|
|
||||||
ops.datbuf = (u8 *)data;
|
|
||||||
ops.oobbuf = (u8 *)oob;
|
|
||||||
|
|
||||||
retval = mtd_write_oob(mtd, addr, &ops);
|
|
||||||
if (retval) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_MTD,
|
|
||||||
"write_oob failed, chunk %d, mtd error %d",
|
|
||||||
nand_chunk, retval);
|
|
||||||
}
|
|
||||||
return retval ? YAFFS_FAIL : YAFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk,
|
|
||||||
u8 *data, int data_len,
|
|
||||||
u8 *oob, int oob_len,
|
|
||||||
enum yaffs_ecc_result *ecc_result)
|
|
||||||
{
|
|
||||||
struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
|
|
||||||
loff_t addr;
|
|
||||||
struct mtd_oob_ops ops;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
|
|
||||||
memset(&ops, 0, sizeof(ops));
|
|
||||||
ops.mode = MTD_OPS_AUTO_OOB;
|
|
||||||
ops.len = (data) ? data_len : 0;
|
|
||||||
ops.ooblen = oob_len;
|
|
||||||
ops.datbuf = data;
|
|
||||||
ops.oobbuf = oob;
|
|
||||||
|
|
||||||
#if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20))
|
|
||||||
/* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug;
|
|
||||||
* help it out with ops.len = ops.ooblen when ops.datbuf == NULL.
|
|
||||||
*/
|
|
||||||
ops.len = (ops.datbuf) ? ops.len : ops.ooblen;
|
|
||||||
#endif
|
|
||||||
/* Read page and oob using MTD.
|
|
||||||
* Check status and determine ECC result.
|
|
||||||
*/
|
|
||||||
retval = mtd_read_oob(mtd, addr, &ops);
|
|
||||||
if (retval)
|
|
||||||
yaffs_trace(YAFFS_TRACE_MTD,
|
|
||||||
"read_oob failed, chunk %d, mtd error %d",
|
|
||||||
nand_chunk, retval);
|
|
||||||
|
|
||||||
switch (retval) {
|
|
||||||
case 0:
|
|
||||||
/* no error */
|
|
||||||
if(ecc_result)
|
|
||||||
*ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case -EUCLEAN:
|
|
||||||
/* MTD's ECC fixed the data */
|
|
||||||
if(ecc_result)
|
|
||||||
*ecc_result = YAFFS_ECC_RESULT_FIXED;
|
|
||||||
dev->n_ecc_fixed++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case -EBADMSG:
|
|
||||||
default:
|
|
||||||
/* MTD's ECC could not fix the data */
|
|
||||||
dev->n_ecc_unfixed++;
|
|
||||||
if(ecc_result)
|
|
||||||
*ecc_result = YAFFS_ECC_RESULT_UNFIXED;
|
|
||||||
return YAFFS_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no)
|
|
||||||
{
|
|
||||||
struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
|
|
||||||
|
|
||||||
loff_t addr;
|
|
||||||
struct erase_info ei;
|
|
||||||
int retval = 0;
|
|
||||||
u32 block_size;
|
|
||||||
|
|
||||||
block_size = dev->param.total_bytes_per_chunk *
|
|
||||||
dev->param.chunks_per_block;
|
|
||||||
addr = ((loff_t) block_no) * block_size;
|
|
||||||
|
|
||||||
ei.mtd = mtd;
|
|
||||||
ei.addr = addr;
|
|
||||||
ei.len = block_size;
|
|
||||||
ei.time = 1000;
|
|
||||||
ei.retries = 2;
|
|
||||||
ei.callback = NULL;
|
|
||||||
ei.priv = (u_long) dev;
|
|
||||||
|
|
||||||
retval = mtd_erase(mtd, &ei);
|
|
||||||
|
|
||||||
if (retval == 0)
|
|
||||||
return YAFFS_OK;
|
|
||||||
|
|
||||||
return YAFFS_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no)
|
|
||||||
{
|
|
||||||
struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
|
|
||||||
int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no);
|
|
||||||
|
|
||||||
retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no);
|
|
||||||
return (retval) ? YAFFS_FAIL : YAFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no)
|
|
||||||
{
|
|
||||||
struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
|
|
||||||
int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no);
|
|
||||||
|
|
||||||
retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no);
|
|
||||||
return (retval) ? YAFFS_FAIL : YAFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_mtd_initialise(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_mtd_deinitialise(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void yaffs_mtd_drv_install(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
struct yaffs_driver *drv = &dev->drv;
|
|
||||||
|
|
||||||
drv->drv_write_chunk_fn = yaffs_mtd_write;
|
|
||||||
drv->drv_read_chunk_fn = yaffs_mtd_read;
|
|
||||||
drv->drv_erase_fn = yaffs_mtd_erase;
|
|
||||||
drv->drv_mark_bad_fn = yaffs_mtd_mark_bad;
|
|
||||||
drv->drv_check_bad_fn = yaffs_mtd_check_bad;
|
|
||||||
drv->drv_initialise_fn = yaffs_mtd_initialise;
|
|
||||||
drv->drv_deinitialise_fn = yaffs_mtd_deinitialise;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct mtd_info * yaffs_get_mtd_device(dev_t sdev)
|
|
||||||
{
|
|
||||||
struct mtd_info *mtd;
|
|
||||||
|
|
||||||
mtd = yaffs_get_mtd_device(sdev);
|
|
||||||
|
|
||||||
/* Check it's an mtd device..... */
|
|
||||||
if (MAJOR(sdev) != MTD_BLOCK_MAJOR)
|
|
||||||
return NULL; /* This isn't an mtd device */
|
|
||||||
|
|
||||||
/* Check it's NAND */
|
|
||||||
if (mtd->type != MTD_NANDFLASH && mtd->type != MTD_MLCNANDFLASH) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
|
||||||
"yaffs: MTD device is not NAND it's type %d",
|
|
||||||
mtd->type);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd));
|
|
||||||
yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize);
|
|
||||||
yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize);
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
|
|
||||||
yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size);
|
|
||||||
#else
|
|
||||||
yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return mtd;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags)
|
|
||||||
{
|
|
||||||
if (yaffs_version == 2) {
|
|
||||||
if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
|
|
||||||
mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) &&
|
|
||||||
!inband_tags) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
|
||||||
"MTD device does not have the right page sizes"
|
|
||||||
);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK ||
|
|
||||||
mtd->oobsize != YAFFS_BYTES_PER_SPARE) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
|
||||||
"MTD device does not support have the right page sizes"
|
|
||||||
);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void yaffs_put_mtd_device(struct mtd_info *mtd)
|
|
||||||
{
|
|
||||||
if(mtd)
|
|
||||||
put_mtd_device(mtd);
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YAFFS_MTDIF_H__
|
|
||||||
#define __YAFFS_MTDIF_H__
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
|
|
||||||
void yaffs_mtd_drv_install(struct yaffs_dev *dev);
|
|
||||||
struct mtd_info * yaffs_get_mtd_device(dev_t sdev);
|
|
||||||
void yaffs_put_mtd_device(struct mtd_info *mtd);
|
|
||||||
int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags);
|
|
||||||
#endif
|
|
|
@ -1,208 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* 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 simple implementation of a name-value store assumes a small number of
|
|
||||||
* values and fits into a small finite buffer.
|
|
||||||
*
|
|
||||||
* Each attribute is stored as a record:
|
|
||||||
* sizeof(int) bytes record size.
|
|
||||||
* strnlen+1 bytes name null terminated.
|
|
||||||
* nbytes value.
|
|
||||||
* ----------
|
|
||||||
* total size stored in record size
|
|
||||||
*
|
|
||||||
* This code has not been tested with unicode yet.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "yaffs_nameval.h"
|
|
||||||
|
|
||||||
#include "yportenv.h"
|
|
||||||
|
|
||||||
static int nval_find(const char *xb, int xb_size, const YCHAR *name,
|
|
||||||
int *exist_size)
|
|
||||||
{
|
|
||||||
int pos = 0;
|
|
||||||
int size;
|
|
||||||
|
|
||||||
memcpy(&size, xb, sizeof(int));
|
|
||||||
while (size > 0 && (size < xb_size) && (pos + size < xb_size)) {
|
|
||||||
if (!strncmp((YCHAR *) (xb + pos + sizeof(int)),
|
|
||||||
name, size)) {
|
|
||||||
if (exist_size)
|
|
||||||
*exist_size = size;
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
pos += size;
|
|
||||||
if (pos < xb_size - sizeof(int))
|
|
||||||
memcpy(&size, xb + pos, sizeof(int));
|
|
||||||
else
|
|
||||||
size = 0;
|
|
||||||
}
|
|
||||||
if (exist_size)
|
|
||||||
*exist_size = 0;
|
|
||||||
return -ENODATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nval_used(const char *xb, int xb_size)
|
|
||||||
{
|
|
||||||
int pos = 0;
|
|
||||||
int size;
|
|
||||||
|
|
||||||
memcpy(&size, xb + pos, sizeof(int));
|
|
||||||
while (size > 0 && (size < xb_size) && (pos + size < xb_size)) {
|
|
||||||
pos += size;
|
|
||||||
if (pos < xb_size - sizeof(int))
|
|
||||||
memcpy(&size, xb + pos, sizeof(int));
|
|
||||||
else
|
|
||||||
size = 0;
|
|
||||||
}
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nval_del(char *xb, int xb_size, const YCHAR *name)
|
|
||||||
{
|
|
||||||
int pos = nval_find(xb, xb_size, name, NULL);
|
|
||||||
int size;
|
|
||||||
|
|
||||||
if (pos < 0 || pos >= xb_size)
|
|
||||||
return -ENODATA;
|
|
||||||
|
|
||||||
/* Find size, shift rest over this record,
|
|
||||||
* then zero out the rest of buffer */
|
|
||||||
memcpy(&size, xb + pos, sizeof(int));
|
|
||||||
memcpy(xb + pos, xb + pos + size, xb_size - (pos + size));
|
|
||||||
memset(xb + (xb_size - size), 0, size);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf,
|
|
||||||
int bsize, int flags)
|
|
||||||
{
|
|
||||||
int pos;
|
|
||||||
int namelen = strnlen(name, xb_size);
|
|
||||||
int reclen;
|
|
||||||
int size_exist = 0;
|
|
||||||
int space;
|
|
||||||
int start;
|
|
||||||
|
|
||||||
pos = nval_find(xb, xb_size, name, &size_exist);
|
|
||||||
|
|
||||||
if (flags & XATTR_CREATE && pos >= 0)
|
|
||||||
return -EEXIST;
|
|
||||||
if (flags & XATTR_REPLACE && pos < 0)
|
|
||||||
return -ENODATA;
|
|
||||||
|
|
||||||
start = nval_used(xb, xb_size);
|
|
||||||
space = xb_size - start + size_exist;
|
|
||||||
|
|
||||||
reclen = (sizeof(int) + namelen + 1 + bsize);
|
|
||||||
|
|
||||||
if (reclen > space)
|
|
||||||
return -ENOSPC;
|
|
||||||
|
|
||||||
if (pos >= 0) {
|
|
||||||
nval_del(xb, xb_size, name);
|
|
||||||
start = nval_used(xb, xb_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = start;
|
|
||||||
|
|
||||||
memcpy(xb + pos, &reclen, sizeof(int));
|
|
||||||
pos += sizeof(int);
|
|
||||||
strncpy((YCHAR *) (xb + pos), name, reclen);
|
|
||||||
pos += (namelen + 1);
|
|
||||||
memcpy(xb + pos, buf, bsize);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf,
|
|
||||||
int bsize)
|
|
||||||
{
|
|
||||||
int pos = nval_find(xb, xb_size, name, NULL);
|
|
||||||
int size;
|
|
||||||
|
|
||||||
if (pos >= 0 && pos < xb_size) {
|
|
||||||
|
|
||||||
memcpy(&size, xb + pos, sizeof(int));
|
|
||||||
pos += sizeof(int); /* advance past record length */
|
|
||||||
size -= sizeof(int);
|
|
||||||
|
|
||||||
/* Advance over name string */
|
|
||||||
while (xb[pos] && size > 0 && pos < xb_size) {
|
|
||||||
pos++;
|
|
||||||
size--;
|
|
||||||
}
|
|
||||||
/*Advance over NUL */
|
|
||||||
pos++;
|
|
||||||
size--;
|
|
||||||
|
|
||||||
/* If bsize is zero then this is a size query.
|
|
||||||
* Return the size, but don't copy.
|
|
||||||
*/
|
|
||||||
if (!bsize)
|
|
||||||
return size;
|
|
||||||
|
|
||||||
if (size <= bsize) {
|
|
||||||
memcpy(buf, xb + pos, size);
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pos >= 0)
|
|
||||||
return -ERANGE;
|
|
||||||
|
|
||||||
return -ENODATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nval_list(const char *xb, int xb_size, char *buf, int bsize)
|
|
||||||
{
|
|
||||||
int pos = 0;
|
|
||||||
int size;
|
|
||||||
int name_len;
|
|
||||||
int ncopied = 0;
|
|
||||||
int filled = 0;
|
|
||||||
|
|
||||||
memcpy(&size, xb + pos, sizeof(int));
|
|
||||||
while (size > sizeof(int) &&
|
|
||||||
size <= xb_size &&
|
|
||||||
(pos + size) < xb_size &&
|
|
||||||
!filled) {
|
|
||||||
pos += sizeof(int);
|
|
||||||
size -= sizeof(int);
|
|
||||||
name_len = strnlen((YCHAR *) (xb + pos), size);
|
|
||||||
if (ncopied + name_len + 1 < bsize) {
|
|
||||||
memcpy(buf, xb + pos, name_len * sizeof(YCHAR));
|
|
||||||
buf += name_len;
|
|
||||||
*buf = '\0';
|
|
||||||
buf++;
|
|
||||||
if (sizeof(YCHAR) > 1) {
|
|
||||||
*buf = '\0';
|
|
||||||
buf++;
|
|
||||||
}
|
|
||||||
ncopied += (name_len + 1);
|
|
||||||
} else {
|
|
||||||
filled = 1;
|
|
||||||
}
|
|
||||||
pos += size;
|
|
||||||
if (pos < xb_size - sizeof(int))
|
|
||||||
memcpy(&size, xb + pos, sizeof(int));
|
|
||||||
else
|
|
||||||
size = 0;
|
|
||||||
}
|
|
||||||
return ncopied;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nval_hasvalues(const char *xb, int xb_size)
|
|
||||||
{
|
|
||||||
return nval_used(xb, xb_size) > 0;
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __NAMEVAL_H__
|
|
||||||
#define __NAMEVAL_H__
|
|
||||||
|
|
||||||
#include "yportenv.h"
|
|
||||||
|
|
||||||
int nval_del(char *xb, int xb_size, const YCHAR * name);
|
|
||||||
int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf,
|
|
||||||
int bsize, int flags);
|
|
||||||
int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf,
|
|
||||||
int bsize);
|
|
||||||
int nval_list(const char *xb, int xb_size, char *buf, int bsize);
|
|
||||||
int nval_hasvalues(const char *xb, int xb_size);
|
|
||||||
#endif
|
|
|
@ -1,122 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "yaffs_nand.h"
|
|
||||||
#include "yaffs_tagscompat.h"
|
|
||||||
|
|
||||||
#include "yaffs_getblockinfo.h"
|
|
||||||
#include "yaffs_summary.h"
|
|
||||||
|
|
||||||
static int apply_chunk_offset(struct yaffs_dev *dev, int chunk)
|
|
||||||
{
|
|
||||||
return chunk - dev->chunk_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk,
|
|
||||||
u8 *buffer, struct yaffs_ext_tags *tags)
|
|
||||||
{
|
|
||||||
int result;
|
|
||||||
struct yaffs_ext_tags local_tags;
|
|
||||||
int flash_chunk = apply_chunk_offset(dev, nand_chunk);
|
|
||||||
|
|
||||||
dev->n_page_reads++;
|
|
||||||
|
|
||||||
/* If there are no tags provided use local tags. */
|
|
||||||
if (!tags)
|
|
||||||
tags = &local_tags;
|
|
||||||
|
|
||||||
result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags);
|
|
||||||
if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) {
|
|
||||||
|
|
||||||
struct yaffs_block_info *bi;
|
|
||||||
bi = yaffs_get_block_info(dev,
|
|
||||||
nand_chunk /
|
|
||||||
dev->param.chunks_per_block);
|
|
||||||
yaffs_handle_chunk_error(dev, bi);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev,
|
|
||||||
int nand_chunk,
|
|
||||||
const u8 *buffer, struct yaffs_ext_tags *tags)
|
|
||||||
{
|
|
||||||
int result;
|
|
||||||
int flash_chunk = apply_chunk_offset(dev, nand_chunk);
|
|
||||||
|
|
||||||
dev->n_page_writes++;
|
|
||||||
|
|
||||||
if (!tags) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags");
|
|
||||||
BUG();
|
|
||||||
return YAFFS_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
tags->seq_number = dev->seq_number;
|
|
||||||
tags->chunk_used = 1;
|
|
||||||
yaffs_trace(YAFFS_TRACE_WRITE,
|
|
||||||
"Writing chunk %d tags %d %d",
|
|
||||||
nand_chunk, tags->obj_id, tags->chunk_id);
|
|
||||||
|
|
||||||
result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk,
|
|
||||||
buffer, tags);
|
|
||||||
|
|
||||||
yaffs_summary_add(dev, tags, nand_chunk);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_mark_bad(struct yaffs_dev *dev, int block_no)
|
|
||||||
{
|
|
||||||
block_no -= dev->block_offset;
|
|
||||||
dev->n_bad_markings++;
|
|
||||||
|
|
||||||
if (dev->param.disable_bad_block_marking)
|
|
||||||
return YAFFS_OK;
|
|
||||||
|
|
||||||
return dev->tagger.mark_bad_fn(dev, block_no);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int yaffs_query_init_block_state(struct yaffs_dev *dev,
|
|
||||||
int block_no,
|
|
||||||
enum yaffs_block_state *state,
|
|
||||||
u32 *seq_number)
|
|
||||||
{
|
|
||||||
block_no -= dev->block_offset;
|
|
||||||
return dev->tagger.query_block_fn(dev, block_no, state, seq_number);
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_erase_block(struct yaffs_dev *dev, int block_no)
|
|
||||||
{
|
|
||||||
int result;
|
|
||||||
|
|
||||||
block_no -= dev->block_offset;
|
|
||||||
dev->n_erasures++;
|
|
||||||
result = dev->drv.drv_erase_fn(dev, block_no);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_init_nand(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
if (dev->drv.drv_initialise_fn)
|
|
||||||
return dev->drv.drv_initialise_fn(dev);
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_deinit_nand(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
if (dev->drv.drv_deinitialise_fn)
|
|
||||||
return dev->drv.drv_deinitialise_fn(dev);
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YAFFS_NAND_H__
|
|
||||||
#define __YAFFS_NAND_H__
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
|
|
||||||
int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk,
|
|
||||||
u8 *buffer, struct yaffs_ext_tags *tags);
|
|
||||||
|
|
||||||
int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev,
|
|
||||||
int nand_chunk,
|
|
||||||
const u8 *buffer, struct yaffs_ext_tags *tags);
|
|
||||||
|
|
||||||
int yaffs_mark_bad(struct yaffs_dev *dev, int block_no);
|
|
||||||
|
|
||||||
int yaffs_query_init_block_state(struct yaffs_dev *dev,
|
|
||||||
int block_no,
|
|
||||||
enum yaffs_block_state *state,
|
|
||||||
unsigned *seq_number);
|
|
||||||
|
|
||||||
int yaffs_erase_block(struct yaffs_dev *dev, int flash_block);
|
|
||||||
|
|
||||||
int yaffs_init_nand(struct yaffs_dev *dev);
|
|
||||||
int yaffs_deinit_nand(struct yaffs_dev *dev);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "yaffs_packedtags1.h"
|
|
||||||
#include "yportenv.h"
|
|
||||||
|
|
||||||
static const u8 all_ff[20] = {
|
|
||||||
0xff, 0xff, 0xff, 0xff,
|
|
||||||
0xff, 0xff, 0xff, 0xff,
|
|
||||||
0xff, 0xff, 0xff, 0xff,
|
|
||||||
0xff, 0xff, 0xff, 0xff,
|
|
||||||
0xff, 0xff, 0xff, 0xff
|
|
||||||
};
|
|
||||||
|
|
||||||
void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt,
|
|
||||||
const struct yaffs_ext_tags *t)
|
|
||||||
{
|
|
||||||
pt->chunk_id = t->chunk_id;
|
|
||||||
pt->serial_number = t->serial_number;
|
|
||||||
pt->n_bytes = t->n_bytes;
|
|
||||||
pt->obj_id = t->obj_id;
|
|
||||||
pt->ecc = 0;
|
|
||||||
pt->deleted = (t->is_deleted) ? 0 : 1;
|
|
||||||
pt->unused_stuff = 0;
|
|
||||||
pt->should_be_ff = 0xffffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_unpack_tags1(struct yaffs_ext_tags *t,
|
|
||||||
const struct yaffs_packed_tags1 *pt)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) {
|
|
||||||
t->block_bad = 0;
|
|
||||||
if (pt->should_be_ff != 0xffffffff)
|
|
||||||
t->block_bad = 1;
|
|
||||||
t->chunk_used = 1;
|
|
||||||
t->obj_id = pt->obj_id;
|
|
||||||
t->chunk_id = pt->chunk_id;
|
|
||||||
t->n_bytes = pt->n_bytes;
|
|
||||||
t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
|
|
||||||
t->is_deleted = (pt->deleted) ? 0 : 1;
|
|
||||||
t->serial_number = pt->serial_number;
|
|
||||||
} else {
|
|
||||||
memset(t, 0, sizeof(struct yaffs_ext_tags));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */
|
|
||||||
|
|
||||||
#ifndef __YAFFS_PACKEDTAGS1_H__
|
|
||||||
#define __YAFFS_PACKEDTAGS1_H__
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
|
|
||||||
struct yaffs_packed_tags1 {
|
|
||||||
u32 chunk_id:20;
|
|
||||||
u32 serial_number:2;
|
|
||||||
u32 n_bytes:10;
|
|
||||||
u32 obj_id:18;
|
|
||||||
u32 ecc:12;
|
|
||||||
u32 deleted:1;
|
|
||||||
u32 unused_stuff:1;
|
|
||||||
unsigned should_be_ff;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt,
|
|
||||||
const struct yaffs_ext_tags *t);
|
|
||||||
void yaffs_unpack_tags1(struct yaffs_ext_tags *t,
|
|
||||||
const struct yaffs_packed_tags1 *pt);
|
|
||||||
#endif
|
|
|
@ -1,197 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "yaffs_packedtags2.h"
|
|
||||||
#include "yportenv.h"
|
|
||||||
#include "yaffs_trace.h"
|
|
||||||
|
|
||||||
/* This code packs a set of extended tags into a binary structure for
|
|
||||||
* NAND storage
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Some of the information is "extra" struff which can be packed in to
|
|
||||||
* speed scanning
|
|
||||||
* This is defined by having the EXTRA_HEADER_INFO_FLAG set.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Extra flags applied to chunk_id */
|
|
||||||
|
|
||||||
#define EXTRA_HEADER_INFO_FLAG 0x80000000
|
|
||||||
#define EXTRA_SHRINK_FLAG 0x40000000
|
|
||||||
#define EXTRA_SHADOWS_FLAG 0x20000000
|
|
||||||
#define EXTRA_SPARE_FLAGS 0x10000000
|
|
||||||
|
|
||||||
#define ALL_EXTRA_FLAGS 0xf0000000
|
|
||||||
|
|
||||||
/* Also, the top 4 bits of the object Id are set to the object type. */
|
|
||||||
#define EXTRA_OBJECT_TYPE_SHIFT (28)
|
|
||||||
#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT)
|
|
||||||
|
|
||||||
static void yaffs_dump_packed_tags2_tags_only(
|
|
||||||
const struct yaffs_packed_tags2_tags_only *ptt)
|
|
||||||
{
|
|
||||||
yaffs_trace(YAFFS_TRACE_MTD,
|
|
||||||
"packed tags obj %d chunk %d byte %d seq %d",
|
|
||||||
ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt)
|
|
||||||
{
|
|
||||||
yaffs_dump_packed_tags2_tags_only(&pt->t);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void yaffs_dump_tags2(const struct yaffs_ext_tags *t)
|
|
||||||
{
|
|
||||||
yaffs_trace(YAFFS_TRACE_MTD,
|
|
||||||
"ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d",
|
|
||||||
t->ecc_result, t->block_bad, t->chunk_used, t->obj_id,
|
|
||||||
t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number,
|
|
||||||
t->seq_number);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t)
|
|
||||||
{
|
|
||||||
if (t->chunk_id != 0 || !t->extra_available)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Check if the file size is too long to store */
|
|
||||||
if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE &&
|
|
||||||
(t->extra_file_size >> 31) != 0)
|
|
||||||
return 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt,
|
|
||||||
const struct yaffs_ext_tags *t)
|
|
||||||
{
|
|
||||||
ptt->chunk_id = t->chunk_id;
|
|
||||||
ptt->seq_number = t->seq_number;
|
|
||||||
ptt->n_bytes = t->n_bytes;
|
|
||||||
ptt->obj_id = t->obj_id;
|
|
||||||
|
|
||||||
/* Only store extra tags for object headers.
|
|
||||||
* If it is a file then only store if the file size is short\
|
|
||||||
* enough to fit.
|
|
||||||
*/
|
|
||||||
if (yaffs_check_tags_extra_packable(t)) {
|
|
||||||
/* Store the extra header info instead */
|
|
||||||
/* We save the parent object in the chunk_id */
|
|
||||||
ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id;
|
|
||||||
if (t->extra_is_shrink)
|
|
||||||
ptt->chunk_id |= EXTRA_SHRINK_FLAG;
|
|
||||||
if (t->extra_shadows)
|
|
||||||
ptt->chunk_id |= EXTRA_SHADOWS_FLAG;
|
|
||||||
|
|
||||||
ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK;
|
|
||||||
ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT);
|
|
||||||
|
|
||||||
if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK)
|
|
||||||
ptt->n_bytes = t->extra_equiv_id;
|
|
||||||
else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE)
|
|
||||||
ptt->n_bytes = (unsigned) t->extra_file_size;
|
|
||||||
else
|
|
||||||
ptt->n_bytes = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
yaffs_dump_packed_tags2_tags_only(ptt);
|
|
||||||
yaffs_dump_tags2(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt,
|
|
||||||
const struct yaffs_ext_tags *t, int tags_ecc)
|
|
||||||
{
|
|
||||||
yaffs_pack_tags2_tags_only(&pt->t, t);
|
|
||||||
|
|
||||||
if (tags_ecc)
|
|
||||||
yaffs_ecc_calc_other((unsigned char *)&pt->t,
|
|
||||||
sizeof(struct yaffs_packed_tags2_tags_only),
|
|
||||||
&pt->ecc);
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t,
|
|
||||||
struct yaffs_packed_tags2_tags_only *ptt)
|
|
||||||
{
|
|
||||||
memset(t, 0, sizeof(struct yaffs_ext_tags));
|
|
||||||
|
|
||||||
if (ptt->seq_number == 0xffffffff)
|
|
||||||
return;
|
|
||||||
|
|
||||||
t->block_bad = 0;
|
|
||||||
t->chunk_used = 1;
|
|
||||||
t->obj_id = ptt->obj_id;
|
|
||||||
t->chunk_id = ptt->chunk_id;
|
|
||||||
t->n_bytes = ptt->n_bytes;
|
|
||||||
t->is_deleted = 0;
|
|
||||||
t->serial_number = 0;
|
|
||||||
t->seq_number = ptt->seq_number;
|
|
||||||
|
|
||||||
/* Do extra header info stuff */
|
|
||||||
if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) {
|
|
||||||
t->chunk_id = 0;
|
|
||||||
t->n_bytes = 0;
|
|
||||||
|
|
||||||
t->extra_available = 1;
|
|
||||||
t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS));
|
|
||||||
t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0;
|
|
||||||
t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0;
|
|
||||||
t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT;
|
|
||||||
t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK;
|
|
||||||
|
|
||||||
if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK)
|
|
||||||
t->extra_equiv_id = ptt->n_bytes;
|
|
||||||
else
|
|
||||||
t->extra_file_size = ptt->n_bytes;
|
|
||||||
}
|
|
||||||
yaffs_dump_packed_tags2_tags_only(ptt);
|
|
||||||
yaffs_dump_tags2(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt,
|
|
||||||
int tags_ecc)
|
|
||||||
{
|
|
||||||
enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
|
|
||||||
|
|
||||||
if (pt->t.seq_number != 0xffffffff && tags_ecc) {
|
|
||||||
/* Chunk is in use and we need to do ECC */
|
|
||||||
|
|
||||||
struct yaffs_ecc_other ecc;
|
|
||||||
int result;
|
|
||||||
yaffs_ecc_calc_other((unsigned char *)&pt->t,
|
|
||||||
sizeof(struct yaffs_packed_tags2_tags_only),
|
|
||||||
&ecc);
|
|
||||||
result =
|
|
||||||
yaffs_ecc_correct_other((unsigned char *)&pt->t,
|
|
||||||
sizeof(struct yaffs_packed_tags2_tags_only),
|
|
||||||
&pt->ecc, &ecc);
|
|
||||||
switch (result) {
|
|
||||||
case 0:
|
|
||||||
ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
ecc_result = YAFFS_ECC_RESULT_FIXED;
|
|
||||||
break;
|
|
||||||
case -1:
|
|
||||||
ecc_result = YAFFS_ECC_RESULT_UNFIXED;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ecc_result = YAFFS_ECC_RESULT_UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
yaffs_unpack_tags2_tags_only(t, &pt->t);
|
|
||||||
|
|
||||||
t->ecc_result = ecc_result;
|
|
||||||
|
|
||||||
yaffs_dump_packed_tags2(pt);
|
|
||||||
yaffs_dump_tags2(t);
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* This is used to pack YAFFS2 tags, not YAFFS1tags. */
|
|
||||||
|
|
||||||
#ifndef __YAFFS_PACKEDTAGS2_H__
|
|
||||||
#define __YAFFS_PACKEDTAGS2_H__
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
#include "yaffs_ecc.h"
|
|
||||||
|
|
||||||
struct yaffs_packed_tags2_tags_only {
|
|
||||||
unsigned seq_number;
|
|
||||||
unsigned obj_id;
|
|
||||||
unsigned chunk_id;
|
|
||||||
unsigned n_bytes;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct yaffs_packed_tags2 {
|
|
||||||
struct yaffs_packed_tags2_tags_only t;
|
|
||||||
struct yaffs_ecc_other ecc;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Full packed tags with ECC, used for oob tags */
|
|
||||||
void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt,
|
|
||||||
const struct yaffs_ext_tags *t, int tags_ecc);
|
|
||||||
void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt,
|
|
||||||
int tags_ecc);
|
|
||||||
|
|
||||||
/* Only the tags part (no ECC for use with inband tags */
|
|
||||||
void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt,
|
|
||||||
const struct yaffs_ext_tags *t);
|
|
||||||
void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t,
|
|
||||||
struct yaffs_packed_tags2_tags_only *pt);
|
|
||||||
#endif
|
|
|
@ -1,312 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Summaries write the useful part of the tags for the chunks in a block into an
|
|
||||||
* an array which is written to the last n chunks of the block.
|
|
||||||
* Reading the summaries gives all the tags for the block in one read. Much
|
|
||||||
* faster.
|
|
||||||
*
|
|
||||||
* Chunks holding summaries are marked with tags making it look like
|
|
||||||
* they are part of a fake file.
|
|
||||||
*
|
|
||||||
* The summary could also be used during gc.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "yaffs_summary.h"
|
|
||||||
#include "yaffs_packedtags2.h"
|
|
||||||
#include "yaffs_nand.h"
|
|
||||||
#include "yaffs_getblockinfo.h"
|
|
||||||
#include "yaffs_bitmap.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The summary is built up in an array of summary tags.
|
|
||||||
* This gets written to the last one or two (maybe more) chunks in a block.
|
|
||||||
* A summary header is written as the first part of each chunk of summary data.
|
|
||||||
* The summary header must match or the summary is rejected.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Summary tags don't need the sequence number because that is redundant. */
|
|
||||||
struct yaffs_summary_tags {
|
|
||||||
unsigned obj_id;
|
|
||||||
unsigned chunk_id;
|
|
||||||
unsigned n_bytes;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Summary header */
|
|
||||||
struct yaffs_summary_header {
|
|
||||||
unsigned version; /* Must match current version */
|
|
||||||
unsigned block; /* Must be this block */
|
|
||||||
unsigned seq; /* Must be this sequence number */
|
|
||||||
unsigned sum; /* Just add up all the bytes in the tags */
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static void yaffs_summary_clear(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
if (!dev->sum_tags)
|
|
||||||
return;
|
|
||||||
memset(dev->sum_tags, 0, dev->chunks_per_summary *
|
|
||||||
sizeof(struct yaffs_summary_tags));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void yaffs_summary_deinit(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
kfree(dev->sum_tags);
|
|
||||||
dev->sum_tags = NULL;
|
|
||||||
kfree(dev->gc_sum_tags);
|
|
||||||
dev->gc_sum_tags = NULL;
|
|
||||||
dev->chunks_per_summary = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_summary_init(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
int sum_bytes;
|
|
||||||
int chunks_used; /* Number of chunks used by summary */
|
|
||||||
int sum_tags_bytes;
|
|
||||||
|
|
||||||
sum_bytes = dev->param.chunks_per_block *
|
|
||||||
sizeof(struct yaffs_summary_tags);
|
|
||||||
|
|
||||||
chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/
|
|
||||||
(dev->data_bytes_per_chunk -
|
|
||||||
sizeof(struct yaffs_summary_header));
|
|
||||||
|
|
||||||
dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used;
|
|
||||||
sum_tags_bytes = sizeof(struct yaffs_summary_tags) *
|
|
||||||
dev->chunks_per_summary;
|
|
||||||
dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS);
|
|
||||||
dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS);
|
|
||||||
if (!dev->sum_tags || !dev->gc_sum_tags) {
|
|
||||||
yaffs_summary_deinit(dev);
|
|
||||||
return YAFFS_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
yaffs_summary_clear(dev);
|
|
||||||
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned yaffs_summary_sum(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
u8 *sum_buffer = (u8 *)dev->sum_tags;
|
|
||||||
int i;
|
|
||||||
unsigned sum = 0;
|
|
||||||
|
|
||||||
i = sizeof(struct yaffs_summary_tags) *
|
|
||||||
dev->chunks_per_summary;
|
|
||||||
while (i > 0) {
|
|
||||||
sum += *sum_buffer;
|
|
||||||
sum_buffer++;
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_summary_write(struct yaffs_dev *dev, int blk)
|
|
||||||
{
|
|
||||||
struct yaffs_ext_tags tags;
|
|
||||||
u8 *buffer;
|
|
||||||
u8 *sum_buffer = (u8 *)dev->sum_tags;
|
|
||||||
int n_bytes;
|
|
||||||
int chunk_in_nand;
|
|
||||||
int chunk_in_block;
|
|
||||||
int result;
|
|
||||||
int this_tx;
|
|
||||||
struct yaffs_summary_header hdr;
|
|
||||||
int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr);
|
|
||||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);
|
|
||||||
|
|
||||||
buffer = yaffs_get_temp_buffer(dev);
|
|
||||||
n_bytes = sizeof(struct yaffs_summary_tags) *
|
|
||||||
dev->chunks_per_summary;
|
|
||||||
memset(&tags, 0, sizeof(struct yaffs_ext_tags));
|
|
||||||
tags.obj_id = YAFFS_OBJECTID_SUMMARY;
|
|
||||||
tags.chunk_id = 1;
|
|
||||||
chunk_in_block = dev->chunks_per_summary;
|
|
||||||
chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block +
|
|
||||||
dev->chunks_per_summary;
|
|
||||||
hdr.version = YAFFS_SUMMARY_VERSION;
|
|
||||||
hdr.block = blk;
|
|
||||||
hdr.seq = bi->seq_number;
|
|
||||||
hdr.sum = yaffs_summary_sum(dev);
|
|
||||||
|
|
||||||
do {
|
|
||||||
this_tx = n_bytes;
|
|
||||||
if (this_tx > sum_bytes_per_chunk)
|
|
||||||
this_tx = sum_bytes_per_chunk;
|
|
||||||
memcpy(buffer, &hdr, sizeof(hdr));
|
|
||||||
memcpy(buffer + sizeof(hdr), sum_buffer, this_tx);
|
|
||||||
tags.n_bytes = this_tx + sizeof(hdr);
|
|
||||||
result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand,
|
|
||||||
buffer, &tags);
|
|
||||||
|
|
||||||
if (result != YAFFS_OK)
|
|
||||||
break;
|
|
||||||
yaffs_set_chunk_bit(dev, blk, chunk_in_block);
|
|
||||||
bi->pages_in_use++;
|
|
||||||
dev->n_free_chunks--;
|
|
||||||
|
|
||||||
n_bytes -= this_tx;
|
|
||||||
sum_buffer += this_tx;
|
|
||||||
chunk_in_nand++;
|
|
||||||
chunk_in_block++;
|
|
||||||
tags.chunk_id++;
|
|
||||||
} while (result == YAFFS_OK && n_bytes > 0);
|
|
||||||
yaffs_release_temp_buffer(dev, buffer);
|
|
||||||
|
|
||||||
|
|
||||||
if (result == YAFFS_OK)
|
|
||||||
bi->has_summary = 1;
|
|
||||||
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_summary_read(struct yaffs_dev *dev,
|
|
||||||
struct yaffs_summary_tags *st,
|
|
||||||
int blk)
|
|
||||||
{
|
|
||||||
struct yaffs_ext_tags tags;
|
|
||||||
u8 *buffer;
|
|
||||||
u8 *sum_buffer = (u8 *)st;
|
|
||||||
int n_bytes;
|
|
||||||
int chunk_id;
|
|
||||||
int chunk_in_nand;
|
|
||||||
int chunk_in_block;
|
|
||||||
int result;
|
|
||||||
int this_tx;
|
|
||||||
struct yaffs_summary_header hdr;
|
|
||||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);
|
|
||||||
int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr);
|
|
||||||
int sum_tags_bytes;
|
|
||||||
|
|
||||||
sum_tags_bytes = sizeof(struct yaffs_summary_tags) *
|
|
||||||
dev->chunks_per_summary;
|
|
||||||
buffer = yaffs_get_temp_buffer(dev);
|
|
||||||
n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary;
|
|
||||||
chunk_in_block = dev->chunks_per_summary;
|
|
||||||
chunk_in_nand = blk * dev->param.chunks_per_block +
|
|
||||||
dev->chunks_per_summary;
|
|
||||||
chunk_id = 1;
|
|
||||||
do {
|
|
||||||
this_tx = n_bytes;
|
|
||||||
if (this_tx > sum_bytes_per_chunk)
|
|
||||||
this_tx = sum_bytes_per_chunk;
|
|
||||||
result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand,
|
|
||||||
buffer, &tags);
|
|
||||||
|
|
||||||
if (tags.chunk_id != chunk_id ||
|
|
||||||
tags.obj_id != YAFFS_OBJECTID_SUMMARY ||
|
|
||||||
tags.chunk_used == 0 ||
|
|
||||||
tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||
|
|
||||||
tags.n_bytes != (this_tx + sizeof(hdr)))
|
|
||||||
result = YAFFS_FAIL;
|
|
||||||
if (result != YAFFS_OK)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (st == dev->sum_tags) {
|
|
||||||
/* If we're scanning then update the block info */
|
|
||||||
yaffs_set_chunk_bit(dev, blk, chunk_in_block);
|
|
||||||
bi->pages_in_use++;
|
|
||||||
}
|
|
||||||
memcpy(&hdr, buffer, sizeof(hdr));
|
|
||||||
memcpy(sum_buffer, buffer + sizeof(hdr), this_tx);
|
|
||||||
n_bytes -= this_tx;
|
|
||||||
sum_buffer += this_tx;
|
|
||||||
chunk_in_nand++;
|
|
||||||
chunk_in_block++;
|
|
||||||
chunk_id++;
|
|
||||||
} while (result == YAFFS_OK && n_bytes > 0);
|
|
||||||
yaffs_release_temp_buffer(dev, buffer);
|
|
||||||
|
|
||||||
if (result == YAFFS_OK) {
|
|
||||||
/* Verify header */
|
|
||||||
if (hdr.version != YAFFS_SUMMARY_VERSION ||
|
|
||||||
hdr.seq != bi->seq_number ||
|
|
||||||
hdr.sum != yaffs_summary_sum(dev))
|
|
||||||
result = YAFFS_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (st == dev->sum_tags && result == YAFFS_OK)
|
|
||||||
bi->has_summary = 1;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_summary_add(struct yaffs_dev *dev,
|
|
||||||
struct yaffs_ext_tags *tags,
|
|
||||||
int chunk_in_nand)
|
|
||||||
{
|
|
||||||
struct yaffs_packed_tags2_tags_only tags_only;
|
|
||||||
struct yaffs_summary_tags *sum_tags;
|
|
||||||
int block_in_nand = chunk_in_nand / dev->param.chunks_per_block;
|
|
||||||
int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block;
|
|
||||||
|
|
||||||
if (!dev->sum_tags)
|
|
||||||
return YAFFS_OK;
|
|
||||||
|
|
||||||
if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) {
|
|
||||||
yaffs_pack_tags2_tags_only(&tags_only, tags);
|
|
||||||
sum_tags = &dev->sum_tags[chunk_in_block];
|
|
||||||
sum_tags->chunk_id = tags_only.chunk_id;
|
|
||||||
sum_tags->n_bytes = tags_only.n_bytes;
|
|
||||||
sum_tags->obj_id = tags_only.obj_id;
|
|
||||||
|
|
||||||
if (chunk_in_block == dev->chunks_per_summary - 1) {
|
|
||||||
/* Time to write out the summary */
|
|
||||||
yaffs_summary_write(dev, block_in_nand);
|
|
||||||
yaffs_summary_clear(dev);
|
|
||||||
yaffs_skip_rest_of_block(dev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_summary_fetch(struct yaffs_dev *dev,
|
|
||||||
struct yaffs_ext_tags *tags,
|
|
||||||
int chunk_in_block)
|
|
||||||
{
|
|
||||||
struct yaffs_packed_tags2_tags_only tags_only;
|
|
||||||
struct yaffs_summary_tags *sum_tags;
|
|
||||||
if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) {
|
|
||||||
sum_tags = &dev->sum_tags[chunk_in_block];
|
|
||||||
tags_only.chunk_id = sum_tags->chunk_id;
|
|
||||||
tags_only.n_bytes = sum_tags->n_bytes;
|
|
||||||
tags_only.obj_id = sum_tags->obj_id;
|
|
||||||
yaffs_unpack_tags2_tags_only(tags, &tags_only);
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
||||||
return YAFFS_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_summary_gc(struct yaffs_dev *dev, int blk)
|
|
||||||
{
|
|
||||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!bi->has_summary)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (i = dev->chunks_per_summary;
|
|
||||||
i < dev->param.chunks_per_block;
|
|
||||||
i++) {
|
|
||||||
if (yaffs_check_chunk_bit(dev, blk, i)) {
|
|
||||||
yaffs_clear_chunk_bit(dev, blk, i);
|
|
||||||
bi->pages_in_use--;
|
|
||||||
dev->n_free_chunks++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YAFFS_SUMMARY_H__
|
|
||||||
#define __YAFFS_SUMMARY_H__
|
|
||||||
|
|
||||||
#include "yaffs_packedtags2.h"
|
|
||||||
|
|
||||||
|
|
||||||
int yaffs_summary_init(struct yaffs_dev *dev);
|
|
||||||
void yaffs_summary_deinit(struct yaffs_dev *dev);
|
|
||||||
|
|
||||||
int yaffs_summary_add(struct yaffs_dev *dev,
|
|
||||||
struct yaffs_ext_tags *tags,
|
|
||||||
int chunk_in_block);
|
|
||||||
int yaffs_summary_fetch(struct yaffs_dev *dev,
|
|
||||||
struct yaffs_ext_tags *tags,
|
|
||||||
int chunk_in_block);
|
|
||||||
int yaffs_summary_read(struct yaffs_dev *dev,
|
|
||||||
struct yaffs_summary_tags *st,
|
|
||||||
int blk);
|
|
||||||
void yaffs_summary_gc(struct yaffs_dev *dev, int blk);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,381 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
#include "yaffs_tagscompat.h"
|
|
||||||
#include "yaffs_ecc.h"
|
|
||||||
#include "yaffs_getblockinfo.h"
|
|
||||||
#include "yaffs_trace.h"
|
|
||||||
|
|
||||||
static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
|
|
||||||
|
|
||||||
|
|
||||||
/********** Tags ECC calculations *********/
|
|
||||||
|
|
||||||
|
|
||||||
void yaffs_calc_tags_ecc(struct yaffs_tags *tags)
|
|
||||||
{
|
|
||||||
/* Calculate an ecc */
|
|
||||||
unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;
|
|
||||||
unsigned i, j;
|
|
||||||
unsigned ecc = 0;
|
|
||||||
unsigned bit = 0;
|
|
||||||
|
|
||||||
tags->ecc = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
for (j = 1; j & 0xff; j <<= 1) {
|
|
||||||
bit++;
|
|
||||||
if (b[i] & j)
|
|
||||||
ecc ^= bit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tags->ecc = ecc;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_check_tags_ecc(struct yaffs_tags *tags)
|
|
||||||
{
|
|
||||||
unsigned ecc = tags->ecc;
|
|
||||||
|
|
||||||
yaffs_calc_tags_ecc(tags);
|
|
||||||
|
|
||||||
ecc ^= tags->ecc;
|
|
||||||
|
|
||||||
if (ecc && ecc <= 64) {
|
|
||||||
/* TODO: Handle the failure better. Retire? */
|
|
||||||
unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;
|
|
||||||
|
|
||||||
ecc--;
|
|
||||||
|
|
||||||
b[ecc / 8] ^= (1 << (ecc & 7));
|
|
||||||
|
|
||||||
/* Now recvalc the ecc */
|
|
||||||
yaffs_calc_tags_ecc(tags);
|
|
||||||
|
|
||||||
return 1; /* recovered error */
|
|
||||||
} else if (ecc) {
|
|
||||||
/* Wierd ecc failure value */
|
|
||||||
/* TODO Need to do somethiong here */
|
|
||||||
return -1; /* unrecovered error */
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/********** Tags **********/
|
|
||||||
|
|
||||||
static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
|
|
||||||
struct yaffs_tags *tags_ptr)
|
|
||||||
{
|
|
||||||
union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;
|
|
||||||
|
|
||||||
yaffs_calc_tags_ecc(tags_ptr);
|
|
||||||
|
|
||||||
spare_ptr->tb0 = tu->as_bytes[0];
|
|
||||||
spare_ptr->tb1 = tu->as_bytes[1];
|
|
||||||
spare_ptr->tb2 = tu->as_bytes[2];
|
|
||||||
spare_ptr->tb3 = tu->as_bytes[3];
|
|
||||||
spare_ptr->tb4 = tu->as_bytes[4];
|
|
||||||
spare_ptr->tb5 = tu->as_bytes[5];
|
|
||||||
spare_ptr->tb6 = tu->as_bytes[6];
|
|
||||||
spare_ptr->tb7 = tu->as_bytes[7];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void yaffs_get_tags_from_spare(struct yaffs_dev *dev,
|
|
||||||
struct yaffs_spare *spare_ptr,
|
|
||||||
struct yaffs_tags *tags_ptr)
|
|
||||||
{
|
|
||||||
union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
tu->as_bytes[0] = spare_ptr->tb0;
|
|
||||||
tu->as_bytes[1] = spare_ptr->tb1;
|
|
||||||
tu->as_bytes[2] = spare_ptr->tb2;
|
|
||||||
tu->as_bytes[3] = spare_ptr->tb3;
|
|
||||||
tu->as_bytes[4] = spare_ptr->tb4;
|
|
||||||
tu->as_bytes[5] = spare_ptr->tb5;
|
|
||||||
tu->as_bytes[6] = spare_ptr->tb6;
|
|
||||||
tu->as_bytes[7] = spare_ptr->tb7;
|
|
||||||
|
|
||||||
result = yaffs_check_tags_ecc(tags_ptr);
|
|
||||||
if (result > 0)
|
|
||||||
dev->n_tags_ecc_fixed++;
|
|
||||||
else if (result < 0)
|
|
||||||
dev->n_tags_ecc_unfixed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void yaffs_spare_init(struct yaffs_spare *spare)
|
|
||||||
{
|
|
||||||
memset(spare, 0xff, sizeof(struct yaffs_spare));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_wr_nand(struct yaffs_dev *dev,
|
|
||||||
int nand_chunk, const u8 *data,
|
|
||||||
struct yaffs_spare *spare)
|
|
||||||
{
|
|
||||||
int data_size = dev->data_bytes_per_chunk;
|
|
||||||
|
|
||||||
return dev->drv.drv_write_chunk_fn(dev, nand_chunk,
|
|
||||||
data, data_size,
|
|
||||||
(u8 *) spare, sizeof(*spare));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_rd_chunk_nand(struct yaffs_dev *dev,
|
|
||||||
int nand_chunk,
|
|
||||||
u8 *data,
|
|
||||||
struct yaffs_spare *spare,
|
|
||||||
enum yaffs_ecc_result *ecc_result,
|
|
||||||
int correct_errors)
|
|
||||||
{
|
|
||||||
int ret_val;
|
|
||||||
struct yaffs_spare local_spare;
|
|
||||||
int data_size;
|
|
||||||
int spare_size;
|
|
||||||
int ecc_result1, ecc_result2;
|
|
||||||
u8 calc_ecc[3];
|
|
||||||
|
|
||||||
if (!spare) {
|
|
||||||
/* If we don't have a real spare, then we use a local one. */
|
|
||||||
/* Need this for the calculation of the ecc */
|
|
||||||
spare = &local_spare;
|
|
||||||
}
|
|
||||||
data_size = dev->data_bytes_per_chunk;
|
|
||||||
spare_size = sizeof(struct yaffs_spare);
|
|
||||||
|
|
||||||
if (dev->param.use_nand_ecc)
|
|
||||||
return dev->drv.drv_read_chunk_fn(dev, nand_chunk,
|
|
||||||
data, data_size,
|
|
||||||
(u8 *) spare, spare_size,
|
|
||||||
ecc_result);
|
|
||||||
|
|
||||||
|
|
||||||
/* Handle the ECC at this level. */
|
|
||||||
|
|
||||||
ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk,
|
|
||||||
data, data_size,
|
|
||||||
(u8 *)spare, spare_size,
|
|
||||||
NULL);
|
|
||||||
if (!data || !correct_errors)
|
|
||||||
return ret_val;
|
|
||||||
|
|
||||||
/* Do ECC correction if needed. */
|
|
||||||
yaffs_ecc_calc(data, calc_ecc);
|
|
||||||
ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc);
|
|
||||||
yaffs_ecc_calc(&data[256], calc_ecc);
|
|
||||||
ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc);
|
|
||||||
|
|
||||||
if (ecc_result1 > 0) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
|
||||||
"**>>yaffs ecc error fix performed on chunk %d:0",
|
|
||||||
nand_chunk);
|
|
||||||
dev->n_ecc_fixed++;
|
|
||||||
} else if (ecc_result1 < 0) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
|
||||||
"**>>yaffs ecc error unfixed on chunk %d:0",
|
|
||||||
nand_chunk);
|
|
||||||
dev->n_ecc_unfixed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ecc_result2 > 0) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
|
||||||
"**>>yaffs ecc error fix performed on chunk %d:1",
|
|
||||||
nand_chunk);
|
|
||||||
dev->n_ecc_fixed++;
|
|
||||||
} else if (ecc_result2 < 0) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
|
||||||
"**>>yaffs ecc error unfixed on chunk %d:1",
|
|
||||||
nand_chunk);
|
|
||||||
dev->n_ecc_unfixed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ecc_result1 || ecc_result2) {
|
|
||||||
/* We had a data problem on this page */
|
|
||||||
yaffs_handle_rd_data_error(dev, nand_chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ecc_result1 < 0 || ecc_result2 < 0)
|
|
||||||
*ecc_result = YAFFS_ECC_RESULT_UNFIXED;
|
|
||||||
else if (ecc_result1 > 0 || ecc_result2 > 0)
|
|
||||||
*ecc_result = YAFFS_ECC_RESULT_FIXED;
|
|
||||||
else
|
|
||||||
*ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
|
|
||||||
|
|
||||||
return ret_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Functions for robustisizing
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk)
|
|
||||||
{
|
|
||||||
int flash_block = nand_chunk / dev->param.chunks_per_block;
|
|
||||||
|
|
||||||
/* Mark the block for retirement */
|
|
||||||
yaffs_get_block_info(dev, flash_block + dev->block_offset)->
|
|
||||||
needs_retiring = 1;
|
|
||||||
yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
|
|
||||||
"**>>Block %d marked for retirement",
|
|
||||||
flash_block);
|
|
||||||
|
|
||||||
/* TODO:
|
|
||||||
* Just do a garbage collection on the affected block
|
|
||||||
* then retire the block
|
|
||||||
* NB recursion
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_tags_compat_wr(struct yaffs_dev *dev,
|
|
||||||
int nand_chunk,
|
|
||||||
const u8 *data, const struct yaffs_ext_tags *ext_tags)
|
|
||||||
{
|
|
||||||
struct yaffs_spare spare;
|
|
||||||
struct yaffs_tags tags;
|
|
||||||
|
|
||||||
yaffs_spare_init(&spare);
|
|
||||||
|
|
||||||
if (ext_tags->is_deleted)
|
|
||||||
spare.page_status = 0;
|
|
||||||
else {
|
|
||||||
tags.obj_id = ext_tags->obj_id;
|
|
||||||
tags.chunk_id = ext_tags->chunk_id;
|
|
||||||
|
|
||||||
tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1);
|
|
||||||
|
|
||||||
if (dev->data_bytes_per_chunk >= 1024)
|
|
||||||
tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3;
|
|
||||||
else
|
|
||||||
tags.n_bytes_msb = 3;
|
|
||||||
|
|
||||||
tags.serial_number = ext_tags->serial_number;
|
|
||||||
|
|
||||||
if (!dev->param.use_nand_ecc && data) {
|
|
||||||
yaffs_ecc_calc(data, spare.ecc1);
|
|
||||||
yaffs_ecc_calc(&data[256], spare.ecc2);
|
|
||||||
}
|
|
||||||
|
|
||||||
yaffs_load_tags_to_spare(&spare, &tags);
|
|
||||||
}
|
|
||||||
return yaffs_wr_nand(dev, nand_chunk, data, &spare);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_tags_compat_rd(struct yaffs_dev *dev,
|
|
||||||
int nand_chunk,
|
|
||||||
u8 *data, struct yaffs_ext_tags *ext_tags)
|
|
||||||
{
|
|
||||||
struct yaffs_spare spare;
|
|
||||||
struct yaffs_tags tags;
|
|
||||||
enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN;
|
|
||||||
static struct yaffs_spare spare_ff;
|
|
||||||
static int init;
|
|
||||||
int deleted;
|
|
||||||
|
|
||||||
if (!init) {
|
|
||||||
memset(&spare_ff, 0xff, sizeof(spare_ff));
|
|
||||||
init = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!yaffs_rd_chunk_nand(dev, nand_chunk,
|
|
||||||
data, &spare, &ecc_result, 1))
|
|
||||||
return YAFFS_FAIL;
|
|
||||||
|
|
||||||
/* ext_tags may be NULL */
|
|
||||||
if (!ext_tags)
|
|
||||||
return YAFFS_OK;
|
|
||||||
|
|
||||||
deleted = (hweight8(spare.page_status) < 7) ? 1 : 0;
|
|
||||||
|
|
||||||
ext_tags->is_deleted = deleted;
|
|
||||||
ext_tags->ecc_result = ecc_result;
|
|
||||||
ext_tags->block_bad = 0; /* We're reading it */
|
|
||||||
/* therefore it is not a bad block */
|
|
||||||
ext_tags->chunk_used =
|
|
||||||
memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0;
|
|
||||||
|
|
||||||
if (ext_tags->chunk_used) {
|
|
||||||
yaffs_get_tags_from_spare(dev, &spare, &tags);
|
|
||||||
ext_tags->obj_id = tags.obj_id;
|
|
||||||
ext_tags->chunk_id = tags.chunk_id;
|
|
||||||
ext_tags->n_bytes = tags.n_bytes_lsb;
|
|
||||||
|
|
||||||
if (dev->data_bytes_per_chunk >= 1024)
|
|
||||||
ext_tags->n_bytes |=
|
|
||||||
(((unsigned)tags.n_bytes_msb) << 10);
|
|
||||||
|
|
||||||
ext_tags->serial_number = tags.serial_number;
|
|
||||||
}
|
|
||||||
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block)
|
|
||||||
{
|
|
||||||
struct yaffs_spare spare;
|
|
||||||
|
|
||||||
memset(&spare, 0xff, sizeof(struct yaffs_spare));
|
|
||||||
|
|
||||||
spare.block_status = 'Y';
|
|
||||||
|
|
||||||
yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL,
|
|
||||||
&spare);
|
|
||||||
yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1,
|
|
||||||
NULL, &spare);
|
|
||||||
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
|
|
||||||
int block_no,
|
|
||||||
enum yaffs_block_state *state,
|
|
||||||
u32 *seq_number)
|
|
||||||
{
|
|
||||||
struct yaffs_spare spare0, spare1;
|
|
||||||
static struct yaffs_spare spare_ff;
|
|
||||||
static int init;
|
|
||||||
enum yaffs_ecc_result dummy;
|
|
||||||
|
|
||||||
if (!init) {
|
|
||||||
memset(&spare_ff, 0xff, sizeof(spare_ff));
|
|
||||||
init = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*seq_number = 0;
|
|
||||||
|
|
||||||
/* Look for bad block markers in the first two chunks */
|
|
||||||
yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block,
|
|
||||||
NULL, &spare0, &dummy, 0);
|
|
||||||
yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1,
|
|
||||||
NULL, &spare1, &dummy, 0);
|
|
||||||
|
|
||||||
if (hweight8(spare0.block_status & spare1.block_status) < 7)
|
|
||||||
*state = YAFFS_BLOCK_STATE_DEAD;
|
|
||||||
else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0)
|
|
||||||
*state = YAFFS_BLOCK_STATE_EMPTY;
|
|
||||||
else
|
|
||||||
*state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
|
|
||||||
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_tags_compat_install(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
if(dev->param.is_yaffs2)
|
|
||||||
return;
|
|
||||||
if(!dev->tagger.write_chunk_tags_fn)
|
|
||||||
dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr;
|
|
||||||
if(!dev->tagger.read_chunk_tags_fn)
|
|
||||||
dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd;
|
|
||||||
if(!dev->tagger.query_block_fn)
|
|
||||||
dev->tagger.query_block_fn = yaffs_tags_compat_query_block;
|
|
||||||
if(!dev->tagger.mark_bad_fn)
|
|
||||||
dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YAFFS_TAGSCOMPAT_H__
|
|
||||||
#define __YAFFS_TAGSCOMPAT_H__
|
|
||||||
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
|
|
||||||
|
|
||||||
int yaffs_tags_compat_wr(struct yaffs_dev *dev,
|
|
||||||
int nand_chunk,
|
|
||||||
const u8 *data, const struct yaffs_ext_tags *tags);
|
|
||||||
int yaffs_tags_compat_rd(struct yaffs_dev *dev,
|
|
||||||
int nand_chunk,
|
|
||||||
u8 *data, struct yaffs_ext_tags *tags);
|
|
||||||
int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no);
|
|
||||||
int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
|
|
||||||
int block_no,
|
|
||||||
enum yaffs_block_state *state,
|
|
||||||
u32 *seq_number);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
void yaffs_tags_compat_install(struct yaffs_dev *dev);
|
|
||||||
void yaffs_calc_tags_ecc(struct yaffs_tags *tags);
|
|
||||||
int yaffs_check_tags_ecc(struct yaffs_tags *tags);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,199 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
#include "yaffs_trace.h"
|
|
||||||
#include "yaffs_packedtags2.h"
|
|
||||||
|
|
||||||
static int yaffs_tags_marshall_write(struct yaffs_dev *dev,
|
|
||||||
int nand_chunk, const u8 *data,
|
|
||||||
const struct yaffs_ext_tags *tags)
|
|
||||||
{
|
|
||||||
struct yaffs_packed_tags2 pt;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
int packed_tags_size =
|
|
||||||
dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
|
|
||||||
void *packed_tags_ptr =
|
|
||||||
dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_MTD,
|
|
||||||
"yaffs_tags_marshall_write chunk %d data %p tags %p",
|
|
||||||
nand_chunk, data, tags);
|
|
||||||
|
|
||||||
/* For yaffs2 writing there must be both data and tags.
|
|
||||||
* If we're using inband tags, then the tags are stuffed into
|
|
||||||
* the end of the data buffer.
|
|
||||||
*/
|
|
||||||
if (!data || !tags)
|
|
||||||
BUG();
|
|
||||||
else if (dev->param.inband_tags) {
|
|
||||||
struct yaffs_packed_tags2_tags_only *pt2tp;
|
|
||||||
pt2tp =
|
|
||||||
(struct yaffs_packed_tags2_tags_only *)(data +
|
|
||||||
dev->
|
|
||||||
data_bytes_per_chunk);
|
|
||||||
yaffs_pack_tags2_tags_only(pt2tp, tags);
|
|
||||||
} else {
|
|
||||||
yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc);
|
|
||||||
}
|
|
||||||
|
|
||||||
retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk,
|
|
||||||
data, dev->param.total_bytes_per_chunk,
|
|
||||||
(dev->param.inband_tags) ? NULL : packed_tags_ptr,
|
|
||||||
(dev->param.inband_tags) ? 0 : packed_tags_size);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_tags_marshall_read(struct yaffs_dev *dev,
|
|
||||||
int nand_chunk, u8 *data,
|
|
||||||
struct yaffs_ext_tags *tags)
|
|
||||||
{
|
|
||||||
int retval = 0;
|
|
||||||
int local_data = 0;
|
|
||||||
u8 spare_buffer[100];
|
|
||||||
enum yaffs_ecc_result ecc_result;
|
|
||||||
|
|
||||||
struct yaffs_packed_tags2 pt;
|
|
||||||
|
|
||||||
int packed_tags_size =
|
|
||||||
dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
|
|
||||||
void *packed_tags_ptr =
|
|
||||||
dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_MTD,
|
|
||||||
"yaffs_tags_marshall_read chunk %d data %p tags %p",
|
|
||||||
nand_chunk, data, tags);
|
|
||||||
|
|
||||||
if (dev->param.inband_tags) {
|
|
||||||
if (!data) {
|
|
||||||
local_data = 1;
|
|
||||||
data = yaffs_get_temp_buffer(dev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev->param.inband_tags || (data && !tags))
|
|
||||||
retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk,
|
|
||||||
data, dev->param.total_bytes_per_chunk,
|
|
||||||
NULL, 0,
|
|
||||||
&ecc_result);
|
|
||||||
else if (tags)
|
|
||||||
retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk,
|
|
||||||
data, dev->param.total_bytes_per_chunk,
|
|
||||||
spare_buffer, packed_tags_size,
|
|
||||||
&ecc_result);
|
|
||||||
else
|
|
||||||
BUG();
|
|
||||||
|
|
||||||
|
|
||||||
if (dev->param.inband_tags) {
|
|
||||||
if (tags) {
|
|
||||||
struct yaffs_packed_tags2_tags_only *pt2tp;
|
|
||||||
pt2tp =
|
|
||||||
(struct yaffs_packed_tags2_tags_only *)
|
|
||||||
&data[dev->data_bytes_per_chunk];
|
|
||||||
yaffs_unpack_tags2_tags_only(tags, pt2tp);
|
|
||||||
}
|
|
||||||
} else if (tags) {
|
|
||||||
memcpy(packed_tags_ptr, spare_buffer, packed_tags_size);
|
|
||||||
yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (local_data)
|
|
||||||
yaffs_release_temp_buffer(dev, data);
|
|
||||||
|
|
||||||
if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) {
|
|
||||||
tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED;
|
|
||||||
dev->n_ecc_unfixed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tags && ecc_result == -YAFFS_ECC_RESULT_FIXED) {
|
|
||||||
if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR)
|
|
||||||
tags->ecc_result = YAFFS_ECC_RESULT_FIXED;
|
|
||||||
dev->n_ecc_fixed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ecc_result < YAFFS_ECC_RESULT_UNFIXED)
|
|
||||||
return YAFFS_OK;
|
|
||||||
else
|
|
||||||
return YAFFS_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no,
|
|
||||||
enum yaffs_block_state *state,
|
|
||||||
u32 *seq_number)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d",
|
|
||||||
block_no);
|
|
||||||
|
|
||||||
retval = dev->drv.drv_check_bad_fn(dev, block_no);
|
|
||||||
|
|
||||||
if (retval== YAFFS_FAIL) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_MTD, "block is bad");
|
|
||||||
|
|
||||||
*state = YAFFS_BLOCK_STATE_DEAD;
|
|
||||||
*seq_number = 0;
|
|
||||||
} else {
|
|
||||||
struct yaffs_ext_tags t;
|
|
||||||
|
|
||||||
yaffs_tags_marshall_read(dev,
|
|
||||||
block_no * dev->param.chunks_per_block,
|
|
||||||
NULL, &t);
|
|
||||||
|
|
||||||
if (t.chunk_used) {
|
|
||||||
*seq_number = t.seq_number;
|
|
||||||
*state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
|
|
||||||
} else {
|
|
||||||
*seq_number = 0;
|
|
||||||
*state = YAFFS_BLOCK_STATE_EMPTY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_MTD,
|
|
||||||
"block query returns seq %d state %d",
|
|
||||||
*seq_number, *state);
|
|
||||||
|
|
||||||
if (retval == 0)
|
|
||||||
return YAFFS_OK;
|
|
||||||
else
|
|
||||||
return YAFFS_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no)
|
|
||||||
{
|
|
||||||
return dev->drv.drv_mark_bad_fn(dev, block_no);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void yaffs_tags_marshall_install(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
if (!dev->param.is_yaffs2)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!dev->tagger.write_chunk_tags_fn)
|
|
||||||
dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write;
|
|
||||||
|
|
||||||
if (!dev->tagger.read_chunk_tags_fn)
|
|
||||||
dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read;
|
|
||||||
|
|
||||||
if (!dev->tagger.query_block_fn)
|
|
||||||
dev->tagger.query_block_fn = yaffs_tags_marshall_query_block;
|
|
||||||
|
|
||||||
if (!dev->tagger.mark_bad_fn)
|
|
||||||
dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YAFFS_TAGSMARSHALL_H__
|
|
||||||
#define __YAFFS_TAGSMARSHALL_H__
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
void yaffs_tags_marshall_install(struct yaffs_dev *dev);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YTRACE_H__
|
|
||||||
#define __YTRACE_H__
|
|
||||||
|
|
||||||
extern unsigned int yaffs_trace_mask;
|
|
||||||
extern unsigned int yaffs_wr_attempts;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tracing flags.
|
|
||||||
* The flags masked in YAFFS_TRACE_ALWAYS are always traced.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define YAFFS_TRACE_OS 0x00000002
|
|
||||||
#define YAFFS_TRACE_ALLOCATE 0x00000004
|
|
||||||
#define YAFFS_TRACE_SCAN 0x00000008
|
|
||||||
#define YAFFS_TRACE_BAD_BLOCKS 0x00000010
|
|
||||||
#define YAFFS_TRACE_ERASE 0x00000020
|
|
||||||
#define YAFFS_TRACE_GC 0x00000040
|
|
||||||
#define YAFFS_TRACE_WRITE 0x00000080
|
|
||||||
#define YAFFS_TRACE_TRACING 0x00000100
|
|
||||||
#define YAFFS_TRACE_DELETION 0x00000200
|
|
||||||
#define YAFFS_TRACE_BUFFERS 0x00000400
|
|
||||||
#define YAFFS_TRACE_NANDACCESS 0x00000800
|
|
||||||
#define YAFFS_TRACE_GC_DETAIL 0x00001000
|
|
||||||
#define YAFFS_TRACE_SCAN_DEBUG 0x00002000
|
|
||||||
#define YAFFS_TRACE_MTD 0x00004000
|
|
||||||
#define YAFFS_TRACE_CHECKPOINT 0x00008000
|
|
||||||
|
|
||||||
#define YAFFS_TRACE_VERIFY 0x00010000
|
|
||||||
#define YAFFS_TRACE_VERIFY_NAND 0x00020000
|
|
||||||
#define YAFFS_TRACE_VERIFY_FULL 0x00040000
|
|
||||||
#define YAFFS_TRACE_VERIFY_ALL 0x000f0000
|
|
||||||
|
|
||||||
#define YAFFS_TRACE_SYNC 0x00100000
|
|
||||||
#define YAFFS_TRACE_BACKGROUND 0x00200000
|
|
||||||
#define YAFFS_TRACE_LOCK 0x00400000
|
|
||||||
#define YAFFS_TRACE_MOUNT 0x00800000
|
|
||||||
|
|
||||||
#define YAFFS_TRACE_ERROR 0x40000000
|
|
||||||
#define YAFFS_TRACE_BUG 0x80000000
|
|
||||||
#define YAFFS_TRACE_ALWAYS 0xf0000000
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,529 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "yaffs_verify.h"
|
|
||||||
#include "yaffs_trace.h"
|
|
||||||
#include "yaffs_bitmap.h"
|
|
||||||
#include "yaffs_getblockinfo.h"
|
|
||||||
#include "yaffs_nand.h"
|
|
||||||
|
|
||||||
int yaffs_skip_verification(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
(void) dev;
|
|
||||||
return !(yaffs_trace_mask &
|
|
||||||
(YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_skip_full_verification(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
(void) dev;
|
|
||||||
return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_skip_nand_verification(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
(void) dev;
|
|
||||||
return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND));
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char * const block_state_name[] = {
|
|
||||||
"Unknown",
|
|
||||||
"Needs scan",
|
|
||||||
"Scanning",
|
|
||||||
"Empty",
|
|
||||||
"Allocating",
|
|
||||||
"Full",
|
|
||||||
"Dirty",
|
|
||||||
"Checkpoint",
|
|
||||||
"Collecting",
|
|
||||||
"Dead"
|
|
||||||
};
|
|
||||||
|
|
||||||
void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n)
|
|
||||||
{
|
|
||||||
int actually_used;
|
|
||||||
int in_use;
|
|
||||||
|
|
||||||
if (yaffs_skip_verification(dev))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Report illegal runtime states */
|
|
||||||
if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES)
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Block %d has undefined state %d",
|
|
||||||
n, bi->block_state);
|
|
||||||
|
|
||||||
switch (bi->block_state) {
|
|
||||||
case YAFFS_BLOCK_STATE_UNKNOWN:
|
|
||||||
case YAFFS_BLOCK_STATE_SCANNING:
|
|
||||||
case YAFFS_BLOCK_STATE_NEEDS_SCAN:
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Block %d has bad run-state %s",
|
|
||||||
n, block_state_name[bi->block_state]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check pages in use and soft deletions are legal */
|
|
||||||
|
|
||||||
actually_used = bi->pages_in_use - bi->soft_del_pages;
|
|
||||||
|
|
||||||
if (bi->pages_in_use < 0 ||
|
|
||||||
bi->pages_in_use > dev->param.chunks_per_block ||
|
|
||||||
bi->soft_del_pages < 0 ||
|
|
||||||
bi->soft_del_pages > dev->param.chunks_per_block ||
|
|
||||||
actually_used < 0 || actually_used > dev->param.chunks_per_block)
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Block %d has illegal values pages_in_used %d soft_del_pages %d",
|
|
||||||
n, bi->pages_in_use, bi->soft_del_pages);
|
|
||||||
|
|
||||||
/* Check chunk bitmap legal */
|
|
||||||
in_use = yaffs_count_chunk_bits(dev, n);
|
|
||||||
if (in_use != bi->pages_in_use)
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Block %d has inconsistent values pages_in_use %d counted chunk bits %d",
|
|
||||||
n, bi->pages_in_use, in_use);
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_verify_collected_blk(struct yaffs_dev *dev,
|
|
||||||
struct yaffs_block_info *bi, int n)
|
|
||||||
{
|
|
||||||
yaffs_verify_blk(dev, bi, n);
|
|
||||||
|
|
||||||
/* After collection the block should be in the erased state */
|
|
||||||
|
|
||||||
if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING &&
|
|
||||||
bi->block_state != YAFFS_BLOCK_STATE_EMPTY) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
|
||||||
"Block %d is in state %d after gc, should be erased",
|
|
||||||
n, bi->block_state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_verify_blocks(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int state_count[YAFFS_NUMBER_OF_BLOCK_STATES];
|
|
||||||
int illegal_states = 0;
|
|
||||||
|
|
||||||
if (yaffs_skip_verification(dev))
|
|
||||||
return;
|
|
||||||
|
|
||||||
memset(state_count, 0, sizeof(state_count));
|
|
||||||
|
|
||||||
for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
|
|
||||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, i);
|
|
||||||
yaffs_verify_blk(dev, bi, i);
|
|
||||||
|
|
||||||
if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES)
|
|
||||||
state_count[bi->block_state]++;
|
|
||||||
else
|
|
||||||
illegal_states++;
|
|
||||||
}
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary");
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"%d blocks have illegal states",
|
|
||||||
illegal_states);
|
|
||||||
if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1)
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Too many allocating blocks");
|
|
||||||
|
|
||||||
for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++)
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"%s %d blocks",
|
|
||||||
block_state_name[i], state_count[i]);
|
|
||||||
|
|
||||||
if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT])
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Checkpoint block count wrong dev %d count %d",
|
|
||||||
dev->blocks_in_checkpt,
|
|
||||||
state_count[YAFFS_BLOCK_STATE_CHECKPOINT]);
|
|
||||||
|
|
||||||
if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY])
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Erased block count wrong dev %d count %d",
|
|
||||||
dev->n_erased_blocks,
|
|
||||||
state_count[YAFFS_BLOCK_STATE_EMPTY]);
|
|
||||||
|
|
||||||
if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1)
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Too many collecting blocks %d (max is 1)",
|
|
||||||
state_count[YAFFS_BLOCK_STATE_COLLECTING]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Verify the object header. oh must be valid, but obj and tags may be NULL in
|
|
||||||
* which case those tests will not be performed.
|
|
||||||
*/
|
|
||||||
void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh,
|
|
||||||
struct yaffs_ext_tags *tags, int parent_check)
|
|
||||||
{
|
|
||||||
if (obj && yaffs_skip_verification(obj->my_dev))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!(tags && obj && oh)) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Verifying object header tags %p obj %p oh %p",
|
|
||||||
tags, obj, oh);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN ||
|
|
||||||
oh->type > YAFFS_OBJECT_TYPE_MAX)
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Obj %d header type is illegal value 0x%x",
|
|
||||||
tags->obj_id, oh->type);
|
|
||||||
|
|
||||||
if (tags->obj_id != obj->obj_id)
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Obj %d header mismatch obj_id %d",
|
|
||||||
tags->obj_id, obj->obj_id);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check that the object's parent ids match if parent_check requested.
|
|
||||||
*
|
|
||||||
* Tests do not apply to the root object.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (parent_check && tags->obj_id > 1 && !obj->parent)
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Obj %d header mismatch parent_id %d obj->parent is NULL",
|
|
||||||
tags->obj_id, oh->parent_obj_id);
|
|
||||||
|
|
||||||
if (parent_check && obj->parent &&
|
|
||||||
oh->parent_obj_id != obj->parent->obj_id &&
|
|
||||||
(oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED ||
|
|
||||||
obj->parent->obj_id != YAFFS_OBJECTID_DELETED))
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Obj %d header mismatch parent_id %d parent_obj_id %d",
|
|
||||||
tags->obj_id, oh->parent_obj_id,
|
|
||||||
obj->parent->obj_id);
|
|
||||||
|
|
||||||
if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Obj %d header name is NULL",
|
|
||||||
obj->obj_id);
|
|
||||||
|
|
||||||
if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Obj %d header name is 0xff",
|
|
||||||
obj->obj_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_verify_file(struct yaffs_obj *obj)
|
|
||||||
{
|
|
||||||
u32 x;
|
|
||||||
int required_depth;
|
|
||||||
int actual_depth;
|
|
||||||
int last_chunk;
|
|
||||||
u32 offset_in_chunk;
|
|
||||||
u32 the_chunk;
|
|
||||||
|
|
||||||
u32 i;
|
|
||||||
struct yaffs_dev *dev;
|
|
||||||
struct yaffs_ext_tags tags;
|
|
||||||
struct yaffs_tnode *tn;
|
|
||||||
u32 obj_id;
|
|
||||||
|
|
||||||
if (!obj)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (yaffs_skip_verification(obj->my_dev))
|
|
||||||
return;
|
|
||||||
|
|
||||||
dev = obj->my_dev;
|
|
||||||
obj_id = obj->obj_id;
|
|
||||||
|
|
||||||
|
|
||||||
/* Check file size is consistent with tnode depth */
|
|
||||||
yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size,
|
|
||||||
&last_chunk, &offset_in_chunk);
|
|
||||||
last_chunk++;
|
|
||||||
x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS;
|
|
||||||
required_depth = 0;
|
|
||||||
while (x > 0) {
|
|
||||||
x >>= YAFFS_TNODES_INTERNAL_BITS;
|
|
||||||
required_depth++;
|
|
||||||
}
|
|
||||||
|
|
||||||
actual_depth = obj->variant.file_variant.top_level;
|
|
||||||
|
|
||||||
/* Check that the chunks in the tnode tree are all correct.
|
|
||||||
* We do this by scanning through the tnode tree and
|
|
||||||
* checking the tags for every chunk match.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (yaffs_skip_nand_verification(dev))
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (i = 1; i <= last_chunk; i++) {
|
|
||||||
tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i);
|
|
||||||
|
|
||||||
if (!tn)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
the_chunk = yaffs_get_group_base(dev, tn, i);
|
|
||||||
if (the_chunk > 0) {
|
|
||||||
yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL,
|
|
||||||
&tags);
|
|
||||||
if (tags.obj_id != obj_id || tags.chunk_id != i)
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)",
|
|
||||||
obj_id, i, the_chunk,
|
|
||||||
tags.obj_id, tags.chunk_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_verify_link(struct yaffs_obj *obj)
|
|
||||||
{
|
|
||||||
if (obj && yaffs_skip_verification(obj->my_dev))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Verify sane equivalent object */
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_verify_symlink(struct yaffs_obj *obj)
|
|
||||||
{
|
|
||||||
if (obj && yaffs_skip_verification(obj->my_dev))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Verify symlink string */
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_verify_special(struct yaffs_obj *obj)
|
|
||||||
{
|
|
||||||
if (obj && yaffs_skip_verification(obj->my_dev))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_verify_obj(struct yaffs_obj *obj)
|
|
||||||
{
|
|
||||||
struct yaffs_dev *dev;
|
|
||||||
u32 chunk_min;
|
|
||||||
u32 chunk_max;
|
|
||||||
u32 chunk_id_ok;
|
|
||||||
u32 chunk_in_range;
|
|
||||||
u32 chunk_wrongly_deleted;
|
|
||||||
u32 chunk_valid;
|
|
||||||
|
|
||||||
if (!obj)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (obj->being_created)
|
|
||||||
return;
|
|
||||||
|
|
||||||
dev = obj->my_dev;
|
|
||||||
|
|
||||||
if (yaffs_skip_verification(dev))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Check sane object header chunk */
|
|
||||||
|
|
||||||
chunk_min = dev->internal_start_block * dev->param.chunks_per_block;
|
|
||||||
chunk_max =
|
|
||||||
(dev->internal_end_block + 1) * dev->param.chunks_per_block - 1;
|
|
||||||
|
|
||||||
chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min &&
|
|
||||||
((unsigned)(obj->hdr_chunk)) <= chunk_max);
|
|
||||||
chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0);
|
|
||||||
chunk_valid = chunk_in_range &&
|
|
||||||
yaffs_check_chunk_bit(dev,
|
|
||||||
obj->hdr_chunk / dev->param.chunks_per_block,
|
|
||||||
obj->hdr_chunk % dev->param.chunks_per_block);
|
|
||||||
chunk_wrongly_deleted = chunk_in_range && !chunk_valid;
|
|
||||||
|
|
||||||
if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted))
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Obj %d has chunk_id %d %s %s",
|
|
||||||
obj->obj_id, obj->hdr_chunk,
|
|
||||||
chunk_id_ok ? "" : ",out of range",
|
|
||||||
chunk_wrongly_deleted ? ",marked as deleted" : "");
|
|
||||||
|
|
||||||
if (chunk_valid && !yaffs_skip_nand_verification(dev)) {
|
|
||||||
struct yaffs_ext_tags tags;
|
|
||||||
struct yaffs_obj_hdr *oh;
|
|
||||||
u8 *buffer = yaffs_get_temp_buffer(dev);
|
|
||||||
|
|
||||||
oh = (struct yaffs_obj_hdr *)buffer;
|
|
||||||
|
|
||||||
yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags);
|
|
||||||
|
|
||||||
yaffs_verify_oh(obj, oh, &tags, 1);
|
|
||||||
|
|
||||||
yaffs_release_temp_buffer(dev, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Verify it has a parent */
|
|
||||||
if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Obj %d has parent pointer %p which does not look like an object",
|
|
||||||
obj->obj_id, obj->parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Verify parent is a directory */
|
|
||||||
if (obj->parent &&
|
|
||||||
obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Obj %d's parent is not a directory (type %d)",
|
|
||||||
obj->obj_id, obj->parent->variant_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (obj->variant_type) {
|
|
||||||
case YAFFS_OBJECT_TYPE_FILE:
|
|
||||||
yaffs_verify_file(obj);
|
|
||||||
break;
|
|
||||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
|
||||||
yaffs_verify_symlink(obj);
|
|
||||||
break;
|
|
||||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
|
||||||
yaffs_verify_dir(obj);
|
|
||||||
break;
|
|
||||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
|
||||||
yaffs_verify_link(obj);
|
|
||||||
break;
|
|
||||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
|
||||||
yaffs_verify_special(obj);
|
|
||||||
break;
|
|
||||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
|
||||||
default:
|
|
||||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
|
||||||
"Obj %d has illegaltype %d",
|
|
||||||
obj->obj_id, obj->variant_type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_verify_objects(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
struct yaffs_obj *obj;
|
|
||||||
int i;
|
|
||||||
struct list_head *lh;
|
|
||||||
|
|
||||||
if (yaffs_skip_verification(dev))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Iterate through the objects in each hash entry */
|
|
||||||
|
|
||||||
for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {
|
|
||||||
list_for_each(lh, &dev->obj_bucket[i].list) {
|
|
||||||
obj = list_entry(lh, struct yaffs_obj, hash_link);
|
|
||||||
yaffs_verify_obj(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_verify_obj_in_dir(struct yaffs_obj *obj)
|
|
||||||
{
|
|
||||||
struct list_head *lh;
|
|
||||||
struct yaffs_obj *list_obj;
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
if (!obj) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify");
|
|
||||||
BUG();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (yaffs_skip_verification(obj->my_dev))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!obj->parent) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent");
|
|
||||||
BUG();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory");
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Iterate through the objects in each hash entry */
|
|
||||||
|
|
||||||
list_for_each(lh, &obj->parent->variant.dir_variant.children) {
|
|
||||||
list_obj = list_entry(lh, struct yaffs_obj, siblings);
|
|
||||||
yaffs_verify_obj(list_obj);
|
|
||||||
if (obj == list_obj)
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count != 1) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
|
||||||
"Object in directory %d times",
|
|
||||||
count);
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void yaffs_verify_dir(struct yaffs_obj *directory)
|
|
||||||
{
|
|
||||||
struct list_head *lh;
|
|
||||||
struct yaffs_obj *list_obj;
|
|
||||||
|
|
||||||
if (!directory) {
|
|
||||||
BUG();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (yaffs_skip_full_verification(directory->my_dev))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
|
||||||
"Directory has wrong type: %d",
|
|
||||||
directory->variant_type);
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Iterate through the objects in each hash entry */
|
|
||||||
|
|
||||||
list_for_each(lh, &directory->variant.dir_variant.children) {
|
|
||||||
list_obj = list_entry(lh, struct yaffs_obj, siblings);
|
|
||||||
if (list_obj->parent != directory) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
|
||||||
"Object in directory list has wrong parent %p",
|
|
||||||
list_obj->parent);
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
yaffs_verify_obj_in_dir(list_obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yaffs_free_verification_failures;
|
|
||||||
|
|
||||||
void yaffs_verify_free_chunks(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
int counted;
|
|
||||||
int difference;
|
|
||||||
|
|
||||||
if (yaffs_skip_verification(dev))
|
|
||||||
return;
|
|
||||||
|
|
||||||
counted = yaffs_count_free_chunks(dev);
|
|
||||||
|
|
||||||
difference = dev->n_free_chunks - counted;
|
|
||||||
|
|
||||||
if (difference) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
|
||||||
"Freechunks verification failure %d %d %d",
|
|
||||||
dev->n_free_chunks, counted, difference);
|
|
||||||
yaffs_free_verification_failures++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int yaffs_verify_file_sane(struct yaffs_obj *in)
|
|
||||||
{
|
|
||||||
(void) in;
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YAFFS_VERIFY_H__
|
|
||||||
#define __YAFFS_VERIFY_H__
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
|
|
||||||
void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi,
|
|
||||||
int n);
|
|
||||||
void yaffs_verify_collected_blk(struct yaffs_dev *dev,
|
|
||||||
struct yaffs_block_info *bi, int n);
|
|
||||||
void yaffs_verify_blocks(struct yaffs_dev *dev);
|
|
||||||
|
|
||||||
void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh,
|
|
||||||
struct yaffs_ext_tags *tags, int parent_check);
|
|
||||||
void yaffs_verify_file(struct yaffs_obj *obj);
|
|
||||||
void yaffs_verify_link(struct yaffs_obj *obj);
|
|
||||||
void yaffs_verify_symlink(struct yaffs_obj *obj);
|
|
||||||
void yaffs_verify_special(struct yaffs_obj *obj);
|
|
||||||
void yaffs_verify_obj(struct yaffs_obj *obj);
|
|
||||||
void yaffs_verify_objects(struct yaffs_dev *dev);
|
|
||||||
void yaffs_verify_obj_in_dir(struct yaffs_obj *obj);
|
|
||||||
void yaffs_verify_dir(struct yaffs_obj *directory);
|
|
||||||
void yaffs_verify_free_chunks(struct yaffs_dev *dev);
|
|
||||||
|
|
||||||
int yaffs_verify_file_sane(struct yaffs_obj *obj);
|
|
||||||
|
|
||||||
int yaffs_skip_verification(struct yaffs_dev *dev);
|
|
||||||
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,422 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "yaffs_yaffs1.h"
|
|
||||||
#include "yportenv.h"
|
|
||||||
#include "yaffs_trace.h"
|
|
||||||
#include "yaffs_bitmap.h"
|
|
||||||
#include "yaffs_getblockinfo.h"
|
|
||||||
#include "yaffs_nand.h"
|
|
||||||
#include "yaffs_attribs.h"
|
|
||||||
|
|
||||||
int yaffs1_scan(struct yaffs_dev *dev)
|
|
||||||
{
|
|
||||||
struct yaffs_ext_tags tags;
|
|
||||||
int blk;
|
|
||||||
int result;
|
|
||||||
int chunk;
|
|
||||||
int c;
|
|
||||||
int deleted;
|
|
||||||
enum yaffs_block_state state;
|
|
||||||
LIST_HEAD(hard_list);
|
|
||||||
struct yaffs_block_info *bi;
|
|
||||||
u32 seq_number;
|
|
||||||
struct yaffs_obj_hdr *oh;
|
|
||||||
struct yaffs_obj *in;
|
|
||||||
struct yaffs_obj *parent;
|
|
||||||
int alloc_failed = 0;
|
|
||||||
struct yaffs_shadow_fixer *shadow_fixers = NULL;
|
|
||||||
u8 *chunk_data;
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
|
||||||
"yaffs1_scan starts intstartblk %d intendblk %d...",
|
|
||||||
dev->internal_start_block, dev->internal_end_block);
|
|
||||||
|
|
||||||
chunk_data = yaffs_get_temp_buffer(dev);
|
|
||||||
|
|
||||||
dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER;
|
|
||||||
|
|
||||||
/* Scan all the blocks to determine their state */
|
|
||||||
bi = dev->block_info;
|
|
||||||
for (blk = dev->internal_start_block; blk <= dev->internal_end_block;
|
|
||||||
blk++) {
|
|
||||||
yaffs_clear_chunk_bits(dev, blk);
|
|
||||||
bi->pages_in_use = 0;
|
|
||||||
bi->soft_del_pages = 0;
|
|
||||||
|
|
||||||
yaffs_query_init_block_state(dev, blk, &state, &seq_number);
|
|
||||||
|
|
||||||
bi->block_state = state;
|
|
||||||
bi->seq_number = seq_number;
|
|
||||||
|
|
||||||
if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK)
|
|
||||||
bi->block_state = state = YAFFS_BLOCK_STATE_DEAD;
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_SCAN_DEBUG,
|
|
||||||
"Block scanning block %d state %d seq %d",
|
|
||||||
blk, state, seq_number);
|
|
||||||
|
|
||||||
if (state == YAFFS_BLOCK_STATE_DEAD) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_BAD_BLOCKS,
|
|
||||||
"block %d is bad", blk);
|
|
||||||
} else if (state == YAFFS_BLOCK_STATE_EMPTY) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty ");
|
|
||||||
dev->n_erased_blocks++;
|
|
||||||
dev->n_free_chunks += dev->param.chunks_per_block;
|
|
||||||
}
|
|
||||||
bi++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* For each block.... */
|
|
||||||
for (blk = dev->internal_start_block;
|
|
||||||
!alloc_failed && blk <= dev->internal_end_block; blk++) {
|
|
||||||
|
|
||||||
cond_resched();
|
|
||||||
|
|
||||||
bi = yaffs_get_block_info(dev, blk);
|
|
||||||
state = bi->block_state;
|
|
||||||
|
|
||||||
deleted = 0;
|
|
||||||
|
|
||||||
/* For each chunk in each block that needs scanning.... */
|
|
||||||
for (c = 0;
|
|
||||||
!alloc_failed && c < dev->param.chunks_per_block &&
|
|
||||||
state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) {
|
|
||||||
/* Read the tags and decide what to do */
|
|
||||||
chunk = blk * dev->param.chunks_per_block + c;
|
|
||||||
|
|
||||||
result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL,
|
|
||||||
&tags);
|
|
||||||
|
|
||||||
/* Let's have a good look at this chunk... */
|
|
||||||
|
|
||||||
if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED ||
|
|
||||||
tags.is_deleted) {
|
|
||||||
/* YAFFS1 only...
|
|
||||||
* A deleted chunk
|
|
||||||
*/
|
|
||||||
deleted++;
|
|
||||||
dev->n_free_chunks++;
|
|
||||||
} else if (!tags.chunk_used) {
|
|
||||||
/* An unassigned chunk in the block
|
|
||||||
* This means that either the block is empty or
|
|
||||||
* this is the one being allocated from
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (c == 0) {
|
|
||||||
/* We're looking at the first chunk in
|
|
||||||
*the block so the block is unused */
|
|
||||||
state = YAFFS_BLOCK_STATE_EMPTY;
|
|
||||||
dev->n_erased_blocks++;
|
|
||||||
} else {
|
|
||||||
/* this is the block being allocated */
|
|
||||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
|
||||||
" Allocating from %d %d",
|
|
||||||
blk, c);
|
|
||||||
state = YAFFS_BLOCK_STATE_ALLOCATING;
|
|
||||||
dev->alloc_block = blk;
|
|
||||||
dev->alloc_page = c;
|
|
||||||
dev->alloc_block_finder = blk;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->n_free_chunks +=
|
|
||||||
(dev->param.chunks_per_block - c);
|
|
||||||
} else if (tags.chunk_id > 0) {
|
|
||||||
/* chunk_id > 0 so it is a data chunk... */
|
|
||||||
unsigned int endpos;
|
|
||||||
|
|
||||||
yaffs_set_chunk_bit(dev, blk, c);
|
|
||||||
bi->pages_in_use++;
|
|
||||||
|
|
||||||
in = yaffs_find_or_create_by_number(dev,
|
|
||||||
tags.obj_id,
|
|
||||||
YAFFS_OBJECT_TYPE_FILE);
|
|
||||||
/* PutChunkIntoFile checks for a clash
|
|
||||||
* (two data chunks with the same chunk_id).
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (!in)
|
|
||||||
alloc_failed = 1;
|
|
||||||
|
|
||||||
if (in) {
|
|
||||||
if (!yaffs_put_chunk_in_file
|
|
||||||
(in, tags.chunk_id, chunk, 1))
|
|
||||||
alloc_failed = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
endpos =
|
|
||||||
(tags.chunk_id - 1) *
|
|
||||||
dev->data_bytes_per_chunk +
|
|
||||||
tags.n_bytes;
|
|
||||||
if (in &&
|
|
||||||
in->variant_type ==
|
|
||||||
YAFFS_OBJECT_TYPE_FILE &&
|
|
||||||
in->variant.file_variant.scanned_size <
|
|
||||||
endpos) {
|
|
||||||
in->variant.file_variant.scanned_size =
|
|
||||||
endpos;
|
|
||||||
if (!dev->param.use_header_file_size) {
|
|
||||||
in->variant.
|
|
||||||
file_variant.file_size =
|
|
||||||
in->variant.
|
|
||||||
file_variant.scanned_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* chunk_id == 0, so it is an ObjectHeader.
|
|
||||||
* Make the object
|
|
||||||
*/
|
|
||||||
yaffs_set_chunk_bit(dev, blk, c);
|
|
||||||
bi->pages_in_use++;
|
|
||||||
|
|
||||||
result = yaffs_rd_chunk_tags_nand(dev, chunk,
|
|
||||||
chunk_data,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
oh = (struct yaffs_obj_hdr *)chunk_data;
|
|
||||||
|
|
||||||
in = yaffs_find_by_number(dev, tags.obj_id);
|
|
||||||
if (in && in->variant_type != oh->type) {
|
|
||||||
/* This should not happen, but somehow
|
|
||||||
* Wev'e ended up with an obj_id that
|
|
||||||
* has been reused but not yet deleted,
|
|
||||||
* and worse still it has changed type.
|
|
||||||
* Delete the old object.
|
|
||||||
*/
|
|
||||||
|
|
||||||
yaffs_del_obj(in);
|
|
||||||
in = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
in = yaffs_find_or_create_by_number(dev,
|
|
||||||
tags.obj_id,
|
|
||||||
oh->type);
|
|
||||||
|
|
||||||
if (!in)
|
|
||||||
alloc_failed = 1;
|
|
||||||
|
|
||||||
if (in && oh->shadows_obj > 0) {
|
|
||||||
|
|
||||||
struct yaffs_shadow_fixer *fixer;
|
|
||||||
fixer =
|
|
||||||
kmalloc(sizeof
|
|
||||||
(struct yaffs_shadow_fixer),
|
|
||||||
GFP_NOFS);
|
|
||||||
if (fixer) {
|
|
||||||
fixer->next = shadow_fixers;
|
|
||||||
shadow_fixers = fixer;
|
|
||||||
fixer->obj_id = tags.obj_id;
|
|
||||||
fixer->shadowed_id =
|
|
||||||
oh->shadows_obj;
|
|
||||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
|
||||||
" Shadow fixer: %d shadows %d",
|
|
||||||
fixer->obj_id,
|
|
||||||
fixer->shadowed_id);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in && in->valid) {
|
|
||||||
/* We have already filled this one.
|
|
||||||
* We have a duplicate and need to
|
|
||||||
* resolve it. */
|
|
||||||
|
|
||||||
unsigned existing_serial = in->serial;
|
|
||||||
unsigned new_serial =
|
|
||||||
tags.serial_number;
|
|
||||||
|
|
||||||
if (((existing_serial + 1) & 3) ==
|
|
||||||
new_serial) {
|
|
||||||
/* Use new one - destroy the
|
|
||||||
* exisiting one */
|
|
||||||
yaffs_chunk_del(dev,
|
|
||||||
in->hdr_chunk,
|
|
||||||
1, __LINE__);
|
|
||||||
in->valid = 0;
|
|
||||||
} else {
|
|
||||||
/* Use existing - destroy
|
|
||||||
* this one. */
|
|
||||||
yaffs_chunk_del(dev, chunk, 1,
|
|
||||||
__LINE__);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in && !in->valid &&
|
|
||||||
(tags.obj_id == YAFFS_OBJECTID_ROOT ||
|
|
||||||
tags.obj_id ==
|
|
||||||
YAFFS_OBJECTID_LOSTNFOUND)) {
|
|
||||||
/* We only load some info, don't fiddle
|
|
||||||
* with directory structure */
|
|
||||||
in->valid = 1;
|
|
||||||
in->variant_type = oh->type;
|
|
||||||
|
|
||||||
in->yst_mode = oh->yst_mode;
|
|
||||||
yaffs_load_attribs(in, oh);
|
|
||||||
in->hdr_chunk = chunk;
|
|
||||||
in->serial = tags.serial_number;
|
|
||||||
|
|
||||||
} else if (in && !in->valid) {
|
|
||||||
/* we need to load this info */
|
|
||||||
|
|
||||||
in->valid = 1;
|
|
||||||
in->variant_type = oh->type;
|
|
||||||
|
|
||||||
in->yst_mode = oh->yst_mode;
|
|
||||||
yaffs_load_attribs(in, oh);
|
|
||||||
in->hdr_chunk = chunk;
|
|
||||||
in->serial = tags.serial_number;
|
|
||||||
|
|
||||||
yaffs_set_obj_name_from_oh(in, oh);
|
|
||||||
in->dirty = 0;
|
|
||||||
|
|
||||||
/* directory stuff...
|
|
||||||
* hook up to parent
|
|
||||||
*/
|
|
||||||
|
|
||||||
parent =
|
|
||||||
yaffs_find_or_create_by_number
|
|
||||||
(dev, oh->parent_obj_id,
|
|
||||||
YAFFS_OBJECT_TYPE_DIRECTORY);
|
|
||||||
if (!parent)
|
|
||||||
alloc_failed = 1;
|
|
||||||
if (parent && parent->variant_type ==
|
|
||||||
YAFFS_OBJECT_TYPE_UNKNOWN) {
|
|
||||||
/* Set up as a directory */
|
|
||||||
parent->variant_type =
|
|
||||||
YAFFS_OBJECT_TYPE_DIRECTORY;
|
|
||||||
INIT_LIST_HEAD(&parent->
|
|
||||||
variant.dir_variant.
|
|
||||||
children);
|
|
||||||
} else if (!parent ||
|
|
||||||
parent->variant_type !=
|
|
||||||
YAFFS_OBJECT_TYPE_DIRECTORY) {
|
|
||||||
/* Hoosterman, a problem....
|
|
||||||
* We're trying to use a
|
|
||||||
* non-directory as a directory
|
|
||||||
*/
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
|
||||||
"yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found."
|
|
||||||
);
|
|
||||||
parent = dev->lost_n_found;
|
|
||||||
}
|
|
||||||
|
|
||||||
yaffs_add_obj_to_dir(parent, in);
|
|
||||||
|
|
||||||
switch (in->variant_type) {
|
|
||||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
|
||||||
/* Todo got a problem */
|
|
||||||
break;
|
|
||||||
case YAFFS_OBJECT_TYPE_FILE:
|
|
||||||
if (dev->param.
|
|
||||||
use_header_file_size)
|
|
||||||
in->variant.
|
|
||||||
file_variant.file_size
|
|
||||||
= yaffs_oh_to_size(oh);
|
|
||||||
break;
|
|
||||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
|
||||||
in->variant.
|
|
||||||
hardlink_variant.equiv_id =
|
|
||||||
oh->equiv_id;
|
|
||||||
list_add(&in->hard_links,
|
|
||||||
&hard_list);
|
|
||||||
break;
|
|
||||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
|
||||||
/* Do nothing */
|
|
||||||
break;
|
|
||||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
|
||||||
/* Do nothing */
|
|
||||||
break;
|
|
||||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
|
||||||
in->variant.symlink_variant.
|
|
||||||
alias =
|
|
||||||
yaffs_clone_str(oh->alias);
|
|
||||||
if (!in->variant.
|
|
||||||
symlink_variant.alias)
|
|
||||||
alloc_failed = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) {
|
|
||||||
/* If we got this far while scanning,
|
|
||||||
* then the block is fully allocated. */
|
|
||||||
state = YAFFS_BLOCK_STATE_FULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state == YAFFS_BLOCK_STATE_ALLOCATING) {
|
|
||||||
/* If the block was partially allocated then
|
|
||||||
* treat it as fully allocated. */
|
|
||||||
state = YAFFS_BLOCK_STATE_FULL;
|
|
||||||
dev->alloc_block = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bi->block_state = state;
|
|
||||||
|
|
||||||
/* Now let's see if it was dirty */
|
|
||||||
if (bi->pages_in_use == 0 &&
|
|
||||||
!bi->has_shrink_hdr &&
|
|
||||||
bi->block_state == YAFFS_BLOCK_STATE_FULL)
|
|
||||||
yaffs_block_became_dirty(dev, blk);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ok, we've done all the scanning.
|
|
||||||
* Fix up the hard link chains.
|
|
||||||
* We should now have scanned all the objects, now it's time to add
|
|
||||||
* these hardlinks.
|
|
||||||
*/
|
|
||||||
|
|
||||||
yaffs_link_fixup(dev, &hard_list);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fix up any shadowed objects.
|
|
||||||
* There should not be more than one of these.
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
struct yaffs_shadow_fixer *fixer;
|
|
||||||
struct yaffs_obj *obj;
|
|
||||||
|
|
||||||
while (shadow_fixers) {
|
|
||||||
fixer = shadow_fixers;
|
|
||||||
shadow_fixers = fixer->next;
|
|
||||||
/* Complete the rename transaction by deleting the
|
|
||||||
* shadowed object then setting the object header
|
|
||||||
to unshadowed.
|
|
||||||
*/
|
|
||||||
obj = yaffs_find_by_number(dev, fixer->shadowed_id);
|
|
||||||
if (obj)
|
|
||||||
yaffs_del_obj(obj);
|
|
||||||
|
|
||||||
obj = yaffs_find_by_number(dev, fixer->obj_id);
|
|
||||||
|
|
||||||
if (obj)
|
|
||||||
yaffs_update_oh(obj, NULL, 1, 0, 0, NULL);
|
|
||||||
|
|
||||||
kfree(fixer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
yaffs_release_temp_buffer(dev, chunk_data);
|
|
||||||
|
|
||||||
if (alloc_failed)
|
|
||||||
return YAFFS_FAIL;
|
|
||||||
|
|
||||||
yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends");
|
|
||||||
|
|
||||||
return YAFFS_OK;
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YAFFS_YAFFS1_H__
|
|
||||||
#define __YAFFS_YAFFS1_H__
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
int yaffs1_scan(struct yaffs_dev *dev);
|
|
||||||
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YAFFS_YAFFS2_H__
|
|
||||||
#define __YAFFS_YAFFS2_H__
|
|
||||||
|
|
||||||
#include "yaffs_guts.h"
|
|
||||||
|
|
||||||
void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev);
|
|
||||||
void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev);
|
|
||||||
void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev,
|
|
||||||
struct yaffs_block_info *bi);
|
|
||||||
void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no,
|
|
||||||
struct yaffs_block_info *bi);
|
|
||||||
int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi);
|
|
||||||
u32 yaffs2_find_refresh_block(struct yaffs_dev *dev);
|
|
||||||
int yaffs2_checkpt_required(struct yaffs_dev *dev);
|
|
||||||
int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev);
|
|
||||||
|
|
||||||
void yaffs2_checkpt_invalidate(struct yaffs_dev *dev);
|
|
||||||
int yaffs2_checkpt_save(struct yaffs_dev *dev);
|
|
||||||
int yaffs2_checkpt_restore(struct yaffs_dev *dev);
|
|
||||||
|
|
||||||
int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size);
|
|
||||||
int yaffs2_scan_backwards(struct yaffs_dev *dev);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,85 +0,0 @@
|
||||||
/*
|
|
||||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
|
||||||
* for Toby Churchill Ltd and Brightstar Engineering
|
|
||||||
*
|
|
||||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __YPORTENV_H__
|
|
||||||
#define __YPORTENV_H__
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Define the MTD version in terms of Linux Kernel versions
|
|
||||||
* This allows yaffs to be used independantly of the kernel
|
|
||||||
* as well as with it.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define MTD_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
|
|
||||||
|
|
||||||
#ifdef YAFFS_OUT_OF_TREE
|
|
||||||
#include "moduleconfig.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <linux/version.h>
|
|
||||||
#define MTD_VERSION_CODE LINUX_VERSION_CODE
|
|
||||||
|
|
||||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19))
|
|
||||||
#include <linux/config.h>
|
|
||||||
#endif
|
|
||||||
#include <linux/version.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/mm.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/vmalloc.h>
|
|
||||||
#include <linux/xattr.h>
|
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/stat.h>
|
|
||||||
#include <linux/sort.h>
|
|
||||||
#include <linux/bitops.h>
|
|
||||||
|
|
||||||
/* These type wrappings are used to support Unicode names in WinCE. */
|
|
||||||
#define YCHAR char
|
|
||||||
#define YUCHAR unsigned char
|
|
||||||
#define _Y(x) x
|
|
||||||
|
|
||||||
#define YAFFS_LOSTNFOUND_NAME "lost+found"
|
|
||||||
#define YAFFS_LOSTNFOUND_PREFIX "obj"
|
|
||||||
|
|
||||||
|
|
||||||
#define YAFFS_ROOT_MODE 0755
|
|
||||||
#define YAFFS_LOSTNFOUND_MODE 0700
|
|
||||||
|
|
||||||
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))
|
|
||||||
#define Y_CURRENT_TIME CURRENT_TIME.tv_sec
|
|
||||||
#define Y_TIME_CONVERT(x) (x).tv_sec
|
|
||||||
#else
|
|
||||||
#define Y_CURRENT_TIME CURRENT_TIME
|
|
||||||
#define Y_TIME_CONVERT(x) (x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define compile_time_assertion(assertion) \
|
|
||||||
({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; })
|
|
||||||
|
|
||||||
|
|
||||||
#define yaffs_printf(msk, fmt, ...) \
|
|
||||||
printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__)
|
|
||||||
|
|
||||||
#define yaffs_trace(msk, fmt, ...) do { \
|
|
||||||
if (yaffs_trace_mask & (msk)) \
|
|
||||||
printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,18 +0,0 @@
|
||||||
--- a/fs/Kconfig
|
|
||||||
+++ b/fs/Kconfig
|
|
||||||
@@ -39,6 +39,7 @@ source "fs/gfs2/Kconfig"
|
|
||||||
source "fs/ocfs2/Kconfig"
|
|
||||||
source "fs/btrfs/Kconfig"
|
|
||||||
source "fs/nilfs2/Kconfig"
|
|
||||||
+source "fs/yaffs2/Kconfig"
|
|
||||||
|
|
||||||
endif # BLOCK
|
|
||||||
|
|
||||||
--- a/fs/Makefile
|
|
||||||
+++ b/fs/Makefile
|
|
||||||
@@ -126,3 +126,5 @@ obj-y += exofs/ # Multiple modules
|
|
||||||
obj-$(CONFIG_CEPH_FS) += ceph/
|
|
||||||
obj-$(CONFIG_PSTORE) += pstore/
|
|
||||||
obj-$(CONFIG_EFIVAR_FS) += efivarfs/
|
|
||||||
+obj-$(CONFIG_YAFFS_FS) += yaffs2/
|
|
||||||
+
|
|
|
@ -1,239 +0,0 @@
|
||||||
Subject: yaffs: fix compat tags handling
|
|
||||||
|
|
||||||
Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
|
|
||||||
---
|
|
||||||
--- a/fs/yaffs2/yaffs_tagscompat.c
|
|
||||||
+++ b/fs/yaffs2/yaffs_tagscompat.c
|
|
||||||
@@ -17,7 +17,9 @@
|
|
||||||
#include "yaffs_getblockinfo.h"
|
|
||||||
#include "yaffs_trace.h"
|
|
||||||
|
|
||||||
+#if 0
|
|
||||||
static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
|
|
||||||
+#endif
|
|
||||||
|
|
||||||
|
|
||||||
/********** Tags ECC calculations *********/
|
|
||||||
@@ -71,6 +73,7 @@ int yaffs_check_tags_ecc(struct yaffs_ta
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
+#if 0
|
|
||||||
/********** Tags **********/
|
|
||||||
|
|
||||||
static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
|
|
||||||
@@ -379,3 +382,214 @@ void yaffs_tags_compat_install(struct ya
|
|
||||||
if(!dev->tagger.mark_bad_fn)
|
|
||||||
dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
|
|
||||||
}
|
|
||||||
+#else
|
|
||||||
+
|
|
||||||
+#include "yaffs_packedtags1.h"
|
|
||||||
+
|
|
||||||
+static int yaffs_tags_compat_write(struct yaffs_dev *dev,
|
|
||||||
+ int nand_chunk,
|
|
||||||
+ const u8 *data,
|
|
||||||
+ const struct yaffs_ext_tags *tags)
|
|
||||||
+{
|
|
||||||
+ struct yaffs_packed_tags1 pt1;
|
|
||||||
+ u8 tag_buf[9];
|
|
||||||
+ int retval;
|
|
||||||
+
|
|
||||||
+ /* we assume that yaffs_packed_tags1 and yaffs_tags are compatible */
|
|
||||||
+ compile_time_assertion(sizeof(struct yaffs_packed_tags1) == 12);
|
|
||||||
+ compile_time_assertion(sizeof(struct yaffs_tags) == 8);
|
|
||||||
+
|
|
||||||
+ yaffs_pack_tags1(&pt1, tags);
|
|
||||||
+ yaffs_calc_tags_ecc((struct yaffs_tags *)&pt1);
|
|
||||||
+
|
|
||||||
+ /* When deleting a chunk, the upper layer provides only skeletal
|
|
||||||
+ * tags, one with is_deleted set. However, we need to update the
|
|
||||||
+ * tags, not erase them completely. So we use the NAND write property
|
|
||||||
+ * that only zeroed-bits stick and set tag bytes to all-ones and
|
|
||||||
+ * zero just the (not) deleted bit.
|
|
||||||
+ */
|
|
||||||
+ if (!dev->param.tags_9bytes) {
|
|
||||||
+ if (tags->is_deleted) {
|
|
||||||
+ memset(&pt1, 0xff, 8);
|
|
||||||
+ /* clear delete status bit to indicate deleted */
|
|
||||||
+ pt1.deleted = 0;
|
|
||||||
+ }
|
|
||||||
+ memcpy(tag_buf, &pt1, 8);
|
|
||||||
+ } else {
|
|
||||||
+ if (tags->is_deleted) {
|
|
||||||
+ memset(tag_buf, 0xff, 8);
|
|
||||||
+ tag_buf[8] = 0;
|
|
||||||
+ } else {
|
|
||||||
+ memcpy(tag_buf, &pt1, 8);
|
|
||||||
+ tag_buf[8] = 0xff;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk,
|
|
||||||
+ data,
|
|
||||||
+ (data) ? dev->data_bytes_per_chunk : 0,
|
|
||||||
+ tag_buf,
|
|
||||||
+ (dev->param.tags_9bytes) ? 9 : 8);
|
|
||||||
+
|
|
||||||
+ return retval;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/* Return with empty extended tags but add ecc_result.
|
|
||||||
+ */
|
|
||||||
+static int return_empty_tags(struct yaffs_ext_tags *tags,
|
|
||||||
+ enum yaffs_ecc_result ecc_result,
|
|
||||||
+ int retval)
|
|
||||||
+{
|
|
||||||
+ if (tags) {
|
|
||||||
+ memset(tags, 0, sizeof(*tags));
|
|
||||||
+ tags->ecc_result = ecc_result;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return retval;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int yaffs_tags_compat_read(struct yaffs_dev *dev,
|
|
||||||
+ int nand_chunk,
|
|
||||||
+ u8 *data,
|
|
||||||
+ struct yaffs_ext_tags *tags)
|
|
||||||
+{
|
|
||||||
+ struct yaffs_packed_tags1 pt1;
|
|
||||||
+ enum yaffs_ecc_result ecc_result;
|
|
||||||
+ int retval;
|
|
||||||
+ int deleted;
|
|
||||||
+ u8 tag_buf[9];
|
|
||||||
+
|
|
||||||
+ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk,
|
|
||||||
+ data, dev->param.total_bytes_per_chunk,
|
|
||||||
+ tag_buf,
|
|
||||||
+ (dev->param.tags_9bytes) ? 9 : 8,
|
|
||||||
+ &ecc_result);
|
|
||||||
+
|
|
||||||
+ switch (ecc_result) {
|
|
||||||
+ case YAFFS_ECC_RESULT_NO_ERROR:
|
|
||||||
+ case YAFFS_ECC_RESULT_FIXED:
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case YAFFS_ECC_RESULT_UNFIXED:
|
|
||||||
+ default:
|
|
||||||
+ return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED, 0);
|
|
||||||
+ tags->block_bad = dev->drv.drv_check_bad_fn(dev, nand_chunk);
|
|
||||||
+ return YAFFS_FAIL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Check for a blank/erased chunk. */
|
|
||||||
+ if (yaffs_check_ff(tag_buf, 8)) {
|
|
||||||
+ /* when blank, upper layers want ecc_result to be <= NO_ERROR */
|
|
||||||
+ return return_empty_tags(tags, YAFFS_ECC_RESULT_NO_ERROR,
|
|
||||||
+ YAFFS_OK);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ memcpy(&pt1, tag_buf, 8);
|
|
||||||
+
|
|
||||||
+ if (!dev->param.tags_9bytes) {
|
|
||||||
+ /* Read deleted status (bit) then return it to it's non-deleted
|
|
||||||
+ * state before performing tags mini-ECC check. pt1.deleted is
|
|
||||||
+ * inverted.
|
|
||||||
+ */
|
|
||||||
+ deleted = !pt1.deleted;
|
|
||||||
+ pt1.deleted = 1;
|
|
||||||
+ } else {
|
|
||||||
+ deleted = (hweight8(tag_buf[8]) < 7) ? 1 : 0;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Check the packed tags mini-ECC and correct if necessary/possible. */
|
|
||||||
+ retval = yaffs_check_tags_ecc((struct yaffs_tags *)&pt1);
|
|
||||||
+ switch (retval) {
|
|
||||||
+ case 0:
|
|
||||||
+ /* no tags error, use MTD result */
|
|
||||||
+ break;
|
|
||||||
+ case 1:
|
|
||||||
+ /* recovered tags-ECC error */
|
|
||||||
+ dev->n_tags_ecc_fixed++;
|
|
||||||
+ if (ecc_result == YAFFS_ECC_RESULT_NO_ERROR)
|
|
||||||
+ ecc_result = YAFFS_ECC_RESULT_FIXED;
|
|
||||||
+ break;
|
|
||||||
+ default:
|
|
||||||
+ /* unrecovered tags-ECC error */
|
|
||||||
+ dev->n_tags_ecc_unfixed++;
|
|
||||||
+ return return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED,
|
|
||||||
+ YAFFS_FAIL);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Unpack the tags to extended form and set ECC result.
|
|
||||||
+ * [set should_be_ff just to keep yaffs_unpack_tags1 happy]
|
|
||||||
+ */
|
|
||||||
+ pt1.should_be_ff = 0xffffffff;
|
|
||||||
+ yaffs_unpack_tags1(tags, &pt1);
|
|
||||||
+ tags->ecc_result = ecc_result;
|
|
||||||
+
|
|
||||||
+ /* Set deleted state */
|
|
||||||
+ tags->is_deleted = deleted;
|
|
||||||
+ return YAFFS_OK;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no)
|
|
||||||
+{
|
|
||||||
+ return dev->drv.drv_mark_bad_fn(dev, block_no);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
|
|
||||||
+ int block_no,
|
|
||||||
+ enum yaffs_block_state *state,
|
|
||||||
+ u32 *seq_number)
|
|
||||||
+{
|
|
||||||
+ struct yaffs_ext_tags tags;
|
|
||||||
+ int retval;
|
|
||||||
+
|
|
||||||
+ yaffs_trace(YAFFS_TRACE_MTD, "%s %d", __func__, block_no);
|
|
||||||
+
|
|
||||||
+ *seq_number = 0;
|
|
||||||
+
|
|
||||||
+ retval = dev->drv.drv_check_bad_fn(dev, block_no);
|
|
||||||
+ if (retval == YAFFS_FAIL) {
|
|
||||||
+ *state = YAFFS_BLOCK_STATE_DEAD;
|
|
||||||
+ goto out;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ yaffs_tags_compat_read(dev, block_no * dev->param.chunks_per_block,
|
|
||||||
+ NULL, &tags);
|
|
||||||
+
|
|
||||||
+ if (tags.ecc_result != YAFFS_ECC_RESULT_NO_ERROR) {
|
|
||||||
+ yaffs_trace(YAFFS_TRACE_MTD, "block %d is marked bad",
|
|
||||||
+ block_no);
|
|
||||||
+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
|
|
||||||
+ } else if (tags.chunk_used) {
|
|
||||||
+ *seq_number = tags.seq_number;
|
|
||||||
+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
|
|
||||||
+ } else {
|
|
||||||
+ *state = YAFFS_BLOCK_STATE_EMPTY;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ retval = YAFFS_OK;
|
|
||||||
+
|
|
||||||
+out:
|
|
||||||
+ yaffs_trace(YAFFS_TRACE_MTD,
|
|
||||||
+ "block query returns seq %u state %d",
|
|
||||||
+ *seq_number, *state);
|
|
||||||
+
|
|
||||||
+ return retval;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void yaffs_tags_compat_install(struct yaffs_dev *dev)
|
|
||||||
+{
|
|
||||||
+ if (dev->param.is_yaffs2)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ if (!dev->tagger.write_chunk_tags_fn)
|
|
||||||
+ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_write;
|
|
||||||
+
|
|
||||||
+ if (!dev->tagger.read_chunk_tags_fn)
|
|
||||||
+ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_read;
|
|
||||||
+
|
|
||||||
+ if (!dev->tagger.query_block_fn)
|
|
||||||
+ dev->tagger.query_block_fn = yaffs_tags_compat_query_block;
|
|
||||||
+
|
|
||||||
+ if (!dev->tagger.mark_bad_fn)
|
|
||||||
+ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
|
|
||||||
+}
|
|
||||||
+#endif
|
|
|
@ -1,115 +0,0 @@
|
||||||
Subject: yaffs: add support for tags-9bytes mount option
|
|
||||||
|
|
||||||
Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
|
|
||||||
---
|
|
||||||
--- a/fs/yaffs2/yaffs_vfs.c
|
|
||||||
+++ b/fs/yaffs2/yaffs_vfs.c
|
|
||||||
@@ -2644,6 +2644,7 @@ static const struct super_operations yaf
|
|
||||||
|
|
||||||
struct yaffs_options {
|
|
||||||
int inband_tags;
|
|
||||||
+ int tags_9bytes;
|
|
||||||
int skip_checkpoint_read;
|
|
||||||
int skip_checkpoint_write;
|
|
||||||
int no_cache;
|
|
||||||
@@ -2683,6 +2684,8 @@ static int yaffs_parse_options(struct ya
|
|
||||||
|
|
||||||
if (!strcmp(cur_opt, "inband-tags")) {
|
|
||||||
options->inband_tags = 1;
|
|
||||||
+ } else if (!strcmp(cur_opt, "tags-9bytes")) {
|
|
||||||
+ options->tags_9bytes = 1;
|
|
||||||
} else if (!strcmp(cur_opt, "tags-ecc-off")) {
|
|
||||||
options->tags_ecc_on = 0;
|
|
||||||
options->tags_ecc_overridden = 1;
|
|
||||||
@@ -2756,7 +2759,6 @@ static struct super_block *yaffs_interna
|
|
||||||
struct yaffs_param *param;
|
|
||||||
|
|
||||||
int read_only = 0;
|
|
||||||
- int inband_tags = 0;
|
|
||||||
|
|
||||||
struct yaffs_options options;
|
|
||||||
|
|
||||||
@@ -2796,6 +2798,9 @@ static struct super_block *yaffs_interna
|
|
||||||
|
|
||||||
memset(&options, 0, sizeof(options));
|
|
||||||
|
|
||||||
+ if (IS_ENABLED(CONFIG_YAFFS_9BYTE_TAGS))
|
|
||||||
+ options.tags_9bytes = 1;
|
|
||||||
+
|
|
||||||
if (yaffs_parse_options(&options, data_str)) {
|
|
||||||
/* Option parsing failed */
|
|
||||||
return NULL;
|
|
||||||
@@ -2829,17 +2834,22 @@ static struct super_block *yaffs_interna
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Added NCB 26/5/2006 for completeness */
|
|
||||||
- if (yaffs_version == 2 && !options.inband_tags
|
|
||||||
- && WRITE_SIZE(mtd) == 512) {
|
|
||||||
+ if (yaffs_version == 2 &&
|
|
||||||
+ (!options.inband_tags || options.tags_9bytes) &&
|
|
||||||
+ WRITE_SIZE(mtd) == 512) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1");
|
|
||||||
yaffs_version = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
- if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) ||
|
|
||||||
- options.inband_tags)
|
|
||||||
- inband_tags = 1;
|
|
||||||
+ if (yaffs_version == 2 &&
|
|
||||||
+ mtd->oobavail < sizeof(struct yaffs_packed_tags2)) {
|
|
||||||
+ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting inband tags");
|
|
||||||
+ options.inband_tags = 1;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0)
|
|
||||||
+ err = yaffs_verify_mtd(mtd, yaffs_version, options.inband_tags,
|
|
||||||
+ options.tags_9bytes);
|
|
||||||
+ if (err < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* OK, so if we got here, we have an MTD that's NAND and looks
|
|
||||||
@@ -2896,7 +2906,8 @@ static struct super_block *yaffs_interna
|
|
||||||
|
|
||||||
param->n_reserved_blocks = 5;
|
|
||||||
param->n_caches = (options.no_cache) ? 0 : 10;
|
|
||||||
- param->inband_tags = inband_tags;
|
|
||||||
+ param->inband_tags = options.inband_tags;
|
|
||||||
+ param->tags_9bytes = options.tags_9bytes;
|
|
||||||
|
|
||||||
param->enable_xattr = 1;
|
|
||||||
if (options.lazy_loading_overridden)
|
|
||||||
--- a/fs/yaffs2/yaffs_mtdif.c
|
|
||||||
+++ b/fs/yaffs2/yaffs_mtdif.c
|
|
||||||
@@ -278,7 +278,8 @@ struct mtd_info * yaffs_get_mtd_device(d
|
|
||||||
return mtd;
|
|
||||||
}
|
|
||||||
|
|
||||||
-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags)
|
|
||||||
+int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags,
|
|
||||||
+ int tags_9bytes)
|
|
||||||
{
|
|
||||||
if (yaffs_version == 2) {
|
|
||||||
if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
|
|
||||||
@@ -297,6 +298,12 @@ int yaffs_verify_mtd(struct mtd_info *mt
|
|
||||||
);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+ if (tags_9bytes && mtd->oobavail < 9) {
|
|
||||||
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
|
|
||||||
+ "MTD device does not support 9-byte tags");
|
|
||||||
+ return -1;
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
--- a/fs/yaffs2/yaffs_mtdif.h
|
|
||||||
+++ b/fs/yaffs2/yaffs_mtdif.h
|
|
||||||
@@ -21,5 +21,6 @@
|
|
||||||
void yaffs_mtd_drv_install(struct yaffs_dev *dev);
|
|
||||||
struct mtd_info * yaffs_get_mtd_device(dev_t sdev);
|
|
||||||
void yaffs_put_mtd_device(struct mtd_info *mtd);
|
|
||||||
-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags);
|
|
||||||
+int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags,
|
|
||||||
+ int tags_9bytes);
|
|
||||||
#endif
|
|
|
@ -1,25 +0,0 @@
|
||||||
--- a/fs/yaffs2/yaffs_vfs.c
|
|
||||||
+++ b/fs/yaffs2/yaffs_vfs.c
|
|
||||||
@@ -774,7 +774,21 @@ static int yaffs_sync_object(struct file
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22))
|
|
||||||
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
|
|
||||||
+static const struct file_operations yaffs_file_operations = {
|
|
||||||
+ .read = new_sync_read,
|
|
||||||
+ .read_iter = generic_file_read_iter,
|
|
||||||
+ .write = new_sync_write,
|
|
||||||
+ .write_iter = generic_file_write_iter,
|
|
||||||
+ .mmap = generic_file_mmap,
|
|
||||||
+ .flush = yaffs_file_flush,
|
|
||||||
+ .fsync = yaffs_sync_object,
|
|
||||||
+ .splice_read = generic_file_splice_read,
|
|
||||||
+ .splice_write = iter_file_splice_write,
|
|
||||||
+ .llseek = generic_file_llseek,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22))
|
|
||||||
static const struct file_operations yaffs_file_operations = {
|
|
||||||
.read = do_sync_read,
|
|
||||||
.write = do_sync_write,
|
|
|
@ -1,6 +1,6 @@
|
||||||
--- a/fs/Kconfig
|
--- a/fs/Kconfig
|
||||||
+++ b/fs/Kconfig
|
+++ b/fs/Kconfig
|
||||||
@@ -62,6 +62,11 @@ config FILE_LOCKING
|
@@ -61,6 +61,11 @@ config FILE_LOCKING
|
||||||
for filesystems like NFS and for the flock() system
|
for filesystems like NFS and for the flock() system
|
||||||
call. Disabling this option saves about 11k.
|
call. Disabling this option saves about 11k.
|
||||||
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
--- a/fs/Kconfig
|
|
||||||
+++ b/fs/Kconfig
|
|
||||||
@@ -30,6 +30,7 @@ source "fs/ocfs2/Kconfig"
|
|
||||||
source "fs/btrfs/Kconfig"
|
|
||||||
source "fs/nilfs2/Kconfig"
|
|
||||||
source "fs/f2fs/Kconfig"
|
|
||||||
+source "fs/yaffs2/Kconfig"
|
|
||||||
|
|
||||||
config FS_DAX
|
|
||||||
bool "Direct Access (DAX) support"
|
|
||||||
--- a/fs/Makefile
|
|
||||||
+++ b/fs/Makefile
|
|
||||||
@@ -126,3 +126,5 @@ obj-y += exofs/ # Multiple modules
|
|
||||||
obj-$(CONFIG_CEPH_FS) += ceph/
|
|
||||||
obj-$(CONFIG_PSTORE) += pstore/
|
|
||||||
obj-$(CONFIG_EFIVAR_FS) += efivarfs/
|
|
||||||
+obj-$(CONFIG_YAFFS_FS) += yaffs2/
|
|
||||||
+
|
|
|
@ -1,239 +0,0 @@
|
||||||
Subject: yaffs: fix compat tags handling
|
|
||||||
|
|
||||||
Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
|
|
||||||
---
|
|
||||||
--- a/fs/yaffs2/yaffs_tagscompat.c
|
|
||||||
+++ b/fs/yaffs2/yaffs_tagscompat.c
|
|
||||||
@@ -17,7 +17,9 @@
|
|
||||||
#include "yaffs_getblockinfo.h"
|
|
||||||
#include "yaffs_trace.h"
|
|
||||||
|
|
||||||
+#if 0
|
|
||||||
static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
|
|
||||||
+#endif
|
|
||||||
|
|
||||||
|
|
||||||
/********** Tags ECC calculations *********/
|
|
||||||
@@ -71,6 +73,7 @@ int yaffs_check_tags_ecc(struct yaffs_ta
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
+#if 0
|
|
||||||
/********** Tags **********/
|
|
||||||
|
|
||||||
static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
|
|
||||||
@@ -379,3 +382,214 @@ void yaffs_tags_compat_install(struct ya
|
|
||||||
if(!dev->tagger.mark_bad_fn)
|
|
||||||
dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
|
|
||||||
}
|
|
||||||
+#else
|
|
||||||
+
|
|
||||||
+#include "yaffs_packedtags1.h"
|
|
||||||
+
|
|
||||||
+static int yaffs_tags_compat_write(struct yaffs_dev *dev,
|
|
||||||
+ int nand_chunk,
|
|
||||||
+ const u8 *data,
|
|
||||||
+ const struct yaffs_ext_tags *tags)
|
|
||||||
+{
|
|
||||||
+ struct yaffs_packed_tags1 pt1;
|
|
||||||
+ u8 tag_buf[9];
|
|
||||||
+ int retval;
|
|
||||||
+
|
|
||||||
+ /* we assume that yaffs_packed_tags1 and yaffs_tags are compatible */
|
|
||||||
+ compile_time_assertion(sizeof(struct yaffs_packed_tags1) == 12);
|
|
||||||
+ compile_time_assertion(sizeof(struct yaffs_tags) == 8);
|
|
||||||
+
|
|
||||||
+ yaffs_pack_tags1(&pt1, tags);
|
|
||||||
+ yaffs_calc_tags_ecc((struct yaffs_tags *)&pt1);
|
|
||||||
+
|
|
||||||
+ /* When deleting a chunk, the upper layer provides only skeletal
|
|
||||||
+ * tags, one with is_deleted set. However, we need to update the
|
|
||||||
+ * tags, not erase them completely. So we use the NAND write property
|
|
||||||
+ * that only zeroed-bits stick and set tag bytes to all-ones and
|
|
||||||
+ * zero just the (not) deleted bit.
|
|
||||||
+ */
|
|
||||||
+ if (!dev->param.tags_9bytes) {
|
|
||||||
+ if (tags->is_deleted) {
|
|
||||||
+ memset(&pt1, 0xff, 8);
|
|
||||||
+ /* clear delete status bit to indicate deleted */
|
|
||||||
+ pt1.deleted = 0;
|
|
||||||
+ }
|
|
||||||
+ memcpy(tag_buf, &pt1, 8);
|
|
||||||
+ } else {
|
|
||||||
+ if (tags->is_deleted) {
|
|
||||||
+ memset(tag_buf, 0xff, 8);
|
|
||||||
+ tag_buf[8] = 0;
|
|
||||||
+ } else {
|
|
||||||
+ memcpy(tag_buf, &pt1, 8);
|
|
||||||
+ tag_buf[8] = 0xff;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk,
|
|
||||||
+ data,
|
|
||||||
+ (data) ? dev->data_bytes_per_chunk : 0,
|
|
||||||
+ tag_buf,
|
|
||||||
+ (dev->param.tags_9bytes) ? 9 : 8);
|
|
||||||
+
|
|
||||||
+ return retval;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/* Return with empty extended tags but add ecc_result.
|
|
||||||
+ */
|
|
||||||
+static int return_empty_tags(struct yaffs_ext_tags *tags,
|
|
||||||
+ enum yaffs_ecc_result ecc_result,
|
|
||||||
+ int retval)
|
|
||||||
+{
|
|
||||||
+ if (tags) {
|
|
||||||
+ memset(tags, 0, sizeof(*tags));
|
|
||||||
+ tags->ecc_result = ecc_result;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return retval;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int yaffs_tags_compat_read(struct yaffs_dev *dev,
|
|
||||||
+ int nand_chunk,
|
|
||||||
+ u8 *data,
|
|
||||||
+ struct yaffs_ext_tags *tags)
|
|
||||||
+{
|
|
||||||
+ struct yaffs_packed_tags1 pt1;
|
|
||||||
+ enum yaffs_ecc_result ecc_result;
|
|
||||||
+ int retval;
|
|
||||||
+ int deleted;
|
|
||||||
+ u8 tag_buf[9];
|
|
||||||
+
|
|
||||||
+ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk,
|
|
||||||
+ data, dev->param.total_bytes_per_chunk,
|
|
||||||
+ tag_buf,
|
|
||||||
+ (dev->param.tags_9bytes) ? 9 : 8,
|
|
||||||
+ &ecc_result);
|
|
||||||
+
|
|
||||||
+ switch (ecc_result) {
|
|
||||||
+ case YAFFS_ECC_RESULT_NO_ERROR:
|
|
||||||
+ case YAFFS_ECC_RESULT_FIXED:
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case YAFFS_ECC_RESULT_UNFIXED:
|
|
||||||
+ default:
|
|
||||||
+ return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED, 0);
|
|
||||||
+ tags->block_bad = dev->drv.drv_check_bad_fn(dev, nand_chunk);
|
|
||||||
+ return YAFFS_FAIL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Check for a blank/erased chunk. */
|
|
||||||
+ if (yaffs_check_ff(tag_buf, 8)) {
|
|
||||||
+ /* when blank, upper layers want ecc_result to be <= NO_ERROR */
|
|
||||||
+ return return_empty_tags(tags, YAFFS_ECC_RESULT_NO_ERROR,
|
|
||||||
+ YAFFS_OK);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ memcpy(&pt1, tag_buf, 8);
|
|
||||||
+
|
|
||||||
+ if (!dev->param.tags_9bytes) {
|
|
||||||
+ /* Read deleted status (bit) then return it to it's non-deleted
|
|
||||||
+ * state before performing tags mini-ECC check. pt1.deleted is
|
|
||||||
+ * inverted.
|
|
||||||
+ */
|
|
||||||
+ deleted = !pt1.deleted;
|
|
||||||
+ pt1.deleted = 1;
|
|
||||||
+ } else {
|
|
||||||
+ deleted = (hweight8(tag_buf[8]) < 7) ? 1 : 0;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Check the packed tags mini-ECC and correct if necessary/possible. */
|
|
||||||
+ retval = yaffs_check_tags_ecc((struct yaffs_tags *)&pt1);
|
|
||||||
+ switch (retval) {
|
|
||||||
+ case 0:
|
|
||||||
+ /* no tags error, use MTD result */
|
|
||||||
+ break;
|
|
||||||
+ case 1:
|
|
||||||
+ /* recovered tags-ECC error */
|
|
||||||
+ dev->n_tags_ecc_fixed++;
|
|
||||||
+ if (ecc_result == YAFFS_ECC_RESULT_NO_ERROR)
|
|
||||||
+ ecc_result = YAFFS_ECC_RESULT_FIXED;
|
|
||||||
+ break;
|
|
||||||
+ default:
|
|
||||||
+ /* unrecovered tags-ECC error */
|
|
||||||
+ dev->n_tags_ecc_unfixed++;
|
|
||||||
+ return return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED,
|
|
||||||
+ YAFFS_FAIL);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Unpack the tags to extended form and set ECC result.
|
|
||||||
+ * [set should_be_ff just to keep yaffs_unpack_tags1 happy]
|
|
||||||
+ */
|
|
||||||
+ pt1.should_be_ff = 0xffffffff;
|
|
||||||
+ yaffs_unpack_tags1(tags, &pt1);
|
|
||||||
+ tags->ecc_result = ecc_result;
|
|
||||||
+
|
|
||||||
+ /* Set deleted state */
|
|
||||||
+ tags->is_deleted = deleted;
|
|
||||||
+ return YAFFS_OK;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no)
|
|
||||||
+{
|
|
||||||
+ return dev->drv.drv_mark_bad_fn(dev, block_no);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
|
|
||||||
+ int block_no,
|
|
||||||
+ enum yaffs_block_state *state,
|
|
||||||
+ u32 *seq_number)
|
|
||||||
+{
|
|
||||||
+ struct yaffs_ext_tags tags;
|
|
||||||
+ int retval;
|
|
||||||
+
|
|
||||||
+ yaffs_trace(YAFFS_TRACE_MTD, "%s %d", __func__, block_no);
|
|
||||||
+
|
|
||||||
+ *seq_number = 0;
|
|
||||||
+
|
|
||||||
+ retval = dev->drv.drv_check_bad_fn(dev, block_no);
|
|
||||||
+ if (retval == YAFFS_FAIL) {
|
|
||||||
+ *state = YAFFS_BLOCK_STATE_DEAD;
|
|
||||||
+ goto out;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ yaffs_tags_compat_read(dev, block_no * dev->param.chunks_per_block,
|
|
||||||
+ NULL, &tags);
|
|
||||||
+
|
|
||||||
+ if (tags.ecc_result != YAFFS_ECC_RESULT_NO_ERROR) {
|
|
||||||
+ yaffs_trace(YAFFS_TRACE_MTD, "block %d is marked bad",
|
|
||||||
+ block_no);
|
|
||||||
+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
|
|
||||||
+ } else if (tags.chunk_used) {
|
|
||||||
+ *seq_number = tags.seq_number;
|
|
||||||
+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
|
|
||||||
+ } else {
|
|
||||||
+ *state = YAFFS_BLOCK_STATE_EMPTY;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ retval = YAFFS_OK;
|
|
||||||
+
|
|
||||||
+out:
|
|
||||||
+ yaffs_trace(YAFFS_TRACE_MTD,
|
|
||||||
+ "block query returns seq %u state %d",
|
|
||||||
+ *seq_number, *state);
|
|
||||||
+
|
|
||||||
+ return retval;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void yaffs_tags_compat_install(struct yaffs_dev *dev)
|
|
||||||
+{
|
|
||||||
+ if (dev->param.is_yaffs2)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ if (!dev->tagger.write_chunk_tags_fn)
|
|
||||||
+ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_write;
|
|
||||||
+
|
|
||||||
+ if (!dev->tagger.read_chunk_tags_fn)
|
|
||||||
+ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_read;
|
|
||||||
+
|
|
||||||
+ if (!dev->tagger.query_block_fn)
|
|
||||||
+ dev->tagger.query_block_fn = yaffs_tags_compat_query_block;
|
|
||||||
+
|
|
||||||
+ if (!dev->tagger.mark_bad_fn)
|
|
||||||
+ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
|
|
||||||
+}
|
|
||||||
+#endif
|
|
|
@ -1,115 +0,0 @@
|
||||||
Subject: yaffs: add support for tags-9bytes mount option
|
|
||||||
|
|
||||||
Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
|
|
||||||
---
|
|
||||||
--- a/fs/yaffs2/yaffs_vfs.c
|
|
||||||
+++ b/fs/yaffs2/yaffs_vfs.c
|
|
||||||
@@ -2644,6 +2644,7 @@ static const struct super_operations yaf
|
|
||||||
|
|
||||||
struct yaffs_options {
|
|
||||||
int inband_tags;
|
|
||||||
+ int tags_9bytes;
|
|
||||||
int skip_checkpoint_read;
|
|
||||||
int skip_checkpoint_write;
|
|
||||||
int no_cache;
|
|
||||||
@@ -2683,6 +2684,8 @@ static int yaffs_parse_options(struct ya
|
|
||||||
|
|
||||||
if (!strcmp(cur_opt, "inband-tags")) {
|
|
||||||
options->inband_tags = 1;
|
|
||||||
+ } else if (!strcmp(cur_opt, "tags-9bytes")) {
|
|
||||||
+ options->tags_9bytes = 1;
|
|
||||||
} else if (!strcmp(cur_opt, "tags-ecc-off")) {
|
|
||||||
options->tags_ecc_on = 0;
|
|
||||||
options->tags_ecc_overridden = 1;
|
|
||||||
@@ -2756,7 +2759,6 @@ static struct super_block *yaffs_interna
|
|
||||||
struct yaffs_param *param;
|
|
||||||
|
|
||||||
int read_only = 0;
|
|
||||||
- int inband_tags = 0;
|
|
||||||
|
|
||||||
struct yaffs_options options;
|
|
||||||
|
|
||||||
@@ -2796,6 +2798,9 @@ static struct super_block *yaffs_interna
|
|
||||||
|
|
||||||
memset(&options, 0, sizeof(options));
|
|
||||||
|
|
||||||
+ if (IS_ENABLED(CONFIG_YAFFS_9BYTE_TAGS))
|
|
||||||
+ options.tags_9bytes = 1;
|
|
||||||
+
|
|
||||||
if (yaffs_parse_options(&options, data_str)) {
|
|
||||||
/* Option parsing failed */
|
|
||||||
return NULL;
|
|
||||||
@@ -2829,17 +2834,22 @@ static struct super_block *yaffs_interna
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Added NCB 26/5/2006 for completeness */
|
|
||||||
- if (yaffs_version == 2 && !options.inband_tags
|
|
||||||
- && WRITE_SIZE(mtd) == 512) {
|
|
||||||
+ if (yaffs_version == 2 &&
|
|
||||||
+ (!options.inband_tags || options.tags_9bytes) &&
|
|
||||||
+ WRITE_SIZE(mtd) == 512) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1");
|
|
||||||
yaffs_version = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
- if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) ||
|
|
||||||
- options.inband_tags)
|
|
||||||
- inband_tags = 1;
|
|
||||||
+ if (yaffs_version == 2 &&
|
|
||||||
+ mtd->oobavail < sizeof(struct yaffs_packed_tags2)) {
|
|
||||||
+ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting inband tags");
|
|
||||||
+ options.inband_tags = 1;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0)
|
|
||||||
+ err = yaffs_verify_mtd(mtd, yaffs_version, options.inband_tags,
|
|
||||||
+ options.tags_9bytes);
|
|
||||||
+ if (err < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* OK, so if we got here, we have an MTD that's NAND and looks
|
|
||||||
@@ -2896,7 +2906,8 @@ static struct super_block *yaffs_interna
|
|
||||||
|
|
||||||
param->n_reserved_blocks = 5;
|
|
||||||
param->n_caches = (options.no_cache) ? 0 : 10;
|
|
||||||
- param->inband_tags = inband_tags;
|
|
||||||
+ param->inband_tags = options.inband_tags;
|
|
||||||
+ param->tags_9bytes = options.tags_9bytes;
|
|
||||||
|
|
||||||
param->enable_xattr = 1;
|
|
||||||
if (options.lazy_loading_overridden)
|
|
||||||
--- a/fs/yaffs2/yaffs_mtdif.c
|
|
||||||
+++ b/fs/yaffs2/yaffs_mtdif.c
|
|
||||||
@@ -278,7 +278,8 @@ struct mtd_info * yaffs_get_mtd_device(d
|
|
||||||
return mtd;
|
|
||||||
}
|
|
||||||
|
|
||||||
-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags)
|
|
||||||
+int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags,
|
|
||||||
+ int tags_9bytes)
|
|
||||||
{
|
|
||||||
if (yaffs_version == 2) {
|
|
||||||
if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
|
|
||||||
@@ -297,6 +298,12 @@ int yaffs_verify_mtd(struct mtd_info *mt
|
|
||||||
);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+ if (tags_9bytes && mtd->oobavail < 9) {
|
|
||||||
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
|
|
||||||
+ "MTD device does not support 9-byte tags");
|
|
||||||
+ return -1;
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
--- a/fs/yaffs2/yaffs_mtdif.h
|
|
||||||
+++ b/fs/yaffs2/yaffs_mtdif.h
|
|
||||||
@@ -21,5 +21,6 @@
|
|
||||||
void yaffs_mtd_drv_install(struct yaffs_dev *dev);
|
|
||||||
struct mtd_info * yaffs_get_mtd_device(dev_t sdev);
|
|
||||||
void yaffs_put_mtd_device(struct mtd_info *mtd);
|
|
||||||
-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags);
|
|
||||||
+int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags,
|
|
||||||
+ int tags_9bytes);
|
|
||||||
#endif
|
|
|
@ -1,29 +0,0 @@
|
||||||
--- a/fs/yaffs2/yaffs_vfs.c
|
|
||||||
+++ b/fs/yaffs2/yaffs_vfs.c
|
|
||||||
@@ -774,7 +774,25 @@ static int yaffs_sync_object(struct file
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22))
|
|
||||||
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
|
|
||||||
+static const struct file_operations yaffs_file_operations = {
|
|
||||||
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0))
|
|
||||||
+ .read = new_sync_read,
|
|
||||||
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) */
|
|
||||||
+ .read_iter = generic_file_read_iter,
|
|
||||||
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0))
|
|
||||||
+ .write = new_sync_write,
|
|
||||||
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) */
|
|
||||||
+ .write_iter = generic_file_write_iter,
|
|
||||||
+ .mmap = generic_file_mmap,
|
|
||||||
+ .flush = yaffs_file_flush,
|
|
||||||
+ .fsync = yaffs_sync_object,
|
|
||||||
+ .splice_read = generic_file_splice_read,
|
|
||||||
+ .splice_write = iter_file_splice_write,
|
|
||||||
+ .llseek = generic_file_llseek,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22))
|
|
||||||
static const struct file_operations yaffs_file_operations = {
|
|
||||||
.read = do_sync_read,
|
|
||||||
.write = do_sync_write,
|
|
|
@ -1,95 +0,0 @@
|
||||||
--- a/fs/yaffs2/yaffs_vfs.c
|
|
||||||
+++ b/fs/yaffs2/yaffs_vfs.c
|
|
||||||
@@ -283,7 +283,7 @@ static int yaffs_readpage_nolock(struct
|
|
||||||
(long long)pos,
|
|
||||||
(unsigned)PAGE_CACHE_SIZE);
|
|
||||||
|
|
||||||
- obj = yaffs_dentry_to_obj(f->f_dentry);
|
|
||||||
+ obj = yaffs_dentry_to_obj(f->f_path.dentry);
|
|
||||||
|
|
||||||
dev = obj->my_dev;
|
|
||||||
|
|
||||||
@@ -481,7 +481,7 @@ static ssize_t yaffs_hold_space(struct f
|
|
||||||
|
|
||||||
int n_free_chunks;
|
|
||||||
|
|
||||||
- obj = yaffs_dentry_to_obj(f->f_dentry);
|
|
||||||
+ obj = yaffs_dentry_to_obj(f->f_path.dentry);
|
|
||||||
|
|
||||||
dev = obj->my_dev;
|
|
||||||
|
|
||||||
@@ -499,7 +499,7 @@ static void yaffs_release_space(struct f
|
|
||||||
struct yaffs_obj *obj;
|
|
||||||
struct yaffs_dev *dev;
|
|
||||||
|
|
||||||
- obj = yaffs_dentry_to_obj(f->f_dentry);
|
|
||||||
+ obj = yaffs_dentry_to_obj(f->f_path.dentry);
|
|
||||||
|
|
||||||
dev = obj->my_dev;
|
|
||||||
|
|
||||||
@@ -591,7 +591,7 @@ static ssize_t yaffs_file_write(struct f
|
|
||||||
struct inode *inode;
|
|
||||||
struct yaffs_dev *dev;
|
|
||||||
|
|
||||||
- obj = yaffs_dentry_to_obj(f->f_dentry);
|
|
||||||
+ obj = yaffs_dentry_to_obj(f->f_path.dentry);
|
|
||||||
|
|
||||||
if (!obj) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_OS,
|
|
||||||
@@ -603,7 +603,7 @@ static ssize_t yaffs_file_write(struct f
|
|
||||||
|
|
||||||
yaffs_gross_lock(dev);
|
|
||||||
|
|
||||||
- inode = f->f_dentry->d_inode;
|
|
||||||
+ inode = f->f_path.dentry->d_inode;
|
|
||||||
|
|
||||||
if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND)
|
|
||||||
ipos = inode->i_size;
|
|
||||||
@@ -727,7 +727,7 @@ static int yaffs_file_flush(struct file
|
|
||||||
static int yaffs_file_flush(struct file *file)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
- struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry);
|
|
||||||
+ struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_path.dentry);
|
|
||||||
|
|
||||||
struct yaffs_dev *dev = obj->my_dev;
|
|
||||||
|
|
||||||
@@ -1734,7 +1734,7 @@ static int yaffs_iterate(struct file *f,
|
|
||||||
|
|
||||||
char name[YAFFS_MAX_NAME_LENGTH + 1];
|
|
||||||
|
|
||||||
- obj = yaffs_dentry_to_obj(f->f_dentry);
|
|
||||||
+ obj = yaffs_dentry_to_obj(f->f_path.dentry);
|
|
||||||
dev = obj->my_dev;
|
|
||||||
|
|
||||||
yaffs_gross_lock(dev);
|
|
||||||
@@ -1798,14 +1798,14 @@ static int yaffs_readdir(struct file *f,
|
|
||||||
struct yaffs_obj *obj;
|
|
||||||
struct yaffs_dev *dev;
|
|
||||||
struct yaffs_search_context *sc;
|
|
||||||
- struct inode *inode = f->f_dentry->d_inode;
|
|
||||||
+ struct inode *inode = f->f_path.dentry->d_inode;
|
|
||||||
unsigned long offset, curoffs;
|
|
||||||
struct yaffs_obj *l;
|
|
||||||
int ret_val = 0;
|
|
||||||
|
|
||||||
char name[YAFFS_MAX_NAME_LENGTH + 1];
|
|
||||||
|
|
||||||
- obj = yaffs_dentry_to_obj(f->f_dentry);
|
|
||||||
+ obj = yaffs_dentry_to_obj(f->f_path.dentry);
|
|
||||||
dev = obj->my_dev;
|
|
||||||
|
|
||||||
yaffs_gross_lock(dev);
|
|
||||||
@@ -1839,10 +1839,10 @@ static int yaffs_readdir(struct file *f,
|
|
||||||
if (offset == 1) {
|
|
||||||
yaffs_trace(YAFFS_TRACE_OS,
|
|
||||||
"yaffs_readdir: entry .. ino %d",
|
|
||||||
- (int)f->f_dentry->d_parent->d_inode->i_ino);
|
|
||||||
+ (int)f->f_path.dentry->d_parent->d_inode->i_ino);
|
|
||||||
yaffs_gross_unlock(dev);
|
|
||||||
if (filldir(dirent, "..", 2, offset,
|
|
||||||
- f->f_dentry->d_parent->d_inode->i_ino,
|
|
||||||
+ f->f_path.dentry->d_parent->d_inode->i_ino,
|
|
||||||
DT_DIR) < 0) {
|
|
||||||
yaffs_gross_lock(dev);
|
|
||||||
goto out;
|
|
|
@ -1,47 +0,0 @@
|
||||||
From d4eb3ab036f8c37c5bc5f45ad0fa4dc34b7228c8 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Kevin Hao <kexin.hao@windriver.com>
|
|
||||||
Date: Wed, 24 Feb 2016 14:37:13 +0800
|
|
||||||
Subject: [PATCH 3/3] yaffs2: using new ->follow_link() and ->put_link()
|
|
||||||
calling conventions
|
|
||||||
|
|
||||||
As what we did in commit 680baacbca69 ("new ->follow_link() and
|
|
||||||
->put_link() calling conventions").
|
|
||||||
|
|
||||||
Signed-off-by: Kevin Hao <kexin.hao@windriver.com>
|
|
||||||
Signed-off-by: Bruce Ashfield <bruce.ashfield@windriver.com>
|
|
||||||
---
|
|
||||||
fs/yaffs2/yaffs_vfs.c | 8 ++++----
|
|
||||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
|
||||||
|
|
||||||
--- a/fs/yaffs2/yaffs_vfs.c
|
|
||||||
+++ b/fs/yaffs2/yaffs_vfs.c
|
|
||||||
@@ -1060,7 +1060,7 @@ static int yaffs_readlink(struct dentry
|
|
||||||
}
|
|
||||||
|
|
||||||
#if (YAFFS_NEW_FOLLOW_LINK == 1)
|
|
||||||
-static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd)
|
|
||||||
+static const char *yaffs_follow_link(struct dentry *dentry, void **cookie)
|
|
||||||
{
|
|
||||||
void *ret;
|
|
||||||
#else
|
|
||||||
@@ -1082,7 +1082,7 @@ static int yaffs_follow_link(struct dent
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
#if (YAFFS_NEW_FOLLOW_LINK == 1)
|
|
||||||
- nd_set_link(nd, alias);
|
|
||||||
+ *cookie = alias;
|
|
||||||
ret = alias;
|
|
||||||
out:
|
|
||||||
if (ret_int)
|
|
||||||
@@ -1114,9 +1114,9 @@ static void yaffs_put_inode(struct inode
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if (YAFFS_NEW_FOLLOW_LINK == 1)
|
|
||||||
-void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias)
|
|
||||||
+void yaffs_put_link(struct inode *inode, void *cookie)
|
|
||||||
{
|
|
||||||
- kfree(alias);
|
|
||||||
+ kfree(cookie);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
--- a/fs/Kconfig
|
--- a/fs/Kconfig
|
||||||
+++ b/fs/Kconfig
|
+++ b/fs/Kconfig
|
||||||
@@ -74,6 +74,11 @@ config FILE_LOCKING
|
@@ -73,6 +73,11 @@ config FILE_LOCKING
|
||||||
for filesystems like NFS and for the flock() system
|
for filesystems like NFS and for the flock() system
|
||||||
call. Disabling this option saves about 11k.
|
call. Disabling this option saves about 11k.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue