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-07-11 18:00:37 +04:00
# include <linux/sched.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-10-16 00:15:19 +04:00
# define BLOCK_GROUP_DATA EXTENT_WRITEBACK
# define BLOCK_GROUP_METADATA EXTENT_UPTODATE
# define BLOCK_GROUP_DIRTY EXTENT_DIRTY
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-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 ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * leaf ;
2007-10-16 00:14:48 +04:00
struct extent_map_tree * free_space_cache ;
2007-05-10 04:13:14 +04:00
int slot ;
u64 last = 0 ;
u64 hole_size ;
2007-09-15 00:15:28 +04:00
u64 first_free ;
2007-05-10 04:13:14 +04:00
int found = 0 ;
root = root - > fs_info - > extent_root ;
2007-10-16 00:14:48 +04:00
free_space_cache = & root - > fs_info - > free_space_cache ;
2007-05-10 04:13:14 +04:00
if ( block_group - > cached )
return 0 ;
2007-10-16 00:14:48 +04:00
2007-05-10 04:13:14 +04:00
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
2007-09-15 00:15:28 +04:00
2007-08-28 00:49:44 +04:00
path - > reada = 2 ;
2007-09-15 00:15:28 +04:00
first_free = block_group - > key . objectid ;
2007-05-10 04:13:14 +04:00
key . objectid = block_group - > key . objectid ;
key . offset = 0 ;
2007-09-15 00:15:28 +04:00
2007-05-10 04:13:14 +04:00
btrfs_set_key_type ( & key , BTRFS_EXTENT_ITEM_KEY ) ;
ret = btrfs_search_slot ( NULL , root , & key , path , 0 , 0 ) ;
2007-09-15 00:15:28 +04:00
2007-05-10 04:13:14 +04:00
if ( ret < 0 )
return ret ;
2007-09-15 00:15:28 +04:00
2007-05-10 04:13:14 +04:00
if ( ret & & path - > slots [ 0 ] > 0 )
path - > slots [ 0 ] - - ;
2007-09-15 00:15:28 +04:00
2007-05-10 04:13:14 +04:00
while ( 1 ) {
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
2007-05-10 04:13:14 +04:00
slot = path - > slots [ 0 ] ;
2007-10-16 00:14:19 +04:00
if ( slot > = btrfs_header_nritems ( leaf ) ) {
2007-05-10 04:13:14 +04:00
ret = btrfs_next_leaf ( root , path ) ;
2007-06-22 22:16:25 +04:00
if ( ret < 0 )
goto err ;
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
break ;
}
}
2007-09-15 00:15:28 +04:00
2007-10-16 00:14:19 +04:00
btrfs_item_key_to_cpu ( leaf , & key , slot ) ;
2007-09-15 00:15:28 +04:00
if ( key . objectid < block_group - > key . objectid ) {
if ( key . objectid + key . offset > first_free )
first_free = key . objectid + key . offset ;
goto next ;
}
2007-05-10 04:13:14 +04:00
if ( key . objectid > = block_group - > key . objectid +
block_group - > key . offset ) {
break ;
}
2007-09-15 00:15:28 +04:00
2007-05-10 04:13:14 +04:00
if ( btrfs_key_type ( & key ) = = BTRFS_EXTENT_ITEM_KEY ) {
if ( ! found ) {
2007-09-15 00:15:28 +04:00
last = first_free ;
2007-05-10 04:13:14 +04:00
found = 1 ;
}
2007-10-16 00:14:48 +04:00
if ( key . objectid > last ) {
hole_size = key . objectid - last ;
set_extent_dirty ( free_space_cache , last ,
last + hole_size - 1 ,
GFP_NOFS ) ;
2007-09-15 00:15:28 +04:00
}
last = key . objectid + key . offset ;
2007-05-10 04:13:14 +04:00
}
2007-09-15 00:15:28 +04:00
next :
2007-05-10 04:13:14 +04:00
path - > slots [ 0 ] + + ;
}
2007-09-15 00:15:28 +04:00
if ( ! found )
last = first_free ;
if ( block_group - > key . objectid +
block_group - > key . offset > last ) {
hole_size = block_group - > key . objectid +
block_group - > key . offset - last ;
2007-10-16 00:14:48 +04:00
set_extent_dirty ( free_space_cache , last ,
last + hole_size - 1 , GFP_NOFS ) ;
2007-09-15 00:15:28 +04:00
}
2007-05-10 04:13:14 +04:00
block_group - > cached = 1 ;
2007-06-22 22:16:25 +04:00
err :
2007-05-10 04:13:14 +04:00
btrfs_free_path ( path ) ;
return 0 ;
}
2007-06-12 05:33:38 +04:00
struct btrfs_block_group_cache * btrfs_lookup_block_group ( struct
btrfs_fs_info * info ,
2007-10-16 00:15:53 +04:00
u64 bytenr )
2007-05-06 18:15:01 +04:00
{
2007-10-16 00:15:19 +04:00
struct extent_map_tree * block_group_cache ;
struct btrfs_block_group_cache * block_group = NULL ;
u64 ptr ;
u64 start ;
u64 end ;
2007-05-06 18:15:01 +04:00
int ret ;
2007-10-16 00:15:19 +04:00
block_group_cache = & info - > block_group_cache ;
ret = find_first_extent_bit ( block_group_cache ,
2007-10-16 00:15:53 +04:00
bytenr , & start , & end ,
2007-10-16 00:15:19 +04:00
BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA ) ;
2007-05-06 18:15:01 +04:00
if ( ret ) {
2007-10-16 00:15:19 +04:00
return NULL ;
2007-05-06 18:15:01 +04:00
}
2007-10-16 00:15:19 +04:00
ret = get_state_private ( block_group_cache , start , & ptr ) ;
if ( ret )
return NULL ;
2007-10-19 17:22:59 +04:00
block_group = ( struct btrfs_block_group_cache * ) ( unsigned long ) ptr ;
2007-10-16 00:15:19 +04:00
2007-10-16 00:15:53 +04:00
if ( block_group - > key . objectid < = bytenr & & bytenr < =
2007-10-16 00:15:19 +04:00
block_group - > key . objectid + block_group - > key . offset )
return block_group ;
2007-05-06 18:15:01 +04:00
return NULL ;
}
2007-05-10 04:13:14 +04:00
static u64 find_search_start ( struct btrfs_root * root ,
struct btrfs_block_group_cache * * cache_ret ,
2007-10-16 00:14:48 +04:00
u64 search_start , int num , int data )
2007-05-10 04:13:14 +04:00
{
int ret ;
struct btrfs_block_group_cache * cache = * cache_ret ;
2007-10-16 00:17:44 +04:00
u64 last ;
2007-10-16 00:14:48 +04:00
u64 start = 0 ;
u64 end = 0 ;
2007-05-10 04:13:14 +04:00
again :
2007-06-22 22:16:25 +04:00
ret = cache_block_group ( root , cache ) ;
if ( ret )
goto out ;
2007-10-16 00:17:44 +04:00
last = max ( search_start , cache - > key . objectid ) ;
2007-05-10 04:13:14 +04:00
while ( 1 ) {
2007-10-16 00:14:48 +04:00
ret = find_first_extent_bit ( & root - > fs_info - > free_space_cache ,
last , & start , & end , EXTENT_DIRTY ) ;
2007-10-16 00:17:44 +04:00
if ( ret ) {
goto new_group ;
}
2007-10-16 00:14:48 +04:00
start = max ( last , start ) ;
last = end + 1 ;
if ( end + 1 - start < num )
continue ;
2007-10-16 00:15:19 +04:00
if ( start + num > = cache - > key . objectid + cache - > key . offset )
2007-05-10 04:13:14 +04:00
goto new_group ;
2007-10-16 00:14:48 +04:00
return start ;
2007-05-10 04:13:14 +04:00
}
out :
2007-10-16 00:15:26 +04:00
return search_start ;
2007-05-10 04:13:14 +04:00
new_group :
2007-10-16 00:17:44 +04:00
last = cache - > key . objectid + cache - > key . offset ;
cache = btrfs_lookup_block_group ( root - > fs_info , last ) ;
2007-05-10 04:13:14 +04:00
if ( ! cache ) {
2007-10-16 00:15:26 +04:00
return search_start ;
2007-05-10 04:13:14 +04:00
}
2007-10-16 00:17:44 +04:00
cache = btrfs_find_block_group ( root , cache , last , data , 0 ) ;
2007-05-10 04:13:14 +04:00
* cache_ret = cache ;
goto again ;
}
2007-06-12 15:43:08 +04:00
static u64 div_factor ( u64 num , int factor )
{
num * = factor ;
do_div ( num , 10 ) ;
return num ;
}
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
{
2007-10-16 00:15:19 +04:00
struct btrfs_block_group_cache * cache ;
struct extent_map_tree * block_group_cache ;
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 ;
u64 used ;
2007-04-30 23:25:45 +04:00
u64 last = 0 ;
u64 hint_last ;
2007-10-16 00:15:19 +04:00
u64 start ;
u64 end ;
u64 free_check ;
u64 ptr ;
int bit ;
2007-04-27 18:08:34 +04:00
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 ;
2007-05-30 00:52:18 +04:00
int data_swap = 0 ;
2007-05-18 21:28:27 +04:00
2007-10-16 00:15:19 +04:00
block_group_cache = & info - > block_group_cache ;
2007-05-18 21:28:27 +04:00
if ( ! owner )
factor = 5 ;
2007-05-06 18:15:01 +04:00
2007-10-16 00:15:19 +04:00
if ( data )
bit = BLOCK_GROUP_DATA ;
else
bit = BLOCK_GROUP_METADATA ;
2007-05-06 18:15:01 +04:00
if ( search_start ) {
struct btrfs_block_group_cache * shint ;
2007-06-12 05:33:38 +04:00
shint = btrfs_lookup_block_group ( info , search_start ) ;
2007-08-29 17:11:44 +04:00
if ( shint & & shint - > data = = data ) {
2007-05-06 18:15:01 +04:00
used = btrfs_block_group_used ( & shint - > item ) ;
2007-10-16 00:15:26 +04:00
if ( used < div_factor ( shint - > key . offset , factor ) ) {
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-10-16 00:15:26 +04:00
if ( used < div_factor ( hint - > key . offset , factor ) ) {
2007-04-30 23:25:45 +04:00
return hint ;
}
2007-10-16 00:17:44 +04:00
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
}
again :
2007-04-27 18:08:34 +04:00
while ( 1 ) {
2007-10-16 00:15:19 +04:00
ret = find_first_extent_bit ( block_group_cache , last ,
& start , & end , bit ) ;
if ( ret )
2007-04-27 18:08:34 +04:00
break ;
2007-10-16 00:15:19 +04:00
ret = get_state_private ( block_group_cache , start , & ptr ) ;
if ( ret )
break ;
2007-10-19 17:22:59 +04:00
cache = ( struct btrfs_block_group_cache * ) ( unsigned long ) ptr ;
2007-10-16 00:15:19 +04:00
last = cache - > key . objectid + cache - > key . offset ;
used = btrfs_block_group_used ( & cache - > item ) ;
if ( full_search )
free_check = cache - > key . offset ;
else
free_check = div_factor ( cache - > key . offset , factor ) ;
2007-10-16 00:15:26 +04:00
if ( used < free_check ) {
2007-10-16 00:15:19 +04:00
found_group = cache ;
goto found ;
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-06 18:15:01 +04:00
last = search_start ;
2007-04-30 23:25:45 +04:00
full_search = 1 ;
goto again ;
}
2007-05-30 00:52:18 +04:00
if ( ! data_swap ) {
data_swap = 1 ;
2007-10-16 00:15:19 +04:00
bit = BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA ;
2007-05-30 00:52:18 +04:00
last = search_start ;
goto again ;
}
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 ,
2007-10-16 00:15:53 +04:00
u64 bytenr , u64 num_bytes )
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-10-16 00:14:19 +04:00
struct extent_buffer * l ;
2007-03-13 17:46:10 +03:00
struct btrfs_extent_item * item ;
2007-03-13 16:49:06 +03:00
u32 refs ;
2007-03-07 19:50:24 +03:00
2007-10-16 00:15:53 +04:00
WARN_ON ( num_bytes < root - > sectorsize ) ;
2007-04-02 19:20:42 +04:00
path = btrfs_alloc_path ( ) ;
2007-06-22 22:16:25 +04:00
if ( ! path )
return - ENOMEM ;
2007-08-09 04:17:12 +04:00
2007-10-16 00:15:53 +04:00
key . objectid = bytenr ;
2007-03-15 19:56:47 +03:00
btrfs_set_key_type ( & key , BTRFS_EXTENT_ITEM_KEY ) ;
2007-10-16 00:15:53 +04:00
key . offset = num_bytes ;
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-06-22 22:16:25 +04:00
if ( ret < 0 )
return ret ;
2007-04-19 00:15:28 +04:00
if ( ret ! = 0 ) {
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-10-16 00:14:19 +04:00
l = path - > nodes [ 0 ] ;
2007-04-02 19:20:42 +04:00
item = btrfs_item_ptr ( l , path - > slots [ 0 ] , struct btrfs_extent_item ) ;
2007-10-16 00:14:19 +04:00
refs = btrfs_extent_refs ( l , item ) ;
btrfs_set_extent_refs ( l , 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-08-10 22:06:19 +04:00
int btrfs_extent_post_op ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root )
{
finish_current_insert ( trans , root - > fs_info - > extent_root ) ;
del_pending_extents ( trans , root - > fs_info - > extent_root ) ;
return 0 ;
}
2007-04-17 21:26:50 +04:00
static int lookup_extent_ref ( struct btrfs_trans_handle * trans ,
2007-10-16 00:15:53 +04:00
struct btrfs_root * root , u64 bytenr ,
u64 num_bytes , 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-10-16 00:14:19 +04:00
struct extent_buffer * l ;
2007-03-13 17:46:10 +03:00
struct btrfs_extent_item * item ;
2007-04-02 19:20:42 +04:00
2007-10-16 00:15:53 +04:00
WARN_ON ( num_bytes < root - > sectorsize ) ;
2007-04-02 19:20:42 +04:00
path = btrfs_alloc_path ( ) ;
2007-10-16 00:15:53 +04:00
key . objectid = bytenr ;
key . offset = num_bytes ;
2007-03-15 19:56:47 +03:00
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-06-22 22:16:25 +04:00
if ( ret < 0 )
goto out ;
2007-10-16 00:14:19 +04:00
if ( ret ! = 0 ) {
btrfs_print_leaf ( root , path - > nodes [ 0 ] ) ;
2007-10-16 00:15:53 +04:00
printk ( " failed to find block number %Lu \n " , bytenr ) ;
2007-03-07 04:08:01 +03:00
BUG ( ) ;
2007-10-16 00:14:19 +04:00
}
l = path - > nodes [ 0 ] ;
2007-04-02 19:20:42 +04:00
item = btrfs_item_ptr ( l , path - > slots [ 0 ] , struct btrfs_extent_item ) ;
2007-10-16 00:14:19 +04:00
* refs = btrfs_extent_refs ( l , item ) ;
2007-06-22 22:16:25 +04:00
out :
2007-04-02 19:20:42 +04:00
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-10-16 00:15:53 +04:00
return btrfs_inc_extent_ref ( trans , root , root - > node - > start ,
root - > node - > len ) ;
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-10-16 00:14:19 +04:00
struct extent_buffer * buf )
2007-03-03 00:08:05 +03:00
{
2007-10-16 00:15:53 +04:00
u64 bytenr ;
2007-10-16 00:14:19 +04:00
u32 nritems ;
struct btrfs_key key ;
2007-03-27 14:33:00 +04:00
struct btrfs_file_extent_item * fi ;
2007-03-03 00:08:05 +03:00
int i ;
2007-10-16 00:15:53 +04:00
int level ;
2007-03-27 14:33:00 +04:00
int ret ;
2007-06-22 22:16:25 +04:00
int faili ;
int err ;
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-10-16 00:14:19 +04:00
2007-10-16 00:15:53 +04:00
level = btrfs_header_level ( buf ) ;
2007-10-16 00:14:19 +04:00
nritems = btrfs_header_nritems ( buf ) ;
for ( i = 0 ; i < nritems ; i + + ) {
2007-10-16 00:15:53 +04:00
if ( level = = 0 ) {
u64 disk_bytenr ;
2007-10-16 00:14:19 +04:00
btrfs_item_key_to_cpu ( buf , & key , i ) ;
if ( btrfs_key_type ( & key ) ! = BTRFS_EXTENT_DATA_KEY )
2007-03-27 14:33:00 +04:00
continue ;
2007-10-16 00:14:19 +04:00
fi = btrfs_item_ptr ( buf , i ,
2007-03-27 14:33:00 +04:00
struct btrfs_file_extent_item ) ;
2007-10-16 00:14:19 +04:00
if ( btrfs_file_extent_type ( buf , fi ) = =
2007-04-19 21:37:44 +04:00
BTRFS_FILE_EXTENT_INLINE )
continue ;
2007-10-16 00:15:53 +04:00
disk_bytenr = btrfs_file_extent_disk_bytenr ( buf , fi ) ;
if ( disk_bytenr = = 0 )
2007-05-24 21:35:57 +04:00
continue ;
2007-10-16 00:15:53 +04:00
ret = btrfs_inc_extent_ref ( trans , root , disk_bytenr ,
btrfs_file_extent_disk_num_bytes ( buf , fi ) ) ;
2007-06-22 22:16:25 +04:00
if ( ret ) {
faili = i ;
goto fail ;
}
2007-03-27 14:33:00 +04:00
} else {
2007-10-16 00:15:53 +04:00
bytenr = btrfs_node_blockptr ( buf , i ) ;
ret = btrfs_inc_extent_ref ( trans , root , bytenr ,
btrfs_level_size ( root , level - 1 ) ) ;
2007-06-22 22:16:25 +04:00
if ( ret ) {
faili = i ;
goto fail ;
}
2007-03-27 14:33:00 +04:00
}
2007-03-03 00:08:05 +03:00
}
return 0 ;
2007-06-22 22:16:25 +04:00
fail :
2007-06-28 23:57:36 +04:00
WARN_ON ( 1 ) ;
2007-06-22 22:16:25 +04:00
for ( i = 0 ; i < faili ; i + + ) {
2007-10-16 00:15:53 +04:00
if ( level = = 0 ) {
u64 disk_bytenr ;
2007-10-16 00:14:19 +04:00
btrfs_item_key_to_cpu ( buf , & key , i ) ;
if ( btrfs_key_type ( & key ) ! = BTRFS_EXTENT_DATA_KEY )
2007-06-22 22:16:25 +04:00
continue ;
2007-10-16 00:14:19 +04:00
fi = btrfs_item_ptr ( buf , i ,
2007-06-22 22:16:25 +04:00
struct btrfs_file_extent_item ) ;
2007-10-16 00:14:19 +04:00
if ( btrfs_file_extent_type ( buf , fi ) = =
2007-06-22 22:16:25 +04:00
BTRFS_FILE_EXTENT_INLINE )
continue ;
2007-10-16 00:15:53 +04:00
disk_bytenr = btrfs_file_extent_disk_bytenr ( buf , fi ) ;
if ( disk_bytenr = = 0 )
2007-06-22 22:16:25 +04:00
continue ;
2007-10-16 00:15:53 +04:00
err = btrfs_free_extent ( trans , root , disk_bytenr ,
btrfs_file_extent_disk_num_bytes ( buf ,
2007-10-16 00:14:19 +04:00
fi ) , 0 ) ;
2007-06-22 22:16:25 +04:00
BUG_ON ( err ) ;
} else {
2007-10-16 00:15:53 +04:00
bytenr = btrfs_node_blockptr ( buf , i ) ;
err = btrfs_free_extent ( trans , root , bytenr ,
btrfs_level_size ( root , level - 1 ) , 0 ) ;
2007-06-22 22:16:25 +04:00
BUG_ON ( err ) ;
}
}
return ret ;
2007-03-03 00:08:05 +03:00
}
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 ;
2007-10-16 00:14:19 +04:00
unsigned long bi ;
struct extent_buffer * leaf ;
2007-04-27 00:46:15 +04:00
ret = btrfs_search_slot ( trans , extent_root , & cache - > key , path , 0 , 1 ) ;
2007-06-22 22:16:25 +04:00
if ( ret < 0 )
goto fail ;
2007-04-27 00:46:15 +04:00
BUG_ON ( ret ) ;
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
bi = btrfs_item_ptr_offset ( leaf , path - > slots [ 0 ] ) ;
write_extent_buffer ( leaf , & cache - > item , bi , sizeof ( cache - > item ) ) ;
btrfs_mark_buffer_dirty ( leaf ) ;
2007-04-27 00:46:15 +04:00
btrfs_release_path ( extent_root , path ) ;
2007-06-22 22:16:25 +04:00
fail :
2007-04-27 00:46:15 +04:00
finish_current_insert ( trans , extent_root ) ;
pending_ret = del_pending_extents ( trans , extent_root ) ;
if ( ret )
return ret ;
if ( pending_ret )
return pending_ret ;
return 0 ;
}
2007-10-16 00:15:19 +04:00
int btrfs_write_dirty_block_groups ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root )
2007-04-27 00:46:15 +04:00
{
2007-10-16 00:15:19 +04:00
struct extent_map_tree * block_group_cache ;
struct btrfs_block_group_cache * cache ;
2007-04-27 00:46:15 +04:00
int ret ;
int err = 0 ;
int werr = 0 ;
struct btrfs_path * path ;
2007-10-16 00:15:19 +04:00
u64 last = 0 ;
u64 start ;
u64 end ;
u64 ptr ;
2007-04-27 00:46:15 +04:00
2007-10-16 00:15:19 +04:00
block_group_cache = & root - > fs_info - > block_group_cache ;
2007-04-27 00:46:15 +04:00
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
while ( 1 ) {
2007-10-16 00:15:19 +04:00
ret = find_first_extent_bit ( block_group_cache , last ,
& start , & end , BLOCK_GROUP_DIRTY ) ;
if ( ret )
2007-04-27 00:46:15 +04:00
break ;
2007-06-22 22:16:25 +04:00
2007-10-16 00:15:19 +04:00
last = end + 1 ;
ret = get_state_private ( block_group_cache , start , & ptr ) ;
if ( ret )
break ;
2007-10-19 17:22:59 +04:00
cache = ( struct btrfs_block_group_cache * ) ( unsigned long ) ptr ;
2007-10-16 00:15:19 +04:00
err = write_one_cache_group ( trans , root ,
path , cache ) ;
/*
* if we fail to write the cache group , we want
* to keep it marked dirty in hopes that a later
* write will work
*/
if ( err ) {
werr = err ;
continue ;
2007-04-27 00:46:15 +04:00
}
2007-10-16 00:15:19 +04:00
clear_extent_bits ( block_group_cache , start , end ,
BLOCK_GROUP_DIRTY , GFP_NOFS ) ;
2007-04-27 00:46:15 +04:00
}
btrfs_free_path ( path ) ;
return werr ;
}
static int update_block_group ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
2007-10-16 00:15:53 +04:00
u64 bytenr , u64 num_bytes , int alloc ,
int mark_free , int data )
2007-04-27 00:46:15 +04:00
{
struct btrfs_block_group_cache * cache ;
struct btrfs_fs_info * info = root - > fs_info ;
2007-10-16 00:15:53 +04:00
u64 total = num_bytes ;
2007-04-27 00:46:15 +04:00
u64 old_val ;
2007-10-16 00:15:53 +04:00
u64 byte_in_group ;
2007-10-16 00:15:19 +04:00
u64 start ;
u64 end ;
2007-05-08 04:03:49 +04:00
2007-04-27 00:46:15 +04:00
while ( total ) {
2007-10-16 00:15:53 +04:00
cache = btrfs_lookup_block_group ( info , bytenr ) ;
2007-05-08 04:03:49 +04:00
if ( ! cache ) {
2007-04-27 00:46:15 +04:00
return - 1 ;
2007-04-27 18:08:34 +04:00
}
2007-10-16 00:15:53 +04:00
byte_in_group = bytenr - cache - > key . objectid ;
WARN_ON ( byte_in_group > cache - > key . offset ) ;
2007-10-16 00:15:19 +04:00
start = cache - > key . objectid ;
end = start + cache - > key . offset - 1 ;
set_extent_bits ( & info - > block_group_cache , start , end ,
BLOCK_GROUP_DIRTY , GFP_NOFS ) ;
2007-04-27 00:46:15 +04:00
old_val = btrfs_block_group_used ( & cache - > item ) ;
2007-10-16 00:15:53 +04:00
num_bytes = min ( total , cache - > key . offset - byte_in_group ) ;
2007-04-27 18:08:34 +04:00
if ( alloc ) {
2007-05-30 00:52:18 +04:00
if ( cache - > data ! = data & &
2007-06-12 15:43:08 +04:00
old_val < ( cache - > key . offset > > 1 ) ) {
2007-10-16 00:15:19 +04:00
int bit_to_clear ;
int bit_to_set ;
cache - > data = data ;
2007-05-30 00:52:18 +04:00
if ( data ) {
2007-11-01 18:28:41 +03:00
bit_to_clear = BLOCK_GROUP_METADATA ;
bit_to_set = BLOCK_GROUP_DATA ;
2007-05-30 00:52:18 +04:00
cache - > item . flags | =
BTRFS_BLOCK_GROUP_DATA ;
} else {
2007-11-01 18:28:41 +03:00
bit_to_clear = BLOCK_GROUP_DATA ;
bit_to_set = BLOCK_GROUP_METADATA ;
2007-05-30 00:52:18 +04:00
cache - > item . flags & =
~ BTRFS_BLOCK_GROUP_DATA ;
}
2007-10-16 00:15:19 +04:00
clear_extent_bits ( & info - > block_group_cache ,
start , end , bit_to_clear ,
GFP_NOFS ) ;
set_extent_bits ( & info - > block_group_cache ,
start , end , bit_to_set ,
GFP_NOFS ) ;
2007-05-30 00:52:18 +04:00
}
2007-10-16 00:15:53 +04:00
old_val + = num_bytes ;
2007-04-27 18:08:34 +04:00
} else {
2007-10-16 00:15:53 +04:00
old_val - = num_bytes ;
2007-10-16 00:14:48 +04:00
if ( mark_free ) {
set_extent_dirty ( & info - > free_space_cache ,
2007-10-16 00:15:53 +04:00
bytenr , bytenr + num_bytes - 1 ,
2007-10-16 00:14:48 +04:00
GFP_NOFS ) ;
2007-05-10 04:13:14 +04:00
}
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-10-16 00:15:53 +04:00
total - = num_bytes ;
bytenr + = num_bytes ;
2007-04-27 00:46:15 +04:00
}
return 0 ;
}
2007-10-16 00:15:26 +04:00
int btrfs_copy_pinned ( struct btrfs_root * root , struct extent_map_tree * copy )
2007-06-28 23:57:36 +04:00
{
u64 last = 0 ;
2007-10-16 00:15:26 +04:00
u64 start ;
u64 end ;
struct extent_map_tree * pinned_extents = & root - > fs_info - > pinned_extents ;
2007-06-28 23:57:36 +04:00
int ret ;
while ( 1 ) {
2007-10-16 00:15:26 +04:00
ret = find_first_extent_bit ( pinned_extents , last ,
& start , & end , EXTENT_DIRTY ) ;
if ( ret )
2007-06-28 23:57:36 +04:00
break ;
2007-10-16 00:15:26 +04:00
set_extent_dirty ( copy , start , end , GFP_NOFS ) ;
last = end + 1 ;
2007-06-28 23:57:36 +04:00
}
return 0 ;
}
int btrfs_finish_extent_commit ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
2007-10-16 00:15:26 +04:00
struct extent_map_tree * unpin )
2007-03-07 04:08:01 +03:00
{
2007-10-16 00:15:26 +04:00
u64 start ;
u64 end ;
2007-03-07 04:08:01 +03:00
int ret ;
2007-10-16 00:15:26 +04:00
struct extent_map_tree * pinned_extents = & root - > fs_info - > pinned_extents ;
2007-10-16 00:14:48 +04:00
struct extent_map_tree * free_space_cache ;
free_space_cache = & root - > fs_info - > free_space_cache ;
2007-03-07 04:08:01 +03:00
while ( 1 ) {
2007-10-16 00:15:26 +04:00
ret = find_first_extent_bit ( unpin , 0 , & start , & end ,
EXTENT_DIRTY ) ;
if ( ret )
2007-03-07 04:08:01 +03:00
break ;
2007-10-16 00:15:26 +04:00
clear_extent_dirty ( pinned_extents , start , end ,
GFP_NOFS ) ;
clear_extent_dirty ( unpin , start , end , GFP_NOFS ) ;
set_extent_dirty ( free_space_cache , start , end , GFP_NOFS ) ;
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 ret ;
2007-10-16 00:15:26 +04:00
int err = 0 ;
u64 start ;
u64 end ;
2007-03-21 03:35:03 +03:00
struct btrfs_fs_info * info = extent_root - > fs_info ;
2007-03-07 19:50:24 +03:00
2007-10-16 00:14:19 +04:00
btrfs_set_stack_extent_refs ( & extent_item , 1 ) ;
2007-03-15 19:56:47 +03:00
btrfs_set_key_type ( & ins , BTRFS_EXTENT_ITEM_KEY ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_stack_extent_owner ( & extent_item ,
extent_root - > root_key . objectid ) ;
2007-03-07 19:50:24 +03:00
2007-08-09 04:17:12 +04:00
while ( 1 ) {
2007-10-16 00:15:26 +04:00
ret = find_first_extent_bit ( & info - > extent_ins , 0 , & start ,
& end , EXTENT_LOCKED ) ;
if ( ret )
2007-08-09 04:17:12 +04:00
break ;
2007-10-16 00:15:26 +04:00
ins . objectid = start ;
ins . offset = end + 1 - start ;
err = btrfs_insert_item ( trans , extent_root , & ins ,
& extent_item , sizeof ( extent_item ) ) ;
clear_extent_bits ( & info - > extent_ins , start , end , EXTENT_LOCKED ,
GFP_NOFS ) ;
2007-03-07 19:50:24 +03:00
}
return 0 ;
}
2007-10-16 00:15:53 +04:00
static int pin_down_bytes ( struct btrfs_root * root , u64 bytenr , u32 num_bytes ,
int pending )
2007-03-22 19:13:20 +03:00
{
2007-10-16 00:15:26 +04:00
int err = 0 ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * buf ;
2007-03-26 18:15:30 +04:00
2007-03-27 19:05:53 +04:00
if ( ! pending ) {
2007-10-16 00:15:53 +04:00
buf = btrfs_find_tree_block ( root , bytenr , num_bytes ) ;
2007-10-16 00:14:19 +04:00
if ( buf ) {
if ( btrfs_buffer_uptodate ( buf ) ) {
2007-04-02 18:50:19 +04:00
u64 transid =
root - > fs_info - > running_transaction - > transid ;
2007-10-16 00:14:19 +04:00
if ( btrfs_header_generation ( buf ) = = transid ) {
free_extent_buffer ( buf ) ;
2007-04-02 18:50:19 +04:00
return 0 ;
}
2007-03-27 19:05:53 +04:00
}
2007-10-16 00:14:19 +04:00
free_extent_buffer ( buf ) ;
2007-03-26 18:15:30 +04:00
}
2007-10-16 00:15:26 +04:00
set_extent_dirty ( & root - > fs_info - > pinned_extents ,
2007-10-16 00:15:53 +04:00
bytenr , bytenr + num_bytes - 1 , GFP_NOFS ) ;
2007-03-27 19:05:53 +04:00
} else {
2007-10-16 00:15:26 +04:00
set_extent_bits ( & root - > fs_info - > pending_del ,
2007-10-16 00:15:53 +04:00
bytenr , bytenr + num_bytes - 1 ,
EXTENT_LOCKED , GFP_NOFS ) ;
2007-03-27 19:05:53 +04:00
}
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-10-16 00:15:53 +04:00
* root , u64 bytenr , u64 num_bytes , int pin ,
2007-05-10 04:13:14 +04:00
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-10-16 00:14:19 +04:00
struct extent_buffer * leaf ;
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-13 16:49:06 +03:00
u32 refs ;
2007-03-07 19:50:24 +03:00
2007-10-16 00:15:53 +04:00
key . objectid = bytenr ;
2007-03-15 19:56:47 +03:00
btrfs_set_key_type ( & key , BTRFS_EXTENT_ITEM_KEY ) ;
2007-10-16 00:15:53 +04:00
key . offset = num_bytes ;
2007-03-07 04:08:01 +03:00
2007-04-02 19:20:42 +04:00
path = btrfs_alloc_path ( ) ;
2007-06-22 22:16:25 +04:00
if ( ! path )
return - ENOMEM ;
2007-04-05 18:38:44 +04:00
2007-06-22 22:16:25 +04:00
ret = btrfs_search_slot ( trans , extent_root , & key , path , - 1 , 1 ) ;
if ( ret < 0 )
return ret ;
BUG_ON ( ret ) ;
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
ei = btrfs_item_ptr ( leaf , path - > slots [ 0 ] ,
2007-03-14 21:14:43 +03:00
struct btrfs_extent_item ) ;
2007-10-16 00:14:19 +04:00
refs = btrfs_extent_refs ( leaf , ei ) ;
BUG_ON ( refs = = 0 ) ;
refs - = 1 ;
btrfs_set_extent_refs ( leaf , ei , refs ) ;
btrfs_mark_buffer_dirty ( leaf ) ;
2007-03-13 16:49:06 +03:00
if ( refs = = 0 ) {
2007-10-16 00:15:53 +04:00
u64 super_used ;
u64 root_used ;
2007-03-25 19:35:08 +04:00
if ( pin ) {
2007-10-16 00:15:53 +04:00
ret = pin_down_bytes ( root , bytenr , num_bytes , 0 ) ;
2007-03-25 19:35:08 +04:00
BUG_ON ( ret ) ;
}
2007-08-29 23:47:34 +04:00
/* block accounting for super block */
2007-10-16 00:15:53 +04:00
super_used = btrfs_super_bytes_used ( & info - > super_copy ) ;
btrfs_set_super_bytes_used ( & info - > super_copy ,
super_used - num_bytes ) ;
2007-08-29 23:47:34 +04:00
/* block accounting for root item */
2007-10-16 00:15:53 +04:00
root_used = btrfs_root_used ( & root - > root_item ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_root_used ( & root - > root_item ,
2007-10-16 00:15:53 +04:00
root_used - num_bytes ) ;
2007-08-29 23:47:34 +04:00
2007-04-02 19:20:42 +04:00
ret = btrfs_del_item ( trans , extent_root , path ) ;
2007-06-22 22:16:25 +04:00
if ( ret ) {
return ret ;
}
2007-10-16 00:15:53 +04:00
ret = update_block_group ( trans , root , bytenr , num_bytes , 0 ,
2007-05-30 00:52:18 +04:00
mark_free , 0 ) ;
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 err = 0 ;
2007-10-16 00:15:26 +04:00
u64 start ;
u64 end ;
struct extent_map_tree * pending_del ;
struct extent_map_tree * pinned_extents ;
2007-03-26 18:15:30 +04:00
2007-10-16 00:15:26 +04:00
pending_del = & extent_root - > fs_info - > pending_del ;
pinned_extents = & extent_root - > fs_info - > pinned_extents ;
2007-03-07 04:08:01 +03:00
while ( 1 ) {
2007-10-16 00:15:26 +04:00
ret = find_first_extent_bit ( pending_del , 0 , & start , & end ,
EXTENT_LOCKED ) ;
if ( ret )
2007-03-07 04:08:01 +03:00
break ;
2007-10-16 00:15:26 +04:00
set_extent_dirty ( pinned_extents , start , end , GFP_NOFS ) ;
clear_extent_bits ( pending_del , start , end , EXTENT_LOCKED ,
GFP_NOFS ) ;
ret = __free_extent ( trans , extent_root ,
start , end + 1 - start , 0 , 0 ) ;
if ( ret )
err = ret ;
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
2007-10-16 00:15:53 +04:00
* root , u64 bytenr , u64 num_bytes , 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-10-16 00:15:53 +04:00
WARN_ON ( num_bytes < root - > sectorsize ) ;
2007-02-26 18:40:21 +03:00
if ( root = = extent_root ) {
2007-10-16 00:15:53 +04:00
pin_down_bytes ( root , bytenr , num_bytes , 1 ) ;
2007-02-26 18:40:21 +03:00
return 0 ;
}
2007-10-16 00:15:53 +04:00
ret = __free_extent ( trans , root , bytenr , num_bytes , 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
2007-10-16 00:15:53 +04:00
* orig_root , u64 num_bytes , u64 empty_size ,
u64 search_start , u64 search_end , u64 hint_byte ,
2007-06-26 20:20:46 +04:00
struct btrfs_key * ins , u64 exclude_start ,
u64 exclude_nr , 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-10-16 00:15:53 +04:00
u64 last_byte = 0 ;
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-10-16 00:14:19 +04:00
struct extent_buffer * 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-10-16 00:15:53 +04:00
u64 total_needed = num_bytes ;
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-30 18:22:12 +04:00
int wrapped = 0 ;
2007-02-26 18:40:21 +03:00
2007-10-16 00:15:53 +04:00
WARN_ON ( num_bytes < root - > sectorsize ) ;
2007-04-04 23:27:52 +04:00
btrfs_set_key_type ( ins , BTRFS_EXTENT_ITEM_KEY ) ;
2007-10-16 00:14:19 +04:00
level = btrfs_header_level ( root - > node ) ;
2007-05-08 04:03:49 +04:00
if ( search_end = = ( u64 ) - 1 )
2007-10-16 00:15:53 +04:00
search_end = btrfs_super_total_bytes ( & info - > super_copy ) ;
if ( hint_byte ) {
block_group = btrfs_lookup_block_group ( info , hint_byte ) ;
2007-05-06 18:15:01 +04:00
block_group = btrfs_find_block_group ( root , block_group ,
2007-10-16 00:15:53 +04:00
hint_byte , 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
}
2007-08-08 00:15:09 +04:00
total_needed + = empty_size ;
2007-06-20 00:23:05 +04:00
path = btrfs_alloc_path ( ) ;
2007-05-06 18:15:01 +04:00
check_failed :
2007-10-16 00:14:48 +04:00
search_start = find_search_start ( root , & block_group ,
search_start , total_needed , data ) ;
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-08-28 00:49:44 +04:00
path - > reada = 2 ;
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
}
2007-10-16 00:14:19 +04:00
l = path - > nodes [ 0 ] ;
btrfs_item_key_to_cpu ( l , & key , path - > slots [ 0 ] ) ;
2007-05-10 04:13:14 +04:00
/*
* 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-10-16 00:14:19 +04:00
l = path - > nodes [ 0 ] ;
2007-04-02 19:20:42 +04:00
slot = path - > slots [ 0 ] ;
2007-10-16 00:14:19 +04:00
if ( slot > = btrfs_header_nritems ( l ) ) {
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-10-16 00:17:44 +04:00
search_start = max ( search_start ,
block_group - > key . objectid ) ;
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 ;
}
2007-10-16 00:15:53 +04:00
ins - > objectid = last_byte > search_start ?
last_byte : search_start ;
2007-05-08 04:03:49 +04:00
ins - > offset = search_end - ins - > objectid ;
2007-10-16 00:17:44 +04:00
BUG_ON ( ins - > objectid > = search_end ) ;
2007-02-26 18:40:21 +03:00
goto check_pending ;
}
2007-10-16 00:14:19 +04:00
btrfs_item_key_to_cpu ( l , & key , slot ) ;
2007-10-16 00:15:19 +04:00
2007-10-16 00:15:53 +04:00
if ( key . objectid > = search_start & & key . objectid > last_byte & &
2007-05-10 04:13:14 +04:00
start_found ) {
2007-10-16 00:15:53 +04:00
if ( last_byte < search_start )
last_byte = search_start ;
hole_size = key . objectid - last_byte ;
if ( hole_size > = num_bytes ) {
ins - > objectid = last_byte ;
2007-05-10 04:13:14 +04:00
ins - > offset = hole_size ;
goto check_pending ;
2007-03-08 00:15:30 +03:00
}
2007-02-26 18:40:21 +03:00
}
2007-10-16 00:15:19 +04:00
if ( btrfs_key_type ( & key ) ! = BTRFS_EXTENT_ITEM_KEY ) {
if ( ! start_found ) {
2007-10-16 00:15:53 +04:00
last_byte = key . objectid ;
2007-10-16 00:15:19 +04:00
start_found = 1 ;
}
2007-05-10 04:13:14 +04:00
goto next ;
2007-10-16 00:15:19 +04:00
}
2007-05-10 04:13:14 +04:00
2007-03-08 00:15:30 +03:00
start_found = 1 ;
2007-10-16 00:15:53 +04:00
last_byte = key . objectid + key . offset ;
2007-10-16 00:14:48 +04:00
2007-10-16 00:15:53 +04:00
if ( ! full_scan & & last_byte > = block_group - > key . objectid +
2007-05-06 18:15:01 +04:00
block_group - > key . offset ) {
btrfs_release_path ( root , path ) ;
search_start = block_group - > key . objectid +
2007-10-16 00:17:44 +04:00
block_group - > key . offset ;
2007-05-06 18:15:01 +04:00
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
}
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-10-16 00:15:53 +04:00
if ( ins - > objectid + num_bytes > = search_end )
2007-09-17 19:00:51 +04:00
goto enospc ;
2007-10-16 00:17:44 +04:00
if ( ! full_scan & & ins - > objectid + num_bytes > = block_group - >
key . objectid + block_group - > key . offset ) {
search_start = block_group - > key . objectid +
block_group - > key . offset ;
goto new_group ;
}
2007-10-16 00:15:26 +04:00
if ( test_range_bit ( & info - > extent_ins , ins - > objectid ,
2007-10-16 00:15:53 +04:00
ins - > objectid + num_bytes - 1 , EXTENT_LOCKED , 0 ) ) {
search_start = ins - > objectid + num_bytes ;
2007-10-16 00:15:26 +04:00
goto new_group ;
}
if ( test_range_bit ( & info - > pinned_extents , ins - > objectid ,
2007-10-16 00:15:53 +04:00
ins - > objectid + num_bytes - 1 , EXTENT_DIRTY , 0 ) ) {
search_start = ins - > objectid + num_bytes ;
2007-10-16 00:15:26 +04:00
goto new_group ;
2007-02-26 18:40:21 +03:00
}
2007-10-16 00:15:53 +04:00
if ( exclude_nr > 0 & & ( ins - > objectid + num_bytes > exclude_start & &
2007-06-26 20:20:46 +04:00
ins - > objectid < exclude_start + exclude_nr ) ) {
search_start = exclude_start + exclude_nr ;
goto new_group ;
}
2007-05-10 04:13:14 +04:00
if ( ! data ) {
2007-06-12 05:33:38 +04:00
block_group = btrfs_lookup_block_group ( info , ins - > objectid ) ;
2007-08-09 04:17:12 +04:00
if ( block_group )
trans - > block_group = block_group ;
2007-04-25 23:52:25 +04:00
}
2007-10-16 00:15:53 +04:00
ins - > offset = num_bytes ;
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-10-16 00:15:53 +04:00
if ( search_start + num_bytes > = search_end ) {
2007-09-17 19:00:51 +04:00
enospc :
2007-05-06 18:15:01 +04:00
search_start = orig_search_start ;
2007-05-30 18:22:12 +04:00
if ( full_scan ) {
ret = - ENOSPC ;
goto error ;
}
2007-08-08 00:15:09 +04:00
if ( wrapped ) {
if ( ! full_scan )
total_needed - = empty_size ;
2007-05-30 18:22:12 +04:00
full_scan = 1 ;
2007-08-08 00:15:09 +04:00
} else
2007-05-30 18:22:12 +04:00
wrapped = 1 ;
2007-05-06 18:15:01 +04:00
}
2007-06-12 05:33:38 +04:00
block_group = btrfs_lookup_block_group ( info , search_start ) ;
2007-05-30 18:22:12 +04:00
cond_resched ( ) ;
2007-05-06 18:15:01 +04:00
if ( ! full_scan )
block_group = btrfs_find_block_group ( root , block_group ,
2007-05-18 21:28:27 +04:00
search_start , data , 0 ) ;
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-10-16 00:15:53 +04:00
u64 num_bytes , u64 empty_size , u64 hint_byte ,
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-10-16 00:15:53 +04:00
u64 super_used , root_used ;
2007-05-30 18:22:12 +04:00
u64 search_start = 0 ;
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-13 17:46:10 +03:00
struct btrfs_extent_item extent_item ;
2007-03-07 19:50:24 +03:00
2007-10-16 00:14:19 +04:00
btrfs_set_stack_extent_refs ( & extent_item , 1 ) ;
btrfs_set_stack_extent_owner ( & extent_item , owner ) ;
2007-02-26 18:40:21 +03:00
2007-10-16 00:15:53 +04:00
WARN_ON ( num_bytes < root - > sectorsize ) ;
ret = find_free_extent ( trans , root , num_bytes , empty_size ,
search_start , search_end , hint_byte , ins ,
2007-08-09 04:17:12 +04:00
trans - > alloc_exclude_start ,
trans - > alloc_exclude_nr , data ) ;
2007-06-28 23:57:36 +04:00
BUG_ON ( ret ) ;
2007-06-26 20:20:46 +04:00
if ( ret )
return ret ;
2007-02-26 18:40:21 +03:00
2007-08-29 23:47:34 +04:00
/* block accounting for super block */
2007-10-16 00:15:53 +04:00
super_used = btrfs_super_bytes_used ( & info - > super_copy ) ;
btrfs_set_super_bytes_used ( & info - > super_copy , super_used + num_bytes ) ;
2007-08-09 04:17:12 +04:00
2007-08-29 23:47:34 +04:00
/* block accounting for root item */
2007-10-16 00:15:53 +04:00
root_used = btrfs_root_used ( & root - > root_item ) ;
btrfs_set_root_used ( & root - > root_item , root_used + num_bytes ) ;
2007-08-29 23:47:34 +04:00
2007-10-16 00:14:48 +04:00
clear_extent_dirty ( & root - > fs_info - > free_space_cache ,
ins - > objectid , ins - > objectid + ins - > offset - 1 ,
GFP_NOFS ) ;
2007-08-09 04:17:12 +04:00
if ( root = = extent_root ) {
2007-10-16 00:15:26 +04:00
set_extent_bits ( & root - > fs_info - > extent_ins , ins - > objectid ,
ins - > objectid + ins - > offset - 1 ,
EXTENT_LOCKED , GFP_NOFS ) ;
2007-10-16 00:17:44 +04:00
WARN_ON ( data = = 1 ) ;
2007-08-09 04:17:12 +04:00
goto update_block ;
}
WARN_ON ( trans - > alloc_exclude_nr ) ;
trans - > alloc_exclude_start = ins - > objectid ;
trans - > alloc_exclude_nr = ins - > offset ;
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-08-09 04:17:12 +04:00
trans - > alloc_exclude_start = 0 ;
trans - > alloc_exclude_nr = 0 ;
2007-06-28 23:57:36 +04:00
BUG_ON ( ret ) ;
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-10-16 00:14:48 +04:00
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
}
2007-08-09 04:17:12 +04:00
update_block :
2007-05-30 00:52:18 +04:00
ret = update_block_group ( trans , root , ins - > objectid , ins - > offset , 1 , 0 ,
data ) ;
2007-06-08 06:13:21 +04:00
BUG_ON ( ret ) ;
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-10-16 00:14:19 +04:00
struct extent_buffer * btrfs_alloc_free_block ( struct btrfs_trans_handle * trans ,
2007-10-16 00:15:53 +04:00
struct btrfs_root * root ,
u32 blocksize , u64 hint ,
2007-10-16 00:14:19 +04:00
u64 empty_size )
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-10-16 00:14:19 +04:00
struct extent_buffer * 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-10-16 00:15:53 +04:00
blocksize , empty_size , hint ,
( u64 ) - 1 , & ins , 0 ) ;
2007-02-26 18:40:21 +03:00
if ( ret ) {
2007-06-22 22:16:25 +04:00
BUG_ON ( ret > 0 ) ;
return ERR_PTR ( ret ) ;
2007-02-26 18:40:21 +03:00
}
2007-10-16 00:15:53 +04:00
buf = btrfs_find_create_tree_block ( root , ins . objectid , blocksize ) ;
2007-06-22 22:16:25 +04:00
if ( ! buf ) {
2007-10-16 00:15:53 +04:00
btrfs_free_extent ( trans , root , ins . objectid , blocksize , 0 ) ;
2007-06-22 22:16:25 +04:00
return ERR_PTR ( - ENOMEM ) ;
}
2007-10-16 00:14:19 +04:00
btrfs_set_buffer_uptodate ( buf ) ;
set_extent_dirty ( & trans - > transaction - > dirty_pages , buf - > start ,
buf - > start + buf - > len - 1 , GFP_NOFS ) ;
2007-10-16 00:19:22 +04:00
set_extent_bits ( & BTRFS_I ( root - > fs_info - > btree_inode ) - > extent_tree ,
buf - > start , buf - > start + buf - > len - 1 ,
EXTENT_CSUM , GFP_NOFS ) ;
buf - > flags | = EXTENT_CSUM ;
2007-10-16 00:17:34 +04:00
btrfs_set_buffer_defrag ( buf ) ;
2007-09-17 18:58:06 +04:00
trans - > blocks_used + + ;
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 ,
2007-10-16 00:14:19 +04:00
struct btrfs_root * root , struct extent_buffer * leaf )
2007-03-27 14:33:00 +04:00
{
2007-10-16 00:14:19 +04:00
struct btrfs_key key ;
2007-03-27 14:33:00 +04:00
struct btrfs_file_extent_item * fi ;
int i ;
int nritems ;
int ret ;
2007-10-16 00:14:19 +04:00
BUG_ON ( ! btrfs_is_leaf ( leaf ) ) ;
nritems = btrfs_header_nritems ( leaf ) ;
2007-03-27 14:33:00 +04:00
for ( i = 0 ; i < nritems ; i + + ) {
2007-10-16 00:15:53 +04:00
u64 disk_bytenr ;
2007-10-16 00:14:19 +04:00
btrfs_item_key_to_cpu ( leaf , & key , i ) ;
if ( btrfs_key_type ( & key ) ! = BTRFS_EXTENT_DATA_KEY )
2007-03-27 14:33:00 +04:00
continue ;
fi = btrfs_item_ptr ( leaf , i , struct btrfs_file_extent_item ) ;
2007-10-16 00:14:19 +04:00
if ( btrfs_file_extent_type ( leaf , fi ) = =
BTRFS_FILE_EXTENT_INLINE )
2007-04-19 21:37:44 +04:00
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-10-16 00:15:53 +04:00
disk_bytenr = btrfs_file_extent_disk_bytenr ( leaf , fi ) ;
if ( disk_bytenr = = 0 )
2007-05-24 21:35:57 +04:00
continue ;
2007-10-16 00:15:53 +04:00
ret = btrfs_free_extent ( trans , root , disk_bytenr ,
btrfs_file_extent_disk_num_bytes ( leaf , fi ) , 0 ) ;
2007-03-27 14:33:00 +04:00
BUG_ON ( ret ) ;
}
return 0 ;
}
2007-06-20 00:23:05 +04:00
static void reada_walk_down ( struct btrfs_root * root ,
2007-10-16 00:14:19 +04:00
struct extent_buffer * node )
2007-06-20 00:23:05 +04:00
{
int i ;
u32 nritems ;
2007-10-16 00:15:53 +04:00
u64 bytenr ;
2007-06-20 00:23:05 +04:00
int ret ;
u32 refs ;
2007-10-16 00:15:53 +04:00
int level ;
u32 blocksize ;
2007-06-20 00:23:05 +04:00
2007-10-16 00:14:19 +04:00
nritems = btrfs_header_nritems ( node ) ;
2007-10-16 00:15:53 +04:00
level = btrfs_header_level ( node ) ;
2007-06-20 00:23:05 +04:00
for ( i = 0 ; i < nritems ; i + + ) {
2007-10-16 00:15:53 +04:00
bytenr = btrfs_node_blockptr ( node , i ) ;
blocksize = btrfs_level_size ( root , level - 1 ) ;
ret = lookup_extent_ref ( NULL , root , bytenr , blocksize , & refs ) ;
2007-06-20 00:23:05 +04:00
BUG_ON ( ret ) ;
if ( refs ! = 1 )
continue ;
2007-08-09 04:17:12 +04:00
mutex_unlock ( & root - > fs_info - > fs_mutex ) ;
2007-10-16 00:15:53 +04:00
ret = readahead_tree_block ( root , bytenr , blocksize ) ;
2007-08-09 04:17:12 +04:00
cond_resched ( ) ;
mutex_lock ( & root - > fs_info - > fs_mutex ) ;
2007-06-20 00:23:05 +04:00
if ( ret )
break ;
}
}
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-10-16 00:14:19 +04:00
struct extent_buffer * next ;
struct extent_buffer * cur ;
2007-10-16 00:15:53 +04:00
u64 bytenr ;
u32 blocksize ;
2007-03-10 14:35:47 +03:00
int ret ;
u32 refs ;
2007-04-02 19:20:42 +04:00
WARN_ON ( * level < 0 ) ;
WARN_ON ( * level > = BTRFS_MAX_LEVEL ) ;
2007-10-16 00:14:19 +04:00
ret = lookup_extent_ref ( trans , root ,
2007-10-16 00:15:53 +04:00
path - > nodes [ * level ] - > start ,
path - > nodes [ * level ] - > len , & refs ) ;
2007-03-10 14:35:47 +03:00
BUG_ON ( ret ) ;
if ( refs > 1 )
goto out ;
2007-06-20 00:23:05 +04:00
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-06-20 00:23:05 +04:00
if ( * level > 0 & & path - > slots [ * level ] = = 0 )
2007-10-16 00:14:19 +04:00
reada_walk_down ( root , cur ) ;
2007-06-20 00:23:05 +04:00
2007-10-16 00:14:19 +04:00
if ( btrfs_header_level ( cur ) ! = * level )
2007-04-02 18:50:19 +04:00
WARN_ON ( 1 ) ;
2007-06-20 00:23:05 +04:00
2007-03-12 19:01:18 +03:00
if ( path - > slots [ * level ] > =
2007-10-16 00:14:19 +04:00
btrfs_header_nritems ( 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-10-16 00:15:53 +04:00
bytenr = btrfs_node_blockptr ( cur , path - > slots [ * level ] ) ;
blocksize = btrfs_level_size ( root , * level - 1 ) ;
ret = lookup_extent_ref ( trans , root , bytenr , blocksize , & 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-10-16 00:15:53 +04:00
ret = btrfs_free_extent ( trans , root , bytenr ,
blocksize , 1 ) ;
2007-03-10 14:35:47 +03:00
BUG_ON ( ret ) ;
continue ;
}
2007-10-16 00:15:53 +04:00
next = btrfs_find_tree_block ( root , bytenr , blocksize ) ;
2007-10-16 00:14:19 +04:00
if ( ! next | | ! btrfs_buffer_uptodate ( next ) ) {
free_extent_buffer ( next ) ;
2007-08-10 22:06:19 +04:00
mutex_unlock ( & root - > fs_info - > fs_mutex ) ;
2007-10-16 00:15:53 +04:00
next = read_tree_block ( root , bytenr , blocksize ) ;
2007-08-10 22:06:19 +04:00
mutex_lock ( & root - > fs_info - > fs_mutex ) ;
/* we dropped the lock, check one more time */
2007-10-16 00:15:53 +04:00
ret = lookup_extent_ref ( trans , root , bytenr ,
blocksize , & refs ) ;
2007-08-10 22:06:19 +04:00
BUG_ON ( ret ) ;
if ( refs ! = 1 ) {
path - > slots [ * level ] + + ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( next ) ;
2007-08-10 22:06:19 +04:00
ret = btrfs_free_extent ( trans , root ,
2007-10-16 00:15:53 +04:00
bytenr , blocksize , 1 ) ;
2007-08-10 22:06:19 +04:00
BUG_ON ( ret ) ;
continue ;
}
}
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-10-16 00:14:19 +04:00
free_extent_buffer ( path - > nodes [ * level - 1 ] ) ;
2007-03-10 14:35:47 +03:00
path - > nodes [ * level - 1 ] = next ;
2007-10-16 00:14:19 +04:00
* level = btrfs_header_level ( 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-10-16 00:15:53 +04:00
ret = btrfs_free_extent ( trans , root , path - > nodes [ * level ] - > start ,
path - > nodes [ * level ] - > len , 1 ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( 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-08-07 23:52:19 +04:00
struct btrfs_root_item * root_item = & root - > root_item ;
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-10-16 00:14:19 +04:00
if ( slot < btrfs_header_nritems ( path - > nodes [ i ] ) - 1 ) {
struct extent_buffer * node ;
struct btrfs_disk_key disk_key ;
node = path - > nodes [ i ] ;
2007-03-10 14:35:47 +03:00
path - > slots [ i ] + + ;
* level = i ;
2007-08-07 23:52:19 +04:00
WARN_ON ( * level = = 0 ) ;
2007-10-16 00:14:19 +04:00
btrfs_node_key ( node , & disk_key , path - > slots [ i ] ) ;
2007-08-07 23:52:19 +04:00
memcpy ( & root_item - > drop_progress ,
2007-10-16 00:14:19 +04:00
& disk_key , sizeof ( disk_key ) ) ;
2007-08-07 23:52:19 +04:00
root_item - > drop_level = i ;
2007-03-10 14:35:47 +03:00
return 0 ;
} else {
2007-03-16 23:20:31 +03:00
ret = btrfs_free_extent ( trans , root ,
2007-10-16 00:15:53 +04:00
path - > nodes [ * level ] - > start ,
path - > nodes [ * level ] - > len , 1 ) ;
2007-03-27 14:33:00 +04:00
BUG_ON ( ret ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( 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-08-07 23:52:19 +04:00
* root )
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-08-07 23:52:19 +04:00
struct btrfs_root_item * root_item = & root - > root_item ;
2007-03-10 14:35:47 +03:00
2007-04-02 19:20:42 +04:00
path = btrfs_alloc_path ( ) ;
BUG_ON ( ! path ) ;
2007-03-10 14:35:47 +03:00
2007-10-16 00:14:19 +04:00
level = btrfs_header_level ( root - > node ) ;
2007-03-10 14:35:47 +03:00
orig_level = level ;
2007-08-07 23:52:19 +04:00
if ( btrfs_disk_key_objectid ( & root_item - > drop_progress ) = = 0 ) {
path - > nodes [ level ] = root - > node ;
2007-10-16 00:14:48 +04:00
extent_buffer_get ( root - > node ) ;
2007-08-07 23:52:19 +04:00
path - > slots [ level ] = 0 ;
} else {
struct btrfs_key key ;
2007-10-16 00:14:19 +04:00
struct btrfs_disk_key found_key ;
struct extent_buffer * node ;
2007-08-08 00:15:09 +04:00
2007-08-07 23:52:19 +04:00
btrfs_disk_key_to_cpu ( & key , & root_item - > drop_progress ) ;
2007-08-08 00:15:09 +04:00
level = root_item - > drop_level ;
path - > lowest_level = level ;
2007-08-07 23:52:19 +04:00
wret = btrfs_search_slot ( NULL , root , & key , path , 0 , 0 ) ;
2007-08-08 00:15:09 +04:00
if ( wret < 0 ) {
2007-08-07 23:52:19 +04:00
ret = wret ;
goto out ;
}
2007-10-16 00:14:19 +04:00
node = path - > nodes [ level ] ;
btrfs_node_key ( node , & found_key , path - > slots [ level ] ) ;
WARN_ON ( memcmp ( & found_key , & root_item - > drop_progress ,
sizeof ( found_key ) ) ) ;
2007-08-07 23:52:19 +04:00
}
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-08-09 04:17:12 +04:00
ret = - EAGAIN ;
break ;
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 ] ) {
2007-10-16 00:14:19 +04:00
free_extent_buffer ( path - > nodes [ i ] ) ;
2007-10-16 00:18:56 +04:00
path - > nodes [ i ] = NULL ;
2007-03-12 16:03:27 +03:00
}
2007-03-10 14:35:47 +03:00
}
2007-08-07 23:52:19 +04:00
out :
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-10-16 00:15:19 +04:00
int btrfs_free_block_groups ( struct btrfs_fs_info * info )
2007-04-27 00:46:15 +04:00
{
2007-10-16 00:15:19 +04:00
u64 start ;
u64 end ;
2007-11-01 18:28:41 +03:00
u64 ptr ;
2007-04-27 00:46:15 +04:00
int ret ;
while ( 1 ) {
2007-10-16 00:15:19 +04:00
ret = find_first_extent_bit ( & info - > block_group_cache , 0 ,
& start , & end , ( unsigned int ) - 1 ) ;
if ( ret )
2007-04-27 00:46:15 +04:00
break ;
2007-11-01 18:28:41 +03:00
ret = get_state_private ( & info - > block_group_cache , start , & ptr ) ;
if ( ! ret )
kfree ( ( void * ) ( unsigned long ) ptr ) ;
2007-10-16 00:15:19 +04:00
clear_extent_bits ( & info - > block_group_cache , start ,
end , ( unsigned int ) - 1 , GFP_NOFS ) ;
2007-04-27 00:46:15 +04:00
}
2007-05-10 04:13:14 +04:00
while ( 1 ) {
2007-10-16 00:14:48 +04:00
ret = find_first_extent_bit ( & info - > free_space_cache , 0 ,
& start , & end , EXTENT_DIRTY ) ;
if ( ret )
2007-05-10 04:13:14 +04:00
break ;
2007-10-16 00:14:48 +04:00
clear_extent_dirty ( & info - > free_space_cache , start ,
end , GFP_NOFS ) ;
2007-05-10 04:13:14 +04:00
}
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 ;
2007-10-16 00:15:19 +04:00
int bit ;
2007-04-27 00:46:15 +04:00
struct btrfs_block_group_cache * cache ;
2007-05-06 18:15:01 +04:00
struct btrfs_fs_info * info = root - > fs_info ;
2007-10-16 00:15:19 +04:00
struct extent_map_tree * block_group_cache ;
2007-04-27 00:46:15 +04:00
struct btrfs_key key ;
struct btrfs_key found_key ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * leaf ;
2007-10-16 00:15:19 +04:00
block_group_cache = & info - > block_group_cache ;
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 ;
2007-10-16 00:15:53 +04:00
key . offset = BTRFS_BLOCK_GROUP_SIZE ;
2007-04-27 00:46:15 +04:00
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 ;
}
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
btrfs_item_key_to_cpu ( leaf , & found_key , path - > slots [ 0 ] ) ;
2007-04-27 00:46:15 +04:00
cache = kmalloc ( sizeof ( * cache ) , GFP_NOFS ) ;
if ( ! cache ) {
err = - 1 ;
break ;
}
2007-05-08 04:03:49 +04:00
2007-10-16 00:14:19 +04:00
read_extent_buffer ( leaf , & cache - > item ,
btrfs_item_ptr_offset ( leaf , path - > slots [ 0 ] ) ,
sizeof ( cache - > item ) ) ;
2007-04-27 00:46:15 +04:00
memcpy ( & cache - > key , & found_key , sizeof ( found_key ) ) ;
2007-05-10 04:13:14 +04:00
cache - > cached = 0 ;
2007-04-27 00:46:15 +04:00
key . objectid = found_key . objectid + found_key . offset ;
btrfs_release_path ( root , path ) ;
2007-10-16 00:14:19 +04:00
2007-10-16 00:15:19 +04:00
if ( cache - > item . flags & BTRFS_BLOCK_GROUP_DATA ) {
bit = BLOCK_GROUP_DATA ;
cache - > data = 1 ;
} else {
bit = BLOCK_GROUP_METADATA ;
cache - > data = 0 ;
2007-04-30 23:25:45 +04:00
}
2007-10-16 00:15:19 +04:00
/* use EXTENT_LOCKED to prevent merging */
set_extent_bits ( block_group_cache , found_key . objectid ,
found_key . objectid + found_key . offset - 1 ,
bit | EXTENT_LOCKED , GFP_NOFS ) ;
set_state_private ( block_group_cache , found_key . objectid ,
2007-10-19 17:22:59 +04:00
( unsigned long ) cache ) ;
2007-10-16 00:15:19 +04:00
2007-04-27 00:46:15 +04:00
if ( key . objectid > =
2007-10-16 00:15:53 +04:00
btrfs_super_total_bytes ( & info - > super_copy ) )
2007-04-27 00:46:15 +04:00
break ;
}
btrfs_free_path ( path ) ;
return 0 ;
}