2006-12-07 07:37:15 +03:00
/*
* Interface between ext4 and JBD
*/
2008-04-30 02:13:32 +04:00
# include "ext4_jbd2.h"
2006-12-07 07:37:15 +03:00
2009-11-23 04:52:12 +03:00
# include <trace/events/ext4.h>
2013-02-08 22:00:31 +04:00
/* Just increment the non-pointer handle value */
static handle_t * ext4_get_nojournal ( void )
{
handle_t * handle = current - > journal_info ;
unsigned long ref_cnt = ( unsigned long ) handle ;
BUG_ON ( ref_cnt > = EXT4_NOJOURNAL_MAX_REF_COUNT ) ;
ref_cnt + + ;
handle = ( handle_t * ) ref_cnt ;
current - > journal_info = handle ;
return handle ;
}
/* Decrement the non-pointer handle value */
static void ext4_put_nojournal ( handle_t * handle )
{
unsigned long ref_cnt = ( unsigned long ) handle ;
BUG_ON ( ref_cnt = = 0 ) ;
ref_cnt - - ;
handle = ( handle_t * ) ref_cnt ;
current - > journal_info = handle ;
}
/*
* Wrappers for jbd2_journal_start / end .
*/
2013-06-04 20:37:50 +04:00
static int ext4_journal_check_start ( struct super_block * sb )
2013-02-08 22:00:31 +04:00
{
journal_t * journal ;
2013-04-04 06:00:52 +04:00
might_sleep ( ) ;
2013-02-08 22:00:31 +04:00
if ( sb - > s_flags & MS_RDONLY )
2013-06-04 20:37:50 +04:00
return - EROFS ;
2013-02-08 22:00:31 +04:00
WARN_ON ( sb - > s_writers . frozen = = SB_FREEZE_COMPLETE ) ;
journal = EXT4_SB ( sb ) - > s_journal ;
/*
* Special case here : if the journal has aborted behind our
* backs ( eg . EIO in the commit thread ) , then we still need to
* take the FS itself readonly cleanly .
*/
2013-06-04 20:37:50 +04:00
if ( journal & & is_journal_aborted ( journal ) ) {
2013-02-08 22:00:31 +04:00
ext4_abort ( sb , " Detected aborted journal " ) ;
2013-06-04 20:37:50 +04:00
return - EROFS ;
2013-02-08 22:00:31 +04:00
}
2013-06-04 20:37:50 +04:00
return 0 ;
}
handle_t * __ext4_journal_start_sb ( struct super_block * sb , unsigned int line ,
int type , int blocks , int rsv_blocks )
{
journal_t * journal ;
int err ;
trace_ext4_journal_start ( sb , blocks , rsv_blocks , _RET_IP_ ) ;
err = ext4_journal_check_start ( sb ) ;
if ( err < 0 )
return ERR_PTR ( err ) ;
journal = EXT4_SB ( sb ) - > s_journal ;
if ( ! journal )
return ext4_get_nojournal ( ) ;
return jbd2__journal_start ( journal , blocks , rsv_blocks , GFP_NOFS ,
type , line ) ;
2013-02-08 22:00:31 +04:00
}
int __ext4_journal_stop ( const char * where , unsigned int line , handle_t * handle )
{
struct super_block * sb ;
int err ;
int rc ;
if ( ! ext4_handle_valid ( handle ) ) {
ext4_put_nojournal ( handle ) ;
return 0 ;
}
2015-05-15 01:55:18 +03:00
2015-10-18 05:57:06 +03:00
err = handle - > h_err ;
2015-05-15 01:55:18 +03:00
if ( ! handle - > h_transaction ) {
2015-10-18 05:57:06 +03:00
rc = jbd2_journal_stop ( handle ) ;
return err ? err : rc ;
2015-05-15 01:55:18 +03:00
}
2013-02-08 22:00:31 +04:00
sb = handle - > h_transaction - > t_journal - > j_private ;
rc = jbd2_journal_stop ( handle ) ;
if ( ! err )
err = rc ;
if ( err )
__ext4_std_error ( sb , where , line , err ) ;
return err ;
}
2013-06-04 20:37:50 +04:00
handle_t * __ext4_journal_start_reserved ( handle_t * handle , unsigned int line ,
int type )
{
struct super_block * sb ;
int err ;
if ( ! ext4_handle_valid ( handle ) )
return ext4_get_nojournal ( ) ;
sb = handle - > h_journal - > j_private ;
trace_ext4_journal_start_reserved ( sb , handle - > h_buffer_credits ,
_RET_IP_ ) ;
err = ext4_journal_check_start ( sb ) ;
if ( err < 0 ) {
jbd2_journal_free_reserved ( handle ) ;
return ERR_PTR ( err ) ;
}
err = jbd2_journal_start_reserved ( handle , type , line ) ;
if ( err < 0 )
return ERR_PTR ( err ) ;
return handle ;
}
2014-05-12 18:50:23 +04:00
static void ext4_journal_abort_handle ( const char * caller , unsigned int line ,
const char * err_fn ,
struct buffer_head * bh ,
handle_t * handle , int err )
2013-02-08 22:00:31 +04:00
{
char nbuf [ 16 ] ;
const char * errstr = ext4_decode_error ( NULL , err , nbuf ) ;
BUG_ON ( ! ext4_handle_valid ( handle ) ) ;
if ( bh )
BUFFER_TRACE ( bh , " abort " ) ;
if ( ! handle - > h_err )
handle - > h_err = err ;
if ( is_handle_aborted ( handle ) )
return ;
printk ( KERN_ERR " EXT4-fs: %s:%d: aborting transaction: %s in %s \n " ,
caller , line , errstr , err_fn ) ;
jbd2_journal_abort_handle ( handle ) ;
}
2010-06-29 22:53:24 +04:00
int __ext4_journal_get_write_access ( const char * where , unsigned int line ,
handle_t * handle , struct buffer_head * bh )
2006-12-07 07:37:15 +03:00
{
2009-01-07 08:06:22 +03:00
int err = 0 ;
2013-04-04 06:00:52 +04:00
might_sleep ( ) ;
2009-01-07 08:06:22 +03:00
if ( ext4_handle_valid ( handle ) ) {
err = jbd2_journal_get_write_access ( handle , bh ) ;
if ( err )
2010-06-29 22:53:24 +04:00
ext4_journal_abort_handle ( where , line , __func__ , bh ,
2009-01-07 08:06:22 +03:00
handle , err ) ;
}
2006-12-07 07:37:15 +03:00
return err ;
}
2009-11-23 04:52:12 +03:00
/*
* The ext4 forget function must perform a revoke if we are freeing data
* which has been journaled . Metadata ( eg . indirect blocks ) must be
* revoked in all cases .
*
* " bh " may be NULL : a metadata block may have been freed from memory
* but there may still be a record of it in the journal , and that record
* still needs to be revoked .
*
* If the handle isn ' t valid we ' re not journaling , but we still need to
* call into ext4_journal_revoke ( ) to put the buffer head .
*/
2010-06-29 22:53:24 +04:00
int __ext4_forget ( const char * where , unsigned int line , handle_t * handle ,
int is_metadata , struct inode * inode ,
struct buffer_head * bh , ext4_fsblk_t blocknr )
2009-11-23 04:52:12 +03:00
{
int err ;
might_sleep ( ) ;
trace_ext4_forget ( inode , is_metadata , blocknr ) ;
BUFFER_TRACE ( bh , " enter " ) ;
jbd_debug ( 4 , " forgetting bh %p: is_metadata = %d, mode %o, "
" data mode %x \n " ,
bh , is_metadata , inode - > i_mode ,
test_opt ( inode - > i_sb , DATA_FLAGS ) ) ;
2009-11-24 19:05:59 +03:00
/* In the no journal case, we can just do a bforget and return */
if ( ! ext4_handle_valid ( handle ) ) {
bforget ( bh ) ;
return 0 ;
}
2009-11-23 04:52:12 +03:00
/* Never use the revoke function if we are doing full data
* journaling : there is no need to , and a V1 superblock won ' t
* support it . Otherwise , only skip the revoke on un - journaled
* data blocks . */
if ( test_opt ( inode - > i_sb , DATA_FLAGS ) = = EXT4_MOUNT_JOURNAL_DATA | |
( ! is_metadata & & ! ext4_should_journal_data ( inode ) ) ) {
if ( bh ) {
BUFFER_TRACE ( bh , " call jbd2_journal_forget " ) ;
2009-11-23 05:00:13 +03:00
err = jbd2_journal_forget ( handle , bh ) ;
if ( err )
2010-06-29 22:53:24 +04:00
ext4_journal_abort_handle ( where , line , __func__ ,
bh , handle , err ) ;
2009-11-23 05:00:13 +03:00
return err ;
2009-11-23 04:52:12 +03:00
}
return 0 ;
}
/*
* data ! = journal & & ( is_metadata | | should_journal_data ( inode ) )
*/
2009-11-24 19:05:59 +03:00
BUFFER_TRACE ( bh , " call jbd2_journal_revoke " ) ;
err = jbd2_journal_revoke ( handle , blocknr , bh ) ;
if ( err ) {
2010-06-29 22:53:24 +04:00
ext4_journal_abort_handle ( where , line , __func__ ,
bh , handle , err ) ;
2010-07-27 19:56:40 +04:00
__ext4_abort ( inode - > i_sb , where , line ,
" error %d when attempting revoke " , err ) ;
2009-11-24 19:05:59 +03:00
}
2009-11-23 04:52:12 +03:00
BUFFER_TRACE ( bh , " exit " ) ;
return err ;
}
2010-06-29 22:53:24 +04:00
int __ext4_journal_get_create_access ( const char * where , unsigned int line ,
2006-12-07 07:37:15 +03:00
handle_t * handle , struct buffer_head * bh )
{
2009-01-07 08:06:22 +03:00
int err = 0 ;
if ( ext4_handle_valid ( handle ) ) {
err = jbd2_journal_get_create_access ( handle , bh ) ;
if ( err )
2010-06-29 22:53:24 +04:00
ext4_journal_abort_handle ( where , line , __func__ ,
bh , handle , err ) ;
2009-01-07 08:06:22 +03:00
}
2006-12-07 07:37:15 +03:00
return err ;
}
2010-06-29 22:53:24 +04:00
int __ext4_handle_dirty_metadata ( const char * where , unsigned int line ,
handle_t * handle , struct inode * inode ,
struct buffer_head * bh )
2006-12-07 07:37:15 +03:00
{
2009-01-07 08:06:22 +03:00
int err = 0 ;
2013-04-04 06:00:52 +04:00
might_sleep ( ) ;
2013-04-22 00:45:54 +04:00
set_buffer_meta ( bh ) ;
set_buffer_prio ( bh ) ;
2009-01-07 08:06:22 +03:00
if ( ext4_handle_valid ( handle ) ) {
err = jbd2_journal_dirty_metadata ( handle , bh ) ;
2014-10-02 06:23:15 +04:00
/* Errors can only happen due to aborted journal or a nasty bug */
if ( ! is_handle_aborted ( handle ) & & WARN_ON_ONCE ( err ) ) {
2013-08-12 17:53:28 +04:00
ext4_journal_abort_handle ( where , line , __func__ , bh ,
handle , err ) ;
2014-03-13 00:38:03 +04:00
if ( inode = = NULL ) {
pr_err ( " EXT4: jbd2_journal_dirty_metadata "
" failed: handle type %u started at "
" line %u, credits %u/%u, errcode %d " ,
handle - > h_type ,
handle - > h_line_no ,
handle - > h_requested_credits ,
handle - > h_buffer_credits , err ) ;
return err ;
}
2013-12-02 18:31:36 +04:00
ext4_error_inode ( inode , where , line ,
bh - > b_blocknr ,
" journal_dirty_metadata failed: "
" handle type %u started at line %u, "
" credits %u/%u, errcode %d " ,
handle - > h_type ,
handle - > h_line_no ,
handle - > h_requested_credits ,
handle - > h_buffer_credits , err ) ;
2011-09-04 18:18:14 +04:00
}
2009-01-07 08:06:22 +03:00
} else {
2010-02-16 23:06:29 +03:00
if ( inode )
2009-09-12 21:41:55 +04:00
mark_buffer_dirty_inode ( bh , inode ) ;
else
mark_buffer_dirty ( bh ) ;
2009-01-07 08:06:22 +03:00
if ( inode & & inode_needs_sync ( inode ) ) {
sync_dirty_buffer ( bh ) ;
if ( buffer_req ( bh ) & & ! buffer_uptodate ( bh ) ) {
2010-07-27 19:56:03 +04:00
struct ext4_super_block * es ;
es = EXT4_SB ( inode - > i_sb ) - > s_es ;
es - > s_last_error_block =
cpu_to_le64 ( bh - > b_blocknr ) ;
2010-07-27 19:56:40 +04:00
ext4_error_inode ( inode , where , line ,
bh - > b_blocknr ,
" IO error syncing itable block " ) ;
2009-01-07 08:06:22 +03:00
err = - EIO ;
}
}
}
2006-12-07 07:37:15 +03:00
return err ;
}
2010-06-12 07:14:04 +04:00
2010-06-29 22:53:24 +04:00
int __ext4_handle_dirty_super ( const char * where , unsigned int line ,
2012-07-23 04:37:31 +04:00
handle_t * handle , struct super_block * sb )
2010-06-12 07:14:04 +04:00
{
struct buffer_head * bh = EXT4_SB ( sb ) - > s_sbh ;
int err = 0 ;
2012-10-10 09:06:58 +04:00
ext4_superblock_csum_set ( sb ) ;
2010-06-12 07:14:04 +04:00
if ( ext4_handle_valid ( handle ) ) {
err = jbd2_journal_dirty_metadata ( handle , bh ) ;
if ( err )
2010-06-29 22:53:24 +04:00
ext4_journal_abort_handle ( where , line , __func__ ,
bh , handle , err ) ;
2012-10-10 09:06:58 +04:00
} else
2012-04-30 02:29:10 +04:00
mark_buffer_dirty ( bh ) ;
2010-06-12 07:14:04 +04:00
return err ;
}