2007-03-21 16:12:56 +01:00
# include <linux/module.h>
2007-02-02 15:18:22 +01:00
# include "ctree.h"
# include "disk-io.h"
2007-03-23 20:56:19 +01:00
# include "transaction.h"
2007-02-23 14:38:36 +01:00
2007-03-16 21:20:31 +01:00
static int split_node ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , int level ) ;
static int split_leaf ( struct btrfs_trans_handle * trans , struct btrfs_root
2007-04-04 20:08:15 +02:00
* root , struct btrfs_key * ins_key ,
struct btrfs_path * path , int data_size ) ;
2007-03-16 21:20:31 +01:00
static int push_node_left ( struct btrfs_trans_handle * trans , struct btrfs_root
2007-03-22 17:13:20 +01:00
* root , struct buffer_head * dst , struct buffer_head
2007-03-16 21:20:31 +01:00
* src ) ;
static int balance_node_right ( struct btrfs_trans_handle * trans , struct
2007-03-22 17:13:20 +01:00
btrfs_root * root , struct buffer_head * dst_buf ,
struct buffer_head * src_buf ) ;
2007-03-16 21:20:31 +01:00
static int del_ptr ( struct btrfs_trans_handle * trans , struct btrfs_root * root ,
struct btrfs_path * path , int level , int slot ) ;
2007-02-20 22:40:44 +01:00
2007-04-04 15:36:31 +02:00
inline void btrfs_init_path ( struct btrfs_path * p )
2007-04-02 16:50:19 +02:00
{
2007-04-04 15:36:31 +02:00
memset ( p , 0 , sizeof ( * p ) ) ;
2007-04-02 16:50:19 +02:00
}
2007-04-04 15:36:31 +02:00
struct btrfs_path * btrfs_alloc_path ( void )
2007-04-02 16:50:19 +02:00
{
2007-04-04 15:36:31 +02:00
struct btrfs_path * path ;
path = kmem_cache_alloc ( btrfs_path_cachep , GFP_NOFS ) ;
if ( path )
btrfs_init_path ( path ) ;
return path ;
2007-04-02 16:50:19 +02:00
}
2007-04-04 15:36:31 +02:00
void btrfs_free_path ( struct btrfs_path * p )
2007-01-26 21:51:26 +01:00
{
2007-04-04 15:36:31 +02:00
btrfs_release_path ( NULL , p ) ;
kmem_cache_free ( btrfs_path_cachep , p ) ;
2007-01-26 21:51:26 +01:00
}
2007-03-13 15:46:10 +01:00
void btrfs_release_path ( struct btrfs_root * root , struct btrfs_path * p )
2007-02-02 15:18:22 +01:00
{
int i ;
2007-03-13 15:46:10 +01:00
for ( i = 0 ; i < BTRFS_MAX_LEVEL ; i + + ) {
2007-02-02 15:18:22 +01:00
if ( ! p - > nodes [ i ] )
break ;
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , p - > nodes [ i ] ) ;
2007-02-02 15:18:22 +01:00
}
2007-02-28 22:35:06 +01:00
memset ( p , 0 , sizeof ( * p ) ) ;
2007-02-02 15:18:22 +01:00
}
2007-03-16 21:20:31 +01:00
static int btrfs_cow_block ( struct btrfs_trans_handle * trans , struct btrfs_root
2007-03-22 17:13:20 +01:00
* root , struct buffer_head * buf , struct buffer_head
* parent , int parent_slot , struct buffer_head
2007-03-16 21:20:31 +01:00
* * cow_ret )
2007-03-02 22:08:05 +01:00
{
2007-03-22 17:13:20 +01:00
struct buffer_head * cow ;
struct btrfs_node * cow_node ;
2007-03-02 22:08:05 +01:00
2007-03-23 20:56:19 +01:00
if ( btrfs_header_generation ( btrfs_buffer_header ( buf ) ) = =
trans - > transid ) {
2007-03-02 22:08:05 +01:00
* cow_ret = buf ;
return 0 ;
}
2007-03-16 21:20:31 +01:00
cow = btrfs_alloc_free_block ( trans , root ) ;
2007-03-22 17:13:20 +01:00
cow_node = btrfs_buffer_node ( cow ) ;
2007-04-02 16:50:19 +02:00
if ( buf - > b_size ! = root - > blocksize | | cow - > b_size ! = root - > blocksize )
WARN_ON ( 1 ) ;
2007-03-22 17:13:20 +01:00
memcpy ( cow_node , btrfs_buffer_node ( buf ) , root - > blocksize ) ;
2007-04-11 21:53:25 +02:00
btrfs_set_header_blocknr ( & cow_node - > header , bh_blocknr ( cow ) ) ;
2007-03-23 20:56:19 +01:00
btrfs_set_header_generation ( & cow_node - > header , trans - > transid ) ;
2007-03-16 21:20:31 +01:00
btrfs_inc_ref ( trans , root , buf ) ;
2007-03-02 22:08:05 +01:00
if ( buf = = root - > node ) {
root - > node = cow ;
2007-03-22 17:13:20 +01:00
get_bh ( cow ) ;
2007-04-02 16:50:19 +02:00
if ( buf ! = root - > commit_root ) {
2007-04-11 21:53:25 +02:00
btrfs_free_extent ( trans , root , bh_blocknr ( buf ) , 1 , 1 ) ;
2007-04-02 16:50:19 +02:00
}
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , buf ) ;
2007-03-02 22:08:05 +01:00
} else {
2007-03-22 17:13:20 +01:00
btrfs_set_node_blockptr ( btrfs_buffer_node ( parent ) , parent_slot ,
2007-04-11 21:53:25 +02:00
bh_blocknr ( cow ) ) ;
2007-03-30 20:27:56 +02:00
btrfs_mark_buffer_dirty ( parent ) ;
2007-04-11 21:53:25 +02:00
btrfs_free_extent ( trans , root , bh_blocknr ( buf ) , 1 , 1 ) ;
2007-03-02 22:08:05 +01:00
}
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , buf ) ;
2007-04-04 15:36:31 +02:00
mark_buffer_dirty ( cow ) ;
2007-04-02 16:50:19 +02:00
* cow_ret = cow ;
2007-03-02 22:08:05 +01:00
return 0 ;
}
2007-02-02 17:05:29 +01:00
/*
* The leaf data grows from end - to - front in the node .
* this returns the address of the start of the last item ,
* which is the stop of the leaf data stack
*/
2007-03-14 19:14:43 +01:00
static inline unsigned int leaf_data_end ( struct btrfs_root * root ,
struct btrfs_leaf * leaf )
2007-01-26 21:51:26 +01:00
{
2007-03-12 17:01:18 +01:00
u32 nr = btrfs_header_nritems ( & leaf - > header ) ;
2007-01-26 21:51:26 +01:00
if ( nr = = 0 )
2007-03-14 19:14:43 +01:00
return BTRFS_LEAF_DATA_SIZE ( root ) ;
2007-03-13 01:12:07 +01:00
return btrfs_item_offset ( leaf - > items + nr - 1 ) ;
2007-01-26 21:51:26 +01:00
}
2007-02-02 17:05:29 +01:00
/*
* compare two keys in a memcmp fashion
*/
2007-03-13 16:09:37 +01:00
static int comp_keys ( struct btrfs_disk_key * disk , struct btrfs_key * k2 )
2007-01-26 21:51:26 +01:00
{
2007-03-12 21:22:34 +01:00
struct btrfs_key k1 ;
btrfs_disk_key_to_cpu ( & k1 , disk ) ;
if ( k1 . objectid > k2 - > objectid )
2007-01-26 21:51:26 +01:00
return 1 ;
2007-03-12 21:22:34 +01:00
if ( k1 . objectid < k2 - > objectid )
2007-01-26 21:51:26 +01:00
return - 1 ;
2007-03-29 21:15:27 +02:00
if ( k1 . flags > k2 - > flags )
return 1 ;
if ( k1 . flags < k2 - > flags )
return - 1 ;
2007-04-17 21:39:32 +02:00
if ( k1 . offset > k2 - > offset )
return 1 ;
if ( k1 . offset < k2 - > offset )
return - 1 ;
2007-01-26 21:51:26 +01:00
return 0 ;
}
2007-02-02 17:05:29 +01:00
2007-03-14 19:14:43 +01:00
static int check_node ( struct btrfs_root * root , struct btrfs_path * path ,
int level )
2007-02-28 22:35:06 +01:00
{
int i ;
2007-03-13 15:46:10 +01:00
struct btrfs_node * parent = NULL ;
2007-03-22 17:13:20 +01:00
struct btrfs_node * node = btrfs_buffer_node ( path - > nodes [ level ] ) ;
2007-02-28 22:35:06 +01:00
int parent_slot ;
2007-03-12 17:01:18 +01:00
u32 nritems = btrfs_header_nritems ( & node - > header ) ;
2007-02-28 22:35:06 +01:00
if ( path - > nodes [ level + 1 ] )
2007-03-22 17:13:20 +01:00
parent = btrfs_buffer_node ( path - > nodes [ level + 1 ] ) ;
2007-02-28 22:35:06 +01:00
parent_slot = path - > slots [ level + 1 ] ;
2007-03-12 17:01:18 +01:00
BUG_ON ( nritems = = 0 ) ;
if ( parent ) {
2007-03-12 21:22:34 +01:00
struct btrfs_disk_key * parent_key ;
2007-03-14 19:14:43 +01:00
parent_key = & parent - > ptrs [ parent_slot ] . key ;
BUG_ON ( memcmp ( parent_key , & node - > ptrs [ 0 ] . key ,
2007-03-12 21:22:34 +01:00
sizeof ( struct btrfs_disk_key ) ) ) ;
2007-03-13 14:28:32 +01:00
BUG_ON ( btrfs_node_blockptr ( parent , parent_slot ) ! =
2007-03-12 17:01:18 +01:00
btrfs_header_blocknr ( & node - > header ) ) ;
2007-02-28 22:35:06 +01:00
}
2007-03-14 19:14:43 +01:00
BUG_ON ( nritems > BTRFS_NODEPTRS_PER_BLOCK ( root ) ) ;
2007-03-12 17:01:18 +01:00
for ( i = 0 ; nritems > 1 & & i < nritems - 2 ; i + + ) {
2007-03-12 21:22:34 +01:00
struct btrfs_key cpukey ;
2007-03-14 19:14:43 +01:00
btrfs_disk_key_to_cpu ( & cpukey , & node - > ptrs [ i + 1 ] . key ) ;
2007-04-20 19:16:02 +02:00
if ( comp_keys ( & node - > ptrs [ i ] . key , & cpukey ) > = 0 ) {
struct btrfs_key bad ;
btrfs_disk_key_to_cpu ( & bad , & node - > ptrs [ i ] . key ) ;
printk ( " check_node level %d i is %d bad comp %Lu %u %Lu, %Lu %u %Lu \n " , level , i , bad . objectid , bad . flags , bad . offset , cpukey . objectid , cpukey . flags , cpukey . offset ) ;
}
2007-03-14 19:14:43 +01:00
BUG_ON ( comp_keys ( & node - > ptrs [ i ] . key , & cpukey ) > = 0 ) ;
2007-02-28 22:35:06 +01:00
}
return 0 ;
}
2007-03-14 19:14:43 +01:00
static int check_leaf ( struct btrfs_root * root , struct btrfs_path * path ,
int level )
2007-02-28 22:35:06 +01:00
{
int i ;
2007-03-22 17:13:20 +01:00
struct btrfs_leaf * leaf = btrfs_buffer_leaf ( path - > nodes [ level ] ) ;
2007-03-13 15:46:10 +01:00
struct btrfs_node * parent = NULL ;
2007-02-28 22:35:06 +01:00
int parent_slot ;
2007-03-12 17:01:18 +01:00
u32 nritems = btrfs_header_nritems ( & leaf - > header ) ;
2007-02-28 22:35:06 +01:00
if ( path - > nodes [ level + 1 ] )
2007-03-22 17:13:20 +01:00
parent = btrfs_buffer_node ( path - > nodes [ level + 1 ] ) ;
2007-02-28 22:35:06 +01:00
parent_slot = path - > slots [ level + 1 ] ;
2007-03-14 19:14:43 +01:00
BUG_ON ( btrfs_leaf_free_space ( root , leaf ) < 0 ) ;
2007-03-12 17:01:18 +01:00
if ( nritems = = 0 )
return 0 ;
if ( parent ) {
2007-03-12 21:22:34 +01:00
struct btrfs_disk_key * parent_key ;
2007-03-14 19:14:43 +01:00
parent_key = & parent - > ptrs [ parent_slot ] . key ;
2007-02-28 22:35:06 +01:00
BUG_ON ( memcmp ( parent_key , & leaf - > items [ 0 ] . key ,
2007-03-12 21:22:34 +01:00
sizeof ( struct btrfs_disk_key ) ) ) ;
2007-03-13 14:28:32 +01:00
BUG_ON ( btrfs_node_blockptr ( parent , parent_slot ) ! =
2007-03-12 17:01:18 +01:00
btrfs_header_blocknr ( & leaf - > header ) ) ;
2007-02-28 22:35:06 +01:00
}
2007-03-12 17:01:18 +01:00
for ( i = 0 ; nritems > 1 & & i < nritems - 2 ; i + + ) {
2007-03-12 21:22:34 +01:00
struct btrfs_key cpukey ;
btrfs_disk_key_to_cpu ( & cpukey , & leaf - > items [ i + 1 ] . key ) ;
2007-02-28 22:35:06 +01:00
BUG_ON ( comp_keys ( & leaf - > items [ i ] . key ,
2007-03-12 21:22:34 +01:00
& cpukey ) > = 0 ) ;
2007-03-13 01:12:07 +01:00
BUG_ON ( btrfs_item_offset ( leaf - > items + i ) ! =
btrfs_item_end ( leaf - > items + i + 1 ) ) ;
2007-02-28 22:35:06 +01:00
if ( i = = 0 ) {
2007-03-13 01:12:07 +01:00
BUG_ON ( btrfs_item_offset ( leaf - > items + i ) +
btrfs_item_size ( leaf - > items + i ) ! =
2007-03-14 19:14:43 +01:00
BTRFS_LEAF_DATA_SIZE ( root ) ) ;
2007-02-28 22:35:06 +01:00
}
}
return 0 ;
}
2007-03-14 19:14:43 +01:00
static int check_block ( struct btrfs_root * root , struct btrfs_path * path ,
int level )
2007-02-28 22:35:06 +01:00
{
2007-04-05 20:28:50 +02:00
struct btrfs_node * node = btrfs_buffer_node ( path - > nodes [ level ] ) ;
if ( memcmp ( node - > header . fsid , root - > fs_info - > disk_super - > fsid ,
sizeof ( node - > header . fsid ) ) )
BUG ( ) ;
2007-02-28 22:35:06 +01:00
if ( level = = 0 )
2007-03-14 19:14:43 +01:00
return check_leaf ( root , path , level ) ;
return check_node ( root , path , level ) ;
2007-02-28 22:35:06 +01:00
}
2007-02-02 17:05:29 +01:00
/*
* search for key in the array p . items p are item_size apart
* and there are ' max ' items in p
* the slot in the array is returned via slot , and it points to
* the place where you would insert key if it is not found in
* the array .
*
* slot may point to max if the key is bigger than all of the keys
*/
2007-03-13 16:09:37 +01:00
static int generic_bin_search ( char * p , int item_size , struct btrfs_key * key ,
2007-01-26 21:51:26 +01:00
int max , int * slot )
{
int low = 0 ;
int high = max ;
int mid ;
int ret ;
2007-03-12 21:22:34 +01:00
struct btrfs_disk_key * tmp ;
2007-01-26 21:51:26 +01:00
while ( low < high ) {
mid = ( low + high ) / 2 ;
2007-03-12 21:22:34 +01:00
tmp = ( struct btrfs_disk_key * ) ( p + mid * item_size ) ;
2007-01-26 21:51:26 +01:00
ret = comp_keys ( tmp , key ) ;
if ( ret < 0 )
low = mid + 1 ;
else if ( ret > 0 )
high = mid ;
else {
* slot = mid ;
return 0 ;
}
}
* slot = low ;
return 1 ;
}
2007-02-24 19:39:08 +01:00
/*
* simple bin_search frontend that does the right thing for
* leaves vs nodes
*/
2007-03-13 16:09:37 +01:00
static int bin_search ( struct btrfs_node * c , struct btrfs_key * key , int * slot )
2007-01-26 21:51:26 +01:00
{
2007-03-12 17:01:18 +01:00
if ( btrfs_is_leaf ( c ) ) {
2007-03-13 15:46:10 +01:00
struct btrfs_leaf * l = ( struct btrfs_leaf * ) c ;
2007-03-13 01:12:07 +01:00
return generic_bin_search ( ( void * ) l - > items ,
sizeof ( struct btrfs_item ) ,
2007-03-12 17:01:18 +01:00
key , btrfs_header_nritems ( & c - > header ) ,
slot ) ;
2007-01-26 21:51:26 +01:00
} else {
2007-03-14 19:14:43 +01:00
return generic_bin_search ( ( void * ) c - > ptrs ,
sizeof ( struct btrfs_key_ptr ) ,
2007-03-12 17:01:18 +01:00
key , btrfs_header_nritems ( & c - > header ) ,
slot ) ;
2007-01-26 21:51:26 +01:00
}
return - 1 ;
}
2007-03-22 17:13:20 +01:00
static struct buffer_head * read_node_slot ( struct btrfs_root * root ,
struct buffer_head * parent_buf ,
2007-03-01 18:04:21 +01:00
int slot )
{
2007-03-22 17:13:20 +01:00
struct btrfs_node * node = btrfs_buffer_node ( parent_buf ) ;
2007-03-01 18:04:21 +01:00
if ( slot < 0 )
return NULL ;
2007-03-12 17:01:18 +01:00
if ( slot > = btrfs_header_nritems ( & node - > header ) )
2007-03-01 18:04:21 +01:00
return NULL ;
2007-03-13 14:28:32 +01:00
return read_tree_block ( root , btrfs_node_blockptr ( node , slot ) ) ;
2007-03-01 18:04:21 +01:00
}
2007-03-16 21:20:31 +01:00
static int balance_level ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , int level )
2007-03-01 18:04:21 +01:00
{
2007-03-22 17:13:20 +01:00
struct buffer_head * right_buf ;
struct buffer_head * mid_buf ;
struct buffer_head * left_buf ;
struct buffer_head * parent_buf = NULL ;
2007-03-13 15:46:10 +01:00
struct btrfs_node * right = NULL ;
struct btrfs_node * mid ;
struct btrfs_node * left = NULL ;
struct btrfs_node * parent = NULL ;
2007-03-01 18:04:21 +01:00
int ret = 0 ;
int wret ;
int pslot ;
int orig_slot = path - > slots [ level ] ;
2007-03-01 21:16:26 +01:00
u64 orig_ptr ;
2007-03-01 18:04:21 +01:00
if ( level = = 0 )
return 0 ;
mid_buf = path - > nodes [ level ] ;
2007-03-22 17:13:20 +01:00
mid = btrfs_buffer_node ( mid_buf ) ;
2007-03-13 14:28:32 +01:00
orig_ptr = btrfs_node_blockptr ( mid , orig_slot ) ;
2007-03-01 21:16:26 +01:00
2007-03-13 15:46:10 +01:00
if ( level < BTRFS_MAX_LEVEL - 1 )
2007-03-01 18:04:21 +01:00
parent_buf = path - > nodes [ level + 1 ] ;
pslot = path - > slots [ level + 1 ] ;
2007-03-17 19:29:23 +01:00
/*
* deal with the case where there is only one pointer in the root
* by promoting the node below to a root
*/
2007-03-01 18:04:21 +01:00
if ( ! parent_buf ) {
2007-03-22 17:13:20 +01:00
struct buffer_head * child ;
2007-04-11 21:53:25 +02:00
u64 blocknr = bh_blocknr ( mid_buf ) ;
2007-03-01 18:04:21 +01:00
2007-03-12 17:01:18 +01:00
if ( btrfs_header_nritems ( & mid - > header ) ! = 1 )
2007-03-01 18:04:21 +01:00
return 0 ;
/* promote the child to a root */
child = read_node_slot ( root , mid_buf , 0 ) ;
BUG_ON ( ! child ) ;
root - > node = child ;
path - > nodes [ level ] = NULL ;
2007-03-30 20:27:56 +02:00
clean_tree_block ( trans , root , mid_buf ) ;
wait_on_buffer ( mid_buf ) ;
2007-03-01 18:04:21 +01:00
/* once for the path */
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , mid_buf ) ;
2007-03-01 18:04:21 +01:00
/* once for the root ptr */
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , mid_buf ) ;
2007-03-16 21:20:31 +01:00
return btrfs_free_extent ( trans , root , blocknr , 1 , 1 ) ;
2007-03-01 18:04:21 +01:00
}
2007-03-22 17:13:20 +01:00
parent = btrfs_buffer_node ( parent_buf ) ;
2007-03-01 18:04:21 +01:00
2007-03-14 19:14:43 +01:00
if ( btrfs_header_nritems ( & mid - > header ) >
BTRFS_NODEPTRS_PER_BLOCK ( root ) / 4 )
2007-03-01 18:04:21 +01:00
return 0 ;
left_buf = read_node_slot ( root , parent_buf , pslot - 1 ) ;
right_buf = read_node_slot ( root , parent_buf , pslot + 1 ) ;
2007-03-01 21:16:26 +01:00
/* first, try to make some room in the middle buffer */
2007-03-01 18:04:21 +01:00
if ( left_buf ) {
2007-03-16 21:20:31 +01:00
btrfs_cow_block ( trans , root , left_buf , parent_buf , pslot - 1 ,
& left_buf ) ;
2007-03-22 17:13:20 +01:00
left = btrfs_buffer_node ( left_buf ) ;
2007-03-12 17:01:18 +01:00
orig_slot + = btrfs_header_nritems ( & left - > header ) ;
2007-03-16 21:20:31 +01:00
wret = push_node_left ( trans , root , left_buf , mid_buf ) ;
2007-03-01 21:16:26 +01:00
if ( wret < 0 )
ret = wret ;
2007-03-01 18:04:21 +01:00
}
2007-03-01 21:16:26 +01:00
/*
* then try to empty the right most buffer into the middle
*/
2007-03-01 18:04:21 +01:00
if ( right_buf ) {
2007-03-16 21:20:31 +01:00
btrfs_cow_block ( trans , root , right_buf , parent_buf , pslot + 1 ,
& right_buf ) ;
2007-03-22 17:13:20 +01:00
right = btrfs_buffer_node ( right_buf ) ;
2007-03-16 21:20:31 +01:00
wret = push_node_left ( trans , root , mid_buf , right_buf ) ;
2007-03-01 21:16:26 +01:00
if ( wret < 0 )
ret = wret ;
2007-03-12 17:01:18 +01:00
if ( btrfs_header_nritems ( & right - > header ) = = 0 ) {
2007-04-11 21:53:25 +02:00
u64 blocknr = bh_blocknr ( right_buf ) ;
2007-03-16 21:20:31 +01:00
clean_tree_block ( trans , root , right_buf ) ;
2007-03-30 20:27:56 +02:00
wait_on_buffer ( right_buf ) ;
btrfs_block_release ( root , right_buf ) ;
2007-03-01 18:04:21 +01:00
right_buf = NULL ;
right = NULL ;
2007-03-16 21:20:31 +01:00
wret = del_ptr ( trans , root , path , level + 1 , pslot +
1 ) ;
2007-03-01 18:04:21 +01:00
if ( wret )
ret = wret ;
2007-03-16 21:20:31 +01:00
wret = btrfs_free_extent ( trans , root , blocknr , 1 , 1 ) ;
2007-03-01 18:04:21 +01:00
if ( wret )
ret = wret ;
} else {
2007-03-30 20:27:56 +02:00
btrfs_memcpy ( root , parent ,
& parent - > ptrs [ pslot + 1 ] . key ,
& right - > ptrs [ 0 ] . key ,
sizeof ( struct btrfs_disk_key ) ) ;
btrfs_mark_buffer_dirty ( parent_buf ) ;
2007-03-01 18:04:21 +01:00
}
}
2007-03-12 17:01:18 +01:00
if ( btrfs_header_nritems ( & mid - > header ) = = 1 ) {
2007-03-01 21:16:26 +01:00
/*
* we ' re not allowed to leave a node with one item in the
* tree during a delete . A deletion from lower in the tree
* could try to delete the only pointer in this node .
* So , pull some keys from the left .
* There has to be a left pointer at this point because
* otherwise we would have pulled some pointers from the
* right
*/
BUG_ON ( ! left_buf ) ;
2007-03-16 21:20:31 +01:00
wret = balance_node_right ( trans , root , mid_buf , left_buf ) ;
2007-03-01 21:16:26 +01:00
if ( wret < 0 )
ret = wret ;
BUG_ON ( wret = = 1 ) ;
}
2007-03-12 17:01:18 +01:00
if ( btrfs_header_nritems ( & mid - > header ) = = 0 ) {
2007-03-01 21:16:26 +01:00
/* we've managed to empty the middle node, drop it */
2007-04-11 21:53:25 +02:00
u64 blocknr = bh_blocknr ( mid_buf ) ;
2007-03-16 21:20:31 +01:00
clean_tree_block ( trans , root , mid_buf ) ;
2007-03-30 20:27:56 +02:00
wait_on_buffer ( mid_buf ) ;
btrfs_block_release ( root , mid_buf ) ;
2007-03-01 18:04:21 +01:00
mid_buf = NULL ;
mid = NULL ;
2007-03-16 21:20:31 +01:00
wret = del_ptr ( trans , root , path , level + 1 , pslot ) ;
2007-03-01 18:04:21 +01:00
if ( wret )
ret = wret ;
2007-03-16 21:20:31 +01:00
wret = btrfs_free_extent ( trans , root , blocknr , 1 , 1 ) ;
2007-03-01 18:04:21 +01:00
if ( wret )
ret = wret ;
2007-03-01 21:16:26 +01:00
} else {
/* update the parent key to reflect our changes */
2007-03-30 20:27:56 +02:00
btrfs_memcpy ( root , parent ,
& parent - > ptrs [ pslot ] . key , & mid - > ptrs [ 0 ] . key ,
sizeof ( struct btrfs_disk_key ) ) ;
btrfs_mark_buffer_dirty ( parent_buf ) ;
2007-03-01 21:16:26 +01:00
}
2007-03-01 18:04:21 +01:00
2007-03-01 21:16:26 +01:00
/* update the path */
2007-03-01 18:04:21 +01:00
if ( left_buf ) {
2007-03-12 17:01:18 +01:00
if ( btrfs_header_nritems ( & left - > header ) > orig_slot ) {
2007-03-22 17:13:20 +01:00
get_bh ( left_buf ) ;
2007-03-01 18:04:21 +01:00
path - > nodes [ level ] = left_buf ;
path - > slots [ level + 1 ] - = 1 ;
path - > slots [ level ] = orig_slot ;
if ( mid_buf )
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , mid_buf ) ;
2007-03-01 18:04:21 +01:00
} else {
2007-03-12 17:01:18 +01:00
orig_slot - = btrfs_header_nritems ( & left - > header ) ;
2007-03-01 18:04:21 +01:00
path - > slots [ level ] = orig_slot ;
}
}
2007-03-01 21:16:26 +01:00
/* double check we haven't messed things up */
2007-03-14 19:14:43 +01:00
check_block ( root , path , level ) ;
2007-03-22 17:13:20 +01:00
if ( orig_ptr ! =
btrfs_node_blockptr ( btrfs_buffer_node ( path - > nodes [ level ] ) ,
path - > slots [ level ] ) )
2007-03-01 21:16:26 +01:00
BUG ( ) ;
2007-03-01 18:04:21 +01:00
if ( right_buf )
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , right_buf ) ;
2007-03-01 18:04:21 +01:00
if ( left_buf )
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , left_buf ) ;
2007-03-01 18:04:21 +01:00
return ret ;
}
2007-04-20 19:16:02 +02:00
/* returns zero if the push worked, non-zero otherwise */
static int push_nodes_for_insert ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct btrfs_path * path , int level )
{
struct buffer_head * right_buf ;
struct buffer_head * mid_buf ;
struct buffer_head * left_buf ;
struct buffer_head * parent_buf = NULL ;
struct btrfs_node * right = NULL ;
struct btrfs_node * mid ;
struct btrfs_node * left = NULL ;
struct btrfs_node * parent = NULL ;
int ret = 0 ;
int wret ;
int pslot ;
int orig_slot = path - > slots [ level ] ;
u64 orig_ptr ;
if ( level = = 0 )
return 1 ;
mid_buf = path - > nodes [ level ] ;
mid = btrfs_buffer_node ( mid_buf ) ;
orig_ptr = btrfs_node_blockptr ( mid , orig_slot ) ;
if ( level < BTRFS_MAX_LEVEL - 1 )
parent_buf = path - > nodes [ level + 1 ] ;
pslot = path - > slots [ level + 1 ] ;
if ( ! parent_buf )
return 1 ;
parent = btrfs_buffer_node ( parent_buf ) ;
left_buf = read_node_slot ( root , parent_buf , pslot - 1 ) ;
/* first, try to make some room in the middle buffer */
if ( left_buf ) {
u32 left_nr ;
btrfs_cow_block ( trans , root , left_buf , parent_buf , pslot - 1 ,
& left_buf ) ;
left = btrfs_buffer_node ( left_buf ) ;
left_nr = btrfs_header_nritems ( & left - > header ) ;
wret = push_node_left ( trans , root , left_buf , mid_buf ) ;
if ( wret < 0 )
ret = wret ;
if ( wret = = 0 ) {
orig_slot + = left_nr ;
btrfs_memcpy ( root , parent ,
& parent - > ptrs [ pslot ] . key ,
& mid - > ptrs [ 0 ] . key ,
sizeof ( struct btrfs_disk_key ) ) ;
btrfs_mark_buffer_dirty ( parent_buf ) ;
if ( btrfs_header_nritems ( & left - > header ) > orig_slot ) {
path - > nodes [ level ] = left_buf ;
path - > slots [ level + 1 ] - = 1 ;
path - > slots [ level ] = orig_slot ;
btrfs_block_release ( root , mid_buf ) ;
} else {
orig_slot - =
btrfs_header_nritems ( & left - > header ) ;
path - > slots [ level ] = orig_slot ;
btrfs_block_release ( root , left_buf ) ;
}
check_node ( root , path , level ) ;
return 0 ;
}
btrfs_block_release ( root , left_buf ) ;
}
right_buf = read_node_slot ( root , parent_buf , pslot + 1 ) ;
/*
* then try to empty the right most buffer into the middle
*/
if ( right_buf ) {
btrfs_cow_block ( trans , root , right_buf , parent_buf , pslot + 1 ,
& right_buf ) ;
right = btrfs_buffer_node ( right_buf ) ;
wret = balance_node_right ( trans , root , right_buf , mid_buf ) ;
if ( wret < 0 )
ret = wret ;
if ( wret = = 0 ) {
btrfs_memcpy ( root , parent ,
& parent - > ptrs [ pslot + 1 ] . key ,
& right - > ptrs [ 0 ] . key ,
sizeof ( struct btrfs_disk_key ) ) ;
btrfs_mark_buffer_dirty ( parent_buf ) ;
if ( btrfs_header_nritems ( & mid - > header ) < = orig_slot ) {
path - > nodes [ level ] = right_buf ;
path - > slots [ level + 1 ] + = 1 ;
path - > slots [ level ] = orig_slot -
btrfs_header_nritems ( & mid - > header ) ;
btrfs_block_release ( root , mid_buf ) ;
} else {
btrfs_block_release ( root , right_buf ) ;
}
check_node ( root , path , level ) ;
return 0 ;
}
btrfs_block_release ( root , right_buf ) ;
}
check_node ( root , path , level ) ;
return 1 ;
}
2007-02-02 17:05:29 +01:00
/*
* look for key in the tree . path is filled in with nodes along the way
* if key is found , we return zero and you can find the item in the leaf
* level of the path ( level 0 )
*
* If the key isn ' t found , the path points to the slot where it should
2007-02-28 22:35:06 +01:00
* be inserted , and 1 is returned . If there are other errors during the
* search a negative error number is returned .
2007-02-24 19:39:08 +01:00
*
* if ins_len > 0 , nodes and leaves will be split as we walk down the
* tree . if ins_len < 0 , nodes will be merged as we walk down the tree ( if
* possible )
2007-02-02 17:05:29 +01:00
*/
2007-03-16 21:20:31 +01:00
int btrfs_search_slot ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_key * key , struct btrfs_path * p , int
ins_len , int cow )
2007-01-26 21:51:26 +01:00
{
2007-03-22 17:13:20 +01:00
struct buffer_head * b ;
struct buffer_head * cow_buf ;
2007-03-13 15:46:10 +01:00
struct btrfs_node * c ;
2007-01-26 21:51:26 +01:00
int slot ;
int ret ;
int level ;
2007-02-22 17:39:13 +01:00
2007-03-30 14:47:31 +02:00
WARN_ON ( p - > nodes [ 0 ] ! = NULL ) ;
WARN_ON ( ! mutex_is_locked ( & root - > fs_info - > fs_mutex ) ) ;
2007-03-01 18:04:21 +01:00
again :
b = root - > node ;
2007-03-22 17:13:20 +01:00
get_bh ( b ) ;
2007-02-02 15:18:22 +01:00
while ( b ) {
2007-03-22 17:13:20 +01:00
c = btrfs_buffer_node ( b ) ;
level = btrfs_header_level ( & c - > header ) ;
2007-03-02 22:08:05 +01:00
if ( cow ) {
int wret ;
2007-03-22 17:13:20 +01:00
wret = btrfs_cow_block ( trans , root , b ,
p - > nodes [ level + 1 ] ,
p - > slots [ level + 1 ] ,
2007-03-16 21:20:31 +01:00
& cow_buf ) ;
2007-03-02 22:08:05 +01:00
b = cow_buf ;
2007-04-02 16:50:19 +02:00
c = btrfs_buffer_node ( b ) ;
2007-03-02 22:08:05 +01:00
}
BUG_ON ( ! cow & & ins_len ) ;
2007-04-02 16:50:19 +02:00
if ( level ! = btrfs_header_level ( & c - > header ) )
WARN_ON ( 1 ) ;
level = btrfs_header_level ( & c - > header ) ;
2007-02-02 15:18:22 +01:00
p - > nodes [ level ] = b ;
2007-03-14 19:14:43 +01:00
ret = check_block ( root , p , level ) ;
2007-02-28 22:35:06 +01:00
if ( ret )
return - 1 ;
2007-01-26 21:51:26 +01:00
ret = bin_search ( c , key , & slot ) ;
2007-03-12 17:01:18 +01:00
if ( ! btrfs_is_leaf ( c ) ) {
2007-01-26 21:51:26 +01:00
if ( ret & & slot > 0 )
slot - = 1 ;
p - > slots [ level ] = slot ;
2007-04-04 20:08:15 +02:00
if ( ins_len > 0 & & btrfs_header_nritems ( & c - > header ) > =
BTRFS_NODEPTRS_PER_BLOCK ( root ) - 1 ) {
2007-03-16 21:20:31 +01:00
int sret = split_node ( trans , root , p , level ) ;
2007-02-22 17:39:13 +01:00
BUG_ON ( sret > 0 ) ;
if ( sret )
return sret ;
b = p - > nodes [ level ] ;
2007-03-22 17:13:20 +01:00
c = btrfs_buffer_node ( b ) ;
2007-02-22 17:39:13 +01:00
slot = p - > slots [ level ] ;
2007-03-01 18:04:21 +01:00
} else if ( ins_len < 0 ) {
2007-03-16 21:20:31 +01:00
int sret = balance_level ( trans , root , p ,
level ) ;
2007-03-01 18:04:21 +01:00
if ( sret )
return sret ;
b = p - > nodes [ level ] ;
if ( ! b )
goto again ;
2007-03-22 17:13:20 +01:00
c = btrfs_buffer_node ( b ) ;
2007-03-01 18:04:21 +01:00
slot = p - > slots [ level ] ;
2007-03-12 17:01:18 +01:00
BUG_ON ( btrfs_header_nritems ( & c - > header ) = = 1 ) ;
2007-02-22 17:39:13 +01:00
}
2007-03-13 14:28:32 +01:00
b = read_tree_block ( root , btrfs_node_blockptr ( c , slot ) ) ;
2007-01-26 21:51:26 +01:00
} else {
2007-03-13 15:46:10 +01:00
struct btrfs_leaf * l = ( struct btrfs_leaf * ) c ;
2007-01-26 21:51:26 +01:00
p - > slots [ level ] = slot ;
2007-03-14 19:14:43 +01:00
if ( ins_len > 0 & & btrfs_leaf_free_space ( root , l ) <
2007-03-13 01:12:07 +01:00
sizeof ( struct btrfs_item ) + ins_len ) {
2007-04-04 20:08:15 +02:00
int sret = split_leaf ( trans , root , key ,
p , ins_len ) ;
2007-02-22 17:39:13 +01:00
BUG_ON ( sret > 0 ) ;
if ( sret )
return sret ;
}
2007-01-26 21:51:26 +01:00
return ret ;
}
}
2007-02-28 22:35:06 +01:00
return 1 ;
2007-01-26 21:51:26 +01:00
}
2007-02-02 17:05:29 +01:00
/*
* adjust the pointers going up the tree , starting at level
* making sure the right key of each node is points to ' key ' .
* This is used after shifting pointers to the left , so it stops
* fixing up pointers when a given leaf / node is not in slot 0 of the
* higher levels
2007-02-28 22:35:06 +01:00
*
* If this fails to write a tree block , it returns - 1 , but continues
* fixing up the blocks in ram so the tree is consistent .
2007-02-02 17:05:29 +01:00
*/
2007-03-16 21:20:31 +01:00
static int fixup_low_keys ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , struct btrfs_disk_key
* key , int level )
2007-01-26 21:51:26 +01:00
{
int i ;
2007-02-28 22:35:06 +01:00
int ret = 0 ;
2007-03-13 15:46:10 +01:00
for ( i = level ; i < BTRFS_MAX_LEVEL ; i + + ) {
struct btrfs_node * t ;
2007-01-26 21:51:26 +01:00
int tslot = path - > slots [ i ] ;
2007-02-02 15:18:22 +01:00
if ( ! path - > nodes [ i ] )
2007-01-26 21:51:26 +01:00
break ;
2007-03-22 17:13:20 +01:00
t = btrfs_buffer_node ( path - > nodes [ i ] ) ;
2007-03-30 20:27:56 +02:00
btrfs_memcpy ( root , t , & t - > ptrs [ tslot ] . key , key , sizeof ( * key ) ) ;
btrfs_mark_buffer_dirty ( path - > nodes [ i ] ) ;
2007-01-26 21:51:26 +01:00
if ( tslot ! = 0 )
break ;
}
2007-02-28 22:35:06 +01:00
return ret ;
2007-01-26 21:51:26 +01:00
}
2007-02-02 17:05:29 +01:00
/*
* try to push data from one node into the next node left in the
2007-03-01 21:16:26 +01:00
* tree .
2007-02-28 22:35:06 +01:00
*
* returns 0 if some ptrs were pushed left , < 0 if there was some horrible
* error , and > 0 if there was no room in the left hand block .
2007-02-02 17:05:29 +01:00
*/
2007-03-16 21:20:31 +01:00
static int push_node_left ( struct btrfs_trans_handle * trans , struct btrfs_root
2007-03-22 17:13:20 +01:00
* root , struct buffer_head * dst_buf , struct
buffer_head * src_buf )
2007-01-26 21:51:26 +01:00
{
2007-03-22 17:13:20 +01:00
struct btrfs_node * src = btrfs_buffer_node ( src_buf ) ;
struct btrfs_node * dst = btrfs_buffer_node ( dst_buf ) ;
2007-01-26 21:51:26 +01:00
int push_items = 0 ;
2007-03-01 18:04:21 +01:00
int src_nritems ;
int dst_nritems ;
2007-02-28 22:35:06 +01:00
int ret = 0 ;
2007-01-26 21:51:26 +01:00
2007-03-12 17:01:18 +01:00
src_nritems = btrfs_header_nritems ( & src - > header ) ;
dst_nritems = btrfs_header_nritems ( & dst - > header ) ;
2007-03-14 19:14:43 +01:00
push_items = BTRFS_NODEPTRS_PER_BLOCK ( root ) - dst_nritems ;
2007-02-02 15:18:22 +01:00
if ( push_items < = 0 ) {
2007-01-26 21:51:26 +01:00
return 1 ;
2007-02-02 15:18:22 +01:00
}
2007-01-26 21:51:26 +01:00
2007-03-01 18:04:21 +01:00
if ( src_nritems < push_items )
2007-03-01 21:16:26 +01:00
push_items = src_nritems ;
2007-03-30 20:27:56 +02:00
btrfs_memcpy ( root , dst , dst - > ptrs + dst_nritems , src - > ptrs ,
push_items * sizeof ( struct btrfs_key_ptr ) ) ;
2007-03-01 18:04:21 +01:00
if ( push_items < src_nritems ) {
2007-03-30 20:27:56 +02:00
btrfs_memmove ( root , src , src - > ptrs , src - > ptrs + push_items ,
2007-03-12 21:22:34 +01:00
( src_nritems - push_items ) *
2007-03-14 19:14:43 +01:00
sizeof ( struct btrfs_key_ptr ) ) ;
2007-03-01 18:04:21 +01:00
}
2007-03-12 17:01:18 +01:00
btrfs_set_header_nritems ( & src - > header , src_nritems - push_items ) ;
btrfs_set_header_nritems ( & dst - > header , dst_nritems + push_items ) ;
2007-03-30 20:27:56 +02:00
btrfs_mark_buffer_dirty ( src_buf ) ;
btrfs_mark_buffer_dirty ( dst_buf ) ;
2007-03-01 21:16:26 +01:00
return ret ;
}
/*
* try to push data from one node into the next node right in the
* tree .
*
* returns 0 if some ptrs were pushed , < 0 if there was some horrible
* error , and > 0 if there was no room in the right hand block .
*
* this will only push up to 1 / 2 the contents of the left node over
*/
2007-03-16 21:20:31 +01:00
static int balance_node_right ( struct btrfs_trans_handle * trans , struct
2007-03-22 17:13:20 +01:00
btrfs_root * root , struct buffer_head * dst_buf ,
struct buffer_head * src_buf )
2007-03-01 21:16:26 +01:00
{
2007-03-22 17:13:20 +01:00
struct btrfs_node * src = btrfs_buffer_node ( src_buf ) ;
struct btrfs_node * dst = btrfs_buffer_node ( dst_buf ) ;
2007-03-01 21:16:26 +01:00
int push_items = 0 ;
int max_push ;
int src_nritems ;
int dst_nritems ;
int ret = 0 ;
2007-03-12 17:01:18 +01:00
src_nritems = btrfs_header_nritems ( & src - > header ) ;
dst_nritems = btrfs_header_nritems ( & dst - > header ) ;
2007-03-14 19:14:43 +01:00
push_items = BTRFS_NODEPTRS_PER_BLOCK ( root ) - dst_nritems ;
2007-03-01 21:16:26 +01:00
if ( push_items < = 0 ) {
return 1 ;
}
max_push = src_nritems / 2 + 1 ;
/* don't try to empty the node */
if ( max_push > src_nritems )
return 1 ;
if ( max_push < push_items )
push_items = max_push ;
2007-03-30 20:27:56 +02:00
btrfs_memmove ( root , dst , dst - > ptrs + push_items , dst - > ptrs ,
dst_nritems * sizeof ( struct btrfs_key_ptr ) ) ;
btrfs_memcpy ( root , dst , dst - > ptrs ,
src - > ptrs + src_nritems - push_items ,
push_items * sizeof ( struct btrfs_key_ptr ) ) ;
2007-03-01 21:16:26 +01:00
2007-03-12 17:01:18 +01:00
btrfs_set_header_nritems ( & src - > header , src_nritems - push_items ) ;
btrfs_set_header_nritems ( & dst - > header , dst_nritems + push_items ) ;
2007-03-01 21:16:26 +01:00
2007-03-30 20:27:56 +02:00
btrfs_mark_buffer_dirty ( src_buf ) ;
btrfs_mark_buffer_dirty ( dst_buf ) ;
2007-02-28 22:35:06 +01:00
return ret ;
2007-01-26 21:51:26 +01:00
}
2007-02-24 19:39:08 +01:00
/*
* helper function to insert a new root level in the tree .
* A new node is allocated , and a single item is inserted to
* point to the existing root
2007-02-28 22:35:06 +01:00
*
* returns zero on success or < 0 on failure .
2007-02-24 19:39:08 +01:00
*/
2007-03-16 21:20:31 +01:00
static int insert_new_root ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , int level )
2007-02-22 17:39:13 +01:00
{
2007-03-22 17:13:20 +01:00
struct buffer_head * t ;
2007-03-13 15:46:10 +01:00
struct btrfs_node * lower ;
struct btrfs_node * c ;
2007-03-12 21:22:34 +01:00
struct btrfs_disk_key * lower_key ;
2007-02-22 17:39:13 +01:00
BUG_ON ( path - > nodes [ level ] ) ;
BUG_ON ( path - > nodes [ level - 1 ] ! = root - > node ) ;
2007-03-16 21:20:31 +01:00
t = btrfs_alloc_free_block ( trans , root ) ;
2007-03-22 17:13:20 +01:00
c = btrfs_buffer_node ( t ) ;
2007-03-14 19:14:43 +01:00
memset ( c , 0 , root - > blocksize ) ;
2007-03-12 17:01:18 +01:00
btrfs_set_header_nritems ( & c - > header , 1 ) ;
btrfs_set_header_level ( & c - > header , level ) ;
2007-04-11 21:53:25 +02:00
btrfs_set_header_blocknr ( & c - > header , bh_blocknr ( t ) ) ;
2007-03-23 20:56:19 +01:00
btrfs_set_header_generation ( & c - > header , trans - > transid ) ;
2007-03-22 17:13:20 +01:00
lower = btrfs_buffer_node ( path - > nodes [ level - 1 ] ) ;
2007-04-05 20:28:50 +02:00
memcpy ( c - > header . fsid , root - > fs_info - > disk_super - > fsid ,
sizeof ( c - > header . fsid ) ) ;
2007-03-12 17:01:18 +01:00
if ( btrfs_is_leaf ( lower ) )
2007-03-13 15:46:10 +01:00
lower_key = & ( ( struct btrfs_leaf * ) lower ) - > items [ 0 ] . key ;
2007-02-22 17:39:13 +01:00
else
2007-03-14 19:14:43 +01:00
lower_key = & lower - > ptrs [ 0 ] . key ;
2007-03-30 20:27:56 +02:00
btrfs_memcpy ( root , c , & c - > ptrs [ 0 ] . key , lower_key ,
sizeof ( struct btrfs_disk_key ) ) ;
2007-04-11 21:53:25 +02:00
btrfs_set_node_blockptr ( c , 0 , bh_blocknr ( path - > nodes [ level - 1 ] ) ) ;
2007-03-23 15:01:08 +01:00
2007-03-30 20:27:56 +02:00
btrfs_mark_buffer_dirty ( t ) ;
2007-03-23 15:01:08 +01:00
2007-02-22 17:39:13 +01:00
/* the super has an extra ref to root->node */
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , root - > node ) ;
2007-02-22 17:39:13 +01:00
root - > node = t ;
2007-03-22 17:13:20 +01:00
get_bh ( t ) ;
2007-02-22 17:39:13 +01:00
path - > nodes [ level ] = t ;
path - > slots [ level ] = 0 ;
return 0 ;
}
2007-02-02 17:05:29 +01:00
/*
* worker function to insert a single pointer in a node .
* the node should have enough room for the pointer already
2007-02-24 19:39:08 +01:00
*
2007-02-02 17:05:29 +01:00
* slot and level indicate where you want the key to go , and
* blocknr is the block the key points to .
2007-02-28 22:35:06 +01:00
*
* returns zero on success and < 0 on any error
2007-02-02 17:05:29 +01:00
*/
2007-03-16 21:20:31 +01:00
static int insert_ptr ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , struct btrfs_disk_key
* key , u64 blocknr , int slot , int level )
2007-02-02 17:05:29 +01:00
{
2007-03-13 15:46:10 +01:00
struct btrfs_node * lower ;
2007-02-02 17:05:29 +01:00
int nritems ;
2007-02-22 17:39:13 +01:00
BUG_ON ( ! path - > nodes [ level ] ) ;
2007-03-22 17:13:20 +01:00
lower = btrfs_buffer_node ( path - > nodes [ level ] ) ;
2007-03-12 17:01:18 +01:00
nritems = btrfs_header_nritems ( & lower - > header ) ;
2007-02-02 17:05:29 +01:00
if ( slot > nritems )
BUG ( ) ;
2007-03-14 19:14:43 +01:00
if ( nritems = = BTRFS_NODEPTRS_PER_BLOCK ( root ) )
2007-02-02 17:05:29 +01:00
BUG ( ) ;
if ( slot ! = nritems ) {
2007-03-30 20:27:56 +02:00
btrfs_memmove ( root , lower , lower - > ptrs + slot + 1 ,
lower - > ptrs + slot ,
( nritems - slot ) * sizeof ( struct btrfs_key_ptr ) ) ;
2007-02-02 17:05:29 +01:00
}
2007-03-30 20:27:56 +02:00
btrfs_memcpy ( root , lower , & lower - > ptrs [ slot ] . key ,
key , sizeof ( struct btrfs_disk_key ) ) ;
2007-03-13 14:28:32 +01:00
btrfs_set_node_blockptr ( lower , slot , blocknr ) ;
2007-03-12 17:01:18 +01:00
btrfs_set_header_nritems ( & lower - > header , nritems + 1 ) ;
2007-03-30 20:27:56 +02:00
btrfs_mark_buffer_dirty ( path - > nodes [ level ] ) ;
2007-02-02 17:05:29 +01:00
return 0 ;
}
2007-02-24 19:39:08 +01:00
/*
* split the node at the specified level in path in two .
* The path is corrected to point to the appropriate node after the split
*
* Before splitting this tries to make some room in the node by pushing
* left and right , if either one works , it returns right away .
2007-02-28 22:35:06 +01:00
*
* returns 0 on success and < 0 on failure
2007-02-24 19:39:08 +01:00
*/
2007-03-16 21:20:31 +01:00
static int split_node ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , int level )
2007-01-26 21:51:26 +01:00
{
2007-03-22 17:13:20 +01:00
struct buffer_head * t ;
2007-03-13 15:46:10 +01:00
struct btrfs_node * c ;
2007-03-22 17:13:20 +01:00
struct buffer_head * split_buffer ;
2007-03-13 15:46:10 +01:00
struct btrfs_node * split ;
2007-01-26 21:51:26 +01:00
int mid ;
2007-02-22 17:39:13 +01:00
int ret ;
2007-02-28 22:35:06 +01:00
int wret ;
2007-03-12 17:01:18 +01:00
u32 c_nritems ;
2007-02-02 15:18:22 +01:00
2007-02-22 17:39:13 +01:00
t = path - > nodes [ level ] ;
2007-03-22 17:13:20 +01:00
c = btrfs_buffer_node ( t ) ;
2007-02-22 17:39:13 +01:00
if ( t = = root - > node ) {
/* trying to split the root, lets make a new one */
2007-03-16 21:20:31 +01:00
ret = insert_new_root ( trans , root , path , level + 1 ) ;
2007-02-22 17:39:13 +01:00
if ( ret )
return ret ;
2007-04-20 19:16:02 +02:00
} else {
ret = push_nodes_for_insert ( trans , root , path , level ) ;
t = path - > nodes [ level ] ;
c = btrfs_buffer_node ( t ) ;
if ( ! ret & &
btrfs_header_nritems ( & c - > header ) <
BTRFS_NODEPTRS_PER_BLOCK ( root ) - 1 )
return 0 ;
2007-01-26 21:51:26 +01:00
}
2007-04-20 19:16:02 +02:00
2007-03-12 17:01:18 +01:00
c_nritems = btrfs_header_nritems ( & c - > header ) ;
2007-03-16 21:20:31 +01:00
split_buffer = btrfs_alloc_free_block ( trans , root ) ;
2007-03-22 17:13:20 +01:00
split = btrfs_buffer_node ( split_buffer ) ;
2007-03-12 17:01:18 +01:00
btrfs_set_header_flags ( & split - > header , btrfs_header_flags ( & c - > header ) ) ;
2007-03-27 15:06:38 +02:00
btrfs_set_header_level ( & split - > header , btrfs_header_level ( & c - > header ) ) ;
2007-04-11 21:53:25 +02:00
btrfs_set_header_blocknr ( & split - > header , bh_blocknr ( split_buffer ) ) ;
2007-03-23 20:56:19 +01:00
btrfs_set_header_generation ( & split - > header , trans - > transid ) ;
2007-04-05 20:28:50 +02:00
memcpy ( split - > header . fsid , root - > fs_info - > disk_super - > fsid ,
sizeof ( split - > header . fsid ) ) ;
2007-03-12 17:01:18 +01:00
mid = ( c_nritems + 1 ) / 2 ;
2007-03-30 20:27:56 +02:00
btrfs_memcpy ( root , split , split - > ptrs , c - > ptrs + mid ,
( c_nritems - mid ) * sizeof ( struct btrfs_key_ptr ) ) ;
2007-03-12 17:01:18 +01:00
btrfs_set_header_nritems ( & split - > header , c_nritems - mid ) ;
btrfs_set_header_nritems ( & c - > header , mid ) ;
2007-02-28 22:35:06 +01:00
ret = 0 ;
2007-03-30 20:27:56 +02:00
btrfs_mark_buffer_dirty ( t ) ;
btrfs_mark_buffer_dirty ( split_buffer ) ;
2007-03-16 21:20:31 +01:00
wret = insert_ptr ( trans , root , path , & split - > ptrs [ 0 ] . key ,
2007-04-11 21:53:25 +02:00
bh_blocknr ( split_buffer ) , path - > slots [ level + 1 ] + 1 ,
2007-03-14 19:14:43 +01:00
level + 1 ) ;
2007-02-28 22:35:06 +01:00
if ( wret )
ret = wret ;
2007-02-24 12:24:44 +01:00
if ( path - > slots [ level ] > = mid ) {
2007-02-22 17:39:13 +01:00
path - > slots [ level ] - = mid ;
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , t ) ;
2007-02-22 17:39:13 +01:00
path - > nodes [ level ] = split_buffer ;
path - > slots [ level + 1 ] + = 1 ;
} else {
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , split_buffer ) ;
2007-01-26 21:51:26 +01:00
}
2007-02-28 22:35:06 +01:00
return ret ;
2007-01-26 21:51:26 +01:00
}
2007-02-02 17:05:29 +01:00
/*
* how many bytes are required to store the items in a leaf . start
* and nr indicate which items in the leaf to check . This totals up the
* space used both by the item structs and the item data
*/
2007-03-13 15:46:10 +01:00
static int leaf_space_used ( struct btrfs_leaf * l , int start , int nr )
2007-01-26 21:51:26 +01:00
{
int data_len ;
2007-04-04 20:08:15 +02:00
int nritems = btrfs_header_nritems ( & l - > header ) ;
int end = min ( nritems , start + nr ) - 1 ;
2007-01-26 21:51:26 +01:00
if ( ! nr )
return 0 ;
2007-03-13 01:12:07 +01:00
data_len = btrfs_item_end ( l - > items + start ) ;
data_len = data_len - btrfs_item_offset ( l - > items + end ) ;
data_len + = sizeof ( struct btrfs_item ) * nr ;
2007-04-04 20:08:15 +02:00
WARN_ON ( data_len < 0 ) ;
2007-01-26 21:51:26 +01:00
return data_len ;
}
2007-04-04 20:08:15 +02:00
/*
* The space between the end of the leaf items and
* the start of the leaf data . IOW , how much room
* the leaf has left for both items and data
*/
int btrfs_leaf_free_space ( struct btrfs_root * root , struct btrfs_leaf * leaf )
{
int nritems = btrfs_header_nritems ( & leaf - > header ) ;
return BTRFS_LEAF_DATA_SIZE ( root ) - leaf_space_used ( leaf , 0 , nritems ) ;
}
2007-02-24 18:47:20 +01:00
/*
* push some data in the path leaf to the right , trying to free up at
* least data_size bytes . returns zero if the push worked , nonzero otherwise
2007-02-28 22:35:06 +01:00
*
* returns 1 if the push failed because the other node didn ' t have enough
* room , 0 if everything worked out and < 0 if there were major errors .
2007-02-24 18:47:20 +01:00
*/
2007-03-16 21:20:31 +01:00
static int push_leaf_right ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , int data_size )
2007-02-24 18:47:20 +01:00
{
2007-03-22 17:13:20 +01:00
struct buffer_head * left_buf = path - > nodes [ 0 ] ;
struct btrfs_leaf * left = btrfs_buffer_leaf ( left_buf ) ;
2007-03-13 15:46:10 +01:00
struct btrfs_leaf * right ;
2007-03-22 17:13:20 +01:00
struct buffer_head * right_buf ;
struct buffer_head * upper ;
struct btrfs_node * upper_node ;
2007-02-24 18:47:20 +01:00
int slot ;
int i ;
int free_space ;
int push_space = 0 ;
int push_items = 0 ;
2007-03-13 01:12:07 +01:00
struct btrfs_item * item ;
2007-03-12 17:01:18 +01:00
u32 left_nritems ;
u32 right_nritems ;
2007-02-24 18:47:20 +01:00
slot = path - > slots [ 1 ] ;
if ( ! path - > nodes [ 1 ] ) {
return 1 ;
}
upper = path - > nodes [ 1 ] ;
2007-03-22 17:13:20 +01:00
upper_node = btrfs_buffer_node ( upper ) ;
if ( slot > = btrfs_header_nritems ( & upper_node - > header ) - 1 ) {
2007-02-24 18:47:20 +01:00
return 1 ;
}
2007-03-22 17:13:20 +01:00
right_buf = read_tree_block ( root ,
btrfs_node_blockptr ( btrfs_buffer_node ( upper ) , slot + 1 ) ) ;
right = btrfs_buffer_leaf ( right_buf ) ;
2007-03-14 19:14:43 +01:00
free_space = btrfs_leaf_free_space ( root , right ) ;
2007-03-13 01:12:07 +01:00
if ( free_space < data_size + sizeof ( struct btrfs_item ) ) {
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , right_buf ) ;
2007-02-24 18:47:20 +01:00
return 1 ;
}
2007-03-02 22:08:05 +01:00
/* cow and double check */
2007-03-16 21:20:31 +01:00
btrfs_cow_block ( trans , root , right_buf , upper , slot + 1 , & right_buf ) ;
2007-03-22 17:13:20 +01:00
right = btrfs_buffer_leaf ( right_buf ) ;
2007-03-14 19:14:43 +01:00
free_space = btrfs_leaf_free_space ( root , right ) ;
2007-03-13 01:12:07 +01:00
if ( free_space < data_size + sizeof ( struct btrfs_item ) ) {
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , right_buf ) ;
2007-03-02 22:08:05 +01:00
return 1 ;
}
2007-03-12 17:01:18 +01:00
left_nritems = btrfs_header_nritems ( & left - > header ) ;
2007-04-18 22:15:28 +02:00
if ( left_nritems = = 0 ) {
btrfs_block_release ( root , right_buf ) ;
return 1 ;
}
for ( i = left_nritems - 1 ; i > = 1 ; i - - ) {
2007-02-24 18:47:20 +01:00
item = left - > items + i ;
if ( path - > slots [ 0 ] = = i )
push_space + = data_size + sizeof ( * item ) ;
2007-03-13 01:12:07 +01:00
if ( btrfs_item_size ( item ) + sizeof ( * item ) + push_space >
free_space )
2007-02-24 18:47:20 +01:00
break ;
push_items + + ;
2007-03-13 01:12:07 +01:00
push_space + = btrfs_item_size ( item ) + sizeof ( * item ) ;
2007-02-24 18:47:20 +01:00
}
if ( push_items = = 0 ) {
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , right_buf ) ;
2007-02-24 18:47:20 +01:00
return 1 ;
}
2007-04-18 22:15:28 +02:00
if ( push_items = = left_nritems )
WARN_ON ( 1 ) ;
2007-03-12 17:01:18 +01:00
right_nritems = btrfs_header_nritems ( & right - > header ) ;
2007-02-24 18:47:20 +01:00
/* push left to right */
2007-03-13 01:12:07 +01:00
push_space = btrfs_item_end ( left - > items + left_nritems - push_items ) ;
2007-03-14 19:14:43 +01:00
push_space - = leaf_data_end ( root , left ) ;
2007-02-24 18:47:20 +01:00
/* make room in the right data area */
2007-03-30 20:27:56 +02:00
btrfs_memmove ( root , right , btrfs_leaf_data ( right ) +
leaf_data_end ( root , right ) - push_space ,
btrfs_leaf_data ( right ) +
leaf_data_end ( root , right ) , BTRFS_LEAF_DATA_SIZE ( root ) -
leaf_data_end ( root , right ) ) ;
2007-02-24 18:47:20 +01:00
/* copy from the left data area */
2007-03-30 20:27:56 +02:00
btrfs_memcpy ( root , right , btrfs_leaf_data ( right ) +
BTRFS_LEAF_DATA_SIZE ( root ) - push_space ,
btrfs_leaf_data ( left ) + leaf_data_end ( root , left ) ,
push_space ) ;
btrfs_memmove ( root , right , right - > items + push_items , right - > items ,
2007-03-13 01:12:07 +01:00
right_nritems * sizeof ( struct btrfs_item ) ) ;
2007-02-24 18:47:20 +01:00
/* copy the items from left to right */
2007-03-30 20:27:56 +02:00
btrfs_memcpy ( root , right , right - > items , left - > items +
left_nritems - push_items ,
push_items * sizeof ( struct btrfs_item ) ) ;
2007-02-24 18:47:20 +01:00
/* update the item pointers */
2007-03-12 17:01:18 +01:00
right_nritems + = push_items ;
btrfs_set_header_nritems ( & right - > header , right_nritems ) ;
2007-03-14 19:14:43 +01:00
push_space = BTRFS_LEAF_DATA_SIZE ( root ) ;
2007-03-12 17:01:18 +01:00
for ( i = 0 ; i < right_nritems ; i + + ) {
2007-03-13 01:12:07 +01:00
btrfs_set_item_offset ( right - > items + i , push_space -
btrfs_item_size ( right - > items + i ) ) ;
push_space = btrfs_item_offset ( right - > items + i ) ;
2007-02-24 18:47:20 +01:00
}
2007-03-12 17:01:18 +01:00
left_nritems - = push_items ;
btrfs_set_header_nritems ( & left - > header , left_nritems ) ;
2007-02-24 18:47:20 +01:00
2007-03-30 20:27:56 +02:00
btrfs_mark_buffer_dirty ( left_buf ) ;
btrfs_mark_buffer_dirty ( right_buf ) ;
2007-04-18 22:15:28 +02:00
2007-03-30 20:27:56 +02:00
btrfs_memcpy ( root , upper_node , & upper_node - > ptrs [ slot + 1 ] . key ,
2007-03-12 21:22:34 +01:00
& right - > items [ 0 ] . key , sizeof ( struct btrfs_disk_key ) ) ;
2007-03-30 20:27:56 +02:00
btrfs_mark_buffer_dirty ( upper ) ;
2007-03-02 22:08:05 +01:00
2007-02-24 18:47:20 +01:00
/* then fixup the leaf pointer in the path */
2007-03-12 17:01:18 +01:00
if ( path - > slots [ 0 ] > = left_nritems ) {
path - > slots [ 0 ] - = left_nritems ;
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , path - > nodes [ 0 ] ) ;
2007-02-24 18:47:20 +01:00
path - > nodes [ 0 ] = right_buf ;
path - > slots [ 1 ] + = 1 ;
} else {
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , right_buf ) ;
2007-02-24 18:47:20 +01:00
}
return 0 ;
}
2007-02-02 17:05:29 +01:00
/*
* push some data in the path leaf to the left , trying to free up at
* least data_size bytes . returns zero if the push worked , nonzero otherwise
*/
2007-03-16 21:20:31 +01:00
static int push_leaf_left ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , int data_size )
2007-01-26 21:51:26 +01:00
{
2007-03-22 17:13:20 +01:00
struct buffer_head * right_buf = path - > nodes [ 0 ] ;
struct btrfs_leaf * right = btrfs_buffer_leaf ( right_buf ) ;
struct buffer_head * t ;
2007-03-13 15:46:10 +01:00
struct btrfs_leaf * left ;
2007-01-26 21:51:26 +01:00
int slot ;
int i ;
int free_space ;
int push_space = 0 ;
int push_items = 0 ;
2007-03-13 01:12:07 +01:00
struct btrfs_item * item ;
2007-03-12 17:01:18 +01:00
u32 old_left_nritems ;
2007-02-28 22:35:06 +01:00
int ret = 0 ;
int wret ;
2007-01-26 21:51:26 +01:00
slot = path - > slots [ 1 ] ;
if ( slot = = 0 ) {
return 1 ;
}
if ( ! path - > nodes [ 1 ] ) {
return 1 ;
}
2007-03-22 17:13:20 +01:00
t = read_tree_block ( root ,
btrfs_node_blockptr ( btrfs_buffer_node ( path - > nodes [ 1 ] ) , slot - 1 ) ) ;
left = btrfs_buffer_leaf ( t ) ;
2007-03-14 19:14:43 +01:00
free_space = btrfs_leaf_free_space ( root , left ) ;
2007-03-13 01:12:07 +01:00
if ( free_space < data_size + sizeof ( struct btrfs_item ) ) {
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , t ) ;
2007-01-26 21:51:26 +01:00
return 1 ;
}
2007-03-02 22:08:05 +01:00
/* cow and double check */
2007-03-16 21:20:31 +01:00
btrfs_cow_block ( trans , root , t , path - > nodes [ 1 ] , slot - 1 , & t ) ;
2007-03-22 17:13:20 +01:00
left = btrfs_buffer_leaf ( t ) ;
2007-03-14 19:14:43 +01:00
free_space = btrfs_leaf_free_space ( root , left ) ;
2007-03-13 01:12:07 +01:00
if ( free_space < data_size + sizeof ( struct btrfs_item ) ) {
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , t ) ;
2007-03-02 22:08:05 +01:00
return 1 ;
}
2007-04-18 22:15:28 +02:00
if ( btrfs_header_nritems ( & right - > header ) = = 0 ) {
btrfs_block_release ( root , t ) ;
return 1 ;
}
for ( i = 0 ; i < btrfs_header_nritems ( & right - > header ) - 1 ; i + + ) {
2007-01-26 21:51:26 +01:00
item = right - > items + i ;
if ( path - > slots [ 0 ] = = i )
push_space + = data_size + sizeof ( * item ) ;
2007-03-13 01:12:07 +01:00
if ( btrfs_item_size ( item ) + sizeof ( * item ) + push_space >
free_space )
2007-01-26 21:51:26 +01:00
break ;
push_items + + ;
2007-03-13 01:12:07 +01:00
push_space + = btrfs_item_size ( item ) + sizeof ( * item ) ;
2007-01-26 21:51:26 +01:00
}
if ( push_items = = 0 ) {
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , t ) ;
2007-01-26 21:51:26 +01:00
return 1 ;
}
2007-04-18 22:15:28 +02:00
if ( push_items = = btrfs_header_nritems ( & right - > header ) )
WARN_ON ( 1 ) ;
2007-01-26 21:51:26 +01:00
/* push data from right to left */
2007-03-30 20:27:56 +02:00
btrfs_memcpy ( root , left , left - > items +
btrfs_header_nritems ( & left - > header ) ,
right - > items , push_items * sizeof ( struct btrfs_item ) ) ;
2007-03-14 19:14:43 +01:00
push_space = BTRFS_LEAF_DATA_SIZE ( root ) -
2007-03-13 01:12:07 +01:00
btrfs_item_offset ( right - > items + push_items - 1 ) ;
2007-03-30 20:27:56 +02:00
btrfs_memcpy ( root , left , btrfs_leaf_data ( left ) +
leaf_data_end ( root , left ) - push_space ,
btrfs_leaf_data ( right ) +
btrfs_item_offset ( right - > items + push_items - 1 ) ,
push_space ) ;
2007-03-12 17:01:18 +01:00
old_left_nritems = btrfs_header_nritems ( & left - > header ) ;
2007-02-02 15:18:22 +01:00
BUG_ON ( old_left_nritems < 0 ) ;
2007-03-13 01:12:07 +01:00
for ( i = old_left_nritems ; i < old_left_nritems + push_items ; i + + ) {
2007-03-14 19:14:43 +01:00
u32 ioff = btrfs_item_offset ( left - > items + i ) ;
btrfs_set_item_offset ( left - > items + i , ioff -
( BTRFS_LEAF_DATA_SIZE ( root ) -
2007-03-13 01:12:07 +01:00
btrfs_item_offset ( left - > items +
old_left_nritems - 1 ) ) ) ;
2007-01-26 21:51:26 +01:00
}
2007-03-12 17:01:18 +01:00
btrfs_set_header_nritems ( & left - > header , old_left_nritems + push_items ) ;
2007-01-26 21:51:26 +01:00
/* fixup right node */
2007-03-13 01:12:07 +01:00
push_space = btrfs_item_offset ( right - > items + push_items - 1 ) -
2007-03-14 19:14:43 +01:00
leaf_data_end ( root , right ) ;
2007-03-30 20:27:56 +02:00
btrfs_memmove ( root , right , btrfs_leaf_data ( right ) +
BTRFS_LEAF_DATA_SIZE ( root ) - push_space ,
btrfs_leaf_data ( right ) +
leaf_data_end ( root , right ) , push_space ) ;
btrfs_memmove ( root , right , right - > items , right - > items + push_items ,
2007-03-12 17:01:18 +01:00
( btrfs_header_nritems ( & right - > header ) - push_items ) *
2007-03-13 01:12:07 +01:00
sizeof ( struct btrfs_item ) ) ;
2007-03-12 17:01:18 +01:00
btrfs_set_header_nritems ( & right - > header ,
btrfs_header_nritems ( & right - > header ) -
push_items ) ;
2007-03-14 19:14:43 +01:00
push_space = BTRFS_LEAF_DATA_SIZE ( root ) ;
2007-02-02 15:18:22 +01:00
2007-03-12 17:01:18 +01:00
for ( i = 0 ; i < btrfs_header_nritems ( & right - > header ) ; i + + ) {
2007-03-13 01:12:07 +01:00
btrfs_set_item_offset ( right - > items + i , push_space -
btrfs_item_size ( right - > items + i ) ) ;
push_space = btrfs_item_offset ( right - > items + i ) ;
2007-01-26 21:51:26 +01:00
}
2007-02-02 15:18:22 +01:00
2007-03-30 20:27:56 +02:00
btrfs_mark_buffer_dirty ( t ) ;
btrfs_mark_buffer_dirty ( right_buf ) ;
2007-03-16 21:20:31 +01:00
wret = fixup_low_keys ( trans , root , path , & right - > items [ 0 ] . key , 1 ) ;
2007-02-28 22:35:06 +01:00
if ( wret )
ret = wret ;
2007-01-26 21:51:26 +01:00
/* then fixup the leaf pointer in the path */
if ( path - > slots [ 0 ] < push_items ) {
path - > slots [ 0 ] + = old_left_nritems ;
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , path - > nodes [ 0 ] ) ;
2007-02-02 15:18:22 +01:00
path - > nodes [ 0 ] = t ;
2007-01-26 21:51:26 +01:00
path - > slots [ 1 ] - = 1 ;
} else {
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , t ) ;
2007-01-26 21:51:26 +01:00
path - > slots [ 0 ] - = push_items ;
}
2007-02-02 15:18:22 +01:00
BUG_ON ( path - > slots [ 0 ] < 0 ) ;
2007-02-28 22:35:06 +01:00
return ret ;
2007-01-26 21:51:26 +01:00
}
2007-02-02 17:05:29 +01:00
/*
* split the path ' s leaf in two , making sure there is at least data_size
* available for the resulting leaf level of the path .
2007-02-28 22:35:06 +01:00
*
* returns 0 if all went well and < 0 on failure .
2007-02-02 17:05:29 +01:00
*/
2007-03-16 21:20:31 +01:00
static int split_leaf ( struct btrfs_trans_handle * trans , struct btrfs_root
2007-04-04 20:08:15 +02:00
* root , struct btrfs_key * ins_key ,
struct btrfs_path * path , int data_size )
2007-01-26 21:51:26 +01:00
{
2007-03-22 17:13:20 +01:00
struct buffer_head * l_buf ;
2007-03-13 15:46:10 +01:00
struct btrfs_leaf * l ;
2007-03-12 17:01:18 +01:00
u32 nritems ;
2007-02-02 15:18:22 +01:00
int mid ;
int slot ;
2007-03-13 15:46:10 +01:00
struct btrfs_leaf * right ;
2007-03-22 17:13:20 +01:00
struct buffer_head * right_buffer ;
2007-03-13 01:12:07 +01:00
int space_needed = data_size + sizeof ( struct btrfs_item ) ;
2007-01-26 21:51:26 +01:00
int data_copy_size ;
int rt_data_off ;
int i ;
2007-04-04 20:08:15 +02:00
int ret = 0 ;
2007-02-28 22:35:06 +01:00
int wret ;
2007-04-04 20:08:15 +02:00
int double_split = 0 ;
struct btrfs_disk_key disk_key ;
2007-02-28 22:35:06 +01:00
2007-03-17 19:29:23 +01:00
/* first try to make some room by pushing left and right */
2007-03-16 21:20:31 +01:00
wret = push_leaf_left ( trans , root , path , data_size ) ;
2007-03-13 16:17:52 +01:00
if ( wret < 0 )
return wret ;
if ( wret ) {
2007-03-16 21:20:31 +01:00
wret = push_leaf_right ( trans , root , path , data_size ) ;
2007-03-13 16:17:52 +01:00
if ( wret < 0 )
return wret ;
}
2007-02-28 22:35:06 +01:00
l_buf = path - > nodes [ 0 ] ;
2007-03-22 17:13:20 +01:00
l = btrfs_buffer_leaf ( l_buf ) ;
2007-02-28 22:35:06 +01:00
/* did the pushes work? */
2007-03-14 19:14:43 +01:00
if ( btrfs_leaf_free_space ( root , l ) > =
sizeof ( struct btrfs_item ) + data_size )
2007-02-28 22:35:06 +01:00
return 0 ;
2007-02-22 17:39:13 +01:00
if ( ! path - > nodes [ 1 ] ) {
2007-03-16 21:20:31 +01:00
ret = insert_new_root ( trans , root , path , 1 ) ;
2007-02-22 17:39:13 +01:00
if ( ret )
return ret ;
}
2007-02-02 15:18:22 +01:00
slot = path - > slots [ 0 ] ;
2007-03-12 17:01:18 +01:00
nritems = btrfs_header_nritems ( & l - > header ) ;
2007-02-02 15:18:22 +01:00
mid = ( nritems + 1 ) / 2 ;
2007-03-16 21:20:31 +01:00
right_buffer = btrfs_alloc_free_block ( trans , root ) ;
2007-02-02 15:18:22 +01:00
BUG_ON ( ! right_buffer ) ;
2007-03-22 17:13:20 +01:00
right = btrfs_buffer_leaf ( right_buffer ) ;
2007-03-14 19:14:43 +01:00
memset ( & right - > header , 0 , sizeof ( right - > header ) ) ;
2007-04-11 21:53:25 +02:00
btrfs_set_header_blocknr ( & right - > header , bh_blocknr ( right_buffer ) ) ;
2007-03-23 20:56:19 +01:00
btrfs_set_header_generation ( & right - > header , trans - > transid ) ;
2007-03-12 17:01:18 +01:00
btrfs_set_header_level ( & right - > header , 0 ) ;
2007-04-05 20:28:50 +02:00
memcpy ( right - > header . fsid , root - > fs_info - > disk_super - > fsid ,
sizeof ( right - > header . fsid ) ) ;
2007-04-04 20:08:15 +02:00
if ( mid < = slot ) {
if ( nritems = = 1 | |
leaf_space_used ( l , mid , nritems - mid ) + space_needed >
BTRFS_LEAF_DATA_SIZE ( root ) ) {
if ( slot > = nritems ) {
btrfs_cpu_key_to_disk ( & disk_key , ins_key ) ;
btrfs_set_header_nritems ( & right - > header , 0 ) ;
wret = insert_ptr ( trans , root , path ,
& disk_key ,
2007-04-11 21:53:25 +02:00
bh_blocknr ( right_buffer ) ,
2007-04-04 20:08:15 +02:00
path - > slots [ 1 ] + 1 , 1 ) ;
if ( wret )
ret = wret ;
btrfs_block_release ( root , path - > nodes [ 0 ] ) ;
path - > nodes [ 0 ] = right_buffer ;
path - > slots [ 0 ] = 0 ;
path - > slots [ 1 ] + = 1 ;
return ret ;
}
mid = slot ;
double_split = 1 ;
}
} else {
if ( leaf_space_used ( l , 0 , mid + 1 ) + space_needed >
BTRFS_LEAF_DATA_SIZE ( root ) ) {
if ( slot = = 0 ) {
btrfs_cpu_key_to_disk ( & disk_key , ins_key ) ;
btrfs_set_header_nritems ( & right - > header , 0 ) ;
wret = insert_ptr ( trans , root , path ,
& disk_key ,
2007-04-11 21:53:25 +02:00
bh_blocknr ( right_buffer ) ,
2007-04-04 20:08:15 +02:00
path - > slots [ 1 ] - 1 , 1 ) ;
if ( wret )
ret = wret ;
btrfs_block_release ( root , path - > nodes [ 0 ] ) ;
path - > nodes [ 0 ] = right_buffer ;
path - > slots [ 0 ] = 0 ;
path - > slots [ 1 ] - = 1 ;
2007-04-18 22:15:28 +02:00
if ( path - > slots [ 1 ] = = 0 ) {
wret = fixup_low_keys ( trans , root ,
path , & disk_key , 1 ) ;
if ( wret )
ret = wret ;
}
2007-04-04 20:08:15 +02:00
return ret ;
}
mid = slot ;
double_split = 1 ;
}
}
btrfs_set_header_nritems ( & right - > header , nritems - mid ) ;
2007-03-14 19:14:43 +01:00
data_copy_size = btrfs_item_end ( l - > items + mid ) -
leaf_data_end ( root , l ) ;
2007-03-30 20:27:56 +02:00
btrfs_memcpy ( root , right , right - > items , l - > items + mid ,
( nritems - mid ) * sizeof ( struct btrfs_item ) ) ;
btrfs_memcpy ( root , right ,
btrfs_leaf_data ( right ) + BTRFS_LEAF_DATA_SIZE ( root ) -
data_copy_size , btrfs_leaf_data ( l ) +
leaf_data_end ( root , l ) , data_copy_size ) ;
2007-03-14 19:14:43 +01:00
rt_data_off = BTRFS_LEAF_DATA_SIZE ( root ) -
btrfs_item_end ( l - > items + mid ) ;
2007-02-02 17:05:29 +01:00
2007-03-13 01:12:07 +01:00
for ( i = 0 ; i < btrfs_header_nritems ( & right - > header ) ; i + + ) {
2007-03-14 19:14:43 +01:00
u32 ioff = btrfs_item_offset ( right - > items + i ) ;
2007-03-13 01:12:07 +01:00
btrfs_set_item_offset ( right - > items + i , ioff + rt_data_off ) ;
}
2007-02-02 17:05:29 +01:00
2007-03-12 17:01:18 +01:00
btrfs_set_header_nritems ( & l - > header , mid ) ;
2007-02-28 22:35:06 +01:00
ret = 0 ;
2007-03-16 21:20:31 +01:00
wret = insert_ptr ( trans , root , path , & right - > items [ 0 ] . key ,
2007-04-11 21:53:25 +02:00
bh_blocknr ( right_buffer ) , path - > slots [ 1 ] + 1 , 1 ) ;
2007-02-28 22:35:06 +01:00
if ( wret )
ret = wret ;
2007-03-30 20:27:56 +02:00
btrfs_mark_buffer_dirty ( right_buffer ) ;
btrfs_mark_buffer_dirty ( l_buf ) ;
2007-02-02 15:18:22 +01:00
BUG_ON ( path - > slots [ 0 ] ! = slot ) ;
2007-01-26 21:51:26 +01:00
if ( mid < = slot ) {
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , path - > nodes [ 0 ] ) ;
2007-02-02 15:18:22 +01:00
path - > nodes [ 0 ] = right_buffer ;
2007-01-26 21:51:26 +01:00
path - > slots [ 0 ] - = mid ;
path - > slots [ 1 ] + = 1 ;
2007-02-02 15:18:22 +01:00
} else
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , right_buffer ) ;
2007-02-02 15:18:22 +01:00
BUG_ON ( path - > slots [ 0 ] < 0 ) ;
2007-04-04 20:08:15 +02:00
if ( ! double_split )
return ret ;
right_buffer = btrfs_alloc_free_block ( trans , root ) ;
BUG_ON ( ! right_buffer ) ;
right = btrfs_buffer_leaf ( right_buffer ) ;
memset ( & right - > header , 0 , sizeof ( right - > header ) ) ;
2007-04-11 21:53:25 +02:00
btrfs_set_header_blocknr ( & right - > header , bh_blocknr ( right_buffer ) ) ;
2007-04-04 20:08:15 +02:00
btrfs_set_header_generation ( & right - > header , trans - > transid ) ;
btrfs_set_header_level ( & right - > header , 0 ) ;
2007-04-05 20:28:50 +02:00
memcpy ( right - > header . fsid , root - > fs_info - > disk_super - > fsid ,
sizeof ( right - > header . fsid ) ) ;
2007-04-04 20:08:15 +02:00
btrfs_cpu_key_to_disk ( & disk_key , ins_key ) ;
btrfs_set_header_nritems ( & right - > header , 0 ) ;
wret = insert_ptr ( trans , root , path ,
& disk_key ,
2007-04-11 21:53:25 +02:00
bh_blocknr ( right_buffer ) ,
2007-04-04 20:08:15 +02:00
path - > slots [ 1 ] , 1 ) ;
if ( wret )
ret = wret ;
2007-04-18 22:15:28 +02:00
if ( path - > slots [ 1 ] = = 0 ) {
wret = fixup_low_keys ( trans , root , path , & disk_key , 1 ) ;
if ( wret )
ret = wret ;
}
2007-04-04 20:08:15 +02:00
btrfs_block_release ( root , path - > nodes [ 0 ] ) ;
path - > nodes [ 0 ] = right_buffer ;
path - > slots [ 0 ] = 0 ;
check_node ( root , path , 1 ) ;
check_leaf ( root , path , 0 ) ;
2007-01-26 21:51:26 +01:00
return ret ;
}
2007-04-17 19:26:50 +02:00
int btrfs_truncate_item ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct btrfs_path * path ,
u32 new_size )
{
int ret = 0 ;
int slot ;
int slot_orig ;
struct btrfs_leaf * leaf ;
struct buffer_head * leaf_buf ;
u32 nritems ;
unsigned int data_end ;
unsigned int old_data_start ;
unsigned int old_size ;
unsigned int size_diff ;
int i ;
slot_orig = path - > slots [ 0 ] ;
leaf_buf = path - > nodes [ 0 ] ;
leaf = btrfs_buffer_leaf ( leaf_buf ) ;
nritems = btrfs_header_nritems ( & leaf - > header ) ;
data_end = leaf_data_end ( root , leaf ) ;
slot = path - > slots [ 0 ] ;
old_data_start = btrfs_item_offset ( leaf - > items + slot ) ;
old_size = btrfs_item_size ( leaf - > items + slot ) ;
BUG_ON ( old_size < = new_size ) ;
size_diff = old_size - new_size ;
BUG_ON ( slot < 0 ) ;
BUG_ON ( slot > = nritems ) ;
/*
* item0 . . itemN . . . dataN . offset . . dataN . size . . data0 . size
*/
/* first correct the data pointers */
for ( i = slot ; i < nritems ; i + + ) {
u32 ioff = btrfs_item_offset ( leaf - > items + i ) ;
btrfs_set_item_offset ( leaf - > items + i ,
ioff + size_diff ) ;
}
/* shift the data */
btrfs_memmove ( root , leaf , btrfs_leaf_data ( leaf ) +
data_end + size_diff , btrfs_leaf_data ( leaf ) +
data_end , old_data_start + new_size - data_end ) ;
btrfs_set_item_size ( leaf - > items + slot , new_size ) ;
btrfs_mark_buffer_dirty ( leaf_buf ) ;
ret = 0 ;
if ( btrfs_leaf_free_space ( root , leaf ) < 0 )
BUG ( ) ;
check_leaf ( root , path , 0 ) ;
return ret ;
}
2007-04-16 15:22:45 +02:00
int btrfs_extend_item ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , u32 data_size )
{
int ret = 0 ;
int slot ;
int slot_orig ;
struct btrfs_leaf * leaf ;
struct buffer_head * leaf_buf ;
u32 nritems ;
unsigned int data_end ;
unsigned int old_data ;
unsigned int old_size ;
int i ;
slot_orig = path - > slots [ 0 ] ;
leaf_buf = path - > nodes [ 0 ] ;
leaf = btrfs_buffer_leaf ( leaf_buf ) ;
nritems = btrfs_header_nritems ( & leaf - > header ) ;
data_end = leaf_data_end ( root , leaf ) ;
if ( btrfs_leaf_free_space ( root , leaf ) < data_size )
BUG ( ) ;
slot = path - > slots [ 0 ] ;
old_data = btrfs_item_end ( leaf - > items + slot ) ;
BUG_ON ( slot < 0 ) ;
BUG_ON ( slot > = nritems ) ;
/*
* item0 . . itemN . . . dataN . offset . . dataN . size . . data0 . size
*/
/* first correct the data pointers */
for ( i = slot ; i < nritems ; i + + ) {
u32 ioff = btrfs_item_offset ( leaf - > items + i ) ;
btrfs_set_item_offset ( leaf - > items + i ,
ioff - data_size ) ;
}
/* shift the data */
btrfs_memmove ( root , leaf , btrfs_leaf_data ( leaf ) +
data_end - data_size , btrfs_leaf_data ( leaf ) +
data_end , old_data - data_end ) ;
data_end = old_data ;
old_size = btrfs_item_size ( leaf - > items + slot ) ;
btrfs_set_item_size ( leaf - > items + slot , old_size + data_size ) ;
btrfs_mark_buffer_dirty ( leaf_buf ) ;
ret = 0 ;
if ( btrfs_leaf_free_space ( root , leaf ) < 0 )
BUG ( ) ;
check_leaf ( root , path , 0 ) ;
return ret ;
}
2007-02-02 17:05:29 +01:00
/*
* Given a key and some data , insert an item into the tree .
* This does all the path init required , making room in the tree if needed .
*/
2007-03-16 21:20:31 +01:00
int btrfs_insert_empty_item ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , struct btrfs_key
* cpu_key , u32 data_size )
2007-01-26 21:51:26 +01:00
{
2007-02-28 22:35:06 +01:00
int ret = 0 ;
2007-01-26 21:51:26 +01:00
int slot ;
2007-02-02 15:18:22 +01:00
int slot_orig ;
2007-03-13 15:46:10 +01:00
struct btrfs_leaf * leaf ;
2007-03-22 17:13:20 +01:00
struct buffer_head * leaf_buf ;
2007-03-12 17:01:18 +01:00
u32 nritems ;
2007-01-26 21:51:26 +01:00
unsigned int data_end ;
2007-03-12 21:22:34 +01:00
struct btrfs_disk_key disk_key ;
btrfs_cpu_key_to_disk ( & disk_key , cpu_key ) ;
2007-01-26 21:51:26 +01:00
2007-02-02 17:05:29 +01:00
/* create a root if there isn't one */
2007-02-22 17:39:13 +01:00
if ( ! root - > node )
2007-02-21 23:04:57 +01:00
BUG ( ) ;
2007-03-16 21:20:31 +01:00
ret = btrfs_search_slot ( trans , root , cpu_key , path , data_size , 1 ) ;
2007-02-02 15:18:22 +01:00
if ( ret = = 0 ) {
2007-03-02 15:47:58 +01:00
return - EEXIST ;
2007-02-28 22:35:06 +01:00
}
2007-03-02 00:59:40 +01:00
if ( ret < 0 )
goto out ;
2007-01-26 21:51:26 +01:00
2007-03-15 17:56:47 +01:00
slot_orig = path - > slots [ 0 ] ;
leaf_buf = path - > nodes [ 0 ] ;
2007-03-22 17:13:20 +01:00
leaf = btrfs_buffer_leaf ( leaf_buf ) ;
2007-02-02 17:05:29 +01:00
2007-03-12 17:01:18 +01:00
nritems = btrfs_header_nritems ( & leaf - > header ) ;
2007-03-14 19:14:43 +01:00
data_end = leaf_data_end ( root , leaf ) ;
2007-02-02 15:18:22 +01:00
2007-03-14 19:14:43 +01:00
if ( btrfs_leaf_free_space ( root , leaf ) <
2007-04-04 20:08:15 +02:00
sizeof ( struct btrfs_item ) + data_size ) {
2007-01-26 21:51:26 +01:00
BUG ( ) ;
2007-04-04 20:08:15 +02:00
}
2007-03-15 17:56:47 +01:00
slot = path - > slots [ 0 ] ;
2007-02-02 15:18:22 +01:00
BUG_ON ( slot < 0 ) ;
2007-01-26 21:51:26 +01:00
if ( slot ! = nritems ) {
int i ;
2007-03-13 01:12:07 +01:00
unsigned int old_data = btrfs_item_end ( leaf - > items + slot ) ;
2007-01-26 21:51:26 +01:00
/*
* item0 . . itemN . . . dataN . offset . . dataN . size . . data0 . size
*/
/* first correct the data pointers */
2007-03-13 01:12:07 +01:00
for ( i = slot ; i < nritems ; i + + ) {
2007-03-14 19:14:43 +01:00
u32 ioff = btrfs_item_offset ( leaf - > items + i ) ;
2007-03-13 01:12:07 +01:00
btrfs_set_item_offset ( leaf - > items + i ,
ioff - data_size ) ;
}
2007-01-26 21:51:26 +01:00
/* shift the items */
2007-03-30 20:27:56 +02:00
btrfs_memmove ( root , leaf , leaf - > items + slot + 1 ,
leaf - > items + slot ,
( nritems - slot ) * sizeof ( struct btrfs_item ) ) ;
2007-01-26 21:51:26 +01:00
/* shift the data */
2007-03-30 20:27:56 +02:00
btrfs_memmove ( root , leaf , btrfs_leaf_data ( leaf ) +
data_end - data_size , btrfs_leaf_data ( leaf ) +
data_end , old_data - data_end ) ;
2007-01-26 21:51:26 +01:00
data_end = old_data ;
}
2007-03-15 17:56:47 +01:00
/* setup the item for the new data */
2007-03-30 20:27:56 +02:00
btrfs_memcpy ( root , leaf , & leaf - > items [ slot ] . key , & disk_key ,
sizeof ( struct btrfs_disk_key ) ) ;
2007-03-13 01:12:07 +01:00
btrfs_set_item_offset ( leaf - > items + slot , data_end - data_size ) ;
btrfs_set_item_size ( leaf - > items + slot , data_size ) ;
2007-03-12 17:01:18 +01:00
btrfs_set_header_nritems ( & leaf - > header , nritems + 1 ) ;
2007-03-30 20:27:56 +02:00
btrfs_mark_buffer_dirty ( leaf_buf ) ;
2007-02-28 22:35:06 +01:00
ret = 0 ;
2007-02-28 15:27:02 +01:00
if ( slot = = 0 )
2007-03-16 21:20:31 +01:00
ret = fixup_low_keys ( trans , root , path , & disk_key , 1 ) ;
2007-02-28 22:35:06 +01:00
2007-03-14 19:14:43 +01:00
if ( btrfs_leaf_free_space ( root , leaf ) < 0 )
2007-01-26 21:51:26 +01:00
BUG ( ) ;
2007-03-15 17:56:47 +01:00
check_leaf ( root , path , 0 ) ;
2007-03-02 00:59:40 +01:00
out :
2007-03-15 17:56:47 +01:00
return ret ;
}
/*
* Given a key and some data , insert an item into the tree .
* This does all the path init required , making room in the tree if needed .
*/
2007-03-16 21:20:31 +01:00
int btrfs_insert_item ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_key * cpu_key , void * data , u32
data_size )
2007-03-15 17:56:47 +01:00
{
int ret = 0 ;
2007-04-02 16:50:19 +02:00
struct btrfs_path * path ;
2007-03-15 17:56:47 +01:00
u8 * ptr ;
2007-04-02 16:50:19 +02:00
path = btrfs_alloc_path ( ) ;
BUG_ON ( ! path ) ;
btrfs_init_path ( path ) ;
ret = btrfs_insert_empty_item ( trans , root , path , cpu_key , data_size ) ;
2007-03-15 17:56:47 +01:00
if ( ! ret ) {
2007-04-02 16:50:19 +02:00
ptr = btrfs_item_ptr ( btrfs_buffer_leaf ( path - > nodes [ 0 ] ) ,
path - > slots [ 0 ] , u8 ) ;
btrfs_memcpy ( root , path - > nodes [ 0 ] - > b_data ,
2007-03-30 20:27:56 +02:00
ptr , data , data_size ) ;
2007-04-02 16:50:19 +02:00
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
2007-03-15 17:56:47 +01:00
}
2007-04-02 16:50:19 +02:00
btrfs_release_path ( root , path ) ;
btrfs_free_path ( path ) ;
2007-02-28 22:35:06 +01:00
return ret ;
2007-01-26 21:51:26 +01:00
}
2007-02-02 17:05:29 +01:00
/*
2007-02-24 12:24:44 +01:00
* delete the pointer from a given node .
2007-02-02 17:05:29 +01:00
*
* If the delete empties a node , the node is removed from the tree ,
* continuing all the way the root if required . The root is converted into
* a leaf if all the nodes are emptied .
*/
2007-03-16 21:20:31 +01:00
static int del_ptr ( struct btrfs_trans_handle * trans , struct btrfs_root * root ,
struct btrfs_path * path , int level , int slot )
2007-01-26 21:51:26 +01:00
{
2007-03-13 15:46:10 +01:00
struct btrfs_node * node ;
2007-03-22 17:13:20 +01:00
struct buffer_head * parent = path - > nodes [ level ] ;
2007-03-12 17:01:18 +01:00
u32 nritems ;
2007-02-28 22:35:06 +01:00
int ret = 0 ;
2007-03-01 18:04:21 +01:00
int wret ;
2007-01-26 21:51:26 +01:00
2007-03-22 17:13:20 +01:00
node = btrfs_buffer_node ( parent ) ;
2007-03-12 17:01:18 +01:00
nritems = btrfs_header_nritems ( & node - > header ) ;
2007-03-01 18:04:21 +01:00
if ( slot ! = nritems - 1 ) {
2007-03-30 20:27:56 +02:00
btrfs_memmove ( root , node , node - > ptrs + slot ,
node - > ptrs + slot + 1 ,
sizeof ( struct btrfs_key_ptr ) *
( nritems - slot - 1 ) ) ;
2007-03-01 18:04:21 +01:00
}
2007-03-12 17:01:18 +01:00
nritems - - ;
btrfs_set_header_nritems ( & node - > header , nritems ) ;
if ( nritems = = 0 & & parent = = root - > node ) {
2007-03-22 17:13:20 +01:00
struct btrfs_header * header = btrfs_buffer_header ( root - > node ) ;
BUG_ON ( btrfs_header_level ( header ) ! = 1 ) ;
2007-03-01 18:04:21 +01:00
/* just turn the root into a leaf and break */
2007-03-22 17:13:20 +01:00
btrfs_set_header_level ( header , 0 ) ;
2007-03-01 18:04:21 +01:00
} else if ( slot = = 0 ) {
2007-03-16 21:20:31 +01:00
wret = fixup_low_keys ( trans , root , path , & node - > ptrs [ 0 ] . key ,
2007-03-14 19:14:43 +01:00
level + 1 ) ;
2007-02-28 22:46:22 +01:00
if ( wret )
ret = wret ;
2007-01-26 21:51:26 +01:00
}
2007-03-30 20:27:56 +02:00
btrfs_mark_buffer_dirty ( parent ) ;
2007-02-28 22:35:06 +01:00
return ret ;
2007-01-26 21:51:26 +01:00
}
2007-02-02 17:05:29 +01:00
/*
* delete the item at the leaf level in path . If that empties
* the leaf , remove it from the tree
*/
2007-03-16 21:20:31 +01:00
int btrfs_del_item ( struct btrfs_trans_handle * trans , struct btrfs_root * root ,
struct btrfs_path * path )
2007-01-26 21:51:26 +01:00
{
int slot ;
2007-03-13 15:46:10 +01:00
struct btrfs_leaf * leaf ;
2007-03-22 17:13:20 +01:00
struct buffer_head * leaf_buf ;
2007-01-26 21:51:26 +01:00
int doff ;
int dsize ;
2007-02-28 22:35:06 +01:00
int ret = 0 ;
int wret ;
2007-03-12 17:01:18 +01:00
u32 nritems ;
2007-01-26 21:51:26 +01:00
2007-02-02 15:18:22 +01:00
leaf_buf = path - > nodes [ 0 ] ;
2007-03-22 17:13:20 +01:00
leaf = btrfs_buffer_leaf ( leaf_buf ) ;
2007-01-26 22:38:42 +01:00
slot = path - > slots [ 0 ] ;
2007-03-13 01:12:07 +01:00
doff = btrfs_item_offset ( leaf - > items + slot ) ;
dsize = btrfs_item_size ( leaf - > items + slot ) ;
2007-03-12 17:01:18 +01:00
nritems = btrfs_header_nritems ( & leaf - > header ) ;
2007-01-26 21:51:26 +01:00
2007-03-12 17:01:18 +01:00
if ( slot ! = nritems - 1 ) {
2007-01-26 21:51:26 +01:00
int i ;
2007-03-14 19:14:43 +01:00
int data_end = leaf_data_end ( root , leaf ) ;
2007-03-30 20:27:56 +02:00
btrfs_memmove ( root , leaf , btrfs_leaf_data ( leaf ) +
data_end + dsize ,
btrfs_leaf_data ( leaf ) + data_end ,
doff - data_end ) ;
2007-03-13 01:12:07 +01:00
for ( i = slot + 1 ; i < nritems ; i + + ) {
2007-03-14 19:14:43 +01:00
u32 ioff = btrfs_item_offset ( leaf - > items + i ) ;
2007-03-13 01:12:07 +01:00
btrfs_set_item_offset ( leaf - > items + i , ioff + dsize ) ;
}
2007-03-30 20:27:56 +02:00
btrfs_memmove ( root , leaf , leaf - > items + slot ,
leaf - > items + slot + 1 ,
sizeof ( struct btrfs_item ) *
( nritems - slot - 1 ) ) ;
2007-01-26 21:51:26 +01:00
}
2007-03-12 17:01:18 +01:00
btrfs_set_header_nritems ( & leaf - > header , nritems - 1 ) ;
nritems - - ;
2007-02-02 17:05:29 +01:00
/* delete the leaf if we've emptied it */
2007-03-12 17:01:18 +01:00
if ( nritems = = 0 ) {
2007-02-02 15:18:22 +01:00
if ( leaf_buf = = root - > node ) {
2007-03-12 17:01:18 +01:00
btrfs_set_header_level ( & leaf - > header , 0 ) ;
2007-02-23 14:38:36 +01:00
} else {
2007-03-16 21:20:31 +01:00
clean_tree_block ( trans , root , leaf_buf ) ;
2007-03-30 20:27:56 +02:00
wait_on_buffer ( leaf_buf ) ;
2007-03-16 21:20:31 +01:00
wret = del_ptr ( trans , root , path , 1 , path - > slots [ 1 ] ) ;
2007-02-28 22:35:06 +01:00
if ( wret )
ret = wret ;
2007-03-16 21:20:31 +01:00
wret = btrfs_free_extent ( trans , root ,
2007-04-11 21:53:25 +02:00
bh_blocknr ( leaf_buf ) , 1 , 1 ) ;
2007-02-28 22:46:22 +01:00
if ( wret )
ret = wret ;
2007-02-23 14:38:36 +01:00
}
2007-01-26 21:51:26 +01:00
} else {
2007-03-12 17:01:18 +01:00
int used = leaf_space_used ( leaf , 0 , nritems ) ;
2007-02-28 22:35:06 +01:00
if ( slot = = 0 ) {
2007-03-16 21:20:31 +01:00
wret = fixup_low_keys ( trans , root , path ,
& leaf - > items [ 0 ] . key , 1 ) ;
2007-02-28 22:35:06 +01:00
if ( wret )
ret = wret ;
}
2007-02-02 17:05:29 +01:00
/* delete the leaf if it is mostly empty */
2007-03-14 19:14:43 +01:00
if ( used < BTRFS_LEAF_DATA_SIZE ( root ) / 3 ) {
2007-01-26 21:51:26 +01:00
/* push_leaf_left fixes the path.
* make sure the path still points to our leaf
* for possible call to del_ptr below
*/
2007-01-26 22:38:42 +01:00
slot = path - > slots [ 1 ] ;
2007-03-22 17:13:20 +01:00
get_bh ( leaf_buf ) ;
2007-03-16 21:20:31 +01:00
wret = push_leaf_left ( trans , root , path , 1 ) ;
2007-02-28 22:35:06 +01:00
if ( wret < 0 )
ret = wret ;
2007-03-02 15:47:58 +01:00
if ( path - > nodes [ 0 ] = = leaf_buf & &
2007-03-12 17:01:18 +01:00
btrfs_header_nritems ( & leaf - > header ) ) {
2007-03-16 21:20:31 +01:00
wret = push_leaf_right ( trans , root , path , 1 ) ;
2007-02-28 22:35:06 +01:00
if ( wret < 0 )
ret = wret ;
}
2007-03-12 17:01:18 +01:00
if ( btrfs_header_nritems ( & leaf - > header ) = = 0 ) {
2007-04-11 21:53:25 +02:00
u64 blocknr = bh_blocknr ( leaf_buf ) ;
2007-03-16 21:20:31 +01:00
clean_tree_block ( trans , root , leaf_buf ) ;
2007-03-30 20:27:56 +02:00
wait_on_buffer ( leaf_buf ) ;
2007-03-16 21:20:31 +01:00
wret = del_ptr ( trans , root , path , 1 , slot ) ;
2007-02-28 22:35:06 +01:00
if ( wret )
ret = wret ;
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , leaf_buf ) ;
2007-03-16 21:20:31 +01:00
wret = btrfs_free_extent ( trans , root , blocknr ,
1 , 1 ) ;
2007-02-28 22:46:22 +01:00
if ( wret )
ret = wret ;
2007-02-24 12:24:44 +01:00
} else {
2007-03-30 20:27:56 +02:00
btrfs_mark_buffer_dirty ( leaf_buf ) ;
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , leaf_buf ) ;
2007-01-26 21:51:26 +01:00
}
2007-03-23 15:01:08 +01:00
} else {
2007-03-30 20:27:56 +02:00
btrfs_mark_buffer_dirty ( leaf_buf ) ;
2007-01-26 21:51:26 +01:00
}
}
2007-02-28 22:35:06 +01:00
return ret ;
2007-01-26 21:51:26 +01:00
}
2007-02-24 19:39:08 +01:00
/*
* walk up the tree as far as required to find the next leaf .
2007-02-28 22:46:22 +01:00
* returns 0 if it found something or 1 if there are no greater leaves .
* returns < 0 on io errors .
2007-02-24 19:39:08 +01:00
*/
2007-03-13 15:46:10 +01:00
int btrfs_next_leaf ( struct btrfs_root * root , struct btrfs_path * path )
2007-02-20 22:40:44 +01:00
{
int slot ;
int level = 1 ;
u64 blocknr ;
2007-03-22 17:13:20 +01:00
struct buffer_head * c ;
struct btrfs_node * c_node ;
struct buffer_head * next = NULL ;
2007-02-20 22:40:44 +01:00
2007-03-13 15:46:10 +01:00
while ( level < BTRFS_MAX_LEVEL ) {
2007-02-20 22:40:44 +01:00
if ( ! path - > nodes [ level ] )
2007-02-28 22:46:22 +01:00
return 1 ;
2007-02-20 22:40:44 +01:00
slot = path - > slots [ level ] + 1 ;
c = path - > nodes [ level ] ;
2007-03-22 17:13:20 +01:00
c_node = btrfs_buffer_node ( c ) ;
if ( slot > = btrfs_header_nritems ( & c_node - > header ) ) {
2007-02-20 22:40:44 +01:00
level + + ;
continue ;
}
2007-03-22 17:13:20 +01:00
blocknr = btrfs_node_blockptr ( c_node , slot ) ;
2007-02-21 23:04:57 +01:00
if ( next )
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , next ) ;
2007-02-20 22:40:44 +01:00
next = read_tree_block ( root , blocknr ) ;
break ;
}
path - > slots [ level ] = slot ;
while ( 1 ) {
level - - ;
c = path - > nodes [ level ] ;
2007-03-13 15:46:10 +01:00
btrfs_block_release ( root , c ) ;
2007-02-20 22:40:44 +01:00
path - > nodes [ level ] = next ;
path - > slots [ level ] = 0 ;
if ( ! level )
break ;
2007-03-13 14:28:32 +01:00
next = read_tree_block ( root ,
2007-03-22 17:13:20 +01:00
btrfs_node_blockptr ( btrfs_buffer_node ( next ) , 0 ) ) ;
2007-02-20 22:40:44 +01:00
}
return 0 ;
}