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"
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
2007-10-16 00:14:19 +04:00
struct extent_buffer * btrfs_find_tree_block ( struct btrfs_root * root ,
u64 blocknr )
2007-03-28 21:57:48 +04:00
{
2007-10-16 00:14:19 +04:00
struct inode * btree_inode = root - > fs_info - > btree_inode ;
2007-10-16 00:14:48 +04:00
struct extent_buffer * eb ;
eb = find_extent_buffer ( & BTRFS_I ( btree_inode ) - > extent_tree ,
2007-10-16 00:14:19 +04:00
blocknr * root - > sectorsize ,
root - > sectorsize , GFP_NOFS ) ;
2007-10-16 00:14:48 +04:00
if ( eb )
eb - > alloc_addr = ( unsigned long ) __builtin_return_address ( 0 ) ;
return eb ;
2007-10-16 00:14:19 +04:00
}
2007-03-28 21:57:48 +04:00
2007-10-16 00:14:19 +04:00
struct extent_buffer * btrfs_find_create_tree_block ( struct btrfs_root * root ,
u64 blocknr )
{
struct inode * btree_inode = root - > fs_info - > btree_inode ;
2007-10-16 00:14:48 +04:00
struct extent_buffer * eb ;
eb = alloc_extent_buffer ( & BTRFS_I ( btree_inode ) - > extent_tree ,
2007-10-16 00:14:19 +04:00
blocknr * root - > sectorsize ,
root - > sectorsize , GFP_NOFS ) ;
2007-10-16 00:14:48 +04:00
eb - > alloc_addr = ( unsigned long ) __builtin_return_address ( 0 ) ;
return eb ;
2007-03-28 21:57:48 +04:00
}
2007-10-16 00:14:19 +04:00
struct extent_map * btree_get_extent ( struct inode * inode , struct page * page ,
size_t page_offset , u64 start , u64 end ,
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 :
em = lookup_extent_mapping ( em_tree , start , end ) ;
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 ;
em - > end = ( i_size_read ( inode ) & ~ ( ( u64 ) PAGE_CACHE_SIZE - 1 ) ) - 1 ;
em - > block_start = 0 ;
em - > block_end = em - > end ;
em - > bdev = inode - > i_sb - > s_bdev ;
ret = add_extent_mapping ( em_tree , em ) ;
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:14:19 +04:00
static int btree_writepage ( struct page * page , struct writeback_control * wbc )
2007-03-28 21:57:48 +04:00
{
2007-10-16 00:14:19 +04:00
struct extent_map_tree * tree ;
tree = & BTRFS_I ( page - > mapping - > host ) - > extent_tree ;
return extent_write_full_page ( tree , page , btree_get_extent , wbc ) ;
}
int btree_readpage ( struct file * file , struct page * page )
{
struct extent_map_tree * tree ;
tree = & BTRFS_I ( page - > mapping - > host ) - > extent_tree ;
return extent_read_full_page ( tree , page , btree_get_extent ) ;
}
2007-03-30 16:47:31 +04:00
2007-10-16 00:14:19 +04:00
static int btree_releasepage ( struct page * page , gfp_t unused_gfp_flags )
{
struct extent_map_tree * tree ;
int ret ;
2007-03-28 21:57:48 +04:00
2007-10-16 00:14:19 +04:00
BUG_ON ( page - > private ! = 1 ) ;
tree = & BTRFS_I ( page - > mapping - > host ) - > extent_tree ;
ret = try_release_extent_mapping ( tree , page ) ;
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
{
2007-10-16 00:14:19 +04:00
struct extent_map_tree * tree ;
tree = & BTRFS_I ( page - > mapping - > host ) - > extent_tree ;
extent_invalidatepage ( tree , page , offset ) ;
btree_releasepage ( page , GFP_NOFS ) ;
2007-03-28 21:57:48 +04:00
}
2007-03-29 23:15:27 +04:00
int btrfs_csum_data ( struct btrfs_root * root , char * data , size_t len ,
char * result )
2007-03-29 03:44:27 +04:00
{
2007-10-16 00:14:19 +04:00
return 0 ;
#if 0
2007-06-22 22:16:24 +04:00
u32 crc ;
crc = crc32c ( 0 , data , len ) ;
memcpy ( result , & crc , BTRFS_CRC32_SIZE ) ;
return 0 ;
2007-10-16 00:14:19 +04:00
# endif
2007-03-29 23:15:27 +04:00
}
2007-06-22 22:16:24 +04:00
2007-10-16 00:14:19 +04:00
#if 0
static int csum_tree_block ( struct btrfs_root * root , struct extent_buffer * buf ,
2007-03-29 23:15:27 +04:00
int verify )
{
2007-10-16 00:14:19 +04:00
return 0 ;
2007-05-10 20:36:17 +04:00
char result [ BTRFS_CRC32_SIZE ] ;
2007-03-29 23:15:27 +04:00
int ret ;
struct btrfs_node * node ;
ret = btrfs_csum_data ( root , bh - > b_data + BTRFS_CSUM_SIZE ,
bh - > b_size - BTRFS_CSUM_SIZE , result ) ;
if ( ret )
return ret ;
2007-03-29 03:44:27 +04:00
if ( verify ) {
2007-05-10 20:36:17 +04:00
if ( memcmp ( bh - > b_data , result , BTRFS_CRC32_SIZE ) ) {
2007-06-12 15:50:13 +04:00
printk ( " btrfs: %s checksum verify failed on %llu \n " ,
root - > fs_info - > sb - > s_id ,
( unsigned long long ) bh_blocknr ( bh ) ) ;
2007-03-29 23:15:27 +04:00
return 1 ;
}
} else {
node = btrfs_buffer_node ( bh ) ;
2007-05-10 20:36:17 +04:00
memcpy ( node - > header . csum , result , BTRFS_CRC32_SIZE ) ;
2007-03-29 23:15:27 +04:00
}
2007-03-29 03:44:27 +04:00
return 0 ;
}
2007-10-16 00:14:19 +04:00
# endif
2007-03-29 03:44:27 +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-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-05-01 16:53:32 +04:00
int readahead_tree_block ( struct btrfs_root * root , u64 blocknr )
{
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:14:19 +04:00
buf = btrfs_find_create_tree_block ( root , blocknr ) ;
if ( ! buf )
2007-05-01 16:53:32 +04:00
return 0 ;
2007-10-16 00:14:19 +04:00
read_extent_buffer_pages ( & BTRFS_I ( btree_inode ) - > extent_tree ,
buf , 0 ) ;
free_extent_buffer ( buf ) ;
2007-05-18 21:28:27 +04:00
return ret ;
2007-05-01 16:53:32 +04:00
}
2007-10-16 00:14:19 +04:00
struct extent_buffer * read_tree_block ( struct btrfs_root * root , u64 blocknr )
2007-02-02 17:18:22 +03:00
{
2007-10-16 00:14:19 +04:00
struct extent_buffer * buf = NULL ;
struct inode * btree_inode = root - > fs_info - > btree_inode ;
buf = btrfs_find_create_tree_block ( root , blocknr ) ;
if ( ! buf )
return NULL ;
read_extent_buffer_pages ( & BTRFS_I ( btree_inode ) - > extent_tree ,
buf , 1 ) ;
2007-10-16 00:14:48 +04:00
buf - > alloc_addr = ( unsigned long ) __builtin_return_address ( 0 ) ;
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 ;
clear_extent_buffer_dirty ( & BTRFS_I ( btree_inode ) - > extent_tree , buf ) ;
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 ;
wait_on_extent_buffer_writeback ( & BTRFS_I ( btree_inode ) - > extent_tree ,
buf ) ;
return 0 ;
}
int set_tree_block_dirty ( struct btrfs_root * root , struct extent_buffer * buf )
{
struct inode * btree_inode = root - > fs_info - > btree_inode ;
set_extent_buffer_dirty ( & BTRFS_I ( btree_inode ) - > extent_tree , buf ) ;
2007-03-02 02:59:40 +03:00
return 0 ;
}
2007-04-02 18:50:19 +04:00
static int __setup_root ( int blocksize ,
2007-03-20 21:38:32 +03:00
struct btrfs_root * root ,
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:14:19 +04:00
root - > sectorsize = blocksize ;
root - > nodesize = blocksize ;
root - > leafsize = blocksize ;
2007-03-14 21:14:43 +03:00
root - > ref_cows = 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 ;
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-09-11 03:58:36 +04:00
init_rwsem ( & root - > snap_sem ) ;
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-04-02 18:50:19 +04:00
static int find_and_setup_root ( int blocksize ,
2007-03-20 21:38:32 +03:00
struct btrfs_root * tree_root ,
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-04-02 18:50:19 +04:00
__setup_root ( blocksize , 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 ) ;
root - > node = read_tree_block ( root ,
btrfs_root_blocknr ( & root - > root_item ) ) ;
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-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 ) {
ret = find_and_setup_root ( fs_info - > sb - > s_blocksize ,
fs_info - > tree_root , fs_info ,
location - > objectid , root ) ;
if ( ret ) {
kfree ( root ) ;
return ERR_PTR ( ret ) ;
}
goto insert ;
}
__setup_root ( fs_info - > sb - > s_blocksize , root , fs_info ,
location - > objectid ) ;
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 ) ) ;
ret = 0 ;
out :
btrfs_release_path ( root , path ) ;
btrfs_free_path ( path ) ;
if ( ret ) {
kfree ( root ) ;
return ERR_PTR ( ret ) ;
}
root - > node = read_tree_block ( root ,
btrfs_root_blocknr ( & root - > root_item ) ) ;
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 ;
}
struct btrfs_root * btrfs_read_fs_root ( struct btrfs_fs_info * fs_info ,
2007-08-29 23:47:34 +04:00
struct btrfs_key * location ,
const char * name , int namelen )
2007-06-22 22:16:25 +04:00
{
struct btrfs_root * root ;
int ret ;
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-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 ) ;
}
2007-09-11 19:15:39 +04:00
ret = btrfs_find_dead_roots ( fs_info - > tree_root ,
root - > root_key . objectid , root ) ;
BUG_ON ( ret ) ;
2007-04-09 18:42:37 +04:00
return root ;
}
2007-04-02 18:50:19 +04:00
struct btrfs_root * open_ctree ( struct super_block * sb )
2007-03-21 18:12:56 +03:00
{
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 ) ;
struct btrfs_fs_info * fs_info = kmalloc ( sizeof ( * fs_info ) ,
GFP_NOFS ) ;
2007-02-02 17:18:22 +03:00
int ret ;
2007-06-12 14:35:45 +04:00
int err = - EIO ;
2007-04-02 18:50:19 +04:00
struct btrfs_super_block * disk_super ;
2007-02-02 17:18:22 +03:00
2007-06-12 14:35:45 +04:00
if ( ! extent_root | | ! tree_root | | ! fs_info ) {
err = - ENOMEM ;
goto fail ;
}
2007-03-26 18:15:30 +04:00
init_bit_radix ( & fs_info - > pinned_radix ) ;
init_bit_radix ( & fs_info - > pending_del_radix ) ;
2007-08-09 04:17:12 +04:00
init_bit_radix ( & fs_info - > extent_ins_radix ) ;
2007-04-09 18:42:37 +04:00
INIT_RADIX_TREE ( & fs_info - > fs_roots_radix , GFP_NOFS ) ;
2007-04-27 00:46:15 +04:00
INIT_RADIX_TREE ( & fs_info - > block_group_radix , GFP_KERNEL ) ;
2007-05-06 18:15:01 +04:00
INIT_RADIX_TREE ( & fs_info - > block_group_data_radix , GFP_KERNEL ) ;
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-08-29 23:47:34 +04:00
memset ( & fs_info - > super_kobj , 0 , sizeof ( fs_info - > super_kobj ) ) ;
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 - > running_transaction = NULL ;
2007-08-11 00:22:09 +04:00
fs_info - > last_trans_committed = 0 ;
2007-03-20 21:38:32 +03:00
fs_info - > tree_root = tree_root ;
fs_info - > extent_root = extent_root ;
2007-03-22 19:13:20 +03:00
fs_info - > sb = sb ;
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 ;
2007-10-16 00:14:19 +04:00
extent_map_tree_init ( & BTRFS_I ( fs_info - > btree_inode ) - > extent_tree ,
fs_info - > btree_inode - > i_mapping ,
GFP_NOFS ) ;
2007-10-16 00:14:48 +04:00
extent_map_tree_init ( & fs_info - > free_space_cache ,
fs_info - > btree_inode - > i_mapping , GFP_NOFS ) ;
2007-04-20 21:16:02 +04:00
fs_info - > do_barriers = 1 ;
2007-06-09 02:11:48 +04:00
fs_info - > closing = 0 ;
2007-06-08 23:33:54 +04:00
INIT_DELAYED_WORK ( & fs_info - > trans_work , btrfs_transaction_cleaner ) ;
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-04-02 18:50:19 +04:00
__setup_root ( sb - > s_blocksize , tree_root ,
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 ,
BTRFS_SUPER_INFO_OFFSET /
sb - > s_blocksize ) ;
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 ) ;
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
2007-04-12 18:43:05 +04:00
i_size_write ( fs_info - > btree_inode ,
btrfs_super_total_blocks ( disk_super ) < <
fs_info - > btree_inode - > i_blkbits ) ;
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-03-22 19:13:20 +03:00
tree_root - > node = read_tree_block ( tree_root ,
btrfs_super_root ( disk_super ) ) ;
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-04-02 18:50:19 +04:00
mutex_lock ( & fs_info - > fs_mutex ) ;
ret = find_and_setup_root ( sb - > s_blocksize , tree_root , fs_info ,
2007-03-22 19:13:20 +03:00
BTRFS_EXTENT_TREE_OBJECTID , extent_root ) ;
2007-06-12 14:35:45 +04:00
if ( ret ) {
mutex_unlock ( & fs_info - > fs_mutex ) ;
goto fail_tree_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 ;
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
fail_tree_root :
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 ) ;
2007-06-12 14:35:45 +04:00
fail_iput :
iput ( fs_info - > btree_inode ) ;
fail :
kfree ( extent_root ) ;
kfree ( tree_root ) ;
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 ;
set_extent_buffer_dirty ( & BTRFS_I ( btree_inode ) - > extent_tree , super ) ;
ret = sync_page_range_nolock ( btree_inode , btree_inode - > i_mapping ,
super - > start , super - > len ) ;
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 ) ;
2007-08-29 23:47:34 +04:00
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 ) ;
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
2007-10-16 00:14:19 +04:00
free_extent_buffer ( fs_info - > sb_buffer ) ;
2007-04-09 18:42:37 +04:00
truncate_inode_pages ( fs_info - > btree_inode - > i_mapping , 0 ) ;
iput ( fs_info - > btree_inode ) ;
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 ) ;
kfree ( fs_info - > extent_root ) ;
kfree ( fs_info - > tree_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:14:37 +04:00
struct inode * btree_inode = buf - > first_page - > mapping - > host ;
2007-10-16 00:14:19 +04:00
return extent_buffer_uptodate ( & BTRFS_I ( btree_inode ) - > extent_tree , buf ) ;
}
int btrfs_set_buffer_uptodate ( struct extent_buffer * buf )
2007-06-28 23:57:36 +04:00
{
2007-10-16 00:14:37 +04:00
struct inode * btree_inode = buf - > first_page - > mapping - > host ;
2007-10-16 00:14:19 +04:00
return set_extent_buffer_uptodate ( & BTRFS_I ( btree_inode ) - > extent_tree ,
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:14:37 +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:14:19 +04:00
( unsigned long long ) extent_buffer_blocknr ( buf ) ,
2007-06-28 23:57:36 +04:00
transid , root - > fs_info - > generation ) ;
WARN_ON ( 1 ) ;
}
2007-10-16 00:14:19 +04:00
set_extent_buffer_dirty ( & BTRFS_I ( btree_inode ) - > extent_tree , buf ) ;
2007-02-02 17:18:22 +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 (
root - > fs_info - > btree_inode - > i_mapping , nr ) ;
2007-05-02 23:53:43 +04:00
}