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 .
*/
2008-06-26 00:01:31 +04:00
# include <linux/version.h>
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-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
2008-04-10 00:28:12 +04:00
# include <linux/workqueue.h>
2008-06-26 00:01:31 +04:00
# include <linux/kthread.h>
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
# include <linux / freezer.h>
# else
# include <linux / sched.h>
# endif
2008-04-11 23:45:51 +04:00
# include "crc32c.h"
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"
2008-06-12 00:50:36 +04:00
# include "async-thread.h"
2008-06-26 00:01:30 +04:00
# include "locking.h"
2008-07-28 23:32:51 +04:00
# include "ref-cache.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 ;
2008-06-12 00:50:36 +04:00
static void end_workqueue_fn ( struct btrfs_work * work ) ;
2008-04-10 00:28:12 +04:00
struct end_io_wq {
struct bio * bio ;
bio_end_io_t * end_io ;
void * private ;
struct btrfs_fs_info * info ;
int error ;
2008-04-10 00:28:12 +04:00
int metadata ;
2008-04-10 00:28:12 +04:00
struct list_head list ;
2008-06-12 00:50:36 +04:00
struct btrfs_work work ;
2008-04-10 00:28:12 +04:00
} ;
2007-11-08 05:08:01 +03:00
2008-04-16 19:14:51 +04:00
struct async_submit_bio {
struct inode * inode ;
struct bio * bio ;
struct list_head list ;
extent_submit_bio_hook_t * submit_bio_hook ;
int rw ;
int mirror_num ;
2008-06-12 00:50:36 +04:00
struct btrfs_work work ;
2008-04-16 19:14:51 +04: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 ;
2008-01-25 00:13:08 +03:00
spin_lock ( & em_tree - > lock ) ;
em = lookup_extent_mapping ( em_tree , start , len ) ;
2008-05-07 19:43:44 +04:00
if ( em ) {
em - > bdev =
BTRFS_I ( inode ) - > root - > fs_info - > fs_devices - > latest_bdev ;
spin_unlock ( & em_tree - > lock ) ;
2007-10-16 00:14:19 +04:00
goto out ;
2008-05-07 19:43:44 +04:00
}
spin_unlock ( & em_tree - > lock ) ;
2008-04-18 18:29:50 +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-04-18 22:17:20 +04:00
em - > len = ( u64 ) - 1 ;
2007-10-16 00:14:19 +04:00
em - > block_start = 0 ;
2008-05-07 19:43:44 +04:00
em - > bdev = BTRFS_I ( inode ) - > root - > fs_info - > fs_devices - > latest_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 ) ;
if ( ret = = - EEXIST ) {
2008-04-18 22:17:20 +04:00
u64 failed_start = em - > start ;
u64 failed_len = em - > len ;
printk ( " failed to insert %Lu %Lu -> %Lu into tree \n " ,
em - > start , em - > len , em - > block_start ) ;
2007-10-16 00:14:19 +04:00
free_extent_map ( em ) ;
2008-04-18 18:29:50 +04:00
em = lookup_extent_mapping ( em_tree , start , len ) ;
2008-04-18 22:17:20 +04:00
if ( em ) {
printk ( " after failing, found %Lu %Lu %Lu \n " ,
em - > start , em - > len , em - > block_start ) ;
2008-04-18 18:29:50 +04:00
ret = 0 ;
2008-04-18 22:17:20 +04:00
} else {
em = lookup_extent_mapping ( em_tree , failed_start ,
failed_len ) ;
if ( em ) {
printk ( " double failure lookup gives us "
" %Lu %Lu -> %Lu \n " , em - > start ,
em - > len , em - > block_start ) ;
free_extent_map ( em ) ;
}
2008-04-18 18:29:50 +04:00
ret = - EIO ;
2008-04-18 22:17:20 +04:00
}
2007-10-16 00:14:19 +04:00
} else if ( ret ) {
2008-04-18 18:29:50 +04:00
free_extent_map ( em ) ;
em = NULL ;
2007-10-16 00:14:19 +04:00
}
2008-04-18 18:29:50 +04:00
spin_unlock ( & em_tree - > lock ) ;
if ( ret )
em = ERR_PTR ( ret ) ;
2007-10-16 00:14:19 +04:00
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 )
{
2008-04-11 23:45:51 +04:00
return btrfs_crc32c ( seed , data , len ) ;
2007-10-16 00:19:22 +04:00
}
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
/* 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 ) ;
printk ( " btrfs: %s checksum verify failed on %llu "
2008-08-04 16:20:15 +04:00
" wanted %X found %X level %d \n " ,
2007-10-16 00:19:22 +04:00
root - > fs_info - > sb - > s_id ,
2008-08-04 16:20:15 +04:00
buf - > start , val , found , 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 ;
}
2008-05-12 21:39:03 +04:00
static int verify_parent_transid ( struct extent_io_tree * io_tree ,
struct extent_buffer * eb , u64 parent_transid )
{
int ret ;
if ( ! parent_transid | | btrfs_header_generation ( eb ) = = parent_transid )
return 0 ;
lock_extent ( io_tree , eb - > start , eb - > start + eb - > len - 1 , GFP_NOFS ) ;
if ( extent_buffer_uptodate ( io_tree , eb ) & &
btrfs_header_generation ( eb ) = = parent_transid ) {
ret = 0 ;
goto out ;
}
printk ( " parent transid verify failed on %llu wanted %llu found %llu \n " ,
( unsigned long long ) eb - > start ,
( unsigned long long ) parent_transid ,
( unsigned long long ) btrfs_header_generation ( eb ) ) ;
ret = 1 ;
clear_extent_buffer_uptodate ( io_tree , eb ) ;
2008-07-30 18:29:12 +04:00
out :
2008-05-12 21:39:03 +04:00
unlock_extent ( io_tree , eb - > start , eb - > start + eb - > len - 1 ,
GFP_NOFS ) ;
return ret ;
}
2008-04-10 00:28:12 +04:00
static int btree_read_extent_buffer_pages ( struct btrfs_root * root ,
struct extent_buffer * eb ,
2008-05-12 20:59:19 +04:00
u64 start , u64 parent_transid )
2008-04-10 00:28:12 +04:00
{
struct extent_io_tree * io_tree ;
int ret ;
int num_copies = 0 ;
int mirror_num = 0 ;
io_tree = & BTRFS_I ( root - > fs_info - > btree_inode ) - > io_tree ;
while ( 1 ) {
ret = read_extent_buffer_pages ( io_tree , eb , start , 1 ,
btree_get_extent , mirror_num ) ;
2008-05-12 21:39:03 +04:00
if ( ! ret & &
! verify_parent_transid ( io_tree , eb , parent_transid ) )
2008-04-10 00:28:12 +04:00
return ret ;
2008-04-29 00:40:52 +04:00
2008-04-10 00:28:12 +04:00
num_copies = btrfs_num_copies ( & root - > fs_info - > mapping_tree ,
eb - > start , eb - > len ) ;
2008-04-29 00:40:52 +04:00
if ( num_copies = = 1 )
2008-04-10 00:28:12 +04:00
return ret ;
2008-04-29 00:40:52 +04:00
2008-04-10 00:28:12 +04:00
mirror_num + + ;
2008-04-29 00:40:52 +04:00
if ( mirror_num > num_copies )
2008-04-10 00:28:12 +04:00
return ret ;
}
return - EIO ;
}
2007-10-16 00:19:22 +04:00
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-04-10 00:28:12 +04:00
int ret ;
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-05-12 20:59:19 +04:00
ret = btree_read_extent_buffer_pages ( root , eb , start + PAGE_CACHE_SIZE ,
btrfs_header_generation ( eb ) ) ;
2008-04-10 00:28:12 +04:00
BUG_ON ( ret ) ;
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-04-10 00:28:12 +04:00
int btree_readpage_end_io_hook ( struct page * page , u64 start , u64 end ,
struct extent_state * state )
{
struct extent_io_tree * tree ;
u64 found_start ;
int found_level ;
unsigned long len ;
struct extent_buffer * eb ;
struct btrfs_root * root = BTRFS_I ( page - > mapping - > host ) - > root ;
2008-04-10 00:28:12 +04:00
int ret = 0 ;
2008-04-10 00:28:12 +04:00
tree = & BTRFS_I ( page - > mapping - > host ) - > io_tree ;
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-04-10 00:28:12 +04:00
2008-04-10 00:28:12 +04:00
found_start = btrfs_header_bytenr ( eb ) ;
if ( found_start ! = start ) {
2008-04-10 00:28:12 +04:00
ret = - EIO ;
2008-04-10 00:28:12 +04:00
goto err ;
}
if ( eb - > first_page ! = page ) {
printk ( " bad first page %lu %lu \n " , eb - > first_page - > index ,
page - > index ) ;
WARN_ON ( 1 ) ;
2008-04-10 00:28:12 +04:00
ret = - EIO ;
2008-04-10 00:28:12 +04:00
goto err ;
}
2008-05-12 21:39:03 +04:00
if ( memcmp_extent_buffer ( eb , root - > fs_info - > fsid ,
( unsigned long ) btrfs_header_fsid ( eb ) ,
BTRFS_FSID_SIZE ) ) {
printk ( " bad fsid on block %Lu \n " , eb - > start ) ;
ret = - EIO ;
goto err ;
}
2008-04-10 00:28:12 +04:00
found_level = btrfs_header_level ( eb ) ;
ret = csum_tree_block ( root , eb , 1 ) ;
2008-04-10 00:28:12 +04:00
if ( ret )
ret = - EIO ;
2008-04-10 00:28:12 +04:00
end = min_t ( u64 , eb - > len , PAGE_CACHE_SIZE ) ;
end = eb - > start + end - 1 ;
err :
free_extent_buffer ( eb ) ;
out :
2008-04-10 00:28:12 +04:00
return ret ;
2008-04-10 00:28:12 +04:00
}
# if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
static void end_workqueue_bio ( struct bio * bio , int err )
# else
static int end_workqueue_bio ( struct bio * bio ,
unsigned int bytes_done , int err )
# endif
{
struct end_io_wq * end_io_wq = bio - > bi_private ;
struct btrfs_fs_info * fs_info ;
# if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
if ( bio - > bi_size )
return 1 ;
# endif
fs_info = end_io_wq - > info ;
end_io_wq - > error = err ;
2008-06-12 00:50:36 +04:00
end_io_wq - > work . func = end_workqueue_fn ;
end_io_wq - > work . flags = 0 ;
2008-07-17 20:53:50 +04:00
if ( bio - > bi_rw & ( 1 < < BIO_RW ) )
btrfs_queue_worker ( & fs_info - > endio_write_workers ,
& end_io_wq - > work ) ;
else
btrfs_queue_worker ( & fs_info - > endio_workers , & end_io_wq - > work ) ;
2008-04-10 00:28:12 +04:00
# if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
return 0 ;
# endif
}
2008-04-10 00:28:12 +04:00
int btrfs_bio_wq_end_io ( struct btrfs_fs_info * info , struct bio * bio ,
int metadata )
2008-03-24 22:01:56 +03:00
{
2008-04-10 00:28:12 +04:00
struct end_io_wq * end_io_wq ;
end_io_wq = kmalloc ( sizeof ( * end_io_wq ) , GFP_NOFS ) ;
if ( ! end_io_wq )
return - ENOMEM ;
end_io_wq - > private = bio - > bi_private ;
end_io_wq - > end_io = bio - > bi_end_io ;
2008-04-10 00:28:12 +04:00
end_io_wq - > info = info ;
2008-04-10 00:28:12 +04:00
end_io_wq - > error = 0 ;
end_io_wq - > bio = bio ;
2008-04-10 00:28:12 +04:00
end_io_wq - > metadata = metadata ;
2008-04-10 00:28:12 +04:00
bio - > bi_private = end_io_wq ;
bio - > bi_end_io = end_workqueue_bio ;
2008-04-10 00:28:12 +04:00
return 0 ;
}
2008-08-15 23:34:15 +04:00
static int congested_async ( struct btrfs_fs_info * info , int iodone )
{
int limit = 256 * info - > fs_devices - > open_devices ;
if ( iodone )
limit = ( limit * 3 ) / 2 ;
if ( atomic_read ( & info - > nr_async_submits ) > limit )
return 1 ;
limit = 8192 * info - > fs_devices - > open_devices ;
if ( iodone )
limit = ( limit * 3 ) / 2 ;
return atomic_read ( & info - > nr_async_bios ) > limit ;
}
2008-06-12 00:50:36 +04:00
static void run_one_async_submit ( struct btrfs_work * work )
{
struct btrfs_fs_info * fs_info ;
struct async_submit_bio * async ;
async = container_of ( work , struct async_submit_bio , work ) ;
fs_info = BTRFS_I ( async - > inode ) - > root - > fs_info ;
atomic_dec ( & fs_info - > nr_async_submits ) ;
2008-08-15 23:34:15 +04:00
if ( ( async - > bio - > bi_rw & ( 1 < < BIO_RW ) ) & &
! congested_async ( fs_info , 1 ) ) {
clear_bdi_congested ( & fs_info - > bdi , WRITE ) ;
}
2008-06-12 00:50:36 +04:00
async - > submit_bio_hook ( async - > inode , async - > rw , async - > bio ,
async - > mirror_num ) ;
kfree ( async ) ;
}
2008-04-16 19:14:51 +04:00
int btrfs_wq_submit_bio ( struct btrfs_fs_info * fs_info , struct inode * inode ,
int rw , struct bio * bio , int mirror_num ,
extent_submit_bio_hook_t * submit_bio_hook )
{
struct async_submit_bio * async ;
async = kmalloc ( sizeof ( * async ) , GFP_NOFS ) ;
if ( ! async )
return - ENOMEM ;
async - > inode = inode ;
async - > rw = rw ;
async - > bio = bio ;
async - > mirror_num = mirror_num ;
async - > submit_bio_hook = submit_bio_hook ;
2008-06-12 00:50:36 +04:00
async - > work . func = run_one_async_submit ;
async - > work . flags = 0 ;
2008-05-16 00:15:45 +04:00
atomic_inc ( & fs_info - > nr_async_submits ) ;
2008-06-12 00:50:36 +04:00
btrfs_queue_worker ( & fs_info - > workers , & async - > work ) ;
2008-04-16 19:14:51 +04:00
return 0 ;
}
static int __btree_submit_bio_hook ( struct inode * inode , int rw , struct bio * bio ,
2008-04-10 00:28:12 +04:00
int mirror_num )
2008-04-10 00:28:12 +04:00
{
struct btrfs_root * root = BTRFS_I ( inode ) - > root ;
u64 offset ;
int ret ;
offset = bio - > bi_sector < < 9 ;
2008-06-12 00:50:36 +04:00
/*
* when we ' re called for a write , we ' re already in the async
* submission context . Just jump ingo btrfs_map_bio
*/
2008-04-10 00:28:12 +04:00
if ( rw & ( 1 < < BIO_RW ) ) {
2008-06-12 00:50:36 +04:00
return btrfs_map_bio ( BTRFS_I ( inode ) - > root , rw , bio ,
mirror_num , 0 ) ;
2008-04-10 00:28:12 +04:00
}
2008-06-12 00:50:36 +04:00
/*
* called for a read , do the setup so that checksum validation
* can happen in the async kernel threads
*/
2008-04-10 00:28:12 +04:00
ret = btrfs_bio_wq_end_io ( root - > fs_info , bio , 1 ) ;
BUG_ON ( ret ) ;
2008-04-10 00:28:12 +04:00
2008-06-12 00:50:36 +04:00
return btrfs_map_bio ( BTRFS_I ( inode ) - > root , rw , bio , mirror_num , 1 ) ;
2008-03-24 22:01:56 +03:00
}
2008-04-16 19:14:51 +04:00
static int btree_submit_bio_hook ( struct inode * inode , int rw , struct bio * bio ,
int mirror_num )
{
2008-06-12 00:50:36 +04:00
/*
* kthread helpers are used to submit writes so that checksumming
* can happen in parallel across all CPUs
*/
2008-04-16 19:14:51 +04:00
if ( ! ( rw & ( 1 < < BIO_RW ) ) ) {
return __btree_submit_bio_hook ( inode , rw , bio , mirror_num ) ;
}
return btrfs_wq_submit_bio ( BTRFS_I ( inode ) - > root - > fs_info ,
inode , rw , bio , mirror_num ,
__btree_submit_bio_hook ) ;
}
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-07-22 19:18:07 +04:00
2008-04-18 18:29:50 +04:00
ret = try_release_extent_state ( map , tree , page , gfp_flags ) ;
2008-07-22 19:18:07 +04:00
if ( ! ret ) {
return 0 ;
}
ret = try_release_extent_buffer ( tree , page ) ;
2007-10-16 00:14:19 +04:00
if ( ret = = 1 ) {
ClearPagePrivate ( page ) ;
set_page_private ( page , 0 ) ;
page_cache_release ( page ) ;
}
2008-07-22 19:18:07 +04:00
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 ) ;
2008-04-19 00:11:30 +04:00
if ( PagePrivate ( page ) ) {
2008-07-22 19:18:07 +04:00
printk ( " warning page private not zero on page %Lu \n " ,
page_offset ( page ) ) ;
2008-04-19 00:11:30 +04:00
ClearPagePrivate ( page ) ;
set_page_private ( page , 0 ) ;
page_cache_release ( page ) ;
}
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 ,
} ;
2008-05-12 20:59:19 +04:00
int readahead_tree_block ( struct btrfs_root * root , u64 bytenr , u32 blocksize ,
u64 parent_transid )
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-04-10 00:28:12 +04:00
buf , 0 , 0 , btree_get_extent , 0 ) ;
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-04-01 21:48:14 +04:00
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 ,
2008-05-12 20:59:19 +04:00
u32 blocksize , u64 parent_transid )
2008-04-01 21:48:14 +04:00
{
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 ;
2008-05-12 20:59:19 +04:00
ret = btree_read_extent_buffer_pages ( root , buf , 0 , parent_transid ) ;
2008-04-10 00:28:12 +04:00
if ( ret = = 0 ) {
buf - > flags | = EXTENT_UPTODATE ;
}
2007-10-16 00:14:19 +04:00
return buf ;
2008-04-10 00:28:12 +04:00
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 ) = =
2008-06-26 00:01:30 +04:00
root - > fs_info - > running_transaction - > transid ) {
WARN_ON ( ! btrfs_tree_locked ( buf ) ) ;
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 ) ;
2008-06-26 00:01:30 +04:00
}
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 ;
2008-07-28 23:32:19 +04:00
root - > ref_tree = 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 ) ;
2008-07-24 20:17:14 +04:00
INIT_LIST_HEAD ( & root - > orphan_list ) ;
2008-07-31 00:29:20 +04:00
INIT_LIST_HEAD ( & root - > dead_list ) ;
2008-06-26 00:01:30 +04:00
spin_lock_init ( & root - > node_lock ) ;
2008-07-31 00:29:20 +04:00
spin_lock_init ( & root - > list_lock ) ;
2008-06-26 00:01:30 +04:00
mutex_init ( & root - > objectid_mutex ) ;
2008-07-28 23:32:51 +04:00
btrfs_leaf_ref_tree_init ( & root - > ref_tree_struct ) ;
root - > ref_tree = & root - > ref_tree_struct ;
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 ) ) ;
2008-06-26 00:01:31 +04:00
root - > defrag_trans_start = fs_info - > generation ;
2007-08-29 23:47:34 +04:00
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 ) ,
2008-05-12 20:59:19 +04:00
blocksize , 0 ) ;
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 ) ,
2008-05-12 20:59:19 +04:00
blocksize , 0 ) ;
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 ;
2008-04-26 00:53:30 +04:00
if ( location - > objectid = = BTRFS_CHUNK_TREE_OBJECTID )
return fs_info - > chunk_root ;
if ( location - > objectid = = BTRFS_DEV_TREE_OBJECTID )
return fs_info - > dev_root ;
2007-12-22 00:27:24 +03:00
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 ;
2008-05-16 00:15:45 +04:00
if ( ( bdi_bits & ( 1 < < BDI_write_congested ) ) & &
2008-08-15 23:34:15 +04:00
congested_async ( info , 0 ) )
2008-05-16 00:15:45 +04:00
return 1 ;
2008-03-26 17:28:07 +03:00
list_for_each ( cur , & info - > fs_devices - > devices ) {
device = list_entry ( cur , struct btrfs_device , dev_list ) ;
2008-05-13 21:46:40 +04:00
if ( ! device - > bdev )
continue ;
2008-03-26 17:28:07 +03:00
bdi = blk_get_backing_dev_info ( device - > bdev ) ;
if ( bdi & & bdi_congested ( bdi , bdi_bits ) ) {
ret = 1 ;
break ;
}
}
return ret ;
}
2008-04-22 17:22:11 +04:00
/*
* this unplugs every device on the box , and it is only used when page
* is null
*/
static void __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 ) ;
}
}
}
2008-03-26 17:28:07 +03:00
void btrfs_unplug_io_fn ( struct backing_dev_info * bdi , struct page * page )
{
2008-04-22 17:22:11 +04:00
struct inode * inode ;
2008-04-21 18:03:05 +04:00
struct extent_map_tree * em_tree ;
struct extent_map * em ;
2008-04-22 21:26:47 +04:00
struct address_space * mapping ;
2008-04-22 17:22:11 +04:00
u64 offset ;
2008-04-22 21:26:47 +04:00
/* the generic O_DIRECT read code does this */
2008-04-22 17:22:11 +04:00
if ( ! page ) {
__unplug_io_fn ( bdi , page ) ;
return ;
}
2008-04-22 21:26:47 +04:00
/*
* page - > mapping may change at any time . Get a consistent copy
* and use that for everything below
*/
smp_mb ( ) ;
mapping = page - > mapping ;
if ( ! mapping )
return ;
inode = mapping - > host ;
2008-04-22 17:22:11 +04:00
offset = page_offset ( page ) ;
2008-03-26 17:28:07 +03:00
2008-04-21 18:03:05 +04:00
em_tree = & BTRFS_I ( inode ) - > extent_tree ;
spin_lock ( & em_tree - > lock ) ;
em = lookup_extent_mapping ( em_tree , offset , PAGE_CACHE_SIZE ) ;
spin_unlock ( & em_tree - > lock ) ;
2008-07-24 17:41:53 +04:00
if ( ! em ) {
__unplug_io_fn ( bdi , page ) ;
2008-04-21 18:03:05 +04:00
return ;
2008-07-24 17:41:53 +04:00
}
2008-04-21 18:03:05 +04:00
2008-07-24 17:41:53 +04:00
if ( em - > block_start > = EXTENT_MAP_LAST_BYTE ) {
free_extent_map ( em ) ;
__unplug_io_fn ( bdi , page ) ;
return ;
}
2008-04-21 18:03:05 +04:00
offset = offset - em - > start ;
btrfs_unplug_page ( & BTRFS_I ( inode ) - > root - > fs_info - > mapping_tree ,
em - > block_start + offset , page ) ;
free_extent_map ( em ) ;
2008-03-26 17:28:07 +03:00
}
static int setup_bdi ( struct btrfs_fs_info * info , struct backing_dev_info * bdi )
{
2008-06-10 06:19:40 +04:00
# if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
2008-03-26 17:28:07 +03:00
bdi_init ( bdi ) ;
2008-04-14 17:48:18 +04:00
# endif
2008-04-19 00:13:31 +04:00
bdi - > ra_pages = default_backing_dev_info . ra_pages ;
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-04-10 00:28:12 +04:00
static int bio_ready_for_csum ( struct bio * bio )
{
u64 length = 0 ;
u64 buf_len = 0 ;
u64 start = 0 ;
struct page * page ;
struct extent_io_tree * io_tree = NULL ;
struct btrfs_fs_info * info = NULL ;
struct bio_vec * bvec ;
int i ;
int ret ;
bio_for_each_segment ( bvec , bio , i ) {
page = bvec - > bv_page ;
if ( page - > private = = EXTENT_PAGE_PRIVATE ) {
length + = bvec - > bv_len ;
continue ;
}
if ( ! page - > private ) {
length + = bvec - > bv_len ;
continue ;
}
length = bvec - > bv_len ;
buf_len = page - > private > > 2 ;
start = page_offset ( page ) + bvec - > bv_offset ;
io_tree = & BTRFS_I ( page - > mapping - > host ) - > io_tree ;
info = BTRFS_I ( page - > mapping - > host ) - > root - > fs_info ;
}
/* are we fully contained in this bio? */
if ( buf_len < = length )
return 1 ;
ret = extent_range_uptodate ( io_tree , start + length ,
start + buf_len - 1 ) ;
if ( ret = = 1 )
return ret ;
return ret ;
}
2008-06-12 00:50:36 +04:00
/*
* called by the kthread helper functions to finally call the bio end_io
* functions . This is where read checksum verification actually happens
*/
static void end_workqueue_fn ( struct btrfs_work * work )
2008-04-10 00:28:12 +04:00
{
struct bio * bio ;
2008-06-12 00:50:36 +04:00
struct end_io_wq * end_io_wq ;
struct btrfs_fs_info * fs_info ;
2008-04-10 00:28:12 +04:00
int error ;
2008-06-12 00:50:36 +04:00
end_io_wq = container_of ( work , struct end_io_wq , work ) ;
bio = end_io_wq - > bio ;
fs_info = end_io_wq - > info ;
2008-04-10 00:28:12 +04:00
2008-06-12 00:50:36 +04:00
/* metadata bios are special because the whole tree block must
* be checksummed at once . This makes sure the entire block is in
* ram and up to date before trying to verify things . For
* blocksize < = pagesize , it is basically a noop
*/
if ( end_io_wq - > metadata & & ! bio_ready_for_csum ( bio ) ) {
btrfs_queue_worker ( & fs_info - > endio_workers ,
& end_io_wq - > work ) ;
return ;
}
error = end_io_wq - > error ;
bio - > bi_private = end_io_wq - > private ;
bio - > bi_end_io = end_io_wq - > end_io ;
kfree ( end_io_wq ) ;
# if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
bio_endio ( bio , bio - > bi_size , error ) ;
2008-04-16 19:14:51 +04:00
# else
2008-06-12 00:50:36 +04:00
bio_endio ( bio , error ) ;
2008-04-16 19:14:51 +04:00
# endif
}
2008-06-26 00:01:31 +04:00
static int cleaner_kthread ( void * arg )
{
struct btrfs_root * root = arg ;
do {
smp_mb ( ) ;
if ( root - > fs_info - > closing )
break ;
vfs_check_frozen ( root - > fs_info - > sb , SB_FREEZE_WRITE ) ;
mutex_lock ( & root - > fs_info - > cleaner_mutex ) ;
btrfs_clean_old_snapshots ( root ) ;
mutex_unlock ( & root - > fs_info - > cleaner_mutex ) ;
if ( freezing ( current ) ) {
refrigerator ( ) ;
} else {
smp_mb ( ) ;
if ( root - > fs_info - > closing )
break ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
schedule ( ) ;
__set_current_state ( TASK_RUNNING ) ;
}
} while ( ! kthread_should_stop ( ) ) ;
return 0 ;
}
static int transaction_kthread ( void * arg )
{
struct btrfs_root * root = arg ;
struct btrfs_trans_handle * trans ;
struct btrfs_transaction * cur ;
unsigned long now ;
unsigned long delay ;
int ret ;
do {
smp_mb ( ) ;
if ( root - > fs_info - > closing )
break ;
delay = HZ * 30 ;
vfs_check_frozen ( root - > fs_info - > sb , SB_FREEZE_WRITE ) ;
mutex_lock ( & root - > fs_info - > transaction_kthread_mutex ) ;
2008-07-30 00:15:18 +04:00
if ( root - > fs_info - > total_ref_cache_size > 20 * 1024 * 1024 ) {
printk ( " btrfs: total reference cache size %Lu \n " ,
root - > fs_info - > total_ref_cache_size ) ;
}
2008-07-28 23:32:19 +04:00
2008-06-26 00:01:31 +04:00
mutex_lock ( & root - > fs_info - > trans_mutex ) ;
cur = root - > fs_info - > running_transaction ;
if ( ! cur ) {
mutex_unlock ( & root - > fs_info - > trans_mutex ) ;
goto sleep ;
}
2008-07-28 23:32:19 +04:00
2008-06-26 00:01:31 +04:00
now = get_seconds ( ) ;
if ( now < cur - > start_time | | now - cur - > start_time < 30 ) {
mutex_unlock ( & root - > fs_info - > trans_mutex ) ;
delay = HZ * 5 ;
goto sleep ;
}
mutex_unlock ( & root - > fs_info - > trans_mutex ) ;
trans = btrfs_start_transaction ( root , 1 ) ;
ret = btrfs_commit_transaction ( trans , root ) ;
sleep :
wake_up_process ( root - > fs_info - > cleaner_kthread ) ;
mutex_unlock ( & root - > fs_info - > transaction_kthread_mutex ) ;
if ( freezing ( current ) ) {
refrigerator ( ) ;
} else {
if ( root - > fs_info - > closing )
break ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
schedule_timeout ( delay ) ;
__set_current_state ( TASK_RUNNING ) ;
}
} while ( ! kthread_should_stop ( ) ) ;
return 0 ;
}
2008-03-24 22:02:07 +03:00
struct btrfs_root * open_ctree ( struct super_block * sb ,
2008-05-13 21:46:40 +04:00
struct btrfs_fs_devices * fs_devices ,
char * options )
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 ;
2008-05-07 19:43:44 +04:00
struct buffer_head * bh ;
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 ;
2008-06-12 05:47:56 +04:00
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 ) ;
2008-08-05 07:17:27 +04:00
INIT_LIST_HEAD ( & fs_info - > delalloc_inodes ) ;
2007-10-16 00:19:22 +04:00
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 ) ;
2008-07-28 23:32:19 +04:00
spin_lock_init ( & fs_info - > ref_cache_lock ) ;
2007-10-16 00:19:22 +04:00
2007-08-29 23:47:34 +04:00
init_completion ( & fs_info - > kobj_unregister ) ;
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 ) ;
2008-05-16 00:15:45 +04:00
atomic_set ( & fs_info - > nr_async_submits , 0 ) ;
2008-08-15 23:34:15 +04:00
atomic_set ( & fs_info - > nr_async_bios , 0 ) ;
2008-06-26 00:01:30 +04:00
atomic_set ( & fs_info - > throttles , 0 ) ;
2008-07-30 00:15:18 +04:00
atomic_set ( & fs_info - > throttle_gen , 0 ) ;
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 ;
2008-06-12 05:47:56 +04:00
fs_info - > thread_pool_size = min ( num_online_cpus ( ) + 2 , 8 ) ;
2008-04-18 22:17:20 +04:00
2008-07-24 19:57:52 +04:00
INIT_LIST_HEAD ( & fs_info - > ordered_extents ) ;
spin_lock_init ( & fs_info - > ordered_extent_lock ) ;
2008-05-07 19:43:44 +04:00
sb - > s_blocksize = 4096 ;
sb - > s_blocksize_bits = blksize_bits ( 4096 ) ;
2008-04-18 22:17:20 +04:00
/*
* we set the i_size on the btree inode to the max possible int .
* the real end of the address space is determined by all of
* the devices in the system
*/
fs_info - > btree_inode - > i_size = OFFSET_MAX ;
2007-03-28 21:57:48 +04:00
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-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 ) ;
2008-06-26 00:01:30 +04:00
mutex_init ( & fs_info - > drop_mutex ) ;
2008-06-26 00:01:30 +04:00
mutex_init ( & fs_info - > alloc_mutex ) ;
mutex_init ( & fs_info - > chunk_mutex ) ;
2008-06-26 00:01:31 +04:00
mutex_init ( & fs_info - > transaction_kthread_mutex ) ;
mutex_init ( & fs_info - > cleaner_mutex ) ;
2008-07-08 22:19:17 +04:00
mutex_init ( & fs_info - > volume_mutex ) ;
2008-07-17 20:53:50 +04:00
init_waitqueue_head ( & fs_info - > transaction_throttle ) ;
2008-07-17 20:54:14 +04:00
init_waitqueue_head ( & fs_info - > transaction_wait ) ;
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-03-28 21:57:48 +04:00
2008-05-07 19:43:44 +04:00
bh = __bread ( fs_devices - > latest_bdev ,
BTRFS_SUPER_INFO_OFFSET / 4096 , 4096 ) ;
if ( ! bh )
2007-06-12 14:35:45 +04:00
goto fail_iput ;
2008-05-07 19:43:44 +04:00
memcpy ( & fs_info - > super_copy , bh - > b_data , sizeof ( fs_info - > super_copy ) ) ;
brelse ( bh ) ;
2007-10-16 00:14:19 +04:00
2008-05-07 19:43:44 +04:00
memcpy ( fs_info - > fsid , fs_info - > super_copy . fsid , 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-06-10 18:40:29 +04:00
err = btrfs_parse_options ( tree_root , options ) ;
if ( err )
goto fail_sb_buffer ;
2008-05-13 21:46:40 +04:00
2008-06-12 05:47:56 +04:00
/*
* we need to start all the end_io workers up front because the
* queue work function gets called at interrupt time , and so it
* cannot dynamically grow .
*/
btrfs_init_workers ( & fs_info - > workers , fs_info - > thread_pool_size ) ;
2008-08-15 23:34:14 +04:00
btrfs_init_workers ( & fs_info - > submit_workers ,
min_t ( u64 , fs_devices - > num_devices ,
fs_info - > thread_pool_size ) ) ;
2008-07-31 23:42:53 +04:00
/* a higher idle thresh on the submit workers makes it much more
* likely that bios will be send down in a sane order to the
* devices
*/
fs_info - > submit_workers . idle_thresh = 64 ;
2008-07-17 20:53:51 +04:00
btrfs_init_workers ( & fs_info - > fixup_workers , 1 ) ;
2008-06-12 05:47:56 +04:00
btrfs_init_workers ( & fs_info - > endio_workers , fs_info - > thread_pool_size ) ;
2008-07-17 20:53:50 +04:00
btrfs_init_workers ( & fs_info - > endio_write_workers ,
fs_info - > thread_pool_size ) ;
2008-07-31 23:42:53 +04:00
/*
* endios are largely parallel and should have a very
* low idle thresh
*/
fs_info - > endio_workers . idle_thresh = 4 ;
fs_info - > endio_write_workers . idle_thresh = 4 ;
2008-06-12 05:47:56 +04:00
btrfs_start_workers ( & fs_info - > workers , 1 ) ;
2008-06-12 22:46:17 +04:00
btrfs_start_workers ( & fs_info - > submit_workers , 1 ) ;
2008-07-17 20:53:51 +04:00
btrfs_start_workers ( & fs_info - > fixup_workers , 1 ) ;
2008-06-12 05:47:56 +04:00
btrfs_start_workers ( & fs_info - > endio_workers , fs_info - > thread_pool_size ) ;
2008-07-17 20:53:50 +04:00
btrfs_start_workers ( & fs_info - > endio_write_workers ,
fs_info - > thread_pool_size ) ;
2008-06-12 05:47:56 +04:00
2008-06-10 18:40:29 +04:00
err = - EINVAL ;
2008-05-14 00:03:06 +04:00
if ( btrfs_super_num_devices ( disk_super ) > fs_devices - > open_devices ) {
2008-03-24 22:02:07 +03:00
printk ( " Btrfs: wanted %llu devices, but found %llu \n " ,
( unsigned long long ) btrfs_super_num_devices ( disk_super ) ,
2008-05-14 00:03:06 +04:00
( unsigned long long ) fs_devices - > open_devices ) ;
2008-05-13 21:46:40 +04:00
if ( btrfs_test_opt ( tree_root , DEGRADED ) )
printk ( " continuing in degraded mode \n " ) ;
else {
goto fail_sb_buffer ;
}
2008-03-24 22:02:07 +03:00
}
2008-05-13 21:46:40 +04:00
2008-04-19 00:13:31 +04:00
fs_info - > bdi . ra_pages * = btrfs_super_num_devices ( disk_super ) ;
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 ;
2008-05-07 19:43:44 +04:00
sb - > s_blocksize = sectorsize ;
sb - > s_blocksize_bits = blksize_bits ( sectorsize ) ;
2007-10-16 00:15:53 +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-06-26 00:01:30 +04:00
mutex_lock ( & fs_info - > chunk_mutex ) ;
2008-03-24 22:01:56 +03:00
ret = btrfs_read_sys_array ( tree_root ) ;
2008-06-26 00:01:30 +04:00
mutex_unlock ( & fs_info - > chunk_mutex ) ;
2008-04-25 17:04:37 +04:00
if ( ret ) {
printk ( " btrfs: failed to read the system array on %s \n " ,
sb - > s_id ) ;
goto fail_sys_array ;
}
2008-03-24 22:01:56 +03:00
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 ) ,
2008-05-12 20:59:19 +04:00
blocksize , 0 ) ;
2008-03-24 22:01:56 +03:00
BUG_ON ( ! chunk_root - > node ) ;
2008-04-15 23:41:47 +04:00
read_extent_buffer ( chunk_root - > node , fs_info - > chunk_tree_uuid ,
( unsigned long ) btrfs_header_chunk_tree_uuid ( chunk_root - > node ) ,
BTRFS_UUID_SIZE ) ;
2008-06-26 00:01:30 +04:00
mutex_lock ( & fs_info - > chunk_mutex ) ;
2008-03-24 22:01:56 +03:00
ret = btrfs_read_chunk_tree ( chunk_root ) ;
2008-06-26 00:01:30 +04:00
mutex_unlock ( & fs_info - > chunk_mutex ) ;
2008-03-24 22:01:56 +03:00
BUG_ON ( ret ) ;
2008-05-13 21:46:40 +04:00
btrfs_close_extra_devices ( fs_devices ) ;
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 ) ,
2008-05-12 20:59:19 +04:00
blocksize , 0 ) ;
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 ;
2008-06-26 00:01:31 +04:00
fs_info - > cleaner_kthread = kthread_run ( cleaner_kthread , tree_root ,
" btrfs-cleaner " ) ;
if ( ! fs_info - > cleaner_kthread )
goto fail_extent_root ;
fs_info - > transaction_kthread = kthread_run ( transaction_kthread ,
tree_root ,
" btrfs-transaction " ) ;
if ( ! fs_info - > transaction_kthread )
2008-06-26 00:01:31 +04:00
goto fail_cleaner ;
2008-06-26 00:01:31 +04:00
2008-04-04 23:40:00 +04:00
2007-04-09 18:42:37 +04:00
return tree_root ;
2007-06-12 14:35:45 +04:00
2008-06-26 00:01:31 +04:00
fail_cleaner :
2008-06-26 00:01:31 +04:00
kthread_stop ( fs_info - > cleaner_kthread ) ;
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 :
2007-10-16 00:14:19 +04:00
free_extent_buffer ( tree_root - > node ) ;
2008-04-25 17:04:37 +04:00
fail_sys_array :
2007-06-12 14:35:45 +04:00
fail_sb_buffer :
2008-07-17 20:53:51 +04:00
btrfs_stop_workers ( & fs_info - > fixup_workers ) ;
2008-06-12 00:50:36 +04:00
btrfs_stop_workers ( & fs_info - > workers ) ;
btrfs_stop_workers ( & fs_info - > endio_workers ) ;
2008-07-17 20:53:50 +04:00
btrfs_stop_workers ( & fs_info - > endio_write_workers ) ;
2008-06-12 22:46:17 +04:00
btrfs_stop_workers ( & fs_info - > submit_workers ) ;
2008-06-12 05:47:56 +04:00
fail_iput :
iput ( fs_info - > btree_inode ) ;
2007-06-12 14:35:45 +04:00
fail :
2008-05-13 21:46:40 +04:00
btrfs_close_devices ( fs_info - > fs_devices ) ;
2008-04-25 17:04:37 +04:00
btrfs_mapping_tree_free ( & fs_info - > mapping_tree ) ;
2007-06-12 14:35:45 +04:00
kfree ( extent_root ) ;
kfree ( tree_root ) ;
2008-06-10 06:19:40 +04:00
# if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
2008-03-26 23:24:23 +03:00
bdi_destroy ( & fs_info - > bdi ) ;
2008-04-14 17:48:18 +04:00
# endif
2007-06-12 14:35:45 +04:00
kfree ( fs_info ) ;
return ERR_PTR ( err ) ;
2007-02-02 17:18:22 +03:00
}
2008-04-11 00:19:33 +04:00
static void btrfs_end_buffer_write_sync ( struct buffer_head * bh , int uptodate )
{
char b [ BDEVNAME_SIZE ] ;
if ( uptodate ) {
set_buffer_uptodate ( bh ) ;
} else {
if ( ! buffer_eopnotsupp ( bh ) & & printk_ratelimit ( ) ) {
printk ( KERN_WARNING " lost page write due to "
" I/O error on %s \n " ,
bdevname ( bh - > b_bdev , b ) ) ;
}
2008-05-12 21:39:03 +04:00
/* note, we dont' set_buffer_write_io_error because we have
* our own ways of dealing with the IO errors
*/
2008-04-11 00:19:33 +04:00
clear_buffer_uptodate ( bh ) ;
}
unlock_buffer ( bh ) ;
put_bh ( bh ) ;
}
int write_all_supers ( struct btrfs_root * root )
{
struct list_head * cur ;
struct list_head * head = & root - > fs_info - > fs_devices - > devices ;
struct btrfs_device * dev ;
2008-05-07 19:43:44 +04:00
struct btrfs_super_block * sb ;
2008-04-11 00:19:33 +04:00
struct btrfs_dev_item * dev_item ;
struct buffer_head * bh ;
int ret ;
int do_barriers ;
2008-04-29 17:38:00 +04:00
int max_errors ;
int total_errors = 0 ;
2008-05-07 19:43:44 +04:00
u32 crc ;
u64 flags ;
2008-04-11 00:19:33 +04:00
2008-04-29 17:38:00 +04:00
max_errors = btrfs_super_num_devices ( & root - > fs_info - > super_copy ) - 1 ;
2008-04-11 00:19:33 +04:00
do_barriers = ! btrfs_test_opt ( root , NOBARRIER ) ;
2008-05-07 19:43:44 +04:00
sb = & root - > fs_info - > super_for_commit ;
dev_item = & sb - > dev_item ;
2008-04-11 00:19:33 +04:00
list_for_each ( cur , head ) {
dev = list_entry ( cur , struct btrfs_device , dev_list ) ;
2008-05-13 21:46:40 +04:00
if ( ! dev - > bdev ) {
total_errors + + ;
continue ;
}
if ( ! dev - > in_fs_metadata )
continue ;
2008-05-07 19:43:44 +04:00
btrfs_set_stack_device_type ( dev_item , dev - > type ) ;
btrfs_set_stack_device_id ( dev_item , dev - > devid ) ;
btrfs_set_stack_device_total_bytes ( dev_item , dev - > total_bytes ) ;
btrfs_set_stack_device_bytes_used ( dev_item , dev - > bytes_used ) ;
btrfs_set_stack_device_io_align ( dev_item , dev - > io_align ) ;
btrfs_set_stack_device_io_width ( dev_item , dev - > io_width ) ;
btrfs_set_stack_device_sector_size ( dev_item , dev - > sector_size ) ;
memcpy ( dev_item - > uuid , dev - > uuid , BTRFS_UUID_SIZE ) ;
flags = btrfs_super_flags ( sb ) ;
btrfs_set_super_flags ( sb , flags | BTRFS_HEADER_FLAG_WRITTEN ) ;
crc = ~ ( u32 ) 0 ;
crc = btrfs_csum_data ( root , ( char * ) sb + BTRFS_CSUM_SIZE , crc ,
BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE ) ;
btrfs_csum_final ( crc , sb - > csum ) ;
bh = __getblk ( dev - > bdev , BTRFS_SUPER_INFO_OFFSET / 4096 ,
2008-04-11 00:19:33 +04:00
BTRFS_SUPER_INFO_SIZE ) ;
2008-05-07 19:43:44 +04:00
memcpy ( bh - > b_data , sb , BTRFS_SUPER_INFO_SIZE ) ;
2008-04-11 00:19:33 +04:00
dev - > pending_io = bh ;
get_bh ( bh ) ;
set_buffer_uptodate ( bh ) ;
lock_buffer ( bh ) ;
bh - > b_end_io = btrfs_end_buffer_write_sync ;
if ( do_barriers & & dev - > barriers ) {
ret = submit_bh ( WRITE_BARRIER , bh ) ;
if ( ret = = - EOPNOTSUPP ) {
printk ( " btrfs: disabling barriers on dev %s \n " ,
dev - > name ) ;
set_buffer_uptodate ( bh ) ;
dev - > barriers = 0 ;
get_bh ( bh ) ;
lock_buffer ( bh ) ;
ret = submit_bh ( WRITE , bh ) ;
}
} else {
ret = submit_bh ( WRITE , bh ) ;
}
2008-04-29 17:38:00 +04:00
if ( ret )
total_errors + + ;
2008-04-11 00:19:33 +04:00
}
2008-04-29 17:38:00 +04:00
if ( total_errors > max_errors ) {
printk ( " btrfs: %d errors while writing supers \n " , total_errors ) ;
BUG ( ) ;
}
total_errors = 0 ;
2008-04-11 00:19:33 +04:00
list_for_each ( cur , head ) {
dev = list_entry ( cur , struct btrfs_device , dev_list ) ;
2008-05-13 21:46:40 +04:00
if ( ! dev - > bdev )
continue ;
if ( ! dev - > in_fs_metadata )
continue ;
2008-04-11 00:19:33 +04:00
BUG_ON ( ! dev - > pending_io ) ;
bh = dev - > pending_io ;
wait_on_buffer ( bh ) ;
if ( ! buffer_uptodate ( dev - > pending_io ) ) {
if ( do_barriers & & dev - > barriers ) {
printk ( " btrfs: disabling barriers on dev %s \n " ,
dev - > name ) ;
set_buffer_uptodate ( bh ) ;
get_bh ( bh ) ;
lock_buffer ( bh ) ;
dev - > barriers = 0 ;
ret = submit_bh ( WRITE , bh ) ;
BUG_ON ( ret ) ;
wait_on_buffer ( bh ) ;
2008-05-12 21:39:03 +04:00
if ( ! buffer_uptodate ( bh ) )
total_errors + + ;
2008-04-11 00:19:33 +04:00
} else {
2008-04-29 17:38:00 +04:00
total_errors + + ;
2008-04-11 00:19:33 +04:00
}
}
dev - > pending_io = NULL ;
brelse ( bh ) ;
}
2008-04-29 17:38:00 +04:00
if ( total_errors > max_errors ) {
printk ( " btrfs: %d errors while writing supers \n " , total_errors ) ;
BUG ( ) ;
}
2008-04-11 00:19:33 +04:00
return 0 ;
}
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
2008-04-11 00:19:33 +04:00
ret = write_all_supers ( root ) ;
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 ;
2008-06-26 00:01:30 +04:00
smp_mb ( ) ;
2008-06-26 00:01:31 +04:00
kthread_stop ( root - > fs_info - > transaction_kthread ) ;
kthread_stop ( root - > fs_info - > cleaner_kthread ) ;
btrfs_clean_old_snapshots ( root ) ;
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 ) ;
2008-04-30 21:59:35 +04:00
2007-03-22 22:59:16 +03:00
write_ctree_super ( NULL , root ) ;
2007-04-09 18:42:37 +04:00
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 ) ;
}
2008-07-28 23:32:19 +04:00
if ( fs_info - > total_ref_cache_size ) {
printk ( " btrfs: at umount reference cache size %Lu \n " ,
fs_info - > total_ref_cache_size ) ;
}
2008-07-31 00:29:20 +04:00
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-04-27 00:46:15 +04:00
btrfs_free_block_groups ( root - > fs_info ) ;
2008-08-05 21:30:48 +04:00
fs_info - > closing = 2 ;
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 ) ;
2007-10-16 00:15:53 +04:00
truncate_inode_pages ( fs_info - > btree_inode - > i_mapping , 0 ) ;
2008-04-19 00:11:30 +04:00
2008-07-17 20:53:51 +04:00
btrfs_stop_workers ( & fs_info - > fixup_workers ) ;
2008-06-12 00:50:36 +04:00
btrfs_stop_workers ( & fs_info - > workers ) ;
btrfs_stop_workers ( & fs_info - > endio_workers ) ;
2008-07-17 20:53:50 +04:00
btrfs_stop_workers ( & fs_info - > endio_write_workers ) ;
2008-06-12 22:46:17 +04:00
btrfs_stop_workers ( & fs_info - > submit_workers ) ;
2008-04-30 21:59:35 +04: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-05-13 21:46:40 +04:00
btrfs_close_devices ( fs_info - > fs_devices ) ;
2008-03-24 22:01:56 +03:00
btrfs_mapping_tree_free ( & fs_info - > mapping_tree ) ;
2008-04-14 17:48:18 +04:00
2008-06-10 06:19:40 +04:00
# if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
2008-03-26 17:28:07 +03:00
bdi_destroy ( & fs_info - > bdi ) ;
2008-04-14 17:48:18 +04:00
# endif
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 ;
}
2008-05-12 21:39:03 +04:00
int btrfs_buffer_uptodate ( struct extent_buffer * buf , u64 parent_transid )
2007-10-16 00:14:19 +04:00
{
2008-05-12 21:39:03 +04:00
int ret ;
2007-10-16 00:18:55 +04:00
struct inode * btree_inode = buf - > first_page - > mapping - > host ;
2008-05-12 21:39:03 +04:00
ret = extent_buffer_uptodate ( & BTRFS_I ( btree_inode ) - > io_tree , buf ) ;
if ( ! ret )
return ret ;
ret = verify_parent_transid ( & BTRFS_I ( btree_inode ) - > io_tree , buf ,
parent_transid ) ;
return ! ret ;
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
2008-06-26 00:01:30 +04:00
WARN_ON ( ! btrfs_tree_locked ( buf ) ) ;
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
}
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
{
2008-05-09 19:52:25 +04:00
/*
* looks as though older kernels can get into trouble with
* this code , they end up stuck in balance_dirty_pages forever
*/
2008-04-30 21:59:35 +04:00
struct extent_io_tree * tree ;
u64 num_dirty ;
u64 start = 0 ;
unsigned long thresh = 16 * 1024 * 1024 ;
tree = & BTRFS_I ( root - > fs_info - > btree_inode ) - > io_tree ;
if ( current_is_pdflush ( ) )
return ;
num_dirty = count_range_bits ( tree , & start , ( u64 ) - 1 ,
thresh , EXTENT_DIRTY ) ;
if ( num_dirty > thresh ) {
balance_dirty_pages_ratelimited_nr (
2008-02-18 20:12:38 +03:00
root - > fs_info - > btree_inode - > i_mapping , 1 ) ;
2008-04-30 21:59:35 +04:00
}
2008-05-09 19:52:25 +04:00
return ;
2007-05-02 23:53:43 +04:00
}
2007-10-16 00:17:34 +04:00
2008-05-12 20:59:19 +04:00
int btrfs_read_buffer ( struct extent_buffer * buf , u64 parent_transid )
2007-10-16 00:17:34 +04:00
{
2007-10-16 00:18:55 +04:00
struct btrfs_root * root = BTRFS_I ( buf - > first_page - > mapping - > host ) - > root ;
2008-04-10 00:28:12 +04:00
int ret ;
2008-05-12 20:59:19 +04:00
ret = btree_read_extent_buffer_pages ( root , buf , 0 , parent_transid ) ;
2008-04-10 00:28:12 +04:00
if ( ret = = 0 ) {
buf - > flags | = EXTENT_UPTODATE ;
}
return ret ;
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-04-10 00:28:12 +04:00
. readpage_end_io_hook = btree_readpage_end_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
} ;