zonefs sets SB_RDONLY from its I/O error handling without holding s_umount: with errors=remount-ro, when the zone report fails and when closing an explicitly opened zone fails. If that happens while the filesystem is frozen through bdev_freeze() thaw_super() sees a read-only superblock and leaves the s_writers rwsems locked. Writers hang and even a remount to read-write doesn't help because the next freeze blocks forever. So record the error in a new ZONEFS_SB_EMERGENCY_RO bit instead. All three places go through zonefs_set_emergency_ro() which keeps printing the warning once. Write opens, write_iter and setattr fail with EROFS and page_mkwrite with SIGBUS. An error on a read-only mount doesn't set the bit. The state lasts until umount and zonefs.rst says so. A remount to read-only works and writes nothing. /proc/mounts keeps showing the filesystem read-write. ftruncate() and write faults through file descriptors and mappings from before the error are refused now too. Fixes: 8dcc1a9d90c1 ("fs: New zonefs file system") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- Documentation/filesystems/zonefs.rst | 4 ++-- fs/zonefs/file.c | 17 +++++++++-------- fs/zonefs/super.c | 34 ++++++++++++++++++++++++++-------- fs/zonefs/zonefs.h | 16 ++++++++++++++++ 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/Documentation/filesystems/zonefs.rst b/Documentation/filesystems/zonefs.rst index c22124c2213d..55ca0f2ef2b2 100644 --- a/Documentation/filesystems/zonefs.rst +++ b/Documentation/filesystems/zonefs.rst @@ -285,8 +285,8 @@ Further notes: * The "errors=remount-ro" mount option is the default behavior of zonefs I/O error processing if no errors mount option is specified. * With the "errors=remount-ro" mount option, the change of the file access - permissions to read-only applies to all files. The file system is remounted - read-only. + permissions to read-only applies to all files. A file system mounted + read-write refuses all writes until it is unmounted. * Access permission and file size changes due to the device transitioning zones to the offline condition are permanent. Remounting or reformatting the device with mkfs.zonefs (mkzonefs) will not change back offline zone files to a good diff --git a/fs/zonefs/file.c b/fs/zonefs/file.c index 5b34849be7a2..8959b6b36a83 100644 --- a/fs/zonefs/file.c +++ b/fs/zonefs/file.c @@ -299,7 +299,7 @@ static vm_fault_t zonefs_filemap_page_mkwrite(struct vm_fault *vmf) struct inode *inode = file_inode(vmf->vma->vm_file); vm_fault_t ret; - if (unlikely(IS_IMMUTABLE(inode))) + if (unlikely(IS_IMMUTABLE(inode) || zonefs_emergency_ro(inode->i_sb))) return VM_FAULT_SIGBUS; /* @@ -601,7 +601,7 @@ static ssize_t zonefs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (unlikely(IS_IMMUTABLE(inode))) return -EPERM; - if (sb_rdonly(inode->i_sb)) + if (sb_rdonly(inode->i_sb) || zonefs_emergency_ro(inode->i_sb)) return -EROFS; /* Write operations beyond the zone capacity are not allowed */ @@ -788,6 +788,9 @@ static int zonefs_file_open(struct inode *inode, struct file *file) { int ret; + if ((file->f_mode & FMODE_WRITE) && zonefs_emergency_ro(inode->i_sb)) + return -EROFS; + file->f_mode |= FMODE_CAN_ODIRECT; ret = generic_file_open(inode, file); if (ret) @@ -825,17 +828,15 @@ static void zonefs_seq_file_write_close(struct inode *inode) /* * Leaving zones explicitly open may lead to a state * where most zones cannot be written (zone resources - * exhausted). So take preventive action by remounting - * read-only. + * exhausted). So take preventive action by making the + * file system read-only. */ if (z->z_flags & ZONEFS_ZONE_OPEN && - !(sb->s_flags & SB_RDONLY)) { + !sb_rdonly(sb) && !zonefs_emergency_ro(sb)) { zonefs_warn(sb, "closing zone at %llu failed %d\n", z->z_sector, ret); - zonefs_warn(sb, - "remounting filesystem read-only\n"); - sb->s_flags |= SB_RDONLY; + zonefs_set_emergency_ro(sb); } goto unlock; } diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c index b97f1f2b8dda..e7c97b75c601 100644 --- a/fs/zonefs/super.c +++ b/fs/zonefs/super.c @@ -247,6 +247,19 @@ static void zonefs_inode_update_mode(struct inode *inode) z->z_mode = inode->i_mode; } +/* + * Refuse writes after an error. SB_RDONLY needs s_umount and a remount, set on + * a frozen file system it makes thaw_super() leave s_writers locked. + */ +void zonefs_set_emergency_ro(struct super_block *sb) +{ + if (sb_rdonly(sb) || + test_and_set_bit(ZONEFS_SB_EMERGENCY_RO, &ZONEFS_SB(sb)->s_state)) + return; + + zonefs_warn(sb, "remounting filesystem read-only\n"); +} + static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx, void *data) { @@ -339,13 +352,11 @@ static void zonefs_handle_io_error(struct inode *inode, struct blk_zone *zone, z->z_flags &= ~ZONEFS_ZONE_OPEN; /* - * If error=remount-ro was specified, any error result in remounting - * the volume as read-only. + * If error=remount-ro was specified, any error makes the volume + * read-only. */ - if ((sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_RO) && !sb_rdonly(sb)) { - zonefs_warn(sb, "remounting filesystem read-only\n"); - sb->s_flags |= SB_RDONLY; - } + if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_RO) + zonefs_set_emergency_ro(sb); /* * Update block usage stats and the inode size to prevent access to @@ -404,8 +415,7 @@ void __zonefs_io_error(struct inode *inode, bool write) if (ret != 1) { zonefs_err(sb, "Get inode %llu zone information failed %d\n", inode->i_ino, ret); - zonefs_warn(sb, "remounting filesystem read-only\n"); - sb->s_flags |= SB_RDONLY; + zonefs_set_emergency_ro(sb); return; } @@ -539,6 +549,9 @@ static int zonefs_inode_setattr(const struct mnt_idmap *idmap, struct inode *inode = d_inode(dentry); int ret; + if (unlikely(zonefs_emergency_ro(inode->i_sb))) + return -EROFS; + if (unlikely(IS_IMMUTABLE(inode))) return -EPERM; @@ -1369,6 +1382,11 @@ static int zonefs_reconfigure(struct fs_context *fc) struct super_block *sb = fc->root->d_sb; struct zonefs_sb_info *sbi = sb->s_fs_info; + /* An error made the file system read-only until it is unmounted. */ + if (sb_rdonly(sb) && !(fc->sb_flags & SB_RDONLY) && + zonefs_emergency_ro(sb)) + return -EROFS; + sync_filesystem(fc->root->d_sb); /* Copy new options from ctx into sbi. */ sbi->s_mount_opts = ctx->s_mount_opts; diff --git a/fs/zonefs/zonefs.h b/fs/zonefs/zonefs.h index 8175652241b5..b14822cb9c02 100644 --- a/fs/zonefs/zonefs.h +++ b/fs/zonefs/zonefs.h @@ -241,6 +241,16 @@ struct zonefs_sb_info { bool s_sysfs_registered; struct kobject s_kobj; struct completion s_kobj_unregister; + + unsigned long s_state; +}; + +/* + * Super block state bits (s_state). + */ +enum { + /* An error made the file system read-only until it is unmounted */ + ZONEFS_SB_EMERGENCY_RO, }; static inline struct zonefs_sb_info *ZONEFS_SB(struct super_block *sb) @@ -248,6 +258,11 @@ static inline struct zonefs_sb_info *ZONEFS_SB(struct super_block *sb) return sb->s_fs_info; } +static inline bool zonefs_emergency_ro(struct super_block *sb) +{ + return test_bit(ZONEFS_SB_EMERGENCY_RO, &ZONEFS_SB(sb)->s_state); +} + #define zonefs_info(sb, format, args...) \ pr_info("zonefs (%s): " format, sb->s_id, ## args) #define zonefs_err(sb, format, args...) \ @@ -260,6 +275,7 @@ void zonefs_inode_account_active(struct inode *inode); int zonefs_inode_zone_mgmt(struct inode *inode, enum req_op op); void zonefs_i_size_write(struct inode *inode, loff_t isize); void zonefs_update_stats(struct inode *inode, loff_t new_isize); +void zonefs_set_emergency_ro(struct super_block *sb); void __zonefs_io_error(struct inode *inode, bool write); static inline void zonefs_io_error(struct inode *inode, bool write) -- 2.53.0