fs: tolerate per-superblock freeze errors on shared devices

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.

Link: https://patch.msgid.link/20260616-work-super-bdev_holder_global-v2-13-7df6b864028e@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2026-06-16 16:08:29 +02:00
parent cdb5146f8d
commit 7a7ef107b4
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2

View File

@ -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;
}