2006-01-16 19:50:04 +03:00
/*
* Copyright ( C ) Sistina Software , Inc . 1997 - 2003 All rights reserved .
2007-12-12 03:49:21 +03:00
* Copyright ( C ) 2004 - 2007 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>
2006-03-28 23:14:04 +04:00
# include <linux/crc32.h>
2006-02-28 01:23:27 +03:00
# include <linux/gfs2_ondisk.h>
2006-07-27 21:53:53 +04:00
# include <linux/bio.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 "bmap.h"
# include "dir.h"
# include "glock.h"
# include "glops.h"
# include "inode.h"
# include "log.h"
# include "meta_io.h"
# include "quota.h"
# include "recovery.h"
# include "rgrp.h"
# include "super.h"
# include "trans.h"
2006-02-28 01:23:27 +03:00
# include "util.h"
2006-01-16 19:50:04 +03:00
2008-12-19 18:32:06 +03:00
/**
* gfs2_jindex_free - Clear all the journal index information
* @ sdp : The GFS2 superblock
*
*/
void gfs2_jindex_free ( struct gfs2_sbd * sdp )
{
struct list_head list , * head ;
struct gfs2_jdesc * jd ;
struct gfs2_journal_extent * jext ;
spin_lock ( & sdp - > sd_jindex_spin ) ;
list_add ( & list , & sdp - > sd_jindex_list ) ;
list_del_init ( & sdp - > sd_jindex_list ) ;
sdp - > sd_journals = 0 ;
spin_unlock ( & sdp - > sd_jindex_spin ) ;
while ( ! list_empty ( & list ) ) {
jd = list_entry ( list . next , struct gfs2_jdesc , jd_list ) ;
head = & jd - > extent_list ;
while ( ! list_empty ( head ) ) {
jext = list_entry ( head - > next ,
struct gfs2_journal_extent ,
extent_list ) ;
list_del ( & jext - > extent_list ) ;
kfree ( jext ) ;
}
list_del ( & jd - > jd_list ) ;
iput ( jd - > jd_inode ) ;
kfree ( jd ) ;
}
}
2006-01-16 19:50:04 +03:00
static struct gfs2_jdesc * jdesc_find_i ( struct list_head * head , unsigned int jid )
{
struct gfs2_jdesc * jd ;
int found = 0 ;
list_for_each_entry ( jd , head , jd_list ) {
if ( jd - > jd_jid = = jid ) {
found = 1 ;
break ;
}
}
if ( ! found )
jd = NULL ;
return jd ;
}
struct gfs2_jdesc * gfs2_jdesc_find ( struct gfs2_sbd * sdp , unsigned int jid )
{
struct gfs2_jdesc * jd ;
spin_lock ( & sdp - > sd_jindex_spin ) ;
jd = jdesc_find_i ( & sdp - > sd_jindex_list , jid ) ;
spin_unlock ( & sdp - > sd_jindex_spin ) ;
return jd ;
}
int gfs2_jdesc_check ( struct gfs2_jdesc * jd )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( jd - > jd_inode ) ;
struct gfs2_sbd * sdp = GFS2_SB ( jd - > jd_inode ) ;
2006-01-16 19:50:04 +03:00
int ar ;
int error ;
2008-11-04 12:47:33 +03:00
if ( ip - > i_disksize < ( 8 < < 20 ) | | ip - > i_disksize > ( 1 < < 30 ) | |
( ip - > i_disksize & ( sdp - > sd_sb . sb_bsize - 1 ) ) ) {
2006-01-16 19:50:04 +03:00
gfs2_consist_inode ( ip ) ;
return - EIO ;
}
2008-11-04 12:47:33 +03:00
jd - > jd_blocks = ip - > i_disksize > > sdp - > sd_sb . sb_bsize_shift ;
2006-01-16 19:50:04 +03:00
2008-11-04 12:47:33 +03:00
error = gfs2_write_alloc_required ( ip , 0 , ip - > i_disksize , & ar ) ;
2006-01-16 19:50:04 +03:00
if ( ! error & & ar ) {
gfs2_consist_inode ( ip ) ;
error = - EIO ;
}
return error ;
}
/**
* gfs2_make_fs_rw - Turn a Read - Only FS into a Read - Write one
* @ sdp : the filesystem
*
* Returns : errno
*/
int gfs2_make_fs_rw ( struct gfs2_sbd * sdp )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( sdp - > sd_jdesc - > jd_inode ) ;
2006-02-28 01:23:27 +03:00
struct gfs2_glock * j_gl = ip - > i_gl ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder t_gh ;
2006-10-14 05:47:13 +04:00
struct gfs2_log_header_host head ;
2006-01-16 19:50:04 +03:00
int error ;
2007-01-22 20:10:39 +03:00
error = gfs2_glock_nq_init ( sdp - > sd_trans_gl , LM_ST_SHARED , 0 , & t_gh ) ;
2006-01-16 19:50:04 +03:00
if ( error )
return error ;
2006-11-20 18:37:45 +03:00
j_gl - > gl_ops - > go_inval ( j_gl , DIO_METADATA ) ;
2006-01-16 19:50:04 +03:00
error = gfs2_find_jhead ( sdp - > sd_jdesc , & head ) ;
if ( error )
goto fail ;
if ( ! ( head . lh_flags & GFS2_LOG_HEAD_UNMOUNT ) ) {
gfs2_consist ( sdp ) ;
error = - EIO ;
goto fail ;
}
/* Initialize some head of the log stuff */
sdp - > sd_log_sequence = head . lh_sequence + 1 ;
gfs2_log_pointers_init ( sdp , head . lh_blkno ) ;
error = gfs2_quota_init ( sdp ) ;
if ( error )
2006-09-04 20:04:26 +04:00
goto fail ;
2006-01-16 19:50:04 +03:00
set_bit ( SDF_JOURNAL_LIVE , & sdp - > sd_flags ) ;
gfs2_glock_dq_uninit ( & t_gh ) ;
return 0 ;
2006-09-04 20:04:26 +04:00
fail :
2006-01-16 19:50:04 +03:00
t_gh . gh_flags | = GL_NOCACHE ;
gfs2_glock_dq_uninit ( & t_gh ) ;
return error ;
}
2007-06-01 17:11:58 +04:00
static void gfs2_statfs_change_in ( struct gfs2_statfs_change_host * sc , const void * buf )
{
const struct gfs2_statfs_change * str = buf ;
sc - > sc_total = be64_to_cpu ( str - > sc_total ) ;
sc - > sc_free = be64_to_cpu ( str - > sc_free ) ;
sc - > sc_dinodes = be64_to_cpu ( str - > sc_dinodes ) ;
}
static void gfs2_statfs_change_out ( const struct gfs2_statfs_change_host * sc , void * buf )
{
struct gfs2_statfs_change * str = buf ;
str - > sc_total = cpu_to_be64 ( sc - > sc_total ) ;
str - > sc_free = cpu_to_be64 ( sc - > sc_free ) ;
str - > sc_dinodes = cpu_to_be64 ( sc - > sc_dinodes ) ;
}
2006-01-16 19:50:04 +03:00
int gfs2_statfs_init ( struct gfs2_sbd * sdp )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * m_ip = GFS2_I ( sdp - > sd_statfs_inode ) ;
2006-10-14 07:43:19 +04:00
struct gfs2_statfs_change_host * m_sc = & sdp - > sd_statfs_master ;
2006-06-14 23:32:57 +04:00
struct gfs2_inode * l_ip = GFS2_I ( sdp - > sd_sc_inode ) ;
2006-10-14 07:43:19 +04:00
struct gfs2_statfs_change_host * l_sc = & sdp - > sd_statfs_local ;
2006-01-16 19:50:04 +03:00
struct buffer_head * m_bh , * l_bh ;
struct gfs2_holder gh ;
int error ;
error = gfs2_glock_nq_init ( m_ip - > i_gl , LM_ST_EXCLUSIVE , GL_NOCACHE ,
& gh ) ;
if ( error )
return error ;
error = gfs2_meta_inode_buffer ( m_ip , & m_bh ) ;
if ( error )
goto out ;
if ( sdp - > sd_args . ar_spectator ) {
spin_lock ( & sdp - > sd_statfs_spin ) ;
gfs2_statfs_change_in ( m_sc , m_bh - > b_data +
sizeof ( struct gfs2_dinode ) ) ;
spin_unlock ( & sdp - > sd_statfs_spin ) ;
} else {
error = gfs2_meta_inode_buffer ( l_ip , & l_bh ) ;
if ( error )
goto out_m_bh ;
spin_lock ( & sdp - > sd_statfs_spin ) ;
gfs2_statfs_change_in ( m_sc , m_bh - > b_data +
sizeof ( struct gfs2_dinode ) ) ;
gfs2_statfs_change_in ( l_sc , l_bh - > b_data +
sizeof ( struct gfs2_dinode ) ) ;
spin_unlock ( & sdp - > sd_statfs_spin ) ;
brelse ( l_bh ) ;
}
2006-09-04 20:04:26 +04:00
out_m_bh :
2006-01-16 19:50:04 +03:00
brelse ( m_bh ) ;
2006-09-04 20:04:26 +04:00
out :
2006-01-16 19:50:04 +03:00
gfs2_glock_dq_uninit ( & gh ) ;
return 0 ;
}
2006-09-04 20:49:07 +04:00
void gfs2_statfs_change ( struct gfs2_sbd * sdp , s64 total , s64 free ,
s64 dinodes )
2006-01-16 19:50:04 +03:00
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * l_ip = GFS2_I ( sdp - > sd_sc_inode ) ;
2006-10-14 07:43:19 +04:00
struct gfs2_statfs_change_host * l_sc = & sdp - > sd_statfs_local ;
2006-01-16 19:50:04 +03:00
struct buffer_head * l_bh ;
int error ;
error = gfs2_meta_inode_buffer ( l_ip , & l_bh ) ;
if ( error )
return ;
2006-01-18 14:19:28 +03:00
gfs2_trans_add_bh ( l_ip - > i_gl , l_bh , 1 ) ;
2006-01-16 19:50:04 +03:00
spin_lock ( & sdp - > sd_statfs_spin ) ;
l_sc - > sc_total + = total ;
l_sc - > sc_free + = free ;
l_sc - > sc_dinodes + = dinodes ;
2006-09-25 17:26:04 +04:00
gfs2_statfs_change_out ( l_sc , l_bh - > b_data + sizeof ( struct gfs2_dinode ) ) ;
2006-01-16 19:50:04 +03:00
spin_unlock ( & sdp - > sd_statfs_spin ) ;
brelse ( l_bh ) ;
}
int gfs2_statfs_sync ( struct gfs2_sbd * sdp )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * m_ip = GFS2_I ( sdp - > sd_statfs_inode ) ;
struct gfs2_inode * l_ip = GFS2_I ( sdp - > sd_sc_inode ) ;
2006-10-14 07:43:19 +04:00
struct gfs2_statfs_change_host * m_sc = & sdp - > sd_statfs_master ;
struct gfs2_statfs_change_host * l_sc = & sdp - > sd_statfs_local ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder gh ;
struct buffer_head * m_bh , * l_bh ;
int error ;
error = gfs2_glock_nq_init ( m_ip - > i_gl , LM_ST_EXCLUSIVE , GL_NOCACHE ,
& gh ) ;
if ( error )
return error ;
error = gfs2_meta_inode_buffer ( m_ip , & m_bh ) ;
if ( error )
goto out ;
spin_lock ( & sdp - > sd_statfs_spin ) ;
gfs2_statfs_change_in ( m_sc , m_bh - > b_data +
2006-09-25 17:26:04 +04:00
sizeof ( struct gfs2_dinode ) ) ;
2006-01-16 19:50:04 +03:00
if ( ! l_sc - > sc_total & & ! l_sc - > sc_free & & ! l_sc - > sc_dinodes ) {
spin_unlock ( & sdp - > sd_statfs_spin ) ;
goto out_bh ;
}
spin_unlock ( & sdp - > sd_statfs_spin ) ;
error = gfs2_meta_inode_buffer ( l_ip , & l_bh ) ;
if ( error )
goto out_bh ;
error = gfs2_trans_begin ( sdp , 2 * RES_DINODE , 0 ) ;
if ( error )
goto out_bh2 ;
2006-01-18 14:19:28 +03:00
gfs2_trans_add_bh ( l_ip - > i_gl , l_bh , 1 ) ;
2006-01-16 19:50:04 +03:00
spin_lock ( & sdp - > sd_statfs_spin ) ;
m_sc - > sc_total + = l_sc - > sc_total ;
m_sc - > sc_free + = l_sc - > sc_free ;
m_sc - > sc_dinodes + = l_sc - > sc_dinodes ;
memset ( l_sc , 0 , sizeof ( struct gfs2_statfs_change ) ) ;
memset ( l_bh - > b_data + sizeof ( struct gfs2_dinode ) ,
0 , sizeof ( struct gfs2_statfs_change ) ) ;
spin_unlock ( & sdp - > sd_statfs_spin ) ;
2006-01-18 14:19:28 +03:00
gfs2_trans_add_bh ( m_ip - > i_gl , m_bh , 1 ) ;
2006-01-16 19:50:04 +03:00
gfs2_statfs_change_out ( m_sc , m_bh - > b_data + sizeof ( struct gfs2_dinode ) ) ;
gfs2_trans_end ( sdp ) ;
2006-09-04 20:04:26 +04:00
out_bh2 :
2006-01-16 19:50:04 +03:00
brelse ( l_bh ) ;
2006-09-04 20:04:26 +04:00
out_bh :
2006-01-16 19:50:04 +03:00
brelse ( m_bh ) ;
2006-09-04 20:04:26 +04:00
out :
2006-01-16 19:50:04 +03:00
gfs2_glock_dq_uninit ( & gh ) ;
return error ;
}
struct lfcc {
struct list_head list ;
struct gfs2_holder gh ;
} ;
/**
* gfs2_lock_fs_check_clean - Stop all writes to the FS and check that all
* journals are clean
* @ sdp : the file system
* @ state : the state to put the transaction lock into
* @ t_gh : the hold on the transaction lock
*
* Returns : errno
*/
2006-04-28 18:59:12 +04:00
static int gfs2_lock_fs_check_clean ( struct gfs2_sbd * sdp ,
struct gfs2_holder * t_gh )
2006-01-16 19:50:04 +03:00
{
2006-02-28 01:23:27 +03:00
struct gfs2_inode * ip ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder ji_gh ;
struct gfs2_jdesc * jd ;
struct lfcc * lfcc ;
LIST_HEAD ( list ) ;
2006-10-14 05:47:13 +04:00
struct gfs2_log_header_host lh ;
2006-01-16 19:50:04 +03:00
int error ;
list_for_each_entry ( jd , & sdp - > sd_jindex_list , jd_list ) {
lfcc = kmalloc ( sizeof ( struct lfcc ) , GFP_KERNEL ) ;
if ( ! lfcc ) {
error = - ENOMEM ;
goto out ;
}
2006-06-14 23:32:57 +04:00
ip = GFS2_I ( jd - > jd_inode ) ;
error = gfs2_glock_nq_init ( ip - > i_gl , LM_ST_SHARED , 0 , & lfcc - > gh ) ;
2006-01-16 19:50:04 +03:00
if ( error ) {
kfree ( lfcc ) ;
goto out ;
}
list_add ( & lfcc - > list , & list ) ;
}
error = gfs2_glock_nq_init ( sdp - > sd_trans_gl , LM_ST_DEFERRED ,
2008-05-21 20:03:22 +04:00
GL_NOCACHE , t_gh ) ;
2006-01-16 19:50:04 +03:00
list_for_each_entry ( jd , & sdp - > sd_jindex_list , jd_list ) {
error = gfs2_jdesc_check ( jd ) ;
if ( error )
break ;
error = gfs2_find_jhead ( jd , & lh ) ;
if ( error )
break ;
if ( ! ( lh . lh_flags & GFS2_LOG_HEAD_UNMOUNT ) ) {
error = - EBUSY ;
break ;
}
}
if ( error )
gfs2_glock_dq_uninit ( t_gh ) ;
2006-09-04 20:04:26 +04:00
out :
2006-01-16 19:50:04 +03:00
while ( ! list_empty ( & list ) ) {
lfcc = list_entry ( list . next , struct lfcc , list ) ;
list_del ( & lfcc - > list ) ;
gfs2_glock_dq_uninit ( & lfcc - > gh ) ;
kfree ( lfcc ) ;
}
gfs2_glock_dq_uninit ( & ji_gh ) ;
return error ;
}
/**
* gfs2_freeze_fs - freezes the file system
* @ sdp : the file system
*
* This function flushes data and meta data for all machines by
* aquiring the transaction log exclusively . All journals are
* ensured to be in a clean state as well .
*
* Returns : errno
*/
int gfs2_freeze_fs ( struct gfs2_sbd * sdp )
{
int error = 0 ;
2006-02-21 15:51:39 +03:00
mutex_lock ( & sdp - > sd_freeze_lock ) ;
2006-01-16 19:50:04 +03:00
if ( ! sdp - > sd_freeze_count + + ) {
error = gfs2_lock_fs_check_clean ( sdp , & sdp - > sd_freeze_gh ) ;
if ( error )
sdp - > sd_freeze_count - - ;
}
2006-02-21 15:51:39 +03:00
mutex_unlock ( & sdp - > sd_freeze_lock ) ;
2006-01-16 19:50:04 +03:00
return error ;
}
/**
* gfs2_unfreeze_fs - unfreezes the file system
* @ sdp : the file system
*
* This function allows the file system to proceed by unlocking
* the exclusively held transaction lock . Other GFS2 nodes are
* now free to acquire the lock shared and go on with their lives .
*
*/
void gfs2_unfreeze_fs ( struct gfs2_sbd * sdp )
{
2006-02-21 15:51:39 +03:00
mutex_lock ( & sdp - > sd_freeze_lock ) ;
2006-01-16 19:50:04 +03:00
if ( sdp - > sd_freeze_count & & ! - - sdp - > sd_freeze_count )
gfs2_glock_dq_uninit ( & sdp - > sd_freeze_gh ) ;
2006-02-21 15:51:39 +03:00
mutex_unlock ( & sdp - > sd_freeze_lock ) ;
2006-01-16 19:50:04 +03:00
}