2006-01-16 19:50:04 +03:00
/*
* Copyright ( C ) Sistina Software , Inc . 1997 - 2003 All rights reserved .
* Copyright ( C ) 2004 - 2005 Red Hat , Inc . All rights reserved .
*
* 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>
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 "glock.h"
# include "log.h"
# include "lops.h"
# include "meta_io.h"
# include "recovery.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
static void glock_lo_add ( struct gfs2_sbd * sdp , struct gfs2_log_element * le )
{
struct gfs2_glock * gl ;
2006-02-28 01:23:27 +03:00
struct gfs2_trans * tr = current - > journal_info ;
2006-01-16 19:50:04 +03:00
2006-02-28 01:23:27 +03:00
tr - > tr_touched = 1 ;
2006-01-16 19:50:04 +03:00
if ( ! list_empty ( & le - > le_list ) )
return ;
gl = container_of ( le , struct gfs2_glock , gl_le ) ;
if ( gfs2_assert_withdraw ( sdp , gfs2_glock_is_held_excl ( gl ) ) )
return ;
gfs2_glock_hold ( gl ) ;
set_bit ( GLF_DIRTY , & gl - > gl_flags ) ;
gfs2_log_lock ( sdp ) ;
sdp - > sd_log_num_gl + + ;
list_add ( & le - > le_list , & sdp - > sd_log_le_gl ) ;
gfs2_log_unlock ( sdp ) ;
}
static void glock_lo_after_commit ( struct gfs2_sbd * sdp , struct gfs2_ail * ai )
{
struct list_head * head = & sdp - > sd_log_le_gl ;
struct gfs2_glock * gl ;
while ( ! list_empty ( head ) ) {
gl = list_entry ( head - > next , struct gfs2_glock , gl_le . le_list ) ;
list_del_init ( & gl - > gl_le . le_list ) ;
sdp - > sd_log_num_gl - - ;
gfs2_assert_withdraw ( sdp , gfs2_glock_is_held_excl ( gl ) ) ;
gfs2_glock_put ( gl ) ;
}
gfs2_assert_warn ( sdp , ! sdp - > sd_log_num_gl ) ;
}
static void buf_lo_add ( struct gfs2_sbd * sdp , struct gfs2_log_element * le )
{
struct gfs2_bufdata * bd = container_of ( le , struct gfs2_bufdata , bd_le ) ;
struct gfs2_trans * tr ;
if ( ! list_empty ( & bd - > bd_list_tr ) )
return ;
2006-02-28 01:23:27 +03:00
tr = current - > journal_info ;
2006-01-16 19:50:04 +03:00
tr - > tr_touched = 1 ;
tr - > tr_num_buf + + ;
list_add ( & bd - > bd_list_tr , & tr - > tr_list_buf ) ;
if ( ! list_empty ( & le - > le_list ) )
return ;
gfs2_trans_add_gl ( bd - > bd_gl ) ;
gfs2_meta_check ( sdp , bd - > bd_bh ) ;
2006-01-18 16:38:44 +03:00
gfs2_pin ( sdp , bd - > bd_bh ) ;
2006-01-16 19:50:04 +03:00
gfs2_log_lock ( sdp ) ;
sdp - > sd_log_num_buf + + ;
list_add ( & le - > le_list , & sdp - > sd_log_le_buf ) ;
gfs2_log_unlock ( sdp ) ;
tr - > tr_num_buf_new + + ;
}
static void buf_lo_incore_commit ( struct gfs2_sbd * sdp , struct gfs2_trans * tr )
{
struct list_head * head = & tr - > tr_list_buf ;
struct gfs2_bufdata * bd ;
while ( ! list_empty ( head ) ) {
bd = list_entry ( head - > next , struct gfs2_bufdata , bd_list_tr ) ;
list_del_init ( & bd - > bd_list_tr ) ;
tr - > tr_num_buf - - ;
}
gfs2_assert_warn ( sdp , ! tr - > tr_num_buf ) ;
}
static void buf_lo_before_commit ( struct gfs2_sbd * sdp )
{
struct buffer_head * bh ;
struct gfs2_log_descriptor * ld ;
struct gfs2_bufdata * bd1 = NULL , * bd2 ;
unsigned int total = sdp - > sd_log_num_buf ;
unsigned int offset = sizeof ( struct gfs2_log_descriptor ) ;
unsigned int limit ;
unsigned int num ;
unsigned n ;
__be64 * ptr ;
offset + = ( sizeof ( __be64 ) - 1 ) ;
offset & = ~ ( sizeof ( __be64 ) - 1 ) ;
limit = ( sdp - > sd_sb . sb_bsize - offset ) / sizeof ( __be64 ) ;
/* for 4k blocks, limit = 503 */
bd1 = bd2 = list_prepare_entry ( bd1 , & sdp - > sd_log_le_buf , bd_le . le_list ) ;
while ( total ) {
num = total ;
if ( total > limit )
num = limit ;
bh = gfs2_log_get_buf ( sdp ) ;
2006-04-07 19:17:32 +04:00
sdp - > sd_log_num_hdrs + + ;
2006-01-16 19:50:04 +03:00
ld = ( struct gfs2_log_descriptor * ) bh - > b_data ;
ptr = ( __be64 * ) ( bh - > b_data + offset ) ;
ld - > ld_header . mh_magic = cpu_to_be32 ( GFS2_MAGIC ) ;
2006-03-31 00:46:23 +04:00
ld - > ld_header . mh_type = cpu_to_be32 ( GFS2_METATYPE_LD ) ;
ld - > ld_header . mh_format = cpu_to_be32 ( GFS2_FORMAT_LD ) ;
2006-01-16 19:50:04 +03:00
ld - > ld_type = cpu_to_be32 ( GFS2_LOG_DESC_METADATA ) ;
ld - > ld_length = cpu_to_be32 ( num + 1 ) ;
ld - > ld_data1 = cpu_to_be32 ( num ) ;
ld - > ld_data2 = cpu_to_be32 ( 0 ) ;
memset ( ld - > ld_reserved , 0 , sizeof ( ld - > ld_reserved ) ) ;
n = 0 ;
2006-02-27 20:00:42 +03:00
list_for_each_entry_continue ( bd1 , & sdp - > sd_log_le_buf ,
bd_le . le_list ) {
2006-01-16 19:50:04 +03:00
* ptr + + = cpu_to_be64 ( bd1 - > bd_bh - > b_blocknr ) ;
if ( + + n > = num )
break ;
}
set_buffer_dirty ( bh ) ;
ll_rw_block ( WRITE , 1 , & bh ) ;
n = 0 ;
2006-02-27 20:00:42 +03:00
list_for_each_entry_continue ( bd2 , & sdp - > sd_log_le_buf ,
bd_le . le_list ) {
2006-01-16 19:50:04 +03:00
bh = gfs2_log_fake_buf ( sdp , bd2 - > bd_bh ) ;
set_buffer_dirty ( bh ) ;
ll_rw_block ( WRITE , 1 , & bh ) ;
if ( + + n > = num )
break ;
}
total - = num ;
}
}
static void buf_lo_after_commit ( struct gfs2_sbd * sdp , struct gfs2_ail * ai )
{
struct list_head * head = & sdp - > sd_log_le_buf ;
struct gfs2_bufdata * bd ;
while ( ! list_empty ( head ) ) {
bd = list_entry ( head - > next , struct gfs2_bufdata , bd_le . le_list ) ;
list_del_init ( & bd - > bd_le . le_list ) ;
sdp - > sd_log_num_buf - - ;
2006-01-18 16:38:44 +03:00
gfs2_unpin ( sdp , bd - > bd_bh , ai ) ;
2006-01-16 19:50:04 +03:00
}
gfs2_assert_warn ( sdp , ! sdp - > sd_log_num_buf ) ;
}
static void buf_lo_before_scan ( struct gfs2_jdesc * jd ,
struct gfs2_log_header * head , int pass )
{
2006-02-28 01:23:27 +03:00
struct gfs2_inode * ip = jd - > jd_inode - > u . generic_ip ;
struct gfs2_sbd * sdp = ip - > i_sbd ;
2006-01-16 19:50:04 +03:00
if ( pass ! = 0 )
return ;
sdp - > sd_found_blocks = 0 ;
sdp - > sd_replayed_blocks = 0 ;
}
static int buf_lo_scan_elements ( struct gfs2_jdesc * jd , unsigned int start ,
struct gfs2_log_descriptor * ld , __be64 * ptr ,
int pass )
{
2006-02-28 01:23:27 +03:00
struct gfs2_inode * ip = jd - > jd_inode - > u . generic_ip ;
struct gfs2_sbd * sdp = ip - > i_sbd ;
struct gfs2_glock * gl = ip - > i_gl ;
2006-01-16 19:50:04 +03:00
unsigned int blks = be32_to_cpu ( ld - > ld_data1 ) ;
struct buffer_head * bh_log , * bh_ip ;
uint64_t blkno ;
int error = 0 ;
if ( pass ! = 1 | | be32_to_cpu ( ld - > ld_type ) ! = GFS2_LOG_DESC_METADATA )
return 0 ;
gfs2_replay_incr_blk ( sdp , & start ) ;
for ( ; blks ; gfs2_replay_incr_blk ( sdp , & start ) , blks - - ) {
blkno = be64_to_cpu ( * ptr + + ) ;
sdp - > sd_found_blocks + + ;
if ( gfs2_revoke_check ( sdp , blkno , start ) )
continue ;
error = gfs2_replay_read_block ( jd , start , & bh_log ) ;
if ( error )
return error ;
bh_ip = gfs2_meta_new ( gl , blkno ) ;
memcpy ( bh_ip - > b_data , bh_log - > b_data , bh_log - > b_size ) ;
if ( gfs2_meta_check ( sdp , bh_ip ) )
error = - EIO ;
else
mark_buffer_dirty ( bh_ip ) ;
brelse ( bh_log ) ;
brelse ( bh_ip ) ;
if ( error )
break ;
sdp - > sd_replayed_blocks + + ;
}
return error ;
}
static void buf_lo_after_scan ( struct gfs2_jdesc * jd , int error , int pass )
{
2006-02-28 01:23:27 +03:00
struct gfs2_inode * ip = jd - > jd_inode - > u . generic_ip ;
struct gfs2_sbd * sdp = ip - > i_sbd ;
2006-01-16 19:50:04 +03:00
if ( error ) {
2006-02-28 01:23:27 +03:00
gfs2_meta_sync ( ip - > i_gl ,
2006-02-27 20:00:42 +03:00
DIO_START | DIO_WAIT ) ;
2006-01-16 19:50:04 +03:00
return ;
}
if ( pass ! = 1 )
return ;
2006-02-28 01:23:27 +03:00
gfs2_meta_sync ( ip - > i_gl , DIO_START | DIO_WAIT ) ;
2006-01-16 19:50:04 +03:00
fs_info ( sdp , " jid=%u: Replayed %u of %u blocks \n " ,
jd - > jd_jid , sdp - > sd_replayed_blocks , sdp - > sd_found_blocks ) ;
}
static void revoke_lo_add ( struct gfs2_sbd * sdp , struct gfs2_log_element * le )
{
struct gfs2_trans * tr ;
2006-02-28 01:23:27 +03:00
tr = current - > journal_info ;
2006-01-16 19:50:04 +03:00
tr - > tr_touched = 1 ;
tr - > tr_num_revoke + + ;
gfs2_log_lock ( sdp ) ;
sdp - > sd_log_num_revoke + + ;
list_add ( & le - > le_list , & sdp - > sd_log_le_revoke ) ;
gfs2_log_unlock ( sdp ) ;
}
static void revoke_lo_before_commit ( struct gfs2_sbd * sdp )
{
struct gfs2_log_descriptor * ld ;
struct gfs2_meta_header * mh ;
struct buffer_head * bh ;
unsigned int offset ;
struct list_head * head = & sdp - > sd_log_le_revoke ;
struct gfs2_revoke * rv ;
if ( ! sdp - > sd_log_num_revoke )
return ;
bh = gfs2_log_get_buf ( sdp ) ;
ld = ( struct gfs2_log_descriptor * ) bh - > b_data ;
ld - > ld_header . mh_magic = cpu_to_be32 ( GFS2_MAGIC ) ;
2006-03-31 00:46:23 +04:00
ld - > ld_header . mh_type = cpu_to_be32 ( GFS2_METATYPE_LD ) ;
ld - > ld_header . mh_format = cpu_to_be32 ( GFS2_FORMAT_LD ) ;
2006-01-16 19:50:04 +03:00
ld - > ld_type = cpu_to_be32 ( GFS2_LOG_DESC_REVOKE ) ;
2006-02-27 20:00:42 +03:00
ld - > ld_length = cpu_to_be32 ( gfs2_struct2blk ( sdp , sdp - > sd_log_num_revoke ,
sizeof ( uint64_t ) ) ) ;
2006-01-16 19:50:04 +03:00
ld - > ld_data1 = cpu_to_be32 ( sdp - > sd_log_num_revoke ) ;
ld - > ld_data2 = cpu_to_be32 ( 0 ) ;
memset ( ld - > ld_reserved , 0 , sizeof ( ld - > ld_reserved ) ) ;
offset = sizeof ( struct gfs2_log_descriptor ) ;
while ( ! list_empty ( head ) ) {
rv = list_entry ( head - > next , struct gfs2_revoke , rv_le . le_list ) ;
2006-02-22 14:15:03 +03:00
list_del_init ( & rv - > rv_le . le_list ) ;
2006-01-16 19:50:04 +03:00
sdp - > sd_log_num_revoke - - ;
if ( offset + sizeof ( uint64_t ) > sdp - > sd_sb . sb_bsize ) {
set_buffer_dirty ( bh ) ;
ll_rw_block ( WRITE , 1 , & bh ) ;
bh = gfs2_log_get_buf ( sdp ) ;
mh = ( struct gfs2_meta_header * ) bh - > b_data ;
mh - > mh_magic = cpu_to_be32 ( GFS2_MAGIC ) ;
2006-03-31 00:46:23 +04:00
mh - > mh_type = cpu_to_be32 ( GFS2_METATYPE_LB ) ;
mh - > mh_format = cpu_to_be32 ( GFS2_FORMAT_LB ) ;
2006-01-16 19:50:04 +03:00
offset = sizeof ( struct gfs2_meta_header ) ;
}
* ( __be64 * ) ( bh - > b_data + offset ) = cpu_to_be64 ( rv - > rv_blkno ) ;
kfree ( rv ) ;
offset + = sizeof ( uint64_t ) ;
}
gfs2_assert_withdraw ( sdp , ! sdp - > sd_log_num_revoke ) ;
set_buffer_dirty ( bh ) ;
ll_rw_block ( WRITE , 1 , & bh ) ;
}
static void revoke_lo_before_scan ( struct gfs2_jdesc * jd ,
struct gfs2_log_header * head , int pass )
{
2006-02-28 01:23:27 +03:00
struct gfs2_inode * ip = jd - > jd_inode - > u . generic_ip ;
struct gfs2_sbd * sdp = ip - > i_sbd ;
2006-01-16 19:50:04 +03:00
if ( pass ! = 0 )
return ;
sdp - > sd_found_revokes = 0 ;
sdp - > sd_replay_tail = head - > lh_tail ;
}
static int revoke_lo_scan_elements ( struct gfs2_jdesc * jd , unsigned int start ,
struct gfs2_log_descriptor * ld , __be64 * ptr ,
int pass )
{
2006-02-28 01:23:27 +03:00
struct gfs2_inode * ip = jd - > jd_inode - > u . generic_ip ;
struct gfs2_sbd * sdp = ip - > i_sbd ;
2006-01-16 19:50:04 +03:00
unsigned int blks = be32_to_cpu ( ld - > ld_length ) ;
unsigned int revokes = be32_to_cpu ( ld - > ld_data1 ) ;
struct buffer_head * bh ;
unsigned int offset ;
uint64_t blkno ;
int first = 1 ;
int error ;
if ( pass ! = 0 | | be32_to_cpu ( ld - > ld_type ) ! = GFS2_LOG_DESC_REVOKE )
return 0 ;
offset = sizeof ( struct gfs2_log_descriptor ) ;
for ( ; blks ; gfs2_replay_incr_blk ( sdp , & start ) , blks - - ) {
error = gfs2_replay_read_block ( jd , start , & bh ) ;
if ( error )
return error ;
if ( ! first )
gfs2_metatype_check ( sdp , bh , GFS2_METATYPE_LB ) ;
while ( offset + sizeof ( uint64_t ) < = sdp - > sd_sb . sb_bsize ) {
blkno = be64_to_cpu ( * ( __be64 * ) ( bh - > b_data + offset ) ) ;
error = gfs2_revoke_add ( sdp , blkno , start ) ;
if ( error < 0 )
return error ;
else if ( error )
sdp - > sd_found_revokes + + ;
if ( ! - - revokes )
break ;
offset + = sizeof ( uint64_t ) ;
}
brelse ( bh ) ;
offset = sizeof ( struct gfs2_meta_header ) ;
first = 0 ;
}
return 0 ;
}
static void revoke_lo_after_scan ( struct gfs2_jdesc * jd , int error , int pass )
{
2006-02-28 01:23:27 +03:00
struct gfs2_inode * ip = jd - > jd_inode - > u . generic_ip ;
struct gfs2_sbd * sdp = ip - > i_sbd ;
2006-01-16 19:50:04 +03:00
if ( error ) {
gfs2_revoke_clean ( sdp ) ;
return ;
}
if ( pass ! = 1 )
return ;
fs_info ( sdp , " jid=%u: Found %u revoke tags \n " ,
jd - > jd_jid , sdp - > sd_found_revokes ) ;
gfs2_revoke_clean ( sdp ) ;
}
static void rg_lo_add ( struct gfs2_sbd * sdp , struct gfs2_log_element * le )
{
struct gfs2_rgrpd * rgd ;
2006-02-28 01:23:27 +03:00
struct gfs2_trans * tr = current - > journal_info ;
2006-01-16 19:50:04 +03:00
2006-02-28 01:23:27 +03:00
tr - > tr_touched = 1 ;
2006-01-16 19:50:04 +03:00
if ( ! list_empty ( & le - > le_list ) )
return ;
rgd = container_of ( le , struct gfs2_rgrpd , rd_le ) ;
gfs2_rgrp_bh_hold ( rgd ) ;
gfs2_log_lock ( sdp ) ;
sdp - > sd_log_num_rg + + ;
list_add ( & le - > le_list , & sdp - > sd_log_le_rg ) ;
gfs2_log_unlock ( sdp ) ;
}
static void rg_lo_after_commit ( struct gfs2_sbd * sdp , struct gfs2_ail * ai )
{
struct list_head * head = & sdp - > sd_log_le_rg ;
struct gfs2_rgrpd * rgd ;
while ( ! list_empty ( head ) ) {
rgd = list_entry ( head - > next , struct gfs2_rgrpd , rd_le . le_list ) ;
list_del_init ( & rgd - > rd_le . le_list ) ;
sdp - > sd_log_num_rg - - ;
gfs2_rgrp_repolish_clones ( rgd ) ;
gfs2_rgrp_bh_put ( rgd ) ;
}
gfs2_assert_warn ( sdp , ! sdp - > sd_log_num_rg ) ;
}
2006-02-08 14:50:51 +03:00
/**
* databuf_lo_add - Add a databuf to the transaction .
*
* This is used in two distinct cases :
* i ) In ordered write mode
* We put the data buffer on a list so that we can ensure that its
* synced to disk at the right time
* ii ) In journaled data mode
* We need to journal the data block in the same way as metadata in
* the functions above . The difference is that here we have a tag
* which is two __be64 ' s being the block number ( as per meta data )
* and a flag which says whether the data block needs escaping or
* not . This means we need a new log entry for each 251 or so data
* blocks , which isn ' t an enormous overhead but twice as much as
* for normal metadata blocks .
*/
2006-01-16 19:50:04 +03:00
static void databuf_lo_add ( struct gfs2_sbd * sdp , struct gfs2_log_element * le )
{
2006-02-08 14:50:51 +03:00
struct gfs2_bufdata * bd = container_of ( le , struct gfs2_bufdata , bd_le ) ;
2006-02-28 01:23:27 +03:00
struct gfs2_trans * tr = current - > journal_info ;
2006-02-08 14:50:51 +03:00
struct address_space * mapping = bd - > bd_bh - > b_page - > mapping ;
2006-02-28 01:23:27 +03:00
struct gfs2_inode * ip = mapping - > host - > u . generic_ip ;
2006-01-16 19:50:04 +03:00
2006-02-08 14:50:51 +03:00
tr - > tr_touched = 1 ;
if ( ! list_empty ( & bd - > bd_list_tr ) & &
( ip - > i_di . di_flags & GFS2_DIF_JDATA ) ) {
tr - > tr_num_buf + + ;
gfs2_trans_add_gl ( bd - > bd_gl ) ;
list_add ( & bd - > bd_list_tr , & tr - > tr_list_buf ) ;
gfs2_pin ( sdp , bd - > bd_bh ) ;
2006-03-02 01:41:58 +03:00
tr - > tr_num_buf_new + + ;
2006-02-08 14:50:51 +03:00
}
2006-01-16 19:50:04 +03:00
gfs2_log_lock ( sdp ) ;
2006-02-22 14:15:03 +03:00
if ( ! list_empty ( & le - > le_list ) ) {
if ( ip - > i_di . di_flags & GFS2_DIF_JDATA )
sdp - > sd_log_num_jdata + + ;
sdp - > sd_log_num_databuf + + ;
list_add ( & le - > le_list , & sdp - > sd_log_le_databuf ) ;
}
2006-01-16 19:50:04 +03:00
gfs2_log_unlock ( sdp ) ;
}
2006-02-08 14:50:51 +03:00
static int gfs2_check_magic ( struct buffer_head * bh )
{
struct page * page = bh - > b_page ;
void * kaddr ;
__be32 * ptr ;
int rv = 0 ;
kaddr = kmap_atomic ( page , KM_USER0 ) ;
ptr = kaddr + bh_offset ( bh ) ;
if ( * ptr = = cpu_to_be32 ( GFS2_MAGIC ) )
rv = 1 ;
kunmap_atomic ( page , KM_USER0 ) ;
return rv ;
}
/**
* databuf_lo_before_commit - Scan the data buffers , writing as we go
*
* Here we scan through the lists of buffers and make the assumption
* that any buffer thats been pinned is being journaled , and that
* any unpinned buffer is an ordered write data buffer and therefore
* will be written back rather than journaled .
*/
2006-01-16 19:50:04 +03:00
static void databuf_lo_before_commit ( struct gfs2_sbd * sdp )
{
LIST_HEAD ( started ) ;
2006-02-08 14:50:51 +03:00
struct gfs2_bufdata * bd1 = NULL , * bd2 , * bdt ;
struct buffer_head * bh = NULL ;
unsigned int offset = sizeof ( struct gfs2_log_descriptor ) ;
struct gfs2_log_descriptor * ld ;
unsigned int limit ;
unsigned int total_dbuf = sdp - > sd_log_num_databuf ;
unsigned int total_jdata = sdp - > sd_log_num_jdata ;
unsigned int num , n ;
2006-02-21 15:51:39 +03:00
__be64 * ptr = NULL ;
2006-01-16 19:50:04 +03:00
2006-02-08 14:50:51 +03:00
offset + = ( 2 * sizeof ( __be64 ) - 1 ) ;
offset & = ~ ( 2 * sizeof ( __be64 ) - 1 ) ;
limit = ( sdp - > sd_sb . sb_bsize - offset ) / sizeof ( __be64 ) ;
2006-01-16 19:50:04 +03:00
2006-02-08 14:50:51 +03:00
/*
* Start writing ordered buffers , write journaled buffers
* into the log along with a header
*/
2006-02-21 15:51:39 +03:00
gfs2_log_lock ( sdp ) ;
2006-02-27 20:00:42 +03:00
bd2 = bd1 = list_prepare_entry ( bd1 , & sdp - > sd_log_le_databuf ,
bd_le . le_list ) ;
2006-02-08 14:50:51 +03:00
while ( total_dbuf ) {
num = total_jdata ;
if ( num > limit )
num = limit ;
n = 0 ;
2006-02-27 20:00:42 +03:00
list_for_each_entry_safe_continue ( bd1 , bdt ,
& sdp - > sd_log_le_databuf ,
bd_le . le_list ) {
2006-02-08 14:50:51 +03:00
/* An ordered write buffer */
if ( bd1 - > bd_bh & & ! buffer_pinned ( bd1 - > bd_bh ) ) {
list_move ( & bd1 - > bd_le . le_list , & started ) ;
if ( bd1 = = bd2 ) {
bd2 = NULL ;
2006-02-27 20:00:42 +03:00
bd2 = list_prepare_entry ( bd2 ,
& sdp - > sd_log_le_databuf ,
bd_le . le_list ) ;
2006-02-08 14:50:51 +03:00
}
total_dbuf - - ;
if ( bd1 - > bd_bh ) {
get_bh ( bd1 - > bd_bh ) ;
if ( buffer_dirty ( bd1 - > bd_bh ) ) {
2006-02-21 15:51:39 +03:00
gfs2_log_unlock ( sdp ) ;
2006-02-08 14:50:51 +03:00
wait_on_buffer ( bd1 - > bd_bh ) ;
2006-02-27 20:00:42 +03:00
ll_rw_block ( WRITE , 1 ,
& bd1 - > bd_bh ) ;
2006-02-21 15:51:39 +03:00
gfs2_log_lock ( sdp ) ;
2006-02-08 14:50:51 +03:00
}
brelse ( bd1 - > bd_bh ) ;
continue ;
}
continue ;
} else if ( bd1 - > bd_bh ) { /* A journaled buffer */
int magic ;
gfs2_log_unlock ( sdp ) ;
if ( ! bh ) {
bh = gfs2_log_get_buf ( sdp ) ;
2006-04-07 19:17:32 +04:00
sdp - > sd_log_num_hdrs + + ;
2006-02-27 20:00:42 +03:00
ld = ( struct gfs2_log_descriptor * )
bh - > b_data ;
2006-02-08 14:50:51 +03:00
ptr = ( __be64 * ) ( bh - > b_data + offset ) ;
2006-02-27 20:00:42 +03:00
ld - > ld_header . mh_magic =
cpu_to_be32 ( GFS2_MAGIC ) ;
ld - > ld_header . mh_type =
2006-03-31 00:46:23 +04:00
cpu_to_be32 ( GFS2_METATYPE_LD ) ;
2006-02-27 20:00:42 +03:00
ld - > ld_header . mh_format =
2006-03-31 00:46:23 +04:00
cpu_to_be32 ( GFS2_FORMAT_LD ) ;
2006-02-27 20:00:42 +03:00
ld - > ld_type =
cpu_to_be32 ( GFS2_LOG_DESC_JDATA ) ;
2006-02-08 14:50:51 +03:00
ld - > ld_length = cpu_to_be32 ( num + 1 ) ;
ld - > ld_data1 = cpu_to_be32 ( num ) ;
ld - > ld_data2 = cpu_to_be32 ( 0 ) ;
memset ( ld - > ld_reserved , 0 , sizeof ( ld - > ld_reserved ) ) ;
}
magic = gfs2_check_magic ( bd1 - > bd_bh ) ;
* ptr + + = cpu_to_be64 ( bd1 - > bd_bh - > b_blocknr ) ;
* ptr + + = cpu_to_be64 ( ( __u64 ) magic ) ;
clear_buffer_escaped ( bd1 - > bd_bh ) ;
if ( unlikely ( magic ! = 0 ) )
set_buffer_escaped ( bd1 - > bd_bh ) ;
2006-02-21 15:51:39 +03:00
gfs2_log_lock ( sdp ) ;
2006-02-08 14:50:51 +03:00
if ( n + + > num )
break ;
}
}
2006-02-21 15:51:39 +03:00
gfs2_log_unlock ( sdp ) ;
2006-01-16 19:50:04 +03:00
if ( bh ) {
2006-02-08 14:50:51 +03:00
set_buffer_dirty ( bh ) ;
ll_rw_block ( WRITE , 1 , & bh ) ;
bh = NULL ;
}
n = 0 ;
2006-02-21 15:51:39 +03:00
gfs2_log_lock ( sdp ) ;
2006-02-27 20:00:42 +03:00
list_for_each_entry_continue ( bd2 , & sdp - > sd_log_le_databuf ,
bd_le . le_list ) {
2006-02-08 14:50:51 +03:00
if ( ! bd2 - > bd_bh )
continue ;
/* copy buffer if it needs escaping */
2006-02-21 15:51:39 +03:00
gfs2_log_unlock ( sdp ) ;
2006-02-08 14:50:51 +03:00
if ( unlikely ( buffer_escaped ( bd2 - > bd_bh ) ) ) {
void * kaddr ;
struct page * page = bd2 - > bd_bh - > b_page ;
bh = gfs2_log_get_buf ( sdp ) ;
kaddr = kmap_atomic ( page , KM_USER0 ) ;
2006-02-27 20:00:42 +03:00
memcpy ( bh - > b_data ,
kaddr + bh_offset ( bd2 - > bd_bh ) ,
sdp - > sd_sb . sb_bsize ) ;
2006-02-08 14:50:51 +03:00
kunmap_atomic ( page , KM_USER0 ) ;
* ( __be32 * ) bh - > b_data = 0 ;
} else {
bh = gfs2_log_fake_buf ( sdp , bd2 - > bd_bh ) ;
2006-01-16 19:50:04 +03:00
}
2006-02-08 14:50:51 +03:00
set_buffer_dirty ( bh ) ;
ll_rw_block ( WRITE , 1 , & bh ) ;
2006-02-21 15:51:39 +03:00
gfs2_log_lock ( sdp ) ;
2006-02-08 14:50:51 +03:00
if ( + + n > = num )
break ;
}
bh = NULL ;
total_dbuf - = num ;
total_jdata - = num ;
2006-01-16 19:50:04 +03:00
}
2006-02-21 15:51:39 +03:00
gfs2_log_unlock ( sdp ) ;
2006-02-08 14:50:51 +03:00
/* Wait on all ordered buffers */
2006-01-16 19:50:04 +03:00
while ( ! list_empty ( & started ) ) {
2006-02-22 14:15:03 +03:00
gfs2_log_lock ( sdp ) ;
2006-02-27 20:00:42 +03:00
bd1 = list_entry ( started . next , struct gfs2_bufdata ,
bd_le . le_list ) ;
2006-02-08 14:50:51 +03:00
list_del ( & bd1 - > bd_le . le_list ) ;
2006-01-16 19:50:04 +03:00
sdp - > sd_log_num_databuf - - ;
2006-02-08 14:50:51 +03:00
bh = bd1 - > bd_bh ;
2006-01-16 19:50:04 +03:00
if ( bh ) {
2006-02-28 01:23:27 +03:00
bh - > b_private = NULL ;
2006-01-16 19:50:04 +03:00
gfs2_log_unlock ( sdp ) ;
wait_on_buffer ( bh ) ;
brelse ( bh ) ;
} else
gfs2_log_unlock ( sdp ) ;
2006-02-08 14:50:51 +03:00
kfree ( bd1 ) ;
2006-01-16 19:50:04 +03:00
}
2006-02-08 14:50:51 +03:00
/* We've removed all the ordered write bufs here, so only jdata left */
gfs2_assert_warn ( sdp , sdp - > sd_log_num_databuf = = sdp - > sd_log_num_jdata ) ;
}
static int databuf_lo_scan_elements ( struct gfs2_jdesc * jd , unsigned int start ,
struct gfs2_log_descriptor * ld ,
__be64 * ptr , int pass )
{
2006-02-28 01:23:27 +03:00
struct gfs2_inode * ip = jd - > jd_inode - > u . generic_ip ;
struct gfs2_sbd * sdp = ip - > i_sbd ;
struct gfs2_glock * gl = ip - > i_gl ;
2006-02-08 14:50:51 +03:00
unsigned int blks = be32_to_cpu ( ld - > ld_data1 ) ;
struct buffer_head * bh_log , * bh_ip ;
uint64_t blkno ;
uint64_t esc ;
int error = 0 ;
if ( pass ! = 1 | | be32_to_cpu ( ld - > ld_type ) ! = GFS2_LOG_DESC_JDATA )
return 0 ;
gfs2_replay_incr_blk ( sdp , & start ) ;
for ( ; blks ; gfs2_replay_incr_blk ( sdp , & start ) , blks - - ) {
blkno = be64_to_cpu ( * ptr + + ) ;
esc = be64_to_cpu ( * ptr + + ) ;
sdp - > sd_found_blocks + + ;
if ( gfs2_revoke_check ( sdp , blkno , start ) )
continue ;
error = gfs2_replay_read_block ( jd , start , & bh_log ) ;
if ( error )
return error ;
bh_ip = gfs2_meta_new ( gl , blkno ) ;
memcpy ( bh_ip - > b_data , bh_log - > b_data , bh_log - > b_size ) ;
/* Unescape */
if ( esc ) {
__be32 * eptr = ( __be32 * ) bh_ip - > b_data ;
* eptr = cpu_to_be32 ( GFS2_MAGIC ) ;
}
mark_buffer_dirty ( bh_ip ) ;
brelse ( bh_log ) ;
brelse ( bh_ip ) ;
if ( error )
break ;
sdp - > sd_replayed_blocks + + ;
}
return error ;
}
/* FIXME: sort out accounting for log blocks etc. */
static void databuf_lo_after_scan ( struct gfs2_jdesc * jd , int error , int pass )
{
2006-02-28 01:23:27 +03:00
struct gfs2_inode * ip = jd - > jd_inode - > u . generic_ip ;
struct gfs2_sbd * sdp = ip - > i_sbd ;
2006-02-08 14:50:51 +03:00
if ( error ) {
2006-02-28 01:23:27 +03:00
gfs2_meta_sync ( ip - > i_gl ,
2006-02-27 20:00:42 +03:00
DIO_START | DIO_WAIT ) ;
2006-02-08 14:50:51 +03:00
return ;
}
if ( pass ! = 1 )
return ;
/* data sync? */
2006-02-28 01:23:27 +03:00
gfs2_meta_sync ( ip - > i_gl , DIO_START | DIO_WAIT ) ;
2006-02-08 14:50:51 +03:00
fs_info ( sdp , " jid=%u: Replayed %u of %u data blocks \n " ,
jd - > jd_jid , sdp - > sd_replayed_blocks , sdp - > sd_found_blocks ) ;
}
static void databuf_lo_after_commit ( struct gfs2_sbd * sdp , struct gfs2_ail * ai )
{
struct list_head * head = & sdp - > sd_log_le_databuf ;
struct gfs2_bufdata * bd ;
while ( ! list_empty ( head ) ) {
bd = list_entry ( head - > next , struct gfs2_bufdata , bd_le . le_list ) ;
2006-02-21 15:51:39 +03:00
list_del ( & bd - > bd_le . le_list ) ;
2006-02-08 14:50:51 +03:00
sdp - > sd_log_num_databuf - - ;
sdp - > sd_log_num_jdata - - ;
gfs2_unpin ( sdp , bd - > bd_bh , ai ) ;
}
2006-01-16 19:50:04 +03:00
gfs2_assert_warn ( sdp , ! sdp - > sd_log_num_databuf ) ;
2006-02-08 14:50:51 +03:00
gfs2_assert_warn ( sdp , ! sdp - > sd_log_num_jdata ) ;
2006-01-16 19:50:04 +03:00
}
2006-02-08 14:50:51 +03:00
2006-04-07 19:17:32 +04:00
const struct gfs2_log_operations gfs2_glock_lops = {
2006-01-16 19:50:04 +03:00
. lo_add = glock_lo_add ,
. lo_after_commit = glock_lo_after_commit ,
. lo_name = " glock "
} ;
2006-04-07 19:17:32 +04:00
const struct gfs2_log_operations gfs2_buf_lops = {
2006-01-16 19:50:04 +03:00
. lo_add = buf_lo_add ,
. lo_incore_commit = buf_lo_incore_commit ,
. lo_before_commit = buf_lo_before_commit ,
. lo_after_commit = buf_lo_after_commit ,
. lo_before_scan = buf_lo_before_scan ,
. lo_scan_elements = buf_lo_scan_elements ,
. lo_after_scan = buf_lo_after_scan ,
. lo_name = " buf "
} ;
2006-04-07 19:17:32 +04:00
const struct gfs2_log_operations gfs2_revoke_lops = {
2006-01-16 19:50:04 +03:00
. lo_add = revoke_lo_add ,
. lo_before_commit = revoke_lo_before_commit ,
. lo_before_scan = revoke_lo_before_scan ,
. lo_scan_elements = revoke_lo_scan_elements ,
. lo_after_scan = revoke_lo_after_scan ,
. lo_name = " revoke "
} ;
2006-04-07 19:17:32 +04:00
const struct gfs2_log_operations gfs2_rg_lops = {
2006-01-16 19:50:04 +03:00
. lo_add = rg_lo_add ,
. lo_after_commit = rg_lo_after_commit ,
. lo_name = " rg "
} ;
2006-04-07 19:17:32 +04:00
const struct gfs2_log_operations gfs2_databuf_lops = {
2006-01-16 19:50:04 +03:00
. lo_add = databuf_lo_add ,
2006-02-08 14:50:51 +03:00
. lo_incore_commit = buf_lo_incore_commit ,
2006-01-16 19:50:04 +03:00
. lo_before_commit = databuf_lo_before_commit ,
2006-02-08 14:50:51 +03:00
. lo_after_commit = databuf_lo_after_commit ,
. lo_scan_elements = databuf_lo_scan_elements ,
. lo_after_scan = databuf_lo_after_scan ,
2006-01-16 19:50:04 +03:00
. lo_name = " databuf "
} ;
2006-04-07 19:17:32 +04:00
const struct gfs2_log_operations * gfs2_log_ops [ ] = {
2006-01-16 19:50:04 +03:00
& gfs2_glock_lops ,
& gfs2_buf_lops ,
& gfs2_revoke_lops ,
& gfs2_rg_lops ,
& gfs2_databuf_lops ,
NULL
} ;