@ -594,6 +594,43 @@ mext_calc_swap_extents(struct ext4_extent *tmp_dext,
return 0 ;
}
/**
* mext_check_coverage - Check that all extents in range has the same type
*
* @ inode : inode in question
* @ from : block offset of inode
* @ count : block count to be checked
* @ uninit : extents expected to be uninitialized
* @ err : pointer to save error value
*
* Return 1 if all extents in range has expected type , and zero otherwise .
*/
static int
mext_check_coverage ( struct inode * inode , ext4_lblk_t from , ext4_lblk_t count ,
int uninit , int * err )
{
struct ext4_ext_path * path = NULL ;
struct ext4_extent * ext ;
ext4_lblk_t last = from + count ;
while ( from < last ) {
* err = get_ext_path ( inode , from , & path ) ;
if ( * err )
return 0 ;
ext = path [ ext_depth ( inode ) ] . p_ext ;
if ( ! ext ) {
ext4_ext_drop_refs ( path ) ;
return 0 ;
}
if ( uninit ! = ext4_ext_is_uninitialized ( ext ) ) {
ext4_ext_drop_refs ( path ) ;
return 0 ;
}
from + = ext4_ext_get_actual_len ( ext ) ;
ext4_ext_drop_refs ( path ) ;
}
return 1 ;
}
/**
* mext_replace_branches - Replace original extents with new extents
*
@ -629,9 +666,6 @@ mext_replace_branches(handle_t *handle, struct inode *orig_inode,
int replaced_count = 0 ;
int dext_alen ;
/* Protect extent trees against block allocations via delalloc */
double_down_write_data_sem ( orig_inode , donor_inode ) ;
/* Get the original extent for the block "orig_off" */
* err = get_ext_path ( orig_inode , orig_off , & orig_path ) ;
if ( * err )
@ -730,8 +764,6 @@ out:
ext4_ext_invalidate_cache ( orig_inode ) ;
ext4_ext_invalidate_cache ( donor_inode ) ;
double_up_write_data_sem ( orig_inode , donor_inode ) ;
return replaced_count ;
}
@ -925,7 +957,46 @@ again:
pagep ) ;
if ( unlikely ( * err < 0 ) )
goto stop_journal ;
/*
* If orig extent was uninitialized it can become initialized
* at any time after i_data_sem was dropped , in order to
* serialize with delalloc we have recheck extent while we
* hold page ' s lock , if it is still the case data copy is not
* necessary , just swap data blocks between orig and donor .
*/
if ( uninit ) {
double_down_write_data_sem ( orig_inode , donor_inode ) ;
/* If any of extents in range became initialized we have to
* fallback to data copying */
uninit = mext_check_coverage ( orig_inode , orig_blk_offset ,
block_len_in_page , 1 , err ) ;
if ( * err )
goto drop_data_sem ;
uninit & = mext_check_coverage ( donor_inode , orig_blk_offset ,
block_len_in_page , 1 , err ) ;
if ( * err )
goto drop_data_sem ;
if ( ! uninit ) {
double_up_write_data_sem ( orig_inode , donor_inode ) ;
goto data_copy ;
}
if ( ( page_has_private ( pagep [ 0 ] ) & &
! try_to_release_page ( pagep [ 0 ] , 0 ) ) | |
( page_has_private ( pagep [ 1 ] ) & &
! try_to_release_page ( pagep [ 1 ] , 0 ) ) ) {
* err = - EBUSY ;
goto drop_data_sem ;
}
replaced_count = mext_replace_branches ( handle , orig_inode ,
donor_inode , orig_blk_offset ,
block_len_in_page , err ) ;
drop_data_sem :
double_up_write_data_sem ( orig_inode , donor_inode ) ;
goto unlock_pages ;
}
data_copy :
* err = mext_page_mkuptodate ( pagep [ 0 ] , from , from + replaced_size ) ;
if ( * err )
goto unlock_pages ;