2007-06-12 17:07:21 +04:00
/*
* Copyright ( C ) 2007 Oracle . All rights reserved .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public
* License along with this program ; if not , write to the
* Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
* Boston , MA 021110 - 1307 , USA .
*/
2007-10-16 00:22:39 +04:00
# include <linux/sched.h>
2007-02-02 17:18:22 +03:00
# include "ctree.h"
# include "disk-io.h"
2007-03-23 22:56:19 +03:00
# include "transaction.h"
2007-10-16 00:14:19 +04:00
# include "print-tree.h"
2008-06-26 00:01:30 +04:00
# include "locking.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
2007-04-04 22:08:15 +04:00
* root , struct btrfs_key * ins_key ,
2007-10-25 23:42:57 +04:00
struct btrfs_path * path , int data_size , int extend ) ;
2007-10-16 00:14:19 +04:00
static int push_node_left ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root , struct extent_buffer * dst ,
2008-04-24 18:54:32 +04:00
struct extent_buffer * src , int empty ) ;
2007-10-16 00:14:19 +04:00
static int balance_node_right ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct extent_buffer * dst_buf ,
struct extent_buffer * 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-04-04 17:36:31 +04:00
inline void btrfs_init_path ( struct btrfs_path * p )
2007-04-02 18:50:19 +04:00
{
2007-04-04 17:36:31 +04:00
memset ( p , 0 , sizeof ( * p ) ) ;
2007-04-02 18:50:19 +04:00
}
2007-04-04 17:36:31 +04:00
struct btrfs_path * btrfs_alloc_path ( void )
2007-04-02 18:50:19 +04:00
{
2007-04-04 17:36:31 +04:00
struct btrfs_path * path ;
path = kmem_cache_alloc ( btrfs_path_cachep , GFP_NOFS ) ;
2007-08-28 00:49:44 +04:00
if ( path ) {
2007-04-04 17:36:31 +04:00
btrfs_init_path ( path ) ;
2007-08-28 00:49:44 +04:00
path - > reada = 1 ;
}
2007-04-04 17:36:31 +04:00
return path ;
2007-04-02 18:50:19 +04:00
}
2007-04-04 17:36:31 +04:00
void btrfs_free_path ( struct btrfs_path * p )
2007-01-26 23:51:26 +03:00
{
2007-04-04 17:36:31 +04:00
btrfs_release_path ( NULL , p ) ;
kmem_cache_free ( btrfs_path_cachep , p ) ;
2007-01-26 23:51:26 +03:00
}
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 ;
2008-06-26 00:01:30 +04:00
int keep = p - > keep_locks ;
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 ] )
2008-06-26 00:01:30 +04:00
continue ;
if ( p - > locks [ i ] ) {
btrfs_tree_unlock ( p - > nodes [ i ] ) ;
p - > locks [ i ] = 0 ;
}
2007-10-16 00:14:19 +04:00
free_extent_buffer ( p - > nodes [ i ] ) ;
2007-02-02 17:18:22 +03:00
}
2007-03-01 00:35:06 +03:00
memset ( p , 0 , sizeof ( * p ) ) ;
2008-06-26 00:01:30 +04:00
p - > keep_locks = keep ;
2007-02-02 17:18:22 +03:00
}
2008-06-26 00:01:30 +04:00
struct extent_buffer * btrfs_root_node ( struct btrfs_root * root )
{
struct extent_buffer * eb ;
spin_lock ( & root - > node_lock ) ;
eb = root - > node ;
extent_buffer_get ( eb ) ;
spin_unlock ( & root - > node_lock ) ;
return eb ;
}
struct extent_buffer * btrfs_lock_root_node ( struct btrfs_root * root )
{
struct extent_buffer * eb ;
while ( 1 ) {
eb = btrfs_root_node ( root ) ;
btrfs_tree_lock ( eb ) ;
spin_lock ( & root - > node_lock ) ;
if ( eb = = root - > node ) {
spin_unlock ( & root - > node_lock ) ;
break ;
}
spin_unlock ( & root - > node_lock ) ;
btrfs_tree_unlock ( eb ) ;
free_extent_buffer ( eb ) ;
}
return eb ;
}
2008-03-24 22:01:56 +03:00
static void add_root_to_dirty_list ( struct btrfs_root * root )
{
if ( root - > track_dirty & & list_empty ( & root - > dirty_list ) ) {
list_add ( & root - > dirty_list ,
& root - > fs_info - > dirty_cowonly_roots ) ;
}
}
2007-12-18 04:14:01 +03:00
int btrfs_copy_root ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct extent_buffer * buf ,
struct extent_buffer * * cow_ret , u64 new_root_objectid )
{
struct extent_buffer * cow ;
u32 nritems ;
int ret = 0 ;
int level ;
struct btrfs_key first_key ;
2007-12-19 00:25:45 +03:00
struct btrfs_root * new_root ;
2007-12-18 04:14:01 +03:00
2007-12-19 00:25:45 +03:00
new_root = kmalloc ( sizeof ( * new_root ) , GFP_NOFS ) ;
if ( ! new_root )
return - ENOMEM ;
memcpy ( new_root , root , sizeof ( * new_root ) ) ;
new_root - > root_key . objectid = new_root_objectid ;
2007-12-18 04:14:01 +03:00
WARN_ON ( root - > ref_cows & & trans - > transid ! =
root - > fs_info - > running_transaction - > transid ) ;
WARN_ON ( root - > ref_cows & & trans - > transid ! = root - > last_trans ) ;
level = btrfs_header_level ( buf ) ;
nritems = btrfs_header_nritems ( buf ) ;
if ( nritems ) {
if ( level = = 0 )
btrfs_item_key_to_cpu ( buf , & first_key , 0 ) ;
else
btrfs_node_key_to_cpu ( buf , & first_key , 0 ) ;
} else {
first_key . objectid = 0 ;
}
2008-06-26 00:01:30 +04:00
cow = btrfs_alloc_free_block ( trans , new_root , buf - > len ,
2007-12-18 04:14:01 +03:00
new_root_objectid ,
trans - > transid , first_key . objectid ,
level , buf - > start , 0 ) ;
2007-12-19 00:25:45 +03:00
if ( IS_ERR ( cow ) ) {
kfree ( new_root ) ;
2007-12-18 04:14:01 +03:00
return PTR_ERR ( cow ) ;
2007-12-19 00:25:45 +03:00
}
2007-12-18 04:14:01 +03:00
copy_extent_buffer ( cow , buf , 0 , 0 , cow - > len ) ;
btrfs_set_header_bytenr ( cow , cow - > start ) ;
btrfs_set_header_generation ( cow , trans - > transid ) ;
btrfs_set_header_owner ( cow , new_root_objectid ) ;
2008-04-01 19:21:32 +04:00
btrfs_clear_header_flag ( cow , BTRFS_HEADER_FLAG_WRITTEN ) ;
2007-12-18 04:14:01 +03:00
WARN_ON ( btrfs_header_generation ( buf ) > trans - > transid ) ;
2007-12-19 00:25:45 +03:00
ret = btrfs_inc_ref ( trans , new_root , buf ) ;
kfree ( new_root ) ;
2007-12-18 04:14:01 +03:00
if ( ret )
return ret ;
btrfs_mark_buffer_dirty ( cow ) ;
* cow_ret = cow ;
return 0 ;
}
int __btrfs_cow_block ( struct btrfs_trans_handle * trans ,
2007-10-16 00:14:19 +04:00
struct btrfs_root * root ,
struct extent_buffer * buf ,
struct extent_buffer * parent , int parent_slot ,
struct extent_buffer * * cow_ret ,
u64 search_start , u64 empty_size )
2007-03-03 00:08:05 +03:00
{
2007-12-11 17:25:06 +03:00
u64 root_gen ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * cow ;
2007-12-11 17:25:06 +03:00
u32 nritems ;
2007-08-08 00:15:09 +04:00
int ret = 0 ;
int different_trans = 0 ;
2007-12-11 17:25:06 +03:00
int level ;
2008-06-26 00:01:30 +04:00
int unlock_orig = 0 ;
2007-12-11 17:25:06 +03:00
struct btrfs_key first_key ;
2008-06-26 00:01:30 +04:00
if ( * cow_ret = = buf )
unlock_orig = 1 ;
WARN_ON ( ! btrfs_tree_locked ( buf ) ) ;
2007-12-11 17:25:06 +03:00
if ( root - > ref_cows ) {
root_gen = trans - > transid ;
} else {
root_gen = 0 ;
}
WARN_ON ( root - > ref_cows & & trans - > transid ! =
root - > fs_info - > running_transaction - > transid ) ;
2007-08-08 00:15:09 +04:00
WARN_ON ( root - > ref_cows & & trans - > transid ! = root - > last_trans ) ;
2007-10-16 00:14:19 +04:00
2007-12-11 17:25:06 +03:00
level = btrfs_header_level ( buf ) ;
nritems = btrfs_header_nritems ( buf ) ;
if ( nritems ) {
if ( level = = 0 )
btrfs_item_key_to_cpu ( buf , & first_key , 0 ) ;
else
btrfs_node_key_to_cpu ( buf , & first_key , 0 ) ;
} else {
first_key . objectid = 0 ;
}
2008-06-26 00:01:30 +04:00
cow = btrfs_alloc_free_block ( trans , root , buf - > len ,
2007-12-11 17:25:06 +03:00
root - > root_key . objectid ,
root_gen , first_key . objectid , level ,
2007-10-16 00:15:53 +04:00
search_start , empty_size ) ;
2007-06-22 22:16:25 +04:00
if ( IS_ERR ( cow ) )
return PTR_ERR ( cow ) ;
2007-08-08 00:15:09 +04:00
2007-10-16 00:14:19 +04:00
copy_extent_buffer ( cow , buf , 0 , 0 , cow - > len ) ;
2007-10-16 00:15:53 +04:00
btrfs_set_header_bytenr ( cow , cow - > start ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_header_generation ( cow , trans - > transid ) ;
btrfs_set_header_owner ( cow , root - > root_key . objectid ) ;
2008-04-01 19:21:32 +04:00
btrfs_clear_header_flag ( cow , BTRFS_HEADER_FLAG_WRITTEN ) ;
2007-08-08 00:15:09 +04:00
2007-10-16 00:14:19 +04:00
WARN_ON ( btrfs_header_generation ( buf ) > trans - > transid ) ;
if ( btrfs_header_generation ( buf ) ! = trans - > transid ) {
2007-08-08 00:15:09 +04:00
different_trans = 1 ;
ret = btrfs_inc_ref ( trans , root , buf ) ;
if ( ret )
return ret ;
} else {
clean_tree_block ( trans , root , buf ) ;
}
2007-03-03 00:08:05 +03:00
if ( buf = = root - > node ) {
2008-06-26 00:01:30 +04:00
WARN_ON ( parent & & parent ! = buf ) ;
2007-12-11 17:25:06 +03:00
root_gen = btrfs_header_generation ( buf ) ;
2008-06-26 00:01:30 +04:00
spin_lock ( & root - > node_lock ) ;
2007-03-03 00:08:05 +03:00
root - > node = cow ;
2007-10-16 00:14:19 +04:00
extent_buffer_get ( cow ) ;
2008-06-26 00:01:30 +04:00
spin_unlock ( & root - > node_lock ) ;
2007-04-02 18:50:19 +04:00
if ( buf ! = root - > commit_root ) {
2007-10-16 00:15:53 +04:00
btrfs_free_extent ( trans , root , buf - > start ,
2007-12-11 17:25:06 +03:00
buf - > len , root - > root_key . objectid ,
root_gen , 0 , 0 , 1 ) ;
2007-04-02 18:50:19 +04:00
}
2007-10-16 00:14:19 +04:00
free_extent_buffer ( buf ) ;
2008-03-24 22:01:56 +03:00
add_root_to_dirty_list ( root ) ;
2007-03-03 00:08:05 +03:00
} else {
2007-12-11 17:25:06 +03:00
root_gen = btrfs_header_generation ( parent ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_node_blockptr ( parent , parent_slot ,
2007-10-16 00:15:53 +04:00
cow - > start ) ;
2007-12-11 17:25:06 +03:00
WARN_ON ( trans - > transid = = 0 ) ;
btrfs_set_node_ptr_generation ( parent , parent_slot ,
trans - > transid ) ;
2007-03-30 22:27:56 +04:00
btrfs_mark_buffer_dirty ( parent ) ;
2007-10-16 00:14:19 +04:00
WARN_ON ( btrfs_header_generation ( parent ) ! = trans - > transid ) ;
2007-12-11 17:25:06 +03:00
btrfs_free_extent ( trans , root , buf - > start , buf - > len ,
btrfs_header_owner ( parent ) , root_gen ,
0 , 0 , 1 ) ;
2007-03-03 00:08:05 +03:00
}
2008-06-26 00:01:30 +04:00
if ( unlock_orig )
btrfs_tree_unlock ( buf ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( buf ) ;
2007-06-28 23:57:36 +04:00
btrfs_mark_buffer_dirty ( cow ) ;
2007-04-02 18:50:19 +04:00
* cow_ret = cow ;
2007-03-03 00:08:05 +03:00
return 0 ;
}
2007-10-16 00:14:19 +04:00
int btrfs_cow_block ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root , struct extent_buffer * buf ,
struct extent_buffer * parent , int parent_slot ,
struct extent_buffer * * cow_ret )
2007-08-08 00:15:09 +04:00
{
u64 search_start ;
2008-01-08 23:46:30 +03:00
u64 header_trans ;
2007-10-16 00:14:48 +04:00
int ret ;
2008-01-08 23:46:30 +03:00
2007-08-08 00:15:09 +04:00
if ( trans - > transaction ! = root - > fs_info - > running_transaction ) {
printk ( KERN_CRIT " trans %Lu running %Lu \n " , trans - > transid ,
root - > fs_info - > running_transaction - > transid ) ;
WARN_ON ( 1 ) ;
}
if ( trans - > transid ! = root - > fs_info - > generation ) {
printk ( KERN_CRIT " trans %Lu running %Lu \n " , trans - > transid ,
root - > fs_info - > generation ) ;
WARN_ON ( 1 ) ;
}
2008-01-08 23:46:30 +03:00
header_trans = btrfs_header_generation ( buf ) ;
2008-04-01 19:21:32 +04:00
spin_lock ( & root - > fs_info - > hash_lock ) ;
if ( header_trans = = trans - > transid & &
! btrfs_header_flag ( buf , BTRFS_HEADER_FLAG_WRITTEN ) ) {
2007-08-08 00:15:09 +04:00
* cow_ret = buf ;
2008-04-01 19:21:32 +04:00
spin_unlock ( & root - > fs_info - > hash_lock ) ;
2007-08-08 00:15:09 +04:00
return 0 ;
}
2008-04-01 19:21:32 +04:00
spin_unlock ( & root - > fs_info - > hash_lock ) ;
2008-03-24 22:01:56 +03:00
search_start = buf - > start & ~ ( ( u64 ) ( 1024 * 1024 * 1024 ) - 1 ) ;
2007-10-16 00:14:48 +04:00
ret = __btrfs_cow_block ( trans , root , buf , parent ,
2007-08-08 00:15:09 +04:00
parent_slot , cow_ret , search_start , 0 ) ;
2007-10-16 00:14:48 +04:00
return ret ;
2007-08-08 00:15:09 +04:00
}
2007-10-16 00:17:34 +04:00
static int close_blocks ( u64 blocknr , u64 other , u32 blocksize )
2007-08-08 00:15:09 +04:00
{
2007-10-16 00:17:34 +04:00
if ( blocknr < other & & other - ( blocknr + blocksize ) < 32768 )
2007-08-08 00:15:09 +04:00
return 1 ;
2007-10-16 00:17:34 +04:00
if ( blocknr > other & & blocknr - ( other + blocksize ) < 32768 )
2007-08-08 00:15:09 +04:00
return 1 ;
return 0 ;
}
2007-11-06 18:26:24 +03:00
/*
* compare two keys in a memcmp fashion
*/
static int comp_keys ( struct btrfs_disk_key * disk , struct btrfs_key * k2 )
{
struct btrfs_key k1 ;
btrfs_disk_key_to_cpu ( & k1 , disk ) ;
if ( k1 . objectid > k2 - > objectid )
return 1 ;
if ( k1 . objectid < k2 - > objectid )
return - 1 ;
if ( k1 . type > k2 - > type )
return 1 ;
if ( k1 . type < k2 - > type )
return - 1 ;
if ( k1 . offset > k2 - > offset )
return 1 ;
if ( k1 . offset < k2 - > offset )
return - 1 ;
return 0 ;
}
2007-08-08 00:15:09 +04:00
int btrfs_realloc_node ( struct btrfs_trans_handle * trans ,
2007-10-16 00:14:19 +04:00
struct btrfs_root * root , struct extent_buffer * parent ,
2007-10-16 00:22:39 +04:00
int start_slot , int cache_only , u64 * last_ret ,
struct btrfs_key * progress )
2007-08-08 00:15:09 +04:00
{
2007-10-16 00:17:34 +04:00
struct extent_buffer * cur ;
struct extent_buffer * tmp ;
2007-08-08 00:15:09 +04:00
u64 blocknr ;
2008-05-12 20:59:19 +04:00
u64 gen ;
2007-08-10 22:06:19 +04:00
u64 search_start = * last_ret ;
u64 last_block = 0 ;
2007-08-08 00:15:09 +04:00
u64 other ;
u32 parent_nritems ;
int end_slot ;
int i ;
int err = 0 ;
2007-08-10 22:42:37 +04:00
int parent_level ;
2007-10-16 00:17:34 +04:00
int uptodate ;
u32 blocksize ;
2007-11-06 18:26:24 +03:00
int progress_passed = 0 ;
struct btrfs_disk_key disk_key ;
2007-08-08 00:15:09 +04:00
2008-06-26 00:01:30 +04:00
/* FIXME this code needs locking */
return 0 ;
2007-10-25 23:43:18 +04:00
parent_level = btrfs_header_level ( parent ) ;
if ( cache_only & & parent_level ! = 1 )
return 0 ;
2007-08-08 00:15:09 +04:00
if ( trans - > transaction ! = root - > fs_info - > running_transaction ) {
printk ( KERN_CRIT " trans %Lu running %Lu \n " , trans - > transid ,
root - > fs_info - > running_transaction - > transid ) ;
WARN_ON ( 1 ) ;
}
if ( trans - > transid ! = root - > fs_info - > generation ) {
printk ( KERN_CRIT " trans %Lu running %Lu \n " , trans - > transid ,
root - > fs_info - > generation ) ;
WARN_ON ( 1 ) ;
}
2007-09-11 03:58:16 +04:00
2007-10-16 00:17:34 +04:00
parent_nritems = btrfs_header_nritems ( parent ) ;
blocksize = btrfs_level_size ( root , parent_level - 1 ) ;
2007-08-08 00:15:09 +04:00
end_slot = parent_nritems ;
if ( parent_nritems = = 1 )
return 0 ;
for ( i = start_slot ; i < end_slot ; i + + ) {
int close = 1 ;
2007-10-16 00:22:39 +04:00
2007-10-25 23:43:18 +04:00
if ( ! parent - > map_token ) {
map_extent_buffer ( parent ,
btrfs_node_key_ptr_offset ( i ) ,
sizeof ( struct btrfs_key_ptr ) ,
& parent - > map_token , & parent - > kaddr ,
& parent - > map_start , & parent - > map_len ,
KM_USER1 ) ;
}
2007-11-06 18:26:24 +03:00
btrfs_node_key ( parent , & disk_key , i ) ;
if ( ! progress_passed & & comp_keys ( & disk_key , progress ) < 0 )
continue ;
progress_passed = 1 ;
2007-10-16 00:17:34 +04:00
blocknr = btrfs_node_blockptr ( parent , i ) ;
2008-05-12 20:59:19 +04:00
gen = btrfs_node_ptr_generation ( parent , i ) ;
2007-08-10 22:06:19 +04:00
if ( last_block = = 0 )
last_block = blocknr ;
2007-10-25 23:43:18 +04:00
2007-08-08 00:15:09 +04:00
if ( i > 0 ) {
2007-10-16 00:17:34 +04:00
other = btrfs_node_blockptr ( parent , i - 1 ) ;
close = close_blocks ( blocknr , other , blocksize ) ;
2007-08-08 00:15:09 +04:00
}
2008-05-24 22:04:53 +04:00
if ( ! close & & i < end_slot - 2 ) {
2007-10-16 00:17:34 +04:00
other = btrfs_node_blockptr ( parent , i + 1 ) ;
close = close_blocks ( blocknr , other , blocksize ) ;
2007-08-08 00:15:09 +04:00
}
2007-08-10 22:06:19 +04:00
if ( close ) {
last_block = blocknr ;
2007-08-08 00:15:09 +04:00
continue ;
2007-08-10 22:06:19 +04:00
}
2007-10-25 23:43:18 +04:00
if ( parent - > map_token ) {
unmap_extent_buffer ( parent , parent - > map_token ,
KM_USER1 ) ;
parent - > map_token = NULL ;
}
2007-08-08 00:15:09 +04:00
2007-10-16 00:17:34 +04:00
cur = btrfs_find_tree_block ( root , blocknr , blocksize ) ;
if ( cur )
2008-05-12 21:39:03 +04:00
uptodate = btrfs_buffer_uptodate ( cur , gen ) ;
2007-10-16 00:17:34 +04:00
else
uptodate = 0 ;
2007-10-25 23:43:18 +04:00
if ( ! cur | | ! uptodate ) {
2007-08-08 00:15:09 +04:00
if ( cache_only ) {
2007-10-16 00:17:34 +04:00
free_extent_buffer ( cur ) ;
2007-08-08 00:15:09 +04:00
continue ;
}
2007-10-16 00:17:34 +04:00
if ( ! cur ) {
cur = read_tree_block ( root , blocknr ,
2008-05-12 20:59:19 +04:00
blocksize , gen ) ;
2007-10-16 00:17:34 +04:00
} else if ( ! uptodate ) {
2008-05-12 20:59:19 +04:00
btrfs_read_buffer ( cur , gen ) ;
2007-08-10 22:42:37 +04:00
}
2007-08-08 00:15:09 +04:00
}
2007-08-10 22:06:19 +04:00
if ( search_start = = 0 )
2007-10-16 00:17:34 +04:00
search_start = last_block ;
2007-08-10 22:06:19 +04:00
2007-10-16 00:17:34 +04:00
err = __btrfs_cow_block ( trans , root , cur , parent , i ,
& tmp , search_start ,
min ( 16 * blocksize ,
( end_slot - i ) * blocksize ) ) ;
2007-08-29 17:11:44 +04:00
if ( err ) {
2007-10-16 00:17:34 +04:00
free_extent_buffer ( cur ) ;
2007-08-08 00:15:09 +04:00
break ;
2007-08-29 17:11:44 +04:00
}
2007-10-16 00:17:34 +04:00
search_start = tmp - > start ;
2007-10-25 23:43:18 +04:00
last_block = tmp - > start ;
2007-08-10 22:42:37 +04:00
* last_ret = search_start ;
if ( parent_level = = 1 )
2007-10-16 00:17:34 +04:00
btrfs_clear_buffer_defrag ( tmp ) ;
free_extent_buffer ( tmp ) ;
2007-08-08 00:15:09 +04:00
}
2007-10-25 23:43:18 +04:00
if ( parent - > map_token ) {
unmap_extent_buffer ( parent , parent - > map_token ,
KM_USER1 ) ;
parent - > map_token = NULL ;
}
2007-08-08 00:15:09 +04:00
return err ;
}
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 ,
2007-10-16 00:14:19 +04:00
struct extent_buffer * leaf )
2007-01-26 23:51:26 +03:00
{
2007-10-16 00:14:19 +04:00
u32 nr = btrfs_header_nritems ( leaf ) ;
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-10-16 00:14:19 +04:00
return btrfs_item_offset_nr ( leaf , nr - 1 ) ;
2007-01-26 23:51:26 +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
{
2007-10-16 00:14:19 +04:00
struct extent_buffer * parent = NULL ;
struct extent_buffer * node = path - > nodes [ level ] ;
struct btrfs_disk_key parent_key ;
struct btrfs_disk_key node_key ;
2007-03-01 00:35:06 +03:00
int parent_slot ;
2007-05-10 19:24:42 +04:00
int slot ;
struct btrfs_key cpukey ;
2007-10-16 00:14:19 +04:00
u32 nritems = btrfs_header_nritems ( node ) ;
2007-03-01 00:35:06 +03:00
if ( path - > nodes [ level + 1 ] )
2007-10-16 00:14:19 +04:00
parent = path - > nodes [ level + 1 ] ;
2007-07-11 18:03:27 +04:00
2007-05-10 19:24:42 +04:00
slot = path - > slots [ level ] ;
2007-03-12 19:01:18 +03:00
BUG_ON ( nritems = = 0 ) ;
if ( parent ) {
2007-07-11 18:03:27 +04:00
parent_slot = path - > slots [ level + 1 ] ;
2007-10-16 00:14:19 +04:00
btrfs_node_key ( parent , & parent_key , parent_slot ) ;
btrfs_node_key ( node , & node_key , 0 ) ;
BUG_ON ( memcmp ( & parent_key , & node_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-10-16 00:15:53 +04:00
btrfs_header_bytenr ( node ) ) ;
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-05-10 19:24:42 +04:00
if ( slot ! = 0 ) {
2007-10-16 00:14:19 +04:00
btrfs_node_key_to_cpu ( node , & cpukey , slot - 1 ) ;
btrfs_node_key ( node , & node_key , slot ) ;
BUG_ON ( comp_keys ( & node_key , & cpukey ) < = 0 ) ;
2007-05-10 19:24:42 +04:00
}
if ( slot < nritems - 1 ) {
2007-10-16 00:14:19 +04:00
btrfs_node_key_to_cpu ( node , & cpukey , slot + 1 ) ;
btrfs_node_key ( node , & node_key , slot ) ;
BUG_ON ( comp_keys ( & node_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
{
2007-10-16 00:14:19 +04:00
struct extent_buffer * leaf = path - > nodes [ level ] ;
struct extent_buffer * parent = NULL ;
2007-03-01 00:35:06 +03:00
int parent_slot ;
2007-05-10 19:24:42 +04:00
struct btrfs_key cpukey ;
2007-10-16 00:14:19 +04:00
struct btrfs_disk_key parent_key ;
struct btrfs_disk_key leaf_key ;
int slot = path - > slots [ 0 ] ;
2007-05-10 19:24:42 +04:00
2007-10-16 00:14:19 +04:00
u32 nritems = btrfs_header_nritems ( leaf ) ;
2007-03-01 00:35:06 +03:00
if ( path - > nodes [ level + 1 ] )
2007-10-16 00:14:19 +04:00
parent = path - > nodes [ level + 1 ] ;
2007-03-12 19:01:18 +03:00
if ( nritems = = 0 )
return 0 ;
if ( parent ) {
2007-07-11 18:03:27 +04:00
parent_slot = path - > slots [ level + 1 ] ;
2007-10-16 00:14:19 +04:00
btrfs_node_key ( parent , & parent_key , parent_slot ) ;
btrfs_item_key ( leaf , & leaf_key , 0 ) ;
2007-08-08 00:15:09 +04:00
2007-10-16 00:14:19 +04:00
BUG_ON ( memcmp ( & parent_key , & leaf_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-10-16 00:15:53 +04:00
btrfs_header_bytenr ( leaf ) ) ;
2007-10-16 00:14:19 +04:00
}
#if 0
for ( i = 0 ; nritems > 1 & & i < nritems - 2 ; i + + ) {
btrfs_item_key_to_cpu ( leaf , & cpukey , i + 1 ) ;
btrfs_item_key ( leaf , & leaf_key , i ) ;
if ( comp_keys ( & leaf_key , & cpukey ) > = 0 ) {
btrfs_print_leaf ( root , leaf ) ;
printk ( " slot %d offset bad key \n " , i ) ;
BUG_ON ( 1 ) ;
}
if ( btrfs_item_offset_nr ( leaf , i ) ! =
btrfs_item_end_nr ( leaf , i + 1 ) ) {
btrfs_print_leaf ( root , leaf ) ;
printk ( " slot %d offset bad \n " , i ) ;
BUG_ON ( 1 ) ;
}
if ( i = = 0 ) {
if ( btrfs_item_offset_nr ( leaf , i ) +
btrfs_item_size_nr ( leaf , i ) ! =
BTRFS_LEAF_DATA_SIZE ( root ) ) {
btrfs_print_leaf ( root , leaf ) ;
printk ( " slot %d first offset bad \n " , i ) ;
BUG_ON ( 1 ) ;
}
}
2007-03-01 00:35:06 +03:00
}
2007-10-16 00:14:19 +04:00
if ( nritems > 0 ) {
if ( btrfs_item_size_nr ( leaf , nritems - 1 ) > 4096 ) {
btrfs_print_leaf ( root , leaf ) ;
printk ( " slot %d bad size \n " , nritems - 1 ) ;
BUG_ON ( 1 ) ;
}
}
# endif
if ( slot ! = 0 & & slot < nritems - 1 ) {
btrfs_item_key ( leaf , & leaf_key , slot ) ;
btrfs_item_key_to_cpu ( leaf , & cpukey , slot - 1 ) ;
if ( comp_keys ( & leaf_key , & cpukey ) < = 0 ) {
btrfs_print_leaf ( root , leaf ) ;
printk ( " slot %d offset bad key \n " , slot ) ;
BUG_ON ( 1 ) ;
}
if ( btrfs_item_offset_nr ( leaf , slot - 1 ) ! =
btrfs_item_end_nr ( leaf , slot ) ) {
btrfs_print_leaf ( root , leaf ) ;
printk ( " slot %d offset bad \n " , slot ) ;
BUG_ON ( 1 ) ;
}
2007-05-10 19:24:42 +04:00
}
if ( slot < nritems - 1 ) {
2007-10-16 00:14:19 +04:00
btrfs_item_key ( leaf , & leaf_key , slot ) ;
btrfs_item_key_to_cpu ( leaf , & cpukey , slot + 1 ) ;
BUG_ON ( comp_keys ( & leaf_key , & cpukey ) > = 0 ) ;
if ( btrfs_item_offset_nr ( leaf , slot ) ! =
btrfs_item_end_nr ( leaf , slot + 1 ) ) {
btrfs_print_leaf ( root , leaf ) ;
printk ( " slot %d offset bad \n " , slot ) ;
BUG_ON ( 1 ) ;
}
2007-03-01 00:35:06 +03:00
}
2007-10-16 00:14:19 +04:00
BUG_ON ( btrfs_item_offset_nr ( leaf , 0 ) +
btrfs_item_size_nr ( leaf , 0 ) ! = BTRFS_LEAF_DATA_SIZE ( root ) ) ;
2007-03-01 00:35:06 +03:00
return 0 ;
}
2008-01-03 18:01:48 +03:00
static int noinline check_block ( struct btrfs_root * root ,
struct btrfs_path * path , int level )
2007-03-01 00:35:06 +03:00
{
2008-04-10 00:28:12 +04:00
u64 found_start ;
2008-04-10 18:23:19 +04:00
return 0 ;
2008-04-10 00:28:12 +04:00
if ( btrfs_header_level ( path - > nodes [ level ] ) ! = level )
printk ( " warning: bad level %Lu wanted %d found %d \n " ,
path - > nodes [ level ] - > start , level ,
btrfs_header_level ( path - > nodes [ level ] ) ) ;
found_start = btrfs_header_bytenr ( path - > nodes [ level ] ) ;
if ( found_start ! = path - > nodes [ level ] - > start ) {
printk ( " warning: bad bytentr %Lu found %Lu \n " ,
path - > nodes [ level ] - > start , found_start ) ;
}
2007-10-16 00:15:53 +04:00
#if 0
2007-10-16 00:14:19 +04:00
struct extent_buffer * buf = path - > nodes [ level ] ;
2007-10-16 00:14:27 +04:00
if ( memcmp_extent_buffer ( buf , root - > fs_info - > fsid ,
( unsigned long ) btrfs_header_fsid ( buf ) ,
BTRFS_FSID_SIZE ) ) {
2007-10-16 00:14:19 +04:00
printk ( " warning bad block %Lu \n " , buf - > start ) ;
2007-10-16 00:15:53 +04:00
return 1 ;
2007-10-16 00:14:19 +04:00
}
2007-10-16 00:15:53 +04:00
# endif
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
/*
2007-10-16 00:14:19 +04:00
* search for key in the extent_buffer . The items start at offset p ,
* and they are item_size apart . There are ' max ' items in p .
*
2007-02-02 19:05:29 +03:00
* 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-10-16 00:14:19 +04:00
static int generic_bin_search ( struct extent_buffer * eb , unsigned long p ,
int item_size , struct btrfs_key * key ,
int max , int * slot )
2007-01-26 23:51:26 +03:00
{
int low = 0 ;
int high = max ;
int mid ;
int ret ;
2007-10-16 00:14:27 +04:00
struct btrfs_disk_key * tmp = NULL ;
2007-10-16 00:14:19 +04:00
struct btrfs_disk_key unaligned ;
unsigned long offset ;
char * map_token = NULL ;
char * kaddr = NULL ;
unsigned long map_start = 0 ;
unsigned long map_len = 0 ;
2007-10-16 00:14:27 +04:00
int err ;
2007-01-26 23:51:26 +03:00
while ( low < high ) {
mid = ( low + high ) / 2 ;
2007-10-16 00:14:19 +04:00
offset = p + mid * item_size ;
if ( ! map_token | | offset < map_start | |
( offset + sizeof ( struct btrfs_disk_key ) ) >
map_start + map_len ) {
2007-10-16 00:14:27 +04:00
if ( map_token ) {
2007-10-16 00:14:19 +04:00
unmap_extent_buffer ( eb , map_token , KM_USER0 ) ;
2007-10-16 00:14:27 +04:00
map_token = NULL ;
}
err = map_extent_buffer ( eb , offset ,
sizeof ( struct btrfs_disk_key ) ,
& map_token , & kaddr ,
& map_start , & map_len , KM_USER0 ) ;
if ( ! err ) {
tmp = ( struct btrfs_disk_key * ) ( kaddr + offset -
map_start ) ;
} else {
read_extent_buffer ( eb , & unaligned ,
offset , sizeof ( unaligned ) ) ;
tmp = & unaligned ;
}
2007-10-16 00:14:19 +04:00
} else {
tmp = ( struct btrfs_disk_key * ) ( kaddr + offset -
map_start ) ;
}
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 ;
2007-10-16 00:14:27 +04:00
if ( map_token )
unmap_extent_buffer ( eb , map_token , KM_USER0 ) ;
2007-01-26 23:51:26 +03:00
return 0 ;
}
}
* slot = low ;
2007-10-16 00:14:19 +04:00
if ( map_token )
unmap_extent_buffer ( eb , map_token , KM_USER0 ) ;
2007-01-26 23:51:26 +03:00
return 1 ;
}
2007-02-24 21:39:08 +03:00
/*
* simple bin_search frontend that does the right thing for
* leaves vs nodes
*/
2007-10-16 00:14:19 +04:00
static int bin_search ( struct extent_buffer * eb , struct btrfs_key * key ,
int level , int * slot )
2007-01-26 23:51:26 +03:00
{
2007-10-16 00:14:19 +04:00
if ( level = = 0 ) {
return generic_bin_search ( eb ,
offsetof ( struct btrfs_leaf , items ) ,
2007-03-13 03:12:07 +03:00
sizeof ( struct btrfs_item ) ,
2007-10-16 00:14:19 +04:00
key , btrfs_header_nritems ( eb ) ,
2007-03-12 19:01:18 +03:00
slot ) ;
2007-01-26 23:51:26 +03:00
} else {
2007-10-16 00:14:19 +04:00
return generic_bin_search ( eb ,
offsetof ( struct btrfs_node , ptrs ) ,
2007-03-14 21:14:43 +03:00
sizeof ( struct btrfs_key_ptr ) ,
2007-10-16 00:14:19 +04:00
key , btrfs_header_nritems ( eb ) ,
2007-03-12 19:01:18 +03:00
slot ) ;
2007-01-26 23:51:26 +03:00
}
return - 1 ;
}
2007-10-16 00:14:19 +04:00
static struct extent_buffer * read_node_slot ( struct btrfs_root * root ,
struct extent_buffer * parent , int slot )
2007-03-01 20:04:21 +03:00
{
2008-05-12 20:59:19 +04:00
int level = btrfs_header_level ( parent ) ;
2007-03-01 20:04:21 +03:00
if ( slot < 0 )
return NULL ;
2007-10-16 00:14:19 +04:00
if ( slot > = btrfs_header_nritems ( parent ) )
2007-03-01 20:04:21 +03:00
return NULL ;
2008-05-12 20:59:19 +04:00
BUG_ON ( level = = 0 ) ;
2007-10-16 00:15:53 +04:00
return read_tree_block ( root , btrfs_node_blockptr ( parent , slot ) ,
2008-05-12 20:59:19 +04:00
btrfs_level_size ( root , level - 1 ) ,
btrfs_node_ptr_generation ( parent , slot ) ) ;
2007-03-01 20:04:21 +03:00
}
2008-01-03 18:01:48 +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-10-16 00:14:19 +04:00
struct extent_buffer * right = NULL ;
struct extent_buffer * mid ;
struct extent_buffer * left = NULL ;
struct extent_buffer * parent = NULL ;
2007-03-01 20:04:21 +03:00
int ret = 0 ;
int wret ;
int pslot ;
int orig_slot = path - > slots [ level ] ;
2007-06-22 22:16:25 +04:00
int err_on_enospc = 0 ;
2007-03-01 23:16:26 +03:00
u64 orig_ptr ;
2007-03-01 20:04:21 +03:00
if ( level = = 0 )
return 0 ;
2007-10-16 00:14:19 +04:00
mid = path - > nodes [ level ] ;
2008-06-26 00:01:30 +04:00
WARN_ON ( ! path - > locks [ level ] ) ;
2007-12-11 17:25:06 +03:00
WARN_ON ( btrfs_header_generation ( mid ) ! = trans - > transid ) ;
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-10-16 00:14:19 +04:00
parent = path - > nodes [ level + 1 ] ;
2007-03-01 20:04:21 +03:00
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-10-16 00:14:19 +04:00
if ( ! parent ) {
struct extent_buffer * child ;
2007-03-01 20:04:21 +03:00
2007-10-16 00:14:19 +04:00
if ( btrfs_header_nritems ( mid ) ! = 1 )
2007-03-01 20:04:21 +03:00
return 0 ;
/* promote the child to a root */
2007-10-16 00:14:19 +04:00
child = read_node_slot ( root , mid , 0 ) ;
2008-06-26 00:01:30 +04:00
btrfs_tree_lock ( child ) ;
2007-03-01 20:04:21 +03:00
BUG_ON ( ! child ) ;
2008-02-01 22:58:07 +03:00
ret = btrfs_cow_block ( trans , root , child , mid , 0 , & child ) ;
BUG_ON ( ret ) ;
2008-06-26 00:01:30 +04:00
spin_lock ( & root - > node_lock ) ;
2007-03-01 20:04:21 +03:00
root - > node = child ;
2008-06-26 00:01:30 +04:00
spin_unlock ( & root - > node_lock ) ;
2008-03-24 22:01:56 +03:00
add_root_to_dirty_list ( root ) ;
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( child ) ;
path - > locks [ level ] = 0 ;
2007-03-01 20:04:21 +03:00
path - > nodes [ level ] = NULL ;
2007-10-16 00:14:19 +04:00
clean_tree_block ( trans , root , mid ) ;
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( mid ) ;
2007-03-01 20:04:21 +03:00
/* once for the path */
2007-10-16 00:14:19 +04:00
free_extent_buffer ( mid ) ;
2007-12-11 17:25:06 +03:00
ret = btrfs_free_extent ( trans , root , mid - > start , mid - > len ,
root - > root_key . objectid ,
btrfs_header_generation ( mid ) , 0 , 0 , 1 ) ;
2007-03-01 20:04:21 +03:00
/* once for the root ptr */
2007-10-16 00:14:19 +04:00
free_extent_buffer ( mid ) ;
2007-10-16 00:15:53 +04:00
return ret ;
2007-03-01 20:04:21 +03:00
}
2007-10-16 00:14:19 +04:00
if ( btrfs_header_nritems ( mid ) >
2007-03-14 21:14:43 +03:00
BTRFS_NODEPTRS_PER_BLOCK ( root ) / 4 )
2007-03-01 20:04:21 +03:00
return 0 ;
2007-10-16 00:14:19 +04:00
if ( btrfs_header_nritems ( mid ) < 2 )
2007-06-22 22:16:25 +04:00
err_on_enospc = 1 ;
2007-10-16 00:14:19 +04:00
left = read_node_slot ( root , parent , pslot - 1 ) ;
if ( left ) {
2008-06-26 00:01:30 +04:00
btrfs_tree_lock ( left ) ;
2007-10-16 00:14:19 +04:00
wret = btrfs_cow_block ( trans , root , left ,
parent , pslot - 1 , & left ) ;
2007-06-22 22:16:25 +04:00
if ( wret ) {
ret = wret ;
goto enospc ;
}
2007-08-28 00:49:44 +04:00
}
2007-10-16 00:14:19 +04:00
right = read_node_slot ( root , parent , pslot + 1 ) ;
if ( right ) {
2008-06-26 00:01:30 +04:00
btrfs_tree_lock ( right ) ;
2007-10-16 00:14:19 +04:00
wret = btrfs_cow_block ( trans , root , right ,
parent , pslot + 1 , & right ) ;
2007-08-28 00:49:44 +04:00
if ( wret ) {
ret = wret ;
goto enospc ;
}
}
/* first, try to make some room in the middle buffer */
2007-10-16 00:14:19 +04:00
if ( left ) {
orig_slot + = btrfs_header_nritems ( left ) ;
2008-04-24 22:42:46 +04:00
wret = push_node_left ( trans , root , left , mid , 1 ) ;
2007-03-01 23:16:26 +03:00
if ( wret < 0 )
ret = wret ;
2007-10-16 00:14:19 +04:00
if ( btrfs_header_nritems ( mid ) < 2 )
2007-06-22 22:16:25 +04:00
err_on_enospc = 1 ;
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-10-16 00:14:19 +04:00
if ( right ) {
2008-04-24 18:54:32 +04:00
wret = push_node_left ( trans , root , mid , right , 1 ) ;
2007-06-22 22:16:25 +04:00
if ( wret < 0 & & wret ! = - ENOSPC )
2007-03-01 23:16:26 +03:00
ret = wret ;
2007-10-16 00:14:19 +04:00
if ( btrfs_header_nritems ( right ) = = 0 ) {
2007-10-16 00:15:53 +04:00
u64 bytenr = right - > start ;
2007-12-11 17:25:06 +03:00
u64 generation = btrfs_header_generation ( parent ) ;
2007-10-16 00:15:53 +04:00
u32 blocksize = right - > len ;
2007-10-16 00:14:19 +04:00
clean_tree_block ( trans , root , right ) ;
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( right ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( right ) ;
2007-03-01 20:04:21 +03:00
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-10-16 00:15:53 +04:00
wret = btrfs_free_extent ( trans , root , bytenr ,
2007-12-11 17:25:06 +03:00
blocksize ,
btrfs_header_owner ( parent ) ,
generation , 0 , 0 , 1 ) ;
2007-03-01 20:04:21 +03:00
if ( wret )
ret = wret ;
} else {
2007-10-16 00:14:19 +04:00
struct btrfs_disk_key right_key ;
btrfs_node_key ( right , & right_key , 0 ) ;
btrfs_set_node_key ( parent , & right_key , pslot + 1 ) ;
btrfs_mark_buffer_dirty ( parent ) ;
2007-03-01 20:04:21 +03:00
}
}
2007-10-16 00:14:19 +04:00
if ( btrfs_header_nritems ( mid ) = = 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
*/
2007-10-16 00:14:19 +04:00
BUG_ON ( ! left ) ;
wret = balance_node_right ( trans , root , mid , left ) ;
2007-06-22 22:16:25 +04:00
if ( wret < 0 ) {
2007-03-01 23:16:26 +03:00
ret = wret ;
2007-06-22 22:16:25 +04:00
goto enospc ;
}
2008-04-24 22:42:46 +04:00
if ( wret = = 1 ) {
wret = push_node_left ( trans , root , left , mid , 1 ) ;
if ( wret < 0 )
ret = wret ;
}
2007-03-01 23:16:26 +03:00
BUG_ON ( wret = = 1 ) ;
}
2007-10-16 00:14:19 +04:00
if ( btrfs_header_nritems ( mid ) = = 0 ) {
2007-03-01 23:16:26 +03:00
/* we've managed to empty the middle node, drop it */
2007-12-11 17:25:06 +03:00
u64 root_gen = btrfs_header_generation ( parent ) ;
2007-10-16 00:15:53 +04:00
u64 bytenr = mid - > start ;
u32 blocksize = mid - > len ;
2008-06-26 00:01:30 +04:00
2007-10-16 00:14:19 +04:00
clean_tree_block ( trans , root , mid ) ;
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( mid ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( mid ) ;
2007-03-01 20:04:21 +03:00
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-12-11 17:25:06 +03:00
wret = btrfs_free_extent ( trans , root , bytenr , blocksize ,
btrfs_header_owner ( parent ) ,
root_gen , 0 , 0 , 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-10-16 00:14:19 +04:00
struct btrfs_disk_key mid_key ;
btrfs_node_key ( mid , & mid_key , 0 ) ;
btrfs_set_node_key ( parent , & mid_key , pslot ) ;
btrfs_mark_buffer_dirty ( parent ) ;
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-10-16 00:14:19 +04:00
if ( left ) {
if ( btrfs_header_nritems ( left ) > orig_slot ) {
extent_buffer_get ( left ) ;
2008-06-26 00:01:30 +04:00
/* left was locked after cow */
2007-10-16 00:14:19 +04:00
path - > nodes [ level ] = left ;
2007-03-01 20:04:21 +03:00
path - > slots [ level + 1 ] - = 1 ;
path - > slots [ level ] = orig_slot ;
2008-06-26 00:01:30 +04:00
if ( mid ) {
btrfs_tree_unlock ( mid ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( mid ) ;
2008-06-26 00:01:30 +04:00
}
2007-03-01 20:04:21 +03:00
} else {
2007-10-16 00:14:19 +04:00
orig_slot - = btrfs_header_nritems ( left ) ;
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 ! =
2007-10-16 00:14:19 +04:00
btrfs_node_blockptr ( path - > nodes [ level ] , path - > slots [ level ] ) )
2007-03-01 23:16:26 +03:00
BUG ( ) ;
2007-06-22 22:16:25 +04:00
enospc :
2008-06-26 00:01:30 +04:00
if ( right ) {
btrfs_tree_unlock ( right ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( right ) ;
2008-06-26 00:01:30 +04:00
}
if ( left ) {
if ( path - > nodes [ level ] ! = left )
btrfs_tree_unlock ( left ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( left ) ;
2008-06-26 00:01:30 +04:00
}
2007-03-01 20:04:21 +03:00
return ret ;
}
2007-04-20 21:16:02 +04:00
/* returns zero if the push worked, non-zero otherwise */
2008-01-03 18:01:48 +03:00
static int noinline push_nodes_for_insert ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct btrfs_path * path , int level )
2007-04-20 21:16:02 +04:00
{
2007-10-16 00:14:19 +04:00
struct extent_buffer * right = NULL ;
struct extent_buffer * mid ;
struct extent_buffer * left = NULL ;
struct extent_buffer * parent = NULL ;
2007-04-20 21:16:02 +04:00
int ret = 0 ;
int wret ;
int pslot ;
int orig_slot = path - > slots [ level ] ;
u64 orig_ptr ;
if ( level = = 0 )
return 1 ;
2007-10-16 00:14:19 +04:00
mid = path - > nodes [ level ] ;
2007-12-11 17:25:06 +03:00
WARN_ON ( btrfs_header_generation ( mid ) ! = trans - > transid ) ;
2007-04-20 21:16:02 +04:00
orig_ptr = btrfs_node_blockptr ( mid , orig_slot ) ;
if ( level < BTRFS_MAX_LEVEL - 1 )
2007-10-16 00:14:19 +04:00
parent = path - > nodes [ level + 1 ] ;
2007-04-20 21:16:02 +04:00
pslot = path - > slots [ level + 1 ] ;
2007-10-16 00:14:19 +04:00
if ( ! parent )
2007-04-20 21:16:02 +04:00
return 1 ;
2007-10-16 00:14:19 +04:00
left = read_node_slot ( root , parent , pslot - 1 ) ;
2007-04-20 21:16:02 +04:00
/* first, try to make some room in the middle buffer */
2007-10-16 00:14:19 +04:00
if ( left ) {
2007-04-20 21:16:02 +04:00
u32 left_nr ;
2008-06-26 00:01:30 +04:00
btrfs_tree_lock ( left ) ;
2007-10-16 00:14:19 +04:00
left_nr = btrfs_header_nritems ( left ) ;
2007-04-20 21:48:57 +04:00
if ( left_nr > = BTRFS_NODEPTRS_PER_BLOCK ( root ) - 1 ) {
wret = 1 ;
} else {
2007-10-16 00:14:19 +04:00
ret = btrfs_cow_block ( trans , root , left , parent ,
pslot - 1 , & left ) ;
2007-06-22 22:16:25 +04:00
if ( ret )
wret = 1 ;
else {
wret = push_node_left ( trans , root ,
2008-04-24 18:54:32 +04:00
left , mid , 0 ) ;
2007-06-22 22:16:25 +04:00
}
2007-04-20 21:48:57 +04:00
}
2007-04-20 21:16:02 +04:00
if ( wret < 0 )
ret = wret ;
if ( wret = = 0 ) {
2007-10-16 00:14:19 +04:00
struct btrfs_disk_key disk_key ;
2007-04-20 21:16:02 +04:00
orig_slot + = left_nr ;
2007-10-16 00:14:19 +04:00
btrfs_node_key ( mid , & disk_key , 0 ) ;
btrfs_set_node_key ( parent , & disk_key , pslot ) ;
btrfs_mark_buffer_dirty ( parent ) ;
if ( btrfs_header_nritems ( left ) > orig_slot ) {
path - > nodes [ level ] = left ;
2007-04-20 21:16:02 +04:00
path - > slots [ level + 1 ] - = 1 ;
path - > slots [ level ] = orig_slot ;
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( mid ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( mid ) ;
2007-04-20 21:16:02 +04:00
} else {
orig_slot - =
2007-10-16 00:14:19 +04:00
btrfs_header_nritems ( left ) ;
2007-04-20 21:16:02 +04:00
path - > slots [ level ] = orig_slot ;
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( left ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( left ) ;
2007-04-20 21:16:02 +04:00
}
return 0 ;
}
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( left ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( left ) ;
2007-04-20 21:16:02 +04:00
}
2008-06-26 00:01:30 +04:00
right = read_node_slot ( root , parent , pslot + 1 ) ;
2007-04-20 21:16:02 +04:00
/*
* then try to empty the right most buffer into the middle
*/
2007-10-16 00:14:19 +04:00
if ( right ) {
2007-04-20 21:48:57 +04:00
u32 right_nr ;
2008-06-26 00:01:30 +04:00
btrfs_tree_lock ( right ) ;
2007-10-16 00:14:19 +04:00
right_nr = btrfs_header_nritems ( right ) ;
2007-04-20 21:48:57 +04:00
if ( right_nr > = BTRFS_NODEPTRS_PER_BLOCK ( root ) - 1 ) {
wret = 1 ;
} else {
2007-10-16 00:14:19 +04:00
ret = btrfs_cow_block ( trans , root , right ,
parent , pslot + 1 ,
& right ) ;
2007-06-22 22:16:25 +04:00
if ( ret )
wret = 1 ;
else {
wret = balance_node_right ( trans , root ,
2007-10-16 00:14:19 +04:00
right , mid ) ;
2007-06-22 22:16:25 +04:00
}
2007-04-20 21:48:57 +04:00
}
2007-04-20 21:16:02 +04:00
if ( wret < 0 )
ret = wret ;
if ( wret = = 0 ) {
2007-10-16 00:14:19 +04:00
struct btrfs_disk_key disk_key ;
btrfs_node_key ( right , & disk_key , 0 ) ;
btrfs_set_node_key ( parent , & disk_key , pslot + 1 ) ;
btrfs_mark_buffer_dirty ( parent ) ;
if ( btrfs_header_nritems ( mid ) < = orig_slot ) {
path - > nodes [ level ] = right ;
2007-04-20 21:16:02 +04:00
path - > slots [ level + 1 ] + = 1 ;
path - > slots [ level ] = orig_slot -
2007-10-16 00:14:19 +04:00
btrfs_header_nritems ( mid ) ;
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( mid ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( mid ) ;
2007-04-20 21:16:02 +04:00
} else {
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( right ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( right ) ;
2007-04-20 21:16:02 +04:00
}
return 0 ;
}
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( right ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( right ) ;
2007-04-20 21:16:02 +04:00
}
return 1 ;
}
2007-08-07 23:52:22 +04:00
/*
* readahead one full node of leaves
*/
static void reada_for_search ( struct btrfs_root * root , struct btrfs_path * path ,
2007-12-22 00:24:26 +03:00
int level , int slot , u64 objectid )
2007-08-07 23:52:22 +04:00
{
2007-10-16 00:14:19 +04:00
struct extent_buffer * node ;
2007-12-22 00:24:26 +03:00
struct btrfs_disk_key disk_key ;
2007-08-07 23:52:22 +04:00
u32 nritems ;
u64 search ;
2007-10-16 00:17:34 +04:00
u64 lowest_read ;
u64 highest_read ;
u64 nread = 0 ;
2007-08-07 23:52:22 +04:00
int direction = path - > reada ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * eb ;
2007-10-16 00:17:34 +04:00
u32 nr ;
u32 blocksize ;
u32 nscan = 0 ;
2007-10-16 00:15:53 +04:00
2007-10-16 00:22:39 +04:00
if ( level ! = 1 )
2007-08-08 00:15:09 +04:00
return ;
if ( ! path - > nodes [ level ] )
2007-08-07 23:52:22 +04:00
return ;
2007-10-16 00:14:19 +04:00
node = path - > nodes [ level ] ;
2008-06-26 00:01:30 +04:00
2007-08-07 23:52:22 +04:00
search = btrfs_node_blockptr ( node , slot ) ;
2007-10-16 00:17:34 +04:00
blocksize = btrfs_level_size ( root , level - 1 ) ;
eb = btrfs_find_tree_block ( root , search , blocksize ) ;
2007-10-16 00:14:19 +04:00
if ( eb ) {
free_extent_buffer ( eb ) ;
2007-08-07 23:52:22 +04:00
return ;
}
2007-10-16 00:17:34 +04:00
highest_read = search ;
lowest_read = search ;
2007-10-16 00:14:19 +04:00
nritems = btrfs_header_nritems ( node ) ;
2007-10-16 00:17:34 +04:00
nr = slot ;
2007-08-07 23:52:22 +04:00
while ( 1 ) {
2007-10-16 00:17:34 +04:00
if ( direction < 0 ) {
if ( nr = = 0 )
break ;
nr - - ;
} else if ( direction > 0 ) {
nr + + ;
if ( nr > = nritems )
break ;
2007-08-07 23:52:22 +04:00
}
2007-12-22 00:24:26 +03:00
if ( path - > reada < 0 & & objectid ) {
btrfs_node_key ( node , & disk_key , nr ) ;
if ( btrfs_disk_key_objectid ( & disk_key ) ! = objectid )
break ;
}
2007-10-16 00:17:34 +04:00
search = btrfs_node_blockptr ( node , nr ) ;
if ( ( search > = lowest_read & & search < = highest_read ) | |
( search < lowest_read & & lowest_read - search < = 32768 ) | |
( search > highest_read & & search - highest_read < = 32768 ) ) {
2008-05-12 20:59:19 +04:00
readahead_tree_block ( root , search , blocksize ,
btrfs_node_ptr_generation ( node , nr ) ) ;
2007-10-16 00:17:34 +04:00
nread + = blocksize ;
}
nscan + + ;
if ( path - > reada < 2 & & ( nread > ( 256 * 1024 ) | | nscan > 32 ) )
break ;
if ( nread > ( 1024 * 1024 ) | | nscan > 128 )
break ;
if ( search < lowest_read )
lowest_read = search ;
if ( search > highest_read )
highest_read = search ;
2007-08-07 23:52:22 +04:00
}
}
2008-06-26 00:01:30 +04:00
static void unlock_up ( struct btrfs_path * path , int level , int lowest_unlock )
{
int i ;
int skip_level = level ;
2008-06-26 00:01:30 +04:00
int no_skips = 0 ;
2008-06-26 00:01:30 +04:00
struct extent_buffer * t ;
for ( i = level ; i < BTRFS_MAX_LEVEL ; i + + ) {
if ( ! path - > nodes [ i ] )
break ;
if ( ! path - > locks [ i ] )
break ;
2008-06-26 00:01:30 +04:00
if ( ! no_skips & & path - > slots [ i ] = = 0 ) {
2008-06-26 00:01:30 +04:00
skip_level = i + 1 ;
continue ;
}
2008-06-26 00:01:30 +04:00
if ( ! no_skips & & path - > keep_locks ) {
2008-06-26 00:01:30 +04:00
u32 nritems ;
t = path - > nodes [ i ] ;
nritems = btrfs_header_nritems ( t ) ;
2008-06-26 00:01:30 +04:00
if ( nritems < 1 | | path - > slots [ i ] > = nritems - 1 ) {
2008-06-26 00:01:30 +04:00
skip_level = i + 1 ;
continue ;
}
}
2008-06-26 00:01:30 +04:00
if ( skip_level < i & & i > = lowest_unlock )
no_skips = 1 ;
2008-06-26 00:01:30 +04:00
t = path - > nodes [ i ] ;
if ( i > = lowest_unlock & & i > skip_level & & path - > locks [ i ] ) {
btrfs_tree_unlock ( t ) ;
path - > locks [ i ] = 0 ;
}
}
}
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-10-16 00:14:19 +04:00
struct extent_buffer * b ;
2008-06-26 00:01:30 +04:00
struct extent_buffer * tmp ;
2007-01-26 23:51:26 +03:00
int slot ;
int ret ;
int level ;
2007-08-07 23:52:22 +04:00
int should_reada = p - > reada ;
2008-06-26 00:01:30 +04:00
int lowest_unlock = 1 ;
2007-08-07 23:52:19 +04:00
u8 lowest_level = 0 ;
2007-08-08 00:15:09 +04:00
lowest_level = p - > lowest_level ;
WARN_ON ( lowest_level & & ins_len ) ;
2007-03-30 16:47:31 +04:00
WARN_ON ( p - > nodes [ 0 ] ! = NULL ) ;
2008-06-26 00:01:30 +04:00
WARN_ON ( root = = root - > fs_info - > extent_root & &
! mutex_is_locked ( & root - > fs_info - > alloc_mutex ) ) ;
WARN_ON ( root = = root - > fs_info - > chunk_root & &
! mutex_is_locked ( & root - > fs_info - > chunk_mutex ) ) ;
WARN_ON ( root = = root - > fs_info - > dev_root & &
! mutex_is_locked ( & root - > fs_info - > chunk_mutex ) ) ;
if ( ins_len < 0 )
lowest_unlock = 2 ;
2007-03-01 20:04:21 +03:00
again :
2008-06-26 00:01:30 +04:00
b = btrfs_lock_root_node ( root ) ;
2008-06-26 00:01:30 +04:00
2007-02-02 17:18:22 +03:00
while ( b ) {
2007-10-16 00:14:19 +04:00
level = btrfs_header_level ( b ) ;
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-08-29 17:11:44 +04:00
& b ) ;
2007-06-22 22:16:25 +04:00
if ( wret ) {
2007-10-16 00:14:19 +04:00
free_extent_buffer ( b ) ;
2007-06-22 22:16:25 +04:00
return wret ;
}
2007-03-03 00:08:05 +03:00
}
BUG_ON ( ! cow & & ins_len ) ;
2007-10-16 00:14:19 +04:00
if ( level ! = btrfs_header_level ( b ) )
2007-04-02 18:50:19 +04:00
WARN_ON ( 1 ) ;
2007-10-16 00:14:19 +04:00
level = btrfs_header_level ( b ) ;
2007-02-02 17:18:22 +03:00
p - > nodes [ level ] = b ;
2008-06-26 00:01:30 +04:00
p - > locks [ level ] = 1 ;
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 ;
2008-06-26 00:01:30 +04:00
2007-10-16 00:14:19 +04:00
ret = bin_search ( b , key , level , & slot ) ;
if ( level ! = 0 ) {
2007-01-26 23:51:26 +03:00
if ( ret & & slot > 0 )
slot - = 1 ;
p - > slots [ level ] = slot ;
2007-10-16 00:14:19 +04:00
if ( ins_len > 0 & & btrfs_header_nritems ( b ) > =
2008-04-24 17:22:51 +04:00
BTRFS_NODEPTRS_PER_BLOCK ( root ) - 3 ) {
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 ] ;
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 ] ;
2007-10-16 00:14:48 +04:00
if ( ! b ) {
btrfs_release_path ( NULL , p ) ;
2007-03-01 20:04:21 +03:00
goto again ;
2007-10-16 00:14:48 +04:00
}
2007-03-01 20:04:21 +03:00
slot = p - > slots [ level ] ;
2007-10-16 00:14:19 +04:00
BUG_ON ( btrfs_header_nritems ( b ) = = 1 ) ;
2007-02-22 19:39:13 +03:00
}
2007-08-07 23:52:19 +04:00
/* this is only true while dropping a snapshot */
2008-06-26 00:01:30 +04:00
if ( level = = lowest_level ) {
unlock_up ( p , level , lowest_unlock ) ;
2007-08-07 23:52:19 +04:00
break ;
2008-06-26 00:01:30 +04:00
}
2008-05-12 20:59:19 +04:00
2007-08-08 00:15:09 +04:00
if ( should_reada )
2007-12-22 00:24:26 +03:00
reada_for_search ( root , p , level , slot ,
key - > objectid ) ;
2008-05-12 20:59:19 +04:00
2008-06-26 00:01:30 +04:00
tmp = btrfs_find_tree_block ( root ,
btrfs_node_blockptr ( b , slot ) ,
btrfs_level_size ( root , level - 1 ) ) ;
if ( tmp & & btrfs_buffer_uptodate ( tmp ,
btrfs_node_ptr_generation ( b , slot ) ) ) {
b = tmp ;
} else {
/*
* reduce lock contention at high levels
* of the btree by dropping locks before
* we read .
*/
if ( level > 1 ) {
btrfs_release_path ( NULL , p ) ;
if ( tmp )
free_extent_buffer ( tmp ) ;
goto again ;
} else {
b = read_node_slot ( root , b , slot ) ;
}
}
btrfs_tree_lock ( b ) ;
unlock_up ( p , level , lowest_unlock ) ;
2007-01-26 23:51:26 +03:00
} else {
p - > slots [ level ] = slot ;
2007-10-16 00:14:19 +04:00
if ( ins_len > 0 & & btrfs_leaf_free_space ( root , b ) <
2007-03-13 03:12:07 +03:00
sizeof ( struct btrfs_item ) + ins_len ) {
2007-04-04 22:08:15 +04:00
int sret = split_leaf ( trans , root , key ,
2007-10-25 23:42:57 +04:00
p , ins_len , ret = = 0 ) ;
2007-02-22 19:39:13 +03:00
BUG_ON ( sret > 0 ) ;
if ( sret )
return sret ;
}
2008-06-26 00:01:30 +04:00
unlock_up ( p , level , lowest_unlock ) ;
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-10-16 00:14:19 +04: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-10-16 00:14:19 +04:00
struct extent_buffer * t ;
2007-03-13 17:46:10 +03:00
for ( i = level ; i < BTRFS_MAX_LEVEL ; i + + ) {
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-10-16 00:14:19 +04:00
t = path - > nodes [ i ] ;
btrfs_set_node_key ( t , key , tslot ) ;
2008-06-26 00:01:30 +04:00
if ( ! btrfs_tree_locked ( path - > nodes [ i ] ) ) {
int ii ;
printk ( " fixup without lock on level %d \n " , btrfs_header_level ( path - > nodes [ i ] ) ) ;
for ( ii = 0 ; ii < BTRFS_MAX_LEVEL ; ii + + ) {
printk ( " level %d slot %d \n " , ii , path - > slots [ ii ] ) ;
}
}
2007-03-30 22:27:56 +04:00
btrfs_mark_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
*/
2008-01-03 18:01:48 +03:00
static int push_node_left ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root , struct extent_buffer * dst ,
2008-04-24 18:54:32 +04:00
struct extent_buffer * src , int empty )
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-10-16 00:14:19 +04:00
src_nritems = btrfs_header_nritems ( src ) ;
dst_nritems = btrfs_header_nritems ( dst ) ;
2007-03-14 21:14:43 +03:00
push_items = BTRFS_NODEPTRS_PER_BLOCK ( root ) - dst_nritems ;
2007-12-11 17:25:06 +03:00
WARN_ON ( btrfs_header_generation ( src ) ! = trans - > transid ) ;
WARN_ON ( btrfs_header_generation ( dst ) ! = trans - > transid ) ;
2007-06-22 22:16:25 +04:00
2008-04-24 22:42:46 +04:00
if ( ! empty & & src_nritems < = 8 )
2008-04-24 18:54:32 +04:00
return 1 ;
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
2008-04-24 22:42:46 +04:00
if ( empty ) {
2008-04-24 18:54:32 +04:00
push_items = min ( src_nritems , push_items ) ;
2008-04-24 22:42:46 +04:00
if ( push_items < src_nritems ) {
/* leave at least 8 pointers in the node if
* we aren ' t going to empty it
*/
if ( src_nritems - push_items < 8 ) {
if ( push_items < = 8 )
return 1 ;
push_items - = 8 ;
}
}
} else
push_items = min ( src_nritems - 8 , push_items ) ;
2007-03-01 23:16:26 +03:00
2007-10-16 00:14:19 +04:00
copy_extent_buffer ( dst , src ,
btrfs_node_key_ptr_offset ( dst_nritems ) ,
btrfs_node_key_ptr_offset ( 0 ) ,
push_items * sizeof ( struct btrfs_key_ptr ) ) ;
2007-03-01 20:04:21 +03:00
if ( push_items < src_nritems ) {
2007-10-16 00:14:19 +04:00
memmove_extent_buffer ( src , btrfs_node_key_ptr_offset ( 0 ) ,
btrfs_node_key_ptr_offset ( push_items ) ,
( src_nritems - push_items ) *
sizeof ( struct btrfs_key_ptr ) ) ;
}
btrfs_set_header_nritems ( src , src_nritems - push_items ) ;
btrfs_set_header_nritems ( dst , dst_nritems + push_items ) ;
btrfs_mark_buffer_dirty ( src ) ;
btrfs_mark_buffer_dirty ( dst ) ;
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-10-16 00:14:19 +04:00
static int balance_node_right ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct extent_buffer * dst ,
struct extent_buffer * src )
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-12-11 17:25:06 +03:00
WARN_ON ( btrfs_header_generation ( src ) ! = trans - > transid ) ;
WARN_ON ( btrfs_header_generation ( dst ) ! = trans - > transid ) ;
2007-10-16 00:14:19 +04:00
src_nritems = btrfs_header_nritems ( src ) ;
dst_nritems = btrfs_header_nritems ( dst ) ;
2007-03-14 21:14:43 +03:00
push_items = BTRFS_NODEPTRS_PER_BLOCK ( root ) - dst_nritems ;
2008-04-24 22:42:46 +04:00
if ( push_items < = 0 ) {
2007-03-01 23:16:26 +03:00
return 1 ;
2008-04-24 22:42:46 +04:00
}
if ( src_nritems < 4 ) {
return 1 ;
}
2007-03-01 23:16:26 +03:00
max_push = src_nritems / 2 + 1 ;
/* don't try to empty the node */
2008-04-24 22:42:46 +04:00
if ( max_push > = src_nritems ) {
2007-03-01 23:16:26 +03:00
return 1 ;
2008-04-24 22:42:46 +04:00
}
2007-08-29 17:11:44 +04:00
2007-03-01 23:16:26 +03:00
if ( max_push < push_items )
push_items = max_push ;
2007-10-16 00:14:19 +04:00
memmove_extent_buffer ( dst , btrfs_node_key_ptr_offset ( push_items ) ,
btrfs_node_key_ptr_offset ( 0 ) ,
( dst_nritems ) *
sizeof ( struct btrfs_key_ptr ) ) ;
2007-03-30 22:27:56 +04:00
2007-10-16 00:14:19 +04:00
copy_extent_buffer ( dst , src ,
btrfs_node_key_ptr_offset ( 0 ) ,
btrfs_node_key_ptr_offset ( src_nritems - push_items ) ,
push_items * sizeof ( struct btrfs_key_ptr ) ) ;
2007-03-01 23:16:26 +03:00
2007-10-16 00:14:19 +04:00
btrfs_set_header_nritems ( src , src_nritems - push_items ) ;
btrfs_set_header_nritems ( dst , dst_nritems + push_items ) ;
2007-03-01 23:16:26 +03:00
2007-10-16 00:14:19 +04:00
btrfs_mark_buffer_dirty ( src ) ;
btrfs_mark_buffer_dirty ( dst ) ;
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
*/
2008-01-03 18:01:48 +03:00
static int noinline insert_new_root ( struct btrfs_trans_handle * trans ,
2007-10-16 00:14:19 +04:00
struct btrfs_root * root ,
struct btrfs_path * path , int level )
2007-02-22 19:39:13 +03:00
{
2007-12-11 17:25:06 +03:00
u64 root_gen ;
u64 lower_gen ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * lower ;
struct extent_buffer * c ;
2008-06-26 00:01:30 +04:00
struct extent_buffer * old ;
2007-10-16 00:14:19 +04: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-12-11 17:25:06 +03:00
if ( root - > ref_cows )
root_gen = trans - > transid ;
else
root_gen = 0 ;
lower = path - > nodes [ level - 1 ] ;
if ( level = = 1 )
btrfs_item_key ( lower , & lower_key , 0 ) ;
else
btrfs_node_key ( lower , & lower_key , 0 ) ;
2008-06-26 00:01:30 +04:00
c = btrfs_alloc_free_block ( trans , root , root - > nodesize ,
2007-12-11 17:25:06 +03:00
root - > root_key . objectid ,
root_gen , lower_key . objectid , level ,
2007-10-16 00:15:53 +04:00
root - > node - > start , 0 ) ;
2007-10-16 00:14:19 +04:00
if ( IS_ERR ( c ) )
return PTR_ERR ( c ) ;
2008-06-26 00:01:30 +04:00
2007-10-16 00:14:19 +04:00
memset_extent_buffer ( c , 0 , 0 , root - > nodesize ) ;
btrfs_set_header_nritems ( c , 1 ) ;
btrfs_set_header_level ( c , level ) ;
2007-10-16 00:15:53 +04:00
btrfs_set_header_bytenr ( c , c - > start ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_header_generation ( c , trans - > transid ) ;
btrfs_set_header_owner ( c , root - > root_key . objectid ) ;
write_extent_buffer ( c , root - > fs_info - > fsid ,
( unsigned long ) btrfs_header_fsid ( c ) ,
BTRFS_FSID_SIZE ) ;
2008-04-15 23:41:47 +04:00
write_extent_buffer ( c , root - > fs_info - > chunk_tree_uuid ,
( unsigned long ) btrfs_header_chunk_tree_uuid ( c ) ,
BTRFS_UUID_SIZE ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_node_key ( c , & lower_key , 0 ) ;
2007-10-16 00:15:53 +04:00
btrfs_set_node_blockptr ( c , 0 , lower - > start ) ;
2007-12-11 17:25:06 +03:00
lower_gen = btrfs_header_generation ( lower ) ;
WARN_ON ( lower_gen = = 0 ) ;
btrfs_set_node_ptr_generation ( c , 0 , lower_gen ) ;
2007-03-23 17:01:08 +03:00
2007-10-16 00:14:19 +04:00
btrfs_mark_buffer_dirty ( c ) ;
2007-03-23 17:01:08 +03:00
2008-06-26 00:01:30 +04:00
spin_lock ( & root - > node_lock ) ;
old = root - > node ;
2007-10-16 00:14:19 +04:00
root - > node = c ;
2008-06-26 00:01:30 +04:00
spin_unlock ( & root - > node_lock ) ;
/* the super has an extra ref to root->node */
free_extent_buffer ( old ) ;
2008-03-24 22:01:56 +03:00
add_root_to_dirty_list ( root ) ;
2007-10-16 00:14:19 +04:00
extent_buffer_get ( c ) ;
path - > nodes [ level ] = c ;
2008-06-26 00:01:30 +04:00
path - > locks [ level ] = 1 ;
2007-02-22 19:39:13 +03:00
path - > slots [ level ] = 0 ;
2007-12-11 17:25:06 +03:00
if ( root - > ref_cows & & lower_gen ! = trans - > transid ) {
struct btrfs_path * back_path = btrfs_alloc_path ( ) ;
int ret ;
2008-06-26 00:01:30 +04:00
mutex_lock ( & root - > fs_info - > alloc_mutex ) ;
2007-12-11 17:25:06 +03:00
ret = btrfs_insert_extent_backref ( trans ,
root - > fs_info - > extent_root ,
path , lower - > start ,
root - > root_key . objectid ,
trans - > transid , 0 , 0 ) ;
BUG_ON ( ret ) ;
2008-06-26 00:01:30 +04:00
mutex_unlock ( & root - > fs_info - > alloc_mutex ) ;
2007-12-11 17:25:06 +03:00
btrfs_free_path ( back_path ) ;
}
2007-02-22 19:39:13 +03:00
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
2007-10-16 00:15:53 +04:00
* key , u64 bytenr , int slot , int level )
2007-02-02 19:05:29 +03:00
{
2007-10-16 00:14:19 +04:00
struct extent_buffer * 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-10-16 00:14:19 +04:00
lower = path - > nodes [ level ] ;
nritems = btrfs_header_nritems ( lower ) ;
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-10-16 00:14:19 +04:00
memmove_extent_buffer ( lower ,
btrfs_node_key_ptr_offset ( slot + 1 ) ,
btrfs_node_key_ptr_offset ( slot ) ,
2007-03-30 22:27:56 +04:00
( nritems - slot ) * sizeof ( struct btrfs_key_ptr ) ) ;
2007-02-02 19:05:29 +03:00
}
2007-10-16 00:14:19 +04:00
btrfs_set_node_key ( lower , key , slot ) ;
2007-10-16 00:15:53 +04:00
btrfs_set_node_blockptr ( lower , slot , bytenr ) ;
2007-12-11 17:25:06 +03:00
WARN_ON ( trans - > transid = = 0 ) ;
btrfs_set_node_ptr_generation ( lower , slot , trans - > transid ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_header_nritems ( lower , nritems + 1 ) ;
btrfs_mark_buffer_dirty ( lower ) ;
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-12-11 17:25:06 +03:00
u64 root_gen ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * c ;
struct extent_buffer * split ;
struct btrfs_disk_key disk_key ;
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-10-16 00:14:19 +04:00
c = path - > nodes [ level ] ;
2007-12-11 17:25:06 +03:00
WARN_ON ( btrfs_header_generation ( c ) ! = trans - > transid ) ;
2007-10-16 00:14:19 +04:00
if ( c = = root - > node ) {
2007-02-22 19:39:13 +03:00
/* 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-04-20 21:16:02 +04:00
} else {
ret = push_nodes_for_insert ( trans , root , path , level ) ;
2007-10-16 00:14:19 +04:00
c = path - > nodes [ level ] ;
if ( ! ret & & btrfs_header_nritems ( c ) <
2008-04-24 17:34:34 +04:00
BTRFS_NODEPTRS_PER_BLOCK ( root ) - 3 )
2007-04-20 21:16:02 +04:00
return 0 ;
2007-06-22 22:16:25 +04:00
if ( ret < 0 )
return ret ;
2007-01-26 23:51:26 +03:00
}
2007-04-20 21:16:02 +04:00
2007-10-16 00:14:19 +04:00
c_nritems = btrfs_header_nritems ( c ) ;
2007-12-11 17:25:06 +03:00
if ( root - > ref_cows )
root_gen = trans - > transid ;
else
root_gen = 0 ;
btrfs_node_key ( c , & disk_key , 0 ) ;
2008-06-26 00:01:30 +04:00
split = btrfs_alloc_free_block ( trans , root , root - > nodesize ,
2007-12-11 17:25:06 +03:00
root - > root_key . objectid ,
root_gen ,
btrfs_disk_key_objectid ( & disk_key ) ,
level , c - > start , 0 ) ;
2007-10-16 00:14:19 +04:00
if ( IS_ERR ( split ) )
return PTR_ERR ( split ) ;
btrfs_set_header_flags ( split , btrfs_header_flags ( c ) ) ;
btrfs_set_header_level ( split , btrfs_header_level ( c ) ) ;
2007-10-16 00:15:53 +04:00
btrfs_set_header_bytenr ( split , split - > start ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_header_generation ( split , trans - > transid ) ;
btrfs_set_header_owner ( split , root - > root_key . objectid ) ;
2008-04-01 19:21:32 +04:00
btrfs_set_header_flags ( split , 0 ) ;
2007-10-16 00:14:19 +04:00
write_extent_buffer ( split , root - > fs_info - > fsid ,
( unsigned long ) btrfs_header_fsid ( split ) ,
BTRFS_FSID_SIZE ) ;
2008-04-15 23:41:47 +04:00
write_extent_buffer ( split , root - > fs_info - > chunk_tree_uuid ,
( unsigned long ) btrfs_header_chunk_tree_uuid ( split ) ,
BTRFS_UUID_SIZE ) ;
2007-06-22 22:16:25 +04:00
2007-03-12 19:01:18 +03:00
mid = ( c_nritems + 1 ) / 2 ;
2007-10-16 00:14:19 +04:00
copy_extent_buffer ( split , c ,
btrfs_node_key_ptr_offset ( 0 ) ,
btrfs_node_key_ptr_offset ( mid ) ,
( c_nritems - mid ) * sizeof ( struct btrfs_key_ptr ) ) ;
btrfs_set_header_nritems ( split , c_nritems - mid ) ;
btrfs_set_header_nritems ( c , mid ) ;
2007-03-01 00:35:06 +03:00
ret = 0 ;
2007-10-16 00:14:19 +04:00
btrfs_mark_buffer_dirty ( c ) ;
btrfs_mark_buffer_dirty ( split ) ;
btrfs_node_key ( split , & disk_key , 0 ) ;
2007-10-16 00:15:53 +04:00
wret = insert_ptr ( trans , root , path , & disk_key , split - > start ,
2007-10-16 00:14:19 +04:00
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 ;
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( c ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( c ) ;
path - > nodes [ level ] = split ;
2007-02-22 19:39:13 +03:00
path - > slots [ level + 1 ] + = 1 ;
} else {
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( split ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( split ) ;
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-10-16 00:14:19 +04:00
static int leaf_space_used ( struct extent_buffer * l , int start , int nr )
2007-01-26 23:51:26 +03:00
{
int data_len ;
2007-10-16 00:14:19 +04:00
int nritems = btrfs_header_nritems ( l ) ;
2007-04-04 22:08:15 +04:00
int end = min ( nritems , start + nr ) - 1 ;
2007-01-26 23:51:26 +03:00
if ( ! nr )
return 0 ;
2007-10-16 00:14:19 +04:00
data_len = btrfs_item_end_nr ( l , start ) ;
data_len = data_len - btrfs_item_offset_nr ( l , end ) ;
2007-03-13 03:12:07 +03:00
data_len + = sizeof ( struct btrfs_item ) * nr ;
2007-04-04 22:08:15 +04:00
WARN_ON ( data_len < 0 ) ;
2007-01-26 23:51:26 +03:00
return data_len ;
}
2007-04-04 22:08:15 +04: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-10-16 00:14:19 +04:00
int btrfs_leaf_free_space ( struct btrfs_root * root , struct extent_buffer * leaf )
2007-04-04 22:08:15 +04:00
{
2007-10-16 00:14:19 +04:00
int nritems = btrfs_header_nritems ( leaf ) ;
int ret ;
ret = BTRFS_LEAF_DATA_SIZE ( root ) - leaf_space_used ( leaf , 0 , nritems ) ;
if ( ret < 0 ) {
printk ( " leaf free space ret %d, leaf data size %lu, used %d nritems %d \n " ,
2007-10-19 17:22:59 +04:00
ret , ( unsigned long ) BTRFS_LEAF_DATA_SIZE ( root ) ,
2007-10-16 00:14:19 +04:00
leaf_space_used ( leaf , 0 , nritems ) , nritems ) ;
}
return ret ;
2007-04-04 22:08:15 +04:00
}
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
2007-11-07 21:31:03 +03:00
* root , struct btrfs_path * path , int data_size ,
int empty )
2007-02-24 20:47:20 +03:00
{
2007-10-16 00:14:19 +04:00
struct extent_buffer * left = path - > nodes [ 0 ] ;
struct extent_buffer * right ;
struct extent_buffer * upper ;
struct btrfs_disk_key disk_key ;
2007-02-24 20:47:20 +03:00
int slot ;
2007-11-07 21:31:03 +03:00
u32 i ;
2007-02-24 20:47:20 +03:00
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 ;
2007-11-07 21:31:03 +03:00
u32 nr ;
2007-03-12 19:01:18 +03:00
u32 right_nritems ;
2007-10-16 00:14:19 +04:00
u32 data_end ;
2007-10-16 00:15:53 +04:00
u32 this_item_size ;
2007-06-22 22:16:25 +04:00
int ret ;
2007-02-24 20:47:20 +03:00
slot = path - > slots [ 1 ] ;
if ( ! path - > nodes [ 1 ] ) {
return 1 ;
}
upper = path - > nodes [ 1 ] ;
2007-10-16 00:14:19 +04:00
if ( slot > = btrfs_header_nritems ( upper ) - 1 )
2007-02-24 20:47:20 +03:00
return 1 ;
2007-10-16 00:14:19 +04:00
2008-06-26 00:01:30 +04:00
WARN_ON ( ! btrfs_tree_locked ( path - > nodes [ 1 ] ) ) ;
2008-05-12 20:59:19 +04:00
right = read_node_slot ( root , upper , slot + 1 ) ;
2008-06-26 00:01:30 +04:00
btrfs_tree_lock ( right ) ;
2007-03-14 21:14:43 +03:00
free_space = btrfs_leaf_free_space ( root , right ) ;
2008-06-26 00:01:30 +04:00
if ( free_space < data_size + sizeof ( struct btrfs_item ) )
goto out_unlock ;
2007-10-16 00:14:19 +04:00
2007-03-03 00:08:05 +03:00
/* cow and double check */
2007-10-16 00:14:19 +04:00
ret = btrfs_cow_block ( trans , root , right , upper ,
slot + 1 , & right ) ;
2008-06-26 00:01:30 +04:00
if ( ret )
goto out_unlock ;
2007-03-14 21:14:43 +03:00
free_space = btrfs_leaf_free_space ( root , right ) ;
2008-06-26 00:01:30 +04:00
if ( free_space < data_size + sizeof ( struct btrfs_item ) )
goto out_unlock ;
2007-03-03 00:08:05 +03:00
2007-10-16 00:14:19 +04:00
left_nritems = btrfs_header_nritems ( left ) ;
2008-06-26 00:01:30 +04:00
if ( left_nritems = = 0 )
goto out_unlock ;
2007-10-16 00:14:19 +04:00
2007-11-07 21:31:03 +03:00
if ( empty )
nr = 0 ;
else
nr = 1 ;
i = left_nritems - 1 ;
while ( i > = nr ) {
2007-10-16 00:14:19 +04:00
item = btrfs_item_nr ( left , i ) ;
2007-10-16 00:15:53 +04:00
2007-02-24 20:47:20 +03:00
if ( path - > slots [ 0 ] = = i )
push_space + = data_size + sizeof ( * item ) ;
2007-10-16 00:15:53 +04:00
if ( ! left - > map_token ) {
map_extent_buffer ( left , ( unsigned long ) item ,
sizeof ( struct btrfs_item ) ,
& left - > map_token , & left - > kaddr ,
& left - > map_start , & left - > map_len ,
KM_USER1 ) ;
}
this_item_size = btrfs_item_size ( left , item ) ;
if ( this_item_size + sizeof ( * item ) + push_space > free_space )
2007-02-24 20:47:20 +03:00
break ;
push_items + + ;
2007-10-16 00:15:53 +04:00
push_space + = this_item_size + sizeof ( * item ) ;
2007-11-07 21:31:03 +03:00
if ( i = = 0 )
break ;
i - - ;
2007-10-16 00:15:53 +04:00
}
if ( left - > map_token ) {
unmap_extent_buffer ( left , left - > map_token , KM_USER1 ) ;
left - > map_token = NULL ;
2007-02-24 20:47:20 +03:00
}
2007-10-16 00:14:19 +04:00
2008-06-26 00:01:30 +04:00
if ( push_items = = 0 )
goto out_unlock ;
2007-10-16 00:14:19 +04:00
2007-11-07 21:31:03 +03:00
if ( ! empty & & push_items = = left_nritems )
2007-04-19 00:15:28 +04:00
WARN_ON ( 1 ) ;
2007-10-16 00:14:19 +04:00
2007-02-24 20:47:20 +03:00
/* push left to right */
2007-10-16 00:14:19 +04:00
right_nritems = btrfs_header_nritems ( right ) ;
2007-11-07 21:31:03 +03:00
2007-10-16 00:14:19 +04:00
push_space = btrfs_item_end_nr ( left , left_nritems - push_items ) ;
2007-03-14 21:14:43 +03:00
push_space - = leaf_data_end ( root , left ) ;
2007-10-16 00:14:19 +04:00
2007-02-24 20:47:20 +03:00
/* make room in the right data area */
2007-10-16 00:14:19 +04:00
data_end = leaf_data_end ( root , right ) ;
memmove_extent_buffer ( right ,
btrfs_leaf_data ( right ) + data_end - push_space ,
btrfs_leaf_data ( right ) + data_end ,
BTRFS_LEAF_DATA_SIZE ( root ) - data_end ) ;
2007-02-24 20:47:20 +03:00
/* copy from the left data area */
2007-10-16 00:14:19 +04:00
copy_extent_buffer ( right , left , btrfs_leaf_data ( right ) +
2007-03-30 22:27:56 +04:00
BTRFS_LEAF_DATA_SIZE ( root ) - push_space ,
btrfs_leaf_data ( left ) + leaf_data_end ( root , left ) ,
push_space ) ;
2007-10-16 00:14:19 +04:00
memmove_extent_buffer ( right , btrfs_item_nr_offset ( push_items ) ,
btrfs_item_nr_offset ( 0 ) ,
right_nritems * sizeof ( struct btrfs_item ) ) ;
2007-02-24 20:47:20 +03:00
/* copy the items from left to right */
2007-10-16 00:14:19 +04:00
copy_extent_buffer ( right , left , btrfs_item_nr_offset ( 0 ) ,
btrfs_item_nr_offset ( left_nritems - push_items ) ,
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 ;
2007-10-16 00:14:19 +04:00
btrfs_set_header_nritems ( right , 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-10-16 00:14:19 +04:00
item = btrfs_item_nr ( right , i ) ;
2007-10-16 00:15:53 +04:00
if ( ! right - > map_token ) {
map_extent_buffer ( right , ( unsigned long ) item ,
sizeof ( struct btrfs_item ) ,
& right - > map_token , & right - > kaddr ,
& right - > map_start , & right - > map_len ,
KM_USER1 ) ;
}
push_space - = btrfs_item_size ( right , item ) ;
btrfs_set_item_offset ( right , item , push_space ) ;
}
if ( right - > map_token ) {
unmap_extent_buffer ( right , right - > map_token , KM_USER1 ) ;
right - > map_token = NULL ;
2007-02-24 20:47:20 +03:00
}
2007-03-12 19:01:18 +03:00
left_nritems - = push_items ;
2007-10-16 00:14:19 +04:00
btrfs_set_header_nritems ( left , left_nritems ) ;
2007-02-24 20:47:20 +03:00
2007-11-07 21:31:03 +03:00
if ( left_nritems )
btrfs_mark_buffer_dirty ( left ) ;
2007-10-16 00:14:19 +04:00
btrfs_mark_buffer_dirty ( right ) ;
2007-04-19 00:15:28 +04:00
2007-10-16 00:14:19 +04:00
btrfs_item_key ( right , & disk_key , 0 ) ;
btrfs_set_node_key ( upper , & disk_key , slot + 1 ) ;
2007-03-30 22:27:56 +04:00
btrfs_mark_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 ;
2008-06-26 00:01:30 +04:00
if ( btrfs_header_nritems ( path - > nodes [ 0 ] ) = = 0 )
clean_tree_block ( trans , root , path - > nodes [ 0 ] ) ;
btrfs_tree_unlock ( path - > nodes [ 0 ] ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( path - > nodes [ 0 ] ) ;
path - > nodes [ 0 ] = right ;
2007-02-24 20:47:20 +03:00
path - > slots [ 1 ] + = 1 ;
} else {
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( right ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( right ) ;
2007-02-24 20:47:20 +03:00
}
return 0 ;
2008-06-26 00:01:30 +04:00
out_unlock :
btrfs_tree_unlock ( right ) ;
free_extent_buffer ( right ) ;
return 1 ;
2007-02-24 20:47:20 +03:00
}
2008-06-26 00:01:30 +04:00
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
2007-11-07 21:31:03 +03:00
* root , struct btrfs_path * path , int data_size ,
int empty )
2007-01-26 23:51:26 +03:00
{
2007-10-16 00:14:19 +04:00
struct btrfs_disk_key disk_key ;
struct extent_buffer * right = path - > nodes [ 0 ] ;
struct extent_buffer * 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-10-16 00:14:19 +04:00
u32 right_nritems ;
2007-11-07 21:31:03 +03:00
u32 nr ;
2007-03-01 00:35:06 +03:00
int ret = 0 ;
int wret ;
2007-10-16 00:15:53 +04:00
u32 this_item_size ;
u32 old_left_item_size ;
2007-01-26 23:51:26 +03:00
slot = path - > slots [ 1 ] ;
2007-10-16 00:14:19 +04:00
if ( slot = = 0 )
2007-01-26 23:51:26 +03:00
return 1 ;
2007-10-16 00:14:19 +04:00
if ( ! path - > nodes [ 1 ] )
2007-01-26 23:51:26 +03:00
return 1 ;
2007-10-16 00:14:19 +04:00
2007-10-19 17:23:27 +04:00
right_nritems = btrfs_header_nritems ( right ) ;
if ( right_nritems = = 0 ) {
return 1 ;
}
2008-06-26 00:01:30 +04:00
WARN_ON ( ! btrfs_tree_locked ( path - > nodes [ 1 ] ) ) ;
2008-05-12 20:59:19 +04:00
left = read_node_slot ( root , path - > nodes [ 1 ] , slot - 1 ) ;
2008-06-26 00:01:30 +04:00
btrfs_tree_lock ( left ) ;
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 ) ) {
2008-06-26 00:01:30 +04:00
ret = 1 ;
goto out ;
2007-01-26 23:51:26 +03:00
}
2007-03-03 00:08:05 +03:00
/* cow and double check */
2007-10-16 00:14:19 +04:00
ret = btrfs_cow_block ( trans , root , left ,
path - > nodes [ 1 ] , slot - 1 , & left ) ;
2007-06-22 22:16:25 +04:00
if ( ret ) {
/* we hit -ENOSPC, but it isn't fatal here */
2008-06-26 00:01:30 +04:00
ret = 1 ;
goto out ;
2007-06-22 22:16:25 +04:00
}
2007-10-19 17:23:27 +04:00
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 ) ) {
2008-06-26 00:01:30 +04:00
ret = 1 ;
goto out ;
2007-03-03 00:08:05 +03:00
}
2007-11-07 21:31:03 +03:00
if ( empty )
nr = right_nritems ;
else
nr = right_nritems - 1 ;
for ( i = 0 ; i < nr ; i + + ) {
2007-10-16 00:14:19 +04:00
item = btrfs_item_nr ( right , i ) ;
2007-10-16 00:15:53 +04:00
if ( ! right - > map_token ) {
map_extent_buffer ( right , ( unsigned long ) item ,
sizeof ( struct btrfs_item ) ,
& right - > map_token , & right - > kaddr ,
& right - > map_start , & right - > map_len ,
KM_USER1 ) ;
}
2007-01-26 23:51:26 +03:00
if ( path - > slots [ 0 ] = = i )
push_space + = data_size + sizeof ( * item ) ;
2007-10-16 00:15:53 +04:00
this_item_size = btrfs_item_size ( right , item ) ;
if ( this_item_size + sizeof ( * item ) + push_space > free_space )
2007-01-26 23:51:26 +03:00
break ;
2007-10-16 00:15:53 +04:00
2007-01-26 23:51:26 +03:00
push_items + + ;
2007-10-16 00:15:53 +04:00
push_space + = this_item_size + sizeof ( * item ) ;
}
if ( right - > map_token ) {
unmap_extent_buffer ( right , right - > map_token , KM_USER1 ) ;
right - > map_token = NULL ;
2007-01-26 23:51:26 +03:00
}
2007-10-16 00:15:53 +04:00
2007-01-26 23:51:26 +03:00
if ( push_items = = 0 ) {
2008-06-26 00:01:30 +04:00
ret = 1 ;
goto out ;
2007-01-26 23:51:26 +03:00
}
2007-11-07 21:31:03 +03:00
if ( ! empty & & push_items = = btrfs_header_nritems ( right ) )
2007-04-19 00:15:28 +04:00
WARN_ON ( 1 ) ;
2007-10-16 00:14:19 +04:00
2007-01-26 23:51:26 +03:00
/* push data from right to left */
2007-10-16 00:14:19 +04:00
copy_extent_buffer ( left , right ,
btrfs_item_nr_offset ( btrfs_header_nritems ( left ) ) ,
btrfs_item_nr_offset ( 0 ) ,
push_items * sizeof ( struct btrfs_item ) ) ;
2007-03-14 21:14:43 +03:00
push_space = BTRFS_LEAF_DATA_SIZE ( root ) -
2007-10-16 00:14:19 +04:00
btrfs_item_offset_nr ( right , push_items - 1 ) ;
copy_extent_buffer ( left , right , btrfs_leaf_data ( left ) +
2007-03-30 22:27:56 +04:00
leaf_data_end ( root , left ) - push_space ,
btrfs_leaf_data ( right ) +
2007-10-16 00:14:19 +04:00
btrfs_item_offset_nr ( right , push_items - 1 ) ,
2007-03-30 22:27:56 +04:00
push_space ) ;
2007-10-16 00:14:19 +04:00
old_left_nritems = btrfs_header_nritems ( left ) ;
2007-02-02 17:18:22 +03:00
BUG_ON ( old_left_nritems < 0 ) ;
2007-10-16 00:15:53 +04:00
old_left_item_size = btrfs_item_offset_nr ( left , old_left_nritems - 1 ) ;
2007-03-13 03:12:07 +03:00
for ( i = old_left_nritems ; i < old_left_nritems + push_items ; i + + ) {
2007-10-16 00:14:19 +04:00
u32 ioff ;
2007-10-16 00:15:53 +04:00
2007-10-16 00:14:19 +04:00
item = btrfs_item_nr ( left , i ) ;
2007-10-16 00:15:53 +04:00
if ( ! left - > map_token ) {
map_extent_buffer ( left , ( unsigned long ) item ,
sizeof ( struct btrfs_item ) ,
& left - > map_token , & left - > kaddr ,
& left - > map_start , & left - > map_len ,
KM_USER1 ) ;
}
2007-10-16 00:14:19 +04:00
ioff = btrfs_item_offset ( left , item ) ;
btrfs_set_item_offset ( left , item ,
2007-10-16 00:15:53 +04:00
ioff - ( BTRFS_LEAF_DATA_SIZE ( root ) - old_left_item_size ) ) ;
2007-01-26 23:51:26 +03:00
}
2007-10-16 00:14:19 +04:00
btrfs_set_header_nritems ( left , old_left_nritems + push_items ) ;
2007-10-16 00:15:53 +04:00
if ( left - > map_token ) {
unmap_extent_buffer ( left , left - > map_token , KM_USER1 ) ;
left - > map_token = NULL ;
}
2007-01-26 23:51:26 +03:00
/* fixup right node */
2007-11-07 21:31:03 +03:00
if ( push_items > right_nritems ) {
printk ( " push items %d nr %u \n " , push_items , right_nritems ) ;
WARN_ON ( 1 ) ;
}
if ( push_items < right_nritems ) {
push_space = btrfs_item_offset_nr ( right , push_items - 1 ) -
leaf_data_end ( root , right ) ;
memmove_extent_buffer ( right , btrfs_leaf_data ( right ) +
BTRFS_LEAF_DATA_SIZE ( root ) - push_space ,
btrfs_leaf_data ( right ) +
leaf_data_end ( root , right ) , push_space ) ;
memmove_extent_buffer ( right , btrfs_item_nr_offset ( 0 ) ,
2007-10-16 00:14:19 +04:00
btrfs_item_nr_offset ( push_items ) ,
( btrfs_header_nritems ( right ) - push_items ) *
sizeof ( struct btrfs_item ) ) ;
2007-11-07 21:31:03 +03:00
}
2007-11-26 18:58:13 +03:00
right_nritems - = push_items ;
btrfs_set_header_nritems ( right , right_nritems ) ;
2007-03-14 21:14:43 +03:00
push_space = BTRFS_LEAF_DATA_SIZE ( root ) ;
2007-10-16 00:14:19 +04:00
for ( i = 0 ; i < right_nritems ; i + + ) {
item = btrfs_item_nr ( right , i ) ;
2007-10-16 00:15:53 +04:00
if ( ! right - > map_token ) {
map_extent_buffer ( right , ( unsigned long ) item ,
sizeof ( struct btrfs_item ) ,
& right - > map_token , & right - > kaddr ,
& right - > map_start , & right - > map_len ,
KM_USER1 ) ;
}
push_space = push_space - btrfs_item_size ( right , item ) ;
btrfs_set_item_offset ( right , item , push_space ) ;
}
if ( right - > map_token ) {
unmap_extent_buffer ( right , right - > map_token , KM_USER1 ) ;
right - > map_token = NULL ;
2007-01-26 23:51:26 +03:00
}
2007-02-02 17:18:22 +03:00
2007-10-16 00:14:19 +04:00
btrfs_mark_buffer_dirty ( left ) ;
2007-11-07 21:31:03 +03:00
if ( right_nritems )
btrfs_mark_buffer_dirty ( right ) ;
2007-05-11 19:33:21 +04:00
2007-10-16 00:14:19 +04:00
btrfs_item_key ( right , & disk_key , 0 ) ;
wret = fixup_low_keys ( trans , root , path , & disk_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 ;
2008-06-26 00:01:30 +04:00
if ( btrfs_header_nritems ( path - > nodes [ 0 ] ) = = 0 )
clean_tree_block ( trans , root , path - > nodes [ 0 ] ) ;
btrfs_tree_unlock ( path - > nodes [ 0 ] ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( path - > nodes [ 0 ] ) ;
path - > nodes [ 0 ] = left ;
2007-01-26 23:51:26 +03:00
path - > slots [ 1 ] - = 1 ;
} else {
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( left ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( left ) ;
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 ;
2008-06-26 00:01:30 +04:00
out :
btrfs_tree_unlock ( left ) ;
free_extent_buffer ( left ) ;
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
2007-04-04 22:08:15 +04:00
* root , struct btrfs_key * ins_key ,
2007-10-25 23:42:57 +04:00
struct btrfs_path * path , int data_size , int extend )
2007-01-26 23:51:26 +03:00
{
2007-12-11 17:25:06 +03:00
u64 root_gen ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * l ;
2007-03-12 19:01:18 +03:00
u32 nritems ;
2007-02-02 17:18:22 +03:00
int mid ;
int slot ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * right ;
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 ;
2007-04-04 22:08:15 +04:00
int ret = 0 ;
2007-03-01 00:35:06 +03:00
int wret ;
2007-10-25 23:42:57 +04:00
int double_split ;
int num_doubles = 0 ;
2007-04-04 22:08:15 +04:00
struct btrfs_disk_key disk_key ;
2007-03-01 00:35:06 +03:00
2007-10-25 23:42:57 +04:00
if ( extend )
space_needed = data_size ;
2007-12-11 17:25:06 +03:00
if ( root - > ref_cows )
root_gen = trans - > transid ;
else
root_gen = 0 ;
2007-03-17 21:29:23 +03:00
/* first try to make some room by pushing left and right */
2007-10-19 17:23:27 +04:00
if ( ins_key - > type ! = BTRFS_DIR_ITEM_KEY ) {
2007-11-07 21:31:03 +03:00
wret = push_leaf_right ( trans , root , path , data_size , 0 ) ;
2007-10-19 17:23:27 +04:00
if ( wret < 0 ) {
2007-03-13 18:17:52 +03:00
return wret ;
2007-10-19 17:23:27 +04:00
}
if ( wret ) {
2007-11-07 21:31:03 +03:00
wret = push_leaf_left ( trans , root , path , data_size , 0 ) ;
2007-10-19 17:23:27 +04:00
if ( wret < 0 )
return wret ;
}
l = path - > nodes [ 0 ] ;
2007-03-01 00:35:06 +03:00
2007-10-19 17:23:27 +04:00
/* did the pushes work? */
2007-10-25 23:42:57 +04:00
if ( btrfs_leaf_free_space ( root , l ) > = space_needed )
2007-10-19 17:23:27 +04:00
return 0 ;
2007-10-16 00:18:25 +04:00
}
2007-03-01 00:35:06 +03:00
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-10-25 23:42:57 +04:00
again :
double_split = 0 ;
l = path - > nodes [ 0 ] ;
2007-02-02 17:18:22 +03:00
slot = path - > slots [ 0 ] ;
2007-10-16 00:14:19 +04:00
nritems = btrfs_header_nritems ( l ) ;
2007-02-02 17:18:22 +03:00
mid = ( nritems + 1 ) / 2 ;
2007-06-22 22:16:25 +04:00
2007-12-11 17:25:06 +03:00
btrfs_item_key ( l , & disk_key , 0 ) ;
2008-06-26 00:01:30 +04:00
right = btrfs_alloc_free_block ( trans , root , root - > leafsize ,
2007-12-11 17:25:06 +03:00
root - > root_key . objectid ,
root_gen , disk_key . objectid , 0 ,
l - > start , 0 ) ;
2008-04-10 00:28:12 +04:00
if ( IS_ERR ( right ) ) {
BUG_ON ( 1 ) ;
2007-10-16 00:14:19 +04:00
return PTR_ERR ( right ) ;
2008-04-10 00:28:12 +04:00
}
2007-10-16 00:14:19 +04:00
memset_extent_buffer ( right , 0 , 0 , sizeof ( struct btrfs_header ) ) ;
2007-10-16 00:15:53 +04:00
btrfs_set_header_bytenr ( right , right - > start ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_header_generation ( right , trans - > transid ) ;
btrfs_set_header_owner ( right , root - > root_key . objectid ) ;
btrfs_set_header_level ( right , 0 ) ;
write_extent_buffer ( right , root - > fs_info - > fsid ,
( unsigned long ) btrfs_header_fsid ( right ) ,
BTRFS_FSID_SIZE ) ;
2008-04-15 23:41:47 +04:00
write_extent_buffer ( right , root - > fs_info - > chunk_tree_uuid ,
( unsigned long ) btrfs_header_chunk_tree_uuid ( right ) ,
BTRFS_UUID_SIZE ) ;
2007-04-04 22:08:15 +04: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 ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_header_nritems ( right , 0 ) ;
2007-04-04 22:08:15 +04:00
wret = insert_ptr ( trans , root , path ,
2007-10-16 00:15:53 +04:00
& disk_key , right - > start ,
2007-04-04 22:08:15 +04:00
path - > slots [ 1 ] + 1 , 1 ) ;
if ( wret )
ret = wret ;
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( path - > nodes [ 0 ] ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( path - > nodes [ 0 ] ) ;
path - > nodes [ 0 ] = right ;
2007-04-04 22:08:15 +04:00
path - > slots [ 0 ] = 0 ;
path - > slots [ 1 ] + = 1 ;
2008-04-04 00:29:02 +04:00
btrfs_mark_buffer_dirty ( right ) ;
2007-04-04 22:08:15 +04:00
return ret ;
}
mid = slot ;
2007-10-16 00:18:25 +04:00
if ( mid ! = nritems & &
leaf_space_used ( l , mid , nritems - mid ) +
space_needed > BTRFS_LEAF_DATA_SIZE ( root ) ) {
double_split = 1 ;
}
2007-04-04 22:08:15 +04:00
}
} else {
if ( leaf_space_used ( l , 0 , mid + 1 ) + space_needed >
BTRFS_LEAF_DATA_SIZE ( root ) ) {
2007-10-25 23:42:57 +04:00
if ( ! extend & & slot = = 0 ) {
2007-04-04 22:08:15 +04:00
btrfs_cpu_key_to_disk ( & disk_key , ins_key ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_header_nritems ( right , 0 ) ;
2007-04-04 22:08:15 +04:00
wret = insert_ptr ( trans , root , path ,
& disk_key ,
2007-10-16 00:15:53 +04:00
right - > start ,
2007-05-11 19:33:21 +04:00
path - > slots [ 1 ] , 1 ) ;
2007-04-04 22:08:15 +04:00
if ( wret )
ret = wret ;
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( path - > nodes [ 0 ] ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( path - > nodes [ 0 ] ) ;
path - > nodes [ 0 ] = right ;
2007-04-04 22:08:15 +04:00
path - > slots [ 0 ] = 0 ;
2007-04-19 00:15:28 +04:00
if ( path - > slots [ 1 ] = = 0 ) {
wret = fixup_low_keys ( trans , root ,
path , & disk_key , 1 ) ;
if ( wret )
ret = wret ;
}
2008-04-04 00:29:02 +04:00
btrfs_mark_buffer_dirty ( right ) ;
2007-04-04 22:08:15 +04:00
return ret ;
2007-10-25 23:42:57 +04:00
} else if ( extend & & slot = = 0 ) {
mid = 1 ;
} else {
mid = slot ;
if ( mid ! = nritems & &
leaf_space_used ( l , mid , nritems - mid ) +
space_needed > BTRFS_LEAF_DATA_SIZE ( root ) ) {
double_split = 1 ;
}
2007-10-19 22:01:21 +04:00
}
2007-04-04 22:08:15 +04:00
}
}
2007-10-16 00:14:19 +04:00
nritems = nritems - mid ;
btrfs_set_header_nritems ( right , nritems ) ;
data_copy_size = btrfs_item_end_nr ( l , mid ) - leaf_data_end ( root , l ) ;
copy_extent_buffer ( right , l , btrfs_item_nr_offset ( 0 ) ,
btrfs_item_nr_offset ( mid ) ,
nritems * sizeof ( struct btrfs_item ) ) ;
copy_extent_buffer ( right , l ,
2007-03-30 22:27:56 +04:00
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-10-16 00:14:19 +04:00
2007-03-14 21:14:43 +03:00
rt_data_off = BTRFS_LEAF_DATA_SIZE ( root ) -
2007-10-16 00:14:19 +04:00
btrfs_item_end_nr ( l , mid ) ;
2007-02-02 19:05:29 +03:00
2007-10-16 00:14:19 +04:00
for ( i = 0 ; i < nritems ; i + + ) {
struct btrfs_item * item = btrfs_item_nr ( right , i ) ;
2007-10-16 00:15:53 +04:00
u32 ioff ;
if ( ! right - > map_token ) {
map_extent_buffer ( right , ( unsigned long ) item ,
sizeof ( struct btrfs_item ) ,
& right - > map_token , & right - > kaddr ,
& right - > map_start , & right - > map_len ,
KM_USER1 ) ;
}
ioff = btrfs_item_offset ( right , item ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_item_offset ( right , item , ioff + rt_data_off ) ;
2007-03-13 03:12:07 +03:00
}
2007-02-02 19:05:29 +03:00
2007-10-16 00:15:53 +04:00
if ( right - > map_token ) {
unmap_extent_buffer ( right , right - > map_token , KM_USER1 ) ;
right - > map_token = NULL ;
}
2007-10-16 00:14:19 +04:00
btrfs_set_header_nritems ( l , mid ) ;
2007-03-01 00:35:06 +03:00
ret = 0 ;
2007-10-16 00:14:19 +04:00
btrfs_item_key ( right , & disk_key , 0 ) ;
2007-10-16 00:15:53 +04:00
wret = insert_ptr ( trans , root , path , & disk_key , right - > start ,
path - > slots [ 1 ] + 1 , 1 ) ;
2007-03-01 00:35:06 +03:00
if ( wret )
ret = wret ;
2007-10-16 00:14:19 +04:00
btrfs_mark_buffer_dirty ( right ) ;
btrfs_mark_buffer_dirty ( l ) ;
2007-02-02 17:18:22 +03:00
BUG_ON ( path - > slots [ 0 ] ! = slot ) ;
2007-10-16 00:14:19 +04:00
2007-01-26 23:51:26 +03:00
if ( mid < = slot ) {
2008-06-26 00:01:30 +04:00
btrfs_tree_unlock ( path - > nodes [ 0 ] ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( path - > nodes [ 0 ] ) ;
path - > nodes [ 0 ] = right ;
2007-01-26 23:51:26 +03:00
path - > slots [ 0 ] - = mid ;
path - > slots [ 1 ] + = 1 ;
2008-06-26 00:01:30 +04:00
} else {
btrfs_tree_unlock ( right ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( right ) ;
2008-06-26 00:01:30 +04:00
}
2007-10-16 00:14:19 +04:00
2007-02-02 17:18:22 +03:00
BUG_ON ( path - > slots [ 0 ] < 0 ) ;
2007-04-04 22:08:15 +04:00
2007-10-25 23:42:57 +04:00
if ( double_split ) {
BUG_ON ( num_doubles ! = 0 ) ;
num_doubles + + ;
goto again ;
2007-04-19 00:15:28 +04:00
}
2007-01-26 23:51:26 +03:00
return ret ;
}
2007-04-17 21:26:50 +04:00
int btrfs_truncate_item ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct btrfs_path * path ,
2007-11-01 18:28:41 +03:00
u32 new_size , int from_end )
2007-04-17 21:26:50 +04:00
{
int ret = 0 ;
int slot ;
int slot_orig ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * leaf ;
struct btrfs_item * item ;
2007-04-17 21:26:50 +04:00
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 ] ;
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
2007-11-01 18:28:41 +03:00
slot = path - > slots [ 0 ] ;
old_size = btrfs_item_size_nr ( leaf , slot ) ;
if ( old_size = = new_size )
return 0 ;
2007-04-17 21:26:50 +04:00
2007-10-16 00:14:19 +04:00
nritems = btrfs_header_nritems ( leaf ) ;
2007-04-17 21:26:50 +04:00
data_end = leaf_data_end ( root , leaf ) ;
2007-10-16 00:14:19 +04:00
old_data_start = btrfs_item_offset_nr ( leaf , slot ) ;
2007-11-01 18:28:41 +03:00
2007-04-17 21:26:50 +04:00
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 + + ) {
2007-10-16 00:14:19 +04:00
u32 ioff ;
item = btrfs_item_nr ( leaf , i ) ;
2007-10-16 00:15:53 +04:00
if ( ! leaf - > map_token ) {
map_extent_buffer ( leaf , ( unsigned long ) item ,
sizeof ( struct btrfs_item ) ,
& leaf - > map_token , & leaf - > kaddr ,
& leaf - > map_start , & leaf - > map_len ,
KM_USER1 ) ;
}
2007-10-16 00:14:19 +04:00
ioff = btrfs_item_offset ( leaf , item ) ;
btrfs_set_item_offset ( leaf , item , ioff + size_diff ) ;
2007-04-17 21:26:50 +04:00
}
2007-10-16 00:15:53 +04:00
if ( leaf - > map_token ) {
unmap_extent_buffer ( leaf , leaf - > map_token , KM_USER1 ) ;
leaf - > map_token = NULL ;
}
2007-04-17 21:26:50 +04:00
/* shift the data */
2007-11-01 18:28:41 +03:00
if ( from_end ) {
memmove_extent_buffer ( leaf , btrfs_leaf_data ( leaf ) +
data_end + size_diff , btrfs_leaf_data ( leaf ) +
data_end , old_data_start + new_size - data_end ) ;
} else {
struct btrfs_disk_key disk_key ;
u64 offset ;
btrfs_item_key ( leaf , & disk_key , slot ) ;
if ( btrfs_disk_key_type ( & disk_key ) = = BTRFS_EXTENT_DATA_KEY ) {
unsigned long ptr ;
struct btrfs_file_extent_item * fi ;
fi = btrfs_item_ptr ( leaf , slot ,
struct btrfs_file_extent_item ) ;
fi = ( struct btrfs_file_extent_item * ) (
( unsigned long ) fi - size_diff ) ;
if ( btrfs_file_extent_type ( leaf , fi ) = =
BTRFS_FILE_EXTENT_INLINE ) {
ptr = btrfs_item_ptr_offset ( leaf , slot ) ;
memmove_extent_buffer ( leaf , ptr ,
( unsigned long ) fi ,
offsetof ( struct btrfs_file_extent_item ,
disk_bytenr ) ) ;
}
}
memmove_extent_buffer ( leaf , btrfs_leaf_data ( leaf ) +
data_end + size_diff , btrfs_leaf_data ( leaf ) +
data_end , old_data_start - data_end ) ;
offset = btrfs_disk_key_offset ( & disk_key ) ;
btrfs_set_disk_key_offset ( & disk_key , offset + size_diff ) ;
btrfs_set_item_key ( leaf , & disk_key , slot ) ;
if ( slot = = 0 )
fixup_low_keys ( trans , root , path , & disk_key , 1 ) ;
}
2007-10-16 00:14:19 +04:00
item = btrfs_item_nr ( leaf , slot ) ;
btrfs_set_item_size ( leaf , item , new_size ) ;
btrfs_mark_buffer_dirty ( leaf ) ;
2007-04-17 21:26:50 +04:00
ret = 0 ;
2007-10-16 00:14:19 +04:00
if ( btrfs_leaf_free_space ( root , leaf ) < 0 ) {
btrfs_print_leaf ( root , leaf ) ;
2007-04-17 21:26:50 +04:00
BUG ( ) ;
2007-10-16 00:14:19 +04:00
}
2007-04-17 21:26:50 +04:00
return ret ;
}
2007-10-16 00:14:19 +04:00
int btrfs_extend_item ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root , struct btrfs_path * path ,
u32 data_size )
2007-04-16 17:22:45 +04:00
{
int ret = 0 ;
int slot ;
int slot_orig ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * leaf ;
struct btrfs_item * item ;
2007-04-16 17:22:45 +04:00
u32 nritems ;
unsigned int data_end ;
unsigned int old_data ;
unsigned int old_size ;
int i ;
slot_orig = path - > slots [ 0 ] ;
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
2007-04-16 17:22:45 +04:00
2007-10-16 00:14:19 +04:00
nritems = btrfs_header_nritems ( leaf ) ;
2007-04-16 17:22:45 +04:00
data_end = leaf_data_end ( root , leaf ) ;
2007-10-16 00:14:19 +04:00
if ( btrfs_leaf_free_space ( root , leaf ) < data_size ) {
btrfs_print_leaf ( root , leaf ) ;
2007-04-16 17:22:45 +04:00
BUG ( ) ;
2007-10-16 00:14:19 +04:00
}
2007-04-16 17:22:45 +04:00
slot = path - > slots [ 0 ] ;
2007-10-16 00:14:19 +04:00
old_data = btrfs_item_end_nr ( leaf , slot ) ;
2007-04-16 17:22:45 +04:00
BUG_ON ( slot < 0 ) ;
2007-10-16 00:18:25 +04:00
if ( slot > = nritems ) {
btrfs_print_leaf ( root , leaf ) ;
printk ( " slot %d too large, nritems %d \n " , slot , nritems ) ;
BUG_ON ( 1 ) ;
}
2007-04-16 17:22:45 +04:00
/*
* item0 . . itemN . . . dataN . offset . . dataN . size . . data0 . size
*/
/* first correct the data pointers */
for ( i = slot ; i < nritems ; i + + ) {
2007-10-16 00:14:19 +04:00
u32 ioff ;
item = btrfs_item_nr ( leaf , i ) ;
2007-10-16 00:15:53 +04:00
if ( ! leaf - > map_token ) {
map_extent_buffer ( leaf , ( unsigned long ) item ,
sizeof ( struct btrfs_item ) ,
& leaf - > map_token , & leaf - > kaddr ,
& leaf - > map_start , & leaf - > map_len ,
KM_USER1 ) ;
}
2007-10-16 00:14:19 +04:00
ioff = btrfs_item_offset ( leaf , item ) ;
btrfs_set_item_offset ( leaf , item , ioff - data_size ) ;
2007-04-16 17:22:45 +04:00
}
2007-10-16 00:14:19 +04:00
2007-10-16 00:15:53 +04:00
if ( leaf - > map_token ) {
unmap_extent_buffer ( leaf , leaf - > map_token , KM_USER1 ) ;
leaf - > map_token = NULL ;
}
2007-04-16 17:22:45 +04:00
/* shift the data */
2007-10-16 00:14:19 +04:00
memmove_extent_buffer ( leaf , btrfs_leaf_data ( leaf ) +
2007-04-16 17:22:45 +04:00
data_end - data_size , btrfs_leaf_data ( leaf ) +
data_end , old_data - data_end ) ;
2007-10-16 00:14:19 +04:00
2007-04-16 17:22:45 +04:00
data_end = old_data ;
2007-10-16 00:14:19 +04:00
old_size = btrfs_item_size_nr ( leaf , slot ) ;
item = btrfs_item_nr ( leaf , slot ) ;
btrfs_set_item_size ( leaf , item , old_size + data_size ) ;
btrfs_mark_buffer_dirty ( leaf ) ;
2007-04-16 17:22:45 +04:00
ret = 0 ;
2007-10-16 00:14:19 +04:00
if ( btrfs_leaf_free_space ( root , leaf ) < 0 ) {
btrfs_print_leaf ( root , leaf ) ;
2007-04-16 17:22:45 +04:00
BUG ( ) ;
2007-10-16 00:14:19 +04:00
}
2007-04-16 17:22:45 +04: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 .
*/
2008-01-29 23:15:18 +03:00
int btrfs_insert_empty_items ( struct btrfs_trans_handle * trans ,
2007-10-16 00:14:19 +04:00
struct btrfs_root * root ,
struct btrfs_path * path ,
2008-01-29 23:15:18 +03:00
struct btrfs_key * cpu_key , u32 * data_size ,
int nr )
2007-01-26 23:51:26 +03:00
{
2007-10-16 00:14:19 +04:00
struct extent_buffer * leaf ;
struct btrfs_item * item ;
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 ;
2008-01-29 23:15:18 +03:00
int i ;
2007-03-12 19:01:18 +03:00
u32 nritems ;
2008-01-29 23:15:18 +03:00
u32 total_size = 0 ;
u32 total_data = 0 ;
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 ;
2008-01-29 23:15:18 +03:00
for ( i = 0 ; i < nr ; i + + ) {
total_data + = data_size [ i ] ;
}
2007-01-26 23:51:26 +03:00
2008-01-29 23:15:18 +03:00
total_size = total_data + ( nr - 1 ) * sizeof ( struct btrfs_item ) ;
ret = btrfs_search_slot ( trans , root , cpu_key , path , total_size , 1 ) ;
2007-02-02 17:18:22 +03:00
if ( ret = = 0 ) {
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 ] ;
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
2007-02-02 19:05:29 +03:00
2007-10-16 00:14:19 +04:00
nritems = btrfs_header_nritems ( leaf ) ;
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 ) <
2008-01-29 23:15:18 +03:00
sizeof ( struct btrfs_item ) + total_size ) {
2007-10-16 00:18:25 +04:00
btrfs_print_leaf ( root , leaf ) ;
printk ( " not enough freespace need %u have %d \n " ,
2008-01-29 23:15:18 +03:00
total_size , btrfs_leaf_free_space ( root , leaf ) ) ;
2007-01-26 23:51:26 +03:00
BUG ( ) ;
2007-04-04 22:08:15 +04:00
}
2007-10-16 00:14:19 +04:00
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-10-16 00:14:19 +04:00
2007-01-26 23:51:26 +03:00
if ( slot ! = nritems ) {
int i ;
2007-10-16 00:14:19 +04:00
unsigned int old_data = btrfs_item_end_nr ( leaf , slot ) ;
2007-01-26 23:51:26 +03:00
2007-10-16 00:14:19 +04:00
if ( old_data < data_end ) {
btrfs_print_leaf ( root , leaf ) ;
printk ( " slot %d old_data %d data_end %d \n " ,
slot , old_data , data_end ) ;
BUG_ON ( 1 ) ;
}
2007-01-26 23:51:26 +03:00
/*
* item0 . . itemN . . . dataN . offset . . dataN . size . . data0 . size
*/
/* first correct the data pointers */
2007-10-16 00:15:53 +04:00
WARN_ON ( leaf - > map_token ) ;
2007-03-13 03:12:07 +03:00
for ( i = slot ; i < nritems ; i + + ) {
2007-10-16 00:14:19 +04:00
u32 ioff ;
2007-10-16 00:15:53 +04:00
2007-10-16 00:14:19 +04:00
item = btrfs_item_nr ( leaf , i ) ;
2007-10-16 00:15:53 +04:00
if ( ! leaf - > map_token ) {
map_extent_buffer ( leaf , ( unsigned long ) item ,
sizeof ( struct btrfs_item ) ,
& leaf - > map_token , & leaf - > kaddr ,
& leaf - > map_start , & leaf - > map_len ,
KM_USER1 ) ;
}
2007-10-16 00:14:19 +04:00
ioff = btrfs_item_offset ( leaf , item ) ;
2008-01-29 23:15:18 +03:00
btrfs_set_item_offset ( leaf , item , ioff - total_data ) ;
2007-03-13 03:12:07 +03:00
}
2007-10-16 00:15:53 +04:00
if ( leaf - > map_token ) {
unmap_extent_buffer ( leaf , leaf - > map_token , KM_USER1 ) ;
leaf - > map_token = NULL ;
}
2007-01-26 23:51:26 +03:00
/* shift the items */
2008-01-29 23:15:18 +03:00
memmove_extent_buffer ( leaf , btrfs_item_nr_offset ( slot + nr ) ,
2007-10-16 00:14:19 +04:00
btrfs_item_nr_offset ( slot ) ,
2007-03-30 22:27:56 +04:00
( nritems - slot ) * sizeof ( struct btrfs_item ) ) ;
2007-01-26 23:51:26 +03:00
/* shift the data */
2007-10-16 00:14:19 +04:00
memmove_extent_buffer ( leaf , btrfs_leaf_data ( leaf ) +
2008-01-29 23:15:18 +03:00
data_end - total_data , btrfs_leaf_data ( leaf ) +
2007-03-30 22:27:56 +04:00
data_end , old_data - data_end ) ;
2007-01-26 23:51:26 +03:00
data_end = old_data ;
}
2007-10-16 00:14:19 +04:00
2007-03-15 19:56:47 +03:00
/* setup the item for the new data */
2008-01-29 23:15:18 +03:00
for ( i = 0 ; i < nr ; i + + ) {
btrfs_cpu_key_to_disk ( & disk_key , cpu_key + i ) ;
btrfs_set_item_key ( leaf , & disk_key , slot + i ) ;
item = btrfs_item_nr ( leaf , slot + i ) ;
btrfs_set_item_offset ( leaf , item , data_end - data_size [ i ] ) ;
data_end - = data_size [ i ] ;
btrfs_set_item_size ( leaf , item , data_size [ i ] ) ;
}
btrfs_set_header_nritems ( leaf , nritems + nr ) ;
2007-10-16 00:14:19 +04:00
btrfs_mark_buffer_dirty ( leaf ) ;
2007-03-01 00:35:06 +03:00
ret = 0 ;
2008-01-30 19:43:54 +03:00
if ( slot = = 0 ) {
btrfs_cpu_key_to_disk ( & disk_key , cpu_key ) ;
2007-03-16 23:20:31 +03:00
ret = fixup_low_keys ( trans , root , path , & disk_key , 1 ) ;
2008-01-30 19:43:54 +03:00
}
2007-03-01 00:35:06 +03:00
2007-10-16 00:14:19 +04:00
if ( btrfs_leaf_free_space ( root , leaf ) < 0 ) {
btrfs_print_leaf ( root , leaf ) ;
2007-01-26 23:51:26 +03:00
BUG ( ) ;
2007-10-16 00:14:19 +04:00
}
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 ;
2007-04-02 18:50:19 +04:00
struct btrfs_path * path ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * leaf ;
unsigned long ptr ;
2007-03-15 19:56:47 +03:00
2007-04-02 18:50:19 +04:00
path = btrfs_alloc_path ( ) ;
BUG_ON ( ! path ) ;
ret = btrfs_insert_empty_item ( trans , root , path , cpu_key , data_size ) ;
2007-03-15 19:56:47 +03:00
if ( ! ret ) {
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
ptr = btrfs_item_ptr_offset ( leaf , path - > slots [ 0 ] ) ;
write_extent_buffer ( leaf , data , ptr , data_size ) ;
btrfs_mark_buffer_dirty ( leaf ) ;
2007-03-15 19:56:47 +03:00
}
2007-04-02 18:50:19 +04:00
btrfs_free_path ( 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-10-16 00:14:19 +04:00
struct extent_buffer * 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-10-16 00:14:19 +04:00
nritems = btrfs_header_nritems ( parent ) ;
2007-03-01 20:04:21 +03:00
if ( slot ! = nritems - 1 ) {
2007-10-16 00:14:19 +04:00
memmove_extent_buffer ( parent ,
btrfs_node_key_ptr_offset ( slot ) ,
btrfs_node_key_ptr_offset ( slot + 1 ) ,
2007-03-30 22:27:56 +04:00
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 - - ;
2007-10-16 00:14:19 +04:00
btrfs_set_header_nritems ( parent , nritems ) ;
2007-03-12 19:01:18 +03:00
if ( nritems = = 0 & & parent = = root - > node ) {
2007-10-16 00:14:19 +04:00
BUG_ON ( btrfs_header_level ( root - > node ) ! = 1 ) ;
2007-03-01 20:04:21 +03:00
/* just turn the root into a leaf and break */
2007-10-16 00:14:19 +04:00
btrfs_set_header_level ( root - > node , 0 ) ;
2007-03-01 20:04:21 +03:00
} else if ( slot = = 0 ) {
2007-10-16 00:14:19 +04:00
struct btrfs_disk_key disk_key ;
btrfs_node_key ( parent , & disk_key , 0 ) ;
wret = fixup_low_keys ( trans , root , path , & disk_key , level + 1 ) ;
2007-03-01 00:46:22 +03:00
if ( wret )
ret = wret ;
2007-01-26 23:51:26 +03:00
}
2007-03-30 22:27:56 +04:00
btrfs_mark_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
*/
2008-01-29 23:11:36 +03:00
int btrfs_del_items ( struct btrfs_trans_handle * trans , struct btrfs_root * root ,
struct btrfs_path * path , int slot , int nr )
2007-01-26 23:51:26 +03:00
{
2007-10-16 00:14:19 +04:00
struct extent_buffer * leaf ;
struct btrfs_item * item ;
2008-01-29 23:11:36 +03:00
int last_off ;
int dsize = 0 ;
2007-03-01 00:35:06 +03:00
int ret = 0 ;
int wret ;
2008-01-29 23:11:36 +03:00
int i ;
2007-03-12 19:01:18 +03:00
u32 nritems ;
2007-01-26 23:51:26 +03:00
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
2008-01-29 23:11:36 +03:00
last_off = btrfs_item_offset_nr ( leaf , slot + nr - 1 ) ;
for ( i = 0 ; i < nr ; i + + )
dsize + = btrfs_item_size_nr ( leaf , slot + i ) ;
2007-10-16 00:14:19 +04:00
nritems = btrfs_header_nritems ( leaf ) ;
2007-01-26 23:51:26 +03:00
2008-01-29 23:11:36 +03:00
if ( slot + nr ! = nritems ) {
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 ) ;
2007-10-16 00:14:19 +04:00
memmove_extent_buffer ( leaf , btrfs_leaf_data ( leaf ) +
2007-03-30 22:27:56 +04:00
data_end + dsize ,
btrfs_leaf_data ( leaf ) + data_end ,
2008-01-29 23:11:36 +03:00
last_off - data_end ) ;
2007-10-16 00:14:19 +04:00
2008-01-29 23:11:36 +03:00
for ( i = slot + nr ; i < nritems ; i + + ) {
2007-10-16 00:14:19 +04:00
u32 ioff ;
2007-10-16 00:15:53 +04:00
2007-10-16 00:14:19 +04:00
item = btrfs_item_nr ( leaf , i ) ;
2007-10-16 00:15:53 +04:00
if ( ! leaf - > map_token ) {
map_extent_buffer ( leaf , ( unsigned long ) item ,
sizeof ( struct btrfs_item ) ,
& leaf - > map_token , & leaf - > kaddr ,
& leaf - > map_start , & leaf - > map_len ,
KM_USER1 ) ;
}
2007-10-16 00:14:19 +04:00
ioff = btrfs_item_offset ( leaf , item ) ;
btrfs_set_item_offset ( leaf , item , ioff + dsize ) ;
2007-03-13 03:12:07 +03:00
}
2007-10-16 00:15:53 +04:00
if ( leaf - > map_token ) {
unmap_extent_buffer ( leaf , leaf - > map_token , KM_USER1 ) ;
leaf - > map_token = NULL ;
}
2007-10-16 00:14:19 +04:00
memmove_extent_buffer ( leaf , btrfs_item_nr_offset ( slot ) ,
2008-01-29 23:11:36 +03:00
btrfs_item_nr_offset ( slot + nr ) ,
2007-03-30 22:27:56 +04:00
sizeof ( struct btrfs_item ) *
2008-01-29 23:11:36 +03:00
( nritems - slot - nr ) ) ;
2007-01-26 23:51:26 +03:00
}
2008-01-29 23:11:36 +03:00
btrfs_set_header_nritems ( leaf , nritems - nr ) ;
nritems - = nr ;
2007-10-16 00:14:19 +04:00
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-10-16 00:14:19 +04:00
if ( leaf = = root - > node ) {
btrfs_set_header_level ( leaf , 0 ) ;
2007-02-23 16:38:36 +03:00
} else {
2007-12-11 17:25:06 +03:00
u64 root_gen = btrfs_header_generation ( path - > nodes [ 1 ] ) ;
2007-03-16 23:20:31 +03:00
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-12-11 17:25:06 +03:00
leaf - > start , leaf - > len ,
btrfs_header_owner ( path - > nodes [ 1 ] ) ,
root_gen , 0 , 0 , 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-10-16 00:14:19 +04:00
struct btrfs_disk_key disk_key ;
btrfs_item_key ( leaf , & disk_key , 0 ) ;
2007-03-16 23:20:31 +03:00
wret = fixup_low_keys ( trans , root , path ,
2007-10-16 00:14:19 +04:00
& disk_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 */
2008-01-29 23:11:36 +03:00
if ( used < BTRFS_LEAF_DATA_SIZE ( root ) / 4 ) {
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-10-16 00:14:19 +04:00
extent_buffer_get ( leaf ) ;
2008-01-29 23:11:36 +03:00
wret = push_leaf_left ( trans , root , path , 1 , 1 ) ;
2007-06-22 22:16:25 +04:00
if ( wret < 0 & & wret ! = - ENOSPC )
2007-03-01 00:35:06 +03:00
ret = wret ;
2007-10-16 00:14:19 +04:00
if ( path - > nodes [ 0 ] = = leaf & &
btrfs_header_nritems ( leaf ) ) {
2008-01-29 23:11:36 +03:00
wret = push_leaf_right ( trans , root , path , 1 , 1 ) ;
2007-06-22 22:16:25 +04:00
if ( wret < 0 & & wret ! = - ENOSPC )
2007-03-01 00:35:06 +03:00
ret = wret ;
}
2007-10-16 00:14:19 +04:00
if ( btrfs_header_nritems ( leaf ) = = 0 ) {
2007-12-11 17:25:06 +03:00
u64 root_gen ;
2007-10-16 00:15:53 +04:00
u64 bytenr = leaf - > start ;
u32 blocksize = leaf - > len ;
2007-10-16 00:14:19 +04:00
2007-12-11 17:25:06 +03:00
root_gen = btrfs_header_generation (
path - > nodes [ 1 ] ) ;
2007-03-16 23:20:31 +03:00
wret = del_ptr ( trans , root , path , 1 , slot ) ;
2007-03-01 00:35:06 +03:00
if ( wret )
ret = wret ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( leaf ) ;
2007-10-16 00:15:53 +04:00
wret = btrfs_free_extent ( trans , root , bytenr ,
2007-12-11 17:25:06 +03:00
blocksize ,
btrfs_header_owner ( path - > nodes [ 1 ] ) ,
root_gen , 0 , 0 , 1 ) ;
2007-03-01 00:46:22 +03:00
if ( wret )
ret = wret ;
2007-02-24 14:24:44 +03:00
} else {
2008-06-26 00:01:30 +04:00
/* if we're still in the path, make sure
* we ' re dirty . Otherwise , one of the
* push_leaf functions must have already
* dirtied this buffer
*/
if ( path - > nodes [ 0 ] = = leaf )
btrfs_mark_buffer_dirty ( leaf ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( leaf ) ;
2007-01-26 23:51:26 +03:00
}
2007-03-23 17:01:08 +03:00
} else {
2007-10-16 00:14:19 +04:00
btrfs_mark_buffer_dirty ( leaf ) ;
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-12-11 17:25:06 +03:00
/*
2008-06-26 00:01:30 +04:00
* search the tree again to find a leaf with lesser keys
2007-12-11 17:25:06 +03:00
* returns 0 if it found something or 1 if there are no lesser leaves .
* returns < 0 on io errors .
*/
int btrfs_prev_leaf ( struct btrfs_root * root , struct btrfs_path * path )
{
2008-06-26 00:01:30 +04:00
struct btrfs_key key ;
struct btrfs_disk_key found_key ;
int ret ;
2007-12-11 17:25:06 +03:00
2008-06-26 00:01:30 +04:00
btrfs_item_key_to_cpu ( path - > nodes [ 0 ] , & key , 0 ) ;
2007-12-11 17:25:06 +03:00
2008-06-26 00:01:30 +04:00
if ( key . offset > 0 )
key . offset - - ;
else if ( key . type > 0 )
key . type - - ;
else if ( key . objectid > 0 )
key . objectid - - ;
else
return 1 ;
2007-12-11 17:25:06 +03:00
2008-06-26 00:01:30 +04:00
btrfs_release_path ( root , path ) ;
ret = btrfs_search_slot ( NULL , root , & key , path , 0 , 0 ) ;
if ( ret < 0 )
return ret ;
btrfs_item_key ( path - > nodes [ 0 ] , & found_key , 0 ) ;
ret = comp_keys ( & found_key , & key ) ;
if ( ret < 0 )
return 0 ;
return 1 ;
2007-12-11 17:25:06 +03:00
}
2007-02-24 21:39:08 +03:00
/*
2008-06-26 00:01:30 +04:00
* search the tree again to find a leaf with greater keys
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 ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * c ;
struct extent_buffer * next = NULL ;
2008-06-26 00:01:30 +04:00
struct btrfs_key key ;
u32 nritems ;
int ret ;
nritems = btrfs_header_nritems ( path - > nodes [ 0 ] ) ;
if ( nritems = = 0 ) {
return 1 ;
}
btrfs_item_key_to_cpu ( path - > nodes [ 0 ] , & key , nritems - 1 ) ;
btrfs_release_path ( root , path ) ;
2008-06-26 00:01:30 +04:00
path - > keep_locks = 1 ;
2008-06-26 00:01:30 +04:00
ret = btrfs_search_slot ( NULL , root , & key , path , 0 , 0 ) ;
path - > keep_locks = 0 ;
if ( ret < 0 )
return ret ;
2008-06-26 00:01:30 +04:00
nritems = btrfs_header_nritems ( path - > nodes [ 0 ] ) ;
if ( nritems > 0 & & path - > slots [ 0 ] < nritems - 1 ) {
2008-06-26 00:01:30 +04:00
goto done ;
}
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-10-16 00:14:19 +04:00
2007-02-21 00:40:44 +03:00
slot = path - > slots [ level ] + 1 ;
c = path - > nodes [ level ] ;
2007-10-16 00:14:19 +04:00
if ( slot > = btrfs_header_nritems ( c ) ) {
2007-02-21 00:40:44 +03:00
level + + ;
2008-06-26 00:01:30 +04:00
if ( level = = BTRFS_MAX_LEVEL ) {
2007-12-11 17:25:06 +03:00
return 1 ;
2008-06-26 00:01:30 +04:00
}
2007-02-21 00:40:44 +03:00
continue ;
}
2007-10-16 00:14:19 +04:00
2008-06-26 00:01:30 +04:00
if ( next ) {
btrfs_tree_unlock ( next ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( next ) ;
2008-06-26 00:01:30 +04:00
}
2007-10-16 00:14:19 +04:00
2008-06-26 00:01:30 +04:00
if ( level = = 1 & & path - > locks [ 1 ] & & path - > reada )
2007-12-22 00:24:26 +03:00
reada_for_search ( root , path , level , slot , 0 ) ;
2007-10-16 00:14:19 +04:00
2008-05-12 20:59:19 +04:00
next = read_node_slot ( root , c , slot ) ;
2008-06-26 00:01:30 +04:00
WARN_ON ( ! btrfs_tree_locked ( c ) ) ;
btrfs_tree_lock ( next ) ;
2007-02-21 00:40:44 +03:00
break ;
}
path - > slots [ level ] = slot ;
while ( 1 ) {
level - - ;
c = path - > nodes [ level ] ;
2008-06-26 00:01:30 +04:00
if ( path - > locks [ level ] )
btrfs_tree_unlock ( c ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( c ) ;
2007-02-21 00:40:44 +03:00
path - > nodes [ level ] = next ;
path - > slots [ level ] = 0 ;
2008-06-26 00:01:30 +04:00
path - > locks [ level ] = 1 ;
2007-02-21 00:40:44 +03:00
if ( ! level )
break ;
2008-06-26 00:01:30 +04:00
if ( level = = 1 & & path - > locks [ 1 ] & & path - > reada )
reada_for_search ( root , path , level , slot , 0 ) ;
2008-05-12 20:59:19 +04:00
next = read_node_slot ( root , next , 0 ) ;
2008-06-26 00:01:30 +04:00
WARN_ON ( ! btrfs_tree_locked ( path - > nodes [ level ] ) ) ;
btrfs_tree_lock ( next ) ;
2007-02-21 00:40:44 +03:00
}
2008-06-26 00:01:30 +04:00
done :
unlock_up ( path , 0 , 1 ) ;
2007-02-21 00:40:44 +03:00
return 0 ;
}
2008-03-24 22:01:56 +03:00
int btrfs_previous_item ( struct btrfs_root * root ,
struct btrfs_path * path , u64 min_objectid ,
int type )
{
struct btrfs_key found_key ;
struct extent_buffer * leaf ;
int ret ;
while ( 1 ) {
if ( path - > slots [ 0 ] = = 0 ) {
ret = btrfs_prev_leaf ( root , path ) ;
if ( ret ! = 0 )
return ret ;
} else {
path - > slots [ 0 ] - - ;
}
leaf = path - > nodes [ 0 ] ;
btrfs_item_key_to_cpu ( leaf , & found_key , path - > slots [ 0 ] ) ;
if ( found_key . type = = type )
return 0 ;
}
return 1 ;
}