2005-04-17 02:20:36 +04:00
/*
* JFFS2 - - Journalling Flash File System , Version 2.
*
2007-04-25 17:16:47 +04:00
* Copyright © 2001 - 2007 Red Hat , Inc .
2010-08-08 17:15:22 +04:00
* Copyright © 2004 - 2010 David Woodhouse < dwmw2 @ infradead . org >
2005-04-17 02:20:36 +04:00
*
* Created by David Woodhouse < dwmw2 @ infradead . org >
*
* For licensing information , see the file ' LICENCE ' in this directory .
*
*/
2012-02-16 03:56:45 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/mtd/mtd.h>
# include <linux/compiler.h>
# include <linux/crc32.h>
# include <linux/sched.h>
# include <linux/pagemap.h>
# include "nodelist.h"
struct erase_priv_struct {
struct jffs2_eraseblock * jeb ;
struct jffs2_sb_info * c ;
} ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
# ifndef __ECOS
static void jffs2_erase_callback ( struct erase_info * ) ;
# endif
static void jffs2_erase_failed ( struct jffs2_sb_info * c , struct jffs2_eraseblock * jeb , uint32_t bad_offset ) ;
static void jffs2_erase_succeeded ( struct jffs2_sb_info * c , struct jffs2_eraseblock * jeb ) ;
static void jffs2_mark_erased_block ( struct jffs2_sb_info * c , struct jffs2_eraseblock * jeb ) ;
static void jffs2_erase_block ( struct jffs2_sb_info * c ,
struct jffs2_eraseblock * jeb )
{
int ret ;
uint32_t bad_offset ;
# ifdef __ECOS
ret = jffs2_flash_erase ( c , jeb ) ;
if ( ! ret ) {
2007-07-10 13:01:22 +04:00
jffs2_erase_succeeded ( c , jeb ) ;
return ;
2005-04-17 02:20:36 +04:00
}
bad_offset = jeb - > offset ;
# else /* Linux */
struct erase_info * instr ;
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " %s(): erase block %#08x (range %#08x-%#08x) \n " ,
__func__ ,
jeb - > offset , jeb - > offset , jeb - > offset + c - > sector_size ) ;
2005-04-17 02:20:36 +04:00
instr = kmalloc ( sizeof ( struct erase_info ) + sizeof ( struct erase_priv_struct ) , GFP_KERNEL ) ;
if ( ! instr ) {
2012-02-16 03:56:44 +04:00
pr_warn ( " kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later \n " ) ;
2008-04-22 18:13:40 +04:00
mutex_lock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & c - > erase_completion_lock ) ;
2006-06-26 11:24:46 +04:00
list_move ( & jeb - > list , & c - > erase_pending_list ) ;
2005-04-17 02:20:36 +04:00
c - > erasing_size - = c - > sector_size ;
c - > dirty_size + = c - > sector_size ;
jeb - > dirty_size = c - > sector_size ;
spin_unlock ( & c - > erase_completion_lock ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
return ;
}
memset ( instr , 0 , sizeof ( * instr ) ) ;
instr - > mtd = c - > mtd ;
instr - > addr = jeb - > offset ;
instr - > len = c - > sector_size ;
instr - > callback = jffs2_erase_callback ;
instr - > priv = ( unsigned long ) ( & instr [ 1 ] ) ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
( ( struct erase_priv_struct * ) instr - > priv ) - > jeb = jeb ;
( ( struct erase_priv_struct * ) instr - > priv ) - > c = c ;
2011-12-23 17:25:39 +04:00
ret = mtd_erase ( c - > mtd , instr ) ;
2005-04-17 02:20:36 +04:00
if ( ! ret )
return ;
bad_offset = instr - > fail_addr ;
kfree ( instr ) ;
# endif /* __ECOS */
if ( ret = = - ENOMEM | | ret = = - EAGAIN ) {
/* Erase failed immediately. Refile it on the list */
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " Erase at 0x%08x failed: %d. Refiling on erase_pending_list \n " ,
jeb - > offset , ret ) ;
2008-04-22 18:13:40 +04:00
mutex_lock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & c - > erase_completion_lock ) ;
2006-06-26 11:24:46 +04:00
list_move ( & jeb - > list , & c - > erase_pending_list ) ;
2005-04-17 02:20:36 +04:00
c - > erasing_size - = c - > sector_size ;
c - > dirty_size + = c - > sector_size ;
jeb - > dirty_size = c - > sector_size ;
spin_unlock ( & c - > erase_completion_lock ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2005-11-07 14:16:07 +03:00
if ( ret = = - EROFS )
2012-02-16 03:56:44 +04:00
pr_warn ( " Erase at 0x%08x failed immediately: -EROFS. Is the sector locked? \n " ,
jeb - > offset ) ;
2005-04-17 02:20:36 +04:00
else
2012-02-16 03:56:44 +04:00
pr_warn ( " Erase at 0x%08x failed immediately: errno %d \n " ,
jeb - > offset , ret ) ;
2005-04-17 02:20:36 +04:00
jffs2_erase_failed ( c , jeb , bad_offset ) ;
}
2010-05-19 19:32:52 +04:00
int jffs2_erase_pending_blocks ( struct jffs2_sb_info * c , int count )
2005-04-17 02:20:36 +04:00
{
struct jffs2_eraseblock * jeb ;
2010-05-19 19:32:52 +04:00
int work_done = 0 ;
2005-04-17 02:20:36 +04:00
2008-04-22 18:13:40 +04:00
mutex_lock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & c - > erase_completion_lock ) ;
while ( ! list_empty ( & c - > erase_complete_list ) | |
! list_empty ( & c - > erase_pending_list ) ) {
if ( ! list_empty ( & c - > erase_complete_list ) ) {
jeb = list_entry ( c - > erase_complete_list . next , struct jffs2_eraseblock , list ) ;
2008-04-23 17:15:24 +04:00
list_move ( & jeb - > list , & c - > erase_checking_list ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & c - > erase_completion_lock ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
jffs2_mark_erased_block ( c , jeb ) ;
2010-05-19 19:32:52 +04:00
work_done + + ;
2005-04-17 02:20:36 +04:00
if ( ! - - count ) {
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " Count reached. jffs2_erase_pending_blocks leaving \n " ) ;
2005-04-17 02:20:36 +04:00
goto done ;
}
} else if ( ! list_empty ( & c - > erase_pending_list ) ) {
jeb = list_entry ( c - > erase_pending_list . next , struct jffs2_eraseblock , list ) ;
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " Starting erase of pending block 0x%08x \n " ,
jeb - > offset ) ;
2005-04-17 02:20:36 +04:00
list_del ( & jeb - > list ) ;
c - > erasing_size + = c - > sector_size ;
c - > wasted_size - = jeb - > wasted_size ;
c - > free_size - = jeb - > free_size ;
c - > used_size - = jeb - > used_size ;
c - > dirty_size - = jeb - > dirty_size ;
jeb - > wasted_size = jeb - > used_size = jeb - > dirty_size = jeb - > free_size = 0 ;
2006-05-25 04:38:27 +04:00
jffs2_free_jeb_node_refs ( c , jeb ) ;
2005-04-17 02:20:36 +04:00
list_add ( & jeb - > list , & c - > erasing_list ) ;
spin_unlock ( & c - > erase_completion_lock ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
jffs2_erase_block ( c , jeb ) ;
} else {
BUG ( ) ;
}
/* Be nice */
2010-09-01 20:03:41 +04:00
cond_resched ( ) ;
2008-04-22 18:13:40 +04:00
mutex_lock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & c - > erase_completion_lock ) ;
}
spin_unlock ( & c - > erase_completion_lock ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
done :
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " jffs2_erase_pending_blocks completed \n " ) ;
2010-05-19 19:32:52 +04:00
return work_done ;
2005-04-17 02:20:36 +04:00
}
static void jffs2_erase_succeeded ( struct jffs2_sb_info * c , struct jffs2_eraseblock * jeb )
{
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " Erase completed successfully at 0x%08x \n " , jeb - > offset ) ;
2008-04-22 18:13:40 +04:00
mutex_lock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & c - > erase_completion_lock ) ;
2006-06-26 11:24:46 +04:00
list_move_tail ( & jeb - > list , & c - > erase_complete_list ) ;
2010-05-19 20:05:14 +04:00
/* Wake the GC thread to mark them clean */
jffs2_garbage_collect_trigger ( c ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & c - > erase_completion_lock ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & c - > erase_free_sem ) ;
2010-05-19 19:37:13 +04:00
wake_up ( & c - > erase_wait ) ;
2005-04-17 02:20:36 +04:00
}
static void jffs2_erase_failed ( struct jffs2_sb_info * c , struct jffs2_eraseblock * jeb , uint32_t bad_offset )
{
/* For NAND, if the failure did not occur at the device level for a
specific physical page , don ' t bother updating the bad block table . */
2008-12-10 16:37:21 +03:00
if ( jffs2_cleanmarker_oob ( c ) & & ( bad_offset ! = ( uint32_t ) MTD_FAIL_ADDR_UNKNOWN ) ) {
2005-04-17 02:20:36 +04:00
/* We had a device-level failure to erase. Let's see if we've
failed too many times . */
if ( ! jffs2_write_nand_badblock ( c , jeb , bad_offset ) ) {
/* We'd like to give this block another try. */
2008-04-22 18:13:40 +04:00
mutex_lock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & c - > erase_completion_lock ) ;
2006-06-26 11:24:46 +04:00
list_move ( & jeb - > list , & c - > erase_pending_list ) ;
2005-04-17 02:20:36 +04:00
c - > erasing_size - = c - > sector_size ;
c - > dirty_size + = c - > sector_size ;
jeb - > dirty_size = c - > sector_size ;
spin_unlock ( & c - > erase_completion_lock ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
return ;
}
}
2008-04-22 18:13:40 +04:00
mutex_lock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & c - > erase_completion_lock ) ;
c - > erasing_size - = c - > sector_size ;
c - > bad_size + = c - > sector_size ;
2006-06-26 11:24:46 +04:00
list_move ( & jeb - > list , & c - > bad_list ) ;
2005-04-17 02:20:36 +04:00
c - > nr_erasing_blocks - - ;
spin_unlock ( & c - > erase_completion_lock ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
wake_up ( & c - > erase_wait ) ;
2005-11-07 14:16:07 +03:00
}
2005-04-17 02:20:36 +04:00
# ifndef __ECOS
static void jffs2_erase_callback ( struct erase_info * instr )
{
struct erase_priv_struct * priv = ( void * ) instr - > priv ;
if ( instr - > state ! = MTD_ERASE_DONE ) {
2012-02-16 03:56:44 +04:00
pr_warn ( " Erase at 0x%08llx finished, but state != MTD_ERASE_DONE. State is 0x%x instead. \n " ,
2008-12-10 16:37:21 +03:00
( unsigned long long ) instr - > addr , instr - > state ) ;
2005-04-17 02:20:36 +04:00
jffs2_erase_failed ( priv - > c , priv - > jeb , instr - > fail_addr ) ;
} else {
jffs2_erase_succeeded ( priv - > c , priv - > jeb ) ;
2005-11-07 14:16:07 +03:00
}
2005-04-17 02:20:36 +04:00
kfree ( instr ) ;
}
# endif /* !__ECOS */
/* Hmmm. Maybe we should accept the extra space it takes and make
this a standard doubly - linked list ? */
static inline void jffs2_remove_node_refs_from_ino_list ( struct jffs2_sb_info * c ,
struct jffs2_raw_node_ref * ref , struct jffs2_eraseblock * jeb )
{
struct jffs2_inode_cache * ic = NULL ;
struct jffs2_raw_node_ref * * prev ;
prev = & ref - > next_in_ino ;
/* Walk the inode's list once, removing any nodes from this eraseblock */
while ( 1 ) {
if ( ! ( * prev ) - > next_in_ino ) {
2005-11-07 14:16:07 +03:00
/* We're looking at the jffs2_inode_cache, which is
2005-04-17 02:20:36 +04:00
at the end of the linked list . Stash it and continue
from the beginning of the list */
ic = ( struct jffs2_inode_cache * ) ( * prev ) ;
prev = & ic - > nodes ;
continue ;
2005-11-07 14:16:07 +03:00
}
2005-04-17 02:20:36 +04:00
2005-02-09 12:09:05 +03:00
if ( SECTOR_ADDR ( ( * prev ) - > flash_offset ) = = jeb - > offset ) {
2005-04-17 02:20:36 +04:00
/* It's in the block we're erasing */
struct jffs2_raw_node_ref * this ;
this = * prev ;
* prev = this - > next_in_ino ;
this - > next_in_ino = NULL ;
if ( this = = ref )
break ;
continue ;
}
/* Not to be deleted. Skip */
prev = & ( ( * prev ) - > next_in_ino ) ;
}
/* PARANOIA */
if ( ! ic ) {
2006-06-11 05:35:15 +04:00
JFFS2_WARNING ( " inode_cache/xattr_datum/xattr_ref "
" not found in remove_node_refs()!! \n " ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " Removed nodes in range 0x%08x-0x%08x from ino #%u \n " ,
jeb - > offset , jeb - > offset + c - > sector_size , ic - > ino ) ;
2005-04-17 02:20:36 +04:00
D2 ( {
int i = 0 ;
struct jffs2_raw_node_ref * this ;
2009-07-07 00:05:40 +04:00
printk ( KERN_DEBUG " After remove_node_refs_from_ino_list: \n " ) ;
2005-04-17 02:20:36 +04:00
this = ic - > nodes ;
2005-11-07 14:16:07 +03:00
2009-07-07 00:05:40 +04:00
printk ( KERN_DEBUG ) ;
2005-04-17 02:20:36 +04:00
while ( this ) {
2012-02-16 03:56:44 +04:00
pr_cont ( " 0x%08x(%d)-> " ,
2009-07-07 00:05:40 +04:00
ref_offset ( this ) , ref_flags ( this ) ) ;
2005-04-17 02:20:36 +04:00
if ( + + i = = 5 ) {
2009-07-07 00:05:40 +04:00
printk ( KERN_DEBUG ) ;
2005-04-17 02:20:36 +04:00
i = 0 ;
}
this = this - > next_in_ino ;
}
2012-02-16 03:56:44 +04:00
pr_cont ( " \n " ) ;
2005-04-17 02:20:36 +04:00
} ) ;
2006-06-11 05:35:15 +04:00
switch ( ic - > class ) {
# ifdef CONFIG_JFFS2_FS_XATTR
case RAWNODE_CLASS_XATTR_DATUM :
jffs2_release_xattr_datum ( c , ( struct jffs2_xattr_datum * ) ic ) ;
break ;
case RAWNODE_CLASS_XATTR_REF :
jffs2_release_xattr_ref ( c , ( struct jffs2_xattr_ref * ) ic ) ;
break ;
# endif
default :
2008-05-01 21:47:17 +04:00
if ( ic - > nodes = = ( void * ) ic & & ic - > pino_nlink = = 0 )
2006-06-11 05:35:15 +04:00
jffs2_del_ino_cache ( c , ic ) ;
}
2005-04-17 02:20:36 +04:00
}
2006-05-25 04:38:27 +04:00
void jffs2_free_jeb_node_refs ( struct jffs2_sb_info * c , struct jffs2_eraseblock * jeb )
2005-04-17 02:20:36 +04:00
{
2006-05-27 00:19:05 +04:00
struct jffs2_raw_node_ref * block , * ref ;
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " Freeing all node refs for eraseblock offset 0x%08x \n " ,
jeb - > offset ) ;
2005-11-07 14:16:07 +03:00
2006-05-27 00:19:05 +04:00
block = ref = jeb - > first_node ;
while ( ref ) {
if ( ref - > flash_offset = = REF_LINK_NODE ) {
ref = ref - > next_in_ino ;
jffs2_free_refblock ( block ) ;
block = ref ;
continue ;
}
if ( ref - > flash_offset ! = REF_EMPTY_NODE & & ref - > next_in_ino )
2005-04-17 02:20:36 +04:00
jffs2_remove_node_refs_from_ino_list ( c , ref , jeb ) ;
/* else it was a non-inode node or already removed, so don't bother */
2006-05-27 00:19:05 +04:00
ref + + ;
2005-04-17 02:20:36 +04:00
}
2006-05-27 00:19:05 +04:00
jeb - > first_node = jeb - > last_node = NULL ;
2005-04-17 02:20:36 +04:00
}
2005-07-15 10:14:44 +04:00
static int jffs2_block_check_erase ( struct jffs2_sb_info * c , struct jffs2_eraseblock * jeb , uint32_t * bad_offset )
2005-04-17 02:20:36 +04:00
{
2005-07-15 10:14:44 +04:00
void * ebuf ;
uint32_t ofs ;
2005-04-17 02:20:36 +04:00
size_t retlen ;
2012-01-10 17:32:29 +04:00
int ret ;
2011-12-28 17:55:42 +04:00
unsigned long * wordebuf ;
2005-11-07 14:16:07 +03:00
2011-12-28 17:55:42 +04:00
ret = mtd_point ( c - > mtd , jeb - > offset , c - > sector_size , & retlen ,
& ebuf , NULL ) ;
if ( ret ! = - EOPNOTSUPP ) {
2007-06-01 17:14:09 +04:00
if ( ret ) {
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " MTD point failed %d \n " , ret ) ;
2007-06-01 17:14:09 +04:00
goto do_flash_read ;
}
if ( retlen < c - > sector_size ) {
/* Don't muck about if it won't let us point to the whole erase sector */
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " MTD point returned len too short: 0x%zx \n " ,
retlen ) ;
2011-12-23 19:05:52 +04:00
mtd_unpoint ( c - > mtd , jeb - > offset , retlen ) ;
2007-06-01 17:14:09 +04:00
goto do_flash_read ;
}
wordebuf = ebuf - sizeof ( * wordebuf ) ;
retlen / = sizeof ( * wordebuf ) ;
do {
if ( * + + wordebuf ! = ~ 0 )
break ;
} while ( - - retlen ) ;
2011-12-23 19:05:52 +04:00
mtd_unpoint ( c - > mtd , jeb - > offset , c - > sector_size ) ;
2008-03-12 22:29:23 +03:00
if ( retlen ) {
2012-02-16 03:56:44 +04:00
pr_warn ( " Newly-erased block contained word 0x%lx at offset 0x%08tx \n " ,
* wordebuf ,
jeb - > offset +
c - > sector_size - retlen * sizeof ( * wordebuf ) ) ;
2008-03-12 22:29:23 +03:00
return - EIO ;
}
2007-06-01 17:14:09 +04:00
return 0 ;
}
do_flash_read :
2005-04-17 02:20:36 +04:00
ebuf = kmalloc ( PAGE_SIZE , GFP_KERNEL ) ;
if ( ! ebuf ) {
2012-02-16 03:56:44 +04:00
pr_warn ( " Failed to allocate page buffer for verifying erase at 0x%08x. Refiling \n " ,
jeb - > offset ) ;
2005-07-15 10:14:44 +04:00
return - EAGAIN ;
}
2005-04-17 02:20:36 +04:00
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " Verifying erase at 0x%08x \n " , jeb - > offset ) ;
2005-04-17 02:20:36 +04:00
2005-07-15 10:14:44 +04:00
for ( ofs = jeb - > offset ; ofs < jeb - > offset + c - > sector_size ; ) {
uint32_t readlen = min ( ( uint32_t ) PAGE_SIZE , jeb - > offset + c - > sector_size - ofs ) ;
int i ;
2005-04-17 02:20:36 +04:00
2005-07-15 10:14:44 +04:00
* bad_offset = ofs ;
2005-04-05 16:51:58 +04:00
2011-12-23 19:30:16 +04:00
ret = mtd_read ( c - > mtd , ofs , readlen , & retlen , ebuf ) ;
2005-07-15 10:14:44 +04:00
if ( ret ) {
2012-02-16 03:56:44 +04:00
pr_warn ( " Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list \n " ,
ofs , ret ) ;
2008-03-12 22:29:23 +03:00
ret = - EIO ;
2005-07-15 10:14:44 +04:00
goto fail ;
}
if ( retlen ! = readlen ) {
2012-02-16 03:56:44 +04:00
pr_warn ( " Short read from newly-erased block at 0x%08x. Wanted %d, got %zd \n " ,
ofs , readlen , retlen ) ;
2008-03-12 22:29:23 +03:00
ret = - EIO ;
2005-07-15 10:14:44 +04:00
goto fail ;
}
for ( i = 0 ; i < readlen ; i + = sizeof ( unsigned long ) ) {
/* It's OK. We know it's properly aligned */
unsigned long * datum = ebuf + i ;
if ( * datum + 1 ) {
* bad_offset + = i ;
2012-02-16 03:56:44 +04:00
pr_warn ( " Newly-erased block contained word 0x%lx at offset 0x%08x \n " ,
* datum , * bad_offset ) ;
2008-03-12 22:29:23 +03:00
ret = - EIO ;
2005-07-15 10:14:44 +04:00
goto fail ;
2005-04-17 02:20:36 +04:00
}
}
2005-07-15 10:14:44 +04:00
ofs + = readlen ;
cond_resched ( ) ;
2005-04-17 02:20:36 +04:00
}
2005-07-15 10:14:44 +04:00
ret = 0 ;
fail :
kfree ( ebuf ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
2005-07-15 10:14:44 +04:00
static void jffs2_mark_erased_block ( struct jffs2_sb_info * c , struct jffs2_eraseblock * jeb )
{
size_t retlen ;
int ret ;
2007-08-11 01:01:30 +04:00
uint32_t uninitialized_var ( bad_offset ) ;
2005-07-15 10:14:44 +04:00
switch ( jffs2_block_check_erase ( c , jeb , & bad_offset ) ) {
case - EAGAIN : goto refile ;
case - EIO : goto filebad ;
}
2005-04-17 02:20:36 +04:00
2005-11-07 14:16:07 +03:00
/* Write the erase complete marker */
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " Writing erased marker to block at 0x%08x \n " , jeb - > offset ) ;
2005-07-15 10:14:44 +04:00
bad_offset = jeb - > offset ;
2005-04-17 02:20:36 +04:00
2005-07-15 10:14:44 +04:00
/* Cleanmarker in oob area or no cleanmarker at all ? */
if ( jffs2_cleanmarker_oob ( c ) | | c - > cleanmarker_size = = 0 ) {
2005-04-17 02:20:36 +04:00
2005-07-15 10:14:44 +04:00
if ( jffs2_cleanmarker_oob ( c ) ) {
if ( jffs2_write_nand_cleanmarker ( c , jeb ) )
goto filebad ;
}
2005-04-17 02:20:36 +04:00
} else {
2005-07-15 10:14:44 +04:00
2005-04-17 02:20:36 +04:00
struct kvec vecs [ 1 ] ;
struct jffs2_unknown_node marker = {
. magic = cpu_to_je16 ( JFFS2_MAGIC_BITMASK ) ,
. nodetype = cpu_to_je16 ( JFFS2_NODETYPE_CLEANMARKER ) ,
. totlen = cpu_to_je32 ( c - > cleanmarker_size )
} ;
2006-05-25 04:50:35 +04:00
jffs2_prealloc_raw_node_refs ( c , jeb , 1 ) ;
2005-07-15 10:14:44 +04:00
2005-04-17 02:20:36 +04:00
marker . hdr_crc = cpu_to_je32 ( crc32 ( 0 , & marker , sizeof ( struct jffs2_unknown_node ) - 4 ) ) ;
vecs [ 0 ] . iov_base = ( unsigned char * ) & marker ;
vecs [ 0 ] . iov_len = sizeof ( marker ) ;
ret = jffs2_flash_direct_writev ( c , vecs , 1 , jeb - > offset , & retlen ) ;
2005-11-07 14:16:07 +03:00
2005-07-15 10:14:44 +04:00
if ( ret | | retlen ! = sizeof ( marker ) ) {
if ( ret )
2012-02-16 03:56:44 +04:00
pr_warn ( " Write clean marker to block at 0x%08x failed: %d \n " ,
2005-07-15 10:14:44 +04:00
jeb - > offset , ret ) ;
else
2012-02-16 03:56:44 +04:00
pr_warn ( " Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd \n " ,
2005-07-15 10:14:44 +04:00
jeb - > offset , sizeof ( marker ) , retlen ) ;
goto filebad ;
2005-04-17 02:20:36 +04:00
}
}
2008-04-23 02:54:38 +04:00
/* Everything else got zeroed before the erase */
jeb - > free_size = c - > sector_size ;
2005-04-17 02:20:36 +04:00
2008-04-22 18:13:40 +04:00
mutex_lock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & c - > erase_completion_lock ) ;
2008-04-23 02:54:38 +04:00
2005-04-17 02:20:36 +04:00
c - > erasing_size - = c - > sector_size ;
2008-04-23 02:54:38 +04:00
c - > free_size + = c - > sector_size ;
/* Account for cleanmarker now, if it's in-band */
if ( c - > cleanmarker_size & & ! jffs2_cleanmarker_oob ( c ) )
jffs2_link_node_ref ( c , jeb , jeb - > offset | REF_NORMAL , c - > cleanmarker_size , NULL ) ;
2005-04-17 02:20:36 +04:00
2008-04-23 17:15:24 +04:00
list_move_tail ( & jeb - > list , & c - > free_list ) ;
2005-04-17 02:20:36 +04:00
c - > nr_erasing_blocks - - ;
c - > nr_free_blocks + + ;
2008-04-23 04:17:51 +04:00
jffs2_dbg_acct_sanity_check_nolock ( c , jeb ) ;
jffs2_dbg_acct_paranoia_check_nolock ( c , jeb ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & c - > erase_completion_lock ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & c - > erase_free_sem ) ;
2005-04-17 02:20:36 +04:00
wake_up ( & c - > erase_wait ) ;
2005-07-15 10:14:44 +04:00
return ;
filebad :
jffs2_erase_failed ( c , jeb , bad_offset ) ;
return ;
2005-04-17 02:20:36 +04:00
2005-07-15 10:14:44 +04:00
refile :
/* Stick it back on the list from whence it came and come back later */
2008-04-22 18:13:40 +04:00
mutex_lock ( & c - > erase_free_sem ) ;
2005-07-15 10:14:44 +04:00
spin_lock ( & c - > erase_completion_lock ) ;
2010-05-19 20:05:14 +04:00
jffs2_garbage_collect_trigger ( c ) ;
2008-04-23 17:15:24 +04:00
list_move ( & jeb - > list , & c - > erase_complete_list ) ;
2005-07-15 10:14:44 +04:00
spin_unlock ( & c - > erase_completion_lock ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & c - > erase_free_sem ) ;
2005-07-15 10:14:44 +04:00
return ;
}