Re: [PATCH RFC v4] bcache: flush backing device before cleaning the writeback dirty keys
"Coly Li" <[email protected]> Thu, 9 Jul 2026 14:30:41 +0800
| Newsgroups | org.kernel.vger.linux-bcache |
|---|---|
| Message-ID | <[email protected]> |
> 2026=E5=B9=B47=E6=9C=889=E6=97=A5 14:23=EF=BC=8CZhou Jifeng <zhoujifeng@k= ylinos.com.cn> =E5=86=99=E9=81=93=EF=BC=9A >=20 > From: Zhou Jifeng <[email protected]> >=20 > Currently, when writeback dirty data completes, write_dirty_finish() > immediately inserts the clean key into the btree. If a power failure > occurs after the clean-key insert but before the backing device has > flushed its volatile write cache, the btree contains a clean key that > points at backing data that never reached stable media. On the next > read, bcache serves stale data from the backing device. >=20 > Fix this by deferring the clean-key btree insert until after an > explicit backing-device flush (REQ_PREFLUSH). Written keys are first > parked in a new rbtree (writeback_flush_pending) keyed by START_KEY. > After enough read_dirty() passes, a full btree scan, or a wrap-around > in refill_dirty(), writeback_finish_batch() issues a synchronous > backing-device flush, and only on success inserts the clean keys into > the btree and removes them from the pending tree. On flush failure > the entries are re-inserted into the current tree for a later retry. >=20 > Key changes: >=20 > - Introduce struct writeback_pending (separate kmalloc'd allocation, > decoupled from the dirty_io closure so the closure can be freed > immediately and the pending entry lives until the flush). >=20 > - Add __writeback_pending_insert() =E2=80=94 inserts into the START_KEY- > ordered rbtree with overlap detection. The rbtree is also used as > an interval tree by bch_writeback_drop_pending() for front-end > write overlap queries; that search is correct only because dirty > btree keys are pairwise disjoint, an invariant enforced at insert > time. >=20 > - writeback_add_to_pending() parks a key after successful writeback > IO. Uses GFP_NOIO because the caller runs on the WQ_MEM_RECLAIM > writeback workqueue. kmalloc failure silently drops the key (it > stays dirty and is re-selected by the next scan). >=20 > - writeback_finish_batch() detaches the entire pending tree into a > local list under spinlock, issues writeback_flush(), then either > cleans the keys (on success) or re-inserts them (on failure). >=20 > - writeback_flush() open-codes blkdev_issue_flush() to call > bch_count_backing_io_errors() on failure. >=20 > - bch_writeback_drop_pending() drops overlapping entries from the > pending tree. Called from cached_dev_write() alongside the > existing bch_keybuf_check_overlapping(); overlapping front-end > writes now force writeback instead of bypassing the cache. >=20 > - refill_dirty() now returns enum writeback_scan_result (PARTIAL, > PARTIAL_WRAP, FULL_SEARCHED) instead of a boolean. Before > wrapping around for a second scan pass, it flushes the pending > tree so the wrap scan won't re-select keys that are still > KEY_DIRTY in the btree but already written to backing and > awaiting flush. writeback_lock may be transiently released > mid-function during this pre-wrap flush. >=20 > - refill_full_stripes() no longer wraps internally; it returns > at the end of the stripe range and delegates the wrap to > refill_dirty(). buf->last_scanned is no longer rewound past > the current position to avoid re-selecting keys already in > writeback_flush_pending. >=20 > - read_dirty() gates deferred flush-and-clean on PASS_PER_FLUSH > (default 5) passes, a full btree scan, or a PARTIAL_WRAP. It > accepts the scan_result from refill_dirty() as a parameter. >=20 > - write_dirty_finish() is simplified: it no longer inserts clean > keys inline; it only calls writeback_add_to_pending(). >=20 > - BDEV_STATE_CLEAN eligibility in the main loop now also requires > writeback_pending_empty(), so a device is not marked clean while > entries are still in the pending tree. >=20 > - Thread exit: the workqueue is destroyed first, then a final > writeback_finish_batch() is attempted (skipped when the cache > set is disabled or the device is detaching), then any remaining > pending entries are drained. The exit path intentionally does > NOT set BDEV_STATE_CLEAN because it lacks the WB_SCAN_FULL_SEARCHED > gate that the in-loop check requires; a kthread_stop mid-scan > (e.g. from bch_cached_dev_attach's error path) must not mark a > still-dirty device clean. >=20 > Signed-off-by: Coly Li <[email protected]> > Signed-off-by: Zhou Jifeng <[email protected]> You cannot add my SOB for me. I know you mean the base idea of this version= was from me, but it is fine to only do your own SOB. I am good with this, since= you already do a lot of analysis and testing. The v4 version format is much more easier to review. I will try my best to response you soon. Thanks for the fix up and testing. Coly Li