Re: [PATCH v7 2/5] block: add task-context bio completion infrastructure
Barry Song <[email protected]>
| Newsgroups | org.kernel.vger.linux-xfs,org.kernel.vger.linux-block,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel,org.kvack.linux-mm,org.ozlabs.lists.linux-erofs |
|---|---|
| Message-ID | <CAGsJ_4wUvLoKGafR3U-ji1kUUaz6K_QdN6QxMT-apSaO5tXp0A@mail.gmail.com> |
On Thu, Jul 30, 2026 at 3:37 PM Tal Zussman <[email protected]> wrote: > > Some bio completion handlers need to run from preemptible task context, > but bio_endio() may be called from IRQ context (e.g., buffer_head > writeback). Callers need a way to ensure their callback eventually runs > from a sleepable context. Add infrastructure for that, in two forms: > > 1. BIO_COMPLETE_IN_TASK, a bio flag the submitter sets when it knows > in advance that its callback needs task context (e.g., dropbehind > writeback). bio_endio() sees the flag and offloads completion to a > worker automatically. > > 2. bio_complete_in_task(), a helper that completion callbacks can > invoke from within bi_end_io() when the deferral decision is > dynamic (e.g., fserror reporting). > > Both share a per-CPU list drained by a work item on a WQ_PERCPU > workqueue. Producers push the bio onto the local CPU's list and schedule > the work item, which then dispatches each bio's bi_end_io() from task > context. > > Both methods are gated on bio_in_atomic(), which returns true in any > context where a sleeping bi_end_io() is unsafe, including > non-preemptible task context. > > Two CPU hotplug callbacks are used to drain remaining bios from the > departing CPU's batch, while maintaining the per-CPU behavior. The > CPUHP_AP_ONLINE_DYN callback disables the per-CPU work item while the > CPU is still online, preventing it from running on an unbound worker > later. CPUHP_BP_PREPARE_DYN then drains any bios added between disabling > the work item and CPU offline. Hi Tal, Thanks very much for the patch. I’m just a bit confused that folio_end_dropbehind() only requires in_task(), and nothing more. void folio_end_dropbehind(struct folio *folio) { if (!folio_test_dropbehind(folio)) return; /* * Hitting !in_task() should not happen off RWF_DONTCACHE writeback, * but can happen if normal writeback just happens to find dirty folios * that were created as part of uncached writeback, and that writeback * would otherwise not need non-IRQ handling. Just skip the * invalidation in that case. */ if (in_task() && folio_trylock(folio)) { filemap_end_dropbehind(folio); folio_unlock(folio); } } But we’re applying a much stricter check with bio_in_atomic(): +static inline bool bio_in_atomic(void) +{ + if (IS_ENABLED(CONFIG_PREEMPTION) && rcu_preempt_depth()) + return true; + if (!IS_ENABLED(CONFIG_PREEMPT_COUNT)) + return true; + return !preemptible(); +} I feel like something may be missing either in folio_end_dropbehind() itself or somewhere in the filesystem/block-device path. Do you know why they don’t match each other? Best Regards Barry