If a read-write root mount is remounted as read-only, the subvolume is also set to read-only. Use a rw_mounts counter to check the number of read-write mounts, and change superblock to read-only only in case there are no read-write subvol mounts. Since sb->s_flags can change while calling legacy_reconfigure(), use sb->s_flags instead of fc->sb_flags to re-evaluate sb->s_flags in reconfigure_super(). Test case: dd if=/dev/zero of=btrfsfs seek=240 count=0 bs=1M mkfs.btrfs btrfsfs mount btrfsfs /mnt btrfs subvol create /mnt/sv mount -o remount,ro /mnt mount -osubvol=/sv btrfsfs /mnt/sv findmnt # /mnt is RO, /mnt/sv RW mount -o remount,ro /mnt findmnt # /mnt is RO, /mnt/sv RO as well umount /mnt{/sv,} rm btrfsfs Reported-by: Fabian Vogt <fvogt@xxxxxxxx> Signed-off-by: Goldwyn Rodrigues <rgoldwyn@xxxxxxxx> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index a2991971c6b5..2bb6869f15af 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1060,6 +1060,9 @@ struct btrfs_fs_info { spinlock_t zone_active_bgs_lock; struct list_head zone_active_bgs; + /* Count of subvol mounts read-write */ + int rw_mounts; + #ifdef CONFIG_BTRFS_FS_REF_VERIFY spinlock_t ref_verify_lock; struct rb_root block_tree; diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 33cfc9e27451..32941e11e551 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1835,6 +1835,11 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, /* mount_subvol() will free subvol_name and mnt_root */ root = mount_subvol(subvol_name, subvol_objectid, mnt_root); + if (!IS_ERR(root) && !(flags & SB_RDONLY)) { + struct btrfs_fs_info *fs_info = btrfs_sb(mnt_root->mnt_sb); + fs_info->rw_mounts++; + } + out: return root; } @@ -1958,6 +1963,9 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data) goto out; if (*flags & SB_RDONLY) { + + if (--fs_info->rw_mounts > 0) + goto out; /* * this also happens on 'umount -rf' or on shutdown, when * the filesystem is busy. @@ -2057,6 +2065,8 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data) if (ret) goto restore; + fs_info->rw_mounts++; + btrfs_clear_sb_rdonly(sb); set_bit(BTRFS_FS_OPEN, &fs_info->flags); diff --git a/fs/super.c b/fs/super.c index f1d4a193602d..fd528f76f14b 100644 --- a/fs/super.c +++ b/fs/super.c @@ -913,7 +913,8 @@ int reconfigure_super(struct fs_context *fc) } WRITE_ONCE(sb->s_flags, ((sb->s_flags & ~fc->sb_flags_mask) | - (fc->sb_flags & fc->sb_flags_mask))); + (sb->s_flags & fc->sb_flags_mask))); + /* Needs to be ordered wrt mnt_is_readonly() */ smp_wmb(); sb->s_readonly_remount = 0; -- Goldwyn