2007-03-21 18:12:56 +03:00
# include <linux/module.h>
2007-02-02 17:18:22 +03:00
# include "ctree.h"
# include "disk-io.h"
2007-02-23 16:38:36 +03:00
2007-03-16 23:20:31 +03: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
* root , struct btrfs_path * path , int data_size ) ;
static int push_node_left ( struct btrfs_trans_handle * trans , struct btrfs_root
2007-03-22 19:13:20 +03:00
* root , struct buffer_head * dst , struct buffer_head
2007-03-16 23:20:31 +03:00
* src ) ;
static int balance_node_right ( struct btrfs_trans_handle * trans , struct
2007-03-22 19:13:20 +03:00
btrfs_root * root , struct buffer_head * dst_buf ,
struct buffer_head * src_buf ) ;
2007-03-16 23:20:31 +03:00
static int del_ptr ( struct btrfs_trans_handle * trans , struct btrfs_root * root ,
struct btrfs_path * path , int level , int slot ) ;
2007-02-21 00:40:44 +03:00
2007-03-13 17:46:10 +03:00
inline void btrfs_init_path ( struct btrfs_path * p )
2007-01-26 23:51:26 +03:00
{
memset ( p , 0 , sizeof ( * p ) ) ;
}
2007-03-13 17:46:10 +03:00
void btrfs_release_path ( struct btrfs_root * root , struct btrfs_path * p )
2007-02-02 17:18:22 +03:00
{
int i ;
2007-03-13 17:46:10 +03:00
for ( i = 0 ; i < BTRFS_MAX_LEVEL ; i + + ) {
2007-02-02 17:18:22 +03:00
if ( ! p - > nodes [ i ] )
break ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , p - > nodes [ i ] ) ;
2007-02-02 17:18:22 +03:00
}
2007-03-01 00:35:06 +03:00
memset ( p , 0 , sizeof ( * p ) ) ;
2007-02-02 17:18:22 +03:00
}
2007-03-16 23:20:31 +03:00
static int btrfs_cow_block ( struct btrfs_trans_handle * trans , struct btrfs_root
2007-03-22 19:13:20 +03:00
* root , struct buffer_head * buf , struct buffer_head
* parent , int parent_slot , struct buffer_head
2007-03-16 23:20:31 +03:00
* * cow_ret )
2007-03-03 00:08:05 +03:00
{
2007-03-22 19:13:20 +03:00
struct buffer_head * cow ;
struct btrfs_node * cow_node ;
2007-03-03 00:08:05 +03:00
2007-03-22 19:13:20 +03:00
if ( ! buffer_dirty ( buf ) ) {
2007-03-03 00:08:05 +03:00
* cow_ret = buf ;
return 0 ;
}
2007-03-16 23:20:31 +03:00
cow = btrfs_alloc_free_block ( trans , root ) ;
2007-03-22 19:13:20 +03:00
cow_node = btrfs_buffer_node ( cow ) ;
memcpy ( cow_node , btrfs_buffer_node ( buf ) , root - > blocksize ) ;
btrfs_set_header_blocknr ( & cow_node - > header , cow - > b_blocknr ) ;
2007-03-03 00:08:05 +03:00
* cow_ret = cow ;
2007-03-16 23:20:31 +03:00
btrfs_inc_ref ( trans , root , buf ) ;
2007-03-03 00:08:05 +03:00
if ( buf = = root - > node ) {
root - > node = cow ;
2007-03-22 19:13:20 +03:00
get_bh ( cow ) ;
2007-03-07 04:08:01 +03:00
if ( buf ! = root - > commit_root )
2007-03-22 19:13:20 +03:00
btrfs_free_extent ( trans , root , buf - > b_blocknr , 1 , 1 ) ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , buf ) ;
2007-03-03 00:08:05 +03:00
} else {
2007-03-22 19:13:20 +03:00
btrfs_set_node_blockptr ( btrfs_buffer_node ( parent ) , parent_slot ,
cow - > b_blocknr ) ;
BUG_ON ( ! buffer_dirty ( parent ) ) ;
btrfs_free_extent ( trans , root , buf - > b_blocknr , 1 , 1 ) ;
2007-03-03 00:08:05 +03:00
}
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , buf ) ;
2007-03-03 00:08:05 +03:00
return 0 ;
}
2007-02-02 19:05:29 +03: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 21:14:43 +03:00
static inline unsigned int leaf_data_end ( struct btrfs_root * root ,
struct btrfs_leaf * leaf )
2007-01-26 23:51:26 +03:00
{
2007-03-12 19:01:18 +03:00
u32 nr = btrfs_header_nritems ( & leaf - > header ) ;
2007-01-26 23:51:26 +03:00
if ( nr = = 0 )
2007-03-14 21:14:43 +03:00
return BTRFS_LEAF_DATA_SIZE ( root ) ;
2007-03-13 03:12:07 +03:00
return btrfs_item_offset ( leaf - > items + nr - 1 ) ;
2007-01-26 23:51:26 +03:00
}
2007-02-02 19:05:29 +03: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
*/
2007-03-14 21:14:43 +03:00
int btrfs_leaf_free_space ( struct btrfs_root * root , struct btrfs_leaf * leaf )
2007-01-26 23:51:26 +03:00
{
2007-03-14 21:14:43 +03:00
int data_end = leaf_data_end ( root , leaf ) ;
2007-03-12 19:01:18 +03:00
int nritems = btrfs_header_nritems ( & leaf - > header ) ;
2007-01-26 23:51:26 +03:00
char * items_end = ( char * ) ( leaf - > items + nritems + 1 ) ;
2007-03-14 21:14:43 +03:00
return ( char * ) ( btrfs_leaf_data ( leaf ) + data_end ) - ( char * ) items_end ;
2007-01-26 23:51:26 +03:00
}
2007-02-02 19:05:29 +03:00
/*
* compare two keys in a memcmp fashion
*/
2007-03-13 18:09:37 +03:00
static int comp_keys ( struct btrfs_disk_key * disk , struct btrfs_key * k2 )
2007-01-26 23:51:26 +03:00
{
2007-03-12 23:22:34 +03:00
struct btrfs_key k1 ;
btrfs_disk_key_to_cpu ( & k1 , disk ) ;
if ( k1 . objectid > k2 - > objectid )
2007-01-26 23:51:26 +03:00
return 1 ;
2007-03-12 23:22:34 +03:00
if ( k1 . objectid < k2 - > objectid )
2007-01-26 23:51:26 +03:00
return - 1 ;
2007-03-15 19:56:47 +03:00
if ( k1 . flags > k2 - > flags )
return 1 ;
if ( k1 . flags < k2 - > flags )
return - 1 ;
2007-03-16 15:46:49 +03:00
if ( k1 . offset > k2 - > offset )
return 1 ;
if ( k1 . offset < k2 - > offset )
return - 1 ;
2007-01-26 23:51:26 +03:00
return 0 ;
}
2007-02-02 19:05:29 +03:00
2007-03-14 21:14:43 +03:00
static int check_node ( struct btrfs_root * root , struct btrfs_path * path ,
int level )
2007-03-01 00:35:06 +03:00
{
int i ;
2007-03-13 17:46:10 +03:00
struct btrfs_node * parent = NULL ;
2007-03-22 19:13:20 +03:00
struct btrfs_node * node = btrfs_buffer_node ( path - > nodes [ level ] ) ;
2007-03-01 00:35:06 +03:00
int parent_slot ;
2007-03-12 19:01:18 +03:00
u32 nritems = btrfs_header_nritems ( & node - > header ) ;
2007-03-01 00:35:06 +03:00
if ( path - > nodes [ level + 1 ] )
2007-03-22 19:13:20 +03:00
parent = btrfs_buffer_node ( path - > nodes [ level + 1 ] ) ;
2007-03-01 00:35:06 +03:00
parent_slot = path - > slots [ level + 1 ] ;
2007-03-12 19:01:18 +03:00
BUG_ON ( nritems = = 0 ) ;
if ( parent ) {
2007-03-12 23:22:34 +03:00
struct btrfs_disk_key * parent_key ;
2007-03-14 21:14:43 +03:00
parent_key = & parent - > ptrs [ parent_slot ] . key ;
BUG_ON ( memcmp ( parent_key , & node - > ptrs [ 0 ] . key ,
2007-03-12 23:22:34 +03:00
sizeof ( struct btrfs_disk_key ) ) ) ;
2007-03-13 16:28:32 +03:00
BUG_ON ( btrfs_node_blockptr ( parent , parent_slot ) ! =
2007-03-12 19:01:18 +03:00
btrfs_header_blocknr ( & node - > header ) ) ;
2007-03-01 00:35:06 +03:00
}
2007-03-14 21:14:43 +03:00
BUG_ON ( nritems > BTRFS_NODEPTRS_PER_BLOCK ( root ) ) ;
2007-03-12 19:01:18 +03:00
for ( i = 0 ; nritems > 1 & & i < nritems - 2 ; i + + ) {
2007-03-12 23:22:34 +03:00
struct btrfs_key cpukey ;
2007-03-14 21:14:43 +03:00
btrfs_disk_key_to_cpu ( & cpukey , & node - > ptrs [ i + 1 ] . key ) ;
BUG_ON ( comp_keys ( & node - > ptrs [ i ] . key , & cpukey ) > = 0 ) ;
2007-03-01 00:35:06 +03:00
}
return 0 ;
}
2007-03-14 21:14:43 +03:00
static int check_leaf ( struct btrfs_root * root , struct btrfs_path * path ,
int level )
2007-03-01 00:35:06 +03:00
{
int i ;
2007-03-22 19:13:20 +03:00
struct btrfs_leaf * leaf = btrfs_buffer_leaf ( path - > nodes [ level ] ) ;
2007-03-13 17:46:10 +03:00
struct btrfs_node * parent = NULL ;
2007-03-01 00:35:06 +03:00
int parent_slot ;
2007-03-12 19:01:18 +03:00
u32 nritems = btrfs_header_nritems ( & leaf - > header ) ;
2007-03-01 00:35:06 +03:00
if ( path - > nodes [ level + 1 ] )
2007-03-22 19:13:20 +03:00
parent = btrfs_buffer_node ( path - > nodes [ level + 1 ] ) ;
2007-03-01 00:35:06 +03:00
parent_slot = path - > slots [ level + 1 ] ;
2007-03-14 21:14:43 +03:00
BUG_ON ( btrfs_leaf_free_space ( root , leaf ) < 0 ) ;
2007-03-12 19:01:18 +03:00
if ( nritems = = 0 )
return 0 ;
if ( parent ) {
2007-03-12 23:22:34 +03:00
struct btrfs_disk_key * parent_key ;
2007-03-14 21:14:43 +03:00
parent_key = & parent - > ptrs [ parent_slot ] . key ;
2007-03-01 00:35:06 +03:00
BUG_ON ( memcmp ( parent_key , & leaf - > items [ 0 ] . key ,
2007-03-12 23:22:34 +03:00
sizeof ( struct btrfs_disk_key ) ) ) ;
2007-03-13 16:28:32 +03:00
BUG_ON ( btrfs_node_blockptr ( parent , parent_slot ) ! =
2007-03-12 19:01:18 +03:00
btrfs_header_blocknr ( & leaf - > header ) ) ;
2007-03-01 00:35:06 +03:00
}
2007-03-12 19:01:18 +03:00
for ( i = 0 ; nritems > 1 & & i < nritems - 2 ; i + + ) {
2007-03-12 23:22:34 +03:00
struct btrfs_key cpukey ;
btrfs_disk_key_to_cpu ( & cpukey , & leaf - > items [ i + 1 ] . key ) ;
2007-03-01 00:35:06 +03:00
BUG_ON ( comp_keys ( & leaf - > items [ i ] . key ,
2007-03-12 23:22:34 +03:00
& cpukey ) > = 0 ) ;
2007-03-13 03:12:07 +03:00
BUG_ON ( btrfs_item_offset ( leaf - > items + i ) ! =
btrfs_item_end ( leaf - > items + i + 1 ) ) ;
2007-03-01 00:35:06 +03:00
if ( i = = 0 ) {
2007-03-13 03:12:07 +03:00
BUG_ON ( btrfs_item_offset ( leaf - > items + i ) +
btrfs_item_size ( leaf - > items + i ) ! =
2007-03-14 21:14:43 +03:00
BTRFS_LEAF_DATA_SIZE ( root ) ) ;
2007-03-01 00:35:06 +03:00
}
}
return 0 ;
}
2007-03-14 21:14:43 +03:00
static int check_block ( struct btrfs_root * root , struct btrfs_path * path ,
int level )
2007-03-01 00:35:06 +03:00
{
if ( level = = 0 )
2007-03-14 21:14:43 +03:00
return check_leaf ( root , path , level ) ;
return check_node ( root , path , level ) ;
2007-03-01 00:35:06 +03:00
}
2007-02-02 19:05:29 +03: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 18:09:37 +03:00
static int generic_bin_search ( char * p , int item_size , struct btrfs_key * key ,
2007-01-26 23:51:26 +03:00
int max , int * slot )
{
int low = 0 ;
int high = max ;
int mid ;
int ret ;
2007-03-12 23:22:34 +03:00
struct btrfs_disk_key * tmp ;
2007-01-26 23:51:26 +03:00
while ( low < high ) {
mid = ( low + high ) / 2 ;
2007-03-12 23:22:34 +03:00
tmp = ( struct btrfs_disk_key * ) ( p + mid * item_size ) ;
2007-01-26 23:51:26 +03: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 21:39:08 +03:00
/*
* simple bin_search frontend that does the right thing for
* leaves vs nodes
*/
2007-03-13 18:09:37 +03:00
static int bin_search ( struct btrfs_node * c , struct btrfs_key * key , int * slot )
2007-01-26 23:51:26 +03:00
{
2007-03-12 19:01:18 +03:00
if ( btrfs_is_leaf ( c ) ) {
2007-03-13 17:46:10 +03:00
struct btrfs_leaf * l = ( struct btrfs_leaf * ) c ;
2007-03-13 03:12:07 +03:00
return generic_bin_search ( ( void * ) l - > items ,
sizeof ( struct btrfs_item ) ,
2007-03-12 19:01:18 +03:00
key , btrfs_header_nritems ( & c - > header ) ,
slot ) ;
2007-01-26 23:51:26 +03:00
} else {
2007-03-14 21:14:43 +03:00
return generic_bin_search ( ( void * ) c - > ptrs ,
sizeof ( struct btrfs_key_ptr ) ,
2007-03-12 19:01:18 +03:00
key , btrfs_header_nritems ( & c - > header ) ,
slot ) ;
2007-01-26 23:51:26 +03:00
}
return - 1 ;
}
2007-03-22 19:13:20 +03:00
static struct buffer_head * read_node_slot ( struct btrfs_root * root ,
struct buffer_head * parent_buf ,
2007-03-01 20:04:21 +03:00
int slot )
{
2007-03-22 19:13:20 +03:00
struct btrfs_node * node = btrfs_buffer_node ( parent_buf ) ;
2007-03-01 20:04:21 +03:00
if ( slot < 0 )
return NULL ;
2007-03-12 19:01:18 +03:00
if ( slot > = btrfs_header_nritems ( & node - > header ) )
2007-03-01 20:04:21 +03:00
return NULL ;
2007-03-13 16:28:32 +03:00
return read_tree_block ( root , btrfs_node_blockptr ( node , slot ) ) ;
2007-03-01 20:04:21 +03:00
}
2007-03-16 23:20:31 +03:00
static int balance_level ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , int level )
2007-03-01 20:04:21 +03:00
{
2007-03-22 19:13:20 +03: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 17:46:10 +03:00
struct btrfs_node * right = NULL ;
struct btrfs_node * mid ;
struct btrfs_node * left = NULL ;
struct btrfs_node * parent = NULL ;
2007-03-01 20:04:21 +03:00
int ret = 0 ;
int wret ;
int pslot ;
int orig_slot = path - > slots [ level ] ;
2007-03-01 23:16:26 +03:00
u64 orig_ptr ;
2007-03-01 20:04:21 +03:00
if ( level = = 0 )
return 0 ;
mid_buf = path - > nodes [ level ] ;
2007-03-22 19:13:20 +03:00
mid = btrfs_buffer_node ( mid_buf ) ;
2007-03-13 16:28:32 +03:00
orig_ptr = btrfs_node_blockptr ( mid , orig_slot ) ;
2007-03-01 23:16:26 +03:00
2007-03-13 17:46:10 +03:00
if ( level < BTRFS_MAX_LEVEL - 1 )
2007-03-01 20:04:21 +03:00
parent_buf = path - > nodes [ level + 1 ] ;
pslot = path - > slots [ level + 1 ] ;
2007-03-17 21:29:23 +03: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 20:04:21 +03:00
if ( ! parent_buf ) {
2007-03-22 19:13:20 +03:00
struct buffer_head * child ;
u64 blocknr = mid_buf - > b_blocknr ;
2007-03-01 20:04:21 +03:00
2007-03-12 19:01:18 +03:00
if ( btrfs_header_nritems ( & mid - > header ) ! = 1 )
2007-03-01 20:04:21 +03: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 ;
/* once for the path */
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , mid_buf ) ;
2007-03-01 20:04:21 +03:00
/* once for the root ptr */
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , mid_buf ) ;
2007-03-16 23:20:31 +03:00
clean_tree_block ( trans , root , mid_buf ) ;
return btrfs_free_extent ( trans , root , blocknr , 1 , 1 ) ;
2007-03-01 20:04:21 +03:00
}
2007-03-22 19:13:20 +03:00
parent = btrfs_buffer_node ( parent_buf ) ;
2007-03-01 20:04:21 +03:00
2007-03-14 21:14:43 +03:00
if ( btrfs_header_nritems ( & mid - > header ) >
BTRFS_NODEPTRS_PER_BLOCK ( root ) / 4 )
2007-03-01 20:04:21 +03: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 23:16:26 +03:00
/* first, try to make some room in the middle buffer */
2007-03-01 20:04:21 +03:00
if ( left_buf ) {
2007-03-16 23:20:31 +03:00
btrfs_cow_block ( trans , root , left_buf , parent_buf , pslot - 1 ,
& left_buf ) ;
2007-03-22 19:13:20 +03:00
left = btrfs_buffer_node ( left_buf ) ;
2007-03-12 19:01:18 +03:00
orig_slot + = btrfs_header_nritems ( & left - > header ) ;
2007-03-16 23:20:31 +03:00
wret = push_node_left ( trans , root , left_buf , mid_buf ) ;
2007-03-01 23:16:26 +03:00
if ( wret < 0 )
ret = wret ;
2007-03-01 20:04:21 +03:00
}
2007-03-01 23:16:26 +03:00
/*
* then try to empty the right most buffer into the middle
*/
2007-03-01 20:04:21 +03:00
if ( right_buf ) {
2007-03-16 23:20:31 +03:00
btrfs_cow_block ( trans , root , right_buf , parent_buf , pslot + 1 ,
& right_buf ) ;
2007-03-22 19:13:20 +03:00
right = btrfs_buffer_node ( right_buf ) ;
2007-03-16 23:20:31 +03:00
wret = push_node_left ( trans , root , mid_buf , right_buf ) ;
2007-03-01 23:16:26 +03:00
if ( wret < 0 )
ret = wret ;
2007-03-12 19:01:18 +03:00
if ( btrfs_header_nritems ( & right - > header ) = = 0 ) {
2007-03-22 19:13:20 +03:00
u64 blocknr = right_buf - > b_blocknr ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , right_buf ) ;
2007-03-16 23:20:31 +03:00
clean_tree_block ( trans , root , right_buf ) ;
2007-03-01 20:04:21 +03:00
right_buf = NULL ;
right = NULL ;
2007-03-16 23:20:31 +03:00
wret = del_ptr ( trans , root , path , level + 1 , pslot +
1 ) ;
2007-03-01 20:04:21 +03:00
if ( wret )
ret = wret ;
2007-03-16 23:20:31 +03:00
wret = btrfs_free_extent ( trans , root , blocknr , 1 , 1 ) ;
2007-03-01 20:04:21 +03:00
if ( wret )
ret = wret ;
} else {
2007-03-14 21:14:43 +03:00
memcpy ( & parent - > ptrs [ pslot + 1 ] . key ,
& right - > ptrs [ 0 ] . key ,
2007-03-12 23:22:34 +03:00
sizeof ( struct btrfs_disk_key ) ) ;
2007-03-22 19:13:20 +03:00
BUG_ON ( ! buffer_dirty ( parent_buf ) ) ;
2007-03-01 20:04:21 +03:00
}
}
2007-03-12 19:01:18 +03:00
if ( btrfs_header_nritems ( & mid - > header ) = = 1 ) {
2007-03-01 23:16:26 +03: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 23:20:31 +03:00
wret = balance_node_right ( trans , root , mid_buf , left_buf ) ;
2007-03-01 23:16:26 +03:00
if ( wret < 0 )
ret = wret ;
BUG_ON ( wret = = 1 ) ;
}
2007-03-12 19:01:18 +03:00
if ( btrfs_header_nritems ( & mid - > header ) = = 0 ) {
2007-03-01 23:16:26 +03:00
/* we've managed to empty the middle node, drop it */
2007-03-22 19:13:20 +03:00
u64 blocknr = mid_buf - > b_blocknr ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , mid_buf ) ;
2007-03-16 23:20:31 +03:00
clean_tree_block ( trans , root , mid_buf ) ;
2007-03-01 20:04:21 +03:00
mid_buf = NULL ;
mid = NULL ;
2007-03-16 23:20:31 +03:00
wret = del_ptr ( trans , root , path , level + 1 , pslot ) ;
2007-03-01 20:04:21 +03:00
if ( wret )
ret = wret ;
2007-03-16 23:20:31 +03:00
wret = btrfs_free_extent ( trans , root , blocknr , 1 , 1 ) ;
2007-03-01 20:04:21 +03:00
if ( wret )
ret = wret ;
2007-03-01 23:16:26 +03:00
} else {
/* update the parent key to reflect our changes */
2007-03-14 21:14:43 +03:00
memcpy ( & parent - > ptrs [ pslot ] . key , & mid - > ptrs [ 0 ] . key ,
2007-03-12 23:22:34 +03:00
sizeof ( struct btrfs_disk_key ) ) ;
2007-03-22 19:13:20 +03:00
BUG_ON ( ! buffer_dirty ( parent_buf ) ) ;
2007-03-01 23:16:26 +03:00
}
2007-03-01 20:04:21 +03:00
2007-03-01 23:16:26 +03:00
/* update the path */
2007-03-01 20:04:21 +03:00
if ( left_buf ) {
2007-03-12 19:01:18 +03:00
if ( btrfs_header_nritems ( & left - > header ) > orig_slot ) {
2007-03-22 19:13:20 +03:00
get_bh ( left_buf ) ;
2007-03-01 20:04:21 +03:00
path - > nodes [ level ] = left_buf ;
path - > slots [ level + 1 ] - = 1 ;
path - > slots [ level ] = orig_slot ;
if ( mid_buf )
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , mid_buf ) ;
2007-03-01 20:04:21 +03:00
} else {
2007-03-12 19:01:18 +03:00
orig_slot - = btrfs_header_nritems ( & left - > header ) ;
2007-03-01 20:04:21 +03:00
path - > slots [ level ] = orig_slot ;
}
}
2007-03-01 23:16:26 +03:00
/* double check we haven't messed things up */
2007-03-14 21:14:43 +03:00
check_block ( root , path , level ) ;
2007-03-22 19:13:20 +03:00
if ( orig_ptr ! =
btrfs_node_blockptr ( btrfs_buffer_node ( path - > nodes [ level ] ) ,
path - > slots [ level ] ) )
2007-03-01 23:16:26 +03:00
BUG ( ) ;
2007-03-01 20:04:21 +03:00
if ( right_buf )
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , right_buf ) ;
2007-03-01 20:04:21 +03:00
if ( left_buf )
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , left_buf ) ;
2007-03-01 20:04:21 +03:00
return ret ;
}
2007-02-02 19:05:29 +03: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-03-01 00:35:06 +03:00
* be inserted , and 1 is returned . If there are other errors during the
* search a negative error number is returned .
2007-02-24 21:39:08 +03: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 19:05:29 +03:00
*/
2007-03-16 23:20:31 +03: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 23:51:26 +03:00
{
2007-03-22 19:13:20 +03:00
struct buffer_head * b ;
struct buffer_head * cow_buf ;
2007-03-13 17:46:10 +03:00
struct btrfs_node * c ;
2007-01-26 23:51:26 +03:00
int slot ;
int ret ;
int level ;
2007-02-22 19:39:13 +03:00
2007-03-01 20:04:21 +03:00
again :
b = root - > node ;
2007-03-22 19:13:20 +03:00
get_bh ( b ) ;
2007-02-02 17:18:22 +03:00
while ( b ) {
2007-03-22 19:13:20 +03:00
c = btrfs_buffer_node ( b ) ;
level = btrfs_header_level ( & c - > header ) ;
2007-03-03 00:08:05 +03:00
if ( cow ) {
int wret ;
2007-03-22 19:13:20 +03:00
wret = btrfs_cow_block ( trans , root , b ,
p - > nodes [ level + 1 ] ,
p - > slots [ level + 1 ] ,
2007-03-16 23:20:31 +03:00
& cow_buf ) ;
2007-03-03 00:08:05 +03:00
b = cow_buf ;
}
BUG_ON ( ! cow & & ins_len ) ;
2007-03-22 19:13:20 +03:00
c = btrfs_buffer_node ( b ) ;
2007-02-02 17:18:22 +03:00
p - > nodes [ level ] = b ;
2007-03-14 21:14:43 +03:00
ret = check_block ( root , p , level ) ;
2007-03-01 00:35:06 +03:00
if ( ret )
return - 1 ;
2007-01-26 23:51:26 +03:00
ret = bin_search ( c , key , & slot ) ;
2007-03-12 19:01:18 +03:00
if ( ! btrfs_is_leaf ( c ) ) {
2007-01-26 23:51:26 +03:00
if ( ret & & slot > 0 )
slot - = 1 ;
p - > slots [ level ] = slot ;
2007-03-12 19:01:18 +03:00
if ( ins_len > 0 & & btrfs_header_nritems ( & c - > header ) = =
2007-03-14 21:14:43 +03:00
BTRFS_NODEPTRS_PER_BLOCK ( root ) ) {
2007-03-16 23:20:31 +03:00
int sret = split_node ( trans , root , p , level ) ;
2007-02-22 19:39:13 +03:00
BUG_ON ( sret > 0 ) ;
if ( sret )
return sret ;
b = p - > nodes [ level ] ;
2007-03-22 19:13:20 +03:00
c = btrfs_buffer_node ( b ) ;
2007-02-22 19:39:13 +03:00
slot = p - > slots [ level ] ;
2007-03-01 20:04:21 +03:00
} else if ( ins_len < 0 ) {
2007-03-16 23:20:31 +03:00
int sret = balance_level ( trans , root , p ,
level ) ;
2007-03-01 20:04:21 +03:00
if ( sret )
return sret ;
b = p - > nodes [ level ] ;
if ( ! b )
goto again ;
2007-03-22 19:13:20 +03:00
c = btrfs_buffer_node ( b ) ;
2007-03-01 20:04:21 +03:00
slot = p - > slots [ level ] ;
2007-03-12 19:01:18 +03:00
BUG_ON ( btrfs_header_nritems ( & c - > header ) = = 1 ) ;
2007-02-22 19:39:13 +03:00
}
2007-03-13 16:28:32 +03:00
b = read_tree_block ( root , btrfs_node_blockptr ( c , slot ) ) ;
2007-01-26 23:51:26 +03:00
} else {
2007-03-13 17:46:10 +03:00
struct btrfs_leaf * l = ( struct btrfs_leaf * ) c ;
2007-01-26 23:51:26 +03:00
p - > slots [ level ] = slot ;
2007-03-14 21:14:43 +03:00
if ( ins_len > 0 & & btrfs_leaf_free_space ( root , l ) <
2007-03-13 03:12:07 +03:00
sizeof ( struct btrfs_item ) + ins_len ) {
2007-03-16 23:20:31 +03:00
int sret = split_leaf ( trans , root , p , ins_len ) ;
2007-02-22 19:39:13 +03:00
BUG_ON ( sret > 0 ) ;
if ( sret )
return sret ;
}
2007-01-26 23:51:26 +03:00
return ret ;
}
}
2007-03-01 00:35:06 +03:00
return 1 ;
2007-01-26 23:51:26 +03:00
}
2007-02-02 19:05:29 +03: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-03-01 00:35:06 +03: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 19:05:29 +03:00
*/
2007-03-16 23:20:31 +03: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 23:51:26 +03:00
{
int i ;
2007-03-01 00:35:06 +03:00
int ret = 0 ;
2007-03-13 17:46:10 +03:00
for ( i = level ; i < BTRFS_MAX_LEVEL ; i + + ) {
struct btrfs_node * t ;
2007-01-26 23:51:26 +03:00
int tslot = path - > slots [ i ] ;
2007-02-02 17:18:22 +03:00
if ( ! path - > nodes [ i ] )
2007-01-26 23:51:26 +03:00
break ;
2007-03-22 19:13:20 +03:00
t = btrfs_buffer_node ( path - > nodes [ i ] ) ;
2007-03-14 21:14:43 +03:00
memcpy ( & t - > ptrs [ tslot ] . key , key , sizeof ( * key ) ) ;
2007-03-22 19:13:20 +03:00
BUG_ON ( ! buffer_dirty ( path - > nodes [ i ] ) ) ;
2007-01-26 23:51:26 +03:00
if ( tslot ! = 0 )
break ;
}
2007-03-01 00:35:06 +03:00
return ret ;
2007-01-26 23:51:26 +03:00
}
2007-02-02 19:05:29 +03:00
/*
* try to push data from one node into the next node left in the
2007-03-01 23:16:26 +03:00
* tree .
2007-03-01 00:35:06 +03: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 19:05:29 +03:00
*/
2007-03-16 23:20:31 +03:00
static int push_node_left ( struct btrfs_trans_handle * trans , struct btrfs_root
2007-03-22 19:13:20 +03:00
* root , struct buffer_head * dst_buf , struct
buffer_head * src_buf )
2007-01-26 23:51:26 +03:00
{
2007-03-22 19:13:20 +03:00
struct btrfs_node * src = btrfs_buffer_node ( src_buf ) ;
struct btrfs_node * dst = btrfs_buffer_node ( dst_buf ) ;
2007-01-26 23:51:26 +03:00
int push_items = 0 ;
2007-03-01 20:04:21 +03:00
int src_nritems ;
int dst_nritems ;
2007-03-01 00:35:06 +03:00
int ret = 0 ;
2007-01-26 23:51:26 +03:00
2007-03-12 19:01:18 +03:00
src_nritems = btrfs_header_nritems ( & src - > header ) ;
dst_nritems = btrfs_header_nritems ( & dst - > header ) ;
2007-03-14 21:14:43 +03:00
push_items = BTRFS_NODEPTRS_PER_BLOCK ( root ) - dst_nritems ;
2007-02-02 17:18:22 +03:00
if ( push_items < = 0 ) {
2007-01-26 23:51:26 +03:00
return 1 ;
2007-02-02 17:18:22 +03:00
}
2007-01-26 23:51:26 +03:00
2007-03-01 20:04:21 +03:00
if ( src_nritems < push_items )
2007-03-01 23:16:26 +03:00
push_items = src_nritems ;
2007-03-14 21:14:43 +03:00
memcpy ( dst - > ptrs + dst_nritems , src - > ptrs ,
push_items * sizeof ( struct btrfs_key_ptr ) ) ;
2007-03-01 20:04:21 +03:00
if ( push_items < src_nritems ) {
2007-03-14 21:14:43 +03:00
memmove ( src - > ptrs , src - > ptrs + push_items ,
2007-03-12 23:22:34 +03:00
( src_nritems - push_items ) *
2007-03-14 21:14:43 +03:00
sizeof ( struct btrfs_key_ptr ) ) ;
2007-03-01 20:04:21 +03:00
}
2007-03-12 19:01:18 +03:00
btrfs_set_header_nritems ( & src - > header , src_nritems - push_items ) ;
btrfs_set_header_nritems ( & dst - > header , dst_nritems + push_items ) ;
2007-03-22 19:13:20 +03:00
BUG_ON ( ! buffer_dirty ( src_buf ) ) ;
BUG_ON ( ! buffer_dirty ( dst_buf ) ) ;
2007-03-01 23:16:26 +03: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 23:20:31 +03:00
static int balance_node_right ( struct btrfs_trans_handle * trans , struct
2007-03-22 19:13:20 +03:00
btrfs_root * root , struct buffer_head * dst_buf ,
struct buffer_head * src_buf )
2007-03-01 23:16:26 +03:00
{
2007-03-22 19:13:20 +03:00
struct btrfs_node * src = btrfs_buffer_node ( src_buf ) ;
struct btrfs_node * dst = btrfs_buffer_node ( dst_buf ) ;
2007-03-01 23:16:26 +03:00
int push_items = 0 ;
int max_push ;
int src_nritems ;
int dst_nritems ;
int ret = 0 ;
2007-03-12 19:01:18 +03:00
src_nritems = btrfs_header_nritems ( & src - > header ) ;
dst_nritems = btrfs_header_nritems ( & dst - > header ) ;
2007-03-14 21:14:43 +03:00
push_items = BTRFS_NODEPTRS_PER_BLOCK ( root ) - dst_nritems ;
2007-03-01 23:16:26 +03: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-14 21:14:43 +03:00
memmove ( dst - > ptrs + push_items , dst - > ptrs ,
dst_nritems * sizeof ( struct btrfs_key_ptr ) ) ;
memcpy ( dst - > ptrs , src - > ptrs + src_nritems - push_items ,
push_items * sizeof ( struct btrfs_key_ptr ) ) ;
2007-03-01 23:16:26 +03:00
2007-03-12 19:01:18 +03:00
btrfs_set_header_nritems ( & src - > header , src_nritems - push_items ) ;
btrfs_set_header_nritems ( & dst - > header , dst_nritems + push_items ) ;
2007-03-01 23:16:26 +03:00
2007-03-22 19:13:20 +03:00
BUG_ON ( ! buffer_dirty ( src_buf ) ) ;
BUG_ON ( ! buffer_dirty ( dst_buf ) ) ;
2007-03-01 00:35:06 +03:00
return ret ;
2007-01-26 23:51:26 +03:00
}
2007-02-24 21:39:08 +03: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-03-01 00:35:06 +03:00
*
* returns zero on success or < 0 on failure .
2007-02-24 21:39:08 +03:00
*/
2007-03-16 23:20:31 +03:00
static int insert_new_root ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , int level )
2007-02-22 19:39:13 +03:00
{
2007-03-22 19:13:20 +03:00
struct buffer_head * t ;
2007-03-13 17:46:10 +03:00
struct btrfs_node * lower ;
struct btrfs_node * c ;
2007-03-12 23:22:34 +03:00
struct btrfs_disk_key * lower_key ;
2007-02-22 19:39:13 +03:00
BUG_ON ( path - > nodes [ level ] ) ;
BUG_ON ( path - > nodes [ level - 1 ] ! = root - > node ) ;
2007-03-16 23:20:31 +03:00
t = btrfs_alloc_free_block ( trans , root ) ;
2007-03-22 19:13:20 +03:00
c = btrfs_buffer_node ( t ) ;
2007-03-14 21:14:43 +03:00
memset ( c , 0 , root - > blocksize ) ;
2007-03-12 19:01:18 +03:00
btrfs_set_header_nritems ( & c - > header , 1 ) ;
btrfs_set_header_level ( & c - > header , level ) ;
2007-03-22 19:13:20 +03:00
btrfs_set_header_blocknr ( & c - > header , t - > b_blocknr ) ;
2007-03-12 19:01:18 +03:00
btrfs_set_header_parentid ( & c - > header ,
2007-03-22 19:13:20 +03:00
btrfs_header_parentid ( btrfs_buffer_header ( root - > node ) ) ) ;
lower = btrfs_buffer_node ( path - > nodes [ level - 1 ] ) ;
2007-03-12 19:01:18 +03:00
if ( btrfs_is_leaf ( lower ) )
2007-03-13 17:46:10 +03:00
lower_key = & ( ( struct btrfs_leaf * ) lower ) - > items [ 0 ] . key ;
2007-02-22 19:39:13 +03:00
else
2007-03-14 21:14:43 +03:00
lower_key = & lower - > ptrs [ 0 ] . key ;
memcpy ( & c - > ptrs [ 0 ] . key , lower_key , sizeof ( struct btrfs_disk_key ) ) ;
2007-03-22 19:13:20 +03:00
btrfs_set_node_blockptr ( c , 0 , path - > nodes [ level - 1 ] - > b_blocknr ) ;
2007-02-22 19:39:13 +03:00
/* the super has an extra ref to root->node */
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , root - > node ) ;
2007-02-22 19:39:13 +03:00
root - > node = t ;
2007-03-22 19:13:20 +03:00
get_bh ( t ) ;
2007-02-22 19:39:13 +03:00
path - > nodes [ level ] = t ;
path - > slots [ level ] = 0 ;
return 0 ;
}
2007-02-02 19:05:29 +03:00
/*
* worker function to insert a single pointer in a node .
* the node should have enough room for the pointer already
2007-02-24 21:39:08 +03:00
*
2007-02-02 19:05:29 +03:00
* slot and level indicate where you want the key to go , and
* blocknr is the block the key points to .
2007-03-01 00:35:06 +03:00
*
* returns zero on success and < 0 on any error
2007-02-02 19:05:29 +03:00
*/
2007-03-16 23:20:31 +03: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 19:05:29 +03:00
{
2007-03-13 17:46:10 +03:00
struct btrfs_node * lower ;
2007-02-02 19:05:29 +03:00
int nritems ;
2007-02-22 19:39:13 +03:00
BUG_ON ( ! path - > nodes [ level ] ) ;
2007-03-22 19:13:20 +03:00
lower = btrfs_buffer_node ( path - > nodes [ level ] ) ;
2007-03-12 19:01:18 +03:00
nritems = btrfs_header_nritems ( & lower - > header ) ;
2007-02-02 19:05:29 +03:00
if ( slot > nritems )
BUG ( ) ;
2007-03-14 21:14:43 +03:00
if ( nritems = = BTRFS_NODEPTRS_PER_BLOCK ( root ) )
2007-02-02 19:05:29 +03:00
BUG ( ) ;
if ( slot ! = nritems ) {
2007-03-14 21:14:43 +03:00
memmove ( lower - > ptrs + slot + 1 , lower - > ptrs + slot ,
( nritems - slot ) * sizeof ( struct btrfs_key_ptr ) ) ;
2007-02-02 19:05:29 +03:00
}
2007-03-14 21:14:43 +03:00
memcpy ( & lower - > ptrs [ slot ] . key , key , sizeof ( struct btrfs_disk_key ) ) ;
2007-03-13 16:28:32 +03:00
btrfs_set_node_blockptr ( lower , slot , blocknr ) ;
2007-03-12 19:01:18 +03:00
btrfs_set_header_nritems ( & lower - > header , nritems + 1 ) ;
2007-03-22 19:13:20 +03:00
BUG_ON ( ! buffer_dirty ( path - > nodes [ level ] ) ) ;
2007-02-02 19:05:29 +03:00
return 0 ;
}
2007-02-24 21:39:08 +03: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-03-01 00:35:06 +03:00
*
* returns 0 on success and < 0 on failure
2007-02-24 21:39:08 +03:00
*/
2007-03-16 23:20:31 +03:00
static int split_node ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , int level )
2007-01-26 23:51:26 +03:00
{
2007-03-22 19:13:20 +03:00
struct buffer_head * t ;
2007-03-13 17:46:10 +03:00
struct btrfs_node * c ;
2007-03-22 19:13:20 +03:00
struct buffer_head * split_buffer ;
2007-03-13 17:46:10 +03:00
struct btrfs_node * split ;
2007-01-26 23:51:26 +03:00
int mid ;
2007-02-22 19:39:13 +03:00
int ret ;
2007-03-01 00:35:06 +03:00
int wret ;
2007-03-12 19:01:18 +03:00
u32 c_nritems ;
2007-02-02 17:18:22 +03:00
2007-02-22 19:39:13 +03:00
t = path - > nodes [ level ] ;
2007-03-22 19:13:20 +03:00
c = btrfs_buffer_node ( t ) ;
2007-02-22 19:39:13 +03:00
if ( t = = root - > node ) {
/* trying to split the root, lets make a new one */
2007-03-16 23:20:31 +03:00
ret = insert_new_root ( trans , root , path , level + 1 ) ;
2007-02-22 19:39:13 +03:00
if ( ret )
return ret ;
2007-01-26 23:51:26 +03:00
}
2007-03-12 19:01:18 +03:00
c_nritems = btrfs_header_nritems ( & c - > header ) ;
2007-03-16 23:20:31 +03:00
split_buffer = btrfs_alloc_free_block ( trans , root ) ;
2007-03-22 19:13:20 +03:00
split = btrfs_buffer_node ( split_buffer ) ;
2007-03-12 19:01:18 +03:00
btrfs_set_header_flags ( & split - > header , btrfs_header_flags ( & c - > header ) ) ;
2007-03-22 19:13:20 +03:00
btrfs_set_header_blocknr ( & split - > header , split_buffer - > b_blocknr ) ;
2007-03-12 19:01:18 +03:00
btrfs_set_header_parentid ( & split - > header ,
2007-03-22 19:13:20 +03:00
btrfs_header_parentid ( btrfs_buffer_header ( root - > node ) ) ) ;
2007-03-12 19:01:18 +03:00
mid = ( c_nritems + 1 ) / 2 ;
2007-03-14 21:14:43 +03:00
memcpy ( split - > ptrs , c - > ptrs + mid ,
( c_nritems - mid ) * sizeof ( struct btrfs_key_ptr ) ) ;
2007-03-12 19:01:18 +03:00
btrfs_set_header_nritems ( & split - > header , c_nritems - mid ) ;
btrfs_set_header_nritems ( & c - > header , mid ) ;
2007-03-01 00:35:06 +03:00
ret = 0 ;
2007-03-22 19:13:20 +03:00
BUG_ON ( ! buffer_dirty ( t ) ) ;
2007-03-16 23:20:31 +03:00
wret = insert_ptr ( trans , root , path , & split - > ptrs [ 0 ] . key ,
2007-03-22 19:13:20 +03:00
split_buffer - > b_blocknr , path - > slots [ level + 1 ] + 1 ,
2007-03-14 21:14:43 +03:00
level + 1 ) ;
2007-03-01 00:35:06 +03:00
if ( wret )
ret = wret ;
2007-02-24 14:24:44 +03:00
if ( path - > slots [ level ] > = mid ) {
2007-02-22 19:39:13 +03:00
path - > slots [ level ] - = mid ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , t ) ;
2007-02-22 19:39:13 +03:00
path - > nodes [ level ] = split_buffer ;
path - > slots [ level + 1 ] + = 1 ;
} else {
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , split_buffer ) ;
2007-01-26 23:51:26 +03:00
}
2007-03-01 00:35:06 +03:00
return ret ;
2007-01-26 23:51:26 +03:00
}
2007-02-02 19:05:29 +03: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 17:46:10 +03:00
static int leaf_space_used ( struct btrfs_leaf * l , int start , int nr )
2007-01-26 23:51:26 +03:00
{
int data_len ;
int end = start + nr - 1 ;
if ( ! nr )
return 0 ;
2007-03-13 03:12:07 +03: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-01-26 23:51:26 +03:00
return data_len ;
}
2007-02-24 20:47:20 +03: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-03-01 00:35:06 +03: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 20:47:20 +03:00
*/
2007-03-16 23:20:31 +03: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 20:47:20 +03:00
{
2007-03-22 19:13:20 +03:00
struct buffer_head * left_buf = path - > nodes [ 0 ] ;
struct btrfs_leaf * left = btrfs_buffer_leaf ( left_buf ) ;
2007-03-13 17:46:10 +03:00
struct btrfs_leaf * right ;
2007-03-22 19:13:20 +03:00
struct buffer_head * right_buf ;
struct buffer_head * upper ;
struct btrfs_node * upper_node ;
2007-02-24 20:47:20 +03:00
int slot ;
int i ;
int free_space ;
int push_space = 0 ;
int push_items = 0 ;
2007-03-13 03:12:07 +03:00
struct btrfs_item * item ;
2007-03-12 19:01:18 +03:00
u32 left_nritems ;
u32 right_nritems ;
2007-02-24 20:47:20 +03:00
slot = path - > slots [ 1 ] ;
if ( ! path - > nodes [ 1 ] ) {
return 1 ;
}
upper = path - > nodes [ 1 ] ;
2007-03-22 19:13:20 +03:00
upper_node = btrfs_buffer_node ( upper ) ;
if ( slot > = btrfs_header_nritems ( & upper_node - > header ) - 1 ) {
2007-02-24 20:47:20 +03:00
return 1 ;
}
2007-03-22 19:13:20 +03: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 21:14:43 +03:00
free_space = btrfs_leaf_free_space ( root , right ) ;
2007-03-13 03:12:07 +03:00
if ( free_space < data_size + sizeof ( struct btrfs_item ) ) {
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , right_buf ) ;
2007-02-24 20:47:20 +03:00
return 1 ;
}
2007-03-03 00:08:05 +03:00
/* cow and double check */
2007-03-16 23:20:31 +03:00
btrfs_cow_block ( trans , root , right_buf , upper , slot + 1 , & right_buf ) ;
2007-03-22 19:13:20 +03:00
right = btrfs_buffer_leaf ( right_buf ) ;
2007-03-14 21:14:43 +03:00
free_space = btrfs_leaf_free_space ( root , right ) ;
2007-03-13 03:12:07 +03:00
if ( free_space < data_size + sizeof ( struct btrfs_item ) ) {
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , right_buf ) ;
2007-03-03 00:08:05 +03:00
return 1 ;
}
2007-03-12 19:01:18 +03:00
left_nritems = btrfs_header_nritems ( & left - > header ) ;
for ( i = left_nritems - 1 ; i > = 0 ; i - - ) {
2007-02-24 20:47:20 +03:00
item = left - > items + i ;
if ( path - > slots [ 0 ] = = i )
push_space + = data_size + sizeof ( * item ) ;
2007-03-13 03:12:07 +03:00
if ( btrfs_item_size ( item ) + sizeof ( * item ) + push_space >
free_space )
2007-02-24 20:47:20 +03:00
break ;
push_items + + ;
2007-03-13 03:12:07 +03:00
push_space + = btrfs_item_size ( item ) + sizeof ( * item ) ;
2007-02-24 20:47:20 +03:00
}
if ( push_items = = 0 ) {
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , right_buf ) ;
2007-02-24 20:47:20 +03:00
return 1 ;
}
2007-03-12 19:01:18 +03:00
right_nritems = btrfs_header_nritems ( & right - > header ) ;
2007-02-24 20:47:20 +03:00
/* push left to right */
2007-03-13 03:12:07 +03:00
push_space = btrfs_item_end ( left - > items + left_nritems - push_items ) ;
2007-03-14 21:14:43 +03:00
push_space - = leaf_data_end ( root , left ) ;
2007-02-24 20:47:20 +03:00
/* make room in the right data area */
2007-03-14 21:14:43 +03:00
memmove ( 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 20:47:20 +03:00
/* copy from the left data area */
2007-03-14 21:14:43 +03:00
memcpy ( btrfs_leaf_data ( right ) + BTRFS_LEAF_DATA_SIZE ( root ) - push_space ,
btrfs_leaf_data ( left ) + leaf_data_end ( root , left ) , push_space ) ;
2007-02-24 20:47:20 +03:00
memmove ( right - > items + push_items , right - > items ,
2007-03-13 03:12:07 +03:00
right_nritems * sizeof ( struct btrfs_item ) ) ;
2007-02-24 20:47:20 +03:00
/* copy the items from left to right */
2007-03-12 19:01:18 +03:00
memcpy ( right - > items , left - > items + left_nritems - push_items ,
2007-03-13 03:12:07 +03:00
push_items * sizeof ( struct btrfs_item ) ) ;
2007-02-24 20:47:20 +03:00
/* update the item pointers */
2007-03-12 19:01:18 +03:00
right_nritems + = push_items ;
btrfs_set_header_nritems ( & right - > header , right_nritems ) ;
2007-03-14 21:14:43 +03:00
push_space = BTRFS_LEAF_DATA_SIZE ( root ) ;
2007-03-12 19:01:18 +03:00
for ( i = 0 ; i < right_nritems ; i + + ) {
2007-03-13 03:12:07 +03: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 20:47:20 +03:00
}
2007-03-12 19:01:18 +03:00
left_nritems - = push_items ;
btrfs_set_header_nritems ( & left - > header , left_nritems ) ;
2007-02-24 20:47:20 +03:00
2007-03-22 19:13:20 +03:00
BUG_ON ( ! buffer_dirty ( left_buf ) ) ;
BUG_ON ( ! buffer_dirty ( right_buf ) ) ;
memcpy ( & upper_node - > ptrs [ slot + 1 ] . key ,
2007-03-12 23:22:34 +03:00
& right - > items [ 0 ] . key , sizeof ( struct btrfs_disk_key ) ) ;
2007-03-22 19:13:20 +03:00
BUG_ON ( ! buffer_dirty ( upper ) ) ;
2007-03-03 00:08:05 +03:00
2007-02-24 20:47:20 +03:00
/* then fixup the leaf pointer in the path */
2007-03-12 19:01:18 +03:00
if ( path - > slots [ 0 ] > = left_nritems ) {
path - > slots [ 0 ] - = left_nritems ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , path - > nodes [ 0 ] ) ;
2007-02-24 20:47:20 +03:00
path - > nodes [ 0 ] = right_buf ;
path - > slots [ 1 ] + = 1 ;
} else {
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , right_buf ) ;
2007-02-24 20:47:20 +03:00
}
return 0 ;
}
2007-02-02 19:05:29 +03: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 23:20:31 +03: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 23:51:26 +03:00
{
2007-03-22 19:13:20 +03: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 17:46:10 +03:00
struct btrfs_leaf * left ;
2007-01-26 23:51:26 +03:00
int slot ;
int i ;
int free_space ;
int push_space = 0 ;
int push_items = 0 ;
2007-03-13 03:12:07 +03:00
struct btrfs_item * item ;
2007-03-12 19:01:18 +03:00
u32 old_left_nritems ;
2007-03-01 00:35:06 +03:00
int ret = 0 ;
int wret ;
2007-01-26 23:51:26 +03:00
slot = path - > slots [ 1 ] ;
if ( slot = = 0 ) {
return 1 ;
}
if ( ! path - > nodes [ 1 ] ) {
return 1 ;
}
2007-03-22 19:13:20 +03: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 21:14:43 +03:00
free_space = btrfs_leaf_free_space ( root , left ) ;
2007-03-13 03:12:07 +03:00
if ( free_space < data_size + sizeof ( struct btrfs_item ) ) {
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , t ) ;
2007-01-26 23:51:26 +03:00
return 1 ;
}
2007-03-03 00:08:05 +03:00
/* cow and double check */
2007-03-16 23:20:31 +03:00
btrfs_cow_block ( trans , root , t , path - > nodes [ 1 ] , slot - 1 , & t ) ;
2007-03-22 19:13:20 +03:00
left = btrfs_buffer_leaf ( t ) ;
2007-03-14 21:14:43 +03:00
free_space = btrfs_leaf_free_space ( root , left ) ;
2007-03-13 03:12:07 +03:00
if ( free_space < data_size + sizeof ( struct btrfs_item ) ) {
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , t ) ;
2007-03-03 00:08:05 +03:00
return 1 ;
}
2007-03-12 19:01:18 +03:00
for ( i = 0 ; i < btrfs_header_nritems ( & right - > header ) ; i + + ) {
2007-01-26 23:51:26 +03:00
item = right - > items + i ;
if ( path - > slots [ 0 ] = = i )
push_space + = data_size + sizeof ( * item ) ;
2007-03-13 03:12:07 +03:00
if ( btrfs_item_size ( item ) + sizeof ( * item ) + push_space >
free_space )
2007-01-26 23:51:26 +03:00
break ;
push_items + + ;
2007-03-13 03:12:07 +03:00
push_space + = btrfs_item_size ( item ) + sizeof ( * item ) ;
2007-01-26 23:51:26 +03:00
}
if ( push_items = = 0 ) {
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , t ) ;
2007-01-26 23:51:26 +03:00
return 1 ;
}
/* push data from right to left */
2007-03-12 19:01:18 +03:00
memcpy ( left - > items + btrfs_header_nritems ( & left - > header ) ,
2007-03-13 03:12:07 +03:00
right - > items , push_items * sizeof ( struct btrfs_item ) ) ;
2007-03-14 21:14:43 +03:00
push_space = BTRFS_LEAF_DATA_SIZE ( root ) -
2007-03-13 03:12:07 +03:00
btrfs_item_offset ( right - > items + push_items - 1 ) ;
2007-03-14 21:14:43 +03:00
memcpy ( btrfs_leaf_data ( left ) + leaf_data_end ( root , left ) - push_space ,
btrfs_leaf_data ( right ) +
btrfs_item_offset ( right - > items + push_items - 1 ) ,
2007-01-26 23:51:26 +03:00
push_space ) ;
2007-03-12 19:01:18 +03:00
old_left_nritems = btrfs_header_nritems ( & left - > header ) ;
2007-02-02 17:18:22 +03:00
BUG_ON ( old_left_nritems < 0 ) ;
2007-03-13 03:12:07 +03:00
for ( i = old_left_nritems ; i < old_left_nritems + push_items ; i + + ) {
2007-03-14 21:14:43 +03: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 03:12:07 +03:00
btrfs_item_offset ( left - > items +
old_left_nritems - 1 ) ) ) ;
2007-01-26 23:51:26 +03:00
}
2007-03-12 19:01:18 +03:00
btrfs_set_header_nritems ( & left - > header , old_left_nritems + push_items ) ;
2007-01-26 23:51:26 +03:00
/* fixup right node */
2007-03-13 03:12:07 +03:00
push_space = btrfs_item_offset ( right - > items + push_items - 1 ) -
2007-03-14 21:14:43 +03:00
leaf_data_end ( root , right ) ;
memmove ( btrfs_leaf_data ( right ) + BTRFS_LEAF_DATA_SIZE ( root ) -
push_space , btrfs_leaf_data ( right ) +
leaf_data_end ( root , right ) , push_space ) ;
2007-01-26 23:51:26 +03:00
memmove ( right - > items , right - > items + push_items ,
2007-03-12 19:01:18 +03:00
( btrfs_header_nritems ( & right - > header ) - push_items ) *
2007-03-13 03:12:07 +03:00
sizeof ( struct btrfs_item ) ) ;
2007-03-12 19:01:18 +03:00
btrfs_set_header_nritems ( & right - > header ,
btrfs_header_nritems ( & right - > header ) -
push_items ) ;
2007-03-14 21:14:43 +03:00
push_space = BTRFS_LEAF_DATA_SIZE ( root ) ;
2007-02-02 17:18:22 +03:00
2007-03-12 19:01:18 +03:00
for ( i = 0 ; i < btrfs_header_nritems ( & right - > header ) ; i + + ) {
2007-03-13 03:12:07 +03: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 23:51:26 +03:00
}
2007-02-02 17:18:22 +03:00
2007-03-22 19:13:20 +03:00
BUG_ON ( ! buffer_dirty ( t ) ) ;
BUG_ON ( ! buffer_dirty ( right_buf ) ) ;
2007-02-02 17:18:22 +03:00
2007-03-16 23:20:31 +03:00
wret = fixup_low_keys ( trans , root , path , & right - > items [ 0 ] . key , 1 ) ;
2007-03-01 00:35:06 +03:00
if ( wret )
ret = wret ;
2007-01-26 23:51:26 +03:00
/* then fixup the leaf pointer in the path */
if ( path - > slots [ 0 ] < push_items ) {
path - > slots [ 0 ] + = old_left_nritems ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , path - > nodes [ 0 ] ) ;
2007-02-02 17:18:22 +03:00
path - > nodes [ 0 ] = t ;
2007-01-26 23:51:26 +03:00
path - > slots [ 1 ] - = 1 ;
} else {
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , t ) ;
2007-01-26 23:51:26 +03:00
path - > slots [ 0 ] - = push_items ;
}
2007-02-02 17:18:22 +03:00
BUG_ON ( path - > slots [ 0 ] < 0 ) ;
2007-03-01 00:35:06 +03:00
return ret ;
2007-01-26 23:51:26 +03:00
}
2007-02-02 19:05:29 +03: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-03-01 00:35:06 +03:00
*
* returns 0 if all went well and < 0 on failure .
2007-02-02 19:05:29 +03:00
*/
2007-03-16 23:20:31 +03:00
static int split_leaf ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , int data_size )
2007-01-26 23:51:26 +03:00
{
2007-03-22 19:13:20 +03:00
struct buffer_head * l_buf ;
2007-03-13 17:46:10 +03:00
struct btrfs_leaf * l ;
2007-03-12 19:01:18 +03:00
u32 nritems ;
2007-02-02 17:18:22 +03:00
int mid ;
int slot ;
2007-03-13 17:46:10 +03:00
struct btrfs_leaf * right ;
2007-03-22 19:13:20 +03:00
struct buffer_head * right_buffer ;
2007-03-13 03:12:07 +03:00
int space_needed = data_size + sizeof ( struct btrfs_item ) ;
2007-01-26 23:51:26 +03:00
int data_copy_size ;
int rt_data_off ;
int i ;
int ret ;
2007-03-01 00:35:06 +03:00
int wret ;
2007-03-17 21:29:23 +03:00
/* first try to make some room by pushing left and right */
2007-03-16 23:20:31 +03:00
wret = push_leaf_left ( trans , root , path , data_size ) ;
2007-03-13 18:17:52 +03:00
if ( wret < 0 )
return wret ;
if ( wret ) {
2007-03-16 23:20:31 +03:00
wret = push_leaf_right ( trans , root , path , data_size ) ;
2007-03-13 18:17:52 +03:00
if ( wret < 0 )
return wret ;
}
2007-03-01 00:35:06 +03:00
l_buf = path - > nodes [ 0 ] ;
2007-03-22 19:13:20 +03:00
l = btrfs_buffer_leaf ( l_buf ) ;
2007-03-01 00:35:06 +03:00
/* did the pushes work? */
2007-03-14 21:14:43 +03:00
if ( btrfs_leaf_free_space ( root , l ) > =
sizeof ( struct btrfs_item ) + data_size )
2007-03-01 00:35:06 +03:00
return 0 ;
2007-02-22 19:39:13 +03:00
if ( ! path - > nodes [ 1 ] ) {
2007-03-16 23:20:31 +03:00
ret = insert_new_root ( trans , root , path , 1 ) ;
2007-02-22 19:39:13 +03:00
if ( ret )
return ret ;
}
2007-02-02 17:18:22 +03:00
slot = path - > slots [ 0 ] ;
2007-03-12 19:01:18 +03:00
nritems = btrfs_header_nritems ( & l - > header ) ;
2007-02-02 17:18:22 +03:00
mid = ( nritems + 1 ) / 2 ;
2007-03-16 23:20:31 +03:00
right_buffer = btrfs_alloc_free_block ( trans , root ) ;
2007-02-02 17:18:22 +03:00
BUG_ON ( ! right_buffer ) ;
BUG_ON ( mid = = nritems ) ;
2007-03-22 19:13:20 +03:00
right = btrfs_buffer_leaf ( right_buffer ) ;
2007-03-14 21:14:43 +03:00
memset ( & right - > header , 0 , sizeof ( right - > header ) ) ;
2007-01-26 23:51:26 +03:00
if ( mid < = slot ) {
2007-02-24 21:39:08 +03:00
/* FIXME, just alloc a new leaf here */
2007-01-26 23:51:26 +03:00
if ( leaf_space_used ( l , mid , nritems - mid ) + space_needed >
2007-03-14 21:14:43 +03:00
BTRFS_LEAF_DATA_SIZE ( root ) )
2007-01-26 23:51:26 +03:00
BUG ( ) ;
} else {
2007-02-24 21:39:08 +03:00
/* FIXME, just alloc a new leaf here */
2007-01-26 23:51:26 +03:00
if ( leaf_space_used ( l , 0 , mid + 1 ) + space_needed >
2007-03-14 21:14:43 +03:00
BTRFS_LEAF_DATA_SIZE ( root ) )
2007-01-26 23:51:26 +03:00
BUG ( ) ;
}
2007-03-12 19:01:18 +03:00
btrfs_set_header_nritems ( & right - > header , nritems - mid ) ;
2007-03-22 19:13:20 +03:00
btrfs_set_header_blocknr ( & right - > header , right_buffer - > b_blocknr ) ;
2007-03-12 19:01:18 +03:00
btrfs_set_header_level ( & right - > header , 0 ) ;
btrfs_set_header_parentid ( & right - > header ,
2007-03-22 19:13:20 +03:00
btrfs_header_parentid ( btrfs_buffer_header ( root - > node ) ) ) ;
2007-03-14 21:14:43 +03:00
data_copy_size = btrfs_item_end ( l - > items + mid ) -
leaf_data_end ( root , l ) ;
2007-01-26 23:51:26 +03:00
memcpy ( right - > items , l - > items + mid ,
2007-03-13 03:12:07 +03:00
( nritems - mid ) * sizeof ( struct btrfs_item ) ) ;
2007-03-14 21:14:43 +03:00
memcpy ( btrfs_leaf_data ( right ) + BTRFS_LEAF_DATA_SIZE ( root ) -
data_copy_size , btrfs_leaf_data ( l ) +
leaf_data_end ( root , l ) , data_copy_size ) ;
rt_data_off = BTRFS_LEAF_DATA_SIZE ( root ) -
btrfs_item_end ( l - > items + mid ) ;
2007-02-02 19:05:29 +03:00
2007-03-13 03:12:07 +03:00
for ( i = 0 ; i < btrfs_header_nritems ( & right - > header ) ; i + + ) {
2007-03-14 21:14:43 +03:00
u32 ioff = btrfs_item_offset ( right - > items + i ) ;
2007-03-13 03:12:07 +03:00
btrfs_set_item_offset ( right - > items + i , ioff + rt_data_off ) ;
}
2007-02-02 19:05:29 +03:00
2007-03-12 19:01:18 +03:00
btrfs_set_header_nritems ( & l - > header , mid ) ;
2007-03-01 00:35:06 +03:00
ret = 0 ;
2007-03-16 23:20:31 +03:00
wret = insert_ptr ( trans , root , path , & right - > items [ 0 ] . key ,
2007-03-22 19:13:20 +03:00
right_buffer - > b_blocknr , path - > slots [ 1 ] + 1 , 1 ) ;
2007-03-01 00:35:06 +03:00
if ( wret )
ret = wret ;
2007-03-22 19:13:20 +03:00
BUG_ON ( ! buffer_dirty ( right_buffer ) ) ;
BUG_ON ( ! buffer_dirty ( l_buf ) ) ;
2007-02-02 17:18:22 +03:00
BUG_ON ( path - > slots [ 0 ] ! = slot ) ;
2007-01-26 23:51:26 +03:00
if ( mid < = slot ) {
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , path - > nodes [ 0 ] ) ;
2007-02-02 17:18:22 +03:00
path - > nodes [ 0 ] = right_buffer ;
2007-01-26 23:51:26 +03:00
path - > slots [ 0 ] - = mid ;
path - > slots [ 1 ] + = 1 ;
2007-02-02 17:18:22 +03:00
} else
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , right_buffer ) ;
2007-02-02 17:18:22 +03:00
BUG_ON ( path - > slots [ 0 ] < 0 ) ;
2007-01-26 23:51:26 +03:00
return ret ;
}
2007-02-02 19:05:29 +03: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 23:20:31 +03: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 23:51:26 +03:00
{
2007-03-01 00:35:06 +03:00
int ret = 0 ;
2007-01-26 23:51:26 +03:00
int slot ;
2007-02-02 17:18:22 +03:00
int slot_orig ;
2007-03-13 17:46:10 +03:00
struct btrfs_leaf * leaf ;
2007-03-22 19:13:20 +03:00
struct buffer_head * leaf_buf ;
2007-03-12 19:01:18 +03:00
u32 nritems ;
2007-01-26 23:51:26 +03:00
unsigned int data_end ;
2007-03-12 23:22:34 +03:00
struct btrfs_disk_key disk_key ;
btrfs_cpu_key_to_disk ( & disk_key , cpu_key ) ;
2007-01-26 23:51:26 +03:00
2007-02-02 19:05:29 +03:00
/* create a root if there isn't one */
2007-02-22 19:39:13 +03:00
if ( ! root - > node )
2007-02-22 01:04:57 +03:00
BUG ( ) ;
2007-03-16 23:20:31 +03:00
ret = btrfs_search_slot ( trans , root , cpu_key , path , data_size , 1 ) ;
2007-02-02 17:18:22 +03:00
if ( ret = = 0 ) {
2007-03-15 19:56:47 +03:00
btrfs_release_path ( root , path ) ;
2007-03-02 17:47:58 +03:00
return - EEXIST ;
2007-03-01 00:35:06 +03:00
}
2007-03-02 02:59:40 +03:00
if ( ret < 0 )
goto out ;
2007-01-26 23:51:26 +03:00
2007-03-15 19:56:47 +03:00
slot_orig = path - > slots [ 0 ] ;
leaf_buf = path - > nodes [ 0 ] ;
2007-03-22 19:13:20 +03:00
leaf = btrfs_buffer_leaf ( leaf_buf ) ;
2007-02-02 19:05:29 +03:00
2007-03-12 19:01:18 +03:00
nritems = btrfs_header_nritems ( & leaf - > header ) ;
2007-03-14 21:14:43 +03:00
data_end = leaf_data_end ( root , leaf ) ;
2007-02-02 17:18:22 +03:00
2007-03-14 21:14:43 +03:00
if ( btrfs_leaf_free_space ( root , leaf ) <
2007-03-13 17:46:10 +03:00
sizeof ( struct btrfs_item ) + data_size )
2007-01-26 23:51:26 +03:00
BUG ( ) ;
2007-03-15 19:56:47 +03:00
slot = path - > slots [ 0 ] ;
2007-02-02 17:18:22 +03:00
BUG_ON ( slot < 0 ) ;
2007-01-26 23:51:26 +03:00
if ( slot ! = nritems ) {
int i ;
2007-03-13 03:12:07 +03:00
unsigned int old_data = btrfs_item_end ( leaf - > items + slot ) ;
2007-01-26 23:51:26 +03:00
/*
* item0 . . itemN . . . dataN . offset . . dataN . size . . data0 . size
*/
/* first correct the data pointers */
2007-03-13 03:12:07 +03:00
for ( i = slot ; i < nritems ; i + + ) {
2007-03-14 21:14:43 +03:00
u32 ioff = btrfs_item_offset ( leaf - > items + i ) ;
2007-03-13 03:12:07 +03:00
btrfs_set_item_offset ( leaf - > items + i ,
ioff - data_size ) ;
}
2007-01-26 23:51:26 +03:00
/* shift the items */
memmove ( leaf - > items + slot + 1 , leaf - > items + slot ,
2007-03-13 03:12:07 +03:00
( nritems - slot ) * sizeof ( struct btrfs_item ) ) ;
2007-01-26 23:51:26 +03:00
/* shift the data */
2007-03-14 21:14:43 +03:00
memmove ( btrfs_leaf_data ( leaf ) + data_end - data_size ,
btrfs_leaf_data ( leaf ) +
2007-01-26 23:51:26 +03:00
data_end , old_data - data_end ) ;
data_end = old_data ;
}
2007-03-15 19:56:47 +03:00
/* setup the item for the new data */
2007-03-12 23:22:34 +03:00
memcpy ( & leaf - > items [ slot ] . key , & disk_key ,
sizeof ( struct btrfs_disk_key ) ) ;
2007-03-13 03:12:07 +03:00
btrfs_set_item_offset ( leaf - > items + slot , data_end - data_size ) ;
btrfs_set_item_size ( leaf - > items + slot , data_size ) ;
2007-03-12 19:01:18 +03:00
btrfs_set_header_nritems ( & leaf - > header , nritems + 1 ) ;
2007-03-01 00:35:06 +03:00
ret = 0 ;
2007-02-28 17:27:02 +03:00
if ( slot = = 0 )
2007-03-16 23:20:31 +03:00
ret = fixup_low_keys ( trans , root , path , & disk_key , 1 ) ;
2007-03-01 00:35:06 +03:00
2007-03-22 19:13:20 +03:00
BUG_ON ( ! buffer_dirty ( leaf_buf ) ) ;
2007-03-14 21:14:43 +03:00
if ( btrfs_leaf_free_space ( root , leaf ) < 0 )
2007-01-26 23:51:26 +03:00
BUG ( ) ;
2007-03-15 19:56:47 +03:00
check_leaf ( root , path , 0 ) ;
2007-03-02 02:59:40 +03:00
out :
2007-03-15 19:56:47 +03: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 23:20:31 +03: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 19:56:47 +03:00
{
int ret = 0 ;
struct btrfs_path path ;
u8 * ptr ;
btrfs_init_path ( & path ) ;
2007-03-16 23:20:31 +03:00
ret = btrfs_insert_empty_item ( trans , root , & path , cpu_key , data_size ) ;
2007-03-15 19:56:47 +03:00
if ( ! ret ) {
2007-03-22 19:13:20 +03:00
ptr = btrfs_item_ptr ( btrfs_buffer_leaf ( path . nodes [ 0 ] ) ,
path . slots [ 0 ] , u8 ) ;
2007-03-15 19:56:47 +03:00
memcpy ( ptr , data , data_size ) ;
}
2007-03-13 17:46:10 +03:00
btrfs_release_path ( root , & path ) ;
2007-03-01 00:35:06 +03:00
return ret ;
2007-01-26 23:51:26 +03:00
}
2007-02-02 19:05:29 +03:00
/*
2007-02-24 14:24:44 +03:00
* delete the pointer from a given node .
2007-02-02 19:05:29 +03: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 23:20:31 +03: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 23:51:26 +03:00
{
2007-03-13 17:46:10 +03:00
struct btrfs_node * node ;
2007-03-22 19:13:20 +03:00
struct buffer_head * parent = path - > nodes [ level ] ;
2007-03-12 19:01:18 +03:00
u32 nritems ;
2007-03-01 00:35:06 +03:00
int ret = 0 ;
2007-03-01 20:04:21 +03:00
int wret ;
2007-01-26 23:51:26 +03:00
2007-03-22 19:13:20 +03:00
node = btrfs_buffer_node ( parent ) ;
2007-03-12 19:01:18 +03:00
nritems = btrfs_header_nritems ( & node - > header ) ;
2007-03-01 20:04:21 +03:00
if ( slot ! = nritems - 1 ) {
2007-03-14 21:14:43 +03:00
memmove ( node - > ptrs + slot , node - > ptrs + slot + 1 ,
sizeof ( struct btrfs_key_ptr ) * ( nritems - slot - 1 ) ) ;
2007-03-01 20:04:21 +03:00
}
2007-03-12 19:01:18 +03:00
nritems - - ;
btrfs_set_header_nritems ( & node - > header , nritems ) ;
if ( nritems = = 0 & & parent = = root - > node ) {
2007-03-22 19:13:20 +03:00
struct btrfs_header * header = btrfs_buffer_header ( root - > node ) ;
BUG_ON ( btrfs_header_level ( header ) ! = 1 ) ;
2007-03-01 20:04:21 +03:00
/* just turn the root into a leaf and break */
2007-03-22 19:13:20 +03:00
btrfs_set_header_level ( header , 0 ) ;
2007-03-01 20:04:21 +03:00
} else if ( slot = = 0 ) {
2007-03-16 23:20:31 +03:00
wret = fixup_low_keys ( trans , root , path , & node - > ptrs [ 0 ] . key ,
2007-03-14 21:14:43 +03:00
level + 1 ) ;
2007-03-01 00:46:22 +03:00
if ( wret )
ret = wret ;
2007-01-26 23:51:26 +03:00
}
2007-03-22 19:13:20 +03:00
BUG_ON ( ! buffer_dirty ( parent ) ) ;
2007-03-01 00:35:06 +03:00
return ret ;
2007-01-26 23:51:26 +03:00
}
2007-02-02 19:05:29 +03:00
/*
* delete the item at the leaf level in path . If that empties
* the leaf , remove it from the tree
*/
2007-03-16 23:20:31 +03:00
int btrfs_del_item ( struct btrfs_trans_handle * trans , struct btrfs_root * root ,
struct btrfs_path * path )
2007-01-26 23:51:26 +03:00
{
int slot ;
2007-03-13 17:46:10 +03:00
struct btrfs_leaf * leaf ;
2007-03-22 19:13:20 +03:00
struct buffer_head * leaf_buf ;
2007-01-26 23:51:26 +03:00
int doff ;
int dsize ;
2007-03-01 00:35:06 +03:00
int ret = 0 ;
int wret ;
2007-03-12 19:01:18 +03:00
u32 nritems ;
2007-01-26 23:51:26 +03:00
2007-02-02 17:18:22 +03:00
leaf_buf = path - > nodes [ 0 ] ;
2007-03-22 19:13:20 +03:00
leaf = btrfs_buffer_leaf ( leaf_buf ) ;
2007-01-27 00:38:42 +03:00
slot = path - > slots [ 0 ] ;
2007-03-13 03:12:07 +03:00
doff = btrfs_item_offset ( leaf - > items + slot ) ;
dsize = btrfs_item_size ( leaf - > items + slot ) ;
2007-03-12 19:01:18 +03:00
nritems = btrfs_header_nritems ( & leaf - > header ) ;
2007-01-26 23:51:26 +03:00
2007-03-12 19:01:18 +03:00
if ( slot ! = nritems - 1 ) {
2007-01-26 23:51:26 +03:00
int i ;
2007-03-14 21:14:43 +03:00
int data_end = leaf_data_end ( root , leaf ) ;
memmove ( btrfs_leaf_data ( leaf ) + data_end + dsize ,
btrfs_leaf_data ( leaf ) + data_end ,
2007-01-26 23:51:26 +03:00
doff - data_end ) ;
2007-03-13 03:12:07 +03:00
for ( i = slot + 1 ; i < nritems ; i + + ) {
2007-03-14 21:14:43 +03:00
u32 ioff = btrfs_item_offset ( leaf - > items + i ) ;
2007-03-13 03:12:07 +03:00
btrfs_set_item_offset ( leaf - > items + i , ioff + dsize ) ;
}
2007-01-26 23:51:26 +03:00
memmove ( leaf - > items + slot , leaf - > items + slot + 1 ,
2007-03-13 03:12:07 +03:00
sizeof ( struct btrfs_item ) *
2007-03-12 19:01:18 +03:00
( nritems - slot - 1 ) ) ;
2007-01-26 23:51:26 +03:00
}
2007-03-12 19:01:18 +03:00
btrfs_set_header_nritems ( & leaf - > header , nritems - 1 ) ;
nritems - - ;
2007-02-02 19:05:29 +03:00
/* delete the leaf if we've emptied it */
2007-03-12 19:01:18 +03:00
if ( nritems = = 0 ) {
2007-02-02 17:18:22 +03:00
if ( leaf_buf = = root - > node ) {
2007-03-12 19:01:18 +03:00
btrfs_set_header_level ( & leaf - > header , 0 ) ;
2007-02-23 16:38:36 +03:00
} else {
2007-03-16 23:20:31 +03:00
clean_tree_block ( trans , root , leaf_buf ) ;
wret = del_ptr ( trans , root , path , 1 , path - > slots [ 1 ] ) ;
2007-03-01 00:35:06 +03:00
if ( wret )
ret = wret ;
2007-03-16 23:20:31 +03:00
wret = btrfs_free_extent ( trans , root ,
2007-03-22 19:13:20 +03:00
leaf_buf - > b_blocknr , 1 , 1 ) ;
2007-03-01 00:46:22 +03:00
if ( wret )
ret = wret ;
2007-02-23 16:38:36 +03:00
}
2007-01-26 23:51:26 +03:00
} else {
2007-03-12 19:01:18 +03:00
int used = leaf_space_used ( leaf , 0 , nritems ) ;
2007-03-01 00:35:06 +03:00
if ( slot = = 0 ) {
2007-03-16 23:20:31 +03:00
wret = fixup_low_keys ( trans , root , path ,
& leaf - > items [ 0 ] . key , 1 ) ;
2007-03-01 00:35:06 +03:00
if ( wret )
ret = wret ;
}
2007-02-02 19:05:29 +03:00
/* delete the leaf if it is mostly empty */
2007-03-14 21:14:43 +03:00
if ( used < BTRFS_LEAF_DATA_SIZE ( root ) / 3 ) {
2007-01-26 23:51:26 +03: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-27 00:38:42 +03:00
slot = path - > slots [ 1 ] ;
2007-03-22 19:13:20 +03:00
get_bh ( leaf_buf ) ;
2007-03-16 23:20:31 +03:00
wret = push_leaf_left ( trans , root , path , 1 ) ;
2007-03-01 00:35:06 +03:00
if ( wret < 0 )
ret = wret ;
2007-03-02 17:47:58 +03:00
if ( path - > nodes [ 0 ] = = leaf_buf & &
2007-03-12 19:01:18 +03:00
btrfs_header_nritems ( & leaf - > header ) ) {
2007-03-16 23:20:31 +03:00
wret = push_leaf_right ( trans , root , path , 1 ) ;
2007-03-01 00:35:06 +03:00
if ( wret < 0 )
ret = wret ;
}
2007-03-12 19:01:18 +03:00
if ( btrfs_header_nritems ( & leaf - > header ) = = 0 ) {
2007-03-22 19:13:20 +03:00
u64 blocknr = leaf_buf - > b_blocknr ;
2007-03-16 23:20:31 +03:00
clean_tree_block ( trans , root , leaf_buf ) ;
wret = del_ptr ( trans , root , path , 1 , slot ) ;
2007-03-01 00:35:06 +03:00
if ( wret )
ret = wret ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , leaf_buf ) ;
2007-03-16 23:20:31 +03:00
wret = btrfs_free_extent ( trans , root , blocknr ,
1 , 1 ) ;
2007-03-01 00:46:22 +03:00
if ( wret )
ret = wret ;
2007-02-24 14:24:44 +03:00
} else {
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , leaf_buf ) ;
2007-01-26 23:51:26 +03:00
}
}
}
2007-03-01 00:35:06 +03:00
return ret ;
2007-01-26 23:51:26 +03:00
}
2007-02-24 21:39:08 +03:00
/*
* walk up the tree as far as required to find the next leaf .
2007-03-01 00:46:22 +03:00
* returns 0 if it found something or 1 if there are no greater leaves .
* returns < 0 on io errors .
2007-02-24 21:39:08 +03:00
*/
2007-03-13 17:46:10 +03:00
int btrfs_next_leaf ( struct btrfs_root * root , struct btrfs_path * path )
2007-02-21 00:40:44 +03:00
{
int slot ;
int level = 1 ;
u64 blocknr ;
2007-03-22 19:13:20 +03:00
struct buffer_head * c ;
struct btrfs_node * c_node ;
struct buffer_head * next = NULL ;
2007-02-21 00:40:44 +03:00
2007-03-13 17:46:10 +03:00
while ( level < BTRFS_MAX_LEVEL ) {
2007-02-21 00:40:44 +03:00
if ( ! path - > nodes [ level ] )
2007-03-01 00:46:22 +03:00
return 1 ;
2007-02-21 00:40:44 +03:00
slot = path - > slots [ level ] + 1 ;
c = path - > nodes [ level ] ;
2007-03-22 19:13:20 +03:00
c_node = btrfs_buffer_node ( c ) ;
if ( slot > = btrfs_header_nritems ( & c_node - > header ) ) {
2007-02-21 00:40:44 +03:00
level + + ;
continue ;
}
2007-03-22 19:13:20 +03:00
blocknr = btrfs_node_blockptr ( c_node , slot ) ;
2007-02-22 01:04:57 +03:00
if ( next )
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , next ) ;
2007-02-21 00:40:44 +03:00
next = read_tree_block ( root , blocknr ) ;
break ;
}
path - > slots [ level ] = slot ;
while ( 1 ) {
level - - ;
c = path - > nodes [ level ] ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , c ) ;
2007-02-21 00:40:44 +03:00
path - > nodes [ level ] = next ;
path - > slots [ level ] = 0 ;
if ( ! level )
break ;
2007-03-13 16:28:32 +03:00
next = read_tree_block ( root ,
2007-03-22 19:13:20 +03:00
btrfs_node_blockptr ( btrfs_buffer_node ( next ) , 0 ) ) ;
2007-02-21 00:40:44 +03:00
}
return 0 ;
}