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
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-02-28 01:23:27 +03:00
# include <linux/gfs2_ondisk.h>
2006-09-19 09:56:29 +04:00
# include <linux/lm_interface.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-07-11 17:46:33 +04:00
# include "ops_address.h"
2006-01-16 19:50:04 +03:00
# define buffer_busy(bh) \
( ( bh ) - > b_state & ( ( 1ul < < BH_Dirty ) | ( 1ul < < BH_Lock ) | ( 1ul < < BH_Pinned ) ) )
# define buffer_in_io(bh) \
( ( bh ) - > b_state & ( ( 1ul < < BH_Dirty ) | ( 1ul < < BH_Lock ) ) )
static int aspace_get_block ( struct inode * inode , sector_t lblock ,
struct buffer_head * bh_result , int create )
{
2006-02-28 01:23:27 +03:00
gfs2_assert_warn ( inode - > i_sb - > s_fs_info , 0 ) ;
2006-01-16 19:50:04 +03:00
return - EOPNOTSUPP ;
}
static int gfs2_aspace_writepage ( struct page * page ,
struct writeback_control * wbc )
{
return block_write_full_page ( page , aspace_get_block , wbc ) ;
}
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 ,
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 ;
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 ;
aspace - > i_size = ~ 0ULL ;
2006-02-28 01:23:27 +03:00
aspace - > u . generic_ip = NULL ;
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_ail1_start_one - Start I / O on a part of the AIL
* @ sdp : the filesystem
* @ tr : the part of the AIL
*
*/
void gfs2_ail1_start_one ( struct gfs2_sbd * sdp , struct gfs2_ail * ai )
{
struct gfs2_bufdata * bd , * s ;
struct buffer_head * bh ;
int retry ;
2006-04-21 00:57:23 +04:00
BUG_ON ( ! spin_is_locked ( & sdp - > sd_log_lock ) ) ;
2006-01-16 19:50:04 +03:00
do {
retry = 0 ;
list_for_each_entry_safe_reverse ( bd , s , & ai - > ai_ail1_list ,
bd_ail_st_list ) {
bh = bd - > bd_bh ;
gfs2_assert ( sdp , bd - > bd_ail = = ai ) ;
if ( ! buffer_busy ( bh ) ) {
2006-05-18 22:10:52 +04:00
if ( ! buffer_uptodate ( bh ) ) {
gfs2_log_unlock ( sdp ) ;
2006-01-16 19:50:04 +03:00
gfs2_io_error_bh ( sdp , bh ) ;
2006-05-18 22:10:52 +04:00
gfs2_log_lock ( sdp ) ;
}
2006-09-20 23:48:09 +04:00
list_move ( & bd - > bd_ail_st_list , & ai - > ai_ail2_list ) ;
2006-01-16 19:50:04 +03:00
continue ;
}
if ( ! buffer_dirty ( bh ) )
continue ;
list_move ( & bd - > bd_ail_st_list , & ai - > ai_ail1_list ) ;
gfs2_log_unlock ( sdp ) ;
wait_on_buffer ( bh ) ;
ll_rw_block ( WRITE , 1 , & bh ) ;
gfs2_log_lock ( sdp ) ;
retry = 1 ;
break ;
}
} while ( retry ) ;
}
/**
* gfs2_ail1_empty_one - Check whether or not a trans in the AIL has been synced
* @ sdp : the filesystem
* @ ai : the AIL entry
*
*/
int gfs2_ail1_empty_one ( struct gfs2_sbd * sdp , struct gfs2_ail * ai , int flags )
{
struct gfs2_bufdata * bd , * s ;
struct buffer_head * bh ;
list_for_each_entry_safe_reverse ( bd , s , & ai - > ai_ail1_list ,
bd_ail_st_list ) {
bh = bd - > bd_bh ;
gfs2_assert ( sdp , bd - > bd_ail = = ai ) ;
if ( buffer_busy ( bh ) ) {
if ( flags & DIO_ALL )
continue ;
else
break ;
}
if ( ! buffer_uptodate ( bh ) )
gfs2_io_error_bh ( sdp , bh ) ;
list_move ( & bd - > bd_ail_st_list , & ai - > ai_ail2_list ) ;
}
return list_empty ( & ai - > ai_ail1_list ) ;
}
/**
* gfs2_ail2_empty_one - Check whether or not a trans in the AIL has been synced
* @ sdp : the filesystem
* @ ai : the AIL entry
*
*/
void gfs2_ail2_empty_one ( struct gfs2_sbd * sdp , struct gfs2_ail * ai )
{
struct list_head * head = & ai - > ai_ail2_list ;
struct gfs2_bufdata * bd ;
while ( ! list_empty ( head ) ) {
bd = list_entry ( head - > prev , struct gfs2_bufdata ,
bd_ail_st_list ) ;
gfs2_assert ( sdp , bd - > bd_ail = = ai ) ;
bd - > bd_ail = NULL ;
list_del ( & bd - > bd_ail_st_list ) ;
list_del ( & bd - > bd_ail_gl_list ) ;
atomic_dec ( & bd - > bd_gl - > gl_ail_count ) ;
brelse ( bd - > bd_bh ) ;
}
}
/**
* ail_empty_gl - remove all buffers for a given lock from the AIL
* @ gl : the glock
*
* None of the buffers should be dirty , locked , or pinned .
*/
void gfs2_ail_empty_gl ( struct gfs2_glock * gl )
{
struct gfs2_sbd * sdp = gl - > gl_sbd ;
unsigned int blocks ;
struct list_head * head = & gl - > gl_ail_list ;
struct gfs2_bufdata * bd ;
struct buffer_head * bh ;
2006-09-04 20:49:07 +04:00
u64 blkno ;
2006-01-16 19:50:04 +03:00
int error ;
blocks = atomic_read ( & gl - > gl_ail_count ) ;
if ( ! blocks )
return ;
error = gfs2_trans_begin ( sdp , 0 , blocks ) ;
if ( gfs2_assert_withdraw ( sdp , ! error ) )
return ;
gfs2_log_lock ( sdp ) ;
while ( ! list_empty ( head ) ) {
bd = list_entry ( head - > next , struct gfs2_bufdata ,
bd_ail_gl_list ) ;
bh = bd - > bd_bh ;
blkno = bh - > b_blocknr ;
gfs2_assert_withdraw ( sdp , ! buffer_busy ( bh ) ) ;
bd - > bd_ail = NULL ;
list_del ( & bd - > bd_ail_st_list ) ;
list_del ( & bd - > bd_ail_gl_list ) ;
atomic_dec ( & gl - > gl_ail_count ) ;
brelse ( bh ) ;
gfs2_log_unlock ( sdp ) ;
gfs2_trans_add_revoke ( sdp , blkno ) ;
gfs2_log_lock ( sdp ) ;
}
gfs2_assert_withdraw ( sdp , ! atomic_read ( & gl - > gl_ail_count ) ) ;
gfs2_log_unlock ( sdp ) ;
gfs2_trans_end ( sdp ) ;
2006-04-07 19:17:32 +04:00
gfs2_log_flush ( sdp , NULL ) ;
2006-01-16 19:50:04 +03:00
}
/**
* gfs2_meta_inval - Invalidate all buffers associated with a glock
* @ gl : the glock
*
*/
void gfs2_meta_inval ( struct gfs2_glock * gl )
{
struct gfs2_sbd * sdp = gl - > gl_sbd ;
struct inode * aspace = gl - > gl_aspace ;
struct address_space * mapping = gl - > gl_aspace - > i_mapping ;
gfs2_assert_withdraw ( sdp , ! atomic_read ( & gl - > gl_ail_count ) ) ;
atomic_inc ( & aspace - > i_writecount ) ;
truncate_inode_pages ( mapping , 0 ) ;
atomic_dec ( & aspace - > i_writecount ) ;
gfs2_assert_withdraw ( sdp , ! mapping - > nrpages ) ;
}
/**
* 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 ) ;
}
/**
* getbuf - Get a buffer with a given address space
* @ sdp : the filesystem
* @ aspace : the address space
* @ blkno : the block number ( filesystem scope )
* @ create : 1 if the buffer should be created
*
* Returns : the buffer
*/
static struct buffer_head * getbuf ( struct gfs2_sbd * sdp , struct inode * aspace ,
2006-09-04 20:49:07 +04:00
u64 blkno , int create )
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 ( ; ; ) {
page = grab_cache_page ( aspace - > i_mapping , index ) ;
if ( page )
break ;
yield ( ) ;
}
} else {
page = find_lock_page ( aspace - > i_mapping , index ) ;
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 ;
bh = getbuf ( gl - > gl_sbd , gl - > gl_aspace , blkno , CREATE ) ;
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 )
{
* bhp = getbuf ( gl - > gl_sbd , gl - > gl_aspace , blkno , CREATE ) ;
2006-09-22 01:05:23 +04:00
if ( ! buffer_uptodate ( * bhp ) )
ll_rw_block ( READ , 1 , bhp ) ;
if ( flags & DIO_WAIT ) {
int error = gfs2_meta_wait ( gl - > gl_sbd , * bhp ) ;
if ( error ) {
brelse ( * bhp ) ;
return error ;
}
}
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 ;
}
2006-02-21 15:51:39 +03:00
bd = kmem_cache_alloc ( gfs2_bufdata_cachep , GFP_NOFS | __GFP_NOFAIL ) ,
2006-01-16 19:50:04 +03:00
memset ( bd , 0 , sizeof ( struct gfs2_bufdata ) ) ;
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
}
/**
2006-01-18 16:38:44 +03:00
* gfs2_pin - Pin a buffer in memory
2006-01-16 19:50:04 +03:00
* @ sdp : the filesystem the buffer belongs to
* @ bh : The buffer to be pinned
*
*/
2006-01-18 16:38:44 +03:00
void gfs2_pin ( struct gfs2_sbd * sdp , struct buffer_head * bh )
2006-01-16 19:50:04 +03:00
{
2006-02-28 01:23:27 +03:00
struct gfs2_bufdata * bd = bh - > b_private ;
2006-01-16 19:50:04 +03:00
gfs2_assert_withdraw ( sdp , test_bit ( SDF_JOURNAL_LIVE , & sdp - > sd_flags ) ) ;
if ( test_set_buffer_pinned ( bh ) )
gfs2_assert_withdraw ( sdp , 0 ) ;
wait_on_buffer ( bh ) ;
/* If this buffer is in the AIL and it has already been written
to in - place disk block , remove it from the AIL . */
gfs2_log_lock ( sdp ) ;
if ( bd - > bd_ail & & ! buffer_in_io ( bh ) )
list_move ( & bd - > bd_ail_st_list , & bd - > bd_ail - > ai_ail2_list ) ;
gfs2_log_unlock ( sdp ) ;
clear_buffer_dirty ( bh ) ;
wait_on_buffer ( bh ) ;
if ( ! buffer_uptodate ( bh ) )
gfs2_io_error_bh ( sdp , bh ) ;
get_bh ( bh ) ;
}
/**
2006-01-18 16:38:44 +03:00
* gfs2_unpin - Unpin a buffer
2006-01-16 19:50:04 +03:00
* @ sdp : the filesystem the buffer belongs to
* @ bh : The buffer to unpin
* @ ai :
*
*/
2006-01-18 16:38:44 +03:00
void gfs2_unpin ( struct gfs2_sbd * sdp , struct buffer_head * bh ,
struct gfs2_ail * ai )
2006-01-16 19:50:04 +03:00
{
2006-02-28 01:23:27 +03:00
struct gfs2_bufdata * bd = bh - > b_private ;
2006-01-16 19:50:04 +03:00
gfs2_assert_withdraw ( sdp , buffer_uptodate ( bh ) ) ;
if ( ! buffer_pinned ( bh ) )
gfs2_assert_withdraw ( sdp , 0 ) ;
mark_buffer_dirty ( bh ) ;
clear_buffer_pinned ( bh ) ;
gfs2_log_lock ( sdp ) ;
if ( bd - > bd_ail ) {
list_del ( & bd - > bd_ail_st_list ) ;
brelse ( bh ) ;
} else {
struct gfs2_glock * gl = bd - > bd_gl ;
list_add ( & bd - > bd_ail_gl_list , & gl - > gl_ail_list ) ;
atomic_inc ( & gl - > gl_ail_count ) ;
}
bd - > bd_ail = ai ;
list_add ( & bd - > bd_ail_st_list , & ai - > ai_ail1_list ) ;
gfs2_log_unlock ( sdp ) ;
}
/**
* 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 inode * aspace = ip - > i_gl - > gl_aspace ;
struct buffer_head * bh ;
while ( blen ) {
bh = getbuf ( sdp , aspace , bstart , NO_CREATE ) ;
if ( bh ) {
2006-02-28 01:23:27 +03:00
struct gfs2_bufdata * bd = bh - > b_private ;
2006-01-16 19:50:04 +03:00
if ( test_clear_buffer_pinned ( bh ) ) {
2006-02-28 01:23:27 +03:00
struct gfs2_trans * tr = current - > journal_info ;
2006-01-16 19:50:04 +03:00
gfs2_log_lock ( sdp ) ;
list_del_init ( & bd - > bd_le . le_list ) ;
gfs2_assert_warn ( sdp , sdp - > sd_log_num_buf ) ;
sdp - > sd_log_num_buf - - ;
gfs2_log_unlock ( sdp ) ;
2006-02-28 01:23:27 +03:00
tr - > tr_num_buf_rm + + ;
2006-01-16 19:50:04 +03:00
brelse ( bh ) ;
}
if ( bd ) {
gfs2_log_lock ( sdp ) ;
if ( bd - > bd_ail ) {
2006-09-04 20:49:07 +04:00
u64 blkno = bh - > b_blocknr ;
2006-01-16 19:50:04 +03:00
bd - > bd_ail = NULL ;
list_del ( & bd - > bd_ail_st_list ) ;
list_del ( & bd - > bd_ail_gl_list ) ;
atomic_dec ( & bd - > bd_gl - > gl_ail_count ) ;
brelse ( bh ) ;
gfs2_log_unlock ( sdp ) ;
gfs2_trans_add_revoke ( sdp , blkno ) ;
} else
gfs2_log_unlock ( sdp ) ;
}
lock_buffer ( bh ) ;
clear_buffer_dirty ( bh ) ;
clear_buffer_uptodate ( bh ) ;
unlock_buffer ( bh ) ;
brelse ( bh ) ;
}
bstart + + ;
blen - - ;
}
}
/**
* gfs2_meta_cache_flush - get rid of any references on buffers for this inode
* @ ip : The GFS2 inode
*
* This releases buffers that are in the most - recently - used array of
* blocks used for indirect block addressing for this inode .
*/
void gfs2_meta_cache_flush ( struct gfs2_inode * ip )
{
struct buffer_head * * bh_slot ;
unsigned int x ;
spin_lock ( & ip - > i_spin ) ;
for ( x = 0 ; x < GFS2_MAX_META_HEIGHT ; x + + ) {
bh_slot = & ip - > i_cache [ x ] ;
if ( ! * bh_slot )
break ;
brelse ( * bh_slot ) ;
* bh_slot = NULL ;
}
spin_unlock ( & ip - > i_spin ) ;
}
/**
* 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
*
* Try to use the gfs2_inode ' s MRU metadata tree cache .
*
* 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 ;
struct buffer_head * bh = NULL , * * bh_slot = ip - > i_cache + height ;
int in_cache = 0 ;
2006-01-16 19:50:04 +03:00
spin_lock ( & ip - > i_spin ) ;
2006-09-22 01:05:23 +04:00
if ( * bh_slot & & ( * bh_slot ) - > b_blocknr = = num ) {
bh = * bh_slot ;
get_bh ( bh ) ;
in_cache = 1 ;
2006-01-16 19:50:04 +03:00
}
spin_unlock ( & ip - > i_spin ) ;
2006-09-22 01:05:23 +04:00
if ( ! bh )
bh = getbuf ( gl - > gl_sbd , gl - > gl_aspace , num , CREATE ) ;
2006-01-16 19:50:04 +03:00
2006-09-22 01:05:23 +04:00
if ( ! bh )
return - ENOBUFS ;
2006-01-16 19:50:04 +03:00
if ( new ) {
2006-09-22 01:05:23 +04:00
if ( gfs2_assert_warn ( sdp , height ) )
goto err ;
meta_prep_new ( bh ) ;
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 ;
if ( ! buffer_uptodate ( bh ) ) {
ll_rw_block ( READ , 1 , & bh ) ;
if ( gfs2_meta_wait ( sdp , bh ) )
goto err ;
}
if ( gfs2_metatype_check ( sdp , bh , mtype ) )
goto err ;
}
2006-01-16 19:50:04 +03:00
2006-09-22 01:05:23 +04:00
if ( ! in_cache ) {
spin_lock ( & ip - > i_spin ) ;
if ( * bh_slot )
brelse ( * bh_slot ) ;
* bh_slot = bh ;
get_bh ( bh ) ;
spin_unlock ( & ip - > i_spin ) ;
2006-01-16 19:50:04 +03:00
}
* bhp = bh ;
return 0 ;
2006-09-22 01:05:23 +04:00
err :
brelse ( bh ) ;
return - EIO ;
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 inode * aspace = gl - > gl_aspace ;
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 ;
first_bh = getbuf ( sdp , aspace , dblock , CREATE ) ;
if ( buffer_uptodate ( first_bh ) )
goto out ;
2006-09-22 01:05:23 +04:00
if ( ! buffer_locked ( first_bh ) )
ll_rw_block ( READ , 1 , & first_bh ) ;
2006-01-16 19:50:04 +03:00
dblock + + ;
extlen - - ;
while ( extlen ) {
bh = getbuf ( sdp , aspace , dblock , CREATE ) ;
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
}
/**
* gfs2_meta_syncfs - sync all the buffers in a filesystem
* @ sdp : the filesystem
*
*/
void gfs2_meta_syncfs ( struct gfs2_sbd * sdp )
{
2006-04-07 19:17:32 +04:00
gfs2_log_flush ( sdp , NULL ) ;
2006-01-16 19:50:04 +03:00
for ( ; ; ) {
gfs2_ail1_start ( sdp , DIO_ALL ) ;
if ( gfs2_ail1_empty ( sdp , DIO_ALL ) )
break ;
2006-04-18 18:09:15 +04:00
msleep ( 10 ) ;
2006-01-16 19:50:04 +03:00
}
}