Re: [PATCH v7 2/5] block: add task-context bio completion infrastructure
From: Barry Song
Date: Sun Aug 16 2026 - 00:12:17 EST
On Thu, Jul 30, 2026 at 3:37 PM Tal Zussman <tz2294@xxxxxxxxxxxx> 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