Skip to content

block,btrfs: fix frozen-superblock strand on device add/remove/replace#967

Open
blktests-ci[bot] wants to merge 6 commits into
linus-master_basefrom
series/1111715=>linus-master
Open

block,btrfs: fix frozen-superblock strand on device add/remove/replace#967
blktests-ci[bot] wants to merge 6 commits into
linus-master_basefrom
series/1111715=>linus-master

Conversation

@blktests-ci

@blktests-ci blktests-ci Bot commented Jun 15, 2026

Copy link
Copy Markdown

Pull request for series with
subject: block,btrfs: fix frozen-superblock strand on device add/remove/replace
version: 1
url: https://patchwork.kernel.org/project/linux-block/list/?series=1111715

@blktests-ci

blktests-ci Bot commented Jun 15, 2026

Copy link
Copy Markdown
Author

Upstream branch: 062871f
series: https://patchwork.kernel.org/project/linux-block/list/?series=1111715
version: 1

@blktests-ci

blktests-ci Bot commented Jun 15, 2026

Copy link
Copy Markdown
Author

Upstream branch: 062871f
series: https://patchwork.kernel.org/project/linux-block/list/?series=1111715
version: 1

@blktests-ci blktests-ci Bot force-pushed the series/1111715=>linus-master branch from f7d71cb to fdf18d2 Compare June 15, 2026 14:53
@blktests-ci blktests-ci Bot force-pushed the linus-master_base branch 2 times, most recently from 199644a to e6d9eb8 Compare June 17, 2026 12:02
@blktests-ci

blktests-ci Bot commented Jun 17, 2026

Copy link
Copy Markdown
Author

Upstream branch: 66affa3
series: https://patchwork.kernel.org/project/linux-block/list/?series=1112269
version: 2

@blktests-ci blktests-ci Bot added V2 and removed V1 labels Jun 17, 2026
@blktests-ci blktests-ci Bot force-pushed the series/1111715=>linus-master branch from fdf18d2 to d5d7683 Compare June 17, 2026 12:17
@blktests-ci

blktests-ci Bot commented Jun 18, 2026

Copy link
Copy Markdown
Author

Upstream branch: 66affa3
series: https://patchwork.kernel.org/project/linux-block/list/?series=1112269
version: 2

@blktests-ci blktests-ci Bot force-pushed the series/1111715=>linus-master branch from d5d7683 to 71f626c Compare June 18, 2026 12:59
@blktests-ci

blktests-ci Bot commented Jun 18, 2026

Copy link
Copy Markdown
Author

Upstream branch: 66affa3
series: https://patchwork.kernel.org/project/linux-block/list/?series=1112269
version: 2

@blktests-ci blktests-ci Bot force-pushed the series/1111715=>linus-master branch from 71f626c to c3c0fd0 Compare June 18, 2026 13:43
@blktests-ci blktests-ci Bot force-pushed the linus-master_base branch from e6d9eb8 to 7d8604f Compare June 24, 2026 01:11
@blktests-ci

blktests-ci Bot commented Jun 24, 2026

Copy link
Copy Markdown
Author

Upstream branch: bade58e
series: https://patchwork.kernel.org/project/linux-block/list/?series=1112269
version: 2

@blktests-ci blktests-ci Bot force-pushed the series/1111715=>linus-master branch from c3c0fd0 to 563d37b Compare June 24, 2026 01:32
@blktests-ci blktests-ci Bot force-pushed the linus-master_base branch from 7d8604f to 4cc45a3 Compare June 26, 2026 08:14
@blktests-ci

blktests-ci Bot commented Jun 26, 2026

Copy link
Copy Markdown
Author

Upstream branch: 4edcdef
series: https://patchwork.kernel.org/project/linux-block/list/?series=1112269
version: 2

@blktests-ci blktests-ci Bot force-pushed the series/1111715=>linus-master branch from 563d37b to e8254d1 Compare June 26, 2026 08:50
@blktests-ci blktests-ci Bot force-pushed the linus-master_base branch from 4cc45a3 to 90ffd56 Compare June 29, 2026 17:14
@blktests-ci

blktests-ci Bot commented Jun 29, 2026

Copy link
Copy Markdown
Author

Upstream branch: dc59e4f
series: https://patchwork.kernel.org/project/linux-block/list/?series=1112269
version: 2

@blktests-ci blktests-ci Bot force-pushed the series/1111715=>linus-master branch from e8254d1 to 9b78d5a Compare June 29, 2026 17:54
@blktests-ci blktests-ci Bot force-pushed the linus-master_base branch from 90ffd56 to d52f35a Compare July 2, 2026 19:18
@blktests-ci

blktests-ci Bot commented Jul 2, 2026

Copy link
Copy Markdown
Author

Upstream branch: 87320be
series: https://patchwork.kernel.org/project/linux-block/list/?series=1112269
version: 2

@blktests-ci blktests-ci Bot force-pushed the series/1111715=>linus-master branch from 9b78d5a to 7e092b1 Compare July 2, 2026 19:53
@blktests-ci blktests-ci Bot force-pushed the linus-master_base branch from d52f35a to a644c13 Compare July 6, 2026 08:28
brauner added 5 commits July 6, 2026 09:30
Add bdev_deny_freeze() and bdev_allow_freeze(), modeled on
deny_write_access()/allow_write_access().  bd_fsfreeze_count becomes a
signed counter: > 0 counts active freezes, < 0 counts deniers, and the
two regimes are mutually exclusive.  bdev_freeze() refuses with -EBUSY
while a deny is held, and bdev_deny_freeze() refuses while the device is
frozen.

A filesystem that mutates a device's membership (a btrfs device add,
remove or replace) denies freezing on the device for the duration, so a
claim a freeze walk might act on is never added or torn down behind the
freezer's back.

The deny/allow helpers are a single atomic on bd_fsfreeze_count and take
no lock, so they can be called while holding s_umount without inverting
against bdev_freeze()'s bd_fsfreeze_mutex -> s_umount order.

Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
bdev_fput() yields the holder claim and then closes the file, which is a
deferred operation.  Split the yield half into bdev_yield_claim() so a caller
can give up the holder while the file - and therefore the block device - is
still open, act on the device, and only then bdev_fput().

A filesystem that made a device unfreezable for a membership change with
bdev_deny_freeze() undoes the deny on release with

	bdev_yield_claim(bdev_file);
	bdev_allow_freeze(file_bdev(bdev_file));
	bdev_fput(bdev_file);

Re-allowing only after the holder is yielded avoids stranding the filesystem
on a racing freeze, and doing it while the file is still open avoids touching
the block device after bdev_fput().  bdev_fput() yields again, which is a
no-op once the claim has already been given up.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Reviewed-by: Jan Kara <jack@suse.cz>
btrfs_rm_device() runs under mnt_want_write_file(), but the claim on the
removed device is released by the ioctl after mnt_drop_write_file(), so a
bdev_freeze() racing that window could freeze the filesystem through the
device just as its claim is torn down, leaving nothing for bdev_thaw() to
rebalance.

The window cannot be closed by reordering the teardown.  btrfs_rm_device()
hands the final bdev_fput() back to the ioctl, run only after
mnt_drop_write_file(), because bdev_release() takes the disk ->open_mutex and
its dependency chain, which must not nest under the superblock's freeze/write
protection -- freeze_super() drops s_umount before draining writers precisely
to keep sb_start_write ordered above s_umount.  Holding mnt_want_write across
bdev_fput() would reintroduce that inversion, so the holder teardown is forced
outside the write-protected section.  A freeze landing in the resulting gap
resolves the still-live holder, rides in, and strands when the claim is
released; no ordering of the close against the drop removes the gap.  The
device itself therefore has to refuse freezing for the whole removal.

Deny freezing the device for the duration of the removal: bdev_deny_freeze()
at the start of btrfs_rm_device() (it cannot be frozen yet, the ioctl holds
the write count), and release it through btrfs_release_device_allow_freeze()
in the ioctls on success, or bdev_allow_freeze() on the error paths that keep
the device a member.  A device frozen before the removal begins is refused
with -EBUSY.

btrfs_release_device_allow_freeze() yields the holder, re-allows freezing,
then closes the device, so the re-allow neither strands the filesystem on a
racing freeze nor touches the block device after the final fput.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
btrfs_init_new_device() opens and claims the new device on a live
superblock without holding the write count, so a bdev_freeze() racing the
window between the claim being published and the device becoming a member
could freeze the filesystem through a claim the add may still abort and tear
down.

Add btrfs_open_device_deny_freeze(): it opens the device once
non-exclusively to take the freeze deny, then claims it by the same dev_t,
so the holder is only ever published while the device is already
unfreezable.  Keep it denied until the add is durable: bdev_allow_freeze()
on each success return (the device is now a committed member),
btrfs_release_device_allow_freeze() on the error unwind.  The deny spans the
whole add, including the seeding tail whose late failures still release the
device.  A device already frozen when the add starts is refused with -EBUSY.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
A device replace opens a target and, on success, frees the source on a live
filesystem from btrfs_dev_replace_finishing() - which cannot fail and also
runs from a kthread on mount resume.  A bdev_freeze() racing the source free
or the target swap-in would freeze the filesystem through a claim that is
being torn down or replaced, leaving nothing for bdev_thaw() to rebalance.

Make both devices unfreezable for the whole replace, with the invariant that
a STARTED replace holds one deny on each device and any other state holds
none.  The target is denied at open (btrfs_open_device_deny_freeze(), undone
on btrfs_init_dev_replace_tgtdev()'s error unwind); the source is denied at
the start of btrfs_dev_replace_start(), before mark_block_group_to_copy() so
every 'leave' unwind sees both denied.

The deny tracks the STARTED state and is dropped whenever the replace leaves
it: btrfs_dev_replace_finishing() re-allows the target it makes a member and
frees the source through btrfs_close_bdev(allow_freeze=true), and its
scrub-error path re-allows both as it cancels.  Its early failures (before
the device swap) keep the replace STARTED and resumable, so both stay denied.
Suspending for unmount re-allows both, so they are reopened freezable at the
next mount where btrfs_resume_dev_replace_async() re-denies them (staying
suspended if a device is frozen right then); a replace cancelled from the
suspended state therefore destroys the target without allowing.
btrfs_close_bdev() and btrfs_destroy_dev_replace_tgtdev() take an allow_freeze
argument to carry this distinction; the unmount path
(btrfs_close_one_device()) passes false.

On resume, a failed kthread_run() re-allows both devices and goes through the
suspend path, resetting the replace to SUSPENDED and finishing the exclusive
operation instead of returning straight away.  The (re)mount still aborts on
that error; routing it through suspend keeps the deny balanced against the
unmount teardown and additionally drops BTRFS_EXCLOP_DEV_REPLACE, closing a
pre-existing leak that was harmless on the failed mount that frees the fs but
would have wedged future exclusive operations after a failed remount-rw.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
@blktests-ci

blktests-ci Bot commented Jul 6, 2026

Copy link
Copy Markdown
Author

Upstream branch: 8cdeaa5
series: https://patchwork.kernel.org/project/linux-block/list/?series=1112269
version: 2

@blktests-ci blktests-ci Bot force-pushed the series/1111715=>linus-master branch from 7e092b1 to 64d7112 Compare July 6, 2026 09:30
@blktests-ci blktests-ci Bot force-pushed the linus-master_base branch from a644c13 to a2204c3 Compare July 7, 2026 08:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant