summaryrefslogtreecommitdiff
path: root/fs/nilfs2/super.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nilfs2/super.c')
-rw-r--r--fs/nilfs2/super.c519
1 files changed, 323 insertions, 196 deletions
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index 48145f505a6a..1fa86b9df73b 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -55,6 +55,8 @@
#include "nilfs.h"
#include "mdt.h"
#include "alloc.h"
+#include "btree.h"
+#include "btnode.h"
#include "page.h"
#include "cpfile.h"
#include "ifile.h"
@@ -67,8 +69,32 @@ MODULE_DESCRIPTION("A New Implementation of the Log-structured Filesystem "
"(NILFS)");
MODULE_LICENSE("GPL");
+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;
+
static int nilfs_remount(struct super_block *sb, int *flags, char *data);
+static void nilfs_set_error(struct nilfs_sb_info *sbi)
+{
+ struct the_nilfs *nilfs = sbi->s_nilfs;
+ struct nilfs_super_block **sbp;
+
+ down_write(&nilfs->ns_sem);
+ if (!(nilfs->ns_mount_state & NILFS_ERROR_FS)) {
+ nilfs->ns_mount_state |= NILFS_ERROR_FS;
+ sbp = nilfs_prepare_super(sbi, 0);
+ if (likely(sbp)) {
+ sbp[0]->s_state |= cpu_to_le16(NILFS_ERROR_FS);
+ if (sbp[1])
+ sbp[1]->s_state |= cpu_to_le16(NILFS_ERROR_FS);
+ nilfs_commit_super(sbi, NILFS_SB_COMMIT_ALL);
+ }
+ }
+ up_write(&nilfs->ns_sem);
+}
+
/**
* nilfs_error() - report failure condition on a filesystem
*
@@ -94,16 +120,7 @@ void nilfs_error(struct super_block *sb, const char *function,
va_end(args);
if (!(sb->s_flags & MS_RDONLY)) {
- struct the_nilfs *nilfs = sbi->s_nilfs;
-
- down_write(&nilfs->ns_sem);
- if (!(nilfs->ns_mount_state & NILFS_ERROR_FS)) {
- nilfs->ns_mount_state |= NILFS_ERROR_FS;
- nilfs->ns_sbp[0]->s_state |=
- cpu_to_le16(NILFS_ERROR_FS);
- nilfs_commit_super(sbi, 1);
- }
- up_write(&nilfs->ns_sem);
+ nilfs_set_error(sbi);
if (nilfs_test_opt(sbi, ERRORS_RO)) {
printk(KERN_CRIT "Remounting filesystem read-only\n");
@@ -129,7 +146,6 @@ void nilfs_warning(struct super_block *sb, const char *function,
va_end(args);
}
-static struct kmem_cache *nilfs_inode_cachep;
struct inode *nilfs_alloc_inode_common(struct the_nilfs *nilfs)
{
@@ -155,52 +171,7 @@ void nilfs_destroy_inode(struct inode *inode)
kmem_cache_free(nilfs_inode_cachep, NILFS_I(inode));
}
-static void init_once(void *obj)
-{
- struct nilfs_inode_info *ii = obj;
-
- 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);
- ii->i_bmap = (struct nilfs_bmap *)&ii->i_bmap_union;
- inode_init_once(&ii->vfs_inode);
-}
-
-static int nilfs_init_inode_cache(void)
-{
- nilfs_inode_cachep = kmem_cache_create("nilfs2_inode_cache",
- sizeof(struct nilfs_inode_info),
- 0, SLAB_RECLAIM_ACCOUNT,
- init_once);
-
- return (nilfs_inode_cachep == NULL) ? -ENOMEM : 0;
-}
-
-static inline void nilfs_destroy_inode_cache(void)
-{
- kmem_cache_destroy(nilfs_inode_cachep);
-}
-
-static void nilfs_clear_inode(struct inode *inode)
-{
- struct nilfs_inode_info *ii = NILFS_I(inode);
-
- /*
- * Free resources allocated in nilfs_read_inode(), here.
- */
- BUG_ON(!list_empty(&ii->i_dirty));
- brelse(ii->i_bh);
- ii->i_bh = NULL;
-
- if (test_bit(NILFS_I_BMAP, &ii->i_state))
- nilfs_bmap_clear(ii->i_bmap);
-
- nilfs_btnode_cache_clear(&ii->i_btnode_cache);
-}
-
-static int nilfs_sync_super(struct nilfs_sb_info *sbi, int dupsb)
+static int nilfs_sync_super(struct nilfs_sb_info *sbi, int flag)
{
struct the_nilfs *nilfs = sbi->s_nilfs;
int err;
@@ -226,12 +197,20 @@ static int nilfs_sync_super(struct nilfs_sb_info *sbi, int dupsb)
printk(KERN_ERR
"NILFS: unable to write superblock (err=%d)\n", err);
if (err == -EIO && nilfs->ns_sbh[1]) {
+ /*
+ * 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);
nilfs_fall_back_super_block(nilfs);
goto retry;
}
} else {
struct nilfs_super_block *sbp = nilfs->ns_sbp[0];
+ nilfs->ns_sbwcount++;
+
/*
* The latest segment becomes trailable from the position
* written in superblock.
@@ -240,66 +219,122 @@ static int nilfs_sync_super(struct nilfs_sb_info *sbi, int dupsb)
/* update GC protection for recent segments */
if (nilfs->ns_sbh[1]) {
- sbp = NULL;
- if (dupsb) {
+ if (flag == NILFS_SB_COMMIT_ALL) {
set_buffer_dirty(nilfs->ns_sbh[1]);
- if (!sync_dirty_buffer(nilfs->ns_sbh[1]))
- sbp = nilfs->ns_sbp[1];
+ if (sync_dirty_buffer(nilfs->ns_sbh[1]) < 0)
+ goto out;
}
+ 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];
}
- if (sbp) {
- 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);
- }
- }
+ 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:
return err;
}
-int nilfs_commit_super(struct nilfs_sb_info *sbi, int dupsb)
+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);
+}
+
+struct nilfs_super_block **nilfs_prepare_super(struct nilfs_sb_info *sbi,
+ int flip)
{
struct the_nilfs *nilfs = sbi->s_nilfs;
struct nilfs_super_block **sbp = nilfs->ns_sbp;
- sector_t nfreeblocks;
- time_t t;
- int err;
- /* nilfs->sem must be locked by the caller. */
- if (sbp[0]->s_magic != NILFS_SUPER_MAGIC) {
- if (sbp[1] && sbp[1]->s_magic == NILFS_SUPER_MAGIC)
- nilfs_swap_super_block(nilfs);
- else {
+ /* nilfs->ns_sem must be locked by the caller. */
+ if (sbp[0]->s_magic != cpu_to_le16(NILFS_SUPER_MAGIC)) {
+ if (sbp[1] &&
+ sbp[1]->s_magic == cpu_to_le16(NILFS_SUPER_MAGIC)) {
+ memcpy(sbp[0], sbp[1], nilfs->ns_sbsize);
+ } else {
printk(KERN_CRIT "NILFS: superblock broke on dev %s\n",
sbi->s_super->s_id);
- return -EIO;
+ return NULL;
}
+ } else if (sbp[1] &&
+ sbp[1]->s_magic != cpu_to_le16(NILFS_SUPER_MAGIC)) {
+ memcpy(sbp[1], sbp[0], nilfs->ns_sbsize);
}
- err = nilfs_count_free_blocks(nilfs, &nfreeblocks);
- if (unlikely(err)) {
- printk(KERN_ERR "NILFS: failed to count free blocks\n");
- return err;
- }
- spin_lock(&nilfs->ns_last_segment_lock);
- sbp[0]->s_last_seq = cpu_to_le64(nilfs->ns_last_seq);
- sbp[0]->s_last_pseg = cpu_to_le64(nilfs->ns_last_pseg);
- sbp[0]->s_last_cno = cpu_to_le64(nilfs->ns_last_cno);
- spin_unlock(&nilfs->ns_last_segment_lock);
+ if (flip && sbp[1])
+ nilfs_swap_super_block(nilfs);
+
+ return sbp;
+}
+
+int nilfs_commit_super(struct nilfs_sb_info *sbi, int flag)
+{
+ 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. */
t = get_seconds();
- nilfs->ns_sbwtime[0] = t;
- sbp[0]->s_free_blocks_count = cpu_to_le64(nfreeblocks);
+ nilfs->ns_sbwtime = t;
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));
- if (dupsb && sbp[1]) {
- memcpy(sbp[1], sbp[0], nilfs->ns_sbsize);
- nilfs->ns_sbwtime[1] = t;
+ 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));
}
clear_nilfs_sb_dirty(nilfs);
- return nilfs_sync_super(sbi, dupsb);
+ return nilfs_sync_super(sbi, flag);
+}
+
+/**
+ * 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)
+{
+ struct nilfs_super_block **sbp;
+ int flag = NILFS_SB_COMMIT;
+ int ret = -EIO;
+
+ sbp = nilfs_prepare_super(sbi, 0);
+ if (sbp) {
+ sbp[0]->s_state = cpu_to_le16(sbi->s_nilfs->ns_mount_state);
+ 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);
+ }
+ return ret;
}
static void nilfs_put_super(struct super_block *sb)
@@ -313,8 +348,7 @@ static void nilfs_put_super(struct super_block *sb)
if (!(sb->s_flags & MS_RDONLY)) {
down_write(&nilfs->ns_sem);
- nilfs->ns_sbp[0]->s_state = cpu_to_le16(nilfs->ns_mount_state);
- nilfs_commit_super(sbi, 1);
+ nilfs_cleanup_super(sbi);
up_write(&nilfs->ns_sem);
}
down_write(&nilfs->ns_super_sem);
@@ -335,6 +369,7 @@ static int nilfs_sync_fs(struct super_block *sb, int wait)
{
struct nilfs_sb_info *sbi = NILFS_SB(sb);
struct the_nilfs *nilfs = sbi->s_nilfs;
+ struct nilfs_super_block **sbp;
int err = 0;
/* This function is called when super block should be written back */
@@ -342,8 +377,13 @@ static int nilfs_sync_fs(struct super_block *sb, int wait)
err = nilfs_construct_segment(sb);
down_write(&nilfs->ns_sem);
- if (nilfs_sb_dirty(nilfs))
- nilfs_commit_super(sbi, 1);
+ if (nilfs_sb_dirty(nilfs)) {
+ 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);
+ }
+ }
up_write(&nilfs->ns_sem);
return err;
@@ -466,20 +506,20 @@ static int nilfs_show_options(struct seq_file *seq, struct vfsmount *vfs)
struct nilfs_sb_info *sbi = NILFS_SB(sb);
if (!nilfs_test_opt(sbi, BARRIER))
- seq_printf(seq, ",nobarrier");
+ seq_puts(seq, ",nobarrier");
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_RO))
- seq_printf(seq, ",errors=remount-ro");
if (nilfs_test_opt(sbi, ERRORS_PANIC))
- seq_printf(seq, ",errors=panic");
+ seq_puts(seq, ",errors=panic");
+ if (nilfs_test_opt(sbi, ERRORS_CONT))
+ seq_puts(seq, ",errors=continue");
if (nilfs_test_opt(sbi, STRICT_ORDER))
- seq_printf(seq, ",order=strict");
+ seq_puts(seq, ",order=strict");
if (nilfs_test_opt(sbi, NORECOVERY))
- seq_printf(seq, ",norecovery");
+ seq_puts(seq, ",norecovery");
if (nilfs_test_opt(sbi, DISCARD))
- seq_printf(seq, ",discard");
+ seq_puts(seq, ",discard");
return 0;
}
@@ -491,7 +531,7 @@ static const struct super_operations nilfs_sops = {
/* .write_inode = nilfs_write_inode, */
/* .put_inode = nilfs_put_inode, */
/* .drop_inode = nilfs_drop_inode, */
- .delete_inode = nilfs_delete_inode,
+ .evict_inode = nilfs_evict_inode,
.put_super = nilfs_put_super,
/* .write_super = nilfs_write_super, */
.sync_fs = nilfs_sync_fs,
@@ -499,7 +539,6 @@ static const struct super_operations nilfs_sops = {
/* .unlockfs */
.statfs = nilfs_statfs,
.remount_fs = nilfs_remount,
- .clear_inode = nilfs_clear_inode,
/* .umount_begin */
.show_options = nilfs_show_options
};
@@ -548,23 +587,25 @@ static const struct export_operations nilfs_export_ops = {
enum {
Opt_err_cont, Opt_err_panic, Opt_err_ro,
- Opt_nobarrier, Opt_snapshot, Opt_order, Opt_norecovery,
- Opt_discard, Opt_err,
+ Opt_barrier, Opt_nobarrier, Opt_snapshot, Opt_order, Opt_norecovery,
+ Opt_discard, Opt_nodiscard, Opt_err,
};
static match_table_t tokens = {
{Opt_err_cont, "errors=continue"},
{Opt_err_panic, "errors=panic"},
{Opt_err_ro, "errors=remount-ro"},
+ {Opt_barrier, "barrier"},
{Opt_nobarrier, "nobarrier"},
{Opt_snapshot, "cp=%u"},
{Opt_order, "order=%s"},
{Opt_norecovery, "norecovery"},
{Opt_discard, "discard"},
+ {Opt_nodiscard, "nodiscard"},
{Opt_err, NULL}
};
-static int parse_options(char *options, struct super_block *sb)
+static int parse_options(char *options, struct super_block *sb, int is_remount)
{
struct nilfs_sb_info *sbi = NILFS_SB(sb);
char *p;
@@ -581,6 +622,9 @@ static int parse_options(char *options, struct super_block *sb)
token = match_token(p, tokens, args);
switch (token) {
+ case Opt_barrier:
+ nilfs_set_opt(sbi, BARRIER);
+ break;
case Opt_nobarrier:
nilfs_clear_opt(sbi, BARRIER);
break;
@@ -606,8 +650,26 @@ static int parse_options(char *options, struct super_block *sb)
case Opt_snapshot:
if (match_int(&args[0], &option) || option <= 0)
return 0;
- if (!(sb->s_flags & MS_RDONLY))
+ 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");
return 0;
+ }
sbi->s_snapshot_cno = option;
nilfs_set_opt(sbi, SNAPSHOT);
break;
@@ -617,6 +679,9 @@ static int parse_options(char *options, struct super_block *sb)
case Opt_discard:
nilfs_set_opt(sbi, DISCARD);
break;
+ case Opt_nodiscard:
+ nilfs_clear_opt(sbi, DISCARD);
+ break;
default:
printk(KERN_ERR
"NILFS: Unrecognized mount option \"%s\"\n", p);
@@ -631,17 +696,24 @@ nilfs_set_default_options(struct nilfs_sb_info *sbi,
struct nilfs_super_block *sbp)
{
sbi->s_mount_opt =
- NILFS_MOUNT_ERRORS_CONT | NILFS_MOUNT_BARRIER;
+ NILFS_MOUNT_ERRORS_RO | NILFS_MOUNT_BARRIER;
}
static int nilfs_setup_super(struct nilfs_sb_info *sbi)
{
struct the_nilfs *nilfs = sbi->s_nilfs;
- struct nilfs_super_block *sbp = nilfs->ns_sbp[0];
- int max_mnt_count = le16_to_cpu(sbp->s_max_mnt_count);
- int mnt_count = le16_to_cpu(sbp->s_mnt_count);
+ struct nilfs_super_block **sbp;
+ int max_mnt_count;
+ int mnt_count;
+
+ /* nilfs->ns_sem must be locked by the caller. */
+ sbp = nilfs_prepare_super(sbi, 0);
+ 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);
- /* nilfs->sem must be locked by the caller. */
if (nilfs->ns_mount_state & NILFS_ERROR_FS) {
printk(KERN_WARNING
"NILFS warning: mounting fs with errors\n");
@@ -652,12 +724,15 @@ static int nilfs_setup_super(struct nilfs_sb_info *sbi)
#endif
}
if (!max_mnt_count)
- sbp->s_max_mnt_count = cpu_to_le16(NILFS_DFL_MAX_MNT_COUNT);
-
- sbp->s_mnt_count = cpu_to_le16(mnt_count + 1);
- sbp->s_state = cpu_to_le16(le16_to_cpu(sbp->s_state) & ~NILFS_VALID_FS);
- sbp->s_mtime = cpu_to_le64(get_seconds());
- return nilfs_commit_super(sbi, 1);
+ sbp[0]->s_max_mnt_count = cpu_to_le16(NILFS_DFL_MAX_MNT_COUNT);
+
+ 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());
+ /* synchronize sbp[1] with sbp[0] */
+ memcpy(sbp[1], sbp[0], nilfs->ns_sbsize);
+ return nilfs_commit_super(sbi, NILFS_SB_COMMIT_ALL);
}
struct nilfs_super_block *nilfs_read_super_block(struct super_block *sb,
@@ -694,7 +769,31 @@ int nilfs_store_magic_and_option(struct super_block *sb,
sbi->s_interval = le32_to_cpu(sbp->s_c_interval);
sbi->s_watermark = le32_to_cpu(sbp->s_c_block_max);
- return !parse_options(data, sb) ? -EINVAL : 0 ;
+ return !parse_options(data, sb, 0) ? -EINVAL : 0 ;
+}
+
+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;
}
/**
@@ -778,9 +877,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
goto failed_sbi;
}
cno = sbi->s_snapshot_cno;
- } else
- /* Read-only mount */
- sbi->s_snapshot_cno = cno;
+ }
}
err = nilfs_attach_checkpoint(sbi, cno);
@@ -845,11 +942,10 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
static int nilfs_remount(struct super_block *sb, int *flags, char *data)
{
struct nilfs_sb_info *sbi = NILFS_SB(sb);
- struct nilfs_super_block *sbp;
struct the_nilfs *nilfs = sbi->s_nilfs;
unsigned long old_sb_flags;
struct nilfs_mount_options old_opts;
- int err;
+ int was_snapshot, err;
lock_kernel();
@@ -857,19 +953,18 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
old_sb_flags = sb->s_flags;
old_opts.mount_opt = sbi->s_mount_opt;
old_opts.snapshot_cno = sbi->s_snapshot_cno;
+ was_snapshot = nilfs_test_opt(sbi, SNAPSHOT);
- if (!parse_options(data, sb)) {
+ if (!parse_options(data, sb, 1)) {
err = -EINVAL;
goto restore_opts;
}
sb->s_flags = (sb->s_flags & ~MS_POSIXACL);
- if ((*flags & MS_RDONLY) &&
- sbi->s_snapshot_cno != old_opts.snapshot_cno) {
- printk(KERN_WARNING "NILFS (device %s): couldn't "
- "remount to a different snapshot.\n",
- sb->s_id);
- err = -EINVAL;
+ err = -EINVAL;
+ if (was_snapshot && !(*flags & MS_RDONLY)) {
+ printk(KERN_ERR "NILFS (device %s): cannot remount snapshot "
+ "read/write.\n", sb->s_id);
goto restore_opts;
}
@@ -877,7 +972,6 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
printk(KERN_WARNING "NILFS (device %s): couldn't "
"remount because the filesystem is in an "
"incomplete recovery state.\n", sb->s_id);
- err = -EINVAL;
goto restore_opts;
}
@@ -888,45 +982,35 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
nilfs_detach_segment_constructor(sbi);
sb->s_flags |= MS_RDONLY;
- sbi->s_snapshot_cno = nilfs_last_cno(nilfs);
- /* nilfs_set_opt(sbi, SNAPSHOT); */
-
/*
* Remounting a valid RW partition RDONLY, so set
* the RDONLY flag and then mark the partition as valid again.
*/
down_write(&nilfs->ns_sem);
- sbp = nilfs->ns_sbp[0];
- if (!(sbp->s_state & le16_to_cpu(NILFS_VALID_FS)) &&
- (nilfs->ns_mount_state & NILFS_VALID_FS))
- sbp->s_state = cpu_to_le16(nilfs->ns_mount_state);
- sbp->s_mtime = cpu_to_le64(get_seconds());
- nilfs_commit_super(sbi, 1);
+ nilfs_cleanup_super(sbi);
up_write(&nilfs->ns_sem);
} else {
+ __u64 features;
+
/*
* 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.)
*/
- if (nilfs->ns_current && nilfs->ns_current != sbi) {
+ 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 because an RW-mount exists.\n",
- sb->s_id);
- err = -EBUSY;
- goto restore_opts;
- }
- if (sbi->s_snapshot_cno != nilfs_last_cno(nilfs)) {
- printk(KERN_WARNING "NILFS (device %s): couldn't "
- "remount because the current RO-mount is not "
- "the latest one.\n",
- sb->s_id);
- err = -EINVAL;
+ "remount RDWR because of unsupported optional "
+ "features (%llx)\n",
+ sb->s_id, (unsigned long long)features);
+ err = -EROFS;
goto restore_opts;
}
+
sb->s_flags &= ~MS_RDONLY;
- nilfs_clear_opt(sbi, SNAPSHOT);
- sbi->s_snapshot_cno = 0;
err = nilfs_attach_segment_constructor(sbi);
if (err)
@@ -935,8 +1019,6 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
down_write(&nilfs->ns_sem);
nilfs_setup_super(sbi);
up_write(&nilfs->ns_sem);
-
- nilfs->ns_current = sbi;
}
out:
up_write(&nilfs->ns_super_sem);
@@ -1022,10 +1104,14 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
{
struct nilfs_super_data sd;
struct super_block *s;
+ fmode_t mode = FMODE_READ;
struct the_nilfs *nilfs;
int err, need_to_close = 1;
- sd.bdev = open_bdev_exclusive(dev_name, flags, fs_type);
+ if (!(flags & MS_RDONLY))
+ mode |= FMODE_WRITE;
+
+ sd.bdev = open_bdev_exclusive(dev_name, mode, fs_type);
if (IS_ERR(sd.bdev))
return PTR_ERR(sd.bdev);
@@ -1092,10 +1178,12 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
/* New superblock instance created */
s->s_flags = flags;
+ s->s_mode = mode;
strlcpy(s->s_id, bdevname(sd.bdev, b), sizeof(s->s_id));
sb_set_blocksize(s, block_size(sd.bdev));
- err = nilfs_fill_super(s, data, flags & MS_VERBOSE, nilfs);
+ err = nilfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0,
+ nilfs);
if (err)
goto cancel_new;
@@ -1106,7 +1194,7 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
mutex_unlock(&nilfs->ns_mount_mutex);
put_nilfs(nilfs);
if (need_to_close)
- close_bdev_exclusive(sd.bdev, flags);
+ close_bdev_exclusive(sd.bdev, mode);
simple_set_mnt(mnt, s);
return 0;
@@ -1114,7 +1202,7 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
mutex_unlock(&nilfs->ns_mount_mutex);
put_nilfs(nilfs);
failed:
- close_bdev_exclusive(sd.bdev, flags);
+ close_bdev_exclusive(sd.bdev, mode);
return err;
@@ -1124,7 +1212,7 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
put_nilfs(nilfs);
deactivate_locked_super(s);
/*
- * deactivate_super() invokes close_bdev_exclusive().
+ * deactivate_locked_super() invokes close_bdev_exclusive().
* We must finish all post-cleaning before this call;
* put_nilfs() needs the block device.
*/
@@ -1139,54 +1227,93 @@ struct file_system_type nilfs_fs_type = {
.fs_flags = FS_REQUIRES_DEV,
};
-static int __init init_nilfs_fs(void)
+static void nilfs_inode_init_once(void *obj)
{
- int err;
-
- err = nilfs_init_inode_cache();
- if (err)
- goto failed;
+ struct nilfs_inode_info *ii = obj;
- err = nilfs_init_transaction_cache();
- if (err)
- goto failed_inode_cache;
+ 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);
+ ii->i_bmap = &ii->i_bmap_data;
+ inode_init_once(&ii->vfs_inode);
+}
- err = nilfs_init_segbuf_cache();
- if (err)
- goto failed_transaction_cache;
+static void nilfs_segbuf_init_once(void *obj)
+{
+ memset(obj, 0, sizeof(struct nilfs_segment_buffer));
+}
- err = nilfs_btree_path_cache_init();
- if (err)
- goto failed_segbuf_cache;
+static void nilfs_destroy_cachep(void)
+{
+ if (nilfs_inode_cachep)
+ kmem_cache_destroy(nilfs_inode_cachep);
+ if (nilfs_transaction_cachep)
+ kmem_cache_destroy(nilfs_transaction_cachep);
+ if (nilfs_segbuf_cachep)
+ kmem_cache_destroy(nilfs_segbuf_cachep);
+ if (nilfs_btree_path_cache)
+ kmem_cache_destroy(nilfs_btree_path_cache);
+}
- err = register_filesystem(&nilfs_fs_type);
- if (err)
- goto failed_btree_path_cache;
+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;
return 0;
- failed_btree_path_cache:
- nilfs_btree_path_cache_destroy();
+fail:
+ nilfs_destroy_cachep();
+ return -ENOMEM;
+}
+
+static int __init init_nilfs_fs(void)
+{
+ int err;
- failed_segbuf_cache:
- nilfs_destroy_segbuf_cache();
+ err = nilfs_init_cachep();
+ if (err)
+ goto fail;
- failed_transaction_cache:
- nilfs_destroy_transaction_cache();
+ err = register_filesystem(&nilfs_fs_type);
+ if (err)
+ goto free_cachep;
- failed_inode_cache:
- nilfs_destroy_inode_cache();
+ printk(KERN_INFO "NILFS version 2 loaded\n");
+ return 0;
- failed:
+free_cachep:
+ nilfs_destroy_cachep();
+fail:
return err;
}
static void __exit exit_nilfs_fs(void)
{
- nilfs_destroy_segbuf_cache();
- nilfs_destroy_transaction_cache();
- nilfs_destroy_inode_cache();
- nilfs_btree_path_cache_destroy();
+ nilfs_destroy_cachep();
unregister_filesystem(&nilfs_fs_type);
}