summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2026-10-12 14:31:19 +0200
committerChristian Brauner <brauner@kernel.org>2026-10-22 18:50:18 +0200
commitc84de57efe88c9c513cfbc3746361502db0978e2 (patch)
tree0a50ec683feffc3cd903c5c066ed7dc50dab1866 /include/linux
parent073d71c5118f40449d9c7df828677aaa437372e1 (diff)
downloadlinux-c84de57efe88c9c513cfbc3746361502db0978e2.tar.gz
linux-c84de57efe88c9c513cfbc3746361502db0978e2.zip
super: convert to plain hashtable
Convert the rhltable to a plain hashtable under a spinlock. The walks keep their place with a cursor entry in the chain. A claim is unhashed the moment it's dropped and a walk that finds a superblock that isn't born yet waits for it to be born or to die and looks again. Since hashing can't fail anymore the entry for the superblock's own s_dev is embedded in struct super_block and the preallocation in alloc_super() goes away. This all simplifies things quite a lot. It also fixes a btrfs bug where fs_bdev_freeze() ended up freezing a btrfs filesystem through a stale member that open_ctree() had already released while the freeze was waiting for SB_BORN. The resume doesn't find a holder for that device anymore so the filesystem stays frozen for good. The walks return superblocks instead of table entries now and user_get_super() hands out the passive reference the walk took. Reproducer, a three device btrfs with a stale copy of a removed member. dm-delay under the remaining members stretches the window between btrfs_open_devices() and btrfs_free_extra_devids(): truncate -s 1G a1 a2 b A1=$(losetup -f --show a1); A2=$(losetup -f --show a2); B=$(losetup -f --show b) mkfs.btrfs -f -d single -m single $A1 $A2 $B cp b b.old mount $A1 /mnt && btrfs device remove 3 /mnt && umount /mnt OLD=$(losetup -f --show b.old) dmsetup create d1 --table "0 $(blockdev --getsz $A1) delay $A1 0 200" dmsetup create d2 --table "0 $(blockdev --getsz $A2) delay $A2 0 200" dmsetup create old --table "0 $(blockdev --getsz $OLD) linear $OLD 0" mount -o device=/dev/mapper/d2,device=/dev/mapper/old /dev/mapper/d1 /mnt & sleep 1; dmsetup suspend old; wait dmsetup resume old touch /mnt/foo # hangs until fsfreeze -u /mnt That left btrfs frozen in 5 of 5 rounds with the same tables issued through raw dm ioctls. The -o degraded variant needs the mount held between the insertion and the bd_fsfreeze_count check in fs_bdev_register() with a kprobe. Fixes: cdb5146f8d5f ("fs: look up superblocks via the device table in fs_holder_ops") Link: https://patch.msgid.link/20261012-work-super-dev_hash-v1-2-305cf71cd334@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/fs/super_types.h11
1 files changed, 9 insertions, 2 deletions
diff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h
index ecd96aeb1cee..72f4288e16c5 100644
--- a/include/linux/fs/super_types.h
+++ b/include/linux/fs/super_types.h
@@ -30,7 +30,6 @@ struct mount;
struct mtd_info;
struct quotactl_ops;
struct shrinker;
-struct super_dev;
struct unicode_map;
struct user_namespace;
struct workqueue_struct;
@@ -132,10 +131,18 @@ struct super_operations {
void (*report_error)(const struct fserror_event *event);
};
+/* A superblock's claim on a device, see fs/super.c. */
+struct super_dev {
+ struct hlist_node sd_hash;
+ struct super_block *sd_sb;
+ dev_t sd_dev;
+ unsigned int sd_claims;
+};
+
struct super_block {
struct list_head s_list; /* Keep this first */
dev_t s_dev; /* search index; _not_ kdev_t */
- struct super_dev *s_super_dev; /* sget_fc()'s device table claim */
+ struct super_dev s_super_dev; /* claim on s_dev */
unsigned char s_blocksize_bits;
unsigned long s_blocksize;
loff_t s_maxbytes; /* Max file size */