diff options
author | Brian Foster <bfoster@redhat.com> | 2023-03-23 14:09:05 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2023-10-22 17:09:58 -0400 |
commit | 8bff9875a695ce9c6635693ff45fb3196688c1c6 (patch) | |
tree | f4487bde634ff9b5cd229d5a737dc07ee8fb7276 /fs/bcachefs/subvolume.c | |
parent | 76c70c57f093d26fcb5a1aac75db12a5caa5614d (diff) | |
download | linux-8bff9875a695ce9c6635693ff45fb3196688c1c6.tar.gz linux-8bff9875a695ce9c6635693ff45fb3196688c1c6.tar.bz2 linux-8bff9875a695ce9c6635693ff45fb3196688c1c6.zip |
bcachefs: use dedicated workqueue for tasks holding write refs
A workqueue resource deadlock has been observed when running fsck
on a filesystem with a full/stuck journal. fsck is not currently
able to repair the fs due to fairly rapid emergency shutdown, but
rather than exit gracefully the fsck process hangs during the
shutdown sequence. Fortunately this is easily recoverable from
userspace, but the root cause involves code shared between the
kernel and userspace and so should be addressed.
The deadlock scenario involves the main task in the bch2_fs_stop()
-> bch2_fs_read_only() path waiting on write references to drain
with the fs state lock held. A bch2_read_only_work() workqueue task
is scheduled on the system_long_wq, blocked on the state lock.
Finally, various other write ref holding workqueue tasks are
scheduled to run on the same workqueue and must complete in order to
release references that the initial task is waiting on.
To avoid this problem, we can split the dependent workqueue tasks
across different workqueues. It's a bit of a waste to create a
dedicated wq for the read-only worker, but there are several tasks
throughout the fs that follow the pattern of acquiring a write
reference and then scheduling to the system wq. Use a local wq
for such tasks to break the subtle dependency between these and the
read-only worker.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
Diffstat (limited to 'fs/bcachefs/subvolume.c')
-rw-r--r-- | fs/bcachefs/subvolume.c | 4 |
1 files changed, 2 insertions, 2 deletions
diff --git a/fs/bcachefs/subvolume.c b/fs/bcachefs/subvolume.c index 43d83705a7ae..6407d19edc0e 100644 --- a/fs/bcachefs/subvolume.c +++ b/fs/bcachefs/subvolume.c @@ -714,7 +714,7 @@ static void bch2_delete_dead_snapshots_work(struct work_struct *work) void bch2_delete_dead_snapshots_async(struct bch_fs *c) { if (bch2_write_ref_tryget(c, BCH_WRITE_REF_delete_dead_snapshots) && - !queue_work(system_long_wq, &c->snapshot_delete_work)) + !queue_work(c->write_ref_wq, &c->snapshot_delete_work)) bch2_write_ref_put(c, BCH_WRITE_REF_delete_dead_snapshots); } @@ -926,7 +926,7 @@ int bch2_subvolume_wait_for_pagecache_and_delete_hook(struct btree_trans *trans, if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_snapshot_delete_pagecache)) return -EROFS; - if (!queue_work(system_long_wq, &c->snapshot_wait_for_pagecache_and_delete_work)) + if (!queue_work(c->write_ref_wq, &c->snapshot_wait_for_pagecache_and_delete_work)) bch2_write_ref_put(c, BCH_WRITE_REF_snapshot_delete_pagecache); return 0; } |