2005-04-17 02:20:36 +04:00
/*
* JFFS2 - - Journalling Flash File System , Version 2.
*
* Copyright ( C ) 2001 - 2003 Red Hat , Inc .
* Copyright ( C ) 2004 Thomas Gleixner < tglx @ linutronix . de >
*
* Created by David Woodhouse < dwmw2 @ infradead . org >
* Modified debugged and enhanced by Thomas Gleixner < tglx @ linutronix . de >
*
* For licensing information , see the file ' LICENCE ' in this directory .
*
2005-09-30 17:59:17 +04:00
* $ Id : wbuf . c , v 1.100 2005 / 09 / 30 13 : 59 : 13 dedekind Exp $
2005-04-17 02:20:36 +04:00
*
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/mtd/mtd.h>
# include <linux/crc32.h>
# include <linux/mtd/nand.h>
2005-10-31 02:03:48 +03:00
# include <linux/jiffies.h>
2005-04-17 02:20:36 +04:00
# include "nodelist.h"
/* For testing write failures */
# undef BREAKME
# undef BREAKMEHEADER
# ifdef BREAKME
static unsigned char * brokenbuf ;
# endif
2005-09-30 17:59:17 +04:00
# define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) )
# define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) )
2005-04-17 02:20:36 +04:00
/* max. erase failures before we mark a block bad */
# define MAX_ERASE_FAILURES 2
struct jffs2_inodirty {
uint32_t ino ;
struct jffs2_inodirty * next ;
} ;
static struct jffs2_inodirty inodirty_nomem ;
static int jffs2_wbuf_pending_for_ino ( struct jffs2_sb_info * c , uint32_t ino )
{
struct jffs2_inodirty * this = c - > wbuf_inodes ;
/* If a malloc failed, consider _everything_ dirty */
if ( this = = & inodirty_nomem )
return 1 ;
/* If ino == 0, _any_ non-GC writes mean 'yes' */
if ( this & & ! ino )
return 1 ;
/* Look to see if the inode in question is pending in the wbuf */
while ( this ) {
if ( this - > ino = = ino )
return 1 ;
this = this - > next ;
}
return 0 ;
}
static void jffs2_clear_wbuf_ino_list ( struct jffs2_sb_info * c )
{
struct jffs2_inodirty * this ;
this = c - > wbuf_inodes ;
if ( this ! = & inodirty_nomem ) {
while ( this ) {
struct jffs2_inodirty * next = this - > next ;
kfree ( this ) ;
this = next ;
}
}
c - > wbuf_inodes = NULL ;
}
static void jffs2_wbuf_dirties_inode ( struct jffs2_sb_info * c , uint32_t ino )
{
struct jffs2_inodirty * new ;
/* Mark the superblock dirty so that kupdated will flush... */
2005-03-18 12:58:09 +03:00
jffs2_erase_pending_trigger ( c ) ;
2005-04-17 02:20:36 +04:00
if ( jffs2_wbuf_pending_for_ino ( c , ino ) )
return ;
new = kmalloc ( sizeof ( * new ) , GFP_KERNEL ) ;
if ( ! new ) {
D1 ( printk ( KERN_DEBUG " No memory to allocate inodirty. Fallback to all considered dirty \n " ) ) ;
jffs2_clear_wbuf_ino_list ( c ) ;
c - > wbuf_inodes = & inodirty_nomem ;
return ;
}
new - > ino = ino ;
new - > next = c - > wbuf_inodes ;
c - > wbuf_inodes = new ;
return ;
}
static inline void jffs2_refile_wbuf_blocks ( struct jffs2_sb_info * c )
{
struct list_head * this , * next ;
static int n ;
if ( list_empty ( & c - > erasable_pending_wbuf_list ) )
return ;
list_for_each_safe ( this , next , & c - > erasable_pending_wbuf_list ) {
struct jffs2_eraseblock * jeb = list_entry ( this , struct jffs2_eraseblock , list ) ;
D1 ( printk ( KERN_DEBUG " Removing eraseblock at 0x%08x from erasable_pending_wbuf_list... \n " , jeb - > offset ) ) ;
list_del ( this ) ;
if ( ( jiffies + ( n + + ) ) & 127 ) {
/* Most of the time, we just erase it immediately. Otherwise we
spend ages scanning it on mount , etc . */
D1 ( printk ( KERN_DEBUG " ...and adding to erase_pending_list \n " ) ) ;
list_add_tail ( & jeb - > list , & c - > erase_pending_list ) ;
c - > nr_erasing_blocks + + ;
jffs2_erase_pending_trigger ( c ) ;
} else {
/* Sometimes, however, we leave it elsewhere so it doesn't get
immediately reused , and we spread the load a bit . */
D1 ( printk ( KERN_DEBUG " ...and adding to erasable_list \n " ) ) ;
list_add_tail ( & jeb - > list , & c - > erasable_list ) ;
}
}
}
2005-01-25 00:24:18 +03:00
# define REFILE_NOTEMPTY 0
# define REFILE_ANYWAY 1
static void jffs2_block_refile ( struct jffs2_sb_info * c , struct jffs2_eraseblock * jeb , int allow_empty )
2005-04-17 02:20:36 +04:00
{
D1 ( printk ( " About to refile bad block at %08x \n " , jeb - > offset ) ) ;
/* File the existing block on the bad_used_list.... */
if ( c - > nextblock = = jeb )
c - > nextblock = NULL ;
else /* Not sure this should ever happen... need more coffee */
list_del ( & jeb - > list ) ;
if ( jeb - > first_node ) {
D1 ( printk ( " Refiling block at %08x to bad_used_list \n " , jeb - > offset ) ) ;
list_add ( & jeb - > list , & c - > bad_used_list ) ;
} else {
2005-01-28 21:53:05 +03:00
BUG_ON ( allow_empty = = REFILE_NOTEMPTY ) ;
2005-04-17 02:20:36 +04:00
/* It has to have had some nodes or we couldn't be here */
D1 ( printk ( " Refiling block at %08x to erase_pending_list \n " , jeb - > offset ) ) ;
list_add ( & jeb - > list , & c - > erase_pending_list ) ;
c - > nr_erasing_blocks + + ;
jffs2_erase_pending_trigger ( c ) ;
}
2006-05-27 00:19:05 +04:00
if ( ! jffs2_prealloc_raw_node_refs ( c , jeb , 1 ) ) {
uint32_t oldfree = jeb - > free_size ;
jffs2_link_node_ref ( c , jeb ,
( jeb - > offset + c - > sector_size - oldfree ) | REF_OBSOLETE ,
oldfree , NULL ) ;
/* convert to wasted */
c - > wasted_size + = oldfree ;
jeb - > wasted_size + = oldfree ;
c - > dirty_size - = oldfree ;
jeb - > dirty_size - = oldfree ;
}
2005-04-17 02:20:36 +04:00
2005-07-24 19:14:17 +04:00
jffs2_dbg_dump_block_lists_nolock ( c ) ;
jffs2_dbg_acct_sanity_check_nolock ( c , jeb ) ;
jffs2_dbg_acct_paranoia_check_nolock ( c , jeb ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-27 00:19:05 +04:00
static struct jffs2_raw_node_ref * * jffs2_incore_replace_raw ( struct jffs2_sb_info * c ,
struct jffs2_inode_info * f ,
struct jffs2_raw_node_ref * raw ,
union jffs2_node_union * node )
{
struct jffs2_node_frag * frag ;
struct jffs2_full_dirent * fd ;
dbg_noderef ( " incore_replace_raw: node at %p is {%04x,%04x} \n " ,
node , je16_to_cpu ( node - > u . magic ) , je16_to_cpu ( node - > u . nodetype ) ) ;
BUG_ON ( je16_to_cpu ( node - > u . magic ) ! = 0x1985 & &
je16_to_cpu ( node - > u . magic ) ! = 0 ) ;
switch ( je16_to_cpu ( node - > u . nodetype ) ) {
case JFFS2_NODETYPE_INODE :
2006-05-27 16:15:16 +04:00
if ( f - > metadata & & f - > metadata - > raw = = raw ) {
dbg_noderef ( " Will replace ->raw in f->metadata at %p \n " , f - > metadata ) ;
return & f - > metadata - > raw ;
}
2006-05-27 00:19:05 +04:00
frag = jffs2_lookup_node_frag ( & f - > fragtree , je32_to_cpu ( node - > i . offset ) ) ;
BUG_ON ( ! frag ) ;
/* Find a frag which refers to the full_dnode we want to modify */
while ( ! frag - > node | | frag - > node - > raw ! = raw ) {
frag = frag_next ( frag ) ;
BUG_ON ( ! frag ) ;
}
dbg_noderef ( " Will replace ->raw in full_dnode at %p \n " , frag - > node ) ;
return & frag - > node - > raw ;
case JFFS2_NODETYPE_DIRENT :
for ( fd = f - > dents ; fd ; fd = fd - > next ) {
if ( fd - > raw = = raw ) {
dbg_noderef ( " Will replace ->raw in full_dirent at %p \n " , fd ) ;
return & fd - > raw ;
}
}
BUG ( ) ;
2006-05-27 16:15:16 +04:00
2006-05-27 00:19:05 +04:00
default :
dbg_noderef ( " Don't care about replacing raw for nodetype %x \n " ,
je16_to_cpu ( node - > u . nodetype ) ) ;
break ;
}
return NULL ;
}
2005-04-17 02:20:36 +04:00
/* Recover from failure to write wbuf. Recover the nodes up to the
* wbuf , not the one which we were starting to try to write . */
static void jffs2_wbuf_recover ( struct jffs2_sb_info * c )
{
struct jffs2_eraseblock * jeb , * new_jeb ;
2006-05-27 00:19:05 +04:00
struct jffs2_raw_node_ref * raw , * next , * first_raw = NULL ;
2005-04-17 02:20:36 +04:00
size_t retlen ;
int ret ;
2006-05-27 00:19:05 +04:00
int nr_refile = 0 ;
2005-04-17 02:20:36 +04:00
unsigned char * buf ;
uint32_t start , end , ofs , len ;
2006-05-25 04:50:35 +04:00
jeb = & c - > blocks [ c - > wbuf_ofs / c - > sector_size ] ;
2005-04-17 02:20:36 +04:00
spin_lock ( & c - > erase_completion_lock ) ;
2005-01-25 00:24:18 +03:00
jffs2_block_refile ( c , jeb , REFILE_NOTEMPTY ) ;
2006-05-27 00:19:05 +04:00
spin_unlock ( & c - > erase_completion_lock ) ;
BUG_ON ( ! ref_obsolete ( jeb - > last_node ) ) ;
2005-04-17 02:20:36 +04:00
/* Find the first node to be recovered, by skipping over every
node which ends before the wbuf starts , or which is obsolete . */
2006-05-27 00:19:05 +04:00
for ( next = raw = jeb - > first_node ; next ; raw = next ) {
next = ref_next ( raw ) ;
if ( ref_obsolete ( raw ) | |
( next & & ref_offset ( next ) < = c - > wbuf_ofs ) ) {
dbg_noderef ( " Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete \n " ,
ref_offset ( raw ) , ref_flags ( raw ) ,
( ref_offset ( raw ) + ref_totlen ( c , jeb , raw ) ) ,
c - > wbuf_ofs ) ;
continue ;
}
dbg_noderef ( " First node to be recovered is at 0x%08x(%d)-0x%08x \n " ,
ref_offset ( raw ) , ref_flags ( raw ) ,
( ref_offset ( raw ) + ref_totlen ( c , jeb , raw ) ) ) ;
first_raw = raw ;
break ;
}
if ( ! first_raw ) {
2005-04-17 02:20:36 +04:00
/* All nodes were obsolete. Nothing to recover. */
D1 ( printk ( KERN_DEBUG " No non-obsolete nodes to be recovered. Just filing block bad \n " ) ) ;
2006-05-27 00:19:05 +04:00
c - > wbuf_len = 0 ;
2005-04-17 02:20:36 +04:00
return ;
}
2006-05-27 00:19:05 +04:00
start = ref_offset ( first_raw ) ;
end = ref_offset ( jeb - > last_node ) ;
nr_refile = 1 ;
2005-04-17 02:20:36 +04:00
2006-05-27 00:19:05 +04:00
/* Count the number of refs which need to be copied */
while ( ( raw = ref_next ( raw ) ) ! = jeb - > last_node )
nr_refile + + ;
2005-04-17 02:20:36 +04:00
2006-05-27 00:19:05 +04:00
dbg_noderef ( " wbuf recover %08x-%08x (%d bytes in %d nodes) \n " ,
start , end , end - start , nr_refile ) ;
2005-04-17 02:20:36 +04:00
buf = NULL ;
if ( start < c - > wbuf_ofs ) {
/* First affected node was already partially written.
* Attempt to reread the old data into our buffer . */
buf = kmalloc ( end - start , GFP_KERNEL ) ;
if ( ! buf ) {
printk ( KERN_CRIT " Malloc failure in wbuf recovery. Data loss ensues. \n " ) ;
goto read_failed ;
}
/* Do the read... */
2006-05-23 19:21:03 +04:00
ret = c - > mtd - > read ( c - > mtd , start , c - > wbuf_ofs - start , & retlen , buf ) ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
if ( ret = = - EBADMSG & & retlen = = c - > wbuf_ofs - start ) {
/* ECC recovered */
ret = 0 ;
}
if ( ret | | retlen ! = c - > wbuf_ofs - start ) {
printk ( KERN_CRIT " Old data are already lost in wbuf recovery. Data loss ensues. \n " ) ;
kfree ( buf ) ;
buf = NULL ;
read_failed :
2006-05-27 00:19:05 +04:00
first_raw = ref_next ( first_raw ) ;
nr_refile - - ;
while ( first_raw & & ref_obsolete ( first_raw ) ) {
first_raw = ref_next ( first_raw ) ;
nr_refile - - ;
}
2005-04-17 02:20:36 +04:00
/* If this was the only node to be recovered, give up */
2006-05-27 00:19:05 +04:00
if ( ! first_raw ) {
c - > wbuf_len = 0 ;
2005-04-17 02:20:36 +04:00
return ;
2006-05-27 00:19:05 +04:00
}
2005-04-17 02:20:36 +04:00
/* It wasn't. Go on and try to recover nodes complete in the wbuf */
2006-05-27 00:19:05 +04:00
start = ref_offset ( first_raw ) ;
dbg_noderef ( " wbuf now recover %08x-%08x (%d bytes in %d nodes) \n " ,
start , end , end - start , nr_refile ) ;
2005-04-17 02:20:36 +04:00
} else {
/* Read succeeded. Copy the remaining data from the wbuf */
memcpy ( buf + ( c - > wbuf_ofs - start ) , c - > wbuf , end - c - > wbuf_ofs ) ;
}
}
/* OK... we're to rewrite (end-start) bytes of data from first_raw onwards.
Either ' buf ' contains the data , or we find it in the wbuf */
/* ... and get an allocation of space from a shiny new block instead */
2006-05-23 03:38:06 +04:00
ret = jffs2_reserve_space_gc ( c , end - start , & len , JFFS2_SUMMARY_NOSUM_SIZE ) ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
printk ( KERN_WARNING " Failed to allocate space for wbuf recovery. Data loss ensues. \n " ) ;
2005-01-28 21:53:05 +03:00
kfree ( buf ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2006-05-27 00:19:05 +04:00
ret = jffs2_prealloc_raw_node_refs ( c , c - > nextblock , nr_refile ) ;
if ( ret ) {
printk ( KERN_WARNING " Failed to allocate node refs for wbuf recovery. Data loss ensues. \n " ) ;
kfree ( buf ) ;
return ;
}
2006-05-23 03:38:06 +04:00
ofs = write_ofs ( c ) ;
2005-04-17 02:20:36 +04:00
if ( end - start > = c - > wbuf_pagesize ) {
2005-01-25 00:24:18 +03:00
/* Need to do another write immediately, but it's possible
2005-01-28 21:53:05 +03:00
that this is just because the wbuf itself is completely
2005-11-07 14:16:07 +03:00
full , and there ' s nothing earlier read back from the
flash . Hence ' buf ' isn ' t necessarily what we ' re writing
2005-01-28 21:53:05 +03:00
from . */
2005-01-25 00:24:18 +03:00
unsigned char * rewrite_buf = buf ? : c - > wbuf ;
2005-04-17 02:20:36 +04:00
uint32_t towrite = ( end - start ) - ( ( end - start ) % c - > wbuf_pagesize ) ;
D1 ( printk ( KERN_DEBUG " Write 0x%x bytes at 0x%08x in wbuf recover \n " ,
towrite , ofs ) ) ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
# ifdef BREAKMEHEADER
static int breakme ;
if ( breakme + + = = 20 ) {
printk ( KERN_NOTICE " Faking write error at 0x%08x \n " , ofs ) ;
breakme = 0 ;
2006-05-23 19:21:03 +04:00
c - > mtd - > write ( c - > mtd , ofs , towrite , & retlen ,
brokenbuf ) ;
2005-04-17 02:20:36 +04:00
ret = - EIO ;
} else
# endif
2006-05-23 19:21:03 +04:00
ret = c - > mtd - > write ( c - > mtd , ofs , towrite , & retlen ,
rewrite_buf ) ;
2005-04-17 02:20:36 +04:00
if ( ret | | retlen ! = towrite ) {
/* Argh. We tried. Really we did. */
printk ( KERN_CRIT " Recovery of wbuf failed due to a second write error \n " ) ;
2005-01-28 21:53:05 +03:00
kfree ( buf ) ;
2005-04-17 02:20:36 +04:00
2006-05-24 05:04:45 +04:00
if ( retlen )
2006-05-27 00:19:05 +04:00
jffs2_add_physical_node_ref ( c , ofs | REF_OBSOLETE , ref_totlen ( c , jeb , first_raw ) , NULL ) ;
2005-04-17 02:20:36 +04:00
return ;
}
printk ( KERN_NOTICE " Recovery of wbuf succeeded to %08x \n " , ofs ) ;
c - > wbuf_len = ( end - start ) - towrite ;
c - > wbuf_ofs = ofs + towrite ;
2005-01-25 00:24:18 +03:00
memmove ( c - > wbuf , rewrite_buf + towrite , c - > wbuf_len ) ;
2005-04-17 02:20:36 +04:00
/* Don't muck about with c->wbuf_inodes. False positives are harmless. */
} else {
/* OK, now we're left with the dregs in whichever buffer we're using */
if ( buf ) {
memcpy ( c - > wbuf , buf , end - start ) ;
} else {
memmove ( c - > wbuf , c - > wbuf + ( start - c - > wbuf_ofs ) , end - start ) ;
}
c - > wbuf_ofs = ofs ;
c - > wbuf_len = end - start ;
}
/* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */
new_jeb = & c - > blocks [ ofs / c - > sector_size ] ;
spin_lock ( & c - > erase_completion_lock ) ;
2006-05-27 00:19:05 +04:00
for ( raw = first_raw ; raw ! = jeb - > last_node ; raw = ref_next ( raw ) ) {
uint32_t rawlen = ref_totlen ( c , jeb , raw ) ;
struct jffs2_inode_cache * ic ;
struct jffs2_raw_node_ref * new_ref ;
struct jffs2_raw_node_ref * * adjust_ref = NULL ;
struct jffs2_inode_info * f = NULL ;
2005-04-17 02:20:36 +04:00
D1 ( printk ( KERN_DEBUG " Refiling block of %08x at %08x(%d) to %08x \n " ,
2006-05-27 00:19:05 +04:00
rawlen , ref_offset ( raw ) , ref_flags ( raw ) , ofs ) ) ;
ic = jffs2_raw_ref_to_ic ( raw ) ;
/* Ick. This XATTR mess should be fixed shortly... */
if ( ic & & ic - > class = = RAWNODE_CLASS_XATTR_DATUM ) {
struct jffs2_xattr_datum * xd = ( void * ) ic ;
BUG_ON ( xd - > node ! = raw ) ;
adjust_ref = & xd - > node ;
raw - > next_in_ino = NULL ;
ic = NULL ;
} else if ( ic & & ic - > class = = RAWNODE_CLASS_XATTR_REF ) {
struct jffs2_xattr_datum * xr = ( void * ) ic ;
BUG_ON ( xr - > node ! = raw ) ;
adjust_ref = & xr - > node ;
raw - > next_in_ino = NULL ;
ic = NULL ;
} else if ( ic & & ic - > class = = RAWNODE_CLASS_INODE_CACHE ) {
struct jffs2_raw_node_ref * * p = & ic - > nodes ;
/* Remove the old node from the per-inode list */
while ( * p & & * p ! = ( void * ) ic ) {
if ( * p = = raw ) {
( * p ) = ( raw - > next_in_ino ) ;
raw - > next_in_ino = NULL ;
break ;
}
p = & ( ( * p ) - > next_in_ino ) ;
}
2005-04-17 02:20:36 +04:00
2006-05-27 00:19:05 +04:00
if ( ic - > state = = INO_STATE_PRESENT & & ! ref_obsolete ( raw ) ) {
/* If it's an in-core inode, then we have to adjust any
full_dirent or full_dnode structure to point to the
new version instead of the old */
f = jffs2_gc_fetch_inode ( c , ic - > ino , ic - > nlink ) ;
if ( IS_ERR ( f ) ) {
/* Should never happen; it _must_ be present */
JFFS2_ERROR ( " Failed to iget() ino #%u, err %ld \n " ,
ic - > ino , PTR_ERR ( f ) ) ;
BUG ( ) ;
}
/* We don't lock f->sem. There's a number of ways we could
end up in here with it already being locked , and nobody ' s
going to modify it on us anyway because we hold the
alloc_sem . We ' re only changing one - > raw pointer too ,
which we can get away with without upsetting readers . */
adjust_ref = jffs2_incore_replace_raw ( c , f , raw ,
( void * ) ( buf ? : c - > wbuf ) + ( ref_offset ( raw ) - start ) ) ;
} else if ( unlikely ( ic - > state ! = INO_STATE_PRESENT & &
ic - > state ! = INO_STATE_CHECKEDABSENT & &
ic - > state ! = INO_STATE_GC ) ) {
JFFS2_ERROR ( " Inode #%u is in strange state %d! \n " , ic - > ino , ic - > state ) ;
BUG ( ) ;
}
}
new_ref = jffs2_link_node_ref ( c , new_jeb , ofs | ref_flags ( raw ) , rawlen , ic ) ;
if ( adjust_ref ) {
BUG_ON ( * adjust_ref ! = raw ) ;
* adjust_ref = new_ref ;
}
if ( f )
jffs2_gc_release_inode ( c , f ) ;
if ( ! ref_obsolete ( raw ) ) {
2005-04-17 02:20:36 +04:00
jeb - > dirty_size + = rawlen ;
jeb - > used_size - = rawlen ;
c - > dirty_size + = rawlen ;
2006-05-27 00:19:05 +04:00
c - > used_size - = rawlen ;
raw - > flash_offset = ref_offset ( raw ) | REF_OBSOLETE ;
BUG_ON ( raw - > next_in_ino ) ;
2005-04-17 02:20:36 +04:00
}
ofs + = rawlen ;
}
2006-05-27 00:19:05 +04:00
kfree ( buf ) ;
2005-04-17 02:20:36 +04:00
/* Fix up the original jeb now it's on the bad_list */
2006-05-27 00:19:05 +04:00
if ( first_raw = = jeb - > first_node ) {
2005-04-17 02:20:36 +04:00
D1 ( printk ( KERN_DEBUG " Failing block at %08x is now empty. Moving to erase_pending_list \n " , jeb - > offset ) ) ;
list_del ( & jeb - > list ) ;
list_add ( & jeb - > list , & c - > erase_pending_list ) ;
c - > nr_erasing_blocks + + ;
jffs2_erase_pending_trigger ( c ) ;
}
2005-07-24 19:14:17 +04:00
jffs2_dbg_acct_sanity_check_nolock ( c , jeb ) ;
2006-05-27 00:19:05 +04:00
jffs2_dbg_acct_paranoia_check_nolock ( c , jeb ) ;
2005-04-17 02:20:36 +04:00
2005-07-24 19:14:17 +04:00
jffs2_dbg_acct_sanity_check_nolock ( c , new_jeb ) ;
2006-05-27 00:19:05 +04:00
jffs2_dbg_acct_paranoia_check_nolock ( c , new_jeb ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & c - > erase_completion_lock ) ;
2006-05-27 00:19:05 +04:00
D1 ( printk ( KERN_DEBUG " wbuf recovery completed OK. wbuf_ofs 0x%08x, len 0x%x \n " , c - > wbuf_ofs , c - > wbuf_len ) ) ;
2005-04-17 02:20:36 +04:00
}
/* Meaning of pad argument:
0 : Do not pad . Probably pointless - we only ever use this when we can ' t pad anyway .
1 : Pad , do not adjust nextblock free_size
2 : Pad , adjust nextblock free_size
*/
# define NOPAD 0
# define PAD_NOACCOUNT 1
# define PAD_ACCOUNTING 2
static int __jffs2_flush_wbuf ( struct jffs2_sb_info * c , int pad )
{
2006-05-27 00:19:05 +04:00
struct jffs2_eraseblock * wbuf_jeb ;
2005-04-17 02:20:36 +04:00
int ret ;
size_t retlen ;
2005-02-09 12:09:05 +03:00
/* Nothing to do if not write-buffering the flash. In particular, we shouldn't
2005-04-17 02:20:36 +04:00
del_timer ( ) the timer we never initialised . */
2005-02-09 12:09:05 +03:00
if ( ! jffs2_is_writebuffered ( c ) )
2005-04-17 02:20:36 +04:00
return 0 ;
if ( ! down_trylock ( & c - > alloc_sem ) ) {
up ( & c - > alloc_sem ) ;
printk ( KERN_CRIT " jffs2_flush_wbuf() called with alloc_sem not locked! \n " ) ;
BUG ( ) ;
}
2005-02-09 12:09:05 +03:00
if ( ! c - > wbuf_len ) /* already checked c->wbuf above */
2005-04-17 02:20:36 +04:00
return 0 ;
2006-05-27 00:19:05 +04:00
wbuf_jeb = & c - > blocks [ c - > wbuf_ofs / c - > sector_size ] ;
if ( jffs2_prealloc_raw_node_refs ( c , wbuf_jeb , c - > nextblock - > allocated_refs + 1 ) )
2006-05-24 05:04:45 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
/* claim remaining space on the page
this happens , if we have a change to a new block ,
or if fsync forces us to flush the writebuffer .
if we have a switch to next page , we will not have
2005-11-07 14:16:07 +03:00
enough remaining space for this .
2005-04-17 02:20:36 +04:00
*/
2005-09-30 17:59:17 +04:00
if ( pad ) {
2005-04-17 02:20:36 +04:00
c - > wbuf_len = PAD ( c - > wbuf_len ) ;
/* Pad with JFFS2_DIRTY_BITMASK initially. this helps out ECC'd NOR
with 8 byte page size */
memset ( c - > wbuf + c - > wbuf_len , 0 , c - > wbuf_pagesize - c - > wbuf_len ) ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
if ( c - > wbuf_len + sizeof ( struct jffs2_unknown_node ) < c - > wbuf_pagesize ) {
struct jffs2_unknown_node * padnode = ( void * ) ( c - > wbuf + c - > wbuf_len ) ;
padnode - > magic = cpu_to_je16 ( JFFS2_MAGIC_BITMASK ) ;
padnode - > nodetype = cpu_to_je16 ( JFFS2_NODETYPE_PADDING ) ;
padnode - > totlen = cpu_to_je32 ( c - > wbuf_pagesize - c - > wbuf_len ) ;
padnode - > hdr_crc = cpu_to_je32 ( crc32 ( 0 , padnode , sizeof ( * padnode ) - 4 ) ) ;
}
}
/* else jffs2_flash_writev has actually filled in the rest of the
buffer for us , and will deal with the node refs etc . later . */
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
# ifdef BREAKME
static int breakme ;
if ( breakme + + = = 20 ) {
printk ( KERN_NOTICE " Faking write error at 0x%08x \n " , c - > wbuf_ofs ) ;
breakme = 0 ;
2006-05-23 19:21:03 +04:00
c - > mtd - > write ( c - > mtd , c - > wbuf_ofs , c - > wbuf_pagesize , & retlen ,
brokenbuf ) ;
2005-04-17 02:20:36 +04:00
ret = - EIO ;
2005-11-07 14:16:07 +03:00
} else
2005-04-17 02:20:36 +04:00
# endif
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
ret = c - > mtd - > write ( c - > mtd , c - > wbuf_ofs , c - > wbuf_pagesize , & retlen , c - > wbuf ) ;
if ( ret | | retlen ! = c - > wbuf_pagesize ) {
if ( ret )
printk ( KERN_WARNING " jffs2_flush_wbuf(): Write failed with %d \n " , ret ) ;
else {
printk ( KERN_WARNING " jffs2_flush_wbuf(): Write was short: %zd instead of %d \n " ,
retlen , c - > wbuf_pagesize ) ;
ret = - EIO ;
}
jffs2_wbuf_recover ( c ) ;
return ret ;
}
/* Adjust free size of the block if we padded. */
2005-09-30 17:59:17 +04:00
if ( pad ) {
2006-05-21 16:00:54 +04:00
uint32_t waste = c - > wbuf_pagesize - c - > wbuf_len ;
2005-04-17 02:20:36 +04:00
D1 ( printk ( KERN_DEBUG " jffs2_flush_wbuf() adjusting free_size of %sblock at %08x \n " ,
2006-05-27 00:19:05 +04:00
( wbuf_jeb = = c - > nextblock ) ? " next " : " " , wbuf_jeb - > offset ) ) ;
2005-04-17 02:20:36 +04:00
2005-11-07 14:16:07 +03:00
/* wbuf_pagesize - wbuf_len is the amount of space that's to be
2005-04-17 02:20:36 +04:00
padded . If there is less free space in the block than that ,
something screwed up */
2006-05-27 00:19:05 +04:00
if ( wbuf_jeb - > free_size < waste ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_CRIT " jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left. \n " ,
2006-05-21 16:00:54 +04:00
c - > wbuf_ofs , c - > wbuf_len , waste ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_CRIT " jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x \n " ,
2006-05-27 00:19:05 +04:00
wbuf_jeb - > offset , wbuf_jeb - > free_size ) ;
2005-04-17 02:20:36 +04:00
BUG ( ) ;
}
2006-05-21 16:00:54 +04:00
spin_lock ( & c - > erase_completion_lock ) ;
2006-05-27 00:19:05 +04:00
jffs2_link_node_ref ( c , wbuf_jeb , ( c - > wbuf_ofs + c - > wbuf_len ) | REF_OBSOLETE , waste , NULL ) ;
2006-05-21 16:00:54 +04:00
/* FIXME: that made it count as dirty. Convert to wasted */
2006-05-27 00:19:05 +04:00
wbuf_jeb - > dirty_size - = waste ;
2006-05-21 16:00:54 +04:00
c - > dirty_size - = waste ;
2006-05-27 00:19:05 +04:00
wbuf_jeb - > wasted_size + = waste ;
2006-05-21 16:00:54 +04:00
c - > wasted_size + = waste ;
} else
spin_lock ( & c - > erase_completion_lock ) ;
2005-04-17 02:20:36 +04:00
/* Stick any now-obsoleted blocks on the erase_pending_list */
jffs2_refile_wbuf_blocks ( c ) ;
jffs2_clear_wbuf_ino_list ( c ) ;
spin_unlock ( & c - > erase_completion_lock ) ;
memset ( c - > wbuf , 0xff , c - > wbuf_pagesize ) ;
/* adjust write buffer offset, else we get a non contiguous write bug */
c - > wbuf_ofs + = c - > wbuf_pagesize ;
c - > wbuf_len = 0 ;
return 0 ;
}
2005-11-07 14:16:07 +03:00
/* Trigger garbage collection to flush the write-buffer.
2005-04-17 02:20:36 +04:00
If ino arg is zero , do it if _any_ real ( i . e . not GC ) writes are
2005-11-07 14:16:07 +03:00
outstanding . If ino arg non - zero , do it only if a write for the
2005-04-17 02:20:36 +04:00
given inode is outstanding . */
int jffs2_flush_wbuf_gc ( struct jffs2_sb_info * c , uint32_t ino )
{
uint32_t old_wbuf_ofs ;
uint32_t old_wbuf_len ;
int ret = 0 ;
D1 ( printk ( KERN_DEBUG " jffs2_flush_wbuf_gc() called for ino #%u... \n " , ino ) ) ;
2005-02-03 01:12:08 +03:00
if ( ! c - > wbuf )
return 0 ;
2005-04-17 02:20:36 +04:00
down ( & c - > alloc_sem ) ;
if ( ! jffs2_wbuf_pending_for_ino ( c , ino ) ) {
D1 ( printk ( KERN_DEBUG " Ino #%d not pending in wbuf. Returning \n " , ino ) ) ;
up ( & c - > alloc_sem ) ;
return 0 ;
}
old_wbuf_ofs = c - > wbuf_ofs ;
old_wbuf_len = c - > wbuf_len ;
if ( c - > unchecked_size ) {
/* GC won't make any progress for a while */
D1 ( printk ( KERN_DEBUG " jffs2_flush_wbuf_gc() padding. Not finished checking \n " ) ) ;
down_write ( & c - > wbuf_sem ) ;
ret = __jffs2_flush_wbuf ( c , PAD_ACCOUNTING ) ;
2005-01-25 00:24:18 +03:00
/* retry flushing wbuf in case jffs2_wbuf_recover
left some data in the wbuf */
if ( ret )
ret = __jffs2_flush_wbuf ( c , PAD_ACCOUNTING ) ;
2005-04-17 02:20:36 +04:00
up_write ( & c - > wbuf_sem ) ;
} else while ( old_wbuf_len & &
old_wbuf_ofs = = c - > wbuf_ofs ) {
up ( & c - > alloc_sem ) ;
D1 ( printk ( KERN_DEBUG " jffs2_flush_wbuf_gc() calls gc pass \n " ) ) ;
ret = jffs2_garbage_collect_pass ( c ) ;
if ( ret ) {
/* GC failed. Flush it with padding instead */
down ( & c - > alloc_sem ) ;
down_write ( & c - > wbuf_sem ) ;
ret = __jffs2_flush_wbuf ( c , PAD_ACCOUNTING ) ;
2005-01-25 00:24:18 +03:00
/* retry flushing wbuf in case jffs2_wbuf_recover
left some data in the wbuf */
if ( ret )
ret = __jffs2_flush_wbuf ( c , PAD_ACCOUNTING ) ;
2005-04-17 02:20:36 +04:00
up_write ( & c - > wbuf_sem ) ;
break ;
}
down ( & c - > alloc_sem ) ;
}
D1 ( printk ( KERN_DEBUG " jffs2_flush_wbuf_gc() ends... \n " ) ) ;
up ( & c - > alloc_sem ) ;
return ret ;
}
/* Pad write-buffer to end and write it, wasting space. */
int jffs2_flush_wbuf_pad ( struct jffs2_sb_info * c )
{
int ret ;
2005-02-03 01:12:08 +03:00
if ( ! c - > wbuf )
return 0 ;
2005-04-17 02:20:36 +04:00
down_write ( & c - > wbuf_sem ) ;
ret = __jffs2_flush_wbuf ( c , PAD_NOACCOUNT ) ;
2005-01-25 00:24:18 +03:00
/* retry - maybe wbuf recover left some data in wbuf. */
if ( ret )
ret = __jffs2_flush_wbuf ( c , PAD_NOACCOUNT ) ;
2005-04-17 02:20:36 +04:00
up_write ( & c - > wbuf_sem ) ;
return ret ;
}
2006-05-23 13:49:14 +04:00
static size_t jffs2_fill_wbuf ( struct jffs2_sb_info * c , const uint8_t * buf ,
size_t len )
2005-04-17 02:20:36 +04:00
{
2006-05-23 13:49:14 +04:00
if ( len & & ! c - > wbuf_len & & ( len > = c - > wbuf_pagesize ) )
return 0 ;
if ( len > ( c - > wbuf_pagesize - c - > wbuf_len ) )
len = c - > wbuf_pagesize - c - > wbuf_len ;
memcpy ( c - > wbuf + c - > wbuf_len , buf , len ) ;
c - > wbuf_len + = ( uint32_t ) len ;
return len ;
}
int jffs2_flash_writev ( struct jffs2_sb_info * c , const struct kvec * invecs ,
unsigned long count , loff_t to , size_t * retlen ,
uint32_t ino )
{
struct jffs2_eraseblock * jeb ;
size_t wbuf_retlen , donelen = 0 ;
2005-04-17 02:20:36 +04:00
uint32_t outvec_to = to ;
2006-05-23 13:49:14 +04:00
int ret , invec ;
2005-04-17 02:20:36 +04:00
2006-05-23 13:49:14 +04:00
/* If not writebuffered flash, don't bother */
2005-02-09 12:09:05 +03:00
if ( ! jffs2_is_writebuffered ( c ) )
2005-04-17 02:20:36 +04:00
return jffs2_flash_direct_writev ( c , invecs , count , to , retlen ) ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
down_write ( & c - > wbuf_sem ) ;
/* If wbuf_ofs is not initialized, set it to target address */
if ( c - > wbuf_ofs = = 0xFFFFFFFF ) {
c - > wbuf_ofs = PAGE_DIV ( to ) ;
2005-11-07 14:16:07 +03:00
c - > wbuf_len = PAGE_MOD ( to ) ;
2005-04-17 02:20:36 +04:00
memset ( c - > wbuf , 0xff , c - > wbuf_pagesize ) ;
}
2006-05-23 13:49:14 +04:00
/*
* Sanity checks on target address . It ' s permitted to write
* at PAD ( c - > wbuf_len + c - > wbuf_ofs ) , and it ' s permitted to
* write at the beginning of a new erase block . Anything else ,
* and you die . New block starts at xxx000c ( 0 - b = block
* header )
*/
2005-02-09 12:09:05 +03:00
if ( SECTOR_ADDR ( to ) ! = SECTOR_ADDR ( c - > wbuf_ofs ) ) {
2005-04-17 02:20:36 +04:00
/* It's a write to a new block */
if ( c - > wbuf_len ) {
2006-05-23 13:49:14 +04:00
D1 ( printk ( KERN_DEBUG " jffs2_flash_writev() to 0x%lx "
" causes flush of wbuf at 0x%08x \n " ,
( unsigned long ) to , c - > wbuf_ofs ) ) ;
2005-04-17 02:20:36 +04:00
ret = __jffs2_flush_wbuf ( c , PAD_NOACCOUNT ) ;
2006-05-23 13:49:14 +04:00
if ( ret )
goto outerr ;
2005-04-17 02:20:36 +04:00
}
/* set pointer to new block */
c - > wbuf_ofs = PAGE_DIV ( to ) ;
2005-11-07 14:16:07 +03:00
c - > wbuf_len = PAGE_MOD ( to ) ;
}
2005-04-17 02:20:36 +04:00
if ( to ! = PAD ( c - > wbuf_ofs + c - > wbuf_len ) ) {
/* We're not writing immediately after the writebuffer. Bad. */
2006-05-23 13:49:14 +04:00
printk ( KERN_CRIT " jffs2_flash_writev(): Non-contiguous write "
" to %08lx \n " , ( unsigned long ) to ) ;
2005-04-17 02:20:36 +04:00
if ( c - > wbuf_len )
printk ( KERN_CRIT " wbuf was previously %08x-%08x \n " ,
2006-05-23 13:49:14 +04:00
c - > wbuf_ofs , c - > wbuf_ofs + c - > wbuf_len ) ;
2005-04-17 02:20:36 +04:00
BUG ( ) ;
}
2006-05-23 13:49:14 +04:00
/* adjust alignment offset */
if ( c - > wbuf_len ! = PAGE_MOD ( to ) ) {
c - > wbuf_len = PAGE_MOD ( to ) ;
/* take care of alignment to next page */
if ( ! c - > wbuf_len ) {
c - > wbuf_len = c - > wbuf_pagesize ;
ret = __jffs2_flush_wbuf ( c , NOPAD ) ;
if ( ret )
goto outerr ;
2005-04-17 02:20:36 +04:00
}
}
2006-05-23 13:49:14 +04:00
for ( invec = 0 ; invec < count ; invec + + ) {
int vlen = invecs [ invec ] . iov_len ;
uint8_t * v = invecs [ invec ] . iov_base ;
2005-01-25 00:24:18 +03:00
2006-05-23 13:49:14 +04:00
wbuf_retlen = jffs2_fill_wbuf ( c , v , vlen ) ;
2005-01-25 00:24:18 +03:00
2006-05-23 13:49:14 +04:00
if ( c - > wbuf_len = = c - > wbuf_pagesize ) {
ret = __jffs2_flush_wbuf ( c , NOPAD ) ;
if ( ret )
goto outerr ;
2005-04-17 02:20:36 +04:00
}
2006-05-23 13:49:14 +04:00
vlen - = wbuf_retlen ;
outvec_to + = wbuf_retlen ;
2005-04-17 02:20:36 +04:00
donelen + = wbuf_retlen ;
2006-05-23 13:49:14 +04:00
v + = wbuf_retlen ;
if ( vlen > = c - > wbuf_pagesize ) {
ret = c - > mtd - > write ( c - > mtd , outvec_to , PAGE_DIV ( vlen ) ,
& wbuf_retlen , v ) ;
if ( ret < 0 | | wbuf_retlen ! = PAGE_DIV ( vlen ) )
goto outfile ;
vlen - = wbuf_retlen ;
outvec_to + = wbuf_retlen ;
c - > wbuf_ofs = outvec_to ;
donelen + = wbuf_retlen ;
v + = wbuf_retlen ;
2005-04-17 02:20:36 +04:00
}
2006-05-23 13:49:14 +04:00
wbuf_retlen = jffs2_fill_wbuf ( c , v , vlen ) ;
if ( c - > wbuf_len = = c - > wbuf_pagesize ) {
ret = __jffs2_flush_wbuf ( c , NOPAD ) ;
if ( ret )
goto outerr ;
}
2005-04-17 02:20:36 +04:00
2006-05-23 13:49:14 +04:00
outvec_to + = wbuf_retlen ;
donelen + = wbuf_retlen ;
2005-04-17 02:20:36 +04:00
}
2006-05-23 13:49:14 +04:00
/*
* If there ' s a remainder in the wbuf and it ' s a non - GC write ,
* remember that the wbuf affects this ino
*/
2005-04-17 02:20:36 +04:00
* retlen = donelen ;
2005-09-07 12:35:26 +04:00
if ( jffs2_sum_active ( ) ) {
int res = jffs2_sum_add_kvec ( c , invecs , count , ( uint32_t ) to ) ;
if ( res )
return res ;
}
2005-04-17 02:20:36 +04:00
if ( c - > wbuf_len & & ino )
jffs2_wbuf_dirties_inode ( c , ino ) ;
ret = 0 ;
2006-05-23 13:49:14 +04:00
up_write ( & c - > wbuf_sem ) ;
return ret ;
outfile :
/*
* At this point we have no problem , c - > wbuf is empty . However
* refile nextblock to avoid writing again to same address .
*/
spin_lock ( & c - > erase_completion_lock ) ;
jeb = & c - > blocks [ outvec_to / c - > sector_size ] ;
jffs2_block_refile ( c , jeb , REFILE_ANYWAY ) ;
spin_unlock ( & c - > erase_completion_lock ) ;
2005-11-07 14:16:07 +03:00
2006-05-23 13:49:14 +04:00
outerr :
* retlen = 0 ;
2005-04-17 02:20:36 +04:00
up_write ( & c - > wbuf_sem ) ;
return ret ;
}
/*
* This is the entry for flash write .
* Check , if we work on NAND FLASH , if so build an kvec and write it via vritev
*/
2006-05-27 00:19:05 +04:00
int jffs2_flash_write ( struct jffs2_sb_info * c , loff_t ofs , size_t len ,
size_t * retlen , const u_char * buf )
2005-04-17 02:20:36 +04:00
{
struct kvec vecs [ 1 ] ;
2005-02-09 12:09:05 +03:00
if ( ! jffs2_is_writebuffered ( c ) )
2005-09-07 12:35:26 +04:00
return jffs2_flash_direct_write ( c , ofs , len , retlen , buf ) ;
2005-04-17 02:20:36 +04:00
vecs [ 0 ] . iov_base = ( unsigned char * ) buf ;
vecs [ 0 ] . iov_len = len ;
return jffs2_flash_writev ( c , vecs , 1 , ofs , retlen , 0 ) ;
}
/*
Handle readback from writebuffer and ECC failure return
*/
int jffs2_flash_read ( struct jffs2_sb_info * c , loff_t ofs , size_t len , size_t * retlen , u_char * buf )
{
loff_t orbf = 0 , owbf = 0 , lwbf = 0 ;
int ret ;
2005-02-09 12:09:05 +03:00
if ( ! jffs2_is_writebuffered ( c ) )
2005-04-17 02:20:36 +04:00
return c - > mtd - > read ( c - > mtd , ofs , len , retlen , buf ) ;
2005-02-09 12:09:05 +03:00
/* Read flash */
2005-04-05 16:51:58 +04:00
down_read ( & c - > wbuf_sem ) ;
2006-05-23 19:21:03 +04:00
ret = c - > mtd - > read ( c - > mtd , ofs , len , retlen , buf ) ;
2005-02-09 12:09:05 +03:00
if ( ( ret = = - EBADMSG ) & & ( * retlen = = len ) ) {
printk ( KERN_WARNING " mtd->read(0x%zx bytes from 0x%llx) returned ECC error \n " ,
len , ofs ) ;
2005-11-07 14:16:07 +03:00
/*
* We have the raw data without ECC correction in the buffer , maybe
2005-02-09 12:09:05 +03:00
* we are lucky and all data or parts are correct . We check the node .
* If data are corrupted node check will sort it out .
* We keep this block , it will fail on write or erase and the we
* mark it bad . Or should we do that now ? But we should give him a chance .
2005-11-07 14:16:07 +03:00
* Maybe we had a system crash or power loss before the ecc write or
2005-02-09 12:09:05 +03:00
* a erase was completed .
* So we return success . : )
*/
ret = 0 ;
2005-11-07 14:16:07 +03:00
}
2005-02-09 12:09:05 +03:00
2005-04-17 02:20:36 +04:00
/* if no writebuffer available or write buffer empty, return */
if ( ! c - > wbuf_pagesize | | ! c - > wbuf_len )
2005-04-05 16:51:58 +04:00
goto exit ;
2005-04-17 02:20:36 +04:00
/* if we read in a different block, return */
2005-02-09 12:09:05 +03:00
if ( SECTOR_ADDR ( ofs ) ! = SECTOR_ADDR ( c - > wbuf_ofs ) )
2005-04-05 16:51:58 +04:00
goto exit ;
2005-04-17 02:20:36 +04:00
if ( ofs > = c - > wbuf_ofs ) {
owbf = ( ofs - c - > wbuf_ofs ) ; /* offset in write buffer */
if ( owbf > c - > wbuf_len ) /* is read beyond write buffer ? */
goto exit ;
lwbf = c - > wbuf_len - owbf ; /* number of bytes to copy */
2005-11-07 14:16:07 +03:00
if ( lwbf > len )
2005-04-17 02:20:36 +04:00
lwbf = len ;
2005-11-07 14:16:07 +03:00
} else {
2005-04-17 02:20:36 +04:00
orbf = ( c - > wbuf_ofs - ofs ) ; /* offset in read buffer */
if ( orbf > len ) /* is write beyond write buffer ? */
goto exit ;
lwbf = len - orbf ; /* number of bytes to copy */
2005-11-07 14:16:07 +03:00
if ( lwbf > c - > wbuf_len )
2005-04-17 02:20:36 +04:00
lwbf = c - > wbuf_len ;
2005-11-07 14:16:07 +03:00
}
2005-04-17 02:20:36 +04:00
if ( lwbf > 0 )
memcpy ( buf + orbf , c - > wbuf + owbf , lwbf ) ;
exit :
up_read ( & c - > wbuf_sem ) ;
return ret ;
}
/*
* Check , if the out of band area is empty
*/
int jffs2_check_oob_empty ( struct jffs2_sb_info * c , struct jffs2_eraseblock * jeb , int mode )
{
unsigned char * buf ;
int ret = 0 ;
int i , len , page ;
size_t retlen ;
int oob_size ;
/* allocate a buffer for all oob data in this sector */
oob_size = c - > mtd - > oobsize ;
len = 4 * oob_size ;
buf = kmalloc ( len , GFP_KERNEL ) ;
if ( ! buf ) {
printk ( KERN_NOTICE " jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed \n " ) ;
return - ENOMEM ;
}
2005-11-07 14:16:07 +03:00
/*
2005-04-17 02:20:36 +04:00
* if mode = 0 , we scan for a total empty oob area , else we have
* to take care of the cleanmarker in the first page of the block
*/
ret = jffs2_flash_read_oob ( c , jeb - > offset , len , & retlen , buf ) ;
if ( ret ) {
D1 ( printk ( KERN_WARNING " jffs2_check_oob_empty(): Read OOB failed %d for block at %08x \n " , ret , jeb - > offset ) ) ;
goto out ;
}
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
if ( retlen < len ) {
D1 ( printk ( KERN_WARNING " jffs2_check_oob_empty(): Read OOB return short read "
" (%zd bytes not %d) for block at %08x \n " , retlen , len , jeb - > offset ) ) ;
ret = - EIO ;
goto out ;
}
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
/* Special check for first page */
for ( i = 0 ; i < oob_size ; i + + ) {
/* Yeah, we know about the cleanmarker. */
2005-11-07 14:16:07 +03:00
if ( mode & & i > = c - > fsdata_pos & &
2005-04-17 02:20:36 +04:00
i < c - > fsdata_pos + c - > fsdata_len )
continue ;
if ( buf [ i ] ! = 0xFF ) {
D2 ( printk ( KERN_DEBUG " Found %02x at %x in OOB for %08x \n " ,
2005-07-17 10:56:26 +04:00
buf [ i ] , i , jeb - > offset ) ) ;
2005-11-07 14:16:07 +03:00
ret = 1 ;
2005-04-17 02:20:36 +04:00
goto out ;
}
}
2005-11-07 14:16:07 +03:00
/* we know, we are aligned :) */
2005-04-17 02:20:36 +04:00
for ( page = oob_size ; page < len ; page + = sizeof ( long ) ) {
unsigned long dat = * ( unsigned long * ) ( & buf [ page ] ) ;
if ( dat ! = - 1 ) {
2005-11-07 14:16:07 +03:00
ret = 1 ;
2005-04-17 02:20:36 +04:00
goto out ;
}
}
out :
2005-11-07 14:16:07 +03:00
kfree ( buf ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
/*
* Scan for a valid cleanmarker and for bad blocks
* For virtual blocks ( concatenated physical blocks ) check the cleanmarker
* only in the first page of the first physical block , but scan for bad blocks in all
* physical blocks
*/
int jffs2_check_nand_cleanmarker ( struct jffs2_sb_info * c , struct jffs2_eraseblock * jeb )
{
struct jffs2_unknown_node n ;
unsigned char buf [ 2 * NAND_MAX_OOBSIZE ] ;
unsigned char * p ;
int ret , i , cnt , retval = 0 ;
size_t retlen , offset ;
int oob_size ;
offset = jeb - > offset ;
oob_size = c - > mtd - > oobsize ;
/* Loop through the physical blocks */
for ( cnt = 0 ; cnt < ( c - > sector_size / c - > mtd - > erasesize ) ; cnt + + ) {
/* Check first if the block is bad. */
if ( c - > mtd - > block_isbad ( c - > mtd , offset ) ) {
D1 ( printk ( KERN_WARNING " jffs2_check_nand_cleanmarker(): Bad block at %08x \n " , jeb - > offset ) ) ;
return 2 ;
}
/*
* We read oob data from page 0 and 1 of the block .
* page 0 contains cleanmarker and badblock info
* page 1 contains failure count of this block
*/
ret = c - > mtd - > read_oob ( c - > mtd , offset , oob_size < < 1 , & retlen , buf ) ;
if ( ret ) {
D1 ( printk ( KERN_WARNING " jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x \n " , ret , jeb - > offset ) ) ;
return ret ;
}
if ( retlen < ( oob_size < < 1 ) ) {
D1 ( printk ( KERN_WARNING " jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x \n " , retlen , oob_size < < 1 , jeb - > offset ) ) ;
return - EIO ;
}
/* Check cleanmarker only on the first physical block */
if ( ! cnt ) {
n . magic = cpu_to_je16 ( JFFS2_MAGIC_BITMASK ) ;
n . nodetype = cpu_to_je16 ( JFFS2_NODETYPE_CLEANMARKER ) ;
n . totlen = cpu_to_je32 ( 8 ) ;
p = ( unsigned char * ) & n ;
for ( i = 0 ; i < c - > fsdata_len ; i + + ) {
if ( buf [ c - > fsdata_pos + i ] ! = p [ i ] ) {
retval = 1 ;
}
}
D1 ( if ( retval = = 1 ) {
printk ( KERN_WARNING " jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x \n " , jeb - > offset ) ;
2006-05-27 00:19:05 +04:00
printk ( KERN_WARNING " OOB at %08zx was " , offset ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < oob_size ; i + + ) {
printk ( " %02x " , buf [ i ] ) ;
}
printk ( " \n " ) ;
} )
}
offset + = c - > mtd - > erasesize ;
}
return retval ;
}
int jffs2_write_nand_cleanmarker ( struct jffs2_sb_info * c , struct jffs2_eraseblock * jeb )
{
struct jffs2_unknown_node n ;
int ret ;
size_t retlen ;
n . magic = cpu_to_je16 ( JFFS2_MAGIC_BITMASK ) ;
n . nodetype = cpu_to_je16 ( JFFS2_NODETYPE_CLEANMARKER ) ;
n . totlen = cpu_to_je32 ( 8 ) ;
ret = jffs2_flash_write_oob ( c , jeb - > offset + c - > fsdata_pos , c - > fsdata_len , & retlen , ( unsigned char * ) & n ) ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
if ( ret ) {
D1 ( printk ( KERN_WARNING " jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d \n " , jeb - > offset , ret ) ) ;
return ret ;
}
if ( retlen ! = c - > fsdata_len ) {
D1 ( printk ( KERN_WARNING " jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d \n " , jeb - > offset , retlen , c - > fsdata_len ) ) ;
return ret ;
}
return 0 ;
}
2005-11-07 14:16:07 +03:00
/*
2005-04-17 02:20:36 +04:00
* On NAND we try to mark this block bad . If the block was erased more
* than MAX_ERASE_FAILURES we mark it finaly bad .
* Don ' t care about failures . This block remains on the erase - pending
* or badblock list as long as nobody manipulates the flash with
* a bootloader or something like that .
*/
int jffs2_write_nand_badblock ( struct jffs2_sb_info * c , struct jffs2_eraseblock * jeb , uint32_t bad_offset )
{
int ret ;
/* if the count is < max, we try to write the counter to the 2nd page oob area */
if ( + + jeb - > bad_count < MAX_ERASE_FAILURES )
return 0 ;
if ( ! c - > mtd - > block_markbad )
return 1 ; // What else can we do?
D1 ( printk ( KERN_WARNING " jffs2_write_nand_badblock(): Marking bad block at %08x \n " , bad_offset ) ) ;
ret = c - > mtd - > block_markbad ( c - > mtd , bad_offset ) ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
if ( ret ) {
D1 ( printk ( KERN_WARNING " jffs2_write_nand_badblock(): Write failed for block at %08x: error %d \n " , jeb - > offset , ret ) ) ;
return ret ;
}
return 1 ;
}
# define NAND_JFFS2_OOB16_FSDALEN 8
static struct nand_oobinfo jffs2_oobinfo_docecc = {
. useecc = MTD_NANDECC_PLACE ,
. eccbytes = 6 ,
. eccpos = { 0 , 1 , 2 , 3 , 4 , 5 }
} ;
static int jffs2_nand_set_oobinfo ( struct jffs2_sb_info * c )
{
2006-05-27 22:36:12 +04:00
struct nand_oobinfo * oinfo = c - > mtd - > oobinfo ;
2005-04-17 02:20:36 +04:00
/* Do this only, if we have an oob buffer */
if ( ! c - > mtd - > oobsize )
return 0 ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
/* Cleanmarker is out-of-band, so inline size zero */
c - > cleanmarker_size = 0 ;
/* Should we use autoplacement ? */
if ( oinfo & & oinfo - > useecc = = MTD_NANDECC_AUTOPLACE ) {
D1 ( printk ( KERN_DEBUG " JFFS2 using autoplace on NAND \n " ) ) ;
/* Get the position of the free bytes */
if ( ! oinfo - > oobfree [ 0 ] [ 1 ] ) {
printk ( KERN_WARNING " jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob \n " ) ;
return - ENOSPC ;
}
c - > fsdata_pos = oinfo - > oobfree [ 0 ] [ 0 ] ;
c - > fsdata_len = oinfo - > oobfree [ 0 ] [ 1 ] ;
if ( c - > fsdata_len > 8 )
c - > fsdata_len = 8 ;
} else {
/* This is just a legacy fallback and should go away soon */
switch ( c - > mtd - > ecctype ) {
case MTD_ECC_RS_DiskOnChip :
printk ( KERN_WARNING " JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it! \n " ) ;
c - > oobinfo = & jffs2_oobinfo_docecc ;
c - > fsdata_pos = 6 ;
c - > fsdata_len = NAND_JFFS2_OOB16_FSDALEN ;
c - > badblock_pos = 15 ;
break ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
default :
D1 ( printk ( KERN_DEBUG " JFFS2 on NAND. No autoplacment info found \n " ) ) ;
return - EINVAL ;
}
}
return 0 ;
}
int jffs2_nand_flash_setup ( struct jffs2_sb_info * c )
{
int res ;
/* Initialise write buffer */
init_rwsem ( & c - > wbuf_sem ) ;
2006-05-23 01:18:05 +04:00
c - > wbuf_pagesize = c - > mtd - > writesize ;
2005-04-17 02:20:36 +04:00
c - > wbuf_ofs = 0xFFFFFFFF ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
c - > wbuf = kmalloc ( c - > wbuf_pagesize , GFP_KERNEL ) ;
if ( ! c - > wbuf )
return - ENOMEM ;
res = jffs2_nand_set_oobinfo ( c ) ;
# ifdef BREAKME
if ( ! brokenbuf )
brokenbuf = kmalloc ( c - > wbuf_pagesize , GFP_KERNEL ) ;
if ( ! brokenbuf ) {
kfree ( c - > wbuf ) ;
return - ENOMEM ;
}
memset ( brokenbuf , 0xdb , c - > wbuf_pagesize ) ;
# endif
return res ;
}
void jffs2_nand_flash_cleanup ( struct jffs2_sb_info * c )
{
kfree ( c - > wbuf ) ;
}
2005-02-09 12:17:45 +03:00
int jffs2_dataflash_setup ( struct jffs2_sb_info * c ) {
c - > cleanmarker_size = 0 ; /* No cleanmarkers needed */
2005-11-07 14:16:07 +03:00
2005-02-09 12:17:45 +03:00
/* Initialize write buffer */
init_rwsem ( & c - > wbuf_sem ) ;
2005-11-07 14:16:07 +03:00
2005-09-30 17:59:17 +04:00
c - > wbuf_pagesize = c - > mtd - > erasesize ;
2005-11-07 14:16:07 +03:00
2005-09-30 17:59:17 +04:00
/* Find a suitable c->sector_size
* - Not too much sectors
* - Sectors have to be at least 4 K + some bytes
* - All known dataflashes have erase sizes of 528 or 1056
* - we take at least 8 eraseblocks and want to have at least 8 K size
* - The concatenation should be a power of 2
*/
c - > sector_size = 8 * c - > mtd - > erasesize ;
2005-11-07 14:16:07 +03:00
2005-09-30 17:59:17 +04:00
while ( c - > sector_size < 8192 ) {
c - > sector_size * = 2 ;
}
2005-11-07 14:16:07 +03:00
2005-09-30 17:59:17 +04:00
/* It may be necessary to adjust the flash size */
c - > flash_size = c - > mtd - > size ;
2005-02-09 12:17:45 +03:00
2005-09-30 17:59:17 +04:00
if ( ( c - > flash_size % c - > sector_size ) ! = 0 ) {
c - > flash_size = ( c - > flash_size / c - > sector_size ) * c - > sector_size ;
printk ( KERN_WARNING " JFFS2 flash size adjusted to %dKiB \n " , c - > flash_size ) ;
} ;
2005-11-07 14:16:07 +03:00
2005-09-30 17:59:17 +04:00
c - > wbuf_ofs = 0xFFFFFFFF ;
2005-02-09 12:17:45 +03:00
c - > wbuf = kmalloc ( c - > wbuf_pagesize , GFP_KERNEL ) ;
if ( ! c - > wbuf )
return - ENOMEM ;
2005-09-30 17:59:17 +04:00
printk ( KERN_INFO " JFFS2 write-buffering enabled buffer (%d) erasesize (%d) \n " , c - > wbuf_pagesize , c - > sector_size ) ;
2005-02-09 12:17:45 +03:00
return 0 ;
}
void jffs2_dataflash_cleanup ( struct jffs2_sb_info * c ) {
kfree ( c - > wbuf ) ;
}
2005-08-06 08:51:33 +04:00
int jffs2_nor_wbuf_flash_setup ( struct jffs2_sb_info * c ) {
2006-05-23 01:18:12 +04:00
/* Cleanmarker currently occupies whole programming regions,
* either one or 2 for 8 Byte STMicro flashes . */
c - > cleanmarker_size = max ( 16u , c - > mtd - > writesize ) ;
2005-08-06 08:51:33 +04:00
/* Initialize write buffer */
init_rwsem ( & c - > wbuf_sem ) ;
2006-05-23 01:18:05 +04:00
c - > wbuf_pagesize = c - > mtd - > writesize ;
2005-08-06 08:51:33 +04:00
c - > wbuf_ofs = 0xFFFFFFFF ;
c - > wbuf = kmalloc ( c - > wbuf_pagesize , GFP_KERNEL ) ;
if ( ! c - > wbuf )
return - ENOMEM ;
return 0 ;
}
void jffs2_nor_wbuf_flash_cleanup ( struct jffs2_sb_info * c ) {
kfree ( c - > wbuf ) ;
}