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>
Subject: [PATCH v2 15/34] ufs: don't set SB_RDONLY after filesystem errors
Date: Thu, 29 Oct 2026 17:07:06 +0100 [thread overview]
Message-ID: <20261029-work-fs-sb_rdonly-v2-15-2f44f64f0b66@kernel.org> (raw)
In-Reply-To: <20261029-work-fs-sb_rdonly-v2-0-2f44f64f0b66@kernel.org>
ufs_error() sets SB_RDONLY on every error, whatever onerror= says, and
ufs_panic() does too, both without holding s_umount. 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 so every
writer hangs. It also leaked the cylinder group info because
ufs_put_super() doesn't free it for a read-only superblock, and a
remount to read-write read it in again over the old copy.
So call sb_set_emergency_ro() instead and let the VFS refuse
modifications. ufs_error() and ufs_panic() still mark the superblock
UFS_FSBAD but don't queue ufs_sync_fs() anymore which does nothing in
that state. ufs_put_cylinder() doesn't store the allocation rotors and
ufs_write_inode() refuses. ufs_handle_dirsync() treats EROFS as success
like ext2 because the directory entry was already changed.
A remount to read-write fails until umount. A remount to read-only works
and keeps the cylinder group info because evicting inodes that were
unlinked while open still loads cylinder groups. Writes through file
descriptors opened before the error fail with EROFS.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
fs/ufs/cylinder.c | 12 +++++++-----
fs/ufs/dir.c | 3 +++
fs/ufs/inode.c | 2 ++
fs/ufs/super.c | 56 ++++++++++++++++++++++++++++++++++++++-----------------
4 files changed, 51 insertions(+), 22 deletions(-)
diff --git a/fs/ufs/cylinder.c b/fs/ufs/cylinder.c
index b930ee1cf853..e0360873a7f8 100644
--- a/fs/ufs/cylinder.c
+++ b/fs/ufs/cylinder.c
@@ -117,12 +117,14 @@ void ufs_put_cylinder (struct super_block * sb, unsigned bitmap_nr)
}
/*
* rotor is not so important data, so we put it to disk
- * at the end of working with cylinder
+ * at the end of working with cylinder, but not after an error
*/
- ucg->cg_rotor = cpu_to_fs32(sb, ucpi->c_rotor);
- ucg->cg_frotor = cpu_to_fs32(sb, ucpi->c_frotor);
- ucg->cg_irotor = cpu_to_fs32(sb, ucpi->c_irotor);
- ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
+ if (!sb_emergency_ro(sb)) {
+ ucg->cg_rotor = cpu_to_fs32(sb, ucpi->c_rotor);
+ ucg->cg_frotor = cpu_to_fs32(sb, ucpi->c_frotor);
+ ucg->cg_irotor = cpu_to_fs32(sb, ucpi->c_irotor);
+ ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
+ }
for (i = 1; i < UCPI_UBH(ucpi)->count; i++) {
brelse (UCPI_UBH(ucpi)->bh[i]);
}
diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c
index e96174b738b4..78c883921115 100644
--- a/fs/ufs/dir.c
+++ b/fs/ufs/dir.c
@@ -64,6 +64,9 @@ static int ufs_handle_dirsync(struct inode *dir)
err = filemap_write_and_wait(dir->i_mapping);
if (!err)
err = sync_inode_metadata(dir, 1);
+ /* The change is done, an error made us read-only in the meantime. */
+ if (err == -EROFS && sb_emergency_ro(dir->i_sb))
+ err = 0;
return err;
}
diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
index c9ff8673fa66..ed5d98c8bfca 100644
--- a/fs/ufs/inode.c
+++ b/fs/ufs/inode.c
@@ -828,6 +828,8 @@ static int ufs_update_inode(struct inode * inode, int do_sync)
int ufs_write_inode(struct inode *inode, struct writeback_control *wbc)
{
+ if (sb_emergency_ro(inode->i_sb))
+ return -EROFS;
return ufs_update_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
}
diff --git a/fs/ufs/super.c b/fs/ufs/super.c
index 3569ac92b065..d82cf73360a7 100644
--- a/fs/ufs/super.c
+++ b/fs/ufs/super.c
@@ -280,11 +280,13 @@ void ufs_error (struct super_block * sb, const char * function,
uspi = UFS_SB(sb)->s_uspi;
usb1 = ubh_get_usb_first(uspi);
- if (!sb_rdonly(sb)) {
+ /*
+ * SB_RDONLY needs s_umount, see sb_set_emergency_ro(). The superblock
+ * marked bad goes out with the block device.
+ */
+ if (!sb_rdonly(sb) && sb_set_emergency_ro(sb)) {
usb1->fs_clean = UFS_FSBAD;
ubh_mark_buffer_dirty(USPI_UBH(uspi));
- ufs_mark_sb_dirty(sb);
- sb->s_flags |= SB_RDONLY;
}
va_start(args, fmt);
vaf.fmt = fmt;
@@ -314,15 +316,14 @@ void ufs_panic (struct super_block * sb, const char * function,
uspi = UFS_SB(sb)->s_uspi;
usb1 = ubh_get_usb_first(uspi);
- if (!sb_rdonly(sb)) {
+ /* See ufs_error(). */
+ if (!sb_rdonly(sb) && sb_set_emergency_ro(sb)) {
usb1->fs_clean = UFS_FSBAD;
ubh_mark_buffer_dirty(USPI_UBH(uspi));
- ufs_mark_sb_dirty(sb);
}
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
- sb->s_flags |= SB_RDONLY;
pr_crit("panic (device %s): %s: %pV\n",
sb->s_id, function, &vaf);
va_end(args);
@@ -512,11 +513,13 @@ static int ufs_read_cylinder_structures(struct super_block *sb)
failed:
kfree (base);
+ sbi->s_csp = NULL;
if (sbi->s_ucg) {
for (i = 0; i < uspi->s_ncg; i++)
if (sbi->s_ucg[i])
brelse (sbi->s_ucg[i]);
kfree (sbi->s_ucg);
+ sbi->s_ucg = NULL;
for (i = 0; i < UFS_MAX_GROUP_LOADED; i++)
kfree (sbi->s_ucpi[i]);
}
@@ -579,7 +582,8 @@ static void ufs_put_cstotal(struct super_block *sb)
* ufs_put_super_internal() - put on-disk intrenal structures
* @sb: pointer to super_block structure
* Put on-disk structures associated with cylinder groups
- * and write them back to disk, also update cs_total on disk
+ * and write them back to disk, also update cs_total on disk.
+ * Nothing is written back after an error.
*/
static void ufs_put_super_internal(struct super_block *sb)
{
@@ -591,10 +595,14 @@ static void ufs_put_super_internal(struct super_block *sb)
UFSD("ENTER\n");
+ base = space = (char*) sbi->s_csp;
+ /* Don't write anything back after an error, see ufs_error(). */
+ if (sb_emergency_ro(sb))
+ goto release;
+
ufs_put_cstotal(sb);
size = uspi->s_cssize;
blks = (size + uspi->s_fsize - 1) >> uspi->s_fshift;
- base = space = (char*) sbi->s_csp;
for (i = 0; i < blks; i++, space += uspi->s_fsize) {
struct buffer_head *bh = sb_bread(sb, uspi->s_csaddr + i);
@@ -607,6 +615,7 @@ static void ufs_put_super_internal(struct super_block *sb)
mark_buffer_dirty(bh);
brelse(bh);
}
+release:
for (i = 0; i < sbi->s_cg_loaded; i++) {
ufs_put_cylinder (sb, i);
kfree (sbi->s_ucpi[i]);
@@ -616,7 +625,9 @@ static void ufs_put_super_internal(struct super_block *sb)
for (i = 0; i < uspi->s_ncg; i++)
brelse (sbi->s_ucg[i]);
kfree (sbi->s_ucg);
+ sbi->s_ucg = NULL;
kfree (base);
+ sbi->s_csp = NULL;
UFSD("EXIT\n");
}
@@ -628,6 +639,10 @@ static int ufs_sync_fs(struct super_block *sb, int wait)
struct ufs_super_block_third * usb3;
unsigned flags;
+ /* Leave the superblock alone after an error, see ufs_error(). */
+ if (sb_emergency_ro(sb))
+ return 0;
+
mutex_lock(&UFS_SB(sb)->s_lock);
UFSD("ENTER\n");
@@ -684,7 +699,8 @@ static void ufs_put_super(struct super_block *sb)
UFSD("ENTER\n");
- if (!sb_rdonly(sb))
+ /* NULL if a remount to read-only freed the cylinder groups */
+ if (sbi->s_ucg)
ufs_put_super_internal(sb);
cancel_delayed_work_sync(&sbi->sync_work);
@@ -1265,14 +1281,20 @@ static int ufs_reconfigure(struct fs_context *fc)
* fs was mouted as rw, remounting ro
*/
if (fc->sb_flags & SB_RDONLY) {
- ufs_put_super_internal(sb);
- usb1->fs_time = ufs_get_seconds(sb);
- if ((flags & UFS_ST_MASK) == UFS_ST_SUN
- || (flags & UFS_ST_MASK) == UFS_ST_SUNOS
- || (flags & UFS_ST_MASK) == UFS_ST_SUNx86)
- ufs_set_fs_state(sb, usb1, usb3,
- UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time));
- ubh_mark_buffer_dirty (USPI_UBH(uspi));
+ /*
+ * After an error leave the superblock alone and keep the
+ * cylinder groups, evict still needs them.
+ */
+ if (!sb_emergency_ro(sb)) {
+ ufs_put_super_internal(sb);
+ usb1->fs_time = ufs_get_seconds(sb);
+ if ((flags & UFS_ST_MASK) == UFS_ST_SUN
+ || (flags & UFS_ST_MASK) == UFS_ST_SUNOS
+ || (flags & UFS_ST_MASK) == UFS_ST_SUNx86)
+ ufs_set_fs_state(sb, usb1, usb3,
+ UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time));
+ ubh_mark_buffer_dirty (USPI_UBH(uspi));
+ }
sb->s_flags |= SB_RDONLY;
} else {
/*
--
2.53.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
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 ` Christian Brauner [this message]
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 ` [PATCH v2 21/34] zonefs: don't set SB_RDONLY after I/O errors Christian Brauner
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-15-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=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