Skip to content

Commit e59399d

Browse files
konisAl Viro
authored andcommitted
nilfs2: correct exclusion control in nilfs_remount function
nilfs_remount() changes mount state of a superblock instance. Even though nilfs accesses other superblock instances during mount or remount, the mount state was not properly protected in nilfs_remount(). Moreover, nilfs_remount() has a lock order reversal problem; nilfs_get_sb() holds: 1. bdev->bd_mount_sem 2. sb->s_umount (sget acquires) and nilfs_remount() holds: 1. sb->s_umount (locked by the caller in vfs) 2. bdev->bd_mount_sem To avoid these problems, this patch divides a semaphore protecting super block instances from nilfs->ns_sem, and applies it to the mount state protection in nilfs_remount(). With this change, bd_mount_sem use is removed from nilfs_remount() and the lock order reversal will be resolved. And the new rw-semaphore, nilfs->ns_super_sem will properly protect the mount state except the modification from nilfs_error function. Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent 6dd4740 commit e59399d

File tree

3 files changed

+33
-31
lines changed

3 files changed

+33
-31
lines changed

fs/nilfs2/super.c

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,10 @@ static void nilfs_put_super(struct super_block *sb)
327327
nilfs_commit_super(sbi, 1);
328328
up_write(&nilfs->ns_sem);
329329
}
330-
down_write(&nilfs->ns_sem);
330+
down_write(&nilfs->ns_super_sem);
331331
if (nilfs->ns_current == sbi)
332332
nilfs->ns_current = NULL;
333-
up_write(&nilfs->ns_sem);
333+
up_write(&nilfs->ns_super_sem);
334334

335335
nilfs_detach_checkpoint(sbi);
336336
put_nilfs(sbi->s_nilfs);
@@ -408,9 +408,9 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno)
408408
struct buffer_head *bh_cp;
409409
int err;
410410

411-
down_write(&nilfs->ns_sem);
411+
down_write(&nilfs->ns_super_sem);
412412
list_add(&sbi->s_list, &nilfs->ns_supers);
413-
up_write(&nilfs->ns_sem);
413+
up_write(&nilfs->ns_super_sem);
414414

415415
sbi->s_ifile = nilfs_mdt_new(
416416
nilfs, sbi->s_super, NILFS_IFILE_INO, NILFS_IFILE_GFP);
@@ -448,9 +448,9 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno)
448448
nilfs_mdt_destroy(sbi->s_ifile);
449449
sbi->s_ifile = NULL;
450450

451-
down_write(&nilfs->ns_sem);
451+
down_write(&nilfs->ns_super_sem);
452452
list_del_init(&sbi->s_list);
453-
up_write(&nilfs->ns_sem);
453+
up_write(&nilfs->ns_super_sem);
454454

455455
return err;
456456
}
@@ -462,9 +462,9 @@ void nilfs_detach_checkpoint(struct nilfs_sb_info *sbi)
462462
nilfs_mdt_clear(sbi->s_ifile);
463463
nilfs_mdt_destroy(sbi->s_ifile);
464464
sbi->s_ifile = NULL;
465-
down_write(&nilfs->ns_sem);
465+
down_write(&nilfs->ns_super_sem);
466466
list_del_init(&sbi->s_list);
467-
up_write(&nilfs->ns_sem);
467+
up_write(&nilfs->ns_super_sem);
468468
}
469469

470470
static int nilfs_mark_recovery_complete(struct nilfs_sb_info *sbi)
@@ -883,10 +883,10 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
883883
goto failed_root;
884884
}
885885

886-
down_write(&nilfs->ns_sem);
886+
down_write(&nilfs->ns_super_sem);
887887
if (!nilfs_test_opt(sbi, SNAPSHOT))
888888
nilfs->ns_current = sbi;
889-
up_write(&nilfs->ns_sem);
889+
up_write(&nilfs->ns_super_sem);
890890

891891
return 0;
892892

@@ -918,6 +918,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
918918

919919
lock_kernel();
920920

921+
down_write(&nilfs->ns_super_sem);
921922
old_sb_flags = sb->s_flags;
922923
old_opts.mount_opt = sbi->s_mount_opt;
923924
old_opts.snapshot_cno = sbi->s_snapshot_cno;
@@ -965,50 +966,45 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
965966
* store the current valid flag. (It may have been changed
966967
* by fsck since we originally mounted the partition.)
967968
*/
968-
down(&sb->s_bdev->bd_mount_sem);
969-
down_read(&nilfs->ns_sem);
970969
if (nilfs->ns_current && nilfs->ns_current != sbi) {
971970
printk(KERN_WARNING "NILFS (device %s): couldn't "
972971
"remount because an RW-mount exists.\n",
973972
sb->s_id);
974-
up_read(&nilfs->ns_sem);
975973
err = -EBUSY;
976-
goto rw_remount_failed;
974+
goto restore_opts;
977975
}
978-
up_read(&nilfs->ns_sem);
979976
if (sbi->s_snapshot_cno != nilfs_last_cno(nilfs)) {
980977
printk(KERN_WARNING "NILFS (device %s): couldn't "
981978
"remount because the current RO-mount is not "
982979
"the latest one.\n",
983980
sb->s_id);
984981
err = -EINVAL;
985-
goto rw_remount_failed;
982+
goto restore_opts;
986983
}
987984
sb->s_flags &= ~MS_RDONLY;
988985
nilfs_clear_opt(sbi, SNAPSHOT);
989986
sbi->s_snapshot_cno = 0;
990987

991988
err = nilfs_attach_segment_constructor(sbi);
992989
if (err)
993-
goto rw_remount_failed;
990+
goto restore_opts;
994991

995992
down_write(&nilfs->ns_sem);
996993
nilfs_setup_super(sbi);
997-
nilfs->ns_current = sbi;
998994
up_write(&nilfs->ns_sem);
999995

1000-
up(&sb->s_bdev->bd_mount_sem);
996+
nilfs->ns_current = sbi;
1001997
}
1002998
out:
999+
up_write(&nilfs->ns_super_sem);
10031000
unlock_kernel();
10041001
return 0;
10051002

1006-
rw_remount_failed:
1007-
up(&sb->s_bdev->bd_mount_sem);
10081003
restore_opts:
10091004
sb->s_flags = old_sb_flags;
10101005
sbi->s_mount_opt = old_opts.mount_opt;
10111006
sbi->s_snapshot_cno = old_opts.snapshot_cno;
1007+
up_write(&nilfs->ns_super_sem);
10121008
unlock_kernel();
10131009
return err;
10141010
}
@@ -1118,15 +1114,15 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
11181114
* (i.e. rw-mount or ro-mount), whereas rw-mount and
11191115
* ro-mount are mutually exclusive.
11201116
*/
1121-
down_read(&nilfs->ns_sem);
1117+
down_read(&nilfs->ns_super_sem);
11221118
if (nilfs->ns_current &&
11231119
((nilfs->ns_current->s_super->s_flags ^ flags)
11241120
& MS_RDONLY)) {
1125-
up_read(&nilfs->ns_sem);
1121+
up_read(&nilfs->ns_super_sem);
11261122
err = -EBUSY;
11271123
goto failed_unlock;
11281124
}
1129-
up_read(&nilfs->ns_sem);
1125+
up_read(&nilfs->ns_super_sem);
11301126
}
11311127

11321128
/*

fs/nilfs2/the_nilfs.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ static struct the_nilfs *alloc_nilfs(struct block_device *bdev)
7272
atomic_set(&nilfs->ns_writer_refcount, -1);
7373
atomic_set(&nilfs->ns_ndirtyblks, 0);
7474
init_rwsem(&nilfs->ns_sem);
75+
init_rwsem(&nilfs->ns_super_sem);
7576
mutex_init(&nilfs->ns_writer_mutex);
7677
INIT_LIST_HEAD(&nilfs->ns_list);
7778
INIT_LIST_HEAD(&nilfs->ns_supers);
@@ -681,10 +682,10 @@ struct nilfs_sb_info *nilfs_find_sbinfo(struct the_nilfs *nilfs,
681682
{
682683
struct nilfs_sb_info *sbi;
683684

684-
down_read(&nilfs->ns_sem);
685+
down_read(&nilfs->ns_super_sem);
685686
/*
686687
* The SNAPSHOT flag and sb->s_flags are supposed to be
687-
* protected with nilfs->ns_sem.
688+
* protected with nilfs->ns_super_sem.
688689
*/
689690
sbi = nilfs->ns_current;
690691
if (rw_mount) {
@@ -705,12 +706,12 @@ struct nilfs_sb_info *nilfs_find_sbinfo(struct the_nilfs *nilfs,
705706
goto found; /* snapshot mount */
706707
}
707708
out:
708-
up_read(&nilfs->ns_sem);
709+
up_read(&nilfs->ns_super_sem);
709710
return NULL;
710711

711712
found:
712713
atomic_inc(&sbi->s_count);
713-
up_read(&nilfs->ns_sem);
714+
up_read(&nilfs->ns_super_sem);
714715
return sbi;
715716
}
716717

@@ -720,7 +721,7 @@ int nilfs_checkpoint_is_mounted(struct the_nilfs *nilfs, __u64 cno,
720721
struct nilfs_sb_info *sbi;
721722
int ret = 0;
722723

723-
down_read(&nilfs->ns_sem);
724+
down_read(&nilfs->ns_super_sem);
724725
if (cno == 0 || cno > nilfs->ns_cno)
725726
goto out_unlock;
726727

@@ -737,6 +738,6 @@ int nilfs_checkpoint_is_mounted(struct the_nilfs *nilfs, __u64 cno,
737738
ret++;
738739

739740
out_unlock:
740-
up_read(&nilfs->ns_sem);
741+
up_read(&nilfs->ns_super_sem);
741742
return ret;
742743
}

fs/nilfs2/the_nilfs.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ enum {
4848
* @ns_bdi: backing dev info
4949
* @ns_writer: back pointer to writable nilfs_sb_info
5050
* @ns_sem: semaphore for shared states
51+
* @ns_super_sem: semaphore for global operations across super block instances
5152
* @ns_writer_mutex: mutex protecting ns_writer attach/detach
5253
* @ns_writer_refcount: number of referrers on ns_writer
5354
* @ns_current: back pointer to current mount
@@ -96,10 +97,15 @@ struct the_nilfs {
9697
struct backing_dev_info *ns_bdi;
9798
struct nilfs_sb_info *ns_writer;
9899
struct rw_semaphore ns_sem;
100+
struct rw_semaphore ns_super_sem;
99101
struct mutex ns_writer_mutex;
100102
atomic_t ns_writer_refcount;
101103

104+
/*
105+
* components protected by ns_super_sem
106+
*/
102107
struct nilfs_sb_info *ns_current;
108+
struct list_head ns_supers;
103109

104110
/*
105111
* used for
@@ -113,7 +119,6 @@ struct the_nilfs {
113119
time_t ns_sbwtime[2];
114120
unsigned ns_sbsize;
115121
unsigned ns_mount_state;
116-
struct list_head ns_supers;
117122

118123
/*
119124
* Following fields are dedicated to a writable FS-instance.

0 commit comments

Comments
 (0)