fs: yaffs: Import yaffs from Thu Dec 23 13:31:37 2010 +1300

commit ddf33fed15c2376bfb602d62dd018c63fce60df8
Author: Timothy Manning <tfhmanning@gmail.com>
Date:   Thu Dec 23 13:31:37 2010 +1300

    yaffs updated direct/timothy_tests/quick_tests
    Signed-off-by: Timothy Manning <tfhmanning@gmail.com>

Change-Id: I5bbe5a05277bdf8a6fe188bbe4c77725b3fa2aae
Signed-off-by: Dima Zavin <dima@android.com>
This commit is contained in:
Arve Hjønnevåg 2010-05-18 20:35:30 -07:00 committed by Stephen Boyd
parent 745015ab24
commit cea60fc92c
45 changed files with 15355 additions and 0 deletions

View File

@ -193,6 +193,10 @@ source "fs/hfsplus/Kconfig"
source "fs/befs/Kconfig"
source "fs/bfs/Kconfig"
source "fs/efs/Kconfig"
# Patched by YAFFS
source "fs/yaffs2/Kconfig"
source "fs/jffs2/Kconfig"
# UBIFS File system configuration
source "fs/ubifs/Kconfig"

View File

@ -126,3 +126,6 @@ obj-y += exofs/ # Multiple modules
obj-$(CONFIG_CEPH_FS) += ceph/
obj-$(CONFIG_PSTORE) += pstore/
obj-$(CONFIG_EFIVAR_FS) += efivarfs/
# Patched by YAFFS
obj-$(CONFIG_YAFFS_FS) += yaffs2/

161
fs/yaffs2/Kconfig Normal file
View File

@ -0,0 +1,161 @@
#
# 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 Filing System, is a filing 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_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.

17
fs/yaffs2/Makefile Normal file
View File

@ -0,0 +1,17 @@
#
# 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_tagsvalidity.o
yaffs-y += yaffs_mtdif.o yaffs_mtdif1.o yaffs_mtdif2.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_verify.o

396
fs/yaffs2/yaffs_allocator.c Normal file
View File

@ -0,0 +1,396 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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"
#ifdef CONFIG_YAFFS_KMALLOC_ALLOCATOR
void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev)
{
dev = dev;
}
void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev)
{
dev = dev;
}
struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev)
{
return (struct yaffs_tnode *)kmalloc(dev->tnode_size, GFP_NOFS);
}
void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn)
{
dev = dev;
kfree(tn);
}
void yaffs_init_raw_objs(struct yaffs_dev *dev)
{
dev = dev;
}
void yaffs_deinit_raw_objs(struct yaffs_dev *dev)
{
dev = dev;
}
struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev)
{
dev = dev;
return (struct yaffs_obj *)kmalloc(sizeof(struct yaffs_obj));
}
void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj)
{
dev = dev;
kfree(obj);
}
#else
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 yaffs_obj *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) {
YBUG();
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) {
allocator->alloc_tnode_list = NULL;
allocator->free_tnodes = NULL;
allocator->n_free_tnodes = 0;
allocator->n_tnodes_created = 0;
} else {
YBUG();
}
}
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) {
YBUG();
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) {
YBUG();
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) {
YBUG();
return;
}
if (tn) {
tn->internal[0] = allocator->free_tnodes;
allocator->free_tnodes = tn;
allocator->n_free_tnodes++;
}
dev->checkpoint_blocks_required = 0; /* force recalculation */
}
static void yaffs_init_raw_objs(struct yaffs_dev *dev)
{
struct yaffs_allocator *allocator = dev->allocator;
if (allocator) {
allocator->allocated_obj_list = NULL;
allocator->free_objs = NULL;
allocator->n_free_objects = 0;
} else {
YBUG();
}
}
static void yaffs_deinit_raw_objs(struct yaffs_dev *dev)
{
struct yaffs_allocator *allocator = dev->allocator;
struct yaffs_obj_list *tmp;
if (!allocator) {
YBUG();
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;
}
allocator->free_objs = NULL;
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) {
YBUG();
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) {
if (new_objs) {
kfree(new_objs);
new_objs = NULL;
}
if (list) {
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 - 1; i++) {
new_objs[i].siblings.next =
(struct list_head *)(&new_objs[i + 1]);
}
new_objs[n_obj - 1].siblings.next = (void *)allocator->free_objs;
allocator->free_objs = new_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 yaffs_allocator *allocator = dev->allocator;
if (!allocator) {
YBUG();
return obj;
}
/* If there are none left make more */
if (!allocator->free_objs)
yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS);
if (allocator->free_objs) {
obj = allocator->free_objs;
allocator->free_objs =
(struct yaffs_obj *)(allocator->free_objs->siblings.next);
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)
YBUG();
else {
/* Link into the free list. */
obj->siblings.next = (struct list_head *)(allocator->free_objs);
allocator->free_objs = obj;
allocator->n_free_objects++;
}
}
void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev)
{
if (dev->allocator) {
yaffs_deinit_raw_tnodes(dev);
yaffs_deinit_raw_objs(dev);
kfree(dev->allocator);
dev->allocator = NULL;
} else {
YBUG();
}
}
void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev)
{
struct yaffs_allocator *allocator;
if (!dev->allocator) {
allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS);
if (allocator) {
dev->allocator = allocator;
yaffs_init_raw_tnodes(dev);
yaffs_init_raw_objs(dev);
}
} else {
YBUG();
}
}
#endif

View File

@ -0,0 +1,30 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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

124
fs/yaffs2/yaffs_attribs.c Normal file
View File

@ -0,0 +1,124 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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"
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;
}
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->ia_uid;
if (valid & ATTR_GID)
obj->yst_gid = attr->ia_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->ia_uid = obj->yst_uid;
valid |= ATTR_UID;
attr->ia_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;
}

28
fs/yaffs2/yaffs_attribs.h Normal file
View File

@ -0,0 +1,28 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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

98
fs/yaffs2/yaffs_bitmap.c Normal file
View File

@ -0,0 +1,98 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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);
YBUG();
}
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);
YBUG();
}
}
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;
}

33
fs/yaffs2/yaffs_bitmap.h Normal file
View File

@ -0,0 +1,33 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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

415
fs/yaffs2/yaffs_checkptrw.c Normal file
View File

@ -0,0 +1,415 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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"
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->param.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);
if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) {
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
"erasing checkpt block %d", i);
dev->n_erasures++;
if (dev->param.
erase_fn(dev,
i - dev->block_offset /* realign */ )) {
bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
dev->n_erased_blocks++;
dev->n_free_chunks +=
dev->param.chunks_per_block;
} else {
dev->param.bad_block_fn(dev, 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 =
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;
int realigned_chunk = chunk - dev->chunk_offset;
dev->param.read_chunk_tags_fn(dev, realigned_chunk,
NULL, &tags);
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
"find next checkpt block: search: block %d oid %d seq %d eccr %d",
i, tags.obj_id, tags.seq_number,
tags.ecc_result);
if (tags.seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) {
/* 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)
{
dev->checkpt_open_write = writing;
/* Got the functions we need? */
if (!dev->param.write_chunk_tags_fn ||
!dev->param.read_chunk_tags_fn ||
!dev->param.erase_fn || !dev->param.bad_block_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;
/* Erase all the blocks in the checkpoint area */
if (writing) {
memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
dev->checkpt_byte_offs = 0;
return yaffs_checkpt_erase(dev);
} else {
int i;
/* 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;
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 realigned_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);
realigned_chunk = chunk - dev->chunk_offset;
dev->n_page_writes++;
dev->param.write_chunk_tags_fn(dev, realigned_chunk,
dev->checkpt_buffer, &tags);
dev->checkpt_byte_offs = 0;
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);
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;
int ok = 1;
struct yaffs_ext_tags tags;
int chunk;
int realigned_chunk;
u8 *data_bytes = (u8 *) data;
if (!dev->checkpt_buffer)
return 0;
if (dev->checkpt_open_write)
return -1;
while (i < n_bytes && ok) {
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;
}
if (dev->checkpt_cur_block < 0)
ok = 0;
else {
chunk = dev->checkpt_cur_block *
dev->param.chunks_per_block +
dev->checkpt_cur_chunk;
realigned_chunk = chunk - dev->chunk_offset;
dev->n_page_reads++;
/* read in the next chunk */
dev->param.read_chunk_tags_fn(dev,
realigned_chunk,
dev->
checkpt_buffer,
&tags);
if (tags.chunk_id != (dev->checkpt_page_seq + 1)
|| tags.ecc_result > YAFFS_ECC_RESULT_FIXED
|| tags.seq_number !=
YAFFS_SEQUENCE_CHECKPOINT_DATA)
ok = 0;
dev->checkpt_byte_offs = 0;
dev->checkpt_page_seq++;
dev->checkpt_cur_chunk++;
if (dev->checkpt_cur_chunk >=
dev->param.chunks_per_block)
dev->checkpt_cur_block = -1;
}
}
if (ok) {
*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;
}
int yaffs_checkpt_close(struct yaffs_dev *dev)
{
if (dev->checkpt_open_write) {
if (dev->checkpt_byte_offs != 0)
yaffs2_checkpt_flush_buffer(dev);
} else if (dev->checkpt_block_list) {
int i;
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;
else {
/* Todo this looks odd... */
}
}
kfree(dev->checkpt_block_list);
dev->checkpt_block_list = NULL;
}
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) {
/* free the buffer */
kfree(dev->checkpt_buffer);
dev->checkpt_buffer = NULL;
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);
}

View File

@ -0,0 +1,33 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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

298
fs/yaffs2/yaffs_ecc.c Normal file
View File

@ -0,0 +1,298 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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.
*
*/
/* 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.
*/
#include "yportenv.h"
#include "yaffs_ecc.h"
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_cacl(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;
#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER
/* Swap the bytes into the wrong order */
t = ecc[0];
ecc[0] = ecc[1];
ecc[1] = t;
#endif
}
/* 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;
#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER
/* swap the bytes to correct for the wrong order */
unsigned char t;
t = d0;
d0 = d1;
d1 = t;
#endif
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;
}

44
fs/yaffs2/yaffs_ecc.h Normal file
View File

@ -0,0 +1,44 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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_cacl(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

View File

@ -0,0 +1,35 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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);
YBUG();
}
return &dev->block_info[blk - dev->internal_start_block];
}
#endif

5164
fs/yaffs2/yaffs_guts.c Normal file

File diff suppressed because it is too large Load Diff

915
fs/yaffs2/yaffs_guts.h Normal file
View File

@ -0,0 +1,915 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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_GUTS_H__
#define __YAFFS_GUTS_H__
#include "yportenv.h"
#define YAFFS_OK 1
#define YAFFS_FAIL 0
/* Give us a Y=0x59,
* Give us an A=0x41,
* Give us an FF=0xFF
* Give us an S=0x53
* And what have we got...
*/
#define YAFFS_MAGIC 0x5941FF53
#define YAFFS_NTNODES_LEVEL0 16
#define YAFFS_TNODES_LEVEL0_BITS 4
#define YAFFS_TNODES_LEVEL0_MASK 0xf
#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2)
#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1)
#define YAFFS_TNODES_INTERNAL_MASK 0x7
#define YAFFS_TNODES_MAX_LEVEL 6
#ifndef CONFIG_YAFFS_NO_YAFFS1
#define YAFFS_BYTES_PER_SPARE 16
#define YAFFS_BYTES_PER_CHUNK 512
#define YAFFS_CHUNK_SIZE_SHIFT 9
#define YAFFS_CHUNKS_PER_BLOCK 32
#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK)
#endif
#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024
#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32
#define YAFFS_MAX_CHUNK_ID 0x000FFFFF
#define YAFFS_ALLOCATION_NOBJECTS 100
#define YAFFS_ALLOCATION_NTNODES 100
#define YAFFS_ALLOCATION_NLINKS 100
#define YAFFS_NOBJECT_BUCKETS 256
#define YAFFS_OBJECT_SPACE 0x40000
#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE -1)
#define YAFFS_CHECKPOINT_VERSION 4
#ifdef CONFIG_YAFFS_UNICODE
#define YAFFS_MAX_NAME_LENGTH 127
#define YAFFS_MAX_ALIAS_LENGTH 79
#else
#define YAFFS_MAX_NAME_LENGTH 255
#define YAFFS_MAX_ALIAS_LENGTH 159
#endif
#define YAFFS_SHORT_NAME_LENGTH 15
/* Some special object ids for pseudo objects */
#define YAFFS_OBJECTID_ROOT 1
#define YAFFS_OBJECTID_LOSTNFOUND 2
#define YAFFS_OBJECTID_UNLINKED 3
#define YAFFS_OBJECTID_DELETED 4
/* Pseudo object ids for checkpointing */
#define YAFFS_OBJECTID_SB_HEADER 0x10
#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20
#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21
#define YAFFS_MAX_SHORT_OP_CACHES 20
#define YAFFS_N_TEMP_BUFFERS 6
/* We limit the number attempts at sucessfully saving a chunk of data.
* Small-page devices have 32 pages per block; large-page devices have 64.
* Default to something in the order of 5 to 10 blocks worth of chunks.
*/
#define YAFFS_WR_ATTEMPTS (5*64)
/* Sequence numbers are used in YAFFS2 to determine block allocation order.
* The range is limited slightly to help distinguish bad numbers from good.
* This also allows us to perhaps in the future use special numbers for
* special purposes.
* EFFFFF00 allows the allocation of 8 blocks per second (~1Mbytes) for 15 years,
* and is a larger number than the lifetime of a 2GB device.
*/
#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000
#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xEFFFFF00
/* Special sequence number for bad block that failed to be marked bad */
#define YAFFS_SEQUENCE_BAD_BLOCK 0xFFFF0000
/* ChunkCache is used for short read/write operations.*/
struct yaffs_cache {
struct yaffs_obj *object;
int chunk_id;
int last_use;
int dirty;
int n_bytes; /* Only valid if the cache is dirty */
int locked; /* Can't push out or flush while locked. */
u8 *data;
};
/* Tags structures in RAM
* NB This uses bitfield. Bitfields should not straddle a u32 boundary otherwise
* the structure size will get blown out.
*/
#ifndef CONFIG_YAFFS_NO_YAFFS1
struct yaffs_tags {
unsigned chunk_id:20;
unsigned serial_number:2;
unsigned n_bytes_lsb:10;
unsigned obj_id:18;
unsigned ecc:12;
unsigned n_bytes_msb:2;
};
union yaffs_tags_union {
struct yaffs_tags as_tags;
u8 as_bytes[8];
};
#endif
/* Stuff used for extended tags in YAFFS2 */
enum yaffs_ecc_result {
YAFFS_ECC_RESULT_UNKNOWN,
YAFFS_ECC_RESULT_NO_ERROR,
YAFFS_ECC_RESULT_FIXED,
YAFFS_ECC_RESULT_UNFIXED
};
enum yaffs_obj_type {
YAFFS_OBJECT_TYPE_UNKNOWN,
YAFFS_OBJECT_TYPE_FILE,
YAFFS_OBJECT_TYPE_SYMLINK,
YAFFS_OBJECT_TYPE_DIRECTORY,
YAFFS_OBJECT_TYPE_HARDLINK,
YAFFS_OBJECT_TYPE_SPECIAL
};
#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL
struct yaffs_ext_tags {
unsigned validity0;
unsigned chunk_used; /* Status of the chunk: used or unused */
unsigned obj_id; /* If 0 then this is not part of an object (unused) */
unsigned chunk_id; /* If 0 then this is a header, else a data chunk */
unsigned n_bytes; /* Only valid for data chunks */
/* The following stuff only has meaning when we read */
enum yaffs_ecc_result ecc_result;
unsigned block_bad;
/* YAFFS 1 stuff */
unsigned is_deleted; /* The chunk is marked deleted */
unsigned serial_number; /* Yaffs1 2-bit serial number */
/* YAFFS2 stuff */
unsigned seq_number; /* The sequence number of this block */
/* Extra info if this is an object header (YAFFS2 only) */
unsigned extra_available; /* There is extra info available if this is not zero */
unsigned extra_parent_id; /* The parent object */
unsigned extra_is_shrink; /* Is it a shrink header? */
unsigned extra_shadows; /* Does this shadow another object? */
enum yaffs_obj_type extra_obj_type; /* What object type? */
unsigned extra_length; /* Length if it is a file */
unsigned extra_equiv_id; /* Equivalent object Id if it is a hard link */
unsigned validity1;
};
/* Spare structure for YAFFS1 */
struct yaffs_spare {
u8 tb0;
u8 tb1;
u8 tb2;
u8 tb3;
u8 page_status; /* set to 0 to delete the chunk */
u8 block_status;
u8 tb4;
u8 tb5;
u8 ecc1[3];
u8 tb6;
u8 tb7;
u8 ecc2[3];
};
/*Special structure for passing through to mtd */
struct yaffs_nand_spare {
struct yaffs_spare spare;
int eccres1;
int eccres2;
};
/* Block data in RAM */
enum yaffs_block_state {
YAFFS_BLOCK_STATE_UNKNOWN = 0,
YAFFS_BLOCK_STATE_SCANNING,
/* Being scanned */
YAFFS_BLOCK_STATE_NEEDS_SCANNING,
/* The block might have something on it (ie it is allocating or full, perhaps empty)
* but it needs to be scanned to determine its true state.
* This state is only valid during scanning.
* NB We tolerate empty because the pre-scanner might be incapable of deciding
* However, if this state is returned on a YAFFS2 device, then we expect a sequence number
*/
YAFFS_BLOCK_STATE_EMPTY,
/* This block is empty */
YAFFS_BLOCK_STATE_ALLOCATING,
/* This block is partially allocated.
* At least one page holds valid data.
* This is the one currently being used for page
* allocation. Should never be more than one of these.
* If a block is only partially allocated at mount it is treated as full.
*/
YAFFS_BLOCK_STATE_FULL,
/* All the pages in this block have been allocated.
* If a block was only partially allocated when mounted we treat
* it as fully allocated.
*/
YAFFS_BLOCK_STATE_DIRTY,
/* The block was full and now all chunks have been deleted.
* Erase me, reuse me.
*/
YAFFS_BLOCK_STATE_CHECKPOINT,
/* This block is assigned to holding checkpoint data. */
YAFFS_BLOCK_STATE_COLLECTING,
/* This block is being garbage collected */
YAFFS_BLOCK_STATE_DEAD
/* This block has failed and is not in use */
};
#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1)
struct yaffs_block_info {
int soft_del_pages:10; /* number of soft deleted pages */
int pages_in_use:10; /* number of pages in use */
unsigned block_state:4; /* One of the above block states. NB use unsigned because enum is sometimes an int */
u32 needs_retiring:1; /* Data has failed on this block, need to get valid data off */
/* and retire the block. */
u32 skip_erased_check:1; /* If this is set we can skip the erased check on this block */
u32 gc_prioritise:1; /* An ECC check or blank check has failed on this block.
It should be prioritised for GC */
u32 chunk_error_strikes:3; /* How many times we've had ecc etc failures on this block and tried to reuse it */
#ifdef CONFIG_YAFFS_YAFFS2
u32 has_shrink_hdr:1; /* This block has at least one shrink object header */
u32 seq_number; /* block sequence number for yaffs2 */
#endif
};
/* -------------------------- Object structure -------------------------------*/
/* This is the object structure as stored on NAND */
struct yaffs_obj_hdr {
enum yaffs_obj_type type;
/* Apply to everything */
int parent_obj_id;
u16 sum_no_longer_used; /* checksum of name. No longer used */
YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];
/* The following apply to directories, files, symlinks - not hard links */
u32 yst_mode; /* protection */
u32 yst_uid;
u32 yst_gid;
u32 yst_atime;
u32 yst_mtime;
u32 yst_ctime;
/* File size applies to files only */
int file_size;
/* Equivalent object id applies to hard links only. */
int equiv_id;
/* Alias is for symlinks only. */
YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1];
u32 yst_rdev; /* device stuff for block and char devices (major/min) */
u32 win_ctime[2];
u32 win_atime[2];
u32 win_mtime[2];
u32 inband_shadowed_obj_id;
u32 inband_is_shrink;
u32 reserved[2];
int shadows_obj; /* This object header shadows the specified object if > 0 */
/* is_shrink applies to object headers written when we shrink the file (ie resize) */
u32 is_shrink;
};
/*--------------------------- Tnode -------------------------- */
struct yaffs_tnode {
struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL];
};
/*------------------------ Object -----------------------------*/
/* An object can be one of:
* - a directory (no data, has children links
* - a regular file (data.... not prunes :->).
* - a symlink [symbolic link] (the alias).
* - a hard link
*/
struct yaffs_file_var {
u32 file_size;
u32 scanned_size;
u32 shrink_size;
int top_level;
struct yaffs_tnode *top;
};
struct yaffs_dir_var {
struct list_head children; /* list of child links */
struct list_head dirty; /* Entry for list of dirty directories */
};
struct yaffs_symlink_var {
YCHAR *alias;
};
struct yaffs_hardlink_var {
struct yaffs_obj *equiv_obj;
u32 equiv_id;
};
union yaffs_obj_var {
struct yaffs_file_var file_variant;
struct yaffs_dir_var dir_variant;
struct yaffs_symlink_var symlink_variant;
struct yaffs_hardlink_var hardlink_variant;
};
struct yaffs_obj {
u8 deleted:1; /* This should only apply to unlinked files. */
u8 soft_del:1; /* it has also been soft deleted */
u8 unlinked:1; /* An unlinked file. The file should be in the unlinked directory. */
u8 fake:1; /* A fake object has no presence on NAND. */
u8 rename_allowed:1; /* Some objects are not allowed to be renamed. */
u8 unlink_allowed:1;
u8 dirty:1; /* the object needs to be written to flash */
u8 valid:1; /* When the file system is being loaded up, this
* object might be created before the data
* is available (ie. file data records appear before the header).
*/
u8 lazy_loaded:1; /* This object has been lazy loaded and is missing some detail */
u8 defered_free:1; /* For Linux kernel. Object is removed from NAND, but is
* still in the inode cache. Free of object is defered.
* until the inode is released.
*/
u8 being_created:1; /* This object is still being created so skip some checks. */
u8 is_shadowed:1; /* This object is shadowed on the way to being renamed. */
u8 xattr_known:1; /* We know if this has object has xattribs or not. */
u8 has_xattr:1; /* This object has xattribs. Valid if xattr_known. */
u8 serial; /* serial number of chunk in NAND. Cached here */
u16 sum; /* sum of the name to speed searching */
struct yaffs_dev *my_dev; /* The device I'm on */
struct list_head hash_link; /* list of objects in this hash bucket */
struct list_head hard_links; /* all the equivalent hard linked objects */
/* directory structure stuff */
/* also used for linking up the free list */
struct yaffs_obj *parent;
struct list_head siblings;
/* Where's my object header in NAND? */
int hdr_chunk;
int n_data_chunks; /* Number of data chunks attached to the file. */
u32 obj_id; /* the object id value */
u32 yst_mode;
#ifndef CONFIG_YAFFS_NO_SHORT_NAMES
YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1];
#endif
#ifdef CONFIG_YAFFS_WINCE
u32 win_ctime[2];
u32 win_mtime[2];
u32 win_atime[2];
#else
u32 yst_uid;
u32 yst_gid;
u32 yst_atime;
u32 yst_mtime;
u32 yst_ctime;
#endif
u32 yst_rdev;
void *my_inode;
enum yaffs_obj_type variant_type;
union yaffs_obj_var variant;
};
struct yaffs_obj_bucket {
struct list_head list;
int count;
};
/* yaffs_checkpt_obj holds the definition of an object as dumped
* by checkpointing.
*/
struct yaffs_checkpt_obj {
int struct_type;
u32 obj_id;
u32 parent_id;
int hdr_chunk;
enum yaffs_obj_type variant_type:3;
u8 deleted:1;
u8 soft_del:1;
u8 unlinked:1;
u8 fake:1;
u8 rename_allowed:1;
u8 unlink_allowed:1;
u8 serial;
int n_data_chunks;
u32 size_or_equiv_obj;
};
/*--------------------- Temporary buffers ----------------
*
* These are chunk-sized working buffers. Each device has a few
*/
struct yaffs_buffer {
u8 *buffer;
int line; /* track from whence this buffer was allocated */
int max_line;
};
/*----------------- Device ---------------------------------*/
struct yaffs_param {
const YCHAR *name;
/*
* Entry parameters set up way early. Yaffs sets up the rest.
* The structure should be zeroed out before use so that unused
* and defualt values are zero.
*/
int inband_tags; /* Use unband tags */
u32 total_bytes_per_chunk; /* Should be >= 512, does not need to be a power of 2 */
int chunks_per_block; /* does not need to be a power of 2 */
int spare_bytes_per_chunk; /* spare area size */
int start_block; /* Start block we're allowed to use */
int end_block; /* End block we're allowed to use */
int n_reserved_blocks; /* We want this tuneable so that we can reduce */
/* reserved blocks on NOR and RAM. */
int n_caches; /* If <= 0, then short op caching is disabled, else
* the number of short op caches (don't use too many).
* 10 to 20 is a good bet.
*/
int use_nand_ecc; /* Flag to decide whether or not to use NANDECC on data (yaffs1) */
int no_tags_ecc; /* Flag to decide whether or not to do ECC on packed tags (yaffs2) */
int is_yaffs2; /* Use yaffs2 mode on this device */
int empty_lost_n_found; /* Auto-empty lost+found directory on mount */
int refresh_period; /* How often we should check to do a block refresh */
/* Checkpoint control. Can be set before or after initialisation */
u8 skip_checkpt_rd;
u8 skip_checkpt_wr;
int enable_xattr; /* Enable xattribs */
/* NAND access functions (Must be set before calling YAFFS) */
int (*write_chunk_fn) (struct yaffs_dev * dev,
int nand_chunk, const u8 * data,
const struct yaffs_spare * spare);
int (*read_chunk_fn) (struct yaffs_dev * dev,
int nand_chunk, u8 * data,
struct yaffs_spare * spare);
int (*erase_fn) (struct yaffs_dev * dev, int flash_block);
int (*initialise_flash_fn) (struct yaffs_dev * dev);
int (*deinitialise_flash_fn) (struct yaffs_dev * dev);
#ifdef CONFIG_YAFFS_YAFFS2
int (*write_chunk_tags_fn) (struct yaffs_dev * dev,
int nand_chunk, const u8 * data,
const struct yaffs_ext_tags * tags);
int (*read_chunk_tags_fn) (struct yaffs_dev * dev,
int nand_chunk, u8 * data,
struct yaffs_ext_tags * tags);
int (*bad_block_fn) (struct yaffs_dev * dev, int block_no);
int (*query_block_fn) (struct yaffs_dev * dev, int block_no,
enum yaffs_block_state * state,
u32 * seq_number);
#endif
/* The remove_obj_fn function must be supplied by OS flavours that
* need it.
* yaffs direct uses it to implement the faster readdir.
* Linux uses it to protect the directory during unlocking.
*/
void (*remove_obj_fn) (struct yaffs_obj * obj);
/* Callback to mark the superblock dirty */
void (*sb_dirty_fn) (struct yaffs_dev * dev);
/* Callback to control garbage collection. */
unsigned (*gc_control) (struct yaffs_dev * dev);
/* Debug control flags. Don't use unless you know what you're doing */
int use_header_file_size; /* Flag to determine if we should use file sizes from the header */
int disable_lazy_load; /* Disable lazy loading on this device */
int wide_tnodes_disabled; /* Set to disable wide tnodes */
int disable_soft_del; /* yaffs 1 only: Set to disable the use of softdeletion. */
int defered_dir_update; /* Set to defer directory updates */
#ifdef CONFIG_YAFFS_AUTO_UNICODE
int auto_unicode;
#endif
int always_check_erased; /* Force chunk erased check always on */
};
struct yaffs_dev {
struct yaffs_param param;
/* Context storage. Holds extra OS specific data for this device */
void *os_context;
void *driver_context;
struct list_head dev_list;
/* Runtime parameters. Set up by YAFFS. */
int data_bytes_per_chunk;
/* Non-wide tnode stuff */
u16 chunk_grp_bits; /* Number of bits that need to be resolved if
* the tnodes are not wide enough.
*/
u16 chunk_grp_size; /* == 2^^chunk_grp_bits */
/* Stuff to support wide tnodes */
u32 tnode_width;
u32 tnode_mask;
u32 tnode_size;
/* Stuff for figuring out file offset to chunk conversions */
u32 chunk_shift; /* Shift value */
u32 chunk_div; /* Divisor after shifting: 1 for power-of-2 sizes */
u32 chunk_mask; /* Mask to use for power-of-2 case */
int is_mounted;
int read_only;
int is_checkpointed;
/* Stuff to support block offsetting to support start block zero */
int internal_start_block;
int internal_end_block;
int block_offset;
int chunk_offset;
/* Runtime checkpointing stuff */
int checkpt_page_seq; /* running sequence number of checkpoint pages */
int checkpt_byte_count;
int checkpt_byte_offs;
u8 *checkpt_buffer;
int checkpt_open_write;
int blocks_in_checkpt;
int checkpt_cur_chunk;
int checkpt_cur_block;
int checkpt_next_block;
int *checkpt_block_list;
int checkpt_max_blocks;
u32 checkpt_sum;
u32 checkpt_xor;
int checkpoint_blocks_required; /* Number of blocks needed to store current checkpoint set */
/* Block Info */
struct yaffs_block_info *block_info;
u8 *chunk_bits; /* bitmap of chunks in use */
unsigned block_info_alt:1; /* was allocated using alternative strategy */
unsigned chunk_bits_alt:1; /* was allocated using alternative strategy */
int chunk_bit_stride; /* Number of bytes of chunk_bits per block.
* Must be consistent with chunks_per_block.
*/
int n_erased_blocks;
int alloc_block; /* Current block being allocated off */
u32 alloc_page;
int alloc_block_finder; /* Used to search for next allocation block */
/* Object and Tnode memory management */
void *allocator;
int n_obj;
int n_tnodes;
int n_hardlinks;
struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS];
u32 bucket_finder;
int n_free_chunks;
/* Garbage collection control */
u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */
u32 n_clean_ups;
unsigned has_pending_prioritised_gc; /* We think this device might have pending prioritised gcs */
unsigned gc_disable;
unsigned gc_block_finder;
unsigned gc_dirtiest;
unsigned gc_pages_in_use;
unsigned gc_not_done;
unsigned gc_block;
unsigned gc_chunk;
unsigned gc_skip;
/* Special directories */
struct yaffs_obj *root_dir;
struct yaffs_obj *lost_n_found;
/* Buffer areas for storing data to recover from write failures TODO
* u8 buffered_data[YAFFS_CHUNKS_PER_BLOCK][YAFFS_BYTES_PER_CHUNK];
* struct yaffs_spare buffered_spare[YAFFS_CHUNKS_PER_BLOCK];
*/
int buffered_block; /* Which block is buffered here? */
int doing_buffered_block_rewrite;
struct yaffs_cache *cache;
int cache_last_use;
/* Stuff for background deletion and unlinked files. */
struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted files live. */
struct yaffs_obj *del_dir; /* Directory where deleted objects are sent to disappear. */
struct yaffs_obj *unlinked_deletion; /* Current file being background deleted. */
int n_deleted_files; /* Count of files awaiting deletion; */
int n_unlinked_files; /* Count of unlinked files. */
int n_bg_deletions; /* Count of background deletions. */
/* Temporary buffer management */
struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS];
int max_temp;
int temp_in_use;
int unmanaged_buffer_allocs;
int unmanaged_buffer_deallocs;
/* yaffs2 runtime stuff */
unsigned seq_number; /* Sequence number of currently allocating block */
unsigned oldest_dirty_seq;
unsigned oldest_dirty_block;
/* Block refreshing */
int refresh_skip; /* A skip down counter. Refresh happens when this gets to zero. */
/* Dirty directory handling */
struct list_head dirty_dirs; /* List of dirty directories */
/* Statistcs */
u32 n_page_writes;
u32 n_page_reads;
u32 n_erasures;
u32 n_erase_failures;
u32 n_gc_copies;
u32 all_gcs;
u32 passive_gc_count;
u32 oldest_dirty_gc_count;
u32 n_gc_blocks;
u32 bg_gcs;
u32 n_retired_writes;
u32 n_retired_blocks;
u32 n_ecc_fixed;
u32 n_ecc_unfixed;
u32 n_tags_ecc_fixed;
u32 n_tags_ecc_unfixed;
u32 n_deletions;
u32 n_unmarked_deletions;
u32 refresh_count;
u32 cache_hits;
};
/* The CheckpointDevice structure holds the device information that changes at runtime and
* must be preserved over unmount/mount cycles.
*/
struct yaffs_checkpt_dev {
int struct_type;
int n_erased_blocks;
int alloc_block; /* Current block being allocated off */
u32 alloc_page;
int n_free_chunks;
int n_deleted_files; /* Count of files awaiting deletion; */
int n_unlinked_files; /* Count of unlinked files. */
int n_bg_deletions; /* Count of background deletions. */
/* yaffs2 runtime stuff */
unsigned seq_number; /* Sequence number of currently allocating block */
};
struct yaffs_checkpt_validity {
int struct_type;
u32 magic;
u32 version;
u32 head;
};
struct yaffs_shadow_fixer {
int obj_id;
int shadowed_id;
struct yaffs_shadow_fixer *next;
};
/* Structure for doing xattr modifications */
struct yaffs_xattr_mod {
int set; /* If 0 then this is a deletion */
const YCHAR *name;
const void *data;
int size;
int flags;
int result;
};
/*----------------------- YAFFS Functions -----------------------*/
int yaffs_guts_initialise(struct yaffs_dev *dev);
void yaffs_deinitialise(struct yaffs_dev *dev);
int yaffs_get_n_free_chunks(struct yaffs_dev *dev);
int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name,
struct yaffs_obj *new_dir, const YCHAR * new_name);
int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name);
int yaffs_del_obj(struct yaffs_obj *obj);
int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size);
int yaffs_get_obj_length(struct yaffs_obj *obj);
int yaffs_get_obj_inode(struct yaffs_obj *obj);
unsigned yaffs_get_obj_type(struct yaffs_obj *obj);
int yaffs_get_obj_link_count(struct yaffs_obj *obj);
/* File operations */
int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset,
int n_bytes);
int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset,
int n_bytes, int write_trhrough);
int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size);
struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent,
const YCHAR * name, u32 mode, u32 uid,
u32 gid);
int yaffs_flush_file(struct yaffs_obj *obj, int update_time, int data_sync);
/* Flushing and checkpointing */
void yaffs_flush_whole_cache(struct yaffs_dev *dev);
int yaffs_checkpoint_save(struct yaffs_dev *dev);
int yaffs_checkpoint_restore(struct yaffs_dev *dev);
/* Directory operations */
struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR * name,
u32 mode, u32 uid, u32 gid);
struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir,
const YCHAR * name);
struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number);
/* Link operations */
struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name,
struct yaffs_obj *equiv_obj);
struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj);
/* Symlink operations */
struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent,
const YCHAR * name, u32 mode, u32 uid,
u32 gid, const YCHAR * alias);
YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj);
/* Special inodes (fifos, sockets and devices) */
struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent,
const YCHAR * name, u32 mode, u32 uid,
u32 gid, u32 rdev);
int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name,
const void *value, int size, int flags);
int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value,
int size);
int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size);
int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name);
/* Special directories */
struct yaffs_obj *yaffs_root(struct yaffs_dev *dev);
struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev);
void yaffs_handle_defered_free(struct yaffs_obj *obj);
void yaffs_update_dirty_dirs(struct yaffs_dev *dev);
int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency);
/* Debug dump */
int yaffs_dump_obj(struct yaffs_obj *obj);
void yaffs_guts_test(struct yaffs_dev *dev);
/* A few useful functions to be used within the core files*/
void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash,
int lyn);
int yaffs_check_ff(u8 * buffer, int n_bytes);
void yaffs_handle_chunk_error(struct yaffs_dev *dev,
struct yaffs_block_info *bi);
u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev, int line_no);
void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 * buffer, int line_no);
struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev,
int number,
enum yaffs_obj_type type);
int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk,
int nand_chunk, int in_scan);
void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name);
void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj,
const struct yaffs_obj_hdr *oh);
void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj);
YCHAR *yaffs_clone_str(const YCHAR * str);
void yaffs_link_fixup(struct yaffs_dev *dev, struct yaffs_obj *hard_list);
void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no);
int yaffs_update_oh(struct yaffs_obj *in, const YCHAR * name,
int force, int is_shrink, int shadows,
struct yaffs_xattr_mod *xop);
void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id,
int backward_scanning);
int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks);
struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev);
struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev,
struct yaffs_file_var *file_struct,
u32 chunk_id,
struct yaffs_tnode *passed_tn);
int yaffs_do_file_wr(struct yaffs_obj *in, const u8 * buffer, loff_t offset,
int n_bytes, int write_trhrough);
void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size);
void yaffs_skip_rest_of_block(struct yaffs_dev *dev);
int yaffs_count_free_chunks(struct yaffs_dev *dev);
struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev,
struct yaffs_file_var *file_struct,
u32 chunk_id);
u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn,
unsigned pos);
int yaffs_is_non_empty_dir(struct yaffs_obj *obj);
#endif

41
fs/yaffs2/yaffs_linux.h Normal file
View File

@ -0,0 +1,41 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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 size of the buffer
* at compile time so we have to allocate it.
*/
struct list_head search_contexts;
void (*put_super_fn) (struct super_block * sb);
struct task_struct *readdir_process;
unsigned mount_id;
};
#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))
#endif

54
fs/yaffs2/yaffs_mtdif.c Normal file
View File

@ -0,0 +1,54 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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 "yaffs_linux.h"
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;
else
return YAFFS_FAIL;
}
int nandmtd_initialise(struct yaffs_dev *dev)
{
return YAFFS_OK;
}

23
fs/yaffs2/yaffs_mtdif.h Normal file
View File

@ -0,0 +1,23 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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"
int nandmtd_erase_block(struct yaffs_dev *dev, int block_no);
int nandmtd_initialise(struct yaffs_dev *dev);
#endif

330
fs/yaffs2/yaffs_mtdif1.c Normal file
View File

@ -0,0 +1,330 @@
/*
* YAFFS: Yet another FFS. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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 module provides the interface between yaffs_nand.c and the
* MTD API. This version is used when the MTD interface supports the
* 'mtd_oob_ops' style calls to read_oob and write_oob, circa 2.6.17,
* and we have small-page NAND device.
*
* These functions are invoked via function pointers in yaffs_nand.c.
* This replaces functionality provided by functions in yaffs_mtdif.c
* and the yaffs_tags compatability functions in yaffs_tagscompat.c that are
* called in yaffs_mtdif.c when the function pointers are NULL.
* We assume the MTD layer is performing ECC (use_nand_ecc is true).
*/
#include "yportenv.h"
#include "yaffs_trace.h"
#include "yaffs_guts.h"
#include "yaffs_packedtags1.h"
#include "yaffs_tagscompat.h" /* for yaffs_calc_tags_ecc */
#include "yaffs_linux.h"
#include "linux/kernel.h"
#include "linux/version.h"
#include "linux/types.h"
#include "linux/mtd/mtd.h"
#ifndef CONFIG_YAFFS_9BYTE_TAGS
# define YTAG1_SIZE 8
#else
# define YTAG1_SIZE 9
#endif
/* Write a chunk (page) of data to NAND.
*
* Caller always provides ExtendedTags data which are converted to a more
* compact (packed) form for storage in NAND. A mini-ECC runs over the
* contents of the tags meta-data; used to valid the tags when read.
*
* - Pack ExtendedTags to packed_tags1 form
* - Compute mini-ECC for packed_tags1
* - Write data and packed tags to NAND.
*
* Note: Due to the use of the packed_tags1 meta-data which does not include
* a full sequence number (as found in the larger packed_tags2 form) it is
* necessary for Yaffs to re-write a chunk/page (just once) to mark it as
* discarded and dirty. This is not ideal: newer NAND parts are supposed
* to be written just once. When Yaffs performs this operation, this
* function is called with a NULL data pointer -- calling MTD write_oob
* without data is valid usage (2.6.17).
*
* Any underlying MTD error results in YAFFS_FAIL.
* Returns YAFFS_OK or YAFFS_FAIL.
*/
int nandmtd1_write_chunk_tags(struct yaffs_dev *dev,
int nand_chunk, const u8 * data,
const struct yaffs_ext_tags *etags)
{
struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
int chunk_bytes = dev->data_bytes_per_chunk;
loff_t addr = ((loff_t) nand_chunk) * chunk_bytes;
struct mtd_oob_ops ops;
struct yaffs_packed_tags1 pt1;
int retval;
/* we assume that packed_tags1 and struct 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, etags);
yaffs_calc_tags_ecc((struct yaffs_tags *)&pt1);
/* When deleting a chunk, the upper layer provides only skeletal
* etags, 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.
*/
#ifndef CONFIG_YAFFS_9BYTE_TAGS
if (etags->is_deleted) {
memset(&pt1, 0xff, 8);
/* clear delete status bit to indicate deleted */
pt1.deleted = 0;
}
#else
((u8 *) & pt1)[8] = 0xff;
if (etags->is_deleted) {
memset(&pt1, 0xff, 8);
/* zero page_status byte to indicate deleted */
((u8 *) & pt1)[8] = 0;
}
#endif
memset(&ops, 0, sizeof(ops));
ops.mode = MTD_OOB_AUTO;
ops.len = (data) ? chunk_bytes : 0;
ops.ooblen = YTAG1_SIZE;
ops.datbuf = (u8 *) data;
ops.oobbuf = (u8 *) & pt1;
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;
}
/* Return with empty ExtendedTags but add ecc_result.
*/
static int rettags(struct yaffs_ext_tags *etags, int ecc_result, int retval)
{
if (etags) {
memset(etags, 0, sizeof(*etags));
etags->ecc_result = ecc_result;
}
return retval;
}
/* Read a chunk (page) from NAND.
*
* Caller expects ExtendedTags data to be usable even on error; that is,
* all members except ecc_result and block_bad are zeroed.
*
* - Check ECC results for data (if applicable)
* - Check for blank/erased block (return empty ExtendedTags if blank)
* - Check the packed_tags1 mini-ECC (correct if necessary/possible)
* - Convert packed_tags1 to ExtendedTags
* - Update ecc_result and block_bad members to refect state.
*
* Returns YAFFS_OK or YAFFS_FAIL.
*/
int nandmtd1_read_chunk_tags(struct yaffs_dev *dev,
int nand_chunk, u8 * data,
struct yaffs_ext_tags *etags)
{
struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
int chunk_bytes = dev->data_bytes_per_chunk;
loff_t addr = ((loff_t) nand_chunk) * chunk_bytes;
int eccres = YAFFS_ECC_RESULT_NO_ERROR;
struct mtd_oob_ops ops;
struct yaffs_packed_tags1 pt1;
int retval;
int deleted;
memset(&ops, 0, sizeof(ops));
ops.mode = MTD_OOB_AUTO;
ops.len = (data) ? chunk_bytes : 0;
ops.ooblen = YTAG1_SIZE;
ops.datbuf = data;
ops.oobbuf = (u8 *) & pt1;
/* 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 */
break;
case -EUCLEAN:
/* MTD's ECC fixed the data */
eccres = YAFFS_ECC_RESULT_FIXED;
dev->n_ecc_fixed++;
break;
case -EBADMSG:
/* MTD's ECC could not fix the data */
dev->n_ecc_unfixed++;
/* fall into... */
default:
rettags(etags, YAFFS_ECC_RESULT_UNFIXED, 0);
etags->block_bad = (mtd->block_isbad) (mtd, addr);
return YAFFS_FAIL;
}
/* Check for a blank/erased chunk.
*/
if (yaffs_check_ff((u8 *) & pt1, 8)) {
/* when blank, upper layers want ecc_result to be <= NO_ERROR */
return rettags(etags, YAFFS_ECC_RESULT_NO_ERROR, YAFFS_OK);
}
#ifndef CONFIG_YAFFS_9BYTE_TAGS
/* 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 = (yaffs_count_bits(((u8 *) & pt1)[8]) < 7);
#endif
/* 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 (eccres == YAFFS_ECC_RESULT_NO_ERROR)
eccres = YAFFS_ECC_RESULT_FIXED;
break;
default:
/* unrecovered tags-ECC error */
dev->n_tags_ecc_unfixed++;
return rettags(etags, 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(etags, &pt1);
etags->ecc_result = eccres;
/* Set deleted state */
etags->is_deleted = deleted;
return YAFFS_OK;
}
/* Mark a block bad.
*
* This is a persistant state.
* Use of this function should be rare.
*
* Returns YAFFS_OK or YAFFS_FAIL.
*/
int nandmtd1_mark_block_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->data_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;
}
/* Check any MTD prerequists.
*
* Returns YAFFS_OK or YAFFS_FAIL.
*/
static int nandmtd1_test_prerequists(struct mtd_info *mtd)
{
/* 2.6.18 has mtd->ecclayout->oobavail */
/* 2.6.21 has mtd->ecclayout->oobavail and mtd->oobavail */
int oobavail = mtd->ecclayout->oobavail;
if (oobavail < YTAG1_SIZE) {
yaffs_trace(YAFFS_TRACE_ERROR,
"mtd device has only %d bytes for tags, need %d",
oobavail, YTAG1_SIZE);
return YAFFS_FAIL;
}
return YAFFS_OK;
}
/* Query for the current state of a specific block.
*
* Examine the tags of the first chunk of the block and return the state:
* - YAFFS_BLOCK_STATE_DEAD, the block is marked bad
* - YAFFS_BLOCK_STATE_NEEDS_SCANNING, the block is in use
* - YAFFS_BLOCK_STATE_EMPTY, the block is clean
*
* Always returns YAFFS_OK.
*/
int nandmtd1_query_block(struct yaffs_dev *dev, int block_no,
enum yaffs_block_state *state_ptr, u32 * seq_ptr)
{
struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
int chunk_num = block_no * dev->param.chunks_per_block;
loff_t addr = (loff_t) chunk_num * dev->data_bytes_per_chunk;
struct yaffs_ext_tags etags;
int state = YAFFS_BLOCK_STATE_DEAD;
int seqnum = 0;
int retval;
/* We don't yet have a good place to test for MTD config prerequists.
* Do it here as we are called during the initial scan.
*/
if (nandmtd1_test_prerequists(mtd) != YAFFS_OK)
return YAFFS_FAIL;
retval = nandmtd1_read_chunk_tags(dev, chunk_num, NULL, &etags);
etags.block_bad = (mtd->block_isbad) (mtd, addr);
if (etags.block_bad) {
yaffs_trace(YAFFS_TRACE_BAD_BLOCKS,
"block %d is marked bad", block_no);
state = YAFFS_BLOCK_STATE_DEAD;
} else if (etags.ecc_result != YAFFS_ECC_RESULT_NO_ERROR) {
/* bad tags, need to look more closely */
state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
} else if (etags.chunk_used) {
state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
seqnum = etags.seq_number;
} else {
state = YAFFS_BLOCK_STATE_EMPTY;
}
*state_ptr = state;
*seq_ptr = seqnum;
/* query always succeeds */
return YAFFS_OK;
}

29
fs/yaffs2/yaffs_mtdif1.h Normal file
View File

@ -0,0 +1,29 @@
/*
* YAFFS: Yet another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* 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_MTDIF1_H__
#define __YAFFS_MTDIF1_H__
int nandmtd1_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
const u8 * data,
const struct yaffs_ext_tags *tags);
int nandmtd1_read_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
u8 * data, struct yaffs_ext_tags *tags);
int nandmtd1_mark_block_bad(struct yaffs_dev *dev, int block_no);
int nandmtd1_query_block(struct yaffs_dev *dev, int block_no,
enum yaffs_block_state *state, u32 * seq_number);
#endif

225
fs/yaffs2/yaffs_mtdif2.c Normal file
View File

@ -0,0 +1,225 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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.
*/
/* mtd interface for YAFFS2 */
#include "yportenv.h"
#include "yaffs_trace.h"
#include "yaffs_mtdif2.h"
#include "linux/mtd/mtd.h"
#include "linux/types.h"
#include "linux/time.h"
#include "yaffs_packedtags2.h"
#include "yaffs_linux.h"
/* NB For use with inband tags....
* We assume that the data buffer is of size total_bytes_per_chunk so that we can also
* use it to load the tags.
*/
int nandmtd2_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
const u8 * data,
const struct yaffs_ext_tags *tags)
{
struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
struct mtd_oob_ops ops;
int retval = 0;
loff_t addr;
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,
"nandmtd2_write_chunk_tags chunk %d data %p tags %p",
nand_chunk, data, tags);
addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
/* 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);
}
ops.mode = MTD_OOB_AUTO;
ops.ooblen = (dev->param.inband_tags) ? 0 : packed_tags_size;
ops.len = dev->param.total_bytes_per_chunk;
ops.ooboffs = 0;
ops.datbuf = (u8 *) data;
ops.oobbuf = (dev->param.inband_tags) ? NULL : packed_tags_ptr;
retval = mtd->write_oob(mtd, addr, &ops);
if (retval == 0)
return YAFFS_OK;
else
return YAFFS_FAIL;
}
int nandmtd2_read_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
u8 * data, struct yaffs_ext_tags *tags)
{
struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
struct mtd_oob_ops ops;
size_t dummy;
int retval = 0;
int local_data = 0;
loff_t addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
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,
"nandmtd2_read_chunk_tags 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, __LINE__);
}
}
if (dev->param.inband_tags || (data && !tags))
retval = mtd->read(mtd, addr, dev->param.total_bytes_per_chunk,
&dummy, data);
else if (tags) {
ops.mode = MTD_OOB_AUTO;
ops.ooblen = packed_tags_size;
ops.len = data ? dev->data_bytes_per_chunk : packed_tags_size;
ops.ooboffs = 0;
ops.datbuf = data;
ops.oobbuf = yaffs_dev_to_lc(dev)->spare_buffer;
retval = mtd->read_oob(mtd, addr, &ops);
}
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,
yaffs_dev_to_lc(dev)->spare_buffer,
packed_tags_size);
yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc);
}
}
if (local_data)
yaffs_release_temp_buffer(dev, data, __LINE__);
if (tags && retval == -EBADMSG
&& tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) {
tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED;
dev->n_ecc_unfixed++;
}
if (tags && retval == -EUCLEAN
&& tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) {
tags->ecc_result = YAFFS_ECC_RESULT_FIXED;
dev->n_ecc_fixed++;
}
if (retval == 0)
return YAFFS_OK;
else
return YAFFS_FAIL;
}
int nandmtd2_mark_block_bad(struct yaffs_dev *dev, int block_no)
{
struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
int retval;
yaffs_trace(YAFFS_TRACE_MTD,
"nandmtd2_mark_block_bad %d", block_no);
retval =
mtd->block_markbad(mtd,
block_no * dev->param.chunks_per_block *
dev->param.total_bytes_per_chunk);
if (retval == 0)
return YAFFS_OK;
else
return YAFFS_FAIL;
}
int nandmtd2_query_block(struct yaffs_dev *dev, int block_no,
enum yaffs_block_state *state, u32 * seq_number)
{
struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
int retval;
yaffs_trace(YAFFS_TRACE_MTD, "nandmtd2_query_block %d", block_no);
retval =
mtd->block_isbad(mtd,
block_no * dev->param.chunks_per_block *
dev->param.total_bytes_per_chunk);
if (retval) {
yaffs_trace(YAFFS_TRACE_MTD, "block is bad");
*state = YAFFS_BLOCK_STATE_DEAD;
*seq_number = 0;
} else {
struct yaffs_ext_tags t;
nandmtd2_read_chunk_tags(dev, block_no *
dev->param.chunks_per_block, NULL, &t);
if (t.chunk_used) {
*seq_number = t.seq_number;
*state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
} else {
*seq_number = 0;
*state = YAFFS_BLOCK_STATE_EMPTY;
}
}
yaffs_trace(YAFFS_TRACE_MTD,
"block is bad seq %d state %d", *seq_number, *state);
if (retval == 0)
return YAFFS_OK;
else
return YAFFS_FAIL;
}

29
fs/yaffs2/yaffs_mtdif2.h Normal file
View File

@ -0,0 +1,29 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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_MTDIF2_H__
#define __YAFFS_MTDIF2_H__
#include "yaffs_guts.h"
int nandmtd2_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
const u8 * data,
const struct yaffs_ext_tags *tags);
int nandmtd2_read_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
u8 * data, struct yaffs_ext_tags *tags);
int nandmtd2_mark_block_bad(struct yaffs_dev *dev, int block_no);
int nandmtd2_query_block(struct yaffs_dev *dev, int block_no,
enum yaffs_block_state *state, u32 * seq_number);
#endif

201
fs/yaffs2/yaffs_nameval.c Normal file
View File

@ -0,0 +1,201 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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) == 0) {
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 -1;
}
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) {
/* 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;
} else {
return -ENODATA;
}
}
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 (size <= bsize) {
memcpy(buf, xb + pos, size);
return size;
}
}
if (pos >= 0)
return -ERANGE;
else
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;
}

28
fs/yaffs2/yaffs_nameval.h Normal file
View File

@ -0,0 +1,28 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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

127
fs/yaffs2/yaffs_nand.c Normal file
View File

@ -0,0 +1,127 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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_tagsvalidity.h"
#include "yaffs_getblockinfo.h"
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 realigned_chunk = nand_chunk - dev->chunk_offset;
dev->n_page_reads++;
/* If there are no tags provided, use local tags to get prioritised gc working */
if (!tags)
tags = &local_tags;
if (dev->param.read_chunk_tags_fn)
result =
dev->param.read_chunk_tags_fn(dev, realigned_chunk, buffer,
tags);
else
result = yaffs_tags_compat_rd(dev,
realigned_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)
{
dev->n_page_writes++;
nand_chunk -= dev->chunk_offset;
if (tags) {
tags->seq_number = dev->seq_number;
tags->chunk_used = 1;
if (!yaffs_validate_tags(tags)) {
yaffs_trace(YAFFS_TRACE_ERROR, "Writing uninitialised tags");
YBUG();
}
yaffs_trace(YAFFS_TRACE_WRITE,
"Writing chunk %d tags %d %d",
nand_chunk, tags->obj_id, tags->chunk_id);
} else {
yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags");
YBUG();
}
if (dev->param.write_chunk_tags_fn)
return dev->param.write_chunk_tags_fn(dev, nand_chunk, buffer,
tags);
else
return yaffs_tags_compat_wr(dev, nand_chunk, buffer, tags);
}
int yaffs_mark_bad(struct yaffs_dev *dev, int block_no)
{
block_no -= dev->block_offset;
if (dev->param.bad_block_fn)
return dev->param.bad_block_fn(dev, block_no);
else
return yaffs_tags_compat_mark_bad(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;
if (dev->param.query_block_fn)
return dev->param.query_block_fn(dev, block_no, state,
seq_number);
else
return yaffs_tags_compat_query_block(dev, block_no,
state, seq_number);
}
int yaffs_erase_block(struct yaffs_dev *dev, int flash_block)
{
int result;
flash_block -= dev->block_offset;
dev->n_erasures++;
result = dev->param.erase_fn(dev, flash_block);
return result;
}
int yaffs_init_nand(struct yaffs_dev *dev)
{
if (dev->param.initialise_flash_fn)
return dev->param.initialise_flash_fn(dev);
return YAFFS_OK;
}

38
fs/yaffs2/yaffs_nand.h Normal file
View File

@ -0,0 +1,38 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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);
#endif

View File

@ -0,0 +1,53 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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"
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)
{
static const u8 all_ff[] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff
};
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));
}
}

View File

@ -0,0 +1,39 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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 {
unsigned chunk_id:20;
unsigned serial_number:2;
unsigned n_bytes:10;
unsigned obj_id:18;
unsigned ecc:12;
unsigned deleted:1;
unsigned 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

View File

@ -0,0 +1,196 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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"
#include "yaffs_tagsvalidity.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);
}
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;
if (t->chunk_id == 0 && t->extra_available) {
/* 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 = t->extra_length;
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));
yaffs_init_tags(t);
if (ptt->seq_number != 0xFFFFFFFF) {
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_length = 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);
}

View File

@ -0,0 +1,47 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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

View File

@ -0,0 +1,422 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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_ecc(const u8 * data, struct yaffs_spare *spare)
{
yaffs_ecc_cacl(data, spare->ecc1);
yaffs_ecc_cacl(&data[256], spare->ecc2);
}
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)
{
if (nand_chunk < dev->param.start_block * dev->param.chunks_per_block) {
yaffs_trace(YAFFS_TRACE_ERROR,
"**>> yaffs chunk %d is not valid",
nand_chunk);
return YAFFS_FAIL;
}
return dev->param.write_chunk_fn(dev, nand_chunk, data, 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;
if (!spare && data) {
/* 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;
}
if (!dev->param.use_nand_ecc) {
ret_val =
dev->param.read_chunk_fn(dev, nand_chunk, data, spare);
if (data && correct_errors) {
/* Do ECC correction */
/* Todo handle any errors */
int ecc_result1, ecc_result2;
u8 calc_ecc[3];
yaffs_ecc_cacl(data, calc_ecc);
ecc_result1 =
yaffs_ecc_correct(data, spare->ecc1, calc_ecc);
yaffs_ecc_cacl(&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;
}
} else {
/* Must allocate enough memory for spare+2*sizeof(int) */
/* for ecc results from device. */
struct yaffs_nand_spare nspare;
memset(&nspare, 0, sizeof(nspare));
ret_val = dev->param.read_chunk_fn(dev, nand_chunk, data,
(struct yaffs_spare *)
&nspare);
memcpy(spare, &nspare, sizeof(struct yaffs_spare));
if (data && correct_errors) {
if (nspare.eccres1 > 0) {
yaffs_trace(YAFFS_TRACE_ERROR,
"**>>mtd ecc error fix performed on chunk %d:0",
nand_chunk);
} else if (nspare.eccres1 < 0) {
yaffs_trace(YAFFS_TRACE_ERROR,
"**>>mtd ecc error unfixed on chunk %d:0",
nand_chunk);
}
if (nspare.eccres2 > 0) {
yaffs_trace(YAFFS_TRACE_ERROR,
"**>>mtd ecc error fix performed on chunk %d:1",
nand_chunk);
} else if (nspare.eccres2 < 0) {
yaffs_trace(YAFFS_TRACE_ERROR,
"**>>mtd ecc error unfixed on chunk %d:1",
nand_chunk);
}
if (nspare.eccres1 || nspare.eccres2) {
/* We had a data problem on this page */
yaffs_handle_rd_data_error(dev, nand_chunk);
}
if (nspare.eccres1 < 0 || nspare.eccres2 < 0)
*ecc_result = YAFFS_ECC_RESULT_UNFIXED;
else if (nspare.eccres1 > 0 || nspare.eccres2 > 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
*/
}
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 & 0x3ff;
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_calc_ecc(data, &spare);
yaffs_load_tags_to_spare(&spare, &tags);
}
return yaffs_wr_nand(dev, nand_chunk, data, &spare);
}
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;
if (!init) {
memset(&spare_ff, 0xFF, sizeof(spare_ff));
init = 1;
}
if (yaffs_rd_chunk_nand(dev, nand_chunk, data, &spare, &ecc_result, 1)) {
/* ext_tags may be NULL */
if (ext_tags) {
int 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)) !=
0) ? 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;
} else {
return YAFFS_FAIL;
}
}
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;
}
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;
yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, NULL,
&spare0, &dummy, 1);
yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1,
NULL, &spare1, &dummy, 1);
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_SCANNING;
return YAFFS_OK;
}

View File

@ -0,0 +1,36 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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"
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);
void yaffs_calc_tags_ecc(struct yaffs_tags *tags);
int yaffs_check_tags_ecc(struct yaffs_tags *tags);
int yaffs_count_bits(u8 byte);
#endif

View File

@ -0,0 +1,27 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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_tagsvalidity.h"
void yaffs_init_tags(struct yaffs_ext_tags *tags)
{
memset(tags, 0, sizeof(struct yaffs_ext_tags));
tags->validity0 = 0xAAAAAAAA;
tags->validity1 = 0x55555555;
}
int yaffs_validate_tags(struct yaffs_ext_tags *tags)
{
return (tags->validity0 == 0xAAAAAAAA && tags->validity1 == 0x55555555);
}

View File

@ -0,0 +1,23 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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_TAGS_VALIDITY_H__
#define __YAFFS_TAGS_VALIDITY_H__
#include "yaffs_guts.h"
void yaffs_init_tags(struct yaffs_ext_tags *tags);
int yaffs_validate_tags(struct yaffs_ext_tags *tags);
#endif

57
fs/yaffs2/yaffs_trace.h Normal file
View File

@ -0,0 +1,57 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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

535
fs/yaffs2/yaffs_verify.c Normal file
View File

@ -0,0 +1,535 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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)
{
dev = dev;
return !(yaffs_trace_mask &
(YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL));
}
static int yaffs_skip_full_verification(struct yaffs_dev *dev)
{
dev = dev;
return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL));
}
static int yaffs_skip_nand_verification(struct yaffs_dev *dev)
{
dev = dev;
return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND));
}
static const char *block_state_name[] = {
"Unknown",
"Needs scanning",
"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_SCANNING:
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) /* Trashed name */
yaffs_trace(YAFFS_TRACE_VERIFY,
"Obj %d header name is 0xFF",
obj->obj_id);
}
void yaffs_verify_file(struct yaffs_obj *obj)
{
int required_depth;
int actual_depth;
u32 last_chunk;
u32 x;
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 */
last_chunk =
obj->variant.file_variant.file_size / dev->data_bytes_per_chunk + 1;
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) {
u32 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, __LINE__);
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, __LINE__);
}
/* 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) {
if (lh) {
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");
YBUG();
return;
}
if (yaffs_skip_verification(obj->my_dev))
return;
if (!obj->parent) {
yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent" );
YBUG();
return;
}
if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory");
YBUG();
}
/* Iterate through the objects in each hash entry */
list_for_each(lh, &obj->parent->variant.dir_variant.children) {
if (lh) {
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);
YBUG();
}
}
void yaffs_verify_dir(struct yaffs_obj *directory)
{
struct list_head *lh;
struct yaffs_obj *list_obj;
if (!directory) {
YBUG();
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);
YBUG();
}
/* Iterate through the objects in each hash entry */
list_for_each(lh, &directory->variant.dir_variant.children) {
if (lh) {
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);
YBUG();
}
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)
{
in = in;
return YAFFS_OK;
}

43
fs/yaffs2/yaffs_verify.h Normal file
View File

@ -0,0 +1,43 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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

2792
fs/yaffs2/yaffs_vfs.c Normal file

File diff suppressed because it is too large Load Diff

433
fs/yaffs2/yaffs_yaffs1.c Normal file
View File

@ -0,0 +1,433 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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;
struct yaffs_obj *hard_list = NULL;
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, __LINE__);
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_SCANNING; 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++;
/*T((" %d %d deleted\n",blk,c)); */
} 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 from */
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;
/* Set block finder here to encourage the allocator to go forth from here. */
}
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;
}
}
/* T((" %d %d data %d %d\n",blk,c,tags.obj_id,tags.chunk_id)); */
} else {
/* chunk_id == 0, so it is an ObjectHeader.
* Thus, we read in the object header and 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 = 0;
}
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, another 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);
if (0 && (parent == dev->del_dir ||
parent ==
dev->unlinked_dir)) {
in->deleted = 1; /* If it is unlinked at start up then it wants deleting */
dev->n_deleted_files++;
}
/* Note re hardlinks.
* Since we might scan a hardlink before its equivalent object is scanned
* we put them all in a list.
* After scanning is complete, we should have all the objects, so we run through this
* list and fix up all the chains.
*/
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
= oh->file_size;
break;
case YAFFS_OBJECT_TYPE_HARDLINK:
in->variant.
hardlink_variant.equiv_id =
oh->equiv_id;
in->hard_links.next =
(struct list_head *)
hard_list;
hard_list = in;
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_SCANNING) {
/* 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 */
{
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, __LINE__);
if (alloc_failed)
return YAFFS_FAIL;
yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends");
return YAFFS_OK;
}

22
fs/yaffs2/yaffs_yaffs1.h Normal file
View File

@ -0,0 +1,22 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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

1598
fs/yaffs2/yaffs_yaffs2.c Normal file

File diff suppressed because it is too large Load Diff

39
fs/yaffs2/yaffs_yaffs2.h Normal file
View File

@ -0,0 +1,39 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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

70
fs/yaffs2/yportenv.h Normal file
View File

@ -0,0 +1,70 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2010 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_LINUX_H__
#define __YPORTENV_LINUX_H__
#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>
#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
#define Y_CURRENT_TIME CURRENT_TIME.tv_sec
#define Y_TIME_CONVERT(x) (x).tv_sec
#define compile_time_assertion(assertion) \
({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; })
#ifndef Y_DUMP_STACK
#define Y_DUMP_STACK() dump_stack()
#endif
#define yaffs_trace(msk, fmt, ...) do { \
if(yaffs_trace_mask & ((msk) | YAFFS_TRACE_ALWAYS)) \
printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \
} while(0)
#ifndef YBUG
#define YBUG() do {\
yaffs_trace(YAFFS_TRACE_BUG,\
"bug " __FILE__ " %d",\
__LINE__);\
Y_DUMP_STACK();\
} while (0)
#endif
#endif