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-03-22 19:13:20 +03:00
# include <linux/fs.h>
2007-03-28 21:57:48 +04:00
# include <linux/blkdev.h>
2007-06-22 22:16:24 +04:00
# include <linux/crc32c.h>
2007-03-29 03:44:27 +04:00
# include <linux/scatterlist.h>
2007-03-30 16:47:31 +04:00
# include <linux/swap.h>
2007-04-09 18:42:37 +04:00
# include <linux/radix-tree.h>
2007-05-02 23:53:43 +04:00
# include <linux/writeback.h>
2007-10-16 00:14:19 +04:00
# include <linux/buffer_head.h> // for block_sync_page
2007-02-02 17:18:22 +03:00
# include "ctree.h"
# include "disk-io.h"
2007-03-16 23:20:31 +03:00
# include "transaction.h"
2007-04-09 18:42:37 +04:00
# include "btrfs_inode.h"
2008-03-24 22:01:56 +03:00
# include "volumes.h"
2007-10-16 00:15:53 +04:00
# include "print-tree.h"
2007-02-02 17:18:22 +03:00
2007-10-16 00:14:19 +04:00
#if 0
static int check_tree_block ( struct btrfs_root * root , struct extent_buffer * buf )
2007-04-11 23:53:25 +04:00
{
2007-10-16 00:14:19 +04:00
if ( extent_buffer_blocknr ( buf ) ! = btrfs_header_blocknr ( buf ) ) {
printk ( KERN_CRIT " buf blocknr(buf) is %llu, header is %llu \n " ,
( unsigned long long ) extent_buffer_blocknr ( buf ) ,
( unsigned long long ) btrfs_header_blocknr ( buf ) ) ;
2007-06-12 14:35:45 +04:00
return 1 ;
2007-03-28 21:57:48 +04:00
}
2007-02-23 16:38:36 +03:00
return 0 ;
2007-02-02 17:18:22 +03:00
}
2007-10-16 00:14:19 +04:00
# endif
2007-02-02 17:18:22 +03:00
2008-01-25 00:13:08 +03:00
static struct extent_io_ops btree_extent_io_ops ;
2007-11-08 05:08:01 +03:00
2007-10-16 00:14:19 +04:00
struct extent_map * btree_get_extent ( struct inode * inode , struct page * page ,
2008-01-25 00:13:08 +03:00
size_t page_offset , u64 start , u64 len ,
2007-10-16 00:14:19 +04:00
int create )
2007-04-11 23:53:25 +04:00
{
2007-10-16 00:14:19 +04:00
struct extent_map_tree * em_tree = & BTRFS_I ( inode ) - > extent_tree ;
struct extent_map * em ;
int ret ;
again :
2008-01-25 00:13:08 +03:00
spin_lock ( & em_tree - > lock ) ;
em = lookup_extent_mapping ( em_tree , start , len ) ;
spin_unlock ( & em_tree - > lock ) ;
2007-10-16 00:14:19 +04:00
if ( em ) {
goto out ;
2007-04-11 23:53:25 +04:00
}
2007-10-16 00:14:19 +04:00
em = alloc_extent_map ( GFP_NOFS ) ;
if ( ! em ) {
em = ERR_PTR ( - ENOMEM ) ;
goto out ;
}
em - > start = 0 ;
2008-01-25 00:13:08 +03:00
em - > len = i_size_read ( inode ) ;
2007-10-16 00:14:19 +04:00
em - > block_start = 0 ;
em - > bdev = inode - > i_sb - > s_bdev ;
2008-01-25 00:13:08 +03:00
spin_lock ( & em_tree - > lock ) ;
2007-10-16 00:14:19 +04:00
ret = add_extent_mapping ( em_tree , em ) ;
2008-01-25 00:13:08 +03:00
spin_unlock ( & em_tree - > lock ) ;
2007-10-16 00:14:19 +04:00
if ( ret = = - EEXIST ) {
free_extent_map ( em ) ;
em = NULL ;
goto again ;
} else if ( ret ) {
em = ERR_PTR ( ret ) ;
}
out :
return em ;
2007-04-11 23:53:25 +04:00
}
2007-10-16 00:19:22 +04:00
u32 btrfs_csum_data ( struct btrfs_root * root , char * data , u32 seed , size_t len )
{
return crc32c ( seed , data , len ) ;
}
void btrfs_csum_final ( u32 crc , char * result )
{
* ( __le32 * ) result = ~ cpu_to_le32 ( crc ) ;
}
static int csum_tree_block ( struct btrfs_root * root , struct extent_buffer * buf ,
int verify )
{
char result [ BTRFS_CRC32_SIZE ] ;
unsigned long len ;
unsigned long cur_len ;
unsigned long offset = BTRFS_CSUM_SIZE ;
char * map_token = NULL ;
char * kaddr ;
unsigned long map_start ;
unsigned long map_len ;
int err ;
u32 crc = ~ ( u32 ) 0 ;
len = buf - > len - offset ;
while ( len > 0 ) {
err = map_private_extent_buffer ( buf , offset , 32 ,
& map_token , & kaddr ,
& map_start , & map_len , KM_USER0 ) ;
if ( err ) {
printk ( " failed to map extent buffer! %lu \n " ,
offset ) ;
return 1 ;
}
cur_len = min ( len , map_len - ( offset - map_start ) ) ;
crc = btrfs_csum_data ( root , kaddr + offset - map_start ,
crc , cur_len ) ;
len - = cur_len ;
offset + = cur_len ;
unmap_extent_buffer ( buf , map_token , KM_USER0 ) ;
}
btrfs_csum_final ( crc , result ) ;
if ( verify ) {
2008-01-08 23:46:27 +03:00
int from_this_trans = 0 ;
if ( root - > fs_info - > running_transaction & &
btrfs_header_generation ( buf ) = =
root - > fs_info - > running_transaction - > transid )
from_this_trans = 1 ;
/* FIXME, this is not good */
2008-04-01 19:21:32 +04:00
if ( memcmp_extent_buffer ( buf , result , 0 , BTRFS_CRC32_SIZE ) ) {
2008-01-08 23:46:27 +03:00
u32 val ;
u32 found = 0 ;
memcpy ( & found , result , BTRFS_CRC32_SIZE ) ;
read_extent_buffer ( buf , & val , 0 , BTRFS_CRC32_SIZE ) ;
2008-04-01 19:21:32 +04:00
WARN_ON ( 1 ) ;
2008-01-08 23:46:27 +03:00
printk ( " btrfs: %s checksum verify failed on %llu "
2008-04-01 19:21:32 +04:00
" wanted %X found %X from_this_trans %d "
" level %d \n " ,
2007-10-16 00:19:22 +04:00
root - > fs_info - > sb - > s_id ,
2008-04-01 19:21:32 +04:00
buf - > start , val , found , from_this_trans ,
btrfs_header_level ( buf ) ) ;
2007-10-16 00:19:22 +04:00
return 1 ;
}
} else {
write_extent_buffer ( buf , result , 0 , BTRFS_CRC32_SIZE ) ;
}
return 0 ;
}
int csum_dirty_buffer ( struct btrfs_root * root , struct page * page )
{
2008-01-25 00:13:08 +03:00
struct extent_io_tree * tree ;
2007-10-30 23:56:53 +03:00
u64 start = ( u64 ) page - > index < < PAGE_CACHE_SHIFT ;
2007-10-16 00:19:22 +04:00
u64 found_start ;
int found_level ;
unsigned long len ;
struct extent_buffer * eb ;
2008-01-25 00:13:08 +03:00
tree = & BTRFS_I ( page - > mapping - > host ) - > io_tree ;
2007-10-16 00:19:22 +04:00
if ( page - > private = = EXTENT_PAGE_PRIVATE )
goto out ;
if ( ! page - > private )
goto out ;
len = page - > private > > 2 ;
if ( len = = 0 ) {
WARN_ON ( 1 ) ;
}
eb = alloc_extent_buffer ( tree , start , len , page , GFP_NOFS ) ;
2008-02-07 18:50:54 +03:00
read_extent_buffer_pages ( tree , eb , start + PAGE_CACHE_SIZE , 1 ,
btree_get_extent ) ;
2008-01-18 18:54:22 +03:00
btrfs_clear_buffer_defrag ( eb ) ;
2007-10-16 00:19:22 +04:00
found_start = btrfs_header_bytenr ( eb ) ;
if ( found_start ! = start ) {
printk ( " warning: eb start incorrect %Lu buffer %Lu len %lu \n " ,
start , found_start , len ) ;
2008-01-09 23:55:33 +03:00
WARN_ON ( 1 ) ;
goto err ;
}
if ( eb - > first_page ! = page ) {
printk ( " bad first page %lu %lu \n " , eb - > first_page - > index ,
page - > index ) ;
WARN_ON ( 1 ) ;
goto err ;
}
if ( ! PageUptodate ( page ) ) {
printk ( " csum not up to date page %lu \n " , page - > index ) ;
WARN_ON ( 1 ) ;
goto err ;
2007-10-16 00:19:22 +04:00
}
found_level = btrfs_header_level ( eb ) ;
2008-04-01 19:21:32 +04:00
spin_lock ( & root - > fs_info - > hash_lock ) ;
btrfs_set_header_flag ( eb , BTRFS_HEADER_FLAG_WRITTEN ) ;
spin_unlock ( & root - > fs_info - > hash_lock ) ;
2007-10-16 00:19:22 +04:00
csum_tree_block ( root , eb , 0 ) ;
2008-01-09 23:55:33 +03:00
err :
2007-10-16 00:19:22 +04:00
free_extent_buffer ( eb ) ;
out :
return 0 ;
}
2007-11-08 05:08:01 +03:00
static int btree_writepage_io_hook ( struct page * page , u64 start , u64 end )
2007-03-28 21:57:48 +04:00
{
2007-10-16 00:19:22 +04:00
struct btrfs_root * root = BTRFS_I ( page - > mapping - > host ) - > root ;
csum_dirty_buffer ( root , page ) ;
2007-11-08 05:08:01 +03:00
return 0 ;
}
2008-03-24 22:01:56 +03:00
static int btree_submit_bio_hook ( struct inode * inode , int rw , struct bio * bio )
{
struct btrfs_root * root = BTRFS_I ( inode ) - > root ;
u64 offset ;
offset = bio - > bi_sector < < 9 ;
if ( offset = = BTRFS_SUPER_INFO_OFFSET ) {
bio - > bi_bdev = root - > fs_info - > sb - > s_bdev ;
submit_bio ( rw , bio ) ;
return 0 ;
}
return btrfs_map_bio ( BTRFS_I ( inode ) - > root , rw , bio ) ;
}
2007-11-08 05:08:01 +03:00
static int btree_writepage ( struct page * page , struct writeback_control * wbc )
{
2008-01-25 00:13:08 +03:00
struct extent_io_tree * tree ;
tree = & BTRFS_I ( page - > mapping - > host ) - > io_tree ;
2007-10-16 00:14:19 +04:00
return extent_write_full_page ( tree , page , btree_get_extent , wbc ) ;
}
2007-11-08 05:08:01 +03:00
static int btree_writepages ( struct address_space * mapping ,
struct writeback_control * wbc )
{
2008-01-25 00:13:08 +03:00
struct extent_io_tree * tree ;
tree = & BTRFS_I ( mapping - > host ) - > io_tree ;
2007-12-11 20:42:00 +03:00
if ( wbc - > sync_mode = = WB_SYNC_NONE ) {
2007-11-27 03:34:41 +03:00
u64 num_dirty ;
u64 start = 0 ;
unsigned long thresh = 96 * 1024 * 1024 ;
2007-11-27 18:52:01 +03:00
if ( wbc - > for_kupdate )
return 0 ;
2007-11-27 19:16:35 +03:00
if ( current_is_pdflush ( ) ) {
thresh = 96 * 1024 * 1024 ;
} else {
thresh = 8 * 1024 * 1024 ;
}
2007-12-22 00:27:21 +03:00
num_dirty = count_range_bits ( tree , & start , ( u64 ) - 1 ,
thresh , EXTENT_DIRTY ) ;
2007-11-27 03:34:41 +03:00
if ( num_dirty < thresh ) {
return 0 ;
}
}
2007-11-08 05:08:01 +03:00
return extent_writepages ( tree , mapping , btree_get_extent , wbc ) ;
}
2007-10-16 00:14:19 +04:00
int btree_readpage ( struct file * file , struct page * page )
{
2008-01-25 00:13:08 +03:00
struct extent_io_tree * tree ;
tree = & BTRFS_I ( page - > mapping - > host ) - > io_tree ;
2007-10-16 00:14:19 +04:00
return extent_read_full_page ( tree , page , btree_get_extent ) ;
}
2007-03-30 16:47:31 +04:00
2008-01-29 17:59:12 +03:00
static int btree_releasepage ( struct page * page , gfp_t gfp_flags )
2007-10-16 00:14:19 +04:00
{
2008-01-25 00:13:08 +03:00
struct extent_io_tree * tree ;
struct extent_map_tree * map ;
2007-10-16 00:14:19 +04:00
int ret ;
2007-03-28 21:57:48 +04:00
2008-01-25 00:13:08 +03:00
tree = & BTRFS_I ( page - > mapping - > host ) - > io_tree ;
map = & BTRFS_I ( page - > mapping - > host ) - > extent_tree ;
2008-01-29 17:59:12 +03:00
ret = try_release_extent_mapping ( map , tree , page , gfp_flags ) ;
2007-10-16 00:14:19 +04:00
if ( ret = = 1 ) {
ClearPagePrivate ( page ) ;
set_page_private ( page , 0 ) ;
page_cache_release ( page ) ;
}
2007-03-28 21:57:48 +04:00
return ret ;
}
2007-10-16 00:14:19 +04:00
static void btree_invalidatepage ( struct page * page , unsigned long offset )
2007-03-28 21:57:48 +04:00
{
2008-01-25 00:13:08 +03:00
struct extent_io_tree * tree ;
tree = & BTRFS_I ( page - > mapping - > host ) - > io_tree ;
2007-10-16 00:14:19 +04:00
extent_invalidatepage ( tree , page , offset ) ;
btree_releasepage ( page , GFP_NOFS ) ;
2007-03-28 21:57:48 +04:00
}
2007-10-16 00:14:19 +04:00
#if 0
2007-03-28 21:57:48 +04:00
static int btree_writepage ( struct page * page , struct writeback_control * wbc )
2007-03-02 02:59:40 +03:00
{
2007-03-29 03:44:27 +04:00
struct buffer_head * bh ;
2007-04-09 18:42:37 +04:00
struct btrfs_root * root = BTRFS_I ( page - > mapping - > host ) - > root ;
2007-03-29 03:44:27 +04:00
struct buffer_head * head ;
if ( ! page_has_buffers ( page ) ) {
create_empty_buffers ( page , root - > fs_info - > sb - > s_blocksize ,
( 1 < < BH_Dirty ) | ( 1 < < BH_Uptodate ) ) ;
}
head = page_buffers ( page ) ;
bh = head ;
do {
if ( buffer_dirty ( bh ) )
csum_tree_block ( root , bh , 0 ) ;
bh = bh - > b_this_page ;
} while ( bh ! = head ) ;
2007-03-28 21:57:48 +04:00
return block_write_full_page ( page , btree_get_block , wbc ) ;
2007-03-02 02:59:40 +03:00
}
2007-10-16 00:14:19 +04:00
# endif
2007-02-02 17:18:22 +03:00
2007-03-28 21:57:48 +04:00
static struct address_space_operations btree_aops = {
. readpage = btree_readpage ,
. writepage = btree_writepage ,
2007-11-08 05:08:01 +03:00
. writepages = btree_writepages ,
2007-10-16 00:14:19 +04:00
. releasepage = btree_releasepage ,
. invalidatepage = btree_invalidatepage ,
2007-03-28 21:57:48 +04:00
. sync_page = block_sync_page ,
} ;
2007-10-16 00:15:53 +04:00
int readahead_tree_block ( struct btrfs_root * root , u64 bytenr , u32 blocksize )
2007-05-01 16:53:32 +04:00
{
2007-10-16 00:14:19 +04:00
struct extent_buffer * buf = NULL ;
struct inode * btree_inode = root - > fs_info - > btree_inode ;
2007-05-18 21:28:27 +04:00
int ret = 0 ;
2007-05-01 16:53:32 +04:00
2007-10-16 00:15:53 +04:00
buf = btrfs_find_create_tree_block ( root , bytenr , blocksize ) ;
2007-10-16 00:14:19 +04:00
if ( ! buf )
2007-05-01 16:53:32 +04:00
return 0 ;
2008-01-25 00:13:08 +03:00
read_extent_buffer_pages ( & BTRFS_I ( btree_inode ) - > io_tree ,
2008-02-07 18:50:54 +03:00
buf , 0 , 0 , btree_get_extent ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( buf ) ;
2007-05-18 21:28:27 +04:00
return ret ;
2007-05-01 16:53:32 +04:00
}
2008-03-24 22:01:56 +03:00
static int close_all_devices ( struct btrfs_fs_info * fs_info )
{
struct list_head * list ;
struct list_head * next ;
struct btrfs_device * device ;
2008-03-24 22:02:07 +03:00
list = & fs_info - > fs_devices - > devices ;
list_for_each ( next , list ) {
2008-03-24 22:01:56 +03:00
device = list_entry ( next , struct btrfs_device , dev_list ) ;
2008-03-24 22:02:07 +03:00
if ( device - > bdev & & device - > bdev ! = fs_info - > sb - > s_bdev )
close_bdev_excl ( device - > bdev ) ;
device - > bdev = NULL ;
2008-03-24 22:01:56 +03:00
}
return 0 ;
}
2008-04-01 21:48:14 +04:00
int btrfs_verify_block_csum ( struct btrfs_root * root ,
struct extent_buffer * buf )
2007-02-02 17:18:22 +03:00
{
2008-01-25 00:13:08 +03:00
struct extent_io_tree * io_tree ;
2008-01-08 23:46:27 +03:00
u64 end ;
2007-10-16 00:19:22 +04:00
int ret ;
2008-04-01 21:48:14 +04:00
io_tree = & BTRFS_I ( root - > fs_info - > btree_inode ) - > io_tree ;
2008-01-08 23:46:27 +03:00
if ( buf - > flags & EXTENT_CSUM )
2008-04-01 21:48:14 +04:00
return 0 ;
2008-01-08 23:46:27 +03:00
2008-04-01 21:48:14 +04:00
end = min_t ( u64 , buf - > len , PAGE_CACHE_SIZE ) ;
end = buf - > start + end - 1 ;
2008-01-25 00:13:08 +03:00
if ( test_range_bit ( io_tree , buf - > start , end , EXTENT_CSUM , 1 ) ) {
2007-10-16 00:19:22 +04:00
buf - > flags | = EXTENT_CSUM ;
2008-04-01 21:48:14 +04:00
return 0 ;
2007-10-16 00:19:22 +04:00
}
2008-01-08 23:46:27 +03:00
2008-01-25 00:13:08 +03:00
lock_extent ( io_tree , buf - > start , end , GFP_NOFS ) ;
2008-01-08 23:46:27 +03:00
2008-01-25 00:13:08 +03:00
if ( test_range_bit ( io_tree , buf - > start , end , EXTENT_CSUM , 1 ) ) {
2008-01-08 23:46:27 +03:00
buf - > flags | = EXTENT_CSUM ;
2008-04-01 21:48:14 +04:00
ret = 0 ;
2008-01-08 23:46:27 +03:00
goto out_unlock ;
}
2007-10-16 00:19:22 +04:00
ret = csum_tree_block ( root , buf , 1 ) ;
2008-01-25 00:13:08 +03:00
set_extent_bits ( io_tree , buf - > start , end , EXTENT_CSUM , GFP_NOFS ) ;
2007-10-16 00:19:22 +04:00
buf - > flags | = EXTENT_CSUM ;
2008-01-08 23:46:27 +03:00
out_unlock :
2008-01-25 00:13:08 +03:00
unlock_extent ( io_tree , buf - > start , end , GFP_NOFS ) ;
2008-04-01 21:48:14 +04:00
return ret ;
}
struct extent_buffer * btrfs_find_tree_block ( struct btrfs_root * root ,
u64 bytenr , u32 blocksize )
{
struct inode * btree_inode = root - > fs_info - > btree_inode ;
struct extent_buffer * eb ;
eb = find_extent_buffer ( & BTRFS_I ( btree_inode ) - > io_tree ,
bytenr , blocksize , GFP_NOFS ) ;
return eb ;
}
struct extent_buffer * btrfs_find_create_tree_block ( struct btrfs_root * root ,
u64 bytenr , u32 blocksize )
{
struct inode * btree_inode = root - > fs_info - > btree_inode ;
struct extent_buffer * eb ;
eb = alloc_extent_buffer ( & BTRFS_I ( btree_inode ) - > io_tree ,
bytenr , blocksize , NULL , GFP_NOFS ) ;
return eb ;
}
struct extent_buffer * read_tree_block ( struct btrfs_root * root , u64 bytenr ,
u32 blocksize )
{
struct extent_buffer * buf = NULL ;
struct inode * btree_inode = root - > fs_info - > btree_inode ;
struct extent_io_tree * io_tree ;
int ret ;
io_tree = & BTRFS_I ( btree_inode ) - > io_tree ;
buf = btrfs_find_create_tree_block ( root , bytenr , blocksize ) ;
if ( ! buf )
return NULL ;
read_extent_buffer_pages ( & BTRFS_I ( btree_inode ) - > io_tree , buf , 0 , 1 ,
btree_get_extent ) ;
ret = btrfs_verify_block_csum ( root , buf ) ;
2007-10-16 00:14:19 +04:00
return buf ;
2007-02-02 17:18:22 +03:00
}
2007-03-16 23:20:31 +03:00
int clean_tree_block ( struct btrfs_trans_handle * trans , struct btrfs_root * root ,
2007-10-16 00:14:19 +04:00
struct extent_buffer * buf )
2007-03-02 02:59:40 +03:00
{
2007-10-16 00:14:19 +04:00
struct inode * btree_inode = root - > fs_info - > btree_inode ;
2008-01-09 23:55:33 +03:00
if ( btrfs_header_generation ( buf ) = =
root - > fs_info - > running_transaction - > transid )
2008-01-25 00:13:08 +03:00
clear_extent_buffer_dirty ( & BTRFS_I ( btree_inode ) - > io_tree ,
2008-01-09 23:55:33 +03:00
buf ) ;
2007-10-16 00:14:19 +04:00
return 0 ;
}
int wait_on_tree_block_writeback ( struct btrfs_root * root ,
struct extent_buffer * buf )
{
struct inode * btree_inode = root - > fs_info - > btree_inode ;
2008-01-25 00:13:08 +03:00
wait_on_extent_buffer_writeback ( & BTRFS_I ( btree_inode ) - > io_tree ,
2007-10-16 00:14:19 +04:00
buf ) ;
return 0 ;
}
2007-10-16 00:15:53 +04:00
static int __setup_root ( u32 nodesize , u32 leafsize , u32 sectorsize ,
2007-11-30 19:30:34 +03:00
u32 stripesize , struct btrfs_root * root ,
2007-03-20 21:38:32 +03:00
struct btrfs_fs_info * fs_info ,
2007-03-22 19:13:20 +03:00
u64 objectid )
2007-02-21 00:40:44 +03:00
{
2007-02-22 01:04:57 +03:00
root - > node = NULL ;
2007-04-09 18:42:37 +04:00
root - > inode = NULL ;
2007-03-07 04:08:01 +03:00
root - > commit_root = NULL ;
2007-10-16 00:15:53 +04:00
root - > sectorsize = sectorsize ;
root - > nodesize = nodesize ;
root - > leafsize = leafsize ;
2007-11-30 19:30:34 +03:00
root - > stripesize = stripesize ;
2007-03-14 21:14:43 +03:00
root - > ref_cows = 0 ;
2008-03-24 22:01:56 +03:00
root - > track_dirty = 0 ;
2007-03-20 21:38:32 +03:00
root - > fs_info = fs_info ;
2007-04-09 18:42:37 +04:00
root - > objectid = objectid ;
root - > last_trans = 0 ;
2007-04-10 20:13:09 +04:00
root - > highest_inode = 0 ;
root - > last_inode_alloc = 0 ;
2007-08-29 23:47:34 +04:00
root - > name = NULL ;
2008-01-03 17:08:48 +03:00
root - > in_sysfs = 0 ;
2008-03-24 22:01:56 +03:00
INIT_LIST_HEAD ( & root - > dirty_list ) ;
2007-03-13 23:47:54 +03:00
memset ( & root - > root_key , 0 , sizeof ( root - > root_key ) ) ;
memset ( & root - > root_item , 0 , sizeof ( root - > root_item ) ) ;
2007-08-08 00:15:09 +04:00
memset ( & root - > defrag_progress , 0 , sizeof ( root - > defrag_progress ) ) ;
2007-08-29 23:47:34 +04:00
memset ( & root - > root_kobj , 0 , sizeof ( root - > root_kobj ) ) ;
init_completion ( & root - > kobj_unregister ) ;
2007-08-08 00:15:09 +04:00
root - > defrag_running = 0 ;
root - > defrag_level = 0 ;
2007-04-21 04:23:12 +04:00
root - > root_key . objectid = objectid ;
2007-03-13 23:47:54 +03:00
return 0 ;
}
2007-10-16 00:15:53 +04:00
static int find_and_setup_root ( struct btrfs_root * tree_root ,
2007-03-20 21:38:32 +03:00
struct btrfs_fs_info * fs_info ,
u64 objectid ,
2007-03-22 19:13:20 +03:00
struct btrfs_root * root )
2007-03-13 23:47:54 +03:00
{
int ret ;
2007-10-16 00:15:53 +04:00
u32 blocksize ;
2007-03-13 23:47:54 +03:00
2007-10-16 00:15:53 +04:00
__setup_root ( tree_root - > nodesize , tree_root - > leafsize ,
2007-11-30 19:30:34 +03:00
tree_root - > sectorsize , tree_root - > stripesize ,
root , fs_info , objectid ) ;
2007-03-13 23:47:54 +03:00
ret = btrfs_find_last_root ( tree_root , objectid ,
& root - > root_item , & root - > root_key ) ;
BUG_ON ( ret ) ;
2007-10-16 00:15:53 +04:00
blocksize = btrfs_level_size ( root , btrfs_root_level ( & root - > root_item ) ) ;
root - > node = read_tree_block ( root , btrfs_root_bytenr ( & root - > root_item ) ,
blocksize ) ;
2007-03-13 23:47:54 +03:00
BUG_ON ( ! root - > node ) ;
2007-02-21 00:40:44 +03:00
return 0 ;
}
2007-06-22 22:16:25 +04:00
struct btrfs_root * btrfs_read_fs_root_no_radix ( struct btrfs_fs_info * fs_info ,
struct btrfs_key * location )
2007-04-09 18:42:37 +04:00
{
struct btrfs_root * root ;
struct btrfs_root * tree_root = fs_info - > tree_root ;
struct btrfs_path * path ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * l ;
2007-04-10 20:13:09 +04:00
u64 highest_inode ;
2007-10-16 00:15:53 +04:00
u32 blocksize ;
2007-04-09 18:42:37 +04:00
int ret = 0 ;
2007-06-22 22:16:25 +04:00
root = kzalloc ( sizeof ( * root ) , GFP_NOFS ) ;
2007-06-09 17:22:25 +04:00
if ( ! root )
2007-04-09 18:42:37 +04:00
return ERR_PTR ( - ENOMEM ) ;
if ( location - > offset = = ( u64 ) - 1 ) {
2007-10-16 00:15:53 +04:00
ret = find_and_setup_root ( tree_root , fs_info ,
2007-04-09 18:42:37 +04:00
location - > objectid , root ) ;
if ( ret ) {
kfree ( root ) ;
return ERR_PTR ( ret ) ;
}
goto insert ;
}
2007-10-16 00:15:53 +04:00
__setup_root ( tree_root - > nodesize , tree_root - > leafsize ,
2007-11-30 19:30:34 +03:00
tree_root - > sectorsize , tree_root - > stripesize ,
root , fs_info , location - > objectid ) ;
2007-04-09 18:42:37 +04:00
path = btrfs_alloc_path ( ) ;
BUG_ON ( ! path ) ;
ret = btrfs_search_slot ( NULL , tree_root , location , path , 0 , 0 ) ;
if ( ret ! = 0 ) {
if ( ret > 0 )
ret = - ENOENT ;
goto out ;
}
2007-10-16 00:14:19 +04:00
l = path - > nodes [ 0 ] ;
read_extent_buffer ( l , & root - > root_item ,
btrfs_item_ptr_offset ( l , path - > slots [ 0 ] ) ,
2007-04-09 18:42:37 +04:00
sizeof ( root - > root_item ) ) ;
2007-10-19 17:22:56 +04:00
memcpy ( & root - > root_key , location , sizeof ( * location ) ) ;
2007-04-09 18:42:37 +04:00
ret = 0 ;
out :
btrfs_release_path ( root , path ) ;
btrfs_free_path ( path ) ;
if ( ret ) {
kfree ( root ) ;
return ERR_PTR ( ret ) ;
}
2007-10-16 00:15:53 +04:00
blocksize = btrfs_level_size ( root , btrfs_root_level ( & root - > root_item ) ) ;
root - > node = read_tree_block ( root , btrfs_root_bytenr ( & root - > root_item ) ,
blocksize ) ;
2007-04-09 18:42:37 +04:00
BUG_ON ( ! root - > node ) ;
insert :
root - > ref_cows = 1 ;
2007-06-22 22:16:25 +04:00
ret = btrfs_find_highest_inode ( root , & highest_inode ) ;
if ( ret = = 0 ) {
root - > highest_inode = highest_inode ;
root - > last_inode_alloc = highest_inode ;
}
return root ;
}
2008-01-08 23:46:30 +03:00
struct btrfs_root * btrfs_lookup_fs_root ( struct btrfs_fs_info * fs_info ,
u64 root_objectid )
{
struct btrfs_root * root ;
if ( root_objectid = = BTRFS_ROOT_TREE_OBJECTID )
return fs_info - > tree_root ;
if ( root_objectid = = BTRFS_EXTENT_TREE_OBJECTID )
return fs_info - > extent_root ;
root = radix_tree_lookup ( & fs_info - > fs_roots_radix ,
( unsigned long ) root_objectid ) ;
return root ;
}
2007-12-22 00:27:24 +03:00
struct btrfs_root * btrfs_read_fs_root_no_name ( struct btrfs_fs_info * fs_info ,
struct btrfs_key * location )
2007-06-22 22:16:25 +04:00
{
struct btrfs_root * root ;
int ret ;
2007-12-22 00:27:24 +03:00
if ( location - > objectid = = BTRFS_ROOT_TREE_OBJECTID )
return fs_info - > tree_root ;
if ( location - > objectid = = BTRFS_EXTENT_TREE_OBJECTID )
return fs_info - > extent_root ;
2007-06-22 22:16:25 +04:00
root = radix_tree_lookup ( & fs_info - > fs_roots_radix ,
( unsigned long ) location - > objectid ) ;
if ( root )
return root ;
root = btrfs_read_fs_root_no_radix ( fs_info , location ) ;
if ( IS_ERR ( root ) )
return root ;
2007-04-11 00:58:11 +04:00
ret = radix_tree_insert ( & fs_info - > fs_roots_radix ,
( unsigned long ) root - > root_key . objectid ,
2007-04-09 18:42:37 +04:00
root ) ;
if ( ret ) {
2007-10-16 00:14:19 +04:00
free_extent_buffer ( root - > node ) ;
2007-04-09 18:42:37 +04:00
kfree ( root ) ;
return ERR_PTR ( ret ) ;
}
2007-12-22 00:27:24 +03:00
ret = btrfs_find_dead_roots ( fs_info - > tree_root ,
root - > root_key . objectid , root ) ;
BUG_ON ( ret ) ;
return root ;
}
struct btrfs_root * btrfs_read_fs_root ( struct btrfs_fs_info * fs_info ,
struct btrfs_key * location ,
const char * name , int namelen )
{
struct btrfs_root * root ;
int ret ;
root = btrfs_read_fs_root_no_name ( fs_info , location ) ;
if ( ! root )
return NULL ;
2007-08-29 23:47:34 +04:00
2008-01-03 17:08:48 +03:00
if ( root - > in_sysfs )
return root ;
2007-08-29 23:47:34 +04:00
ret = btrfs_set_root_name ( root , name , namelen ) ;
if ( ret ) {
2007-10-16 00:14:19 +04:00
free_extent_buffer ( root - > node ) ;
2007-08-29 23:47:34 +04:00
kfree ( root ) ;
return ERR_PTR ( ret ) ;
}
ret = btrfs_sysfs_add_root ( root ) ;
if ( ret ) {
2007-10-16 00:14:19 +04:00
free_extent_buffer ( root - > node ) ;
2007-08-29 23:47:34 +04:00
kfree ( root - > name ) ;
kfree ( root ) ;
return ERR_PTR ( ret ) ;
}
2008-01-03 17:08:48 +03:00
root - > in_sysfs = 1 ;
2007-04-09 18:42:37 +04:00
return root ;
}
2007-10-16 00:19:22 +04:00
#if 0
static int add_hasher ( struct btrfs_fs_info * info , char * type ) {
struct btrfs_hasher * hasher ;
hasher = kmalloc ( sizeof ( * hasher ) , GFP_NOFS ) ;
if ( ! hasher )
return - ENOMEM ;
hasher - > hash_tfm = crypto_alloc_hash ( type , 0 , CRYPTO_ALG_ASYNC ) ;
if ( ! hasher - > hash_tfm ) {
kfree ( hasher ) ;
return - EINVAL ;
}
spin_lock ( & info - > hash_lock ) ;
list_add ( & hasher - > list , & info - > hashers ) ;
spin_unlock ( & info - > hash_lock ) ;
return 0 ;
}
# endif
2008-03-26 17:28:07 +03:00
static int btrfs_congested_fn ( void * congested_data , int bdi_bits )
{
struct btrfs_fs_info * info = ( struct btrfs_fs_info * ) congested_data ;
int ret = 0 ;
struct list_head * cur ;
struct btrfs_device * device ;
struct backing_dev_info * bdi ;
list_for_each ( cur , & info - > fs_devices - > devices ) {
device = list_entry ( cur , struct btrfs_device , dev_list ) ;
bdi = blk_get_backing_dev_info ( device - > bdev ) ;
if ( bdi & & bdi_congested ( bdi , bdi_bits ) ) {
ret = 1 ;
break ;
}
}
return ret ;
}
void btrfs_unplug_io_fn ( struct backing_dev_info * bdi , struct page * page )
{
struct list_head * cur ;
struct btrfs_device * device ;
struct btrfs_fs_info * info ;
info = ( struct btrfs_fs_info * ) bdi - > unplug_io_data ;
list_for_each ( cur , & info - > fs_devices - > devices ) {
device = list_entry ( cur , struct btrfs_device , dev_list ) ;
bdi = blk_get_backing_dev_info ( device - > bdev ) ;
if ( bdi - > unplug_io_fn ) {
bdi - > unplug_io_fn ( bdi , page ) ;
}
}
}
static int setup_bdi ( struct btrfs_fs_info * info , struct backing_dev_info * bdi )
{
bdi_init ( bdi ) ;
2008-03-26 19:02:55 +03:00
bdi - > ra_pages = default_backing_dev_info . ra_pages * 4 ;
2008-03-26 17:28:07 +03:00
bdi - > state = 0 ;
bdi - > capabilities = default_backing_dev_info . capabilities ;
bdi - > unplug_io_fn = btrfs_unplug_io_fn ;
bdi - > unplug_io_data = info ;
bdi - > congested_fn = btrfs_congested_fn ;
bdi - > congested_data = info ;
return 0 ;
}
2008-03-24 22:02:07 +03:00
struct btrfs_root * open_ctree ( struct super_block * sb ,
struct btrfs_fs_devices * fs_devices )
2007-03-21 18:12:56 +03:00
{
2007-10-16 00:15:53 +04:00
u32 sectorsize ;
u32 nodesize ;
u32 leafsize ;
u32 blocksize ;
2007-11-30 19:30:34 +03:00
u32 stripesize ;
2007-03-22 19:13:20 +03:00
struct btrfs_root * extent_root = kmalloc ( sizeof ( struct btrfs_root ) ,
GFP_NOFS ) ;
struct btrfs_root * tree_root = kmalloc ( sizeof ( struct btrfs_root ) ,
GFP_NOFS ) ;
2008-04-04 00:29:03 +04:00
struct btrfs_fs_info * fs_info = kzalloc ( sizeof ( * fs_info ) ,
2007-03-22 19:13:20 +03:00
GFP_NOFS ) ;
2008-03-24 22:01:56 +03:00
struct btrfs_root * chunk_root = kmalloc ( sizeof ( struct btrfs_root ) ,
GFP_NOFS ) ;
struct btrfs_root * dev_root = kmalloc ( sizeof ( struct btrfs_root ) ,
GFP_NOFS ) ;
2007-02-02 17:18:22 +03:00
int ret ;
2008-04-01 19:21:34 +04:00
int err = - EINVAL ;
2007-04-02 18:50:19 +04:00
struct btrfs_super_block * disk_super ;
2008-04-04 00:29:03 +04:00
2007-06-12 14:35:45 +04:00
if ( ! extent_root | | ! tree_root | | ! fs_info ) {
err = - ENOMEM ;
goto fail ;
}
2007-04-09 18:42:37 +04:00
INIT_RADIX_TREE ( & fs_info - > fs_roots_radix , GFP_NOFS ) ;
2007-04-20 05:01:03 +04:00
INIT_LIST_HEAD ( & fs_info - > trans_list ) ;
2007-06-09 02:11:48 +04:00
INIT_LIST_HEAD ( & fs_info - > dead_roots ) ;
2007-10-16 00:19:22 +04:00
INIT_LIST_HEAD ( & fs_info - > hashers ) ;
spin_lock_init ( & fs_info - > hash_lock ) ;
2007-12-22 00:27:21 +03:00
spin_lock_init ( & fs_info - > delalloc_lock ) ;
2008-01-15 16:40:48 +03:00
spin_lock_init ( & fs_info - > new_trans_lock ) ;
2007-10-16 00:19:22 +04:00
2007-08-29 23:47:34 +04:00
init_completion ( & fs_info - > kobj_unregister ) ;
2007-04-02 18:50:19 +04:00
sb_set_blocksize ( sb , 4096 ) ;
2007-03-20 21:38:32 +03:00
fs_info - > tree_root = tree_root ;
fs_info - > extent_root = extent_root ;
2008-03-24 22:01:56 +03:00
fs_info - > chunk_root = chunk_root ;
fs_info - > dev_root = dev_root ;
2008-03-24 22:02:07 +03:00
fs_info - > fs_devices = fs_devices ;
2008-03-24 22:01:56 +03:00
INIT_LIST_HEAD ( & fs_info - > dirty_cowonly_roots ) ;
2008-03-24 22:01:59 +03:00
INIT_LIST_HEAD ( & fs_info - > space_info ) ;
2008-03-24 22:01:56 +03:00
btrfs_mapping_init ( & fs_info - > mapping_tree ) ;
2007-03-22 19:13:20 +03:00
fs_info - > sb = sb ;
2007-12-18 04:14:04 +03:00
fs_info - > max_extent = ( u64 ) - 1 ;
2008-01-30 00:03:38 +03:00
fs_info - > max_inline = 8192 * 1024 ;
2008-03-26 17:28:07 +03:00
setup_bdi ( fs_info , & fs_info - > bdi ) ;
2007-03-28 21:57:48 +04:00
fs_info - > btree_inode = new_inode ( sb ) ;
fs_info - > btree_inode - > i_ino = 1 ;
2007-04-02 18:50:19 +04:00
fs_info - > btree_inode - > i_nlink = 1 ;
2007-03-28 21:57:48 +04:00
fs_info - > btree_inode - > i_size = sb - > s_bdev - > bd_inode - > i_size ;
fs_info - > btree_inode - > i_mapping - > a_ops = & btree_aops ;
2008-03-26 17:28:07 +03:00
fs_info - > btree_inode - > i_mapping - > backing_dev_info = & fs_info - > bdi ;
2008-01-25 00:13:08 +03:00
extent_io_tree_init ( & BTRFS_I ( fs_info - > btree_inode ) - > io_tree ,
2007-10-16 00:14:19 +04:00
fs_info - > btree_inode - > i_mapping ,
GFP_NOFS ) ;
2008-01-25 00:13:08 +03:00
extent_map_tree_init ( & BTRFS_I ( fs_info - > btree_inode ) - > extent_tree ,
GFP_NOFS ) ;
BTRFS_I ( fs_info - > btree_inode ) - > io_tree . ops = & btree_extent_io_ops ;
2007-11-08 05:08:01 +03:00
2008-01-25 00:13:08 +03:00
extent_io_tree_init ( & fs_info - > free_space_cache ,
2007-10-16 00:14:48 +04:00
fs_info - > btree_inode - > i_mapping , GFP_NOFS ) ;
2008-01-25 00:13:08 +03:00
extent_io_tree_init ( & fs_info - > block_group_cache ,
2007-10-16 00:15:19 +04:00
fs_info - > btree_inode - > i_mapping , GFP_NOFS ) ;
2008-01-25 00:13:08 +03:00
extent_io_tree_init ( & fs_info - > pinned_extents ,
2007-10-16 00:15:26 +04:00
fs_info - > btree_inode - > i_mapping , GFP_NOFS ) ;
2008-01-25 00:13:08 +03:00
extent_io_tree_init ( & fs_info - > pending_del ,
2007-10-16 00:15:26 +04:00
fs_info - > btree_inode - > i_mapping , GFP_NOFS ) ;
2008-01-25 00:13:08 +03:00
extent_io_tree_init ( & fs_info - > extent_ins ,
2007-10-16 00:15:26 +04:00
fs_info - > btree_inode - > i_mapping , GFP_NOFS ) ;
2007-04-20 21:16:02 +04:00
fs_info - > do_barriers = 1 ;
2008-01-18 18:54:22 +03:00
2007-12-19 00:15:09 +03:00
# if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,18)
INIT_WORK ( & fs_info - > trans_work , btrfs_transaction_cleaner , fs_info ) ;
# else
2007-06-08 23:33:54 +04:00
INIT_DELAYED_WORK ( & fs_info - > trans_work , btrfs_transaction_cleaner ) ;
2007-12-19 00:15:09 +03:00
# endif
2007-04-09 18:42:37 +04:00
BTRFS_I ( fs_info - > btree_inode ) - > root = tree_root ;
memset ( & BTRFS_I ( fs_info - > btree_inode ) - > location , 0 ,
sizeof ( struct btrfs_key ) ) ;
2007-03-30 16:47:31 +04:00
insert_inode_hash ( fs_info - > btree_inode ) ;
2007-03-28 21:57:48 +04:00
mapping_set_gfp_mask ( fs_info - > btree_inode - > i_mapping , GFP_NOFS ) ;
2007-06-12 14:35:45 +04:00
2007-03-22 22:59:16 +03:00
mutex_init ( & fs_info - > trans_mutex ) ;
2007-03-24 02:47:49 +03:00
mutex_init ( & fs_info - > fs_mutex ) ;
2007-03-13 23:47:54 +03:00
2007-10-16 00:19:22 +04:00
#if 0
ret = add_hasher ( fs_info , " crc32c " ) ;
if ( ret ) {
printk ( " btrfs: failed hash setup, modprobe cryptomgr? \n " ) ;
err = - ENOMEM ;
goto fail_iput ;
}
# endif
2008-03-24 22:01:56 +03:00
__setup_root ( 4096 , 4096 , 4096 , 4096 , tree_root ,
2007-04-02 18:50:19 +04:00
fs_info , BTRFS_ROOT_TREE_OBJECTID ) ;
2007-04-11 23:53:25 +04:00
2007-04-02 18:50:19 +04:00
fs_info - > sb_buffer = read_tree_block ( tree_root ,
2007-10-16 00:15:53 +04:00
BTRFS_SUPER_INFO_OFFSET ,
2008-03-24 22:01:56 +03:00
4096 ) ;
2007-03-28 21:57:48 +04:00
2007-04-09 18:42:37 +04:00
if ( ! fs_info - > sb_buffer )
2007-06-12 14:35:45 +04:00
goto fail_iput ;
2007-10-16 00:14:19 +04:00
read_extent_buffer ( fs_info - > sb_buffer , & fs_info - > super_copy , 0 ,
sizeof ( fs_info - > super_copy ) ) ;
read_extent_buffer ( fs_info - > sb_buffer , fs_info - > fsid ,
( unsigned long ) btrfs_super_fsid ( fs_info - > sb_buffer ) ,
BTRFS_FSID_SIZE ) ;
2008-03-24 22:01:56 +03:00
2007-10-16 00:14:19 +04:00
disk_super = & fs_info - > super_copy ;
2007-04-09 18:42:37 +04:00
if ( ! btrfs_super_root ( disk_super ) )
2007-06-12 14:35:45 +04:00
goto fail_sb_buffer ;
2007-04-09 18:42:37 +04:00
2008-03-24 22:02:07 +03:00
if ( btrfs_super_num_devices ( disk_super ) ! = fs_devices - > num_devices ) {
printk ( " Btrfs: wanted %llu devices, but found %llu \n " ,
( unsigned long long ) btrfs_super_num_devices ( disk_super ) ,
( unsigned long long ) fs_devices - > num_devices ) ;
goto fail_sb_buffer ;
}
2007-10-16 00:15:53 +04:00
nodesize = btrfs_super_nodesize ( disk_super ) ;
leafsize = btrfs_super_leafsize ( disk_super ) ;
sectorsize = btrfs_super_sectorsize ( disk_super ) ;
2007-11-30 19:30:34 +03:00
stripesize = btrfs_super_stripesize ( disk_super ) ;
2007-10-16 00:15:53 +04:00
tree_root - > nodesize = nodesize ;
tree_root - > leafsize = leafsize ;
tree_root - > sectorsize = sectorsize ;
2007-11-30 19:30:34 +03:00
tree_root - > stripesize = stripesize ;
2007-10-16 00:22:25 +04:00
sb_set_blocksize ( sb , sectorsize ) ;
2007-10-16 00:15:53 +04:00
2007-04-12 18:43:05 +04:00
i_size_write ( fs_info - > btree_inode ,
2007-10-16 00:15:53 +04:00
btrfs_super_total_bytes ( disk_super ) ) ;
2007-04-12 18:43:05 +04:00
2007-06-12 14:35:45 +04:00
if ( strncmp ( ( char * ) ( & disk_super - > magic ) , BTRFS_MAGIC ,
sizeof ( disk_super - > magic ) ) ) {
printk ( " btrfs: valid FS not found on %s \n " , sb - > s_id ) ;
goto fail_sb_buffer ;
}
2007-10-16 00:19:22 +04:00
2008-03-24 22:01:56 +03:00
mutex_lock ( & fs_info - > fs_mutex ) ;
2008-03-24 22:02:07 +03:00
2008-03-24 22:01:56 +03:00
ret = btrfs_read_sys_array ( tree_root ) ;
BUG_ON ( ret ) ;
blocksize = btrfs_level_size ( tree_root ,
btrfs_super_chunk_root_level ( disk_super ) ) ;
__setup_root ( nodesize , leafsize , sectorsize , stripesize ,
chunk_root , fs_info , BTRFS_CHUNK_TREE_OBJECTID ) ;
chunk_root - > node = read_tree_block ( chunk_root ,
btrfs_super_chunk_root ( disk_super ) ,
blocksize ) ;
BUG_ON ( ! chunk_root - > node ) ;
ret = btrfs_read_chunk_tree ( chunk_root ) ;
BUG_ON ( ret ) ;
2007-10-16 00:15:53 +04:00
blocksize = btrfs_level_size ( tree_root ,
btrfs_super_root_level ( disk_super ) ) ;
2007-10-16 00:19:22 +04:00
2008-03-24 22:01:56 +03:00
2007-03-22 19:13:20 +03:00
tree_root - > node = read_tree_block ( tree_root ,
2007-10-16 00:15:53 +04:00
btrfs_super_root ( disk_super ) ,
blocksize ) ;
2007-06-12 14:35:45 +04:00
if ( ! tree_root - > node )
goto fail_sb_buffer ;
2007-03-13 23:47:54 +03:00
2007-10-16 00:15:53 +04:00
ret = find_and_setup_root ( tree_root , fs_info ,
2007-03-22 19:13:20 +03:00
BTRFS_EXTENT_TREE_OBJECTID , extent_root ) ;
2008-03-24 22:01:56 +03:00
if ( ret )
2007-06-12 14:35:45 +04:00
goto fail_tree_root ;
2008-03-24 22:01:56 +03:00
extent_root - > track_dirty = 1 ;
ret = find_and_setup_root ( tree_root , fs_info ,
BTRFS_DEV_TREE_OBJECTID , dev_root ) ;
dev_root - > track_dirty = 1 ;
if ( ret )
goto fail_extent_root ;
2007-03-13 23:47:54 +03:00
2007-04-27 00:46:15 +04:00
btrfs_read_block_groups ( extent_root ) ;
2007-04-09 18:42:37 +04:00
fs_info - > generation = btrfs_super_generation ( disk_super ) + 1 ;
2008-04-04 23:40:00 +04:00
fs_info - > data_alloc_profile = ( u64 ) - 1 ;
fs_info - > metadata_alloc_profile = ( u64 ) - 1 ;
fs_info - > system_alloc_profile = fs_info - > metadata_alloc_profile ;
2007-04-05 21:35:25 +04:00
mutex_unlock ( & fs_info - > fs_mutex ) ;
2007-04-09 18:42:37 +04:00
return tree_root ;
2007-06-12 14:35:45 +04:00
2008-03-24 22:01:56 +03:00
fail_extent_root :
free_extent_buffer ( extent_root - > node ) ;
2007-06-12 14:35:45 +04:00
fail_tree_root :
2008-03-24 22:01:56 +03:00
mutex_unlock ( & fs_info - > fs_mutex ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( tree_root - > node ) ;
2007-06-12 14:35:45 +04:00
fail_sb_buffer :
2007-10-16 00:14:19 +04:00
free_extent_buffer ( fs_info - > sb_buffer ) ;
2008-03-26 23:24:23 +03:00
extent_io_tree_empty_lru ( & BTRFS_I ( fs_info - > btree_inode ) - > io_tree ) ;
2007-06-12 14:35:45 +04:00
fail_iput :
iput ( fs_info - > btree_inode ) ;
fail :
2008-03-24 22:02:07 +03:00
close_all_devices ( fs_info ) ;
2007-06-12 14:35:45 +04:00
kfree ( extent_root ) ;
kfree ( tree_root ) ;
2008-03-26 23:24:23 +03:00
bdi_destroy ( & fs_info - > bdi ) ;
2007-06-12 14:35:45 +04:00
kfree ( fs_info ) ;
return ERR_PTR ( err ) ;
2007-02-02 17:18:22 +03:00
}
2007-03-16 23:20:31 +03:00
int write_ctree_super ( struct btrfs_trans_handle * trans , struct btrfs_root
2007-03-22 22:59:16 +03:00
* root )
2007-02-02 17:18:22 +03:00
{
2007-04-20 21:16:02 +04:00
int ret ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * super = root - > fs_info - > sb_buffer ;
struct inode * btree_inode = root - > fs_info - > btree_inode ;
2008-01-09 17:23:21 +03:00
struct super_block * sb = root - > fs_info - > sb ;
2007-10-16 00:14:19 +04:00
2008-01-09 17:23:21 +03:00
if ( ! btrfs_test_opt ( root , NOBARRIER ) )
blkdev_issue_flush ( sb - > s_bdev , NULL ) ;
2008-01-25 00:13:08 +03:00
set_extent_buffer_dirty ( & BTRFS_I ( btree_inode ) - > io_tree , super ) ;
2007-10-16 00:14:19 +04:00
ret = sync_page_range_nolock ( btree_inode , btree_inode - > i_mapping ,
super - > start , super - > len ) ;
2008-01-09 17:23:21 +03:00
if ( ! btrfs_test_opt ( root , NOBARRIER ) )
blkdev_issue_flush ( sb - > s_bdev , NULL ) ;
2007-10-16 00:14:19 +04:00
return ret ;
2007-02-22 01:04:57 +03:00
}
2007-06-22 22:16:25 +04:00
int btrfs_free_fs_root ( struct btrfs_fs_info * fs_info , struct btrfs_root * root )
2007-04-11 00:58:11 +04:00
{
radix_tree_delete ( & fs_info - > fs_roots_radix ,
( unsigned long ) root - > root_key . objectid ) ;
2008-01-14 22:41:16 +03:00
if ( root - > in_sysfs )
btrfs_sysfs_del_root ( root ) ;
2007-04-11 00:58:11 +04:00
if ( root - > inode )
iput ( root - > inode ) ;
if ( root - > node )
2007-10-16 00:14:19 +04:00
free_extent_buffer ( root - > node ) ;
2007-04-11 00:58:11 +04:00
if ( root - > commit_root )
2007-10-16 00:14:19 +04:00
free_extent_buffer ( root - > commit_root ) ;
2007-08-29 23:47:34 +04:00
if ( root - > name )
kfree ( root - > name ) ;
2007-04-11 00:58:11 +04:00
kfree ( root ) ;
return 0 ;
}
2007-05-02 23:53:43 +04:00
static int del_fs_roots ( struct btrfs_fs_info * fs_info )
2007-04-09 18:42:37 +04:00
{
int ret ;
struct btrfs_root * gang [ 8 ] ;
int i ;
while ( 1 ) {
ret = radix_tree_gang_lookup ( & fs_info - > fs_roots_radix ,
( void * * ) gang , 0 ,
ARRAY_SIZE ( gang ) ) ;
if ( ! ret )
break ;
2007-04-11 00:58:11 +04:00
for ( i = 0 ; i < ret ; i + + )
2007-06-22 22:16:25 +04:00
btrfs_free_fs_root ( fs_info , gang [ i ] ) ;
2007-04-09 18:42:37 +04:00
}
return 0 ;
}
2007-04-12 20:14:00 +04:00
2007-03-22 19:13:20 +03:00
int close_ctree ( struct btrfs_root * root )
2007-02-22 01:04:57 +03:00
{
2007-03-13 23:47:54 +03:00
int ret ;
2007-03-16 23:20:31 +03:00
struct btrfs_trans_handle * trans ;
2007-04-09 18:42:37 +04:00
struct btrfs_fs_info * fs_info = root - > fs_info ;
2007-03-16 23:20:31 +03:00
2007-06-09 02:11:48 +04:00
fs_info - > closing = 1 ;
2007-06-08 23:33:54 +04:00
btrfs_transaction_flush_work ( root ) ;
2007-04-09 18:42:37 +04:00
mutex_lock ( & fs_info - > fs_mutex ) ;
2007-08-08 00:15:09 +04:00
btrfs_defrag_dirty_roots ( root - > fs_info ) ;
2007-03-22 22:59:16 +03:00
trans = btrfs_start_transaction ( root , 1 ) ;
2007-06-22 22:16:25 +04:00
ret = btrfs_commit_transaction ( trans , root ) ;
2007-03-22 22:59:16 +03:00
/* run commit again to drop the original snapshot */
trans = btrfs_start_transaction ( root , 1 ) ;
btrfs_commit_transaction ( trans , root ) ;
ret = btrfs_write_and_wait_transaction ( NULL , root ) ;
2007-03-13 23:47:54 +03:00
BUG_ON ( ret ) ;
2007-03-22 22:59:16 +03:00
write_ctree_super ( NULL , root ) ;
2007-04-09 18:42:37 +04:00
mutex_unlock ( & fs_info - > fs_mutex ) ;
2008-01-31 19:05:37 +03:00
if ( fs_info - > delalloc_bytes ) {
printk ( " btrfs: at unmount delalloc count %Lu \n " ,
fs_info - > delalloc_bytes ) ;
}
2007-04-09 18:42:37 +04:00
if ( fs_info - > extent_root - > node )
2007-10-16 00:14:19 +04:00
free_extent_buffer ( fs_info - > extent_root - > node ) ;
2007-10-16 00:14:48 +04:00
2007-04-09 18:42:37 +04:00
if ( fs_info - > tree_root - > node )
2007-10-16 00:14:19 +04:00
free_extent_buffer ( fs_info - > tree_root - > node ) ;
2007-10-16 00:14:48 +04:00
2008-03-24 22:01:56 +03:00
if ( root - > fs_info - > chunk_root - > node ) ;
free_extent_buffer ( root - > fs_info - > chunk_root - > node ) ;
if ( root - > fs_info - > dev_root - > node ) ;
free_extent_buffer ( root - > fs_info - > dev_root - > node ) ;
2007-10-16 00:14:19 +04:00
free_extent_buffer ( fs_info - > sb_buffer ) ;
2007-04-11 23:53:25 +04:00
2007-04-27 00:46:15 +04:00
btrfs_free_block_groups ( root - > fs_info ) ;
2007-04-09 18:42:37 +04:00
del_fs_roots ( fs_info ) ;
2007-12-18 04:14:04 +03:00
filemap_write_and_wait ( fs_info - > btree_inode - > i_mapping ) ;
2008-01-25 00:13:08 +03:00
extent_io_tree_empty_lru ( & fs_info - > free_space_cache ) ;
extent_io_tree_empty_lru ( & fs_info - > block_group_cache ) ;
extent_io_tree_empty_lru ( & fs_info - > pinned_extents ) ;
extent_io_tree_empty_lru ( & fs_info - > pending_del ) ;
extent_io_tree_empty_lru ( & fs_info - > extent_ins ) ;
extent_io_tree_empty_lru ( & BTRFS_I ( fs_info - > btree_inode ) - > io_tree ) ;
2007-12-18 04:14:04 +03:00
2007-10-16 00:15:53 +04:00
truncate_inode_pages ( fs_info - > btree_inode - > i_mapping , 0 ) ;
2007-12-18 04:14:04 +03:00
2007-10-16 00:15:53 +04:00
iput ( fs_info - > btree_inode ) ;
2007-10-16 00:19:22 +04:00
#if 0
while ( ! list_empty ( & fs_info - > hashers ) ) {
struct btrfs_hasher * hasher ;
hasher = list_entry ( fs_info - > hashers . next , struct btrfs_hasher ,
hashers ) ;
list_del ( & hasher - > hashers ) ;
crypto_free_hash ( & fs_info - > hash_tfm ) ;
kfree ( hasher ) ;
}
# endif
2008-03-24 22:01:56 +03:00
close_all_devices ( fs_info ) ;
btrfs_mapping_tree_free ( & fs_info - > mapping_tree ) ;
2008-03-26 17:28:07 +03:00
bdi_destroy ( & fs_info - > bdi ) ;
2008-03-24 22:01:56 +03:00
2007-04-09 18:42:37 +04:00
kfree ( fs_info - > extent_root ) ;
kfree ( fs_info - > tree_root ) ;
2008-03-24 22:01:56 +03:00
kfree ( fs_info - > chunk_root ) ;
kfree ( fs_info - > dev_root ) ;
2007-02-02 17:18:22 +03:00
return 0 ;
}
2007-10-16 00:14:19 +04:00
int btrfs_buffer_uptodate ( struct extent_buffer * buf )
{
2007-10-16 00:18:55 +04:00
struct inode * btree_inode = buf - > first_page - > mapping - > host ;
2008-01-25 00:13:08 +03:00
return extent_buffer_uptodate ( & BTRFS_I ( btree_inode ) - > io_tree , buf ) ;
2007-10-16 00:14:19 +04:00
}
int btrfs_set_buffer_uptodate ( struct extent_buffer * buf )
2007-06-28 23:57:36 +04:00
{
2007-10-16 00:18:55 +04:00
struct inode * btree_inode = buf - > first_page - > mapping - > host ;
2008-01-25 00:13:08 +03:00
return set_extent_buffer_uptodate ( & BTRFS_I ( btree_inode ) - > io_tree ,
2007-10-16 00:14:19 +04:00
buf ) ;
}
2007-08-08 00:15:09 +04:00
2007-10-16 00:14:19 +04:00
void btrfs_mark_buffer_dirty ( struct extent_buffer * buf )
{
2007-10-16 00:18:55 +04:00
struct btrfs_root * root = BTRFS_I ( buf - > first_page - > mapping - > host ) - > root ;
2007-10-16 00:14:19 +04:00
u64 transid = btrfs_header_generation ( buf ) ;
struct inode * btree_inode = root - > fs_info - > btree_inode ;
2007-08-08 00:15:09 +04:00
2007-06-28 23:57:36 +04:00
if ( transid ! = root - > fs_info - > generation ) {
printk ( KERN_CRIT " transid mismatch buffer %llu, found %Lu running %Lu \n " ,
2007-10-16 00:15:53 +04:00
( unsigned long long ) buf - > start ,
2007-06-28 23:57:36 +04:00
transid , root - > fs_info - > generation ) ;
WARN_ON ( 1 ) ;
}
2008-01-25 00:13:08 +03:00
set_extent_buffer_dirty ( & BTRFS_I ( btree_inode ) - > io_tree , buf ) ;
2007-02-02 17:18:22 +03:00
}
2008-01-08 23:46:30 +03:00
void btrfs_throttle ( struct btrfs_root * root )
{
2008-01-09 23:55:33 +03:00
struct backing_dev_info * bdi ;
bdi = root - > fs_info - > sb - > s_bdev - > bd_inode - > i_mapping - > backing_dev_info ;
2008-01-17 20:01:41 +03:00
if ( root - > fs_info - > throttles & & bdi_write_congested ( bdi ) ) {
# if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
2008-01-09 23:55:33 +03:00
congestion_wait ( WRITE , HZ / 20 ) ;
2008-01-17 20:01:41 +03:00
# else
blk_congestion_wait ( WRITE , HZ / 20 ) ;
# endif
}
2008-01-08 23:46:30 +03:00
}
2007-09-17 18:58:06 +04:00
void btrfs_btree_balance_dirty ( struct btrfs_root * root , unsigned long nr )
2007-05-02 23:53:43 +04:00
{
2007-09-17 18:58:06 +04:00
balance_dirty_pages_ratelimited_nr (
2008-02-18 20:12:38 +03:00
root - > fs_info - > btree_inode - > i_mapping , 1 ) ;
2007-05-02 23:53:43 +04:00
}
2007-10-16 00:17:34 +04:00
void btrfs_set_buffer_defrag ( struct extent_buffer * buf )
{
2007-10-16 00:18:55 +04:00
struct btrfs_root * root = BTRFS_I ( buf - > first_page - > mapping - > host ) - > root ;
2007-10-16 00:17:34 +04:00
struct inode * btree_inode = root - > fs_info - > btree_inode ;
2008-01-25 00:13:08 +03:00
set_extent_bits ( & BTRFS_I ( btree_inode ) - > io_tree , buf - > start ,
2007-10-16 00:17:34 +04:00
buf - > start + buf - > len - 1 , EXTENT_DEFRAG , GFP_NOFS ) ;
}
void btrfs_set_buffer_defrag_done ( struct extent_buffer * buf )
{
2007-10-16 00:18:55 +04:00
struct btrfs_root * root = BTRFS_I ( buf - > first_page - > mapping - > host ) - > root ;
2007-10-16 00:17:34 +04:00
struct inode * btree_inode = root - > fs_info - > btree_inode ;
2008-01-25 00:13:08 +03:00
set_extent_bits ( & BTRFS_I ( btree_inode ) - > io_tree , buf - > start ,
2007-10-16 00:17:34 +04:00
buf - > start + buf - > len - 1 , EXTENT_DEFRAG_DONE ,
GFP_NOFS ) ;
}
int btrfs_buffer_defrag ( struct extent_buffer * buf )
{
2007-10-16 00:18:55 +04:00
struct btrfs_root * root = BTRFS_I ( buf - > first_page - > mapping - > host ) - > root ;
2007-10-16 00:17:34 +04:00
struct inode * btree_inode = root - > fs_info - > btree_inode ;
2008-01-25 00:13:08 +03:00
return test_range_bit ( & BTRFS_I ( btree_inode ) - > io_tree ,
2007-10-16 00:17:34 +04:00
buf - > start , buf - > start + buf - > len - 1 , EXTENT_DEFRAG , 0 ) ;
}
int btrfs_buffer_defrag_done ( struct extent_buffer * buf )
{
2007-10-16 00:18:55 +04:00
struct btrfs_root * root = BTRFS_I ( buf - > first_page - > mapping - > host ) - > root ;
2007-10-16 00:17:34 +04:00
struct inode * btree_inode = root - > fs_info - > btree_inode ;
2008-01-25 00:13:08 +03:00
return test_range_bit ( & BTRFS_I ( btree_inode ) - > io_tree ,
2007-10-16 00:17:34 +04:00
buf - > start , buf - > start + buf - > len - 1 ,
EXTENT_DEFRAG_DONE , 0 ) ;
}
int btrfs_clear_buffer_defrag_done ( struct extent_buffer * buf )
{
2007-10-16 00:18:55 +04:00
struct btrfs_root * root = BTRFS_I ( buf - > first_page - > mapping - > host ) - > root ;
2007-10-16 00:17:34 +04:00
struct inode * btree_inode = root - > fs_info - > btree_inode ;
2008-01-25 00:13:08 +03:00
return clear_extent_bits ( & BTRFS_I ( btree_inode ) - > io_tree ,
2007-10-16 00:17:34 +04:00
buf - > start , buf - > start + buf - > len - 1 ,
EXTENT_DEFRAG_DONE , GFP_NOFS ) ;
}
int btrfs_clear_buffer_defrag ( struct extent_buffer * buf )
{
2007-10-16 00:18:55 +04:00
struct btrfs_root * root = BTRFS_I ( buf - > first_page - > mapping - > host ) - > root ;
2007-10-16 00:17:34 +04:00
struct inode * btree_inode = root - > fs_info - > btree_inode ;
2008-01-25 00:13:08 +03:00
return clear_extent_bits ( & BTRFS_I ( btree_inode ) - > io_tree ,
2007-10-16 00:17:34 +04:00
buf - > start , buf - > start + buf - > len - 1 ,
EXTENT_DEFRAG , GFP_NOFS ) ;
}
int btrfs_read_buffer ( struct extent_buffer * buf )
{
2007-10-16 00:18:55 +04:00
struct btrfs_root * root = BTRFS_I ( buf - > first_page - > mapping - > host ) - > root ;
2007-10-16 00:17:34 +04:00
struct inode * btree_inode = root - > fs_info - > btree_inode ;
2008-01-25 00:13:08 +03:00
return read_extent_buffer_pages ( & BTRFS_I ( btree_inode ) - > io_tree ,
2008-02-07 18:50:54 +03:00
buf , 0 , 1 , btree_get_extent ) ;
2007-10-16 00:17:34 +04:00
}
2007-11-08 05:08:01 +03:00
2008-01-25 00:13:08 +03:00
static struct extent_io_ops btree_extent_io_ops = {
2007-11-08 05:08:01 +03:00
. writepage_io_hook = btree_writepage_io_hook ,
2008-03-24 22:01:56 +03:00
. submit_bio_hook = btree_submit_bio_hook ,
2008-03-24 22:02:07 +03:00
/* note we're sharing with inode.c for the merge bio hook */
. merge_bio_hook = btrfs_merge_bio_hook ,
2007-11-08 05:08:01 +03:00
} ;