2006-01-16 19:50:04 +03:00
/*
* Copyright ( C ) Sistina Software , Inc . 1997 - 2003 All rights reserved .
2006-05-18 23:09:15 +04:00
* Copyright ( C ) 2004 - 2006 Red Hat , Inc . All rights reserved .
2006-01-16 19:50:04 +03:00
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
* of the GNU General Public License v .2 .
*/
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/completion.h>
# include <linux/buffer_head.h>
# include <linux/pagemap.h>
2006-05-06 00:59:11 +04:00
# include <linux/pagevec.h>
2006-01-30 14:55:32 +03:00
# include <linux/mpage.h>
2006-02-14 14:54:42 +03:00
# include <linux/fs.h>
2006-02-28 01:23:27 +03:00
# include <linux/gfs2_ondisk.h>
2006-01-16 19:50:04 +03:00
# include "gfs2.h"
2006-02-28 01:23:27 +03:00
# include "lm_interface.h"
# include "incore.h"
2006-01-16 19:50:04 +03:00
# include "bmap.h"
# include "glock.h"
# include "inode.h"
# include "log.h"
# include "meta_io.h"
# include "ops_address.h"
# include "quota.h"
# include "trans.h"
2006-02-08 14:50:51 +03:00
# include "rgrp.h"
2006-02-15 13:15:18 +03:00
# include "ops_file.h"
2006-02-28 01:23:27 +03:00
# include "util.h"
2006-07-11 17:46:33 +04:00
# include "glops.h"
2006-01-16 19:50:04 +03:00
2006-07-26 19:27:10 +04:00
static void gfs2_page_add_databufs ( struct gfs2_inode * ip , struct page * page ,
unsigned int from , unsigned int to )
{
struct buffer_head * head = page_buffers ( page ) ;
unsigned int bsize = head - > b_size ;
struct buffer_head * bh ;
unsigned int start , end ;
for ( bh = head , start = 0 ; bh ! = head | | ! start ;
bh = bh - > b_this_page , start = end ) {
end = start + bsize ;
if ( end < = from | | start > = to )
continue ;
gfs2_trans_add_bh ( ip - > i_gl , bh , 0 ) ;
}
}
2006-01-16 19:50:04 +03:00
/**
2006-01-30 12:39:10 +03:00
* gfs2_get_block - Fills in a buffer head with details about a block
2006-01-16 19:50:04 +03:00
* @ inode : The inode
* @ lblock : The block number to look up
* @ bh_result : The buffer head to return the result in
* @ create : Non - zero if we may add block to the file
*
* Returns : errno
*/
2006-01-30 12:39:10 +03:00
int gfs2_get_block ( struct inode * inode , sector_t lblock ,
struct buffer_head * bh_result , int create )
2006-01-16 19:50:04 +03:00
{
int new = create ;
uint64_t dblock ;
int error ;
2006-05-06 00:59:11 +04:00
int boundary ;
2006-01-16 19:50:04 +03:00
2006-05-06 00:59:11 +04:00
error = gfs2_block_map ( inode , lblock , & new , & dblock , & boundary ) ;
2006-01-16 19:50:04 +03:00
if ( error )
return error ;
if ( ! dblock )
return 0 ;
map_bh ( bh_result , inode - > i_sb , dblock ) ;
if ( new )
set_buffer_new ( bh_result ) ;
2006-05-06 00:59:11 +04:00
if ( boundary )
set_buffer_boundary ( bh_result ) ;
2006-01-16 19:50:04 +03:00
return 0 ;
}
/**
* get_block_noalloc - Fills in a buffer head with details about a block
* @ inode : The inode
* @ lblock : The block number to look up
* @ bh_result : The buffer head to return the result in
* @ create : Non - zero if we may add block to the file
*
* Returns : errno
*/
static int get_block_noalloc ( struct inode * inode , sector_t lblock ,
struct buffer_head * bh_result , int create )
{
int new = 0 ;
uint64_t dblock ;
int error ;
2006-05-06 00:59:11 +04:00
int boundary ;
2006-01-16 19:50:04 +03:00
2006-05-06 00:59:11 +04:00
error = gfs2_block_map ( inode , lblock , & new , & dblock , & boundary ) ;
2006-01-16 19:50:04 +03:00
if ( error )
return error ;
if ( dblock )
map_bh ( bh_result , inode - > i_sb , dblock ) ;
2006-06-14 23:32:57 +04:00
else if ( gfs2_assert_withdraw ( GFS2_SB ( inode ) , ! create ) )
2006-01-16 19:50:04 +03:00
error = - EIO ;
2006-05-06 00:59:11 +04:00
if ( boundary )
set_buffer_boundary ( bh_result ) ;
2006-01-16 19:50:04 +03:00
return error ;
}
/**
* gfs2_writepage - Write complete page
* @ page : Page to write
*
* Returns : errno
*
2006-02-08 14:50:51 +03:00
* Some of this is copied from block_write_full_page ( ) although we still
* call it to do most of the work .
2006-01-16 19:50:04 +03:00
*/
static int gfs2_writepage ( struct page * page , struct writeback_control * wbc )
{
2006-02-08 14:50:51 +03:00
struct inode * inode = page - > mapping - > host ;
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( page - > mapping - > host ) ;
struct gfs2_sbd * sdp = GFS2_SB ( page - > mapping - > host ) ;
2006-02-08 14:50:51 +03:00
loff_t i_size = i_size_read ( inode ) ;
pgoff_t end_index = i_size > > PAGE_CACHE_SHIFT ;
unsigned offset ;
2006-01-16 19:50:04 +03:00
int error ;
2006-02-08 14:50:51 +03:00
int done_trans = 0 ;
2006-01-16 19:50:04 +03:00
if ( gfs2_assert_withdraw ( sdp , gfs2_glock_is_held_excl ( ip - > i_gl ) ) ) {
unlock_page ( page ) ;
return - EIO ;
}
2006-02-28 01:23:27 +03:00
if ( current - > journal_info )
2006-02-08 14:50:51 +03:00
goto out_ignore ;
/* Is the page fully outside i_size? (truncate in progress) */
offset = i_size & ( PAGE_CACHE_SIZE - 1 ) ;
2006-05-02 20:09:42 +04:00
if ( page - > index > end_index | | ( page - > index = = end_index & & ! offset ) ) {
2006-02-08 14:50:51 +03:00
page - > mapping - > a_ops - > invalidatepage ( page , 0 ) ;
2006-01-16 19:50:04 +03:00
unlock_page ( page ) ;
2006-02-08 14:50:51 +03:00
return 0 ; /* don't care */
2006-01-16 19:50:04 +03:00
}
2006-02-08 14:50:51 +03:00
if ( sdp - > sd_args . ar_data = = GFS2_DATA_ORDERED | | gfs2_is_jdata ( ip ) ) {
error = gfs2_trans_begin ( sdp , RES_DINODE + 1 , 0 ) ;
if ( error )
goto out_ignore ;
gfs2_page_add_databufs ( ip , page , 0 , sdp - > sd_vfs - > s_blocksize - 1 ) ;
done_trans = 1 ;
}
error = block_write_full_page ( page , get_block_noalloc , wbc ) ;
if ( done_trans )
gfs2_trans_end ( sdp ) ;
2006-01-16 19:50:04 +03:00
gfs2_meta_cache_flush ( ip ) ;
return error ;
2006-02-08 14:50:51 +03:00
out_ignore :
redirty_page_for_writepage ( wbc , page ) ;
unlock_page ( page ) ;
return 0 ;
2006-01-16 19:50:04 +03:00
}
2006-05-06 00:59:11 +04:00
static int zero_readpage ( struct page * page )
{
void * kaddr ;
kaddr = kmap_atomic ( page , KM_USER0 ) ;
memset ( kaddr , 0 , PAGE_CACHE_SIZE ) ;
kunmap_atomic ( page , KM_USER0 ) ;
SetPageUptodate ( page ) ;
return 0 ;
}
2006-01-16 19:50:04 +03:00
/**
* stuffed_readpage - Fill in a Linux page with stuffed file data
* @ ip : the inode
* @ page : the page
*
* Returns : errno
*/
static int stuffed_readpage ( struct gfs2_inode * ip , struct page * page )
{
struct buffer_head * dibh ;
void * kaddr ;
int error ;
2006-05-06 00:59:11 +04:00
/* Only the first page of a stuffed file might contain data */
if ( unlikely ( page - > index ) )
return zero_readpage ( page ) ;
2006-01-16 19:50:04 +03:00
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( error )
return error ;
2006-02-15 15:26:19 +03:00
kaddr = kmap_atomic ( page , KM_USER0 ) ;
2006-05-06 00:59:11 +04:00
memcpy ( kaddr , dibh - > b_data + sizeof ( struct gfs2_dinode ) ,
2006-01-16 19:50:04 +03:00
ip - > i_di . di_size ) ;
2006-05-06 00:59:11 +04:00
memset ( kaddr + ip - > i_di . di_size , 0 , PAGE_CACHE_SIZE - ip - > i_di . di_size ) ;
2006-02-15 15:26:19 +03:00
kunmap_atomic ( page , KM_USER0 ) ;
2006-01-16 19:50:04 +03:00
brelse ( dibh ) ;
SetPageUptodate ( page ) ;
return 0 ;
}
/**
* gfs2_readpage - readpage with locking
2006-02-08 14:50:51 +03:00
* @ file : The file to read a page for . N . B . This may be NULL if we are
* reading an internal file .
2006-01-16 19:50:04 +03:00
* @ page : The page to read
*
* Returns : errno
*/
static int gfs2_readpage ( struct file * file , struct page * page )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( page - > mapping - > host ) ;
struct gfs2_sbd * sdp = GFS2_SB ( page - > mapping - > host ) ;
2006-02-08 14:50:51 +03:00
struct gfs2_holder gh ;
2006-01-16 19:50:04 +03:00
int error ;
2006-08-04 23:41:22 +04:00
int do_unlock = 0 ;
2006-01-16 19:50:04 +03:00
2006-05-06 00:59:11 +04:00
if ( likely ( file ! = & gfs2_internal_file_sentinal ) ) {
2006-08-04 23:41:22 +04:00
if ( file ) {
struct gfs2_file * gf = file - > private_data ;
if ( test_bit ( GFF_EXLOCK , & gf - > f_flags ) )
goto skip_lock ;
}
2006-04-18 18:09:15 +04:00
gfs2_holder_init ( ip - > i_gl , LM_ST_SHARED , GL_ATIME | GL_AOP , & gh ) ;
2006-08-04 23:41:22 +04:00
do_unlock = 1 ;
2006-02-15 13:15:18 +03:00
error = gfs2_glock_nq_m_atime ( 1 , & gh ) ;
2006-05-06 00:59:11 +04:00
if ( unlikely ( error ) )
2006-02-15 13:15:18 +03:00
goto out_unlock ;
}
2006-01-16 19:50:04 +03:00
2006-08-04 23:41:22 +04:00
skip_lock :
2006-02-08 14:50:51 +03:00
if ( gfs2_is_stuffed ( ip ) ) {
2006-05-06 00:59:11 +04:00
error = stuffed_readpage ( ip , page ) ;
unlock_page ( page ) ;
2006-01-16 19:50:04 +03:00
} else
2006-02-08 14:50:51 +03:00
error = mpage_readpage ( page , gfs2_get_block ) ;
2006-01-16 19:50:04 +03:00
if ( unlikely ( test_bit ( SDF_SHUTDOWN , & sdp - > sd_flags ) ) )
error = - EIO ;
2006-02-15 13:15:18 +03:00
if ( file ! = & gfs2_internal_file_sentinal ) {
gfs2_glock_dq_m ( 1 , & gh ) ;
gfs2_holder_uninit ( & gh ) ;
}
2006-02-08 14:50:51 +03:00
out :
2006-01-16 19:50:04 +03:00
return error ;
2006-02-08 14:50:51 +03:00
out_unlock :
unlock_page ( page ) ;
2006-08-04 23:41:22 +04:00
if ( do_unlock )
2006-05-06 00:59:11 +04:00
gfs2_holder_uninit ( & gh ) ;
goto out ;
}
/**
* gfs2_readpages - Read a bunch of pages at once
*
* Some notes :
* 1. This is only for readahead , so we can simply ignore any things
* which are slightly inconvenient ( such as locking conflicts between
* the page lock and the glock ) and return having done no I / O . Its
* obviously not something we ' d want to do on too regular a basis .
* Any I / O we ignore at this time will be done via readpage later .
* 2. We have to handle stuffed files here too .
* 3. mpage_readpages ( ) does most of the heavy lifting in the common case .
* 4. gfs2_get_block ( ) is relied upon to set BH_Boundary in the right places .
* 5. We use LM_FLAG_TRY_1CB here , effectively we then have lock - ahead as
* well as read - ahead .
*/
static int gfs2_readpages ( struct file * file , struct address_space * mapping ,
struct list_head * pages , unsigned nr_pages )
{
struct inode * inode = mapping - > host ;
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( inode ) ;
struct gfs2_sbd * sdp = GFS2_SB ( inode ) ;
2006-05-06 00:59:11 +04:00
struct gfs2_holder gh ;
unsigned page_idx ;
int ret ;
2006-08-04 23:41:22 +04:00
int do_unlock = 0 ;
2006-05-06 00:59:11 +04:00
if ( likely ( file ! = & gfs2_internal_file_sentinal ) ) {
2006-08-04 23:41:22 +04:00
if ( file ) {
struct gfs2_file * gf = file - > private_data ;
if ( test_bit ( GFF_EXLOCK , & gf - > f_flags ) )
goto skip_lock ;
}
2006-05-06 00:59:11 +04:00
gfs2_holder_init ( ip - > i_gl , LM_ST_SHARED ,
LM_FLAG_TRY_1CB | GL_ATIME | GL_AOP , & gh ) ;
2006-08-04 23:41:22 +04:00
do_unlock = 1 ;
2006-05-06 00:59:11 +04:00
ret = gfs2_glock_nq_m_atime ( 1 , & gh ) ;
if ( ret = = GLR_TRYFAILED )
goto out_noerror ;
if ( unlikely ( ret ) )
goto out_unlock ;
}
2006-08-04 23:41:22 +04:00
skip_lock :
2006-05-06 00:59:11 +04:00
if ( gfs2_is_stuffed ( ip ) ) {
struct pagevec lru_pvec ;
pagevec_init ( & lru_pvec , 0 ) ;
for ( page_idx = 0 ; page_idx < nr_pages ; page_idx + + ) {
2006-07-10 23:47:01 +04:00
struct page * page = list_entry ( pages - > prev , struct page , lru ) ;
prefetchw ( & page - > flags ) ;
2006-05-06 00:59:11 +04:00
list_del ( & page - > lru ) ;
if ( ! add_to_page_cache ( page , mapping ,
page - > index , GFP_KERNEL ) ) {
ret = stuffed_readpage ( ip , page ) ;
unlock_page ( page ) ;
if ( ! pagevec_add ( & lru_pvec , page ) )
__pagevec_lru_add ( & lru_pvec ) ;
2006-07-10 23:47:01 +04:00
} else {
page_cache_release ( page ) ;
2006-05-06 00:59:11 +04:00
}
}
pagevec_lru_add ( & lru_pvec ) ;
ret = 0 ;
} else {
/* What we really want to do .... */
ret = mpage_readpages ( mapping , pages , nr_pages , gfs2_get_block ) ;
}
2006-08-04 23:41:22 +04:00
if ( do_unlock ) {
2006-05-06 00:59:11 +04:00
gfs2_glock_dq_m ( 1 , & gh ) ;
gfs2_holder_uninit ( & gh ) ;
}
out :
if ( unlikely ( test_bit ( SDF_SHUTDOWN , & sdp - > sd_flags ) ) )
ret = - EIO ;
return ret ;
out_noerror :
ret = 0 ;
out_unlock :
/* unlock all pages, we can't do any I/O right now */
for ( page_idx = 0 ; page_idx < nr_pages ; page_idx + + ) {
2006-07-10 23:47:01 +04:00
struct page * page = list_entry ( pages - > prev , struct page , lru ) ;
2006-05-06 00:59:11 +04:00
list_del ( & page - > lru ) ;
unlock_page ( page ) ;
page_cache_release ( page ) ;
}
2006-08-04 23:41:22 +04:00
if ( do_unlock )
2006-05-06 00:59:11 +04:00
gfs2_holder_uninit ( & gh ) ;
2006-02-08 14:50:51 +03:00
goto out ;
2006-01-16 19:50:04 +03:00
}
/**
* gfs2_prepare_write - Prepare to write a page to a file
* @ file : The file to write to
* @ page : The page which is to be prepared for writing
* @ from : From ( byte range within page )
* @ to : To ( byte range within page )
*
* Returns : errno
*/
static int gfs2_prepare_write ( struct file * file , struct page * page ,
unsigned from , unsigned to )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( page - > mapping - > host ) ;
struct gfs2_sbd * sdp = GFS2_SB ( page - > mapping - > host ) ;
2006-02-08 14:50:51 +03:00
unsigned int data_blocks , ind_blocks , rblocks ;
int alloc_required ;
2006-01-16 19:50:04 +03:00
int error = 0 ;
2006-02-08 14:50:51 +03:00
loff_t pos = ( ( loff_t ) page - > index < < PAGE_CACHE_SHIFT ) + from ;
loff_t end = ( ( loff_t ) page - > index < < PAGE_CACHE_SHIFT ) + to ;
struct gfs2_alloc * al ;
2006-01-16 19:50:04 +03:00
2006-04-18 18:09:15 +04:00
gfs2_holder_init ( ip - > i_gl , LM_ST_EXCLUSIVE , GL_ATIME | GL_AOP , & ip - > i_gh ) ;
2006-02-08 14:50:51 +03:00
error = gfs2_glock_nq_m_atime ( 1 , & ip - > i_gh ) ;
if ( error )
goto out_uninit ;
2006-01-16 19:50:04 +03:00
2006-02-08 14:50:51 +03:00
gfs2_write_calc_reserv ( ip , to - from , & data_blocks , & ind_blocks ) ;
error = gfs2_write_alloc_required ( ip , pos , from - to , & alloc_required ) ;
if ( error )
goto out_unlock ;
2006-01-16 19:50:04 +03:00
2006-02-08 14:50:51 +03:00
if ( alloc_required ) {
al = gfs2_alloc_get ( ip ) ;
error = gfs2_quota_lock ( ip , NO_QUOTA_CHANGE , NO_QUOTA_CHANGE ) ;
if ( error )
goto out_alloc_put ;
error = gfs2_quota_check ( ip , ip - > i_di . di_uid , ip - > i_di . di_gid ) ;
if ( error )
goto out_qunlock ;
al - > al_requested = data_blocks + ind_blocks ;
error = gfs2_inplace_reserve ( ip ) ;
if ( error )
goto out_qunlock ;
}
rblocks = RES_DINODE + ind_blocks ;
if ( gfs2_is_jdata ( ip ) )
rblocks + = data_blocks ? data_blocks : 1 ;
if ( ind_blocks | | data_blocks )
rblocks + = RES_STATFS + RES_QUOTA ;
error = gfs2_trans_begin ( sdp , rblocks , 0 ) ;
if ( error )
goto out ;
if ( gfs2_is_stuffed ( ip ) ) {
if ( end > sdp - > sd_sb . sb_bsize - sizeof ( struct gfs2_dinode ) ) {
2006-07-26 18:51:20 +04:00
error = gfs2_unstuff_dinode ( ip , page ) ;
2006-02-15 15:26:19 +03:00
if ( error = = 0 )
goto prepare_write ;
} else if ( ! PageUptodate ( page ) )
2006-01-16 19:50:04 +03:00
error = stuffed_readpage ( ip , page ) ;
2006-02-15 15:26:19 +03:00
goto out ;
2006-02-08 14:50:51 +03:00
}
2006-02-15 15:26:19 +03:00
prepare_write :
2006-02-08 14:50:51 +03:00
error = block_prepare_write ( page , from , to , gfs2_get_block ) ;
out :
if ( error ) {
gfs2_trans_end ( sdp ) ;
if ( alloc_required ) {
gfs2_inplace_release ( ip ) ;
out_qunlock :
gfs2_quota_unlock ( ip ) ;
out_alloc_put :
gfs2_alloc_put ( ip ) ;
}
out_unlock :
gfs2_glock_dq_m ( 1 , & ip - > i_gh ) ;
out_uninit :
gfs2_holder_uninit ( & ip - > i_gh ) ;
}
2006-01-16 19:50:04 +03:00
return error ;
}
/**
* gfs2_commit_write - Commit write to a file
* @ file : The file to write to
* @ page : The page containing the data
* @ from : From ( byte range within page )
* @ to : To ( byte range within page )
*
* Returns : errno
*/
static int gfs2_commit_write ( struct file * file , struct page * page ,
unsigned from , unsigned to )
{
struct inode * inode = page - > mapping - > host ;
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( inode ) ;
struct gfs2_sbd * sdp = GFS2_SB ( inode ) ;
2006-02-08 14:50:51 +03:00
int error = - EOPNOTSUPP ;
struct buffer_head * dibh ;
struct gfs2_alloc * al = & ip - > i_alloc ; ;
2006-01-16 19:50:04 +03:00
2006-02-08 14:50:51 +03:00
if ( gfs2_assert_withdraw ( sdp , gfs2_glock_is_locked_by_me ( ip - > i_gl ) ) )
goto fail_nounlock ;
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( error )
goto fail_endtrans ;
gfs2_trans_add_bh ( ip - > i_gl , dibh , 1 ) ;
2006-01-16 19:50:04 +03:00
if ( gfs2_is_stuffed ( ip ) ) {
uint64_t file_size ;
void * kaddr ;
file_size = ( ( uint64_t ) page - > index < < PAGE_CACHE_SHIFT ) + to ;
2006-02-08 14:50:51 +03:00
kaddr = kmap_atomic ( page , KM_USER0 ) ;
2006-01-16 19:50:04 +03:00
memcpy ( dibh - > b_data + sizeof ( struct gfs2_dinode ) + from ,
2006-02-08 14:50:51 +03:00
( char * ) kaddr + from , to - from ) ;
kunmap_atomic ( page , KM_USER0 ) ;
2006-01-16 19:50:04 +03:00
SetPageUptodate ( page ) ;
if ( inode - > i_size < file_size )
i_size_write ( inode , file_size ) ;
} else {
2006-02-27 20:00:42 +03:00
if ( sdp - > sd_args . ar_data = = GFS2_DATA_ORDERED | |
gfs2_is_jdata ( ip ) )
2006-01-31 13:00:25 +03:00
gfs2_page_add_databufs ( ip , page , from , to ) ;
2006-01-16 19:50:04 +03:00
error = generic_commit_write ( file , page , from , to ) ;
if ( error )
goto fail ;
}
2006-02-08 14:50:51 +03:00
if ( ip - > i_di . di_size < inode - > i_size )
ip - > i_di . di_size = inode - > i_size ;
gfs2_dinode_out ( & ip - > i_di , dibh - > b_data ) ;
brelse ( dibh ) ;
gfs2_trans_end ( sdp ) ;
if ( al - > al_requested ) {
gfs2_inplace_release ( ip ) ;
gfs2_quota_unlock ( ip ) ;
gfs2_alloc_put ( ip ) ;
}
gfs2_glock_dq_m ( 1 , & ip - > i_gh ) ;
gfs2_holder_uninit ( & ip - > i_gh ) ;
2006-01-16 19:50:04 +03:00
return 0 ;
2006-02-08 14:50:51 +03:00
fail :
brelse ( dibh ) ;
fail_endtrans :
gfs2_trans_end ( sdp ) ;
if ( al - > al_requested ) {
gfs2_inplace_release ( ip ) ;
gfs2_quota_unlock ( ip ) ;
gfs2_alloc_put ( ip ) ;
}
gfs2_glock_dq_m ( 1 , & ip - > i_gh ) ;
gfs2_holder_uninit ( & ip - > i_gh ) ;
fail_nounlock :
2006-01-16 19:50:04 +03:00
ClearPageUptodate ( page ) ;
return error ;
}
/**
* gfs2_bmap - Block map function
* @ mapping : Address space info
* @ lblock : The block to map
*
* Returns : The disk address for the block or 0 on hole or error
*/
static sector_t gfs2_bmap ( struct address_space * mapping , sector_t lblock )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( mapping - > host ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder i_gh ;
sector_t dblock = 0 ;
int error ;
error = gfs2_glock_nq_init ( ip - > i_gl , LM_ST_SHARED , LM_FLAG_ANY , & i_gh ) ;
if ( error )
return 0 ;
if ( ! gfs2_is_stuffed ( ip ) )
2006-01-30 12:39:10 +03:00
dblock = generic_block_bmap ( mapping , lblock , gfs2_get_block ) ;
2006-01-16 19:50:04 +03:00
gfs2_glock_dq_uninit ( & i_gh ) ;
return dblock ;
}
static void discard_buffer ( struct gfs2_sbd * sdp , struct buffer_head * bh )
{
2006-01-18 16:14:40 +03:00
struct gfs2_bufdata * bd ;
2006-01-16 19:50:04 +03:00
gfs2_log_lock ( sdp ) ;
2006-02-28 01:23:27 +03:00
bd = bh - > b_private ;
2006-01-18 16:14:40 +03:00
if ( bd ) {
bd - > bd_bh = NULL ;
2006-02-28 01:23:27 +03:00
bh - > b_private = NULL ;
2006-01-16 19:50:04 +03:00
gfs2_log_unlock ( sdp ) ;
brelse ( bh ) ;
} else
gfs2_log_unlock ( sdp ) ;
lock_buffer ( bh ) ;
clear_buffer_dirty ( bh ) ;
bh - > b_bdev = NULL ;
clear_buffer_mapped ( bh ) ;
clear_buffer_req ( bh ) ;
clear_buffer_new ( bh ) ;
clear_buffer_delay ( bh ) ;
unlock_buffer ( bh ) ;
}
2006-04-01 01:48:41 +04:00
static void gfs2_invalidatepage ( struct page * page , unsigned long offset )
2006-01-16 19:50:04 +03:00
{
2006-02-28 01:23:27 +03:00
struct gfs2_sbd * sdp = page - > mapping - > host - > i_sb - > s_fs_info ;
2006-01-16 19:50:04 +03:00
struct buffer_head * head , * bh , * next ;
unsigned int curr_off = 0 ;
BUG_ON ( ! PageLocked ( page ) ) ;
if ( ! page_has_buffers ( page ) )
2006-04-01 01:48:41 +04:00
return ;
2006-01-16 19:50:04 +03:00
bh = head = page_buffers ( page ) ;
do {
unsigned int next_off = curr_off + bh - > b_size ;
next = bh - > b_this_page ;
if ( offset < = curr_off )
discard_buffer ( sdp , bh ) ;
curr_off = next_off ;
bh = next ;
} while ( bh ! = head ) ;
if ( ! offset )
2006-04-01 01:48:41 +04:00
try_to_release_page ( page , 0 ) ;
2006-01-16 19:50:04 +03:00
2006-04-01 01:48:41 +04:00
return ;
2006-01-16 19:50:04 +03:00
}
2006-07-26 01:24:12 +04:00
static ssize_t gfs2_direct_IO ( int rw , struct kiocb * iocb ,
const struct iovec * iov , loff_t offset ,
unsigned long nr_segs )
2006-02-14 14:54:42 +03:00
{
struct file * file = iocb - > ki_filp ;
struct inode * inode = file - > f_mapping - > host ;
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( inode ) ;
2006-02-14 14:54:42 +03:00
struct gfs2_holder gh ;
int rv ;
2006-07-26 01:24:12 +04:00
if ( rw = = READ )
mutex_lock ( & inode - > i_mutex ) ;
2006-02-14 14:54:42 +03:00
/*
2006-07-26 01:24:12 +04:00
* Shared lock , even if its a write , since we do no allocation
2006-02-14 14:54:42 +03:00
* on this path . All we need change is atime .
*/
gfs2_holder_init ( ip - > i_gl , LM_ST_SHARED , GL_ATIME , & gh ) ;
rv = gfs2_glock_nq_m_atime ( 1 , & gh ) ;
if ( rv )
goto out ;
2006-07-26 01:24:12 +04:00
if ( offset > i_size_read ( inode ) )
goto out ;
2006-02-14 14:54:42 +03:00
/*
* Should we return an error here ? I can ' t see that O_DIRECT for
* a journaled file makes any sense . For now we ' ll silently fall
* back to buffered I / O , likewise we do the same for stuffed
* files since they are ( a ) small and ( b ) unaligned .
*/
if ( gfs2_is_jdata ( ip ) )
goto out ;
if ( gfs2_is_stuffed ( ip ) )
goto out ;
2006-07-26 01:24:12 +04:00
rv = blockdev_direct_IO_own_locking ( rw , iocb , inode ,
inode - > i_sb - > s_bdev ,
iov , offset , nr_segs ,
gfs2_get_block , NULL ) ;
2006-02-14 14:54:42 +03:00
out :
gfs2_glock_dq_m ( 1 , & gh ) ;
gfs2_holder_uninit ( & gh ) ;
2006-07-26 01:24:12 +04:00
if ( rw = = READ )
mutex_unlock ( & inode - > i_mutex ) ;
2006-02-14 14:54:42 +03:00
return rv ;
}
2006-07-11 17:46:33 +04:00
/**
* stuck_releasepage - We ' re stuck in gfs2_releasepage ( ) . Print stuff out .
* @ bh : the buffer we ' re stuck on
*
*/
static void stuck_releasepage ( struct buffer_head * bh )
{
struct inode * inode = bh - > b_page - > mapping - > host ;
struct gfs2_sbd * sdp = inode - > i_sb - > s_fs_info ;
struct gfs2_bufdata * bd = bh - > b_private ;
struct gfs2_glock * gl ;
fs_warn ( sdp , " stuck in gfs2_releasepage() %p \n " , inode ) ;
fs_warn ( sdp , " blkno = %llu, bh->b_count = %d \n " ,
( unsigned long long ) bh - > b_blocknr , atomic_read ( & bh - > b_count ) ) ;
fs_warn ( sdp , " pinned = %u \n " , buffer_pinned ( bh ) ) ;
fs_warn ( sdp , " bh->b_private = %s \n " , ( bd ) ? " !NULL " : " NULL " ) ;
if ( ! bd )
return ;
gl = bd - > bd_gl ;
fs_warn ( sdp , " gl = (%u, %llu) \n " ,
gl - > gl_name . ln_type , ( unsigned long long ) gl - > gl_name . ln_number ) ;
fs_warn ( sdp , " bd_list_tr = %s, bd_le.le_list = %s \n " ,
( list_empty ( & bd - > bd_list_tr ) ) ? " no " : " yes " ,
( list_empty ( & bd - > bd_le . le_list ) ) ? " no " : " yes " ) ;
if ( gl - > gl_ops = = & gfs2_inode_glops ) {
struct gfs2_inode * ip = gl - > gl_object ;
unsigned int x ;
if ( ! ip )
return ;
fs_warn ( sdp , " ip = %llu %llu \n " ,
( unsigned long long ) ip - > i_num . no_formal_ino ,
( unsigned long long ) ip - > i_num . no_addr ) ;
for ( x = 0 ; x < GFS2_MAX_META_HEIGHT ; x + + )
fs_warn ( sdp , " ip->i_cache[%u] = %s \n " ,
x , ( ip - > i_cache [ x ] ) ? " !NULL " : " NULL " ) ;
}
}
/**
* gfs2_aspace_releasepage - free the metadata associated with a page
* @ page : the page that ' s being released
* @ gfp_mask : passed from Linux VFS , ignored by us
*
* Call try_to_free_buffers ( ) if the buffers in this page can be
* released .
*
* Returns : 0
*/
int gfs2_releasepage ( struct page * page , gfp_t gfp_mask )
{
struct inode * aspace = page - > mapping - > host ;
struct gfs2_sbd * sdp = aspace - > i_sb - > s_fs_info ;
struct buffer_head * bh , * head ;
struct gfs2_bufdata * bd ;
unsigned long t ;
if ( ! page_has_buffers ( page ) )
goto out ;
head = bh = page_buffers ( page ) ;
do {
t = jiffies ;
while ( atomic_read ( & bh - > b_count ) ) {
if ( atomic_read ( & aspace - > i_writecount ) ) {
if ( time_after_eq ( jiffies , t +
gfs2_tune_get ( sdp , gt_stall_secs ) * HZ ) ) {
stuck_releasepage ( bh ) ;
t = jiffies ;
}
yield ( ) ;
continue ;
}
return 0 ;
}
gfs2_assert_warn ( sdp , ! buffer_pinned ( bh ) ) ;
bd = bh - > b_private ;
if ( bd ) {
gfs2_assert_warn ( sdp , bd - > bd_bh = = bh ) ;
gfs2_assert_warn ( sdp , list_empty ( & bd - > bd_list_tr ) ) ;
gfs2_assert_warn ( sdp , list_empty ( & bd - > bd_le . le_list ) ) ;
gfs2_assert_warn ( sdp , ! bd - > bd_ail ) ;
kmem_cache_free ( gfs2_bufdata_cachep , bd ) ;
bh - > b_private = NULL ;
}
bh = bh - > b_this_page ;
}
while ( bh ! = head ) ;
2006-07-26 01:24:12 +04:00
out :
2006-07-11 17:46:33 +04:00
return try_to_free_buffers ( page ) ;
}
2006-07-03 21:37:30 +04:00
const struct address_space_operations gfs2_file_aops = {
2006-01-16 19:50:04 +03:00
. writepage = gfs2_writepage ,
. readpage = gfs2_readpage ,
2006-05-06 00:59:11 +04:00
. readpages = gfs2_readpages ,
2006-01-16 19:50:04 +03:00
. sync_page = block_sync_page ,
. prepare_write = gfs2_prepare_write ,
. commit_write = gfs2_commit_write ,
. bmap = gfs2_bmap ,
. invalidatepage = gfs2_invalidatepage ,
2006-07-11 17:46:33 +04:00
. releasepage = gfs2_releasepage ,
2006-01-16 19:50:04 +03:00
. direct_IO = gfs2_direct_IO ,
} ;