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) <[email protected]>
---
 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


Reply via email to