2007-03-21 18:12:56 +03:00
# include <linux/module.h>
2007-02-26 18:40:21 +03:00
# include "ctree.h"
# include "disk-io.h"
# include "print-tree.h"
2007-03-16 23:20:31 +03:00
# include "transaction.h"
2007-02-26 18:40:21 +03:00
2007-03-16 23:20:31 +03:00
static int find_free_extent ( struct btrfs_trans_handle * trans , struct btrfs_root
* orig_root , u64 num_blocks , u64 search_start , u64
2007-05-03 17:06:49 +04:00
search_end , struct btrfs_key * ins , int data ) ;
2007-03-16 23:20:31 +03:00
static int finish_current_insert ( struct btrfs_trans_handle * trans , struct
btrfs_root * extent_root ) ;
2007-03-22 19:13:20 +03:00
static int del_pending_extents ( struct btrfs_trans_handle * trans , struct
btrfs_root * extent_root ) ;
2007-02-26 18:40:21 +03:00
2007-05-18 21:28:27 +04:00
static void reada_extent_leaves ( struct btrfs_root * root ,
struct btrfs_path * path , u64 limit )
{
struct btrfs_node * node ;
int i ;
int nritems ;
u64 item_objectid ;
u64 blocknr ;
int slot ;
int ret ;
if ( ! path - > nodes [ 1 ] )
return ;
node = btrfs_buffer_node ( path - > nodes [ 1 ] ) ;
slot = path - > slots [ 1 ] + 1 ;
nritems = btrfs_header_nritems ( & node - > header ) ;
for ( i = slot ; i < nritems & & i < slot + 8 ; i + + ) {
item_objectid = btrfs_disk_key_objectid ( & node - > ptrs [ i ] . key ) ;
if ( item_objectid > limit )
break ;
blocknr = btrfs_node_blockptr ( node , i ) ;
ret = readahead_tree_block ( root , blocknr ) ;
if ( ret )
break ;
}
}
2007-05-10 04:13:14 +04:00
static int cache_block_group ( struct btrfs_root * root ,
struct btrfs_block_group_cache * block_group )
{
struct btrfs_path * path ;
int ret ;
struct btrfs_key key ;
struct btrfs_leaf * leaf ;
struct radix_tree_root * extent_radix ;
int slot ;
u64 i ;
u64 last = 0 ;
u64 hole_size ;
2007-05-18 21:28:27 +04:00
u64 limit ;
2007-05-10 04:13:14 +04:00
int found = 0 ;
root = root - > fs_info - > extent_root ;
extent_radix = & root - > fs_info - > extent_map_radix ;
if ( block_group - > cached )
return 0 ;
if ( block_group - > data )
return 0 ;
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
printk ( " cache block group %Lu \n " , block_group - > key . objectid ) ;
key . objectid = block_group - > key . objectid ;
key . flags = 0 ;
key . offset = 0 ;
btrfs_set_key_type ( & key , BTRFS_EXTENT_ITEM_KEY ) ;
ret = btrfs_search_slot ( NULL , root , & key , path , 0 , 0 ) ;
if ( ret < 0 )
return ret ;
if ( ret & & path - > slots [ 0 ] > 0 )
path - > slots [ 0 ] - - ;
2007-05-18 21:28:27 +04:00
limit = block_group - > key . objectid + block_group - > key . offset ;
reada_extent_leaves ( root , path , limit ) ;
2007-05-10 04:13:14 +04:00
while ( 1 ) {
leaf = btrfs_buffer_leaf ( path - > nodes [ 0 ] ) ;
slot = path - > slots [ 0 ] ;
if ( slot > = btrfs_header_nritems ( & leaf - > header ) ) {
2007-05-18 21:28:27 +04:00
reada_extent_leaves ( root , path , limit ) ;
2007-05-10 04:13:14 +04:00
ret = btrfs_next_leaf ( root , path ) ;
2007-05-18 21:28:27 +04:00
if ( ret = = 0 ) {
2007-05-10 04:13:14 +04:00
continue ;
2007-05-18 21:28:27 +04:00
} else {
2007-05-10 04:13:14 +04:00
if ( found ) {
hole_size = block_group - > key . objectid +
block_group - > key . offset - last ;
} else {
last = block_group - > key . objectid ;
hole_size = block_group - > key . offset ;
}
for ( i = 0 ; i < hole_size ; i + + ) {
set_radix_bit ( extent_radix ,
last + i ) ;
}
break ;
}
}
btrfs_disk_key_to_cpu ( & key , & leaf - > items [ slot ] . key ) ;
if ( key . objectid > = block_group - > key . objectid +
block_group - > key . offset ) {
if ( found ) {
hole_size = block_group - > key . objectid +
block_group - > key . offset - last ;
} else {
last = block_group - > key . objectid ;
hole_size = block_group - > key . offset ;
}
for ( i = 0 ; i < hole_size ; i + + ) {
set_radix_bit ( extent_radix , last + i ) ;
}
break ;
}
if ( btrfs_key_type ( & key ) = = BTRFS_EXTENT_ITEM_KEY ) {
if ( ! found ) {
last = key . objectid + key . offset ;
found = 1 ;
} else {
hole_size = key . objectid - last ;
for ( i = 0 ; i < hole_size ; i + + ) {
set_radix_bit ( extent_radix , last + i ) ;
}
last = key . objectid + key . offset ;
}
}
path - > slots [ 0 ] + + ;
}
block_group - > cached = 1 ;
btrfs_free_path ( path ) ;
return 0 ;
}
2007-05-06 18:15:01 +04:00
static struct btrfs_block_group_cache * lookup_block_group ( struct
btrfs_fs_info * info ,
u64 blocknr )
{
struct btrfs_block_group_cache * block_group ;
int ret ;
ret = radix_tree_gang_lookup ( & info - > block_group_radix ,
( void * * ) & block_group ,
blocknr , 1 ) ;
if ( ret ) {
2007-05-08 04:03:49 +04:00
if ( block_group - > key . objectid < = blocknr & & blocknr < =
2007-05-06 18:15:01 +04:00
block_group - > key . objectid + block_group - > key . offset )
return block_group ;
}
ret = radix_tree_gang_lookup ( & info - > block_group_data_radix ,
( void * * ) & block_group ,
blocknr , 1 ) ;
if ( ret ) {
2007-05-08 04:03:49 +04:00
if ( block_group - > key . objectid < = blocknr & & blocknr < =
2007-05-06 18:15:01 +04:00
block_group - > key . objectid + block_group - > key . offset )
return block_group ;
}
2007-05-08 04:03:49 +04:00
WARN_ON ( 1 ) ;
printk ( " lookup_block_group fails for blocknr %Lu \n " , blocknr ) ;
printk ( " last ret was %d \n " , ret ) ;
if ( ret ) {
printk ( " last block group was %Lu %Lu \n " , block_group - > key . objectid , block_group - > key . offset ) ;
}
2007-05-06 18:15:01 +04:00
return NULL ;
}
2007-05-10 04:13:14 +04:00
static u64 leaf_range ( struct btrfs_root * root )
{
u64 size = BTRFS_LEAF_DATA_SIZE ( root ) ;
size = size / ( sizeof ( struct btrfs_extent_item ) +
sizeof ( struct btrfs_item ) ) ;
return size ;
}
static u64 find_search_start ( struct btrfs_root * root ,
struct btrfs_block_group_cache * * cache_ret ,
u64 search_start , int num )
{
unsigned long gang [ 8 ] ;
int ret ;
struct btrfs_block_group_cache * cache = * cache_ret ;
u64 last = max ( search_start , cache - > key . objectid ) ;
if ( cache - > data )
goto out ;
if ( num > 1 ) {
last = max ( last , cache - > last_prealloc ) ;
}
again :
cache_block_group ( root , cache ) ;
while ( 1 ) {
ret = find_first_radix_bit ( & root - > fs_info - > extent_map_radix ,
gang , last , ARRAY_SIZE ( gang ) ) ;
if ( ! ret )
goto out ;
last = gang [ ret - 1 ] + 1 ;
if ( num > 1 ) {
if ( ret ! = ARRAY_SIZE ( gang ) ) {
goto new_group ;
}
if ( gang [ ret - 1 ] - gang [ 0 ] > leaf_range ( root ) ) {
continue ;
}
}
if ( gang [ 0 ] > = cache - > key . objectid + cache - > key . offset ) {
goto new_group ;
}
return gang [ 0 ] ;
}
out :
return max ( cache - > last_alloc , search_start ) ;
new_group :
cache = lookup_block_group ( root - > fs_info , last + cache - > key . offset - 1 ) ;
if ( ! cache ) {
return max ( ( * cache_ret ) - > last_alloc , search_start ) ;
}
cache = btrfs_find_block_group ( root , cache ,
2007-05-18 21:28:27 +04:00
last + cache - > key . offset - 1 , 0 , 0 ) ;
2007-05-10 04:13:14 +04:00
* cache_ret = cache ;
goto again ;
}
2007-04-30 23:25:45 +04:00
struct btrfs_block_group_cache * btrfs_find_block_group ( struct btrfs_root * root ,
struct btrfs_block_group_cache
2007-05-06 18:15:01 +04:00
* hint , u64 search_start ,
2007-05-18 21:28:27 +04:00
int data , int owner )
2007-04-27 18:08:34 +04:00
{
struct btrfs_block_group_cache * cache [ 8 ] ;
2007-04-30 23:25:45 +04:00
struct btrfs_block_group_cache * found_group = NULL ;
2007-04-27 18:08:34 +04:00
struct btrfs_fs_info * info = root - > fs_info ;
2007-05-06 18:15:01 +04:00
struct radix_tree_root * radix ;
2007-04-27 18:08:34 +04:00
u64 used ;
2007-04-30 23:25:45 +04:00
u64 last = 0 ;
u64 hint_last ;
2007-04-27 18:08:34 +04:00
int i ;
int ret ;
2007-04-30 23:25:45 +04:00
int full_search = 0 ;
2007-05-18 21:28:27 +04:00
int factor = 8 ;
if ( ! owner )
factor = 5 ;
2007-05-06 18:15:01 +04:00
if ( data )
radix = & info - > block_group_data_radix ;
else
radix = & info - > block_group_radix ;
if ( search_start ) {
struct btrfs_block_group_cache * shint ;
shint = lookup_block_group ( info , search_start ) ;
if ( shint - > data = = data ) {
used = btrfs_block_group_used ( & shint - > item ) ;
if ( used + shint - > pinned <
2007-05-18 21:28:27 +04:00
( shint - > key . offset * factor ) / 10 ) {
2007-05-06 18:15:01 +04:00
return shint ;
}
}
}
if ( hint & & hint - > data = = data ) {
2007-04-30 23:25:45 +04:00
used = btrfs_block_group_used ( & hint - > item ) ;
2007-05-18 21:28:27 +04:00
if ( used + hint - > pinned < ( hint - > key . offset * factor ) / 10 ) {
2007-04-30 23:25:45 +04:00
return hint ;
}
2007-05-06 18:15:01 +04:00
if ( used > = ( hint - > key . offset * 8 ) / 10 ) {
radix_tree_tag_clear ( radix ,
hint - > key . objectid +
hint - > key . offset - 1 ,
BTRFS_BLOCK_GROUP_AVAIL ) ;
}
2007-05-10 19:24:42 +04:00
last = hint - > key . offset * 3 ;
2007-05-06 18:15:01 +04:00
if ( hint - > key . objectid > = last )
2007-05-10 04:13:14 +04:00
last = max ( search_start + hint - > key . offset - 1 ,
hint - > key . objectid - last ) ;
2007-05-06 18:15:01 +04:00
else
last = hint - > key . objectid + hint - > key . offset ;
2007-04-30 23:25:45 +04:00
hint_last = last ;
} else {
2007-05-10 04:13:14 +04:00
if ( hint )
hint_last = max ( hint - > key . objectid , search_start ) ;
else
hint_last = search_start ;
last = hint_last ;
2007-04-30 23:25:45 +04:00
}
2007-04-27 18:08:34 +04:00
while ( 1 ) {
2007-05-06 18:15:01 +04:00
ret = radix_tree_gang_lookup_tag ( radix , ( void * * ) cache ,
2007-04-27 18:08:34 +04:00
last , ARRAY_SIZE ( cache ) ,
2007-04-30 23:25:45 +04:00
BTRFS_BLOCK_GROUP_AVAIL ) ;
2007-04-27 18:08:34 +04:00
if ( ! ret )
break ;
for ( i = 0 ; i < ret ; i + + ) {
2007-05-03 17:06:49 +04:00
last = cache [ i ] - > key . objectid +
cache [ i ] - > key . offset ;
2007-04-27 18:08:34 +04:00
used = btrfs_block_group_used ( & cache [ i ] - > item ) ;
2007-05-06 18:15:01 +04:00
if ( used + cache [ i ] - > pinned <
2007-05-18 21:28:27 +04:00
( cache [ i ] - > key . offset * factor ) / 10 ) {
2007-04-30 23:25:45 +04:00
found_group = cache [ i ] ;
goto found ;
2007-04-27 18:08:34 +04:00
}
2007-05-06 18:15:01 +04:00
if ( used > = ( cache [ i ] - > key . offset * 8 ) / 10 ) {
radix_tree_tag_clear ( radix ,
cache [ i ] - > key . objectid +
cache [ i ] - > key . offset - 1 ,
BTRFS_BLOCK_GROUP_AVAIL ) ;
}
2007-04-27 18:08:34 +04:00
}
2007-05-18 21:28:27 +04:00
cond_resched ( ) ;
2007-04-27 18:08:34 +04:00
}
2007-04-30 23:25:45 +04:00
last = hint_last ;
again :
2007-04-27 18:08:34 +04:00
while ( 1 ) {
2007-05-06 18:15:01 +04:00
ret = radix_tree_gang_lookup ( radix , ( void * * ) cache ,
last , ARRAY_SIZE ( cache ) ) ;
2007-04-27 18:08:34 +04:00
if ( ! ret )
break ;
for ( i = 0 ; i < ret ; i + + ) {
2007-05-03 17:06:49 +04:00
last = cache [ i ] - > key . objectid +
cache [ i ] - > key . offset ;
2007-04-27 18:08:34 +04:00
used = btrfs_block_group_used ( & cache [ i ] - > item ) ;
2007-05-06 18:15:01 +04:00
if ( used + cache [ i ] - > pinned < cache [ i ] - > key . offset ) {
2007-04-30 23:25:45 +04:00
found_group = cache [ i ] ;
goto found ;
2007-04-27 18:08:34 +04:00
}
2007-05-06 18:15:01 +04:00
if ( used > = cache [ i ] - > key . offset ) {
radix_tree_tag_clear ( radix ,
cache [ i ] - > key . objectid +
cache [ i ] - > key . offset - 1 ,
BTRFS_BLOCK_GROUP_AVAIL ) ;
}
2007-04-27 18:08:34 +04:00
}
2007-05-18 21:28:27 +04:00
cond_resched ( ) ;
2007-04-27 18:08:34 +04:00
}
2007-04-30 23:25:45 +04:00
if ( ! full_search ) {
2007-05-18 21:28:27 +04:00
printk ( " find block group doing full search data %d start %Lu \n " , data , search_start ) ;
2007-05-06 18:15:01 +04:00
last = search_start ;
2007-04-30 23:25:45 +04:00
full_search = 1 ;
goto again ;
}
if ( ! found_group ) {
2007-05-18 21:28:27 +04:00
printk ( " find block group bailing to zero data %d \n " , data ) ;
2007-05-06 18:15:01 +04:00
ret = radix_tree_gang_lookup ( radix ,
2007-04-30 23:25:45 +04:00
( void * * ) & found_group , 0 , 1 ) ;
BUG_ON ( ret ! = 1 ) ;
}
2007-05-06 18:15:01 +04:00
found :
2007-04-30 23:25:45 +04:00
return found_group ;
2007-04-27 18:08:34 +04:00
}
2007-04-17 21:26:50 +04:00
int btrfs_inc_extent_ref ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
u64 blocknr , u64 num_blocks )
2007-03-03 00:08:05 +03:00
{
2007-04-02 19:20:42 +04:00
struct btrfs_path * path ;
2007-03-03 00:08:05 +03:00
int ret ;
2007-03-12 23:22:34 +03:00
struct btrfs_key key ;
2007-03-13 17:46:10 +03:00
struct btrfs_leaf * l ;
struct btrfs_extent_item * item ;
2007-03-12 23:22:34 +03:00
struct btrfs_key ins ;
2007-03-13 16:49:06 +03:00
u32 refs ;
2007-03-07 19:50:24 +03:00
2007-03-20 21:38:32 +03:00
find_free_extent ( trans , root - > fs_info - > extent_root , 0 , 0 , ( u64 ) - 1 ,
2007-05-03 17:06:49 +04:00
& ins , 0 ) ;
2007-04-02 19:20:42 +04:00
path = btrfs_alloc_path ( ) ;
BUG_ON ( ! path ) ;
btrfs_init_path ( path ) ;
2007-03-03 00:08:05 +03:00
key . objectid = blocknr ;
key . flags = 0 ;
2007-03-15 19:56:47 +03:00
btrfs_set_key_type ( & key , BTRFS_EXTENT_ITEM_KEY ) ;
2007-03-27 14:33:00 +04:00
key . offset = num_blocks ;
2007-04-02 19:20:42 +04:00
ret = btrfs_search_slot ( trans , root - > fs_info - > extent_root , & key , path ,
2007-03-20 21:38:32 +03:00
0 , 1 ) ;
2007-04-19 00:15:28 +04:00
if ( ret ! = 0 ) {
printk ( " can't find block %Lu %Lu \n " , blocknr , num_blocks ) ;
2007-03-07 04:08:01 +03:00
BUG ( ) ;
2007-04-19 00:15:28 +04:00
}
2007-03-03 00:08:05 +03:00
BUG_ON ( ret ! = 0 ) ;
2007-04-02 19:20:42 +04:00
l = btrfs_buffer_leaf ( path - > nodes [ 0 ] ) ;
item = btrfs_item_ptr ( l , path - > slots [ 0 ] , struct btrfs_extent_item ) ;
2007-03-13 16:49:06 +03:00
refs = btrfs_extent_refs ( item ) ;
btrfs_set_extent_refs ( item , refs + 1 ) ;
2007-04-02 19:20:42 +04:00
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
2007-03-07 04:08:01 +03:00
2007-04-02 19:20:42 +04:00
btrfs_release_path ( root - > fs_info - > extent_root , path ) ;
btrfs_free_path ( path ) ;
2007-03-20 21:38:32 +03:00
finish_current_insert ( trans , root - > fs_info - > extent_root ) ;
2007-03-22 19:13:20 +03:00
del_pending_extents ( trans , root - > fs_info - > extent_root ) ;
2007-03-03 00:08:05 +03:00
return 0 ;
}
2007-04-17 21:26:50 +04:00
static int lookup_extent_ref ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root , u64 blocknr ,
u64 num_blocks , u32 * refs )
2007-03-07 04:08:01 +03:00
{
2007-04-02 19:20:42 +04:00
struct btrfs_path * path ;
2007-03-07 04:08:01 +03:00
int ret ;
2007-03-12 23:22:34 +03:00
struct btrfs_key key ;
2007-03-13 17:46:10 +03:00
struct btrfs_leaf * l ;
struct btrfs_extent_item * item ;
2007-04-02 19:20:42 +04:00
path = btrfs_alloc_path ( ) ;
btrfs_init_path ( path ) ;
2007-03-07 04:08:01 +03:00
key . objectid = blocknr ;
2007-03-27 14:33:00 +04:00
key . offset = num_blocks ;
2007-03-15 19:56:47 +03:00
key . flags = 0 ;
btrfs_set_key_type ( & key , BTRFS_EXTENT_ITEM_KEY ) ;
2007-04-02 19:20:42 +04:00
ret = btrfs_search_slot ( trans , root - > fs_info - > extent_root , & key , path ,
2007-03-20 21:38:32 +03:00
0 , 0 ) ;
2007-03-07 04:08:01 +03:00
if ( ret ! = 0 )
BUG ( ) ;
2007-04-02 19:20:42 +04:00
l = btrfs_buffer_leaf ( path - > nodes [ 0 ] ) ;
item = btrfs_item_ptr ( l , path - > slots [ 0 ] , struct btrfs_extent_item ) ;
2007-03-13 16:49:06 +03:00
* refs = btrfs_extent_refs ( item ) ;
2007-04-02 19:20:42 +04:00
btrfs_release_path ( root - > fs_info - > extent_root , path ) ;
btrfs_free_path ( path ) ;
2007-03-07 04:08:01 +03:00
return 0 ;
}
2007-04-10 17:27:04 +04:00
int btrfs_inc_root_ref ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root )
{
2007-04-17 21:26:50 +04:00
return btrfs_inc_extent_ref ( trans , root , bh_blocknr ( root - > node ) , 1 ) ;
2007-04-10 17:27:04 +04:00
}
2007-03-16 23:20:31 +03:00
int btrfs_inc_ref ( struct btrfs_trans_handle * trans , struct btrfs_root * root ,
2007-03-22 19:13:20 +03:00
struct buffer_head * buf )
2007-03-03 00:08:05 +03:00
{
u64 blocknr ;
2007-03-22 19:13:20 +03:00
struct btrfs_node * buf_node ;
2007-03-27 14:33:00 +04:00
struct btrfs_leaf * buf_leaf ;
struct btrfs_disk_key * key ;
struct btrfs_file_extent_item * fi ;
2007-03-03 00:08:05 +03:00
int i ;
2007-03-27 14:33:00 +04:00
int leaf ;
int ret ;
2007-03-07 04:08:01 +03:00
2007-03-13 23:47:54 +03:00
if ( ! root - > ref_cows )
2007-03-07 04:08:01 +03:00
return 0 ;
2007-03-22 19:13:20 +03:00
buf_node = btrfs_buffer_node ( buf ) ;
2007-03-27 14:33:00 +04:00
leaf = btrfs_is_leaf ( buf_node ) ;
buf_leaf = btrfs_buffer_leaf ( buf ) ;
2007-03-22 19:13:20 +03:00
for ( i = 0 ; i < btrfs_header_nritems ( & buf_node - > header ) ; i + + ) {
2007-03-27 14:33:00 +04:00
if ( leaf ) {
2007-05-24 21:35:57 +04:00
u64 disk_blocknr ;
2007-03-27 14:33:00 +04:00
key = & buf_leaf - > items [ i ] . key ;
if ( btrfs_disk_key_type ( key ) ! = BTRFS_EXTENT_DATA_KEY )
continue ;
fi = btrfs_item_ptr ( buf_leaf , i ,
struct btrfs_file_extent_item ) ;
2007-04-19 21:37:44 +04:00
if ( btrfs_file_extent_type ( fi ) = =
BTRFS_FILE_EXTENT_INLINE )
continue ;
2007-05-24 21:35:57 +04:00
disk_blocknr = btrfs_file_extent_disk_blocknr ( fi ) ;
if ( disk_blocknr = = 0 )
continue ;
ret = btrfs_inc_extent_ref ( trans , root , disk_blocknr ,
2007-03-27 14:33:00 +04:00
btrfs_file_extent_disk_num_blocks ( fi ) ) ;
BUG_ON ( ret ) ;
} else {
blocknr = btrfs_node_blockptr ( buf_node , i ) ;
2007-04-17 21:26:50 +04:00
ret = btrfs_inc_extent_ref ( trans , root , blocknr , 1 ) ;
2007-03-27 14:33:00 +04:00
BUG_ON ( ret ) ;
}
2007-03-03 00:08:05 +03:00
}
return 0 ;
}
2007-04-27 00:46:15 +04:00
static int write_one_cache_group ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct btrfs_path * path ,
struct btrfs_block_group_cache * cache )
{
int ret ;
int pending_ret ;
struct btrfs_root * extent_root = root - > fs_info - > extent_root ;
struct btrfs_block_group_item * bi ;
struct btrfs_key ins ;
2007-05-03 17:06:49 +04:00
find_free_extent ( trans , extent_root , 0 , 0 , ( u64 ) - 1 , & ins , 0 ) ;
2007-04-27 00:46:15 +04:00
ret = btrfs_search_slot ( trans , extent_root , & cache - > key , path , 0 , 1 ) ;
BUG_ON ( ret ) ;
bi = btrfs_item_ptr ( btrfs_buffer_leaf ( path - > nodes [ 0 ] ) , path - > slots [ 0 ] ,
struct btrfs_block_group_item ) ;
memcpy ( bi , & cache - > item , sizeof ( * bi ) ) ;
mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
btrfs_release_path ( extent_root , path ) ;
finish_current_insert ( trans , extent_root ) ;
pending_ret = del_pending_extents ( trans , extent_root ) ;
if ( ret )
return ret ;
if ( pending_ret )
return pending_ret ;
2007-05-06 18:15:01 +04:00
if ( cache - > data )
cache - > last_alloc = cache - > first_free ;
2007-04-27 00:46:15 +04:00
return 0 ;
}
2007-05-06 18:15:01 +04:00
static int write_dirty_block_radix ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct radix_tree_root * radix )
2007-04-27 00:46:15 +04:00
{
struct btrfs_block_group_cache * cache [ 8 ] ;
int ret ;
int err = 0 ;
int werr = 0 ;
int i ;
struct btrfs_path * path ;
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
while ( 1 ) {
ret = radix_tree_gang_lookup_tag ( radix , ( void * * ) cache ,
0 , ARRAY_SIZE ( cache ) ,
BTRFS_BLOCK_GROUP_DIRTY ) ;
if ( ! ret )
break ;
for ( i = 0 ; i < ret ; i + + ) {
radix_tree_tag_clear ( radix , cache [ i ] - > key . objectid +
cache [ i ] - > key . offset - 1 ,
BTRFS_BLOCK_GROUP_DIRTY ) ;
err = write_one_cache_group ( trans , root ,
path , cache [ i ] ) ;
if ( err )
werr = err ;
}
}
btrfs_free_path ( path ) ;
return werr ;
}
2007-05-06 18:15:01 +04:00
int btrfs_write_dirty_block_groups ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root )
{
int ret ;
int ret2 ;
ret = write_dirty_block_radix ( trans , root ,
& root - > fs_info - > block_group_radix ) ;
ret2 = write_dirty_block_radix ( trans , root ,
& root - > fs_info - > block_group_data_radix ) ;
if ( ret )
return ret ;
if ( ret2 )
return ret2 ;
return 0 ;
}
2007-04-27 00:46:15 +04:00
static int update_block_group ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
2007-05-10 04:13:14 +04:00
u64 blocknr , u64 num , int alloc , int mark_free )
2007-04-27 00:46:15 +04:00
{
struct btrfs_block_group_cache * cache ;
struct btrfs_fs_info * info = root - > fs_info ;
u64 total = num ;
u64 old_val ;
u64 block_in_group ;
2007-05-10 04:13:14 +04:00
u64 i ;
2007-05-08 04:03:49 +04:00
2007-04-27 00:46:15 +04:00
while ( total ) {
2007-05-08 04:03:49 +04:00
cache = lookup_block_group ( info , blocknr ) ;
if ( ! cache ) {
2007-04-27 18:08:34 +04:00
printk ( KERN_CRIT " blocknr %Lu lookup failed \n " ,
blocknr ) ;
2007-04-27 00:46:15 +04:00
return - 1 ;
2007-04-27 18:08:34 +04:00
}
2007-04-27 00:46:15 +04:00
block_in_group = blocknr - cache - > key . objectid ;
WARN_ON ( block_in_group > cache - > key . offset ) ;
2007-05-08 04:03:49 +04:00
radix_tree_tag_set ( cache - > radix , cache - > key . objectid +
2007-05-06 18:15:01 +04:00
cache - > key . offset - 1 ,
2007-04-27 00:46:15 +04:00
BTRFS_BLOCK_GROUP_DIRTY ) ;
old_val = btrfs_block_group_used ( & cache - > item ) ;
num = min ( total , cache - > key . offset - block_in_group ) ;
2007-04-27 18:08:34 +04:00
if ( alloc ) {
2007-04-27 00:46:15 +04:00
old_val + = num ;
2007-04-27 18:08:34 +04:00
if ( blocknr > cache - > last_alloc )
cache - > last_alloc = blocknr ;
2007-05-10 04:13:14 +04:00
if ( ! cache - > data ) {
for ( i = 0 ; i < num ; i + + ) {
clear_radix_bit ( & info - > extent_map_radix ,
blocknr + i ) ;
}
}
2007-04-27 18:08:34 +04:00
} else {
2007-04-27 00:46:15 +04:00
old_val - = num ;
2007-04-27 18:08:34 +04:00
if ( blocknr < cache - > first_free )
cache - > first_free = blocknr ;
2007-05-10 04:13:14 +04:00
if ( ! cache - > data & & mark_free ) {
for ( i = 0 ; i < num ; i + + ) {
set_radix_bit ( & info - > extent_map_radix ,
blocknr + i ) ;
}
}
2007-05-18 21:28:27 +04:00
if ( old_val < ( cache - > key . offset * 5 ) / 10 & &
old_val + num > = ( cache - > key . offset * 5 ) / 10 ) {
2007-05-10 04:13:14 +04:00
printk ( " group %Lu now available \n " , cache - > key . objectid ) ;
radix_tree_tag_set ( cache - > radix ,
cache - > key . objectid +
cache - > key . offset - 1 ,
BTRFS_BLOCK_GROUP_AVAIL ) ;
}
2007-04-27 18:08:34 +04:00
}
2007-04-27 00:46:15 +04:00
btrfs_set_block_group_used ( & cache - > item , old_val ) ;
2007-05-10 04:13:14 +04:00
total - = num ;
blocknr + = num ;
2007-04-27 00:46:15 +04:00
}
return 0 ;
}
2007-05-03 17:06:49 +04:00
static int try_remove_page ( struct address_space * mapping , unsigned long index )
{
int ret ;
ret = invalidate_mapping_pages ( mapping , index , index ) ;
return ret ;
}
2007-03-16 23:20:31 +03:00
int btrfs_finish_extent_commit ( struct btrfs_trans_handle * trans , struct
btrfs_root * root )
2007-03-07 04:08:01 +03:00
{
2007-03-26 18:15:30 +04:00
unsigned long gang [ 8 ] ;
2007-05-03 17:06:49 +04:00
struct inode * btree_inode = root - > fs_info - > btree_inode ;
2007-05-06 18:15:01 +04:00
struct btrfs_block_group_cache * block_group ;
2007-03-16 15:56:18 +03:00
u64 first = 0 ;
2007-03-07 04:08:01 +03:00
int ret ;
int i ;
2007-03-26 18:15:30 +04:00
struct radix_tree_root * pinned_radix = & root - > fs_info - > pinned_radix ;
2007-05-10 04:13:14 +04:00
struct radix_tree_root * extent_radix = & root - > fs_info - > extent_map_radix ;
2007-03-07 04:08:01 +03:00
while ( 1 ) {
2007-05-10 04:13:14 +04:00
ret = find_first_radix_bit ( pinned_radix , gang , 0 ,
2007-03-26 18:15:30 +04:00
ARRAY_SIZE ( gang ) ) ;
2007-03-07 04:08:01 +03:00
if ( ! ret )
break ;
2007-03-16 15:56:18 +03:00
if ( ! first )
2007-03-26 18:15:30 +04:00
first = gang [ 0 ] ;
2007-03-08 00:15:30 +03:00
for ( i = 0 ; i < ret ; i + + ) {
2007-03-26 18:15:30 +04:00
clear_radix_bit ( pinned_radix , gang [ i ] ) ;
2007-05-06 18:15:01 +04:00
block_group = lookup_block_group ( root - > fs_info ,
gang [ i ] ) ;
if ( block_group ) {
WARN_ON ( block_group - > pinned = = 0 ) ;
block_group - > pinned - - ;
if ( gang [ i ] < block_group - > last_alloc )
block_group - > last_alloc = gang [ i ] ;
2007-05-10 04:13:14 +04:00
if ( gang [ i ] < block_group - > last_prealloc )
block_group - > last_prealloc = gang [ i ] ;
if ( ! block_group - > data )
set_radix_bit ( extent_radix , gang [ i ] ) ;
2007-05-06 18:15:01 +04:00
}
2007-05-03 17:06:49 +04:00
try_remove_page ( btree_inode - > i_mapping ,
gang [ i ] < < ( PAGE_CACHE_SHIFT -
btree_inode - > i_blkbits ) ) ;
2007-03-08 00:15:30 +03:00
}
2007-03-07 04:08:01 +03:00
}
return 0 ;
}
2007-03-16 23:20:31 +03:00
static int finish_current_insert ( struct btrfs_trans_handle * trans , struct
btrfs_root * extent_root )
2007-03-07 19:50:24 +03:00
{
2007-03-12 23:22:34 +03:00
struct btrfs_key ins ;
2007-03-13 17:46:10 +03:00
struct btrfs_extent_item extent_item ;
2007-03-07 19:50:24 +03:00
int i ;
int ret ;
2007-03-21 03:35:03 +03:00
u64 super_blocks_used ;
struct btrfs_fs_info * info = extent_root - > fs_info ;
2007-03-07 19:50:24 +03:00
2007-03-13 16:49:06 +03:00
btrfs_set_extent_refs ( & extent_item , 1 ) ;
2007-03-07 19:50:24 +03:00
ins . offset = 1 ;
ins . flags = 0 ;
2007-03-15 19:56:47 +03:00
btrfs_set_key_type ( & ins , BTRFS_EXTENT_ITEM_KEY ) ;
2007-04-24 01:01:05 +04:00
btrfs_set_extent_owner ( & extent_item , extent_root - > root_key . objectid ) ;
2007-03-07 19:50:24 +03:00
2007-04-25 23:52:25 +04:00
for ( i = 0 ; i < extent_root - > fs_info - > extent_tree_insert_nr ; i + + ) {
ins . objectid = extent_root - > fs_info - > extent_tree_insert [ i ] ;
2007-03-21 03:35:03 +03:00
super_blocks_used = btrfs_super_blocks_used ( info - > disk_super ) ;
btrfs_set_super_blocks_used ( info - > disk_super ,
super_blocks_used + 1 ) ;
2007-03-16 23:20:31 +03:00
ret = btrfs_insert_item ( trans , extent_root , & ins , & extent_item ,
sizeof ( extent_item ) ) ;
2007-03-07 19:50:24 +03:00
BUG_ON ( ret ) ;
}
2007-04-25 23:52:25 +04:00
extent_root - > fs_info - > extent_tree_insert_nr = 0 ;
extent_root - > fs_info - > extent_tree_prealloc_nr = 0 ;
2007-03-07 19:50:24 +03:00
return 0 ;
}
2007-03-26 18:15:30 +04:00
static int pin_down_block ( struct btrfs_root * root , u64 blocknr , int pending )
2007-03-22 19:13:20 +03:00
{
int err ;
2007-03-25 19:35:08 +04:00
struct btrfs_header * header ;
2007-03-26 18:15:30 +04:00
struct buffer_head * bh ;
2007-03-27 19:05:53 +04:00
if ( ! pending ) {
2007-03-28 21:57:48 +04:00
bh = btrfs_find_tree_block ( root , blocknr ) ;
2007-04-02 18:50:19 +04:00
if ( bh ) {
if ( buffer_uptodate ( bh ) ) {
u64 transid =
root - > fs_info - > running_transaction - > transid ;
header = btrfs_buffer_header ( bh ) ;
if ( btrfs_header_generation ( header ) = =
transid ) {
btrfs_block_release ( root , bh ) ;
return 0 ;
}
2007-03-27 19:05:53 +04:00
}
2007-03-30 22:27:56 +04:00
btrfs_block_release ( root , bh ) ;
2007-03-26 18:15:30 +04:00
}
err = set_radix_bit ( & root - > fs_info - > pinned_radix , blocknr ) ;
2007-05-06 18:15:01 +04:00
if ( ! err ) {
struct btrfs_block_group_cache * cache ;
cache = lookup_block_group ( root - > fs_info , blocknr ) ;
if ( cache )
cache - > pinned + + ;
}
2007-03-27 19:05:53 +04:00
} else {
err = set_radix_bit ( & root - > fs_info - > pending_del_radix , blocknr ) ;
}
2007-05-06 18:15:01 +04:00
BUG_ON ( err < 0 ) ;
2007-03-22 19:13:20 +03:00
return 0 ;
}
2007-02-26 18:40:21 +03:00
/*
2007-03-07 04:08:01 +03:00
* remove an extent from the root , returns 0 on success
2007-02-26 18:40:21 +03:00
*/
2007-03-16 23:20:31 +03:00
static int __free_extent ( struct btrfs_trans_handle * trans , struct btrfs_root
2007-05-10 04:13:14 +04:00
* root , u64 blocknr , u64 num_blocks , int pin ,
int mark_free )
2007-03-07 04:08:01 +03:00
{
2007-04-02 19:20:42 +04:00
struct btrfs_path * path ;
2007-03-12 23:22:34 +03:00
struct btrfs_key key ;
2007-03-21 03:35:03 +03:00
struct btrfs_fs_info * info = root - > fs_info ;
struct btrfs_root * extent_root = info - > extent_root ;
2007-03-07 04:08:01 +03:00
int ret ;
2007-03-13 17:46:10 +03:00
struct btrfs_extent_item * ei ;
2007-03-12 23:22:34 +03:00
struct btrfs_key ins ;
2007-03-13 16:49:06 +03:00
u32 refs ;
2007-03-07 19:50:24 +03:00
2007-03-07 04:08:01 +03:00
key . objectid = blocknr ;
key . flags = 0 ;
2007-03-15 19:56:47 +03:00
btrfs_set_key_type ( & key , BTRFS_EXTENT_ITEM_KEY ) ;
2007-03-07 04:08:01 +03:00
key . offset = num_blocks ;
2007-05-03 17:06:49 +04:00
find_free_extent ( trans , root , 0 , 0 , ( u64 ) - 1 , & ins , 0 ) ;
2007-04-02 19:20:42 +04:00
path = btrfs_alloc_path ( ) ;
BUG_ON ( ! path ) ;
btrfs_init_path ( path ) ;
2007-04-05 18:38:44 +04:00
2007-04-02 19:20:42 +04:00
ret = btrfs_search_slot ( trans , extent_root , & key , path , - 1 , 1 ) ;
2007-03-07 04:08:01 +03:00
if ( ret ) {
2007-03-21 18:12:56 +03:00
printk ( " failed to find %Lu \n " , key . objectid ) ;
2007-03-13 17:46:10 +03:00
btrfs_print_tree ( extent_root , extent_root - > node ) ;
2007-03-21 18:12:56 +03:00
printk ( " failed to find %Lu \n " , key . objectid ) ;
2007-03-07 04:08:01 +03:00
BUG ( ) ;
}
2007-04-02 19:20:42 +04:00
ei = btrfs_item_ptr ( btrfs_buffer_leaf ( path - > nodes [ 0 ] ) , path - > slots [ 0 ] ,
2007-03-14 21:14:43 +03:00
struct btrfs_extent_item ) ;
2007-03-07 04:08:01 +03:00
BUG_ON ( ei - > refs = = 0 ) ;
2007-03-13 16:49:06 +03:00
refs = btrfs_extent_refs ( ei ) - 1 ;
btrfs_set_extent_refs ( ei , refs ) ;
2007-04-02 19:20:42 +04:00
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
2007-03-13 16:49:06 +03:00
if ( refs = = 0 ) {
2007-03-21 03:35:03 +03:00
u64 super_blocks_used ;
2007-03-25 19:35:08 +04:00
if ( pin ) {
2007-03-26 18:15:30 +04:00
ret = pin_down_block ( root , blocknr , 0 ) ;
2007-03-25 19:35:08 +04:00
BUG_ON ( ret ) ;
}
2007-03-21 03:35:03 +03:00
super_blocks_used = btrfs_super_blocks_used ( info - > disk_super ) ;
btrfs_set_super_blocks_used ( info - > disk_super ,
super_blocks_used - num_blocks ) ;
2007-04-02 19:20:42 +04:00
ret = btrfs_del_item ( trans , extent_root , path ) ;
2007-03-07 04:08:01 +03:00
if ( ret )
BUG ( ) ;
2007-05-10 04:13:14 +04:00
ret = update_block_group ( trans , root , blocknr , num_blocks , 0 ,
mark_free ) ;
2007-04-27 00:46:15 +04:00
BUG_ON ( ret ) ;
2007-03-07 04:08:01 +03:00
}
2007-04-02 19:20:42 +04:00
btrfs_free_path ( path ) ;
2007-03-16 23:20:31 +03:00
finish_current_insert ( trans , extent_root ) ;
2007-03-07 04:08:01 +03:00
return ret ;
}
/*
* find all the blocks marked as pending in the radix tree and remove
* them from the extent map
*/
2007-03-16 23:20:31 +03:00
static int del_pending_extents ( struct btrfs_trans_handle * trans , struct
btrfs_root * extent_root )
2007-03-07 04:08:01 +03:00
{
int ret ;
2007-03-22 19:13:20 +03:00
int wret ;
int err = 0 ;
2007-03-26 18:15:30 +04:00
unsigned long gang [ 4 ] ;
2007-03-07 04:08:01 +03:00
int i ;
2007-03-26 18:15:30 +04:00
struct radix_tree_root * pending_radix ;
struct radix_tree_root * pinned_radix ;
2007-05-06 18:15:01 +04:00
struct btrfs_block_group_cache * cache ;
2007-03-26 18:15:30 +04:00
pending_radix = & extent_root - > fs_info - > pending_del_radix ;
pinned_radix = & extent_root - > fs_info - > pinned_radix ;
2007-03-07 04:08:01 +03:00
while ( 1 ) {
2007-05-10 04:13:14 +04:00
ret = find_first_radix_bit ( pending_radix , gang , 0 ,
2007-03-26 18:15:30 +04:00
ARRAY_SIZE ( gang ) ) ;
2007-03-07 04:08:01 +03:00
if ( ! ret )
break ;
for ( i = 0 ; i < ret ; i + + ) {
2007-03-26 18:15:30 +04:00
wret = set_radix_bit ( pinned_radix , gang [ i ] ) ;
2007-05-06 18:15:01 +04:00
if ( wret = = 0 ) {
cache = lookup_block_group ( extent_root - > fs_info ,
gang [ i ] ) ;
if ( cache )
cache - > pinned + + ;
}
if ( wret < 0 ) {
printk ( KERN_CRIT " set_radix_bit, err %d \n " ,
wret ) ;
BUG_ON ( wret < 0 ) ;
}
2007-03-26 18:15:30 +04:00
wret = clear_radix_bit ( pending_radix , gang [ i ] ) ;
BUG_ON ( wret ) ;
2007-03-23 17:01:08 +03:00
wret = __free_extent ( trans , extent_root ,
2007-05-10 04:13:14 +04:00
gang [ i ] , 1 , 0 , 0 ) ;
2007-03-22 19:13:20 +03:00
if ( wret )
err = wret ;
2007-02-26 18:40:21 +03:00
}
}
2007-03-22 19:13:20 +03:00
return err ;
2007-02-26 18:40:21 +03:00
}
/*
* remove an extent from the root , returns 0 on success
*/
2007-03-16 23:20:31 +03:00
int btrfs_free_extent ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , u64 blocknr , u64 num_blocks , int pin )
2007-02-26 18:40:21 +03:00
{
2007-03-20 21:38:32 +03:00
struct btrfs_root * extent_root = root - > fs_info - > extent_root ;
2007-02-26 18:40:21 +03:00
int pending_ret ;
int ret ;
2007-03-07 04:08:01 +03:00
2007-02-26 18:40:21 +03:00
if ( root = = extent_root ) {
2007-03-26 18:15:30 +04:00
pin_down_block ( root , blocknr , 1 ) ;
2007-02-26 18:40:21 +03:00
return 0 ;
}
2007-05-10 04:13:14 +04:00
ret = __free_extent ( trans , root , blocknr , num_blocks , pin , pin = = 0 ) ;
2007-03-22 19:13:20 +03:00
pending_ret = del_pending_extents ( trans , root - > fs_info - > extent_root ) ;
2007-02-26 18:40:21 +03:00
return ret ? ret : pending_ret ;
}
/*
* walks the btree of allocated extents and find a hole of a given size .
* The key ins is changed to record the hole :
* ins - > objectid = = block start
2007-03-15 19:56:47 +03:00
* ins - > flags = BTRFS_EXTENT_ITEM_KEY
2007-02-26 18:40:21 +03:00
* ins - > offset = = number of blocks
* Any available blocks before search_start are skipped .
*/
2007-03-16 23:20:31 +03:00
static int find_free_extent ( struct btrfs_trans_handle * trans , struct btrfs_root
* orig_root , u64 num_blocks , u64 search_start , u64
2007-05-03 17:06:49 +04:00
search_end , struct btrfs_key * ins , int data )
2007-02-26 18:40:21 +03:00
{
2007-04-02 19:20:42 +04:00
struct btrfs_path * path ;
2007-03-12 23:22:34 +03:00
struct btrfs_key key ;
2007-02-26 18:40:21 +03:00
int ret ;
u64 hole_size = 0 ;
int slot = 0 ;
2007-03-22 19:13:20 +03:00
u64 last_block = 0 ;
2007-03-07 19:50:24 +03:00
u64 test_block ;
2007-05-06 18:15:01 +04:00
u64 orig_search_start = search_start ;
2007-02-26 18:40:21 +03:00
int start_found ;
2007-03-13 17:46:10 +03:00
struct btrfs_leaf * l ;
2007-03-20 21:38:32 +03:00
struct btrfs_root * root = orig_root - > fs_info - > extent_root ;
2007-04-25 23:52:25 +04:00
struct btrfs_fs_info * info = root - > fs_info ;
2007-03-08 00:15:30 +03:00
int total_needed = num_blocks ;
2007-04-25 23:52:25 +04:00
int total_found = 0 ;
int fill_prealloc = 0 ;
2007-03-22 19:13:20 +03:00
int level ;
2007-05-03 17:06:49 +04:00
struct btrfs_block_group_cache * block_group ;
2007-05-06 18:15:01 +04:00
int full_scan = 0 ;
2007-05-18 21:28:27 +04:00
u64 limit ;
2007-02-26 18:40:21 +03:00
2007-04-04 23:27:52 +04:00
path = btrfs_alloc_path ( ) ;
ins - > flags = 0 ;
btrfs_set_key_type ( ins , BTRFS_EXTENT_ITEM_KEY ) ;
2007-03-22 19:13:20 +03:00
level = btrfs_header_level ( btrfs_buffer_header ( root - > node ) ) ;
2007-04-25 23:52:25 +04:00
if ( num_blocks = = 0 ) {
fill_prealloc = 1 ;
num_blocks = 1 ;
2007-04-28 23:17:08 +04:00
total_needed = ( min ( level + 1 , BTRFS_MAX_LEVEL ) + 2 ) * 3 ;
2007-04-25 23:52:25 +04:00
}
2007-05-08 04:03:49 +04:00
if ( search_end = = ( u64 ) - 1 )
search_end = btrfs_super_total_blocks ( info - > disk_super ) ;
2007-05-06 18:15:01 +04:00
if ( search_start ) {
block_group = lookup_block_group ( info , search_start ) ;
block_group = btrfs_find_block_group ( root , block_group ,
2007-05-18 21:28:27 +04:00
search_start , data , 1 ) ;
2007-05-06 18:15:01 +04:00
} else {
block_group = btrfs_find_block_group ( root ,
trans - > block_group , 0 ,
2007-05-18 21:28:27 +04:00
data , 1 ) ;
2007-05-06 18:15:01 +04:00
}
check_failed :
2007-05-08 04:03:49 +04:00
if ( ! full_scan & & block_group - > data ! = data )
2007-05-06 18:15:01 +04:00
WARN_ON ( 1 ) ;
2007-05-10 04:13:14 +04:00
if ( ! data )
search_start = find_search_start ( root , & block_group ,
search_start , total_needed ) ;
else
search_start = max ( block_group - > last_alloc , search_start ) ;
2007-04-02 19:20:42 +04:00
btrfs_init_path ( path ) ;
2007-02-26 18:40:21 +03:00
ins - > objectid = search_start ;
ins - > offset = 0 ;
start_found = 0 ;
2007-05-10 04:13:14 +04:00
2007-04-02 19:20:42 +04:00
ret = btrfs_search_slot ( trans , root , ins , path , 0 , 0 ) ;
2007-03-01 00:46:22 +03:00
if ( ret < 0 )
goto error ;
2007-03-01 00:35:06 +03:00
2007-05-10 04:13:14 +04:00
if ( path - > slots [ 0 ] > 0 ) {
2007-04-02 19:20:42 +04:00
path - > slots [ 0 ] - - ;
2007-05-10 04:13:14 +04:00
}
l = btrfs_buffer_leaf ( path - > nodes [ 0 ] ) ;
btrfs_disk_key_to_cpu ( & key , & l - > items [ path - > slots [ 0 ] ] . key ) ;
/*
* a rare case , go back one key if we hit a block group item
* instead of an extent item
*/
if ( btrfs_key_type ( & key ) ! = BTRFS_EXTENT_ITEM_KEY & &
key . objectid + key . offset > = search_start ) {
ins - > objectid = key . objectid ;
ins - > offset = key . offset - 1 ;
btrfs_release_path ( root , path ) ;
ret = btrfs_search_slot ( trans , root , ins , path , 0 , 0 ) ;
if ( ret < 0 )
goto error ;
if ( path - > slots [ 0 ] > 0 ) {
path - > slots [ 0 ] - - ;
}
}
2007-03-08 00:15:30 +03:00
2007-02-26 18:40:21 +03:00
while ( 1 ) {
2007-04-02 19:20:42 +04:00
l = btrfs_buffer_leaf ( path - > nodes [ 0 ] ) ;
slot = path - > slots [ 0 ] ;
2007-03-12 19:01:18 +03:00
if ( slot > = btrfs_header_nritems ( & l - > header ) ) {
2007-04-25 23:52:25 +04:00
if ( fill_prealloc ) {
info - > extent_tree_prealloc_nr = 0 ;
total_found = 0 ;
}
2007-05-18 21:28:27 +04:00
if ( start_found )
limit = last_block +
block_group - > key . offset / 2 ;
else
limit = search_start +
block_group - > key . offset / 2 ;
2007-04-02 19:20:42 +04:00
ret = btrfs_next_leaf ( root , path ) ;
2007-02-26 18:40:21 +03:00
if ( ret = = 0 )
continue ;
2007-03-01 00:46:22 +03:00
if ( ret < 0 )
goto error ;
2007-02-26 18:40:21 +03:00
if ( ! start_found ) {
ins - > objectid = search_start ;
2007-05-08 04:03:49 +04:00
ins - > offset = search_end - search_start ;
2007-02-26 18:40:21 +03:00
start_found = 1 ;
goto check_pending ;
}
ins - > objectid = last_block > search_start ?
last_block : search_start ;
2007-05-08 04:03:49 +04:00
ins - > offset = search_end - ins - > objectid ;
2007-02-26 18:40:21 +03:00
goto check_pending ;
}
2007-05-10 04:13:14 +04:00
2007-03-12 23:22:34 +03:00
btrfs_disk_key_to_cpu ( & key , & l - > items [ slot ] . key ) ;
2007-05-10 04:13:14 +04:00
if ( key . objectid > = search_start & & key . objectid > last_block & &
start_found ) {
if ( last_block < search_start )
last_block = search_start ;
hole_size = key . objectid - last_block ;
if ( hole_size > = num_blocks ) {
ins - > objectid = last_block ;
ins - > offset = hole_size ;
goto check_pending ;
2007-03-08 00:15:30 +03:00
}
2007-02-26 18:40:21 +03:00
}
2007-05-10 04:13:14 +04:00
if ( btrfs_key_type ( & key ) ! = BTRFS_EXTENT_ITEM_KEY )
goto next ;
2007-03-08 00:15:30 +03:00
start_found = 1 ;
2007-03-12 23:22:34 +03:00
last_block = key . objectid + key . offset ;
2007-05-06 18:15:01 +04:00
if ( last_block > = block_group - > key . objectid +
block_group - > key . offset ) {
btrfs_release_path ( root , path ) ;
search_start = block_group - > key . objectid +
block_group - > key . offset * 2 ;
goto new_group ;
}
2007-04-27 00:46:15 +04:00
next :
2007-04-02 19:20:42 +04:00
path - > slots [ 0 ] + + ;
2007-05-18 21:28:27 +04:00
cond_resched ( ) ;
2007-02-26 18:40:21 +03:00
}
// FIXME -ENOSPC
check_pending :
/* we have to make sure we didn't find an extent that has already
* been allocated by the map tree or the original allocation
*/
2007-04-02 19:20:42 +04:00
btrfs_release_path ( root , path ) ;
2007-02-26 18:40:21 +03:00
BUG_ON ( ins - > objectid < search_start ) ;
2007-05-10 04:13:14 +04:00
2007-05-08 04:03:49 +04:00
if ( ins - > objectid + num_blocks > = search_end ) {
2007-05-06 18:15:01 +04:00
if ( full_scan )
2007-04-28 16:48:10 +04:00
return - ENOSPC ;
2007-05-06 18:15:01 +04:00
search_start = orig_search_start ;
full_scan = 1 ;
goto new_group ;
2007-04-28 16:48:10 +04:00
}
2007-03-07 19:50:24 +03:00
for ( test_block = ins - > objectid ;
2007-04-25 23:52:25 +04:00
test_block < ins - > objectid + num_blocks ; test_block + + ) {
if ( test_radix_bit ( & info - > pinned_radix , test_block ) ) {
2007-03-07 19:50:24 +03:00
search_start = test_block + 1 ;
2007-05-06 18:15:01 +04:00
goto new_group ;
2007-02-26 18:40:21 +03:00
}
}
2007-04-25 23:52:25 +04:00
if ( ! fill_prealloc & & info - > extent_tree_insert_nr ) {
u64 last =
info - > extent_tree_insert [ info - > extent_tree_insert_nr - 1 ] ;
if ( ins - > objectid + num_blocks >
info - > extent_tree_insert [ 0 ] & &
ins - > objectid < = last ) {
search_start = last + 1 ;
2007-05-10 04:13:14 +04:00
WARN_ON ( ! full_scan ) ;
2007-05-06 18:15:01 +04:00
goto new_group ;
2007-04-25 23:52:25 +04:00
}
}
if ( ! fill_prealloc & & info - > extent_tree_prealloc_nr ) {
u64 first =
info - > extent_tree_prealloc [ info - > extent_tree_prealloc_nr - 1 ] ;
if ( ins - > objectid + num_blocks > first & &
ins - > objectid < = info - > extent_tree_prealloc [ 0 ] ) {
search_start = info - > extent_tree_prealloc [ 0 ] + 1 ;
2007-05-10 04:13:14 +04:00
WARN_ON ( ! full_scan ) ;
2007-05-06 18:15:01 +04:00
goto new_group ;
2007-04-25 23:52:25 +04:00
}
}
if ( fill_prealloc ) {
int nr ;
test_block = ins - > objectid ;
2007-05-10 04:13:14 +04:00
if ( test_block - info - > extent_tree_prealloc [ total_needed - 1 ] > =
leaf_range ( root ) ) {
total_found = 0 ;
info - > extent_tree_prealloc_nr = total_found ;
}
2007-04-25 23:52:25 +04:00
while ( test_block < ins - > objectid + ins - > offset & &
total_found < total_needed ) {
nr = total_needed - total_found - 1 ;
BUG_ON ( nr < 0 ) ;
2007-04-27 18:08:34 +04:00
info - > extent_tree_prealloc [ nr ] = test_block ;
2007-04-25 23:52:25 +04:00
total_found + + ;
test_block + + ;
}
if ( total_found < total_needed ) {
search_start = test_block ;
2007-05-06 18:15:01 +04:00
goto new_group ;
2007-04-25 23:52:25 +04:00
}
2007-04-27 18:08:34 +04:00
info - > extent_tree_prealloc_nr = total_found ;
}
2007-05-10 04:13:14 +04:00
if ( ! data ) {
block_group = lookup_block_group ( info , ins - > objectid ) ;
if ( block_group ) {
if ( fill_prealloc )
block_group - > last_prealloc =
info - > extent_tree_prealloc [ total_needed - 1 ] ;
else
trans - > block_group = block_group ;
}
2007-04-25 23:52:25 +04:00
}
2007-03-07 19:50:24 +03:00
ins - > offset = num_blocks ;
2007-04-02 19:20:42 +04:00
btrfs_free_path ( path ) ;
2007-02-26 18:40:21 +03:00
return 0 ;
2007-05-06 18:15:01 +04:00
new_group :
2007-05-08 04:03:49 +04:00
if ( search_start + num_blocks > = search_end ) {
2007-05-06 18:15:01 +04:00
search_start = orig_search_start ;
2007-05-10 04:13:14 +04:00
printk ( " doing full scan! \n " ) ;
2007-05-06 18:15:01 +04:00
full_scan = 1 ;
}
block_group = lookup_block_group ( info , search_start ) ;
if ( ! full_scan )
block_group = btrfs_find_block_group ( root , block_group ,
2007-05-18 21:28:27 +04:00
search_start , data , 0 ) ;
cond_resched ( ) ;
2007-05-06 18:15:01 +04:00
goto check_failed ;
2007-03-01 00:46:22 +03:00
error :
2007-04-02 19:20:42 +04:00
btrfs_release_path ( root , path ) ;
btrfs_free_path ( path ) ;
2007-03-01 00:46:22 +03:00
return ret ;
2007-02-26 18:40:21 +03:00
}
/*
* finds a free extent and does all the dirty work required for allocation
* returns the key for the extent through ins , and a tree buffer for
* the first block of the extent through buf .
*
* returns 0 if everything worked , non - zero otherwise .
*/
2007-04-21 04:23:12 +04:00
int btrfs_alloc_extent ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root , u64 owner ,
2007-04-24 20:07:39 +04:00
u64 num_blocks , u64 search_start ,
2007-05-03 17:06:49 +04:00
u64 search_end , struct btrfs_key * ins , int data )
2007-02-26 18:40:21 +03:00
{
int ret ;
int pending_ret ;
2007-03-21 03:35:03 +03:00
u64 super_blocks_used ;
struct btrfs_fs_info * info = root - > fs_info ;
struct btrfs_root * extent_root = info - > extent_root ;
2007-03-13 17:46:10 +03:00
struct btrfs_extent_item extent_item ;
2007-04-25 23:52:25 +04:00
struct btrfs_key prealloc_key ;
2007-03-07 19:50:24 +03:00
2007-03-13 16:49:06 +03:00
btrfs_set_extent_refs ( & extent_item , 1 ) ;
2007-04-21 04:23:12 +04:00
btrfs_set_extent_owner ( & extent_item , owner ) ;
2007-02-26 18:40:21 +03:00
2007-03-07 19:50:24 +03:00
if ( root = = extent_root ) {
2007-04-25 23:52:25 +04:00
int nr ;
BUG_ON ( info - > extent_tree_prealloc_nr = = 0 ) ;
2007-03-07 19:50:24 +03:00
BUG_ON ( num_blocks ! = 1 ) ;
ins - > offset = 1 ;
2007-04-25 23:52:25 +04:00
info - > extent_tree_prealloc_nr - - ;
nr = info - > extent_tree_prealloc_nr ;
ins - > objectid = info - > extent_tree_prealloc [ nr ] ;
info - > extent_tree_insert [ info - > extent_tree_insert_nr + + ] =
ins - > objectid ;
2007-04-27 00:46:15 +04:00
ret = update_block_group ( trans , root ,
2007-05-10 04:13:14 +04:00
ins - > objectid , ins - > offset , 1 , 0 ) ;
2007-04-27 00:46:15 +04:00
BUG_ON ( ret ) ;
2007-02-26 18:40:21 +03:00
return 0 ;
}
2007-05-10 04:13:14 +04:00
/*
* if we ' re doing a data allocation , preallocate room in the
* extent tree first . This way the extent tree blocks end up
* in the correct block group .
*/
if ( data ) {
2007-05-18 21:28:27 +04:00
ret = find_free_extent ( trans , root , 0 , 0 ,
2007-05-10 04:13:14 +04:00
search_end , & prealloc_key , 0 ) ;
if ( ret ) {
return ret ;
}
if ( prealloc_key . objectid + prealloc_key . offset > = search_end ) {
int nr = info - > extent_tree_prealloc_nr ;
search_end = info - > extent_tree_prealloc [ nr - 1 ] - 1 ;
} else {
search_start = info - > extent_tree_prealloc [ 0 ] + 1 ;
}
}
2007-04-25 23:52:25 +04:00
/* do the real allocation */
2007-03-16 23:20:31 +03:00
ret = find_free_extent ( trans , root , num_blocks , search_start ,
2007-05-03 17:06:49 +04:00
search_end , ins , data ) ;
2007-05-10 04:13:14 +04:00
if ( ret ) {
2007-03-07 19:50:24 +03:00
return ret ;
2007-05-10 04:13:14 +04:00
}
2007-02-26 18:40:21 +03:00
2007-05-10 04:13:14 +04:00
/*
* if we ' re doing a metadata allocation , preallocate space in the
* extent tree second . This way , we don ' t create a tiny hole
* in the allocation map between any unused preallocation blocks
* and the metadata block we ' re actually allocating . On disk ,
* it ' ll go :
* [ block we ' ve allocated ] , [ used prealloc 1 ] , [ unused prealloc ]
* The unused prealloc will get reused the next time around .
*/
if ( ! data ) {
if ( ins - > objectid + ins - > offset > = search_end )
search_end = ins - > objectid - 1 ;
else
search_start = ins - > objectid + ins - > offset ;
2007-05-08 04:03:49 +04:00
2007-05-10 04:13:14 +04:00
ret = find_free_extent ( trans , root , 0 , search_start ,
search_end , & prealloc_key , 0 ) ;
if ( ret ) {
return ret ;
}
}
2007-04-25 23:52:25 +04:00
2007-03-21 03:35:03 +03:00
super_blocks_used = btrfs_super_blocks_used ( info - > disk_super ) ;
btrfs_set_super_blocks_used ( info - > disk_super , super_blocks_used +
num_blocks ) ;
2007-03-16 23:20:31 +03:00
ret = btrfs_insert_item ( trans , extent_root , ins , & extent_item ,
sizeof ( extent_item ) ) ;
2007-03-07 19:50:24 +03:00
2007-03-16 23:20:31 +03:00
finish_current_insert ( trans , extent_root ) ;
2007-03-22 19:13:20 +03:00
pending_ret = del_pending_extents ( trans , extent_root ) ;
2007-05-10 04:13:14 +04:00
if ( ret ) {
2007-03-07 19:50:24 +03:00
return ret ;
2007-05-10 04:13:14 +04:00
}
if ( pending_ret ) {
2007-03-07 19:50:24 +03:00
return pending_ret ;
2007-05-10 04:13:14 +04:00
}
ret = update_block_group ( trans , root , ins - > objectid , ins - > offset , 1 , 0 ) ;
2007-03-07 19:50:24 +03:00
return 0 ;
2007-02-26 18:40:21 +03:00
}
/*
* helper function to allocate a block for a given tree
* returns the tree buffer or NULL .
*/
2007-03-22 19:13:20 +03:00
struct buffer_head * btrfs_alloc_free_block ( struct btrfs_trans_handle * trans ,
2007-04-30 23:25:45 +04:00
struct btrfs_root * root , u64 hint )
2007-02-26 18:40:21 +03:00
{
2007-03-12 23:22:34 +03:00
struct btrfs_key ins ;
2007-02-26 18:40:21 +03:00
int ret ;
2007-03-22 19:13:20 +03:00
struct buffer_head * buf ;
2007-02-26 18:40:21 +03:00
2007-04-21 04:23:12 +04:00
ret = btrfs_alloc_extent ( trans , root , root - > root_key . objectid ,
2007-05-18 21:28:27 +04:00
1 , hint , ( unsigned long ) - 1 , & ins , 0 ) ;
2007-02-26 18:40:21 +03:00
if ( ret ) {
BUG ( ) ;
return NULL ;
}
2007-04-27 00:46:15 +04:00
BUG_ON ( ret ) ;
2007-03-28 21:57:48 +04:00
buf = btrfs_find_create_tree_block ( root , ins . objectid ) ;
2007-03-23 18:00:45 +03:00
set_buffer_uptodate ( buf ) ;
2007-05-01 16:53:32 +04:00
set_buffer_checked ( buf ) ;
2007-04-28 17:29:35 +04:00
set_radix_bit ( & trans - > transaction - > dirty_pages , buf - > b_page - > index ) ;
2007-02-26 18:40:21 +03:00
return buf ;
}
2007-03-07 04:08:01 +03:00
2007-03-27 14:33:00 +04:00
static int drop_leaf_ref ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root , struct buffer_head * cur )
{
struct btrfs_disk_key * key ;
struct btrfs_leaf * leaf ;
struct btrfs_file_extent_item * fi ;
int i ;
int nritems ;
int ret ;
BUG_ON ( ! btrfs_is_leaf ( btrfs_buffer_node ( cur ) ) ) ;
leaf = btrfs_buffer_leaf ( cur ) ;
nritems = btrfs_header_nritems ( & leaf - > header ) ;
for ( i = 0 ; i < nritems ; i + + ) {
2007-05-24 21:35:57 +04:00
u64 disk_blocknr ;
2007-03-27 14:33:00 +04:00
key = & leaf - > items [ i ] . key ;
if ( btrfs_disk_key_type ( key ) ! = BTRFS_EXTENT_DATA_KEY )
continue ;
fi = btrfs_item_ptr ( leaf , i , struct btrfs_file_extent_item ) ;
2007-04-19 21:37:44 +04:00
if ( btrfs_file_extent_type ( fi ) = = BTRFS_FILE_EXTENT_INLINE )
continue ;
2007-03-27 14:33:00 +04:00
/*
* FIXME make sure to insert a trans record that
* repeats the snapshot del on crash
*/
2007-05-24 21:35:57 +04:00
disk_blocknr = btrfs_file_extent_disk_blocknr ( fi ) ;
if ( disk_blocknr = = 0 )
continue ;
ret = btrfs_free_extent ( trans , root , disk_blocknr ,
2007-03-27 14:33:00 +04:00
btrfs_file_extent_disk_num_blocks ( fi ) ,
0 ) ;
BUG_ON ( ret ) ;
}
return 0 ;
}
2007-03-13 18:09:37 +03:00
/*
* helper function for drop_snapshot , this walks down the tree dropping ref
* counts as it goes .
*/
2007-03-16 23:20:31 +03:00
static int walk_down_tree ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , int * level )
2007-03-10 14:35:47 +03:00
{
2007-03-22 19:13:20 +03:00
struct buffer_head * next ;
struct buffer_head * cur ;
2007-03-10 14:35:47 +03:00
u64 blocknr ;
int ret ;
u32 refs ;
2007-04-02 19:20:42 +04:00
WARN_ON ( * level < 0 ) ;
WARN_ON ( * level > = BTRFS_MAX_LEVEL ) ;
2007-04-17 21:26:50 +04:00
ret = lookup_extent_ref ( trans , root , bh_blocknr ( path - > nodes [ * level ] ) ,
2007-03-27 14:33:00 +04:00
1 , & refs ) ;
2007-03-10 14:35:47 +03:00
BUG_ON ( ret ) ;
if ( refs > 1 )
goto out ;
2007-03-13 18:09:37 +03:00
/*
* walk down to the last node level and free all the leaves
*/
2007-03-27 14:33:00 +04:00
while ( * level > = 0 ) {
2007-04-02 19:20:42 +04:00
WARN_ON ( * level < 0 ) ;
WARN_ON ( * level > = BTRFS_MAX_LEVEL ) ;
2007-03-10 14:35:47 +03:00
cur = path - > nodes [ * level ] ;
2007-04-02 18:50:19 +04:00
if ( btrfs_header_level ( btrfs_buffer_header ( cur ) ) ! = * level )
WARN_ON ( 1 ) ;
2007-03-12 19:01:18 +03:00
if ( path - > slots [ * level ] > =
2007-03-22 19:13:20 +03:00
btrfs_header_nritems ( btrfs_buffer_header ( cur ) ) )
2007-03-10 14:35:47 +03:00
break ;
2007-03-27 14:33:00 +04:00
if ( * level = = 0 ) {
ret = drop_leaf_ref ( trans , root , cur ) ;
BUG_ON ( ret ) ;
break ;
}
2007-03-22 19:13:20 +03:00
blocknr = btrfs_node_blockptr ( btrfs_buffer_node ( cur ) ,
path - > slots [ * level ] ) ;
2007-04-17 21:26:50 +04:00
ret = lookup_extent_ref ( trans , root , blocknr , 1 , & refs ) ;
2007-03-27 14:33:00 +04:00
BUG_ON ( ret ) ;
if ( refs ! = 1 ) {
2007-03-10 14:35:47 +03:00
path - > slots [ * level ] + + ;
2007-03-16 23:20:31 +03:00
ret = btrfs_free_extent ( trans , root , blocknr , 1 , 1 ) ;
2007-03-10 14:35:47 +03:00
BUG_ON ( ret ) ;
continue ;
}
next = read_tree_block ( root , blocknr ) ;
2007-04-02 19:20:42 +04:00
WARN_ON ( * level < = 0 ) ;
2007-03-12 16:03:27 +03:00
if ( path - > nodes [ * level - 1 ] )
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , path - > nodes [ * level - 1 ] ) ;
2007-03-10 14:35:47 +03:00
path - > nodes [ * level - 1 ] = next ;
2007-03-22 19:13:20 +03:00
* level = btrfs_header_level ( btrfs_buffer_header ( next ) ) ;
2007-03-10 14:35:47 +03:00
path - > slots [ * level ] = 0 ;
}
out :
2007-04-02 19:20:42 +04:00
WARN_ON ( * level < 0 ) ;
WARN_ON ( * level > = BTRFS_MAX_LEVEL ) ;
2007-03-27 14:33:00 +04:00
ret = btrfs_free_extent ( trans , root ,
2007-04-11 23:53:25 +04:00
bh_blocknr ( path - > nodes [ * level ] ) , 1 , 1 ) ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , path - > nodes [ * level ] ) ;
2007-03-10 14:35:47 +03:00
path - > nodes [ * level ] = NULL ;
* level + = 1 ;
BUG_ON ( ret ) ;
return 0 ;
}
2007-03-13 18:09:37 +03:00
/*
* helper for dropping snapshots . This walks back up the tree in the path
* to find the first node higher up where we haven ' t yet gone through
* all the slots
*/
2007-03-16 23:20:31 +03:00
static int walk_up_tree ( struct btrfs_trans_handle * trans , struct btrfs_root
* root , struct btrfs_path * path , int * level )
2007-03-10 14:35:47 +03:00
{
int i ;
int slot ;
int ret ;
2007-03-13 17:46:10 +03:00
for ( i = * level ; i < BTRFS_MAX_LEVEL - 1 & & path - > nodes [ i ] ; i + + ) {
2007-03-10 14:35:47 +03:00
slot = path - > slots [ i ] ;
2007-03-22 19:13:20 +03:00
if ( slot < btrfs_header_nritems (
btrfs_buffer_header ( path - > nodes [ i ] ) ) - 1 ) {
2007-03-10 14:35:47 +03:00
path - > slots [ i ] + + ;
* level = i ;
return 0 ;
} else {
2007-03-16 23:20:31 +03:00
ret = btrfs_free_extent ( trans , root ,
2007-04-11 23:53:25 +04:00
bh_blocknr ( path - > nodes [ * level ] ) ,
2007-03-16 23:20:31 +03:00
1 , 1 ) ;
2007-03-27 14:33:00 +04:00
BUG_ON ( ret ) ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , path - > nodes [ * level ] ) ;
2007-03-12 16:03:27 +03:00
path - > nodes [ * level ] = NULL ;
2007-03-10 14:35:47 +03:00
* level = i + 1 ;
}
}
return 1 ;
}
2007-03-13 18:09:37 +03:00
/*
* drop the reference count on the tree rooted at ' snap ' . This traverses
* the tree freeing any blocks that have a ref count of zero after being
* decremented .
*/
2007-03-16 23:20:31 +03:00
int btrfs_drop_snapshot ( struct btrfs_trans_handle * trans , struct btrfs_root
2007-03-22 19:13:20 +03:00
* root , struct buffer_head * snap )
2007-03-10 14:35:47 +03:00
{
2007-03-13 23:47:54 +03:00
int ret = 0 ;
2007-03-13 18:09:37 +03:00
int wret ;
2007-03-10 14:35:47 +03:00
int level ;
2007-04-02 19:20:42 +04:00
struct btrfs_path * path ;
2007-03-10 14:35:47 +03:00
int i ;
int orig_level ;
2007-04-02 19:20:42 +04:00
path = btrfs_alloc_path ( ) ;
BUG_ON ( ! path ) ;
btrfs_init_path ( path ) ;
2007-03-10 14:35:47 +03:00
2007-03-22 19:13:20 +03:00
level = btrfs_header_level ( btrfs_buffer_header ( snap ) ) ;
2007-03-10 14:35:47 +03:00
orig_level = level ;
2007-04-02 19:20:42 +04:00
path - > nodes [ level ] = snap ;
path - > slots [ level ] = 0 ;
2007-03-10 14:35:47 +03:00
while ( 1 ) {
2007-04-02 19:20:42 +04:00
wret = walk_down_tree ( trans , root , path , & level ) ;
2007-03-13 18:09:37 +03:00
if ( wret > 0 )
2007-03-10 14:35:47 +03:00
break ;
2007-03-13 18:09:37 +03:00
if ( wret < 0 )
ret = wret ;
2007-04-02 19:20:42 +04:00
wret = walk_up_tree ( trans , root , path , & level ) ;
2007-03-13 18:09:37 +03:00
if ( wret > 0 )
2007-03-10 14:35:47 +03:00
break ;
2007-03-13 18:09:37 +03:00
if ( wret < 0 )
ret = wret ;
2007-05-02 23:53:43 +04:00
btrfs_btree_balance_dirty ( root ) ;
2007-03-10 14:35:47 +03:00
}
2007-03-12 16:03:27 +03:00
for ( i = 0 ; i < = orig_level ; i + + ) {
2007-04-02 19:20:42 +04:00
if ( path - > nodes [ i ] ) {
btrfs_block_release ( root , path - > nodes [ i ] ) ;
2007-03-12 16:03:27 +03:00
}
2007-03-10 14:35:47 +03:00
}
2007-04-02 19:20:42 +04:00
btrfs_free_path ( path ) ;
2007-03-13 18:09:37 +03:00
return ret ;
2007-03-10 14:35:47 +03:00
}
2007-04-27 00:46:15 +04:00
2007-05-06 18:15:01 +04:00
static int free_block_group_radix ( struct radix_tree_root * radix )
2007-04-27 00:46:15 +04:00
{
int ret ;
struct btrfs_block_group_cache * cache [ 8 ] ;
int i ;
while ( 1 ) {
2007-05-06 18:15:01 +04:00
ret = radix_tree_gang_lookup ( radix , ( void * * ) cache , 0 ,
2007-04-27 00:46:15 +04:00
ARRAY_SIZE ( cache ) ) ;
if ( ! ret )
break ;
for ( i = 0 ; i < ret ; i + + ) {
2007-05-06 18:15:01 +04:00
radix_tree_delete ( radix , cache [ i ] - > key . objectid +
2007-04-27 00:46:15 +04:00
cache [ i ] - > key . offset - 1 ) ;
kfree ( cache [ i ] ) ;
}
}
return 0 ;
}
2007-05-06 18:15:01 +04:00
int btrfs_free_block_groups ( struct btrfs_fs_info * info )
{
int ret ;
int ret2 ;
2007-05-10 04:13:14 +04:00
unsigned long gang [ 16 ] ;
int i ;
2007-05-06 18:15:01 +04:00
ret = free_block_group_radix ( & info - > block_group_radix ) ;
ret2 = free_block_group_radix ( & info - > block_group_data_radix ) ;
if ( ret )
return ret ;
if ( ret2 )
return ret2 ;
2007-05-10 04:13:14 +04:00
while ( 1 ) {
ret = find_first_radix_bit ( & info - > extent_map_radix ,
gang , 0 , ARRAY_SIZE ( gang ) ) ;
if ( ! ret )
break ;
for ( i = 0 ; i < ret ; i + + ) {
clear_radix_bit ( & info - > extent_map_radix , gang [ i ] ) ;
}
}
2007-05-06 18:15:01 +04:00
return 0 ;
}
2007-04-27 00:46:15 +04:00
int btrfs_read_block_groups ( struct btrfs_root * root )
{
struct btrfs_path * path ;
int ret ;
int err = 0 ;
struct btrfs_block_group_item * bi ;
struct btrfs_block_group_cache * cache ;
2007-05-06 18:15:01 +04:00
struct btrfs_fs_info * info = root - > fs_info ;
struct radix_tree_root * radix ;
2007-04-27 00:46:15 +04:00
struct btrfs_key key ;
struct btrfs_key found_key ;
struct btrfs_leaf * leaf ;
u64 group_size_blocks = BTRFS_BLOCK_GROUP_SIZE / root - > blocksize ;
2007-04-30 23:25:45 +04:00
u64 used ;
2007-05-06 18:15:01 +04:00
u64 nr = 0 ;
2007-04-27 00:46:15 +04:00
2007-05-06 18:15:01 +04:00
root = info - > extent_root ;
2007-04-27 00:46:15 +04:00
key . objectid = 0 ;
key . offset = group_size_blocks ;
key . flags = 0 ;
btrfs_set_key_type ( & key , BTRFS_BLOCK_GROUP_ITEM_KEY ) ;
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
while ( 1 ) {
2007-05-06 18:15:01 +04:00
ret = btrfs_search_slot ( NULL , info - > extent_root ,
2007-04-27 00:46:15 +04:00
& key , path , 0 , 0 ) ;
if ( ret ! = 0 ) {
err = ret ;
break ;
}
leaf = btrfs_buffer_leaf ( path - > nodes [ 0 ] ) ;
btrfs_disk_key_to_cpu ( & found_key ,
& leaf - > items [ path - > slots [ 0 ] ] . key ) ;
cache = kmalloc ( sizeof ( * cache ) , GFP_NOFS ) ;
if ( ! cache ) {
err = - 1 ;
break ;
}
2007-05-08 04:03:49 +04:00
2007-05-10 04:13:14 +04:00
if ( nr % 3 )
2007-05-08 04:03:49 +04:00
radix = & info - > block_group_data_radix ;
else
radix = & info - > block_group_radix ;
2007-04-27 00:46:15 +04:00
bi = btrfs_item_ptr ( leaf , path - > slots [ 0 ] ,
struct btrfs_block_group_item ) ;
memcpy ( & cache - > item , bi , sizeof ( * bi ) ) ;
memcpy ( & cache - > key , & found_key , sizeof ( found_key ) ) ;
2007-04-30 23:25:45 +04:00
cache - > last_alloc = cache - > key . objectid ;
cache - > first_free = cache - > key . objectid ;
2007-05-10 04:13:14 +04:00
cache - > last_prealloc = cache - > key . objectid ;
2007-05-06 18:15:01 +04:00
cache - > pinned = 0 ;
2007-05-10 04:13:14 +04:00
cache - > cached = 0 ;
if ( nr % 3 )
cache - > data = 1 ;
else
cache - > data = 0 ;
2007-05-08 04:03:49 +04:00
cache - > radix = radix ;
2007-04-27 00:46:15 +04:00
key . objectid = found_key . objectid + found_key . offset ;
btrfs_release_path ( root , path ) ;
2007-05-06 18:15:01 +04:00
ret = radix_tree_insert ( radix , found_key . objectid +
2007-04-27 00:46:15 +04:00
found_key . offset - 1 ,
( void * ) cache ) ;
BUG_ON ( ret ) ;
2007-04-30 23:25:45 +04:00
used = btrfs_block_group_used ( bi ) ;
2007-05-06 18:15:01 +04:00
if ( used < ( key . offset * 8 ) / 10 ) {
radix_tree_tag_set ( radix , found_key . objectid +
2007-04-30 23:25:45 +04:00
found_key . offset - 1 ,
BTRFS_BLOCK_GROUP_AVAIL ) ;
}
2007-04-27 00:46:15 +04:00
if ( key . objectid > =
2007-05-06 18:15:01 +04:00
btrfs_super_total_blocks ( info - > disk_super ) )
2007-04-27 00:46:15 +04:00
break ;
2007-05-06 18:15:01 +04:00
nr + + ;
2007-04-27 00:46:15 +04:00
}
btrfs_free_path ( path ) ;
return 0 ;
}