Linux EXT4 FS development
 help / color / mirror / Atom feed
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 v3 17/33] ufs: don't set SB_RDONLY after filesystem errors
Date: Fri, 13 Nov 2026 12:59:13 +0100	[thread overview]
Message-ID: <20261113-work-fs-sb_rdonly-v3-17-0be92bb73709@kernel.org> (raw)
In-Reply-To: <20261113-work-fs-sb_rdonly-v3-0-0be92bb73709@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.
ufs_write_inode() keeps working because writeback still writes the data
and the blocks the inode points at.

After a remount to read-only a remount to read-write fails until umount.
A plain remount to read-write right after the error succeeds but the
filesystem stays read-only. 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/super.c    | 56 ++++++++++++++++++++++++++++++++++++++-----------------
 2 files changed, 46 insertions(+), 22 deletions(-)

diff --git a/fs/ufs/cylinder.c b/fs/ufs/cylinder.c
index b930ee1cf8530..e0360873a7f8d 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/super.c b/fs/ufs/super.c
index 3569ac92b065b..d82cf73360a79 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


  parent reply	other threads:[~2026-11-13 12:01 UTC|newest]

Thread overview: 70+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-13 11:58 [PATCH v3 00/33] fs: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-11-13 11:58 ` [PATCH v3 01/33] jfs: free uncommitted anonymous tlocks when evicting an inode Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:58 ` [PATCH v3 02/33] jfs: use jfs_symlink_inode_operations in jfs_iget() Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:58 ` [PATCH v3 03/33] ecryptfs: don't leak the inode when a lookup can't open the lower file Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 04/33] ecryptfs: fall back to a read-only " Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 05/33] fs: add a generic emergency read-only state Christian Brauner
2026-11-13 14:53   ` Jan Kara
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 06/33] ext2: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-11-13 14:54   ` Jan Kara
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 07/33] jfs: " Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 08/33] nilfs2: " Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 09/33] fat: " Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 10/33] exfat: keep the current mount options on remount Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 11/33] exfat: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 12/33] exfat: use the generic emergency read-only state Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 13/33] hfs: don't set SB_RDONLY after MDB write errors Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 14/33] hfs: use the generic emergency read-only state Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 15/33] hfsplus: don't set SB_RDONLY when creating the attributes tree Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 16/33] affs: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` Christian Brauner [this message]
2026-11-14 12:03   ` [PATCH v3 17/33] ufs: " sashiko-bot
2026-11-13 11:59 ` [PATCH v3 18/33] hpfs: " Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 19/33] ntfs: " Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 20/33] ntfs: use the generic emergency read-only state Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 21/33] ocfs2: keep the current mount options on remount Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 22/33] ocfs2: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 23/33] zonefs: don't set SB_RDONLY after I/O errors Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 24/33] zonefs: use the generic emergency read-only state Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 25/33] btrfs: check BTRFS_FS_STATE_RO instead of SB_RDONLY at runtime Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 26/33] btrfs: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 27/33] gfs2: wait for a pending withdraw before remounting read-only Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 28/33] gfs2: don't set SB_RDONLY on withdraw Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 29/33] efivarfs: stop using SB_RDONLY for read-only efivar ops Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 30/33] ubifs: don't set SB_RDONLY after errors Christian Brauner
2026-11-14  2:17   ` Zhihao Cheng
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 31/33] ext4: allow remounting read-only in the emergency state Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 32/33] ext4: use the generic emergency read-only state Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 33/33] xfs: allow remounting read-only after a shutdown Christian Brauner
2026-11-14 12:03   ` sashiko-bot

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=20261113-work-fs-sb_rdonly-v3-17-0be92bb73709@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