Re: [PATCH v3 1/2] writeback: let foreign flushes reach dying cgwbs

From: Tejun Heo

Date: Tue Sep 29 2026 - 14:01:32 EST


Hello, Liz.

On Tue, Sep 29, 2026 at 01:40:49AM +0000, Liz Fong-Jones wrote:
> if (test_bit(WB_registered, &bdi->wb.state) &&
> blkcg_cgwb_list->next && memcg_cgwb_list->next) {
> - /* we might have raced another instance of this function */
> - ret = radix_tree_insert(&bdi->cgwb_tree, memcg_css->id, wb);
> + /*
> + * We might have raced another instance of this function. A
> + * dying wb keeps its slot until released; take it over.
> + */
> + slot = radix_tree_lookup_slot(&bdi->cgwb_tree, memcg_css->id);
> + if (!slot) {
> + ret = radix_tree_insert(&bdi->cgwb_tree, memcg_css->id, wb);
> + } else {
> + old_wb = radix_tree_deref_slot_protected(slot, &cgwb_lock);
> + if (wb_dying(old_wb)) {
> + radix_tree_replace_slot(&bdi->cgwb_tree, slot, wb);

This can also replace the wb of a removed memcg:

1. A cgroup with io enabled is removed. Killing its io css makes
cgroup_get_e_css() return the parent's right away, but
memcg_cgwb_list->next stays set until wb_memcg_offline().

2. In between, wb_get_create() for the memcg, e.g. from
__inode_attach_wb() or inode_switch_wbs(), kills the dirty wb on
blkcg mismatch and a new wb takes over its slot.

3. wb_memcg_offline() kills the new wb. Foreign flushes for the memcg
now find the new wb while the dirty inodes stay on the old one, as
css_is_dying() keeps wbc_attach_and_unlock_inode() from switching
them, so the stall comes back.

Can you also fail the link when the memcg is dying?

if (test_bit(WB_registered, &bdi->wb.state) &&
blkcg_cgwb_list->next && memcg_cgwb_list->next &&
!css_is_dying(memcg_css)) {

CSS_DYING is set before the io css is killed. Testing it here, after
the blkcg lookup and under cgwb_lock, catches every removal that could
have made the old wb replaceable.

Thanks.

--
tejun