2009-04-07 06:01:35 +04:00
/*
* super . c - NILFS module and super block management .
*
* 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 >
*/
/*
* linux / fs / ext2 / super . c
*
* Copyright ( C ) 1992 , 1993 , 1994 , 1995
* Remy Card ( card @ masi . ibp . fr )
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie ( Paris VI )
*
* from
*
* linux / fs / minix / inode . c
*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
*
* Big - endian to little - endian byte - swapping / bitmaps by
* David S . Miller ( davem @ caip . rutgers . edu ) , 1995
*/
# include <linux/module.h>
# include <linux/string.h>
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/blkdev.h>
# include <linux/parser.h>
# include <linux/random.h>
# include <linux/crc32.h>
# include <linux/vfs.h>
# include <linux/writeback.h>
# include <linux/kobject.h>
# include <linux/exportfs.h>
2009-06-24 15:06:34 +04:00
# include <linux/seq_file.h>
# include <linux/mount.h>
2009-04-07 06:01:35 +04:00
# include "nilfs.h"
# include "mdt.h"
# include "alloc.h"
2010-07-10 15:52:09 +04:00
# include "btree.h"
# include "btnode.h"
2009-04-07 06:01:35 +04:00
# include "page.h"
# include "cpfile.h"
# include "ifile.h"
# include "dat.h"
# include "segment.h"
# include "segbuf.h"
MODULE_AUTHOR ( " NTT Corp. " ) ;
MODULE_DESCRIPTION ( " A New Implementation of the Log-structured Filesystem "
" (NILFS) " ) ;
MODULE_LICENSE ( " GPL " ) ;
2010-04-05 20:54:11 +04:00
struct kmem_cache * nilfs_inode_cachep ;
struct kmem_cache * nilfs_transaction_cachep ;
struct kmem_cache * nilfs_segbuf_cachep ;
struct kmem_cache * nilfs_btree_path_cache ;
2009-04-07 06:01:35 +04:00
static int nilfs_remount ( struct super_block * sb , int * flags , char * data ) ;
2010-06-28 12:49:30 +04:00
static void nilfs_set_error ( struct nilfs_sb_info * sbi )
{
struct the_nilfs * nilfs = sbi - > s_nilfs ;
2010-06-28 12:49:32 +04:00
struct nilfs_super_block * * sbp ;
2010-06-28 12:49:30 +04:00
down_write ( & nilfs - > ns_sem ) ;
if ( ! ( nilfs - > ns_mount_state & NILFS_ERROR_FS ) ) {
nilfs - > ns_mount_state | = NILFS_ERROR_FS ;
2010-06-28 12:49:33 +04:00
sbp = nilfs_prepare_super ( sbi , 0 ) ;
2010-06-28 12:49:32 +04:00
if ( likely ( sbp ) ) {
sbp [ 0 ] - > s_state | = cpu_to_le16 ( NILFS_ERROR_FS ) ;
2010-06-28 12:49:33 +04:00
if ( sbp [ 1 ] )
sbp [ 1 ] - > s_state | = cpu_to_le16 ( NILFS_ERROR_FS ) ;
nilfs_commit_super ( sbi , NILFS_SB_COMMIT_ALL ) ;
2010-06-28 12:49:32 +04:00
}
2010-06-28 12:49:30 +04:00
}
up_write ( & nilfs - > ns_sem ) ;
}
2009-04-07 06:01:35 +04:00
/**
* nilfs_error ( ) - report failure condition on a filesystem
*
* nilfs_error ( ) sets an ERROR_FS flag on the superblock as well as
* reporting an error message . It should be called when NILFS detects
* incoherences or defects of meta data on disk . As for sustainable
* errors such as a single - shot I / O error , nilfs_warning ( ) or the printk ( )
* function should be used instead .
*
* The segment constructor must not call this function because it can
* kill itself .
*/
void nilfs_error ( struct super_block * sb , const char * function ,
const char * fmt , . . . )
{
struct nilfs_sb_info * sbi = NILFS_SB ( sb ) ;
va_list args ;
va_start ( args , fmt ) ;
printk ( KERN_CRIT " NILFS error (device %s): %s: " , sb - > s_id , function ) ;
vprintk ( fmt , args ) ;
printk ( " \n " ) ;
va_end ( args ) ;
if ( ! ( sb - > s_flags & MS_RDONLY ) ) {
2010-06-28 12:49:30 +04:00
nilfs_set_error ( sbi ) ;
2009-04-07 06:01:35 +04:00
if ( nilfs_test_opt ( sbi , ERRORS_RO ) ) {
printk ( KERN_CRIT " Remounting filesystem read-only \n " ) ;
sb - > s_flags | = MS_RDONLY ;
}
}
if ( nilfs_test_opt ( sbi , ERRORS_PANIC ) )
panic ( " NILFS (device %s): panic forced after error \n " ,
sb - > s_id ) ;
}
void nilfs_warning ( struct super_block * sb , const char * function ,
const char * fmt , . . . )
{
va_list args ;
va_start ( args , fmt ) ;
printk ( KERN_WARNING " NILFS warning (device %s): %s: " ,
sb - > s_id , function ) ;
vprintk ( fmt , args ) ;
printk ( " \n " ) ;
va_end ( args ) ;
}
2009-05-27 17:11:46 +04:00
struct inode * nilfs_alloc_inode_common ( struct the_nilfs * nilfs )
2009-04-07 06:01:35 +04:00
{
struct nilfs_inode_info * ii ;
ii = kmem_cache_alloc ( nilfs_inode_cachep , GFP_NOFS ) ;
if ( ! ii )
return NULL ;
ii - > i_bh = NULL ;
ii - > i_state = 0 ;
2010-08-20 16:20:29 +04:00
ii - > i_cno = 0 ;
2009-04-07 06:01:35 +04:00
ii - > vfs_inode . i_version = 1 ;
2009-05-27 17:11:46 +04:00
nilfs_btnode_cache_init ( & ii - > i_btnode_cache , nilfs - > ns_bdi ) ;
2009-04-07 06:01:35 +04:00
return & ii - > vfs_inode ;
}
2009-05-27 17:11:46 +04:00
struct inode * nilfs_alloc_inode ( struct super_block * sb )
{
return nilfs_alloc_inode_common ( NILFS_SB ( sb ) - > s_nilfs ) ;
}
2009-04-07 06:01:35 +04:00
void nilfs_destroy_inode ( struct inode * inode )
{
2010-08-20 18:46:06 +04:00
struct nilfs_mdt_info * mdi = NILFS_MDT ( inode ) ;
if ( mdi ) {
kfree ( mdi - > mi_bgl ) ; /* kfree(NULL) is safe */
kfree ( mdi ) ;
}
2009-04-07 06:01:35 +04:00
kmem_cache_free ( nilfs_inode_cachep , NILFS_I ( inode ) ) ;
}
2010-06-28 12:49:33 +04:00
static int nilfs_sync_super ( struct nilfs_sb_info * sbi , int flag )
2009-04-07 06:01:35 +04:00
{
struct the_nilfs * nilfs = sbi - > s_nilfs ;
int err ;
retry :
2009-04-07 06:01:59 +04:00
set_buffer_dirty ( nilfs - > ns_sbh [ 0 ] ) ;
2010-08-11 19:05:45 +04:00
if ( nilfs_test_opt ( sbi , BARRIER ) ) {
err = __sync_dirty_buffer ( nilfs - > ns_sbh [ 0 ] ,
WRITE_SYNC | WRITE_BARRIER ) ;
if ( err = = - EOPNOTSUPP ) {
nilfs_warning ( sbi - > s_super , __func__ ,
" barrier-based sync failed. "
" disabling barriers \n " ) ;
nilfs_clear_opt ( sbi , BARRIER ) ;
goto retry ;
}
} else {
err = sync_dirty_buffer ( nilfs - > ns_sbh [ 0 ] ) ;
2009-04-07 06:01:35 +04:00
}
2010-08-11 19:05:45 +04:00
2009-04-07 06:01:59 +04:00
if ( unlikely ( err ) ) {
2009-04-07 06:01:35 +04:00
printk ( KERN_ERR
" NILFS: unable to write superblock (err=%d) \n " , err ) ;
2009-04-07 06:01:59 +04:00
if ( err = = - EIO & & nilfs - > ns_sbh [ 1 ] ) {
2010-06-28 12:49:33 +04:00
/*
* sbp [ 0 ] points to newer log than sbp [ 1 ] ,
* so copy sbp [ 0 ] to sbp [ 1 ] to take over sbp [ 0 ] .
*/
memcpy ( nilfs - > ns_sbp [ 1 ] , nilfs - > ns_sbp [ 0 ] ,
nilfs - > ns_sbsize ) ;
2009-04-07 06:01:59 +04:00
nilfs_fall_back_super_block ( nilfs ) ;
goto retry ;
}
} else {
struct nilfs_super_block * sbp = nilfs - > ns_sbp [ 0 ] ;
2010-06-28 12:49:33 +04:00
nilfs - > ns_sbwcount + + ;
2009-04-07 06:01:59 +04:00
/*
* The latest segment becomes trailable from the position
* written in superblock .
*/
2009-04-07 06:01:35 +04:00
clear_nilfs_discontinued ( nilfs ) ;
2009-04-07 06:01:59 +04:00
/* update GC protection for recent segments */
if ( nilfs - > ns_sbh [ 1 ] ) {
2010-06-28 12:49:33 +04:00
if ( flag = = NILFS_SB_COMMIT_ALL ) {
2009-04-07 06:01:59 +04:00
set_buffer_dirty ( nilfs - > ns_sbh [ 1 ] ) ;
2010-06-28 12:49:33 +04:00
if ( sync_dirty_buffer ( nilfs - > ns_sbh [ 1 ] ) < 0 )
goto out ;
2009-04-07 06:01:59 +04:00
}
2010-06-28 12:49:33 +04:00
if ( le64_to_cpu ( nilfs - > ns_sbp [ 1 ] - > s_last_cno ) <
le64_to_cpu ( nilfs - > ns_sbp [ 0 ] - > s_last_cno ) )
sbp = nilfs - > ns_sbp [ 1 ] ;
2009-04-07 06:01:59 +04:00
}
2009-04-07 06:01:35 +04:00
2010-06-28 12:49:33 +04:00
spin_lock ( & nilfs - > ns_last_segment_lock ) ;
nilfs - > ns_prot_seq = le64_to_cpu ( sbp - > s_last_seq ) ;
spin_unlock ( & nilfs - > ns_last_segment_lock ) ;
}
out :
2009-04-07 06:01:35 +04:00
return err ;
}
2010-06-28 12:49:31 +04:00
void nilfs_set_log_cursor ( struct nilfs_super_block * sbp ,
struct the_nilfs * nilfs )
{
sector_t nfreeblocks ;
/* nilfs->ns_sem must be locked by the caller. */
nilfs_count_free_blocks ( nilfs , & nfreeblocks ) ;
sbp - > s_free_blocks_count = cpu_to_le64 ( nfreeblocks ) ;
spin_lock ( & nilfs - > ns_last_segment_lock ) ;
sbp - > s_last_seq = cpu_to_le64 ( nilfs - > ns_last_seq ) ;
sbp - > s_last_pseg = cpu_to_le64 ( nilfs - > ns_last_pseg ) ;
sbp - > s_last_cno = cpu_to_le64 ( nilfs - > ns_last_cno ) ;
spin_unlock ( & nilfs - > ns_last_segment_lock ) ;
}
2010-06-28 12:49:33 +04:00
struct nilfs_super_block * * nilfs_prepare_super ( struct nilfs_sb_info * sbi ,
int flip )
2009-04-07 06:01:35 +04:00
{
struct the_nilfs * nilfs = sbi - > s_nilfs ;
2009-04-07 06:01:59 +04:00
struct nilfs_super_block * * sbp = nilfs - > ns_sbp ;
2009-04-07 06:01:35 +04:00
2010-06-28 12:49:32 +04:00
/* nilfs->ns_sem must be locked by the caller. */
2010-05-01 05:07:07 +04:00
if ( sbp [ 0 ] - > s_magic ! = cpu_to_le16 ( NILFS_SUPER_MAGIC ) ) {
2010-06-28 12:49:32 +04:00
if ( sbp [ 1 ] & &
sbp [ 1 ] - > s_magic = = cpu_to_le16 ( NILFS_SUPER_MAGIC ) ) {
2010-06-28 12:49:33 +04:00
memcpy ( sbp [ 0 ] , sbp [ 1 ] , nilfs - > ns_sbsize ) ;
2010-06-28 12:49:32 +04:00
} else {
2009-04-07 06:01:59 +04:00
printk ( KERN_CRIT " NILFS: superblock broke on dev %s \n " ,
sbi - > s_super - > s_id ) ;
2010-06-28 12:49:32 +04:00
return NULL ;
2009-04-07 06:01:59 +04:00
}
2010-06-28 12:49:33 +04:00
} else if ( sbp [ 1 ] & &
sbp [ 1 ] - > s_magic ! = cpu_to_le16 ( NILFS_SUPER_MAGIC ) ) {
memcpy ( sbp [ 1 ] , sbp [ 0 ] , nilfs - > ns_sbsize ) ;
2009-04-07 06:01:59 +04:00
}
2010-06-28 12:49:33 +04:00
if ( flip & & sbp [ 1 ] )
nilfs_swap_super_block ( nilfs ) ;
2010-06-28 12:49:32 +04:00
return sbp ;
}
2010-06-28 12:49:33 +04:00
int nilfs_commit_super ( struct nilfs_sb_info * sbi , int flag )
2010-06-28 12:49:32 +04:00
{
struct the_nilfs * nilfs = sbi - > s_nilfs ;
struct nilfs_super_block * * sbp = nilfs - > ns_sbp ;
time_t t ;
/* nilfs->ns_sem must be locked by the caller. */
2009-04-07 06:01:59 +04:00
t = get_seconds ( ) ;
2010-06-28 12:49:33 +04:00
nilfs - > ns_sbwtime = t ;
2009-04-07 06:01:59 +04:00
sbp [ 0 ] - > s_wtime = cpu_to_le64 ( t ) ;
sbp [ 0 ] - > s_sum = 0 ;
sbp [ 0 ] - > s_sum = cpu_to_le32 ( crc32_le ( nilfs - > ns_crc_seed ,
( unsigned char * ) sbp [ 0 ] ,
nilfs - > ns_sbsize ) ) ;
2010-06-28 12:49:33 +04:00
if ( flag = = NILFS_SB_COMMIT_ALL & & sbp [ 1 ] ) {
sbp [ 1 ] - > s_wtime = sbp [ 0 ] - > s_wtime ;
sbp [ 1 ] - > s_sum = 0 ;
sbp [ 1 ] - > s_sum = cpu_to_le32 ( crc32_le ( nilfs - > ns_crc_seed ,
( unsigned char * ) sbp [ 1 ] ,
nilfs - > ns_sbsize ) ) ;
2009-04-07 06:01:59 +04:00
}
2009-12-08 18:57:52 +03:00
clear_nilfs_sb_dirty ( nilfs ) ;
2010-06-28 12:49:33 +04:00
return nilfs_sync_super ( sbi , flag ) ;
2009-04-07 06:01:35 +04:00
}
2010-06-28 12:49:29 +04:00
/**
* nilfs_cleanup_super ( ) - write filesystem state for cleanup
* @ sbi : nilfs_sb_info to be unmounted or degraded to read - only
*
* This function restores state flags in the on - disk super block .
* This will set " clean " flag ( i . e . NILFS_VALID_FS ) unless the
* filesystem was not clean previously .
*/
int nilfs_cleanup_super ( struct nilfs_sb_info * sbi )
{
2010-06-28 12:49:32 +04:00
struct nilfs_super_block * * sbp ;
2010-06-28 12:49:33 +04:00
int flag = NILFS_SB_COMMIT ;
2010-06-28 12:49:32 +04:00
int ret = - EIO ;
2010-06-28 12:49:29 +04:00
2010-06-28 12:49:33 +04:00
sbp = nilfs_prepare_super ( sbi , 0 ) ;
2010-06-28 12:49:32 +04:00
if ( sbp ) {
sbp [ 0 ] - > s_state = cpu_to_le16 ( sbi - > s_nilfs - > ns_mount_state ) ;
2010-06-28 12:49:33 +04:00
nilfs_set_log_cursor ( sbp [ 0 ] , sbi - > s_nilfs ) ;
if ( sbp [ 1 ] & & sbp [ 0 ] - > s_last_cno = = sbp [ 1 ] - > s_last_cno ) {
/*
* make the " clean " flag also to the opposite
* super block if both super blocks point to
* the same checkpoint .
*/
sbp [ 1 ] - > s_state = sbp [ 0 ] - > s_state ;
flag = NILFS_SB_COMMIT_ALL ;
}
ret = nilfs_commit_super ( sbi , flag ) ;
2010-06-28 12:49:32 +04:00
}
2010-06-28 12:49:29 +04:00
return ret ;
}
2009-04-07 06:01:35 +04:00
static void nilfs_put_super ( struct super_block * sb )
{
struct nilfs_sb_info * sbi = NILFS_SB ( sb ) ;
struct the_nilfs * nilfs = sbi - > s_nilfs ;
nilfs_detach_segment_constructor ( sbi ) ;
if ( ! ( sb - > s_flags & MS_RDONLY ) ) {
down_write ( & nilfs - > ns_sem ) ;
2010-06-28 12:49:29 +04:00
nilfs_cleanup_super ( sbi ) ;
2009-04-07 06:01:35 +04:00
up_write ( & nilfs - > ns_sem ) ;
}
2009-06-07 20:39:32 +04:00
down_write ( & nilfs - > ns_super_sem ) ;
2009-06-07 20:39:30 +04:00
if ( nilfs - > ns_current = = sbi )
nilfs - > ns_current = NULL ;
2009-06-07 20:39:32 +04:00
up_write ( & nilfs - > ns_super_sem ) ;
2009-04-07 06:01:35 +04:00
nilfs_detach_checkpoint ( sbi ) ;
put_nilfs ( sbi - > s_nilfs ) ;
sbi - > s_super = NULL ;
sb - > s_fs_info = NULL ;
2009-06-07 20:39:31 +04:00
nilfs_put_sbinfo ( sbi ) ;
2009-04-07 06:01:35 +04:00
}
static int nilfs_sync_fs ( struct super_block * sb , int wait )
{
2009-07-22 20:26:33 +04:00
struct nilfs_sb_info * sbi = NILFS_SB ( sb ) ;
struct the_nilfs * nilfs = sbi - > s_nilfs ;
2010-06-28 12:49:32 +04:00
struct nilfs_super_block * * sbp ;
2009-04-07 06:01:35 +04:00
int err = 0 ;
/* This function is called when super block should be written back */
if ( wait )
err = nilfs_construct_segment ( sb ) ;
2009-07-22 20:26:33 +04:00
down_write ( & nilfs - > ns_sem ) ;
2010-06-28 12:49:32 +04:00
if ( nilfs_sb_dirty ( nilfs ) ) {
2010-06-28 12:49:33 +04:00
sbp = nilfs_prepare_super ( sbi , nilfs_sb_will_flip ( nilfs ) ) ;
if ( likely ( sbp ) ) {
nilfs_set_log_cursor ( sbp [ 0 ] , nilfs ) ;
nilfs_commit_super ( sbi , NILFS_SB_COMMIT ) ;
}
2010-06-28 12:49:32 +04:00
}
2009-07-22 20:26:33 +04:00
up_write ( & nilfs - > ns_sem ) ;
2009-04-07 06:01:35 +04:00
return err ;
}
2010-08-25 12:45:44 +04:00
int nilfs_attach_checkpoint ( struct nilfs_sb_info * sbi , __u64 cno , int curr_mnt ,
struct nilfs_root * * rootp )
2009-04-07 06:01:35 +04:00
{
struct the_nilfs * nilfs = sbi - > s_nilfs ;
2010-08-25 12:45:44 +04:00
struct nilfs_root * root ;
2009-04-07 06:01:35 +04:00
struct nilfs_checkpoint * raw_cp ;
struct buffer_head * bh_cp ;
2010-08-25 12:45:44 +04:00
int err = - ENOMEM ;
root = nilfs_find_or_create_root (
nilfs , curr_mnt ? NILFS_CPTREE_CURRENT_CNO : cno ) ;
if ( ! root )
return err ;
2009-04-07 06:01:35 +04:00
2009-06-07 20:39:32 +04:00
down_write ( & nilfs - > ns_super_sem ) ;
2009-04-07 06:01:35 +04:00
list_add ( & sbi - > s_list , & nilfs - > ns_supers ) ;
2009-06-07 20:39:32 +04:00
up_write ( & nilfs - > ns_super_sem ) ;
2009-04-07 06:01:35 +04:00
2009-11-12 17:56:43 +03:00
sbi - > s_ifile = nilfs_ifile_new ( sbi , nilfs - > ns_inode_size ) ;
2009-04-07 06:01:35 +04:00
if ( ! sbi - > s_ifile )
2010-08-13 07:42:24 +04:00
goto delist ;
2009-04-07 06:01:35 +04:00
2009-08-18 10:58:24 +04:00
down_read ( & nilfs - > ns_segctor_sem ) ;
2009-04-07 06:01:35 +04:00
err = nilfs_cpfile_get_checkpoint ( nilfs - > ns_cpfile , cno , 0 , & raw_cp ,
& bh_cp ) ;
2009-08-18 10:58:24 +04:00
up_read ( & nilfs - > ns_segctor_sem ) ;
2009-04-07 06:01:35 +04:00
if ( unlikely ( err ) ) {
if ( err = = - ENOENT | | err = = - EINVAL ) {
printk ( KERN_ERR
" NILFS: Invalid checkpoint "
" (checkpoint number=%llu) \n " ,
( unsigned long long ) cno ) ;
err = - EINVAL ;
}
goto failed ;
}
err = nilfs_read_inode_common ( sbi - > s_ifile , & raw_cp - > cp_ifile_inode ) ;
if ( unlikely ( err ) )
goto failed_bh ;
atomic_set ( & sbi - > s_inodes_count , le64_to_cpu ( raw_cp - > cp_inodes_count ) ) ;
atomic_set ( & sbi - > s_blocks_count , le64_to_cpu ( raw_cp - > cp_blocks_count ) ) ;
nilfs_cpfile_put_checkpoint ( nilfs - > ns_cpfile , cno , bh_cp ) ;
2010-08-25 12:45:44 +04:00
* rootp = root ;
2009-04-07 06:01:35 +04:00
return 0 ;
failed_bh :
nilfs_cpfile_put_checkpoint ( nilfs - > ns_cpfile , cno , bh_cp ) ;
failed :
nilfs_mdt_destroy ( sbi - > s_ifile ) ;
sbi - > s_ifile = NULL ;
2010-08-13 07:42:24 +04:00
delist :
2009-06-07 20:39:32 +04:00
down_write ( & nilfs - > ns_super_sem ) ;
2009-04-07 06:01:35 +04:00
list_del_init ( & sbi - > s_list ) ;
2009-06-07 20:39:32 +04:00
up_write ( & nilfs - > ns_super_sem ) ;
2010-08-25 12:45:44 +04:00
nilfs_put_root ( root ) ;
2009-04-07 06:01:35 +04:00
return err ;
}
void nilfs_detach_checkpoint ( struct nilfs_sb_info * sbi )
{
struct the_nilfs * nilfs = sbi - > s_nilfs ;
nilfs_mdt_destroy ( sbi - > s_ifile ) ;
sbi - > s_ifile = NULL ;
2009-06-07 20:39:32 +04:00
down_write ( & nilfs - > ns_super_sem ) ;
2009-04-07 06:01:35 +04:00
list_del_init ( & sbi - > s_list ) ;
2009-06-07 20:39:32 +04:00
up_write ( & nilfs - > ns_super_sem ) ;
2009-04-07 06:01:35 +04:00
}
static int nilfs_statfs ( struct dentry * dentry , struct kstatfs * buf )
{
struct super_block * sb = dentry - > d_sb ;
struct nilfs_sb_info * sbi = NILFS_SB ( sb ) ;
2009-03-26 04:16:57 +03:00
struct the_nilfs * nilfs = sbi - > s_nilfs ;
u64 id = huge_encode_dev ( sb - > s_bdev - > bd_dev ) ;
2009-04-07 06:01:35 +04:00
unsigned long long blocks ;
unsigned long overhead ;
unsigned long nrsvblocks ;
sector_t nfreeblocks ;
int err ;
/*
* Compute all of the segment blocks
*
* The blocks before first segment and after last segment
* are excluded .
*/
blocks = nilfs - > ns_blocks_per_segment * nilfs - > ns_nsegments
- nilfs - > ns_first_data_block ;
nrsvblocks = nilfs - > ns_nrsvsegs * nilfs - > ns_blocks_per_segment ;
/*
* Compute the overhead
*
2010-03-13 21:32:40 +03:00
* When distributing meta data blocks outside segment structure ,
2009-04-07 06:01:35 +04:00
* We must count them as the overhead .
*/
overhead = 0 ;
err = nilfs_count_free_blocks ( nilfs , & nfreeblocks ) ;
if ( unlikely ( err ) )
return err ;
buf - > f_type = NILFS_SUPER_MAGIC ;
buf - > f_bsize = sb - > s_blocksize ;
buf - > f_blocks = blocks - overhead ;
buf - > f_bfree = nfreeblocks ;
buf - > f_bavail = ( buf - > f_bfree > = nrsvblocks ) ?
( buf - > f_bfree - nrsvblocks ) : 0 ;
buf - > f_files = atomic_read ( & sbi - > s_inodes_count ) ;
buf - > f_ffree = 0 ; /* nilfs_count_free_inodes(sb); */
buf - > f_namelen = NILFS_NAME_LEN ;
2009-03-26 04:16:57 +03:00
buf - > f_fsid . val [ 0 ] = ( u32 ) id ;
buf - > f_fsid . val [ 1 ] = ( u32 ) ( id > > 32 ) ;
2009-04-07 06:01:35 +04:00
return 0 ;
}
2009-06-24 15:06:34 +04:00
static int nilfs_show_options ( struct seq_file * seq , struct vfsmount * vfs )
{
struct super_block * sb = vfs - > mnt_sb ;
struct nilfs_sb_info * sbi = NILFS_SB ( sb ) ;
if ( ! nilfs_test_opt ( sbi , BARRIER ) )
2010-07-05 09:40:27 +04:00
seq_puts ( seq , " ,nobarrier " ) ;
2009-06-24 15:06:34 +04:00
if ( nilfs_test_opt ( sbi , SNAPSHOT ) )
seq_printf ( seq , " ,cp=%llu " ,
( unsigned long long int ) sbi - > s_snapshot_cno ) ;
if ( nilfs_test_opt ( sbi , ERRORS_PANIC ) )
2010-07-05 09:40:27 +04:00
seq_puts ( seq , " ,errors=panic " ) ;
2010-04-02 13:02:33 +04:00
if ( nilfs_test_opt ( sbi , ERRORS_CONT ) )
2010-07-05 09:40:27 +04:00
seq_puts ( seq , " ,errors=continue " ) ;
2009-06-24 15:06:34 +04:00
if ( nilfs_test_opt ( sbi , STRICT_ORDER ) )
2010-07-05 09:40:27 +04:00
seq_puts ( seq , " ,order=strict " ) ;
2009-11-19 21:28:01 +03:00
if ( nilfs_test_opt ( sbi , NORECOVERY ) )
2010-07-05 09:40:27 +04:00
seq_puts ( seq , " ,norecovery " ) ;
2010-01-30 12:06:35 +03:00
if ( nilfs_test_opt ( sbi , DISCARD ) )
2010-07-05 09:40:27 +04:00
seq_puts ( seq , " ,discard " ) ;
2009-06-24 15:06:34 +04:00
return 0 ;
}
2009-09-22 04:01:09 +04:00
static const struct super_operations nilfs_sops = {
2009-04-07 06:01:35 +04:00
. alloc_inode = nilfs_alloc_inode ,
. destroy_inode = nilfs_destroy_inode ,
. dirty_inode = nilfs_dirty_inode ,
/* .write_inode = nilfs_write_inode, */
/* .put_inode = nilfs_put_inode, */
/* .drop_inode = nilfs_drop_inode, */
2010-06-07 19:55:00 +04:00
. evict_inode = nilfs_evict_inode ,
2009-04-07 06:01:35 +04:00
. put_super = nilfs_put_super ,
2009-07-22 20:33:49 +04:00
/* .write_super = nilfs_write_super, */
2009-04-07 06:01:35 +04:00
. sync_fs = nilfs_sync_fs ,
/* .write_super_lockfs */
/* .unlockfs */
. statfs = nilfs_statfs ,
. remount_fs = nilfs_remount ,
/* .umount_begin */
2009-06-24 15:06:34 +04:00
. show_options = nilfs_show_options
2009-04-07 06:01:35 +04:00
} ;
static struct inode *
nilfs_nfs_get_inode ( struct super_block * sb , u64 ino , u32 generation )
{
struct inode * inode ;
2010-08-25 12:45:44 +04:00
struct nilfs_root * root ;
2009-04-07 06:01:35 +04:00
if ( ino < NILFS_FIRST_INO ( sb ) & & ino ! = NILFS_ROOT_INO & &
ino ! = NILFS_SKETCH_INO )
return ERR_PTR ( - ESTALE ) ;
2010-08-25 12:45:44 +04:00
root = nilfs_lookup_root ( NILFS_SB ( sb ) - > s_nilfs ,
NILFS_CPTREE_CURRENT_CNO ) ;
if ( ! root )
return ERR_PTR ( - ESTALE ) ;
/* new file handle type is required to export snapshots */
inode = nilfs_iget ( sb , root , ino ) ;
nilfs_put_root ( root ) ;
2009-04-07 06:01:35 +04:00
if ( IS_ERR ( inode ) )
return ERR_CAST ( inode ) ;
if ( generation & & inode - > i_generation ! = generation ) {
iput ( inode ) ;
return ERR_PTR ( - ESTALE ) ;
}
return inode ;
}
static struct dentry *
nilfs_fh_to_dentry ( struct super_block * sb , struct fid * fid , int fh_len ,
int fh_type )
{
return generic_fh_to_dentry ( sb , fid , fh_len , fh_type ,
nilfs_nfs_get_inode ) ;
}
static struct dentry *
nilfs_fh_to_parent ( struct super_block * sb , struct fid * fid , int fh_len ,
int fh_type )
{
return generic_fh_to_parent ( sb , fid , fh_len , fh_type ,
nilfs_nfs_get_inode ) ;
}
2009-09-22 04:01:10 +04:00
static const struct export_operations nilfs_export_ops = {
2009-04-07 06:01:35 +04:00
. fh_to_dentry = nilfs_fh_to_dentry ,
. fh_to_parent = nilfs_fh_to_parent ,
. get_parent = nilfs_get_parent ,
} ;
enum {
Opt_err_cont , Opt_err_panic , Opt_err_ro ,
2010-07-05 08:00:08 +04:00
Opt_barrier , Opt_nobarrier , Opt_snapshot , Opt_order , Opt_norecovery ,
2010-07-05 09:27:04 +04:00
Opt_discard , Opt_nodiscard , Opt_err ,
2009-04-07 06:01:35 +04:00
} ;
static match_table_t tokens = {
{ Opt_err_cont , " errors=continue " } ,
{ Opt_err_panic , " errors=panic " } ,
{ Opt_err_ro , " errors=remount-ro " } ,
2010-07-05 08:00:08 +04:00
{ Opt_barrier , " barrier " } ,
2009-11-12 08:07:26 +03:00
{ Opt_nobarrier , " nobarrier " } ,
2009-04-07 06:01:35 +04:00
{ Opt_snapshot , " cp=%u " } ,
{ Opt_order , " order=%s " } ,
2009-11-19 21:28:01 +03:00
{ Opt_norecovery , " norecovery " } ,
2010-01-30 12:06:35 +03:00
{ Opt_discard , " discard " } ,
2010-07-05 09:27:04 +04:00
{ Opt_nodiscard , " nodiscard " } ,
2009-04-07 06:01:35 +04:00
{ Opt_err , NULL }
} ;
2010-07-05 15:08:33 +04:00
static int parse_options ( char * options , struct super_block * sb , int is_remount )
2009-04-07 06:01:35 +04:00
{
struct nilfs_sb_info * sbi = NILFS_SB ( sb ) ;
char * p ;
substring_t args [ MAX_OPT_ARGS ] ;
int option ;
if ( ! options )
return 1 ;
while ( ( p = strsep ( & options , " , " ) ) ! = NULL ) {
int token ;
if ( ! * p )
continue ;
token = match_token ( p , tokens , args ) ;
switch ( token ) {
2010-07-05 08:00:08 +04:00
case Opt_barrier :
nilfs_set_opt ( sbi , BARRIER ) ;
break ;
2009-11-12 08:07:26 +03:00
case Opt_nobarrier :
nilfs_clear_opt ( sbi , BARRIER ) ;
2009-04-07 06:01:35 +04:00
break ;
case Opt_order :
if ( strcmp ( args [ 0 ] . from , " relaxed " ) = = 0 )
/* Ordered data semantics */
nilfs_clear_opt ( sbi , STRICT_ORDER ) ;
else if ( strcmp ( args [ 0 ] . from , " strict " ) = = 0 )
/* Strict in-order semantics */
nilfs_set_opt ( sbi , STRICT_ORDER ) ;
else
return 0 ;
break ;
case Opt_err_panic :
nilfs_write_opt ( sbi , ERROR_MODE , ERRORS_PANIC ) ;
break ;
case Opt_err_ro :
nilfs_write_opt ( sbi , ERROR_MODE , ERRORS_RO ) ;
break ;
case Opt_err_cont :
nilfs_write_opt ( sbi , ERROR_MODE , ERRORS_CONT ) ;
break ;
case Opt_snapshot :
if ( match_int ( & args [ 0 ] , & option ) | | option < = 0 )
return 0 ;
2010-07-05 15:08:33 +04:00
if ( is_remount ) {
if ( ! nilfs_test_opt ( sbi , SNAPSHOT ) ) {
printk ( KERN_ERR
" NILFS: cannot change regular "
" mount to snapshot. \n " ) ;
return 0 ;
} else if ( option ! = sbi - > s_snapshot_cno ) {
printk ( KERN_ERR
" NILFS: cannot remount to a "
" different snapshot. \n " ) ;
return 0 ;
}
break ;
}
if ( ! ( sb - > s_flags & MS_RDONLY ) ) {
printk ( KERN_ERR " NILFS: cannot mount snapshot "
" read/write. A read-only option is "
" required. \n " ) ;
2009-04-07 06:01:35 +04:00
return 0 ;
2010-07-05 15:08:33 +04:00
}
2009-04-07 06:01:35 +04:00
sbi - > s_snapshot_cno = option ;
nilfs_set_opt ( sbi , SNAPSHOT ) ;
break ;
2009-11-19 21:28:01 +03:00
case Opt_norecovery :
nilfs_set_opt ( sbi , NORECOVERY ) ;
break ;
2010-01-30 12:06:35 +03:00
case Opt_discard :
nilfs_set_opt ( sbi , DISCARD ) ;
break ;
2010-07-05 09:27:04 +04:00
case Opt_nodiscard :
nilfs_clear_opt ( sbi , DISCARD ) ;
break ;
2009-04-07 06:01:35 +04:00
default :
printk ( KERN_ERR
" NILFS: Unrecognized mount option \" %s \" \n " , p ) ;
return 0 ;
}
}
return 1 ;
}
static inline void
nilfs_set_default_options ( struct nilfs_sb_info * sbi ,
struct nilfs_super_block * sbp )
{
sbi - > s_mount_opt =
2010-04-02 13:02:33 +04:00
NILFS_MOUNT_ERRORS_RO | NILFS_MOUNT_BARRIER ;
2009-04-07 06:01:35 +04:00
}
static int nilfs_setup_super ( struct nilfs_sb_info * sbi )
{
struct the_nilfs * nilfs = sbi - > s_nilfs ;
2010-06-28 12:49:32 +04:00
struct nilfs_super_block * * sbp ;
int max_mnt_count ;
int mnt_count ;
/* nilfs->ns_sem must be locked by the caller. */
2010-06-28 12:49:33 +04:00
sbp = nilfs_prepare_super ( sbi , 0 ) ;
2010-06-28 12:49:32 +04:00
if ( ! sbp )
return - EIO ;
max_mnt_count = le16_to_cpu ( sbp [ 0 ] - > s_max_mnt_count ) ;
mnt_count = le16_to_cpu ( sbp [ 0 ] - > s_mnt_count ) ;
2009-04-07 06:01:35 +04:00
2009-11-19 10:58:40 +03:00
if ( nilfs - > ns_mount_state & NILFS_ERROR_FS ) {
2009-04-07 06:01:35 +04:00
printk ( KERN_WARNING
" NILFS warning: mounting fs with errors \n " ) ;
#if 0
} else if ( max_mnt_count > = 0 & & mnt_count > = max_mnt_count ) {
printk ( KERN_WARNING
" NILFS warning: maximal mount count reached \n " ) ;
# endif
}
if ( ! max_mnt_count )
2010-06-28 12:49:32 +04:00
sbp [ 0 ] - > s_max_mnt_count = cpu_to_le16 ( NILFS_DFL_MAX_MNT_COUNT ) ;
2009-04-07 06:01:35 +04:00
2010-06-28 12:49:32 +04:00
sbp [ 0 ] - > s_mnt_count = cpu_to_le16 ( mnt_count + 1 ) ;
sbp [ 0 ] - > s_state =
cpu_to_le16 ( le16_to_cpu ( sbp [ 0 ] - > s_state ) & ~ NILFS_VALID_FS ) ;
sbp [ 0 ] - > s_mtime = cpu_to_le64 ( get_seconds ( ) ) ;
2010-06-28 12:49:33 +04:00
/* synchronize sbp[1] with sbp[0] */
memcpy ( sbp [ 1 ] , sbp [ 0 ] , nilfs - > ns_sbsize ) ;
return nilfs_commit_super ( sbi , NILFS_SB_COMMIT_ALL ) ;
2009-04-07 06:01:35 +04:00
}
2009-04-07 06:01:59 +04:00
struct nilfs_super_block * nilfs_read_super_block ( struct super_block * sb ,
u64 pos , int blocksize ,
struct buffer_head * * pbh )
2009-04-07 06:01:35 +04:00
{
2009-04-07 06:01:59 +04:00
unsigned long long sb_index = pos ;
unsigned long offset ;
2009-04-07 06:01:35 +04:00
2009-04-07 06:01:59 +04:00
offset = do_div ( sb_index , blocksize ) ;
2009-04-07 06:01:35 +04:00
* pbh = sb_bread ( sb , sb_index ) ;
2009-04-07 06:01:59 +04:00
if ( ! * pbh )
2009-04-07 06:01:35 +04:00
return NULL ;
return ( struct nilfs_super_block * ) ( ( char * ) ( * pbh ) - > b_data + offset ) ;
}
int nilfs_store_magic_and_option ( struct super_block * sb ,
struct nilfs_super_block * sbp ,
char * data )
{
struct nilfs_sb_info * sbi = NILFS_SB ( sb ) ;
sb - > s_magic = le16_to_cpu ( sbp - > s_magic ) ;
/* FS independent flags */
# ifdef NILFS_ATIME_DISABLE
sb - > s_flags | = MS_NOATIME ;
# endif
nilfs_set_default_options ( sbi , sbp ) ;
sbi - > s_resuid = le16_to_cpu ( sbp - > s_def_resuid ) ;
sbi - > s_resgid = le16_to_cpu ( sbp - > s_def_resgid ) ;
sbi - > s_interval = le32_to_cpu ( sbp - > s_c_interval ) ;
sbi - > s_watermark = le32_to_cpu ( sbp - > s_c_block_max ) ;
2010-07-05 15:08:33 +04:00
return ! parse_options ( data , sb , 0 ) ? - EINVAL : 0 ;
2009-04-07 06:01:35 +04:00
}
2010-07-21 22:22:20 +04:00
int nilfs_check_feature_compatibility ( struct super_block * sb ,
struct nilfs_super_block * sbp )
{
__u64 features ;
features = le64_to_cpu ( sbp - > s_feature_incompat ) &
~ NILFS_FEATURE_INCOMPAT_SUPP ;
if ( features ) {
printk ( KERN_ERR " NILFS: couldn't mount because of unsupported "
" optional features (%llx) \n " ,
( unsigned long long ) features ) ;
return - EINVAL ;
}
features = le64_to_cpu ( sbp - > s_feature_compat_ro ) &
~ NILFS_FEATURE_COMPAT_RO_SUPP ;
if ( ! ( sb - > s_flags & MS_RDONLY ) & & features ) {
printk ( KERN_ERR " NILFS: couldn't mount RDWR because of "
" unsupported optional features (%llx) \n " ,
( unsigned long long ) features ) ;
return - EINVAL ;
}
return 0 ;
}
2009-04-07 06:01:35 +04:00
/**
* nilfs_fill_super ( ) - initialize a super block instance
* @ sb : super_block
* @ data : mount options
* @ silent : silent mode flag
* @ nilfs : the_nilfs struct
*
2009-06-07 20:39:33 +04:00
* This function is called exclusively by nilfs - > ns_mount_mutex .
2009-04-07 06:01:35 +04:00
* So , the recovery process is protected from other simultaneous mounts .
*/
static int
nilfs_fill_super ( struct super_block * sb , void * data , int silent ,
struct the_nilfs * nilfs )
{
struct nilfs_sb_info * sbi ;
2010-08-25 12:45:44 +04:00
struct nilfs_root * fsroot ;
2009-04-07 06:01:35 +04:00
struct inode * root ;
__u64 cno ;
2010-08-25 12:45:44 +04:00
int err , curr_mnt ;
2009-04-07 06:01:35 +04:00
sbi = kzalloc ( sizeof ( * sbi ) , GFP_KERNEL ) ;
if ( ! sbi )
return - ENOMEM ;
sb - > s_fs_info = sbi ;
get_nilfs ( nilfs ) ;
sbi - > s_nilfs = nilfs ;
sbi - > s_super = sb ;
2009-06-07 20:39:31 +04:00
atomic_set ( & sbi - > s_count , 1 ) ;
2009-04-07 06:01:35 +04:00
err = init_nilfs ( nilfs , sbi , ( char * ) data ) ;
if ( err )
goto failed_sbi ;
spin_lock_init ( & sbi - > s_inode_lock ) ;
INIT_LIST_HEAD ( & sbi - > s_dirty_files ) ;
INIT_LIST_HEAD ( & sbi - > s_list ) ;
/*
* Following initialization is overlapped because
* nilfs_sb_info structure has been cleared at the beginning .
* But we reserve them to keep our interest and make ready
* for the future change .
*/
get_random_bytes ( & sbi - > s_next_generation ,
sizeof ( sbi - > s_next_generation ) ) ;
spin_lock_init ( & sbi - > s_next_gen_lock ) ;
sb - > s_op = & nilfs_sops ;
sb - > s_export_op = & nilfs_export_ops ;
sb - > s_root = NULL ;
2009-04-07 06:02:00 +04:00
sb - > s_time_gran = 1 ;
2010-05-03 16:00:48 +04:00
sb - > s_bdi = nilfs - > ns_bdi ;
2009-04-07 06:01:35 +04:00
2009-11-19 10:58:40 +03:00
err = load_nilfs ( nilfs , sbi ) ;
if ( err )
goto failed_sbi ;
2009-04-07 06:01:35 +04:00
cno = nilfs_last_cno ( nilfs ) ;
2010-08-25 12:45:44 +04:00
curr_mnt = true ;
2009-04-07 06:01:35 +04:00
if ( sb - > s_flags & MS_RDONLY ) {
if ( nilfs_test_opt ( sbi , SNAPSHOT ) ) {
2009-08-12 10:17:59 +04:00
down_read ( & nilfs - > ns_segctor_sem ) ;
2009-04-07 06:01:55 +04:00
err = nilfs_cpfile_is_snapshot ( nilfs - > ns_cpfile ,
sbi - > s_snapshot_cno ) ;
2009-08-12 10:17:59 +04:00
up_read ( & nilfs - > ns_segctor_sem ) ;
if ( err < 0 ) {
if ( err = = - ENOENT )
err = - EINVAL ;
2009-04-07 06:01:55 +04:00
goto failed_sbi ;
2009-08-12 10:17:59 +04:00
}
2009-04-07 06:01:55 +04:00
if ( ! err ) {
2009-04-07 06:01:35 +04:00
printk ( KERN_ERR
" NILFS: The specified checkpoint is "
" not a snapshot "
" (checkpoint number=%llu). \n " ,
( unsigned long long ) sbi - > s_snapshot_cno ) ;
err = - EINVAL ;
goto failed_sbi ;
}
cno = sbi - > s_snapshot_cno ;
2010-08-25 12:45:44 +04:00
curr_mnt = false ;
2010-05-09 16:51:53 +04:00
}
2009-04-07 06:01:35 +04:00
}
2010-08-25 12:45:44 +04:00
err = nilfs_attach_checkpoint ( sbi , cno , curr_mnt , & fsroot ) ;
2009-04-07 06:01:35 +04:00
if ( err ) {
printk ( KERN_ERR " NILFS: error loading a checkpoint "
" (checkpoint number=%llu). \n " , ( unsigned long long ) cno ) ;
goto failed_sbi ;
}
if ( ! ( sb - > s_flags & MS_RDONLY ) ) {
2009-04-07 06:01:58 +04:00
err = nilfs_attach_segment_constructor ( sbi ) ;
2009-04-07 06:01:35 +04:00
if ( err )
goto failed_checkpoint ;
}
2010-08-25 12:45:44 +04:00
root = nilfs_iget ( sb , fsroot , NILFS_ROOT_INO ) ;
2009-04-07 06:01:35 +04:00
if ( IS_ERR ( root ) ) {
printk ( KERN_ERR " NILFS: get root inode failed \n " ) ;
err = PTR_ERR ( root ) ;
goto failed_segctor ;
}
if ( ! S_ISDIR ( root - > i_mode ) | | ! root - > i_blocks | | ! root - > i_size ) {
iput ( root ) ;
printk ( KERN_ERR " NILFS: corrupt root inode. \n " ) ;
err = - EINVAL ;
goto failed_segctor ;
}
sb - > s_root = d_alloc_root ( root ) ;
if ( ! sb - > s_root ) {
iput ( root ) ;
printk ( KERN_ERR " NILFS: get root dentry failed \n " ) ;
err = - ENOMEM ;
goto failed_segctor ;
}
2010-08-25 12:45:44 +04:00
nilfs_put_root ( fsroot ) ;
2009-04-07 06:01:35 +04:00
if ( ! ( sb - > s_flags & MS_RDONLY ) ) {
down_write ( & nilfs - > ns_sem ) ;
nilfs_setup_super ( sbi ) ;
up_write ( & nilfs - > ns_sem ) ;
}
2009-06-07 20:39:32 +04:00
down_write ( & nilfs - > ns_super_sem ) ;
2009-06-07 20:39:30 +04:00
if ( ! nilfs_test_opt ( sbi , SNAPSHOT ) )
nilfs - > ns_current = sbi ;
2009-06-07 20:39:32 +04:00
up_write ( & nilfs - > ns_super_sem ) ;
2009-06-07 20:39:30 +04:00
2009-04-07 06:01:35 +04:00
return 0 ;
failed_segctor :
nilfs_detach_segment_constructor ( sbi ) ;
failed_checkpoint :
nilfs_detach_checkpoint ( sbi ) ;
2010-08-25 12:45:44 +04:00
nilfs_put_root ( fsroot ) ;
2009-04-07 06:01:35 +04:00
failed_sbi :
put_nilfs ( nilfs ) ;
sb - > s_fs_info = NULL ;
2009-06-07 20:39:31 +04:00
nilfs_put_sbinfo ( sbi ) ;
2009-04-07 06:01:35 +04:00
return err ;
}
static int nilfs_remount ( struct super_block * sb , int * flags , char * data )
{
struct nilfs_sb_info * sbi = NILFS_SB ( sb ) ;
struct the_nilfs * nilfs = sbi - > s_nilfs ;
unsigned long old_sb_flags ;
struct nilfs_mount_options old_opts ;
2010-05-09 16:51:53 +04:00
int was_snapshot , err ;
2009-04-07 06:01:35 +04:00
2009-06-07 20:39:32 +04:00
down_write ( & nilfs - > ns_super_sem ) ;
2009-04-07 06:01:35 +04:00
old_sb_flags = sb - > s_flags ;
old_opts . mount_opt = sbi - > s_mount_opt ;
old_opts . snapshot_cno = sbi - > s_snapshot_cno ;
2010-05-09 16:51:53 +04:00
was_snapshot = nilfs_test_opt ( sbi , SNAPSHOT ) ;
2009-04-07 06:01:35 +04:00
2010-07-05 15:08:33 +04:00
if ( ! parse_options ( data , sb , 1 ) ) {
2009-04-07 06:01:35 +04:00
err = - EINVAL ;
goto restore_opts ;
}
sb - > s_flags = ( sb - > s_flags & ~ MS_POSIXACL ) ;
2010-05-09 16:51:53 +04:00
err = - EINVAL ;
2010-07-05 15:08:33 +04:00
if ( was_snapshot & & ! ( * flags & MS_RDONLY ) ) {
printk ( KERN_ERR " NILFS (device %s): cannot remount snapshot "
" read/write. \n " , sb - > s_id ) ;
goto restore_opts ;
2009-04-07 06:01:35 +04:00
}
2009-11-19 21:28:01 +03:00
if ( ! nilfs_valid_fs ( nilfs ) ) {
printk ( KERN_WARNING " NILFS (device %s): couldn't "
" remount because the filesystem is in an "
" incomplete recovery state. \n " , sb - > s_id ) ;
goto restore_opts ;
}
2009-04-07 06:01:35 +04:00
if ( ( * flags & MS_RDONLY ) = = ( sb - > s_flags & MS_RDONLY ) )
goto out ;
if ( * flags & MS_RDONLY ) {
/* Shutting down the segment constructor */
nilfs_detach_segment_constructor ( sbi ) ;
sb - > s_flags | = MS_RDONLY ;
/*
* Remounting a valid RW partition RDONLY , so set
* the RDONLY flag and then mark the partition as valid again .
*/
down_write ( & nilfs - > ns_sem ) ;
2010-06-28 12:49:29 +04:00
nilfs_cleanup_super ( sbi ) ;
2009-04-07 06:01:35 +04:00
up_write ( & nilfs - > ns_sem ) ;
} else {
2010-07-21 22:22:20 +04:00
__u64 features ;
2009-04-07 06:01:35 +04:00
/*
* Mounting a RDONLY partition read - write , so reread and
* store the current valid flag . ( It may have been changed
* by fsck since we originally mounted the partition . )
*/
2010-07-21 22:22:20 +04:00
down_read ( & nilfs - > ns_sem ) ;
features = le64_to_cpu ( nilfs - > ns_sbp [ 0 ] - > s_feature_compat_ro ) &
~ NILFS_FEATURE_COMPAT_RO_SUPP ;
up_read ( & nilfs - > ns_sem ) ;
if ( features ) {
printk ( KERN_WARNING " NILFS (device %s): couldn't "
" remount RDWR because of unsupported optional "
" features (%llx) \n " ,
sb - > s_id , ( unsigned long long ) features ) ;
err = - EROFS ;
goto restore_opts ;
}
2009-04-07 06:01:35 +04:00
sb - > s_flags & = ~ MS_RDONLY ;
2009-04-07 06:01:58 +04:00
err = nilfs_attach_segment_constructor ( sbi ) ;
2009-04-07 06:01:35 +04:00
if ( err )
2009-06-07 20:39:32 +04:00
goto restore_opts ;
2009-04-07 06:01:35 +04:00
down_write ( & nilfs - > ns_sem ) ;
nilfs_setup_super ( sbi ) ;
up_write ( & nilfs - > ns_sem ) ;
}
out :
2009-06-07 20:39:32 +04:00
up_write ( & nilfs - > ns_super_sem ) ;
2009-04-07 06:01:35 +04:00
return 0 ;
restore_opts :
sb - > s_flags = old_sb_flags ;
sbi - > s_mount_opt = old_opts . mount_opt ;
sbi - > s_snapshot_cno = old_opts . snapshot_cno ;
2009-06-07 20:39:32 +04:00
up_write ( & nilfs - > ns_super_sem ) ;
2009-04-07 06:01:35 +04:00
return err ;
}
struct nilfs_super_data {
struct block_device * bdev ;
2009-06-07 20:39:31 +04:00
struct nilfs_sb_info * sbi ;
2009-04-07 06:01:35 +04:00
__u64 cno ;
int flags ;
} ;
/**
* nilfs_identify - pre - read mount options needed to identify mount instance
* @ data : mount options
* @ sd : nilfs_super_data
*/
static int nilfs_identify ( char * data , struct nilfs_super_data * sd )
{
char * p , * options = data ;
substring_t args [ MAX_OPT_ARGS ] ;
int option , token ;
int ret = 0 ;
do {
p = strsep ( & options , " , " ) ;
if ( p ! = NULL & & * p ) {
token = match_token ( p , tokens , args ) ;
if ( token = = Opt_snapshot ) {
if ( ! ( sd - > flags & MS_RDONLY ) )
ret + + ;
else {
ret = match_int ( & args [ 0 ] , & option ) ;
if ( ! ret ) {
if ( option > 0 )
sd - > cno = option ;
else
ret + + ;
}
}
}
if ( ret )
printk ( KERN_ERR
" NILFS: invalid mount option: %s \n " , p ) ;
}
if ( ! options )
break ;
BUG_ON ( options = = data ) ;
* ( options - 1 ) = ' , ' ;
} while ( ! ret ) ;
return ret ;
}
static int nilfs_set_bdev_super ( struct super_block * s , void * data )
{
struct nilfs_super_data * sd = data ;
s - > s_bdev = sd - > bdev ;
s - > s_dev = s - > s_bdev - > bd_dev ;
return 0 ;
}
static int nilfs_test_bdev_super ( struct super_block * s , void * data )
{
struct nilfs_super_data * sd = data ;
2009-06-07 20:39:31 +04:00
return sd - > sbi & & s - > s_fs_info = = ( void * ) sd - > sbi ;
2009-04-07 06:01:35 +04:00
}
static int
nilfs_get_sb ( struct file_system_type * fs_type , int flags ,
const char * dev_name , void * data , struct vfsmount * mnt )
{
struct nilfs_super_data sd ;
2009-06-07 20:39:29 +04:00
struct super_block * s ;
2010-05-08 21:57:57 +04:00
fmode_t mode = FMODE_READ ;
2009-06-07 20:39:29 +04:00
struct the_nilfs * nilfs ;
2009-04-07 06:01:35 +04:00
int err , need_to_close = 1 ;
2010-05-08 21:57:57 +04:00
if ( ! ( flags & MS_RDONLY ) )
mode | = FMODE_WRITE ;
sd . bdev = open_bdev_exclusive ( dev_name , mode , fs_type ) ;
2010-02-24 15:25:32 +03:00
if ( IS_ERR ( sd . bdev ) )
2009-04-07 06:01:35 +04:00
return PTR_ERR ( sd . bdev ) ;
/*
* To get mount instance using sget ( ) vfs - routine , NILFS needs
* much more information than normal filesystems to identify mount
* instance . For snapshot mounts , not only a mount type ( ro - mount
* or rw - mount ) but also a checkpoint number is required .
*/
sd . cno = 0 ;
sd . flags = flags ;
if ( nilfs_identify ( ( char * ) data , & sd ) ) {
err = - EINVAL ;
goto failed ;
}
2009-06-07 20:39:29 +04:00
nilfs = find_or_create_nilfs ( sd . bdev ) ;
if ( ! nilfs ) {
err = - ENOMEM ;
goto failed ;
}
2009-06-07 20:39:33 +04:00
mutex_lock ( & nilfs - > ns_mount_mutex ) ;
2009-06-07 20:39:30 +04:00
if ( ! sd . cno ) {
/*
* Check if an exclusive mount exists or not .
* Snapshot mounts coexist with a current mount
* ( i . e . rw - mount or ro - mount ) , whereas rw - mount and
* ro - mount are mutually exclusive .
*/
2009-06-07 20:39:32 +04:00
down_read ( & nilfs - > ns_super_sem ) ;
2009-06-07 20:39:30 +04:00
if ( nilfs - > ns_current & &
( ( nilfs - > ns_current - > s_super - > s_flags ^ flags )
& MS_RDONLY ) ) {
2009-06-07 20:39:32 +04:00
up_read ( & nilfs - > ns_super_sem ) ;
2009-06-07 20:39:30 +04:00
err = - EBUSY ;
goto failed_unlock ;
}
2009-06-07 20:39:32 +04:00
up_read ( & nilfs - > ns_super_sem ) ;
2009-04-07 06:01:35 +04:00
}
/*
2009-06-07 20:39:31 +04:00
* Find existing nilfs_sb_info struct
2009-04-07 06:01:35 +04:00
*/
2009-06-07 20:39:31 +04:00
sd . sbi = nilfs_find_sbinfo ( nilfs , ! ( flags & MS_RDONLY ) , sd . cno ) ;
/*
* Get super block instance holding the nilfs_sb_info struct .
* A new instance is allocated if no existing mount is present or
* existing instance has been unmounted .
*/
2009-06-07 20:39:29 +04:00
s = sget ( fs_type , nilfs_test_bdev_super , nilfs_set_bdev_super , & sd ) ;
2009-06-07 20:39:31 +04:00
if ( sd . sbi )
nilfs_put_sbinfo ( sd . sbi ) ;
2009-06-07 20:39:29 +04:00
if ( IS_ERR ( s ) ) {
err = PTR_ERR ( s ) ;
goto failed_unlock ;
2009-04-07 06:01:35 +04:00
}
if ( ! s - > s_root ) {
char b [ BDEVNAME_SIZE ] ;
2009-06-07 20:39:29 +04:00
/* New superblock instance created */
2009-04-07 06:01:35 +04:00
s - > s_flags = flags ;
2010-05-08 22:01:32 +04:00
s - > s_mode = mode ;
2009-04-07 06:01:35 +04:00
strlcpy ( s - > s_id , bdevname ( sd . bdev , b ) , sizeof ( s - > s_id ) ) ;
sb_set_blocksize ( s , block_size ( sd . bdev ) ) ;
2010-05-09 04:48:31 +04:00
err = nilfs_fill_super ( s , data , flags & MS_SILENT ? 1 : 0 ,
nilfs ) ;
2009-04-07 06:01:35 +04:00
if ( err )
goto cancel_new ;
s - > s_flags | = MS_ACTIVE ;
need_to_close = 0 ;
}
2009-06-07 20:39:33 +04:00
mutex_unlock ( & nilfs - > ns_mount_mutex ) ;
2009-04-07 06:01:35 +04:00
put_nilfs ( nilfs ) ;
if ( need_to_close )
2010-05-08 21:57:57 +04:00
close_bdev_exclusive ( sd . bdev , mode ) ;
2009-04-07 06:01:35 +04:00
simple_set_mnt ( mnt , s ) ;
return 0 ;
failed_unlock :
2009-06-07 20:39:33 +04:00
mutex_unlock ( & nilfs - > ns_mount_mutex ) ;
2009-06-07 20:39:29 +04:00
put_nilfs ( nilfs ) ;
2009-04-07 06:01:35 +04:00
failed :
2010-05-08 21:57:57 +04:00
close_bdev_exclusive ( sd . bdev , mode ) ;
2009-04-07 06:01:35 +04:00
return err ;
cancel_new :
/* Abandoning the newly allocated superblock */
2009-06-07 20:39:33 +04:00
mutex_unlock ( & nilfs - > ns_mount_mutex ) ;
2009-06-07 20:39:29 +04:00
put_nilfs ( nilfs ) ;
2009-08-09 00:52:02 +04:00
deactivate_locked_super ( s ) ;
2009-04-07 06:01:35 +04:00
/*
2010-05-09 05:05:21 +04:00
* deactivate_locked_super ( ) invokes close_bdev_exclusive ( ) .
2009-04-07 06:01:35 +04:00
* We must finish all post - cleaning before this call ;
2009-06-07 20:39:33 +04:00
* put_nilfs ( ) needs the block device .
2009-04-07 06:01:35 +04:00
*/
return err ;
}
struct file_system_type nilfs_fs_type = {
. owner = THIS_MODULE ,
. name = " nilfs2 " ,
. get_sb = nilfs_get_sb ,
. kill_sb = kill_block_super ,
. fs_flags = FS_REQUIRES_DEV ,
} ;
2010-04-05 20:54:11 +04:00
static void nilfs_inode_init_once ( void * obj )
2009-04-07 06:01:35 +04:00
{
2010-04-05 20:54:11 +04:00
struct nilfs_inode_info * ii = obj ;
2009-04-07 06:01:35 +04:00
2010-04-05 20:54:11 +04:00
INIT_LIST_HEAD ( & ii - > i_dirty ) ;
# ifdef CONFIG_NILFS_XATTR
init_rwsem ( & ii - > xattr_sem ) ;
# endif
nilfs_btnode_cache_init_once ( & ii - > i_btnode_cache ) ;
2010-07-10 15:52:09 +04:00
ii - > i_bmap = & ii - > i_bmap_data ;
2010-04-05 20:54:11 +04:00
inode_init_once ( & ii - > vfs_inode ) ;
}
2009-04-07 06:01:35 +04:00
2010-04-05 20:54:11 +04:00
static void nilfs_segbuf_init_once ( void * obj )
{
memset ( obj , 0 , sizeof ( struct nilfs_segment_buffer ) ) ;
}
2009-04-07 06:01:35 +04:00
2010-04-05 20:54:11 +04:00
static void nilfs_destroy_cachep ( void )
{
2010-05-22 07:49:32 +04:00
if ( nilfs_inode_cachep )
2010-04-05 20:54:11 +04:00
kmem_cache_destroy ( nilfs_inode_cachep ) ;
2010-05-22 07:49:32 +04:00
if ( nilfs_transaction_cachep )
2010-04-05 20:54:11 +04:00
kmem_cache_destroy ( nilfs_transaction_cachep ) ;
2010-05-22 07:49:32 +04:00
if ( nilfs_segbuf_cachep )
2010-04-05 20:54:11 +04:00
kmem_cache_destroy ( nilfs_segbuf_cachep ) ;
2010-05-22 07:49:32 +04:00
if ( nilfs_btree_path_cache )
2010-04-05 20:54:11 +04:00
kmem_cache_destroy ( nilfs_btree_path_cache ) ;
}
2009-04-07 06:01:35 +04:00
2010-04-05 20:54:11 +04:00
static int __init nilfs_init_cachep ( void )
{
nilfs_inode_cachep = kmem_cache_create ( " nilfs2_inode_cache " ,
sizeof ( struct nilfs_inode_info ) , 0 ,
SLAB_RECLAIM_ACCOUNT , nilfs_inode_init_once ) ;
if ( ! nilfs_inode_cachep )
goto fail ;
nilfs_transaction_cachep = kmem_cache_create ( " nilfs2_transaction_cache " ,
sizeof ( struct nilfs_transaction_info ) , 0 ,
SLAB_RECLAIM_ACCOUNT , NULL ) ;
if ( ! nilfs_transaction_cachep )
goto fail ;
nilfs_segbuf_cachep = kmem_cache_create ( " nilfs2_segbuf_cache " ,
sizeof ( struct nilfs_segment_buffer ) , 0 ,
SLAB_RECLAIM_ACCOUNT , nilfs_segbuf_init_once ) ;
if ( ! nilfs_segbuf_cachep )
goto fail ;
nilfs_btree_path_cache = kmem_cache_create ( " nilfs2_btree_path_cache " ,
sizeof ( struct nilfs_btree_path ) * NILFS_BTREE_LEVEL_MAX ,
0 , 0 , NULL ) ;
if ( ! nilfs_btree_path_cache )
goto fail ;
2009-04-07 06:01:35 +04:00
return 0 ;
2010-04-05 20:54:11 +04:00
fail :
nilfs_destroy_cachep ( ) ;
return - ENOMEM ;
}
static int __init init_nilfs_fs ( void )
{
int err ;
2009-04-07 06:01:35 +04:00
2010-04-05 20:54:11 +04:00
err = nilfs_init_cachep ( ) ;
if ( err )
goto fail ;
2009-04-07 06:01:35 +04:00
2010-04-05 20:54:11 +04:00
err = register_filesystem ( & nilfs_fs_type ) ;
if ( err )
goto free_cachep ;
2009-04-07 06:01:35 +04:00
2010-04-09 19:09:53 +04:00
printk ( KERN_INFO " NILFS version 2 loaded \n " ) ;
2010-04-05 20:54:11 +04:00
return 0 ;
2009-04-07 06:01:35 +04:00
2010-04-05 20:54:11 +04:00
free_cachep :
nilfs_destroy_cachep ( ) ;
fail :
2009-04-07 06:01:35 +04:00
return err ;
}
static void __exit exit_nilfs_fs ( void )
{
2010-04-05 20:54:11 +04:00
nilfs_destroy_cachep ( ) ;
2009-04-07 06:01:35 +04:00
unregister_filesystem ( & nilfs_fs_type ) ;
}
module_init ( init_nilfs_fs )
module_exit ( exit_nilfs_fs )