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-03-28 23:14:04 +04:00
# include <linux/crc32.h>
2006-01-16 19:50:04 +03:00
# include <asm/semaphore.h>
# 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 "log.h"
# include "lops.h"
# include "meta_io.h"
2006-02-28 01:23:27 +03:00
# include "util.h"
2006-03-28 23:14:04 +04:00
# include "dir.h"
2006-01-16 19:50:04 +03:00
# define PULL 1
/**
* gfs2_struct2blk - compute stuff
* @ sdp : the filesystem
* @ nstruct : the number of structures
* @ ssize : the size of the structures
*
* Compute the number of log descriptor blocks needed to hold a certain number
* of structures of a certain size .
*
* Returns : the number of blocks needed ( minimum is always 1 )
*/
unsigned int gfs2_struct2blk ( struct gfs2_sbd * sdp , unsigned int nstruct ,
unsigned int ssize )
{
unsigned int blks ;
unsigned int first , second ;
blks = 1 ;
2006-02-27 20:00:42 +03:00
first = ( sdp - > sd_sb . sb_bsize - sizeof ( struct gfs2_log_descriptor ) ) /
ssize ;
2006-01-16 19:50:04 +03:00
if ( nstruct > first ) {
2006-02-27 20:00:42 +03:00
second = ( sdp - > sd_sb . sb_bsize -
sizeof ( struct gfs2_meta_header ) ) / ssize ;
2006-02-28 01:23:27 +03:00
blks + = DIV_ROUND_UP ( nstruct - first , second ) ;
2006-01-16 19:50:04 +03:00
}
return blks ;
}
void gfs2_ail1_start ( struct gfs2_sbd * sdp , int flags )
{
struct list_head * head = & sdp - > sd_ail1_list ;
uint64_t sync_gen ;
struct list_head * first , * tmp ;
struct gfs2_ail * first_ai , * ai ;
gfs2_log_lock ( sdp ) ;
if ( list_empty ( head ) ) {
gfs2_log_unlock ( sdp ) ;
return ;
}
sync_gen = sdp - > sd_ail_sync_gen + + ;
first = head - > prev ;
first_ai = list_entry ( first , struct gfs2_ail , ai_list ) ;
first_ai - > ai_sync_gen = sync_gen ;
gfs2_ail1_start_one ( sdp , first_ai ) ;
if ( flags & DIO_ALL )
first = NULL ;
for ( ; ; ) {
2006-03-29 18:12:12 +04:00
if ( first & & ( head - > prev ! = first | |
gfs2_ail1_empty_one ( sdp , first_ai , 0 ) ) )
2006-01-16 19:50:04 +03:00
break ;
for ( tmp = head - > prev ; tmp ! = head ; tmp = tmp - > prev ) {
ai = list_entry ( tmp , struct gfs2_ail , ai_list ) ;
if ( ai - > ai_sync_gen > = sync_gen )
continue ;
ai - > ai_sync_gen = sync_gen ;
gfs2_ail1_start_one ( sdp , ai ) ;
break ;
}
if ( tmp = = head )
break ;
}
gfs2_log_unlock ( sdp ) ;
}
int gfs2_ail1_empty ( struct gfs2_sbd * sdp , int flags )
{
struct gfs2_ail * ai , * s ;
int ret ;
gfs2_log_lock ( sdp ) ;
list_for_each_entry_safe_reverse ( ai , s , & sdp - > sd_ail1_list , ai_list ) {
if ( gfs2_ail1_empty_one ( sdp , ai , flags ) )
list_move ( & ai - > ai_list , & sdp - > sd_ail2_list ) ;
else if ( ! ( flags & DIO_ALL ) )
break ;
}
ret = list_empty ( & sdp - > sd_ail1_list ) ;
gfs2_log_unlock ( sdp ) ;
return ret ;
}
static void ail2_empty ( struct gfs2_sbd * sdp , unsigned int new_tail )
{
struct gfs2_ail * ai , * safe ;
unsigned int old_tail = sdp - > sd_log_tail ;
int wrap = ( new_tail < old_tail ) ;
int a , b , rm ;
gfs2_log_lock ( sdp ) ;
list_for_each_entry_safe ( ai , safe , & sdp - > sd_ail2_list , ai_list ) {
a = ( old_tail < = ai - > ai_first ) ;
b = ( ai - > ai_first < new_tail ) ;
rm = ( wrap ) ? ( a | | b ) : ( a & & b ) ;
if ( ! rm )
continue ;
gfs2_ail2_empty_one ( sdp , ai ) ;
list_del ( & ai - > ai_list ) ;
gfs2_assert_warn ( sdp , list_empty ( & ai - > ai_ail1_list ) ) ;
gfs2_assert_warn ( sdp , list_empty ( & ai - > ai_ail2_list ) ) ;
kfree ( ai ) ;
}
gfs2_log_unlock ( sdp ) ;
}
/**
* gfs2_log_reserve - Make a log reservation
* @ sdp : The GFS2 superblock
* @ blks : The number of blocks to reserve
*
* Returns : errno
*/
int gfs2_log_reserve ( struct gfs2_sbd * sdp , unsigned int blks )
{
unsigned int try = 0 ;
if ( gfs2_assert_warn ( sdp , blks ) | |
gfs2_assert_warn ( sdp , blks < = sdp - > sd_jdesc - > jd_blocks ) )
return - EINVAL ;
2006-03-28 23:14:04 +04:00
mutex_lock ( & sdp - > sd_log_reserve_mutex ) ;
2006-03-29 18:12:12 +04:00
gfs2_log_lock ( sdp ) ;
while ( sdp - > sd_log_blks_free < = blks ) {
2006-01-16 19:50:04 +03:00
gfs2_log_unlock ( sdp ) ;
gfs2_ail1_empty ( sdp , 0 ) ;
gfs2_log_flush ( sdp ) ;
if ( try + + )
gfs2_ail1_start ( sdp , 0 ) ;
2006-03-29 18:12:12 +04:00
gfs2_log_lock ( sdp ) ;
2006-01-16 19:50:04 +03:00
}
2006-03-29 18:12:12 +04:00
sdp - > sd_log_blks_free - = blks ;
gfs2_log_unlock ( sdp ) ;
mutex_unlock ( & sdp - > sd_log_reserve_mutex ) ;
down_read ( & sdp - > sd_log_flush_lock ) ;
2006-01-16 19:50:04 +03:00
return 0 ;
}
/**
* gfs2_log_release - Release a given number of log blocks
* @ sdp : The GFS2 superblock
* @ blks : The number of blocks
*
*/
void gfs2_log_release ( struct gfs2_sbd * sdp , unsigned int blks )
{
2006-03-29 18:12:12 +04:00
up_read ( & sdp - > sd_log_flush_lock ) ;
2006-01-16 19:50:04 +03:00
gfs2_log_lock ( sdp ) ;
sdp - > sd_log_blks_free + = blks ;
gfs2_assert_withdraw ( sdp ,
sdp - > sd_log_blks_free < = sdp - > sd_jdesc - > jd_blocks ) ;
gfs2_log_unlock ( sdp ) ;
}
static uint64_t log_bmap ( struct gfs2_sbd * sdp , unsigned int lbn )
{
int new = 0 ;
uint64_t dbn ;
int error ;
2006-02-28 01:23:27 +03:00
error = gfs2_block_map ( sdp - > sd_jdesc - > jd_inode - > u . generic_ip ,
2006-02-27 20:00:42 +03:00
lbn , & new , & dbn , NULL ) ;
2006-01-16 19:50:04 +03:00
gfs2_assert_withdraw ( sdp , ! error & & dbn ) ;
return dbn ;
}
/**
* log_distance - Compute distance between two journal blocks
* @ sdp : The GFS2 superblock
* @ newer : The most recent journal block of the pair
* @ older : The older journal block of the pair
*
* Compute the distance ( in the journal direction ) between two
* blocks in the journal
*
* Returns : the distance in blocks
*/
static inline unsigned int log_distance ( struct gfs2_sbd * sdp ,
unsigned int newer ,
unsigned int older )
{
int dist ;
dist = newer - older ;
if ( dist < 0 )
dist + = sdp - > sd_jdesc - > jd_blocks ;
return dist ;
}
static unsigned int current_tail ( struct gfs2_sbd * sdp )
{
struct gfs2_ail * ai ;
unsigned int tail ;
gfs2_log_lock ( sdp ) ;
if ( list_empty ( & sdp - > sd_ail1_list ) )
tail = sdp - > sd_log_head ;
else {
ai = list_entry ( sdp - > sd_ail1_list . prev ,
struct gfs2_ail , ai_list ) ;
tail = ai - > ai_first ;
}
gfs2_log_unlock ( sdp ) ;
return tail ;
}
static inline void log_incr_head ( struct gfs2_sbd * sdp )
{
if ( sdp - > sd_log_flush_head = = sdp - > sd_log_tail )
gfs2_assert_withdraw ( sdp ,
sdp - > sd_log_flush_head = = sdp - > sd_log_head ) ;
if ( + + sdp - > sd_log_flush_head = = sdp - > sd_jdesc - > jd_blocks ) {
sdp - > sd_log_flush_head = 0 ;
sdp - > sd_log_flush_wrapped = 1 ;
}
}
/**
* gfs2_log_get_buf - Get and initialize a buffer to use for log control data
* @ sdp : The GFS2 superblock
*
* Returns : the buffer_head
*/
struct buffer_head * gfs2_log_get_buf ( struct gfs2_sbd * sdp )
{
uint64_t blkno = log_bmap ( sdp , sdp - > sd_log_flush_head ) ;
struct gfs2_log_buf * lb ;
struct buffer_head * bh ;
2006-01-18 16:20:16 +03:00
lb = kzalloc ( sizeof ( struct gfs2_log_buf ) , GFP_NOFS | __GFP_NOFAIL ) ;
2006-01-16 19:50:04 +03:00
list_add ( & lb - > lb_list , & sdp - > sd_log_flush_list ) ;
bh = lb - > lb_bh = sb_getblk ( sdp - > sd_vfs , blkno ) ;
lock_buffer ( bh ) ;
memset ( bh - > b_data , 0 , bh - > b_size ) ;
set_buffer_uptodate ( bh ) ;
clear_buffer_dirty ( bh ) ;
unlock_buffer ( bh ) ;
log_incr_head ( sdp ) ;
return bh ;
}
/**
* gfs2_log_fake_buf - Build a fake buffer head to write metadata buffer to log
* @ sdp : the filesystem
* @ data : the data the buffer_head should point to
*
* Returns : the log buffer descriptor
*/
struct buffer_head * gfs2_log_fake_buf ( struct gfs2_sbd * sdp ,
struct buffer_head * real )
{
uint64_t blkno = log_bmap ( sdp , sdp - > sd_log_flush_head ) ;
struct gfs2_log_buf * lb ;
struct buffer_head * bh ;
2006-01-18 16:20:16 +03:00
lb = kzalloc ( sizeof ( struct gfs2_log_buf ) , GFP_NOFS | __GFP_NOFAIL ) ;
2006-01-16 19:50:04 +03:00
list_add ( & lb - > lb_list , & sdp - > sd_log_flush_list ) ;
lb - > lb_real = real ;
bh = lb - > lb_bh = alloc_buffer_head ( GFP_NOFS | __GFP_NOFAIL ) ;
atomic_set ( & bh - > b_count , 1 ) ;
bh - > b_state = ( 1 < < BH_Mapped ) | ( 1 < < BH_Uptodate ) ;
2006-02-08 14:50:51 +03:00
set_bh_page ( bh , real - > b_page , bh_offset ( real ) ) ;
2006-01-16 19:50:04 +03:00
bh - > b_blocknr = blkno ;
bh - > b_size = sdp - > sd_sb . sb_bsize ;
bh - > b_bdev = sdp - > sd_vfs - > s_bdev ;
log_incr_head ( sdp ) ;
return bh ;
}
static void log_pull_tail ( struct gfs2_sbd * sdp , unsigned int new_tail , int pull )
{
unsigned int dist = log_distance ( sdp , new_tail , sdp - > sd_log_tail ) ;
ail2_empty ( sdp , new_tail ) ;
gfs2_log_lock ( sdp ) ;
sdp - > sd_log_blks_free + = dist - ( ( pull ) ? 1 : 0 ) ;
gfs2_assert_withdraw ( sdp ,
sdp - > sd_log_blks_free < = sdp - > sd_jdesc - > jd_blocks ) ;
gfs2_log_unlock ( sdp ) ;
sdp - > sd_log_tail = new_tail ;
}
/**
* log_write_header - Get and initialize a journal header buffer
* @ sdp : The GFS2 superblock
*
* Returns : the initialized log buffer descriptor
*/
static void log_write_header ( struct gfs2_sbd * sdp , uint32_t flags , int pull )
{
uint64_t blkno = log_bmap ( sdp , sdp - > sd_log_flush_head ) ;
struct buffer_head * bh ;
struct gfs2_log_header * lh ;
unsigned int tail ;
uint32_t hash ;
bh = sb_getblk ( sdp - > sd_vfs , blkno ) ;
lock_buffer ( bh ) ;
memset ( bh - > b_data , 0 , bh - > b_size ) ;
set_buffer_uptodate ( bh ) ;
clear_buffer_dirty ( bh ) ;
unlock_buffer ( bh ) ;
gfs2_ail1_empty ( sdp , 0 ) ;
tail = current_tail ( sdp ) ;
lh = ( struct gfs2_log_header * ) bh - > b_data ;
memset ( lh , 0 , sizeof ( struct gfs2_log_header ) ) ;
lh - > lh_header . mh_magic = cpu_to_be32 ( GFS2_MAGIC ) ;
2006-03-31 00:46:23 +04:00
lh - > lh_header . mh_type = cpu_to_be32 ( GFS2_METATYPE_LH ) ;
lh - > lh_header . mh_format = cpu_to_be32 ( GFS2_FORMAT_LH ) ;
2006-01-16 19:50:04 +03:00
lh - > lh_sequence = be64_to_cpu ( sdp - > sd_log_sequence + + ) ;
lh - > lh_flags = be32_to_cpu ( flags ) ;
lh - > lh_tail = be32_to_cpu ( tail ) ;
lh - > lh_blkno = be32_to_cpu ( sdp - > sd_log_flush_head ) ;
hash = gfs2_disk_hash ( bh - > b_data , sizeof ( struct gfs2_log_header ) ) ;
lh - > lh_hash = cpu_to_be32 ( hash ) ;
set_buffer_dirty ( bh ) ;
if ( sync_dirty_buffer ( bh ) )
gfs2_io_error_bh ( sdp , bh ) ;
brelse ( bh ) ;
if ( sdp - > sd_log_tail ! = tail )
log_pull_tail ( sdp , tail , pull ) ;
else
gfs2_assert_withdraw ( sdp , ! pull ) ;
sdp - > sd_log_idle = ( tail = = sdp - > sd_log_flush_head ) ;
log_incr_head ( sdp ) ;
}
static void log_flush_commit ( struct gfs2_sbd * sdp )
{
struct list_head * head = & sdp - > sd_log_flush_list ;
struct gfs2_log_buf * lb ;
struct buffer_head * bh ;
unsigned int d ;
d = log_distance ( sdp , sdp - > sd_log_flush_head , sdp - > sd_log_head ) ;
gfs2_assert_withdraw ( sdp , d + 1 = = sdp - > sd_log_blks_reserved ) ;
while ( ! list_empty ( head ) ) {
lb = list_entry ( head - > next , struct gfs2_log_buf , lb_list ) ;
list_del ( & lb - > lb_list ) ;
bh = lb - > lb_bh ;
wait_on_buffer ( bh ) ;
if ( ! buffer_uptodate ( bh ) )
gfs2_io_error_bh ( sdp , bh ) ;
if ( lb - > lb_real ) {
while ( atomic_read ( & bh - > b_count ) ! = 1 ) /* Grrrr... */
schedule ( ) ;
free_buffer_head ( bh ) ;
} else
brelse ( bh ) ;
kfree ( lb ) ;
}
log_write_header ( sdp , 0 , 0 ) ;
}
/**
* gfs2_log_flush_i - flush incore transaction ( s )
* @ sdp : the filesystem
* @ gl : The glock structure to flush . If NULL , flush the whole incore log
*
*/
void gfs2_log_flush_i ( struct gfs2_sbd * sdp , struct gfs2_glock * gl )
{
struct gfs2_ail * ai ;
2006-01-18 16:20:16 +03:00
ai = kzalloc ( sizeof ( struct gfs2_ail ) , GFP_NOFS | __GFP_NOFAIL ) ;
2006-01-16 19:50:04 +03:00
INIT_LIST_HEAD ( & ai - > ai_ail1_list ) ;
INIT_LIST_HEAD ( & ai - > ai_ail2_list ) ;
2006-03-29 18:12:12 +04:00
down_write ( & sdp - > sd_log_flush_lock ) ;
2006-02-21 15:51:39 +03:00
if ( gl ) {
gfs2_log_lock ( sdp ) ;
if ( list_empty ( & gl - > gl_le . le_list ) ) {
gfs2_log_unlock ( sdp ) ;
2006-03-29 18:12:12 +04:00
up_write ( & sdp - > sd_log_flush_lock ) ;
2006-02-21 15:51:39 +03:00
kfree ( ai ) ;
return ;
}
gfs2_log_unlock ( sdp ) ;
}
2006-01-16 19:50:04 +03:00
gfs2_assert_withdraw ( sdp ,
sdp - > sd_log_num_buf = = sdp - > sd_log_commited_buf ) ;
gfs2_assert_withdraw ( sdp ,
sdp - > sd_log_num_revoke = = sdp - > sd_log_commited_revoke ) ;
sdp - > sd_log_flush_head = sdp - > sd_log_head ;
sdp - > sd_log_flush_wrapped = 0 ;
ai - > ai_first = sdp - > sd_log_flush_head ;
lops_before_commit ( sdp ) ;
if ( ! list_empty ( & sdp - > sd_log_flush_list ) )
log_flush_commit ( sdp ) ;
else if ( sdp - > sd_log_tail ! = current_tail ( sdp ) & & ! sdp - > sd_log_idle )
log_write_header ( sdp , 0 , PULL ) ;
lops_after_commit ( sdp , ai ) ;
sdp - > sd_log_head = sdp - > sd_log_flush_head ;
if ( sdp - > sd_log_flush_wrapped )
sdp - > sd_log_wraps + + ;
sdp - > sd_log_blks_reserved =
sdp - > sd_log_commited_buf =
sdp - > sd_log_commited_revoke = 0 ;
gfs2_log_lock ( sdp ) ;
if ( ! list_empty ( & ai - > ai_ail1_list ) ) {
list_add ( & ai - > ai_list , & sdp - > sd_ail1_list ) ;
ai = NULL ;
}
gfs2_log_unlock ( sdp ) ;
sdp - > sd_vfs - > s_dirt = 0 ;
2006-03-29 18:12:12 +04:00
up_write ( & sdp - > sd_log_flush_lock ) ;
2006-01-16 19:50:04 +03:00
kfree ( ai ) ;
}
static void log_refund ( struct gfs2_sbd * sdp , struct gfs2_trans * tr )
{
unsigned int reserved = 1 ;
unsigned int old ;
gfs2_log_lock ( sdp ) ;
sdp - > sd_log_commited_buf + = tr - > tr_num_buf_new - tr - > tr_num_buf_rm ;
gfs2_assert_withdraw ( sdp , ( ( int ) sdp - > sd_log_commited_buf ) > = 0 ) ;
sdp - > sd_log_commited_revoke + = tr - > tr_num_revoke - tr - > tr_num_revoke_rm ;
gfs2_assert_withdraw ( sdp , ( ( int ) sdp - > sd_log_commited_revoke ) > = 0 ) ;
if ( sdp - > sd_log_commited_buf )
2006-02-27 20:00:42 +03:00
reserved + = 1 + sdp - > sd_log_commited_buf +
sdp - > sd_log_commited_buf / 503 ;
2006-01-16 19:50:04 +03:00
if ( sdp - > sd_log_commited_revoke )
reserved + = gfs2_struct2blk ( sdp , sdp - > sd_log_commited_revoke ,
sizeof ( uint64_t ) ) ;
old = sdp - > sd_log_blks_free ;
sdp - > sd_log_blks_free + = tr - > tr_reserved -
( reserved - sdp - > sd_log_blks_reserved ) ;
gfs2_assert_withdraw ( sdp ,
sdp - > sd_log_blks_free > = old ) ;
gfs2_assert_withdraw ( sdp ,
sdp - > sd_log_blks_free < = sdp - > sd_jdesc - > jd_blocks ) ;
sdp - > sd_log_blks_reserved = reserved ;
gfs2_log_unlock ( sdp ) ;
}
/**
* gfs2_log_commit - Commit a transaction to the log
* @ sdp : the filesystem
* @ tr : the transaction
*
* Returns : errno
*/
void gfs2_log_commit ( struct gfs2_sbd * sdp , struct gfs2_trans * tr )
{
log_refund ( sdp , tr ) ;
lops_incore_commit ( sdp , tr ) ;
sdp - > sd_vfs - > s_dirt = 1 ;
2006-03-29 18:12:12 +04:00
up_read ( & sdp - > sd_log_flush_lock ) ;
2006-01-16 19:50:04 +03:00
gfs2_log_lock ( sdp ) ;
if ( sdp - > sd_log_num_buf > gfs2_tune_get ( sdp , gt_incore_log_blocks ) ) {
gfs2_log_unlock ( sdp ) ;
gfs2_log_flush ( sdp ) ;
} else
gfs2_log_unlock ( sdp ) ;
}
/**
* gfs2_log_shutdown - write a shutdown header into a journal
* @ sdp : the filesystem
*
*/
void gfs2_log_shutdown ( struct gfs2_sbd * sdp )
{
2006-03-29 18:12:12 +04:00
down_write ( & sdp - > sd_log_flush_lock ) ;
2006-01-16 19:50:04 +03:00
gfs2_assert_withdraw ( sdp , ! sdp - > sd_log_blks_reserved ) ;
gfs2_assert_withdraw ( sdp , ! sdp - > sd_log_num_gl ) ;
gfs2_assert_withdraw ( sdp , ! sdp - > sd_log_num_buf ) ;
2006-02-08 14:50:51 +03:00
gfs2_assert_withdraw ( sdp , ! sdp - > sd_log_num_jdata ) ;
2006-01-16 19:50:04 +03:00
gfs2_assert_withdraw ( sdp , ! sdp - > sd_log_num_revoke ) ;
gfs2_assert_withdraw ( sdp , ! sdp - > sd_log_num_rg ) ;
gfs2_assert_withdraw ( sdp , ! sdp - > sd_log_num_databuf ) ;
gfs2_assert_withdraw ( sdp , list_empty ( & sdp - > sd_ail1_list ) ) ;
sdp - > sd_log_flush_head = sdp - > sd_log_head ;
sdp - > sd_log_flush_wrapped = 0 ;
log_write_header ( sdp , GFS2_LOG_HEAD_UNMOUNT , 0 ) ;
gfs2_assert_withdraw ( sdp , sdp - > sd_log_blks_free = =
sdp - > sd_jdesc - > jd_blocks ) ;
gfs2_assert_withdraw ( sdp , sdp - > sd_log_head = = sdp - > sd_log_tail ) ;
gfs2_assert_withdraw ( sdp , list_empty ( & sdp - > sd_ail2_list ) ) ;
sdp - > sd_log_head = sdp - > sd_log_flush_head ;
if ( sdp - > sd_log_flush_wrapped )
sdp - > sd_log_wraps + + ;
sdp - > sd_log_tail = sdp - > sd_log_head ;
2006-03-29 18:12:12 +04:00
up_write ( & sdp - > sd_log_flush_lock ) ;
2006-01-16 19:50:04 +03:00
}