xfs: document the invalidate_bdev call in invalidate_bdev
authorChristoph Hellwig <hch@lst.de>
Wed, 9 Aug 2023 22:05:39 +0000 (15:05 -0700)
committerChristian Brauner <brauner@kernel.org>
Thu, 10 Aug 2023 08:34:55 +0000 (10:34 +0200)
Copy and paste the commit message from Darrick into a comment to explain
the seemingly odd invalidate_bdev in xfs_shutdown_devices.

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

index 18d34e8..46d0cbe 100644 (file)
@@ -410,6 +410,32 @@ STATIC void
 xfs_shutdown_devices(
        struct xfs_mount        *mp)
 {
+       /*
+        * Udev is triggered whenever anyone closes a block device or unmounts
+        * a file systemm on a block device.
+        * The default udev rules invoke blkid to read the fs super and create
+        * symlinks to the bdev under /dev/disk.  For this, it uses buffered
+        * reads through the page cache.
+        *
+        * xfs_db also uses buffered reads to examine metadata.  There is no
+        * coordination between xfs_db and udev, which means that they can run
+        * concurrently.  Note there is no coordination between the kernel and
+        * blkid either.
+        *
+        * On a system with 64k pages, the page cache can cache the superblock
+        * and the root inode (and hence the root directory) with the same 64k
+        * page.  If udev spawns blkid after the mkfs and the system is busy
+        * enough that it is still running when xfs_db starts up, they'll both
+        * read from the same page in the pagecache.
+        *
+        * The unmount writes updated inode metadata to disk directly.  The XFS
+        * buffer cache does not use the bdev pagecache, so it needs to
+        * invalidate that pagecache on unmount.  If the above scenario occurs,
+        * the pagecache no longer reflects what's on disk, xfs_db reads the
+        * stale metadata, and fails to find /a.  Most of the time this succeeds
+        * because closing a bdev invalidates the page cache, but when processes
+        * race, everyone loses.
+        */
        if (mp->m_logdev_targp && mp->m_logdev_targp != mp->m_ddev_targp) {
                blkdev_issue_flush(mp->m_logdev_targp->bt_bdev);
                invalidate_bdev(mp->m_logdev_targp->bt_bdev);