2006-01-16 19:50:04 +03:00
/*
* Copyright ( C ) Sistina Software , Inc . 1997 - 2003 All rights reserved .
2008-04-29 21:35:48 +04:00
* Copyright ( C ) 2004 - 2008 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
2006-09-01 19:05:15 +04:00
* of the GNU General Public License version 2.
2006-01-16 19:50:04 +03:00
*/
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/completion.h>
# include <linux/buffer_head.h>
# include <linux/mm.h>
# include <linux/pagemap.h>
# include <linux/writeback.h>
# include <linux/swap.h>
# include <linux/delay.h>
2006-10-02 19:38:25 +04:00
# include <linux/bio.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 "incore.h"
2006-01-16 19:50:04 +03:00
# include "glock.h"
# include "glops.h"
# include "inode.h"
# include "log.h"
# include "lops.h"
# include "meta_io.h"
# include "rgrp.h"
# include "trans.h"
2006-02-28 01:23:27 +03:00
# include "util.h"
2006-01-16 19:50:04 +03:00
2009-04-20 11:16:26 +04:00
static int gfs2_aspace_writepage ( struct page * page , struct writeback_control * wbc )
2006-01-16 19:50:04 +03:00
{
2009-04-20 11:16:26 +04:00
int err ;
struct buffer_head * bh , * head ;
int nr_underway = 0 ;
int write_op = ( 1 < < BIO_RW_META ) | ( ( wbc - > sync_mode = = WB_SYNC_ALL ?
WRITE_SYNC_PLUG : WRITE ) ) ;
2006-01-16 19:50:04 +03:00
2009-04-20 11:16:26 +04:00
BUG_ON ( ! PageLocked ( page ) ) ;
BUG_ON ( ! page_has_buffers ( page ) ) ;
head = page_buffers ( page ) ;
bh = head ;
do {
if ( ! buffer_mapped ( bh ) )
continue ;
/*
* If it ' s a fully non - blocking write attempt and we cannot
* lock the buffer then redirty the page . Note that this can
* potentially cause a busy - wait loop from pdflush and kswapd
* activity , but those code paths have their own higher - level
* throttling .
*/
if ( wbc - > sync_mode ! = WB_SYNC_NONE | | ! wbc - > nonblocking ) {
lock_buffer ( bh ) ;
} else if ( ! trylock_buffer ( bh ) ) {
redirty_page_for_writepage ( wbc , page ) ;
continue ;
}
if ( test_clear_buffer_dirty ( bh ) ) {
mark_buffer_async_write ( bh ) ;
} else {
unlock_buffer ( bh ) ;
}
} while ( ( bh = bh - > b_this_page ) ! = head ) ;
/*
* The page and its buffers are protected by PageWriteback ( ) , so we can
* drop the bh refcounts early .
*/
BUG_ON ( PageWriteback ( page ) ) ;
set_page_writeback ( page ) ;
do {
struct buffer_head * next = bh - > b_this_page ;
if ( buffer_async_write ( bh ) ) {
submit_bh ( write_op , bh ) ;
nr_underway + + ;
}
bh = next ;
} while ( bh ! = head ) ;
unlock_page ( page ) ;
err = 0 ;
if ( nr_underway = = 0 )
end_page_writeback ( page ) ;
return err ;
2006-01-16 19:50:04 +03:00
}
2006-07-03 21:37:30 +04:00
static const struct address_space_operations aspace_aops = {
2006-01-16 19:50:04 +03:00
. writepage = gfs2_aspace_writepage ,
2006-07-11 17:46:33 +04:00
. releasepage = gfs2_releasepage ,
2007-11-01 12:34:14 +03:00
. sync_page = block_sync_page ,
2006-01-16 19:50:04 +03:00
} ;
/**
* gfs2_aspace_get - Create and initialize a struct inode structure
* @ sdp : the filesystem the aspace is in
*
* Right now a struct inode is just a struct inode . Maybe Linux
* will supply a more lightweight address space construct ( that works )
* in the future .
*
* Make sure pages / buffers in this aspace aren ' t in high memory .
*
* Returns : the aspace
*/
struct inode * gfs2_aspace_get ( struct gfs2_sbd * sdp )
{
struct inode * aspace ;
2008-04-29 21:35:48 +04:00
struct gfs2_inode * ip ;
2006-01-16 19:50:04 +03:00
aspace = new_inode ( sdp - > sd_vfs ) ;
if ( aspace ) {
2006-07-11 17:50:54 +04:00
mapping_set_gfp_mask ( aspace - > i_mapping , GFP_NOFS ) ;
2006-01-16 19:50:04 +03:00
aspace - > i_mapping - > a_ops = & aspace_aops ;
2010-01-08 16:44:49 +03:00
aspace - > i_size = MAX_LFS_FILESIZE ;
2008-04-29 21:35:48 +04:00
ip = GFS2_I ( aspace ) ;
clear_bit ( GIF_USER , & ip - > i_flags ) ;
2006-01-16 19:50:04 +03:00
insert_inode_hash ( aspace ) ;
}
return aspace ;
}
void gfs2_aspace_put ( struct inode * aspace )
{
remove_inode_hash ( aspace ) ;
iput ( aspace ) ;
}
/**
* gfs2_meta_sync - Sync all buffers associated with a glock
* @ gl : The glock
*
*/
2006-09-22 01:05:23 +04:00
void gfs2_meta_sync ( struct gfs2_glock * gl )
2006-01-16 19:50:04 +03:00
{
struct address_space * mapping = gl - > gl_aspace - > i_mapping ;
2006-09-22 01:05:23 +04:00
int error ;
2006-01-16 19:50:04 +03:00
2006-09-22 01:05:23 +04:00
filemap_fdatawrite ( mapping ) ;
error = filemap_fdatawait ( mapping ) ;
2006-01-16 19:50:04 +03:00
if ( error )
gfs2_io_error ( gl - > gl_sbd ) ;
}
/**
2008-05-21 20:03:22 +04:00
* gfs2_getbuf - Get a buffer with a given address space
2006-11-23 19:16:32 +03:00
* @ gl : the glock
2006-01-16 19:50:04 +03:00
* @ blkno : the block number ( filesystem scope )
* @ create : 1 if the buffer should be created
*
* Returns : the buffer
*/
2008-05-21 20:03:22 +04:00
struct buffer_head * gfs2_getbuf ( struct gfs2_glock * gl , u64 blkno , int create )
2006-01-16 19:50:04 +03:00
{
2006-11-23 19:16:32 +03:00
struct address_space * mapping = gl - > gl_aspace - > i_mapping ;
struct gfs2_sbd * sdp = gl - > gl_sbd ;
2006-01-16 19:50:04 +03:00
struct page * page ;
struct buffer_head * bh ;
unsigned int shift ;
unsigned long index ;
unsigned int bufnum ;
shift = PAGE_CACHE_SHIFT - sdp - > sd_sb . sb_bsize_shift ;
index = blkno > > shift ; /* convert block to page */
bufnum = blkno - ( index < < shift ) ; /* block buf index within page */
if ( create ) {
for ( ; ; ) {
2006-11-23 19:16:32 +03:00
page = grab_cache_page ( mapping , index ) ;
2006-01-16 19:50:04 +03:00
if ( page )
break ;
yield ( ) ;
}
} else {
2006-11-23 19:16:32 +03:00
page = find_lock_page ( mapping , index ) ;
2006-01-16 19:50:04 +03:00
if ( ! page )
return NULL ;
}
if ( ! page_has_buffers ( page ) )
create_empty_buffers ( page , sdp - > sd_sb . sb_bsize , 0 ) ;
/* Locate header for our buffer within our page */
for ( bh = page_buffers ( page ) ; bufnum - - ; bh = bh - > b_this_page )
/* Do nothing */ ;
get_bh ( bh ) ;
if ( ! buffer_mapped ( bh ) )
map_bh ( bh , sdp - > sd_vfs , blkno ) ;
unlock_page ( page ) ;
mark_page_accessed ( page ) ;
page_cache_release ( page ) ;
return bh ;
}
static void meta_prep_new ( struct buffer_head * bh )
{
struct gfs2_meta_header * mh = ( struct gfs2_meta_header * ) bh - > b_data ;
lock_buffer ( bh ) ;
clear_buffer_dirty ( bh ) ;
set_buffer_uptodate ( bh ) ;
unlock_buffer ( bh ) ;
mh - > mh_magic = cpu_to_be32 ( GFS2_MAGIC ) ;
}
/**
* gfs2_meta_new - Get a block
* @ gl : The glock associated with this block
* @ blkno : The block number
*
* Returns : The buffer
*/
2006-09-04 20:49:07 +04:00
struct buffer_head * gfs2_meta_new ( struct gfs2_glock * gl , u64 blkno )
2006-01-16 19:50:04 +03:00
{
struct buffer_head * bh ;
2008-05-21 20:03:22 +04:00
bh = gfs2_getbuf ( gl , blkno , CREATE ) ;
2006-01-16 19:50:04 +03:00
meta_prep_new ( bh ) ;
return bh ;
}
/**
* gfs2_meta_read - Read a block from disk
* @ gl : The glock covering the block
* @ blkno : The block number
2006-09-22 01:05:23 +04:00
* @ flags : flags
2006-01-16 19:50:04 +03:00
* @ bhp : the place where the buffer is returned ( NULL on failure )
*
* Returns : errno
*/
2006-09-04 20:49:07 +04:00
int gfs2_meta_read ( struct gfs2_glock * gl , u64 blkno , int flags ,
2006-01-16 19:50:04 +03:00
struct buffer_head * * bhp )
{
2009-04-07 17:13:01 +04:00
struct gfs2_sbd * sdp = gl - > gl_sbd ;
struct buffer_head * bh ;
if ( unlikely ( test_bit ( SDF_SHUTDOWN , & sdp - > sd_flags ) ) )
return - EIO ;
* bhp = bh = gfs2_getbuf ( gl , blkno , CREATE ) ;
lock_buffer ( bh ) ;
if ( buffer_uptodate ( bh ) ) {
unlock_buffer ( bh ) ;
return 0 ;
}
bh - > b_end_io = end_buffer_read_sync ;
get_bh ( bh ) ;
submit_bh ( READ_SYNC | ( 1 < < BIO_RW_META ) , bh ) ;
if ( ! ( flags & DIO_WAIT ) )
return 0 ;
wait_on_buffer ( bh ) ;
if ( unlikely ( ! buffer_uptodate ( bh ) ) ) {
struct gfs2_trans * tr = current - > journal_info ;
if ( tr & & tr - > tr_touched )
gfs2_io_error_bh ( sdp , bh ) ;
brelse ( bh ) ;
return - EIO ;
2006-09-22 01:05:23 +04:00
}
2006-01-16 19:50:04 +03:00
2006-09-22 01:05:23 +04:00
return 0 ;
2006-01-16 19:50:04 +03:00
}
/**
2006-09-22 01:05:23 +04:00
* gfs2_meta_wait - Reread a block from disk
2006-01-16 19:50:04 +03:00
* @ sdp : the filesystem
2006-09-22 01:05:23 +04:00
* @ bh : The block to wait for
2006-01-16 19:50:04 +03:00
*
* Returns : errno
*/
2006-09-22 01:05:23 +04:00
int gfs2_meta_wait ( struct gfs2_sbd * sdp , struct buffer_head * bh )
2006-01-16 19:50:04 +03:00
{
if ( unlikely ( test_bit ( SDF_SHUTDOWN , & sdp - > sd_flags ) ) )
return - EIO ;
2006-09-22 01:05:23 +04:00
wait_on_buffer ( bh ) ;
2006-01-16 19:50:04 +03:00
2006-09-22 01:05:23 +04:00
if ( ! buffer_uptodate ( bh ) ) {
struct gfs2_trans * tr = current - > journal_info ;
if ( tr & & tr - > tr_touched )
gfs2_io_error_bh ( sdp , bh ) ;
return - EIO ;
2006-01-16 19:50:04 +03:00
}
2006-09-22 01:05:23 +04:00
if ( unlikely ( test_bit ( SDF_SHUTDOWN , & sdp - > sd_flags ) ) )
return - EIO ;
2006-01-16 19:50:04 +03:00
return 0 ;
}
/**
2006-01-18 14:32:00 +03:00
* gfs2_attach_bufdata - attach a struct gfs2_bufdata structure to a buffer
2006-01-16 19:50:04 +03:00
* @ gl : the glock the buffer belongs to
* @ bh : The buffer to be attached to
2006-01-18 14:32:00 +03:00
* @ meta : Flag to indicate whether its metadata or not
2006-01-16 19:50:04 +03:00
*/
2006-02-27 20:00:42 +03:00
void gfs2_attach_bufdata ( struct gfs2_glock * gl , struct buffer_head * bh ,
int meta )
2006-01-16 19:50:04 +03:00
{
struct gfs2_bufdata * bd ;
2006-02-08 14:50:51 +03:00
if ( meta )
lock_page ( bh - > b_page ) ;
2006-01-16 19:50:04 +03:00
2006-02-28 01:23:27 +03:00
if ( bh - > b_private ) {
2006-02-08 14:50:51 +03:00
if ( meta )
unlock_page ( bh - > b_page ) ;
2006-01-16 19:50:04 +03:00
return ;
}
2008-01-16 17:45:39 +03:00
bd = kmem_cache_zalloc ( gfs2_bufdata_cachep , GFP_NOFS | __GFP_NOFAIL ) ;
2006-01-16 19:50:04 +03:00
bd - > bd_bh = bh ;
bd - > bd_gl = gl ;
INIT_LIST_HEAD ( & bd - > bd_list_tr ) ;
2006-09-04 22:47:06 +04:00
if ( meta )
2006-01-18 14:32:00 +03:00
lops_init_le ( & bd - > bd_le , & gfs2_buf_lops ) ;
2006-09-04 22:47:06 +04:00
else
2006-01-18 14:32:00 +03:00
lops_init_le ( & bd - > bd_le , & gfs2_databuf_lops ) ;
2006-02-28 01:23:27 +03:00
bh - > b_private = bd ;
2006-01-16 19:50:04 +03:00
2006-02-08 14:50:51 +03:00
if ( meta )
unlock_page ( bh - > b_page ) ;
2006-01-16 19:50:04 +03:00
}
2007-09-17 13:59:52 +04:00
void gfs2_remove_from_journal ( struct buffer_head * bh , struct gfs2_trans * tr , int meta )
{
struct gfs2_sbd * sdp = GFS2_SB ( bh - > b_page - > mapping - > host ) ;
struct gfs2_bufdata * bd = bh - > b_private ;
if ( test_clear_buffer_pinned ( bh ) ) {
list_del_init ( & bd - > bd_le . le_list ) ;
if ( meta ) {
gfs2_assert_warn ( sdp , sdp - > sd_log_num_buf ) ;
sdp - > sd_log_num_buf - - ;
tr - > tr_num_buf_rm + + ;
} else {
gfs2_assert_warn ( sdp , sdp - > sd_log_num_databuf ) ;
sdp - > sd_log_num_databuf - - ;
tr - > tr_num_databuf_rm + + ;
}
tr - > tr_touched = 1 ;
brelse ( bh ) ;
}
if ( bd ) {
if ( bd - > bd_ail ) {
2007-10-15 19:29:05 +04:00
gfs2_remove_from_ail ( bd ) ;
2007-09-17 13:59:52 +04:00
bh - > b_private = NULL ;
bd - > bd_bh = NULL ;
bd - > bd_blkno = bh - > b_blocknr ;
gfs2_trans_add_revoke ( sdp , bd ) ;
}
}
clear_buffer_dirty ( bh ) ;
clear_buffer_uptodate ( bh ) ;
}
2006-01-16 19:50:04 +03:00
/**
* gfs2_meta_wipe - make inode ' s buffers so they aren ' t dirty / pinned anymore
* @ ip : the inode who owns the buffers
* @ bstart : the first buffer in the run
* @ blen : the number of buffers in the run
*
*/
2006-09-04 20:49:07 +04:00
void gfs2_meta_wipe ( struct gfs2_inode * ip , u64 bstart , u32 blen )
2006-01-16 19:50:04 +03:00
{
2006-06-14 23:32:57 +04:00
struct gfs2_sbd * sdp = GFS2_SB ( & ip - > i_inode ) ;
2006-01-16 19:50:04 +03:00
struct buffer_head * bh ;
while ( blen ) {
2008-05-21 20:03:22 +04:00
bh = gfs2_getbuf ( ip - > i_gl , bstart , NO_CREATE ) ;
2006-01-16 19:50:04 +03:00
if ( bh ) {
2007-09-03 14:01:33 +04:00
lock_buffer ( bh ) ;
gfs2_log_lock ( sdp ) ;
2007-09-17 13:59:52 +04:00
gfs2_remove_from_journal ( bh , current - > journal_info , 1 ) ;
2007-09-03 14:01:33 +04:00
gfs2_log_unlock ( sdp ) ;
2006-01-16 19:50:04 +03:00
unlock_buffer ( bh ) ;
brelse ( bh ) ;
}
bstart + + ;
blen - - ;
}
}
/**
* gfs2_meta_indirect_buffer - Get a metadata buffer
* @ ip : The GFS2 inode
* @ height : The level of this buf in the metadata ( indir addr ) tree ( if any )
* @ num : The block number ( device relative ) of the buffer
* @ new : Non - zero if we may create a new buffer
* @ bhp : the buffer is returned here
*
* Returns : errno
*/
2006-09-04 20:49:07 +04:00
int gfs2_meta_indirect_buffer ( struct gfs2_inode * ip , int height , u64 num ,
2006-01-16 19:50:04 +03:00
int new , struct buffer_head * * bhp )
{
2006-09-22 01:05:23 +04:00
struct gfs2_sbd * sdp = GFS2_SB ( & ip - > i_inode ) ;
struct gfs2_glock * gl = ip - > i_gl ;
2007-10-15 19:29:05 +04:00
struct buffer_head * bh ;
int ret = 0 ;
2006-01-16 19:50:04 +03:00
if ( new ) {
2007-10-15 19:29:05 +04:00
BUG_ON ( height = = 0 ) ;
bh = gfs2_meta_new ( gl , num ) ;
2006-01-18 14:19:28 +03:00
gfs2_trans_add_bh ( ip - > i_gl , bh , 1 ) ;
2006-01-16 19:50:04 +03:00
gfs2_metatype_set ( bh , GFS2_METATYPE_IN , GFS2_FORMAT_IN ) ;
gfs2_buffer_clear_tail ( bh , sizeof ( struct gfs2_meta_header ) ) ;
2006-09-22 01:05:23 +04:00
} else {
u32 mtype = height ? GFS2_METATYPE_IN : GFS2_METATYPE_DI ;
2007-10-15 19:29:05 +04:00
ret = gfs2_meta_read ( gl , num , DIO_WAIT , & bh ) ;
if ( ret = = 0 & & gfs2_metatype_check ( sdp , bh , mtype ) ) {
brelse ( bh ) ;
ret = - EIO ;
2006-09-22 01:05:23 +04:00
}
2006-01-16 19:50:04 +03:00
}
* bhp = bh ;
2007-10-15 19:29:05 +04:00
return ret ;
2006-01-16 19:50:04 +03:00
}
/**
* gfs2_meta_ra - start readahead on an extent of a file
* @ gl : the glock the blocks belong to
* @ dblock : the starting disk block
* @ extlen : the number of blocks in the extent
*
2006-09-22 01:05:23 +04:00
* returns : the first buffer in the extent
2006-01-16 19:50:04 +03:00
*/
2006-09-22 01:05:23 +04:00
struct buffer_head * gfs2_meta_ra ( struct gfs2_glock * gl , u64 dblock , u32 extlen )
2006-01-16 19:50:04 +03:00
{
struct gfs2_sbd * sdp = gl - > gl_sbd ;
struct buffer_head * first_bh , * bh ;
2006-09-04 20:49:07 +04:00
u32 max_ra = gfs2_tune_get ( sdp , gt_max_readahead ) > >
2006-02-27 20:00:42 +03:00
sdp - > sd_sb . sb_bsize_shift ;
2006-01-16 19:50:04 +03:00
2006-09-22 01:05:23 +04:00
BUG_ON ( ! extlen ) ;
if ( max_ra < 1 )
max_ra = 1 ;
2006-01-16 19:50:04 +03:00
if ( extlen > max_ra )
extlen = max_ra ;
2008-05-21 20:03:22 +04:00
first_bh = gfs2_getbuf ( gl , dblock , CREATE ) ;
2006-01-16 19:50:04 +03:00
if ( buffer_uptodate ( first_bh ) )
goto out ;
2006-09-22 01:05:23 +04:00
if ( ! buffer_locked ( first_bh ) )
2009-04-07 17:13:01 +04:00
ll_rw_block ( READ_SYNC | ( 1 < < BIO_RW_META ) , 1 , & first_bh ) ;
2006-01-16 19:50:04 +03:00
dblock + + ;
extlen - - ;
while ( extlen ) {
2008-05-21 20:03:22 +04:00
bh = gfs2_getbuf ( gl , dblock , CREATE ) ;
2006-01-16 19:50:04 +03:00
2006-09-22 01:05:23 +04:00
if ( ! buffer_uptodate ( bh ) & & ! buffer_locked ( bh ) )
ll_rw_block ( READA , 1 , & bh ) ;
brelse ( bh ) ;
2006-01-16 19:50:04 +03:00
dblock + + ;
extlen - - ;
2006-09-22 01:05:23 +04:00
if ( ! buffer_locked ( first_bh ) & & buffer_uptodate ( first_bh ) )
goto out ;
2006-01-16 19:50:04 +03:00
}
2006-09-22 01:05:23 +04:00
wait_on_buffer ( first_bh ) ;
2006-09-04 20:04:26 +04:00
out :
2006-09-22 01:05:23 +04:00
return first_bh ;
2006-01-16 19:50:04 +03:00
}