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-12-22 00:27:24 +03:00
# include <linux/pagemap.h>
2007-12-11 17:25:06 +03:00
# include "hash.h"
2008-04-11 23:45:51 +04:00
# include "crc32c.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"
2008-03-24 22:01:56 +03:00
# include "volumes.h"
2007-02-26 18:40:21 +03:00
2008-03-24 22:01:56 +03:00
# define BLOCK_GROUP_DATA EXTENT_WRITEBACK
2007-10-16 00:15:19 +04:00
# define BLOCK_GROUP_METADATA EXTENT_UPTODATE
2008-03-24 22:01:56 +03:00
# define BLOCK_GROUP_SYSTEM EXTENT_NEW
2007-10-16 00:15:19 +04:00
# 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 ) ;
2008-01-03 21:56:30 +03:00
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 ;
2008-01-25 00:13:08 +03:00
struct extent_io_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 ;
2007-11-30 18:09:33 +03:00
if ( ! block_group )
return 0 ;
2007-05-10 04:13:14 +04:00
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 ;
btrfs_set_key_type ( & key , BTRFS_EXTENT_ITEM_KEY ) ;
ret = btrfs_search_slot ( NULL , root , & key , path , 0 , 0 ) ;
if ( ret < 0 )
return ret ;
2008-03-24 22:01:56 +03:00
ret = btrfs_previous_item ( root , path , 0 , BTRFS_EXTENT_ITEM_KEY ) ;
2008-01-03 21:56:30 +03:00
if ( ret < 0 )
return ret ;
if ( ret = = 0 ) {
leaf = path - > nodes [ 0 ] ;
btrfs_item_key_to_cpu ( leaf , & key , path - > slots [ 0 ] ) ;
if ( key . objectid + key . offset > first_free )
first_free = key . objectid + key . offset ;
}
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-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 ) {
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
{
2008-01-25 00:13:08 +03:00
struct extent_io_tree * block_group_cache ;
2007-10-16 00:15:19 +04:00
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 ,
2008-03-24 22:01:56 +03:00
BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA |
BLOCK_GROUP_SYSTEM ) ;
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-11-16 22:57:09 +03: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 ;
}
2008-03-24 22:01:56 +03:00
static int block_group_bits ( struct btrfs_block_group_cache * cache , u64 bits )
{
2008-03-25 23:50:33 +03:00
return ( cache - > flags & bits ) = = bits ;
2008-03-24 22:01:56 +03:00
}
static int noinline find_search_start ( struct btrfs_root * root ,
2008-01-03 18:01:48 +03:00
struct btrfs_block_group_cache * * cache_ret ,
2008-03-24 22:01:56 +03:00
u64 * start_ret , int num , int data )
2007-05-10 04:13:14 +04:00
{
int ret ;
struct btrfs_block_group_cache * cache = * cache_ret ;
2008-02-18 20:12:38 +03:00
struct extent_io_tree * free_space_cache ;
2008-03-24 22:02:03 +03:00
struct extent_state * state ;
2007-10-16 00:17:44 +04:00
u64 last ;
2007-10-16 00:14:48 +04:00
u64 start = 0 ;
2007-11-08 05:08:16 +03:00
u64 cache_miss = 0 ;
2008-01-08 23:46:31 +03:00
u64 total_fs_bytes ;
2008-03-24 22:01:56 +03:00
u64 search_start = * start_ret ;
2007-11-06 18:26:29 +03:00
int wrapped = 0 ;
2007-05-10 04:13:14 +04:00
2008-03-24 22:01:56 +03:00
if ( ! cache )
2007-12-04 21:18:24 +03:00
goto out ;
2008-01-08 23:46:31 +03:00
total_fs_bytes = btrfs_super_total_bytes ( & root - > fs_info - > super_copy ) ;
2008-02-18 20:12:38 +03:00
free_space_cache = & root - > fs_info - > free_space_cache ;
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-11-06 18:26:29 +03:00
2007-10-16 00:17:44 +04:00
last = max ( search_start , cache - > key . objectid ) ;
2008-03-24 22:01:56 +03:00
if ( ! block_group_bits ( cache , data ) ) {
goto new_group ;
}
2007-10-16 00:17:44 +04:00
2008-03-24 22:02:03 +03:00
spin_lock_irq ( & free_space_cache - > lock ) ;
state = find_first_extent_bit_state ( free_space_cache , last , EXTENT_DIRTY ) ;
2007-05-10 04:13:14 +04:00
while ( 1 ) {
2008-03-24 22:02:03 +03:00
if ( ! state ) {
2007-11-08 05:08:16 +03:00
if ( ! cache_miss )
cache_miss = last ;
2008-03-24 22:02:03 +03:00
spin_unlock_irq ( & free_space_cache - > lock ) ;
2007-10-16 00:17:44 +04:00
goto new_group ;
}
2007-10-16 00:14:48 +04:00
2008-03-24 22:02:03 +03:00
start = max ( last , state - > start ) ;
last = state - > end + 1 ;
2007-11-08 05:08:16 +03:00
if ( last - start < num ) {
if ( last = = cache - > key . objectid + cache - > key . offset )
cache_miss = start ;
2008-03-24 22:02:03 +03:00
do {
state = extent_state_next ( state ) ;
} while ( state & & ! ( state - > state & EXTENT_DIRTY ) ) ;
2007-10-16 00:14:48 +04:00
continue ;
2007-11-08 05:08:16 +03:00
}
2008-03-24 22:02:03 +03:00
spin_unlock_irq ( & free_space_cache - > lock ) ;
2008-03-24 22:01:56 +03:00
if ( start + num > cache - > key . objectid + cache - > key . offset )
2007-05-10 04:13:14 +04:00
goto new_group ;
2008-01-08 23:46:31 +03:00
if ( start + num > total_fs_bytes )
goto new_group ;
2008-04-04 00:29:03 +04:00
if ( ! block_group_bits ( cache , data ) ) {
2008-04-04 00:29:03 +04:00
printk ( " block group bits don't match %Lu %d \n " , cache - > flags , data ) ;
2008-04-04 00:29:03 +04:00
}
2008-03-24 22:01:56 +03:00
* start_ret = start ;
return 0 ;
2008-04-04 00:29:03 +04:00
}
out :
2007-12-04 21:18:24 +03:00
cache = btrfs_lookup_block_group ( root - > fs_info , search_start ) ;
if ( ! cache ) {
2008-03-24 22:01:56 +03:00
printk ( " Unable to find block group for %Lu \n " , search_start ) ;
2007-12-04 21:18:24 +03:00
WARN_ON ( 1 ) ;
}
2008-03-24 22:01:56 +03:00
return - ENOSPC ;
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 ;
2007-11-06 18:26:29 +03:00
wrapped :
2007-10-16 00:17:44 +04:00
cache = btrfs_lookup_block_group ( root - > fs_info , last ) ;
2008-01-08 23:46:31 +03:00
if ( ! cache | | cache - > key . objectid > = total_fs_bytes ) {
2007-11-26 18:55:49 +03:00
no_cache :
2007-11-06 18:26:29 +03:00
if ( ! wrapped ) {
wrapped = 1 ;
last = search_start ;
goto wrapped ;
}
2007-12-04 21:18:24 +03:00
goto out ;
2007-05-10 04:13:14 +04:00
}
2007-11-08 05:08:16 +03:00
if ( cache_miss & & ! cache - > cached ) {
cache_block_group ( root , cache ) ;
last = cache_miss ;
cache = btrfs_lookup_block_group ( root - > fs_info , last ) ;
}
2007-12-04 21:18:24 +03:00
cache = btrfs_find_block_group ( root , cache , last , data , 0 ) ;
2007-11-26 18:55:49 +03:00
if ( ! cache )
goto no_cache ;
2007-05-10 04:13:14 +04:00
* cache_ret = cache ;
2007-11-08 05:08:16 +03:00
cache_miss = 0 ;
2007-05-10 04:13:14 +04:00
goto again ;
}
2007-06-12 15:43:08 +04:00
static u64 div_factor ( u64 num , int factor )
{
2007-11-08 05:08:16 +03:00
if ( factor = = 10 )
return num ;
2007-06-12 15:43:08 +04:00
num * = factor ;
do_div ( num , 10 ) ;
return num ;
}
2008-03-24 22:01:59 +03:00
static int block_group_state_bits ( u64 flags )
{
int bits = 0 ;
if ( flags & BTRFS_BLOCK_GROUP_DATA )
bits | = BLOCK_GROUP_DATA ;
if ( flags & BTRFS_BLOCK_GROUP_METADATA )
bits | = BLOCK_GROUP_METADATA ;
if ( flags & BTRFS_BLOCK_GROUP_SYSTEM )
bits | = BLOCK_GROUP_SYSTEM ;
return bits ;
}
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 ;
2008-01-25 00:13:08 +03:00
struct extent_io_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 ;
2008-01-08 23:46:31 +03:00
u64 total_fs_bytes ;
2007-10-16 00:15:19 +04:00
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-10-16 00:15:19 +04:00
block_group_cache = & info - > block_group_cache ;
2008-01-08 23:46:31 +03:00
total_fs_bytes = btrfs_super_total_bytes ( & root - > fs_info - > super_copy ) ;
2007-10-16 00:15:19 +04:00
2007-05-18 21:28:27 +04:00
if ( ! owner )
2007-11-06 18:26:29 +03:00
factor = 8 ;
2007-05-06 18:15:01 +04:00
2008-03-24 22:01:59 +03:00
bit = block_group_state_bits ( data ) ;
2007-05-06 18:15:01 +04:00
2008-01-08 23:46:31 +03:00
if ( search_start & & search_start < total_fs_bytes ) {
2007-05-06 18:15:01 +04:00
struct btrfs_block_group_cache * shint ;
2007-06-12 05:33:38 +04:00
shint = btrfs_lookup_block_group ( info , search_start ) ;
2008-03-24 22:01:56 +03:00
if ( shint & & block_group_bits ( shint , data ) ) {
2007-05-06 18:15:01 +04:00
used = btrfs_block_group_used ( & shint - > item ) ;
2007-11-16 22:57:08 +03:00
if ( used + shint - > pinned <
div_factor ( shint - > key . offset , factor ) ) {
2007-05-06 18:15:01 +04:00
return shint ;
}
}
}
2008-03-24 22:01:56 +03:00
if ( hint & & block_group_bits ( hint , data ) & &
hint - > key . objectid < total_fs_bytes ) {
2007-04-30 23:25:45 +04:00
used = btrfs_block_group_used ( & hint - > item ) ;
2007-11-16 22:57:08 +03:00
if ( used + hint - > pinned <
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 ;
2008-01-08 23:46:31 +03:00
if ( hint_last > = total_fs_bytes )
hint_last = search_start ;
2007-05-10 04:13:14 +04:00
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 ) ;
2008-01-08 23:46:31 +03:00
if ( cache - > key . objectid > total_fs_bytes )
break ;
2008-04-04 00:29:03 +04:00
if ( block_group_bits ( cache , data ) ) {
if ( full_search )
free_check = cache - > key . offset ;
else
free_check = div_factor ( cache - > key . offset ,
factor ) ;
2008-03-24 22:01:59 +03:00
2008-04-04 00:29:03 +04:00
if ( used + cache - > pinned < free_check ) {
found_group = cache ;
goto found ;
}
2008-03-24 22:01:59 +03: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-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-12-11 17:25:06 +03:00
static u64 hash_extent_ref ( u64 root_objectid , u64 ref_generation ,
2007-12-11 17:25:06 +03:00
u64 owner , u64 owner_offset )
{
u32 high_crc = ~ ( u32 ) 0 ;
u32 low_crc = ~ ( u32 ) 0 ;
__le64 lenum ;
lenum = cpu_to_le64 ( root_objectid ) ;
2008-04-11 23:45:51 +04:00
high_crc = btrfs_crc32c ( high_crc , & lenum , sizeof ( lenum ) ) ;
2007-12-11 17:25:06 +03:00
lenum = cpu_to_le64 ( ref_generation ) ;
2008-04-11 23:45:51 +04:00
low_crc = btrfs_crc32c ( low_crc , & lenum , sizeof ( lenum ) ) ;
2008-02-01 22:51:59 +03:00
if ( owner > = BTRFS_FIRST_FREE_OBJECTID ) {
lenum = cpu_to_le64 ( owner ) ;
2008-04-11 23:45:51 +04:00
low_crc = btrfs_crc32c ( low_crc , & lenum , sizeof ( lenum ) ) ;
2008-02-01 22:51:59 +03:00
lenum = cpu_to_le64 ( owner_offset ) ;
2008-04-11 23:45:51 +04:00
low_crc = btrfs_crc32c ( low_crc , & lenum , sizeof ( lenum ) ) ;
2008-02-01 22:51:59 +03:00
}
2007-12-11 17:25:06 +03:00
return ( ( u64 ) high_crc < < 32 ) | ( u64 ) low_crc ;
}
2007-12-11 17:25:06 +03:00
static int match_extent_ref ( struct extent_buffer * leaf ,
struct btrfs_extent_ref * disk_ref ,
struct btrfs_extent_ref * cpu_ref )
{
int ret ;
int len ;
if ( cpu_ref - > objectid )
len = sizeof ( * cpu_ref ) ;
else
len = 2 * sizeof ( u64 ) ;
ret = memcmp_extent_buffer ( leaf , cpu_ref , ( unsigned long ) disk_ref ,
len ) ;
return ret = = 0 ;
}
2008-01-03 18:01:48 +03:00
static int noinline lookup_extent_backref ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct btrfs_path * path , u64 bytenr ,
u64 root_objectid ,
u64 ref_generation , u64 owner ,
u64 owner_offset , int del )
2007-12-11 17:25:06 +03:00
{
u64 hash ;
struct btrfs_key key ;
2007-12-11 17:25:06 +03:00
struct btrfs_key found_key ;
2007-12-11 17:25:06 +03:00
struct btrfs_extent_ref ref ;
2007-12-11 17:25:06 +03:00
struct extent_buffer * leaf ;
struct btrfs_extent_ref * disk_ref ;
int ret ;
int ret2 ;
btrfs_set_stack_ref_root ( & ref , root_objectid ) ;
btrfs_set_stack_ref_generation ( & ref , ref_generation ) ;
btrfs_set_stack_ref_objectid ( & ref , owner ) ;
btrfs_set_stack_ref_offset ( & ref , owner_offset ) ;
hash = hash_extent_ref ( root_objectid , ref_generation , owner ,
owner_offset ) ;
key . offset = hash ;
key . objectid = bytenr ;
key . type = BTRFS_EXTENT_REF_KEY ;
while ( 1 ) {
ret = btrfs_search_slot ( trans , root , & key , path ,
del ? - 1 : 0 , del ) ;
if ( ret < 0 )
goto out ;
leaf = path - > nodes [ 0 ] ;
if ( ret ! = 0 ) {
u32 nritems = btrfs_header_nritems ( leaf ) ;
if ( path - > slots [ 0 ] > = nritems ) {
ret2 = btrfs_next_leaf ( root , path ) ;
if ( ret2 )
goto out ;
leaf = path - > nodes [ 0 ] ;
}
btrfs_item_key_to_cpu ( leaf , & found_key , path - > slots [ 0 ] ) ;
if ( found_key . objectid ! = bytenr | |
found_key . type ! = BTRFS_EXTENT_REF_KEY )
goto out ;
key . offset = found_key . offset ;
if ( del ) {
btrfs_release_path ( root , path ) ;
continue ;
}
}
disk_ref = btrfs_item_ptr ( path - > nodes [ 0 ] ,
path - > slots [ 0 ] ,
struct btrfs_extent_ref ) ;
if ( match_extent_ref ( path - > nodes [ 0 ] , disk_ref , & ref ) ) {
ret = 0 ;
goto out ;
}
btrfs_item_key_to_cpu ( leaf , & found_key , path - > slots [ 0 ] ) ;
key . offset = found_key . offset + 1 ;
btrfs_release_path ( root , path ) ;
}
out :
return ret ;
}
2007-12-11 20:42:00 +03:00
/*
* Back reference rules . Back refs have three main goals :
*
* 1 ) differentiate between all holders of references to an extent so that
* when a reference is dropped we can make sure it was a valid reference
* before freeing the extent .
*
* 2 ) Provide enough information to quickly find the holders of an extent
* if we notice a given block is corrupted or bad .
*
* 3 ) Make it easy to migrate blocks for FS shrinking or storage pool
* maintenance . This is actually the same as # 2 , but with a slightly
* different use case .
*
* File extents can be referenced by :
*
* - multiple snapshots , subvolumes , or different generations in one subvol
* - different files inside a single subvolume ( in theory , not implemented yet )
* - different offsets inside a file ( bookend extents in file . c )
*
* The extent ref structure has fields for :
*
* - Objectid of the subvolume root
* - Generation number of the tree holding the reference
* - objectid of the file holding the reference
* - offset in the file corresponding to the key holding the reference
*
* When a file extent is allocated the fields are filled in :
* ( root_key . objectid , trans - > transid , inode objectid , offset in file )
*
* When a leaf is cow ' d new references are added for every file extent found
* in the leaf . It looks the same as the create case , but trans - > transid
* will be different when the block is cow ' d .
*
* ( root_key . objectid , trans - > transid , inode objectid , offset in file )
*
* When a file extent is removed either during snapshot deletion or file
* truncation , the corresponding back reference is found
* by searching for :
*
* ( btrfs_header_owner ( leaf ) , btrfs_header_generation ( leaf ) ,
* inode objectid , offset in file )
*
* Btree extents can be referenced by :
*
* - Different subvolumes
* - Different generations of the same subvolume
*
* Storing sufficient information for a full reverse mapping of a btree
* block would require storing the lowest key of the block in the backref ,
* and it would require updating that lowest key either before write out or
* every time it changed . Instead , the objectid of the lowest key is stored
* along with the level of the tree block . This provides a hint
* about where in the btree the block can be found . Searches through the
* btree only need to look for a pointer to that block , so they stop one
* level higher than the level recorded in the backref .
*
* Some btrees do not do reference counting on their extents . These
* include the extent tree and the tree of tree roots . Backrefs for these
* trees always have a generation of zero .
*
* When a tree block is created , back references are inserted :
*
2007-12-13 19:13:32 +03:00
* ( root - > root_key . objectid , trans - > transid or zero , level , lowest_key_objectid )
2007-12-11 20:42:00 +03:00
*
* When a tree block is cow ' d in a reference counted root ,
* new back references are added for all the blocks it points to .
* These are of the form ( trans - > transid will have increased since creation ) :
*
2007-12-13 19:13:32 +03:00
* ( root - > root_key . objectid , trans - > transid , level , lowest_key_objectid )
2007-12-11 20:42:00 +03:00
*
* Because the lowest_key_objectid and the level are just hints
* they are not used when backrefs are deleted . When a backref is deleted :
*
* if backref was for a tree root :
* root_objectid = root - > root_key . objectid
* else
* root_objectid = btrfs_header_owner ( parent )
*
* ( root_objectid , btrfs_header_generation ( parent ) or zero , 0 , 0 )
*
* Back Reference Key hashing :
*
* Back references have four fields , each 64 bits long . Unfortunately ,
* This is hashed into a single 64 bit number and placed into the key offset .
* The key objectid corresponds to the first byte in the extent , and the
* key type is set to BTRFS_EXTENT_REF_KEY
*/
2007-12-11 17:25:06 +03:00
int btrfs_insert_extent_backref ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct btrfs_path * path , u64 bytenr ,
u64 root_objectid , u64 ref_generation ,
u64 owner , u64 owner_offset )
{
u64 hash ;
struct btrfs_key key ;
struct btrfs_extent_ref ref ;
struct btrfs_extent_ref * disk_ref ;
2007-12-11 17:25:06 +03:00
int ret ;
btrfs_set_stack_ref_root ( & ref , root_objectid ) ;
2007-12-11 17:25:06 +03:00
btrfs_set_stack_ref_generation ( & ref , ref_generation ) ;
2007-12-11 17:25:06 +03:00
btrfs_set_stack_ref_objectid ( & ref , owner ) ;
btrfs_set_stack_ref_offset ( & ref , owner_offset ) ;
2007-12-11 17:25:06 +03:00
hash = hash_extent_ref ( root_objectid , ref_generation , owner ,
owner_offset ) ;
2007-12-11 17:25:06 +03:00
key . offset = hash ;
key . objectid = bytenr ;
key . type = BTRFS_EXTENT_REF_KEY ;
ret = btrfs_insert_empty_item ( trans , root , path , & key , sizeof ( ref ) ) ;
while ( ret = = - EEXIST ) {
2007-12-11 17:25:06 +03:00
disk_ref = btrfs_item_ptr ( path - > nodes [ 0 ] , path - > slots [ 0 ] ,
struct btrfs_extent_ref ) ;
if ( match_extent_ref ( path - > nodes [ 0 ] , disk_ref , & ref ) )
goto out ;
key . offset + + ;
btrfs_release_path ( root , path ) ;
ret = btrfs_insert_empty_item ( trans , root , path , & key ,
sizeof ( ref ) ) ;
2007-12-11 17:25:06 +03:00
}
2007-12-11 17:25:06 +03:00
if ( ret )
goto out ;
disk_ref = btrfs_item_ptr ( path - > nodes [ 0 ] , path - > slots [ 0 ] ,
struct btrfs_extent_ref ) ;
write_extent_buffer ( path - > nodes [ 0 ] , & ref , ( unsigned long ) disk_ref ,
sizeof ( ref ) ) ;
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
out :
btrfs_release_path ( root , path ) ;
return ret ;
2007-12-11 17:25:06 +03:00
}
2007-04-17 21:26:50 +04:00
int btrfs_inc_extent_ref ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
2007-12-11 17:25:06 +03:00
u64 bytenr , u64 num_bytes ,
2007-12-11 17:25:06 +03:00
u64 root_objectid , u64 ref_generation ,
2007-12-11 17:25:06 +03:00
u64 owner , u64 owner_offset )
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
2008-01-08 23:46:31 +03:00
path - > reada = 0 ;
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 ) ;
2007-12-11 17:25:06 +03:00
2008-01-08 23:46:31 +03:00
path - > reada = 0 ;
2007-12-11 17:25:06 +03:00
ret = btrfs_insert_extent_backref ( trans , root - > fs_info - > extent_root ,
path , bytenr , root_objectid ,
ref_generation , owner , owner_offset ) ;
BUG_ON ( ret ) ;
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-12-11 17:25:06 +03:00
btrfs_free_path ( path ) ;
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 ( ) ;
2008-01-08 23:46:31 +03:00
path - > reada = 0 ;
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-12-18 04:14:01 +03:00
u32 btrfs_count_snapshots_in_path ( struct btrfs_root * root ,
struct btrfs_path * count_path ,
u64 first_extent )
{
struct btrfs_root * extent_root = root - > fs_info - > extent_root ;
struct btrfs_path * path ;
u64 bytenr ;
u64 found_objectid ;
2008-01-03 17:08:27 +03:00
u64 root_objectid = root - > root_key . objectid ;
2007-12-18 04:14:01 +03:00
u32 total_count = 0 ;
u32 cur_count ;
u32 nritems ;
int ret ;
struct btrfs_key key ;
struct btrfs_key found_key ;
struct extent_buffer * l ;
struct btrfs_extent_item * item ;
struct btrfs_extent_ref * ref_item ;
int level = - 1 ;
path = btrfs_alloc_path ( ) ;
again :
if ( level = = - 1 )
bytenr = first_extent ;
else
bytenr = count_path - > nodes [ level ] - > start ;
cur_count = 0 ;
key . objectid = bytenr ;
key . offset = 0 ;
btrfs_set_key_type ( & key , BTRFS_EXTENT_ITEM_KEY ) ;
ret = btrfs_search_slot ( NULL , extent_root , & key , path , 0 , 0 ) ;
if ( ret < 0 )
goto out ;
BUG_ON ( ret = = 0 ) ;
l = path - > nodes [ 0 ] ;
btrfs_item_key_to_cpu ( l , & found_key , path - > slots [ 0 ] ) ;
if ( found_key . objectid ! = bytenr | |
found_key . type ! = BTRFS_EXTENT_ITEM_KEY ) {
goto out ;
}
item = btrfs_item_ptr ( l , path - > slots [ 0 ] , struct btrfs_extent_item ) ;
while ( 1 ) {
2008-01-03 21:23:19 +03:00
l = path - > nodes [ 0 ] ;
2007-12-18 04:14:01 +03:00
nritems = btrfs_header_nritems ( l ) ;
if ( path - > slots [ 0 ] > = nritems ) {
ret = btrfs_next_leaf ( extent_root , path ) ;
if ( ret = = 0 )
continue ;
break ;
}
btrfs_item_key_to_cpu ( l , & found_key , path - > slots [ 0 ] ) ;
if ( found_key . objectid ! = bytenr )
break ;
2008-01-03 21:23:19 +03:00
2007-12-18 04:14:01 +03:00
if ( found_key . type ! = BTRFS_EXTENT_REF_KEY ) {
path - > slots [ 0 ] + + ;
continue ;
}
cur_count + + ;
ref_item = btrfs_item_ptr ( l , path - > slots [ 0 ] ,
struct btrfs_extent_ref ) ;
found_objectid = btrfs_ref_root ( l , ref_item ) ;
2008-01-03 17:08:27 +03:00
if ( found_objectid ! = root_objectid ) {
total_count = 2 ;
2008-01-03 17:08:48 +03:00
goto out ;
2008-01-03 17:08:27 +03:00
}
total_count = 1 ;
2007-12-18 04:14:01 +03:00
path - > slots [ 0 ] + + ;
}
if ( cur_count = = 0 ) {
total_count = 0 ;
goto out ;
}
if ( level > = 0 & & root - > node = = count_path - > nodes [ level ] )
goto out ;
level + + ;
btrfs_release_path ( root , path ) ;
goto again ;
out :
btrfs_free_path ( path ) ;
return total_count ;
}
2007-04-10 17:27:04 +04:00
int btrfs_inc_root_ref ( struct btrfs_trans_handle * trans ,
2007-12-11 17:25:06 +03:00
struct btrfs_root * root , u64 owner_objectid )
2007-04-10 17:27:04 +04:00
{
2007-12-11 17:25:06 +03:00
u64 generation ;
u64 key_objectid ;
u64 level ;
u32 nritems ;
struct btrfs_disk_key disk_key ;
level = btrfs_header_level ( root - > node ) ;
generation = trans - > transid ;
nritems = btrfs_header_nritems ( root - > node ) ;
if ( nritems > 0 ) {
if ( level = = 0 )
btrfs_item_key ( root - > node , & disk_key , 0 ) ;
else
btrfs_node_key ( root - > node , & disk_key , 0 ) ;
key_objectid = btrfs_disk_key_objectid ( & disk_key ) ;
} else {
key_objectid = 0 ;
}
2007-10-16 00:15:53 +04:00
return btrfs_inc_extent_ref ( trans , root , root - > node - > start ,
2007-12-11 17:25:06 +03:00
root - > node - > len , owner_objectid ,
2007-12-13 19:13:32 +03:00
generation , level , key_objectid ) ;
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 ;
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 ,
2007-12-11 17:25:06 +03:00
btrfs_file_extent_disk_num_bytes ( buf , fi ) ,
root - > root_key . objectid , trans - > transid ,
key . objectid , key . offset ) ;
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 ) ;
2007-12-13 17:48:07 +03:00
btrfs_node_key_to_cpu ( buf , & key , i ) ;
2007-10-16 00:15:53 +04:00
ret = btrfs_inc_extent_ref ( trans , root , bytenr ,
2007-12-11 17:25:06 +03:00
btrfs_level_size ( root , level - 1 ) ,
root - > root_key . objectid ,
2007-12-13 19:13:32 +03:00
trans - > transid ,
level - 1 , key . objectid ) ;
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-12-11 17:25:06 +03:00
#if 0
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 ) ;
}
}
2007-12-11 17:25:06 +03:00
# endif
2007-06-22 22:16:25 +04:00
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
{
2008-01-25 00:13:08 +03:00
struct extent_io_tree * block_group_cache ;
2007-10-16 00:15:19 +04:00
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 ;
}
2008-03-24 22:01:59 +03:00
static struct btrfs_space_info * __find_space_info ( struct btrfs_fs_info * info ,
u64 flags )
{
struct list_head * head = & info - > space_info ;
struct list_head * cur ;
struct btrfs_space_info * found ;
list_for_each ( cur , head ) {
found = list_entry ( cur , struct btrfs_space_info , list ) ;
if ( found - > flags = = flags )
return found ;
}
return NULL ;
}
2008-03-25 23:50:33 +03:00
static int update_space_info ( struct btrfs_fs_info * info , u64 flags ,
u64 total_bytes , u64 bytes_used ,
struct btrfs_space_info * * space_info )
{
struct btrfs_space_info * found ;
found = __find_space_info ( info , flags ) ;
if ( found ) {
found - > total_bytes + = total_bytes ;
found - > bytes_used + = bytes_used ;
WARN_ON ( found - > total_bytes < found - > bytes_used ) ;
* space_info = found ;
return 0 ;
}
found = kmalloc ( sizeof ( * found ) , GFP_NOFS ) ;
if ( ! found )
return - ENOMEM ;
list_add ( & found - > list , & info - > space_info ) ;
found - > flags = flags ;
found - > total_bytes = total_bytes ;
found - > bytes_used = bytes_used ;
found - > bytes_pinned = 0 ;
found - > full = 0 ;
* space_info = found ;
return 0 ;
}
2008-04-04 00:29:03 +04:00
static void set_avail_alloc_bits ( struct btrfs_fs_info * fs_info , u64 flags )
{
u64 extra_flags = flags & ( BTRFS_BLOCK_GROUP_RAID0 |
2008-04-04 00:29:03 +04:00
BTRFS_BLOCK_GROUP_RAID1 |
2008-04-16 18:49:51 +04:00
BTRFS_BLOCK_GROUP_RAID10 |
2008-04-04 00:29:03 +04:00
BTRFS_BLOCK_GROUP_DUP ) ;
2008-04-04 00:29:03 +04:00
if ( extra_flags ) {
if ( flags & BTRFS_BLOCK_GROUP_DATA )
fs_info - > avail_data_alloc_bits | = extra_flags ;
if ( flags & BTRFS_BLOCK_GROUP_METADATA )
fs_info - > avail_metadata_alloc_bits | = extra_flags ;
if ( flags & BTRFS_BLOCK_GROUP_SYSTEM )
fs_info - > avail_system_alloc_bits | = extra_flags ;
}
}
2008-03-25 23:50:33 +03:00
2008-03-24 22:01:59 +03:00
static int do_chunk_alloc ( struct btrfs_trans_handle * trans ,
struct btrfs_root * extent_root , u64 alloc_bytes ,
u64 flags )
{
struct btrfs_space_info * space_info ;
u64 thresh ;
u64 start ;
u64 num_bytes ;
int ret ;
space_info = __find_space_info ( extent_root - > fs_info , flags ) ;
2008-03-25 23:50:33 +03:00
if ( ! space_info ) {
ret = update_space_info ( extent_root - > fs_info , flags ,
0 , 0 , & space_info ) ;
BUG_ON ( ret ) ;
}
2008-03-24 22:01:59 +03:00
BUG_ON ( ! space_info ) ;
if ( space_info - > full )
return 0 ;
2008-04-04 00:29:03 +04:00
thresh = div_factor ( space_info - > total_bytes , 6 ) ;
2008-03-24 22:01:59 +03:00
if ( ( space_info - > bytes_used + space_info - > bytes_pinned + alloc_bytes ) <
thresh )
return 0 ;
ret = btrfs_alloc_chunk ( trans , extent_root , & start , & num_bytes , flags ) ;
if ( ret = = - ENOSPC ) {
printk ( " space info full %Lu \n " , flags ) ;
space_info - > full = 1 ;
return 0 ;
}
BUG_ON ( ret ) ;
ret = btrfs_make_block_group ( trans , extent_root , 0 , flags ,
2008-04-15 23:41:47 +04:00
BTRFS_FIRST_CHUNK_TREE_OBJECTID , start , num_bytes ) ;
2008-03-24 22:01:59 +03:00
BUG_ON ( ret ) ;
2008-03-25 23:50:33 +03:00
2008-03-24 22:01:59 +03:00
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-10-16 00:15:53 +04:00
u64 bytenr , u64 num_bytes , int alloc ,
2008-03-24 22:01:56 +03:00
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 ;
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-10-16 00:15:53 +04:00
old_val + = num_bytes ;
2008-03-24 22:01:59 +03:00
cache - > space_info - > bytes_used + = num_bytes ;
2007-04-27 18:08:34 +04:00
} else {
2007-10-16 00:15:53 +04:00
old_val - = num_bytes ;
2008-03-24 22:01:59 +03:00
cache - > space_info - > bytes_used - = 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 ;
}
2008-03-24 22:01:59 +03:00
2007-11-16 22:57:08 +03:00
static int update_pinned_extents ( struct btrfs_root * root ,
u64 bytenr , u64 num , int pin )
{
u64 len ;
struct btrfs_block_group_cache * cache ;
struct btrfs_fs_info * fs_info = root - > fs_info ;
if ( pin ) {
set_extent_dirty ( & fs_info - > pinned_extents ,
bytenr , bytenr + num - 1 , GFP_NOFS ) ;
} else {
clear_extent_dirty ( & fs_info - > pinned_extents ,
bytenr , bytenr + num - 1 , GFP_NOFS ) ;
}
while ( num > 0 ) {
cache = btrfs_lookup_block_group ( fs_info , bytenr ) ;
WARN_ON ( ! cache ) ;
len = min ( num , cache - > key . offset -
( bytenr - cache - > key . objectid ) ) ;
if ( pin ) {
cache - > pinned + = len ;
2008-03-24 22:01:59 +03:00
cache - > space_info - > bytes_pinned + = len ;
2007-11-16 22:57:08 +03:00
fs_info - > total_pinned + = len ;
} else {
cache - > pinned - = len ;
2008-03-24 22:01:59 +03:00
cache - > space_info - > bytes_pinned - = len ;
2007-11-16 22:57:08 +03:00
fs_info - > total_pinned - = len ;
}
bytenr + = len ;
num - = len ;
}
return 0 ;
}
2007-04-27 00:46:15 +04:00
2008-01-25 00:13:08 +03:00
int btrfs_copy_pinned ( struct btrfs_root * root , struct extent_io_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 ;
2008-01-25 00:13:08 +03:00
struct extent_io_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 ,
2008-01-25 00:13:08 +03:00
struct extent_io_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 ;
2008-01-25 00:13:08 +03:00
struct extent_io_tree * free_space_cache ;
2007-10-16 00:14:48 +04:00
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-11-16 22:57:08 +03:00
update_pinned_extents ( root , start , end + 1 - start , 0 ) ;
2007-10-16 00:15:26 +04:00
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 ;
}
2008-01-03 18:01:48 +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-12-11 17:25:06 +03:00
u64 start ;
u64 end ;
struct btrfs_fs_info * info = extent_root - > fs_info ;
2007-12-11 20:42:00 +03:00
struct extent_buffer * eb ;
2007-12-11 17:25:06 +03:00
struct btrfs_path * path ;
2007-03-12 23:22:34 +03:00
struct btrfs_key ins ;
2007-12-11 20:42:00 +03:00
struct btrfs_disk_key first ;
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-12-11 20:42:00 +03:00
int level ;
2007-10-16 00:15:26 +04:00
int err = 0 ;
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-12-11 17:25:06 +03:00
path = btrfs_alloc_path ( ) ;
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-12-11 20:42:00 +03:00
eb = read_tree_block ( extent_root , ins . objectid , ins . offset ) ;
level = btrfs_header_level ( eb ) ;
if ( level = = 0 ) {
btrfs_item_key ( eb , & first , 0 ) ;
} else {
btrfs_node_key ( eb , & first , 0 ) ;
}
2007-12-11 17:25:06 +03:00
err = btrfs_insert_extent_backref ( trans , extent_root , path ,
start , extent_root - > root_key . objectid ,
2007-12-13 19:13:32 +03:00
0 , level ,
btrfs_disk_key_objectid ( & first ) ) ;
2007-12-11 17:25:06 +03:00
BUG_ON ( err ) ;
2007-12-11 20:42:00 +03:00
free_extent_buffer ( eb ) ;
2007-03-07 19:50:24 +03:00
}
2007-12-11 17:25:06 +03:00
btrfs_free_path ( path ) ;
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 ;
2008-01-08 23:46:30 +03:00
u64 header_transid =
btrfs_header_generation ( buf ) ;
2008-04-04 23:40:00 +04:00
if ( header_transid = = transid & &
! btrfs_header_flag ( buf ,
BTRFS_HEADER_FLAG_WRITTEN ) ) {
2008-01-09 23:55:33 +03:00
clean_tree_block ( NULL , root , buf ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( buf ) ;
2007-11-06 18:25:25 +03:00
return 1 ;
2007-04-02 18:50:19 +04:00
}
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-11-16 22:57:08 +03:00
update_pinned_extents ( root , bytenr , num_bytes , 1 ) ;
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-12-11 17:25:06 +03:00
* root , u64 bytenr , u64 num_bytes ,
u64 root_objectid , u64 ref_generation ,
u64 owner_objectid , u64 owner_offset , 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 ;
2008-02-19 00:33:44 +03:00
int extent_slot = 0 ;
int found_extent = 0 ;
int num_to_del = 1 ;
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-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
2008-01-08 23:46:31 +03:00
path - > reada = 0 ;
2007-12-11 17:25:06 +03:00
ret = lookup_extent_backref ( trans , extent_root , path ,
bytenr , root_objectid ,
ref_generation ,
owner_objectid , owner_offset , 1 ) ;
if ( ret = = 0 ) {
2008-02-19 00:33:44 +03:00
struct btrfs_key found_key ;
extent_slot = path - > slots [ 0 ] ;
while ( extent_slot > 0 ) {
extent_slot - - ;
btrfs_item_key_to_cpu ( path - > nodes [ 0 ] , & found_key ,
extent_slot ) ;
if ( found_key . objectid ! = bytenr )
break ;
if ( found_key . type = = BTRFS_EXTENT_ITEM_KEY & &
found_key . offset = = num_bytes ) {
found_extent = 1 ;
break ;
}
if ( path - > slots [ 0 ] - extent_slot > 5 )
break ;
}
if ( ! found_extent )
ret = btrfs_del_item ( trans , extent_root , path ) ;
2007-12-11 17:25:06 +03:00
} else {
btrfs_print_leaf ( extent_root , path - > nodes [ 0 ] ) ;
WARN_ON ( 1 ) ;
printk ( " Unable to find ref byte nr %Lu root %Lu "
" gen %Lu owner %Lu offset %Lu \n " , bytenr ,
root_objectid , ref_generation , owner_objectid ,
owner_offset ) ;
}
2008-02-19 00:33:44 +03:00
if ( ! found_extent ) {
btrfs_release_path ( extent_root , path ) ;
ret = btrfs_search_slot ( trans , extent_root , & key , path , - 1 , 1 ) ;
if ( ret < 0 )
return ret ;
BUG_ON ( ret ) ;
extent_slot = path - > slots [ 0 ] ;
}
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
2008-02-19 00:33:44 +03:00
ei = btrfs_item_ptr ( leaf , extent_slot ,
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 ) ;
2008-02-19 00:33:44 +03:00
2007-10-16 00:14:19 +04:00
btrfs_mark_buffer_dirty ( leaf ) ;
2008-02-19 00:33:44 +03:00
if ( refs = = 0 & & found_extent & & path - > slots [ 0 ] = = extent_slot + 1 ) {
/* if the back ref and the extent are next to each other
* they get deleted below in one shot
*/
path - > slots [ 0 ] = extent_slot ;
num_to_del = 2 ;
} else if ( found_extent ) {
/* otherwise delete the extent back ref */
ret = btrfs_del_item ( trans , extent_root , path ) ;
BUG_ON ( ret ) ;
/* if refs are 0, we need to setup the path for deletion */
if ( refs = = 0 ) {
btrfs_release_path ( extent_root , path ) ;
ret = btrfs_search_slot ( trans , extent_root , & key , path ,
- 1 , 1 ) ;
if ( ret < 0 )
return ret ;
BUG_ON ( ret ) ;
}
}
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-11-06 18:25:25 +03:00
if ( ret > 0 )
mark_free = 1 ;
BUG_ON ( ret < 0 ) ;
2007-03-25 19:35:08 +04: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-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 ) ;
2008-02-19 00:33:44 +03:00
ret = btrfs_del_items ( trans , extent_root , path , path - > slots [ 0 ] ,
num_to_del ) ;
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 ,
2008-03-24 22:01:56 +03:00
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 err = 0 ;
2007-10-16 00:15:26 +04:00
u64 start ;
u64 end ;
2008-01-25 00:13:08 +03:00
struct extent_io_tree * pending_del ;
struct extent_io_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-11-16 22:57:08 +03:00
update_pinned_extents ( extent_root , start , end + 1 - start , 1 ) ;
2007-10-16 00:15:26 +04:00
clear_extent_bits ( pending_del , start , end , EXTENT_LOCKED ,
GFP_NOFS ) ;
ret = __free_extent ( trans , extent_root ,
2007-12-11 17:25:06 +03:00
start , end + 1 - start ,
extent_root - > root_key . objectid ,
0 , 0 , 0 , 0 , 0 ) ;
2007-10-16 00:15:26 +04:00
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-12-11 17:25:06 +03:00
* root , u64 bytenr , u64 num_bytes ,
u64 root_objectid , u64 ref_generation ,
u64 owner_objectid , u64 owner_offset , 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-12-11 17:25:06 +03:00
if ( ! root - > ref_cows )
ref_generation = 0 ;
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-12-11 17:25:06 +03:00
ret = __free_extent ( trans , root , bytenr , num_bytes , root_objectid ,
ref_generation , owner_objectid , owner_offset ,
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 ;
}
2007-11-30 19:30:34 +03:00
static u64 stripe_align ( struct btrfs_root * root , u64 val )
{
u64 mask = ( ( u64 ) root - > stripesize - 1 ) ;
u64 ret = ( val + mask ) & ~ mask ;
return ret ;
}
2007-02-26 18:40:21 +03:00
/*
* 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 .
*/
2008-01-03 18:01:48 +03:00
static int noinline find_free_extent ( struct btrfs_trans_handle * trans ,
struct btrfs_root * orig_root ,
u64 num_bytes , u64 empty_size ,
u64 search_start , u64 search_end ,
u64 hint_byte , struct btrfs_key * ins ,
u64 exclude_start , u64 exclude_nr ,
int data )
2007-02-26 18:40:21 +03:00
{
2007-11-30 19:30:34 +03:00
int ret ;
2007-05-06 18:15:01 +04:00
u64 orig_search_start = search_start ;
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 ;
2008-03-24 22:02:07 +03:00
u64 * last_ptr = NULL ;
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 ;
2008-03-24 22:02:07 +03:00
int empty_cluster = 2 * 1024 * 1024 ;
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 ) ;
2008-03-24 22:02:07 +03:00
if ( data & BTRFS_BLOCK_GROUP_METADATA ) {
last_ptr = & root - > fs_info - > last_alloc ;
2008-04-04 00:29:03 +04:00
empty_cluster = 256 * 1024 ;
2008-03-24 22:02:07 +03:00
}
if ( ( data & BTRFS_BLOCK_GROUP_DATA ) & & btrfs_test_opt ( root , SSD ) ) {
last_ptr = & root - > fs_info - > last_data_alloc ;
}
if ( last_ptr ) {
if ( * last_ptr )
hint_byte = * last_ptr ;
else {
empty_size + = empty_cluster ;
}
}
2008-03-24 22:01:28 +03:00
if ( search_end = = ( u64 ) - 1 )
search_end = btrfs_super_total_bytes ( & info - > super_copy ) ;
2008-03-24 22:01:56 +03:00
2007-10-16 00:15:53 +04:00
if ( hint_byte ) {
block_group = btrfs_lookup_block_group ( info , hint_byte ) ;
2007-12-04 21:18:24 +03:00
if ( ! block_group )
hint_byte = search_start ;
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 ) ;
2008-03-24 22:02:07 +03:00
if ( last_ptr & & * last_ptr = = 0 & & block_group )
hint_byte = block_group - > key . objectid ;
2007-05-06 18:15:01 +04:00
} else {
block_group = btrfs_find_block_group ( root ,
2007-12-04 21:18:24 +03:00
trans - > block_group ,
search_start , data , 1 ) ;
2007-05-06 18:15:01 +04:00
}
2008-03-24 22:02:07 +03:00
search_start = max ( search_start , hint_byte ) ;
2007-05-06 18:15:01 +04:00
2007-08-08 00:15:09 +04:00
total_needed + = empty_size ;
2008-03-24 22:01:56 +03:00
2007-05-06 18:15:01 +04:00
check_failed :
2007-12-13 17:02:46 +03:00
if ( ! block_group ) {
block_group = btrfs_lookup_block_group ( info , search_start ) ;
if ( ! block_group )
block_group = btrfs_lookup_block_group ( info ,
orig_search_start ) ;
}
2008-03-24 22:01:56 +03:00
ret = find_search_start ( root , & block_group , & search_start ,
total_needed , data ) ;
2008-03-24 22:02:07 +03:00
if ( ret = = - ENOSPC & & last_ptr & & * last_ptr ) {
* last_ptr = 0 ;
block_group = btrfs_lookup_block_group ( info ,
orig_search_start ) ;
search_start = orig_search_start ;
ret = find_search_start ( root , & block_group , & search_start ,
total_needed , data ) ;
}
if ( ret = = - ENOSPC )
goto enospc ;
2008-03-24 22:01:56 +03:00
if ( ret )
2008-01-03 21:56:30 +03:00
goto error ;
2007-10-16 00:17:44 +04:00
2008-03-24 22:02:07 +03:00
if ( last_ptr & & * last_ptr & & search_start ! = * last_ptr ) {
* last_ptr = 0 ;
if ( ! empty_size ) {
empty_size + = empty_cluster ;
total_needed + = empty_size ;
}
block_group = btrfs_lookup_block_group ( info ,
orig_search_start ) ;
search_start = orig_search_start ;
ret = find_search_start ( root , & block_group ,
& search_start , total_needed , data ) ;
if ( ret = = - ENOSPC )
goto enospc ;
if ( ret )
goto error ;
}
2008-03-24 22:01:56 +03:00
search_start = stripe_align ( root , search_start ) ;
ins - > objectid = search_start ;
ins - > offset = num_bytes ;
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 ;
2008-03-24 22:01:56 +03:00
if ( ins - > objectid + num_bytes >
block_group - > key . objectid + block_group - > key . offset ) {
2007-10-16 00:17:44 +04:00
search_start = block_group - > key . objectid +
block_group - > key . offset ;
goto new_group ;
}
2008-03-24 22:01:56 +03:00
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 ;
}
2008-03-24 22:01:56 +03:00
2007-10-16 00:15:26 +04:00
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
}
2008-03-24 22:01:56 +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 ;
}
2008-03-24 22:01:56 +03:00
2008-03-24 22:01:59 +03:00
if ( ! ( data & BTRFS_BLOCK_GROUP_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 ;
2008-03-24 22:02:07 +03:00
if ( last_ptr ) {
* last_ptr = ins - > objectid + ins - > offset ;
if ( * last_ptr = =
btrfs_super_total_bytes ( & root - > fs_info - > super_copy ) ) {
* last_ptr = 0 ;
}
}
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-12-04 21:18:24 +03:00
block_group = btrfs_find_block_group ( root , block_group ,
search_start , data , 0 ) ;
2007-05-06 18:15:01 +04:00
goto check_failed ;
2007-03-01 00:46:22 +03:00
error :
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 ,
2007-12-11 17:25:06 +03:00
struct btrfs_root * root ,
2008-04-14 17:46:10 +04:00
u64 num_bytes , u64 min_alloc_size ,
u64 root_objectid , u64 ref_generation ,
2007-12-11 17:25:06 +03:00
u64 owner , u64 owner_offset ,
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 ;
2008-01-08 23:46:31 +03:00
u64 super_used ;
u64 root_used ;
2007-05-30 18:22:12 +04:00
u64 search_start = 0 ;
2007-12-22 00:27:24 +03:00
u64 new_hint ;
2008-04-04 00:29:03 +04:00
u64 alloc_profile ;
2008-02-01 22:51:59 +03:00
u32 sizes [ 2 ] ;
2007-03-21 03:35:03 +03:00
struct btrfs_fs_info * info = root - > fs_info ;
struct btrfs_root * extent_root = info - > extent_root ;
2008-02-01 22:51:59 +03:00
struct btrfs_extent_item * extent_item ;
struct btrfs_extent_ref * ref ;
2007-12-11 17:25:06 +03:00
struct btrfs_path * path ;
2008-02-01 22:51:59 +03:00
struct btrfs_key keys [ 2 ] ;
2008-01-02 18:01:11 +03:00
2008-03-24 22:01:59 +03:00
if ( data ) {
2008-04-04 00:29:03 +04:00
alloc_profile = info - > avail_data_alloc_bits &
info - > data_alloc_profile ;
data = BTRFS_BLOCK_GROUP_DATA | alloc_profile ;
2008-03-24 22:01:59 +03:00
} else if ( root = = root - > fs_info - > chunk_root ) {
2008-04-04 00:29:03 +04:00
alloc_profile = info - > avail_system_alloc_bits &
info - > system_alloc_profile ;
data = BTRFS_BLOCK_GROUP_SYSTEM | alloc_profile ;
2008-03-24 22:01:59 +03:00
} else {
2008-04-04 00:29:03 +04:00
alloc_profile = info - > avail_metadata_alloc_bits &
info - > metadata_alloc_profile ;
data = BTRFS_BLOCK_GROUP_METADATA | alloc_profile ;
2008-03-24 22:01:59 +03:00
}
2008-04-14 17:46:10 +04:00
again :
2008-03-24 22:01:59 +03:00
if ( root - > ref_cows ) {
2008-03-25 23:50:33 +03:00
if ( ! ( data & BTRFS_BLOCK_GROUP_METADATA ) ) {
2008-03-24 22:01:59 +03:00
ret = do_chunk_alloc ( trans , root - > fs_info - > extent_root ,
2008-03-24 22:02:07 +03:00
2 * 1024 * 1024 ,
2008-03-25 23:50:33 +03:00
BTRFS_BLOCK_GROUP_METADATA |
2008-04-04 00:29:03 +04:00
( info - > metadata_alloc_profile &
info - > avail_metadata_alloc_bits ) ) ;
2008-03-24 22:01:59 +03:00
BUG_ON ( ret ) ;
}
ret = do_chunk_alloc ( trans , root - > fs_info - > extent_root ,
2008-04-04 00:29:03 +04:00
num_bytes + 2 * 1024 * 1024 , data ) ;
2008-03-24 22:01:59 +03:00
BUG_ON ( ret ) ;
}
2008-03-24 22:01:56 +03:00
2008-01-02 18:01:11 +03:00
new_hint = max ( hint_byte , root - > fs_info - > alloc_start ) ;
2007-12-22 00:27:24 +03:00
if ( new_hint < btrfs_super_total_bytes ( & info - > super_copy ) )
hint_byte = new_hint ;
2008-01-02 18:01:11 +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 ) ;
2008-04-14 17:46:10 +04:00
if ( ret = = - ENOSPC & & num_bytes > min_alloc_size ) {
num_bytes = num_bytes > > 1 ;
num_bytes = max ( num_bytes , min_alloc_size ) ;
goto again ;
}
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-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-07 19:50:24 +03:00
2008-02-01 22:51:59 +03:00
memcpy ( & keys [ 0 ] , ins , sizeof ( * ins ) ) ;
keys [ 1 ] . offset = hash_extent_ref ( root_objectid , ref_generation ,
owner , owner_offset ) ;
keys [ 1 ] . objectid = ins - > objectid ;
keys [ 1 ] . type = BTRFS_EXTENT_REF_KEY ;
sizes [ 0 ] = sizeof ( * extent_item ) ;
sizes [ 1 ] = sizeof ( * ref ) ;
2007-12-11 17:25:06 +03:00
path = btrfs_alloc_path ( ) ;
BUG_ON ( ! path ) ;
2008-02-01 22:51:59 +03:00
ret = btrfs_insert_empty_items ( trans , extent_root , path , keys ,
sizes , 2 ) ;
2007-08-09 04:17:12 +04:00
2007-06-28 23:57:36 +04:00
BUG_ON ( ret ) ;
2008-02-01 22:51:59 +03:00
extent_item = btrfs_item_ptr ( path - > nodes [ 0 ] , path - > slots [ 0 ] ,
struct btrfs_extent_item ) ;
btrfs_set_extent_refs ( path - > nodes [ 0 ] , extent_item , 1 ) ;
ref = btrfs_item_ptr ( path - > nodes [ 0 ] , path - > slots [ 0 ] + 1 ,
struct btrfs_extent_ref ) ;
btrfs_set_ref_root ( path - > nodes [ 0 ] , ref , root_objectid ) ;
btrfs_set_ref_generation ( path - > nodes [ 0 ] , ref , ref_generation ) ;
btrfs_set_ref_objectid ( path - > nodes [ 0 ] , ref , owner ) ;
btrfs_set_ref_offset ( path - > nodes [ 0 ] , ref , owner_offset ) ;
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
trans - > alloc_exclude_start = 0 ;
trans - > alloc_exclude_nr = 0 ;
2007-12-11 17:25:06 +03:00
btrfs_free_path ( path ) ;
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 :
2008-03-24 22:01:56 +03:00
ret = update_block_group ( trans , root , ins - > objectid , ins - > offset , 1 , 0 ) ;
2008-02-04 18:10:13 +03:00
if ( ret ) {
printk ( " update block group failed for %Lu %Lu \n " ,
ins - > objectid , ins - > offset ) ;
BUG ( ) ;
}
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 ,
2007-12-11 17:25:06 +03:00
u32 blocksize ,
u64 root_objectid , u64 hint ,
u64 empty_size )
{
u64 ref_generation ;
if ( root - > ref_cows )
ref_generation = trans - > transid ;
else
ref_generation = 0 ;
return __btrfs_alloc_free_block ( trans , root , blocksize , root_objectid ,
ref_generation , 0 , 0 , hint , empty_size ) ;
}
/*
* helper function to allocate a block for a given tree
* returns the tree buffer or NULL .
*/
struct extent_buffer * __btrfs_alloc_free_block ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
u32 blocksize ,
u64 root_objectid ,
u64 ref_generation ,
u64 first_objectid ,
int level ,
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
2008-04-14 17:46:10 +04:00
ret = btrfs_alloc_extent ( trans , root , blocksize , blocksize ,
2007-12-11 17:25:06 +03:00
root_objectid , ref_generation ,
2007-12-13 19:13:32 +03:00
level , first_objectid , empty_size , hint ,
2007-10-16 00:15:53 +04:00
( 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-12-11 17:25:06 +03:00
btrfs_free_extent ( trans , root , ins . objectid , blocksize ,
root - > root_key . objectid , ref_generation ,
0 , 0 , 0 ) ;
2007-06-22 22:16:25 +04:00
return ERR_PTR ( - ENOMEM ) ;
}
2008-01-09 23:55:33 +03:00
btrfs_set_header_generation ( buf , trans - > transid ) ;
clean_tree_block ( trans , root , buf ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_buffer_uptodate ( buf ) ;
2008-01-09 23:55:33 +03:00
if ( PageDirty ( buf - > first_page ) ) {
printk ( " page %lu dirty \n " , buf - > first_page - > index ) ;
WARN_ON ( 1 ) ;
}
2007-10-16 00:14:19 +04:00
set_extent_dirty ( & trans - > transaction - > dirty_pages , buf - > start ,
buf - > start + buf - > len - 1 , GFP_NOFS ) ;
2008-02-15 21:19:35 +03:00
if ( ! btrfs_test_opt ( root , SSD ) )
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
2008-01-03 18:01:48 +03:00
static int noinline drop_leaf_ref ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct extent_buffer * leaf )
2007-03-27 14:33:00 +04:00
{
2007-12-11 17:25:06 +03:00
u64 leaf_owner ;
u64 leaf_generation ;
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-12-11 17:25:06 +03:00
leaf_owner = btrfs_header_owner ( leaf ) ;
leaf_generation = btrfs_header_generation ( 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 ,
2007-12-11 17:25:06 +03:00
btrfs_file_extent_disk_num_bytes ( leaf , fi ) ,
leaf_owner , leaf_generation ,
key . objectid , key . offset , 0 ) ;
2007-03-27 14:33:00 +04:00
BUG_ON ( ret ) ;
}
return 0 ;
}
2008-01-03 18:01:48 +03:00
static void noinline reada_walk_down ( struct btrfs_root * root ,
2008-01-25 00:13:14 +03:00
struct extent_buffer * node ,
int slot )
2007-06-20 00:23:05 +04:00
{
2007-10-16 00:15:53 +04:00
u64 bytenr ;
2008-01-25 00:13:14 +03:00
u64 last = 0 ;
u32 nritems ;
2007-06-20 00:23:05 +04:00
u32 refs ;
2007-10-16 00:15:53 +04:00
u32 blocksize ;
2008-01-25 00:13:14 +03:00
int ret ;
int i ;
int level ;
int skipped = 0 ;
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 ) ;
2008-01-25 00:13:14 +03:00
if ( level )
return ;
for ( i = slot ; i < nritems & & skipped < 32 ; i + + ) {
2007-10-16 00:15:53 +04:00
bytenr = btrfs_node_blockptr ( node , i ) ;
2008-01-25 00:13:14 +03:00
if ( last & & ( ( bytenr > last & & bytenr - last > 32 * 1024 ) | |
( last > bytenr & & last - bytenr > 32 * 1024 ) ) ) {
skipped + + ;
2007-06-20 00:23:05 +04:00
continue ;
2008-01-25 00:13:14 +03:00
}
blocksize = btrfs_level_size ( root , level - 1 ) ;
if ( i ! = slot ) {
ret = lookup_extent_ref ( NULL , root , bytenr ,
blocksize , & refs ) ;
BUG_ON ( ret ) ;
if ( refs ! = 1 ) {
skipped + + ;
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 ) ;
2008-01-25 00:13:14 +03:00
last = 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 .
*/
2008-01-03 18:01:48 +03:00
static int noinline 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-12-11 17:25:06 +03:00
u64 root_owner ;
u64 root_gen ;
u64 bytenr ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * next ;
struct extent_buffer * cur ;
2007-12-11 17:25:06 +03:00
struct extent_buffer * parent ;
2007-10-16 00:15:53 +04:00
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
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-12-11 17:25:06 +03:00
parent = path - > nodes [ * level ] ;
root_owner = btrfs_header_owner ( parent ) ;
root_gen = btrfs_header_generation ( parent ) ;
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 ,
2007-12-11 17:25:06 +03:00
blocksize , root_owner ,
root_gen , 0 , 0 , 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 ) ;
2008-01-25 00:13:14 +03:00
reada_walk_down ( root , cur , path - > slots [ * level ] ) ;
2008-04-04 00:29:03 +04:00
mutex_unlock ( & root - > fs_info - > fs_mutex ) ;
2007-10-16 00:15:53 +04:00
next = read_tree_block ( root , bytenr , blocksize ) ;
2008-04-04 00:29:03 +04:00
mutex_lock ( & root - > fs_info - > fs_mutex ) ;
2007-08-10 22:06:19 +04:00
2008-04-04 00:29:03 +04:00
/* we've dropped the lock, double check */
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 ) {
2007-12-11 17:25:06 +03:00
parent = path - > nodes [ * level ] ;
root_owner = btrfs_header_owner ( parent ) ;
root_gen = btrfs_header_generation ( parent ) ;
2007-08-10 22:06:19 +04:00
path - > slots [ * level ] + + ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( next ) ;
2007-12-11 17:25:06 +03:00
ret = btrfs_free_extent ( trans , root , bytenr ,
blocksize ,
root_owner ,
root_gen , 0 , 0 , 1 ) ;
2007-08-10 22:06:19 +04:00
BUG_ON ( ret ) ;
continue ;
}
2008-04-01 21:48:14 +04:00
} else if ( next ) {
btrfs_verify_block_csum ( root , next ) ;
2007-08-10 22:06:19 +04:00
}
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-12-11 17:25:06 +03:00
if ( path - > nodes [ * level ] = = root - > node ) {
root_owner = root - > root_key . objectid ;
parent = path - > nodes [ * level ] ;
} else {
parent = path - > nodes [ * level + 1 ] ;
root_owner = btrfs_header_owner ( parent ) ;
}
root_gen = btrfs_header_generation ( parent ) ;
2007-10-16 00:15:53 +04:00
ret = btrfs_free_extent ( trans , root , path - > nodes [ * level ] - > start ,
2007-12-11 17:25:06 +03:00
path - > nodes [ * level ] - > len ,
root_owner , root_gen , 0 , 0 , 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
*/
2008-01-03 18:01:48 +03:00
static int noinline 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
{
2007-12-11 17:25:06 +03:00
u64 root_owner ;
u64 root_gen ;
struct btrfs_root_item * root_item = & root - > root_item ;
2007-03-10 14:35:47 +03:00
int i ;
int slot ;
int ret ;
2007-08-07 23:52:19 +04:00
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-12-11 17:25:06 +03:00
if ( path - > nodes [ * level ] = = root - > node ) {
root_owner = root - > root_key . objectid ;
root_gen =
btrfs_header_generation ( path - > nodes [ * level ] ) ;
} else {
struct extent_buffer * node ;
node = path - > nodes [ * level + 1 ] ;
root_owner = btrfs_header_owner ( node ) ;
root_gen = btrfs_header_generation ( node ) ;
}
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 ,
2007-12-11 17:25:06 +03:00
path - > nodes [ * level ] - > len ,
root_owner , root_gen , 0 , 0 , 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 ;
}
2008-01-03 18:01:48 +03:00
static int noinline relocate_inode_pages ( struct inode * inode , u64 start ,
u64 len )
2007-12-22 00:27:24 +03:00
{
u64 page_start ;
u64 page_end ;
u64 delalloc_start ;
u64 existing_delalloc ;
unsigned long last_index ;
unsigned long i ;
struct page * page ;
2008-01-25 00:13:08 +03:00
struct extent_io_tree * io_tree = & BTRFS_I ( inode ) - > io_tree ;
2008-01-03 17:08:48 +03:00
struct file_ra_state * ra ;
ra = kzalloc ( sizeof ( * ra ) , GFP_NOFS ) ;
2007-12-22 00:27:24 +03:00
mutex_lock ( & inode - > i_mutex ) ;
2008-01-03 17:08:48 +03:00
i = start > > PAGE_CACHE_SHIFT ;
2007-12-22 00:27:24 +03:00
last_index = ( start + len - 1 ) > > PAGE_CACHE_SHIFT ;
2008-01-03 17:08:48 +03:00
file_ra_state_init ( ra , inode - > i_mapping ) ;
btrfs_force_ra ( inode - > i_mapping , ra , NULL , i , last_index ) ;
kfree ( ra ) ;
2007-12-22 00:27:24 +03:00
2008-01-03 17:08:48 +03:00
for ( ; i < = last_index ; i + + ) {
2007-12-22 00:27:24 +03:00
page = grab_cache_page ( inode - > i_mapping , i ) ;
if ( ! page )
goto out_unlock ;
if ( ! PageUptodate ( page ) ) {
btrfs_readpage ( NULL , page ) ;
lock_page ( page ) ;
if ( ! PageUptodate ( page ) ) {
unlock_page ( page ) ;
page_cache_release ( page ) ;
goto out_unlock ;
}
}
page_start = ( u64 ) page - > index < < PAGE_CACHE_SHIFT ;
page_end = page_start + PAGE_CACHE_SIZE - 1 ;
2008-01-25 00:13:08 +03:00
lock_extent ( io_tree , page_start , page_end , GFP_NOFS ) ;
2007-12-22 00:27:24 +03:00
delalloc_start = page_start ;
2008-01-25 00:13:08 +03:00
existing_delalloc = count_range_bits ( io_tree ,
& delalloc_start , page_end ,
PAGE_CACHE_SIZE , EXTENT_DELALLOC ) ;
2007-12-22 00:27:24 +03:00
2008-01-25 00:13:08 +03:00
set_extent_delalloc ( io_tree , page_start ,
2007-12-22 00:27:24 +03:00
page_end , GFP_NOFS ) ;
2008-01-25 00:13:08 +03:00
unlock_extent ( io_tree , page_start , page_end , GFP_NOFS ) ;
2007-12-22 00:27:24 +03:00
set_page_dirty ( page ) ;
unlock_page ( page ) ;
page_cache_release ( page ) ;
}
out_unlock :
mutex_unlock ( & inode - > i_mutex ) ;
return 0 ;
}
2008-01-03 17:08:48 +03:00
/*
* note , this releases the path
*/
2008-01-03 18:01:48 +03:00
static int noinline relocate_one_reference ( struct btrfs_root * extent_root ,
2007-12-22 00:27:24 +03:00
struct btrfs_path * path ,
2008-01-03 17:08:48 +03:00
struct btrfs_key * extent_key )
2007-12-22 00:27:24 +03:00
{
struct inode * inode ;
struct btrfs_root * found_root ;
2008-01-03 17:08:48 +03:00
struct btrfs_key * root_location ;
struct btrfs_extent_ref * ref ;
u64 ref_root ;
u64 ref_gen ;
u64 ref_objectid ;
u64 ref_offset ;
2007-12-22 00:27:24 +03:00
int ret ;
2008-01-03 17:08:48 +03:00
ref = btrfs_item_ptr ( path - > nodes [ 0 ] , path - > slots [ 0 ] ,
struct btrfs_extent_ref ) ;
ref_root = btrfs_ref_root ( path - > nodes [ 0 ] , ref ) ;
ref_gen = btrfs_ref_generation ( path - > nodes [ 0 ] , ref ) ;
ref_objectid = btrfs_ref_objectid ( path - > nodes [ 0 ] , ref ) ;
ref_offset = btrfs_ref_offset ( path - > nodes [ 0 ] , ref ) ;
btrfs_release_path ( extent_root , path ) ;
root_location = kmalloc ( sizeof ( * root_location ) , GFP_NOFS ) ;
root_location - > objectid = ref_root ;
2007-12-22 00:27:24 +03:00
if ( ref_gen = = 0 )
2008-01-03 17:08:48 +03:00
root_location - > offset = 0 ;
2007-12-22 00:27:24 +03:00
else
2008-01-03 17:08:48 +03:00
root_location - > offset = ( u64 ) - 1 ;
root_location - > type = BTRFS_ROOT_ITEM_KEY ;
2007-12-22 00:27:24 +03:00
found_root = btrfs_read_fs_root_no_name ( extent_root - > fs_info ,
2008-01-03 17:08:48 +03:00
root_location ) ;
2007-12-22 00:27:24 +03:00
BUG_ON ( ! found_root ) ;
2008-01-03 17:08:48 +03:00
kfree ( root_location ) ;
2007-12-22 00:27:24 +03:00
if ( ref_objectid > = BTRFS_FIRST_FREE_OBJECTID ) {
mutex_unlock ( & extent_root - > fs_info - > fs_mutex ) ;
inode = btrfs_iget_locked ( extent_root - > fs_info - > sb ,
ref_objectid , found_root ) ;
if ( inode - > i_state & I_NEW ) {
/* the inode and parent dir are two different roots */
BTRFS_I ( inode ) - > root = found_root ;
BTRFS_I ( inode ) - > location . objectid = ref_objectid ;
BTRFS_I ( inode ) - > location . type = BTRFS_INODE_ITEM_KEY ;
BTRFS_I ( inode ) - > location . offset = 0 ;
btrfs_read_locked_inode ( inode ) ;
unlock_new_inode ( inode ) ;
}
/* this can happen if the reference is not against
* the latest version of the tree root
*/
if ( is_bad_inode ( inode ) ) {
mutex_lock ( & extent_root - > fs_info - > fs_mutex ) ;
goto out ;
}
relocate_inode_pages ( inode , ref_offset , extent_key - > offset ) ;
/* FIXME, data=ordered will help get rid of this */
filemap_fdatawrite ( inode - > i_mapping ) ;
iput ( inode ) ;
mutex_lock ( & extent_root - > fs_info - > fs_mutex ) ;
} else {
struct btrfs_trans_handle * trans ;
struct btrfs_key found_key ;
struct extent_buffer * eb ;
int level ;
int i ;
trans = btrfs_start_transaction ( found_root , 1 ) ;
eb = read_tree_block ( found_root , extent_key - > objectid ,
extent_key - > offset ) ;
level = btrfs_header_level ( eb ) ;
if ( level = = 0 )
btrfs_item_key_to_cpu ( eb , & found_key , 0 ) ;
else
btrfs_node_key_to_cpu ( eb , & found_key , 0 ) ;
free_extent_buffer ( eb ) ;
path - > lowest_level = level ;
2008-01-02 18:01:11 +03:00
path - > reada = 2 ;
2007-12-22 00:27:24 +03:00
ret = btrfs_search_slot ( trans , found_root , & found_key , path ,
0 , 1 ) ;
path - > lowest_level = 0 ;
for ( i = level ; i < BTRFS_MAX_LEVEL ; i + + ) {
if ( ! path - > nodes [ i ] )
break ;
free_extent_buffer ( path - > nodes [ i ] ) ;
path - > nodes [ i ] = NULL ;
}
btrfs_release_path ( found_root , path ) ;
btrfs_end_transaction ( trans , found_root ) ;
}
out :
return 0 ;
}
2008-01-03 18:01:48 +03:00
static int noinline relocate_one_extent ( struct btrfs_root * extent_root ,
struct btrfs_path * path ,
struct btrfs_key * extent_key )
2007-12-22 00:27:24 +03:00
{
struct btrfs_key key ;
struct btrfs_key found_key ;
struct extent_buffer * leaf ;
u32 nritems ;
u32 item_size ;
int ret = 0 ;
key . objectid = extent_key - > objectid ;
key . type = BTRFS_EXTENT_REF_KEY ;
key . offset = 0 ;
while ( 1 ) {
ret = btrfs_search_slot ( NULL , extent_root , & key , path , 0 , 0 ) ;
if ( ret < 0 )
goto out ;
ret = 0 ;
leaf = path - > nodes [ 0 ] ;
nritems = btrfs_header_nritems ( leaf ) ;
if ( path - > slots [ 0 ] = = nritems )
goto out ;
btrfs_item_key_to_cpu ( leaf , & found_key , path - > slots [ 0 ] ) ;
if ( found_key . objectid ! = extent_key - > objectid )
break ;
if ( found_key . type ! = BTRFS_EXTENT_REF_KEY )
break ;
key . offset = found_key . offset + 1 ;
item_size = btrfs_item_size_nr ( leaf , path - > slots [ 0 ] ) ;
2008-01-03 17:08:48 +03:00
ret = relocate_one_reference ( extent_root , path , extent_key ) ;
2007-12-22 00:27:24 +03:00
if ( ret )
goto out ;
}
ret = 0 ;
out :
btrfs_release_path ( extent_root , path ) ;
return ret ;
}
int btrfs_shrink_extent_tree ( struct btrfs_root * root , u64 new_size )
{
struct btrfs_trans_handle * trans ;
struct btrfs_root * tree_root = root - > fs_info - > tree_root ;
struct btrfs_path * path ;
u64 cur_byte ;
u64 total_found ;
struct btrfs_fs_info * info = root - > fs_info ;
2008-01-25 00:13:08 +03:00
struct extent_io_tree * block_group_cache ;
2007-12-22 00:27:24 +03:00
struct btrfs_key key ;
2008-01-03 22:14:39 +03:00
struct btrfs_key found_key ;
2007-12-22 00:27:24 +03:00
struct extent_buffer * leaf ;
u32 nritems ;
int ret ;
2008-01-05 00:47:16 +03:00
int progress = 0 ;
2007-12-22 00:27:24 +03:00
btrfs_set_super_total_bytes ( & info - > super_copy , new_size ) ;
2008-01-08 23:46:31 +03:00
clear_extent_dirty ( & info - > free_space_cache , new_size , ( u64 ) - 1 ,
GFP_NOFS ) ;
2007-12-22 00:27:24 +03:00
block_group_cache = & info - > block_group_cache ;
path = btrfs_alloc_path ( ) ;
root = root - > fs_info - > extent_root ;
2008-01-02 18:01:11 +03:00
path - > reada = 2 ;
2007-12-22 00:27:24 +03:00
again :
total_found = 0 ;
key . objectid = new_size ;
key . offset = 0 ;
key . type = 0 ;
2008-01-03 22:14:39 +03:00
cur_byte = key . objectid ;
2008-01-03 17:08:48 +03:00
2008-01-03 22:14:39 +03:00
ret = btrfs_search_slot ( NULL , root , & key , path , 0 , 0 ) ;
if ( ret < 0 )
goto out ;
2008-03-24 22:01:56 +03:00
ret = btrfs_previous_item ( root , path , 0 , BTRFS_EXTENT_ITEM_KEY ) ;
2008-01-03 22:14:39 +03:00
if ( ret < 0 )
goto out ;
if ( ret = = 0 ) {
leaf = path - > nodes [ 0 ] ;
btrfs_item_key_to_cpu ( leaf , & found_key , path - > slots [ 0 ] ) ;
if ( found_key . objectid + found_key . offset > new_size ) {
cur_byte = found_key . objectid ;
key . objectid = cur_byte ;
}
}
btrfs_release_path ( root , path ) ;
while ( 1 ) {
2007-12-22 00:27:24 +03:00
ret = btrfs_search_slot ( NULL , root , & key , path , 0 , 0 ) ;
if ( ret < 0 )
goto out ;
2008-01-03 22:14:39 +03:00
2007-12-22 00:27:24 +03:00
leaf = path - > nodes [ 0 ] ;
2008-01-03 22:14:39 +03:00
nritems = btrfs_header_nritems ( leaf ) ;
next :
if ( path - > slots [ 0 ] > = nritems ) {
ret = btrfs_next_leaf ( root , path ) ;
if ( ret < 0 )
goto out ;
if ( ret = = 1 ) {
ret = 0 ;
break ;
2007-12-22 00:27:24 +03:00
}
2008-01-03 22:14:39 +03:00
leaf = path - > nodes [ 0 ] ;
nritems = btrfs_header_nritems ( leaf ) ;
2007-12-22 00:27:24 +03:00
}
2008-01-03 22:14:39 +03:00
btrfs_item_key_to_cpu ( leaf , & found_key , path - > slots [ 0 ] ) ;
2008-01-05 00:47:16 +03:00
if ( progress & & need_resched ( ) ) {
memcpy ( & key , & found_key , sizeof ( key ) ) ;
mutex_unlock ( & root - > fs_info - > fs_mutex ) ;
cond_resched ( ) ;
mutex_lock ( & root - > fs_info - > fs_mutex ) ;
btrfs_release_path ( root , path ) ;
btrfs_search_slot ( NULL , root , & key , path , 0 , 0 ) ;
progress = 0 ;
goto next ;
}
progress = 1 ;
2008-01-03 22:14:39 +03:00
if ( btrfs_key_type ( & found_key ) ! = BTRFS_EXTENT_ITEM_KEY | |
found_key . objectid + found_key . offset < = cur_byte ) {
2007-12-22 00:27:24 +03:00
path - > slots [ 0 ] + + ;
goto next ;
}
2008-01-03 22:14:39 +03:00
2007-12-22 00:27:24 +03:00
total_found + + ;
cur_byte = found_key . objectid + found_key . offset ;
key . objectid = cur_byte ;
btrfs_release_path ( root , path ) ;
ret = relocate_one_extent ( root , path , & found_key ) ;
}
btrfs_release_path ( root , path ) ;
if ( total_found > 0 ) {
trans = btrfs_start_transaction ( tree_root , 1 ) ;
btrfs_commit_transaction ( trans , tree_root ) ;
mutex_unlock ( & root - > fs_info - > fs_mutex ) ;
btrfs_clean_old_snapshots ( tree_root ) ;
mutex_lock ( & root - > fs_info - > fs_mutex ) ;
trans = btrfs_start_transaction ( tree_root , 1 ) ;
btrfs_commit_transaction ( trans , tree_root ) ;
goto again ;
}
trans = btrfs_start_transaction ( root , 1 ) ;
key . objectid = new_size ;
key . offset = 0 ;
key . type = 0 ;
while ( 1 ) {
2008-01-03 17:08:48 +03:00
u64 ptr ;
2007-12-22 00:27:24 +03:00
ret = btrfs_search_slot ( trans , root , & key , path , - 1 , 1 ) ;
if ( ret < 0 )
goto out ;
2008-01-03 22:14:39 +03:00
2007-12-22 00:27:24 +03:00
leaf = path - > nodes [ 0 ] ;
nritems = btrfs_header_nritems ( leaf ) ;
2008-01-03 22:14:39 +03:00
bg_next :
if ( path - > slots [ 0 ] > = nritems ) {
ret = btrfs_next_leaf ( root , path ) ;
if ( ret < 0 )
break ;
if ( ret = = 1 ) {
ret = 0 ;
break ;
2007-12-22 00:27:24 +03:00
}
2008-01-03 22:14:39 +03:00
leaf = path - > nodes [ 0 ] ;
2008-01-04 17:34:54 +03:00
btrfs_item_key_to_cpu ( leaf , & found_key , path - > slots [ 0 ] ) ;
/*
* btrfs_next_leaf doesn ' t cow buffers , we have to
* do the search again
*/
memcpy ( & key , & found_key , sizeof ( key ) ) ;
btrfs_release_path ( root , path ) ;
2008-01-05 00:47:16 +03:00
goto resched_check ;
2008-01-03 22:14:39 +03:00
}
btrfs_item_key_to_cpu ( leaf , & found_key , path - > slots [ 0 ] ) ;
if ( btrfs_key_type ( & found_key ) ! = BTRFS_BLOCK_GROUP_ITEM_KEY ) {
printk ( " shrinker found key %Lu %u %Lu \n " ,
found_key . objectid , found_key . type ,
found_key . offset ) ;
path - > slots [ 0 ] + + ;
2007-12-22 00:27:24 +03:00
goto bg_next ;
}
ret = get_state_private ( & info - > block_group_cache ,
found_key . objectid , & ptr ) ;
if ( ! ret )
kfree ( ( void * ) ( unsigned long ) ptr ) ;
clear_extent_bits ( & info - > block_group_cache , found_key . objectid ,
found_key . objectid + found_key . offset - 1 ,
( unsigned int ) - 1 , GFP_NOFS ) ;
key . objectid = found_key . objectid + 1 ;
btrfs_del_item ( trans , root , path ) ;
btrfs_release_path ( root , path ) ;
2008-01-05 00:47:16 +03:00
resched_check :
if ( need_resched ( ) ) {
mutex_unlock ( & root - > fs_info - > fs_mutex ) ;
cond_resched ( ) ;
mutex_lock ( & root - > fs_info - > fs_mutex ) ;
}
2007-12-22 00:27:24 +03:00
}
clear_extent_dirty ( & info - > free_space_cache , new_size , ( u64 ) - 1 ,
GFP_NOFS ) ;
btrfs_commit_transaction ( trans , root ) ;
out :
btrfs_free_path ( path ) ;
return ret ;
}
int btrfs_grow_extent_tree ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root , u64 new_size )
{
2008-03-24 22:01:56 +03:00
btrfs_set_super_total_bytes ( & root - > fs_info - > super_copy , new_size ) ;
return 0 ;
}
2007-12-22 00:27:24 +03:00
2008-03-24 22:01:56 +03:00
int find_first_block_group ( struct btrfs_root * root , struct btrfs_path * path ,
struct btrfs_key * key )
{
int ret ;
struct btrfs_key found_key ;
struct extent_buffer * leaf ;
int slot ;
2007-12-22 00:27:24 +03:00
2008-03-24 22:01:56 +03:00
ret = btrfs_search_slot ( NULL , root , key , path , 0 , 0 ) ;
if ( ret < 0 )
return ret ;
while ( 1 ) {
slot = path - > slots [ 0 ] ;
2007-12-22 00:27:24 +03:00
leaf = path - > nodes [ 0 ] ;
2008-03-24 22:01:56 +03:00
if ( slot > = btrfs_header_nritems ( leaf ) ) {
ret = btrfs_next_leaf ( root , path ) ;
if ( ret = = 0 )
continue ;
if ( ret < 0 )
goto error ;
break ;
2007-12-22 00:27:24 +03:00
}
2008-03-24 22:01:56 +03:00
btrfs_item_key_to_cpu ( leaf , & found_key , slot ) ;
2007-12-22 00:27:24 +03:00
2008-03-24 22:01:56 +03:00
if ( found_key . objectid > = key - > objectid & &
found_key . type = = BTRFS_BLOCK_GROUP_ITEM_KEY )
return 0 ;
path - > slots [ 0 ] + + ;
2007-12-22 00:27:24 +03:00
}
2008-03-24 22:01:56 +03:00
ret = - ENOENT ;
error :
return ret ;
2007-12-22 00:27:24 +03:00
}
2007-04-27 00:46:15 +04:00
int btrfs_read_block_groups ( struct btrfs_root * root )
{
struct btrfs_path * path ;
int ret ;
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 ;
2008-03-24 22:01:59 +03:00
struct btrfs_space_info * space_info ;
2008-01-25 00:13:08 +03:00
struct extent_io_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-05-06 18:15:01 +04:00
root = info - > extent_root ;
2007-04-27 00:46:15 +04:00
key . objectid = 0 ;
2008-03-24 22:01:56 +03:00
key . offset = 0 ;
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 ) {
2008-03-24 22:01:56 +03:00
ret = find_first_block_group ( root , path , & key ) ;
if ( ret > 0 ) {
ret = 0 ;
goto error ;
2007-04-27 00:46:15 +04:00
}
2008-03-24 22:01:56 +03:00
if ( ret ! = 0 )
goto error ;
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 ) {
2008-03-24 22:01:56 +03:00
ret = - ENOMEM ;
2007-04-27 00:46:15 +04:00
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-11-16 22:57:08 +03:00
cache - > pinned = 0 ;
2008-03-24 22:01:56 +03:00
2007-04-27 00:46:15 +04:00
key . objectid = found_key . objectid + found_key . offset ;
btrfs_release_path ( root , path ) ;
2008-03-24 22:01:56 +03:00
cache - > flags = btrfs_block_group_flags ( & cache - > item ) ;
bit = 0 ;
if ( cache - > flags & BTRFS_BLOCK_GROUP_DATA ) {
2007-10-16 00:15:19 +04:00
bit = BLOCK_GROUP_DATA ;
2008-03-24 22:01:56 +03:00
} else if ( cache - > flags & BTRFS_BLOCK_GROUP_SYSTEM ) {
bit = BLOCK_GROUP_SYSTEM ;
} else if ( cache - > flags & BTRFS_BLOCK_GROUP_METADATA ) {
2007-10-16 00:15:19 +04:00
bit = BLOCK_GROUP_METADATA ;
2007-04-30 23:25:45 +04:00
}
2008-04-04 00:29:03 +04:00
set_avail_alloc_bits ( info , cache - > flags ) ;
2007-10-16 00:15:19 +04:00
2008-03-24 22:01:59 +03:00
ret = update_space_info ( info , cache - > flags , found_key . offset ,
btrfs_block_group_used ( & cache - > item ) ,
& space_info ) ;
BUG_ON ( ret ) ;
cache - > space_info = space_info ;
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 ;
}
2008-03-24 22:01:56 +03:00
ret = 0 ;
error :
2007-04-27 00:46:15 +04:00
btrfs_free_path ( path ) ;
2008-03-24 22:01:56 +03:00
return ret ;
2007-04-27 00:46:15 +04:00
}
2008-03-24 22:01:59 +03:00
int btrfs_make_block_group ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root , u64 bytes_used ,
2008-04-15 23:41:47 +04:00
u64 type , u64 chunk_objectid , u64 chunk_offset ,
2008-03-24 22:01:59 +03:00
u64 size )
{
int ret ;
int bit = 0 ;
struct btrfs_root * extent_root ;
struct btrfs_block_group_cache * cache ;
struct extent_io_tree * block_group_cache ;
extent_root = root - > fs_info - > extent_root ;
block_group_cache = & root - > fs_info - > block_group_cache ;
cache = kmalloc ( sizeof ( * cache ) , GFP_NOFS ) ;
BUG_ON ( ! cache ) ;
2008-04-15 23:41:47 +04:00
cache - > key . objectid = chunk_offset ;
2008-03-24 22:01:59 +03:00
cache - > key . offset = size ;
cache - > cached = 0 ;
cache - > pinned = 0 ;
2008-04-15 23:41:47 +04:00
2008-03-24 22:01:59 +03:00
btrfs_set_key_type ( & cache - > key , BTRFS_BLOCK_GROUP_ITEM_KEY ) ;
memset ( & cache - > item , 0 , sizeof ( cache - > item ) ) ;
btrfs_set_block_group_used ( & cache - > item , bytes_used ) ;
btrfs_set_block_group_chunk_objectid ( & cache - > item , chunk_objectid ) ;
cache - > flags = type ;
btrfs_set_block_group_flags ( & cache - > item , type ) ;
ret = update_space_info ( root - > fs_info , cache - > flags , size , bytes_used ,
& cache - > space_info ) ;
BUG_ON ( ret ) ;
2008-04-04 23:40:00 +04:00
bit = block_group_state_bits ( type ) ;
2008-04-15 23:41:47 +04:00
set_extent_bits ( block_group_cache , chunk_offset ,
chunk_offset + size - 1 ,
2008-03-24 22:01:59 +03:00
bit | EXTENT_LOCKED , GFP_NOFS ) ;
2008-04-15 23:41:47 +04:00
set_state_private ( block_group_cache , chunk_offset ,
( unsigned long ) cache ) ;
2008-03-24 22:01:59 +03:00
ret = btrfs_insert_item ( trans , extent_root , & cache - > key , & cache - > item ,
sizeof ( cache - > item ) ) ;
BUG_ON ( ret ) ;
finish_current_insert ( trans , extent_root ) ;
ret = del_pending_extents ( trans , extent_root ) ;
BUG_ON ( ret ) ;
2008-04-04 23:40:00 +04:00
set_avail_alloc_bits ( extent_root - > fs_info , type ) ;
2008-03-24 22:01:59 +03:00
return 0 ;
}