diff options
| author | Christian Brauner <brauner@kernel.org> | 2026-10-12 14:31:19 +0200 |
|---|---|---|
| committer | Christian Brauner <brauner@kernel.org> | 2026-10-22 18:50:18 +0200 |
| commit | c84de57efe88c9c513cfbc3746361502db0978e2 (patch) | |
| tree | 0a50ec683feffc3cd903c5c066ed7dc50dab1866 /include/linux | |
| parent | 073d71c5118f40449d9c7df828677aaa437372e1 (diff) | |
| download | linux-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.h | 11 |
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 */ |
