2007-06-12 21:07:21 +08: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-06-12 18:35:45 +08:00
# include <linux/fs.h>
# include <linux/pagemap.h>
# include <linux/highmem.h>
# include <linux/time.h>
# include <linux/init.h>
# include <linux/string.h>
# include <linux/smp_lock.h>
# include <linux/backing-dev.h>
# include <linux/mpage.h>
# include <linux/swap.h>
# include <linux/writeback.h>
# include <linux/statfs.h>
# include <linux/compat.h>
2007-07-26 00:31:35 +08:00
# include <linux/version.h>
2007-06-12 18:35:45 +08:00
# include "ctree.h"
# include "disk-io.h"
# include "transaction.h"
# include "btrfs_inode.h"
2008-01-09 04:46:30 +08:00
# include "ordered-data.h"
2007-06-12 18:35:45 +08:00
# include "ioctl.h"
# include "print-tree.h"
static int btrfs_copy_from_user ( loff_t pos , int num_pages , int write_bytes ,
struct page * * prepared_pages ,
const char __user * buf )
{
long page_fault = 0 ;
int i ;
int offset = pos & ( PAGE_CACHE_SIZE - 1 ) ;
for ( i = 0 ; i < num_pages & & write_bytes > 0 ; i + + , offset = 0 ) {
size_t count = min_t ( size_t ,
PAGE_CACHE_SIZE - offset , write_bytes ) ;
struct page * page = prepared_pages [ i ] ;
fault_in_pages_readable ( buf , count ) ;
/* Copy data from userspace to the current page */
kmap ( page ) ;
page_fault = __copy_from_user ( page_address ( page ) + offset ,
buf , count ) ;
/* Flush processor's dcache for this page */
flush_dcache_page ( page ) ;
kunmap ( page ) ;
buf + = count ;
write_bytes - = count ;
if ( page_fault )
break ;
}
return page_fault ? - EFAULT : 0 ;
}
static void btrfs_drop_pages ( struct page * * pages , size_t num_pages )
{
size_t i ;
for ( i = 0 ; i < num_pages ; i + + ) {
if ( ! pages [ i ] )
break ;
unlock_page ( pages [ i ] ) ;
mark_page_accessed ( pages [ i ] ) ;
page_cache_release ( pages [ i ] ) ;
}
}
2008-01-03 23:01:48 +08:00
static int noinline insert_inline_extent ( struct btrfs_trans_handle * trans ,
2007-08-28 04:49:44 +08:00
struct btrfs_root * root , struct inode * inode ,
2007-10-16 04:18:25 +08:00
u64 offset , size_t size ,
struct page * * pages , size_t page_offset ,
int num_pages )
2007-06-23 02:16:25 +08:00
{
struct btrfs_key key ;
struct btrfs_path * path ;
2007-10-16 04:14:19 +08:00
struct extent_buffer * leaf ;
char * kaddr ;
unsigned long ptr ;
2007-06-23 02:16:25 +08:00
struct btrfs_file_extent_item * ei ;
2007-10-16 04:18:25 +08:00
struct page * page ;
2007-06-23 02:16:25 +08:00
u32 datasize ;
int err = 0 ;
int ret ;
2007-10-16 04:18:25 +08:00
int i ;
ssize_t cur_size ;
2007-06-23 02:16:25 +08:00
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
btrfs_set_trans_block_group ( trans , inode ) ;
key . objectid = inode - > i_ino ;
key . offset = offset ;
btrfs_set_key_type ( & key , BTRFS_EXTENT_DATA_KEY ) ;
2007-10-16 04:18:25 +08:00
ret = btrfs_search_slot ( trans , root , & key , path , 0 , 1 ) ;
if ( ret < 0 ) {
2007-06-23 02:16:25 +08:00
err = ret ;
goto fail ;
}
2007-10-16 04:18:25 +08:00
if ( ret = = 1 ) {
2007-11-01 23:28:41 +08:00
struct btrfs_key found_key ;
if ( path - > slots [ 0 ] = = 0 )
goto insert ;
2007-10-16 04:18:25 +08:00
path - > slots [ 0 ] - - ;
leaf = path - > nodes [ 0 ] ;
2007-11-01 23:28:41 +08:00
btrfs_item_key_to_cpu ( leaf , & found_key , path - > slots [ 0 ] ) ;
if ( found_key . objectid ! = inode - > i_ino )
goto insert ;
if ( found_key . type ! = BTRFS_EXTENT_DATA_KEY )
goto insert ;
2007-10-16 04:18:25 +08:00
ei = btrfs_item_ptr ( leaf , path - > slots [ 0 ] ,
struct btrfs_file_extent_item ) ;
if ( btrfs_file_extent_type ( leaf , ei ) ! =
BTRFS_FILE_EXTENT_INLINE ) {
goto insert ;
}
btrfs_item_key_to_cpu ( leaf , & key , path - > slots [ 0 ] ) ;
ret = 0 ;
}
if ( ret = = 0 ) {
u32 found_size ;
2007-10-26 03:42:57 +08:00
u64 found_end ;
2007-10-16 04:18:25 +08:00
leaf = path - > nodes [ 0 ] ;
ei = btrfs_item_ptr ( leaf , path - > slots [ 0 ] ,
struct btrfs_file_extent_item ) ;
if ( btrfs_file_extent_type ( leaf , ei ) ! =
BTRFS_FILE_EXTENT_INLINE ) {
err = ret ;
btrfs_print_leaf ( root , leaf ) ;
printk ( " found wasn't inline offset %Lu inode %lu \n " ,
offset , inode - > i_ino ) ;
goto fail ;
}
found_size = btrfs_file_extent_inline_len ( leaf ,
btrfs_item_nr ( leaf , path - > slots [ 0 ] ) ) ;
2007-10-26 03:42:57 +08:00
found_end = key . offset + found_size ;
2007-10-16 04:18:25 +08:00
2007-10-26 03:42:57 +08:00
if ( found_end < offset + size ) {
2007-10-16 04:18:25 +08:00
btrfs_release_path ( root , path ) ;
ret = btrfs_search_slot ( trans , root , & key , path ,
2007-10-26 03:42:57 +08:00
offset + size - found_end , 1 ) ;
2007-10-16 04:18:25 +08:00
BUG_ON ( ret ! = 0 ) ;
2007-11-01 23:28:41 +08:00
2007-10-16 04:18:25 +08:00
ret = btrfs_extend_item ( trans , root , path ,
2007-10-26 03:42:57 +08:00
offset + size - found_end ) ;
2007-10-16 04:18:25 +08:00
if ( ret ) {
err = ret ;
goto fail ;
}
leaf = path - > nodes [ 0 ] ;
ei = btrfs_item_ptr ( leaf , path - > slots [ 0 ] ,
struct btrfs_file_extent_item ) ;
2008-02-09 02:49:28 +08:00
inode - > i_blocks + = ( offset + size - found_end ) > > 9 ;
2007-10-16 04:18:25 +08:00
}
2007-10-26 03:42:57 +08:00
if ( found_end < offset ) {
ptr = btrfs_file_extent_inline_start ( ei ) + found_size ;
memset_extent_buffer ( leaf , 0 , ptr , offset - found_end ) ;
}
2007-10-16 04:18:25 +08:00
} else {
insert :
btrfs_release_path ( root , path ) ;
2007-10-26 03:42:57 +08:00
datasize = offset + size - key . offset ;
2008-02-09 02:49:28 +08:00
inode - > i_blocks + = datasize > > 9 ;
2007-10-26 03:42:57 +08:00
datasize = btrfs_file_extent_calc_inline_size ( datasize ) ;
2007-10-16 04:18:25 +08:00
ret = btrfs_insert_empty_item ( trans , root , path , & key ,
datasize ) ;
if ( ret ) {
err = ret ;
printk ( " got bad ret %d \n " , ret ) ;
goto fail ;
}
leaf = path - > nodes [ 0 ] ;
ei = btrfs_item_ptr ( leaf , path - > slots [ 0 ] ,
struct btrfs_file_extent_item ) ;
btrfs_set_file_extent_generation ( leaf , ei , trans - > transid ) ;
btrfs_set_file_extent_type ( leaf , ei , BTRFS_FILE_EXTENT_INLINE ) ;
}
2007-10-26 03:42:57 +08:00
ptr = btrfs_file_extent_inline_start ( ei ) + offset - key . offset ;
2007-10-16 04:18:25 +08:00
cur_size = size ;
i = 0 ;
while ( size > 0 ) {
page = pages [ i ] ;
kaddr = kmap_atomic ( page , KM_USER0 ) ;
2007-10-19 21:22:59 +08:00
cur_size = min_t ( size_t , PAGE_CACHE_SIZE - page_offset , size ) ;
2007-10-16 04:18:25 +08:00
write_extent_buffer ( leaf , kaddr + page_offset , ptr , cur_size ) ;
kunmap_atomic ( kaddr , KM_USER0 ) ;
page_offset = 0 ;
ptr + = cur_size ;
size - = cur_size ;
if ( i > = num_pages ) {
printk ( " i %d num_pages %d \n " , i , num_pages ) ;
}
i + + ;
}
2007-10-16 04:14:19 +08:00
btrfs_mark_buffer_dirty ( leaf ) ;
2007-06-23 02:16:25 +08:00
fail :
btrfs_free_path ( path ) ;
return err ;
}
2008-01-03 23:01:48 +08:00
static int noinline dirty_and_release_pages ( struct btrfs_trans_handle * trans ,
2007-06-12 18:35:45 +08:00
struct btrfs_root * root ,
struct file * file ,
struct page * * pages ,
size_t num_pages ,
loff_t pos ,
size_t write_bytes )
{
int err = 0 ;
2007-08-28 04:49:44 +08:00
int i ;
2007-12-19 05:15:09 +08:00
struct inode * inode = fdentry ( file ) - > d_inode ;
2008-01-25 05:13:08 +08:00
struct extent_io_tree * io_tree = & BTRFS_I ( inode ) - > io_tree ;
2007-10-16 04:15:53 +08:00
u64 hint_byte ;
u64 num_bytes ;
2007-08-28 04:49:44 +08:00
u64 start_pos ;
u64 end_of_last_block ;
u64 end_pos = pos + write_bytes ;
2007-11-06 23:26:26 +08:00
u64 inline_size ;
2007-08-28 04:49:44 +08:00
loff_t isize = i_size_read ( inode ) ;
2007-06-12 18:35:45 +08:00
2007-10-16 04:14:19 +08:00
start_pos = pos & ~ ( ( u64 ) root - > sectorsize - 1 ) ;
2007-10-16 04:15:53 +08:00
num_bytes = ( write_bytes + pos - start_pos +
root - > sectorsize - 1 ) & ~ ( ( u64 ) root - > sectorsize - 1 ) ;
2007-06-12 18:35:45 +08:00
2007-10-16 04:15:53 +08:00
end_of_last_block = start_pos + num_bytes - 1 ;
2008-01-25 05:13:08 +08:00
lock_extent ( io_tree , start_pos , end_of_last_block , GFP_NOFS ) ;
2007-08-28 04:49:44 +08:00
mutex_lock ( & root - > fs_info - > fs_mutex ) ;
trans = btrfs_start_transaction ( root , 1 ) ;
if ( ! trans ) {
err = - ENOMEM ;
goto out_unlock ;
}
btrfs_set_trans_block_group ( trans , inode ) ;
2007-10-16 04:15:53 +08:00
hint_byte = 0 ;
2007-08-28 04:49:44 +08:00
if ( ( end_of_last_block & 4095 ) = = 0 ) {
2007-09-11 08:02:22 +08:00
printk ( " strange end of last %Lu %zu %Lu \n " , start_pos , write_bytes , end_of_last_block ) ;
2007-08-28 04:49:44 +08:00
}
2008-01-25 05:13:08 +08:00
set_extent_uptodate ( io_tree , start_pos , end_of_last_block , GFP_NOFS ) ;
2007-08-28 04:49:44 +08:00
/* FIXME...EIEIO, ENOSPC and more */
/* insert any holes we need to create */
2008-02-05 22:07:49 +08:00
if ( isize < end_pos ) {
2007-08-28 04:49:44 +08:00
u64 last_pos_in_file ;
u64 hole_size ;
2007-10-16 04:14:19 +08:00
u64 mask = root - > sectorsize - 1 ;
2007-08-28 04:49:44 +08:00
last_pos_in_file = ( isize + mask ) & ~ mask ;
2008-01-23 05:47:59 +08:00
hole_size = ( end_pos - last_pos_in_file + mask ) & ~ mask ;
2008-02-05 22:07:49 +08:00
if ( last_pos_in_file < end_pos ) {
2007-08-30 23:54:02 +08:00
err = btrfs_drop_extents ( trans , root , inode ,
last_pos_in_file ,
last_pos_in_file + hole_size ,
2007-10-16 04:18:25 +08:00
last_pos_in_file ,
2007-10-16 04:15:53 +08:00
& hint_byte ) ;
2007-08-30 23:54:02 +08:00
if ( err )
goto failed ;
2007-08-28 04:49:44 +08:00
err = btrfs_insert_file_extent ( trans , root ,
inode - > i_ino ,
last_pos_in_file ,
0 , 0 , hole_size ) ;
2008-01-25 05:13:08 +08:00
btrfs_drop_extent_cache ( inode , last_pos_in_file ,
last_pos_in_file + hole_size - 1 ) ;
2008-01-23 05:47:59 +08:00
btrfs_check_file ( root , inode ) ;
2007-08-28 04:49:44 +08:00
}
if ( err )
2007-06-12 18:35:45 +08:00
goto failed ;
2007-08-28 04:49:44 +08:00
}
/*
* either allocate an extent for the new bytes or setup the key
* to show we are doing inline data in the extent
*/
2007-10-16 04:18:25 +08:00
inline_size = end_pos ;
if ( isize > = BTRFS_MAX_INLINE_DATA_SIZE ( root ) | |
2008-01-30 05:03:38 +08:00
inline_size > root - > fs_info - > max_inline | |
( inline_size & ( root - > sectorsize - 1 ) ) = = 0 | |
2007-10-16 04:18:25 +08:00
inline_size > = BTRFS_MAX_INLINE_DATA_SIZE ( root ) ) {
2007-08-28 04:49:44 +08:00
u64 last_end ;
2007-12-22 05:27:21 +08:00
u64 existing_delalloc = 0 ;
2007-10-16 04:18:25 +08:00
2007-08-28 04:49:44 +08:00
for ( i = 0 ; i < num_pages ; i + + ) {
struct page * p = pages [ i ] ;
SetPageUptodate ( p ) ;
2007-08-28 04:49:44 +08:00
set_page_dirty ( p ) ;
2007-06-12 18:35:45 +08:00
}
2007-10-31 04:56:53 +08:00
last_end = ( u64 ) ( pages [ num_pages - 1 ] - > index ) < <
PAGE_CACHE_SHIFT ;
2007-08-28 04:49:44 +08:00
last_end + = PAGE_CACHE_SIZE - 1 ;
2007-12-22 05:27:21 +08:00
if ( start_pos < isize ) {
u64 delalloc_start = start_pos ;
2008-01-25 05:13:08 +08:00
existing_delalloc = count_range_bits ( io_tree ,
2007-12-22 05:27:21 +08:00
& delalloc_start ,
end_of_last_block , ( u64 ) - 1 ,
EXTENT_DELALLOC ) ;
}
2008-01-25 05:13:08 +08:00
set_extent_delalloc ( io_tree , start_pos , end_of_last_block ,
2007-08-28 04:49:44 +08:00
GFP_NOFS ) ;
2008-01-09 04:46:30 +08:00
btrfs_add_ordered_inode ( inode ) ;
2007-08-28 04:49:44 +08:00
} else {
2007-10-16 04:18:25 +08:00
u64 aligned_end ;
2007-08-28 04:49:44 +08:00
/* step one, delete the existing extents in this range */
2007-10-16 04:18:25 +08:00
aligned_end = ( pos + write_bytes + root - > sectorsize - 1 ) &
~ ( ( u64 ) root - > sectorsize - 1 ) ;
2007-08-30 23:54:02 +08:00
err = btrfs_drop_extents ( trans , root , inode , start_pos ,
2007-11-01 23:28:41 +08:00
aligned_end , aligned_end , & hint_byte ) ;
2007-08-30 23:54:02 +08:00
if ( err )
goto failed ;
2007-11-06 23:26:26 +08:00
if ( isize > inline_size )
inline_size = min_t ( u64 , isize , aligned_end ) ;
inline_size - = start_pos ;
2007-08-28 04:49:44 +08:00
err = insert_inline_extent ( trans , root , inode , start_pos ,
2007-11-06 23:26:26 +08:00
inline_size , pages , 0 , num_pages ) ;
2008-01-25 05:13:08 +08:00
btrfs_drop_extent_cache ( inode , start_pos , aligned_end - 1 ) ;
2007-08-28 04:49:44 +08:00
BUG_ON ( err ) ;
}
if ( end_pos > isize ) {
i_size_write ( inode , end_pos ) ;
btrfs_update_inode ( trans , root , inode ) ;
2007-06-12 18:35:45 +08:00
}
failed :
2007-08-28 04:49:44 +08:00
err = btrfs_end_transaction ( trans , root ) ;
out_unlock :
mutex_unlock ( & root - > fs_info - > fs_mutex ) ;
2008-01-25 05:13:08 +08:00
unlock_extent ( io_tree , start_pos , end_of_last_block , GFP_NOFS ) ;
2007-06-12 18:35:45 +08:00
return err ;
}
2007-08-28 04:49:44 +08:00
int btrfs_drop_extent_cache ( struct inode * inode , u64 start , u64 end )
{
struct extent_map * em ;
2008-04-17 23:29:12 +08:00
struct extent_map * split = NULL ;
struct extent_map * split2 = NULL ;
2007-08-28 04:49:44 +08:00
struct extent_map_tree * em_tree = & BTRFS_I ( inode ) - > extent_tree ;
2008-02-15 23:40:50 +08:00
u64 len = end - start + 1 ;
2008-04-17 23:29:12 +08:00
int ret ;
int testend = 1 ;
2007-08-28 04:49:44 +08:00
2008-04-17 23:29:12 +08:00
if ( end = = ( u64 ) - 1 ) {
2008-02-15 23:40:50 +08:00
len = ( u64 ) - 1 ;
2008-04-17 23:29:12 +08:00
testend = 0 ;
}
2007-08-28 04:49:44 +08:00
while ( 1 ) {
2008-04-17 23:29:12 +08:00
if ( ! split )
split = alloc_extent_map ( GFP_NOFS ) ;
if ( ! split2 )
split2 = alloc_extent_map ( GFP_NOFS ) ;
2008-01-25 05:13:08 +08:00
spin_lock ( & em_tree - > lock ) ;
2008-02-15 23:40:50 +08:00
em = lookup_extent_mapping ( em_tree , start , len ) ;
2008-01-25 05:13:08 +08:00
if ( ! em ) {
spin_unlock ( & em_tree - > lock ) ;
2007-08-28 04:49:44 +08:00
break ;
2008-01-25 05:13:08 +08:00
}
2007-08-28 04:49:44 +08:00
remove_extent_mapping ( em_tree , em ) ;
2008-04-17 23:29:12 +08:00
if ( em - > block_start < EXTENT_MAP_LAST_BYTE & &
em - > start < start ) {
split - > start = em - > start ;
split - > len = start - em - > start ;
split - > block_start = em - > block_start ;
split - > bdev = em - > bdev ;
split - > flags = em - > flags ;
ret = add_extent_mapping ( em_tree , split ) ;
BUG_ON ( ret ) ;
free_extent_map ( split ) ;
split = split2 ;
split2 = NULL ;
}
if ( em - > block_start < EXTENT_MAP_LAST_BYTE & &
testend & & em - > start + em - > len > start + len ) {
u64 diff = start + len - em - > start ;
split - > start = start + len ;
split - > len = em - > start + em - > len - ( start + len ) ;
split - > bdev = em - > bdev ;
split - > flags = em - > flags ;
split - > block_start = em - > block_start + diff ;
ret = add_extent_mapping ( em_tree , split ) ;
BUG_ON ( ret ) ;
free_extent_map ( split ) ;
split = NULL ;
}
2008-01-25 05:13:08 +08:00
spin_unlock ( & em_tree - > lock ) ;
2007-08-28 04:49:44 +08:00
/* once for us */
free_extent_map ( em ) ;
/* once for the tree*/
free_extent_map ( em ) ;
}
2008-04-17 23:29:12 +08:00
if ( split )
free_extent_map ( split ) ;
if ( split2 )
free_extent_map ( split2 ) ;
2007-08-28 04:49:44 +08:00
return 0 ;
}
2008-01-23 05:47:59 +08:00
int btrfs_check_file ( struct btrfs_root * root , struct inode * inode )
{
return 0 ;
#if 0
struct btrfs_path * path ;
struct btrfs_key found_key ;
struct extent_buffer * leaf ;
struct btrfs_file_extent_item * extent ;
u64 last_offset = 0 ;
int nritems ;
int slot ;
int found_type ;
int ret ;
int err = 0 ;
u64 extent_end = 0 ;
path = btrfs_alloc_path ( ) ;
ret = btrfs_lookup_file_extent ( NULL , root , path , inode - > i_ino ,
last_offset , 0 ) ;
while ( 1 ) {
nritems = btrfs_header_nritems ( path - > nodes [ 0 ] ) ;
if ( path - > slots [ 0 ] > = nritems ) {
ret = btrfs_next_leaf ( root , path ) ;
if ( ret )
goto out ;
nritems = btrfs_header_nritems ( path - > nodes [ 0 ] ) ;
}
slot = path - > slots [ 0 ] ;
leaf = path - > nodes [ 0 ] ;
btrfs_item_key_to_cpu ( leaf , & found_key , slot ) ;
if ( found_key . objectid ! = inode - > i_ino )
break ;
if ( found_key . type ! = BTRFS_EXTENT_DATA_KEY )
goto out ;
2008-02-09 02:49:28 +08:00
if ( found_key . offset < last_offset ) {
2008-01-23 05:47:59 +08:00
WARN_ON ( 1 ) ;
btrfs_print_leaf ( root , leaf ) ;
printk ( " inode %lu found offset %Lu expected %Lu \n " ,
inode - > i_ino , found_key . offset , last_offset ) ;
err = 1 ;
goto out ;
}
extent = btrfs_item_ptr ( leaf , slot ,
struct btrfs_file_extent_item ) ;
found_type = btrfs_file_extent_type ( leaf , extent ) ;
if ( found_type = = BTRFS_FILE_EXTENT_REG ) {
extent_end = found_key . offset +
btrfs_file_extent_num_bytes ( leaf , extent ) ;
} else if ( found_type = = BTRFS_FILE_EXTENT_INLINE ) {
struct btrfs_item * item ;
item = btrfs_item_nr ( leaf , slot ) ;
extent_end = found_key . offset +
btrfs_file_extent_inline_len ( leaf , item ) ;
extent_end = ( extent_end + root - > sectorsize - 1 ) &
~ ( ( u64 ) root - > sectorsize - 1 ) ;
}
last_offset = extent_end ;
path - > slots [ 0 ] + + ;
}
2008-02-09 02:49:28 +08:00
if ( 0 & & last_offset < inode - > i_size ) {
2008-01-23 05:47:59 +08:00
WARN_ON ( 1 ) ;
btrfs_print_leaf ( root , leaf ) ;
printk ( " inode %lu found offset %Lu size %Lu \n " , inode - > i_ino ,
last_offset , inode - > i_size ) ;
err = 1 ;
}
out :
btrfs_free_path ( path ) ;
return err ;
# endif
}
2007-06-12 18:35:45 +08:00
/*
* this is very complex , but the basic idea is to drop all extents
* in the range start - end . hint_block is filled in with a block number
* that would be a good hint to the block allocator for this file .
*
* If an extent intersects the range but is not entirely inside the range
* it is either truncated or split . Anything entirely inside the range
* is deleted from the tree .
*/
int btrfs_drop_extents ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root , struct inode * inode ,
2007-11-30 23:09:33 +08:00
u64 start , u64 end , u64 inline_limit , u64 * hint_byte )
2007-06-12 18:35:45 +08:00
{
2007-11-30 23:09:33 +08:00
u64 extent_end = 0 ;
u64 search_start = start ;
2007-10-16 04:14:19 +08:00
struct extent_buffer * leaf ;
2007-06-12 18:35:45 +08:00
struct btrfs_file_extent_item * extent ;
struct btrfs_path * path ;
2007-11-30 23:09:33 +08:00
struct btrfs_key key ;
struct btrfs_file_extent_item old ;
int keep ;
int slot ;
2007-06-12 18:35:45 +08:00
int bookend ;
int found_type ;
int found_extent ;
int found_inline ;
2007-06-29 03:57:36 +08:00
int recow ;
2007-11-30 23:09:33 +08:00
int ret ;
2007-06-12 18:35:45 +08:00
2007-08-28 04:49:44 +08:00
btrfs_drop_extent_cache ( inode , start , end - 1 ) ;
2007-06-12 18:35:45 +08:00
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
while ( 1 ) {
2007-06-29 03:57:36 +08:00
recow = 0 ;
2007-06-12 18:35:45 +08:00
btrfs_release_path ( root , path ) ;
ret = btrfs_lookup_file_extent ( trans , root , path , inode - > i_ino ,
search_start , - 1 ) ;
if ( ret < 0 )
goto out ;
if ( ret > 0 ) {
if ( path - > slots [ 0 ] = = 0 ) {
ret = 0 ;
goto out ;
}
path - > slots [ 0 ] - - ;
}
2007-06-18 21:57:58 +08:00
next_slot :
2007-06-12 18:35:45 +08:00
keep = 0 ;
bookend = 0 ;
found_extent = 0 ;
found_inline = 0 ;
extent = NULL ;
2007-10-16 04:14:19 +08:00
leaf = path - > nodes [ 0 ] ;
2007-06-12 18:35:45 +08:00
slot = path - > slots [ 0 ] ;
2007-06-18 21:57:58 +08:00
ret = 0 ;
2007-10-16 04:14:19 +08:00
btrfs_item_key_to_cpu ( leaf , & key , slot ) ;
2008-02-06 04:40:36 +08:00
if ( btrfs_key_type ( & key ) = = BTRFS_EXTENT_DATA_KEY & &
key . offset > = end ) {
2007-06-12 18:35:45 +08:00
goto out ;
}
2008-02-06 04:40:36 +08:00
if ( btrfs_key_type ( & key ) > BTRFS_EXTENT_DATA_KEY | |
key . objectid ! = inode - > i_ino ) {
2007-06-12 18:35:45 +08:00
goto out ;
}
2007-06-29 03:57:36 +08:00
if ( recow ) {
search_start = key . offset ;
continue ;
}
2007-06-18 21:57:58 +08:00
if ( btrfs_key_type ( & key ) = = BTRFS_EXTENT_DATA_KEY ) {
extent = btrfs_item_ptr ( leaf , slot ,
struct btrfs_file_extent_item ) ;
2007-10-16 04:14:19 +08:00
found_type = btrfs_file_extent_type ( leaf , extent ) ;
2007-06-18 21:57:58 +08:00
if ( found_type = = BTRFS_FILE_EXTENT_REG ) {
2007-11-08 10:08:16 +08:00
extent_end =
btrfs_file_extent_disk_bytenr ( leaf ,
extent ) ;
if ( extent_end )
* hint_byte = extent_end ;
2007-06-18 21:57:58 +08:00
extent_end = key . offset +
2007-10-16 04:15:53 +08:00
btrfs_file_extent_num_bytes ( leaf , extent ) ;
2007-06-18 21:57:58 +08:00
found_extent = 1 ;
} else if ( found_type = = BTRFS_FILE_EXTENT_INLINE ) {
2007-10-16 04:14:19 +08:00
struct btrfs_item * item ;
item = btrfs_item_nr ( leaf , slot ) ;
2007-06-18 21:57:58 +08:00
found_inline = 1 ;
extent_end = key . offset +
2007-10-16 04:14:19 +08:00
btrfs_file_extent_inline_len ( leaf , item ) ;
2007-06-18 21:57:58 +08:00
}
} else {
extent_end = search_start ;
2007-06-12 18:35:45 +08:00
}
/* we found nothing we can drop */
2007-06-18 21:57:58 +08:00
if ( ( ! found_extent & & ! found_inline ) | |
search_start > = extent_end ) {
int nextret ;
u32 nritems ;
2007-10-16 04:14:19 +08:00
nritems = btrfs_header_nritems ( leaf ) ;
2007-06-18 21:57:58 +08:00
if ( slot > = nritems - 1 ) {
nextret = btrfs_next_leaf ( root , path ) ;
if ( nextret )
goto out ;
2007-06-29 03:57:36 +08:00
recow = 1 ;
2007-06-18 21:57:58 +08:00
} else {
path - > slots [ 0 ] + + ;
}
goto next_slot ;
2007-06-12 18:35:45 +08:00
}
if ( found_inline ) {
2007-10-16 04:14:19 +08:00
u64 mask = root - > sectorsize - 1 ;
2007-06-12 18:35:45 +08:00
search_start = ( extent_end + mask ) & ~ mask ;
} else
search_start = extent_end ;
2007-12-15 00:14:42 +08:00
if ( end < = extent_end & & start > = key . offset & & found_inline ) {
2007-11-01 23:28:41 +08:00
* hint_byte = EXTENT_MAP_INLINE ;
2007-12-15 00:14:42 +08:00
continue ;
2007-11-01 23:28:41 +08:00
}
2007-06-12 18:35:45 +08:00
if ( end < extent_end & & end > = key . offset ) {
if ( found_extent ) {
2007-10-16 04:15:53 +08:00
u64 disk_bytenr =
btrfs_file_extent_disk_bytenr ( leaf , extent ) ;
u64 disk_num_bytes =
btrfs_file_extent_disk_num_bytes ( leaf ,
2007-10-16 04:14:19 +08:00
extent ) ;
read_extent_buffer ( leaf , & old ,
( unsigned long ) extent ,
sizeof ( old ) ) ;
2007-10-16 04:15:53 +08:00
if ( disk_bytenr ! = 0 ) {
2007-06-12 18:35:45 +08:00
ret = btrfs_inc_extent_ref ( trans , root ,
2007-12-11 22:25:06 +08:00
disk_bytenr , disk_num_bytes ,
root - > root_key . objectid ,
trans - > transid ,
key . objectid , end ) ;
2007-06-12 18:35:45 +08:00
BUG_ON ( ret ) ;
}
}
2007-11-01 23:28:41 +08:00
bookend = 1 ;
2008-01-31 03:39:54 +08:00
if ( found_inline & & start < = key . offset )
2007-11-01 23:28:41 +08:00
keep = 1 ;
2007-06-12 18:35:45 +08:00
}
/* truncate existing extent */
if ( start > key . offset ) {
u64 new_num ;
u64 old_num ;
keep = 1 ;
2007-10-16 04:14:19 +08:00
WARN_ON ( start & ( root - > sectorsize - 1 ) ) ;
2007-06-12 18:35:45 +08:00
if ( found_extent ) {
2007-10-16 04:15:53 +08:00
new_num = start - key . offset ;
old_num = btrfs_file_extent_num_bytes ( leaf ,
extent ) ;
* hint_byte =
btrfs_file_extent_disk_bytenr ( leaf ,
extent ) ;
if ( btrfs_file_extent_disk_bytenr ( leaf ,
extent ) ) {
2008-02-09 02:49:28 +08:00
dec_i_blocks ( inode , old_num - new_num ) ;
2007-06-12 18:35:45 +08:00
}
2007-10-16 04:15:53 +08:00
btrfs_set_file_extent_num_bytes ( leaf , extent ,
new_num ) ;
2007-10-16 04:14:19 +08:00
btrfs_mark_buffer_dirty ( leaf ) ;
2007-11-30 23:09:33 +08:00
} else if ( key . offset < inline_limit & &
( end > extent_end ) & &
( inline_limit < extent_end ) ) {
2007-10-16 04:18:25 +08:00
u32 new_size ;
new_size = btrfs_file_extent_calc_inline_size (
2007-11-30 23:09:33 +08:00
inline_limit - key . offset ) ;
2008-02-09 02:49:28 +08:00
dec_i_blocks ( inode , ( extent_end - key . offset ) -
( inline_limit - key . offset ) ) ;
2007-10-16 04:18:25 +08:00
btrfs_truncate_item ( trans , root , path ,
2007-11-01 23:28:41 +08:00
new_size , 1 ) ;
2007-06-12 18:35:45 +08:00
}
}
/* delete the entire extent */
if ( ! keep ) {
2007-10-16 04:15:53 +08:00
u64 disk_bytenr = 0 ;
u64 disk_num_bytes = 0 ;
u64 extent_num_bytes = 0 ;
2007-12-11 22:25:06 +08:00
u64 root_gen ;
2007-12-12 01:42:00 +08:00
u64 root_owner ;
2007-12-11 22:25:06 +08:00
2007-12-12 01:42:00 +08:00
root_gen = btrfs_header_generation ( leaf ) ;
root_owner = btrfs_header_owner ( leaf ) ;
2007-06-12 18:35:45 +08:00
if ( found_extent ) {
2007-10-16 04:15:53 +08:00
disk_bytenr =
btrfs_file_extent_disk_bytenr ( leaf ,
2007-10-16 04:14:19 +08:00
extent ) ;
2007-10-16 04:15:53 +08:00
disk_num_bytes =
btrfs_file_extent_disk_num_bytes ( leaf ,
2007-10-16 04:14:19 +08:00
extent ) ;
2007-10-16 04:15:53 +08:00
extent_num_bytes =
btrfs_file_extent_num_bytes ( leaf , extent ) ;
* hint_byte =
btrfs_file_extent_disk_bytenr ( leaf ,
extent ) ;
2007-06-12 18:35:45 +08:00
}
ret = btrfs_del_item ( trans , root , path ) ;
2007-06-23 02:16:25 +08:00
/* TODO update progress marker and return */
2007-06-12 18:35:45 +08:00
BUG_ON ( ret ) ;
btrfs_release_path ( root , path ) ;
extent = NULL ;
2007-10-16 04:15:53 +08:00
if ( found_extent & & disk_bytenr ! = 0 ) {
2008-02-09 02:49:28 +08:00
dec_i_blocks ( inode , extent_num_bytes ) ;
2007-06-12 18:35:45 +08:00
ret = btrfs_free_extent ( trans , root ,
2007-12-11 22:25:06 +08:00
disk_bytenr ,
disk_num_bytes ,
2007-12-12 01:42:00 +08:00
root_owner ,
2007-12-11 22:25:06 +08:00
root_gen , inode - > i_ino ,
key . offset , 0 ) ;
2007-06-12 18:35:45 +08:00
}
BUG_ON ( ret ) ;
if ( ! bookend & & search_start > = end ) {
ret = 0 ;
goto out ;
}
if ( ! bookend )
continue ;
}
2008-01-31 03:39:54 +08:00
if ( bookend & & found_inline & & start < = key . offset ) {
2007-11-01 23:28:41 +08:00
u32 new_size ;
new_size = btrfs_file_extent_calc_inline_size (
2008-01-31 03:39:54 +08:00
extent_end - end ) ;
2008-02-09 02:49:28 +08:00
dec_i_blocks ( inode , ( extent_end - key . offset ) -
( extent_end - end ) ) ;
2007-11-01 23:28:41 +08:00
btrfs_truncate_item ( trans , root , path , new_size , 0 ) ;
}
2007-06-12 18:35:45 +08:00
/* create bookend, splitting the extent in two */
if ( bookend & & found_extent ) {
struct btrfs_key ins ;
ins . objectid = inode - > i_ino ;
ins . offset = end ;
btrfs_set_key_type ( & ins , BTRFS_EXTENT_DATA_KEY ) ;
btrfs_release_path ( root , path ) ;
ret = btrfs_insert_empty_item ( trans , root , path , & ins ,
sizeof ( * extent ) ) ;
2007-06-18 21:57:58 +08:00
2007-10-16 04:14:19 +08:00
leaf = path - > nodes [ 0 ] ;
2007-06-18 21:57:58 +08:00
if ( ret ) {
2007-10-16 04:14:19 +08:00
btrfs_print_leaf ( root , leaf ) ;
printk ( " got %d on inserting %Lu %u %Lu start %Lu end %Lu found %Lu %Lu keep was %d \n " , ret , ins . objectid , ins . type , ins . offset , start , end , key . offset , extent_end , keep ) ;
2007-06-18 21:57:58 +08:00
}
2007-06-12 18:35:45 +08:00
BUG_ON ( ret ) ;
2007-10-16 04:14:19 +08:00
extent = btrfs_item_ptr ( leaf , path - > slots [ 0 ] ,
struct btrfs_file_extent_item ) ;
write_extent_buffer ( leaf , & old ,
( unsigned long ) extent , sizeof ( old ) ) ;
btrfs_set_file_extent_offset ( leaf , extent ,
2007-10-16 04:15:53 +08:00
le64_to_cpu ( old . offset ) + end - key . offset ) ;
WARN_ON ( le64_to_cpu ( old . num_bytes ) <
( extent_end - end ) ) ;
btrfs_set_file_extent_num_bytes ( leaf , extent ,
extent_end - end ) ;
2007-10-16 04:14:19 +08:00
btrfs_set_file_extent_type ( leaf , extent ,
2007-06-12 18:35:45 +08:00
BTRFS_FILE_EXTENT_REG ) ;
2007-10-16 04:15:53 +08:00
2007-06-12 18:35:45 +08:00
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
2007-10-16 04:15:53 +08:00
if ( le64_to_cpu ( old . disk_bytenr ) ! = 0 ) {
2007-06-12 18:35:45 +08:00
inode - > i_blocks + =
2007-10-16 04:15:53 +08:00
btrfs_file_extent_num_bytes ( leaf ,
extent ) > > 9 ;
2007-06-12 18:35:45 +08:00
}
ret = 0 ;
goto out ;
}
}
out :
btrfs_free_path ( path ) ;
2008-02-09 02:49:28 +08:00
btrfs_check_file ( root , inode ) ;
2007-06-12 18:35:45 +08:00
return ret ;
}
/*
* this gets pages into the page cache and locks them down
*/
2008-01-03 23:01:48 +08:00
static int prepare_pages ( struct btrfs_root * root , struct file * file ,
struct page * * pages , size_t num_pages ,
loff_t pos , unsigned long first_index ,
unsigned long last_index , size_t write_bytes )
2007-06-12 18:35:45 +08:00
{
int i ;
unsigned long index = pos > > PAGE_CACHE_SHIFT ;
2007-12-19 05:15:09 +08:00
struct inode * inode = fdentry ( file ) - > d_inode ;
2007-06-12 18:35:45 +08:00
int err = 0 ;
2007-06-18 21:57:58 +08:00
u64 start_pos ;
2007-10-16 04:14:19 +08:00
start_pos = pos & ~ ( ( u64 ) root - > sectorsize - 1 ) ;
2007-06-12 18:35:45 +08:00
memset ( pages , 0 , num_pages * sizeof ( struct page * ) ) ;
for ( i = 0 ; i < num_pages ; i + + ) {
pages [ i ] = grab_cache_page ( inode - > i_mapping , index + i ) ;
if ( ! pages [ i ] ) {
err = - ENOMEM ;
2007-08-28 04:49:44 +08:00
BUG_ON ( 1 ) ;
2007-06-12 18:35:45 +08:00
}
2007-12-19 05:15:09 +08:00
# if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,18)
ClearPageDirty ( pages [ i ] ) ;
# else
2007-06-29 03:57:36 +08:00
cancel_dirty_page ( pages [ i ] , PAGE_CACHE_SIZE ) ;
2007-12-19 05:15:09 +08:00
# endif
2007-06-29 03:57:36 +08:00
wait_on_page_writeback ( pages [ i ] ) ;
2007-09-17 23:25:58 +08:00
set_page_extent_mapped ( pages [ i ] ) ;
2007-08-28 04:49:44 +08:00
WARN_ON ( ! PageLocked ( pages [ i ] ) ) ;
2007-06-12 18:35:45 +08:00
}
2008-02-20 00:29:24 +08:00
if ( start_pos < inode - > i_size ) {
u64 last_pos ;
2008-02-20 05:24:18 +08:00
last_pos = ( ( u64 ) index + num_pages ) < < PAGE_CACHE_SHIFT ;
2008-02-20 01:55:05 +08:00
lock_extent ( & BTRFS_I ( inode ) - > io_tree ,
start_pos , last_pos - 1 , GFP_NOFS ) ;
2008-02-20 00:29:24 +08:00
clear_extent_bits ( & BTRFS_I ( inode ) - > io_tree , start_pos ,
last_pos - 1 , EXTENT_DIRTY | EXTENT_DELALLOC ,
GFP_NOFS ) ;
2008-02-20 01:55:05 +08:00
unlock_extent ( & BTRFS_I ( inode ) - > io_tree ,
start_pos , last_pos - 1 , GFP_NOFS ) ;
2008-02-20 00:29:24 +08:00
}
2007-06-12 18:35:45 +08:00
return 0 ;
}
static ssize_t btrfs_file_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos )
{
loff_t pos ;
2007-10-30 02:36:41 +08:00
loff_t start_pos ;
ssize_t num_written = 0 ;
ssize_t err = 0 ;
2007-06-12 18:35:45 +08:00
int ret = 0 ;
2007-12-19 05:15:09 +08:00
struct inode * inode = fdentry ( file ) - > d_inode ;
2007-06-12 18:35:45 +08:00
struct btrfs_root * root = BTRFS_I ( inode ) - > root ;
2007-06-18 21:57:58 +08:00
struct page * * pages = NULL ;
int nrptrs ;
2007-06-12 18:35:45 +08:00
struct page * pinned [ 2 ] ;
unsigned long first_index ;
unsigned long last_index ;
2007-06-18 21:57:58 +08:00
nrptrs = min ( ( count + PAGE_CACHE_SIZE - 1 ) / PAGE_CACHE_SIZE ,
PAGE_CACHE_SIZE / ( sizeof ( struct page * ) ) ) ;
2007-06-12 18:35:45 +08:00
pinned [ 0 ] = NULL ;
pinned [ 1 ] = NULL ;
2007-10-30 02:36:41 +08:00
2007-06-12 18:35:45 +08:00
pos = * ppos ;
2007-10-30 02:36:41 +08:00
start_pos = pos ;
2007-06-12 18:35:45 +08:00
vfs_check_frozen ( inode - > i_sb , SB_FREEZE_WRITE ) ;
current - > backing_dev_info = inode - > i_mapping - > backing_dev_info ;
err = generic_write_checks ( file , & pos , & count , S_ISBLK ( inode - > i_mode ) ) ;
if ( err )
2007-12-22 05:27:21 +08:00
goto out_nolock ;
2007-06-12 18:35:45 +08:00
if ( count = = 0 )
2007-12-22 05:27:21 +08:00
goto out_nolock ;
2007-12-19 05:15:09 +08:00
err = remove_suid ( fdentry ( file ) ) ;
2007-06-12 18:35:45 +08:00
if ( err )
2007-12-22 05:27:21 +08:00
goto out_nolock ;
2007-06-12 18:35:45 +08:00
file_update_time ( file ) ;
2007-06-18 21:57:58 +08:00
pages = kmalloc ( nrptrs * sizeof ( struct page * ) , GFP_KERNEL ) ;
2007-06-12 18:35:45 +08:00
mutex_lock ( & inode - > i_mutex ) ;
first_index = pos > > PAGE_CACHE_SHIFT ;
last_index = ( pos + count ) > > PAGE_CACHE_SHIFT ;
2008-04-22 21:24:20 +08:00
/*
* if this is a nodatasum mount , force summing off for the inode
* all the time . That way a later mount with summing on won ' t
* get confused
*/
if ( btrfs_test_opt ( root , NODATASUM ) )
btrfs_set_flag ( inode , NODATASUM ) ;
2007-06-12 18:35:45 +08:00
/*
* there are lots of better ways to do this , but this code
* makes sure the first and last page in the file range are
* up to date and ready for cow
*/
if ( ( pos & ( PAGE_CACHE_SIZE - 1 ) ) ) {
pinned [ 0 ] = grab_cache_page ( inode - > i_mapping , first_index ) ;
if ( ! PageUptodate ( pinned [ 0 ] ) ) {
2007-06-16 01:50:00 +08:00
ret = btrfs_readpage ( NULL , pinned [ 0 ] ) ;
2007-06-12 18:35:45 +08:00
BUG_ON ( ret ) ;
wait_on_page_locked ( pinned [ 0 ] ) ;
} else {
unlock_page ( pinned [ 0 ] ) ;
}
}
if ( ( pos + count ) & ( PAGE_CACHE_SIZE - 1 ) ) {
pinned [ 1 ] = grab_cache_page ( inode - > i_mapping , last_index ) ;
if ( ! PageUptodate ( pinned [ 1 ] ) ) {
2007-06-16 01:50:00 +08:00
ret = btrfs_readpage ( NULL , pinned [ 1 ] ) ;
2007-06-12 18:35:45 +08:00
BUG_ON ( ret ) ;
wait_on_page_locked ( pinned [ 1 ] ) ;
} else {
unlock_page ( pinned [ 1 ] ) ;
}
}
while ( count > 0 ) {
size_t offset = pos & ( PAGE_CACHE_SIZE - 1 ) ;
2007-06-23 02:16:24 +08:00
size_t write_bytes = min ( count , nrptrs *
( size_t ) PAGE_CACHE_SIZE -
2007-06-18 21:57:58 +08:00
offset ) ;
2007-06-12 18:35:45 +08:00
size_t num_pages = ( write_bytes + PAGE_CACHE_SIZE - 1 ) > >
PAGE_CACHE_SHIFT ;
2007-06-18 21:57:58 +08:00
WARN_ON ( num_pages > nrptrs ) ;
2007-06-12 18:35:45 +08:00
memset ( pages , 0 , sizeof ( pages ) ) ;
2007-12-22 05:27:21 +08:00
mutex_lock ( & root - > fs_info - > fs_mutex ) ;
ret = btrfs_check_free_space ( root , write_bytes , 0 ) ;
mutex_unlock ( & root - > fs_info - > fs_mutex ) ;
if ( ret )
goto out ;
2007-06-12 18:35:45 +08:00
ret = prepare_pages ( root , file , pages , num_pages ,
pos , first_index , last_index ,
2007-06-18 21:57:58 +08:00
write_bytes ) ;
2007-06-23 02:16:25 +08:00
if ( ret )
goto out ;
2007-06-12 18:35:45 +08:00
ret = btrfs_copy_from_user ( pos , num_pages ,
write_bytes , pages , buf ) ;
2007-06-23 02:16:25 +08:00
if ( ret ) {
btrfs_drop_pages ( pages , num_pages ) ;
goto out ;
}
2007-06-12 18:35:45 +08:00
ret = dirty_and_release_pages ( NULL , root , file , pages ,
num_pages , pos , write_bytes ) ;
btrfs_drop_pages ( pages , num_pages ) ;
2007-06-23 02:16:25 +08:00
if ( ret )
goto out ;
2007-06-12 18:35:45 +08:00
buf + = write_bytes ;
count - = write_bytes ;
pos + = write_bytes ;
num_written + = write_bytes ;
2007-06-18 21:57:58 +08:00
balance_dirty_pages_ratelimited_nr ( inode - > i_mapping , num_pages ) ;
2007-11-27 23:52:01 +08:00
if ( num_pages < ( root - > leafsize > > PAGE_CACHE_SHIFT ) + 1 )
btrfs_btree_balance_dirty ( root , 1 ) ;
2008-01-09 04:46:30 +08:00
btrfs_throttle ( root ) ;
2007-06-12 18:35:45 +08:00
cond_resched ( ) ;
}
out :
2007-12-22 05:27:21 +08:00
mutex_unlock ( & inode - > i_mutex ) ;
2008-01-04 02:46:11 +08:00
2007-12-22 05:27:21 +08:00
out_nolock :
2007-06-18 21:57:58 +08:00
kfree ( pages ) ;
2007-06-12 18:35:45 +08:00
if ( pinned [ 0 ] )
page_cache_release ( pinned [ 0 ] ) ;
if ( pinned [ 1 ] )
page_cache_release ( pinned [ 1 ] ) ;
* ppos = pos ;
2007-10-30 02:36:41 +08:00
if ( num_written > 0 & & ( ( file - > f_flags & O_SYNC ) | | IS_SYNC ( inode ) ) ) {
err = sync_page_range ( inode , inode - > i_mapping ,
start_pos , num_written ) ;
if ( err < 0 )
num_written = err ;
2008-04-10 22:23:21 +08:00
} else if ( num_written > 0 & & ( file - > f_flags & O_DIRECT ) ) {
2008-04-14 21:48:18 +08:00
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
do_sync_file_range ( file , start_pos ,
start_pos + num_written - 1 ,
SYNC_FILE_RANGE_WRITE |
SYNC_FILE_RANGE_WAIT_AFTER ) ;
# else
2008-04-10 22:23:21 +08:00
do_sync_mapping_range ( inode - > i_mapping , start_pos ,
start_pos + num_written - 1 ,
SYNC_FILE_RANGE_WRITE |
SYNC_FILE_RANGE_WAIT_AFTER ) ;
2008-04-14 21:48:18 +08:00
# endif
2008-04-10 22:23:21 +08:00
invalidate_mapping_pages ( inode - > i_mapping ,
start_pos > > PAGE_CACHE_SHIFT ,
( start_pos + num_written - 1 ) > > PAGE_CACHE_SHIFT ) ;
2007-10-30 02:36:41 +08:00
}
2007-06-12 18:35:45 +08:00
current - > backing_dev_info = NULL ;
2008-04-25 20:51:48 +08:00
btrfs_ordered_throttle ( root , inode ) ;
2007-06-12 18:35:45 +08:00
return num_written ? num_written : err ;
}
static int btrfs_sync_file ( struct file * file ,
struct dentry * dentry , int datasync )
{
struct inode * inode = dentry - > d_inode ;
struct btrfs_root * root = BTRFS_I ( inode ) - > root ;
2007-08-11 04:22:09 +08:00
int ret = 0 ;
2007-06-12 18:35:45 +08:00
struct btrfs_trans_handle * trans ;
/*
2007-08-11 04:22:09 +08:00
* check the transaction that last modified this inode
* and see if its already been committed
2007-06-12 18:35:45 +08:00
*/
mutex_lock ( & root - > fs_info - > fs_mutex ) ;
2007-08-11 04:22:09 +08:00
if ( ! BTRFS_I ( inode ) - > last_trans )
goto out ;
mutex_lock ( & root - > fs_info - > trans_mutex ) ;
if ( BTRFS_I ( inode ) - > last_trans < =
root - > fs_info - > last_trans_committed ) {
BTRFS_I ( inode ) - > last_trans = 0 ;
mutex_unlock ( & root - > fs_info - > trans_mutex ) ;
goto out ;
}
mutex_unlock ( & root - > fs_info - > trans_mutex ) ;
/*
2007-08-28 04:49:44 +08:00
* ok we haven ' t committed the transaction yet , lets do a commit
*/
2007-06-12 18:35:45 +08:00
trans = btrfs_start_transaction ( root , 1 ) ;
if ( ! trans ) {
ret = - ENOMEM ;
goto out ;
}
ret = btrfs_commit_transaction ( trans , root ) ;
out :
2007-08-11 04:22:09 +08:00
mutex_unlock ( & root - > fs_info - > fs_mutex ) ;
2007-06-12 18:35:45 +08:00
return ret > 0 ? EIO : ret ;
}
2007-06-16 01:50:00 +08:00
static struct vm_operations_struct btrfs_file_vm_ops = {
2007-07-26 00:31:35 +08:00
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
. nopage = filemap_nopage ,
. populate = filemap_populate ,
# else
. fault = filemap_fault ,
# endif
2007-06-16 01:50:00 +08:00
. page_mkwrite = btrfs_page_mkwrite ,
} ;
static int btrfs_file_mmap ( struct file * filp , struct vm_area_struct * vma )
{
vma - > vm_ops = & btrfs_file_vm_ops ;
file_accessed ( filp ) ;
return 0 ;
}
2007-06-12 18:35:45 +08:00
struct file_operations btrfs_file_operations = {
. llseek = generic_file_llseek ,
. read = do_sync_read ,
2007-06-16 01:50:00 +08:00
. aio_read = generic_file_aio_read ,
2007-12-15 01:56:58 +08:00
. splice_read = generic_file_splice_read ,
2007-12-19 05:15:09 +08:00
# if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,18)
. sendfile = generic_file_sendfile ,
# endif
2007-06-12 18:35:45 +08:00
. write = btrfs_file_write ,
2007-06-16 01:50:00 +08:00
. mmap = btrfs_file_mmap ,
2007-06-12 18:35:45 +08:00
. open = generic_file_open ,
. fsync = btrfs_sync_file ,
2007-09-14 22:22:47 +08:00
. unlocked_ioctl = btrfs_ioctl ,
2007-06-12 18:35:45 +08:00
# ifdef CONFIG_COMPAT
2007-09-14 22:22:47 +08:00
. compat_ioctl = btrfs_ioctl ,
2007-06-12 18:35:45 +08:00
# endif
} ;