2005-12-15 14:31:24 -08:00
/* -*- mode: c; c-basic-offset: 8; -*-
* vim : noexpandtab sw = 8 ts = 8 sts = 0 :
*
* extent_map . c
*
2007-01-17 12:31:35 -08:00
* Block / Cluster mapping functions
2005-12-15 14:31:24 -08:00
*
* Copyright ( C ) 2004 Oracle . All rights reserved .
*
* 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 program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public
* License along with this program ; if not , write to the
* Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
* Boston , MA 021110 - 1307 , USA .
*/
# include <linux/fs.h>
# include <linux/init.h>
# include <linux/types.h>
# define MLOG_MASK_PREFIX ML_EXTENT_MAP
# include <cluster/masklog.h>
# include "ocfs2.h"
2007-01-17 12:31:35 -08:00
# include "alloc.h"
2005-12-15 14:31:24 -08:00
# include "extent_map.h"
# include "inode.h"
# include "super.h"
# include "buffer_head_io.h"
2007-03-09 16:26:50 -08:00
/*
* Return the 1 st index within el which contains an extent start
* larger than v_cluster .
*/
static int ocfs2_search_for_hole_index ( struct ocfs2_extent_list * el ,
u32 v_cluster )
{
int i ;
struct ocfs2_extent_rec * rec ;
for ( i = 0 ; i < le16_to_cpu ( el - > l_next_free_rec ) ; i + + ) {
rec = & el - > l_recs [ i ] ;
if ( v_cluster < le32_to_cpu ( rec - > e_cpos ) )
break ;
}
return i ;
}
/*
* Figure out the size of a hole which starts at v_cluster within the given
* extent list .
*
* If there is no more allocation past v_cluster , we return the maximum
* cluster size minus v_cluster .
*
* If we have in - inode extents , then el points to the dinode list and
* eb_bh is NULL . Otherwise , eb_bh should point to the extent block
* containing el .
*/
static int ocfs2_figure_hole_clusters ( struct inode * inode ,
struct ocfs2_extent_list * el ,
struct buffer_head * eb_bh ,
u32 v_cluster ,
u32 * num_clusters )
{
int ret , i ;
struct buffer_head * next_eb_bh = NULL ;
struct ocfs2_extent_block * eb , * next_eb ;
i = ocfs2_search_for_hole_index ( el , v_cluster ) ;
if ( i = = le16_to_cpu ( el - > l_next_free_rec ) & & eb_bh ) {
eb = ( struct ocfs2_extent_block * ) eb_bh - > b_data ;
/*
* Check the next leaf for any extents .
*/
if ( le64_to_cpu ( eb - > h_next_leaf_blk ) = = 0ULL )
goto no_more_extents ;
ret = ocfs2_read_block ( OCFS2_SB ( inode - > i_sb ) ,
le64_to_cpu ( eb - > h_next_leaf_blk ) ,
& next_eb_bh , OCFS2_BH_CACHED , inode ) ;
if ( ret ) {
mlog_errno ( ret ) ;
goto out ;
}
next_eb = ( struct ocfs2_extent_block * ) next_eb_bh - > b_data ;
if ( ! OCFS2_IS_VALID_EXTENT_BLOCK ( next_eb ) ) {
ret = - EROFS ;
OCFS2_RO_ON_INVALID_EXTENT_BLOCK ( inode - > i_sb , next_eb ) ;
goto out ;
}
el = & next_eb - > h_list ;
i = ocfs2_search_for_hole_index ( el , v_cluster ) ;
}
no_more_extents :
if ( i = = le16_to_cpu ( el - > l_next_free_rec ) ) {
/*
* We ' re at the end of our existing allocation . Just
* return the maximum number of clusters we could
* possibly allocate .
*/
* num_clusters = UINT_MAX - v_cluster ;
} else {
* num_clusters = le32_to_cpu ( el - > l_recs [ i ] . e_cpos ) - v_cluster ;
}
ret = 0 ;
out :
brelse ( next_eb_bh ) ;
return ret ;
}
2005-12-15 14:31:24 -08:00
/*
2007-01-17 12:31:35 -08:00
* Return the index of the extent record which contains cluster # v_cluster .
* - 1 is returned if it was not found .
2005-12-15 14:31:24 -08:00
*
2007-01-17 12:31:35 -08:00
* Should work fine on interior and exterior nodes .
2005-12-15 14:31:24 -08:00
*/
2007-01-17 12:31:35 -08:00
static int ocfs2_search_extent_list ( struct ocfs2_extent_list * el ,
u32 v_cluster )
2005-12-15 14:31:24 -08:00
{
2007-01-17 12:31:35 -08:00
int ret = - 1 ;
int i ;
2005-12-15 14:31:24 -08:00
struct ocfs2_extent_rec * rec ;
2007-03-07 16:46:57 -08:00
u32 rec_end , rec_start , clusters ;
2005-12-15 14:31:24 -08:00
2007-01-17 12:31:35 -08:00
for ( i = 0 ; i < le16_to_cpu ( el - > l_next_free_rec ) ; i + + ) {
2005-12-15 14:31:24 -08:00
rec = & el - > l_recs [ i ] ;
2006-02-28 17:58:36 -08:00
2007-01-17 12:31:35 -08:00
rec_start = le32_to_cpu ( rec - > e_cpos ) ;
2007-03-07 16:46:57 -08:00
clusters = ocfs2_rec_clusters ( el , rec ) ;
rec_end = rec_start + clusters ;
2006-02-28 17:58:36 -08:00
2007-01-17 12:31:35 -08:00
if ( v_cluster > = rec_start & & v_cluster < rec_end ) {
ret = i ;
break ;
2005-12-15 14:31:24 -08:00
}
}
return ret ;
}
2007-02-09 20:24:12 -08:00
int ocfs2_get_clusters ( struct inode * inode , u32 v_cluster ,
2007-03-09 16:21:46 -08:00
u32 * p_cluster , u32 * num_clusters ,
unsigned int * extent_flags )
2005-12-15 14:31:24 -08:00
{
2007-01-17 12:31:35 -08:00
int ret , i ;
2007-03-09 16:21:46 -08:00
unsigned int flags = 0 ;
2007-01-17 12:31:35 -08:00
struct buffer_head * di_bh = NULL ;
struct buffer_head * eb_bh = NULL ;
2005-12-15 14:31:24 -08:00
struct ocfs2_dinode * di ;
2007-01-17 12:31:35 -08:00
struct ocfs2_extent_block * eb ;
2005-12-15 14:31:24 -08:00
struct ocfs2_extent_list * el ;
2007-01-17 12:31:35 -08:00
struct ocfs2_extent_rec * rec ;
u32 coff ;
2005-12-15 14:31:24 -08:00
2007-01-17 12:31:35 -08:00
ret = ocfs2_read_block ( OCFS2_SB ( inode - > i_sb ) , OCFS2_I ( inode ) - > ip_blkno ,
& di_bh , OCFS2_BH_CACHED , inode ) ;
2005-12-15 14:31:24 -08:00
if ( ret ) {
mlog_errno ( ret ) ;
2007-01-17 12:31:35 -08:00
goto out ;
2005-12-15 14:31:24 -08:00
}
2007-01-17 12:31:35 -08:00
di = ( struct ocfs2_dinode * ) di_bh - > b_data ;
el = & di - > id2 . i_list ;
2005-12-15 14:31:24 -08:00
2007-01-17 12:31:35 -08:00
if ( el - > l_tree_depth ) {
ret = ocfs2_find_leaf ( inode , el , v_cluster , & eb_bh ) ;
if ( ret ) {
mlog_errno ( ret ) ;
goto out ;
}
2005-12-15 14:31:24 -08:00
2007-01-17 12:31:35 -08:00
eb = ( struct ocfs2_extent_block * ) eb_bh - > b_data ;
el = & eb - > h_list ;
2007-03-07 16:46:57 -08:00
if ( el - > l_tree_depth ) {
ocfs2_error ( inode - > i_sb ,
" Inode %lu has non zero tree depth in "
" leaf block %llu \n " , inode - > i_ino ,
( unsigned long long ) eb_bh - > b_blocknr ) ;
ret = - EROFS ;
goto out ;
}
2006-04-27 16:36:14 -07:00
}
2005-12-15 14:31:24 -08:00
2007-01-17 12:31:35 -08:00
i = ocfs2_search_extent_list ( el , v_cluster ) ;
if ( i = = - 1 ) {
2006-04-27 16:36:14 -07:00
/*
2007-01-17 12:31:35 -08:00
* A hole was found . Return some canned values that
2007-03-09 16:26:50 -08:00
* callers can key on . If asked for , num_clusters will
* be populated with the size of the hole .
2006-04-27 16:36:14 -07:00
*/
2007-01-17 12:31:35 -08:00
* p_cluster = 0 ;
2007-03-09 16:26:50 -08:00
if ( num_clusters ) {
ret = ocfs2_figure_hole_clusters ( inode , el , eb_bh ,
v_cluster ,
num_clusters ) ;
if ( ret ) {
mlog_errno ( ret ) ;
goto out ;
}
}
2007-01-17 12:31:35 -08:00
} else {
rec = & el - > l_recs [ i ] ;
2005-12-15 14:31:24 -08:00
2007-01-17 12:31:35 -08:00
BUG_ON ( v_cluster < le32_to_cpu ( rec - > e_cpos ) ) ;
2005-12-15 14:31:24 -08:00
2007-01-17 12:31:35 -08:00
if ( ! rec - > e_blkno ) {
ocfs2_error ( inode - > i_sb , " Inode %lu has bad extent "
" record (%u, %u, 0) " , inode - > i_ino ,
le32_to_cpu ( rec - > e_cpos ) ,
2007-03-07 16:46:57 -08:00
ocfs2_rec_clusters ( el , rec ) ) ;
2007-01-17 12:31:35 -08:00
ret = - EROFS ;
goto out ;
2005-12-15 14:31:24 -08:00
}
2007-01-17 12:31:35 -08:00
coff = v_cluster - le32_to_cpu ( rec - > e_cpos ) ;
2005-12-15 14:31:24 -08:00
2007-01-17 12:31:35 -08:00
* p_cluster = ocfs2_blocks_to_clusters ( inode - > i_sb ,
le64_to_cpu ( rec - > e_blkno ) ) ;
* p_cluster = * p_cluster + coff ;
2005-12-15 14:31:24 -08:00
2007-01-17 12:31:35 -08:00
if ( num_clusters )
2007-03-07 16:46:57 -08:00
* num_clusters = ocfs2_rec_clusters ( el , rec ) - coff ;
2007-03-09 16:21:46 -08:00
flags = rec - > e_flags ;
2005-12-15 14:31:24 -08:00
}
2007-03-09 16:21:46 -08:00
if ( extent_flags )
* extent_flags = flags ;
2007-01-17 12:31:35 -08:00
out :
brelse ( di_bh ) ;
brelse ( eb_bh ) ;
2005-12-15 14:31:24 -08:00
return ret ;
}
/*
2007-01-17 12:31:35 -08:00
* This expects alloc_sem to be held . The allocation cannot change at
* all while the map is in the process of being updated .
2005-12-15 14:31:24 -08:00
*/
2007-01-17 12:31:35 -08:00
int ocfs2_extent_map_get_blocks ( struct inode * inode , u64 v_blkno , u64 * p_blkno ,
2007-03-09 16:26:50 -08:00
u64 * ret_count , unsigned int * extent_flags )
2005-12-15 14:31:24 -08:00
{
int ret ;
int bpc = ocfs2_clusters_to_blocks ( inode - > i_sb , 1 ) ;
2007-01-17 12:31:35 -08:00
u32 cpos , num_clusters , p_cluster ;
u64 boff = 0 ;
2005-12-15 14:31:24 -08:00
cpos = ocfs2_blocks_to_clusters ( inode - > i_sb , v_blkno ) ;
2007-03-09 16:21:46 -08:00
ret = ocfs2_get_clusters ( inode , cpos , & p_cluster , & num_clusters ,
extent_flags ) ;
2005-12-15 14:31:24 -08:00
if ( ret ) {
mlog_errno ( ret ) ;
2007-01-17 12:31:35 -08:00
goto out ;
2005-12-15 14:31:24 -08:00
}
2007-01-17 12:31:35 -08:00
/*
* p_cluster = = 0 indicates a hole .
*/
if ( p_cluster ) {
boff = ocfs2_clusters_to_blocks ( inode - > i_sb , p_cluster ) ;
2005-12-15 14:31:24 -08:00
boff + = ( v_blkno & ( u64 ) ( bpc - 1 ) ) ;
}
2007-01-17 12:31:35 -08:00
* p_blkno = boff ;
2005-12-15 14:31:24 -08:00
2007-01-17 12:31:35 -08:00
if ( ret_count ) {
* ret_count = ocfs2_clusters_to_blocks ( inode - > i_sb , num_clusters ) ;
* ret_count - = v_blkno & ( u64 ) ( bpc - 1 ) ;
2005-12-15 14:31:24 -08:00
}
2007-01-17 12:31:35 -08:00
out :
return ret ;
2005-12-15 14:31:24 -08:00
}