summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2026-06-16 16:08:28 +0200
committerChristian Brauner <brauner@kernel.org>2026-06-29 10:31:53 +0200
commitcdb5146f8d5f938ec624d78d8ff001f1a60c17cf (patch)
tree1baf974da8bfb9d9d1a2ced1db26fdc783cd55ff /include
parent86b53849431452859a041993aa2ac6388908feab (diff)
downloadlinux-next-cdb5146f8d5f938ec624d78d8ff001f1a60c17cf.tar.gz
linux-next-cdb5146f8d5f938ec624d78d8ff001f1a60c17cf.zip
fs: look up superblocks via the device table in fs_holder_ops
Switch the fs_holder_ops callbacks from recovering the single owning superblock out of bdev->bd_holder to walking the device-to-superblock table and acting on every superblock registered for the device. The holder argument becomes purely the block layer's exclusivity token and is no longer needed by the fs specific callbacks. All devices opened with fs_holder_ops are registered by now: the main device since setup_bdev_super() switched to fs_bdev_file_open_by_dev() and the extra devices (xfs log and realtime devices, btrfs member devices, the ext4 external journal) since the preceding per-filesystem conversions. So no event is lost in the switchover. The walk uses a refcount-pinning cursor: each step takes a reference on the entry via sd_ref and resumes from its sd_node. Unlinking an entry is deferred to the last unpin, so a cursor never resumes from a removed node. mark_dead and sync only need the passive reference the entry holds plus s_umount, which they take with super_lock_shared(). freeze and thaw additionally need an active reference and acquire it with get_active_super(), which waits for the superblock to be born before taking s_active. Taking s_active before the superblock is born would pin a still-mounting superblock so a racing mount that aborts could never drop s_active to zero and reach SB_DYING, deadlocking the wait for SB_BORN. This is how filesystems_freeze() and filesystems_thaw() acquire it too. One semantic change: when no live superblock uses the device anymore (the holder is dying or was never registered), fs_bdev_freeze() and fs_bdev_thaw() now return 0 - freeze after syncing the block device - where they used to return -EINVAL. The freeze-deny release path moves to the table in the same switchover. A device made unfreezable for a btrfs membership change must drop its table entry before re-allowing freezing; otherwise a freeze racing the release reaches the superblock through the still-registered entry and is stranded once the release unlinks it. Split fs_bdev_unregister() out of fs_bdev_file_release() - the inverse of fs_bdev_register() - so btrfs_release_device_allow_freeze() can drop the {dev, sb} entry, re-allow freezing on the still-open device, then close it. Re-allowing only after the entry is gone keeps a racing freeze from reaching the superblock, and doing it while the file is still open avoids touching the block device after the close. btrfs previously yielded bd_holder before re-allowing, which this commit makes irrelevant to freeze resolution. Link: https://patch.msgid.link/20260616-work-super-bdev_holder_global-v2-12-7df6b864028e@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Diffstat (limited to 'include')
-rw-r--r--include/linux/fs/super.h1
1 files changed, 1 insertions, 0 deletions
diff --git a/include/linux/fs/super.h b/include/linux/fs/super.h
index caf358483144..733d439f01ed 100644
--- a/include/linux/fs/super.h
+++ b/include/linux/fs/super.h
@@ -242,6 +242,7 @@ struct file *fs_bdev_file_open_by_dev(dev_t dev, blk_mode_t mode, void *holder,
struct super_block *sb);
struct file *fs_bdev_file_open_by_path(const char *path, blk_mode_t mode,
void *holder, struct super_block *sb);
+void fs_bdev_unregister(struct file *bdev_file, struct super_block *sb);
void fs_bdev_file_release(struct file *bdev_file, struct super_block *sb);
#endif /* _LINUX_FS_SUPER_H */