From: Christian Brauner <brauner@kernel.org>
To: linux-fsdevel@vger.kernel.org, Jan Kara <jack@suse.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>,
ecryptfs@vger.kernel.org, linux-ext4@vger.kernel.org,
jfs-discussion@lists.sourceforge.net,
linux-nilfs@vger.kernel.org, exfat@lists.linux.dev,
linux-mm@kvack.org, ntfs@lists.linux.dev,
ocfs2-devel@lists.linux.dev, linux-btrfs@vger.kernel.org,
gfs2@lists.linux.dev, linux-efi@vger.kernel.org,
linux-mtd@lists.infradead.org, linux-xfs@vger.kernel.org,
"Christian Brauner (Amutable)" <brauner@kernel.org>,
stable@vger.kernel.org
Subject: [PATCH v2 21/34] zonefs: don't set SB_RDONLY after I/O errors
Date: Thu, 29 Oct 2026 17:07:12 +0100 [thread overview]
Message-ID: <20261029-work-fs-sb_rdonly-v2-21-2f44f64f0b66@kernel.org> (raw)
In-Reply-To: <20261029-work-fs-sb_rdonly-v2-0-2f44f64f0b66@kernel.org>
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) <brauner@kernel.org>
---
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
next prev parent reply other threads:[~2026-10-29 16:08 UTC|newest]
Thread overview: 38+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-29 16:06 [PATCH v2 00/34] fs: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-10-29 16:06 ` [PATCH v2 01/34] ecryptfs: don't leak the inode when a lookup can't open the lower file Christian Brauner
2026-10-29 16:06 ` [PATCH v2 02/34] ecryptfs: fall back to a read-only " Christian Brauner
2026-10-29 16:06 ` [PATCH v2 03/34] fs: add a generic emergency read-only state Christian Brauner
2026-10-29 16:06 ` [PATCH v2 04/34] ext2: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-10-29 16:06 ` [PATCH v2 05/34] jfs: " Christian Brauner
2026-10-29 16:06 ` [PATCH v2 06/34] nilfs2: " Christian Brauner
2026-10-29 16:06 ` [PATCH v2 07/34] fat: " Christian Brauner
2026-10-29 16:06 ` [PATCH v2 08/34] exfat: keep the current mount options on remount Christian Brauner
2026-10-29 16:07 ` [PATCH v2 09/34] exfat: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-10-29 16:07 ` [PATCH v2 10/34] exfat: use the generic emergency read-only state Christian Brauner
2026-10-29 16:07 ` [PATCH v2 11/34] hfs: don't set SB_RDONLY after MDB write errors Christian Brauner
2026-10-29 16:07 ` [PATCH v2 12/34] hfs: use the generic emergency read-only state Christian Brauner
2026-10-29 16:07 ` [PATCH v2 13/34] hfsplus: don't set SB_RDONLY when creating the attributes tree Christian Brauner
2026-10-29 16:07 ` [PATCH v2 14/34] affs: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-10-29 16:07 ` [PATCH v2 15/34] ufs: " Christian Brauner
2026-10-29 16:07 ` [PATCH v2 16/34] hpfs: " Christian Brauner
2026-10-29 16:07 ` [PATCH v2 17/34] ntfs: " Christian Brauner
2026-10-29 16:07 ` [PATCH v2 18/34] ntfs: use the generic emergency read-only state Christian Brauner
2026-10-29 16:07 ` [PATCH v2 19/34] ocfs2: keep the current mount options on remount Christian Brauner
2026-10-30 2:58 ` Joseph Qi
2026-10-29 16:07 ` [PATCH v2 20/34] ocfs2: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-10-30 3:01 ` Joseph Qi
2026-10-29 16:07 ` Christian Brauner [this message]
2026-10-29 16:07 ` [PATCH v2 22/34] zonefs: use the generic emergency read-only state Christian Brauner
2026-10-29 16:07 ` [PATCH v2 23/34] btrfs: check BTRFS_FS_STATE_RO instead of SB_RDONLY at runtime Christian Brauner
2026-10-29 16:07 ` [PATCH v2 24/34] btrfs: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-10-29 16:07 ` [PATCH v2 25/34] gfs2: wait for a pending withdraw before remounting read-only Christian Brauner
2026-10-29 16:07 ` [PATCH v2 26/34] gfs2: don't set SB_RDONLY on withdraw Christian Brauner
2026-10-29 16:07 ` [PATCH v2 27/34] efivarfs: stop using SB_RDONLY for read-only efivar ops Christian Brauner
2026-10-29 16:07 ` [PATCH v2 28/34] ubifs: don't set SB_RDONLY after errors Christian Brauner
2026-10-30 3:42 ` Zhihao Cheng
2026-10-29 16:07 ` [PATCH v2 29/34] selftests/filesystems: test filesystem errors while frozen Christian Brauner
2026-10-29 16:07 ` [PATCH v2 30/34] ext4: allow remounting read-only in the emergency state Christian Brauner
2026-10-29 16:07 ` [PATCH v2 31/34] ext4: use the generic emergency read-only state Christian Brauner
2026-10-29 16:07 ` [PATCH v2 32/34] selftests/filesystems: test ext4 remounts in the emergency state Christian Brauner
2026-10-29 16:07 ` [PATCH v2 33/34] xfs: allow remounting read-only after a shutdown Christian Brauner
2026-10-29 16:07 ` [PATCH v2 34/34] selftests/filesystems: test xfs remounts " Christian Brauner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261029-work-fs-sb_rdonly-v2-21-2f44f64f0b66@kernel.org \
--to=brauner@kernel.org \
--cc=ecryptfs@vger.kernel.org \
--cc=exfat@lists.linux.dev \
--cc=gfs2@lists.linux.dev \
--cc=jack@suse.com \
--cc=jfs-discussion@lists.sourceforge.net \
--cc=linux-btrfs@vger.kernel.org \
--cc=linux-efi@vger.kernel.org \
--cc=linux-ext4@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linux-mtd@lists.infradead.org \
--cc=linux-nilfs@vger.kernel.org \
--cc=linux-xfs@vger.kernel.org \
--cc=ntfs@lists.linux.dev \
--cc=ocfs2-devel@lists.linux.dev \
--cc=stable@vger.kernel.org \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox