Add yaffs2 file system: Fifth patchset
cover Add yaffs2 file system: Fifth patchset
This is the fifth submission of a yaffs2 patchset.
I would like to thank everyone that has given comments so far, with particular mention of Ryan Mallon at Bluewater Systems.
I would like to thank CELF (now part of Linux Foundation) and Google for sponsoring the effort to get yaffs2 mainlined.
This code is patched from 7ad3c1635865d2e214eaef02ec53f0eb3956daa6 at git://www.aleph1.co.uk/yaffs2.
This patch set takes onboard almost all of Ryan's comments to make the code paths cleaner.
This code ran various tests for the whole night with no problems seen.
Comments/feedback welcome, but I'm hopefully getting pretty close to acceptance :-).
-- Charles
PATCH 1/10 Add yaffs2 file system: allocators, bitmap and block handling
--- fs/yaffs2/yaffs_allocator.c | 357 ++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_allocator.h | 30 ++++ fs/yaffs2/yaffs_bitmap.c | 97 +++++++++++ fs/yaffs2/yaffs_bitmap.h | 33 ++++ fs/yaffs2/yaffs_getblockinfo.h | 35 ++++ 5 files changed, 552 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_allocator.c create mode 100644 fs/yaffs2/yaffs_allocator.h create mode 100644 fs/yaffs2/yaffs_bitmap.c create mode 100644 fs/yaffs2/yaffs_bitmap.h create mode 100644 fs/yaffs2/yaffs_getblockinfo.h
fs/yaffs2/yaffs_allocator.c new file +357 -0
@@ -0,0 +1,357 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#include "yaffs_allocator.h" +#include "yaffs_guts.h" +#include "yaffs_trace.h" +#include "yportenv.h" + +/* +* Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks +* of approx 100 objects that are themn allocated singly. +* This is basically a simplified slab allocator. +* +* We don't use the Linux slab allocator because slab does not allow +* us to dump all the objects in one hit when we do a umount and tear +* down all the tnodes and objects. slab requires that we first free +* the individual objects. +* +* Once yaffs has been mainlined I shall try to motivate for a change +* to slab to provide the extra features we need here. +*/ + +struct yaffs_tnode_list { +struct yaffs_tnode_list *next; +struct yaffs_tnode *tnodes; +}; + +struct yaffs_obj_list { +struct yaffs_obj_list *next; +struct yaffs_obj *objects; +}; + +struct yaffs_allocator { +int n_tnodes_created; +struct yaffs_tnode *free_tnodes; +int n_free_tnodes; +struct yaffs_tnode_list *alloc_tnode_list; + +int n_obj_created; +struct list_head free_objs; +int n_free_objects; + +struct yaffs_obj_list *allocated_obj_list; +}; + +static void yaffs_deinit_raw_tnodes (struct yaffs_dev *dev) +{ +struct yaffs_allocator *allocator = + (struct yaffs_allocator *)dev->allocator; +struct yaffs_tnode_list *tmp; + +if (!allocator) { +BUG (); +return ; + } + +while (allocator->alloc_tnode_list) { + tmp = allocator->alloc_tnode_list->next; + +kfree (allocator->alloc_tnode_list->tnodes); +kfree (allocator->alloc_tnode_list); + allocator->alloc_tnode_list = tmp; + } + + allocator->free_tnodes =NULL ; + allocator->n_free_tnodes =0 ; + allocator->n_tnodes_created =0 ; +} + +static void yaffs_init_raw_tnodes (struct yaffs_dev *dev) +{ +struct yaffs_allocator *allocator = dev->allocator; + +if (!allocator) { +BUG (); +return ; + } + + allocator->alloc_tnode_list =NULL ; + allocator->free_tnodes =NULL ; + allocator->n_free_tnodes =0 ; + allocator->n_tnodes_created =0 ; +} + +static int yaffs_create_tnodes (struct yaffs_dev *dev,int n_tnodes) +{ +struct yaffs_allocator *allocator = + (struct yaffs_allocator *)dev->allocator; +int i; +struct yaffs_tnode *new_tnodes; +u8 *mem; +struct yaffs_tnode *curr; +struct yaffs_tnode *next; +struct yaffs_tnode_list *tnl; + +if (!allocator) { +BUG (); +return YAFFS_FAIL ; + } + +if (n_tnodes <1 ) +return YAFFS_OK ; + +/* make these things */ + new_tnodes =kmalloc (n_tnodes * dev->tnode_size,GFP_NOFS ); + mem = (u8 *) new_tnodes; + +if (!new_tnodes) { +yaffs_trace (YAFFS_TRACE_ERROR , +"yaffs: Could not allocate Tnodes" ); +return YAFFS_FAIL ; + } + +/* New hookup for wide tnodes */ +for (i =0 ; i < n_tnodes -1 ; i++) { + curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size]; + next = (struct yaffs_tnode *)&mem[(i +1 ) * dev->tnode_size]; + curr->internal[0 ] = next; + } + + curr = (struct yaffs_tnode *)&mem[(n_tnodes -1 ) * dev->tnode_size]; + curr->internal[0 ] = allocator->free_tnodes; + allocator->free_tnodes = (struct yaffs_tnode *)mem; + + allocator->n_free_tnodes += n_tnodes; + allocator->n_tnodes_created += n_tnodes; + +/* Now add this bunch of tnodes to a list for freeing up. +* NB If we can't add this to the management list it isn't fatal +* but it just means we can't free this bunch of tnodes later. +*/ + tnl =kmalloc (sizeof (struct yaffs_tnode_list ),GFP_NOFS ); +if (!tnl) { +yaffs_trace (YAFFS_TRACE_ERROR , +"Could not add tnodes to management list" ); +return YAFFS_FAIL ; + }else { + tnl->tnodes = new_tnodes; + tnl->next = allocator->alloc_tnode_list; + allocator->alloc_tnode_list = tnl; + } + +yaffs_trace (YAFFS_TRACE_ALLOCATE ,"Tnodes added" ); + +return YAFFS_OK ; +} + +struct yaffs_tnode *yaffs_alloc_raw_tnode (struct yaffs_dev *dev) +{ +struct yaffs_allocator *allocator = + (struct yaffs_allocator *)dev->allocator; +struct yaffs_tnode *tn =NULL ; + +if (!allocator) { +BUG (); +return NULL ; + } + +/* If there are none left make more */ +if (!allocator->free_tnodes) +yaffs_create_tnodes (dev,YAFFS_ALLOCATION_NTNODES ); + +if (allocator->free_tnodes) { + tn = allocator->free_tnodes; + allocator->free_tnodes = allocator->free_tnodes->internal[0 ]; + allocator->n_free_tnodes--; + } + +return tn; +} + +/* FreeTnode frees up a tnode and puts it back on the free list */ +void yaffs_free_raw_tnode (struct yaffs_dev *dev,struct yaffs_tnode *tn) +{ +struct yaffs_allocator *allocator = dev->allocator; + +if (!allocator) { +BUG (); +return ; + } + +if (tn) { + tn->internal[0 ] = allocator->free_tnodes; + allocator->free_tnodes = tn; + allocator->n_free_tnodes++; + } + dev->checkpoint_blocks_required =0 ;/* force recalculation */ +} + +/*--------------- yaffs_obj alloaction ------------------------ +* +* Free yaffs_objs are stored in a list using obj->siblings. +* The blocks of allocated objects are stored in a linked list. +*/ + +static void yaffs_init_raw_objs (struct yaffs_dev *dev) +{ +struct yaffs_allocator *allocator = dev->allocator; + +if (!allocator) { +BUG (); +return ; + } + + allocator->allocated_obj_list =NULL ; +INIT_LIST_HEAD (&allocator->free_objs); + allocator->n_free_objects =0 ; +} + +static void yaffs_deinit_raw_objs (struct yaffs_dev *dev) +{ +struct yaffs_allocator *allocator = dev->allocator; +struct yaffs_obj_list *tmp; + +if (!allocator) { +BUG (); +return ; + } + +while (allocator->allocated_obj_list) { + tmp = allocator->allocated_obj_list->next; +kfree (allocator->allocated_obj_list->objects); +kfree (allocator->allocated_obj_list); + allocator->allocated_obj_list = tmp; + } + +INIT_LIST_HEAD (&allocator->free_objs); + allocator->n_free_objects =0 ; + allocator->n_obj_created =0 ; +} + +static int yaffs_create_free_objs (struct yaffs_dev *dev,int n_obj) +{ +struct yaffs_allocator *allocator = dev->allocator; +int i; +struct yaffs_obj *new_objs; +struct yaffs_obj_list *list; + +if (!allocator) { +BUG (); +return YAFFS_FAIL ; + } + +if (n_obj <1 ) +return YAFFS_OK ; + +/* make these things */ + new_objs =kmalloc (n_obj *sizeof (struct yaffs_obj ),GFP_NOFS ); + list =kmalloc (sizeof (struct yaffs_obj_list ),GFP_NOFS ); + +if (!new_objs || !list) { +kfree (new_objs); + new_objs =NULL ; +kfree (list); + list =NULL ; +yaffs_trace (YAFFS_TRACE_ALLOCATE , +"Could not allocate more objects" ); +return YAFFS_FAIL ; + } + +/* Hook them into the free list */ +for (i =0 ; i < n_obj; i++) +list_add (&new_objs[i].siblings, &allocator->free_objs); + + allocator->n_free_objects += n_obj; + allocator->n_obj_created += n_obj; + +/* Now add this bunch of Objects to a list for freeing up. */ + + list->objects = new_objs; + list->next = allocator->allocated_obj_list; + allocator->allocated_obj_list = list; + +return YAFFS_OK ; +} + +struct yaffs_obj *yaffs_alloc_raw_obj (struct yaffs_dev *dev) +{ +struct yaffs_obj *obj =NULL ; +struct list_head *lh; +struct yaffs_allocator *allocator = dev->allocator; + +if (!allocator) { +BUG (); +return obj; + } + +/* If there are none left make more */ +if (list_empty (&allocator->free_objs)) +yaffs_create_free_objs (dev,YAFFS_ALLOCATION_NOBJECTS ); + +if (!list_empty (&allocator->free_objs)) { + lh = allocator->free_objs.next; + obj = list_entry(lh, struct yaffs_obj, siblings); +list_del_init (lh); + allocator->n_free_objects--; + } + +return obj; +} + +void yaffs_free_raw_obj (struct yaffs_dev *dev,struct yaffs_obj *obj) +{ + +struct yaffs_allocator *allocator = dev->allocator; + +if (!allocator) { +BUG (); +return ; + } + +/* Link into the free list. */ +list_add (&obj->siblings, &allocator->free_objs); + allocator->n_free_objects++; +} + +void yaffs_deinit_raw_tnodes_and_objs (struct yaffs_dev *dev) +{ + +if (!dev->allocator) { +BUG (); +return ; + } + +yaffs_deinit_raw_tnodes (dev); +yaffs_deinit_raw_objs (dev); +kfree (dev->allocator); + dev->allocator =NULL ; +} + +void yaffs_init_raw_tnodes_and_objs (struct yaffs_dev *dev) +{ +struct yaffs_allocator *allocator; + +if (dev->allocator) { +BUG (); +return ; + } + + allocator =kmalloc (sizeof (struct yaffs_allocator ),GFP_NOFS ); +if (allocator) { + dev->allocator = allocator; +yaffs_init_raw_tnodes (dev); +yaffs_init_raw_objs (dev); + } +} +
fs/yaffs2/yaffs_allocator.h new file +30 -0
@@ -0,0 +1,30 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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
fs/yaffs2/yaffs_bitmap.c new file +97 -0
97 lines not on this page · raw message
fs/yaffs2/yaffs_bitmap.h new file +33 -0
@@ -0,0 +1,33 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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
fs/yaffs2/yaffs_getblockinfo.h new file +35 -0
@@ -0,0 +1,35 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License version 2.1 as +* published by the Free Software Foundation. +* +* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. +*/ + +#ifndef __YAFFS_GETBLOCKINFO_H__ +#define __YAFFS_GETBLOCKINFO_H__ + +#include "yaffs_guts.h" +#include "yaffs_trace.h" + +/* Function to manipulate block info */ +static inline struct yaffs_block_info *yaffs_get_block_info (struct yaffs_dev + *dev,int blk) +{ +if (blk < dev->internal_start_block || blk > dev->internal_end_block) { +yaffs_trace (YAFFS_TRACE_ERROR , +"**>> yaffs: get_block_info block %d is not valid" , + blk); +BUG (); + } +return &dev->block_info[blk - dev->internal_start_block]; +} + +#endif
Signature · 1 line
1.7.1
PATCH 2/10 Add yaffs2 file system: attrib and xattrib handling
In reply to Charles Manning--- fs/yaffs2/yaffs_attribs.c | 124 ++++++++++++++++++++++++++++ fs/yaffs2/yaffs_attribs.h | 28 ++++++ fs/yaffs2/yaffs_nameval.c | 201 +++++++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_nameval.h | 28 ++++++ 4 files changed, 381 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_attribs.c create mode 100644 fs/yaffs2/yaffs_attribs.h create mode 100644 fs/yaffs2/yaffs_nameval.c create mode 100644 fs/yaffs2/yaffs_nameval.h
fs/yaffs2/yaffs_attribs.c new file +124 -0
@@ -0,0 +1,124 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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 ; +}
fs/yaffs2/yaffs_attribs.h new file +28 -0
@@ -0,0 +1,28 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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
fs/yaffs2/yaffs_nameval.c new file +201 -0
201 lines not on this page · raw message
fs/yaffs2/yaffs_nameval.h new file +28 -0
@@ -0,0 +1,28 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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
Signature · 1 line
1.7.1
Re: [PATCH 02/10] Add yaffs2 file system: attrib and xattrib handling
Quoted 1 line · Charles Manning
Signed-off-by Charles Manning <[email protected]>
Hi Charles,
Some comments below,
~Ryan
Quoted 131 lines
--- fs/yaffs2/yaffs_attribs.c | 124 ++++++++++++++++++++++++++++ fs/yaffs2/yaffs_attribs.h | 28 ++++++ fs/yaffs2/yaffs_nameval.c | 201 +++++++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_nameval.h | 28 ++++++ 4 files changed, 381 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_attribs.c create mode 100644 fs/yaffs2/yaffs_attribs.h create mode 100644 fs/yaffs2/yaffs_nameval.c create mode 100644 fs/yaffs2/yaffs_nameval.hfs/yaffs2/yaffs_attribs.c new file +114
-0@@ -0,0 +1,124 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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 ;
fs/yaffs2/yaffs_attribs.c line 124
Quoted 10 lines
fs/yaffs2/yaffs_attribs.c new file +10
-0+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 ; +}
Is there are reason this cannot be written as:
int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr)
{
attr->ia_mode = obj->yst_mode;
attr->ia_uid = obj->yst_uid;
attr->ia_gid = obj->yst_gid;
Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime;
Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime;
Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime;
attr->ia_size = yaffs_get_file_size(obj);
attr->ia_valid = (ATTR_MODE | ATTR_UID | ATTR_GID |
ATTR_ATIME | ATTR_CTIME | ATTR_MTIME |
ATTR_SIZE);
return YAFFS_OK;
}Which is a bit more readable?
Quoted 53 lines
fs/yaffs2/yaffs_attribs.h new file +28
-0@@ -0,0 +1,28 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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 fs/yaffs2/yaffs_nameval.c new file +13
-0@@ -0,0 +1,201 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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. +*/ +
fs/yaffs2/yaffs_nameval.c line 23
Quoted 10 lines
fs/yaffs2/yaffs_nameval.c new file +10
-0+/* +* 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 feels like it should have helper functions to minimise the amount of pointer arithmetic and memcpy magic.
Quoted 6 lines
fs/yaffs2/yaffs_nameval.c new file +6
-0+* +* This code has not been tested with unicode yet. +*/ + +#include "yaffs_nameval.h" +
fs/yaffs2/yaffs_nameval.c line 39
Quoted 10 lines
fs/yaffs2/yaffs_nameval.c new file +10
-0+#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)) {
You don't need thr parens around the expressions here.
fs/yaffs2/yaffs_nameval.c line 40
Quoted 1 line
fs/yaffs2/yaffs_nameval.c new file +1
-0+if (!strncmp ((YCHAR *) (xb + pos +sizeof (int )), name, size)) {
Make a helper function:
static YCHAR *nval_name(const char *xb, int pos)
{
return (YCHAR *)(xb + pos + sizeof(int));
}Then:
if (!strncmp(nval_name(xb, pos), name, size)) {Much easier to read.
fs/yaffs2/yaffs_nameval.c line 49
Quoted 9 lines
fs/yaffs2/yaffs_nameval.c new file +9
-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 ;
The else here is effectively an error break with the test for size > 0 in the while loop? Why not remove the size > 0 test and just do a break here? Probably reverse the test too:
if (pos >= xb_size - sizeof(int)) break; memcpy(&size, xb + pos, sizeof(int));
Quoted 8 lines
fs/yaffs2/yaffs_nameval.c new file +8
-0+ } +if (exist_size) + *exist_size =0 ; +return -ENODATA ; +} + +static int nval_used (const char *xb,int xb_size) +{
fs/yaffs2/yaffs_nameval.c line 67
Quoted 10 lines
fs/yaffs2/yaffs_nameval.c new file +10
-0+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 ;
Same here, remove the size > 0 test and just break from the loop.
Quoted 19 lines
fs/yaffs2/yaffs_nameval.c new file +19
-0+ } +return pos; +} + +int nval_del (char *xb,int xb_size,const YCHAR *name) +{ +int pos =nval_find (xb, xb_size, name,NULL ); +int size; + +if (pos <0 || pos >= xb_size) +return -ENODATA ; + +/* Find size, shift rest over this record, +* then zero out the rest of buffer */ +memcpy (&size, xb + pos,sizeof (int )); +memcpy (xb + pos, xb + pos + size, xb_size - (pos + size)); +memset (xb + (xb_size - size),0 , size); +return 0 ; +}
fs/yaffs2/yaffs_nameval.c line 96
Quoted 10 lines
fs/yaffs2/yaffs_nameval.c new file +10
-0+ +int nval_set (char *xb,int xb_size,const YCHAR *name,const char *buf, +int bsize,int flags) +{ +int pos; +int namelen =strnlen (name, xb_size); +int reclen; +int size_exist =0 ; +int space; +int start;
These int definitions could all go on one line.
Quoted 18 lines
fs/yaffs2/yaffs_nameval.c new file +18
-0+ + 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);
fs/yaffs2/yaffs_nameval.c line 124
Quoted 10 lines
fs/yaffs2/yaffs_nameval.c new file +10
-0+ 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);
static const char *nval_value(const char *xb, int pos, int namelen)
{
return xb + sizeof(int) + namelen + 1;
}With the above helper replace the above with:
memcpy(xb + pos, &reclen, sizeof(int)); strncpy(nval_name(xb, pos), name, namelen); strncpy(nval_value(xb, pos, namelen), buf, bsize); pos += reclen;
Note the change from reclen to namelen in the first strncpy. I think reclen is incorrect here?
fs/yaffs2/yaffs_nameval.c line 134
Quoted 10 lines
fs/yaffs2/yaffs_nameval.c new file +10
-0+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) {
if (pos >= xb_size)
return -ERANGE; if (pos < 0) return -ENODATA;
Then drop the indentation for the code below.
Quoted 22 lines
fs/yaffs2/yaffs_nameval.c new file +22
-0+ +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 ; +
fs/yaffs2/yaffs_nameval.c line 166
Quoted 10 lines
fs/yaffs2/yaffs_nameval.c new file +10
-0+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 ;
These can all go on one line.
fs/yaffs2/yaffs_nameval.c line 175
Quoted 9 lines
fs/yaffs2/yaffs_nameval.c new file +9
-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);
name_len = strnlen(nval_name(xb, pos), size);
fs/yaffs2/yaffs_nameval.c line 184
Quoted 9 lines
fs/yaffs2/yaffs_nameval.c new file +9
-0+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++; + }
This could be simplified as:
memset(buf, 0, sizeof(YCHAR)); buf += sizeof(YCHAR);
Quoted 51 lines
fs/yaffs2/yaffs_nameval.c new file +17
-0+ 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 ; +}fs/yaffs2/yaffs_nameval.h new file +28
-0@@ -0,0 +1,28 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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
Signature · 7 lines
Bluewater Systems Ltd - ARM Technology Solution Centre Ryan Mallon 5 Amuri Park, 404 Barbadoes St [email protected] PO Box 13 889, Christchurch 8013 http://www.bluewatersys.com New Zealand Phone: +64 3 3779127 Freecall: Australia 1800 148 751 Fax: +64 3 3779135 USA 1800 261 2934
PATCH 3/10 Add yaffs2 file system: checkpoint streaming
In reply to Charles Manning--- fs/yaffs2/yaffs_checkptrw.c | 408 +++++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_checkptrw.h | 33 ++++ 2 files changed, 441 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_checkptrw.c create mode 100644 fs/yaffs2/yaffs_checkptrw.h
fs/yaffs2/yaffs_checkptrw.c new file +408 -0
@@ -0,0 +1,408 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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) +{ +int i; + + 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); + } + +/* 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 ; +break ; + } + + 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 ; +break ; + } + + 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 ; + } + + *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) +{ +int i; + +if (dev->checkpt_open_write) { +if (dev->checkpt_byte_offs !=0 ) +yaffs2_checkpt_flush_buffer (dev); + }else if (dev->checkpt_block_list) { +for (i =0 ; + i < dev->blocks_in_checkpt && + dev->checkpt_block_list[i] >=0 ; i++) { +int blk = dev->checkpt_block_list[i]; +struct yaffs_block_info *bi =NULL ; + +if (dev->internal_start_block <= blk && + blk <= dev->internal_end_block) + bi =yaffs_get_block_info (dev, blk); +if (bi && bi->block_state ==YAFFS_BLOCK_STATE_EMPTY ) + bi->block_state =YAFFS_BLOCK_STATE_CHECKPOINT ; + } +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); +}
fs/yaffs2/yaffs_checkptrw.h new file +33 -0
@@ -0,0 +1,33 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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
Signature · 1 line
1.7.1
Re: [PATCH 03/10] Add yaffs2 file system: checkpoint streaming
Quoted 41 lines · Charles Manning
Signed-off-by Charles Manning <[email protected]>--- fs/yaffs2/yaffs_checkptrw.c | 408 +++++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_checkptrw.h | 33 ++++ 2 files changed, 441 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_checkptrw.c create mode 100644 fs/yaffs2/yaffs_checkptrw.hfs/yaffs2/yaffs_checkptrw.c new file +27
-0@@ -0,0 +1,408 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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)
fs/yaffs2/yaffs_checkptrw.c line 37
Quoted 10 lines
fs/yaffs2/yaffs_checkptrw.c new file +10
-0+{ +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++) {
Why does 'i' need 'function scope'? can't it just have 'for scope'?
for (int i = dev->internal_start_block; i <= dev->internal_end_block; i++) {Quoted 5 lines
fs/yaffs2/yaffs_checkptrw.c new file +5
-0+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; +
fs/yaffs2/yaffs_checkptrw.c line 78
Quoted 10 lines
fs/yaffs2/yaffs_checkptrw.c new file +10
-0+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;
'for scope' ?
fs/yaffs2/yaffs_checkptrw.c line 107
Quoted 11 lines
fs/yaffs2/yaffs_checkptrw.c new file +11
-0+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;
and here and elsewhere (can't be bothered to point out every one)...
Signature · 3 lines
Jesper Juhl <[email protected]> http://www.chaosbits.net/ Plain text mails only, please. Don't top-post http://www.catb.org/~esr/jargon/html/T/top-post.html
Re: [PATCH 03/10] Add yaffs2 file system: checkpoint streaming
Quoted 56 lines · Jesper Juhl
Quoted 41 lines · Charles Manning
Signed-off-by Charles Manning <[email protected]>--- fs/yaffs2/yaffs_checkptrw.c | 408 +++++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_checkptrw.h | 33 ++++ 2 files changed, 441 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_checkptrw.c create mode 100644 fs/yaffs2/yaffs_checkptrw.hfs/yaffs2/yaffs_checkptrw.c new file +27
-0@@ -0,0 +1,408 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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)fs/yaffs2/yaffs_checkptrw.c line 37
Quoted 10 lines
fs/yaffs2/yaffs_checkptrw.c new file +10
-0+{ +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++) {Why does 'i' need 'function scope'? can't it just have 'for scope'?
Quoted 1 line
for (int i = dev->internal_start_block; i <= dev->internal_end_block; i++) {Because we don't do that in kernel code.
ryan@okiwi:linux-2.6$ grep "for (int" * -R | wc -l 16
Those sixteen should be fixed :-). Half of them are in drivers/staging.
~Ryan
Signature · 7 lines
Bluewater Systems Ltd - ARM Technology Solution Centre Ryan Mallon 5 Amuri Park, 404 Barbadoes St [email protected] PO Box 13 889, Christchurch 8013 http://www.bluewatersys.com New Zealand Phone: +64 3 3779127 Freecall: Australia 1800 148 751 Fax: +64 3 3779135 USA 1800 261 2934
Re: [PATCH 03/10] Add yaffs2 file system: checkpoint streaming
Quoted 61 lines · On Friday 11 February 2011 11:44:43 Ryan Mallon wrote:
Quoted 57 lines · Jesper Juhl
Quoted 52 lines · Charles Manning
Signed-off-by Charles Manning <[email protected]>--- fs/yaffs2/yaffs_checkptrw.c | 408 +++++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_checkptrw.h | 33 ++++ 2 files changed, 441 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_checkptrw.c create mode 100644 fs/yaffs2/yaffs_checkptrw.hfs/yaffs2/yaffs_checkptrw.c new file +2
-0@@ -0,0 +1,408 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <[email protected]>
+ *
+ * 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.
+ */fs/yaffs2/yaffs_checkptrw.c new file +7
-0+ +#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; +fs/yaffs2/yaffs_checkptrw.c new file +17
-0+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++)Quoted 1 line
{
Why does 'i' need 'function scope'? can't it just have 'for scope'?
Quoted 2 lines
for (int i = dev->internal_start_block; i <=
dev->internal_end_block; i++) {
Quoted 6 lines
Because we don't do that in kernel code.
ryan@okiwi:linux-2.6$ grep "for (int" * -R | wc -l 16Those sixteen should be fixed :-). Half of them are in
drivers/staging.
It is also not very portable. For us older codgers it is also very ugly.
-- Charles
PATCH 4/10 Add yaffs2 file system: flash interface and ecc handling
In reply to Charles Manning--- fs/yaffs2/yaffs_ecc.c | 296 +++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_ecc.h | 44 ++++++ fs/yaffs2/yaffs_mtdif.c | 53 ++++++++ fs/yaffs2/yaffs_mtdif.h | 23 ++++ fs/yaffs2/yaffs_mtdif1.c | 329 ++++++++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_mtdif1.h | 29 ++++ fs/yaffs2/yaffs_mtdif2.c | 209 +++++++++++++++++++++++++++++ fs/yaffs2/yaffs_mtdif2.h | 29 ++++ fs/yaffs2/yaffs_nand.c | 119 +++++++++++++++++ fs/yaffs2/yaffs_nand.h | 38 ++++++ 10 files changed, 1169 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_ecc.c create mode 100644 fs/yaffs2/yaffs_ecc.h create mode 100644 fs/yaffs2/yaffs_mtdif.c create mode 100644 fs/yaffs2/yaffs_mtdif.h create mode 100644 fs/yaffs2/yaffs_mtdif1.c create mode 100644 fs/yaffs2/yaffs_mtdif1.h create mode 100644 fs/yaffs2/yaffs_mtdif2.c create mode 100644 fs/yaffs2/yaffs_mtdif2.h create mode 100644 fs/yaffs2/yaffs_nand.c create mode 100644 fs/yaffs2/yaffs_nand.h
fs/yaffs2/yaffs_ecc.c new file +296 -0
@@ -0,0 +1,296 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +/* +* This code implements the ECC algorithm used in SmartMedia. +* +* The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. +* The two unused bit are set to 1. +* The ECC can correct single bit errors in a 256-byte page of data. Thus, two +* such ECC blocks are used on a 512-byte NAND page. +* +*/ + +#include "yportenv.h" + +#include "yaffs_ecc.h" + +/* Table generated by gen-ecc.c +* Using a table means we do not have to calculate p1..p4 and p1'..p4' +* for each byte of data. These are instead provided in a table in bits7..2. +* Bit 0 of each entry indicates whether the entry has an odd or even parity, +* and therefore this bytes influence on the line parity. +*/ + +static const unsigned char column_parity_table[] = { +0x00 ,0x55 ,0x59 ,0x0c ,0x65 ,0x30 ,0x3c ,0x69 , +0x69 ,0x3c ,0x30 ,0x65 ,0x0c ,0x59 ,0x55 ,0x00 , +0x95 ,0xc0 ,0xcc ,0x99 ,0xf0 ,0xa5 ,0xa9 ,0xfc , +0xfc ,0xa9 ,0xa5 ,0xf0 ,0x99 ,0xcc ,0xc0 ,0x95 , +0x99 ,0xcc ,0xc0 ,0x95 ,0xfc ,0xa9 ,0xa5 ,0xf0 , +0xf0 ,0xa5 ,0xa9 ,0xfc ,0x95 ,0xc0 ,0xcc ,0x99 , +0x0c ,0x59 ,0x55 ,0x00 ,0x69 ,0x3c ,0x30 ,0x65 , +0x65 ,0x30 ,0x3c ,0x69 ,0x00 ,0x55 ,0x59 ,0x0c , +0xa5 ,0xf0 ,0xfc ,0xa9 ,0xc0 ,0x95 ,0x99 ,0xcc , +0xcc ,0x99 ,0x95 ,0xc0 ,0xa9 ,0xfc ,0xf0 ,0xa5 , +0x30 ,0x65 ,0x69 ,0x3c ,0x55 ,0x00 ,0x0c ,0x59 , +0x59 ,0x0c ,0x00 ,0x55 ,0x3c ,0x69 ,0x65 ,0x30 , +0x3c ,0x69 ,0x65 ,0x30 ,0x59 ,0x0c ,0x00 ,0x55 , +0x55 ,0x00 ,0x0c ,0x59 ,0x30 ,0x65 ,0x69 ,0x3c , +0xa9 ,0xfc ,0xf0 ,0xa5 ,0xcc ,0x99 ,0x95 ,0xc0 , +0xc0 ,0x95 ,0x99 ,0xcc ,0xa5 ,0xf0 ,0xfc ,0xa9 , +0xa9 ,0xfc ,0xf0 ,0xa5 ,0xcc ,0x99 ,0x95 ,0xc0 , +0xc0 ,0x95 ,0x99 ,0xcc ,0xa5 ,0xf0 ,0xfc ,0xa9 , +0x3c ,0x69 ,0x65 ,0x30 ,0x59 ,0x0c ,0x00 ,0x55 , +0x55 ,0x00 ,0x0c ,0x59 ,0x30 ,0x65 ,0x69 ,0x3c , +0x30 ,0x65 ,0x69 ,0x3c ,0x55 ,0x00 ,0x0c ,0x59 , +0x59 ,0x0c ,0x00 ,0x55 ,0x3c ,0x69 ,0x65 ,0x30 , +0xa5 ,0xf0 ,0xfc ,0xa9 ,0xc0 ,0x95 ,0x99 ,0xcc , +0xcc ,0x99 ,0x95 ,0xc0 ,0xa9 ,0xfc ,0xf0 ,0xa5 , +0x0c ,0x59 ,0x55 ,0x00 ,0x69 ,0x3c ,0x30 ,0x65 , +0x65 ,0x30 ,0x3c ,0x69 ,0x00 ,0x55 ,0x59 ,0x0c , +0x99 ,0xcc ,0xc0 ,0x95 ,0xfc ,0xa9 ,0xa5 ,0xf0 , +0xf0 ,0xa5 ,0xa9 ,0xfc ,0x95 ,0xc0 ,0xcc ,0x99 , +0x95 ,0xc0 ,0xcc ,0x99 ,0xf0 ,0xa5 ,0xa9 ,0xfc , +0xfc ,0xa9 ,0xa5 ,0xf0 ,0x99 ,0xcc ,0xc0 ,0x95 , +0x00 ,0x55 ,0x59 ,0x0c ,0x65 ,0x30 ,0x3c ,0x69 , +0x69 ,0x3c ,0x30 ,0x65 ,0x0c ,0x59 ,0x55 ,0x00 , +}; + + +/* Calculate the ECC for a 256-byte block of data */ +void yaffs_ecc_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 ; +}
fs/yaffs2/yaffs_ecc.h new file +44 -0
44 lines not on this page · raw message
fs/yaffs2/yaffs_mtdif.c new file +53 -0
53 lines not on this page · raw message
fs/yaffs2/yaffs_mtdif.h new file +23 -0
@@ -0,0 +1,23 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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
fs/yaffs2/yaffs_mtdif1.c new file +329 -0
329 lines not on this page · raw message
fs/yaffs2/yaffs_mtdif1.h new file +29 -0
@@ -0,0 +1,29 @@ +/* +* YAFFS: Yet another Flash File System. A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* 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
fs/yaffs2/yaffs_mtdif2.c new file +209 -0
209 lines not on this page · raw message
fs/yaffs2/yaffs_mtdif2.h new file +29 -0
@@ -0,0 +1,29 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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
fs/yaffs2/yaffs_nand.c new file +119 -0
119 lines not on this page · raw message
fs/yaffs2/yaffs_nand.h new file +38 -0
@@ -0,0 +1,38 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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
Signature · 1 line
1.7.1
PATCH 5/10 Add yaffs2 file system: tags handling
In reply to Charles Manning--- fs/yaffs2/yaffs_packedtags1.c | 56 ++++++ fs/yaffs2/yaffs_packedtags1.h | 39 ++++ fs/yaffs2/yaffs_packedtags2.c | 183 ++++++++++++++++++ fs/yaffs2/yaffs_packedtags2.h | 47 +++++ fs/yaffs2/yaffs_tagscompat.c | 407 ++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_tagscompat.h | 36 ++++ fs/yaffs2/yaffs_tagsvalidity.c | 29 +++ fs/yaffs2/yaffs_tagsvalidity.h | 23 +++ 8 files changed, 820 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_packedtags1.c create mode 100644 fs/yaffs2/yaffs_packedtags1.h create mode 100644 fs/yaffs2/yaffs_packedtags2.c create mode 100644 fs/yaffs2/yaffs_packedtags2.h create mode 100644 fs/yaffs2/yaffs_tagscompat.c create mode 100644 fs/yaffs2/yaffs_tagscompat.h create mode 100644 fs/yaffs2/yaffs_tagsvalidity.c create mode 100644 fs/yaffs2/yaffs_tagsvalidity.h
fs/yaffs2/yaffs_packedtags1.c new file +56 -0
@@ -0,0 +1,56 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#include "yaffs_packedtags1.h" +#include "yportenv.h" + +static const u8 all_ff[20 ] = { +0xff ,0xff ,0xff ,0xff , +0xff ,0xff ,0xff ,0xff , +0xff ,0xff ,0xff ,0xff , +0xff ,0xff ,0xff ,0xff , +0xff ,0xff ,0xff ,0xff +}; + +void yaffs_pack_tags1 (struct yaffs_packed_tags1 *pt, +const struct yaffs_ext_tags *t) +{ + pt->chunk_id = t->chunk_id; + pt->serial_number = t->serial_number; + pt->n_bytes = t->n_bytes; + pt->obj_id = t->obj_id; + pt->ecc =0 ; + pt->deleted = (t->is_deleted) ?0 :1 ; + pt->unused_stuff =0 ; + pt->should_be_ff =0xffffffff ; +} + +void yaffs_unpack_tags1 (struct yaffs_ext_tags *t, +const struct yaffs_packed_tags1 *pt) +{ + +if (memcmp (all_ff, pt,sizeof (struct yaffs_packed_tags1 ))) { + t->block_bad =0 ; +if (pt->should_be_ff !=0xffffffff ) + t->block_bad =1 ; + t->chunk_used =1 ; + t->obj_id = pt->obj_id; + t->chunk_id = pt->chunk_id; + t->n_bytes = pt->n_bytes; + t->ecc_result =YAFFS_ECC_RESULT_NO_ERROR ; + t->is_deleted = (pt->deleted) ?0 :1 ; + t->serial_number = pt->serial_number; + }else { +memset (t,0 ,sizeof (struct yaffs_ext_tags )); + } +}
fs/yaffs2/yaffs_packedtags1.h new file +39 -0
@@ -0,0 +1,39 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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
fs/yaffs2/yaffs_packedtags2.c new file +183 -0
183 lines not on this page · raw message
fs/yaffs2/yaffs_packedtags2.h new file +47 -0
47 lines not on this page · raw message
fs/yaffs2/yaffs_tagscompat.c new file +407 -0
407 lines not on this page · raw message
fs/yaffs2/yaffs_tagscompat.h new file +36 -0
@@ -0,0 +1,36 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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
fs/yaffs2/yaffs_tagsvalidity.c new file +29 -0
@@ -0,0 +1,29 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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" + +#define VALID0 0xaaaaaaaa +#define VALID1 0x55555555 + +void yaffs_init_tags (struct yaffs_ext_tags *tags) +{ +memset (tags,0 ,sizeof (struct yaffs_ext_tags )); + tags->validity0 =VALID0 ; + tags->validity1 =VALID1 ; +} + +int yaffs_validate_tags (struct yaffs_ext_tags *tags) +{ +return (tags->validity0 ==VALID0 && tags->validity1 ==VALID1 ); +}
fs/yaffs2/yaffs_tagsvalidity.h new file +23 -0
@@ -0,0 +1,23 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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
Signature · 1 line
1.7.1
PATCH 6/10 Add yaffs2 file system: tracing and verification handling
In reply to Charles Manning--- fs/yaffs2/yaffs_trace.h | 57 +++++ fs/yaffs2/yaffs_verify.c | 525 ++++++++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_verify.h | 43 ++++ 3 files changed, 625 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_trace.h create mode 100644 fs/yaffs2/yaffs_verify.c create mode 100644 fs/yaffs2/yaffs_verify.h
fs/yaffs2/yaffs_trace.h new file +57 -0
@@ -0,0 +1,57 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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
fs/yaffs2/yaffs_verify.c new file +525 -0
525 lines not on this page · raw message
fs/yaffs2/yaffs_verify.h new file +43 -0
43 lines not on this page · raw message
Signature · 1 line
1.7.1
Re: [PATCH 06/10] Add yaffs2 file system: tracing and verification handling
Quoted 1 line · Charles Manning
Signed-off-by Charles Manning <[email protected]>
Hi Charles,
A few comments below.
~Ryan
Quoted 80 lines
--- fs/yaffs2/yaffs_trace.h | 57 +++++ fs/yaffs2/yaffs_verify.c | 525 ++++++++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_verify.h | 43 ++++ 3 files changed, 625 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_trace.h create mode 100644 fs/yaffs2/yaffs_verify.c create mode 100644 fs/yaffs2/yaffs_verify.hfs/yaffs2/yaffs_trace.h new file +57
-0@@ -0,0 +1,57 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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 fs/yaffs2/yaffs_verify.c new file +2
-0@@ -0,0 +1,525 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
fs/yaffs2/yaffs_verify.c line 12
Quoted 10 lines
fs/yaffs2/yaffs_verify.c new file +10
-0+* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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. +*/
I think it would be useful to have a Kconfig option called YAFFS_DEBUG which enables debugging (use pr_debug) and verification code (i.e. includes this file). This would allow smaller kernel builds for people who do not need the trace/verification functionality.
fs/yaffs2/yaffs_verify.c line 22
Quoted 9 lines
fs/yaffs2/yaffs_verify.c new file +9
-0+#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;
Delete this line, or even better, remove the unused function argument completely. Same goes for other functions.
Quoted 88 lines
fs/yaffs2/yaffs_verify.c new file +88
-0+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 *const block_state_name[] = { +"Unknown" , +"Needs scan" , +"Scanning" , +"Empty" , +"Allocating" , +"Full" , +"Dirty" , +"Checkpoint" , +"Collecting" , +"Dead" +}; + +void yaffs_verify_blk (struct yaffs_dev *dev,struct yaffs_block_info *bi,int n) +{ +int actually_used; +int in_use; + +if (yaffs_skip_verification (dev)) +return ; + +/* Report illegal runtime states */ +if (bi->block_state >=YAFFS_NUMBER_OF_BLOCK_STATES ) +yaffs_trace (YAFFS_TRACE_VERIFY , +"Block %d has undefined state %d" , + n, bi->block_state); + +switch (bi->block_state) { +case YAFFS_BLOCK_STATE_UNKNOWN : +case YAFFS_BLOCK_STATE_SCANNING : +case YAFFS_BLOCK_STATE_NEEDS_SCAN : +yaffs_trace (YAFFS_TRACE_VERIFY , +"Block %d has bad run-state %s" , + n, block_state_name[bi->block_state]); + } + +/* Check pages in use and soft deletions are legal */ + + actually_used = bi->pages_in_use - bi->soft_del_pages; + +if (bi->pages_in_use <0 || + bi->pages_in_use > dev->param.chunks_per_block || + bi->soft_del_pages <0 || + bi->soft_del_pages > dev->param.chunks_per_block || + actually_used <0 || actually_used > dev->param.chunks_per_block) +yaffs_trace (YAFFS_TRACE_VERIFY , +"Block %d has illegal values pages_in_used %d soft_del_pages %d" , + n, bi->pages_in_use, bi->soft_del_pages); + +/* Check chunk bitmap legal */ + in_use =yaffs_count_chunk_bits (dev, n); +if (in_use != bi->pages_in_use) +yaffs_trace (YAFFS_TRACE_VERIFY , +"Block %d has inconsistent values pages_in_use %d counted chunk bits %d" , + n, bi->pages_in_use, in_use); +} + +void yaffs_verify_collected_blk (struct yaffs_dev *dev, +struct yaffs_block_info *bi,int n) +{ +yaffs_verify_blk (dev, bi, n); + +/* After collection the block should be in the erased state */ + +if (bi->block_state !=YAFFS_BLOCK_STATE_COLLECTING && + bi->block_state !=YAFFS_BLOCK_STATE_EMPTY ) { +yaffs_trace (YAFFS_TRACE_ERROR , +"Block %d is in state %d after gc, should be erased" , + n, bi->block_state); + } +} +
fs/yaffs2/yaffs_verify.c line 120
Quoted 10 lines
fs/yaffs2/yaffs_verify.c new file +10
-0+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));
You could just assign it to {0} above and drop this memset.
Quoted 93 lines
fs/yaffs2/yaffs_verify.c new file +93
-0+ +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,
fs/yaffs2/yaffs_verify.c line 223
Quoted 10 lines
fs/yaffs2/yaffs_verify.c new file +10
-0+"Obj %d header name is NULL" , + obj->obj_id); + +if (tags->obj_id >1 && ((u8) (oh->name[0 ])) ==0xff )/* Junk name */ +yaffs_trace (YAFFS_TRACE_VERIFY , +"Obj %d header name is 0xff" , + obj->obj_id); +} + +void yaffs_verify_file (struct yaffs_obj *obj)
Should be static, since it is only called from this file. Same probably applies to some other functions.
fs/yaffs2/yaffs_verify.c line 230
Quoted 7 lines
fs/yaffs2/yaffs_verify.c new file +7
-0+{ +int required_depth; +int actual_depth; +u32 last_chunk; +u32 the_chunk; +u32 x; +u32 i;
Put commonly typed variable declarations on one line.
Quoted 49 lines
fs/yaffs2/yaffs_verify.c new file +49
-0+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) +continue ; + + the_chunk =yaffs_get_group_base (dev, tn, i); +if (the_chunk >0 ) { +yaffs_rd_chunk_tags_nand (dev, the_chunk,NULL , + &tags); +if (tags.obj_id != obj_id || tags.chunk_id != i) +yaffs_trace (YAFFS_TRACE_VERIFY , +"Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)" , + obj_id, i, the_chunk, + tags.obj_id, tags.chunk_id);
fs/yaffs2/yaffs_verify.c line 289
Quoted 10 lines
fs/yaffs2/yaffs_verify.c new file +10
-0+ } + } +} + +void yaffs_verify_link (struct yaffs_obj *obj) +{ +if (obj &&yaffs_skip_verification (obj->my_dev)) +return ; + +/* Verify sane equivalent object */
This function does nothing. Get rid of it.
fs/yaffs2/yaffs_verify.c line 297
Quoted 8 lines
fs/yaffs2/yaffs_verify.c new file +8
-0+} + +void yaffs_verify_symlink (struct yaffs_obj *obj) +{ +if (obj &&yaffs_skip_verification (obj->my_dev)) +return ; + +/* Verify symlink string */
Same here.
fs/yaffs2/yaffs_verify.c line 303
Quoted 6 lines
fs/yaffs2/yaffs_verify.c new file +6
-0+} + +void yaffs_verify_special (struct yaffs_obj *obj) +{ +if (obj &&yaffs_skip_verification (obj->my_dev)) +return ;
and here.
fs/yaffs2/yaffs_verify.c line 306
Quoted 3 lines
fs/yaffs2/yaffs_verify.c new file +3
-0+} + +void yaffs_verify_obj (struct yaffs_obj *obj)
Should be static.
fs/yaffs2/yaffs_verify.c line 314
Quoted 8 lines
fs/yaffs2/yaffs_verify.c new file +8
-0+{ +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;
Can all go on one line.
fs/yaffs2/yaffs_verify.c line 237
Quoted 3 lines
fs/yaffs2/yaffs_verify.c new file +3
-0+ +if (!obj) +return ;
Should this just be left to oops, or is it valid to try and verify a NULL object?
Quoted 32 lines
fs/yaffs2/yaffs_verify.c new file +32
-0+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;
fs/yaffs2/yaffs_verify.c line 360
Quoted 10 lines
fs/yaffs2/yaffs_verify.c new file +10
-0+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__);
The verification code shouldn't be performance critical, so there shouldn't be any need to use the temporary buffer pool. You also have no error management if the temporary buffer allocation fails so you will just explode when you try and dereference buffer/oh. Why not:
oh = kzalloc(sizeof(struct yaffs_obj_hdr), GFP_NOFS);
if (!oh) {
pr_err("Failed to allocate memory for object verify\n");
return;
}
yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *)oh, &tags);
yaffs_verify_oh(obj, oh, &tags, 1);
kfree(oh);Similiar applies elsewhere.
Quoted 46 lines
fs/yaffs2/yaffs_verify.c new file +46
-0+ } + +/* 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;
fs/yaffs2/yaffs_verify.c line 416
Quoted 10 lines
fs/yaffs2/yaffs_verify.c new file +10
-0+struct list_head *lh; + +if (yaffs_skip_verification (dev)) +return ; + +/* Iterate through the objects in each hash entry */ + +for (i =0 ; i <YAFFS_NOBJECT_BUCKETS ; i++) { +list_for_each (lh, &dev->obj_bucket[i].list) { + obj = list_entry(lh, struct yaffs_obj, hash_link);
list_for_each_entry
Quoted 19 lines
fs/yaffs2/yaffs_verify.c new file +19
-0+yaffs_verify_obj (obj); + } + } +} + +void yaffs_verify_obj_in_dir (struct yaffs_obj *obj) +{ +struct list_head *lh; +struct yaffs_obj *list_obj; +int count =0 ; + +if (!obj) { +yaffs_trace (YAFFS_TRACE_ALWAYS ,"No object to verify" ); +BUG (); +return ; + } + +if (yaffs_skip_verification (obj->my_dev)) +return ;
fs/yaffs2/yaffs_verify.c line 445
Quoted 10 lines
fs/yaffs2/yaffs_verify.c new file +10
-0+ +if (!obj->parent) { +yaffs_trace (YAFFS_TRACE_ALWAYS ,"Object does not have parent" ); +BUG (); +return ; + } + +if (obj->parent->variant_type !=YAFFS_OBJECT_TYPE_DIRECTORY ) { +yaffs_trace (YAFFS_TRACE_ALWAYS ,"Parent is not directory" ); +BUG ();
return?
fs/yaffs2/yaffs_verify.c line 451
Quoted 6 lines
fs/yaffs2/yaffs_verify.c new file +6
-0+ } + +/* Iterate through the objects in each hash entry */ + +list_for_each (lh, &obj->parent->variant.dir_variant.children) { + list_obj = list_entry(lh, struct yaffs_obj, siblings);
list_for_each_entry
Quoted 27 lines
fs/yaffs2/yaffs_verify.c new file +27
-0+yaffs_verify_obj (list_obj); +if (obj == list_obj) + count++; + } + +if (count !=1 ) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"Object in directory %d times" , + count); +BUG (); + } +} + +void yaffs_verify_dir (struct yaffs_obj *directory) +{ +struct list_head *lh; +struct yaffs_obj *list_obj; + +if (!directory) { +BUG (); +return ; + } + +if (yaffs_skip_full_verification (directory->my_dev)) +return ; + +if (directory->variant_type !=YAFFS_OBJECT_TYPE_DIRECTORY ) {
fs/yaffs2/yaffs_verify.c line 488
Quoted 10 lines
fs/yaffs2/yaffs_verify.c new file +10
-0+yaffs_trace (YAFFS_TRACE_ALWAYS , +"Directory has wrong type: %d" , + directory->variant_type); +BUG (); + } + +/* Iterate through the objects in each hash entry */ + +list_for_each (lh, &directory->variant.dir_variant.children) { + list_obj = list_entry(lh, struct yaffs_obj, siblings);
list_for_each_entry
Quoted 26 lines
fs/yaffs2/yaffs_verify.c new file +26
-0+if (list_obj->parent != directory) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"Object in directory list has wrong parent %p" , + list_obj->parent); +BUG (); + } +yaffs_verify_obj_in_dir (list_obj); + } +} + +static int yaffs_free_verification_failures; + +void yaffs_verify_free_chunks (struct yaffs_dev *dev) +{ +int counted; +int difference; + +if (yaffs_skip_verification (dev)) +return ; + + counted =yaffs_count_free_chunks (dev); + + difference = dev->n_free_chunks - counted; + +if (difference) { + yaffs_trace(YAFFS_TRACE_ALWAYS,
fs/yaffs2/yaffs_verify.c line 524
Quoted 10 lines
fs/yaffs2/yaffs_verify.c new file +10
-0+"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 ;
This does nothing. Remove it.
Quoted 40 lines
fs/yaffs2/yaffs_verify.c new file +1
-0+}fs/yaffs2/yaffs_verify.h new file +33
-0@@ -0,0 +1,43 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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);
fs/yaffs2/yaffs_verify.h line 43
Quoted 10 lines
fs/yaffs2/yaffs_verify.h new file +10
-0+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
Many of these are local to yaffs_verify.c and should be made static and removed from the header file.
~Ryan
PATCH 7/10 Add yaffs2 file system: yaffs1 and yaffs2 mode handling
In reply to Charles Manning--- fs/yaffs2/yaffs_yaffs1.c | 424 +++++++++++++ fs/yaffs2/yaffs_yaffs1.h | 22 + fs/yaffs2/yaffs_yaffs2.c | 1509 ++++++++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_yaffs2.h | 39 ++ 4 files changed, 1994 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_yaffs1.c create mode 100644 fs/yaffs2/yaffs_yaffs1.h create mode 100644 fs/yaffs2/yaffs_yaffs2.c create mode 100644 fs/yaffs2/yaffs_yaffs2.h
fs/yaffs2/yaffs_yaffs1.c new file +424 -0
@@ -0,0 +1,424 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#include "yaffs_yaffs1.h" +#include "yportenv.h" +#include "yaffs_trace.h" +#include "yaffs_bitmap.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_nand.h" +#include "yaffs_attribs.h" + +int yaffs1_scan (struct yaffs_dev *dev) +{ +struct yaffs_ext_tags tags; +int blk; +int result; +int chunk; +int c; +int deleted; +enum yaffs_block_state state; +LIST_HEAD (hard_list); +struct yaffs_block_info *bi; +u32 seq_number; +struct yaffs_obj_hdr *oh; +struct yaffs_obj *in; +struct yaffs_obj *parent; +int alloc_failed =0 ; +struct yaffs_shadow_fixer *shadow_fixers =NULL ; +u8 *chunk_data; + +yaffs_trace (YAFFS_TRACE_SCAN , +"yaffs1_scan starts intstartblk %d intendblk %d..." , + dev->internal_start_block, dev->internal_end_block); + + chunk_data =yaffs_get_temp_buffer (dev, __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_SCAN ; c++) { +/* Read the tags and decide what to do */ + chunk = blk * dev->param.chunks_per_block + c; + + result =yaffs_rd_chunk_tags_nand (dev, chunk,NULL , + &tags); + +/* Let's have a good look at this chunk... */ + +if (tags.ecc_result ==YAFFS_ECC_RESULT_UNFIXED || + tags.is_deleted) { +/* YAFFS1 only... +* A deleted chunk +*/ + deleted++; + dev->n_free_chunks++; + }else if (!tags.chunk_used) { +/* An unassigned chunk in the block +* This means that either the block is empty or +* this is the one being allocated from +*/ + +if (c ==0 ) { +/* We're looking at the first chunk in +*the block so the block is unused */ + state =YAFFS_BLOCK_STATE_EMPTY ; + dev->n_erased_blocks++; + }else { +/* this is the block being allocated */ +yaffs_trace (YAFFS_TRACE_SCAN , +" Allocating from %d %d" , + blk, c); + state =YAFFS_BLOCK_STATE_ALLOCATING ; + dev->alloc_block = blk; + dev->alloc_page = c; + dev->alloc_block_finder = blk; + + } + + dev->n_free_chunks += + (dev->param.chunks_per_block - c); + }else if (tags.chunk_id >0 ) { +/* chunk_id > 0 so it is a data chunk... */ +unsigned int endpos; + +yaffs_set_chunk_bit (dev, blk, c); + bi->pages_in_use++; + + in =yaffs_find_or_create_by_number (dev, + tags.obj_id, +YAFFS_OBJECT_TYPE_FILE ); +/* PutChunkIntoFile checks for a clash +* (two data chunks with the same chunk_id). +*/ + +if (!in) + alloc_failed =1 ; + +if (in) { +if (!yaffs_put_chunk_in_file + (in, tags.chunk_id, chunk,1 )) + alloc_failed =1 ; + } + + endpos = + (tags.chunk_id -1 ) * + dev->data_bytes_per_chunk + + tags.n_bytes; +if (in && + in->variant_type == +YAFFS_OBJECT_TYPE_FILE && + in->variant.file_variant.scanned_size < + endpos) { + in->variant.file_variant.scanned_size = + endpos; +if (!dev->param.use_header_file_size) { + in->variant. + file_variant.file_size = + in->variant. + file_variant.scanned_size; + } + + } + }else { +/* chunk_id == 0, so it is an ObjectHeader. +* Make the object +*/ +yaffs_set_chunk_bit (dev, blk, c); + bi->pages_in_use++; + + result =yaffs_rd_chunk_tags_nand (dev, chunk, + chunk_data, +NULL ); + + oh = (struct yaffs_obj_hdr *)chunk_data; + + in =yaffs_find_by_number (dev, tags.obj_id); +if (in && in->variant_type != oh->type) { +/* This should not happen, but somehow +* Wev'e ended up with an obj_id that +* has been reused but not yet deleted, +* and worse still it has changed type. +* Delete the old object. +*/ + +yaffs_del_obj (in); + in =NULL ; + } + + in =yaffs_find_or_create_by_number (dev, + tags.obj_id, + oh->type); + +if (!in) + alloc_failed =1 ; + +if (in && oh->shadows_obj >0 ) { + +struct yaffs_shadow_fixer *fixer; + fixer = +kmalloc (sizeof + (struct yaffs_shadow_fixer ), +GFP_NOFS ); +if (fixer) { + fixer->next = shadow_fixers; + shadow_fixers = fixer; + fixer->obj_id = tags.obj_id; + fixer->shadowed_id = + oh->shadows_obj; +yaffs_trace (YAFFS_TRACE_SCAN , +" Shadow fixer: %d shadows %d" , + fixer->obj_id, + fixer->shadowed_id); + + } + + } + +if (in && in->valid) { +/* We have already filled this one. +* We have a duplicate and need to +* resolve it. */ + +unsigned existing_serial = in->serial; +unsigned new_serial = + tags.serial_number; + +if (((existing_serial +1 ) &3 ) == + new_serial) { +/* Use new one - destroy the +* exisiting one */ +yaffs_chunk_del (dev, + in->hdr_chunk, +1 , __LINE__); + in->valid =0 ; + }else { +/* Use existing - destroy +* this one. */ +yaffs_chunk_del (dev, chunk,1 , + __LINE__); + } + } + +if (in && !in->valid && + (tags.obj_id ==YAFFS_OBJECTID_ROOT || + tags.obj_id == +YAFFS_OBJECTID_LOSTNFOUND )) { +/* We only load some info, don't fiddle +* with directory structure */ + in->valid =1 ; + in->variant_type = oh->type; + + in->yst_mode = oh->yst_mode; +yaffs_load_attribs (in, oh); + in->hdr_chunk = chunk; + in->serial = tags.serial_number; + + }else if (in && !in->valid) { +/* we need to load this info */ + + in->valid =1 ; + in->variant_type = oh->type; + + in->yst_mode = oh->yst_mode; +yaffs_load_attribs (in, oh); + in->hdr_chunk = chunk; + in->serial = tags.serial_number; + +yaffs_set_obj_name_from_oh (in, oh); + in->dirty =0 ; + +/* directory stuff... +* hook up to parent +*/ + + parent = +yaffs_find_or_create_by_number + (dev, oh->parent_obj_id, +YAFFS_OBJECT_TYPE_DIRECTORY ); +if (!parent) + alloc_failed =1 ; +if (parent && parent->variant_type == +YAFFS_OBJECT_TYPE_UNKNOWN ) { +/* Set up as a directory */ + parent->variant_type = +YAFFS_OBJECT_TYPE_DIRECTORY ; +INIT_LIST_HEAD (&parent-> + variant.dir_variant. + children); + }else if (!parent || + parent->variant_type != +YAFFS_OBJECT_TYPE_DIRECTORY ) { +/* Hoosterman, a problem.... +* We're trying to use a +* non-directory as a directory +*/ + +yaffs_trace (YAFFS_TRACE_ERROR , +"yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." + ); + parent = dev->lost_n_found; + } + +yaffs_add_obj_to_dir (parent, in); + +switch (in->variant_type) { +case YAFFS_OBJECT_TYPE_UNKNOWN : +/* Todo got a problem */ +break ; +case YAFFS_OBJECT_TYPE_FILE : +if (dev->param. + use_header_file_size) + + in->variant. + file_variant.file_size + = oh->file_size; + +break ; +case YAFFS_OBJECT_TYPE_HARDLINK : + in->variant. + hardlink_variant.equiv_id = + oh->equiv_id; +list_add (&in->hard_links, + &hard_list); +break ; +case YAFFS_OBJECT_TYPE_DIRECTORY : +/* Do nothing */ +break ; +case YAFFS_OBJECT_TYPE_SPECIAL : +/* Do nothing */ +break ; +case YAFFS_OBJECT_TYPE_SYMLINK : + in->variant.symlink_variant. + alias = +yaffs_clone_str (oh->alias); +if (!in->variant. + symlink_variant.alias) + alloc_failed =1 ; +break ; + } + } + } + } + +if (state ==YAFFS_BLOCK_STATE_NEEDS_SCAN ) { +/* If we got this far while scanning, +* then the block is fully allocated. */ + state =YAFFS_BLOCK_STATE_FULL ; + } + +if (state ==YAFFS_BLOCK_STATE_ALLOCATING ) { +/* If the block was partially allocated then +* treat it as fully allocated. */ + state =YAFFS_BLOCK_STATE_FULL ; + dev->alloc_block =-1 ; + } + + bi->block_state = state; + +/* Now let's see if it was dirty */ +if (bi->pages_in_use ==0 && + !bi->has_shrink_hdr && + bi->block_state ==YAFFS_BLOCK_STATE_FULL ) +yaffs_block_became_dirty (dev, blk); + } + +/* Ok, we've done all the scanning. +* Fix up the hard link chains. +* We should now have scanned all the objects, now it's time to add +* these hardlinks. +*/ + +yaffs_link_fixup (dev, &hard_list); + +/* +* Fix up any shadowed objects. +* There should not be more than one of these. +*/ + { +struct yaffs_shadow_fixer *fixer; +struct yaffs_obj *obj; + +while (shadow_fixers) { + fixer = shadow_fixers; + shadow_fixers = fixer->next; +/* Complete the rename transaction by deleting the +* shadowed object then setting the object header +to unshadowed. +*/ + obj =yaffs_find_by_number (dev, fixer->shadowed_id); +if (obj) +yaffs_del_obj (obj); + + obj =yaffs_find_by_number (dev, fixer->obj_id); + +if (obj) +yaffs_update_oh (obj,NULL ,1 ,0 ,0 ,NULL ); + +kfree (fixer); + } + } + +yaffs_release_temp_buffer (dev, chunk_data, __LINE__); + +if (alloc_failed) +return YAFFS_FAIL ; + +yaffs_trace (YAFFS_TRACE_SCAN ,"yaffs1_scan ends" ); + +return YAFFS_OK ; +}
fs/yaffs2/yaffs_yaffs1.h new file +22 -0
@@ -0,0 +1,22 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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
fs/yaffs2/yaffs_yaffs2.c new file +1509 -0
1,509 lines not on this page · raw message
fs/yaffs2/yaffs_yaffs2.h new file +39 -0
@@ -0,0 +1,39 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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
Signature · 1 line
1.7.1
PATCH 8/10 Add yaffs2 file system: core guts code
In reply to Charles Manning--- fs/yaffs2/yaffs_guts.c | 5001 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_guts.h | 938 +++++++++ 2 files changed, 5939 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_guts.c create mode 100644 fs/yaffs2/yaffs_guts.h
fs/yaffs2/yaffs_guts.c new file +5001 -0
@@ -0,0 +1,5001 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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_trace.h" + +#include "yaffs_guts.h" +#include "yaffs_tagsvalidity.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_tagscompat.h" +#include "yaffs_nand.h" +#include "yaffs_yaffs1.h" +#include "yaffs_yaffs2.h" +#include "yaffs_bitmap.h" +#include "yaffs_verify.h" +#include "yaffs_nand.h" +#include "yaffs_packedtags2.h" +#include "yaffs_nameval.h" +#include "yaffs_allocator.h" +#include "yaffs_attribs.h" + +/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ +#define YAFFS_GC_GOOD_ENOUGH 2 +#define YAFFS_GC_PASSIVE_THRESHOLD 4 + +#include "yaffs_ecc.h" + +/* Forward declarations */ + +static int yaffs_wr_data_obj (struct yaffs_obj *in,int inode_chunk, +const u8 *buffer,int n_bytes,int use_reserve); + + + +/* Function to calculate chunk and offset */ + +static inline void yaffs_addr_to_chunk (struct yaffs_dev *dev,loff_t addr, +int *chunk_out,u32 *offset_out) +{ +int chunk; +u32 offset; + + chunk = (u32) (addr >> dev->chunk_shift); + +if (dev->chunk_div ==1 ) { +/* easy power of 2 case */ + offset = (u32) (addr & dev->chunk_mask); + }else { +/* Non power-of-2 case */ + +loff_t chunk_base; + + chunk /= dev->chunk_div; + + chunk_base = ((loff_t ) chunk) * dev->data_bytes_per_chunk; + offset = (u32) (addr - chunk_base); + } + + *chunk_out = chunk; + *offset_out = offset; +} + +/* Function to return the number of shifts for a power of 2 greater than or +* equal to the given number +* Note we don't try to cater for all possible numbers and this does not have to +* be hellishly efficient. +*/ + +static inline u32 calc_shifts_ceiling (u32 x) +{ +int extra_bits; +int shifts; + + shifts = extra_bits =0 ; + +while (x >1 ) { +if (x &1 ) + extra_bits++; + x >>=1 ; + shifts++; + } + +if (extra_bits) + shifts++; + +return shifts; +} + +/* Function to return the number of shifts to get a 1 in bit 0 +*/ + +static inline u32 calc_shifts (u32 x) +{ +u32 shifts; + + shifts =0 ; + +if (!x) +return 0 ; + +while (!(x &1 )) { + x >>=1 ; + shifts++; + } + +return shifts; +} + +/* +* Temporary buffer manipulations. +*/ + +static int yaffs_init_tmp_buffers (struct yaffs_dev *dev) +{ +int i; +u8 *buf = (u8 *)1 ; + +memset (dev->temp_buffer,0 ,sizeof (dev->temp_buffer)); + +for (i =0 ; buf && i <YAFFS_N_TEMP_BUFFERS ; i++) { + dev->temp_buffer[i].line =0 ;/* not in use */ + dev->temp_buffer[i].buffer = buf = +kmalloc (dev->param.total_bytes_per_chunk,GFP_NOFS ); + } + +return buf ?YAFFS_OK :YAFFS_FAIL ; +} + +u8 *yaffs_get_temp_buffer (struct yaffs_dev * dev,int line_no) +{ +int i; +int j; + + dev->temp_in_use++; +if (dev->temp_in_use > dev->max_temp) + dev->max_temp = dev->temp_in_use; + +for (i =0 ; i <YAFFS_N_TEMP_BUFFERS ; i++) { +if (dev->temp_buffer[i].line ==0 ) { + dev->temp_buffer[i].line = line_no; +if ((i +1 ) > dev->max_temp) { + dev->max_temp = i +1 ; +for (j =0 ; j <= i; j++) + dev->temp_buffer[j].max_line = + dev->temp_buffer[j].line; + } + +return dev->temp_buffer[i].buffer; + } + } + +yaffs_trace (YAFFS_TRACE_BUFFERS , +"Out of temp buffers at line %d, other held by lines:" , + line_no); +for (i =0 ; i <YAFFS_N_TEMP_BUFFERS ; i++) +yaffs_trace (YAFFS_TRACE_BUFFERS , +" %d" , dev->temp_buffer[i].line); + +/* +* If we got here then we have to allocate an unmanaged one +* This is not good. +*/ + + dev->unmanaged_buffer_allocs++; +return kmalloc (dev->data_bytes_per_chunk,GFP_NOFS ); + +} + +void yaffs_release_temp_buffer (struct yaffs_dev *dev,u8 *buffer,int line_no) +{ +int i; + + dev->temp_in_use--; + +for (i =0 ; i <YAFFS_N_TEMP_BUFFERS ; i++) { +if (dev->temp_buffer[i].buffer == buffer) { + dev->temp_buffer[i].line =0 ; +return ; + } + } + +if (buffer) { +/* assume it is an unmanaged one. */ +yaffs_trace (YAFFS_TRACE_BUFFERS , +"Releasing unmanaged temp buffer in line %d" , + line_no); +kfree (buffer); + dev->unmanaged_buffer_deallocs++; + } + +} + +/* +* Determine if we have a managed buffer. +*/ +int yaffs_is_managed_tmp_buffer (struct yaffs_dev *dev,const u8 *buffer) +{ +int i; + +for (i =0 ; i <YAFFS_N_TEMP_BUFFERS ; i++) { +if (dev->temp_buffer[i].buffer == buffer) +return 1 ; + } + +for (i =0 ; i < dev->param.n_caches; i++) { +if (dev->cache[i].data == buffer) +return 1 ; + } + +if (buffer == dev->checkpt_buffer) +return 1 ; + +yaffs_trace (YAFFS_TRACE_ALWAYS , +"yaffs: unmaged buffer detected." ); +return 0 ; +} + +/* +* Functions for robustisizing TODO +* +*/ + +static void yaffs_handle_chunk_wr_ok (struct yaffs_dev *dev,int nand_chunk, +const u8 *data, +const struct yaffs_ext_tags *tags) +{ + dev = dev; + nand_chunk = nand_chunk; + data = data; + tags = tags; +} + +static void yaffs_handle_chunk_update (struct yaffs_dev *dev,int nand_chunk, +const struct yaffs_ext_tags *tags) +{ + dev = dev; + nand_chunk = nand_chunk; + tags = tags; +} + +void yaffs_handle_chunk_error (struct yaffs_dev *dev, +struct yaffs_block_info *bi) +{ +if (!bi->gc_prioritise) { + bi->gc_prioritise =1 ; + dev->has_pending_prioritised_gc =1 ; + bi->chunk_error_strikes++; + +if (bi->chunk_error_strikes >3 ) { + bi->needs_retiring =1 ;/* Too many stikes, so retire */ +yaffs_trace (YAFFS_TRACE_ALWAYS , +"yaffs: Block struck out" ); + + } + } +} + +static void yaffs_handle_chunk_wr_error (struct yaffs_dev *dev,int nand_chunk, +int erased_ok) +{ +int flash_block = nand_chunk / dev->param.chunks_per_block; +struct yaffs_block_info *bi =yaffs_get_block_info (dev, flash_block); + +yaffs_handle_chunk_error (dev, bi); + +if (erased_ok) { +/* Was an actual write failure, +* so mark the block for retirement.*/ + bi->needs_retiring =1 ; +yaffs_trace (YAFFS_TRACE_ERROR |YAFFS_TRACE_BAD_BLOCKS , +"**>> Block %d needs retiring" , flash_block); + } + +/* Delete the chunk */ +yaffs_chunk_del (dev, nand_chunk,1 , __LINE__); +yaffs_skip_rest_of_block (dev); +} + +/* +* Verification code +*/ + +/* +* Simple hash function. Needs to have a reasonable spread +*/ + +static inline int yaffs_hash_fn (int n) +{ + n =abs (n); +return n %YAFFS_NOBJECT_BUCKETS ; +} + +/* +* Access functions to useful fake objects. +* Note that root might have a presence in NAND if permissions are set. +*/ + +struct yaffs_obj *yaffs_root (struct yaffs_dev *dev) +{ +return dev->root_dir; +} + +struct yaffs_obj *yaffs_lost_n_found (struct yaffs_dev *dev) +{ +return dev->lost_n_found; +} + +/* +* Erased NAND checking functions +*/ + +int yaffs_check_ff (u8 *buffer,int n_bytes) +{ +/* Horrible, slow implementation */ +while (n_bytes--) { +if (*buffer !=0xff ) +return 0 ; + buffer++; + } +return 1 ; +} + +static int yaffs_check_chunk_erased (struct yaffs_dev *dev,int nand_chunk) +{ +int retval =YAFFS_OK ; +u8 *data =yaffs_get_temp_buffer (dev, __LINE__); +struct yaffs_ext_tags tags; +int result; + + result =yaffs_rd_chunk_tags_nand (dev, nand_chunk, data, &tags); + +if (tags.ecc_result >YAFFS_ECC_RESULT_NO_ERROR ) + retval =YAFFS_FAIL ; + +if (!yaffs_check_ff (data, dev->data_bytes_per_chunk) || + tags.chunk_used) { +yaffs_trace (YAFFS_TRACE_NANDACCESS , +"Chunk %d not erased" , nand_chunk); + retval =YAFFS_FAIL ; + } + +yaffs_release_temp_buffer (dev, data, __LINE__); + +return retval; + +} + +static int yaffs_verify_chunk_written (struct yaffs_dev *dev, +int nand_chunk, +const u8 *data, +struct yaffs_ext_tags *tags) +{ +int retval =YAFFS_OK ; +struct yaffs_ext_tags temp_tags; +u8 *buffer =yaffs_get_temp_buffer (dev, __LINE__); +int result; + + result =yaffs_rd_chunk_tags_nand (dev, nand_chunk, buffer, &temp_tags); +if (memcmp (buffer, data, dev->data_bytes_per_chunk) || + temp_tags.obj_id != tags->obj_id || + temp_tags.chunk_id != tags->chunk_id || + temp_tags.n_bytes != tags->n_bytes) + retval =YAFFS_FAIL ; + +yaffs_release_temp_buffer (dev, buffer, __LINE__); + +return retval; +} + + +int yaffs_check_alloc_available (struct yaffs_dev *dev,int n_chunks) +{ +int reserved_chunks; +int reserved_blocks = dev->param.n_reserved_blocks; +int checkpt_blocks; + + checkpt_blocks =yaffs_calc_checkpt_blocks_required (dev); + + reserved_chunks = + (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; + +return (dev->n_free_chunks > (reserved_chunks + n_chunks)); +} + +static int yaffs_find_alloc_block (struct yaffs_dev *dev) +{ +int i; +struct yaffs_block_info *bi; + +if (dev->n_erased_blocks <1 ) { +/* Hoosterman we've got a problem. +* Can't get space to gc +*/ +yaffs_trace (YAFFS_TRACE_ERROR , +"yaffs tragedy: no more erased blocks" ); + +return -1 ; + } + +/* Find an empty block. */ + +for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { + dev->alloc_block_finder++; +if (dev->alloc_block_finder < dev->internal_start_block + || dev->alloc_block_finder > dev->internal_end_block) { + dev->alloc_block_finder = dev->internal_start_block; + } + + bi =yaffs_get_block_info (dev, dev->alloc_block_finder); + +if (bi->block_state ==YAFFS_BLOCK_STATE_EMPTY ) { + bi->block_state =YAFFS_BLOCK_STATE_ALLOCATING ; + dev->seq_number++; + bi->seq_number = dev->seq_number; + dev->n_erased_blocks--; +yaffs_trace (YAFFS_TRACE_ALLOCATE , +"Allocated block %d, seq %d, %d left" , + dev->alloc_block_finder, dev->seq_number, + dev->n_erased_blocks); +return dev->alloc_block_finder; + } + } + +yaffs_trace (YAFFS_TRACE_ALWAYS , +"yaffs tragedy: no more erased blocks, but there should have been %d" , + dev->n_erased_blocks); + +return -1 ; +} + +static int yaffs_alloc_chunk (struct yaffs_dev *dev,int use_reserver, +struct yaffs_block_info **block_ptr) +{ +int ret_val; +struct yaffs_block_info *bi; + +if (dev->alloc_block <0 ) { +/* Get next block to allocate off */ + dev->alloc_block =yaffs_find_alloc_block (dev); + dev->alloc_page =0 ; + } + +if (!use_reserver && !yaffs_check_alloc_available (dev,1 )) { +/* No space unless we're allowed to use the reserve. */ +return -1 ; + } + +if (dev->n_erased_blocks < dev->param.n_reserved_blocks + && dev->alloc_page ==0 ) +yaffs_trace (YAFFS_TRACE_ALLOCATE ,"Allocating reserve" ); + +/* Next page please.... */ +if (dev->alloc_block >=0 ) { + bi =yaffs_get_block_info (dev, dev->alloc_block); + + ret_val = (dev->alloc_block * dev->param.chunks_per_block) + + dev->alloc_page; + bi->pages_in_use++; +yaffs_set_chunk_bit (dev, dev->alloc_block, dev->alloc_page); + + dev->alloc_page++; + + dev->n_free_chunks--; + +/* If the block is full set the state to full */ +if (dev->alloc_page >= dev->param.chunks_per_block) { + bi->block_state =YAFFS_BLOCK_STATE_FULL ; + dev->alloc_block =-1 ; + } + +if (block_ptr) + *block_ptr = bi; + +return ret_val; + } + +yaffs_trace (YAFFS_TRACE_ERROR , +"!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" ); + +return -1 ; +} + +static int yaffs_get_erased_chunks (struct yaffs_dev *dev) +{ +int n; + + n = dev->n_erased_blocks * dev->param.chunks_per_block; + +if (dev->alloc_block >0 ) + n += (dev->param.chunks_per_block - dev->alloc_page); + +return n; + +} + +/* +* yaffs_skip_rest_of_block() skips over the rest of the allocation block +* if we don't want to write to it. +*/ +void yaffs_skip_rest_of_block (struct yaffs_dev *dev) +{ +struct yaffs_block_info *bi; + +if (dev->alloc_block >0 ) { + bi =yaffs_get_block_info (dev, dev->alloc_block); +if (bi->block_state ==YAFFS_BLOCK_STATE_ALLOCATING ) { + bi->block_state =YAFFS_BLOCK_STATE_FULL ; + dev->alloc_block =-1 ; + } + } +} + +static int yaffs_write_new_chunk (struct yaffs_dev *dev, +const u8 *data, +struct yaffs_ext_tags *tags,int use_reserver) +{ +int attempts =0 ; +int write_ok =0 ; +int chunk; + +yaffs2_checkpt_invalidate (dev); + +do { +struct yaffs_block_info *bi =0 ; +int erased_ok =0 ; + + chunk =yaffs_alloc_chunk (dev, use_reserver, &bi); +if (chunk <0 ) { +/* no space */ +break ; + } + +/* First check this chunk is erased, if it needs +* checking. The checking policy (unless forced +* always on) is as follows: +* +* Check the first page we try to write in a block. +* If the check passes then we don't need to check any +* more. If the check fails, we check again... +* If the block has been erased, we don't need to check. +* +* However, if the block has been prioritised for gc, +* then we think there might be something odd about +* this block and stop using it. +* +* Rationale: We should only ever see chunks that have +* not been erased if there was a partially written +* chunk due to power loss. This checking policy should +* catch that case with very few checks and thus save a +* lot of checks that are most likely not needed. +* +* Mods to the above +* If an erase check fails or the write fails we skip the +* rest of the block. +*/ + +/* let's give it a try */ + attempts++; + +if (dev->param.always_check_erased) + bi->skip_erased_check =0 ; + +if (!bi->skip_erased_check) { + erased_ok =yaffs_check_chunk_erased (dev, chunk); +if (erased_ok !=YAFFS_OK ) { +yaffs_trace (YAFFS_TRACE_ERROR , +"**>> yaffs chunk %d was not erased" , + chunk); + +/* If not erased, delete this one, +* skip rest of block and +* try another chunk */ +yaffs_chunk_del (dev, chunk,1 , __LINE__); +yaffs_skip_rest_of_block (dev); +continue ; + } + } + + write_ok =yaffs_wr_chunk_tags_nand (dev, chunk, data, tags); + +if (!bi->skip_erased_check) + write_ok = +yaffs_verify_chunk_written (dev, chunk, data, tags); + +if (write_ok !=YAFFS_OK ) { +/* Clean up aborted write, skip to next block and +* try another chunk */ +yaffs_handle_chunk_wr_error (dev, chunk, erased_ok); +continue ; + } + + bi->skip_erased_check =1 ; + +/* Copy the data into the robustification buffer */ +yaffs_handle_chunk_wr_ok (dev, chunk, data, tags); + + }while (write_ok !=YAFFS_OK && + (yaffs_wr_attempts <=0 || attempts <= yaffs_wr_attempts)); + +if (!write_ok) + chunk =-1 ; + +if (attempts >1 ) { +yaffs_trace (YAFFS_TRACE_ERROR , +"**>> yaffs write required %d attempts" , + attempts); + dev->n_retired_writes += (attempts -1 ); + } + +return chunk; +} + +/* +* Block retiring for handling a broken block. +*/ + +static void yaffs_retire_block (struct yaffs_dev *dev,int flash_block) +{ +struct yaffs_block_info *bi =yaffs_get_block_info (dev, flash_block); + +yaffs2_checkpt_invalidate (dev); + +yaffs2_clear_oldest_dirty_seq (dev, bi); + +if (yaffs_mark_bad (dev, flash_block) !=YAFFS_OK ) { +if (yaffs_erase_block (dev, flash_block) !=YAFFS_OK ) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"yaffs: Failed to mark bad and erase block %d" , + flash_block); + }else { +struct yaffs_ext_tags tags; +int chunk_id = + flash_block * dev->param.chunks_per_block; + +u8 *buffer =yaffs_get_temp_buffer (dev, __LINE__); + +memset (buffer,0xff , dev->data_bytes_per_chunk); +yaffs_init_tags (&tags); + tags.seq_number =YAFFS_SEQUENCE_BAD_BLOCK ; +if (dev->param.write_chunk_tags_fn (dev, chunk_id - + dev->chunk_offset, + buffer, + &tags) !=YAFFS_OK ) +yaffs_trace (YAFFS_TRACE_ALWAYS , +"yaffs: Failed to write bad block marker to block %d" , + flash_block); + +yaffs_release_temp_buffer (dev, buffer, __LINE__); + } + } + + bi->block_state =YAFFS_BLOCK_STATE_DEAD ; + bi->gc_prioritise =0 ; + bi->needs_retiring =0 ; + + dev->n_retired_blocks++; +} + +/*---------------- Name handling functions ------------*/ + +static u16 yaffs_calc_name_sum (const YCHAR *name) +{ +u16 sum =0 ; +u16 i =1 ; + +if (!name) +return 0 ; + +while ((*name) && i < (YAFFS_MAX_NAME_LENGTH /2 )) { + +/* 0x1f mask is case insensitive */ + sum += ((*name) &0x1f ) * i; + i++; + name++; + } +return sum; +} + +void yaffs_set_obj_name (struct yaffs_obj *obj,const YCHAR * name) +{ +#ifndef CONFIG_YAFFS_NO_SHORT_NAMES +memset (obj->short_name,0 ,sizeof (obj->short_name)); +if (name && +strnlen (name,YAFFS_SHORT_NAME_LENGTH +1 ) <= +YAFFS_SHORT_NAME_LENGTH ) +strcpy (obj->short_name, name); +else + obj->short_name[0 ] =_Y ('\0' ); +#endif + obj->sum =yaffs_calc_name_sum (name); +} + +void yaffs_set_obj_name_from_oh (struct yaffs_obj *obj, +const struct yaffs_obj_hdr *oh) +{ +#ifdef CONFIG_YAFFS_AUTO_UNICODE +YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH +1 ]; +memset (tmp_name,0 ,sizeof (tmp_name)); +yaffs_load_name_from_oh (obj->my_dev, tmp_name, oh->name, +YAFFS_MAX_NAME_LENGTH +1 ); +yaffs_set_obj_name (obj, tmp_name); +#else +yaffs_set_obj_name (obj, oh->name); +#endif +} + +/*-------------------- TNODES ------------------- + +* List of spare tnodes +* The list is hooked together using the first pointer +* in the tnode. +*/ + +struct yaffs_tnode *yaffs_get_tnode (struct yaffs_dev *dev) +{ +struct yaffs_tnode *tn =yaffs_alloc_raw_tnode (dev); + +if (tn) { +memset (tn,0 , dev->tnode_size); + dev->n_tnodes++; + } + + dev->checkpoint_blocks_required =0 ;/* force recalculation */ + +return tn; +} + +/* FreeTnode frees up a tnode and puts it back on the free list */ +static void yaffs_free_tnode (struct yaffs_dev *dev,struct yaffs_tnode *tn) +{ +yaffs_free_raw_tnode (dev, tn); + dev->n_tnodes--; + dev->checkpoint_blocks_required =0 ;/* force recalculation */ +} + +static void yaffs_deinit_tnodes_and_objs (struct yaffs_dev *dev) +{ +yaffs_deinit_raw_tnodes_and_objs (dev); + dev->n_obj =0 ; + dev->n_tnodes =0 ; +} + +void yaffs_load_tnode_0 (struct yaffs_dev *dev,struct yaffs_tnode *tn, +unsigned pos,unsigned val) +{ +u32 *map = (u32 *) tn; +u32 bit_in_map; +u32 bit_in_word; +u32 word_in_map; +u32 mask; + + pos &=YAFFS_TNODES_LEVEL0_MASK ; + val >>= dev->chunk_grp_bits; + + bit_in_map = pos * dev->tnode_width; + word_in_map = bit_in_map /32 ; + bit_in_word = bit_in_map & (32 -1 ); + + mask = dev->tnode_mask << bit_in_word; + + map[word_in_map] &= ~mask; + map[word_in_map] |= (mask & (val << bit_in_word)); + +if (dev->tnode_width > (32 - bit_in_word)) { + bit_in_word = (32 - bit_in_word); + word_in_map++; + mask = + dev->tnode_mask >> bit_in_word; + map[word_in_map] &= ~mask; + map[word_in_map] |= (mask & (val >> bit_in_word)); + } +} + +u32 yaffs_get_group_base (struct yaffs_dev *dev,struct yaffs_tnode *tn, +unsigned pos) +{ +u32 *map = (u32 *) tn; +u32 bit_in_map; +u32 bit_in_word; +u32 word_in_map; +u32 val; + + pos &=YAFFS_TNODES_LEVEL0_MASK ; + + bit_in_map = pos * dev->tnode_width; + word_in_map = bit_in_map /32 ; + bit_in_word = bit_in_map & (32 -1 ); + + val = map[word_in_map] >> bit_in_word; + +if (dev->tnode_width > (32 - bit_in_word)) { + bit_in_word = (32 - bit_in_word); + word_in_map++; + val |= (map[word_in_map] << bit_in_word); + } + + val &= dev->tnode_mask; + val <<= dev->chunk_grp_bits; + +return val; +} + +/* ------------------- End of individual tnode manipulation -----------------*/ + +/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ +* The look up tree is represented by the top tnode and the number of top_level +* in the tree. 0 means only the level 0 tnode is in the tree. +*/ + +/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ +struct yaffs_tnode *yaffs_find_tnode_0 (struct yaffs_dev *dev, +struct yaffs_file_var *file_struct, +u32 chunk_id) +{ +struct yaffs_tnode *tn = file_struct->top; +u32 i; +int required_depth; +int level = file_struct->top_level; + + dev = dev; + +/* Check sane level and chunk Id */ +if (level <0 || level >YAFFS_TNODES_MAX_LEVEL ) +return NULL ; + +if (chunk_id >YAFFS_MAX_CHUNK_ID ) +return NULL ; + +/* First check we're tall enough (ie enough top_level) */ + + i = chunk_id >>YAFFS_TNODES_LEVEL0_BITS ; + required_depth =0 ; +while (i) { + i >>=YAFFS_TNODES_INTERNAL_BITS ; + required_depth++; + } + +if (required_depth > file_struct->top_level) +return NULL ;/* Not tall enough, so we can't find it */ + +/* Traverse down to level 0 */ +while (level >0 && tn) { + tn = tn->internal[(chunk_id >> + (YAFFS_TNODES_LEVEL0_BITS + + (level -1 ) * +YAFFS_TNODES_INTERNAL_BITS )) & +YAFFS_TNODES_INTERNAL_MASK ]; + level--; + } + +return tn; +} + +/* add_find_tnode_0 finds the level 0 tnode if it exists, +* otherwise first expands the tree. +* This happens in two steps: +* 1. If the tree isn't tall enough, then make it taller. +* 2. Scan down the tree towards the level 0 tnode adding tnodes if required. +* +* Used when modifying the tree. +* +* If the tn argument is NULL, then a fresh tnode will be added otherwise the +* specified tn will be plugged into the ttree. +*/ + +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 required_depth; +int i; +int l; +struct yaffs_tnode *tn; +u32 x; + +/* Check sane level and page Id */ +if (file_struct->top_level <0 || + file_struct->top_level >YAFFS_TNODES_MAX_LEVEL ) +return NULL ; + +if (chunk_id >YAFFS_MAX_CHUNK_ID ) +return NULL ; + +/* First check we're tall enough (ie enough top_level) */ + + x = chunk_id >>YAFFS_TNODES_LEVEL0_BITS ; + required_depth =0 ; +while (x) { + x >>=YAFFS_TNODES_INTERNAL_BITS ; + required_depth++; + } + +if (required_depth > file_struct->top_level) { +/* Not tall enough, gotta make the tree taller */ +for (i = file_struct->top_level; i < required_depth; i++) { + + tn =yaffs_get_tnode (dev); + +if (tn) { + tn->internal[0 ] = file_struct->top; + file_struct->top = tn; + file_struct->top_level++; + }else { +yaffs_trace (YAFFS_TRACE_ERROR , +"yaffs: no more tnodes" ); +return NULL ; + } + } + } + +/* Traverse down to level 0, adding anything we need */ + + l = file_struct->top_level; + tn = file_struct->top; + +if (l >0 ) { +while (l >0 && tn) { + x = (chunk_id >> + (YAFFS_TNODES_LEVEL0_BITS + + (l -1 ) *YAFFS_TNODES_INTERNAL_BITS )) & +YAFFS_TNODES_INTERNAL_MASK ; + +if ((l >1 ) && !tn->internal[x]) { +/* Add missing non-level-zero tnode */ + tn->internal[x] =yaffs_get_tnode (dev); +if (!tn->internal[x]) +return NULL ; + }else if (l ==1 ) { +/* Looking from level 1 at level 0 */ +if (passed_tn) { +/* If we already have one, release it */ +if (tn->internal[x]) +yaffs_free_tnode (dev, + tn->internal[x]); + tn->internal[x] = passed_tn; + + }else if (!tn->internal[x]) { +/* Don't have one, none passed in */ + tn->internal[x] =yaffs_get_tnode (dev); +if (!tn->internal[x]) +return NULL ; + } + } + + tn = tn->internal[x]; + l--; + } + }else { +/* top is level 0 */ +if (passed_tn) { +memcpy (tn, passed_tn, + (dev->tnode_width *YAFFS_NTNODES_LEVEL0 ) /8 ); +yaffs_free_tnode (dev, passed_tn); + } + } + +return tn; +} + +static int yaffs_tags_match (const struct yaffs_ext_tags *tags,int obj_id, +int chunk_obj) +{ +return (tags->chunk_id == chunk_obj && + tags->obj_id == obj_id && + !tags->is_deleted) ?1 :0 ; + +} + +static int yaffs_find_chunk_in_group (struct yaffs_dev *dev,int the_chunk, +struct yaffs_ext_tags *tags,int obj_id, +int inode_chunk) +{ +int j; + +for (j =0 ; the_chunk && j < dev->chunk_grp_size; j++) { +if (yaffs_check_chunk_bit + (dev, the_chunk / dev->param.chunks_per_block, + the_chunk % dev->param.chunks_per_block)) { + +if (dev->chunk_grp_size ==1 ) +return the_chunk; +else { +yaffs_rd_chunk_tags_nand (dev, the_chunk,NULL , + tags); +if (yaffs_tags_match (tags, + obj_id, inode_chunk)) { +/* found it; */ +return the_chunk; + } + } + } + the_chunk++; + } +return -1 ; +} + +static int yaffs_find_chunk_in_file (struct yaffs_obj *in,int inode_chunk, +struct yaffs_ext_tags *tags) +{ +/*Get the Tnode, then get the level 0 offset chunk offset */ +struct yaffs_tnode *tn; +int the_chunk =-1 ; +struct yaffs_ext_tags local_tags; +int ret_val =-1 ; +struct yaffs_dev *dev = in->my_dev; + +if (!tags) { +/* Passed a NULL, so use our own tags space */ + tags = &local_tags; + } + + tn =yaffs_find_tnode_0 (dev, &in->variant.file_variant, inode_chunk); + +if (!tn) +return ret_val; + + the_chunk =yaffs_get_group_base (dev, tn, inode_chunk); + + ret_val =yaffs_find_chunk_in_group (dev, the_chunk, tags, in->obj_id, + inode_chunk); +return ret_val; +} + +static int yaffs_find_del_file_chunk (struct yaffs_obj *in,int inode_chunk, +struct yaffs_ext_tags *tags) +{ +/* Get the Tnode, then get the level 0 offset chunk offset */ +struct yaffs_tnode *tn; +int the_chunk =-1 ; +struct yaffs_ext_tags local_tags; +struct yaffs_dev *dev = in->my_dev; +int ret_val =-1 ; + +if (!tags) { +/* Passed a NULL, so use our own tags space */ + tags = &local_tags; + } + + tn =yaffs_find_tnode_0 (dev, &in->variant.file_variant, inode_chunk); + +if (!tn) +return ret_val; + + the_chunk =yaffs_get_group_base (dev, tn, inode_chunk); + + ret_val =yaffs_find_chunk_in_group (dev, the_chunk, tags, in->obj_id, + inode_chunk); + +/* Delete the entry in the filestructure (if found) */ +if (ret_val !=-1 ) +yaffs_load_tnode_0 (dev, tn, inode_chunk,0 ); + +return ret_val; +} + +int yaffs_put_chunk_in_file (struct yaffs_obj *in,int inode_chunk, +int nand_chunk,int in_scan) +{ +/* NB in_scan is zero unless scanning. +* For forward scanning, in_scan is > 0; +* for backward scanning in_scan is < 0 +* +* nand_chunk = 0 is a dummy insert to make sure the tnodes are there. +*/ + +struct yaffs_tnode *tn; +struct yaffs_dev *dev = in->my_dev; +int existing_cunk; +struct yaffs_ext_tags existing_tags; +struct yaffs_ext_tags new_tags; +unsigned existing_serial, new_serial; + +if (in->variant_type !=YAFFS_OBJECT_TYPE_FILE ) { +/* Just ignore an attempt at putting a chunk into a non-file +* during scanning. +* If it is not during Scanning then something went wrong! +*/ +if (!in_scan) { +yaffs_trace (YAFFS_TRACE_ERROR , +"yaffs tragedy:attempt to put data chunk into a non-file" + ); +BUG (); + } + +yaffs_chunk_del (dev, nand_chunk,1 , __LINE__); +return YAFFS_OK ; + } + + tn =yaffs_add_find_tnode_0 (dev, + &in->variant.file_variant, + inode_chunk,NULL ); +if (!tn) +return YAFFS_FAIL ; + +if (!nand_chunk) +/* Dummy insert, bail now */ +return YAFFS_OK ; + + existing_cunk =yaffs_get_group_base (dev, tn, inode_chunk); + +if (in_scan !=0 ) { +/* If we're scanning then we need to test for duplicates +* NB This does not need to be efficient since it should only +* happen when the power fails during a write, then only one +* chunk should ever be affected. +* +* Correction for YAFFS2: This could happen quite a lot and we +* need to think about efficiency! TODO +* Update: For backward scanning we don't need to re-read tags +* so this is quite cheap. +*/ + +if (existing_cunk >0 ) { +/* NB Right now existing chunk will not be real +* chunk_id if the chunk group size > 1 +* thus we have to do a FindChunkInFile to get the +* real chunk id. +* +* We have a duplicate now we need to decide which +* one to use: +* +* Backwards scanning YAFFS2: The old one is what +* we use, dump the new one. +* YAFFS1: Get both sets of tags and compare serial +* numbers. +*/ + +if (in_scan >0 ) { +/* Only do this for forward scanning */ +yaffs_rd_chunk_tags_nand (dev, + nand_chunk, +NULL , &new_tags); + +/* Do a proper find */ + existing_cunk = +yaffs_find_chunk_in_file (in, inode_chunk, + &existing_tags); + } + +if (existing_cunk <=0 ) { +/*Hoosterman - how did this happen? */ + +yaffs_trace (YAFFS_TRACE_ERROR , +"yaffs tragedy: existing chunk < 0 in scan" + ); + + } + +/* NB The deleted flags should be false, otherwise +* the chunks will not be loaded during a scan +*/ + +if (in_scan >0 ) { + new_serial = new_tags.serial_number; + existing_serial = existing_tags.serial_number; + } + +if ((in_scan >0 ) && + (existing_cunk <=0 || + ((existing_serial +1 ) &3 ) == new_serial)) { +/* Forward scanning. +* Use new +* Delete the old one and drop through to +* update the tnode +*/ +yaffs_chunk_del (dev, existing_cunk,1 , + __LINE__); + }else { +/* Backward scanning or we want to use the +* existing one +* Delete the new one and return early so that +* the tnode isn't changed +*/ +yaffs_chunk_del (dev, nand_chunk,1 , __LINE__); +return YAFFS_OK ; + } + } + + } + +if (existing_cunk ==0 ) + in->n_data_chunks++; + +yaffs_load_tnode_0 (dev, tn, inode_chunk, nand_chunk); + +return YAFFS_OK ; +} + +static void yaffs_soft_del_chunk (struct yaffs_dev *dev,int chunk) +{ +struct yaffs_block_info *the_block; +unsigned block_no; + +yaffs_trace (YAFFS_TRACE_DELETION ,"soft delete chunk %d" , chunk); + + block_no = chunk / dev->param.chunks_per_block; + the_block =yaffs_get_block_info (dev, block_no); +if (the_block) { + the_block->soft_del_pages++; + dev->n_free_chunks++; +yaffs2_update_oldest_dirty_seq (dev, block_no, the_block); + } +} + +/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all +* the chunks in the file. +* All soft deleting does is increment the block's softdelete count and pulls +* the chunk out of the tnode. +* Thus, essentially this is the same as DeleteWorker except that the chunks +* are soft deleted. +*/ + +static int yaffs_soft_del_worker (struct yaffs_obj *in,struct yaffs_tnode *tn, +u32 level,int chunk_offset) +{ +int i; +int the_chunk; +int all_done =1 ; +struct yaffs_dev *dev = in->my_dev; + +if (!tn) +return 1 ; + +if (level >0 ) { +for (i =YAFFS_NTNODES_INTERNAL -1 ; + all_done && i >=0 ; + i--) { +if (tn->internal[i]) { + all_done = +yaffs_soft_del_worker (in, + tn->internal[i], + level -1 , + (chunk_offset << +YAFFS_TNODES_INTERNAL_BITS ) + + i); +if (all_done) { +yaffs_free_tnode (dev, + tn->internal[i]); + tn->internal[i] =NULL ; + }else { +/* Can this happen? */ + } + } + } +return (all_done) ?1 :0 ; + } + +/* level 0 */ +for (i =YAFFS_NTNODES_LEVEL0 -1 ; i >=0 ; i--) { + the_chunk =yaffs_get_group_base (dev, tn, i); +if (the_chunk) { +yaffs_soft_del_chunk (dev, the_chunk); +yaffs_load_tnode_0 (dev, tn, i,0 ); + } + } +return 1 ; +} + +static void yaffs_remove_obj_from_dir (struct yaffs_obj *obj) +{ +struct yaffs_dev *dev = obj->my_dev; +struct yaffs_obj *parent; + +yaffs_verify_obj_in_dir (obj); + parent = obj->parent; + +yaffs_verify_dir (parent); + +if (dev && dev->param.remove_obj_fn) + dev->param.remove_obj_fn (obj); + +list_del_init (&obj->siblings); + obj->parent =NULL ; + +yaffs_verify_dir (parent); +} + +void yaffs_add_obj_to_dir (struct yaffs_obj *directory,struct yaffs_obj *obj) +{ +if (!directory) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"tragedy: Trying to add an object to a null pointer directory" + ); +BUG (); +return ; + } +if (directory->variant_type !=YAFFS_OBJECT_TYPE_DIRECTORY ) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"tragedy: Trying to add an object to a non-directory" + ); +BUG (); + } + +if (obj->siblings.prev ==NULL ) { +/* Not initialised */ +BUG (); + } + +yaffs_verify_dir (directory); + +yaffs_remove_obj_from_dir (obj); + +/* Now add it */ +list_add (&obj->siblings, &directory->variant.dir_variant.children); + obj->parent = directory; + +if (directory == obj->my_dev->unlinked_dir + || directory == obj->my_dev->del_dir) { + obj->unlinked =1 ; + obj->my_dev->n_unlinked_files++; + obj->rename_allowed =0 ; + } + +yaffs_verify_dir (directory); +yaffs_verify_obj_in_dir (obj); +} + +static int yaffs_change_obj_name (struct yaffs_obj *obj, +struct yaffs_obj *new_dir, +const YCHAR *new_name,int force,int shadows) +{ +int unlink_op; +int del_op; +struct yaffs_obj *existing_target; + +if (new_dir ==NULL ) + new_dir = obj->parent;/* use the old directory */ + +if (new_dir->variant_type !=YAFFS_OBJECT_TYPE_DIRECTORY ) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"tragedy: yaffs_change_obj_name: new_dir is not a directory" + ); +BUG (); + } + + unlink_op = (new_dir == obj->my_dev->unlinked_dir); + del_op = (new_dir == obj->my_dev->del_dir); + + existing_target =yaffs_find_by_name (new_dir, new_name); + +/* If the object is a file going into the unlinked directory, +* then it is OK to just stuff it in since duplicate names are OK. +* else only proceed if the new name does not exist and we're putting +* it into a directory. +*/ +if (!(unlink_op || del_op || force || + shadows >0 || !existing_target) || + new_dir->variant_type !=YAFFS_OBJECT_TYPE_DIRECTORY ) +return YAFFS_FAIL ; + +yaffs_set_obj_name (obj, new_name); + obj->dirty =1 ; +yaffs_add_obj_to_dir (new_dir, obj); + +if (unlink_op) + obj->unlinked =1 ; + +/* If it is a deletion then we mark it as a shrink for gc */ +if (yaffs_update_oh (obj, new_name,0 , del_op, shadows,NULL ) >=0 ) +return YAFFS_OK ; + +return YAFFS_FAIL ; +} + +/*------------------------ Short Operations Cache ------------------------------ +* In many situations where there is no high level buffering a lot of +* reads might be short sequential reads, and a lot of writes may be short +* sequential writes. eg. scanning/writing a jpeg file. +* In these cases, a short read/write cache can provide a huge perfomance +* benefit with dumb-as-a-rock code. +* In Linux, the page cache provides read buffering and the short op cache +* provides write buffering. +* +* There are a small number (~10) of cache chunks per device so that we don't +* need a very intelligent search. +*/ + +static int yaffs_obj_cache_dirty (struct yaffs_obj *obj) +{ +struct yaffs_dev *dev = obj->my_dev; +int i; +struct yaffs_cache *cache; +int n_caches = obj->my_dev->param.n_caches; + +for (i =0 ; i < n_caches; i++) { + cache = &dev->cache[i]; +if (cache->object == obj && cache->dirty) +return 1 ; + } + +return 0 ; +} + +static void yaffs_flush_file_cache (struct yaffs_obj *obj) +{ +struct yaffs_dev *dev = obj->my_dev; +int lowest =-99 ;/* Stop compiler whining. */ +int i; +struct yaffs_cache *cache; +int chunk_written =0 ; +int n_caches = obj->my_dev->param.n_caches; + +if (n_caches <1 ) +return ; +do { + cache =NULL ; + +/* Find the lowest dirty chunk for this object */ +for (i =0 ; i < n_caches; i++) { +if (dev->cache[i].object == obj && + dev->cache[i].dirty) { +if (!cache || + dev->cache[i].chunk_id < lowest) { + cache = &dev->cache[i]; + lowest = cache->chunk_id; + } + } + } + +if (cache && !cache->locked) { +/* Write it out and free it up */ + chunk_written = +yaffs_wr_data_obj (cache->object, + cache->chunk_id, + cache->data, + cache->n_bytes,1 ); + cache->dirty =0 ; + cache->object =NULL ; + } + }while (cache && chunk_written >0 ); + +if (cache) +/* Hoosterman, disk full while writing cache out. */ +yaffs_trace (YAFFS_TRACE_ERROR , +"yaffs tragedy: no space during cache write" ); +} + +/*yaffs_flush_whole_cache(dev) +* +* +*/ + +void yaffs_flush_whole_cache (struct yaffs_dev *dev) +{ +struct yaffs_obj *obj; +int n_caches = dev->param.n_caches; +int i; + +/* Find a dirty object in the cache and flush it... +* until there are no further dirty objects. +*/ +do { + obj =NULL ; +for (i =0 ; i < n_caches && !obj; i++) { +if (dev->cache[i].object && dev->cache[i].dirty) + obj = dev->cache[i].object; + } +if (obj) +yaffs_flush_file_cache (obj); + }while (obj); + +} + +/* Grab us a cache chunk for use. +* First look for an empty one. +* Then look for the least recently used non-dirty one. +* Then look for the least recently used dirty one...., flush and look again. +*/ +static struct yaffs_cache *yaffs_grab_chunk_worker (struct yaffs_dev *dev) +{ +int i; + +if (dev->param.n_caches >0 ) { +for (i =0 ; i < dev->param.n_caches; i++) { +if (!dev->cache[i].object) +return &dev->cache[i]; + } + } +return NULL ; +} + +static struct yaffs_cache *yaffs_grab_chunk_cache (struct yaffs_dev *dev) +{ +struct yaffs_cache *cache; +struct yaffs_obj *the_obj; +int usage; +int i; +int pushout; + +if (dev->param.n_caches <1 ) +return NULL ; + +/* Try find a non-dirty one... */ + + cache =yaffs_grab_chunk_worker (dev); + +if (!cache) { +/* They were all dirty, find the LRU object and flush +* its cache, then find again. +* NB what's here is not very accurate, +* we actually flush the object with the LRU chunk. +*/ + +/* With locking we can't assume we can use entry zero, +* Set the_obj to a valid pointer for Coverity. */ + the_obj = dev->cache[0 ].object; + usage =-1 ; + cache =NULL ; + pushout =-1 ; + +for (i =0 ; i < dev->param.n_caches; i++) { +if (dev->cache[i].object && + !dev->cache[i].locked && + (dev->cache[i].last_use < usage || + !cache)) { + usage = dev->cache[i].last_use; + the_obj = dev->cache[i].object; + cache = &dev->cache[i]; + pushout = i; + } + } + +if (!cache || cache->dirty) { +/* Flush and try again */ +yaffs_flush_file_cache (the_obj); + cache =yaffs_grab_chunk_worker (dev); + } + } +return cache; +} + +/* Find a cached chunk */ +static struct yaffs_cache *yaffs_find_chunk_cache (const struct yaffs_obj *obj, +int chunk_id) +{ +struct yaffs_dev *dev = obj->my_dev; +int i; + +if (dev->param.n_caches <1 ) +return NULL ; + +for (i =0 ; i < dev->param.n_caches; i++) { +if (dev->cache[i].object == obj && + dev->cache[i].chunk_id == chunk_id) { + dev->cache_hits++; + +return &dev->cache[i]; + } + } +return NULL ; +} + +/* Mark the chunk for the least recently used algorithym */ +static void yaffs_use_cache (struct yaffs_dev *dev,struct yaffs_cache *cache, +int is_write) +{ +int i; + +if (dev->param.n_caches <1 ) +return ; + +if (dev->cache_last_use <0 || + dev->cache_last_use >100000000 ) { +/* Reset the cache usages */ +for (i =1 ; i < dev->param.n_caches; i++) + dev->cache[i].last_use =0 ; + + dev->cache_last_use =0 ; + } + dev->cache_last_use++; + cache->last_use = dev->cache_last_use; + +if (is_write) + cache->dirty =1 ; +} + +/* Invalidate a single cache page. +* Do this when a whole page gets written, +* ie the short cache for this page is no longer valid. +*/ +static void yaffs_invalidate_chunk_cache (struct yaffs_obj *object,int chunk_id) +{ +struct yaffs_cache *cache; + +if (object->my_dev->param.n_caches >0 ) { + cache =yaffs_find_chunk_cache (object, chunk_id); + +if (cache) + cache->object =NULL ; + } +} + +/* Invalidate all the cache pages associated with this object +* Do this whenever ther file is deleted or resized. +*/ +static void yaffs_invalidate_whole_cache (struct yaffs_obj *in) +{ +int i; +struct yaffs_dev *dev = in->my_dev; + +if (dev->param.n_caches >0 ) { +/* Invalidate it. */ +for (i =0 ; i < dev->param.n_caches; i++) { +if (dev->cache[i].object == in) + dev->cache[i].object =NULL ; + } + } +} + +static void yaffs_unhash_obj (struct yaffs_obj *obj) +{ +int bucket; +struct yaffs_dev *dev = obj->my_dev; + +/* If it is still linked into the bucket list, free from the list */ +if (!list_empty (&obj->hash_link)) { +list_del_init (&obj->hash_link); + bucket =yaffs_hash_fn (obj->obj_id); + dev->obj_bucket[bucket].count--; + } +} + +/* FreeObject frees up a Object and puts it back on the free list */ +static void yaffs_free_obj (struct yaffs_obj *obj) +{ +struct yaffs_dev *dev; + +if (!obj) { +BUG (); +return ; + } + dev = obj->my_dev; +yaffs_trace (YAFFS_TRACE_OS ,"FreeObject %p inode %p" , + obj, obj->my_inode); +if (obj->parent) +BUG (); +if (!list_empty (&obj->siblings)) +BUG (); + +if (obj->my_inode) { +/* We're still hooked up to a cached inode. +* Don't delete now, but mark for later deletion +*/ + obj->defered_free =1 ; +return ; + } + +yaffs_unhash_obj (obj); + +yaffs_free_raw_obj (dev, obj); + dev->n_obj--; + dev->checkpoint_blocks_required =0 ;/* force recalculation */ +} + +void yaffs_handle_defered_free (struct yaffs_obj *obj) +{ +if (obj->defered_free) +yaffs_free_obj (obj); +} + +static int yaffs_generic_obj_del (struct yaffs_obj *in) +{ +/* Iinvalidate the file's data in the cache, without flushing. */ +yaffs_invalidate_whole_cache (in); + +if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { +/* Move to unlinked directory so we have a deletion record */ +yaffs_change_obj_name (in, in->my_dev->del_dir,_Y ("deleted" ),0 , +0 ); + } + +yaffs_remove_obj_from_dir (in); +yaffs_chunk_del (in->my_dev, in->hdr_chunk,1 , __LINE__); + in->hdr_chunk =0 ; + +yaffs_free_obj (in); +return YAFFS_OK ; + +} + +static void yaffs_soft_del_file (struct yaffs_obj *obj) +{ +if (!obj->deleted || + obj->variant_type !=YAFFS_OBJECT_TYPE_FILE || + obj->soft_del) +return ; + +if (obj->n_data_chunks <=0 ) { +/* Empty file with no duplicate object headers, +* just delete it immediately */ +yaffs_free_tnode (obj->my_dev, obj->variant.file_variant.top); + obj->variant.file_variant.top =NULL ; +yaffs_trace (YAFFS_TRACE_TRACING , +"yaffs: Deleting empty file %d" , + obj->obj_id); +yaffs_generic_obj_del (obj); + }else { +yaffs_soft_del_worker (obj, + obj->variant.file_variant.top, + obj->variant. + file_variant.top_level,0 ); + obj->soft_del =1 ; + } +} + +/* Pruning removes any part of the file structure tree that is beyond the +* bounds of the file (ie that does not point to chunks). +* +* A file should only get pruned when its size is reduced. +* +* Before pruning, the chunks must be pulled from the tree and the +* level 0 tnode entries must be zeroed out. +* Could also use this for file deletion, but that's probably better handled +* by a special case. +* +* This function is recursive. For levels > 0 the function is called again on +* any sub-tree. For level == 0 we just check if the sub-tree has data. +* If there is no data in a subtree then it is pruned. +*/ + +static struct yaffs_tnode *yaffs_prune_worker (struct yaffs_dev *dev, +struct yaffs_tnode *tn,u32 level, +int del0) +{ +int i; +int has_data; + +if (!tn) +return tn; + + has_data =0 ; + +if (level >0 ) { +for (i =0 ; i <YAFFS_NTNODES_INTERNAL ; i++) { +if (tn->internal[i]) { + tn->internal[i] = +yaffs_prune_worker (dev, + tn->internal[i], + level -1 , + (i ==0 ) ? del0 :1 ); + } + +if (tn->internal[i]) + has_data++; + } + }else { +int tnode_size_u32 = dev->tnode_size /sizeof (u32); +u32 *map = (u32 *) tn; + +for (i =0 ; !has_data && i < tnode_size_u32; i++) { +if (map[i]) + has_data++; + } + } + +if (has_data ==0 && del0) { +/* Free and return NULL */ +yaffs_free_tnode (dev, tn); + tn =NULL ; + } +return tn; +} + +static int yaffs_prune_tree (struct yaffs_dev *dev, +struct yaffs_file_var *file_struct) +{ +int i; +int has_data; +int done =0 ; +struct yaffs_tnode *tn; + +if (file_struct->top_level <1 ) +return YAFFS_OK ; + + file_struct->top = +yaffs_prune_worker (dev, file_struct->top, file_struct->top_level,0 ); + +/* Now we have a tree with all the non-zero branches NULL but +* the height is the same as it was. +* Let's see if we can trim internal tnodes to shorten the tree. +* We can do this if only the 0th element in the tnode is in use +* (ie all the non-zero are NULL) +*/ + +while (file_struct->top_level && !done) { + tn = file_struct->top; + + has_data =0 ; +for (i =1 ; i <YAFFS_NTNODES_INTERNAL ; i++) { +if (tn->internal[i]) + has_data++; + } + +if (!has_data) { + file_struct->top = tn->internal[0 ]; + file_struct->top_level--; +yaffs_free_tnode (dev, tn); + }else { + done =1 ; + } + } + +return YAFFS_OK ; +} + +/*-------------------- End of File Structure functions.-------------------*/ + +/* alloc_empty_obj gets us a clean Object.*/ +static struct yaffs_obj *yaffs_alloc_empty_obj (struct yaffs_dev *dev) +{ +struct yaffs_obj *obj =yaffs_alloc_raw_obj (dev); + +if (!obj) +return obj; + + dev->n_obj++; + +/* Now sweeten it up... */ + +memset (obj,0 ,sizeof (struct yaffs_obj )); + obj->being_created =1 ; + + obj->my_dev = dev; + obj->hdr_chunk =0 ; + obj->variant_type =YAFFS_OBJECT_TYPE_UNKNOWN ; +INIT_LIST_HEAD (&(obj->hard_links)); +INIT_LIST_HEAD (&(obj->hash_link)); +INIT_LIST_HEAD (&obj->siblings); + +/* Now make the directory sane */ +if (dev->root_dir) { + obj->parent = dev->root_dir; +list_add (&(obj->siblings), + &dev->root_dir->variant.dir_variant.children); + } + +/* Add it to the lost and found directory. +* NB Can't put root or lost-n-found in lost-n-found so +* check if lost-n-found exists first +*/ +if (dev->lost_n_found) +yaffs_add_obj_to_dir (dev->lost_n_found, obj); + + obj->being_created =0 ; + + dev->checkpoint_blocks_required =0 ;/* force recalculation */ + +return obj; +} + +static int yaffs_find_nice_bucket (struct yaffs_dev *dev) +{ +int i; +int l =999 ; +int lowest =999999 ; + +/* Search for the shortest list or one that +* isn't too long. +*/ + +for (i =0 ; i <10 && lowest >4 ; i++) { + dev->bucket_finder++; + dev->bucket_finder %=YAFFS_NOBJECT_BUCKETS ; +if (dev->obj_bucket[dev->bucket_finder].count < lowest) { + lowest = dev->obj_bucket[dev->bucket_finder].count; + l = dev->bucket_finder; + } + } + +return l; +} + +static int yaffs_new_obj_id (struct yaffs_dev *dev) +{ +int bucket =yaffs_find_nice_bucket (dev); +int found =0 ; +struct list_head *i; +u32 n = (u32 ) bucket; + +/* Now find an object value that has not already been taken +* by scanning the list. +*/ + +while (!found) { + found =1 ; + n +=YAFFS_NOBJECT_BUCKETS ; +if (1 || dev->obj_bucket[bucket].count >0 ) { +list_for_each (i, &dev->obj_bucket[bucket].list) { +/* If there is already one in the list */ + if (i && list_entry(i, struct yaffs_obj, + hash_link)->obj_id == n) { + found =0 ; + } + } + } + } +return n; +} + +static void yaffs_hash_obj (struct yaffs_obj *in) +{ +int bucket =yaffs_hash_fn (in->obj_id); +struct yaffs_dev *dev = in->my_dev; + +list_add (&in->hash_link, &dev->obj_bucket[bucket].list); + dev->obj_bucket[bucket].count++; +} + +struct yaffs_obj *yaffs_find_by_number (struct yaffs_dev *dev,u32 number) +{ +int bucket =yaffs_hash_fn (number); +struct list_head *i; +struct yaffs_obj *in; + +list_for_each (i, &dev->obj_bucket[bucket].list) { +/* Look if it is in the list */ + in = list_entry(i, struct yaffs_obj, hash_link); +if (in->obj_id == number) { +/* Don't show if it is defered free */ +if (in->defered_free) +return NULL ; +return in; + } + } + +return NULL ; +} + +struct yaffs_obj *yaffs_new_obj (struct yaffs_dev *dev,int number, +enum yaffs_obj_type type) +{ +struct yaffs_obj *the_obj =NULL ; +struct yaffs_tnode *tn =NULL ; + +if (number <0 ) + number =yaffs_new_obj_id (dev); + +if (type ==YAFFS_OBJECT_TYPE_FILE ) { + tn =yaffs_get_tnode (dev); +if (!tn) +return NULL ; + } + + the_obj =yaffs_alloc_empty_obj (dev); +if (!the_obj) { +if (tn) +yaffs_free_tnode (dev, tn); +return NULL ; + } + + the_obj->fake =0 ; + the_obj->rename_allowed =1 ; + the_obj->unlink_allowed =1 ; + the_obj->obj_id = number; +yaffs_hash_obj (the_obj); + the_obj->variant_type = type; +yaffs_load_current_time (the_obj,1 ,1 ); + +switch (type) { +case YAFFS_OBJECT_TYPE_FILE : + the_obj->variant.file_variant.file_size =0 ; + the_obj->variant.file_variant.scanned_size =0 ; + the_obj->variant.file_variant.shrink_size = ~0 ;/* max */ + the_obj->variant.file_variant.top_level =0 ; + the_obj->variant.file_variant.top = tn; +break ; +case YAFFS_OBJECT_TYPE_DIRECTORY : +INIT_LIST_HEAD (&the_obj->variant.dir_variant.children); +INIT_LIST_HEAD (&the_obj->variant.dir_variant.dirty); +break ; +case YAFFS_OBJECT_TYPE_SYMLINK : +case YAFFS_OBJECT_TYPE_HARDLINK : +case YAFFS_OBJECT_TYPE_SPECIAL : +/* No action required */ +break ; +case YAFFS_OBJECT_TYPE_UNKNOWN : +/* todo this should not happen */ +break ; + } +return the_obj; +} + +static struct yaffs_obj *yaffs_create_fake_dir (struct yaffs_dev *dev, +int number,u32 mode) +{ + +struct yaffs_obj *obj = +yaffs_new_obj (dev, number,YAFFS_OBJECT_TYPE_DIRECTORY ); + +if (!obj) +return NULL ; + + obj->fake =1 ;/* it is fake so it might not use NAND */ + obj->rename_allowed =0 ; + obj->unlink_allowed =0 ; + obj->deleted =0 ; + obj->unlinked =0 ; + obj->yst_mode = mode; + obj->my_dev = dev; + obj->hdr_chunk =0 ;/* Not a valid chunk. */ +return obj; + +} + + +static void yaffs_init_tnodes_and_objs (struct yaffs_dev *dev) +{ +int i; + + dev->n_obj =0 ; + dev->n_tnodes =0 ; +yaffs_init_raw_tnodes_and_objs (dev); + +for (i =0 ; i <YAFFS_NOBJECT_BUCKETS ; i++) { +INIT_LIST_HEAD (&dev->obj_bucket[i].list); + dev->obj_bucket[i].count =0 ; + } +} + +struct yaffs_obj *yaffs_find_or_create_by_number (struct yaffs_dev *dev, +int number, +enum yaffs_obj_type type) +{ +struct yaffs_obj *the_obj =NULL ; + +if (number >0 ) + the_obj =yaffs_find_by_number (dev, number); + +if (!the_obj) + the_obj =yaffs_new_obj (dev, number, type); + +return the_obj; + +} + +YCHAR *yaffs_clone_str (const YCHAR *str) +{ +YCHAR *new_str =NULL ; +int len; + +if (!str) + str =_Y ("" ); + + len =strnlen (str,YAFFS_MAX_ALIAS_LENGTH ); + new_str =kmalloc ((len +1 ) *sizeof (YCHAR ),GFP_NOFS ); +if (new_str) { +strncpy (new_str, str, len); + new_str[len] =0 ; + } +return new_str; + +} +/* +*yaffs_update_parent() handles fixing a directories mtime and ctime when a new +* link (ie. name) is created or deleted in the directory. +* +* ie. +* create dir/a : update dir's mtime/ctime +* rm dir/a: update dir's mtime/ctime +* modify dir/a: don't update dir's mtimme/ctime +* +* This can be handled immediately or defered. Defering helps reduce the number +* of updates when many files in a directory are changed within a brief period. +* +* If the directory updating is defered then yaffs_update_dirty_dirs must be +* called periodically. +*/ + +static void yaffs_update_parent (struct yaffs_obj *obj) +{ +struct yaffs_dev *dev; + +if (!obj) +return ; + dev = obj->my_dev; + obj->dirty =1 ; +yaffs_load_current_time (obj,0 ,1 ); +if (dev->param.defered_dir_update) { +struct list_head *link = &obj->variant.dir_variant.dirty; + +if (list_empty (link)) { +list_add (link, &dev->dirty_dirs); +yaffs_trace (YAFFS_TRACE_BACKGROUND , +"Added object %d to dirty directories" , + obj->obj_id); + } + + }else { +yaffs_update_oh (obj,NULL ,0 ,0 ,0 ,NULL ); + } +} + +void yaffs_update_dirty_dirs (struct yaffs_dev *dev) +{ +struct list_head *link; +struct yaffs_obj *obj; +struct yaffs_dir_var *d_s; +union yaffs_obj_var *o_v; + +yaffs_trace (YAFFS_TRACE_BACKGROUND ,"Update dirty directories" ); + +while (!list_empty (&dev->dirty_dirs)) { + link = dev->dirty_dirs.next; +list_del_init (link); + + d_s = list_entry(link, struct yaffs_dir_var, dirty); + o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); + obj = list_entry(o_v, struct yaffs_obj, variant); + +yaffs_trace (YAFFS_TRACE_BACKGROUND ,"Update directory %d" , + obj->obj_id); + +if (obj->dirty) +yaffs_update_oh (obj,NULL ,0 ,0 ,0 ,NULL ); + } +} + +/* +* Mknod (create) a new object. +* equiv_obj only has meaning for a hard link; +* alias_str only has meaning for a symlink. +* rdev only has meaning for devices (a subset of special objects) +*/ + +static struct yaffs_obj *yaffs_create_obj (enum yaffs_obj_type type, +struct yaffs_obj *parent, +const YCHAR *name, +u32 mode, +u32 uid, +u32 gid, +struct yaffs_obj *equiv_obj, +const YCHAR *alias_str,u32 rdev) +{ +struct yaffs_obj *in; +YCHAR *str =NULL ; +struct yaffs_dev *dev = parent->my_dev; + +/* Check if the entry exists. +* If it does then fail the call since we don't want a dup. */ +if (yaffs_find_by_name (parent, name)) +return NULL ; + +if (type ==YAFFS_OBJECT_TYPE_SYMLINK ) { + str =yaffs_clone_str (alias_str); +if (!str) +return NULL ; + } + + in =yaffs_new_obj (dev,-1 , type); + +if (!in) { +kfree (str); +return NULL ; + } + + in->hdr_chunk =0 ; + in->valid =1 ; + in->variant_type = type; + + in->yst_mode = mode; + +yaffs_attribs_init (in, gid, uid, rdev); + + in->n_data_chunks =0 ; + +yaffs_set_obj_name (in, name); + in->dirty =1 ; + +yaffs_add_obj_to_dir (parent, in); + + in->my_dev = parent->my_dev; + +switch (type) { +case YAFFS_OBJECT_TYPE_SYMLINK : + in->variant.symlink_variant.alias = str; +break ; +case YAFFS_OBJECT_TYPE_HARDLINK : + in->variant.hardlink_variant.equiv_obj = equiv_obj; + in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; +list_add (&in->hard_links, &equiv_obj->hard_links); +break ; +case YAFFS_OBJECT_TYPE_FILE : +case YAFFS_OBJECT_TYPE_DIRECTORY : +case YAFFS_OBJECT_TYPE_SPECIAL : +case YAFFS_OBJECT_TYPE_UNKNOWN : +/* do nothing */ +break ; + } + +if (yaffs_update_oh (in, name,0 ,0 ,0 ,NULL ) <0 ) { +/* Could not create the object header, fail */ +yaffs_del_obj (in); + in =NULL ; + } + +if (in) +yaffs_update_parent (parent); + +return in; +} + +struct yaffs_obj *yaffs_create_file (struct yaffs_obj *parent, +const YCHAR *name,u32 mode,u32 uid, +u32 gid) +{ +return yaffs_create_obj (YAFFS_OBJECT_TYPE_FILE , parent, name, mode, + uid, gid,NULL ,NULL ,0 ); +} + +struct yaffs_obj *yaffs_create_dir (struct yaffs_obj *parent,const YCHAR *name, +u32 mode,u32 uid,u32 gid) +{ +return yaffs_create_obj (YAFFS_OBJECT_TYPE_DIRECTORY , parent, name, + mode, uid, gid,NULL ,NULL ,0 ); +} + +struct yaffs_obj *yaffs_create_special (struct yaffs_obj *parent, +const YCHAR *name,u32 mode,u32 uid, +u32 gid,u32 rdev) +{ +return yaffs_create_obj (YAFFS_OBJECT_TYPE_SPECIAL , parent, name, mode, + uid, gid,NULL ,NULL , rdev); +} + +struct yaffs_obj *yaffs_create_symlink (struct yaffs_obj *parent, +const YCHAR *name,u32 mode,u32 uid, +u32 gid,const YCHAR *alias) +{ +return yaffs_create_obj (YAFFS_OBJECT_TYPE_SYMLINK , parent, name, mode, + uid, gid,NULL , alias,0 ); +} + +/* yaffs_link_obj returns the object id of the equivalent object.*/ +struct yaffs_obj *yaffs_link_obj (struct yaffs_obj *parent,const YCHAR * name, +struct yaffs_obj *equiv_obj) +{ +/* Get the real object in case we were fed a hard link obj */ + equiv_obj =yaffs_get_equivalent_obj (equiv_obj); + +if (yaffs_create_obj (YAFFS_OBJECT_TYPE_HARDLINK , + parent, name,0 ,0 ,0 , + equiv_obj,NULL ,0 )) +return equiv_obj; + +return NULL ; + +} + + + +/*---------------------- Block Management and Page Allocation -------------*/ + +static void yaffs_deinit_blocks (struct yaffs_dev *dev) +{ +if (dev->block_info_alt && dev->block_info) +vfree (dev->block_info); +else +kfree (dev->block_info); + + dev->block_info_alt =0 ; + + dev->block_info =NULL ; + +if (dev->chunk_bits_alt && dev->chunk_bits) +vfree (dev->chunk_bits); +else +kfree (dev->chunk_bits); + dev->chunk_bits_alt =0 ; + dev->chunk_bits =NULL ; +} + +static int yaffs_init_blocks (struct yaffs_dev *dev) +{ +int n_blocks = dev->internal_end_block - dev->internal_start_block +1 ; + + dev->block_info =NULL ; + dev->chunk_bits =NULL ; + dev->alloc_block =-1 ;/* force it to get a new one */ + +/* If the first allocation strategy fails, thry the alternate one */ + dev->block_info = +kmalloc (n_blocks *sizeof (struct yaffs_block_info ),GFP_NOFS ); +if (!dev->block_info) { + dev->block_info = +vmalloc (n_blocks *sizeof (struct yaffs_block_info )); + dev->block_info_alt =1 ; + }else { + dev->block_info_alt =0 ; + } + +if (!dev->block_info) + goto alloc_error; + +/* Set up dynamic blockinfo stuff. Round up bytes. */ + dev->chunk_bit_stride = (dev->param.chunks_per_block +7 ) /8 ; + dev->chunk_bits = +kmalloc (dev->chunk_bit_stride * n_blocks,GFP_NOFS ); +if (!dev->chunk_bits) { + dev->chunk_bits = +vmalloc (dev->chunk_bit_stride * n_blocks); + dev->chunk_bits_alt =1 ; + }else { + dev->chunk_bits_alt =0 ; + } +if (!dev->chunk_bits) + goto alloc_error; + + +memset (dev->block_info,0 , n_blocks *sizeof (struct yaffs_block_info )); +memset (dev->chunk_bits,0 , dev->chunk_bit_stride * n_blocks); +return YAFFS_OK ; + +alloc_error: +yaffs_deinit_blocks (dev); +return YAFFS_FAIL ; +} + + +void yaffs_block_became_dirty (struct yaffs_dev *dev,int block_no) +{ +struct yaffs_block_info *bi =yaffs_get_block_info (dev, block_no); +int erased_ok =0 ; +int i; + +/* If the block is still healthy erase it and mark as clean. +* If the block has had a data failure, then retire it. +*/ + +yaffs_trace (YAFFS_TRACE_GC |YAFFS_TRACE_ERASE , +"yaffs_block_became_dirty block %d state %d %s" , + block_no, bi->block_state, + (bi->needs_retiring) ?"needs retiring" :"" ); + +yaffs2_clear_oldest_dirty_seq (dev, bi); + + bi->block_state =YAFFS_BLOCK_STATE_DIRTY ; + +/* If this is the block being garbage collected then stop gc'ing */ +if (block_no == dev->gc_block) + dev->gc_block =0 ; + +/* If this block is currently the best candidate for gc +* then drop as a candidate */ +if (block_no == dev->gc_dirtiest) { + dev->gc_dirtiest =0 ; + dev->gc_pages_in_use =0 ; + } + +if (!bi->needs_retiring) { +yaffs2_checkpt_invalidate (dev); + erased_ok =yaffs_erase_block (dev, block_no); +if (!erased_ok) { + dev->n_erase_failures++; +yaffs_trace (YAFFS_TRACE_ERROR |YAFFS_TRACE_BAD_BLOCKS , +"**>> Erasure failed %d" , block_no); + } + } + +/* Verify erasure if needed */ +if (erased_ok && + ((yaffs_trace_mask &YAFFS_TRACE_ERASE ) || + !yaffs_skip_verification (dev))) { +for (i =0 ; i < dev->param.chunks_per_block; i++) { +if (!yaffs_check_chunk_erased (dev, + block_no * dev->param.chunks_per_block + i)) { +yaffs_trace (YAFFS_TRACE_ERROR , +">>Block %d erasure supposedly OK, but chunk %d not erased" , + block_no, i); + } + } + } + +if (!erased_ok) { +/* We lost a block of free space */ + dev->n_free_chunks -= dev->param.chunks_per_block; +yaffs_retire_block (dev, block_no); +yaffs_trace (YAFFS_TRACE_ERROR |YAFFS_TRACE_BAD_BLOCKS , +"**>> Block %d retired" , block_no); +return ; + } + +/* Clean it up... */ + bi->block_state =YAFFS_BLOCK_STATE_EMPTY ; + bi->seq_number =0 ; + dev->n_erased_blocks++; + bi->pages_in_use =0 ; + bi->soft_del_pages =0 ; + bi->has_shrink_hdr =0 ; + bi->skip_erased_check =1 ;/* Clean, so no need to check */ + bi->gc_prioritise =0 ; +yaffs_clear_chunk_bits (dev, block_no); + +yaffs_trace (YAFFS_TRACE_ERASE ,"Erased block %d" , block_no); +} + +static inline int yaffs_gc_process_chunk (struct yaffs_dev *dev, +struct yaffs_block_info *bi, +int old_chunk,u8 *buffer) +{ +int new_chunk; +int mark_flash =1 ; +struct yaffs_ext_tags tags; +struct yaffs_obj *object; +int matching_chunk; +int ret_val =YAFFS_OK ; + +yaffs_init_tags (&tags); +yaffs_rd_chunk_tags_nand (dev, old_chunk, + buffer, &tags); + object =yaffs_find_by_number (dev, tags.obj_id); + +yaffs_trace (YAFFS_TRACE_GC_DETAIL , +"Collecting chunk in block %d, %d %d %d " , + dev->gc_chunk, tags.obj_id, + tags.chunk_id, tags.n_bytes); + +if (object && !yaffs_skip_verification (dev)) { +if (tags.chunk_id ==0 ) + matching_chunk = + object->hdr_chunk; +else if (object->soft_del) +/* Defeat the test */ + matching_chunk = old_chunk; +else + matching_chunk = +yaffs_find_chunk_in_file + (object, tags.chunk_id, +NULL ); + +if (old_chunk != matching_chunk) +yaffs_trace (YAFFS_TRACE_ERROR , +"gc: page in gc mismatch: %d %d %d %d" , + old_chunk, + matching_chunk, + tags.obj_id, + tags.chunk_id); + } + +if (!object) { +yaffs_trace (YAFFS_TRACE_ERROR , +"page %d in gc has no object: %d %d %d " , + old_chunk, + tags.obj_id, tags.chunk_id, + tags.n_bytes); + } + +if (object && + object->deleted && + object->soft_del && tags.chunk_id !=0 ) { +/* Data chunk in a soft deleted file, +* throw it away. +* It's a soft deleted data chunk, +* No need to copy this, just forget +* about it and fix up the object. +*/ + +/* Free chunks already includes +* softdeleted chunks, how ever this +* chunk is going to soon be really +* deleted which will increment free +* chunks. We have to decrement free +* chunks so this works out properly. +*/ + dev->n_free_chunks--; + bi->soft_del_pages--; + + object->n_data_chunks--; +if (object->n_data_chunks <=0 ) { +/* remeber to clean up obj */ + dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; + dev->n_clean_ups++; + } + mark_flash =0 ; + }else if (object) { +/* It's either a data chunk in a live +* file or an ObjectHeader, so we're +* interested in it. +* NB Need to keep the ObjectHeaders of +* deleted files until the whole file +* has been deleted off +*/ + tags.serial_number++; + dev->n_gc_copies++; + +if (tags.chunk_id ==0 ) { +/* It is an object Id, +* We need to nuke the +* shrinkheader flags since its +* work is done. +* Also need to clean up +* shadowing. +*/ +struct yaffs_obj_hdr *oh; + oh = (struct yaffs_obj_hdr *) buffer; + + oh->is_shrink =0 ; + tags.extra_is_shrink =0 ; + oh->shadows_obj =0 ; + oh->inband_shadowed_obj_id =0 ; + tags.extra_shadows =0 ; + +/* Update file size */ +if (object->variant_type ==YAFFS_OBJECT_TYPE_FILE ) { + oh->file_size = + object->variant.file_variant.file_size; + tags.extra_length = oh->file_size; + } + +yaffs_verify_oh (object, oh, &tags,1 ); + new_chunk = +yaffs_write_new_chunk (dev, (u8 *) oh, &tags,1 ); + }else { + new_chunk = +yaffs_write_new_chunk (dev, buffer, &tags,1 ); + } + +if (new_chunk <0 ) { + ret_val =YAFFS_FAIL ; + }else { + +/* Now fix up the Tnodes etc. */ + +if (tags.chunk_id ==0 ) { +/* It's a header */ + object->hdr_chunk = new_chunk; + object->serial = tags.serial_number; + }else { +/* It's a data chunk */ +yaffs_put_chunk_in_file (object, tags.chunk_id, + new_chunk,0 ); + } + } + } +if (ret_val ==YAFFS_OK ) +yaffs_chunk_del (dev, old_chunk, mark_flash, __LINE__); +return ret_val; +} + +static int yaffs_gc_block (struct yaffs_dev *dev,int block,int whole_block) +{ +int old_chunk; +int ret_val =YAFFS_OK ; +int i; +int is_checkpt_block; +int max_copies; +int chunks_before =yaffs_get_erased_chunks (dev); +int chunks_after; +struct yaffs_block_info *bi =yaffs_get_block_info (dev, block); + + is_checkpt_block = (bi->block_state ==YAFFS_BLOCK_STATE_CHECKPOINT ); + +yaffs_trace (YAFFS_TRACE_TRACING , +"Collecting block %d, in use %d, shrink %d, whole_block %d" , + block, bi->pages_in_use, bi->has_shrink_hdr, + whole_block); + +/*yaffs_verify_free_chunks(dev); */ + +if (bi->block_state ==YAFFS_BLOCK_STATE_FULL ) + bi->block_state =YAFFS_BLOCK_STATE_COLLECTING ; + + bi->has_shrink_hdr =0 ;/* clear the flag so that the block can erase */ + + dev->gc_disable =1 ; + +if (is_checkpt_block || !yaffs_still_some_chunks (dev, block)) { +yaffs_trace (YAFFS_TRACE_TRACING , +"Collecting block %d that has no chunks in use" , + block); +yaffs_block_became_dirty (dev, block); + }else { + +u8 *buffer =yaffs_get_temp_buffer (dev, __LINE__); + +yaffs_verify_blk (dev, bi, block); + + max_copies = (whole_block) ? dev->param.chunks_per_block :5 ; + old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; + +for (/* init already done */ ; + ret_val ==YAFFS_OK && + dev->gc_chunk < dev->param.chunks_per_block && + (bi->block_state ==YAFFS_BLOCK_STATE_COLLECTING ) && + max_copies >0 ; + dev->gc_chunk++, old_chunk++) { +if (yaffs_check_chunk_bit (dev, block, dev->gc_chunk)) { +/* Page is in use and might need to be copied */ + max_copies--; + ret_val =yaffs_gc_process_chunk (dev, bi, + old_chunk, buffer); + } + } +yaffs_release_temp_buffer (dev, buffer, __LINE__); + } + +yaffs_verify_collected_blk (dev, bi, block); + +if (bi->block_state ==YAFFS_BLOCK_STATE_COLLECTING ) { +/* +* The gc did not complete. Set block state back to FULL +* because checkpointing does not restore gc. +*/ + bi->block_state =YAFFS_BLOCK_STATE_FULL ; + }else { +/* The gc completed. */ +/* Do any required cleanups */ +for (i =0 ; i < dev->n_clean_ups; i++) { +/* Time to delete the file too */ +struct yaffs_obj *object = +yaffs_find_by_number (dev, dev->gc_cleanup_list[i]); +if (object) { +yaffs_free_tnode (dev, + object->variant.file_variant.top); + object->variant.file_variant.top =NULL ; +yaffs_trace (YAFFS_TRACE_GC , +"yaffs: About to finally delete object %d" , + object->obj_id); +yaffs_generic_obj_del (object); + object->my_dev->n_deleted_files--; + } + + } + chunks_after =yaffs_get_erased_chunks (dev); +if (chunks_before >= chunks_after) +yaffs_trace (YAFFS_TRACE_GC , +"gc did not increase free chunks before %d after %d" , + chunks_before, chunks_after); + dev->gc_block =0 ; + dev->gc_chunk =0 ; + dev->n_clean_ups =0 ; + } + + dev->gc_disable =0 ; + +return ret_val; +} + +/* +* find_gc_block() selects the dirtiest block (or close enough) +* for garbage collection. +*/ + +static unsigned yaffs_find_gc_block (struct yaffs_dev *dev, +int aggressive,int background) +{ +int i; +int iterations; +unsigned selected =0 ; +int prioritised =0 ; +int prioritised_exist =0 ; +struct yaffs_block_info *bi; +int threshold; + +/* First let's see if we need to grab a prioritised block */ +if (dev->has_pending_prioritised_gc && !aggressive) { + dev->gc_dirtiest =0 ; + bi = dev->block_info; +for (i = dev->internal_start_block; + i <= dev->internal_end_block && !selected; i++) { + +if (bi->gc_prioritise) { + prioritised_exist =1 ; +if (bi->block_state ==YAFFS_BLOCK_STATE_FULL && +yaffs_block_ok_for_gc (dev, bi)) { + selected = i; + prioritised =1 ; + } + } + bi++; + } + +/* +* If there is a prioritised block and none was selected then +* this happened because there is at least one old dirty block +* gumming up the works. Let's gc the oldest dirty block. +*/ + +if (prioritised_exist && + !selected && dev->oldest_dirty_block >0 ) + selected = dev->oldest_dirty_block; + +if (!prioritised_exist)/* None found, so we can clear this */ + dev->has_pending_prioritised_gc =0 ; + } + +/* If we're doing aggressive GC then we are happy to take a less-dirty +* block, and search harder. +* else (leasurely gc), then we only bother to do this if the +* block has only a few pages in use. +*/ + +if (!selected) { +int pages_used; +int n_blocks = + dev->internal_end_block - dev->internal_start_block +1 ; +if (aggressive) { + threshold = dev->param.chunks_per_block; + iterations = n_blocks; + }else { +int max_threshold; + +if (background) + max_threshold = dev->param.chunks_per_block /2 ; +else + max_threshold = dev->param.chunks_per_block /8 ; + +if (max_threshold <YAFFS_GC_PASSIVE_THRESHOLD ) + max_threshold =YAFFS_GC_PASSIVE_THRESHOLD ; + + threshold = background ? (dev->gc_not_done +2 ) *2 :0 ; +if (threshold <YAFFS_GC_PASSIVE_THRESHOLD ) + threshold =YAFFS_GC_PASSIVE_THRESHOLD ; +if (threshold > max_threshold) + threshold = max_threshold; + + iterations = n_blocks /16 +1 ; +if (iterations >100 ) + iterations =100 ; + } + +for (i =0 ; + i < iterations && + (dev->gc_dirtiest <1 || + dev->gc_pages_in_use >YAFFS_GC_GOOD_ENOUGH ); + i++) { + dev->gc_block_finder++; +if (dev->gc_block_finder < dev->internal_start_block || + dev->gc_block_finder > dev->internal_end_block) + dev->gc_block_finder = + dev->internal_start_block; + + bi =yaffs_get_block_info (dev, dev->gc_block_finder); + + pages_used = bi->pages_in_use - bi->soft_del_pages; + +if (bi->block_state ==YAFFS_BLOCK_STATE_FULL && + pages_used < dev->param.chunks_per_block && + (dev->gc_dirtiest <1 || + pages_used < dev->gc_pages_in_use) && +yaffs_block_ok_for_gc (dev, bi)) { + dev->gc_dirtiest = dev->gc_block_finder; + dev->gc_pages_in_use = pages_used; + } + } + +if (dev->gc_dirtiest >0 && dev->gc_pages_in_use <= threshold) + selected = dev->gc_dirtiest; + } + +/* +* If nothing has been selected for a while, try the oldest dirty +* because that's gumming up the works. +*/ + +if (!selected && dev->param.is_yaffs2 && + dev->gc_not_done >= (background ?10 :20 )) { +yaffs2_find_oldest_dirty_seq (dev); +if (dev->oldest_dirty_block >0 ) { + selected = dev->oldest_dirty_block; + dev->gc_dirtiest = selected; + dev->oldest_dirty_gc_count++; + bi =yaffs_get_block_info (dev, selected); + dev->gc_pages_in_use = + bi->pages_in_use - bi->soft_del_pages; + }else { + dev->gc_not_done =0 ; + } + } + +if (selected) { +yaffs_trace (YAFFS_TRACE_GC , +"GC Selected block %d with %d free, prioritised:%d" , + selected, + dev->param.chunks_per_block - dev->gc_pages_in_use, + prioritised); + + dev->n_gc_blocks++; +if (background) + dev->bg_gcs++; + + dev->gc_dirtiest =0 ; + dev->gc_pages_in_use =0 ; + dev->gc_not_done =0 ; +if (dev->refresh_skip >0 ) + dev->refresh_skip--; + }else { + dev->gc_not_done++; +yaffs_trace (YAFFS_TRACE_GC , +"GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s" , + dev->gc_block_finder, dev->gc_not_done, threshold, + dev->gc_dirtiest, dev->gc_pages_in_use, + dev->oldest_dirty_block, background ?" bg" :"" ); + } + +return selected; +} + +/* New garbage collector +* If we're very low on erased blocks then we do aggressive garbage collection +* otherwise we do "leasurely" garbage collection. +* Aggressive gc looks further (whole array) and will accept less dirty blocks. +* Passive gc only inspects smaller areas and only accepts more dirty blocks. +* +* The idea is to help clear out space in a more spread-out manner. +* Dunno if it really does anything useful. +*/ +static int yaffs_check_gc (struct yaffs_dev *dev,int background) +{ +int aggressive =0 ; +int gc_ok =YAFFS_OK ; +int max_tries =0 ; +int min_erased; +int erased_chunks; +int checkpt_block_adjust; + +if (dev->param.gc_control && (dev->param.gc_control (dev) &1 ) ==0 ) +return YAFFS_OK ; + +if (dev->gc_disable) +/* Bail out so we don't get recursive gc */ +return YAFFS_OK ; + +/* This loop should pass the first time. +* Only loops here if the collection does not increase space. +*/ + +do { + max_tries++; + + checkpt_block_adjust =yaffs_calc_checkpt_blocks_required (dev); + + min_erased = + dev->param.n_reserved_blocks + checkpt_block_adjust +1 ; + erased_chunks = + dev->n_erased_blocks * dev->param.chunks_per_block; + +/* If we need a block soon then do aggressive gc. */ +if (dev->n_erased_blocks < min_erased) + aggressive =1 ; +else { +if (!background + && erased_chunks > (dev->n_free_chunks /4 )) +break ; + +if (dev->gc_skip >20 ) + dev->gc_skip =20 ; +if (erased_chunks < dev->n_free_chunks /2 || + dev->gc_skip <1 || background) + aggressive =0 ; +else { + dev->gc_skip--; +break ; + } + } + + dev->gc_skip =5 ; + +/* If we don't already have a block being gc'd then see if we +* should start another */ + +if (dev->gc_block <1 && !aggressive) { + dev->gc_block =yaffs2_find_refresh_block (dev); + dev->gc_chunk =0 ; + dev->n_clean_ups =0 ; + } +if (dev->gc_block <1 ) { + dev->gc_block = +yaffs_find_gc_block (dev, aggressive, background); + dev->gc_chunk =0 ; + dev->n_clean_ups =0 ; + } + +if (dev->gc_block >0 ) { + dev->all_gcs++; +if (!aggressive) + dev->passive_gc_count++; + +yaffs_trace (YAFFS_TRACE_GC , +"yaffs: GC n_erased_blocks %d aggressive %d" , + dev->n_erased_blocks, aggressive); + + gc_ok =yaffs_gc_block (dev, dev->gc_block, aggressive); + } + +if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && + dev->gc_block >0 ) { +yaffs_trace (YAFFS_TRACE_GC , +"yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d" , + dev->n_erased_blocks, max_tries, + dev->gc_block); + } + }while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && + (dev->gc_block >0 ) && (max_tries <2 )); + +return aggressive ? gc_ok :YAFFS_OK ; +} + +/* +* yaffs_bg_gc() +* Garbage collects. Intended to be called from a background thread. +* Returns non-zero if at least half the free chunks are erased. +*/ +int yaffs_bg_gc (struct yaffs_dev *dev,unsigned urgency) +{ +int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; + +yaffs_trace (YAFFS_TRACE_BACKGROUND ,"Background gc %u" , urgency); + +yaffs_check_gc (dev,1 ); +return erased_chunks > dev->n_free_chunks /2 ; +} + +/*-------------------- Data file manipulation -----------------*/ + +static int yaffs_rd_data_obj (struct yaffs_obj *in,int inode_chunk,u8 * buffer) +{ +int nand_chunk =yaffs_find_chunk_in_file (in, inode_chunk,NULL ); + +if (nand_chunk >=0 ) +return yaffs_rd_chunk_tags_nand (in->my_dev, nand_chunk, + buffer,NULL ); +else { +yaffs_trace (YAFFS_TRACE_NANDACCESS , +"Chunk %d not found zero instead" , + nand_chunk); +/* get sane (zero) data if you read a hole */ +memset (buffer,0 , in->my_dev->data_bytes_per_chunk); +return 0 ; + } + +} + +void yaffs_chunk_del (struct yaffs_dev *dev,int chunk_id,int mark_flash, +int lyn) +{ +int block; +int page; +struct yaffs_ext_tags tags; +struct yaffs_block_info *bi; + +if (chunk_id <=0 ) +return ; + + dev->n_deletions++; + block = chunk_id / dev->param.chunks_per_block; + page = chunk_id % dev->param.chunks_per_block; + +if (!yaffs_check_chunk_bit (dev, block, page)) +yaffs_trace (YAFFS_TRACE_VERIFY , +"Deleting invalid chunk %d" , chunk_id); + + bi =yaffs_get_block_info (dev, block); + +yaffs2_update_oldest_dirty_seq (dev, block, bi); + +yaffs_trace (YAFFS_TRACE_DELETION , +"line %d delete of chunk %d" , + lyn, chunk_id); + +if (!dev->param.is_yaffs2 && mark_flash && + bi->block_state !=YAFFS_BLOCK_STATE_COLLECTING ) { + +yaffs_init_tags (&tags); + tags.is_deleted =1 ; +yaffs_wr_chunk_tags_nand (dev, chunk_id,NULL , &tags); +yaffs_handle_chunk_update (dev, chunk_id, &tags); + }else { + dev->n_unmarked_deletions++; + } + +/* Pull out of the management area. +* If the whole block became dirty, this will kick off an erasure. +*/ +if (bi->block_state ==YAFFS_BLOCK_STATE_ALLOCATING || + bi->block_state ==YAFFS_BLOCK_STATE_FULL || + bi->block_state ==YAFFS_BLOCK_STATE_NEEDS_SCAN || + bi->block_state ==YAFFS_BLOCK_STATE_COLLECTING ) { + dev->n_free_chunks++; +yaffs_clear_chunk_bit (dev, block, page); + bi->pages_in_use--; + +if (bi->pages_in_use ==0 && + !bi->has_shrink_hdr && + bi->block_state !=YAFFS_BLOCK_STATE_ALLOCATING && + bi->block_state !=YAFFS_BLOCK_STATE_NEEDS_SCAN ) { +yaffs_block_became_dirty (dev, block); + } + } +}
Lines of this file are not shown. View the raw message.
fs/yaffs2/yaffs_guts.h new file +938 -0
938 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
1.7.1
Part of the diff is not shown here. View the raw message.
Re: [PATCH 08/10] Add yaffs2 file system: core guts code
Quoted 1 line · Charles Manning
Signed-off-by Charles Manning <[email protected]>
Hi Charles,
More comments below. The cache "locking" thing needs to be sorted.
~Ryan
Quoted 52 lines
--- fs/yaffs2/yaffs_guts.c | 5001 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_guts.h | 938 +++++++++ 2 files changed, 5939 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_guts.c create mode 100644 fs/yaffs2/yaffs_guts.hfs/yaffs2/yaffs_guts.c new file +39
-0@@ -0,0 +1,5001 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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_trace.h" + +#include "yaffs_guts.h" +#include "yaffs_tagsvalidity.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_tagscompat.h" +#include "yaffs_nand.h" +#include "yaffs_yaffs1.h" +#include "yaffs_yaffs2.h" +#include "yaffs_bitmap.h" +#include "yaffs_verify.h" +#include "yaffs_nand.h" +#include "yaffs_packedtags2.h" +#include "yaffs_nameval.h" +#include "yaffs_allocator.h" +#include "yaffs_attribs.h" + +/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ +#define YAFFS_GC_GOOD_ENOUGH 2 +#define YAFFS_GC_PASSIVE_THRESHOLD 4 + +#include "yaffs_ecc.h" + +/* Forward declarations */ +
fs/yaffs2/yaffs_guts.c line 49
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+static int yaffs_wr_data_obj (struct yaffs_obj *in,int inode_chunk, +const u8 *buffer,int n_bytes,int use_reserve); + + + +/* Function to calculate chunk and offset */ + +static inline void yaffs_addr_to_chunk (struct yaffs_dev *dev,loff_t addr, +int *chunk_out,u32 *offset_out) +{
Possibly should drop inline from this function as it is quite big.
fs/yaffs2/yaffs_guts.c line 57
Quoted 8 lines
fs/yaffs2/yaffs_guts.c new file +8
-0+int chunk; +u32 offset; + + chunk = (u32) (addr >> dev->chunk_shift); + +if (dev->chunk_div ==1 ) { +/* easy power of 2 case */ + offset = (u32) (addr & dev->chunk_mask);
You shouldn't need an explicit cast here (or below).
Quoted 59 lines
fs/yaffs2/yaffs_guts.c new file +59
-0+ }else { +/* Non power-of-2 case */ + +loff_t chunk_base; + + chunk /= dev->chunk_div; + + chunk_base = ((loff_t ) chunk) * dev->data_bytes_per_chunk; + offset = (u32) (addr - chunk_base); + } + + *chunk_out = chunk; + *offset_out = offset; +} + +/* Function to return the number of shifts for a power of 2 greater than or +* equal to the given number +* Note we don't try to cater for all possible numbers and this does not have to +* be hellishly efficient. +*/ + +static inline u32 calc_shifts_ceiling (u32 x) +{ +int extra_bits; +int shifts; + + shifts = extra_bits =0 ; + +while (x >1 ) { +if (x &1 ) + extra_bits++; + x >>=1 ; + shifts++; + } + +if (extra_bits) + shifts++; + +return shifts; +} + +/* Function to return the number of shifts to get a 1 in bit 0 +*/ + +static inline u32 calc_shifts (u32 x) +{ +u32 shifts; + + shifts =0 ; + +if (!x) +return 0 ; + +while (!(x &1 )) { + x >>=1 ; + shifts++; + } + +return shifts;
fs/yaffs2/yaffs_guts.c line 126
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+} + +/* +* Temporary buffer manipulations. +*/ + +static int yaffs_init_tmp_buffers (struct yaffs_dev *dev) +{ +int i; +u8 *buf = (u8 *)1 ;
This looks suspect.
fs/yaffs2/yaffs_guts.c line 133
Quoted 7 lines
fs/yaffs2/yaffs_guts.c new file +7
-0+ +memset (dev->temp_buffer,0 ,sizeof (dev->temp_buffer)); + +for (i =0 ; buf && i <YAFFS_N_TEMP_BUFFERS ; i++) { + dev->temp_buffer[i].line =0 ;/* not in use */ + dev->temp_buffer[i].buffer = buf = +kmalloc (dev->param.total_bytes_per_chunk,GFP_NOFS );
The assignment to buf is sneakily hidden in this statement. This function should also free any buffers it has allocated if it fails. Something like this:
static int yaffs_init_tmp_buffers(struct yaffs_dev *dev)
{
int i;
for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
dev->temp_buffer[i].line = 0;
dev->temp_buffer[i].buffer =
kmalloc(dev->param.total_bytes_per_chunk,
GFP_NOFS);
if (!dev->temp_buffer[i].buffer)
goto fail;
}
return YAFFS_OK;fail:
while (--i >= 0) kfree(dev->temp_buffer[i].buffer); return YAFFS_FAIL; }
fs/yaffs2/yaffs_guts.c line 144
Quoted 11 lines
fs/yaffs2/yaffs_guts.c new file +11
-0+ } + +return buf ?YAFFS_OK :YAFFS_FAIL ; +} + +u8 *yaffs_get_temp_buffer (struct yaffs_dev * dev,int line_no) +{ +int i; +int j; + + dev->temp_in_use++;
Should this get incremented even if the allocation fails?
fs/yaffs2/yaffs_guts.c line 146
Quoted 2 lines
fs/yaffs2/yaffs_guts.c new file +2
-0+if (dev->temp_in_use > dev->max_temp) + dev->max_temp = dev->temp_in_use;
If dev->temp_in_use >= dev->max_temp then is the for loop below still worth entering or can we just jump over it knowing that there are no free buffers?
fs/yaffs2/yaffs_guts.c line 149
Quoted 3 lines
fs/yaffs2/yaffs_guts.c new file +3
-0+ +for (i =0 ; i <YAFFS_N_TEMP_BUFFERS ; i++) { +if (dev->temp_buffer[i].line ==0 ) {
if (dev->temp_buffer[i].line != 0) continue;
Then drop the level of indentation.
Quoted 65 lines
fs/yaffs2/yaffs_guts.c new file +65
-0+ dev->temp_buffer[i].line = line_no; +if ((i +1 ) > dev->max_temp) { + dev->max_temp = i +1 ; +for (j =0 ; j <= i; j++) + dev->temp_buffer[j].max_line = + dev->temp_buffer[j].line; + } + +return dev->temp_buffer[i].buffer; + } + } + +yaffs_trace (YAFFS_TRACE_BUFFERS , +"Out of temp buffers at line %d, other held by lines:" , + line_no); +for (i =0 ; i <YAFFS_N_TEMP_BUFFERS ; i++) +yaffs_trace (YAFFS_TRACE_BUFFERS , +" %d" , dev->temp_buffer[i].line); + +/* +* If we got here then we have to allocate an unmanaged one +* This is not good. +*/ + + dev->unmanaged_buffer_allocs++; +return kmalloc (dev->data_bytes_per_chunk,GFP_NOFS ); + +} + +void yaffs_release_temp_buffer (struct yaffs_dev *dev,u8 *buffer,int line_no) +{ +int i; + + dev->temp_in_use--; + +for (i =0 ; i <YAFFS_N_TEMP_BUFFERS ; i++) { +if (dev->temp_buffer[i].buffer == buffer) { + dev->temp_buffer[i].line =0 ; +return ; + } + } + +if (buffer) { +/* assume it is an unmanaged one. */ +yaffs_trace (YAFFS_TRACE_BUFFERS , +"Releasing unmanaged temp buffer in line %d" , + line_no); +kfree (buffer); + dev->unmanaged_buffer_deallocs++; + } + +} + +/* +* Determine if we have a managed buffer. +*/ +int yaffs_is_managed_tmp_buffer (struct yaffs_dev *dev,const u8 *buffer) +{ +int i; + +for (i =0 ; i <YAFFS_N_TEMP_BUFFERS ; i++) { +if (dev->temp_buffer[i].buffer == buffer) +return 1 ; + } +
fs/yaffs2/yaffs_guts.c line 224
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+for (i =0 ; i < dev->param.n_caches; i++) { +if (dev->cache[i].data == buffer) +return 1 ; + } + +if (buffer == dev->checkpt_buffer) +return 1 ; + +yaffs_trace (YAFFS_TRACE_ALWAYS , +"yaffs: unmaged buffer detected." );
Typo "unmaged".
Quoted 6 lines
fs/yaffs2/yaffs_guts.c new file +6
-0+return 0 ; +} + +/* +* Functions for robustisizing TODO +*
fs/yaffs2/yaffs_guts.c line 240
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+*/ + +static void yaffs_handle_chunk_wr_ok (struct yaffs_dev *dev,int nand_chunk, +const u8 *data, +const struct yaffs_ext_tags *tags) +{ + dev = dev; + nand_chunk = nand_chunk; + data = data; + tags = tags;
Remove this function, it doesn't do anything useful.
fs/yaffs2/yaffs_guts.c line 248
Quoted 8 lines
fs/yaffs2/yaffs_guts.c new file +8
-0+} + +static void yaffs_handle_chunk_update (struct yaffs_dev *dev,int nand_chunk, +const struct yaffs_ext_tags *tags) +{ + dev = dev; + nand_chunk = nand_chunk; + tags = tags;
and this one.
fs/yaffs2/yaffs_guts.c line 254
Quoted 6 lines
fs/yaffs2/yaffs_guts.c new file +6
-0+} + +void yaffs_handle_chunk_error (struct yaffs_dev *dev, +struct yaffs_block_info *bi) +{ +if (!bi->gc_prioritise) {
if (bi->gc_prioritise) return;
Drop indentation.
Quoted 67 lines
fs/yaffs2/yaffs_guts.c new file +67
-0+ bi->gc_prioritise =1 ; + dev->has_pending_prioritised_gc =1 ; + bi->chunk_error_strikes++; + +if (bi->chunk_error_strikes >3 ) { + bi->needs_retiring =1 ;/* Too many stikes, so retire */ +yaffs_trace (YAFFS_TRACE_ALWAYS , +"yaffs: Block struck out" ); + + } + } +} + +static void yaffs_handle_chunk_wr_error (struct yaffs_dev *dev,int nand_chunk, +int erased_ok) +{ +int flash_block = nand_chunk / dev->param.chunks_per_block; +struct yaffs_block_info *bi =yaffs_get_block_info (dev, flash_block); + +yaffs_handle_chunk_error (dev, bi); + +if (erased_ok) { +/* Was an actual write failure, +* so mark the block for retirement.*/ + bi->needs_retiring =1 ; +yaffs_trace (YAFFS_TRACE_ERROR |YAFFS_TRACE_BAD_BLOCKS , +"**>> Block %d needs retiring" , flash_block); + } + +/* Delete the chunk */ +yaffs_chunk_del (dev, nand_chunk,1 , __LINE__); +yaffs_skip_rest_of_block (dev); +} + +/* +* Verification code +*/ + +/* +* Simple hash function. Needs to have a reasonable spread +*/ + +static inline int yaffs_hash_fn (int n) +{ + n =abs (n); +return n %YAFFS_NOBJECT_BUCKETS ; +} + +/* +* Access functions to useful fake objects. +* Note that root might have a presence in NAND if permissions are set. +*/ + +struct yaffs_obj *yaffs_root (struct yaffs_dev *dev) +{ +return dev->root_dir; +} + +struct yaffs_obj *yaffs_lost_n_found (struct yaffs_dev *dev) +{ +return dev->lost_n_found; +} + +/* +* Erased NAND checking functions +*/ +
fs/yaffs2/yaffs_guts.c line 331
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+int yaffs_check_ff (u8 *buffer,int n_bytes) +{ +/* Horrible, slow implementation */ +while (n_bytes--) { +if (*buffer !=0xff ) +return 0 ; + buffer++; + } +return 1 ; +}
Maybe something like:
/* Add more 0xffs to taste */
static u8 ff_buf[] = {0xff, 0xff, 0xff, 0xff};
int yaffs_check_ff(u8 *buffer, int n_bytes)
{
int pos = 0;
while (pos < n_bytes) {
int len = min(ARRAY_SIZE, n_bytes);
if (memcmp(buffer + pos, ff_buf, len) != 0)
return 0;
pos += len;
}
return 1;
}Is more effecient?
fs/yaffs2/yaffs_guts.c line 340
Quoted 9 lines
fs/yaffs2/yaffs_guts.c new file +9
-0+ +static int yaffs_check_chunk_erased (struct yaffs_dev *dev,int nand_chunk) +{ +int retval =YAFFS_OK ; +u8 *data =yaffs_get_temp_buffer (dev, __LINE__); +struct yaffs_ext_tags tags; +int result; + + result =yaffs_rd_chunk_tags_nand (dev, nand_chunk, data, &tags);
result is unused?
Quoted 18 lines
fs/yaffs2/yaffs_guts.c new file +18
-0+ +if (tags.ecc_result >YAFFS_ECC_RESULT_NO_ERROR ) + retval =YAFFS_FAIL ; + +if (!yaffs_check_ff (data, dev->data_bytes_per_chunk) || + tags.chunk_used) { +yaffs_trace (YAFFS_TRACE_NANDACCESS , +"Chunk %d not erased" , nand_chunk); + retval =YAFFS_FAIL ; + } + +yaffs_release_temp_buffer (dev, data, __LINE__); + +return retval; + +} + +static int yaffs_verify_chunk_written(struct yaffs_dev *dev,
fs/yaffs2/yaffs_guts.c line 368
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+int nand_chunk, + const u8 *data, + struct yaffs_ext_tags *tags) +{ + int retval = YAFFS_OK; +struct yaffs_ext_tags temp_tags; +u8 *buffer =yaffs_get_temp_buffer (dev, __LINE__); +int result; + + result =yaffs_rd_chunk_tags_nand (dev, nand_chunk, buffer, &temp_tags);
result is unused.
Quoted 238 lines
fs/yaffs2/yaffs_guts.c new file +238
-0+if (memcmp (buffer, data, dev->data_bytes_per_chunk) || + temp_tags.obj_id != tags->obj_id || + temp_tags.chunk_id != tags->chunk_id || + temp_tags.n_bytes != tags->n_bytes) + retval =YAFFS_FAIL ; + +yaffs_release_temp_buffer (dev, buffer, __LINE__); + +return retval; +} + + +int yaffs_check_alloc_available (struct yaffs_dev *dev,int n_chunks) +{ +int reserved_chunks; +int reserved_blocks = dev->param.n_reserved_blocks; +int checkpt_blocks; + + checkpt_blocks =yaffs_calc_checkpt_blocks_required (dev); + + reserved_chunks = + (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; + +return (dev->n_free_chunks > (reserved_chunks + n_chunks)); +} + +static int yaffs_find_alloc_block (struct yaffs_dev *dev) +{ +int i; +struct yaffs_block_info *bi; + +if (dev->n_erased_blocks <1 ) { +/* Hoosterman we've got a problem. +* Can't get space to gc +*/ +yaffs_trace (YAFFS_TRACE_ERROR , +"yaffs tragedy: no more erased blocks" ); + +return -1 ; + } + +/* Find an empty block. */ + +for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { + dev->alloc_block_finder++; +if (dev->alloc_block_finder < dev->internal_start_block + || dev->alloc_block_finder > dev->internal_end_block) { + dev->alloc_block_finder = dev->internal_start_block; + } + + bi =yaffs_get_block_info (dev, dev->alloc_block_finder); + +if (bi->block_state ==YAFFS_BLOCK_STATE_EMPTY ) { + bi->block_state =YAFFS_BLOCK_STATE_ALLOCATING ; + dev->seq_number++; + bi->seq_number = dev->seq_number; + dev->n_erased_blocks--; +yaffs_trace (YAFFS_TRACE_ALLOCATE , +"Allocated block %d, seq %d, %d left" , + dev->alloc_block_finder, dev->seq_number, + dev->n_erased_blocks); +return dev->alloc_block_finder; + } + } + +yaffs_trace (YAFFS_TRACE_ALWAYS , +"yaffs tragedy: no more erased blocks, but there should have been %d" , + dev->n_erased_blocks); + +return -1 ; +} + +static int yaffs_alloc_chunk (struct yaffs_dev *dev,int use_reserver, +struct yaffs_block_info **block_ptr) +{ +int ret_val; +struct yaffs_block_info *bi; + +if (dev->alloc_block <0 ) { +/* Get next block to allocate off */ + dev->alloc_block =yaffs_find_alloc_block (dev); + dev->alloc_page =0 ; + } + +if (!use_reserver && !yaffs_check_alloc_available (dev,1 )) { +/* No space unless we're allowed to use the reserve. */ +return -1 ; + } + +if (dev->n_erased_blocks < dev->param.n_reserved_blocks + && dev->alloc_page ==0 ) +yaffs_trace (YAFFS_TRACE_ALLOCATE ,"Allocating reserve" ); + +/* Next page please.... */ +if (dev->alloc_block >=0 ) { + bi =yaffs_get_block_info (dev, dev->alloc_block); + + ret_val = (dev->alloc_block * dev->param.chunks_per_block) + + dev->alloc_page; + bi->pages_in_use++; +yaffs_set_chunk_bit (dev, dev->alloc_block, dev->alloc_page); + + dev->alloc_page++; + + dev->n_free_chunks--; + +/* If the block is full set the state to full */ +if (dev->alloc_page >= dev->param.chunks_per_block) { + bi->block_state =YAFFS_BLOCK_STATE_FULL ; + dev->alloc_block =-1 ; + } + +if (block_ptr) + *block_ptr = bi; + +return ret_val; + } + +yaffs_trace (YAFFS_TRACE_ERROR , +"!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" ); + +return -1 ; +} + +static int yaffs_get_erased_chunks (struct yaffs_dev *dev) +{ +int n; + + n = dev->n_erased_blocks * dev->param.chunks_per_block; + +if (dev->alloc_block >0 ) + n += (dev->param.chunks_per_block - dev->alloc_page); + +return n; + +} + +/* +* yaffs_skip_rest_of_block() skips over the rest of the allocation block +* if we don't want to write to it. +*/ +void yaffs_skip_rest_of_block (struct yaffs_dev *dev) +{ +struct yaffs_block_info *bi; + +if (dev->alloc_block >0 ) { + bi =yaffs_get_block_info (dev, dev->alloc_block); +if (bi->block_state ==YAFFS_BLOCK_STATE_ALLOCATING ) { + bi->block_state =YAFFS_BLOCK_STATE_FULL ; + dev->alloc_block =-1 ; + } + } +} + +static int yaffs_write_new_chunk (struct yaffs_dev *dev, +const u8 *data, +struct yaffs_ext_tags *tags,int use_reserver) +{ +int attempts =0 ; +int write_ok =0 ; +int chunk; + +yaffs2_checkpt_invalidate (dev); + + do { + struct yaffs_block_info *bi = 0; + int erased_ok = 0; + + chunk = yaffs_alloc_chunk(dev, use_reserver, &bi); + if (chunk < 0) { + /* no space */ + break; + } + + /* First check this chunk is erased, if it needs + * checking. The checking policy (unless forced + * always on) is as follows: + * + * Check the first page we try to write in a block. + * If the check passes then we don't need to check any + * more. If the check fails, we check again... + * If the block has been erased, we don't need to check. + * + * However, if the block has been prioritised for gc, + * then we think there might be something odd about + * this block and stop using it. + * + * Rationale: We should only ever see chunks that have + * not been erased if there was a partially written + * chunk due to power loss. This checking policy should + * catch that case with very few checks and thus save a + * lot of checks that are most likely not needed. + * + * Mods to the above + * If an erase check fails or the write fails we skip the + * rest of the block. + */ + + /* let's give it a try */ + attempts++; + + if (dev->param.always_check_erased) + bi->skip_erased_check = 0; + + if (!bi->skip_erased_check) { + erased_ok = yaffs_check_chunk_erased(dev, chunk); + if (erased_ok != YAFFS_OK) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>> yaffs chunk %d was not erased", + chunk); + + /* If not erased, delete this one, + * skip rest of block and + * try another chunk */ + yaffs_chunk_del(dev, chunk, 1, __LINE__); + yaffs_skip_rest_of_block(dev); + continue; + } + } + + write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); + + if (!bi->skip_erased_check) + write_ok = + yaffs_verify_chunk_written(dev, chunk, data, tags); + + if (write_ok != YAFFS_OK) { + /* Clean up aborted write, skip to next block and + * try another chunk */ + yaffs_handle_chunk_wr_error(dev, chunk, erased_ok); + continue; + } + + bi->skip_erased_check = 1; + + /* Copy the data into the robustification buffer */ + yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); +
fs/yaffs2/yaffs_guts.c line 616
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+ }while (write_ok !=YAFFS_OK && + (yaffs_wr_attempts <=0 || attempts <= yaffs_wr_attempts)); + +if (!write_ok) + chunk =-1 ; + +if (attempts >1 ) { +yaffs_trace (YAFFS_TRACE_ERROR , +"**>> yaffs write required %d attempts" , + attempts);
Printing the block/page number here would be useful.
Quoted 53 lines
fs/yaffs2/yaffs_guts.c new file +53
-0+ dev->n_retired_writes += (attempts -1 ); + } + +return chunk; +} + +/* +* Block retiring for handling a broken block. +*/ + +static void yaffs_retire_block (struct yaffs_dev *dev,int flash_block) +{ +struct yaffs_block_info *bi =yaffs_get_block_info (dev, flash_block); + +yaffs2_checkpt_invalidate (dev); + +yaffs2_clear_oldest_dirty_seq (dev, bi); + +if (yaffs_mark_bad (dev, flash_block) !=YAFFS_OK ) { +if (yaffs_erase_block (dev, flash_block) !=YAFFS_OK ) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"yaffs: Failed to mark bad and erase block %d" , + flash_block); + }else { +struct yaffs_ext_tags tags; +int chunk_id = + flash_block * dev->param.chunks_per_block; + +u8 *buffer =yaffs_get_temp_buffer (dev, __LINE__); + +memset (buffer,0xff , dev->data_bytes_per_chunk); +yaffs_init_tags (&tags); + tags.seq_number =YAFFS_SEQUENCE_BAD_BLOCK ; +if (dev->param.write_chunk_tags_fn (dev, chunk_id - + dev->chunk_offset, + buffer, + &tags) !=YAFFS_OK ) +yaffs_trace (YAFFS_TRACE_ALWAYS , +"yaffs: Failed to write bad block marker to block %d" , + flash_block); + +yaffs_release_temp_buffer (dev, buffer, __LINE__); + } + } + + bi->block_state =YAFFS_BLOCK_STATE_DEAD ; + bi->gc_prioritise =0 ; + bi->needs_retiring =0 ; + + dev->n_retired_blocks++; +} + +/*---------------- Name handling functions ------------*/
fs/yaffs2/yaffs_guts.c line 679
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+ +static u16 yaffs_calc_name_sum (const YCHAR *name) +{ +u16 sum =0 ; +u16 i =1 ; + +if (!name) +return 0 ; + +while ((*name) && i < (YAFFS_MAX_NAME_LENGTH /2 )) {
Don't need parens around *name.
fs/yaffs2/yaffs_guts.c line 691
Quoted 12 lines
fs/yaffs2/yaffs_guts.c new file +12
-0+ +/* 0x1f mask is case insensitive */ + sum += ((*name) &0x1f ) * i; + i++; + name++; + } +return sum; +} + +void yaffs_set_obj_name (struct yaffs_obj *obj,const YCHAR * name) +{ +#ifndef CONFIG_YAFFS_NO_SHORT_NAMES
This appears not to be a selectable config option anymore. Should be removed.
Quoted 5 lines
fs/yaffs2/yaffs_guts.c new file +5
-0+memset (obj->short_name,0 ,sizeof (obj->short_name)); +if (name && +strnlen (name,YAFFS_SHORT_NAME_LENGTH +1 ) <= +YAFFS_SHORT_NAME_LENGTH ) +strcpy (obj->short_name, name);
fs/yaffs2/yaffs_guts.c line 706
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+ else + obj->short_name[0] = _Y('\0'); +#endif + obj->sum =yaffs_calc_name_sum (name); +} + +void yaffs_set_obj_name_from_oh (struct yaffs_obj *obj, +const struct yaffs_obj_hdr *oh) +{ +#ifdef CONFIG_YAFFS_AUTO_UNICODE
Same here. Remove this ifdef.
Quoted 22 lines
fs/yaffs2/yaffs_guts.c new file +22
-0+YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH +1 ]; +memset (tmp_name,0 ,sizeof (tmp_name)); +yaffs_load_name_from_oh (obj->my_dev, tmp_name, oh->name, +YAFFS_MAX_NAME_LENGTH +1 ); +yaffs_set_obj_name (obj, tmp_name); +#else +yaffs_set_obj_name (obj, oh->name); +#endif +} + +/*-------------------- TNODES ------------------- + +* List of spare tnodes +* The list is hooked together using the first pointer +* in the tnode. +*/ + +struct yaffs_tnode *yaffs_get_tnode (struct yaffs_dev *dev) +{ +struct yaffs_tnode *tn =yaffs_alloc_raw_tnode (dev); + +if (tn) {
fs/yaffs2/yaffs_guts.c line 738
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+memset (tn,0 , dev->tnode_size); + dev->n_tnodes++; + } + + dev->checkpoint_blocks_required =0 ;/* force recalculation */ + +return tn; +} + +/* FreeTnode frees up a tnode and puts it back on the free list */
Funciton name in the comment is wrong.
Quoted 72 lines
fs/yaffs2/yaffs_guts.c new file +72
-0+static void yaffs_free_tnode (struct yaffs_dev *dev,struct yaffs_tnode *tn) +{ +yaffs_free_raw_tnode (dev, tn); + dev->n_tnodes--; + dev->checkpoint_blocks_required =0 ;/* force recalculation */ +} + +static void yaffs_deinit_tnodes_and_objs (struct yaffs_dev *dev) +{ +yaffs_deinit_raw_tnodes_and_objs (dev); + dev->n_obj =0 ; + dev->n_tnodes =0 ; +} + +void yaffs_load_tnode_0 (struct yaffs_dev *dev,struct yaffs_tnode *tn, +unsigned pos,unsigned val) +{ +u32 *map = (u32 *) tn; +u32 bit_in_map; +u32 bit_in_word; +u32 word_in_map; +u32 mask; + + pos &=YAFFS_TNODES_LEVEL0_MASK ; + val >>= dev->chunk_grp_bits; + + bit_in_map = pos * dev->tnode_width; + word_in_map = bit_in_map /32 ; + bit_in_word = bit_in_map & (32 -1 ); + + mask = dev->tnode_mask << bit_in_word; + + map[word_in_map] &= ~mask; + map[word_in_map] |= (mask & (val << bit_in_word)); + +if (dev->tnode_width > (32 - bit_in_word)) { + bit_in_word = (32 - bit_in_word); + word_in_map++; + mask = + dev->tnode_mask >> bit_in_word; + map[word_in_map] &= ~mask; + map[word_in_map] |= (mask & (val >> bit_in_word)); + } +} + +u32 yaffs_get_group_base (struct yaffs_dev *dev,struct yaffs_tnode *tn, +unsigned pos) +{ +u32 *map = (u32 *) tn; +u32 bit_in_map; +u32 bit_in_word; +u32 word_in_map; +u32 val; + + pos &=YAFFS_TNODES_LEVEL0_MASK ; + + bit_in_map = pos * dev->tnode_width; + word_in_map = bit_in_map /32 ; + bit_in_word = bit_in_map & (32 -1 ); + + val = map[word_in_map] >> bit_in_word; + +if (dev->tnode_width > (32 - bit_in_word)) { + bit_in_word = (32 - bit_in_word); + word_in_map++; + val |= (map[word_in_map] << bit_in_word); + } + + val &= dev->tnode_mask; + val <<= dev->chunk_grp_bits; + +return val;
fs/yaffs2/yaffs_guts.c line 820
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+} + +/* ------------------- End of individual tnode manipulation -----------------*/ + +/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ +* The look up tree is represented by the top tnode and the number of top_level +* in the tree. 0 means only the level 0 tnode is in the tree. +*/ + +/* FindLevel0Tnode finds the level 0 tnode, if one exists. */
Function name in comment is wrong.
fs/yaffs2/yaffs_guts.c line 830
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+struct yaffs_tnode *yaffs_find_tnode_0 (struct yaffs_dev *dev, +struct yaffs_file_var *file_struct, +u32 chunk_id) +{ +struct yaffs_tnode *tn = file_struct->top; +u32 i; +int required_depth; +int level = file_struct->top_level; + + dev = dev;
Delete this line.
Quoted 192 lines
fs/yaffs2/yaffs_guts.c new file +192
-0+ +/* Check sane level and chunk Id */ +if (level <0 || level >YAFFS_TNODES_MAX_LEVEL ) +return NULL ; + +if (chunk_id >YAFFS_MAX_CHUNK_ID ) +return NULL ; + +/* First check we're tall enough (ie enough top_level) */ + + i = chunk_id >>YAFFS_TNODES_LEVEL0_BITS ; + required_depth =0 ; +while (i) { + i >>=YAFFS_TNODES_INTERNAL_BITS ; + required_depth++; + } + +if (required_depth > file_struct->top_level) +return NULL ;/* Not tall enough, so we can't find it */ + +/* Traverse down to level 0 */ +while (level >0 && tn) { + tn = tn->internal[(chunk_id >> + (YAFFS_TNODES_LEVEL0_BITS + + (level -1 ) * +YAFFS_TNODES_INTERNAL_BITS )) & +YAFFS_TNODES_INTERNAL_MASK ]; + level--; + } + +return tn; +} + +/* add_find_tnode_0 finds the level 0 tnode if it exists, +* otherwise first expands the tree. +* This happens in two steps: +* 1. If the tree isn't tall enough, then make it taller. +* 2. Scan down the tree towards the level 0 tnode adding tnodes if required. +* +* Used when modifying the tree. +* +* If the tn argument is NULL, then a fresh tnode will be added otherwise the +* specified tn will be plugged into the ttree. +*/ + +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 required_depth; +int i; +int l; +struct yaffs_tnode *tn; +u32 x; + +/* Check sane level and page Id */ +if (file_struct->top_level <0 || + file_struct->top_level >YAFFS_TNODES_MAX_LEVEL ) +return NULL ; + +if (chunk_id >YAFFS_MAX_CHUNK_ID ) +return NULL ; + +/* First check we're tall enough (ie enough top_level) */ + + x = chunk_id >>YAFFS_TNODES_LEVEL0_BITS ; + required_depth =0 ; +while (x) { + x >>=YAFFS_TNODES_INTERNAL_BITS ; + required_depth++; + } + +if (required_depth > file_struct->top_level) { +/* Not tall enough, gotta make the tree taller */ +for (i = file_struct->top_level; i < required_depth; i++) { + + tn =yaffs_get_tnode (dev); + +if (tn) { + tn->internal[0 ] = file_struct->top; + file_struct->top = tn; + file_struct->top_level++; + }else { +yaffs_trace (YAFFS_TRACE_ERROR , +"yaffs: no more tnodes" ); +return NULL ; + } + } + } + +/* Traverse down to level 0, adding anything we need */ + + l = file_struct->top_level; + tn = file_struct->top; + +if (l >0 ) { +while (l >0 && tn) { + x = (chunk_id >> + (YAFFS_TNODES_LEVEL0_BITS + + (l -1 ) *YAFFS_TNODES_INTERNAL_BITS )) & +YAFFS_TNODES_INTERNAL_MASK ; + +if ((l >1 ) && !tn->internal[x]) { +/* Add missing non-level-zero tnode */ + tn->internal[x] =yaffs_get_tnode (dev); +if (!tn->internal[x]) +return NULL ; + }else if (l ==1 ) { +/* Looking from level 1 at level 0 */ +if (passed_tn) { +/* If we already have one, release it */ +if (tn->internal[x]) +yaffs_free_tnode (dev, + tn->internal[x]); + tn->internal[x] = passed_tn; + + }else if (!tn->internal[x]) { +/* Don't have one, none passed in */ + tn->internal[x] =yaffs_get_tnode (dev); +if (!tn->internal[x]) +return NULL ; + } + } + + tn = tn->internal[x]; + l--; + } + }else { +/* top is level 0 */ +if (passed_tn) { +memcpy (tn, passed_tn, + (dev->tnode_width *YAFFS_NTNODES_LEVEL0 ) /8 ); +yaffs_free_tnode (dev, passed_tn); + } + } + +return tn; +} + +static int yaffs_tags_match (const struct yaffs_ext_tags *tags,int obj_id, +int chunk_obj) +{ +return (tags->chunk_id == chunk_obj && + tags->obj_id == obj_id && + !tags->is_deleted) ?1 :0 ; + +} + +static int yaffs_find_chunk_in_group (struct yaffs_dev *dev,int the_chunk, +struct yaffs_ext_tags *tags,int obj_id, +int inode_chunk) +{ +int j; + +for (j =0 ; the_chunk && j < dev->chunk_grp_size; j++) { +if (yaffs_check_chunk_bit + (dev, the_chunk / dev->param.chunks_per_block, + the_chunk % dev->param.chunks_per_block)) { + +if (dev->chunk_grp_size ==1 ) +return the_chunk; +else { +yaffs_rd_chunk_tags_nand (dev, the_chunk,NULL , + tags); +if (yaffs_tags_match (tags, + obj_id, inode_chunk)) { +/* found it; */ +return the_chunk; + } + } + } + the_chunk++; + } +return -1 ; +} + +static int yaffs_find_chunk_in_file (struct yaffs_obj *in,int inode_chunk, +struct yaffs_ext_tags *tags) +{ +/*Get the Tnode, then get the level 0 offset chunk offset */ +struct yaffs_tnode *tn; +int the_chunk =-1 ; +struct yaffs_ext_tags local_tags; +int ret_val =-1 ; +struct yaffs_dev *dev = in->my_dev; + +if (!tags) { +/* Passed a NULL, so use our own tags space */ + tags = &local_tags; + } +
fs/yaffs2/yaffs_guts.c line 1032
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+ tn =yaffs_find_tnode_0 (dev, &in->variant.file_variant, inode_chunk); + +if (!tn) +return ret_val; + + the_chunk =yaffs_get_group_base (dev, tn, inode_chunk); + + ret_val =yaffs_find_chunk_in_group (dev, the_chunk, tags, in->obj_id, + inode_chunk); +return ret_val;
You don't need the intermediate ret_val here, just do the return directly.
Quoted 68 lines
fs/yaffs2/yaffs_guts.c new file +68
-0+} + +static int yaffs_find_del_file_chunk (struct yaffs_obj *in,int inode_chunk, +struct yaffs_ext_tags *tags) +{ +/* Get the Tnode, then get the level 0 offset chunk offset */ +struct yaffs_tnode *tn; +int the_chunk =-1 ; +struct yaffs_ext_tags local_tags; +struct yaffs_dev *dev = in->my_dev; +int ret_val =-1 ; + +if (!tags) { +/* Passed a NULL, so use our own tags space */ + tags = &local_tags; + } + + tn =yaffs_find_tnode_0 (dev, &in->variant.file_variant, inode_chunk); + +if (!tn) +return ret_val; + + the_chunk =yaffs_get_group_base (dev, tn, inode_chunk); + + ret_val =yaffs_find_chunk_in_group (dev, the_chunk, tags, in->obj_id, + inode_chunk); + +/* Delete the entry in the filestructure (if found) */ +if (ret_val !=-1 ) +yaffs_load_tnode_0 (dev, tn, inode_chunk,0 ); + +return ret_val; +} + +int yaffs_put_chunk_in_file (struct yaffs_obj *in,int inode_chunk, +int nand_chunk,int in_scan) +{ +/* NB in_scan is zero unless scanning. +* For forward scanning, in_scan is > 0; +* for backward scanning in_scan is < 0 +* +* nand_chunk = 0 is a dummy insert to make sure the tnodes are there. +*/ + +struct yaffs_tnode *tn; +struct yaffs_dev *dev = in->my_dev; +int existing_cunk; +struct yaffs_ext_tags existing_tags; +struct yaffs_ext_tags new_tags; +unsigned existing_serial, new_serial; + +if (in->variant_type !=YAFFS_OBJECT_TYPE_FILE ) { +/* Just ignore an attempt at putting a chunk into a non-file +* during scanning. +* If it is not during Scanning then something went wrong! +*/ +if (!in_scan) { +yaffs_trace (YAFFS_TRACE_ERROR , +"yaffs tragedy:attempt to put data chunk into a non-file" + ); +BUG (); + } + +yaffs_chunk_del (dev, nand_chunk,1 , __LINE__); +return YAFFS_OK ; + } + + tn = yaffs_add_find_tnode_0(dev,
fs/yaffs2/yaffs_guts.c line 1110
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+ &in->variant.file_variant, + inode_chunk, NULL); +if (!tn) +return YAFFS_FAIL ; + +if (!nand_chunk) +/* Dummy insert, bail now */ +return YAFFS_OK ; + + existing_cunk =yaffs_get_group_base (dev, tn, inode_chunk);
s/cunk/chunk/ ? I'm too paranoid to google and see if 'cunk' is a real word :-).
Quoted 97 lines
fs/yaffs2/yaffs_guts.c new file +97
-0+ +if (in_scan !=0 ) { +/* If we're scanning then we need to test for duplicates +* NB This does not need to be efficient since it should only +* happen when the power fails during a write, then only one +* chunk should ever be affected. +* +* Correction for YAFFS2: This could happen quite a lot and we +* need to think about efficiency! TODO +* Update: For backward scanning we don't need to re-read tags +* so this is quite cheap. +*/ + +if (existing_cunk >0 ) { +/* NB Right now existing chunk will not be real +* chunk_id if the chunk group size > 1 +* thus we have to do a FindChunkInFile to get the +* real chunk id. +* +* We have a duplicate now we need to decide which +* one to use: +* +* Backwards scanning YAFFS2: The old one is what +* we use, dump the new one. +* YAFFS1: Get both sets of tags and compare serial +* numbers. +*/ + +if (in_scan >0 ) { +/* Only do this for forward scanning */ +yaffs_rd_chunk_tags_nand (dev, + nand_chunk, +NULL , &new_tags); + +/* Do a proper find */ + existing_cunk = +yaffs_find_chunk_in_file (in, inode_chunk, + &existing_tags); + } + +if (existing_cunk <=0 ) { +/*Hoosterman - how did this happen? */ + +yaffs_trace (YAFFS_TRACE_ERROR , +"yaffs tragedy: existing chunk < 0 in scan" + ); + + } + +/* NB The deleted flags should be false, otherwise +* the chunks will not be loaded during a scan +*/ + +if (in_scan >0 ) { + new_serial = new_tags.serial_number; + existing_serial = existing_tags.serial_number; + } + +if ((in_scan >0 ) && + (existing_cunk <=0 || + ((existing_serial +1 ) &3 ) == new_serial)) { +/* Forward scanning. +* Use new +* Delete the old one and drop through to +* update the tnode +*/ +yaffs_chunk_del (dev, existing_cunk,1 , + __LINE__); + }else { +/* Backward scanning or we want to use the +* existing one +* Delete the new one and return early so that +* the tnode isn't changed +*/ +yaffs_chunk_del (dev, nand_chunk,1 , __LINE__); +return YAFFS_OK ; + } + } + + } + +if (existing_cunk ==0 ) + in->n_data_chunks++; + +yaffs_load_tnode_0 (dev, tn, inode_chunk, nand_chunk); + +return YAFFS_OK ; +} + +static void yaffs_soft_del_chunk (struct yaffs_dev *dev,int chunk) +{ +struct yaffs_block_info *the_block; +unsigned block_no; + +yaffs_trace (YAFFS_TRACE_DELETION ,"soft delete chunk %d" , chunk); + + block_no = chunk / dev->param.chunks_per_block;
fs/yaffs2/yaffs_guts.c line 1217
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+ the_block =yaffs_get_block_info (dev, block_no); +if (the_block) { + the_block->soft_del_pages++; + dev->n_free_chunks++; +yaffs2_update_oldest_dirty_seq (dev, block_no, the_block); + } +} + +/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all +* the chunks in the file.
Old function name in comment.
Quoted 25 lines
fs/yaffs2/yaffs_guts.c new file +25
-0+* All soft deleting does is increment the block's softdelete count and pulls +* the chunk out of the tnode. +* Thus, essentially this is the same as DeleteWorker except that the chunks +* are soft deleted. +*/ + +static int yaffs_soft_del_worker (struct yaffs_obj *in,struct yaffs_tnode *tn, +u32 level,int chunk_offset) +{ +int i; +int the_chunk; +int all_done =1 ; +struct yaffs_dev *dev = in->my_dev; + +if (!tn) +return 1 ; + +if (level >0 ) { +for (i =YAFFS_NTNODES_INTERNAL -1 ; + all_done && i >=0 ; + i--) { +if (tn->internal[i]) { + all_done = + yaffs_soft_del_worker(in, + tn->internal[i],
fs/yaffs2/yaffs_guts.c line 1252
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+ level -1 , + (chunk_offset << +YAFFS_TNODES_INTERNAL_BITS ) + + i); +if (all_done) { +yaffs_free_tnode (dev, + tn->internal[i]); + tn->internal[i] =NULL ; + }else { +/* Can this happen? */
Remove the empty else, or put a warning/bug in it.
fs/yaffs2/yaffs_guts.c line 1256
Quoted 4 lines
fs/yaffs2/yaffs_guts.c new file +4
-0+ } + } + } +return (all_done) ?1 :0 ;
Parens are unnecessary.
fs/yaffs2/yaffs_guts.c line 1260
Quoted 4 lines
fs/yaffs2/yaffs_guts.c new file +4
-0+ } + +/* level 0 */ +for (i =YAFFS_NTNODES_LEVEL0 -1 ; i >=0 ; i--) {
Idenentation looks mucked up here. Delete the space before the for.
Quoted 197 lines
fs/yaffs2/yaffs_guts.c new file +197
-0+ the_chunk =yaffs_get_group_base (dev, tn, i); +if (the_chunk) { +yaffs_soft_del_chunk (dev, the_chunk); +yaffs_load_tnode_0 (dev, tn, i,0 ); + } + } +return 1 ; +} + +static void yaffs_remove_obj_from_dir (struct yaffs_obj *obj) +{ +struct yaffs_dev *dev = obj->my_dev; +struct yaffs_obj *parent; + +yaffs_verify_obj_in_dir (obj); + parent = obj->parent; + +yaffs_verify_dir (parent); + +if (dev && dev->param.remove_obj_fn) + dev->param.remove_obj_fn (obj); + +list_del_init (&obj->siblings); + obj->parent =NULL ; + +yaffs_verify_dir (parent); +} + +void yaffs_add_obj_to_dir (struct yaffs_obj *directory,struct yaffs_obj *obj) +{ +if (!directory) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"tragedy: Trying to add an object to a null pointer directory" + ); +BUG (); +return ; + } +if (directory->variant_type !=YAFFS_OBJECT_TYPE_DIRECTORY ) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"tragedy: Trying to add an object to a non-directory" + ); +BUG (); + } + +if (obj->siblings.prev ==NULL ) { +/* Not initialised */ +BUG (); + } + +yaffs_verify_dir (directory); + +yaffs_remove_obj_from_dir (obj); + +/* Now add it */ +list_add (&obj->siblings, &directory->variant.dir_variant.children); + obj->parent = directory; + +if (directory == obj->my_dev->unlinked_dir + || directory == obj->my_dev->del_dir) { + obj->unlinked =1 ; + obj->my_dev->n_unlinked_files++; + obj->rename_allowed =0 ; + } + +yaffs_verify_dir (directory); +yaffs_verify_obj_in_dir (obj); +} + +static int yaffs_change_obj_name (struct yaffs_obj *obj, +struct yaffs_obj *new_dir, +const YCHAR *new_name,int force,int shadows) +{ +int unlink_op; +int del_op; +struct yaffs_obj *existing_target; + +if (new_dir ==NULL ) + new_dir = obj->parent;/* use the old directory */ + +if (new_dir->variant_type !=YAFFS_OBJECT_TYPE_DIRECTORY ) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"tragedy: yaffs_change_obj_name: new_dir is not a directory" + ); +BUG (); + } + + unlink_op = (new_dir == obj->my_dev->unlinked_dir); + del_op = (new_dir == obj->my_dev->del_dir); + + existing_target =yaffs_find_by_name (new_dir, new_name); + +/* If the object is a file going into the unlinked directory, +* then it is OK to just stuff it in since duplicate names are OK. +* else only proceed if the new name does not exist and we're putting +* it into a directory. +*/ +if (!(unlink_op || del_op || force || + shadows >0 || !existing_target) || + new_dir->variant_type !=YAFFS_OBJECT_TYPE_DIRECTORY ) +return YAFFS_FAIL ; + +yaffs_set_obj_name (obj, new_name); + obj->dirty =1 ; +yaffs_add_obj_to_dir (new_dir, obj); + +if (unlink_op) + obj->unlinked =1 ; + +/* If it is a deletion then we mark it as a shrink for gc */ +if (yaffs_update_oh (obj, new_name,0 , del_op, shadows,NULL ) >=0 ) +return YAFFS_OK ; + +return YAFFS_FAIL ; +} + +/*------------------------ Short Operations Cache ------------------------------ +* In many situations where there is no high level buffering a lot of +* reads might be short sequential reads, and a lot of writes may be short +* sequential writes. eg. scanning/writing a jpeg file. +* In these cases, a short read/write cache can provide a huge perfomance +* benefit with dumb-as-a-rock code. +* In Linux, the page cache provides read buffering and the short op cache +* provides write buffering. +* +* There are a small number (~10) of cache chunks per device so that we don't +* need a very intelligent search. +*/ + +static int yaffs_obj_cache_dirty (struct yaffs_obj *obj) +{ +struct yaffs_dev *dev = obj->my_dev; +int i; +struct yaffs_cache *cache; +int n_caches = obj->my_dev->param.n_caches; + +for (i =0 ; i < n_caches; i++) { + cache = &dev->cache[i]; +if (cache->object == obj && cache->dirty) +return 1 ; + } + +return 0 ; +} + +static void yaffs_flush_file_cache (struct yaffs_obj *obj) +{ +struct yaffs_dev *dev = obj->my_dev; +int lowest =-99 ;/* Stop compiler whining. */ +int i; +struct yaffs_cache *cache; +int chunk_written =0 ; +int n_caches = obj->my_dev->param.n_caches; + +if (n_caches <1 ) +return ; +do { + cache =NULL ; + +/* Find the lowest dirty chunk for this object */ +for (i =0 ; i < n_caches; i++) { +if (dev->cache[i].object == obj && + dev->cache[i].dirty) { +if (!cache || + dev->cache[i].chunk_id < lowest) { + cache = &dev->cache[i]; + lowest = cache->chunk_id; + } + } + } + +if (cache && !cache->locked) { +/* Write it out and free it up */ + chunk_written = +yaffs_wr_data_obj (cache->object, + cache->chunk_id, + cache->data, + cache->n_bytes,1 ); + cache->dirty =0 ; + cache->object =NULL ; + } + }while (cache && chunk_written >0 ); + +if (cache) +/* Hoosterman, disk full while writing cache out. */ +yaffs_trace (YAFFS_TRACE_ERROR , +"yaffs tragedy: no space during cache write" ); +} + +/*yaffs_flush_whole_cache(dev) +* +* +*/ + +void yaffs_flush_whole_cache (struct yaffs_dev *dev) +{ +struct yaffs_obj *obj; +int n_caches = dev->param.n_caches;
fs/yaffs2/yaffs_guts.c line 1467
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+int i; + +/* Find a dirty object in the cache and flush it... +* until there are no further dirty objects. +*/ + do { + obj = NULL; + for (i = 0; i < n_caches && !obj; i++) { + if (dev->cache[i].object && dev->cache[i].dirty) + obj = dev->cache[i].object;
Can we break out of the loop here?
Quoted 87 lines
fs/yaffs2/yaffs_guts.c new file +87
-0+ } +if (obj) +yaffs_flush_file_cache (obj); + }while (obj); + +} + +/* Grab us a cache chunk for use. +* First look for an empty one. +* Then look for the least recently used non-dirty one. +* Then look for the least recently used dirty one...., flush and look again. +*/ +static struct yaffs_cache *yaffs_grab_chunk_worker (struct yaffs_dev *dev) +{ +int i; + +if (dev->param.n_caches >0 ) { +for (i =0 ; i < dev->param.n_caches; i++) { +if (!dev->cache[i].object) +return &dev->cache[i]; + } + } +return NULL ; +} + +static struct yaffs_cache *yaffs_grab_chunk_cache (struct yaffs_dev *dev) +{ +struct yaffs_cache *cache; +struct yaffs_obj *the_obj; +int usage; +int i; +int pushout; + +if (dev->param.n_caches <1 ) +return NULL ; + +/* Try find a non-dirty one... */ + + cache =yaffs_grab_chunk_worker (dev); + +if (!cache) { +/* They were all dirty, find the LRU object and flush +* its cache, then find again. +* NB what's here is not very accurate, +* we actually flush the object with the LRU chunk. +*/ + +/* With locking we can't assume we can use entry zero, +* Set the_obj to a valid pointer for Coverity. */ + the_obj = dev->cache[0 ].object; + usage =-1 ; + cache =NULL ; + pushout =-1 ; + +for (i =0 ; i < dev->param.n_caches; i++) { +if (dev->cache[i].object && + !dev->cache[i].locked && + (dev->cache[i].last_use < usage || + !cache)) { + usage = dev->cache[i].last_use; + the_obj = dev->cache[i].object; + cache = &dev->cache[i]; + pushout = i; + } + } + +if (!cache || cache->dirty) { +/* Flush and try again */ +yaffs_flush_file_cache (the_obj); + cache =yaffs_grab_chunk_worker (dev); + } + } +return cache; +} + +/* Find a cached chunk */ +static struct yaffs_cache *yaffs_find_chunk_cache (const struct yaffs_obj *obj, +int chunk_id) +{ +struct yaffs_dev *dev = obj->my_dev; +int i; + +if (dev->param.n_caches <1 ) +return NULL ; + +for (i =0 ; i < dev->param.n_caches; i++) { + if (dev->cache[i].object == obj &&
fs/yaffs2/yaffs_guts.c line 1564
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+ dev->cache[i].chunk_id == chunk_id) { + dev->cache_hits++; + +return &dev->cache[i]; + } + } +return NULL ; +} + +/* Mark the chunk for the least recently used algorithym */
Typo "algorithym".
fs/yaffs2/yaffs_guts.c line 1574
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+static void yaffs_use_cache (struct yaffs_dev *dev,struct yaffs_cache *cache, +int is_write) +{ +int i; + +if (dev->param.n_caches <1 ) +return ; + +if (dev->cache_last_use <0 || + dev->cache_last_use >100000000 ) {
Should possibly be a define to make it more clear what the big magic number means.
Quoted 50 lines
fs/yaffs2/yaffs_guts.c new file +50
-0+/* Reset the cache usages */ +for (i =1 ; i < dev->param.n_caches; i++) + dev->cache[i].last_use =0 ; + + dev->cache_last_use =0 ; + } + dev->cache_last_use++; + cache->last_use = dev->cache_last_use; + +if (is_write) + cache->dirty =1 ; +} + +/* Invalidate a single cache page. +* Do this when a whole page gets written, +* ie the short cache for this page is no longer valid. +*/ +static void yaffs_invalidate_chunk_cache (struct yaffs_obj *object,int chunk_id) +{ +struct yaffs_cache *cache; + +if (object->my_dev->param.n_caches >0 ) { + cache =yaffs_find_chunk_cache (object, chunk_id); + +if (cache) + cache->object =NULL ; + } +} + +/* Invalidate all the cache pages associated with this object +* Do this whenever ther file is deleted or resized. +*/ +static void yaffs_invalidate_whole_cache (struct yaffs_obj *in) +{ +int i; +struct yaffs_dev *dev = in->my_dev; + +if (dev->param.n_caches >0 ) { +/* Invalidate it. */ +for (i =0 ; i < dev->param.n_caches; i++) { +if (dev->cache[i].object == in) + dev->cache[i].object =NULL ; + } + } +} + +static void yaffs_unhash_obj (struct yaffs_obj *obj) +{ +int bucket; +struct yaffs_dev *dev = obj->my_dev;
fs/yaffs2/yaffs_guts.c line 1634
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+ +/* If it is still linked into the bucket list, free from the list */ +if (!list_empty (&obj->hash_link)) { +list_del_init (&obj->hash_link); + bucket =yaffs_hash_fn (obj->obj_id); + dev->obj_bucket[bucket].count--; + } +} + +/* FreeObject frees up a Object and puts it back on the free list */
Old function name in comment.
Quoted 30 lines
fs/yaffs2/yaffs_guts.c new file +30
-0+static void yaffs_free_obj (struct yaffs_obj *obj) +{ +struct yaffs_dev *dev; + +if (!obj) { +BUG (); +return ; + } + dev = obj->my_dev; +yaffs_trace (YAFFS_TRACE_OS ,"FreeObject %p inode %p" , + obj, obj->my_inode); +if (obj->parent) +BUG (); +if (!list_empty (&obj->siblings)) +BUG (); + +if (obj->my_inode) { +/* We're still hooked up to a cached inode. +* Don't delete now, but mark for later deletion +*/ + obj->defered_free =1 ; +return ; + } + +yaffs_unhash_obj (obj); + +yaffs_free_raw_obj (dev, obj); + dev->n_obj--; + dev->checkpoint_blocks_required =0 ;/* force recalculation */ +}
fs/yaffs2/yaffs_guts.c line 1674
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+ +void yaffs_handle_defered_free (struct yaffs_obj *obj) +{ +if (obj->defered_free) +yaffs_free_obj (obj); +} + +static int yaffs_generic_obj_del (struct yaffs_obj *in) +{ +/* Iinvalidate the file's data in the cache, without flushing. */
Typo "Iinvalidate".
Quoted 182 lines
fs/yaffs2/yaffs_guts.c new file +182
-0+yaffs_invalidate_whole_cache (in); + +if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { +/* Move to unlinked directory so we have a deletion record */ +yaffs_change_obj_name (in, in->my_dev->del_dir,_Y ("deleted" ),0 , +0 ); + } + +yaffs_remove_obj_from_dir (in); +yaffs_chunk_del (in->my_dev, in->hdr_chunk,1 , __LINE__); + in->hdr_chunk =0 ; + +yaffs_free_obj (in); +return YAFFS_OK ; + +} + +static void yaffs_soft_del_file (struct yaffs_obj *obj) +{ +if (!obj->deleted || + obj->variant_type !=YAFFS_OBJECT_TYPE_FILE || + obj->soft_del) +return ; + +if (obj->n_data_chunks <=0 ) { +/* Empty file with no duplicate object headers, +* just delete it immediately */ +yaffs_free_tnode (obj->my_dev, obj->variant.file_variant.top); + obj->variant.file_variant.top =NULL ; +yaffs_trace (YAFFS_TRACE_TRACING , +"yaffs: Deleting empty file %d" , + obj->obj_id); +yaffs_generic_obj_del (obj); + }else { +yaffs_soft_del_worker (obj, + obj->variant.file_variant.top, + obj->variant. + file_variant.top_level,0 ); + obj->soft_del =1 ; + } +} + +/* Pruning removes any part of the file structure tree that is beyond the +* bounds of the file (ie that does not point to chunks). +* +* A file should only get pruned when its size is reduced. +* +* Before pruning, the chunks must be pulled from the tree and the +* level 0 tnode entries must be zeroed out. +* Could also use this for file deletion, but that's probably better handled +* by a special case. +* +* This function is recursive. For levels > 0 the function is called again on +* any sub-tree. For level == 0 we just check if the sub-tree has data. +* If there is no data in a subtree then it is pruned. +*/ + +static struct yaffs_tnode *yaffs_prune_worker (struct yaffs_dev *dev, +struct yaffs_tnode *tn,u32 level, +int del0) +{ +int i; +int has_data; + +if (!tn) +return tn; + + has_data =0 ; + +if (level >0 ) { +for (i =0 ; i <YAFFS_NTNODES_INTERNAL ; i++) { +if (tn->internal[i]) { + tn->internal[i] = +yaffs_prune_worker (dev, + tn->internal[i], + level -1 , + (i ==0 ) ? del0 :1 ); + } + +if (tn->internal[i]) + has_data++; + } + }else { +int tnode_size_u32 = dev->tnode_size /sizeof (u32); +u32 *map = (u32 *) tn; + +for (i =0 ; !has_data && i < tnode_size_u32; i++) { +if (map[i]) + has_data++; + } + } + +if (has_data ==0 && del0) { +/* Free and return NULL */ +yaffs_free_tnode (dev, tn); + tn =NULL ; + } +return tn; +} + +static int yaffs_prune_tree (struct yaffs_dev *dev, +struct yaffs_file_var *file_struct) +{ +int i; +int has_data; +int done =0 ; +struct yaffs_tnode *tn; + +if (file_struct->top_level <1 ) +return YAFFS_OK ; + + file_struct->top = +yaffs_prune_worker (dev, file_struct->top, file_struct->top_level,0 ); + +/* Now we have a tree with all the non-zero branches NULL but +* the height is the same as it was. +* Let's see if we can trim internal tnodes to shorten the tree. +* We can do this if only the 0th element in the tnode is in use +* (ie all the non-zero are NULL) +*/ + +while (file_struct->top_level && !done) { + tn = file_struct->top; + + has_data =0 ; +for (i =1 ; i <YAFFS_NTNODES_INTERNAL ; i++) { +if (tn->internal[i]) + has_data++; + } + +if (!has_data) { + file_struct->top = tn->internal[0 ]; + file_struct->top_level--; +yaffs_free_tnode (dev, tn); + }else { + done =1 ; + } + } + +return YAFFS_OK ; +} + +/*-------------------- End of File Structure functions.-------------------*/ + +/* alloc_empty_obj gets us a clean Object.*/ +static struct yaffs_obj *yaffs_alloc_empty_obj (struct yaffs_dev *dev) +{ +struct yaffs_obj *obj =yaffs_alloc_raw_obj (dev); + +if (!obj) +return obj; + + dev->n_obj++; + +/* Now sweeten it up... */ + +memset (obj,0 ,sizeof (struct yaffs_obj )); + obj->being_created =1 ; + + obj->my_dev = dev; + obj->hdr_chunk =0 ; + obj->variant_type =YAFFS_OBJECT_TYPE_UNKNOWN ; +INIT_LIST_HEAD (&(obj->hard_links)); +INIT_LIST_HEAD (&(obj->hash_link)); +INIT_LIST_HEAD (&obj->siblings); + +/* Now make the directory sane */ +if (dev->root_dir) { + obj->parent = dev->root_dir; +list_add (&(obj->siblings), + &dev->root_dir->variant.dir_variant.children); + } + +/* Add it to the lost and found directory. +* NB Can't put root or lost-n-found in lost-n-found so +* check if lost-n-found exists first +*/ +if (dev->lost_n_found) +yaffs_add_obj_to_dir (dev->lost_n_found, obj); + + obj->being_created =0 ; +
fs/yaffs2/yaffs_guts.c line 1866
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+ dev->checkpoint_blocks_required =0 ;/* force recalculation */ + +return obj; +} + +static int yaffs_find_nice_bucket (struct yaffs_dev *dev) +{ +int i; +int l =999 ; +int lowest =999999 ;
l/lowest = INT_MAX?
Quoted 22 lines
fs/yaffs2/yaffs_guts.c new file +22
-0+ +/* Search for the shortest list or one that +* isn't too long. +*/ + +for (i =0 ; i <10 && lowest >4 ; i++) { + dev->bucket_finder++; + dev->bucket_finder %=YAFFS_NOBJECT_BUCKETS ; +if (dev->obj_bucket[dev->bucket_finder].count < lowest) { + lowest = dev->obj_bucket[dev->bucket_finder].count; + l = dev->bucket_finder; + } + } + +return l; +} + +static int yaffs_new_obj_id (struct yaffs_dev *dev) +{ +int bucket =yaffs_find_nice_bucket (dev); +int found =0 ; +struct list_head *i;
fs/yaffs2/yaffs_guts.c line 1898
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+u32 n = (u32 ) bucket; + +/* Now find an object value that has not already been taken +* by scanning the list. +*/ + +while (!found) { + found =1 ; + n +=YAFFS_NOBJECT_BUCKETS ; +if (1 || dev->obj_bucket[bucket].count >0 ) {
Err, this if statement always succeeds. Is this left over testing code?
Quoted 139 lines
fs/yaffs2/yaffs_guts.c new file +139
-0+list_for_each (i, &dev->obj_bucket[bucket].list) { +/* If there is already one in the list */ + if (i && list_entry(i, struct yaffs_obj, + hash_link)->obj_id == n) { + found =0 ; + } + } + } + } +return n; +} + +static void yaffs_hash_obj (struct yaffs_obj *in) +{ +int bucket =yaffs_hash_fn (in->obj_id); +struct yaffs_dev *dev = in->my_dev; + +list_add (&in->hash_link, &dev->obj_bucket[bucket].list); + dev->obj_bucket[bucket].count++; +} + +struct yaffs_obj *yaffs_find_by_number (struct yaffs_dev *dev,u32 number) +{ +int bucket =yaffs_hash_fn (number); +struct list_head *i; +struct yaffs_obj *in; + +list_for_each (i, &dev->obj_bucket[bucket].list) { +/* Look if it is in the list */ + in = list_entry(i, struct yaffs_obj, hash_link); +if (in->obj_id == number) { +/* Don't show if it is defered free */ +if (in->defered_free) +return NULL ; +return in; + } + } + +return NULL ; +} + +struct yaffs_obj *yaffs_new_obj (struct yaffs_dev *dev,int number, +enum yaffs_obj_type type) +{ +struct yaffs_obj *the_obj =NULL ; +struct yaffs_tnode *tn =NULL ; + +if (number <0 ) + number =yaffs_new_obj_id (dev); + +if (type ==YAFFS_OBJECT_TYPE_FILE ) { + tn =yaffs_get_tnode (dev); +if (!tn) +return NULL ; + } + + the_obj =yaffs_alloc_empty_obj (dev); +if (!the_obj) { +if (tn) +yaffs_free_tnode (dev, tn); +return NULL ; + } + + the_obj->fake =0 ; + the_obj->rename_allowed =1 ; + the_obj->unlink_allowed =1 ; + the_obj->obj_id = number; +yaffs_hash_obj (the_obj); + the_obj->variant_type = type; +yaffs_load_current_time (the_obj,1 ,1 ); + +switch (type) { +case YAFFS_OBJECT_TYPE_FILE : + the_obj->variant.file_variant.file_size =0 ; + the_obj->variant.file_variant.scanned_size =0 ; + the_obj->variant.file_variant.shrink_size = ~0 ;/* max */ + the_obj->variant.file_variant.top_level =0 ; + the_obj->variant.file_variant.top = tn; +break ; +case YAFFS_OBJECT_TYPE_DIRECTORY : +INIT_LIST_HEAD (&the_obj->variant.dir_variant.children); +INIT_LIST_HEAD (&the_obj->variant.dir_variant.dirty); +break ; +case YAFFS_OBJECT_TYPE_SYMLINK : +case YAFFS_OBJECT_TYPE_HARDLINK : +case YAFFS_OBJECT_TYPE_SPECIAL : +/* No action required */ +break ; +case YAFFS_OBJECT_TYPE_UNKNOWN : +/* todo this should not happen */ +break ; + } +return the_obj; +} + +static struct yaffs_obj *yaffs_create_fake_dir (struct yaffs_dev *dev, +int number,u32 mode) +{ + +struct yaffs_obj *obj = +yaffs_new_obj (dev, number,YAFFS_OBJECT_TYPE_DIRECTORY ); + +if (!obj) +return NULL ; + + obj->fake =1 ;/* it is fake so it might not use NAND */ + obj->rename_allowed =0 ; + obj->unlink_allowed =0 ; + obj->deleted =0 ; + obj->unlinked =0 ; + obj->yst_mode = mode; + obj->my_dev = dev; + obj->hdr_chunk =0 ;/* Not a valid chunk. */ +return obj; + +} + + +static void yaffs_init_tnodes_and_objs (struct yaffs_dev *dev) +{ +int i; + + dev->n_obj =0 ; + dev->n_tnodes =0 ; +yaffs_init_raw_tnodes_and_objs (dev); + +for (i =0 ; i <YAFFS_NOBJECT_BUCKETS ; i++) { +INIT_LIST_HEAD (&dev->obj_bucket[i].list); + dev->obj_bucket[i].count =0 ; + } +} + +struct yaffs_obj *yaffs_find_or_create_by_number (struct yaffs_dev *dev, +int number, +enum yaffs_obj_type type) +{ +struct yaffs_obj *the_obj =NULL ; + +if (number >0 )
fs/yaffs2/yaffs_guts.c line 2047
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+ the_obj =yaffs_find_by_number (dev, number); + +if (!the_obj) + the_obj =yaffs_new_obj (dev, number, type); + +return the_obj; + +} + +YCHAR *yaffs_clone_str (const YCHAR *str)
kstrdup?
Quoted 243 lines
fs/yaffs2/yaffs_guts.c new file +243
-0+{ +YCHAR *new_str =NULL ; +int len; + +if (!str) + str =_Y ("" ); + + len =strnlen (str,YAFFS_MAX_ALIAS_LENGTH ); + new_str =kmalloc ((len +1 ) *sizeof (YCHAR ),GFP_NOFS ); +if (new_str) { +strncpy (new_str, str, len); + new_str[len] =0 ; + } +return new_str; + +} +/* +*yaffs_update_parent() handles fixing a directories mtime and ctime when a new +* link (ie. name) is created or deleted in the directory. +* +* ie. +* create dir/a : update dir's mtime/ctime +* rm dir/a: update dir's mtime/ctime +* modify dir/a: don't update dir's mtimme/ctime +* +* This can be handled immediately or defered. Defering helps reduce the number +* of updates when many files in a directory are changed within a brief period. +* +* If the directory updating is defered then yaffs_update_dirty_dirs must be +* called periodically. +*/ + +static void yaffs_update_parent (struct yaffs_obj *obj) +{ +struct yaffs_dev *dev; + +if (!obj) +return ; + dev = obj->my_dev; + obj->dirty =1 ; +yaffs_load_current_time (obj,0 ,1 ); +if (dev->param.defered_dir_update) { +struct list_head *link = &obj->variant.dir_variant.dirty; + +if (list_empty (link)) { +list_add (link, &dev->dirty_dirs); +yaffs_trace (YAFFS_TRACE_BACKGROUND , +"Added object %d to dirty directories" , + obj->obj_id); + } + + }else { +yaffs_update_oh (obj,NULL ,0 ,0 ,0 ,NULL ); + } +} + +void yaffs_update_dirty_dirs (struct yaffs_dev *dev) +{ +struct list_head *link; +struct yaffs_obj *obj; +struct yaffs_dir_var *d_s; +union yaffs_obj_var *o_v; + +yaffs_trace (YAFFS_TRACE_BACKGROUND ,"Update dirty directories" ); + +while (!list_empty (&dev->dirty_dirs)) { + link = dev->dirty_dirs.next; +list_del_init (link); + + d_s = list_entry(link, struct yaffs_dir_var, dirty); + o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); + obj = list_entry(o_v, struct yaffs_obj, variant); + +yaffs_trace (YAFFS_TRACE_BACKGROUND ,"Update directory %d" , + obj->obj_id); + +if (obj->dirty) +yaffs_update_oh (obj,NULL ,0 ,0 ,0 ,NULL ); + } +} + +/* +* Mknod (create) a new object. +* equiv_obj only has meaning for a hard link; +* alias_str only has meaning for a symlink. +* rdev only has meaning for devices (a subset of special objects) +*/ + +static struct yaffs_obj *yaffs_create_obj (enum yaffs_obj_type type, +struct yaffs_obj *parent, +const YCHAR *name, +u32 mode, +u32 uid, +u32 gid, +struct yaffs_obj *equiv_obj, +const YCHAR *alias_str,u32 rdev) +{ +struct yaffs_obj *in; +YCHAR *str =NULL ; +struct yaffs_dev *dev = parent->my_dev; + +/* Check if the entry exists. +* If it does then fail the call since we don't want a dup. */ +if (yaffs_find_by_name (parent, name)) +return NULL ; + +if (type ==YAFFS_OBJECT_TYPE_SYMLINK ) { + str =yaffs_clone_str (alias_str); +if (!str) +return NULL ; + } + + in =yaffs_new_obj (dev,-1 , type); + +if (!in) { +kfree (str); +return NULL ; + } + + in->hdr_chunk =0 ; + in->valid =1 ; + in->variant_type = type; + + in->yst_mode = mode; + +yaffs_attribs_init (in, gid, uid, rdev); + + in->n_data_chunks =0 ; + +yaffs_set_obj_name (in, name); + in->dirty =1 ; + +yaffs_add_obj_to_dir (parent, in); + + in->my_dev = parent->my_dev; + +switch (type) { +case YAFFS_OBJECT_TYPE_SYMLINK : + in->variant.symlink_variant.alias = str; +break ; +case YAFFS_OBJECT_TYPE_HARDLINK : + in->variant.hardlink_variant.equiv_obj = equiv_obj; + in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; +list_add (&in->hard_links, &equiv_obj->hard_links); +break ; +case YAFFS_OBJECT_TYPE_FILE : +case YAFFS_OBJECT_TYPE_DIRECTORY : +case YAFFS_OBJECT_TYPE_SPECIAL : +case YAFFS_OBJECT_TYPE_UNKNOWN : +/* do nothing */ +break ; + } + +if (yaffs_update_oh (in, name,0 ,0 ,0 ,NULL ) <0 ) { +/* Could not create the object header, fail */ +yaffs_del_obj (in); + in =NULL ; + } + +if (in) +yaffs_update_parent (parent); + +return in; +} + +struct yaffs_obj *yaffs_create_file (struct yaffs_obj *parent, +const YCHAR *name,u32 mode,u32 uid, +u32 gid) +{ +return yaffs_create_obj (YAFFS_OBJECT_TYPE_FILE , parent, name, mode, + uid, gid,NULL ,NULL ,0 ); +} + +struct yaffs_obj *yaffs_create_dir (struct yaffs_obj *parent,const YCHAR *name, +u32 mode,u32 uid,u32 gid) +{ +return yaffs_create_obj (YAFFS_OBJECT_TYPE_DIRECTORY , parent, name, + mode, uid, gid,NULL ,NULL ,0 ); +} + +struct yaffs_obj *yaffs_create_special (struct yaffs_obj *parent, +const YCHAR *name,u32 mode,u32 uid, +u32 gid,u32 rdev) +{ +return yaffs_create_obj (YAFFS_OBJECT_TYPE_SPECIAL , parent, name, mode, + uid, gid,NULL ,NULL , rdev); +} + +struct yaffs_obj *yaffs_create_symlink (struct yaffs_obj *parent, +const YCHAR *name,u32 mode,u32 uid, +u32 gid,const YCHAR *alias) +{ +return yaffs_create_obj (YAFFS_OBJECT_TYPE_SYMLINK , parent, name, mode, + uid, gid,NULL , alias,0 ); +} + +/* yaffs_link_obj returns the object id of the equivalent object.*/ +struct yaffs_obj *yaffs_link_obj (struct yaffs_obj *parent,const YCHAR * name, +struct yaffs_obj *equiv_obj) +{ +/* Get the real object in case we were fed a hard link obj */ + equiv_obj =yaffs_get_equivalent_obj (equiv_obj); + +if (yaffs_create_obj (YAFFS_OBJECT_TYPE_HARDLINK , + parent, name,0 ,0 ,0 , + equiv_obj,NULL ,0 )) +return equiv_obj; + +return NULL ; + +} + + + +/*---------------------- Block Management and Page Allocation -------------*/ + +static void yaffs_deinit_blocks (struct yaffs_dev *dev) +{ +if (dev->block_info_alt && dev->block_info) +vfree (dev->block_info); +else +kfree (dev->block_info); + + dev->block_info_alt =0 ; + + dev->block_info =NULL ; + +if (dev->chunk_bits_alt && dev->chunk_bits) +vfree (dev->chunk_bits); +else +kfree (dev->chunk_bits); + dev->chunk_bits_alt =0 ; + dev->chunk_bits =NULL ; +} + +static int yaffs_init_blocks (struct yaffs_dev *dev) +{ +int n_blocks = dev->internal_end_block - dev->internal_start_block +1 ; + + dev->block_info =NULL ; + dev->chunk_bits =NULL ; + dev->alloc_block =-1 ;/* force it to get a new one */ +
fs/yaffs2/yaffs_guts.c line 2300
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+/* If the first allocation strategy fails, thry the alternate one */ + dev->block_info = +kmalloc (n_blocks *sizeof (struct yaffs_block_info ),GFP_NOFS ); +if (!dev->block_info) { + dev->block_info = +vmalloc (n_blocks *sizeof (struct yaffs_block_info )); + dev->block_info_alt =1 ; + }else { + dev->block_info_alt =0 ; + }
I still think this should just use the one allocation scheme (probably vmalloc) rather than attempting both. It makes the code a bit simpler.
Quoted 820 lines
fs/yaffs2/yaffs_guts.c new file +820
-0+ +if (!dev->block_info) + goto alloc_error; + +/* Set up dynamic blockinfo stuff. Round up bytes. */ + dev->chunk_bit_stride = (dev->param.chunks_per_block +7 ) /8 ; + dev->chunk_bits = +kmalloc (dev->chunk_bit_stride * n_blocks,GFP_NOFS ); +if (!dev->chunk_bits) { + dev->chunk_bits = +vmalloc (dev->chunk_bit_stride * n_blocks); + dev->chunk_bits_alt =1 ; + }else { + dev->chunk_bits_alt =0 ; + } +if (!dev->chunk_bits) + goto alloc_error; + + +memset (dev->block_info,0 , n_blocks *sizeof (struct yaffs_block_info )); +memset (dev->chunk_bits,0 , dev->chunk_bit_stride * n_blocks); +return YAFFS_OK ; + +alloc_error: +yaffs_deinit_blocks (dev); +return YAFFS_FAIL ; +} + + +void yaffs_block_became_dirty (struct yaffs_dev *dev,int block_no) +{ +struct yaffs_block_info *bi =yaffs_get_block_info (dev, block_no); +int erased_ok =0 ; +int i; + +/* If the block is still healthy erase it and mark as clean. +* If the block has had a data failure, then retire it. +*/ + +yaffs_trace (YAFFS_TRACE_GC |YAFFS_TRACE_ERASE , +"yaffs_block_became_dirty block %d state %d %s" , + block_no, bi->block_state, + (bi->needs_retiring) ?"needs retiring" :"" ); + +yaffs2_clear_oldest_dirty_seq (dev, bi); + + bi->block_state =YAFFS_BLOCK_STATE_DIRTY ; + +/* If this is the block being garbage collected then stop gc'ing */ +if (block_no == dev->gc_block) + dev->gc_block =0 ; + +/* If this block is currently the best candidate for gc +* then drop as a candidate */ +if (block_no == dev->gc_dirtiest) { + dev->gc_dirtiest =0 ; + dev->gc_pages_in_use =0 ; + } + +if (!bi->needs_retiring) { +yaffs2_checkpt_invalidate (dev); + erased_ok =yaffs_erase_block (dev, block_no); +if (!erased_ok) { + dev->n_erase_failures++; +yaffs_trace (YAFFS_TRACE_ERROR |YAFFS_TRACE_BAD_BLOCKS , +"**>> Erasure failed %d" , block_no); + } + } + +/* Verify erasure if needed */ +if (erased_ok && + ((yaffs_trace_mask &YAFFS_TRACE_ERASE ) || + !yaffs_skip_verification (dev))) { +for (i =0 ; i < dev->param.chunks_per_block; i++) { +if (!yaffs_check_chunk_erased (dev, + block_no * dev->param.chunks_per_block + i)) { +yaffs_trace (YAFFS_TRACE_ERROR , +">>Block %d erasure supposedly OK, but chunk %d not erased" , + block_no, i); + } + } + } + +if (!erased_ok) { +/* We lost a block of free space */ + dev->n_free_chunks -= dev->param.chunks_per_block; +yaffs_retire_block (dev, block_no); +yaffs_trace (YAFFS_TRACE_ERROR |YAFFS_TRACE_BAD_BLOCKS , +"**>> Block %d retired" , block_no); +return ; + } + +/* Clean it up... */ + bi->block_state =YAFFS_BLOCK_STATE_EMPTY ; + bi->seq_number =0 ; + dev->n_erased_blocks++; + bi->pages_in_use =0 ; + bi->soft_del_pages =0 ; + bi->has_shrink_hdr =0 ; + bi->skip_erased_check =1 ;/* Clean, so no need to check */ + bi->gc_prioritise =0 ; +yaffs_clear_chunk_bits (dev, block_no); + +yaffs_trace (YAFFS_TRACE_ERASE ,"Erased block %d" , block_no); +} + +static inline int yaffs_gc_process_chunk (struct yaffs_dev *dev, +struct yaffs_block_info *bi, +int old_chunk,u8 *buffer) +{ +int new_chunk; +int mark_flash =1 ; +struct yaffs_ext_tags tags; +struct yaffs_obj *object; +int matching_chunk; +int ret_val =YAFFS_OK ; + +yaffs_init_tags (&tags); +yaffs_rd_chunk_tags_nand (dev, old_chunk, + buffer, &tags); + object =yaffs_find_by_number (dev, tags.obj_id); + +yaffs_trace (YAFFS_TRACE_GC_DETAIL , +"Collecting chunk in block %d, %d %d %d " , + dev->gc_chunk, tags.obj_id, + tags.chunk_id, tags.n_bytes); + +if (object && !yaffs_skip_verification (dev)) { +if (tags.chunk_id ==0 ) + matching_chunk = + object->hdr_chunk; +else if (object->soft_del) +/* Defeat the test */ + matching_chunk = old_chunk; +else + matching_chunk = +yaffs_find_chunk_in_file + (object, tags.chunk_id, +NULL ); + +if (old_chunk != matching_chunk) +yaffs_trace (YAFFS_TRACE_ERROR , +"gc: page in gc mismatch: %d %d %d %d" , + old_chunk, + matching_chunk, + tags.obj_id, + tags.chunk_id); + } + +if (!object) { +yaffs_trace (YAFFS_TRACE_ERROR , +"page %d in gc has no object: %d %d %d " , + old_chunk, + tags.obj_id, tags.chunk_id, + tags.n_bytes); + } + +if (object && + object->deleted && + object->soft_del && tags.chunk_id !=0 ) { +/* Data chunk in a soft deleted file, +* throw it away. +* It's a soft deleted data chunk, +* No need to copy this, just forget +* about it and fix up the object. +*/ + +/* Free chunks already includes +* softdeleted chunks, how ever this +* chunk is going to soon be really +* deleted which will increment free +* chunks. We have to decrement free +* chunks so this works out properly. +*/ + dev->n_free_chunks--; + bi->soft_del_pages--; + + object->n_data_chunks--; +if (object->n_data_chunks <=0 ) { +/* remeber to clean up obj */ + dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; + dev->n_clean_ups++; + } + mark_flash =0 ; + }else if (object) { +/* It's either a data chunk in a live +* file or an ObjectHeader, so we're +* interested in it. +* NB Need to keep the ObjectHeaders of +* deleted files until the whole file +* has been deleted off +*/ + tags.serial_number++; + dev->n_gc_copies++; + +if (tags.chunk_id ==0 ) { +/* It is an object Id, +* We need to nuke the +* shrinkheader flags since its +* work is done. +* Also need to clean up +* shadowing. +*/ +struct yaffs_obj_hdr *oh; + oh = (struct yaffs_obj_hdr *) buffer; + + oh->is_shrink =0 ; + tags.extra_is_shrink =0 ; + oh->shadows_obj =0 ; + oh->inband_shadowed_obj_id =0 ; + tags.extra_shadows =0 ; + +/* Update file size */ +if (object->variant_type ==YAFFS_OBJECT_TYPE_FILE ) { + oh->file_size = + object->variant.file_variant.file_size; + tags.extra_length = oh->file_size; + } + +yaffs_verify_oh (object, oh, &tags,1 ); + new_chunk = +yaffs_write_new_chunk (dev, (u8 *) oh, &tags,1 ); + }else { + new_chunk = +yaffs_write_new_chunk (dev, buffer, &tags,1 ); + } + +if (new_chunk <0 ) { + ret_val =YAFFS_FAIL ; + }else { + +/* Now fix up the Tnodes etc. */ + +if (tags.chunk_id ==0 ) { +/* It's a header */ + object->hdr_chunk = new_chunk; + object->serial = tags.serial_number; + }else { +/* It's a data chunk */ +yaffs_put_chunk_in_file (object, tags.chunk_id, + new_chunk,0 ); + } + } + } +if (ret_val ==YAFFS_OK ) +yaffs_chunk_del (dev, old_chunk, mark_flash, __LINE__); +return ret_val; +} + +static int yaffs_gc_block (struct yaffs_dev *dev,int block,int whole_block) +{ +int old_chunk; +int ret_val =YAFFS_OK ; +int i; +int is_checkpt_block; +int max_copies; +int chunks_before =yaffs_get_erased_chunks (dev); +int chunks_after; +struct yaffs_block_info *bi =yaffs_get_block_info (dev, block); + + is_checkpt_block = (bi->block_state ==YAFFS_BLOCK_STATE_CHECKPOINT ); + +yaffs_trace (YAFFS_TRACE_TRACING , +"Collecting block %d, in use %d, shrink %d, whole_block %d" , + block, bi->pages_in_use, bi->has_shrink_hdr, + whole_block); + +/*yaffs_verify_free_chunks(dev); */ + +if (bi->block_state ==YAFFS_BLOCK_STATE_FULL ) + bi->block_state =YAFFS_BLOCK_STATE_COLLECTING ; + + bi->has_shrink_hdr =0 ;/* clear the flag so that the block can erase */ + + dev->gc_disable =1 ; + +if (is_checkpt_block || !yaffs_still_some_chunks (dev, block)) { +yaffs_trace (YAFFS_TRACE_TRACING , +"Collecting block %d that has no chunks in use" , + block); +yaffs_block_became_dirty (dev, block); + }else { + +u8 *buffer =yaffs_get_temp_buffer (dev, __LINE__); + +yaffs_verify_blk (dev, bi, block); + + max_copies = (whole_block) ? dev->param.chunks_per_block :5 ; + old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; + +for (/* init already done */ ; + ret_val ==YAFFS_OK && + dev->gc_chunk < dev->param.chunks_per_block && + (bi->block_state ==YAFFS_BLOCK_STATE_COLLECTING ) && + max_copies >0 ; + dev->gc_chunk++, old_chunk++) { +if (yaffs_check_chunk_bit (dev, block, dev->gc_chunk)) { +/* Page is in use and might need to be copied */ + max_copies--; + ret_val =yaffs_gc_process_chunk (dev, bi, + old_chunk, buffer); + } + } +yaffs_release_temp_buffer (dev, buffer, __LINE__); + } + +yaffs_verify_collected_blk (dev, bi, block); + +if (bi->block_state ==YAFFS_BLOCK_STATE_COLLECTING ) { +/* +* The gc did not complete. Set block state back to FULL +* because checkpointing does not restore gc. +*/ + bi->block_state =YAFFS_BLOCK_STATE_FULL ; + }else { +/* The gc completed. */ +/* Do any required cleanups */ +for (i =0 ; i < dev->n_clean_ups; i++) { +/* Time to delete the file too */ +struct yaffs_obj *object = +yaffs_find_by_number (dev, dev->gc_cleanup_list[i]); +if (object) { +yaffs_free_tnode (dev, + object->variant.file_variant.top); + object->variant.file_variant.top =NULL ; +yaffs_trace (YAFFS_TRACE_GC , +"yaffs: About to finally delete object %d" , + object->obj_id); +yaffs_generic_obj_del (object); + object->my_dev->n_deleted_files--; + } + + } + chunks_after =yaffs_get_erased_chunks (dev); +if (chunks_before >= chunks_after) +yaffs_trace (YAFFS_TRACE_GC , +"gc did not increase free chunks before %d after %d" , + chunks_before, chunks_after); + dev->gc_block =0 ; + dev->gc_chunk =0 ; + dev->n_clean_ups =0 ; + } + + dev->gc_disable =0 ; + +return ret_val; +} + +/* +* find_gc_block() selects the dirtiest block (or close enough) +* for garbage collection. +*/ + +static unsigned yaffs_find_gc_block (struct yaffs_dev *dev, +int aggressive,int background) +{ +int i; +int iterations; +unsigned selected =0 ; +int prioritised =0 ; +int prioritised_exist =0 ; +struct yaffs_block_info *bi; +int threshold; + +/* First let's see if we need to grab a prioritised block */ +if (dev->has_pending_prioritised_gc && !aggressive) { + dev->gc_dirtiest =0 ; + bi = dev->block_info; +for (i = dev->internal_start_block; + i <= dev->internal_end_block && !selected; i++) { + +if (bi->gc_prioritise) { + prioritised_exist =1 ; +if (bi->block_state ==YAFFS_BLOCK_STATE_FULL && +yaffs_block_ok_for_gc (dev, bi)) { + selected = i; + prioritised =1 ; + } + } + bi++; + } + +/* +* If there is a prioritised block and none was selected then +* this happened because there is at least one old dirty block +* gumming up the works. Let's gc the oldest dirty block. +*/ + +if (prioritised_exist && + !selected && dev->oldest_dirty_block >0 ) + selected = dev->oldest_dirty_block; + +if (!prioritised_exist)/* None found, so we can clear this */ + dev->has_pending_prioritised_gc =0 ; + } + +/* If we're doing aggressive GC then we are happy to take a less-dirty +* block, and search harder. +* else (leasurely gc), then we only bother to do this if the +* block has only a few pages in use. +*/ + +if (!selected) { +int pages_used; +int n_blocks = + dev->internal_end_block - dev->internal_start_block +1 ; +if (aggressive) { + threshold = dev->param.chunks_per_block; + iterations = n_blocks; + }else { +int max_threshold; + +if (background) + max_threshold = dev->param.chunks_per_block /2 ; +else + max_threshold = dev->param.chunks_per_block /8 ; + +if (max_threshold <YAFFS_GC_PASSIVE_THRESHOLD ) + max_threshold =YAFFS_GC_PASSIVE_THRESHOLD ; + + threshold = background ? (dev->gc_not_done +2 ) *2 :0 ; +if (threshold <YAFFS_GC_PASSIVE_THRESHOLD ) + threshold =YAFFS_GC_PASSIVE_THRESHOLD ; +if (threshold > max_threshold) + threshold = max_threshold; + + iterations = n_blocks /16 +1 ; +if (iterations >100 ) + iterations =100 ; + } + +for (i =0 ; + i < iterations && + (dev->gc_dirtiest <1 || + dev->gc_pages_in_use >YAFFS_GC_GOOD_ENOUGH ); + i++) { + dev->gc_block_finder++; +if (dev->gc_block_finder < dev->internal_start_block || + dev->gc_block_finder > dev->internal_end_block) + dev->gc_block_finder = + dev->internal_start_block; + + bi =yaffs_get_block_info (dev, dev->gc_block_finder); + + pages_used = bi->pages_in_use - bi->soft_del_pages; + +if (bi->block_state ==YAFFS_BLOCK_STATE_FULL && + pages_used < dev->param.chunks_per_block && + (dev->gc_dirtiest <1 || + pages_used < dev->gc_pages_in_use) && +yaffs_block_ok_for_gc (dev, bi)) { + dev->gc_dirtiest = dev->gc_block_finder; + dev->gc_pages_in_use = pages_used; + } + } + +if (dev->gc_dirtiest >0 && dev->gc_pages_in_use <= threshold) + selected = dev->gc_dirtiest; + } + +/* +* If nothing has been selected for a while, try the oldest dirty +* because that's gumming up the works. +*/ + +if (!selected && dev->param.is_yaffs2 && + dev->gc_not_done >= (background ?10 :20 )) { +yaffs2_find_oldest_dirty_seq (dev); +if (dev->oldest_dirty_block >0 ) { + selected = dev->oldest_dirty_block; + dev->gc_dirtiest = selected; + dev->oldest_dirty_gc_count++; + bi =yaffs_get_block_info (dev, selected); + dev->gc_pages_in_use = + bi->pages_in_use - bi->soft_del_pages; + }else { + dev->gc_not_done =0 ; + } + } + +if (selected) { +yaffs_trace (YAFFS_TRACE_GC , +"GC Selected block %d with %d free, prioritised:%d" , + selected, + dev->param.chunks_per_block - dev->gc_pages_in_use, + prioritised); + + dev->n_gc_blocks++; +if (background) + dev->bg_gcs++; + + dev->gc_dirtiest =0 ; + dev->gc_pages_in_use =0 ; + dev->gc_not_done =0 ; +if (dev->refresh_skip >0 ) + dev->refresh_skip--; + }else { + dev->gc_not_done++; +yaffs_trace (YAFFS_TRACE_GC , +"GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s" , + dev->gc_block_finder, dev->gc_not_done, threshold, + dev->gc_dirtiest, dev->gc_pages_in_use, + dev->oldest_dirty_block, background ?" bg" :"" ); + } + +return selected; +} + +/* New garbage collector +* If we're very low on erased blocks then we do aggressive garbage collection +* otherwise we do "leasurely" garbage collection. +* Aggressive gc looks further (whole array) and will accept less dirty blocks. +* Passive gc only inspects smaller areas and only accepts more dirty blocks. +* +* The idea is to help clear out space in a more spread-out manner. +* Dunno if it really does anything useful. +*/ +static int yaffs_check_gc (struct yaffs_dev *dev,int background) +{ +int aggressive =0 ; +int gc_ok =YAFFS_OK ; +int max_tries =0 ; +int min_erased; +int erased_chunks; +int checkpt_block_adjust; + +if (dev->param.gc_control && (dev->param.gc_control (dev) &1 ) ==0 ) +return YAFFS_OK ; + +if (dev->gc_disable) +/* Bail out so we don't get recursive gc */ +return YAFFS_OK ; + +/* This loop should pass the first time. +* Only loops here if the collection does not increase space. +*/ + +do { + max_tries++; + + checkpt_block_adjust =yaffs_calc_checkpt_blocks_required (dev); + + min_erased = + dev->param.n_reserved_blocks + checkpt_block_adjust +1 ; + erased_chunks = + dev->n_erased_blocks * dev->param.chunks_per_block; + +/* If we need a block soon then do aggressive gc. */ +if (dev->n_erased_blocks < min_erased) + aggressive =1 ; +else { +if (!background + && erased_chunks > (dev->n_free_chunks /4 )) +break ; + +if (dev->gc_skip >20 ) + dev->gc_skip =20 ; +if (erased_chunks < dev->n_free_chunks /2 || + dev->gc_skip <1 || background) + aggressive =0 ; +else { + dev->gc_skip--; +break ; + } + } + + dev->gc_skip =5 ; + +/* If we don't already have a block being gc'd then see if we +* should start another */ + +if (dev->gc_block <1 && !aggressive) { + dev->gc_block =yaffs2_find_refresh_block (dev); + dev->gc_chunk =0 ; + dev->n_clean_ups =0 ; + } +if (dev->gc_block <1 ) { + dev->gc_block = +yaffs_find_gc_block (dev, aggressive, background); + dev->gc_chunk =0 ; + dev->n_clean_ups =0 ; + } + +if (dev->gc_block >0 ) { + dev->all_gcs++; +if (!aggressive) + dev->passive_gc_count++; + +yaffs_trace (YAFFS_TRACE_GC , +"yaffs: GC n_erased_blocks %d aggressive %d" , + dev->n_erased_blocks, aggressive); + + gc_ok =yaffs_gc_block (dev, dev->gc_block, aggressive); + } + +if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && + dev->gc_block >0 ) { +yaffs_trace (YAFFS_TRACE_GC , +"yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d" , + dev->n_erased_blocks, max_tries, + dev->gc_block); + } + }while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && + (dev->gc_block >0 ) && (max_tries <2 )); + +return aggressive ? gc_ok :YAFFS_OK ; +} + +/* +* yaffs_bg_gc() +* Garbage collects. Intended to be called from a background thread. +* Returns non-zero if at least half the free chunks are erased. +*/ +int yaffs_bg_gc (struct yaffs_dev *dev,unsigned urgency) +{ +int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; + +yaffs_trace (YAFFS_TRACE_BACKGROUND ,"Background gc %u" , urgency); + +yaffs_check_gc (dev,1 ); +return erased_chunks > dev->n_free_chunks /2 ; +} + +/*-------------------- Data file manipulation -----------------*/ + +static int yaffs_rd_data_obj (struct yaffs_obj *in,int inode_chunk,u8 * buffer) +{ +int nand_chunk =yaffs_find_chunk_in_file (in, inode_chunk,NULL ); + +if (nand_chunk >=0 ) +return yaffs_rd_chunk_tags_nand (in->my_dev, nand_chunk, + buffer,NULL ); +else { +yaffs_trace (YAFFS_TRACE_NANDACCESS , +"Chunk %d not found zero instead" , + nand_chunk); +/* get sane (zero) data if you read a hole */ +memset (buffer,0 , in->my_dev->data_bytes_per_chunk); +return 0 ; + } + +} + +void yaffs_chunk_del (struct yaffs_dev *dev,int chunk_id,int mark_flash, +int lyn) +{ +int block; +int page; +struct yaffs_ext_tags tags; +struct yaffs_block_info *bi; + +if (chunk_id <=0 ) +return ; + + dev->n_deletions++; + block = chunk_id / dev->param.chunks_per_block; + page = chunk_id % dev->param.chunks_per_block; + +if (!yaffs_check_chunk_bit (dev, block, page)) +yaffs_trace (YAFFS_TRACE_VERIFY , +"Deleting invalid chunk %d" , chunk_id); + + bi =yaffs_get_block_info (dev, block); + +yaffs2_update_oldest_dirty_seq (dev, block, bi); + +yaffs_trace (YAFFS_TRACE_DELETION , +"line %d delete of chunk %d" , + lyn, chunk_id); + +if (!dev->param.is_yaffs2 && mark_flash && + bi->block_state !=YAFFS_BLOCK_STATE_COLLECTING ) { + +yaffs_init_tags (&tags); + tags.is_deleted =1 ; +yaffs_wr_chunk_tags_nand (dev, chunk_id,NULL , &tags); +yaffs_handle_chunk_update (dev, chunk_id, &tags); + }else { + dev->n_unmarked_deletions++; + } + +/* Pull out of the management area. +* If the whole block became dirty, this will kick off an erasure. +*/ +if (bi->block_state ==YAFFS_BLOCK_STATE_ALLOCATING || + bi->block_state ==YAFFS_BLOCK_STATE_FULL || + bi->block_state ==YAFFS_BLOCK_STATE_NEEDS_SCAN || + bi->block_state ==YAFFS_BLOCK_STATE_COLLECTING ) { + dev->n_free_chunks++; +yaffs_clear_chunk_bit (dev, block, page);Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
fs/yaffs2/yaffs_guts.c line 3000
Quoted 10 lines
fs/yaffs2/yaffs_guts.c new file +10
-0+ bi->pages_in_use--; + +if (bi->pages_in_use ==0 && + !bi->has_shrink_hdr && + bi->block_state !=YAFFS_BLOCK_STATE_ALLOCATING && + bi->block_state !=YAFFS_BLOCK_STATE_NEEDS_SCAN ) { +yaffs_block_became_dirty (dev, block); + } + } +}
Possibly yaffs_get_temp_buffer should return void * to avoid this sort of casting.
fs/yaffs2/yaffs_guts.c
Quoted 5 lines
fs/yaffs2/yaffs_guts.c new file +5
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
But then you cast it back to u8 *?
fs/yaffs2/yaffs_guts.c
Quoted 59 lines
fs/yaffs2/yaffs_guts.c new file +59
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
result is unused.
fs/yaffs2/yaffs_guts.c
Quoted 19 lines
fs/yaffs2/yaffs_guts.c new file +19
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Remove this ifdef, it is not configurable.
fs/yaffs2/yaffs_guts.c
Quoted 27 lines
fs/yaffs2/yaffs_guts.c new file +27
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Same again.
fs/yaffs2/yaffs_guts.c
Quoted 40 lines
fs/yaffs2/yaffs_guts.c new file +40
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Old name in comment.
fs/yaffs2/yaffs_guts.c
Quoted 96 lines
fs/yaffs2/yaffs_guts.c new file +96
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
The casting between u8 * and char * in this file is a bit annoying. Is it possible to minimise this?
fs/yaffs2/yaffs_guts.c
Quoted 116 lines
fs/yaffs2/yaffs_guts.c new file +116
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
This looks a bit odd. If cache->locked is meant to be protecting something then should it either being using atomic ops (and appropriate barriers) or a proper lock. Are we potentially in parallel here with something else which could be checking cache->locked? If so this code is not probably not safe. If not, toggling cache->locked is not particularly useful.
fs/yaffs2/yaffs_guts.c
Quoted 345 lines
fs/yaffs2/yaffs_guts.c new file +345
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
You don't need the del_now intermediate. Just check !in->my_inode directly.
fs/yaffs2/yaffs_guts.c
Quoted 239 lines
fs/yaffs2/yaffs_guts.c new file +239
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Remove this ifdef, it can't be configured.
fs/yaffs2/yaffs_guts.c
Quoted 247 lines
fs/yaffs2/yaffs_guts.c new file +247
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
I would move this code inline where it is called from. It's quite obvious what it does and is only used in one place anyway.
fs/yaffs2/yaffs_guts.c
Quoted 115 lines
fs/yaffs2/yaffs_guts.c new file +115
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Non-existent Kconfig option.
fs/yaffs2/yaffs_guts.c
Quoted 372 lines
fs/yaffs2/yaffs_guts.c new file +372
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
This has the same problems as the temporary buffer allocation function. Probably best to move the cache allocation into its own function and correclty free any allocated buffers if an allocation fails.
If the init fails we should probably just bail from this function instead of having if (!init_failed) everywhere right?
fs/yaffs2/yaffs_guts.c
Quoted 130 lines
fs/yaffs2/yaffs_guts.c new file +130
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Ah okay, the temporary/cache buffers do actually get freed. It's not particularly obvious though. They should get freed in the error paths for the allocation functions. Makes it easier to verify that there are no leaks.
fs/yaffs2/yaffs_guts.h
Quoted 508 lines
fs/yaffs2/yaffs_guts.c new file +61
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Doesn't exist.
fs/yaffs2/yaffs_guts.h
Quoted 4 lines
fs/yaffs2/yaffs_guts.h new file +4
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Nope!
fs/yaffs2/yaffs_guts.h
Quoted 148 lines
fs/yaffs2/yaffs_guts.h new file +148
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Doesn't exist.
Quoted 345 lines
fs/yaffs2/yaffs_guts.h new file +345
-0Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Signature · 7 lines
Bluewater Systems Ltd - ARM Technology Solution Centre Ryan Mallon 5 Amuri Park, 404 Barbadoes St [email protected] PO Box 13 889, Christchurch 8013 http://www.bluewatersys.com New Zealand Phone: +64 3 3779127 Freecall: Australia 1800 148 751 Fax: +64 3 3779135 USA 1800 261 2934
Part of the diff is not shown here. View the raw message.
PATCH 9/10 Add yaffs2 file system: Linux glue code
In reply to Charles Manning--- fs/yaffs2/yaffs_linux.h | 41 + fs/yaffs2/yaffs_vfs.c | 2745 +++++++++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yportenv.h | 59 + 3 files changed, 2845 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_linux.h create mode 100644 fs/yaffs2/yaffs_vfs.c create mode 100644 fs/yaffs2/yportenv.h
fs/yaffs2/yaffs_linux.h new file +41 -0
@@ -0,0 +1,41 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License version 2.1 as +* published by the Free Software Foundation. +* +* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. +*/ + +#ifndef __YAFFS_LINUX_H__ +#define __YAFFS_LINUX_H__ + +#include "yportenv.h" + +struct yaffs_linux_context { +struct list_head context_list;/* List of these we have mounted */ +struct yaffs_dev *dev; +struct super_block *super; +struct task_struct *bg_thread;/* Background thread for this device */ +int bg_running; +struct mutex gross_lock;/* Gross locking mutex*/ +u8 *spare_buffer;/* For mtdif2 use. Don't know the buffer size +* at compile time so we have to allocate it. +*/ +struct list_head search_contexts; +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
fs/yaffs2/yaffs_vfs.c new file +2745 -0
2,745 lines not on this page · raw message
fs/yaffs2/yportenv.h new file +59 -0
59 lines not on this page · raw message
Signature · 1 line
1.7.1
Re: [PATCH 09/10] Add yaffs2 file system: Linux glue code
Quoted 1 line · Charles Manning
Signed-off-by Charles Manning <[email protected]>
Hi Charles,
More comments below. The locking I think needs some work before this is okay for mainline. It would be interesting to stress test yaffs on an SMP machine. None of the boards which we run yaffs on are though :-(.
~Ryan
Quoted 338 lines
--- fs/yaffs2/yaffs_linux.h | 41 + fs/yaffs2/yaffs_vfs.c | 2745 +++++++++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yportenv.h | 59 + 3 files changed, 2845 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/yaffs_linux.h create mode 100644 fs/yaffs2/yaffs_vfs.c create mode 100644 fs/yaffs2/yportenv.hfs/yaffs2/yaffs_linux.h new file +41
-0@@ -0,0 +1,41 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License version 2.1 as +* published by the Free Software Foundation. +* +* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. +*/ + +#ifndef __YAFFS_LINUX_H__ +#define __YAFFS_LINUX_H__ + +#include "yportenv.h" + +struct yaffs_linux_context { +struct list_head context_list;/* List of these we have mounted */ +struct yaffs_dev *dev; +struct super_block *super; +struct task_struct *bg_thread;/* Background thread for this device */ +int bg_running; +struct mutex gross_lock;/* Gross locking mutex*/ +u8 *spare_buffer;/* For mtdif2 use. Don't know the buffer size +* at compile time so we have to allocate it. +*/ +struct list_head search_contexts; +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 fs/yaffs2/yaffs_vfs.c new file +276
-0@@ -0,0 +1,2745 @@ +/* +* YAFFS: Yet Another Flash File System. A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* Acknowledgements: +* Luc van OostenRyck for numerous patches. +* Nick Bane for numerous patches. +* Nick Bane for 2.5/2.6 integration. +* Andras Toth for mknod rdev issue. +* Michael Fischer for finding the problem with inode inconsistency. +* Some code bodily lifted from JFFS +* +* 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 is the file system front-end to YAFFS that hooks it up to +* the VFS. +* +* Special notes: +* >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with +* this superblock +* >> 2.6: sb->s_fs_info points to the struct yaffs_dev associated with this +* superblock +* >> inode->u.generic_ip points to the associated struct yaffs_obj. +*/ + +/* +* NB There are two variants of Linux VFS glue code. This variant supports +* a single version and should not include any multi-version code. +*/ +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> +#include <linux/smp_lock.h> +#include <linux/pagemap.h> +#include <linux/mtd/mtd.h> +#include <linux/interrupt.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/namei.h> +#include <linux/exportfs.h> +#include <linux/kthread.h> +#include <linux/delay.h> +#include <linux/freezer.h> +#include <asm/div64.h> +#include <linux/statfs.h> +#include <linux/uaccess.h> +#include <linux/mtd/mtd.h> + +#include "yportenv.h" +#include "yaffs_trace.h" +#include "yaffs_guts.h" +#include "yaffs_attribs.h" +#include "yaffs_linux.h" +#include "yaffs_mtdif.h" +#include "yaffs_mtdif1.h" +#include "yaffs_mtdif2.h" + +unsigned int yaffs_trace_mask =YAFFS_TRACE_BAD_BLOCKS |YAFFS_TRACE_ALWAYS ; +unsigned int yaffs_wr_attempts =YAFFS_WR_ATTEMPTS ; +unsigned int yaffs_auto_checkpoint =1 ; +unsigned int yaffs_gc_control =1 ; +unsigned int yaffs_bg_enable =1 ; + +/* Module Parameters */ +module_param (yaffs_trace_mask, uint,0644 ); +module_param (yaffs_wr_attempts, uint,0644 ); +module_param (yaffs_auto_checkpoint, uint,0644 ); +module_param (yaffs_gc_control, uint,0644 ); +module_param (yaffs_bg_enable, uint,0644 ); + +#define yaffs_devname (sb, buf) bdevname(sb->s_bdev, buf) + +static uint32_t YCALCBLOCKS (uint64_t partition_size,uint32_t block_size) +{ +uint64_t result = partition_size; + +do_div (result, block_size); +return (uint32_t ) result; +} + +#define yaffs_inode_to_obj_lv (iptr) ((iptr)->i_private) +#define yaffs_inode_to_obj (iptr)\ + ((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr))) +#define yaffs_dentry_to_obj (dptr) yaffs_inode_to_obj((dptr)->d_inode) +#define yaffs_super_to_dev (sb) ((struct yaffs_dev *)sb->s_fs_info) + +#define update_dir_time (dir) do {\ + (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ + } while (0) + + +static unsigned yaffs_gc_control_callback (struct yaffs_dev *dev) +{ +return yaffs_gc_control; +} + +static void yaffs_gross_lock (struct yaffs_dev *dev) +{ +yaffs_trace (YAFFS_TRACE_LOCK ,"yaffs locking %p" , current); +mutex_lock (&(yaffs_dev_to_lc (dev)->gross_lock)); +yaffs_trace (YAFFS_TRACE_LOCK ,"yaffs locked %p" , current); +} + +static void yaffs_gross_unlock (struct yaffs_dev *dev) +{ +yaffs_trace (YAFFS_TRACE_LOCK ,"yaffs unlocking %p" , current); +mutex_unlock (&(yaffs_dev_to_lc (dev)->gross_lock)); +} + +static void yaffs_fill_inode_from_obj (struct inode *inode, +struct yaffs_obj *obj); + +static struct inode *yaffs_iget (struct super_block *sb,unsigned long ino) +{ +struct inode *inode; +struct yaffs_obj *obj; +struct yaffs_dev *dev =yaffs_super_to_dev (sb); + +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_iget for %lu" , ino); + + inode =iget_locked (sb, ino); +if (!inode) +return ERR_PTR (-ENOMEM ); +if (!(inode->i_state &I_NEW )) +return inode; + +/* NB This is called as a side effect of other functions, but +* we had to release the lock to prevent deadlocks, so +* need to lock again. +*/ + +yaffs_gross_lock (dev); + + obj =yaffs_find_by_number (dev, inode->i_ino); +yaffs_fill_inode_from_obj (inode, obj); + +yaffs_gross_unlock (dev); + +unlock_new_inode (inode); +return inode; +} + +struct inode *yaffs_get_inode (struct super_block *sb,int mode,int dev, +struct yaffs_obj *obj) +{ +struct inode *inode; + +if (!sb) { +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_get_inode for NULL super_block!!" ); +return NULL ; + + } + +if (!obj) { +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_get_inode for NULL object!!" ); +return NULL ; + + } + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_get_inode for object %d" , + obj->obj_id); + + inode =yaffs_iget (sb, obj->obj_id); +if (IS_ERR (inode)) +return NULL ; + +/* NB Side effect: iget calls back to yaffs_read_inode(). */ +/* iget also increments the inode's i_count */ +/* NB You can't be holding gross_lock or deadlock will happen! */ + +return inode; +} + +static int yaffs_mknod (struct inode *dir,struct dentry *dentry,int mode, +dev_t rdev) +{ +struct inode *inode; +struct yaffs_obj *obj =NULL ; +struct yaffs_dev *dev; +struct yaffs_obj *parent =yaffs_inode_to_obj (dir); +int error; +uid_t uid = current->cred->fsuid; +gid_t gid = + (dir->i_mode &S_ISGID ) ? dir->i_gid : current->cred->fsgid; + +if ((dir->i_mode &S_ISGID ) &&S_ISDIR (mode)) + mode |=S_ISGID ; + +if (!parent) { +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_mknod: could not get parent object" ); +return -EPERM ; + } + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_mknod: parent object %d type %d" , + parent->obj_id, parent->variant_type); + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_mknod: making oject for %s, mode %x dev %x" , + dentry->d_name.name, mode, rdev); + + dev = parent->my_dev; + +yaffs_gross_lock (dev); + +if (yaffs_get_n_free_chunks (dev) <1 ) { + error = -ENOSPC ; + goto err_out; + } + +switch (mode &S_IFMT ) { +default : +/* Special (socket, fifo, device...) */ +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_mknod: making special" ); + obj = +yaffs_create_special (parent, dentry->d_name.name, mode, uid, + gid,old_encode_dev (rdev)); +break ; +case S_IFREG :/* file */ +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_mknod: making file" ); + obj =yaffs_create_file (parent, dentry->d_name.name, mode, uid, + gid); +break ; +case S_IFDIR :/* directory */ +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_mknod: making directory" ); + obj =yaffs_create_dir (parent, dentry->d_name.name, mode, + uid, gid); +break ; +case S_IFLNK :/* symlink */ +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_mknod: making symlink" ); + obj =NULL ;/* Do we ever get here? */ +break ; + } + +if (!obj) { + error = -ENOMEM ; + goto err_out; + } + +/* Can not call yaffs_get_inode() with gross lock held */ +yaffs_gross_unlock (dev); + + + inode =yaffs_get_inode (dir->i_sb, mode, rdev, obj); +d_instantiate (dentry, inode); +update_dir_time (dir); +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_mknod created object %d count = %d" , + obj->obj_id,atomic_read (&inode->i_count)); +yaffs_fill_inode_from_obj (dir, parent); +return 0 ; + +err_out: +yaffs_gross_unlock (dev); +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_mknod error %d" , error); +return error; + +} + +static int yaffs_mkdir (struct inode *dir,struct dentry *dentry,int mode)
fs/yaffs2/yaffs_vfs.c line 286
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+{ +return yaffs_mknod (dir, dentry, mode |S_IFDIR ,0 ); +} + +static int yaffs_create (struct inode *dir,struct dentry *dentry,int mode, +struct nameidata *n) +{ +return yaffs_mknod (dir, dentry, mode |S_IFREG ,0 ); +} +
It's often ideal to have a single return statement from a function, which makes it easier to verify that all paths through the code clean themselves up correctly. See below:
fs/yaffs2/yaffs_vfs.c line 293
Quoted 7 lines
fs/yaffs2/yaffs_vfs.c new file +7
-0+static int yaffs_link (struct dentry *old_dentry,struct inode *dir, +struct dentry *dentry) +{ +struct inode *inode = old_dentry->d_inode; +struct yaffs_obj *obj =NULL ; +struct yaffs_obj *link =NULL ; +struct yaffs_dev *dev;
int ret = 0;
Quoted 4 lines
fs/yaffs2/yaffs_vfs.c new file +4
-0+ +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_link" ); + + obj =yaffs_inode_to_obj (inode);
fs/yaffs2/yaffs_vfs.c line 307
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ dev = obj->my_dev; + +yaffs_gross_lock (dev); + +if (!S_ISDIR (inode->i_mode))/* Don't link directories */ + link = +yaffs_link_obj (yaffs_inode_to_obj (dir), dentry->d_name.name, + obj); + +if (link) {
if (!link) {
yaffs_gross_unlock(dev);
ret = -EPERM;
goto out;
}fs/yaffs2/yaffs_vfs.c line 319
Quoted 12 lines
fs/yaffs2/yaffs_vfs.c new file +12
-0+ old_dentry->d_inode->i_nlink =yaffs_get_obj_link_count (obj); +d_instantiate (dentry, old_dentry->d_inode); +atomic_inc (&old_dentry->d_inode->i_count); +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_link link count %d i_count %d" , + old_dentry->d_inode->i_nlink, +atomic_read (&old_dentry->d_inode->i_count)); + } + +yaffs_gross_unlock (dev); + +if (link) {
Don't need this if statement.
fs/yaffs2/yaffs_vfs.c line 324
Quoted 5 lines
fs/yaffs2/yaffs_vfs.c new file +5
-0+update_dir_time (dir); +return 0 ; + } + +return -EPERM ;
out:
return ret;
Quoted 9 lines
fs/yaffs2/yaffs_vfs.c new file +9
-0+} + +static int yaffs_symlink (struct inode *dir,struct dentry *dentry, +const char *symname) +{ +struct yaffs_obj *obj; +struct yaffs_dev *dev; +struct inode *inode; +uid_t uid = current->cred->fsuid;
fs/yaffs2/yaffs_vfs.c line 343
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+gid_t gid = + (dir->i_mode &S_ISGID ) ? dir->i_gid : current->cred->fsgid; + +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_symlink" ); + + dev =yaffs_inode_to_obj (dir)->my_dev; +yaffs_gross_lock (dev); + obj =yaffs_create_symlink (yaffs_inode_to_obj (dir), dentry->d_name.name, +S_IFLNK |S_IRWXUGO , uid, gid, symname); +yaffs_gross_unlock (dev);
What does yaffs_gross_lock actually protect? yaffs_create_symlink calls yaffs_create_obj, which looks like large parts of it can be run safely without the lock held. Probably the lock just needs to be around the entry of the new object, not the creation right?
Pushing the lock down into the functions it is around in this file will make it more obvious what the lock is protecting and make it easier to replace it with a more fine grained locking approach.
Quoted 13 lines
fs/yaffs2/yaffs_vfs.c new file +13
-0+ +if (!obj) { +yaffs_trace (YAFFS_TRACE_OS ,"symlink not created" ); +return -ENOMEM ; + } + + inode =yaffs_get_inode (dir->i_sb, obj->yst_mode,0 , obj); +d_instantiate (dentry, inode); +update_dir_time (dir); +yaffs_trace (YAFFS_TRACE_OS ,"symlink created OK" ); + +return 0 ; +}
fs/yaffs2/yaffs_vfs.c line 366
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ +static struct dentry *yaffs_lookup (struct inode *dir,struct dentry *dentry, +struct nameidata *n) +{ +struct yaffs_obj *obj; +struct inode *inode =NULL ; +struct yaffs_dev *dev =yaffs_inode_to_obj (dir)->my_dev; + +if (current !=yaffs_dev_to_lc (dev)->readdir_process) +yaffs_gross_lock (dev);
How come you don't need to acquire the lock if we are the readdir_process? This implies that the locking is messed up somewhere and this code exists to prevent a deadlock when call into this function within the same process by another function which is already holding the lock? This can lead to buggy code if the calling function is ever changed and we end up not holding the lock here when we should be.
If this is the case then the locking should be fixed so that these kind of situations don't happen (e.g. by pushing the lock down to the actual bits it protects). If there is another reason why the lock is selectively taken here then there should at least be a comment explaining the behaviour.
fs/yaffs2/yaffs_vfs.c line 374
Quoted 8 lines
fs/yaffs2/yaffs_vfs.c new file +8
-0+ +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_lookup for %d:%s" , +yaffs_inode_to_obj (dir)->obj_id, dentry->d_name.name); + + obj =yaffs_find_by_name (yaffs_inode_to_obj (dir), dentry->d_name.name); + + obj =yaffs_get_equivalent_obj (obj);/* in case it was a hardlink */
Pushing the lock down into these functions, which probably reduces it to being around the actual object lookup, will remove the need to temporarily release the lock while calling yaffs_get_inode below.
Quoted 37 lines
fs/yaffs2/yaffs_vfs.c new file +37
-0+ +/* Can't hold gross lock when calling yaffs_get_inode() */ +if (current !=yaffs_dev_to_lc (dev)->readdir_process) +yaffs_gross_unlock (dev); + +if (obj) { +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_lookup found %d" , obj->obj_id); + + inode =yaffs_get_inode (dir->i_sb, obj->yst_mode,0 , obj); + +if (inode) { +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_loookup dentry" ); +d_add (dentry, inode); +/* return dentry; */ +return NULL ; + } + + }else { +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_lookup not found" ); + + } + +d_add (dentry, inode); + +return NULL ; +} + +static int yaffs_unlink (struct inode *dir,struct dentry *dentry) +{ +int ret_val; +struct yaffs_dev *dev; +struct yaffs_obj *obj; + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_unlink %d:%s" , + (int )(dir->i_ino), dentry->d_name.name);
fs/yaffs2/yaffs_vfs.c line 421
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ obj =yaffs_inode_to_obj (dir); + dev = obj->my_dev; + +yaffs_gross_lock (dev); + + ret_val =yaffs_unlinker (obj, dentry->d_name.name); + +if (ret_val ==YAFFS_OK ) { + dentry->d_inode->i_nlink--; + dir->i_version++;
I don't know the full details of the filesystem locking, but I get the impression that the above two statements should already be protected by the vfs locks? If so the yaffs gross lock should be just protecting yaffs_unlinker (and ideally pushed down into it).
Quoted 53 lines
fs/yaffs2/yaffs_vfs.c new file +53
-0+yaffs_gross_unlock (dev); +mark_inode_dirty (dentry->d_inode); +update_dir_time (dir); +return 0 ; + } +yaffs_gross_unlock (dev); +return -ENOTEMPTY ; +} + +static int yaffs_sync_object (struct file *file,int datasync) +{ + +struct yaffs_obj *obj; +struct yaffs_dev *dev; +struct dentry *dentry = file->f_path.dentry; + + obj =yaffs_dentry_to_obj (dentry); + + dev = obj->my_dev; + +yaffs_trace (YAFFS_TRACE_OS |YAFFS_TRACE_SYNC ,"yaffs_sync_object" ); +yaffs_gross_lock (dev); +yaffs_flush_file (obj,1 , datasync); +yaffs_gross_unlock (dev); +return 0 ; +} +/* +* The VFS layer already does all the dentry stuff for rename. +* +* NB: POSIX says you can rename an object over an old object of the same name +*/ +static int yaffs_rename (struct inode *old_dir,struct dentry *old_dentry, +struct inode *new_dir,struct dentry *new_dentry) +{ +struct yaffs_dev *dev; +int ret_val =YAFFS_FAIL ; +struct yaffs_obj *target; + +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_rename" ); + dev =yaffs_inode_to_obj (old_dir)->my_dev; + +yaffs_gross_lock (dev); + +/* Check if the target is an existing directory that is not empty. */ + target =yaffs_find_by_name (yaffs_inode_to_obj (new_dir), + new_dentry->d_name.name); + +if (target && target->variant_type ==YAFFS_OBJECT_TYPE_DIRECTORY && + !list_empty (&target->variant.dir_variant.children)) { + +yaffs_trace (YAFFS_TRACE_OS ,"target is non-empty dir" ); + + ret_val =YAFFS_FAIL ;
fs/yaffs2/yaffs_vfs.c line 484
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ }else { +/* Now does unlinking internally using shadowing mechanism */ +yaffs_trace (YAFFS_TRACE_OS ,"calling yaffs_rename_obj" ); + + ret_val =yaffs_rename_obj (yaffs_inode_to_obj (old_dir), + old_dentry->d_name.name, +yaffs_inode_to_obj (new_dir), + new_dentry->d_name.name); + } +yaffs_gross_unlock (dev);
Looks like the lock should just be around (pushed down into) yaffs_find_by_name and yaffs_rename_obj.
fs/yaffs2/yaffs_vfs.c line 488
Quoted 4 lines
fs/yaffs2/yaffs_vfs.c new file +4
-0+ +if (ret_val ==YAFFS_OK ) { +if (target) { + new_dentry->d_inode->i_nlink--;
Okay, we did this above with gross lock held, now we do it without holding the lock.
Quoted 61 lines
fs/yaffs2/yaffs_vfs.c new file +61
-0+mark_inode_dirty (new_dentry->d_inode); + } + +update_dir_time (old_dir); +if (old_dir != new_dir) +update_dir_time (new_dir); +return 0 ; + }else { +return -ENOTEMPTY ; + } +} + +static int yaffs_setattr (struct dentry *dentry,struct iattr *attr) +{ +struct inode *inode = dentry->d_inode; +int error =0 ; +struct yaffs_dev *dev; +int result; + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_setattr of object %d" , +yaffs_inode_to_obj (inode)->obj_id); + +/* Fail if a requested resize >= 2GB */ +if (attr->ia_valid &ATTR_SIZE && (attr->ia_size >>31 )) + error = -EINVAL ; + +if (!error) + error =inode_change_ok (inode, attr); + +if (!error) { +setattr_copy (inode, attr); +yaffs_trace (YAFFS_TRACE_OS ,"inode_setattr called" ); +if (attr->ia_valid &ATTR_SIZE ) { +truncate_setsize (inode, attr->ia_size); + inode->i_blocks = (inode->i_size +511 ) >>9 ; + } + dev =yaffs_inode_to_obj (inode)->my_dev; +if (attr->ia_valid &ATTR_SIZE ) { +yaffs_trace (YAFFS_TRACE_OS ,"resize to %d(%x)" , + (int )(attr->ia_size), + (int )(attr->ia_size)); + } + +yaffs_gross_lock (dev); + result =yaffs_set_attribs (yaffs_inode_to_obj (inode), attr); +if (result !=YAFFS_OK ) + error = -EPERM ; +yaffs_gross_unlock (dev); + } + +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_setattr done returning %d" , error); + +return error; +} + +#ifdef CONFIG_YAFFS_XATTR +static int yaffs_setxattr (struct dentry *dentry,const char *name, +const void *value,size_t size,int flags) +{ +struct inode *inode = dentry->d_inode;
fs/yaffs2/yaffs_vfs.c line 559
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+int error; +struct yaffs_dev *dev; +struct yaffs_obj *obj =yaffs_inode_to_obj (inode); + +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_setxattr of object %d" , obj->obj_id); + + dev = obj->my_dev; +yaffs_gross_lock (dev); + error =yaffs_set_xattrib (obj, name, value, size, flags); +yaffs_gross_unlock (dev);
This should almost definitely be a separate lock to the other operations. Shouldn't need to lock the whole filesystem to update an attribute on a single object, at least not for the entire operation.
Quoted 79 lines
fs/yaffs2/yaffs_vfs.c new file +79
-0+ +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_setxattr done returning %d" , error); + +return error; +} + +static ssize_t yaffs_getxattr (struct dentry *dentry,const char *name, +void *buff,size_t size) +{ +struct inode *inode = dentry->d_inode; +int error; +struct yaffs_dev *dev; +struct yaffs_obj *obj =yaffs_inode_to_obj (inode); + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_getxattr \"%s\" from object %d" , + name, obj->obj_id); + + dev = obj->my_dev; +yaffs_gross_lock (dev); + error =yaffs_get_xattrib (obj, name, buff, size); +yaffs_gross_unlock (dev); + +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_getxattr done returning %d" , error); + +return error; +} + +static int yaffs_removexattr (struct dentry *dentry,const char *name) +{ +struct inode *inode = dentry->d_inode; +int error; +struct yaffs_dev *dev; +struct yaffs_obj *obj =yaffs_inode_to_obj (inode); + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_removexattr of object %d" , obj->obj_id); + + dev = obj->my_dev; +yaffs_gross_lock (dev); + error =yaffs_remove_xattrib (obj, name); +yaffs_gross_unlock (dev); + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_removexattr done returning %d" , error); + +return error; +} + +static ssize_t yaffs_listxattr (struct dentry *dentry,char *buff,size_t size) +{ +struct inode *inode = dentry->d_inode; +int error; +struct yaffs_dev *dev; +struct yaffs_obj *obj =yaffs_inode_to_obj (inode); + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_listxattr of object %d" , obj->obj_id); + + dev = obj->my_dev; +yaffs_gross_lock (dev); + error =yaffs_list_xattrib (obj, buff, size); +yaffs_gross_unlock (dev); + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_listxattr done returning %d" , error); + +return error; +} + +#endif + +static const struct inode_operations yaffs_dir_inode_operations = { + .create = yaffs_create, + .lookup = yaffs_lookup, + .link = yaffs_link, + .unlink = yaffs_unlink, + .symlink = yaffs_symlink, + .mkdir = yaffs_mkdir,
fs/yaffs2/yaffs_vfs.c line 648
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ .rmdir = yaffs_unlink, + .mknod = yaffs_mknod, + .rename = yaffs_rename, + .setattr = yaffs_setattr, +#ifdef CONFIG_YAFFS_XATTR + .setxattr = yaffs_setxattr, + .getxattr = yaffs_getxattr, + .listxattr = yaffs_listxattr, + .removexattr = yaffs_removexattr, +#endif
Nitpick. Tab delimiting struct initialisations makes them easier to read.
Quoted 100 lines
fs/yaffs2/yaffs_vfs.c new file +100
-0+}; +/*-----------------------------------------------------------------*/ +/* Directory search context allows us to unlock access to yaffs during +* filldir without causing problems with the directory being modified. +* This is similar to the tried and tested mechanism used in yaffs direct. +* +* A search context iterates along a doubly linked list of siblings in the +* directory. If the iterating object is deleted then this would corrupt +* the list iteration, likely causing a crash. The search context avoids +* this by using the remove_obj_fn to move the search context to the +* next object before the object is deleted. +* +* Many readdirs (and thus seach conexts) may be alive simulateously so +* each struct yaffs_dev has a list of these. +* +* A seach context lives for the duration of a readdir. +* +* All these functions must be called while yaffs is locked. +*/ + +struct yaffs_search_context { +struct yaffs_dev *dev; +struct yaffs_obj *dir_obj; +struct yaffs_obj *next_return; +struct list_head others; +}; + +/* +* yaffs_new_search() creates a new search context, initialises it and +* adds it to the device's search context list. +* +* Called at start of readdir. +*/ +static struct yaffs_search_context *yaffs_new_search (struct yaffs_obj *dir) +{ +struct yaffs_dev *dev = dir->my_dev; +struct yaffs_search_context *sc = +kmalloc (sizeof (struct yaffs_search_context ),GFP_NOFS ); + +if (!sc) +return NULL ; + + sc->dir_obj = dir; + sc->dev = dev; +if (list_empty (&sc->dir_obj->variant.dir_variant.children)) + sc->next_return =NULL ; +else + sc->next_return = +list_entry (dir->variant.dir_variant.children.next, + struct yaffs_obj, siblings); +INIT_LIST_HEAD (&sc->others); +list_add (&sc->others, &(yaffs_dev_to_lc (dev)->search_contexts)); + +return sc; +} + +/* +* yaffs_search_end() disposes of a search context and cleans up. +*/ +static void yaffs_search_end (struct yaffs_search_context *sc) +{ +if (sc) { +list_del (&sc->others); +kfree (sc); + } +} + +/* +* yaffs_search_advance() moves a search context to the next object. +* Called when the search iterates or when an object removal causes +* the search context to be moved to the next object. +*/ +static void yaffs_search_advance (struct yaffs_search_context *sc) +{ +if (!sc) +return ; + +if (sc->next_return ==NULL || +list_empty (&sc->dir_obj->variant.dir_variant.children)) + sc->next_return =NULL ; +else { +struct list_head *next = sc->next_return->siblings.next; + +if (next == &sc->dir_obj->variant.dir_variant.children) + sc->next_return =NULL ;/* end of list */ +else + sc->next_return = + list_entry(next, struct yaffs_obj, siblings); + } +} + +/* +* yaffs_remove_obj_callback() is called when an object is unlinked. +* We check open search contexts and advance any which are currently +* on the object being iterated. +*/ +static void yaffs_remove_obj_callback (struct yaffs_obj *obj) +{ + +struct list_head *i;
fs/yaffs2/yaffs_vfs.c line 758
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+struct yaffs_search_context *sc; +struct list_head *search_contexts = + &(yaffs_dev_to_lc (obj->my_dev)->search_contexts); + +/* Iterate through the directory search contexts. +* If any are currently on the object being removed, then advance +* the search context to the next object to prevent a hanging pointer. +*/ +list_for_each (i, search_contexts) { + sc = list_entry(i, struct yaffs_search_context, others);
list_for_each_entry.
Quoted 37 lines
fs/yaffs2/yaffs_vfs.c new file +37
-0+if (sc->next_return == obj) +yaffs_search_advance (sc); + } + +} + +static int yaffs_readdir (struct file *f,void *dirent,filldir_t filldir) +{ +struct yaffs_obj *obj; +struct yaffs_dev *dev; +struct yaffs_search_context *sc; +struct inode *inode = f->f_dentry->d_inode; +unsigned long offset, curoffs; +struct yaffs_obj *l; +int ret_val =0 ; +char name[YAFFS_MAX_NAME_LENGTH +1 ]; + + obj =yaffs_dentry_to_obj (f->f_dentry); + dev = obj->my_dev; + +yaffs_gross_lock (dev); + +yaffs_dev_to_lc (dev)->readdir_process = current; + + offset = f->f_pos; + + sc =yaffs_new_search (obj); +if (!sc) { + ret_val = -ENOMEM ; + goto out; + } + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_readdir: starting at %d" , (int )offset); + +if (offset ==0 ) { + yaffs_trace(YAFFS_TRACE_OS,
fs/yaffs2/yaffs_vfs.c line 805
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+"yaffs_readdir: entry . ino %d" , + (int)inode->i_ino); +yaffs_gross_unlock (dev); +if (filldir (dirent,"." ,1 , offset, inode->i_ino,DT_DIR ) <0 ) { +yaffs_gross_lock (dev); + goto out; + } +yaffs_gross_lock (dev); + offset++; + f->f_pos++;
offset is local and f->f_pos is protected by vfs locks? (other filesystems don't lock around it), so the lock doesn't need to be held here. We release it a few lines down in the offset = 1 case anyway.
Quoted 16 lines
fs/yaffs2/yaffs_vfs.c new file +16
-0+ } +if (offset ==1 ) { +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_readdir: entry .. ino %d" , + (int )f->f_dentry->d_parent->d_inode->i_ino); +yaffs_gross_unlock (dev); +if (filldir (dirent,".." ,2 , offset, + f->f_dentry->d_parent->d_inode->i_ino, +DT_DIR ) <0 ) { +yaffs_gross_lock (dev); + goto out; + } +yaffs_gross_lock (dev); + offset++; + f->f_pos++; + }
fs/yaffs2/yaffs_vfs.c line 831
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ + curoffs =1 ; + +/* If the directory has changed since the open or last call to +readdir, rewind to after the 2 canned entries. */ +if (f->f_version != inode->i_version) { + offset =2 ; + f->f_pos = offset; + f->f_version = inode->i_version; + }
We probably don't need to hold the yaffs lock across any of this.
fs/yaffs2/yaffs_vfs.c line 838
Quoted 7 lines
fs/yaffs2/yaffs_vfs.c new file +7
-0+ +while (sc->next_return) { + curoffs++; + l = sc->next_return; +if (curoffs >= offset) { +int this_inode =yaffs_get_obj_inode (l); +int this_type =yaffs_get_obj_type (l);
We call yaffs_get_obj_inode elsewhere without the lock held and yaffs_get_obj_type doesn't do anything that would require the entire filesystem to be locked, so we probably don't need the lock held here.
fs/yaffs2/yaffs_vfs.c line 840
Quoted 2 lines
fs/yaffs2/yaffs_vfs.c new file +2
-0+ +yaffs_get_obj_name (l, name,YAFFS_MAX_NAME_LENGTH +1 );
We may need it here, but fine grained locking inside yaffs_guts would be more ideal.
Quoted 8 lines
fs/yaffs2/yaffs_vfs.c new file +8
-0+yaffs_trace (YAFFS_TRACE_OS , +"yaffs_readdir: %s inode %d" , + name,yaffs_get_obj_inode (l)); + +yaffs_gross_unlock (dev); + + if (filldir(dirent, + name,
fs/yaffs2/yaffs_vfs.c line 858
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ strlen(name), + offset, this_inode, this_type) < 0) { +yaffs_gross_lock (dev); + goto out; + } + +yaffs_gross_lock (dev); + + offset++; + f->f_pos++;
Don't need it here. So I think the lock can at least be reduced to being around yaffs_get_obj_name in this loop, yaffs_new_search above and yaffs_search_advance/yaffs_search_end and the setting of readdir_process below.
Quoted 8 lines
fs/yaffs2/yaffs_vfs.c new file +8
-0+ } +yaffs_search_advance (sc); + } + +out: +yaffs_search_end (sc); +yaffs_dev_to_lc (dev)->readdir_process =NULL ; +yaffs_gross_unlock (dev);
fs/yaffs2/yaffs_vfs.c line 876
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ +return ret_val; +} + +static const struct file_operations yaffs_dir_operations = { + .read = generic_read_dir, + .readdir = yaffs_readdir, + .fsync = yaffs_sync_object, + .llseek = generic_file_llseek, +};
Nitpick. Tab delimit.
Quoted 23 lines
fs/yaffs2/yaffs_vfs.c new file +23
-0+ + + +static int yaffs_file_flush (struct file *file,fl_owner_t id) +{ +struct yaffs_obj *obj =yaffs_dentry_to_obj (file->f_dentry); +struct yaffs_dev *dev = obj->my_dev; + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_file_flush object %d (%s)" , + obj->obj_id, obj->dirty ?"dirty" :"clean" ); + +yaffs_gross_lock (dev); + +yaffs_flush_file (obj,1 ,0 ); + +yaffs_gross_unlock (dev); + +return 0 ; +} + +static const struct file_operations yaffs_file_operations = { + .read = do_sync_read,
fs/yaffs2/yaffs_vfs.c line 909
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, + .mmap = generic_file_mmap, + .flush = yaffs_file_flush, + .fsync = yaffs_sync_object, + .splice_read = generic_file_splice_read, + .splice_write = generic_file_splice_write, + .llseek = generic_file_llseek, +};
Nitpick. Tab delimit.
Quoted 20 lines
fs/yaffs2/yaffs_vfs.c new file +20
-0+ + +/* ExportFS support */ +static struct inode *yaffs2_nfs_get_inode (struct super_block *sb,uint64_t ino, +uint32_t generation) +{ +return yaffs_iget (sb, ino); +} + +static struct dentry *yaffs2_fh_to_dentry (struct super_block *sb, +struct fid *fid,int fh_len, +int fh_type) +{ +return generic_fh_to_dentry (sb, fid, fh_len, fh_type, + yaffs2_nfs_get_inode); +} + +static struct dentry *yaffs2_fh_to_parent (struct super_block *sb, +struct fid *fid,int fh_len, +int fh_type)
fs/yaffs2/yaffs_vfs.c line 939
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+{ +return generic_fh_to_parent (sb, fid, fh_len, fh_type, + yaffs2_nfs_get_inode); +} + +struct dentry *yaffs2_get_parent (struct dentry *dentry) +{ + +struct super_block *sb = dentry->d_inode->i_sb; +struct dentry *parent =ERR_PTR (-ENOENT );
Don't init this.
fs/yaffs2/yaffs_vfs.c line 946
Quoted 7 lines
fs/yaffs2/yaffs_vfs.c new file +7
-0+struct inode *inode; +unsigned long parent_ino; +struct yaffs_obj *d_obj; +struct yaffs_obj *parent_obj; + + d_obj =yaffs_inode_to_obj (dentry->d_inode); +
if (!d_obj) return -ERR_PTR(-ENOENT);
fs/yaffs2/yaffs_vfs.c line 949
Quoted 3 lines
fs/yaffs2/yaffs_vfs.c new file +3
-0+if (d_obj) { + parent_obj = d_obj->parent; +if (parent_obj) {
if (!parent_obj) return -ERR_PTR(-ENOENT);
fs/yaffs2/yaffs_vfs.c line 954
Quoted 5 lines
fs/yaffs2/yaffs_vfs.c new file +5
-0+ parent_ino =yaffs_get_obj_inode (parent_obj); + inode =yaffs_iget (sb, parent_ino); + +if (IS_ERR (inode)) { + parent =ERR_CAST (inode);
if (IS_ERR(inode)) return ERR_CAST(inode);
fs/yaffs2/yaffs_vfs.c line 959
Quoted 5 lines
fs/yaffs2/yaffs_vfs.c new file +5
-0+ }else { + parent =d_obtain_alias (inode); +if (!IS_ERR (parent)) { + parent =ERR_PTR (-ENOMEM ); +iput (inode);
return ERR_PTR(-ENOMEM);
fs/yaffs2/yaffs_vfs.c line 964
Quoted 5 lines
fs/yaffs2/yaffs_vfs.c new file +5
-0+ } + } + } + } +return parent;
You alternatively set parent and have gotos if you want to retain the single return statement.
Quoted 44 lines
fs/yaffs2/yaffs_vfs.c new file +44
-0+} + +/* Just declare a zero structure as a NULL value implies +* using the default functions of exportfs. +*/ + +static struct export_operations yaffs_export_ops = { + .fh_to_dentry = yaffs2_fh_to_dentry, + .fh_to_parent = yaffs2_fh_to_parent, + .get_parent = yaffs2_get_parent, +}; + + +/*-----------------------------------------------------------------*/ + +static int yaffs_readlink (struct dentry *dentry,char __user * buffer, +int buflen) +{ +unsigned char *alias; +int ret; +struct yaffs_dev *dev =yaffs_dentry_to_obj (dentry)->my_dev; + +yaffs_gross_lock (dev); + + alias =yaffs_get_symlink_alias (yaffs_dentry_to_obj (dentry)); + +yaffs_gross_unlock (dev); + +if (!alias) +return -ENOMEM ; + + ret =vfs_readlink (dentry, buffer, buflen, alias); +kfree (alias); +return ret; +} + +static void *yaffs_follow_link (struct dentry *dentry,struct nameidata *nd) +{ +unsigned char *alias; +void *ret; +struct yaffs_dev *dev =yaffs_dentry_to_obj (dentry)->my_dev; + +yaffs_gross_lock (dev); +
fs/yaffs2/yaffs_vfs.c line 1018
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ alias =yaffs_get_symlink_alias (yaffs_dentry_to_obj (dentry)); +yaffs_gross_unlock (dev); + +if (!alias) { + ret =ERR_PTR (-ENOMEM ); + goto out; + } + +nd_set_link (nd, alias); + ret = (void *)alias;
No need for the cast.
Quoted 60 lines
fs/yaffs2/yaffs_vfs.c new file +60
-0+out: +return ret; +} + +void yaffs_put_link (struct dentry *dentry,struct nameidata *nd,void *alias) +{ +kfree (alias); +} + + +static void yaffs_unstitch_obj (struct inode *inode,struct yaffs_obj *obj) +{ +/* Clear the association between the inode and +* the struct yaffs_obj. +*/ + obj->my_inode =NULL ; +yaffs_inode_to_obj_lv (inode) =NULL ; + +/* If the object freeing was deferred, then the real +* free happens now. +* This should fix the inode inconsistency problem. +*/ +yaffs_handle_defered_free (obj); +} + +/* yaffs_evict_inode combines into one operation what was previously done in +* yaffs_clear_inode() and yaffs_delete_inode() +* +*/ +static void yaffs_evict_inode (struct inode *inode) +{ +struct yaffs_obj *obj; +struct yaffs_dev *dev; +int deleteme =0 ; + + obj =yaffs_inode_to_obj (inode); + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_evict_inode: ino %d, count %d %s" , + (int )inode->i_ino, +atomic_read (&inode->i_count), + obj ?"object exists" :"null object" ); + +if (!inode->i_nlink && !is_bad_inode (inode)) + deleteme =1 ; +truncate_inode_pages (&inode->i_data,0 ); +end_writeback (inode); + +if (deleteme && obj) { + dev = obj->my_dev; +yaffs_gross_lock (dev); +yaffs_del_obj (obj); +yaffs_gross_unlock (dev); + } +if (obj) { + dev = obj->my_dev; +yaffs_gross_lock (dev); +yaffs_unstitch_obj (inode, obj); +yaffs_gross_unlock (dev); + }
fs/yaffs2/yaffs_vfs.c line 1088
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ +} + +static void yaffs_touch_super (struct yaffs_dev *dev) +{ +struct super_block *sb =yaffs_dev_to_lc (dev)->super; + +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_touch_super() sb = %p" , sb); +if (sb) + sb->s_dirt =1 ;
Does this need lock/unlock_super around it?
Quoted 56 lines
fs/yaffs2/yaffs_vfs.c new file +56
-0+} + +static int yaffs_readpage_nolock (struct file *f,struct page *pg) +{ +/* Lifted from jffs2 */ + +struct yaffs_obj *obj; +unsigned char *pg_buf; +int ret; +struct yaffs_dev *dev; + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_readpage_nolock at %08x, size %08x" , + (unsigned )(pg->index <<PAGE_CACHE_SHIFT ), + (unsigned )PAGE_CACHE_SIZE ); + + obj =yaffs_dentry_to_obj (f->f_dentry); + + dev = obj->my_dev; + +BUG_ON (!PageLocked (pg)); + + pg_buf =kmap (pg); +/* FIXME: Can kmap fail? */ + +yaffs_gross_lock (dev); + + ret =yaffs_file_rd (obj, pg_buf, + pg->index <<PAGE_CACHE_SHIFT ,PAGE_CACHE_SIZE ); + +yaffs_gross_unlock (dev); + +if (ret >=0 ) + ret =0 ; + +if (ret) { +ClearPageUptodate (pg); +SetPageError (pg); + }else { +SetPageUptodate (pg); +ClearPageError (pg); + } + +flush_dcache_page (pg); +kunmap (pg); + +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_readpage_nolock done" ); +return ret; +} + +static int yaffs_readpage_unlock (struct file *f,struct page *pg) +{ +int ret =yaffs_readpage_nolock (f, pg); + +unlock_page (pg); +return ret;
fs/yaffs2/yaffs_vfs.c line 1154
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+} + +static int yaffs_readpage (struct file *f,struct page *pg) +{ +int ret; + +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_readpage" ); + ret =yaffs_readpage_unlock (f, pg); +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_readpage done" ); +return ret;
This looks a bit odd, but jffs2 does it this way too (with an additonal lock held).
Quoted 112 lines
fs/yaffs2/yaffs_vfs.c new file +112
-0+} + +/* writepage inspired by/stolen from smbfs */ + +static int yaffs_writepage (struct page *page,struct writeback_control *wbc) +{ +struct yaffs_dev *dev; +struct address_space *mapping = page->mapping; +struct inode *inode; +unsigned long end_index; +char *buffer; +struct yaffs_obj *obj; +int n_written =0 ; +unsigned n_bytes; +loff_t i_size; + +if (!mapping) +BUG (); + inode = mapping->host; +if (!inode) +BUG (); + i_size =i_size_read (inode); + + end_index = i_size >>PAGE_CACHE_SHIFT ; + +if (page->index < end_index) + n_bytes =PAGE_CACHE_SIZE ; +else { + n_bytes = i_size & (PAGE_CACHE_SIZE -1 ); + +if (page->index > end_index || !n_bytes) { +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_writepage at %08x, inode size = %08x!!!" , + (unsigned )(page->index <<PAGE_CACHE_SHIFT ), + (unsigned )inode->i_size); +yaffs_trace (YAFFS_TRACE_OS , +" -> don't care!!" ); + +zero_user_segment (page,0 ,PAGE_CACHE_SIZE ); +set_page_writeback (page); +unlock_page (page); +end_page_writeback (page); +return 0 ; + } + } + +if (n_bytes !=PAGE_CACHE_SIZE ) +zero_user_segment (page, n_bytes,PAGE_CACHE_SIZE ); + +get_page (page); + + buffer =kmap (page); + + obj =yaffs_inode_to_obj (inode); + dev = obj->my_dev; +yaffs_gross_lock (dev); + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_writepage at %08x, size %08x" , + (unsigned )(page->index <<PAGE_CACHE_SHIFT ), n_bytes); +yaffs_trace (YAFFS_TRACE_OS , +"writepag0: obj = %05x, ino = %05x" , + (int )obj->variant.file_variant.file_size, (int )inode->i_size); + + n_written =yaffs_wr_file (obj, buffer, + page->index <<PAGE_CACHE_SHIFT , n_bytes,0 ); + +yaffs_touch_super (dev); + +yaffs_trace (YAFFS_TRACE_OS , +"writepag1: obj = %05x, ino = %05x" , + (int )obj->variant.file_variant.file_size, (int )inode->i_size); + +yaffs_gross_unlock (dev); + +kunmap (page); +set_page_writeback (page); +unlock_page (page); +end_page_writeback (page); +put_page (page); + +return (n_written == n_bytes) ?0 : -ENOSPC ; +} + +/* Space holding and freeing is done to ensure we have space available for +* write_begin/end. +* For now we just assume few parallel writes and check against a small +* number. +* Todo: need to do this with a counter to handle parallel reads better +*/ + +static ssize_t yaffs_hold_space (struct file *f) +{ +struct yaffs_obj *obj; +struct yaffs_dev *dev; +int n_free_chunks; + + obj =yaffs_dentry_to_obj (f->f_dentry); + + dev = obj->my_dev; + +yaffs_gross_lock (dev); + + n_free_chunks =yaffs_get_n_free_chunks (dev); + +yaffs_gross_unlock (dev); + +return (n_free_chunks >20 ) ?1 :0 ; +} + +static void yaffs_release_space (struct file *f) +{
fs/yaffs2/yaffs_vfs.c line 1276
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+struct yaffs_obj *obj; +struct yaffs_dev *dev; + + obj =yaffs_dentry_to_obj (f->f_dentry); + + dev = obj->my_dev; + +yaffs_gross_lock (dev); + +yaffs_gross_unlock (dev);
What is this for? Delete it.
Quoted 73 lines
fs/yaffs2/yaffs_vfs.c new file +73
-0+} + +static int yaffs_write_begin (struct file *filp,struct address_space *mapping, +loff_t pos,unsigned len,unsigned flags, +struct page **pagep,void **fsdata) +{ +struct page *pg =NULL ; +pgoff_t index = pos >>PAGE_CACHE_SHIFT ; +int ret =0 ; +int space_held =0 ; + +/* Get a page */ + pg =grab_cache_page_write_begin (mapping, index, flags); + + *pagep = pg; +if (!pg) { + ret = -ENOMEM ; + goto out; + } +yaffs_trace (YAFFS_TRACE_OS , +"start yaffs_write_begin index %d(%x) uptodate %d" , + (int )index, (int )index,PageUptodate (pg) ?1 :0 ); + +/* Get fs space */ + space_held =yaffs_hold_space (filp); + +if (!space_held) { + ret = -ENOSPC ; + goto out; + } + +/* Update page if required */ + +if (!PageUptodate (pg)) + ret =yaffs_readpage_nolock (filp, pg); + +if (ret) + goto out; + +/* Happy path return */ +yaffs_trace (YAFFS_TRACE_OS ,"end yaffs_write_begin - ok" ); + +return 0 ; + +out: +yaffs_trace (YAFFS_TRACE_OS , +"end yaffs_write_begin fail returning %d" , ret); +if (space_held) +yaffs_release_space (filp); +if (pg) { +unlock_page (pg); +page_cache_release (pg); + } +return ret; +} + +static ssize_t yaffs_file_write (struct file *f,const char *buf,size_t n, +loff_t *pos) +{ +struct yaffs_obj *obj; +int n_written, ipos; +struct inode *inode; +struct yaffs_dev *dev; + + obj =yaffs_dentry_to_obj (f->f_dentry); + +if (!obj) { +/* This should not happen */ +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_file_write: hey obj is null!" ); +return -EINVAL ; + } +
fs/yaffs2/yaffs_vfs.c line 1359
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ dev = obj->my_dev; + +yaffs_gross_lock (dev); + + inode = f->f_dentry->d_inode; + +if (!S_ISBLK (inode->i_mode) && f->f_flags &O_APPEND ) + ipos = inode->i_size; +else + ipos = *pos;
Do we need gross lock around this? Seems like it should be protected by the inode's lock.
Quoted 14 lines
fs/yaffs2/yaffs_vfs.c new file +14
-0+ +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_file_write about to write writing %u(%x) bytes to object %d at %d(%x)" , + (unsigned )n, (unsigned )n, obj->obj_id, ipos, ipos); + + n_written =yaffs_wr_file (obj, buf, ipos, n,0 ); + +yaffs_touch_super (dev); + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_file_write: %d(%x) bytes written" , + (unsigned )n, (unsigned )n); + +if (n_written >0 ) {
fs/yaffs2/yaffs_vfs.c line 1383
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ ipos += n_written; + *pos = ipos; +if (ipos > inode->i_size) { + inode->i_size = ipos; + inode->i_blocks = (ipos +511 ) >>9 ; + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_file_write size updated to %d bytes, %d blocks" , + ipos, (int )(inode->i_blocks)); + }
Same for this bit. What is yaffs lock protecting in the above piece of code?
Quoted 39 lines
fs/yaffs2/yaffs_vfs.c new file +39
-0+ } +yaffs_gross_unlock (dev); +return (n_written ==0 ) && (n >0 ) ? -ENOSPC : n_written; +} + +static int yaffs_write_end (struct file *filp,struct address_space *mapping, +loff_t pos,unsigned len,unsigned copied, +struct page *pg,void *fsdadata) +{ +int ret =0 ; +void *addr, *kva; +uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE -1 ); + + kva =kmap (pg); + addr = kva + offset_into_page; + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_write_end addr %p pos %x n_bytes %d" , + addr, (unsigned )pos, copied); + + ret =yaffs_file_write (filp, addr, copied, &pos); + +if (ret != copied) { +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_write_end not same size ret %d copied %d" , + ret, copied); +SetPageError (pg); + } + +kunmap (pg); + +yaffs_release_space (filp); +unlock_page (pg); +page_cache_release (pg); +return ret; +} + +static int yaffs_statfs (struct dentry *dentry,struct kstatfs *buf) +{
fs/yaffs2/yaffs_vfs.c line 1432
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+struct yaffs_dev *dev =yaffs_dentry_to_obj (dentry)->my_dev; +struct super_block *sb = dentry->d_sb; + +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_statfs" ); + +yaffs_gross_lock (dev); + + buf->f_type =YAFFS_MAGIC ; + buf->f_bsize = sb->s_blocksize; + buf->f_namelen =255 ;
Looking at other filesystems suggests that this is already protected by the vfs or does not require locking.
Quoted 37 lines
fs/yaffs2/yaffs_vfs.c new file +37
-0+ +if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk -1 )) { +/* Do this if chunk size is not a power of 2 */ + +uint64_t bytes_in_dev; +uint64_t bytes_free; + + bytes_in_dev = + ((uint64_t ) + ((dev->param.end_block - dev->param.start_block +1 ))) * + ((uint64_t ) (dev->param.chunks_per_block * + dev->data_bytes_per_chunk)); + +do_div (bytes_in_dev, sb->s_blocksize); +/* bytes_in_dev becomes the number of blocks */ + buf->f_blocks = bytes_in_dev; + + bytes_free = ((uint64_t ) (yaffs_get_n_free_chunks (dev))) * + ((uint64_t ) (dev->data_bytes_per_chunk)); + +do_div (bytes_free, sb->s_blocksize); + + buf->f_bfree = bytes_free; + + }else if (sb->s_blocksize > dev->data_bytes_per_chunk) { + + buf->f_blocks = + (dev->param.end_block - dev->param.start_block +1 ) * + dev->param.chunks_per_block / + (sb->s_blocksize / dev->data_bytes_per_chunk); + buf->f_bfree = +yaffs_get_n_free_chunks (dev) / + (sb->s_blocksize / dev->data_bytes_per_chunk); + }else { + buf->f_blocks = + (dev->param.end_block - dev->param.start_block +1 ) * + dev->param.chunks_per_block *
fs/yaffs2/yaffs_vfs.c line 1479
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ (dev->data_bytes_per_chunk / sb->s_blocksize); + + buf->f_bfree = +yaffs_get_n_free_chunks (dev) * + (dev->data_bytes_per_chunk / sb->s_blocksize); + } + + buf->f_files =0 ; + buf->f_ffree =0 ; + buf->f_bavail = buf->f_bfree;
and this probably doesn't need to be protected either. Shuffle the lock so that it is protecting just the parts it needs to.
fs/yaffs2/yaffs_vfs.c line 1489
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ +yaffs_gross_unlock (dev); +return 0 ; +} + +static void yaffs_flush_inodes (struct super_block *sb) +{ +struct inode *iptr; +struct yaffs_obj *obj; +
Possibly you want a comment saying that this function is called with gross lock held, since currently yaffs_flush_file requires gross lock to be held. Better solution would be to fix the locking :-).
fs/yaffs2/yaffs_vfs.c line 1500
Quoted 11 lines
fs/yaffs2/yaffs_vfs.c new file +11
-0+list_for_each_entry (iptr, &sb->s_inodes, i_sb_list) { + obj =yaffs_inode_to_obj (iptr); +if (obj) { +yaffs_trace (YAFFS_TRACE_OS , +"flushing obj %d" , obj->obj_id); +yaffs_flush_file (obj,1 ,0 ); + } + } +} + +static void yaffs_flush_super (struct super_block *sb,int do_checkpoint)
This is also called with gross lock held. Comment?
Quoted 147 lines
fs/yaffs2/yaffs_vfs.c new file +147
-0+{ +struct yaffs_dev *dev =yaffs_super_to_dev (sb); + +if (!dev) +return ; + +yaffs_flush_inodes (sb); +yaffs_update_dirty_dirs (dev); +yaffs_flush_whole_cache (dev); +if (do_checkpoint) +yaffs_checkpoint_save (dev); +} + +static unsigned yaffs_bg_gc_urgency (struct yaffs_dev *dev) +{ +unsigned erased_chunks = + dev->n_erased_blocks * dev->param.chunks_per_block; +struct yaffs_linux_context *context =yaffs_dev_to_lc (dev); +unsigned scattered =0 ;/* Free chunks not in an erased block */ + +if (erased_chunks < dev->n_free_chunks) + scattered = (dev->n_free_chunks - erased_chunks); + +if (!context->bg_running) +return 0 ; +else if (scattered < (dev->param.chunks_per_block *2 )) +return 0 ; +else if (erased_chunks > dev->n_free_chunks /2 ) +return 0 ; +else if (erased_chunks > dev->n_free_chunks /4 ) +return 1 ; +else +return 2 ; +} + +static int yaffs_do_sync_fs (struct super_block *sb,int request_checkpoint) +{ + +struct yaffs_dev *dev =yaffs_super_to_dev (sb); +unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint &4 ); +unsigned gc_urgent =yaffs_bg_gc_urgency (dev); +int do_checkpoint; + +yaffs_trace (YAFFS_TRACE_OS |YAFFS_TRACE_SYNC |YAFFS_TRACE_BACKGROUND , +"yaffs_do_sync_fs: gc-urgency %d %s %s%s" , + gc_urgent, + sb->s_dirt ?"dirty" :"clean" , + request_checkpoint ?"checkpoint requested" :"no checkpoint" , + oneshot_checkpoint ?" one-shot" :"" ); + +yaffs_gross_lock (dev); + do_checkpoint = ((request_checkpoint && !gc_urgent) || + oneshot_checkpoint) && !dev->is_checkpointed; + +if (sb->s_dirt || do_checkpoint) { +yaffs_flush_super (sb, !dev->is_checkpointed && do_checkpoint); + sb->s_dirt =0 ; +if (oneshot_checkpoint) + yaffs_auto_checkpoint &= ~4 ; + } +yaffs_gross_unlock (dev); + +return 0 ; +} + +/* +* yaffs background thread functions . +* yaffs_bg_thread_fn() the thread function +* yaffs_bg_start() launches the background thread. +* yaffs_bg_stop() cleans up the background thread. +* +* NB: +* The thread should only run after the yaffs is initialised +* The thread should be stopped before yaffs is unmounted. +* The thread should not do any writing while the fs is in read only. +*/ + +void yaffs_background_waker (unsigned long data) +{ +wake_up_process ((struct task_struct *)data); +} + +static int yaffs_bg_thread_fn (void *data) +{ +struct yaffs_dev *dev = (struct yaffs_dev *)data; +struct yaffs_linux_context *context =yaffs_dev_to_lc (dev); +unsigned long now = jiffies; +unsigned long next_dir_update = now; +unsigned long next_gc = now; +unsigned long expires; +unsigned int urgency; +int gc_result; +struct timer_list timer; + +yaffs_trace (YAFFS_TRACE_BACKGROUND , +"yaffs_background starting for dev %p" , (void *)dev); + +set_freezable (); +while (context->bg_running) { +yaffs_trace (YAFFS_TRACE_BACKGROUND ,"yaffs_background" ); + +if (kthread_should_stop ()) +break ; + +if (try_to_freeze ()) +continue ; + +yaffs_gross_lock (dev); + + now = jiffies; + +if (time_after (now, next_dir_update) && yaffs_bg_enable) { +yaffs_update_dirty_dirs (dev); + next_dir_update = now +HZ ; + } + +if (time_after (now, next_gc) && yaffs_bg_enable) { +if (!dev->is_checkpointed) { + urgency =yaffs_bg_gc_urgency (dev); + gc_result =yaffs_bg_gc (dev, urgency); +if (urgency >1 ) + next_gc = now +HZ /20 +1 ; +else if (urgency >0 ) + next_gc = now +HZ /10 +1 ; +else + next_gc = now +HZ *2 ; + }else { +/* +* gc not running so set to next_dir_update +* to cut down on wake ups +*/ + next_gc = next_dir_update; + } + } +yaffs_gross_unlock (dev); + expires = next_dir_update; +if (time_before (next_gc, expires)) + expires = next_gc; +if (time_before (expires, now)) + expires = now +HZ ; + +init_timer_on_stack (&timer); + timer.expires = expires +1 ; + timer.data = (unsigned long )current; + timer.function = yaffs_background_waker; + +set_current_state (TASK_INTERRUPTIBLE );
fs/yaffs2/yaffs_vfs.c line 1657
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+add_timer (&timer); +schedule (); +del_timer_sync (&timer); + } + +return 0 ; +} + +static int yaffs_bg_start (struct yaffs_dev *dev) +{
We hold the lock when calling this function too. This could possibly be a garbage collector lock?
Quoted 15 lines
fs/yaffs2/yaffs_vfs.c new file +15
-0+int retval =0 ; +struct yaffs_linux_context *context =yaffs_dev_to_lc (dev); + +if (dev->read_only) +return -1 ; + + context->bg_running =1 ; + + context->bg_thread =kthread_run (yaffs_bg_thread_fn, + (void *)dev,"yaffs-bg-%d" , + context->mount_id); + +if (IS_ERR (context->bg_thread)) { + retval =PTR_ERR (context->bg_thread); + context->bg_thread =NULL ;
fs/yaffs2/yaffs_vfs.c line 1682
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ context->bg_running =0 ; + } +return retval; +} + +static void yaffs_bg_stop (struct yaffs_dev *dev) +{ +struct yaffs_linux_context *ctxt =yaffs_dev_to_lc (dev); + + ctxt->bg_running =0 ;
This is not called with gross lock held (as far as I can tell) so the access on bg_running is potentially racy?
Quoted 23 lines
fs/yaffs2/yaffs_vfs.c new file +23
-0+ +if (ctxt->bg_thread) { +kthread_stop (ctxt->bg_thread); + ctxt->bg_thread =NULL ; + } +} + +static void yaffs_write_super (struct super_block *sb) +{ +unsigned request_checkpoint = (yaffs_auto_checkpoint >=2 ); + +yaffs_trace (YAFFS_TRACE_OS |YAFFS_TRACE_SYNC |YAFFS_TRACE_BACKGROUND , +"yaffs_write_super%s" , + request_checkpoint ?" checkpt" :"" ); + +yaffs_do_sync_fs (sb, request_checkpoint); + +} + +static int yaffs_sync_fs (struct super_block *sb,int wait) +{ +unsigned request_checkpoint = (yaffs_auto_checkpoint >=1 ); +
fs/yaffs2/yaffs_vfs.c line 1715
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+yaffs_trace (YAFFS_TRACE_OS |YAFFS_TRACE_SYNC , +"yaffs_sync_fs%s" , request_checkpoint ?" checkpt" :"" ); + +yaffs_do_sync_fs (sb, request_checkpoint); + +return 0 ; +} + +static LIST_HEAD (yaffs_context_list); +struct mutex yaffs_context_lock;
Make this static, and initialise it here?
Quoted 98 lines
fs/yaffs2/yaffs_vfs.c new file +98
-0+ +struct yaffs_options { +int inband_tags; +int skip_checkpoint_read; +int skip_checkpoint_write; +int no_cache; +int tags_ecc_on; +int tags_ecc_overridden; +int lazy_loading_enabled; +int lazy_loading_overridden; +int empty_lost_and_found; +int empty_lost_and_found_overridden; +}; + +#define MAX_OPT_LEN 30 +static int yaffs_parse_options (struct yaffs_options *options, +const char *options_str) +{ +char cur_opt[MAX_OPT_LEN +1 ]; +int p; +int error =0 ; + +/* Parse through the options which is a comma seperated list */ + +while (options_str && *options_str && !error) { +memset (cur_opt,0 ,MAX_OPT_LEN +1 ); + p =0 ; + +while (*options_str ==',' ) + options_str++; + +while (*options_str && *options_str !=',' ) { +if (p <MAX_OPT_LEN ) { + cur_opt[p] = *options_str; + p++; + } + options_str++; + } + +if (!strcmp (cur_opt,"inband-tags" )) { + options->inband_tags =1 ; + }else if (!strcmp (cur_opt,"tags-ecc-off" )) { + options->tags_ecc_on =0 ; + options->tags_ecc_overridden =1 ; + }else if (!strcmp (cur_opt,"tags-ecc-on" )) { + options->tags_ecc_on =1 ; + options->tags_ecc_overridden =1 ; + }else if (!strcmp (cur_opt,"lazy-loading-off" )) { + options->lazy_loading_enabled =0 ; + options->lazy_loading_overridden =1 ; + }else if (!strcmp (cur_opt,"lazy-loading-on" )) { + options->lazy_loading_enabled =1 ; + options->lazy_loading_overridden =1 ; + }else if (!strcmp (cur_opt,"empty-lost-and-found-off" )) { + options->empty_lost_and_found =0 ; + options->empty_lost_and_found_overridden =1 ; + }else if (!strcmp (cur_opt,"empty-lost-and-found-on" )) { + options->empty_lost_and_found =1 ; + options->empty_lost_and_found_overridden =1 ; + }else if (!strcmp (cur_opt,"no-cache" )) { + options->no_cache =1 ; + }else if (!strcmp (cur_opt,"no-checkpoint-read" )) { + options->skip_checkpoint_read =1 ; + }else if (!strcmp (cur_opt,"no-checkpoint-write" )) { + options->skip_checkpoint_write =1 ; + }else if (!strcmp (cur_opt,"no-checkpoint" )) { + options->skip_checkpoint_read =1 ; + options->skip_checkpoint_write =1 ; + }else { +printk (KERN_INFO "yaffs: Bad mount option \"%s\"\n" , + cur_opt); + error =1 ; + } + } +return error; +} + +static const struct address_space_operations yaffs_file_address_operations = { + .readpage = yaffs_readpage, + .writepage = yaffs_writepage, + .write_begin = yaffs_write_begin, + .write_end = yaffs_write_end, +}; + + + +static const struct inode_operations yaffs_file_inode_operations = { + .setattr = yaffs_setattr, +#ifdef CONFIG_YAFFS_XATTR + .setxattr = yaffs_setxattr, + .getxattr = yaffs_getxattr, + .listxattr = yaffs_listxattr, + .removexattr = yaffs_removexattr, +#endif +}; + +static const struct inode_operations yaffs_symlink_inode_operations = { + .readlink = yaffs_readlink,
fs/yaffs2/yaffs_vfs.c line 1823
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ .follow_link = yaffs_follow_link, + .put_link = yaffs_put_link, + .setattr = yaffs_setattr, +#ifdef CONFIG_YAFFS_XATTR + .setxattr = yaffs_setxattr, + .getxattr = yaffs_getxattr, + .listxattr = yaffs_listxattr, + .removexattr = yaffs_removexattr, +#endif +};
Nitpick. Tab delimit struct initialisations.
Quoted 301 lines
fs/yaffs2/yaffs_vfs.c new file +301
-0+ +static void yaffs_fill_inode_from_obj (struct inode *inode, +struct yaffs_obj *obj) +{ +u32 mode; + +if (!inode || !obj) { +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_fill_inode invalid parameters" ); +return ; + } + +/* Check mode against the variant type +* and attempt to repair if broken. */ + mode = obj->yst_mode; + +switch (obj->variant_type) { +case YAFFS_OBJECT_TYPE_FILE : +if (!S_ISREG (mode)) { + obj->yst_mode &= ~S_IFMT ; + obj->yst_mode |=S_IFREG ; + } +break ; +case YAFFS_OBJECT_TYPE_SYMLINK : +if (!S_ISLNK (mode)) { + obj->yst_mode &= ~S_IFMT ; + obj->yst_mode |=S_IFLNK ; + } +break ; +case YAFFS_OBJECT_TYPE_DIRECTORY : +if (!S_ISDIR (mode)) { + obj->yst_mode &= ~S_IFMT ; + obj->yst_mode |=S_IFDIR ; + } +break ; +case YAFFS_OBJECT_TYPE_UNKNOWN : +case YAFFS_OBJECT_TYPE_HARDLINK : +case YAFFS_OBJECT_TYPE_SPECIAL : +default : +/* TODO? */ +break ; + } + + inode->i_flags |=S_NOATIME ; + inode->i_ino = obj->obj_id; + inode->i_mode = obj->yst_mode; + inode->i_uid = obj->yst_uid; + inode->i_gid = obj->yst_gid; + + inode->i_rdev =old_decode_dev (obj->yst_rdev); + + inode->i_atime.tv_sec = (time_t) (obj->yst_atime); + inode->i_atime.tv_nsec =0 ; + inode->i_mtime.tv_sec = (time_t ) obj->yst_mtime; + inode->i_mtime.tv_nsec =0 ; + inode->i_ctime.tv_sec = (time_t ) obj->yst_ctime; + inode->i_ctime.tv_nsec =0 ; + inode->i_size =yaffs_get_obj_length (obj); + inode->i_blocks = (inode->i_size +511 ) >>9 ; + inode->i_nlink =yaffs_get_obj_link_count (obj); +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_fill_inode mode %x uid %d gid %d size %d count %d" , + inode->i_mode, inode->i_uid, inode->i_gid, + (int )inode->i_size,atomic_read (&inode->i_count)); + +switch (obj->yst_mode &S_IFMT ) { +default :/* fifo, device or socket */ +init_special_inode (inode, obj->yst_mode, +old_decode_dev (obj->yst_rdev)); +break ; +case S_IFREG :/* file */ + inode->i_op = &yaffs_file_inode_operations; + inode->i_fop = &yaffs_file_operations; + inode->i_mapping->a_ops = &yaffs_file_address_operations; +break ; +case S_IFDIR :/* directory */ + inode->i_op = &yaffs_dir_inode_operations; + inode->i_fop = &yaffs_dir_operations; +break ; +case S_IFLNK :/* symlink */ + inode->i_op = &yaffs_symlink_inode_operations; +break ; + } + +yaffs_inode_to_obj_lv (inode) = obj; + obj->my_inode = inode; +} + +static void yaffs_put_super (struct super_block *sb) +{ +struct yaffs_dev *dev =yaffs_super_to_dev (sb); + +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_put_super" ); + +yaffs_trace (YAFFS_TRACE_OS |YAFFS_TRACE_BACKGROUND , +"Shutting down yaffs background thread" ); +yaffs_bg_stop (dev); +yaffs_trace (YAFFS_TRACE_OS |YAFFS_TRACE_BACKGROUND , +"yaffs background thread shut down" ); + +yaffs_gross_lock (dev); + +yaffs_flush_super (sb,1 ); + +if (yaffs_dev_to_lc (dev)->put_super_fn) +yaffs_dev_to_lc (dev)->put_super_fn (sb); + +yaffs_deinitialise (dev); + +yaffs_gross_unlock (dev); +mutex_lock (&yaffs_context_lock); +list_del_init (&(yaffs_dev_to_lc (dev)->context_list)); +mutex_unlock (&yaffs_context_lock); + +if (yaffs_dev_to_lc (dev)->spare_buffer) { +kfree (yaffs_dev_to_lc (dev)->spare_buffer); +yaffs_dev_to_lc (dev)->spare_buffer =NULL ; + } + +kfree (dev); +} + +static void yaffs_mtd_put_super (struct super_block *sb) +{ +struct mtd_info *mtd =yaffs_dev_to_mtd (yaffs_super_to_dev (sb)); + +if (mtd->sync) + mtd->sync (mtd); + +put_mtd_device (mtd); +} + +static const struct super_operations yaffs_super_ops = { + .statfs = yaffs_statfs, + .put_super = yaffs_put_super, + .evict_inode = yaffs_evict_inode, + .sync_fs = yaffs_sync_fs, + .write_super = yaffs_write_super, +}; + +static struct super_block *yaffs_internal_read_super (int yaffs_version, +struct super_block *sb, +void *data,int silent) +{ +int n_blocks; +struct inode *inode =NULL ; +struct dentry *root; +struct yaffs_dev *dev =0 ; +char devname_buf[BDEVNAME_SIZE +1 ]; +struct mtd_info *mtd; +int err; +char *data_str = (char *)data; +struct yaffs_linux_context *context =NULL ; +struct yaffs_param *param; +int read_only =0 ; +struct yaffs_options options; +unsigned mount_id; +int found; +struct yaffs_linux_context *context_iterator; +struct list_head *l; + +if (!sb) { +printk (KERN_INFO "yaffs: sb is NULL\n" ); +return NULL ; + } + + sb->s_magic =YAFFS_MAGIC ; + sb->s_op = &yaffs_super_ops; + sb->s_flags |=MS_NOATIME ; + + read_only = ((sb->s_flags &MS_RDONLY ) !=0 ); + + sb->s_export_op = &yaffs_export_ops; + +if (!sb->s_dev) +printk (KERN_INFO "yaffs: sb->s_dev is NULL\n" ); +else if (!yaffs_devname (sb, devname_buf)) +printk (KERN_INFO "yaffs: devname is NULL\n" ); +else +printk (KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n" , + sb->s_dev, +yaffs_devname (sb, devname_buf), read_only ?"ro" :"rw" ); + +if (!data_str) + data_str ="" ; + +printk (KERN_INFO "yaffs: passed flags \"%s\"\n" , data_str); + +memset (&options,0 ,sizeof (options)); + +if (yaffs_parse_options (&options, data_str)) +/* Option parsing failed */ +return NULL ; + + sb->s_blocksize =PAGE_CACHE_SIZE ; + sb->s_blocksize_bits =PAGE_CACHE_SHIFT ; + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_read_super: Using yaffs%d" , yaffs_version); +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_read_super: block size %d" , (int )(sb->s_blocksize)); + +yaffs_trace (YAFFS_TRACE_ALWAYS , +"Attempting MTD mount of %u.%u,\"%s\"" , +MAJOR (sb->s_dev),MINOR (sb->s_dev), +yaffs_devname (sb, devname_buf)); + +/* Check it's an mtd device..... */ +if (MAJOR (sb->s_dev) !=MTD_BLOCK_MAJOR ) +return NULL ;/* This isn't an mtd device */ + +/* Get the device */ + mtd =get_mtd_device (NULL ,MINOR (sb->s_dev)); +if (!mtd) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"MTD device #%u doesn't appear to exist" , +MINOR (sb->s_dev)); +return NULL ; + } +/* Check it's NAND */ +if (mtd->type !=MTD_NANDFLASH ) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"MTD device is not NAND it's type %d" , + mtd->type); +return NULL ; + } + +yaffs_trace (YAFFS_TRACE_OS ," erase %p" , mtd->erase); +yaffs_trace (YAFFS_TRACE_OS ," read %p" , mtd->read); +yaffs_trace (YAFFS_TRACE_OS ," write %p" , mtd->write); +yaffs_trace (YAFFS_TRACE_OS ," readoob %p" , mtd->read_oob); +yaffs_trace (YAFFS_TRACE_OS ," writeoob %p" , mtd->write_oob); +yaffs_trace (YAFFS_TRACE_OS ," block_isbad %p" , mtd->block_isbad); +yaffs_trace (YAFFS_TRACE_OS ," block_markbad %p" , mtd->block_markbad); +yaffs_trace (YAFFS_TRACE_OS ," writesize %d" , mtd->writesize); +yaffs_trace (YAFFS_TRACE_OS ," oobsize %d" , mtd->oobsize); +yaffs_trace (YAFFS_TRACE_OS ," erasesize %d" , mtd->erasesize); +yaffs_trace (YAFFS_TRACE_OS ," size %lld" , mtd->size); + +#ifdef CONFIG_YAFFS_AUTO_YAFFS2 + +if (yaffs_version ==1 && mtd->writesize >=2048 ) { +yaffs_trace (YAFFS_TRACE_ALWAYS ,"auto selecting yaffs2" ); + yaffs_version =2 ; + } + +/* Added NCB 26/5/2006 for completeness */ +if (yaffs_version ==2 && !options.inband_tags && + mtd->writesize ==512 ) { +yaffs_trace (YAFFS_TRACE_ALWAYS ,"auto selecting yaffs1" ); + yaffs_version =1 ; + } +#endif + +if (yaffs_version ==2 ) { +/* Check for version 2 style functions */ +if (!mtd->erase || + !mtd->block_isbad || + !mtd->block_markbad || + !mtd->read || + !mtd->write || !mtd->read_oob || !mtd->write_oob) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"MTD device does not support required functions" ); +return NULL ; + } + +if ((mtd->writesize <YAFFS_MIN_YAFFS2_CHUNK_SIZE || + mtd->oobsize <YAFFS_MIN_YAFFS2_SPARE_SIZE ) && + !options.inband_tags) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"MTD device does not have the right page sizes" ); +return NULL ; + } + }else { +/* Check for V1 style functions */ +if (!mtd->erase || + !mtd->read || + !mtd->write || !mtd->read_oob || !mtd->write_oob) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"MTD device does not support required functions" ); +return NULL ; + } + +if (mtd->writesize <YAFFS_BYTES_PER_CHUNK || + mtd->oobsize !=YAFFS_BYTES_PER_SPARE ) { +yaffs_trace (YAFFS_TRACE_ALWAYS , +"MTD device does not support have the right page sizes" ); +return NULL ; + } + } + +/* OK, so if we got here, we have an MTD that's NAND and looks +* like it has the right capabilities +* Set the struct yaffs_dev up for mtd +*/ + +if (!read_only && !(mtd->flags &MTD_WRITEABLE )) { + read_only =1 ; +printk (KERN_INFO +"yaffs: mtd is read only, setting superblock read only" ); + sb->s_flags |=MS_RDONLY ;
fs/yaffs2/yaffs_vfs.c line 2134
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ } + + dev =kmalloc (sizeof (struct yaffs_dev ),GFP_KERNEL ); + context =kmalloc (sizeof (struct yaffs_linux_context ),GFP_KERNEL ); + +if (!dev || !context) { +kfree (dev); +kfree (context); + dev =NULL ; + context =NULL ;
Don't need the NULL assignments. Should also bail here rather than testing just !dev below.
fs/yaffs2/yaffs_vfs.c line 2146
Quoted 12 lines
fs/yaffs2/yaffs_vfs.c new file +12
-0+ } + +if (!dev) { +/* Deep shit could not allocate device structure */ +yaffs_trace (YAFFS_TRACE_ALWAYS , +"yaffs_read_super failed trying to allocate yaffs_dev" ); +return NULL ; + } +memset (dev,0 ,sizeof (struct yaffs_dev )); + param = &(dev->param); + +memset (context,0 ,sizeof (struct yaffs_linux_context ));
We will explode here if context allocation failed above.
Quoted 17 lines
fs/yaffs2/yaffs_vfs.c new file +17
-0+ dev->os_context = context; +INIT_LIST_HEAD (&(context->context_list)); + context->dev = dev; + context->super = sb; + + dev->read_only = read_only; + + sb->s_fs_info = dev; + + dev->driver_context = mtd; + param->name = mtd->name; + +/* Set up the memory size parameters.... */ + + n_blocks = +YCALCBLOCKS (mtd->size, + (YAFFS_CHUNKS_PER_BLOCK *YAFFS_BYTES_PER_CHUNK ));
fs/yaffs2/yaffs_vfs.c line 2173
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ + param->start_block =0 ; + param->end_block = n_blocks -1 ; + param->chunks_per_block =YAFFS_CHUNKS_PER_BLOCK ; + param->total_bytes_per_chunk =YAFFS_BYTES_PER_CHUNK ; + param->n_reserved_blocks =5 ; + param->n_caches = (options.no_cache) ?0 :10 ; + param->inband_tags = options.inband_tags; + +#ifdef CONFIG_YAFFS_DISABLE_LAZY_LOAD
Non-existent Kconfig option
fs/yaffs2/yaffs_vfs.c line 2186
Quoted 13 lines
fs/yaffs2/yaffs_vfs.c new file +13
-0+ param->disable_lazy_load =1 ; +#endif +#ifdef CONFIG_YAFFS_XATTR + param->enable_xattr =1 ; +#endif +if (options.lazy_loading_overridden) + param->disable_lazy_load = !options.lazy_loading_enabled; + +#ifdef CONFIG_YAFFS_DISABLE_TAGS_ECC + param->no_tags_ecc =1 ; +#endif + +#ifdef CONFIG_YAFFS_DISABLE_BACKGROUND
This ifdef block is empty. Change to ifndef.
Quoted 63 lines
fs/yaffs2/yaffs_vfs.c new file +63
-0+#else + param->defered_dir_update =1 ; +#endif + +if (options.tags_ecc_overridden) + param->no_tags_ecc = !options.tags_ecc_on; + +#ifdef CONFIG_YAFFS_EMPTY_LOST_AND_FOUND + param->empty_lost_n_found =1 ; +#endif + +#ifdef CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING + param->refresh_period =0 ; +#else + param->refresh_period =500 ; +#endif + +#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED + param->always_check_erased =1 ; +#endif + +if (options.empty_lost_and_found_overridden) + param->empty_lost_n_found = options.empty_lost_and_found; + +/* ... and the functions. */ +if (yaffs_version ==2 ) { + param->write_chunk_tags_fn = nandmtd2_write_chunk_tags; + param->read_chunk_tags_fn = nandmtd2_read_chunk_tags; + param->bad_block_fn = nandmtd2_mark_block_bad; + param->query_block_fn = nandmtd2_query_block; +yaffs_dev_to_lc (dev)->spare_buffer = +kmalloc (mtd->oobsize,GFP_NOFS ); + param->is_yaffs2 =1 ; + param->total_bytes_per_chunk = mtd->writesize; + param->chunks_per_block = mtd->erasesize / mtd->writesize; + n_blocks =YCALCBLOCKS (mtd->size, mtd->erasesize); + + param->start_block =0 ; + param->end_block = n_blocks -1 ; + }else { +/* use the MTD interface in yaffs_mtdif1.c */ + param->write_chunk_tags_fn = nandmtd1_write_chunk_tags; + param->read_chunk_tags_fn = nandmtd1_read_chunk_tags; + param->bad_block_fn = nandmtd1_mark_block_bad; + param->query_block_fn = nandmtd1_query_block; + param->is_yaffs2 =0 ; + } +/* ... and common functions */ + param->erase_fn = nandmtd_erase_block; + param->initialise_flash_fn = nandmtd_initialise; + +yaffs_dev_to_lc (dev)->put_super_fn = yaffs_mtd_put_super; + + param->sb_dirty_fn = yaffs_touch_super; + param->gc_control = yaffs_gc_control_callback; + +yaffs_dev_to_lc (dev)->super = sb; + +#ifndef CONFIG_YAFFS_DOES_ECC + param->use_nand_ecc =1 ; +#endif + + param->skip_checkpt_rd = options.skip_checkpoint_read;
fs/yaffs2/yaffs_vfs.c line 2259
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ param->skip_checkpt_wr = options.skip_checkpoint_write; + +mutex_lock (&yaffs_context_lock); +/* Get a mount id */ +for (mount_id =0 , found =0 ; !found; mount_id++) { + found =1 ; +list_for_each (l, &yaffs_context_list) { + context_iterator = + list_entry(l, struct yaffs_linux_context, + context_list);
list_for_each_entry
Quoted 7 lines
fs/yaffs2/yaffs_vfs.c new file +7
-0+if (context_iterator->mount_id == mount_id) + found =0 ; + } + } + context->mount_id = mount_id; + + list_add_tail(&(yaffs_dev_to_lc(dev)->context_list),
fs/yaffs2/yaffs_vfs.c line 2276
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ &yaffs_context_list); +mutex_unlock (&yaffs_context_lock); + +/* Directory search handling... */ +INIT_LIST_HEAD (&(yaffs_dev_to_lc (dev)->search_contexts)); + param->remove_obj_fn = yaffs_remove_obj_callback; + +mutex_init (&(yaffs_dev_to_lc (dev)->gross_lock)); + +yaffs_gross_lock (dev);
Can we be in parallel here? We are initialising the filesystem.
Quoted 180 lines
fs/yaffs2/yaffs_vfs.c new file +180
-0+ + err =yaffs_guts_initialise (dev); + +yaffs_trace (YAFFS_TRACE_OS , +"yaffs_read_super: guts initialised %s" , + (err ==YAFFS_OK ) ?"OK" :"FAILED" ); + +if (err ==YAFFS_OK ) +yaffs_bg_start (dev); + +if (!context->bg_thread) + param->defered_dir_update =0 ; + +/* Release lock before yaffs_get_inode() */ +yaffs_gross_unlock (dev); + +/* Create root inode */ +if (err ==YAFFS_OK ) + inode =yaffs_get_inode (sb,S_IFDIR |0755 ,0 ,yaffs_root (dev)); + +if (!inode) +return NULL ; + + inode->i_op = &yaffs_dir_inode_operations; + inode->i_fop = &yaffs_dir_operations; + +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_read_super: got root inode" ); + + root =d_alloc_root (inode); + +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_read_super: d_alloc_root done" ); + +if (!root) { +iput (inode); +return NULL ; + } + sb->s_root = root; + sb->s_dirt = !dev->is_checkpointed; +yaffs_trace (YAFFS_TRACE_ALWAYS , +"yaffs_read_super: is_checkpointed %d" , + dev->is_checkpointed); + +yaffs_trace (YAFFS_TRACE_OS ,"yaffs_read_super: done" ); +return sb; +} + +static int yaffs_internal_read_super_mtd (struct super_block *sb,void *data, +int silent) +{ +return yaffs_internal_read_super (1 , sb, data, silent) ?0 : -EINVAL ; +} + +static int yaffs_read_super (struct file_system_type *fs, +int flags,const char *dev_name, +void *data,struct vfsmount *mnt) +{ + +return get_sb_bdev (fs, flags, dev_name, data, + yaffs_internal_read_super_mtd, mnt); +} + +static struct file_system_type yaffs_fs_type = { + .owner =THIS_MODULE , + .name ="yaffs" , + .get_sb = yaffs_read_super, + .kill_sb = kill_block_super, + .fs_flags =FS_REQUIRES_DEV , +}; + +#ifdef CONFIG_YAFFS_YAFFS2 + +static int yaffs2_internal_read_super_mtd (struct super_block *sb,void *data, +int silent) +{ +return yaffs_internal_read_super (2 , sb, data, silent) ?0 : -EINVAL ; +} + +static int yaffs2_read_super (struct file_system_type *fs, +int flags,const char *dev_name,void *data, +struct vfsmount *mnt) +{ +return get_sb_bdev (fs, flags, dev_name, data, + yaffs2_internal_read_super_mtd, mnt); +} + +static struct file_system_type yaffs2_fs_type = { + .owner =THIS_MODULE , + .name ="yaffs2" , + .get_sb = yaffs2_read_super, + .kill_sb = kill_block_super, + .fs_flags =FS_REQUIRES_DEV , +}; +#endif /* CONFIG_YAFFS_YAFFS2 */ + +static struct proc_dir_entry *my_proc_entry; + +static char *yaffs_dump_dev_part0 (char *buf,struct yaffs_dev *dev) +{ +struct yaffs_param *param = &dev->param; + + buf +=sprintf (buf,"start_block........... %d\n" , param->start_block); + buf +=sprintf (buf,"end_block............. %d\n" , param->end_block); + buf +=sprintf (buf,"total_bytes_per_chunk. %d\n" , + param->total_bytes_per_chunk); + buf +=sprintf (buf,"use_nand_ecc.......... %d\n" , + param->use_nand_ecc); + buf +=sprintf (buf,"no_tags_ecc........... %d\n" , param->no_tags_ecc); + buf +=sprintf (buf,"is_yaffs2............. %d\n" , param->is_yaffs2); + buf +=sprintf (buf,"inband_tags........... %d\n" , param->inband_tags); + buf +=sprintf (buf,"empty_lost_n_found.... %d\n" , + param->empty_lost_n_found); + buf +=sprintf (buf,"disable_lazy_load..... %d\n" , + param->disable_lazy_load); + buf +=sprintf (buf,"refresh_period........ %d\n" , + param->refresh_period); + buf +=sprintf (buf,"n_caches.............. %d\n" , param->n_caches); + buf +=sprintf (buf,"n_reserved_blocks..... %d\n" , + param->n_reserved_blocks); + buf +=sprintf (buf,"always_check_erased... %d\n" , + param->always_check_erased); + +return buf; +} + +static char *yaffs_dump_dev_part1 (char *buf,struct yaffs_dev *dev) +{ + buf += +sprintf (buf,"data_bytes_per_chunk.. %d\n" , + dev->data_bytes_per_chunk); + buf +=sprintf (buf,"chunk_grp_bits........ %d\n" , dev->chunk_grp_bits); + buf +=sprintf (buf,"chunk_grp_size........ %d\n" , dev->chunk_grp_size); + buf += +sprintf (buf,"n_erased_blocks....... %d\n" , dev->n_erased_blocks); + buf += +sprintf (buf,"blocks_in_checkpt..... %d\n" , dev->blocks_in_checkpt); + buf +=sprintf (buf,"\n" ); + buf +=sprintf (buf,"n_tnodes.............. %d\n" , dev->n_tnodes); + buf +=sprintf (buf,"n_obj................. %d\n" , dev->n_obj); + buf +=sprintf (buf,"n_free_chunks......... %d\n" , dev->n_free_chunks); + buf +=sprintf (buf,"\n" ); + buf +=sprintf (buf,"n_page_writes......... %u\n" , dev->n_page_writes); + buf +=sprintf (buf,"n_page_reads.......... %u\n" , dev->n_page_reads); + buf +=sprintf (buf,"n_erasures............ %u\n" , dev->n_erasures); + buf +=sprintf (buf,"n_gc_copies........... %u\n" , dev->n_gc_copies); + buf +=sprintf (buf,"all_gcs............... %u\n" , dev->all_gcs); + buf += +sprintf (buf,"passive_gc_count...... %u\n" , dev->passive_gc_count); + buf += +sprintf (buf,"oldest_dirty_gc_count. %u\n" , + dev->oldest_dirty_gc_count); + buf +=sprintf (buf,"n_gc_blocks........... %u\n" , dev->n_gc_blocks); + buf +=sprintf (buf,"bg_gcs................ %u\n" , dev->bg_gcs); + buf += +sprintf (buf,"n_retired_writes...... %u\n" , dev->n_retired_writes); + buf += +sprintf (buf,"n_retired_blocks...... %u\n" , dev->n_retired_blocks); + buf +=sprintf (buf,"n_ecc_fixed........... %u\n" , dev->n_ecc_fixed); + buf +=sprintf (buf,"n_ecc_unfixed......... %u\n" , dev->n_ecc_unfixed); + buf += +sprintf (buf,"n_tags_ecc_fixed...... %u\n" , dev->n_tags_ecc_fixed); + buf += +sprintf (buf,"n_tags_ecc_unfixed.... %u\n" , + dev->n_tags_ecc_unfixed); + buf +=sprintf (buf,"cache_hits............ %u\n" , dev->cache_hits); + buf += +sprintf (buf,"n_deleted_files....... %u\n" , dev->n_deleted_files); + buf += +sprintf (buf,"n_unlinked_files...... %u\n" , dev->n_unlinked_files); + buf +=sprintf (buf,"refresh_count......... %u\n" , dev->refresh_count); + buf +=sprintf (buf,"n_bg_deletions........ %u\n" , dev->n_bg_deletions); + +return buf; +} + +static int yaffs_proc_read (char *page, +char **start, +off_t offset,int count,int *eof,void *data) +{ +struct list_head *item; +char *buf = page;
fs/yaffs2/yaffs_vfs.c line 2466
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+int step = offset; +int n =0 ; + +/* Get proc_file_read() to step 'offset' by one on each sucessive call. +* We use 'offset' (*ppos) to indicate where we are in dev_list. +* This also assumes the user has posted a read buffer large +* enough to hold the complete output; but that's life in /proc. +*/ + + *(int *)start =1 ;
Ick.
Quoted 7 lines
fs/yaffs2/yaffs_vfs.c new file +7
-0+ +/* Print header first */ +if (step ==0 ) + buf +=sprintf (buf,"YAFFS built:" __DATE__" " __TIME__"\n" ); +else if (step ==1 ) + buf +=sprintf (buf,"\n" ); +else {
fs/yaffs2/yaffs_vfs.c line 2483
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ step -=2 ; + +mutex_lock (&yaffs_context_lock); + +/* Locate and print the Nth entry. +* Order N-squared but N is small. */ +list_for_each (item, &yaffs_context_list) { +struct yaffs_linux_context *dc = + list_entry(item, struct yaffs_linux_context, + context_list);
list_for_each_entry
Quoted 53 lines
fs/yaffs2/yaffs_vfs.c new file +53
-0+struct yaffs_dev *dev = dc->dev; + +if (n < (step & ~1 )) { + n +=2 ; +continue ; + } +if ((step &1 ) ==0 ) { + buf += +sprintf (buf,"\nDevice %d \"%s\"\n" , n, + dev->param.name); + buf =yaffs_dump_dev_part0 (buf, dev); + }else { + buf =yaffs_dump_dev_part1 (buf, dev); + } + +break ; + } +mutex_unlock (&yaffs_context_lock); + } + +return buf - page < count ? buf - page : count; +} + + +/** +* Set the verbosity of the warnings and error messages. +* +* Note that the names can only be a..z or _ with the current code. +*/ + +static struct { +char *mask_name; +unsigned mask_bitfield; +} mask_flags[] = { + {"allocate" ,YAFFS_TRACE_ALLOCATE }, + {"always" ,YAFFS_TRACE_ALWAYS }, + {"background" ,YAFFS_TRACE_BACKGROUND }, + {"bad_blocks" ,YAFFS_TRACE_BAD_BLOCKS }, + {"buffers" ,YAFFS_TRACE_BUFFERS }, + {"bug" ,YAFFS_TRACE_BUG }, + {"checkpt" ,YAFFS_TRACE_CHECKPOINT }, + {"deletion" ,YAFFS_TRACE_DELETION }, + {"erase" ,YAFFS_TRACE_ERASE }, + {"error" ,YAFFS_TRACE_ERROR }, + {"gc_detail" ,YAFFS_TRACE_GC_DETAIL }, + {"gc" ,YAFFS_TRACE_GC }, + {"lock" ,YAFFS_TRACE_LOCK }, + {"mtd" ,YAFFS_TRACE_MTD }, + {"nandaccess" ,YAFFS_TRACE_NANDACCESS }, + {"os" ,YAFFS_TRACE_OS }, + {"scan_debug" ,YAFFS_TRACE_SCAN_DEBUG }, + {"scan" ,YAFFS_TRACE_SCAN }, + {"mount" ,YAFFS_TRACE_MOUNT },
fs/yaffs2/yaffs_vfs.c line 2546
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ {"tracing", YAFFS_TRACE_TRACING}, + {"sync", YAFFS_TRACE_SYNC}, + {"write", YAFFS_TRACE_WRITE}, + {"verify", YAFFS_TRACE_VERIFY}, + {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, + {"verify_full", YAFFS_TRACE_VERIFY_FULL}, + {"verify_all", YAFFS_TRACE_VERIFY_ALL}, + {"all", 0xffffffff}, + {"none", 0}, + {NULL, 0},
Tab delimit to make more readable.
Quoted 133 lines
fs/yaffs2/yaffs_vfs.c new file +133
-0+}; + +#define MAX_MASK_NAME_LENGTH 40 +static int yaffs_proc_write_trace_options (struct file *file,const char *buf, +unsigned long count,void *data) +{ +unsigned rg =0 , mask_bitfield; +char *end; +char *mask_name; +const char *x; +char substring[MAX_MASK_NAME_LENGTH +1 ]; +int i; +int done =0 ; +int add, len =0 ; +int pos =0 ; + + rg = yaffs_trace_mask; + +while (!done && (pos < count)) { + done =1 ; +while ((pos < count) &&isspace (buf[pos])) + pos++; + +switch (buf[pos]) { +case '+' : +case '-' : +case '=' : + add = buf[pos]; + pos++; +break ; + +default : + add =' ' ; +break ; + } + mask_name =NULL ; + + mask_bitfield =simple_strtoul (buf + pos, &end,0 ); + +if (end > buf + pos) { + mask_name ="numeral" ; + len = end - (buf + pos); + pos += len; + done =0 ; + }else { +for (x = buf + pos, i =0 ; + (*x =='_' || (*x >='a' && *x <='z' )) && + i <MAX_MASK_NAME_LENGTH ; x++, i++, pos++) + substring[i] = *x; + substring[i] ='\0' ; + +for (i =0 ; mask_flags[i].mask_name !=NULL ; i++) { +if (strcmp (substring, mask_flags[i].mask_name) + ==0 ) { + mask_name = mask_flags[i].mask_name; + mask_bitfield = + mask_flags[i].mask_bitfield; + done =0 ; +break ; + } + } + } + +if (mask_name !=NULL ) { + done =0 ; +switch (add) { +case '-' : + rg &= ~mask_bitfield; +break ; +case '+' : + rg |= mask_bitfield; +break ; +case '=' : + rg = mask_bitfield; +break ; +default : + rg |= mask_bitfield; +break ; + } + } + } + + yaffs_trace_mask = rg |YAFFS_TRACE_ALWAYS ; + +printk (KERN_DEBUG "new trace = 0x%08X\n" , yaffs_trace_mask); + +if (rg &YAFFS_TRACE_ALWAYS ) { +for (i =0 ; mask_flags[i].mask_name !=NULL ; i++) { +char flag; + flag = ((rg & mask_flags[i].mask_bitfield) == + mask_flags[i].mask_bitfield) ?'+' :'-' ; +printk (KERN_DEBUG "%c%s\n" , flag, + mask_flags[i].mask_name); + } + } + +return count; +} + +static int yaffs_proc_write (struct file *file,const char *buf, +unsigned long count,void *data) +{ +return yaffs_proc_write_trace_options (file, buf, count, data); +} + +/* Stuff to handle installation of file systems */ +struct file_system_to_install { +struct file_system_type *fst; +int installed; +}; + +static struct file_system_to_install fs_to_install[] = { + {&yaffs_fs_type,0 }, + {&yaffs2_fs_type,0 }, + {NULL ,0 } +}; + +static int __init init_yaffs_fs (void ) +{ +int error =0 ; +struct file_system_to_install *fsinst; + +yaffs_trace (YAFFS_TRACE_ALWAYS , +"yaffs built " __DATE__" " __TIME__" Installing." ); + +#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED +yaffs_trace (YAFFS_TRACE_ALWAYS , +"\n\nYAFFS-WARNING CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED selected.\n\n\n" ); +#endif + +mutex_init (&yaffs_context_lock); + +/* Install the proc_fs entries */
fs/yaffs2/yaffs_vfs.c line 2689
Quoted 10 lines
fs/yaffs2/yaffs_vfs.c new file +10
-0+ my_proc_entry =create_proc_entry ("yaffs" , +S_IRUGO |S_IFREG ,NULL ); + +if (my_proc_entry) { + my_proc_entry->write_proc = yaffs_proc_write; + my_proc_entry->read_proc = yaffs_proc_read; + my_proc_entry->data =NULL ; + }else { +return -ENOMEM ; + }
Reverse this if statement to make the success path more obvious.
Quoted 102 lines
fs/yaffs2/yaffs_vfs.c new file +56
-0+ + +/* Now add the file system entries */ + + fsinst = fs_to_install; + +while (fsinst->fst && !error) { + error =register_filesystem (fsinst->fst); +if (!error) + fsinst->installed =1 ; + fsinst++; + } + +/* Any errors? uninstall */ +if (error) { + fsinst = fs_to_install; + +while (fsinst->fst) { +if (fsinst->installed) { +unregister_filesystem (fsinst->fst); + fsinst->installed =0 ; + } + fsinst++; + } + } + +return error; +} + +static void __exit exit_yaffs_fs(void) +{ + +struct file_system_to_install *fsinst; + +yaffs_trace (YAFFS_TRACE_ALWAYS , +"yaffs built " __DATE__" " __TIME__" removing." ); + +remove_proc_entry ("yaffs" ,NULL ); + + fsinst = fs_to_install; + +while (fsinst->fst) { +if (fsinst->installed) { +unregister_filesystem (fsinst->fst); + fsinst->installed =0 ; + } + fsinst++; + } +} + +module_init(init_yaffs_fs) +module_exit(exit_yaffs_fs) + +MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); +MODULE_AUTHOR ("Charles Manning, Aleph One Ltd., 2002-2011" ); +MODULE_LICENSE ("GPL" );fs/yaffs2/yportenv.h new file +40
-0@@ -0,0 +1,59 @@ +/* +* YAFFS: Yet another Flash File System . A NAND-flash specific file system. +* +* Copyright (C) 2002-2011 Aleph One Ltd. +* for Toby Churchill Ltd and Brightstar Engineering +* +* Created by Charles Manning <[email protected]> +* +* 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> + +/* These type wrappings are used to support Unicode names in WinCE. */ +#define YCHAR char +#define YUCHAR unsigned char +#define _Y (x) x + +#define YAFFS_LOSTNFOUND_NAME "lost+found" +#define YAFFS_LOSTNFOUND_PREFIX "obj"
fs/yaffs2/yportenv.h line 50
Quoted 10 lines
fs/yaffs2/yportenv.h new file +10
-0+ + +#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; })
This can probably be replaced with BUILD_BUG_ON and friends.
fs/yaffs2/yportenv.h line 56
Quoted 6 lines
fs/yaffs2/yportenv.h new file +6
-0+ + +#define yaffs_trace (msk, fmt, ...) do { \ + if (yaffs_trace_mask & (msk)) \ + printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ +} while (0)
Using pr_debug here would allow you to completly remove all of the tracing code if you don't need debugging output. Handy for embedded devices, which is what yaffs is aimed at. You can #define DEBUG at the top of this file to include debugging support, or add it as a Kconfig option.
~Ryan
Signature · 7 lines
Bluewater Systems Ltd - ARM Technology Solution Centre Ryan Mallon 5 Amuri Park, 404 Barbadoes St [email protected] PO Box 13 889, Christchurch 8013 http://www.bluewatersys.com New Zealand Phone: +64 3 3779127 Freecall: Australia 1800 148 751 Fax: +64 3 3779135 USA 1800 261 2934
Re: [PATCH 09/10] Add yaffs2 file system: Linux glue code
Quoted 8 lines · On Friday 11 February 2011 11:07:31 Ryan Mallon wrote:
Quoted 1 line · Charles Manning
Signed-off-by Charles Manning <[email protected]>Hi Charles,
More comments below. The locking I think needs some work before this is okay for mainline. It would be interesting to stress test yaffs on an SMP machine. None of the boards which we run yaffs on are though :-(.
yaffs can be run on a PC using nandsim and I have done so with the multi-version code. I am setting up another PC for single-version (ie. mainlining) testing.
I'll take an in depth look at your comments, but it is important to understand the locking model a bit better,
yaffs just uses one big lock. We need to consider locking for many reasons and need to ensure that stuff does not get modified behind our back. For example setattr on one object can cause garbage collection to change the flash location - and thus the tnode tree - of another object.
There is probably scope to change the gross locking model to a r/w lock which would mean that read operations could proceed better.
I did a non-open-source project of looking at finer grained locking but found that there were not many gains for what I looked at. Ultimately anything that goes to flash is serialised by the flash access.
-- Charles
Re: [PATCH 09/10] Add yaffs2 file system: Linux glue code
In reply to Charles ManningQuoted 2 lines · Charles Manning
Signed-off-by Charles Manning <[email protected]>---
<snip>
fs/yaffs2/yaffs_vfs.c line 710
Quoted 6 lines
fs/yaffs2/yaffs_vfs.c new file +6
-0+/* +* yaffs_search_end() disposes of a search context and cleans up. +*/ +static void yaffs_search_end (struct yaffs_search_context *sc) +{ +if (sc) {
This test, and the similar one in yaffs_search_advance, are not needed. There is no way that sc can be passed as NULL here. Even if that could happen you probably want to oops rather than silently carrying on.
I think there are similar issues in other functions throughout the yaffs code where you are testing if various pointers are NULL and returning silently if they are. All of these cases should be fixed to remove the extraneous code.
~Ryan
Signature · 7 lines
Bluewater Systems Ltd - ARM Technology Solution Centre Ryan Mallon 5 Amuri Park, 404 Barbadoes St [email protected] PO Box 13 889, Christchurch 8013 http://www.bluewatersys.com New Zealand Phone: +64 3 3779127 Freecall: Australia 1800 148 751 Fax: +64 3 3779135 USA 1800 261 2934
PATCH 10/10 Add yaffs2 file system: hok in to Linux tree
In reply to Charles Manningyaffs2's own Kconfig and Makefile as well as hooking up to existing fs/Kconfig and fs/Makefile and add entry to MAINTAINERS.
--- MAINTAINERS | 7 ++ fs/Kconfig | 1 + fs/Makefile | 1 + fs/yaffs2/Kconfig | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/Makefile | 17 ++++++ 5 files changed, 187 insertions(+), 0 deletions(-) create mode 100644 fs/yaffs2/Kconfig create mode 100644 fs/yaffs2/Makefile
MAINTAINERS +7 -0
@@ -6916,6 +6916,13 @@ L: [email protected] S: Maintained F: drivers/tty/serial/uartlite.c +YAFFS2 FLASH FILE SYSTEM +M: Charles Manning <[email protected]> +W: http://www.yaffs.net/ +L: [email protected] +S: Maintained +F: fs/yaffs2 + YAM DRIVER FOR AX.25 M: Jean-Paul Roubelat <[email protected]> L: [email protected]
fs/Kconfig +1 -0
@@ -175,6 +175,7 @@ source "fs/hfsplus/Kconfig" source "fs/befs/Kconfig" source "fs/bfs/Kconfig" source "fs/efs/Kconfig" +source "fs/yaffs2/Kconfig" source "fs/jffs2/Kconfig" # UBIFS File system configuration source "fs/ubifs/Kconfig"
fs/Makefile +1 -0
@@ -121,3 +121,4 @@ obj-$(CONFIG_BTRFS_FS) += btrfs/ obj-$(CONFIG_GFS2_FS) += gfs2/ obj-$(CONFIG_EXOFS_FS) += exofs/ obj-$(CONFIG_CEPH_FS) += ceph/ +obj-$(CONFIG_YAFFS_FS) += yaffs2/
fs/yaffs2/Kconfig new file +161 -0
161 lines not on this page · raw message
fs/yaffs2/Makefile new file +17 -0
@@ -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 +
Signature · 1 line
1.7.1
Quoted 2 lines · Charles Manning
Comments/feedback welcome, but I'm hopefully getting pretty close to acceptance :-).
It's not anywhere near ready as long as you insist keeping that silly Linux glue code. Also make sure it passes the xfstests QA suite
(http://git.kernel.org/?p=fs/xfs/xfstests-dev.git;a=summary), which might need a bit work to be made run depending on how quirky your mkfs/fsck tools are.
Quoted 6 lines · On Wednesday 09 February 2011 17:52:21 Christoph Hellwig wrote:
Quoted 2 lines · Charles Manning
Comments/feedback welcome, but I'm hopefully getting pretty close to acceptance :-).
It's not anywhere near ready as long as you insist keeping that silly Linux glue code.
Can you be a bit more specific?
Did you actually look or did you just get hives when you saw "glue code"? The current glue code is very far from what was in the original patch set. In the first set there were all sorts of abstractions which have been purged and almost entirely removed.
The current "glue code" really just partitions off the vfs interaction from the file system internal operations.
This partitioning serves a purpose: it allows the code to be easily integrated into applications etc (ie outside of the kernel) and also allows the multi-version code to be supported more cleanly. Multi-version is incredibly important in the RealWorld (TM).
Quoted 4 lines
Also make sure it passes the xfstests QA suite
(http://git.kernel.org/?p=fs/xfs/xfstests-dev.git;a=summary), which might need a bit work to be made run depending on how quirky your mkfs/fsck tools are.
Thanks. I'll try those.
It does pass fsx and some other tests.
-- Charles
Quoted 1 line · Charles Manning
Can you be a bit more specific?
Just compare the mes in yaffs_linux.c with a normal linux filesystem. All that gross magic nfsd detection in readdir could have been removed long ago. ->readlink should not be implemented by a normal filesystem but use generic_readlink, the fs-specific inode should embedd the vfs inode instead of requiring two allocations, tons of useless function pointer indirections like sb_dirty_fn and the put_super_fn really must go away. The procfs interfaces should be replaced by something saner, the insane amount of ad-hoc tracing crap should be replaced by much less strategically placed trace events, and all those stupid compile time options have absolutely no business at all beeing there for a filesystem - remember you can get media from all over the place. If you can't encode these difference in your on-disk format it has absolutely no business going into mainline with this format.
And last but not least there's no way we'll merge a filesystem with a global mutex and all kinds of hacky release and reaquire semantics these days. I really think you need to get back to the drawing board.
Chrisoph
Quoted 7 lines · On Wednesday 16 February 2011 21:04:20 Christoph Hellwig wrote:
Quoted 1 line · Charles Manning
Can you be a bit more specific?
Just compare the mes in
yaffs_linux.cwith a normal linux filesystem. All that gross magic nfsd detection in readdir could have been removed long ago. ->readlink should not be implemented by a normal filesystem but usegeneric_readlink,
Fair comment. I'm sure there are better ways to handle that.
Quoted 2 lines
the fs-specific inode should embedd the vfs
inode instead of requiring two allocations,
The approach used here significantly reduces memory footprint. The yaffs inodes (== yaffs objects) live all the time while the vfs inodes come and go. It is far better to just link these together when in use.
Quoted 3 lines
tons of useless function
pointer indirections likesb_dirty_fnand theput_super_fnreally must go away.
Ok.
Quoted 5 lines
The procfs interfaces should be replaced by something saner, the insane amount of ad-hoc tracing crap should be replaced by much less strategically placed trace events, and all those stupid compile time options have absolutely no business at all beeing there for a filesystem -
Why not?
Quoted 1 line
remember you can get media from all over the place.
No you can't. This is a flash file system for soldered down flash. I think that is a fundamental place where your understanding of what yaffs is falls down.
Quoted 2 lines
If you can't encode these difference in your on-disk format it has absolutely no business going into mainline with this format.
Yaffs does not really have an on-disk format like most other fs do.
Quoted 3 lines
And last but not least there's no way we'll merge a filesystem with a global mutex and all kinds of hacky release and reaquire semantics these days.
I could change the locking but that would require considerable work.
Quoted 1 line
I really think you need to get back to the drawing board.
I'm starting to think that the distance from where yaffs is now to what people will accept into the kernel tree might be quite large.
I might just be easier to walk away from the mainlining effort. Yaffs is a highly successful and useful fs and very easy to patch in to a kernel. People could continue to use it that way as they have for the last 9 ears or so.
-- Charles
Quoted 8 lines · Charles Manning
Quoted 5 lines · On Wednesday 16 February 2011 21:04:20 Christoph Hellwig wrote:
The procfs interfaces should be replaced by something saner, the insane amount of ad-hoc tracing crap should be replaced by much less strategically placed trace events, and all those stupid compile time options have absolutely no business at all beeing there for a filesystem -
Quoted 7 lines
Why not?
Quoted 1 line
remember you can get media from all over the place.
No you can't. This is a flash file system for soldered down flash. I think that is a fundamental place where your understanding of what yaffs is falls down.
Even for embedded systems people do end up wanting to do things like using the same kernel on multiple systems which may have different hardware configurations (distros and reference boards are the obvious examples, but I've worked on systems where multiple generations and builds of the product were in active use and similar enough to be maintained from the same kernel). Even with single system kernels there's still an issue with things like reference boards where users are doing things like picking up a new upstream kernel rather than the vendor BSP.
Quoted 4 lines
Quoted 2 lines
If you can't encode these difference in your on-disk format it has absolutely no business going into mainline with this format.
Yaffs does not really have an on-disk format like most other fs do.
I'm not sure exactly how you'd do this for a filesystem but this is starting to sound a lot like platform data... Though presumably if the data isn't stored on the device currently it'd be a simple matter of programming (if wasteful) to add it.
Quoted 8 lines · On Thursday 17 February 2011 14:48:08 Mark Brown wrote:
Quoted 7 lines · Charles Manning
Quoted 5 lines · On Wednesday 16 February 2011 21:04:20 Christoph Hellwig wrote:
The procfs interfaces should be replaced by something saner, the insane amount of ad-hoc tracing crap should be replaced by much less strategically placed trace events, and all those stupid compile time options have absolutely no business at all beeing there for a filesystem -
Quoted 7 lines
Quoted 7 lines
Why not?
Quoted 1 line
remember you can get media from all over the place.
No you can't. This is a flash file system for soldered down flash. I think that is a fundamental place where your understanding of what yaffs is falls down.
I'm not sure I really understand what Christoph means by "get media from all over the place". I took that to mean he thinks people will pug in random flash cards and would need to fiddle with options to make them work. That would of course suck badly.
That is obviously not the case for a flash file system used on hard-wired flash where the system integrator is in control. Users don't get to fiddle.
Quoted 10 lines
Even for embedded systems people do end up wanting to do things like using the same kernel on multiple systems which may have different hardware configurations (distros and reference boards are the obvious examples, but I've worked on systems where multiple generations and builds of the product were in active use and similar enough to be maintained from the same kernel). Even with single system kernels there's still an issue with things like reference boards where users are doing things like picking up a new upstream kernel rather than the vendor BSP.
Every one of the "stupid compile time options" is there because someone that actually **uses** yaffs wanted it. None are there just for fun. The compile-time switches are very limited - mostly just there to set up default runtime flags that can be overridden at runtime. Some of them are there to work around bugs and limitations in the mtd.
Even with BSPs, there will often be some board tuning to, do things like set up the mtd partitions.
Picking up an new kernel is easy, so long as the mtd code has not been broken in the interum.
Last week I dropped the yaffs code into an omap3 build of 2.6.37 with no fiddling and with default settings and it "just worked".
Quoted 10 lines
Quoted 5 lines
Quoted 3 lines
If you can't encode these difference in your on-disk format it has absolutely no business going into mainline with this format.
Yaffs does not really have an on-disk format like most other fs do.
I'm not sure exactly how you'd do this for a filesystem but this is starting to sound a lot like platform data... Though presumably if the data isn't stored on the device currently it'd be a simple matter of programming (if wasteful) to add it.
NB flash file system - not file system! Working via mtd is different to working via blocks.
Most of the required data is passed through mtd and you don't need any special platform data beyond the nand setup in the board tuning file.
Quoted 36 lines · Charles Manning
Quoted 8 lines · On Thursday 17 February 2011 14:48:08 Mark Brown wrote:
Quoted 7 lines · Charles Manning
Quoted 5 lines · On Wednesday 16 February 2011 21:04:20 Christoph Hellwig wrote:
The procfs interfaces should be replaced by something saner, the insane amount of ad-hoc tracing crap should be replaced by much less strategically placed trace events, and all those stupid compile time options have absolutely no business at all beeing there for a filesystem -
Quoted 7 lines
Quoted 7 lines
Why not?
Quoted 1 line
remember you can get media from all over the place.
No you can't. This is a flash file system for soldered down flash. I think that is a fundamental place where your understanding of what yaffs is falls down.
I'm not sure I really understand what Christoph means by "get media from all over the place". I took that to mean he thinks people will pug in random flash cards and would need to fiddle with options to make them work. That would of course suck badly.
That is obviously not the case for a flash file system used on hard-wired flash where the system integrator is in control. Users don't get to fiddle.
Quoted 10 lines
Even for embedded systems people do end up wanting to do things like using the same kernel on multiple systems which may have different hardware configurations (distros and reference boards are the obvious examples, but I've worked on systems where multiple generations and builds of the product were in active use and similar enough to be maintained from the same kernel). Even with single system kernels there's still an issue with things like reference boards where users are doing things like picking up a new upstream kernel rather than the vendor BSP.
Quoted 5 lines
Every one of the "stupid compile time options" is there because someone that actually **uses** yaffs wanted it. None are there just for fun. The compile-time switches are very limited - mostly just there to set up default runtime flags that can be overridden at runtime. Some of them are there to work around bugs and limitations in the mtd.
The Kconfig options which are can be overridden by runtime flags should simply be removed. Pick a default and stick with it. Existing users will either have to modify their mount flags to suit, continuing using an old version, or patch the yaffs code themselves.
It's also possibly worth removing some of the more esoteric options such as the ECC options at least for the first attempt at getting Yaffs into mainline. It will make it easier to get the code accepted, and if enough users complain that the feature is not there a sensible way to patch it in can be added later.
I think the downside at the moment is that it is getting less and less likely that you can maintain a single yaffs codebase and get it merged into mainline. I really think you are going to need to fork the code.
Quoted 9 lines
Even with BSPs, there will often be some board tuning to, do things like set up the mtd partitions.
Picking up an new kernel is easy, so long as the mtd code has not been broken in the interum.
Last week I dropped the yaffs code into an omap3 build of 2.6.37 with no fiddling and with default settings and it "just worked".
Mark is talking about the case where you have a single kernel which can be run on multiple boards/platforms. This is becoming increasingly common for development and testing purposes. Because some of the yaffs config options are compile time only selectable, it may not be possible to support yaffs correctly on multiple boards in a single kernel. This is why it is much more ideal for config options to be mount flags.
~Ryan
Quoted 48 lines · On Thursday 17 February 2011 15:52:18 Ryan Mallon wrote:
Quoted 36 lines · Charles Manning
Quoted 8 lines · On Thursday 17 February 2011 14:48:08 Mark Brown wrote:
Quoted 7 lines · Charles Manning
Quoted 5 lines · On Wednesday 16 February 2011 21:04:20 Christoph Hellwig wrote:
The procfs interfaces should be replaced by something saner, the insane amount of ad-hoc tracing crap should be replaced by much less strategically placed trace events, and all those stupid compile time options have absolutely no business at all beeing there for a filesystem -
Quoted 7 lines
Quoted 7 lines
Why not?
Quoted 1 line
remember you can get media from all over the place.
No you can't. This is a flash file system for soldered down flash. I think that is a fundamental place where your understanding of what yaffs is falls down.
I'm not sure I really understand what Christoph means by "get media from all over the place". I took that to mean he thinks people will pug in random flash cards and would need to fiddle with options to make them work. That would of course suck badly.
That is obviously not the case for a flash file system used on hard-wired flash where the system integrator is in control. Users don't get to fiddle.
Quoted 9 lines
Even for embedded systems people do end up wanting to do things like using the same kernel on multiple systems which may have different hardware configurations (distros and reference boards are the obvious examples, but I've worked on systems where multiple generations and builds of the product were in active use and similar enough to be maintained from the same kernel). Even with single system kernels there's still an issue with things like reference boards where users are doing things like picking up a new upstream kernel rather than the vendor BSP.
Quoted 5 lines
Every one of the "stupid compile time options" is there because someone that actually **uses** yaffs wanted it. None are there just for fun. The compile-time switches are very limited - mostly just there to set up default runtime flags that can be overridden at runtime. Some of them are there to work around bugs and limitations in the mtd.
The
Kconfigoptions which are can be overridden by runtime flags should simply be removed. Pick a default and stick with it. Existing users will either have to modify their mount flags to suit, continuing using an old version, or patch the yaffs code themselves.
Quoted 5 lines
It's also possibly worth removing some of the more esoteric options such as the ECC options at least for the first attempt at getting Yaffs into mainline. It will make it easier to get the code accepted, and if enough users complain that the feature is not there a sensible way to patch it in can be added later.
Ok. It is easy enough to strip out the Kconfigs and some compile time flags to make two versions: one to match the vfs-glue being mainlined and another for the patch-in.sh version.
Quoted 4 lines
I think the downside at the moment is that it is getting less and less likely that you can maintain a single yaffs codebase and get it merged into mainline. I really think you are going to need to fork the code.
The part I struggle with is that if I fork the code to keep a full featured code base (that real integrators want) in yaffs.net git and a stripped version that is accepted into the kernel (but lacks what real integrators want), then that is a waste of effort. It is pointless putting something into the kernel if anyone really wanting to use yaffs has to do what they do now and patch in a "full fat" out of tree version.
So far the objections can be broadly classified as follows:
(A) Stuff that is clearly wrong or taste issues that should be fixed and is easy to do (eg. some of the odd-ball code you have pointed out). Even some of the configs could be sorted with this too).
(B) Stuff that is there and is useful and real integrators tell me they want to see kept (eg the tracing stuff) but unfortunately some kernel folk don't like.
(C) Stuff which would potentially improve the performance - but perhaps not as much many think - and is a hell of a lot of work to do (eg. moving away from single locking).
I really have no objections to (A). (C) would just take time, effort and motivation but can be done.
(B) seems counter productive. It is more important, IMHO, to have yaffs being useful rather than mainlined. Getting a "yaffs-lite" into the kernel seems like a very hollow victory.
Quoted 6 lines
Quoted 6 lines
Even with BSPs, there will often be some board tuning to, do things like set up the mtd partitions.
Picking up an new kernel is easy, so long as the mtd code has not been broken in the interum.
Quoted 8 lines
Quoted 2 lines
Last week I dropped the yaffs code into an omap3 build of 2.6.37 with no fiddling and with default settings and it "just worked".
Mark is talking about the case where you have a single kernel which can be run on multiple boards/platforms. This is becoming increasingly common for development and testing purposes. Because some of the yaffs config options are compile time only selectable, it may not be possible to support yaffs correctly on multiple boards in a single kernel.
It is certainly possible. No problems at all. Balloon does that and I thought Bluewater do too. Same for running up yaffs in an omap multi-board kernel. I dropped yaffs into am omap build using defaults and everything "just works" on the boards I tested on.
Same for running on a PC using nandsim. Just compile with defaults and drop it in and it will work with any nandsim configuration by sniffing the mtd partition info and setting things up accordingly.
Quoted 2 lines
This
is why it is much more ideal for config options to be mount flags.
It would be better if those were info passed through mtd (or determined from mtd) so that the flags were not needed. I'll have a look at what can be sorted at runtime.
The compile flags that set defaults are there to provide flexibility, but I can get rid of those easy enough.
What is the really hard is getting rid of the single locking (estimate the thick end of a month of work). That is the really hard part.
Stripping out the tracing is easy enough but pointless if people (ie those actually using yaffs) really want it. Same for the proc interface (though that could be moved to sysfs).
-- Charles
Quoted 3 lines · Charles Manning
Stripping out the tracing is easy enough but pointless if people (ie those actually using yaffs) really want it. Same for the proc interface (though that could be moved to sysfs).
Use debugfs please, if it's debugging stuff. If it's "real" stuff, then yes, it should be in sysfs, you already have a place for it in /sys/fs/ to use. Just abide by the sysfs rules and you will be fine.
thanks,
greg k-h
Quoted 9 lines · Greg KH
Quoted 3 lines · Charles Manning
Stripping out the tracing is easy enough but pointless if people (ie those actually using yaffs) really want it. Same for the proc interface (though that could be moved to sysfs).
Use debugfs please, if it's debugging stuff. If it's "real" stuff, then yes, it should be in sysfs, you already have a place for it in
/sys/fs/to use. Just abide by the sysfs rules and you will be fine.
For the proc stuff - for tracing stuff then tracepoints are likely to be a good option if it's useful to people.
Quoted 7 lines · Mark Brown
Quoted 5 lines · Greg KH
Quoted 3 lines · Charles Manning
Stripping out the tracing is easy enough but pointless if people (ie those actually using yaffs) really want it. Same for the proc interface (though that could be moved to sysfs).
Quoted 6 lines
Quoted 3 lines
Use debugfs please, if it's debugging stuff. If it's "real" stuff, then yes, it should be in sysfs, you already have a place for it in
/sys/fs/to use. Just abide by the sysfs rules and you will be fine.For the proc stuff - for tracing stuff then tracepoints are likely to be a good option if it's useful to people.
Then use the in-kernel tracing functionality, don't roll your own. And that is not in /proc, so it should be there for this filesystem either.
thanks,
greg k-h
Quoted 7 lines · Greg KH
Quoted 2 lines · Mark Brown
For the proc stuff - for tracing stuff then tracepoints are likely to be a good option if it's useful to people.
Then use the in-kernel tracing functionality, don't roll your own. And that is not in /proc, so it should be there for this filesystem either.
That'd be the tracepoints I was mentioning, then...
Quoted 10 lines · Mark Brown
Quoted 7 lines · Greg KH
Quoted 2 lines · Mark Brown
For the proc stuff - for tracing stuff then tracepoints are likely to be a good option if it's useful to people.
Then use the in-kernel tracing functionality, don't roll your own. And that is not in /proc, so it should be there for this filesystem either.
That'd be the tracepoints I was mentioning, then...
Are you suggesting that the yaffs_trace function should be replaced with tracepoints?
yaffs_trace is basically just a wrapper around printk, which I suggested should be replaced with pr_debug so that it can be compiled out completely. Other drivers and filesystems have similar custom debugging functions.
I haven't used tracepoints, but it seems like they are better suited to tracing specific events than as a general printk style debugging replacement?
~Ryan
Signature · 7 lines
Bluewater Systems Ltd - ARM Technology Solution Centre Ryan Mallon 5 Amuri Park, 404 Barbadoes St [email protected] PO Box 13 889, Christchurch 8013 http://www.bluewatersys.com New Zealand Phone: +64 3 3779127 Freecall: Australia 1800 148 751 Fax: +64 3 3779135 USA 1800 261 2934
Quoted 15 lines · Ryan Mallon
Quoted 10 lines · Mark Brown
Quoted 7 lines · Greg KH
Quoted 2 lines · Mark Brown
For the proc stuff - for tracing stuff then tracepoints are likely to be a good option if it's useful to people.
Then use the in-kernel tracing functionality, don't roll your own. And that is not in /proc, so it should be there for this filesystem either.
That'd be the tracepoints I was mentioning, then...
Are you suggesting that the
yaffs_tracefunction should be replaced with tracepoints?
Quoted 8 lines
yaffs_traceis basically just a wrapper around printk, which I suggested should be replaced withpr_debugso that it can be compiled out completely. Other drivers and filesystems have similar custom debugging functions.I haven't used tracepoints, but it seems like they are better suited to tracing specific events than as a general printk style debugging replacement?
If you want printk(), then yes, use pr_debug() as it ties into the dynamic debug infrastructure, which is great.
Then you can remove the proc files, as the kernel already controls the debug interface through the standard way, no need for a custom one.
thanks,
greg k-h
Quoted 16 lines · On Friday 18 February 2011 13:58:52 Greg KH wrote:
Quoted 15 lines · Ryan Mallon
Quoted 10 lines · Mark Brown
Quoted 7 lines · Greg KH
Quoted 2 lines · Mark Brown
For the proc stuff - for tracing stuff then tracepoints are likely to be a good option if it's useful to people.
Then use the in-kernel tracing functionality, don't roll your own. And that is not in /proc, so it should be there for this filesystem either.
That'd be the tracepoints I was mentioning, then...
Are you suggesting that the
yaffs_tracefunction should be replaced with tracepoints?
Quoted 8 lines
Quoted 8 lines
yaffs_traceis basically just a wrapper around printk, which I suggested should be replaced withpr_debugso that it can be compiled out completely. Other drivers and filesystems have similar custom debugging functions.I haven't used tracepoints, but it seems like they are better suited to tracing specific events than as a general printk style debugging replacement?
The procfs is not used for tracing as , it is just one of the two ways ofsetting a trace mask to select what to trace (the other is to set a trace mask).
eg. echo +gc > /proc/yaffs
turns on the garbage collector tracing.
I will remove the /proc interface and write a userspace script to do the equivalent.
Realtime selection of tracing is valuable. It allows you to set up a test case with tracing disabled then select what you want to trace to get detail as you run the test case
I still intend to keep the tracing printk-based tracing:
#define yaffs_trace(msk, fmt, ...) do { \
if (yaffs_trace_mask & (msk)) \
printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \
} while (0)Quoted 5 lines
If you want
printk(), then yes, usepr_debug()as it ties into the dynamic debug infrastructure, which is great.Then you can remove the proc files, as the kernel already controls the debug interface through the standard way, no need for a custom one.
Thanks.
I was not aware of pr_debug I shall investigate how it works.
-- CHarles
Quoted 32 lines · Charles Manning
Quoted 16 lines · On Friday 18 February 2011 13:58:52 Greg KH wrote:
Quoted 15 lines · Ryan Mallon
Quoted 10 lines · Mark Brown
Quoted 7 lines · Greg KH
Quoted 2 lines · Mark Brown
For the proc stuff - for tracing stuff then tracepoints are likely to be a good option if it's useful to people.
Then use the in-kernel tracing functionality, don't roll your own. And that is not in /proc, so it should be there for this filesystem either.
That'd be the tracepoints I was mentioning, then...
Are you suggesting that the
yaffs_tracefunction should be replaced with tracepoints?Quoted 8 lines
Quoted 8 lines
yaffs_traceis basically just a wrapper around printk, which I suggested should be replaced withpr_debugso that it can be compiled out completely. Other drivers and filesystems have similar custom debugging functions.I haven't used tracepoints, but it seems like they are better suited to tracing specific events than as a general printk style debugging replacement?
The procfs is not used for tracing as , it is just one of the two ways ofsetting a trace mask to select what to trace (the other is to set a trace mask).
eg. echo +gc >
/proc/yaffs
Quoted 8 lines
turns on the garbage collector tracing.
I will remove the /proc interface and write a userspace script to do the equivalent.
Realtime selection of tracing is valuable. It allows you to set up a test case with tracing disabled then select what you want to trace to get detail as you run the test case
I agree, so please use the in-kernel tracing code which provides this infrastructure for you.
Quoted 6 lines
I still intend to keep the tracing printk-based tracing:
#define yaffs_trace(msk, fmt, ...) do { \ if (yaffs_trace_mask & (msk)) \ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ } while (0)
No, please don't invent your own stuff like this, again, use the in-kernel functionality provided for this.
thanks,
greg k-h
Quoted 11 lines · Greg KH
Quoted 7 lines · Charles Manning
On Friday 18 February 2011 13:58:52 Greg KH wrote:
I still intend to keep the tracing printk-based tracing:#define yaffs_trace(msk, fmt, ...) do { \ if (yaffs_trace_mask & (msk)) \ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ } while (0)No, please don't invent your own stuff like this, again, use the in-kernel functionality provided for this.
Do you mean using pr_debug? Other filesystems (see for example fs/ubifs/debug.h:dbg_do_msg) and drivers have similar approaches to this to allow printk debugging with multiple message levels.
~Ryan
Signature · 7 lines
Bluewater Systems Ltd - ARM Technology Solution Centre Ryan Mallon 5 Amuri Park, 404 Barbadoes St [email protected] PO Box 13 889, Christchurch 8013 http://www.bluewatersys.com New Zealand Phone: +64 3 3779127 Freecall: Australia 1800 148 751 Fax: +64 3 3779135 USA 1800 261 2934
Quoted 10 lines · Ryan Mallon
Quoted 9 lines · Greg KH
Quoted 7 lines · Charles Manning
On Friday 18 February 2011 13:58:52 Greg KH wrote:
I still intend to keep the tracing printk-based tracing:#define yaffs_trace(msk, fmt, ...) do { \ if (yaffs_trace_mask & (msk)) \ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ } while (0)
Quoted 6 lines
Quoted 2 lines
No, please don't invent your own stuff like this, again, use the in-kernel functionality provided for this.
Do you mean using
pr_debug? Other filesystems (see for example fs/ubifs/debug.h:dbg_do_msg) and drivers have similar approaches to this to allow printk debugging with multiple message levels.
Yes, other ones do have this, and it's a pain, and not consistant across the kernel, and usually just not worth it at all.
Please use the standardized functions for this, you do not need to roll your own at all.
Especially for trace stuff.
thanks,
greg k-h
Quoted 18 lines · Greg KH
Quoted 10 lines · Ryan Mallon
Quoted 9 lines · Greg KH
Quoted 7 lines · Charles Manning
On Friday 18 February 2011 13:58:52 Greg KH wrote:
I still intend to keep the tracing printk-based tracing:#define yaffs_trace(msk, fmt, ...) do { \ if (yaffs_trace_mask & (msk)) \ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ } while (0)Quoted 6 lines
Quoted 2 lines
No, please don't invent your own stuff like this, again, use the in-kernel functionality provided for this.
Do you mean using
pr_debug? Other filesystems (see for example fs/ubifs/debug.h:dbg_do_msg) and drivers have similar approaches to this to allow printk debugging with multiple message levels.
Quoted 5 lines
Yes, other ones do have this, and it's a pain, and not consistant across the kernel, and usually just not worth it at all.
Please use the standardized functions for this, you do not need to roll your own at all.
What standardised functions are there for managing debug printk's with different log types? For example, yaffs currently allows printk debugging to be enabled individually for garbage collection, check pointing, bad blocks, etc. I am at least in favour of switching to pr_debug and using pr_fmt for the "yaffs: " prefix, but what should the yaffs_trace_mask be replaced with?
My understanding is that replacing the proc interface which controls the yaffs_trace_mask with a more sane sysfs one (ie exposing yaffs_trace_mask directly), and keeping the yaffs_trace macro but replacing the printk with pr_debug so that it can be compiled out completely should be sufficient. i.e.:
#ifdef CONFIG_YAFFS_DEBUG
#define DEBUG
#endif
#define pr_fmt "yaffs: " fmt
#define yaffs_trace(msk, fmt, ...) do { \
if (yaffs_trace_mask & (msk)) \
pr_debug(fmt, ##__VA_ARGS__); \
} while (0)Quoted 2 lines
Especially for trace stuff.
I think we are conflating two things here. Tracepoints have been suggested as a replacement for some parts of the printk debugging, such as the mtd debugging. However, it is likely that much of the printk debugging remains useful and is not ideally replaced by tracepoints.
~Ryan
Signature · 7 lines
Bluewater Systems Ltd - ARM Technology Solution Centre Ryan Mallon 5 Amuri Park, 404 Barbadoes St [email protected] PO Box 13 889, Christchurch 8013 http://www.bluewatersys.com New Zealand Phone: +64 3 3779127 Freecall: Australia 1800 148 751 Fax: +64 3 3779135 USA 1800 261 2934
Quoted 6 lines · Ryan Mallon
Quoted 1 line · Mark Brown
That'd be the tracepoints I was mentioning, then...
Are you suggesting that the
yaffs_tracefunction should be replaced with tracepoints?
Not really. I'm suggesting that the useful callers might well be best replaced by tracepoints.
Quoted 8 lines
yaffs_traceis basically just a wrapper around printk, which I suggested should be replaced withpr_debugso that it can be compiled out completely. Other drivers and filesystems have similar custom debugging functions.I haven't used tracepoints, but it seems like they are better suited to tracing specific events than as a general printk style debugging replacement?
Yup, but often things that are being traced by printk() debugging can usefully be converted to tracepoints. I'm not sure exactly what people use this logging for but some of the YAFFS_TRACE_MTD type trace which logs interactions with the hardware seems like a good candidate for example.
Quoted 13 lines · Charles Manning
Quoted 11 lines · On Thursday 17 February 2011 14:48:08 Mark Brown wrote:
On Thu, Feb 17, 2011 at 11:12:06AM +1300, Charles Manning wrote:
Even for embedded systems people do end up wanting to do things like using the same kernel on multiple systems which may have different hardware configurations (distros and reference boards are the obvious examples, but I've worked on systems where multiple generations and builds of the product were in active use and similar enough to be maintained from the same kernel). Even with single system kernels there's still an issue with things like reference boards where users are doing things like picking up a new upstream kernel rather than the vendor BSP.
Quoted 2 lines
Every one of the "stupid compile time options" is there because someone that actually **uses** yaffs wanted it. None are there just for fun. The
The fact that users have asked for something doesn't mean that the particular implementation that was chosen to meet the need makes sense. Having lots of compile time options is terribly common in many embedded systems but that doesn't mean it's a good idea to do things this way.
Quoted 3 lines
compile-time switches are very limited - mostly just there to set up default runtime flags that can be overridden at runtime. Some of them are there to work around bugs and limitations in the mtd.
If these are just setting overridable defaults then that sounds like stuff that doesn't need to be in the kernel in the first place, they're just as trivial to tweak in userspace as with in kernel defines.. For the hardware workarounds having this in the kernel seems reasonable, we just need to figure out a way to propagate it from the machine driver to the filesystem.
Quoted 5 lines
Even with BSPs, there will often be some board tuning to, do things like set up the mtd partitions.
Picking up an new kernel is easy, so long as the mtd code has not been broken in the interum.
You're missing the use case here - I'm talking about the situation where you completely ignore the vendor BSP and go direct to mainline without reference to the vendor provided stuff. The user may not even have the vendor provided BSP to look at.
Quoted 7 lines
Quoted 4 lines
I'm not sure exactly how you'd do this for a filesystem but this is starting to sound a lot like platform data... Though presumably if the data isn't stored on the device currently it'd be a simple matter of programming (if wasteful) to add it.
NB flash file system - not file system! Working via mtd is different to working via blocks.
I do have *some* familiarity with embedded systems. The fact that the device is a flash device doesn't stop you recording metadata on it - obviously you've got some data on there to record the metadata for stuff like directory layout. If some of this is needed to even read the device then it can't go on the device, but otherwise it could be included somehow.
Quoted 2 lines
Most of the required data is passed through mtd and you don't need any special platform data beyond the nand setup in the board tuning file.
Plus the #define based configuration the code is currently doing; clearly some additional configuration is required. I rather suspect the issue here arises from some combination of the cross platform stuff resulting in things that are clearly not idiomatic for Linux and people not being able to figure out how to propagate the data into Linux so just bodging it with ifdefs.
This sort of stuff is really common when taking old code into mainline - people do all sorts of things that aren't suitable for mainline, often around configurability, and we really need to take a step back and userstand what the underlying goal is and how to express it in an idiomatic fashion.
Quoted 26 lines · On Thursday 17 February 2011 16:49:14 Mark Brown wrote:
Quoted 13 lines · Charles Manning
Quoted 11 lines · On Thursday 17 February 2011 14:48:08 Mark Brown wrote:
On Thu, Feb 17, 2011 at 11:12:06AM +1300, Charles Manning wrote:
Even for embedded systems people do end up wanting to do things like using the same kernel on multiple systems which may have different hardware configurations (distros and reference boards are the obvious examples, but I've worked on systems where multiple generations and builds of the product were in active use and similar enough to be maintained from the same kernel). Even with single system kernels there's still an issue with things like reference boards where users are doing things like picking up a new upstream kernel rather than the vendor BSP.
Quoted 2 lines
Every one of the "stupid compile time options" is there because someone that actually **uses** yaffs wanted it. None are there just for fun. The
The fact that users have asked for something doesn't mean that the particular implementation that was chosen to meet the need makes sense. Having lots of compile time options is terribly common in many embedded systems but that doesn't mean it's a good idea to do things this way.
Quoted 3 lines
compile-time switches are very limited - mostly just there to set up default runtime flags that can be overridden at runtime. Some of them are there to work around bugs and limitations in the mtd.
Quoted 6 lines
If these are just setting overridable defaults then that sounds like stuff that doesn't need to be in the kernel in the first place, they're just as trivial to tweak in userspace as with in kernel defines.. For the hardware workarounds having this in the kernel seems reasonable, we just need to figure out a way to propagate it from the machine driver to the filesystem.
Ok. I have no problems dumping all the compile switches that set up defaults or work around mtd bugs. If it is mainlined then this implies that mtd breakage will be fixed in the mtd - something that was not possible in the out-of-kernel code.
Quoted 10 lines
Quoted 5 lines
Even with BSPs, there will often be some board tuning to, do things like set up the mtd partitions.
Picking up an new kernel is easy, so long as the mtd code has not been broken in the interum.
You're missing the use case here - I'm talking about the situation where you completely ignore the vendor BSP and go direct to mainline without reference to the vendor provided stuff. The user may not even have the vendor provided BSP to look at.
I don't understand what problem you are trying to illustrate.
First off, kernel BSPs should always be available in accordance with the GPL.
Secondly the defaults work for just about every case. So long as the mtd has been set up then dropping in yaffs using defaults is generally pretty straight forward and there is no tuning required,
Some tuning is required if the mtd does not pass through all the info that is required. In the fullness of time I would expect that this info would be provided by mtd.
Quoted 10 lines
Quoted 8 lines
Quoted 5 lines
I'm not sure exactly how you'd do this for a filesystem but this is starting to sound a lot like platform data... Though presumably if the data isn't stored on the device currently it'd be a simple matter of programming (if wasteful) to add it.
NB flash file system - not file system! Working via mtd is different to working via blocks.
I do have *some* familiarity with embedded systems.
My point was that the "platform data" -- well just about all of it -- is exposed via the mtd interface. That tells you the number of blocks, block size, bytes per block etc. Enough to figure out almost all the runtime stuff.
Quoted 6 lines
The fact that the
device is a flash device doesn't stop you recording metadata on it - obviously you've got some data on there to record the metadata for stuff like directory layout. If some of this is needed to even read the device then it can't go on the device, but otherwise it could be included somehow.
At this stage yaffs does not really need or use a "superblock" (and it does not store directories). Superblocks are bad in that they provide a single point of failure.
I think I can dump all the config stuff and just set it to the most useful defaults. Many of the settings are really only there for debug etc.
Having looked through the Kconfig I reckon I can cut that and the associated #ifs back to:CONFIG_YAFFS_FS
and add a new one to strip debuggingCONFIG_YAFFS_DEBUG
-- Charles
Quoted 10 lines · Charles Manning
Quoted 4 lines · On Thursday 17 February 2011 16:49:14 Mark Brown wrote:
You're missing the use case here - I'm talking about the situation where you completely ignore the vendor BSP and go direct to mainline without reference to the vendor provided stuff. The user may not even have the vendor provided BSP to look at.
I don't understand what problem you are trying to illustrate.
First off, kernel BSPs should always be available in accordance with the GPL.
Sure, but the effort involved in obtaining them (especially for older boards) often greatly exceeds the value of looking at them - often it's much easier to go direct to mainline.
Quoted 7 lines
Secondly the defaults work for just about every case. So long as the mtd has been set up then dropping in yaffs using defaults is generally pretty straight forward and there is no tuning required,
Some tuning is required if the mtd does not pass through all the info that is required. In the fullness of time I would expect that this info would be provided by mtd.
Your descriptions made it sound like tthis was higher level information that was being provided. If it's just fixups to the flash description then I would expect that the flash driver would handle it as you suggest.
Quoted 3 lines
My point was that the "platform data" -- well just about all of it -- is exposed via the mtd interface. That tells you the number of blocks, block size, bytes per block etc. Enough to figure out almost all the runtime stuff.
Right, which is why I'm assuming that this build time configuration stuff is things that can't be read directly from the hardware and needs to be passed in via some other mechanism. If it's stuff that can be read from the MTD subsystem then clearly it should be read from the MTD subsystem.
Quoted 2 lines
I think I can dump all the config stuff and just set it to the most useful defaults. Many of the settings are really only there for debug etc.
That sounds like a good plan.
Hi!
Quoted 5 lines
Quoted 1 line
remember you can get media from all over the place.
No you can't. This is a flash file system for soldered down flash. I think that is a fundamental place where your understanding of what yaffs is falls down.
That needs to go into Documentation/ somewhere...
...so you can't for example prepare yaffs image on host before copying it to target? What is gained by not having on-disk format?
Quoted 4 lines
Quoted 2 lines
If you can't encode these difference in your on-disk format it has absolutely no business going into mainline with this format.
Yaffs does not really have an on-disk format like most other fs do.
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Hi Charles,
2011/2/9 Charles Manning <[email protected]>:
Quoted 1 line
This is the fifth submission of a yaffs2 patchset.
Is this mainlining attempt still going on?
Yours,
Linus Walleij