2007-08-08 00:15:09 +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 .
*/
# include <linux/sched.h>
# include "ctree.h"
# include "disk-io.h"
# include "print-tree.h"
# include "transaction.h"
2008-06-26 00:01:31 +04:00
# include "locking.h"
2007-08-08 00:15:09 +04:00
2009-01-06 05:25:51 +03:00
/* defrag all the leaves in a given btree. If cache_only == 1, don't read
* things from disk , otherwise read all the leaves and try to get key order to
2008-09-29 23:18:18 +04:00
* better reflect disk order
*/
2009-01-06 05:25:51 +03:00
2007-08-08 00:15:09 +04:00
int btrfs_defrag_leaves ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root , int cache_only )
{
struct btrfs_path * path = NULL ;
2008-06-26 00:01:31 +04:00
struct btrfs_key key ;
2007-08-08 00:15:09 +04:00
int ret = 0 ;
int wret ;
int level ;
int orig_level ;
2007-08-10 22:06:19 +04:00
int is_extent = 0 ;
2008-06-26 00:01:31 +04:00
int next_key_ret = 0 ;
2007-08-10 22:06:19 +04:00
u64 last_ret = 0 ;
2008-06-26 00:01:31 +04:00
u64 min_trans = 0 ;
if ( cache_only )
goto out ;
2007-08-10 22:06:19 +04:00
2008-06-26 00:01:31 +04:00
if ( root - > fs_info - > extent_root = = root ) {
2008-06-26 00:01:31 +04:00
/*
* there ' s recursion here right now in the tree locking ,
* we can ' t defrag the extent root without deadlock
*/
goto out ;
2008-06-26 00:01:31 +04:00
}
2008-06-26 00:01:30 +04:00
2007-08-10 22:06:19 +04:00
if ( root - > ref_cows = = 0 & & ! is_extent )
2007-08-08 00:15:09 +04:00
goto out ;
2007-10-16 00:14:19 +04:00
2008-02-15 21:19:35 +03:00
if ( btrfs_test_opt ( root , SSD ) )
goto out ;
2007-08-08 00:15:09 +04:00
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
2007-10-16 00:14:19 +04:00
level = btrfs_header_level ( root - > node ) ;
2007-08-08 00:15:09 +04:00
orig_level = level ;
2007-10-16 00:19:46 +04:00
2009-01-06 05:25:51 +03:00
if ( level = = 0 )
2007-08-08 00:15:09 +04:00
goto out ;
2009-01-06 05:25:51 +03:00
2007-08-08 00:15:09 +04:00
if ( root - > defrag_progress . objectid = = 0 ) {
2008-06-26 00:01:31 +04:00
struct extent_buffer * root_node ;
2008-05-24 22:04:53 +04:00
u32 nritems ;
2008-06-26 00:01:31 +04:00
root_node = btrfs_lock_root_node ( root ) ;
nritems = btrfs_header_nritems ( root_node ) ;
2008-05-24 22:04:53 +04:00
root - > defrag_max . objectid = 0 ;
/* from above we know this is not a leaf */
2008-06-26 00:01:31 +04:00
btrfs_node_key_to_cpu ( root_node , & root - > defrag_max ,
2008-05-24 22:04:53 +04:00
nritems - 1 ) ;
2008-06-26 00:01:31 +04:00
btrfs_tree_unlock ( root_node ) ;
free_extent_buffer ( root_node ) ;
memset ( & key , 0 , sizeof ( key ) ) ;
2007-08-08 00:15:09 +04:00
} else {
2008-06-26 00:01:31 +04:00
memcpy ( & key , & root - > defrag_progress , sizeof ( key ) ) ;
2007-08-08 00:15:09 +04:00
}
2008-06-26 00:01:31 +04:00
path - > keep_locks = 1 ;
2008-06-26 00:01:31 +04:00
if ( cache_only )
min_trans = root - > defrag_trans_start ;
2008-09-06 00:13:11 +04:00
ret = btrfs_search_forward ( root , & key , NULL , path ,
cache_only , min_trans ) ;
2008-06-26 00:01:31 +04:00
if ( ret < 0 )
goto out ;
if ( ret > 0 ) {
ret = 0 ;
goto out ;
}
btrfs_release_path ( root , path ) ;
2008-06-26 00:01:31 +04:00
wret = btrfs_search_slot ( trans , root , & key , path , 0 , 1 ) ;
2007-08-08 00:15:09 +04:00
2008-06-26 00:01:31 +04:00
if ( wret < 0 ) {
ret = wret ;
goto out ;
}
if ( ! path - > nodes [ 1 ] ) {
ret = 0 ;
goto out ;
}
path - > slots [ 1 ] = btrfs_header_nritems ( path - > nodes [ 1 ] ) ;
2008-06-26 00:01:31 +04:00
next_key_ret = btrfs_find_next_key ( root , path , & key , 1 , cache_only ,
min_trans ) ;
2008-06-26 00:01:31 +04:00
ret = btrfs_realloc_node ( trans , root ,
path - > nodes [ 1 ] , 0 ,
cache_only , & last_ret ,
& root - > defrag_progress ) ;
WARN_ON ( ret & & ret ! = - EAGAIN ) ;
if ( next_key_ret = = 0 ) {
memcpy ( & root - > defrag_progress , & key , sizeof ( key ) ) ;
ret = - EAGAIN ;
2007-08-08 00:15:09 +04:00
}
2008-06-26 00:01:31 +04:00
2008-06-26 00:01:31 +04:00
btrfs_release_path ( root , path ) ;
2008-06-26 00:01:31 +04:00
if ( is_extent )
btrfs_extent_post_op ( trans , root ) ;
2007-08-08 00:15:09 +04:00
out :
if ( path )
btrfs_free_path ( path ) ;
2008-05-24 22:04:53 +04:00
if ( ret = = - EAGAIN ) {
if ( root - > defrag_max . objectid > root - > defrag_progress . objectid )
goto done ;
if ( root - > defrag_max . type > root - > defrag_progress . type )
goto done ;
if ( root - > defrag_max . offset > root - > defrag_progress . offset )
goto done ;
ret = 0 ;
}
done :
2007-08-08 00:15:09 +04:00
if ( ret ! = - EAGAIN ) {
memset ( & root - > defrag_progress , 0 ,
sizeof ( root - > defrag_progress ) ) ;
2008-06-26 00:01:31 +04:00
root - > defrag_trans_start = trans - > transid ;
2007-08-08 00:15:09 +04:00
}
return ret ;
}