Re: [PATCH v4 1/6] fs: enhance and rename shutdown() callback to remove_bdev()

Qu Wenruo
2025-07-10T09:55:24Z

在 2025/7/10 18:10, Christian Brauner 写道:
> On Tue, Jul 08, 2025 at 01:20:50PM -0700, Darrick J. Wong wrote:
>> On Tue, Jul 08, 2025 at 12:20:00PM +0200, Jan Kara wrote:
>>> On Mon 07-07-25 17:45:32, Darrick J. Wong wrote:
>>>> On Tue, Jul 08, 2025 at 08:52:47AM +0930, Qu Wenruo wrote:
>>>>> 在 2025/7/8 08:32, Dave Chinner 写道:
>>>>>> On Fri, Jul 04, 2025 at 10:12:29AM +0930, Qu Wenruo wrote:
>>>>>>> Currently all the filesystems implementing the
>>>>>>> super_opearations::shutdown() callback can not afford losing a device.
>>>>>>>
>>>>>>> Thus fs_bdev_mark_dead() will just call the shutdown() callback for the
>>>>>>> involved filesystem.
>>>>>>>
>>>>>>> But it will no longer be the case, with multi-device filesystems like
>>>>>>> btrfs and bcachefs the filesystem can handle certain device loss without
>>>>>>> shutting down the whole filesystem.
>>>>>>>
>>>>>>> To allow those multi-device filesystems to be integrated to use
>>>>>>> fs_holder_ops:
>>>>>>>
>>>>>>> - Replace super_opearation::shutdown() with
>>>>>>> super_opearations::remove_bdev()
>>>>>>> To better describe when the callback is called.
>>>>>>
>>>>>> This conflates cause with action.
>>>>>>
>>>>>> The shutdown callout is an action that the filesystem must execute,
>>>>>> whilst "remove bdev" is a cause notification that might require an
>>>>>> action to be take.
>>>>>>
>>>>>> Yes, the cause could be someone doing hot-unplug of the block
>>>>>> device, but it could also be something going wrong in software
>>>>>> layers below the filesystem. e.g. dm-thinp having an unrecoverable
>>>>>> corruption or ENOSPC errors.
>>>>>>
>>>>>> We already have a "cause" notification: blk_holder_ops->mark_dead().
>>>>>>
>>>>>> The generic fs action that is taken by this notification is
>>>>>> fs_bdev_mark_dead(). That action is to invalidate caches and shut
>>>>>> down the filesystem.
>>>>>>
>>>>>> btrfs needs to do something different to a blk_holder_ops->mark_dead
>>>>>> notification. i.e. it needs an action that is different to
>>>>>> fs_bdev_mark_dead().
>>>>>>
>>>>>> Indeed, this is how bcachefs already handles "single device
>>>>>> died" events for multi-device filesystems - see
>>>>>> bch2_fs_bdev_mark_dead().
>>>>>
>>>>> I do not think it's the correct way to go, especially when there is already
>>>>> fs_holder_ops.
>>>>>
>>>>> We're always going towards a more generic solution, other than letting the
>>>>> individual fs to do the same thing slightly differently.
>>>>
>>>> On second thought -- it's weird that you'd flush the filesystem and
>>>> shrink the inode/dentry caches in a "your device went away" handler.
>>>> Fancy filesystems like bcachefs and btrfs would likely just shift IO to
>>>> a different bdev, right? And there's no good reason to run shrinkers on
>>>> either of those fses, right?
>>>
>>> I agree it is awkward and bcachefs avoids these in case of removal it can
>>> handle gracefully AFAICS.
>>>
>>>>> Yes, the naming is not perfect and mixing cause and action, but the end
>>>>> result is still a more generic and less duplicated code base.
>>>>
>>>> I think dchinner makes a good point that if your filesystem can do
>>>> something clever on device removal, it should provide its own block
>>>> device holder ops instead of using fs_holder_ops. I don't understand
>>>> why you need a "generic" solution for btrfs when it's not going to do
>>>> what the others do anyway.
>>>
>>> Well, I'd also say just go for own fs_holder_ops if it was not for the
>>> awkward "get super from bdev" step. As Christian wrote we've encapsulated
>>> that in fs/super.c and bdev_super_lock() in particular but the calling
>>> conventions for the fs_holder_ops are not very nice (holding
>>> bdev_holder_lock, need to release it before grabbing practically anything
>>> else) so I'd have much greater peace of mind if this didn't spread too
>>> much. Once you call bdev_super_lock() and hold on to sb with s_umount held,
>>> things are much more conventional for the fs land so I'd like if this
>>> step happened before any fs hook got called. So I prefer something like
>>> Qu's proposal of separate sb op for device removal over exporting
>>> bdev_super_lock(). Like:
>>>
>>> static void fs_bdev_mark_dead(struct block_device *bdev, bool surprise)
>>> {
>>> struct super_block *sb;
>>>
>>> sb = bdev_super_lock(bdev, false);
>>> if (!sb)
>>> return;
>>>
>>> if (sb->s_op->remove_bdev) {
>>> sb->s_op->remove_bdev(sb, bdev, surprise);
>>> return;
>>> }
>>
>> It feels odd but I could live with this, particularly since that's the
>> direction that brauner is laying down. :)
>
> I want to reiterate that no one is saying "under no circumstances
> implement your own holder ops". But if you rely on the VFS locking then
> you better not spill it's guts into your filesystem and make us export
> this bloody locking that half the world had implemented wrong in their
> drivers in the first places spewing endless syzbot deadlocks reports
> that we had to track down and fix. That will not happen again similar
> way we don't bleed all the nastiness of other locking paths.
>
> Please all stop long philosophical treatises about things no on has ever
> argued. btrfs wants to rely on the VFS infra. That is fine and well. We
> will support and enable this.
>
> I think the two method idea is fine given that they now are clearly
> delineated.
>
> Thanks for providing some clarity here, Darrick and Qu.
>

So the next update would be something like this for fs_bdev_mark_dead():

sb = bdev_super_lock();
if (!sb)
return;
if (!surprise)
sync_filesystem(sb);
+ if (sb->s_op->remove_bdev) {
+ ret = sb->s_op->remove_bdev();
+ if (!ret) {
+ /* Fs can handle the dev loss. */
+ super_unlock_shared();
+ return;
+ }
+ }
+ /* Fs can not handle the dev loss, shutdown. */
shrink_dcache_sb();
evict_inodes();
if (sb->s_op->shutdown)
sb->s_op->shutdown();
super_unlock_shared();

This means ->remove_bdev() must have a return value to indicate if the
fs can handle the loss.
And any error, no matter if it's not enough tolerance from the fs or
some other problem during the dev loss handling, the old shutdown
behavior will be triggered.

Would this be an acceptable solution?

Thanks,
Qu