2007-03-22 15:59:16 -04:00
# include <linux/module.h>
# include <linux/fs.h>
# include "ctree.h"
# include "disk-io.h"
# include "transaction.h"
2007-03-25 11:35:08 -04:00
static int total_trans = 0 ;
2007-04-02 10:50:19 -04:00
extern struct kmem_cache * btrfs_trans_handle_cachep ;
extern struct kmem_cache * btrfs_transaction_cachep ;
2007-04-09 10:42:37 -04:00
# define BTRFS_ROOT_TRANS_TAG 0
2007-04-02 10:50:19 -04:00
# define TRANS_MAGIC 0xE1E10E
2007-03-22 15:59:16 -04:00
static void put_transaction ( struct btrfs_transaction * transaction )
{
2007-04-02 10:50:19 -04:00
WARN_ON ( transaction - > use_count = = 0 ) ;
2007-03-22 15:59:16 -04:00
transaction - > use_count - - ;
2007-04-02 10:50:19 -04:00
WARN_ON ( transaction - > magic ! = TRANS_MAGIC ) ;
2007-03-25 11:35:08 -04:00
if ( transaction - > use_count = = 0 ) {
WARN_ON ( total_trans = = 0 ) ;
total_trans - - ;
2007-04-19 21:01:03 -04:00
list_del_init ( & transaction - > list ) ;
2007-04-02 10:50:19 -04:00
memset ( transaction , 0 , sizeof ( * transaction ) ) ;
kmem_cache_free ( btrfs_transaction_cachep , transaction ) ;
2007-03-25 11:35:08 -04:00
}
2007-03-22 15:59:16 -04:00
}
static int join_transaction ( struct btrfs_root * root )
{
struct btrfs_transaction * cur_trans ;
cur_trans = root - > fs_info - > running_transaction ;
if ( ! cur_trans ) {
2007-04-02 10:50:19 -04:00
cur_trans = kmem_cache_alloc ( btrfs_transaction_cachep ,
GFP_NOFS ) ;
2007-03-25 11:35:08 -04:00
total_trans + + ;
2007-03-22 15:59:16 -04:00
BUG_ON ( ! cur_trans ) ;
2007-04-09 10:42:37 -04:00
root - > fs_info - > generation + + ;
2007-03-22 15:59:16 -04:00
root - > fs_info - > running_transaction = cur_trans ;
cur_trans - > num_writers = 0 ;
2007-04-09 10:42:37 -04:00
cur_trans - > transid = root - > fs_info - > generation ;
2007-03-22 15:59:16 -04:00
init_waitqueue_head ( & cur_trans - > writer_wait ) ;
init_waitqueue_head ( & cur_trans - > commit_wait ) ;
2007-04-02 10:50:19 -04:00
cur_trans - > magic = TRANS_MAGIC ;
2007-03-22 15:59:16 -04:00
cur_trans - > in_commit = 0 ;
2007-03-23 10:01:08 -04:00
cur_trans - > use_count = 1 ;
2007-03-22 15:59:16 -04:00
cur_trans - > commit_done = 0 ;
2007-04-19 21:01:03 -04:00
list_add_tail ( & cur_trans - > list , & root - > fs_info - > trans_list ) ;
2007-03-22 15:59:16 -04:00
}
cur_trans - > num_writers + + ;
return 0 ;
}
struct btrfs_trans_handle * btrfs_start_transaction ( struct btrfs_root * root ,
int num_blocks )
{
2007-04-02 10:50:19 -04:00
struct btrfs_trans_handle * h =
kmem_cache_alloc ( btrfs_trans_handle_cachep , GFP_NOFS ) ;
2007-03-22 15:59:16 -04:00
int ret ;
2007-04-09 10:42:37 -04:00
u64 running_trans_id ;
2007-03-22 15:59:16 -04:00
mutex_lock ( & root - > fs_info - > trans_mutex ) ;
ret = join_transaction ( root ) ;
BUG_ON ( ret ) ;
2007-04-09 10:42:37 -04:00
running_trans_id = root - > fs_info - > running_transaction - > transid ;
if ( root ! = root - > fs_info - > tree_root & & root - > last_trans <
running_trans_id ) {
radix_tree_tag_set ( & root - > fs_info - > fs_roots_radix ,
2007-04-10 16:58:11 -04:00
( unsigned long ) root - > root_key . objectid ,
BTRFS_ROOT_TRANS_TAG ) ;
2007-04-09 10:42:37 -04:00
root - > commit_root = root - > node ;
get_bh ( root - > node ) ;
}
root - > last_trans = running_trans_id ;
h - > transid = running_trans_id ;
2007-03-22 15:59:16 -04:00
h - > transaction = root - > fs_info - > running_transaction ;
h - > blocks_reserved = num_blocks ;
h - > blocks_used = 0 ;
root - > fs_info - > running_transaction - > use_count + + ;
mutex_unlock ( & root - > fs_info - > trans_mutex ) ;
2007-04-02 10:50:19 -04:00
h - > magic = h - > magic2 = TRANS_MAGIC ;
2007-03-22 15:59:16 -04:00
return h ;
}
int btrfs_end_transaction ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root )
{
struct btrfs_transaction * cur_trans ;
2007-04-06 15:37:36 -04:00
2007-04-02 10:50:19 -04:00
WARN_ON ( trans - > magic ! = TRANS_MAGIC ) ;
WARN_ON ( trans - > magic2 ! = TRANS_MAGIC ) ;
2007-03-22 15:59:16 -04:00
mutex_lock ( & root - > fs_info - > trans_mutex ) ;
cur_trans = root - > fs_info - > running_transaction ;
2007-03-23 10:01:08 -04:00
WARN_ON ( cur_trans - > num_writers < 1 ) ;
2007-03-22 15:59:16 -04:00
if ( waitqueue_active ( & cur_trans - > writer_wait ) )
wake_up ( & cur_trans - > writer_wait ) ;
cur_trans - > num_writers - - ;
put_transaction ( cur_trans ) ;
mutex_unlock ( & root - > fs_info - > trans_mutex ) ;
2007-03-30 14:27:56 -04:00
memset ( trans , 0 , sizeof ( * trans ) ) ;
2007-04-02 10:50:19 -04:00
kmem_cache_free ( btrfs_trans_handle_cachep , trans ) ;
2007-03-22 15:59:16 -04:00
return 0 ;
}
int btrfs_write_and_wait_transaction ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root )
{
2007-04-02 14:53:59 -04:00
filemap_write_and_wait ( root - > fs_info - > btree_inode - > i_mapping ) ;
2007-03-22 15:59:16 -04:00
return 0 ;
}
int btrfs_commit_tree_roots ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root )
{
int ret ;
u64 old_extent_block ;
struct btrfs_fs_info * fs_info = root - > fs_info ;
struct btrfs_root * tree_root = fs_info - > tree_root ;
struct btrfs_root * extent_root = fs_info - > extent_root ;
2007-04-12 10:43:05 -04:00
struct btrfs_root * dev_root = fs_info - > dev_root ;
2007-03-22 15:59:16 -04:00
2007-04-12 10:43:05 -04:00
if ( btrfs_super_device_root ( fs_info - > disk_super ) ! =
bh_blocknr ( dev_root - > node ) ) {
btrfs_set_super_device_root ( fs_info - > disk_super ,
bh_blocknr ( dev_root - > node ) ) ;
}
2007-03-22 15:59:16 -04:00
while ( 1 ) {
old_extent_block = btrfs_root_blocknr ( & extent_root - > root_item ) ;
2007-04-11 15:53:25 -04:00
if ( old_extent_block = = bh_blocknr ( extent_root - > node ) )
2007-03-22 15:59:16 -04:00
break ;
btrfs_set_root_blocknr ( & extent_root - > root_item ,
2007-04-11 15:53:25 -04:00
bh_blocknr ( extent_root - > node ) ) ;
2007-03-22 15:59:16 -04:00
ret = btrfs_update_root ( trans , tree_root ,
& extent_root - > root_key ,
& extent_root - > root_item ) ;
BUG_ON ( ret ) ;
}
return 0 ;
}
static int wait_for_commit ( struct btrfs_root * root ,
struct btrfs_transaction * commit )
{
DEFINE_WAIT ( wait ) ;
while ( ! commit - > commit_done ) {
prepare_to_wait ( & commit - > commit_wait , & wait ,
TASK_UNINTERRUPTIBLE ) ;
if ( commit - > commit_done )
break ;
mutex_unlock ( & root - > fs_info - > trans_mutex ) ;
schedule ( ) ;
mutex_lock ( & root - > fs_info - > trans_mutex ) ;
}
finish_wait ( & commit - > commit_wait , & wait ) ;
return 0 ;
}
2007-04-09 10:42:37 -04:00
struct dirty_root {
struct list_head list ;
struct btrfs_key snap_key ;
struct buffer_head * commit_root ;
struct btrfs_root * root ;
} ;
int add_dirty_roots ( struct btrfs_trans_handle * trans ,
struct radix_tree_root * radix , struct list_head * list )
{
struct dirty_root * dirty ;
struct btrfs_root * gang [ 8 ] ;
struct btrfs_root * root ;
int i ;
int ret ;
int err ;
while ( 1 ) {
ret = radix_tree_gang_lookup_tag ( radix , ( void * * ) gang , 0 ,
ARRAY_SIZE ( gang ) ,
BTRFS_ROOT_TRANS_TAG ) ;
if ( ret = = 0 )
break ;
for ( i = 0 ; i < ret ; i + + ) {
root = gang [ i ] ;
2007-04-10 16:58:11 -04:00
radix_tree_tag_clear ( radix ,
( unsigned long ) root - > root_key . objectid ,
BTRFS_ROOT_TRANS_TAG ) ;
2007-04-09 10:42:37 -04:00
if ( root - > commit_root = = root - > node ) {
2007-04-11 15:53:25 -04:00
WARN_ON ( bh_blocknr ( root - > node ) ! =
2007-04-09 10:42:37 -04:00
btrfs_root_blocknr ( & root - > root_item ) ) ;
brelse ( root - > commit_root ) ;
root - > commit_root = NULL ;
continue ;
}
dirty = kmalloc ( sizeof ( * dirty ) , GFP_NOFS ) ;
BUG_ON ( ! dirty ) ;
memcpy ( & dirty - > snap_key , & root - > root_key ,
sizeof ( root - > root_key ) ) ;
dirty - > commit_root = root - > commit_root ;
root - > commit_root = NULL ;
dirty - > root = root ;
root - > root_key . offset = root - > fs_info - > generation ;
btrfs_set_root_blocknr ( & root - > root_item ,
2007-04-11 15:53:25 -04:00
bh_blocknr ( root - > node ) ) ;
2007-04-09 10:42:37 -04:00
err = btrfs_insert_root ( trans , root - > fs_info - > tree_root ,
& root - > root_key ,
& root - > root_item ) ;
BUG_ON ( err ) ;
list_add ( & dirty - > list , list ) ;
}
}
return 0 ;
}
int drop_dirty_roots ( struct btrfs_root * tree_root , struct list_head * list )
{
struct dirty_root * dirty ;
struct btrfs_trans_handle * trans ;
int ret ;
while ( ! list_empty ( list ) ) {
dirty = list_entry ( list - > next , struct dirty_root , list ) ;
list_del_init ( & dirty - > list ) ;
trans = btrfs_start_transaction ( tree_root , 1 ) ;
ret = btrfs_drop_snapshot ( trans , dirty - > root ,
dirty - > commit_root ) ;
BUG_ON ( ret ) ;
ret = btrfs_del_root ( trans , tree_root , & dirty - > snap_key ) ;
BUG_ON ( ret ) ;
ret = btrfs_end_transaction ( trans , tree_root ) ;
BUG_ON ( ret ) ;
kfree ( dirty ) ;
}
return 0 ;
}
2007-03-22 15:59:16 -04:00
int btrfs_commit_transaction ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root )
{
int ret = 0 ;
struct btrfs_transaction * cur_trans ;
2007-04-19 21:01:03 -04:00
struct btrfs_transaction * prev_trans = NULL ;
2007-04-09 10:42:37 -04:00
struct list_head dirty_fs_roots ;
2007-03-22 15:59:16 -04:00
DEFINE_WAIT ( wait ) ;
2007-04-09 10:42:37 -04:00
INIT_LIST_HEAD ( & dirty_fs_roots ) ;
2007-04-06 15:37:36 -04:00
2007-03-22 15:59:16 -04:00
mutex_lock ( & root - > fs_info - > trans_mutex ) ;
if ( trans - > transaction - > in_commit ) {
cur_trans = trans - > transaction ;
trans - > transaction - > use_count + + ;
btrfs_end_transaction ( trans , root ) ;
ret = wait_for_commit ( root , cur_trans ) ;
BUG_ON ( ret ) ;
put_transaction ( cur_trans ) ;
mutex_unlock ( & root - > fs_info - > trans_mutex ) ;
return 0 ;
}
2007-04-02 10:50:19 -04:00
cur_trans = trans - > transaction ;
trans - > transaction - > in_commit = 1 ;
2007-03-22 15:59:16 -04:00
while ( trans - > transaction - > num_writers > 1 ) {
2007-04-02 10:50:19 -04:00
WARN_ON ( cur_trans ! = trans - > transaction ) ;
2007-03-22 15:59:16 -04:00
prepare_to_wait ( & trans - > transaction - > writer_wait , & wait ,
TASK_UNINTERRUPTIBLE ) ;
if ( trans - > transaction - > num_writers < = 1 )
break ;
mutex_unlock ( & root - > fs_info - > trans_mutex ) ;
schedule ( ) ;
mutex_lock ( & root - > fs_info - > trans_mutex ) ;
2007-04-02 10:50:19 -04:00
finish_wait ( & trans - > transaction - > writer_wait , & wait ) ;
2007-03-22 15:59:16 -04:00
}
finish_wait ( & trans - > transaction - > writer_wait , & wait ) ;
2007-04-02 10:50:19 -04:00
WARN_ON ( cur_trans ! = trans - > transaction ) ;
2007-04-09 10:42:37 -04:00
add_dirty_roots ( trans , & root - > fs_info - > fs_roots_radix , & dirty_fs_roots ) ;
2007-03-22 15:59:16 -04:00
ret = btrfs_commit_tree_roots ( trans , root ) ;
BUG_ON ( ret ) ;
2007-03-25 11:35:08 -04:00
cur_trans = root - > fs_info - > running_transaction ;
root - > fs_info - > running_transaction = NULL ;
2007-04-19 21:01:03 -04:00
if ( cur_trans - > list . prev ! = & root - > fs_info - > trans_list ) {
prev_trans = list_entry ( cur_trans - > list . prev ,
struct btrfs_transaction , list ) ;
if ( prev_trans - > commit_done )
prev_trans = NULL ;
else
prev_trans - > use_count + + ;
}
2007-03-25 11:35:08 -04:00
mutex_unlock ( & root - > fs_info - > trans_mutex ) ;
2007-04-19 21:01:03 -04:00
mutex_unlock ( & root - > fs_info - > fs_mutex ) ;
2007-03-22 15:59:16 -04:00
ret = btrfs_write_and_wait_transaction ( trans , root ) ;
2007-04-19 21:01:03 -04:00
if ( prev_trans ) {
mutex_lock ( & root - > fs_info - > trans_mutex ) ;
wait_for_commit ( root , prev_trans ) ;
put_transaction ( prev_trans ) ;
mutex_unlock ( & root - > fs_info - > trans_mutex ) ;
}
btrfs_set_super_generation ( root - > fs_info - > disk_super ,
cur_trans - > transid ) ;
2007-03-22 15:59:16 -04:00
BUG_ON ( ret ) ;
write_ctree_super ( trans , root ) ;
2007-04-19 21:01:03 -04:00
mutex_lock ( & root - > fs_info - > fs_mutex ) ;
2007-03-25 11:35:08 -04:00
btrfs_finish_extent_commit ( trans , root ) ;
mutex_lock ( & root - > fs_info - > trans_mutex ) ;
2007-04-02 10:50:19 -04:00
cur_trans - > commit_done = 1 ;
wake_up ( & cur_trans - > commit_wait ) ;
2007-03-25 11:35:08 -04:00
put_transaction ( cur_trans ) ;
2007-03-22 15:59:16 -04:00
put_transaction ( cur_trans ) ;
2007-03-25 11:35:08 -04:00
mutex_unlock ( & root - > fs_info - > trans_mutex ) ;
2007-04-02 10:50:19 -04:00
kmem_cache_free ( btrfs_trans_handle_cachep , trans ) ;
2007-03-22 15:59:16 -04:00
2007-04-09 10:42:37 -04:00
drop_dirty_roots ( root - > fs_info - > tree_root , & dirty_fs_roots ) ;
2007-03-22 15:59:16 -04:00
return ret ;
}