2009-04-07 06:01:35 +04:00
/*
* the_nilfs . c - the_nilfs shared structure .
*
* Copyright ( C ) 2005 - 2008 Nippon Telegraph and Telephone Corporation .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*
* Written by Ryusuke Konishi < ryusuke @ osrg . net >
*
*/
# include <linux/buffer_head.h>
# include <linux/slab.h>
# include <linux/blkdev.h>
# include <linux/backing-dev.h>
2011-03-09 05:05:08 +03:00
# include <linux/random.h>
2009-04-07 06:01:59 +04:00
# include <linux/crc32.h>
2009-04-07 06:01:35 +04:00
# include "nilfs.h"
# include "segment.h"
# include "alloc.h"
# include "cpfile.h"
# include "sufile.h"
# include "dat.h"
# include "segbuf.h"
2009-06-07 20:39:29 +04:00
2010-06-28 14:15:26 +04:00
static int nilfs_valid_sb ( struct nilfs_super_block * sbp ) ;
2009-04-07 06:01:35 +04:00
void nilfs_set_last_segment ( struct the_nilfs * nilfs ,
sector_t start_blocknr , u64 seq , __u64 cno )
{
spin_lock ( & nilfs - > ns_last_segment_lock ) ;
nilfs - > ns_last_pseg = start_blocknr ;
nilfs - > ns_last_seq = seq ;
nilfs - > ns_last_cno = cno ;
2010-06-29 09:42:13 +04:00
if ( ! nilfs_sb_dirty ( nilfs ) ) {
if ( nilfs - > ns_prev_seq = = nilfs - > ns_last_seq )
goto stay_cursor ;
set_nilfs_sb_dirty ( nilfs ) ;
}
nilfs - > ns_prev_seq = nilfs - > ns_last_seq ;
stay_cursor :
2009-04-07 06:01:35 +04:00
spin_unlock ( & nilfs - > ns_last_segment_lock ) ;
}
/**
2010-09-08 21:07:56 +04:00
* alloc_nilfs - allocate a nilfs object
2009-04-07 06:01:35 +04:00
* @ bdev : block device to which the_nilfs is related
*
* Return Value : On success , pointer to the_nilfs is returned .
* On error , NULL is returned .
*/
2010-09-08 21:07:56 +04:00
struct the_nilfs * alloc_nilfs ( struct block_device * bdev )
2009-04-07 06:01:35 +04:00
{
struct the_nilfs * nilfs ;
nilfs = kzalloc ( sizeof ( * nilfs ) , GFP_KERNEL ) ;
if ( ! nilfs )
return NULL ;
nilfs - > ns_bdev = bdev ;
atomic_set ( & nilfs - > ns_ndirtyblks , 0 ) ;
init_rwsem ( & nilfs - > ns_sem ) ;
2012-07-31 01:42:07 +04:00
mutex_init ( & nilfs - > ns_snapshot_mount_mutex ) ;
2011-03-09 05:05:07 +03:00
INIT_LIST_HEAD ( & nilfs - > ns_dirty_files ) ;
2010-08-20 14:06:11 +04:00
INIT_LIST_HEAD ( & nilfs - > ns_gc_inodes ) ;
2011-03-09 05:05:07 +03:00
spin_lock_init ( & nilfs - > ns_inode_lock ) ;
2011-03-09 05:05:08 +03:00
spin_lock_init ( & nilfs - > ns_next_gen_lock ) ;
2009-04-07 06:01:35 +04:00
spin_lock_init ( & nilfs - > ns_last_segment_lock ) ;
2010-08-14 07:59:15 +04:00
nilfs - > ns_cptree = RB_ROOT ;
spin_lock_init ( & nilfs - > ns_cptree_lock ) ;
2009-04-07 06:01:35 +04:00
init_rwsem ( & nilfs - > ns_segctor_sem ) ;
return nilfs ;
}
2009-06-07 20:39:29 +04:00
/**
2010-09-08 21:07:56 +04:00
* destroy_nilfs - destroy nilfs object
* @ nilfs : nilfs object to be released
2009-04-07 06:01:35 +04:00
*/
2010-09-08 21:07:56 +04:00
void destroy_nilfs ( struct the_nilfs * nilfs )
2009-04-07 06:01:35 +04:00
{
might_sleep ( ) ;
if ( nilfs_init ( nilfs ) ) {
2009-04-07 06:01:59 +04:00
brelse ( nilfs - > ns_sbh [ 0 ] ) ;
brelse ( nilfs - > ns_sbh [ 1 ] ) ;
2009-04-07 06:01:35 +04:00
}
kfree ( nilfs ) ;
}
2010-09-05 07:20:59 +04:00
static int nilfs_load_super_root ( struct the_nilfs * nilfs ,
struct super_block * sb , sector_t sr_block )
2009-04-07 06:01:35 +04:00
{
struct buffer_head * bh_sr ;
struct nilfs_super_root * raw_sr ;
2009-04-07 06:01:59 +04:00
struct nilfs_super_block * * sbp = nilfs - > ns_sbp ;
2010-09-05 07:20:59 +04:00
struct nilfs_inode * rawi ;
2009-04-07 06:01:35 +04:00
unsigned dat_entry_size , segment_usage_size , checkpoint_size ;
unsigned inode_size ;
int err ;
2010-05-22 20:39:02 +04:00
err = nilfs_read_super_root_block ( nilfs , sr_block , & bh_sr , 1 ) ;
2009-04-07 06:01:35 +04:00
if ( unlikely ( err ) )
return err ;
down_read ( & nilfs - > ns_sem ) ;
2009-04-07 06:01:59 +04:00
dat_entry_size = le16_to_cpu ( sbp [ 0 ] - > s_dat_entry_size ) ;
checkpoint_size = le16_to_cpu ( sbp [ 0 ] - > s_checkpoint_size ) ;
segment_usage_size = le16_to_cpu ( sbp [ 0 ] - > s_segment_usage_size ) ;
2009-04-07 06:01:35 +04:00
up_read ( & nilfs - > ns_sem ) ;
inode_size = nilfs - > ns_inode_size ;
2010-09-05 07:20:59 +04:00
rawi = ( void * ) bh_sr - > b_data + NILFS_SR_DAT_OFFSET ( inode_size ) ;
err = nilfs_dat_read ( sb , dat_entry_size , rawi , & nilfs - > ns_dat ) ;
if ( err )
2009-04-07 06:01:35 +04:00
goto failed ;
2010-09-05 07:20:59 +04:00
rawi = ( void * ) bh_sr - > b_data + NILFS_SR_CPFILE_OFFSET ( inode_size ) ;
err = nilfs_cpfile_read ( sb , checkpoint_size , rawi , & nilfs - > ns_cpfile ) ;
if ( err )
2009-04-07 06:01:35 +04:00
goto failed_dat ;
2010-09-05 07:20:59 +04:00
rawi = ( void * ) bh_sr - > b_data + NILFS_SR_SUFILE_OFFSET ( inode_size ) ;
err = nilfs_sufile_read ( sb , segment_usage_size , rawi ,
& nilfs - > ns_sufile ) ;
if ( err )
2009-04-07 06:01:35 +04:00
goto failed_cpfile ;
raw_sr = ( struct nilfs_super_root * ) bh_sr - > b_data ;
nilfs - > ns_nongc_ctime = le64_to_cpu ( raw_sr - > sr_nongc_ctime ) ;
failed :
brelse ( bh_sr ) ;
return err ;
failed_cpfile :
2010-09-05 07:20:59 +04:00
iput ( nilfs - > ns_cpfile ) ;
2009-04-07 06:01:35 +04:00
failed_dat :
2010-09-05 07:20:59 +04:00
iput ( nilfs - > ns_dat ) ;
2009-04-07 06:01:35 +04:00
goto failed ;
}
static void nilfs_init_recovery_info ( struct nilfs_recovery_info * ri )
{
memset ( ri , 0 , sizeof ( * ri ) ) ;
INIT_LIST_HEAD ( & ri - > ri_used_segments ) ;
}
static void nilfs_clear_recovery_info ( struct nilfs_recovery_info * ri )
{
nilfs_dispose_segment_list ( & ri - > ri_used_segments ) ;
}
2010-06-28 14:15:24 +04:00
/**
* nilfs_store_log_cursor - load log cursor from a super block
* @ nilfs : nilfs object
* @ sbp : buffer storing super block to be read
*
* nilfs_store_log_cursor ( ) reads the last position of the log
* containing a super root from a given super block , and initializes
* relevant information on the nilfs object preparatory for log
* scanning and recovery .
*/
static int nilfs_store_log_cursor ( struct the_nilfs * nilfs ,
struct nilfs_super_block * sbp )
{
int ret = 0 ;
nilfs - > ns_last_pseg = le64_to_cpu ( sbp - > s_last_pseg ) ;
nilfs - > ns_last_cno = le64_to_cpu ( sbp - > s_last_cno ) ;
nilfs - > ns_last_seq = le64_to_cpu ( sbp - > s_last_seq ) ;
2010-06-29 09:42:13 +04:00
nilfs - > ns_prev_seq = nilfs - > ns_last_seq ;
2010-06-28 14:15:24 +04:00
nilfs - > ns_seg_seq = nilfs - > ns_last_seq ;
nilfs - > ns_segnum =
nilfs_get_segnum_of_block ( nilfs , nilfs - > ns_last_pseg ) ;
nilfs - > ns_cno = nilfs - > ns_last_cno + 1 ;
if ( nilfs - > ns_segnum > = nilfs - > ns_nsegments ) {
printk ( KERN_ERR " NILFS invalid last segment number. \n " ) ;
ret = - EINVAL ;
}
return ret ;
}
2009-04-07 06:01:35 +04:00
/**
* load_nilfs - load and recover the nilfs
* @ nilfs : the_nilfs structure to be released
2011-03-09 05:05:08 +03:00
* @ sb : super block isntance used to recover past segment
2009-04-07 06:01:35 +04:00
*
* load_nilfs ( ) searches and load the latest super root ,
* attaches the last segment , and does recovery if needed .
* The caller must call this exclusively for simultaneous mounts .
*/
2011-03-09 05:05:08 +03:00
int load_nilfs ( struct the_nilfs * nilfs , struct super_block * sb )
2009-04-07 06:01:35 +04:00
{
struct nilfs_recovery_info ri ;
2011-03-09 05:05:08 +03:00
unsigned int s_flags = sb - > s_flags ;
2009-04-07 06:01:35 +04:00
int really_read_only = bdev_read_only ( nilfs - > ns_bdev ) ;
2009-11-19 13:58:46 +03:00
int valid_fs = nilfs_valid_fs ( nilfs ) ;
2009-11-19 10:58:40 +03:00
int err ;
2009-04-07 06:01:35 +04:00
2009-11-19 10:58:40 +03:00
if ( ! valid_fs ) {
printk ( KERN_WARNING " NILFS warning: mounting unchecked fs \n " ) ;
if ( s_flags & MS_RDONLY ) {
printk ( KERN_INFO " NILFS: INFO: recovery "
" required for readonly filesystem. \n " ) ;
printk ( KERN_INFO " NILFS: write access will "
" be enabled during recovery. \n " ) ;
2009-04-07 06:01:35 +04:00
}
}
2009-11-19 10:58:40 +03:00
nilfs_init_recovery_info ( & ri ) ;
2010-05-22 20:39:02 +04:00
err = nilfs_search_super_root ( nilfs , & ri ) ;
2009-04-07 06:01:35 +04:00
if ( unlikely ( err ) ) {
2010-06-28 14:15:26 +04:00
struct nilfs_super_block * * sbp = nilfs - > ns_sbp ;
int blocksize ;
if ( err ! = - EINVAL )
goto scan_error ;
if ( ! nilfs_valid_sb ( sbp [ 1 ] ) ) {
printk ( KERN_WARNING
" NILFS warning: unable to fall back to spare "
" super block \n " ) ;
goto scan_error ;
}
printk ( KERN_INFO
" NILFS: try rollback from an earlier position \n " ) ;
/*
* restore super block with its spare and reconfigure
* relevant states of the nilfs object .
*/
memcpy ( sbp [ 0 ] , sbp [ 1 ] , nilfs - > ns_sbsize ) ;
nilfs - > ns_crc_seed = le32_to_cpu ( sbp [ 0 ] - > s_crc_seed ) ;
nilfs - > ns_sbwtime = le64_to_cpu ( sbp [ 0 ] - > s_wtime ) ;
/* verify consistency between two super blocks */
blocksize = BLOCK_SIZE < < le32_to_cpu ( sbp [ 0 ] - > s_log_block_size ) ;
if ( blocksize ! = nilfs - > ns_blocksize ) {
printk ( KERN_WARNING
" NILFS warning: blocksize differs between "
" two super blocks (%d != %d) \n " ,
blocksize , nilfs - > ns_blocksize ) ;
goto scan_error ;
}
err = nilfs_store_log_cursor ( nilfs , sbp [ 0 ] ) ;
if ( err )
goto scan_error ;
/* drop clean flag to allow roll-forward and recovery */
nilfs - > ns_mount_state & = ~ NILFS_VALID_FS ;
valid_fs = 0 ;
err = nilfs_search_super_root ( nilfs , & ri ) ;
if ( err )
goto scan_error ;
2009-04-07 06:01:35 +04:00
}
2011-03-09 05:05:08 +03:00
err = nilfs_load_super_root ( nilfs , sb , ri . ri_super_root ) ;
2009-04-07 06:01:35 +04:00
if ( unlikely ( err ) ) {
printk ( KERN_ERR " NILFS: error loading super root. \n " ) ;
goto failed ;
}
2009-11-19 10:58:40 +03:00
if ( valid_fs )
goto skip_recovery ;
if ( s_flags & MS_RDONLY ) {
2010-07-21 22:22:20 +04:00
__u64 features ;
2011-03-09 05:05:07 +03:00
if ( nilfs_test_opt ( nilfs , NORECOVERY ) ) {
2009-11-19 21:28:01 +03:00
printk ( KERN_INFO " NILFS: norecovery option specified. "
" skipping roll-forward recovery \n " ) ;
goto skip_recovery ;
}
2010-07-21 22:22:20 +04:00
features = le64_to_cpu ( nilfs - > ns_sbp [ 0 ] - > s_feature_compat_ro ) &
~ NILFS_FEATURE_COMPAT_RO_SUPP ;
if ( features ) {
printk ( KERN_ERR " NILFS: couldn't proceed with "
" recovery because of unsupported optional "
" features (%llx) \n " ,
( unsigned long long ) features ) ;
err = - EROFS ;
goto failed_unload ;
}
2009-11-19 10:58:40 +03:00
if ( really_read_only ) {
printk ( KERN_ERR " NILFS: write access "
" unavailable, cannot proceed. \n " ) ;
err = - EROFS ;
goto failed_unload ;
2009-04-07 06:01:35 +04:00
}
2011-03-09 05:05:08 +03:00
sb - > s_flags & = ~ MS_RDONLY ;
2011-03-09 05:05:07 +03:00
} else if ( nilfs_test_opt ( nilfs , NORECOVERY ) ) {
2009-11-19 21:28:01 +03:00
printk ( KERN_ERR " NILFS: recovery cancelled because norecovery "
" option was specified for a read/write mount \n " ) ;
err = - EINVAL ;
goto failed_unload ;
2009-11-19 10:58:40 +03:00
}
2011-03-09 05:05:08 +03:00
err = nilfs_salvage_orphan_logs ( nilfs , sb , & ri ) ;
2009-11-19 10:58:40 +03:00
if ( err )
goto failed_unload ;
down_write ( & nilfs - > ns_sem ) ;
2010-06-28 12:49:29 +04:00
nilfs - > ns_mount_state | = NILFS_VALID_FS ; /* set "clean" flag */
2011-03-09 05:05:08 +03:00
err = nilfs_cleanup_super ( sb ) ;
2009-11-19 10:58:40 +03:00
up_write ( & nilfs - > ns_sem ) ;
if ( err ) {
printk ( KERN_ERR " NILFS: failed to update super block. "
" recovery unfinished. \n " ) ;
goto failed_unload ;
2009-04-07 06:01:35 +04:00
}
2009-11-19 10:58:40 +03:00
printk ( KERN_INFO " NILFS: recovery complete. \n " ) ;
2009-04-07 06:01:35 +04:00
2009-11-19 10:58:40 +03:00
skip_recovery :
nilfs_clear_recovery_info ( & ri ) ;
2011-03-09 05:05:08 +03:00
sb - > s_flags = s_flags ;
2009-11-19 10:58:40 +03:00
return 0 ;
2010-06-28 14:15:26 +04:00
scan_error :
printk ( KERN_ERR " NILFS: error searching super root. \n " ) ;
goto failed ;
2009-11-19 10:58:40 +03:00
failed_unload :
2010-09-05 07:20:59 +04:00
iput ( nilfs - > ns_cpfile ) ;
iput ( nilfs - > ns_sufile ) ;
iput ( nilfs - > ns_dat ) ;
2009-04-07 06:01:35 +04:00
failed :
nilfs_clear_recovery_info ( & ri ) ;
2011-03-09 05:05:08 +03:00
sb - > s_flags = s_flags ;
2009-04-07 06:01:35 +04:00
return err ;
}
static unsigned long long nilfs_max_size ( unsigned int blkbits )
{
unsigned int max_bits ;
unsigned long long res = MAX_LFS_FILESIZE ; /* page cache limit */
max_bits = blkbits + NILFS_BMAP_KEY_BIT ; /* bmap size limit */
if ( max_bits < 64 )
res = min_t ( unsigned long long , res , ( 1ULL < < max_bits ) - 1 ) ;
return res ;
}
2011-05-04 20:23:58 +04:00
/**
* nilfs_nrsvsegs - calculate the number of reserved segments
* @ nilfs : nilfs object
* @ nsegs : total number of segments
*/
unsigned long nilfs_nrsvsegs ( struct the_nilfs * nilfs , unsigned long nsegs )
{
return max_t ( unsigned long , NILFS_MIN_NRSVSEGS ,
DIV_ROUND_UP ( nsegs * nilfs - > ns_r_segments_percentage ,
100 ) ) ;
}
void nilfs_set_nsegments ( struct the_nilfs * nilfs , unsigned long nsegs )
{
nilfs - > ns_nsegments = nsegs ;
nilfs - > ns_nrsvsegs = nilfs_nrsvsegs ( nilfs , nsegs ) ;
}
2009-04-07 06:01:59 +04:00
static int nilfs_store_disk_layout ( struct the_nilfs * nilfs ,
struct nilfs_super_block * sbp )
2009-04-07 06:01:35 +04:00
{
2010-08-09 19:58:41 +04:00
if ( le32_to_cpu ( sbp - > s_rev_level ) < NILFS_MIN_SUPP_REV ) {
printk ( KERN_ERR " NILFS: unsupported revision "
2009-04-07 06:01:35 +04:00
" (superblock rev.=%d.%d, current rev.=%d.%d). "
" Please check the version of mkfs.nilfs. \n " ,
le32_to_cpu ( sbp - > s_rev_level ) ,
le16_to_cpu ( sbp - > s_minor_rev_level ) ,
NILFS_CURRENT_REV , NILFS_MINOR_REV ) ;
return - EINVAL ;
}
2009-04-07 06:01:59 +04:00
nilfs - > ns_sbsize = le16_to_cpu ( sbp - > s_bytes ) ;
if ( nilfs - > ns_sbsize > BLOCK_SIZE )
return - EINVAL ;
2009-04-07 06:01:35 +04:00
nilfs - > ns_inode_size = le16_to_cpu ( sbp - > s_inode_size ) ;
nilfs - > ns_first_ino = le32_to_cpu ( sbp - > s_first_ino ) ;
nilfs - > ns_blocks_per_segment = le32_to_cpu ( sbp - > s_blocks_per_segment ) ;
if ( nilfs - > ns_blocks_per_segment < NILFS_SEG_MIN_BLOCKS ) {
2010-03-13 22:01:27 +03:00
printk ( KERN_ERR " NILFS: too short segment. \n " ) ;
2009-04-07 06:01:35 +04:00
return - EINVAL ;
}
nilfs - > ns_first_data_block = le64_to_cpu ( sbp - > s_first_data_block ) ;
nilfs - > ns_r_segments_percentage =
le32_to_cpu ( sbp - > s_r_segments_percentage ) ;
2012-03-17 04:08:38 +04:00
if ( nilfs - > ns_r_segments_percentage < 1 | |
nilfs - > ns_r_segments_percentage > 99 ) {
printk ( KERN_ERR " NILFS: invalid reserved segments percentage. \n " ) ;
return - EINVAL ;
}
2011-05-04 20:23:58 +04:00
nilfs_set_nsegments ( nilfs , le64_to_cpu ( sbp - > s_nsegments ) ) ;
2009-04-07 06:01:35 +04:00
nilfs - > ns_crc_seed = le32_to_cpu ( sbp - > s_crc_seed ) ;
return 0 ;
}
2009-04-07 06:01:59 +04:00
static int nilfs_valid_sb ( struct nilfs_super_block * sbp )
{
static unsigned char sum [ 4 ] ;
const int sumoff = offsetof ( struct nilfs_super_block , s_sum ) ;
size_t bytes ;
u32 crc ;
if ( ! sbp | | le16_to_cpu ( sbp - > s_magic ) ! = NILFS_SUPER_MAGIC )
return 0 ;
bytes = le16_to_cpu ( sbp - > s_bytes ) ;
if ( bytes > BLOCK_SIZE )
return 0 ;
crc = crc32_le ( le32_to_cpu ( sbp - > s_crc_seed ) , ( unsigned char * ) sbp ,
sumoff ) ;
crc = crc32_le ( crc , sum , 4 ) ;
crc = crc32_le ( crc , ( unsigned char * ) sbp + sumoff + 4 ,
bytes - sumoff - 4 ) ;
return crc = = le32_to_cpu ( sbp - > s_sum ) ;
}
static int nilfs_sb2_bad_offset ( struct nilfs_super_block * sbp , u64 offset )
{
return offset < ( ( le64_to_cpu ( sbp - > s_nsegments ) *
le32_to_cpu ( sbp - > s_blocks_per_segment ) ) < <
( le32_to_cpu ( sbp - > s_log_block_size ) + 10 ) ) ;
}
static void nilfs_release_super_block ( struct the_nilfs * nilfs )
{
int i ;
for ( i = 0 ; i < 2 ; i + + ) {
if ( nilfs - > ns_sbp [ i ] ) {
brelse ( nilfs - > ns_sbh [ i ] ) ;
nilfs - > ns_sbh [ i ] = NULL ;
nilfs - > ns_sbp [ i ] = NULL ;
}
}
}
void nilfs_fall_back_super_block ( struct the_nilfs * nilfs )
{
brelse ( nilfs - > ns_sbh [ 0 ] ) ;
nilfs - > ns_sbh [ 0 ] = nilfs - > ns_sbh [ 1 ] ;
nilfs - > ns_sbp [ 0 ] = nilfs - > ns_sbp [ 1 ] ;
nilfs - > ns_sbh [ 1 ] = NULL ;
nilfs - > ns_sbp [ 1 ] = NULL ;
}
void nilfs_swap_super_block ( struct the_nilfs * nilfs )
{
struct buffer_head * tsbh = nilfs - > ns_sbh [ 0 ] ;
struct nilfs_super_block * tsbp = nilfs - > ns_sbp [ 0 ] ;
nilfs - > ns_sbh [ 0 ] = nilfs - > ns_sbh [ 1 ] ;
nilfs - > ns_sbp [ 0 ] = nilfs - > ns_sbp [ 1 ] ;
nilfs - > ns_sbh [ 1 ] = tsbh ;
nilfs - > ns_sbp [ 1 ] = tsbp ;
}
static int nilfs_load_super_block ( struct the_nilfs * nilfs ,
struct super_block * sb , int blocksize ,
struct nilfs_super_block * * sbpp )
{
struct nilfs_super_block * * sbp = nilfs - > ns_sbp ;
struct buffer_head * * sbh = nilfs - > ns_sbh ;
u64 sb2off = NILFS_SB2_OFFSET_BYTES ( nilfs - > ns_bdev - > bd_inode - > i_size ) ;
int valid [ 2 ] , swp = 0 ;
sbp [ 0 ] = nilfs_read_super_block ( sb , NILFS_SB_OFFSET_BYTES , blocksize ,
& sbh [ 0 ] ) ;
sbp [ 1 ] = nilfs_read_super_block ( sb , sb2off , blocksize , & sbh [ 1 ] ) ;
if ( ! sbp [ 0 ] ) {
if ( ! sbp [ 1 ] ) {
printk ( KERN_ERR " NILFS: unable to read superblock \n " ) ;
return - EIO ;
}
printk ( KERN_WARNING
2011-01-23 18:28:22 +03:00
" NILFS warning: unable to read primary superblock "
" (blocksize = %d) \n " , blocksize ) ;
} else if ( ! sbp [ 1 ] ) {
2009-04-07 06:01:59 +04:00
printk ( KERN_WARNING
2011-01-23 18:28:22 +03:00
" NILFS warning: unable to read secondary superblock "
" (blocksize = %d) \n " , blocksize ) ;
}
2009-04-07 06:01:59 +04:00
2010-05-01 06:54:21 +04:00
/*
* Compare two super blocks and set 1 in swp if the secondary
* super block is valid and newer . Otherwise , set 0 in swp .
*/
2009-04-07 06:01:59 +04:00
valid [ 0 ] = nilfs_valid_sb ( sbp [ 0 ] ) ;
valid [ 1 ] = nilfs_valid_sb ( sbp [ 1 ] ) ;
2010-05-01 06:54:21 +04:00
swp = valid [ 1 ] & & ( ! valid [ 0 ] | |
le64_to_cpu ( sbp [ 1 ] - > s_last_cno ) >
le64_to_cpu ( sbp [ 0 ] - > s_last_cno ) ) ;
2009-04-07 06:01:59 +04:00
if ( valid [ swp ] & & nilfs_sb2_bad_offset ( sbp [ swp ] , sb2off ) ) {
brelse ( sbh [ 1 ] ) ;
sbh [ 1 ] = NULL ;
sbp [ 1 ] = NULL ;
2012-03-17 04:08:39 +04:00
valid [ 1 ] = 0 ;
2009-04-07 06:01:59 +04:00
swp = 0 ;
}
if ( ! valid [ swp ] ) {
nilfs_release_super_block ( nilfs ) ;
printk ( KERN_ERR " NILFS: Can't find nilfs on dev %s. \n " ,
sb - > s_id ) ;
return - EINVAL ;
}
2010-08-15 15:16:11 +04:00
if ( ! valid [ ! swp ] )
2009-04-07 06:01:59 +04:00
printk ( KERN_WARNING " NILFS warning: broken superblock. "
2011-01-23 18:28:22 +03:00
" using spare superblock (blocksize = %d). \n " , blocksize ) ;
2010-08-15 15:16:11 +04:00
if ( swp )
2009-04-07 06:01:59 +04:00
nilfs_swap_super_block ( nilfs ) ;
2010-06-28 12:49:33 +04:00
nilfs - > ns_sbwcount = 0 ;
nilfs - > ns_sbwtime = le64_to_cpu ( sbp [ 0 ] - > s_wtime ) ;
2009-04-07 06:01:59 +04:00
nilfs - > ns_prot_seq = le64_to_cpu ( sbp [ valid [ 1 ] & ! swp ] - > s_last_seq ) ;
* sbpp = sbp [ 0 ] ;
return 0 ;
}
2009-04-07 06:01:35 +04:00
/**
* init_nilfs - initialize a NILFS instance .
* @ nilfs : the_nilfs structure
* @ sb : super block
* @ data : mount options
*
* init_nilfs ( ) performs common initialization per block device ( e . g .
* reading the super block , getting disk layout information , initializing
2010-08-15 20:54:52 +04:00
* shared fields in the_nilfs ) .
2009-04-07 06:01:35 +04:00
*
* Return Value : On success , 0 is returned . On error , a negative error
* code is returned .
*/
2011-03-09 05:05:08 +03:00
int init_nilfs ( struct the_nilfs * nilfs , struct super_block * sb , char * data )
2009-04-07 06:01:35 +04:00
{
struct nilfs_super_block * sbp ;
int blocksize ;
2009-04-07 06:01:59 +04:00
int err ;
2009-04-07 06:01:35 +04:00
down_write ( & nilfs - > ns_sem ) ;
2010-07-25 17:44:53 +04:00
blocksize = sb_min_blocksize ( sb , NILFS_MIN_BLOCK_SIZE ) ;
2009-04-07 06:01:59 +04:00
if ( ! blocksize ) {
printk ( KERN_ERR " NILFS: unable to set blocksize \n " ) ;
2009-04-07 06:01:35 +04:00
err = - EINVAL ;
goto out ;
}
2009-04-07 06:01:59 +04:00
err = nilfs_load_super_block ( nilfs , sb , blocksize , & sbp ) ;
if ( err )
goto out ;
2009-04-07 06:01:35 +04:00
err = nilfs_store_magic_and_option ( sb , sbp , data ) ;
if ( err )
goto failed_sbh ;
2010-07-21 22:22:20 +04:00
err = nilfs_check_feature_compatibility ( sb , sbp ) ;
if ( err )
goto failed_sbh ;
2009-04-07 06:01:35 +04:00
blocksize = BLOCK_SIZE < < le32_to_cpu ( sbp - > s_log_block_size ) ;
2010-07-25 17:44:53 +04:00
if ( blocksize < NILFS_MIN_BLOCK_SIZE | |
blocksize > NILFS_MAX_BLOCK_SIZE ) {
printk ( KERN_ERR " NILFS: couldn't mount because of unsupported "
" filesystem blocksize %d \n " , blocksize ) ;
err = - EINVAL ;
goto failed_sbh ;
}
2009-04-07 06:01:35 +04:00
if ( sb - > s_blocksize ! = blocksize ) {
2009-05-23 01:17:49 +04:00
int hw_blocksize = bdev_logical_block_size ( sb - > s_bdev ) ;
2009-04-07 06:01:59 +04:00
if ( blocksize < hw_blocksize ) {
printk ( KERN_ERR
" NILFS: blocksize %d too small for device "
" (sector-size = %d). \n " ,
blocksize , hw_blocksize ) ;
2009-04-07 06:01:35 +04:00
err = - EINVAL ;
2009-04-07 06:01:59 +04:00
goto failed_sbh ;
}
nilfs_release_super_block ( nilfs ) ;
sb_set_blocksize ( sb , blocksize ) ;
err = nilfs_load_super_block ( nilfs , sb , blocksize , & sbp ) ;
if ( err )
2009-04-07 06:01:35 +04:00
goto out ;
/* not failed_sbh; sbh is released automatically
when reloading fails . */
}
nilfs - > ns_blocksize_bits = sb - > s_blocksize_bits ;
2010-05-22 19:17:48 +04:00
nilfs - > ns_blocksize = blocksize ;
2009-04-07 06:01:35 +04:00
2011-03-09 05:05:08 +03:00
get_random_bytes ( & nilfs - > ns_next_generation ,
sizeof ( nilfs - > ns_next_generation ) ) ;
2009-04-07 06:01:59 +04:00
err = nilfs_store_disk_layout ( nilfs , sbp ) ;
2009-04-07 06:01:35 +04:00
if ( err )
goto failed_sbh ;
sb - > s_maxbytes = nilfs_max_size ( sb - > s_blocksize_bits ) ;
nilfs - > ns_mount_state = le16_to_cpu ( sbp - > s_state ) ;
2010-06-28 14:15:24 +04:00
err = nilfs_store_log_cursor ( nilfs , sbp ) ;
if ( err )
2009-04-07 06:01:35 +04:00
goto failed_sbh ;
set_nilfs_init ( nilfs ) ;
err = 0 ;
out :
up_write ( & nilfs - > ns_sem ) ;
return err ;
failed_sbh :
2009-04-07 06:01:59 +04:00
nilfs_release_super_block ( nilfs ) ;
2009-04-07 06:01:35 +04:00
goto out ;
}
2010-01-30 12:06:35 +03:00
int nilfs_discard_segments ( struct the_nilfs * nilfs , __u64 * segnump ,
size_t nsegs )
{
sector_t seg_start , seg_end ;
sector_t start = 0 , nblocks = 0 ;
unsigned int sects_per_block ;
__u64 * sn ;
int ret = 0 ;
sects_per_block = ( 1 < < nilfs - > ns_blocksize_bits ) /
bdev_logical_block_size ( nilfs - > ns_bdev ) ;
for ( sn = segnump ; sn < segnump + nsegs ; sn + + ) {
nilfs_get_segment_range ( nilfs , * sn , & seg_start , & seg_end ) ;
if ( ! nblocks ) {
start = seg_start ;
nblocks = seg_end - seg_start + 1 ;
} else if ( start + nblocks = = seg_start ) {
nblocks + = seg_end - seg_start + 1 ;
} else {
ret = blkdev_issue_discard ( nilfs - > ns_bdev ,
start * sects_per_block ,
nblocks * sects_per_block ,
2010-09-16 22:51:46 +04:00
GFP_NOFS , 0 ) ;
2010-01-30 12:06:35 +03:00
if ( ret < 0 )
return ret ;
nblocks = 0 ;
}
}
if ( nblocks )
ret = blkdev_issue_discard ( nilfs - > ns_bdev ,
start * sects_per_block ,
nblocks * sects_per_block ,
2010-09-16 22:51:46 +04:00
GFP_NOFS , 0 ) ;
2010-01-30 12:06:35 +03:00
return ret ;
}
2009-04-07 06:01:35 +04:00
int nilfs_count_free_blocks ( struct the_nilfs * nilfs , sector_t * nblocks )
{
unsigned long ncleansegs ;
2010-12-26 18:07:30 +03:00
down_read ( & NILFS_MDT ( nilfs - > ns_dat ) - > mi_sem ) ;
2009-11-13 02:45:32 +03:00
ncleansegs = nilfs_sufile_get_ncleansegs ( nilfs - > ns_sufile ) ;
2010-12-26 18:07:30 +03:00
up_read ( & NILFS_MDT ( nilfs - > ns_dat ) - > mi_sem ) ;
2009-11-13 02:45:32 +03:00
* nblocks = ( sector_t ) ncleansegs * nilfs - > ns_blocks_per_segment ;
return 0 ;
2009-04-07 06:01:35 +04:00
}
int nilfs_near_disk_full ( struct the_nilfs * nilfs )
{
unsigned long ncleansegs , nincsegs ;
2009-11-13 02:45:32 +03:00
ncleansegs = nilfs_sufile_get_ncleansegs ( nilfs - > ns_sufile ) ;
nincsegs = atomic_read ( & nilfs - > ns_ndirtyblks ) /
nilfs - > ns_blocks_per_segment + 1 ;
return ncleansegs < = nilfs - > ns_nrsvsegs + nincsegs ;
2009-04-07 06:01:35 +04:00
}
2010-08-14 07:59:15 +04:00
struct nilfs_root * nilfs_lookup_root ( struct the_nilfs * nilfs , __u64 cno )
2009-06-07 20:39:31 +04:00
{
2010-08-14 07:59:15 +04:00
struct rb_node * n ;
struct nilfs_root * root ;
spin_lock ( & nilfs - > ns_cptree_lock ) ;
n = nilfs - > ns_cptree . rb_node ;
while ( n ) {
root = rb_entry ( n , struct nilfs_root , rb_node ) ;
if ( cno < root - > cno ) {
n = n - > rb_left ;
} else if ( cno > root - > cno ) {
n = n - > rb_right ;
} else {
atomic_inc ( & root - > count ) ;
spin_unlock ( & nilfs - > ns_cptree_lock ) ;
return root ;
}
2009-06-07 20:39:31 +04:00
}
2010-08-14 07:59:15 +04:00
spin_unlock ( & nilfs - > ns_cptree_lock ) ;
2009-06-07 20:39:31 +04:00
return NULL ;
}
2010-08-14 07:59:15 +04:00
struct nilfs_root *
nilfs_find_or_create_root ( struct the_nilfs * nilfs , __u64 cno )
2009-04-07 06:01:35 +04:00
{
2010-08-14 07:59:15 +04:00
struct rb_node * * p , * parent ;
struct nilfs_root * root , * new ;
2009-04-07 06:01:35 +04:00
2010-08-14 07:59:15 +04:00
root = nilfs_lookup_root ( nilfs , cno ) ;
if ( root )
return root ;
2009-04-07 06:01:35 +04:00
2010-08-14 07:59:15 +04:00
new = kmalloc ( sizeof ( * root ) , GFP_KERNEL ) ;
if ( ! new )
return NULL ;
spin_lock ( & nilfs - > ns_cptree_lock ) ;
p = & nilfs - > ns_cptree . rb_node ;
parent = NULL ;
while ( * p ) {
parent = * p ;
root = rb_entry ( parent , struct nilfs_root , rb_node ) ;
if ( cno < root - > cno ) {
p = & ( * p ) - > rb_left ;
} else if ( cno > root - > cno ) {
p = & ( * p ) - > rb_right ;
} else {
atomic_inc ( & root - > count ) ;
spin_unlock ( & nilfs - > ns_cptree_lock ) ;
kfree ( new ) ;
return root ;
2009-04-07 06:01:35 +04:00
}
}
2010-08-14 07:59:15 +04:00
new - > cno = cno ;
new - > ifile = NULL ;
new - > nilfs = nilfs ;
atomic_set ( & new - > count , 1 ) ;
2013-07-04 02:08:06 +04:00
atomic64_set ( & new - > inodes_count , 0 ) ;
atomic64_set ( & new - > blocks_count , 0 ) ;
2010-08-14 07:59:15 +04:00
rb_link_node ( & new - > rb_node , parent , p ) ;
rb_insert_color ( & new - > rb_node , & nilfs - > ns_cptree ) ;
spin_unlock ( & nilfs - > ns_cptree_lock ) ;
return new ;
}
void nilfs_put_root ( struct nilfs_root * root )
{
if ( atomic_dec_and_test ( & root - > count ) ) {
struct the_nilfs * nilfs = root - > nilfs ;
spin_lock ( & nilfs - > ns_cptree_lock ) ;
rb_erase ( & root - > rb_node , & nilfs - > ns_cptree ) ;
spin_unlock ( & nilfs - > ns_cptree_lock ) ;
if ( root - > ifile )
2010-09-05 07:20:59 +04:00
iput ( root - > ifile ) ;
2010-08-14 07:59:15 +04:00
kfree ( root ) ;
}
2009-04-07 06:01:35 +04:00
}