xfs: drop s_umount over opening the log and RT devices
authorChristoph Hellwig <hch@lst.de>
Wed, 2 Aug 2023 15:41:30 +0000 (17:41 +0200)
committerChristian Brauner <brauner@kernel.org>
Fri, 11 Aug 2023 11:15:04 +0000 (13:15 +0200)
Just like get_tree_bdev needs to drop s_umount when opening the main
device, we need to do the same for the xfs log and RT devices to avoid a
potential lock order reversal with s_unmount for the mark_dead path.

It might be preferable to just drop s_umount over ->fill_super entirely,
but that will require a fairly massive audit first, so we'll do the easy
version here first.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Message-Id: <20230802154131.2221419-12-hch@lst.de>
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/xfs/xfs_super.c

index 46d0cbe..97ef41a 100644 (file)
@@ -462,17 +462,24 @@ STATIC int
 xfs_open_devices(
        struct xfs_mount        *mp)
 {
-       struct block_device     *ddev = mp->m_super->s_bdev;
+       struct super_block      *sb = mp->m_super;
+       struct block_device     *ddev = sb->s_bdev;
        struct block_device     *logdev = NULL, *rtdev = NULL;
        int                     error;
 
        /*
+        * blkdev_put() can't be called under s_umount, see the comment
+        * in get_tree_bdev() for more details
+        */
+       up_write(&sb->s_umount);
+
+       /*
         * Open real time and log devices - order is important.
         */
        if (mp->m_logname) {
                error = xfs_blkdev_get(mp, mp->m_logname, &logdev);
                if (error)
-                       return error;
+                       goto out_relock;
        }
 
        if (mp->m_rtname) {
@@ -510,7 +517,10 @@ xfs_open_devices(
                mp->m_logdev_targp = mp->m_ddev_targp;
        }
 
-       return 0;
+       error = 0;
+out_relock:
+       down_write(&sb->s_umount);
+       return error;
 
  out_free_rtdev_targ:
        if (mp->m_rtdev_targp)
@@ -523,7 +533,7 @@ xfs_open_devices(
  out_close_logdev:
        if (logdev && logdev != ddev)
                blkdev_put(logdev, mp);
-       return error;
+       goto out_relock;
 }
 
 /*