Skip to content

Commit d7439fb

Browse files
jankarabrauner
authored andcommitted
fs: Provide helpers for manipulating sb->s_readonly_remount
Provide helpers to set and clear sb->s_readonly_remount including appropriate memory barriers. Also use this opportunity to document what the barriers pair with and why they are needed. Suggested-by: Dave Chinner <[email protected]> Signed-off-by: Jan Kara <[email protected]> Reviewed-by: Dave Chinner <[email protected]> Message-Id: <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent c541dce commit d7439fb

File tree

4 files changed

+64
-21
lines changed

4 files changed

+64
-21
lines changed

fs/internal.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,47 @@ void put_super(struct super_block *sb);
120120
extern bool mount_capable(struct fs_context *);
121121
int sb_init_dio_done_wq(struct super_block *sb);
122122

123+
/*
124+
* Prepare superblock for changing its read-only state (i.e., either remount
125+
* read-write superblock read-only or vice versa). After this function returns
126+
* mnt_is_readonly() will return true for any mount of the superblock if its
127+
* caller is able to observe any changes done by the remount. This holds until
128+
* sb_end_ro_state_change() is called.
129+
*/
130+
static inline void sb_start_ro_state_change(struct super_block *sb)
131+
{
132+
WRITE_ONCE(sb->s_readonly_remount, 1);
133+
/*
134+
* For RO->RW transition, the barrier pairs with the barrier in
135+
* mnt_is_readonly() making sure if mnt_is_readonly() sees SB_RDONLY
136+
* cleared, it will see s_readonly_remount set.
137+
* For RW->RO transition, the barrier pairs with the barrier in
138+
* __mnt_want_write() before the mnt_is_readonly() check. The barrier
139+
* makes sure if __mnt_want_write() sees MNT_WRITE_HOLD already
140+
* cleared, it will see s_readonly_remount set.
141+
*/
142+
smp_wmb();
143+
}
144+
145+
/*
146+
* Ends section changing read-only state of the superblock. After this function
147+
* returns if mnt_is_readonly() returns false, the caller will be able to
148+
* observe all the changes remount did to the superblock.
149+
*/
150+
static inline void sb_end_ro_state_change(struct super_block *sb)
151+
{
152+
/*
153+
* This barrier provides release semantics that pairs with
154+
* the smp_rmb() acquire semantics in mnt_is_readonly().
155+
* This barrier pair ensure that when mnt_is_readonly() sees
156+
* 0 for sb->s_readonly_remount, it will also see all the
157+
* preceding flag changes that were made during the RO state
158+
* change.
159+
*/
160+
smp_wmb();
161+
WRITE_ONCE(sb->s_readonly_remount, 0);
162+
}
163+
123164
/*
124165
* open.c
125166
*/

fs/namespace.c

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -309,9 +309,16 @@ static unsigned int mnt_get_writers(struct mount *mnt)
309309

310310
static int mnt_is_readonly(struct vfsmount *mnt)
311311
{
312-
if (mnt->mnt_sb->s_readonly_remount)
312+
if (READ_ONCE(mnt->mnt_sb->s_readonly_remount))
313313
return 1;
314-
/* Order wrt setting s_flags/s_readonly_remount in do_remount() */
314+
/*
315+
* The barrier pairs with the barrier in sb_start_ro_state_change()
316+
* making sure if we don't see s_readonly_remount set yet, we also will
317+
* not see any superblock / mount flag changes done by remount.
318+
* It also pairs with the barrier in sb_end_ro_state_change()
319+
* assuring that if we see s_readonly_remount already cleared, we will
320+
* see the values of superblock / mount flags updated by remount.
321+
*/
315322
smp_rmb();
316323
return __mnt_is_readonly(mnt);
317324
}
@@ -364,9 +371,11 @@ int __mnt_want_write(struct vfsmount *m)
364371
}
365372
}
366373
/*
367-
* After the slowpath clears MNT_WRITE_HOLD, mnt_is_readonly will
368-
* be set to match its requirements. So we must not load that until
369-
* MNT_WRITE_HOLD is cleared.
374+
* The barrier pairs with the barrier sb_start_ro_state_change() making
375+
* sure that if we see MNT_WRITE_HOLD cleared, we will also see
376+
* s_readonly_remount set (or even SB_RDONLY / MNT_READONLY flags) in
377+
* mnt_is_readonly() and bail in case we are racing with remount
378+
* read-only.
370379
*/
371380
smp_rmb();
372381
if (mnt_is_readonly(m)) {
@@ -588,10 +597,8 @@ int sb_prepare_remount_readonly(struct super_block *sb)
588597
if (!err && atomic_long_read(&sb->s_remove_count))
589598
err = -EBUSY;
590599

591-
if (!err) {
592-
sb->s_readonly_remount = 1;
593-
smp_wmb();
594-
}
600+
if (!err)
601+
sb_start_ro_state_change(sb);
595602
list_for_each_entry(mnt, &sb->s_mounts, mnt_instance) {
596603
if (mnt->mnt.mnt_flags & MNT_WRITE_HOLD)
597604
mnt->mnt.mnt_flags &= ~MNT_WRITE_HOLD;

fs/super.c

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -944,21 +944,18 @@ int reconfigure_super(struct fs_context *fc)
944944
*/
945945
if (remount_ro) {
946946
if (force) {
947-
sb->s_readonly_remount = 1;
948-
smp_wmb();
947+
sb_start_ro_state_change(sb);
949948
} else {
950949
retval = sb_prepare_remount_readonly(sb);
951950
if (retval)
952951
return retval;
953952
}
954953
} else if (remount_rw) {
955954
/*
956-
* We set s_readonly_remount here to protect filesystem's
957-
* reconfigure code from writes from userspace until
958-
* reconfigure finishes.
955+
* Protect filesystem's reconfigure code from writes from
956+
* userspace until reconfigure finishes.
959957
*/
960-
sb->s_readonly_remount = 1;
961-
smp_wmb();
958+
sb_start_ro_state_change(sb);
962959
}
963960

964961
if (fc->ops->reconfigure) {
@@ -974,9 +971,7 @@ int reconfigure_super(struct fs_context *fc)
974971

975972
WRITE_ONCE(sb->s_flags, ((sb->s_flags & ~fc->sb_flags_mask) |
976973
(fc->sb_flags & fc->sb_flags_mask)));
977-
/* Needs to be ordered wrt mnt_is_readonly() */
978-
smp_wmb();
979-
sb->s_readonly_remount = 0;
974+
sb_end_ro_state_change(sb);
980975

981976
/*
982977
* Some filesystems modify their metadata via some other path than the
@@ -991,7 +986,7 @@ int reconfigure_super(struct fs_context *fc)
991986
return 0;
992987

993988
cancel_readonly:
994-
sb->s_readonly_remount = 0;
989+
sb_end_ro_state_change(sb);
995990
return retval;
996991
}
997992

include/linux/fs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1248,7 +1248,7 @@ struct super_block {
12481248
*/
12491249
atomic_long_t s_fsnotify_connectors;
12501250

1251-
/* Being remounted read-only */
1251+
/* Read-only state of the superblock is being changed */
12521252
int s_readonly_remount;
12531253

12541254
/* per-sb errseq_t for reporting writeback errors via syncfs */

0 commit comments

Comments
 (0)