aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nilfs2/the_nilfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nilfs2/the_nilfs.c')
-rw-r--r--fs/nilfs2/the_nilfs.c113
1 files changed, 108 insertions, 5 deletions
diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c
index a91f15b8673c..e4e5c78bcc93 100644
--- a/fs/nilfs2/the_nilfs.c
+++ b/fs/nilfs2/the_nilfs.c
@@ -35,6 +35,10 @@
#include "seglist.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)
{
@@ -55,7 +59,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;
@@ -68,7 +72,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;
@@ -78,6 +85,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
*
@@ -86,13 +132,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);
@@ -613,13 +666,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;
@@ -636,6 +739,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;
}