When several superblocks share a device, keep it frozen even if some of them failed to freeze and swallow the error: rolling the others back via thaw_super() can fail too, so neither is a clear win. A single filesystem still reports its error, and a sync_blockdev() failure is always reported. Thaw follows the same rule. A device can only be shared once superblocks claim it with a common exclusivity token, which erofs starts doing in the next patch; for everyone else the loop visits exactly one superblock and the behavior is unchanged. Signed-off-by: Christian Brauner (Amutable) --- fs/super.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/fs/super.c b/fs/super.c index 236e868209a4..a83f58755cf8 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1548,13 +1548,15 @@ static void fs_bdev_sync(struct block_device *bdev) * devices is frozen once per device and stays frozen until all are thawed; the * block layer nests these freezes so the count stays balanced. * - * Return: 0, or the first error from freezing a superblock or syncing the - * block device. + * Return: 0, or the error from the one superblock on a single-fs device. When + * several superblocks share @bdev a per-superblock failure is swallowed + * (see below), but a sync_blockdev() failure is always reported. */ static int fs_bdev_freeze(struct block_device *bdev) { dev_t dev = bdev->bd_dev; struct super_dev *sb_dev; + unsigned int count = 0; int error = 0, err; lockdep_assert_held(&bdev->bd_fsfreeze_mutex); @@ -1568,8 +1570,17 @@ static int fs_bdev_freeze(struct block_device *bdev) if (err && !error) error = err; deactivate_super(sb_dev->sd_sb); + count++; } + /* + * When several superblocks share the device, keep it frozen even if some + * of them failed to freeze and swallow the error: rolling the rest back + * via thaw_super() can fail too, so neither is a clear win. A single + * filesystem (count == 1) still reports its error. + */ + if (error && count > 1) + error = 0; if (!error) error = sync_blockdev(bdev); return error; @@ -1583,12 +1594,14 @@ static int fs_bdev_freeze(struct block_device *bdev) * A zero return does not imply a superblock is fully unfrozen; it may have been * frozen more than once (by the kernel or via another device). * - * Return: 0, or the first error from thawing a superblock. + * Return: 0, or the first error on a single-fs device; a shared device swallows + * per-superblock errors, as fs_bdev_freeze() does. */ static int fs_bdev_thaw(struct block_device *bdev) { dev_t dev = bdev->bd_dev; struct super_dev *sb_dev; + unsigned int count = 0; int error = 0, err; lockdep_assert_held(&bdev->bd_fsfreeze_mutex); @@ -1602,8 +1615,12 @@ static int fs_bdev_thaw(struct block_device *bdev) if (err && !error) error = err; deactivate_super(sb_dev->sd_sb); + count++; } + /* Shared device: swallow per-superblock errors, like fs_bdev_freeze(). */ + if (error && count > 1) + error = 0; return error; } -- 2.47.3