affs_error() sets SB_RDONLY from whatever context hit the error and without holding s_umount. If that happens while the filesystem is frozen through bdev_freeze(), e.g. by a device mapper suspend, thaw_super() sees a read-only superblock and leaves the s_writers rwsems locked so every writer hangs. So call sb_set_emergency_ro() instead and let the VFS refuse modifications. affs_write_inode() refuses as well and affs_commit_super() leaves the root block alone in that state. A remount to read-write fails until umount. A remount to read-only works and keeps the bitmap around because evict and the last close still free blocks, affs_kill_sb() frees it. Writes through file descriptors opened before the error fail with EROFS. Signed-off-by: Christian Brauner (Amutable) --- fs/affs/amigaffs.c | 4 ++-- fs/affs/inode.c | 2 ++ fs/affs/super.c | 14 ++++++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c index affa20d2b73d..40ee1998ff33 100644 --- a/fs/affs/amigaffs.c +++ b/fs/affs/amigaffs.c @@ -483,9 +483,9 @@ affs_error(struct super_block *sb, const char *function, const char *fmt, ...) vaf.fmt = fmt; vaf.va = &args; pr_crit("error (device %s): %s(): %pV\n", sb->s_id, function, &vaf); - if (!sb_rdonly(sb)) + /* SB_RDONLY needs s_umount, see sb_set_emergency_ro(). */ + if (!sb_rdonly(sb) && sb_set_emergency_ro(sb)) pr_warn("Remounting filesystem read-only\n"); - sb->s_flags |= SB_RDONLY; va_end(args); } diff --git a/fs/affs/inode.c b/fs/affs/inode.c index c7de9c793ab8..009245ad6c99 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -173,6 +173,8 @@ affs_write_inode(struct inode *inode, struct writeback_control *wbc) pr_debug("write_inode(%llu)\n", inode->i_ino); + if (sb_emergency_ro(sb)) + return -EROFS; if (!inode->i_nlink) // possibly free block return 0; diff --git a/fs/affs/super.c b/fs/affs/super.c index ed8225b3c93d..655d7282a8de 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -36,6 +36,9 @@ affs_commit_super(struct super_block *sb, int wait) struct buffer_head *bh = sbi->s_root_bh; struct affs_root_tail *tail = AFFS_ROOT_TAIL(sb, bh); + if (sb_emergency_ro(sb)) + return; + lock_buffer(bh); affs_secs_to_datestamp(ktime_get_real_seconds(), &tail->disk_change); affs_fix_checksum(sb, bh); @@ -82,7 +85,7 @@ void affs_mark_sb_dirty(struct super_block *sb) struct affs_sb_info *sbi = AFFS_SB(sb); unsigned long delay; - if (sb_rdonly(sb)) + if (sb_rdonly(sb) || sb_emergency_ro(sb)) return; spin_lock(&sbi->work_lock); @@ -550,10 +553,13 @@ static int affs_reconfigure(struct fs_context *fc) if ((bool)(fc->sb_flags & SB_RDONLY) == sb_rdonly(sb)) return 0; - if (fc->sb_flags & SB_RDONLY) - affs_free_bitmap(sb); - else + if (fc->sb_flags & SB_RDONLY) { + /* After an error evict still frees blocks, keep the bitmap. */ + if (!sb_emergency_ro(sb)) + affs_free_bitmap(sb); + } else { res = affs_init_bitmap(sb, &fc->sb_flags); + } return res; } -- 2.53.0