aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2011-11-21 06:11:31 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2012-01-06 23:20:12 -0500
commit4ed5e82fe77f4147cf386327c9a63a2dd7eff518 (patch)
treef4eaeefaf5d293014457892ac31f878eece07331
parent39f7c4db1d2d9e2e2a90abdf34811783089d217d (diff)
vfs: protect remounting superblock read-only
Currently remouting superblock read-only is racy in a major way. With the per mount read-only infrastructure it is now possible to prevent most races, which this patch attempts. Before starting the remount read-only, iterate through all mounts belonging to the superblock and if none of them have any pending writes, set sb->s_readonly_remount. This indicates that remount is in progress and no further write requests are allowed. If the remount succeeds set MS_RDONLY and reset s_readonly_remount. If the remounting is unsuccessful just reset s_readonly_remount. This can result in transient EROFS errors, despite the fact the remount failed. Unfortunately hodling off writes is difficult as remount itself may touch the filesystem (e.g. through load_nls()) which would deadlock. A later patch deals with delayed writes due to nlink going to zero. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Tested-by: Toshiyuki Okajima <toshi.okajima@jp.fujitsu.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/internal.h1
-rw-r--r--fs/namespace.c40
-rw-r--r--fs/super.c22
-rw-r--r--include/linux/fs.h3
4 files changed, 61 insertions, 5 deletions
diff --git a/fs/internal.h b/fs/internal.h
index 2523a4029452..9962c59ba280 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -52,6 +52,7 @@ extern int finish_automount(struct vfsmount *, struct path *);
52 52
53extern void mnt_make_longterm(struct vfsmount *); 53extern void mnt_make_longterm(struct vfsmount *);
54extern void mnt_make_shortterm(struct vfsmount *); 54extern void mnt_make_shortterm(struct vfsmount *);
55extern int sb_prepare_remount_readonly(struct super_block *);
55 56
56extern void __init mnt_init(void); 57extern void __init mnt_init(void);
57 58
diff --git a/fs/namespace.c b/fs/namespace.c
index 145217b088d1..98ebc78b21ab 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -273,6 +273,15 @@ static unsigned int mnt_get_writers(struct mount *mnt)
273#endif 273#endif
274} 274}
275 275
276static int mnt_is_readonly(struct vfsmount *mnt)
277{
278 if (mnt->mnt_sb->s_readonly_remount)
279 return 1;
280 /* Order wrt setting s_flags/s_readonly_remount in do_remount() */
281 smp_rmb();
282 return __mnt_is_readonly(mnt);
283}
284
276/* 285/*
277 * Most r/o checks on a fs are for operations that take 286 * Most r/o checks on a fs are for operations that take
278 * discrete amounts of time, like a write() or unlink(). 287 * discrete amounts of time, like a write() or unlink().
@@ -312,7 +321,7 @@ int mnt_want_write(struct vfsmount *m)
312 * MNT_WRITE_HOLD is cleared. 321 * MNT_WRITE_HOLD is cleared.
313 */ 322 */
314 smp_rmb(); 323 smp_rmb();
315 if (__mnt_is_readonly(m)) { 324 if (mnt_is_readonly(m)) {
316 mnt_dec_writers(mnt); 325 mnt_dec_writers(mnt);
317 ret = -EROFS; 326 ret = -EROFS;
318 goto out; 327 goto out;
@@ -435,6 +444,35 @@ static void __mnt_unmake_readonly(struct mount *mnt)
435 br_write_unlock(vfsmount_lock); 444 br_write_unlock(vfsmount_lock);
436} 445}
437 446
447int sb_prepare_remount_readonly(struct super_block *sb)
448{
449 struct mount *mnt;
450 int err = 0;
451
452 br_write_lock(vfsmount_lock);
453 list_for_each_entry(mnt, &sb->s_mounts, mnt_instance) {
454 if (!(mnt->mnt.mnt_flags & MNT_READONLY)) {
455 mnt->mnt.mnt_flags |= MNT_WRITE_HOLD;
456 smp_mb();
457 if (mnt_get_writers(mnt) > 0) {
458 err = -EBUSY;
459 break;
460 }
461 }
462 }
463 if (!err) {
464 sb->s_readonly_remount = 1;
465 smp_wmb();
466 }
467 list_for_each_entry(mnt, &sb->s_mounts, mnt_instance) {
468 if (mnt->mnt.mnt_flags & MNT_WRITE_HOLD)
469 mnt->mnt.mnt_flags &= ~MNT_WRITE_HOLD;
470 }
471 br_write_unlock(vfsmount_lock);
472
473 return err;
474}
475
438static void free_vfsmnt(struct mount *mnt) 476static void free_vfsmnt(struct mount *mnt)
439{ 477{
440 kfree(mnt->mnt_devname); 478 kfree(mnt->mnt_devname);
diff --git a/fs/super.c b/fs/super.c
index 993ca8f128d6..6acc02237e3e 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -723,23 +723,33 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
723 /* If we are remounting RDONLY and current sb is read/write, 723 /* If we are remounting RDONLY and current sb is read/write,
724 make sure there are no rw files opened */ 724 make sure there are no rw files opened */
725 if (remount_ro) { 725 if (remount_ro) {
726 if (force) 726 if (force) {
727 mark_files_ro(sb); 727 mark_files_ro(sb);
728 else if (!fs_may_remount_ro(sb)) 728 } else {
729 return -EBUSY; 729 retval = sb_prepare_remount_readonly(sb);
730 if (retval)
731 return retval;
732
733 retval = -EBUSY;
734 if (!fs_may_remount_ro(sb))
735 goto cancel_readonly;
736 }
730 } 737 }
731 738
732 if (sb->s_op->remount_fs) { 739 if (sb->s_op->remount_fs) {
733 retval = sb->s_op->remount_fs(sb, &flags, data); 740 retval = sb->s_op->remount_fs(sb, &flags, data);
734 if (retval) { 741 if (retval) {
735 if (!force) 742 if (!force)
736 return retval; 743 goto cancel_readonly;
737 /* If forced remount, go ahead despite any errors */ 744 /* If forced remount, go ahead despite any errors */
738 WARN(1, "forced remount of a %s fs returned %i\n", 745 WARN(1, "forced remount of a %s fs returned %i\n",
739 sb->s_type->name, retval); 746 sb->s_type->name, retval);
740 } 747 }
741 } 748 }
742 sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK); 749 sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);
750 /* Needs to be ordered wrt mnt_is_readonly() */
751 smp_wmb();
752 sb->s_readonly_remount = 0;
743 753
744 /* 754 /*
745 * Some filesystems modify their metadata via some other path than the 755 * Some filesystems modify their metadata via some other path than the
@@ -752,6 +762,10 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
752 if (remount_ro && sb->s_bdev) 762 if (remount_ro && sb->s_bdev)
753 invalidate_bdev(sb->s_bdev); 763 invalidate_bdev(sb->s_bdev);
754 return 0; 764 return 0;
765
766cancel_readonly:
767 sb->s_readonly_remount = 0;
768 return retval;
755} 769}
756 770
757static void do_emergency_remount(struct work_struct *work) 771static void do_emergency_remount(struct work_struct *work)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 03385acd71e8..7b8a681b1ef4 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1482,6 +1482,9 @@ struct super_block {
1482 int cleancache_poolid; 1482 int cleancache_poolid;
1483 1483
1484 struct shrinker s_shrink; /* per-sb shrinker handle */ 1484 struct shrinker s_shrink; /* per-sb shrinker handle */
1485
1486 /* Being remounted read-only */
1487 int s_readonly_remount;
1485}; 1488};
1486 1489
1487/* superblock cache pruning functions */ 1490/* superblock cache pruning functions */