diff --git a/fs/bcachefs/alloc_background.c b/fs/bcachefs/alloc_background.c index 17bcebbd1f2a..23de3ecc6a1e 100644 --- a/fs/bcachefs/alloc_background.c +++ b/fs/bcachefs/alloc_background.c @@ -1760,7 +1760,7 @@ static void bch2_do_discards_work(struct work_struct *work) void bch2_do_discards(struct bch_fs *c) { if (bch2_write_ref_tryget(c, BCH_WRITE_REF_discard) && - !queue_work(system_long_wq, &c->discard_work)) + !queue_work(c->write_ref_wq, &c->discard_work)) bch2_write_ref_put(c, BCH_WRITE_REF_discard); } @@ -1886,7 +1886,7 @@ err: void bch2_do_invalidates(struct bch_fs *c) { if (bch2_write_ref_tryget(c, BCH_WRITE_REF_invalidate) && - !queue_work(system_long_wq, &c->invalidate_work)) + !queue_work(c->write_ref_wq, &c->invalidate_work)) bch2_write_ref_put(c, BCH_WRITE_REF_invalidate); } diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h index c1f27b4910a0..fcbbc88d77c2 100644 --- a/fs/bcachefs/bcachefs.h +++ b/fs/bcachefs/bcachefs.h @@ -808,6 +808,12 @@ struct bch_fs { struct workqueue_struct *btree_io_complete_wq; /* copygc needs its own workqueue for index updates.. */ struct workqueue_struct *copygc_wq; + /* + * Use a dedicated wq for write ref holder tasks. Required to avoid + * dependency problems with other wq tasks that can block on ref + * draining, such as read-only transition. + */ + struct workqueue_struct *write_ref_wq; /* ALLOCATION */ struct bch_devs_mask rw_devs[BCH_DATA_NR]; diff --git a/fs/bcachefs/ec.c b/fs/bcachefs/ec.c index 1e621dcc1d37..a444f6d513e5 100644 --- a/fs/bcachefs/ec.c +++ b/fs/bcachefs/ec.c @@ -826,7 +826,7 @@ static void ec_stripe_delete_work(struct work_struct *work) void bch2_do_stripe_deletes(struct bch_fs *c) { if (bch2_write_ref_tryget(c, BCH_WRITE_REF_stripe_delete) && - !schedule_work(&c->ec_stripe_delete_work)) + !queue_work(c->write_ref_wq, &c->ec_stripe_delete_work)) bch2_write_ref_put(c, BCH_WRITE_REF_stripe_delete); } 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; } diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c index d6f2f453c027..a209de24064c 100644 --- a/fs/bcachefs/super.c +++ b/fs/bcachefs/super.c @@ -493,6 +493,8 @@ static void __bch2_fs_free(struct bch_fs *c) kfree(c->journal_seq_blacklist_table); kfree(c->unused_inode_hints); + if (c->write_ref_wq) + destroy_workqueue(c->write_ref_wq); if (c->io_complete_wq) destroy_workqueue(c->io_complete_wq); if (c->copygc_wq) @@ -787,6 +789,8 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts) WQ_FREEZABLE|WQ_MEM_RECLAIM|WQ_CPU_INTENSIVE, 1)) || !(c->io_complete_wq = alloc_workqueue("bcachefs_io", WQ_FREEZABLE|WQ_HIGHPRI|WQ_MEM_RECLAIM, 1)) || + !(c->write_ref_wq = alloc_workqueue("bcachefs_write_ref", + WQ_FREEZABLE, 0)) || #ifndef BCH_WRITE_REF_DEBUG percpu_ref_init(&c->writes, bch2_writes_disabled, PERCPU_REF_INIT_DEAD, GFP_KERNEL) ||