summaryrefslogtreecommitdiff
path: root/fs/nilfs2
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nilfs2')
-rw-r--r--fs/nilfs2/cpfile.c6
-rw-r--r--fs/nilfs2/sb.h1
-rw-r--r--fs/nilfs2/super.c256
-rw-r--r--fs/nilfs2/the_nilfs.c115
-rw-r--r--fs/nilfs2/the_nilfs.h23
5 files changed, 227 insertions, 174 deletions
diff --git a/fs/nilfs2/cpfile.c b/fs/nilfs2/cpfile.c
index 4184c1c2992c..7d49813f66d6 100644
--- a/fs/nilfs2/cpfile.c
+++ b/fs/nilfs2/cpfile.c
@@ -863,11 +863,11 @@ int nilfs_cpfile_change_cpmode(struct inode *cpfile, __u64 cno, int mode)
case NILFS_CHECKPOINT:
/*
* Check for protecting existing snapshot mounts:
- * bd_mount_sem is used to make this operation atomic and
+ * ns_mount_mutex is used to make this operation atomic and
* exclusive with a new mount job. Though it doesn't cover
* umount, it's enough for the purpose.
*/
- down(&nilfs->ns_bdev->bd_mount_sem);
+ mutex_lock(&nilfs->ns_mount_mutex);
if (nilfs_checkpoint_is_mounted(nilfs, cno, 1)) {
/* Current implementation does not have to protect
plain read-only mounts since they are exclusive
@@ -876,7 +876,7 @@ int nilfs_cpfile_change_cpmode(struct inode *cpfile, __u64 cno, int mode)
ret = -EBUSY;
} else
ret = nilfs_cpfile_clear_snapshot(cpfile, cno);
- up(&nilfs->ns_bdev->bd_mount_sem);
+ mutex_unlock(&nilfs->ns_mount_mutex);
return ret;
case NILFS_SNAPSHOT:
return nilfs_cpfile_set_snapshot(cpfile, cno);
diff --git a/fs/nilfs2/sb.h b/fs/nilfs2/sb.h
index adccd4fc654e..0776ccc2504a 100644
--- a/fs/nilfs2/sb.h
+++ b/fs/nilfs2/sb.h
@@ -60,6 +60,7 @@ struct nilfs_sb_info {
struct super_block *s_super; /* reverse pointer to super_block */
struct the_nilfs *s_nilfs;
struct list_head s_list; /* list head for nilfs->ns_supers */
+ atomic_t s_count; /* reference count */
/* Segment constructor */
struct list_head s_dirty_files; /* dirty files list */
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index 9acf30b60600..ab785f85aa50 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -65,9 +65,8 @@ MODULE_DESCRIPTION("A New Implementation of the Log-structured Filesystem "
"(NILFS)");
MODULE_LICENSE("GPL");
+static void nilfs_write_super(struct super_block *sb);
static int nilfs_remount(struct super_block *sb, int *flags, char *data);
-static int test_exclusive_mount(struct file_system_type *fs_type,
- struct block_device *bdev, int flags);
/**
* nilfs_error() - report failure condition on a filesystem
@@ -320,6 +319,11 @@ static void nilfs_put_super(struct super_block *sb)
struct nilfs_sb_info *sbi = NILFS_SB(sb);
struct the_nilfs *nilfs = sbi->s_nilfs;
+ lock_kernel();
+
+ if (sb->s_dirt)
+ nilfs_write_super(sb);
+
nilfs_detach_segment_constructor(sbi);
if (!(sb->s_flags & MS_RDONLY)) {
@@ -328,12 +332,18 @@ static void nilfs_put_super(struct super_block *sb)
nilfs_commit_super(sbi, 1);
up_write(&nilfs->ns_sem);
}
+ down_write(&nilfs->ns_super_sem);
+ if (nilfs->ns_current == sbi)
+ nilfs->ns_current = NULL;
+ up_write(&nilfs->ns_super_sem);
nilfs_detach_checkpoint(sbi);
put_nilfs(sbi->s_nilfs);
sbi->s_super = NULL;
sb->s_fs_info = NULL;
- kfree(sbi);
+ nilfs_put_sbinfo(sbi);
+
+ unlock_kernel();
}
/**
@@ -388,6 +398,8 @@ static int nilfs_sync_fs(struct super_block *sb, int wait)
{
int err = 0;
+ nilfs_write_super(sb);
+
/* This function is called when super block should be written back */
if (wait)
err = nilfs_construct_segment(sb);
@@ -401,9 +413,9 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno)
struct buffer_head *bh_cp;
int err;
- down_write(&nilfs->ns_sem);
+ down_write(&nilfs->ns_super_sem);
list_add(&sbi->s_list, &nilfs->ns_supers);
- up_write(&nilfs->ns_sem);
+ up_write(&nilfs->ns_super_sem);
sbi->s_ifile = nilfs_mdt_new(
nilfs, sbi->s_super, NILFS_IFILE_INO, NILFS_IFILE_GFP);
@@ -441,9 +453,9 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno)
nilfs_mdt_destroy(sbi->s_ifile);
sbi->s_ifile = NULL;
- down_write(&nilfs->ns_sem);
+ down_write(&nilfs->ns_super_sem);
list_del_init(&sbi->s_list);
- up_write(&nilfs->ns_sem);
+ up_write(&nilfs->ns_super_sem);
return err;
}
@@ -455,9 +467,9 @@ void nilfs_detach_checkpoint(struct nilfs_sb_info *sbi)
nilfs_mdt_clear(sbi->s_ifile);
nilfs_mdt_destroy(sbi->s_ifile);
sbi->s_ifile = NULL;
- down_write(&nilfs->ns_sem);
+ down_write(&nilfs->ns_super_sem);
list_del_init(&sbi->s_list);
- up_write(&nilfs->ns_sem);
+ up_write(&nilfs->ns_super_sem);
}
static int nilfs_mark_recovery_complete(struct nilfs_sb_info *sbi)
@@ -757,7 +769,7 @@ int nilfs_store_magic_and_option(struct super_block *sb,
* @silent: silent mode flag
* @nilfs: the_nilfs struct
*
- * This function is called exclusively by bd_mount_mutex.
+ * This function is called exclusively by nilfs->ns_mount_mutex.
* So, the recovery process is protected from other simultaneous mounts.
*/
static int
@@ -778,6 +790,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
get_nilfs(nilfs);
sbi->s_nilfs = nilfs;
sbi->s_super = sb;
+ atomic_set(&sbi->s_count, 1);
err = init_nilfs(nilfs, sbi, (char *)data);
if (err)
@@ -875,6 +888,11 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
goto failed_root;
}
+ down_write(&nilfs->ns_super_sem);
+ if (!nilfs_test_opt(sbi, SNAPSHOT))
+ nilfs->ns_current = sbi;
+ up_write(&nilfs->ns_super_sem);
+
return 0;
failed_root:
@@ -890,7 +908,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
failed_sbi:
put_nilfs(nilfs);
sb->s_fs_info = NULL;
- kfree(sbi);
+ nilfs_put_sbinfo(sbi);
return err;
}
@@ -903,6 +921,9 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
struct nilfs_mount_options old_opts;
int err;
+ lock_kernel();
+
+ down_write(&nilfs->ns_super_sem);
old_sb_flags = sb->s_flags;
old_opts.mount_opt = sbi->s_mount_opt;
old_opts.snapshot_cno = sbi->s_snapshot_cno;
@@ -950,14 +971,12 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
* store the current valid flag. (It may have been changed
* by fsck since we originally mounted the partition.)
*/
- down(&sb->s_bdev->bd_mount_sem);
- /* Check existing RW-mount */
- if (test_exclusive_mount(sb->s_type, sb->s_bdev, 0)) {
+ if (nilfs->ns_current && nilfs->ns_current != sbi) {
printk(KERN_WARNING "NILFS (device %s): couldn't "
- "remount because a RW-mount exists.\n",
+ "remount because an RW-mount exists.\n",
sb->s_id);
err = -EBUSY;
- goto rw_remount_failed;
+ goto restore_opts;
}
if (sbi->s_snapshot_cno != nilfs_last_cno(nilfs)) {
printk(KERN_WARNING "NILFS (device %s): couldn't "
@@ -965,7 +984,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
"the latest one.\n",
sb->s_id);
err = -EINVAL;
- goto rw_remount_failed;
+ goto restore_opts;
}
sb->s_flags &= ~MS_RDONLY;
nilfs_clear_opt(sbi, SNAPSHOT);
@@ -973,28 +992,31 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
err = nilfs_attach_segment_constructor(sbi);
if (err)
- goto rw_remount_failed;
+ goto restore_opts;
down_write(&nilfs->ns_sem);
nilfs_setup_super(sbi);
up_write(&nilfs->ns_sem);
- up(&sb->s_bdev->bd_mount_sem);
+ nilfs->ns_current = sbi;
}
out:
+ up_write(&nilfs->ns_super_sem);
+ unlock_kernel();
return 0;
- rw_remount_failed:
- up(&sb->s_bdev->bd_mount_sem);
restore_opts:
sb->s_flags = old_sb_flags;
sbi->s_mount_opt = old_opts.mount_opt;
sbi->s_snapshot_cno = old_opts.snapshot_cno;
+ up_write(&nilfs->ns_super_sem);
+ unlock_kernel();
return err;
}
struct nilfs_super_data {
struct block_device *bdev;
+ struct nilfs_sb_info *sbi;
__u64 cno;
int flags;
};
@@ -1053,33 +1075,7 @@ static int nilfs_test_bdev_super(struct super_block *s, void *data)
{
struct nilfs_super_data *sd = data;
- return s->s_bdev == sd->bdev;
-}
-
-static int nilfs_test_bdev_super2(struct super_block *s, void *data)
-{
- struct nilfs_super_data *sd = data;
- int ret;
-
- if (s->s_bdev != sd->bdev)
- return 0;
-
- if (!((s->s_flags | sd->flags) & MS_RDONLY))
- return 1; /* Reuse an old R/W-mode super_block */
-
- if (s->s_flags & sd->flags & MS_RDONLY) {
- if (down_read_trylock(&s->s_umount)) {
- ret = s->s_root &&
- (sd->cno == NILFS_SB(s)->s_snapshot_cno);
- up_read(&s->s_umount);
- /*
- * This path is locked with sb_lock by sget().
- * So, drop_super() causes deadlock.
- */
- return ret;
- }
- }
- return 0;
+ return sd->sbi && s->s_fs_info == (void *)sd->sbi;
}
static int
@@ -1087,8 +1083,8 @@ 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;
- struct super_block *s, *s2;
- struct the_nilfs *nilfs = NULL;
+ struct super_block *s;
+ struct the_nilfs *nilfs;
int err, need_to_close = 1;
sd.bdev = open_bdev_exclusive(dev_name, flags, fs_type);
@@ -1100,7 +1096,6 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
* 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.
- * The results are passed in sget() using nilfs_super_data.
*/
sd.cno = 0;
sd.flags = flags;
@@ -1109,64 +1104,59 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
goto failed;
}
- /*
- * once the super is inserted into the list by sget, s_umount
- * will protect the lockfs code from trying to start a snapshot
- * while we are mounting
- */
- down(&sd.bdev->bd_mount_sem);
- if (!sd.cno &&
- (err = test_exclusive_mount(fs_type, sd.bdev, flags ^ MS_RDONLY))) {
- err = (err < 0) ? : -EBUSY;
- goto failed_unlock;
+ nilfs = find_or_create_nilfs(sd.bdev);
+ if (!nilfs) {
+ err = -ENOMEM;
+ goto failed;
}
- /*
- * Phase-1: search any existent instance and get the_nilfs
- */
- s = sget(fs_type, nilfs_test_bdev_super, nilfs_set_bdev_super, &sd);
- if (IS_ERR(s))
- goto error_s;
-
- if (!s->s_root) {
- err = -ENOMEM;
- nilfs = alloc_nilfs(sd.bdev);
- if (!nilfs)
- goto cancel_new;
- } else {
- struct nilfs_sb_info *sbi = NILFS_SB(s);
+ mutex_lock(&nilfs->ns_mount_mutex);
+ if (!sd.cno) {
/*
- * s_umount protects super_block from unmount process;
- * It covers pointers of nilfs_sb_info and the_nilfs.
+ * 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.
*/
- nilfs = sbi->s_nilfs;
- get_nilfs(nilfs);
- up_write(&s->s_umount);
+ down_read(&nilfs->ns_super_sem);
+ if (nilfs->ns_current &&
+ ((nilfs->ns_current->s_super->s_flags ^ flags)
+ & MS_RDONLY)) {
+ up_read(&nilfs->ns_super_sem);
+ err = -EBUSY;
+ goto failed_unlock;
+ }
+ up_read(&nilfs->ns_super_sem);
+ }
- /*
- * Phase-2: search specified snapshot or R/W mode super_block
- */
- if (!sd.cno)
- /* trying to get the latest checkpoint. */
- sd.cno = nilfs_last_cno(nilfs);
+ /*
+ * Find existing nilfs_sb_info struct
+ */
+ sd.sbi = nilfs_find_sbinfo(nilfs, !(flags & MS_RDONLY), sd.cno);
- s2 = sget(fs_type, nilfs_test_bdev_super2,
- nilfs_set_bdev_super, &sd);
- deactivate_super(s);
- /*
- * Although deactivate_super() invokes close_bdev_exclusive() at
- * kill_block_super(). Here, s is an existent mount; we need
- * one more close_bdev_exclusive() call.
- */
- s = s2;
- if (IS_ERR(s))
- goto error_s;
+ if (!sd.cno)
+ /* trying to get the latest checkpoint. */
+ sd.cno = nilfs_last_cno(nilfs);
+
+ /*
+ * 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.
+ */
+ s = sget(fs_type, nilfs_test_bdev_super, nilfs_set_bdev_super, &sd);
+ if (sd.sbi)
+ nilfs_put_sbinfo(sd.sbi);
+
+ if (IS_ERR(s)) {
+ err = PTR_ERR(s);
+ goto failed_unlock;
}
if (!s->s_root) {
char b[BDEVNAME_SIZE];
+ /* New superblock instance created */
s->s_flags = flags;
strlcpy(s->s_id, bdevname(sd.bdev, b), sizeof(s->s_id));
sb_set_blocksize(s, block_size(sd.bdev));
@@ -1177,26 +1167,18 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
s->s_flags |= MS_ACTIVE;
need_to_close = 0;
- } else if (!(s->s_flags & MS_RDONLY)) {
- err = -EBUSY;
}
- up(&sd.bdev->bd_mount_sem);
+ mutex_unlock(&nilfs->ns_mount_mutex);
put_nilfs(nilfs);
if (need_to_close)
close_bdev_exclusive(sd.bdev, flags);
simple_set_mnt(mnt, s);
return 0;
- error_s:
- up(&sd.bdev->bd_mount_sem);
- if (nilfs)
- put_nilfs(nilfs);
- close_bdev_exclusive(sd.bdev, flags);
- return PTR_ERR(s);
-
failed_unlock:
- up(&sd.bdev->bd_mount_sem);
+ mutex_unlock(&nilfs->ns_mount_mutex);
+ put_nilfs(nilfs);
failed:
close_bdev_exclusive(sd.bdev, flags);
@@ -1204,70 +1186,18 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
cancel_new:
/* Abandoning the newly allocated superblock */
- up(&sd.bdev->bd_mount_sem);
- if (nilfs)
- put_nilfs(nilfs);
+ mutex_unlock(&nilfs->ns_mount_mutex);
+ put_nilfs(nilfs);
up_write(&s->s_umount);
deactivate_super(s);
/*
* deactivate_super() invokes close_bdev_exclusive().
* We must finish all post-cleaning before this call;
- * put_nilfs() and unlocking bd_mount_sem need the block device.
+ * put_nilfs() needs the block device.
*/
return err;
}
-static int nilfs_test_bdev_super3(struct super_block *s, void *data)
-{
- struct nilfs_super_data *sd = data;
- int ret;
-
- if (s->s_bdev != sd->bdev)
- return 0;
- if (down_read_trylock(&s->s_umount)) {
- ret = (s->s_flags & MS_RDONLY) && s->s_root &&
- nilfs_test_opt(NILFS_SB(s), SNAPSHOT);
- up_read(&s->s_umount);
- if (ret)
- return 0; /* ignore snapshot mounts */
- }
- return !((sd->flags ^ s->s_flags) & MS_RDONLY);
-}
-
-static int __false_bdev_super(struct super_block *s, void *data)
-{
-#if 0 /* XXX: workaround for lock debug. This is not good idea */
- up_write(&s->s_umount);
-#endif
- return -EFAULT;
-}
-
-/**
- * test_exclusive_mount - check whether an exclusive RW/RO mount exists or not.
- * fs_type: filesystem type
- * bdev: block device
- * flag: 0 (check rw-mount) or MS_RDONLY (check ro-mount)
- * res: pointer to an integer to store result
- *
- * This function must be called within a section protected by bd_mount_mutex.
- */
-static int test_exclusive_mount(struct file_system_type *fs_type,
- struct block_device *bdev, int flags)
-{
- struct super_block *s;
- struct nilfs_super_data sd = { .flags = flags, .bdev = bdev };
-
- s = sget(fs_type, nilfs_test_bdev_super3, __false_bdev_super, &sd);
- if (IS_ERR(s)) {
- if (PTR_ERR(s) != -EFAULT)
- return PTR_ERR(s);
- return 0; /* Not found */
- }
- up_write(&s->s_umount);
- deactivate_super(s);
- return 1; /* Found */
-}
-
struct file_system_type nilfs_fs_type = {
.owner = THIS_MODULE,
.name = "nilfs2",
diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c
index dfe2e320276e..8b8889825716 100644
--- a/fs/nilfs2/the_nilfs.c
+++ b/fs/nilfs2/the_nilfs.c
@@ -34,6 +34,10 @@
#include "dat.h"
#include "segbuf.h"
+
+static LIST_HEAD(nilfs_objects);
+static DEFINE_SPINLOCK(nilfs_lock);
+
void nilfs_set_last_segment(struct the_nilfs *nilfs,
sector_t start_blocknr, u64 seq, __u64 cno)
{
@@ -54,7 +58,7 @@ void nilfs_set_last_segment(struct the_nilfs *nilfs,
* Return Value: On success, pointer to the_nilfs is returned.
* On error, NULL is returned.
*/
-struct the_nilfs *alloc_nilfs(struct block_device *bdev)
+static struct the_nilfs *alloc_nilfs(struct block_device *bdev)
{
struct the_nilfs *nilfs;
@@ -67,7 +71,10 @@ struct the_nilfs *alloc_nilfs(struct block_device *bdev)
atomic_set(&nilfs->ns_writer_refcount, -1);
atomic_set(&nilfs->ns_ndirtyblks, 0);
init_rwsem(&nilfs->ns_sem);
+ init_rwsem(&nilfs->ns_super_sem);
+ mutex_init(&nilfs->ns_mount_mutex);
mutex_init(&nilfs->ns_writer_mutex);
+ INIT_LIST_HEAD(&nilfs->ns_list);
INIT_LIST_HEAD(&nilfs->ns_supers);
spin_lock_init(&nilfs->ns_last_segment_lock);
nilfs->ns_gc_inodes_h = NULL;
@@ -77,6 +84,45 @@ struct the_nilfs *alloc_nilfs(struct block_device *bdev)
}
/**
+ * find_or_create_nilfs - find or create nilfs object
+ * @bdev: block device to which the_nilfs is related
+ *
+ * find_nilfs() looks up an existent nilfs object created on the
+ * device and gets the reference count of the object. If no nilfs object
+ * is found on the device, a new nilfs object is allocated.
+ *
+ * Return Value: On success, pointer to the nilfs object is returned.
+ * On error, NULL is returned.
+ */
+struct the_nilfs *find_or_create_nilfs(struct block_device *bdev)
+{
+ struct the_nilfs *nilfs, *new = NULL;
+
+ retry:
+ spin_lock(&nilfs_lock);
+ list_for_each_entry(nilfs, &nilfs_objects, ns_list) {
+ if (nilfs->ns_bdev == bdev) {
+ get_nilfs(nilfs);
+ spin_unlock(&nilfs_lock);
+ if (new)
+ put_nilfs(new);
+ return nilfs; /* existing object */
+ }
+ }
+ if (new) {
+ list_add_tail(&new->ns_list, &nilfs_objects);
+ spin_unlock(&nilfs_lock);
+ return new; /* new object */
+ }
+ spin_unlock(&nilfs_lock);
+
+ new = alloc_nilfs(bdev);
+ if (new)
+ goto retry;
+ return NULL; /* insufficient memory */
+}
+
+/**
* put_nilfs - release a reference to the_nilfs
* @nilfs: the_nilfs structure to be released
*
@@ -85,13 +131,20 @@ struct the_nilfs *alloc_nilfs(struct block_device *bdev)
*/
void put_nilfs(struct the_nilfs *nilfs)
{
- if (!atomic_dec_and_test(&nilfs->ns_count))
+ spin_lock(&nilfs_lock);
+ if (!atomic_dec_and_test(&nilfs->ns_count)) {
+ spin_unlock(&nilfs_lock);
return;
+ }
+ list_del_init(&nilfs->ns_list);
+ spin_unlock(&nilfs_lock);
+
/*
- * Increment of ns_count never occur below because the caller
+ * Increment of ns_count never occurs below because the caller
* of get_nilfs() holds at least one reference to the_nilfs.
* Thus its exclusion control is not required here.
*/
+
might_sleep();
if (nilfs_loaded(nilfs)) {
nilfs_mdt_clear(nilfs->ns_sufile);
@@ -514,7 +567,7 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data)
blocksize = BLOCK_SIZE << le32_to_cpu(sbp->s_log_block_size);
if (sb->s_blocksize != blocksize) {
- int hw_blocksize = bdev_hardsect_size(sb->s_bdev);
+ int hw_blocksize = bdev_logical_block_size(sb->s_bdev);
if (blocksize < hw_blocksize) {
printk(KERN_ERR
@@ -612,13 +665,63 @@ int nilfs_near_disk_full(struct the_nilfs *nilfs)
return ret;
}
+/**
+ * nilfs_find_sbinfo - find existing nilfs_sb_info structure
+ * @nilfs: nilfs object
+ * @rw_mount: mount type (non-zero value for read/write mount)
+ * @cno: checkpoint number (zero for read-only mount)
+ *
+ * nilfs_find_sbinfo() returns the nilfs_sb_info structure which
+ * @rw_mount and @cno (in case of snapshots) matched. If no instance
+ * was found, NULL is returned. Although the super block instance can
+ * be unmounted after this function returns, the nilfs_sb_info struct
+ * is kept on memory until nilfs_put_sbinfo() is called.
+ */
+struct nilfs_sb_info *nilfs_find_sbinfo(struct the_nilfs *nilfs,
+ int rw_mount, __u64 cno)
+{
+ struct nilfs_sb_info *sbi;
+
+ down_read(&nilfs->ns_super_sem);
+ /*
+ * The SNAPSHOT flag and sb->s_flags are supposed to be
+ * protected with nilfs->ns_super_sem.
+ */
+ sbi = nilfs->ns_current;
+ if (rw_mount) {
+ if (sbi && !(sbi->s_super->s_flags & MS_RDONLY))
+ goto found; /* read/write mount */
+ else
+ goto out;
+ } else if (cno == 0) {
+ if (sbi && (sbi->s_super->s_flags & MS_RDONLY))
+ goto found; /* read-only mount */
+ else
+ goto out;
+ }
+
+ list_for_each_entry(sbi, &nilfs->ns_supers, s_list) {
+ if (nilfs_test_opt(sbi, SNAPSHOT) &&
+ sbi->s_snapshot_cno == cno)
+ goto found; /* snapshot mount */
+ }
+ out:
+ up_read(&nilfs->ns_super_sem);
+ return NULL;
+
+ found:
+ atomic_inc(&sbi->s_count);
+ up_read(&nilfs->ns_super_sem);
+ return sbi;
+}
+
int nilfs_checkpoint_is_mounted(struct the_nilfs *nilfs, __u64 cno,
int snapshot_mount)
{
struct nilfs_sb_info *sbi;
int ret = 0;
- down_read(&nilfs->ns_sem);
+ down_read(&nilfs->ns_super_sem);
if (cno == 0 || cno > nilfs->ns_cno)
goto out_unlock;
@@ -635,6 +738,6 @@ int nilfs_checkpoint_is_mounted(struct the_nilfs *nilfs, __u64 cno,
ret++;
out_unlock:
- up_read(&nilfs->ns_sem);
+ up_read(&nilfs->ns_super_sem);
return ret;
}
diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h
index 30fe58778d05..e8adbffc626f 100644
--- a/fs/nilfs2/the_nilfs.h
+++ b/fs/nilfs2/the_nilfs.h
@@ -43,12 +43,16 @@ enum {
* struct the_nilfs - struct to supervise multiple nilfs mount points
* @ns_flags: flags
* @ns_count: reference count
+ * @ns_list: list head for nilfs_list
* @ns_bdev: block device
* @ns_bdi: backing dev info
* @ns_writer: back pointer to writable nilfs_sb_info
* @ns_sem: semaphore for shared states
+ * @ns_super_sem: semaphore for global operations across super block instances
+ * @ns_mount_mutex: mutex protecting mount process of nilfs
* @ns_writer_mutex: mutex protecting ns_writer attach/detach
* @ns_writer_refcount: number of referrers on ns_writer
+ * @ns_current: back pointer to current mount
* @ns_sbh: buffer heads of on-disk super blocks
* @ns_sbp: pointers to super block data
* @ns_sbwtime: previous write time of super blocks
@@ -88,15 +92,24 @@ enum {
struct the_nilfs {
unsigned long ns_flags;
atomic_t ns_count;
+ struct list_head ns_list;
struct block_device *ns_bdev;
struct backing_dev_info *ns_bdi;
struct nilfs_sb_info *ns_writer;
struct rw_semaphore ns_sem;
+ struct rw_semaphore ns_super_sem;
+ struct mutex ns_mount_mutex;
struct mutex ns_writer_mutex;
atomic_t ns_writer_refcount;
/*
+ * components protected by ns_super_sem
+ */
+ struct nilfs_sb_info *ns_current;
+ struct list_head ns_supers;
+
+ /*
* used for
* - loading the latest checkpoint exclusively.
* - allocating a new full segment.
@@ -108,7 +121,6 @@ struct the_nilfs {
time_t ns_sbwtime[2];
unsigned ns_sbsize;
unsigned ns_mount_state;
- struct list_head ns_supers;
/*
* Following fields are dedicated to a writable FS-instance.
@@ -191,11 +203,12 @@ THE_NILFS_FNS(DISCONTINUED, discontinued)
#define NILFS_ALTSB_FREQ 60 /* spare superblock */
void nilfs_set_last_segment(struct the_nilfs *, sector_t, u64, __u64);
-struct the_nilfs *alloc_nilfs(struct block_device *);
+struct the_nilfs *find_or_create_nilfs(struct block_device *);
void put_nilfs(struct the_nilfs *);
int init_nilfs(struct the_nilfs *, struct nilfs_sb_info *, char *);
int load_nilfs(struct the_nilfs *, struct nilfs_sb_info *);
int nilfs_count_free_blocks(struct the_nilfs *, sector_t *);
+struct nilfs_sb_info *nilfs_find_sbinfo(struct the_nilfs *, int, __u64);
int nilfs_checkpoint_is_mounted(struct the_nilfs *, __u64, int);
int nilfs_near_disk_full(struct the_nilfs *);
void nilfs_fall_back_super_block(struct the_nilfs *);
@@ -238,6 +251,12 @@ nilfs_detach_writer(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi)
mutex_unlock(&nilfs->ns_writer_mutex);
}
+static inline void nilfs_put_sbinfo(struct nilfs_sb_info *sbi)
+{
+ if (!atomic_dec_and_test(&sbi->s_count))
+ kfree(sbi);
+}
+
static inline void
nilfs_get_segment_range(struct the_nilfs *nilfs, __u64 segnum,
sector_t *seg_start, sector_t *seg_end)