freenode
Kernel & Low-Level

Kernel stops treating failed buffer writes as stale memory

A 21-patch series stops clearing BH_Uptodate on metadata write failure so filesystems keep the data they meant to write and report errors via BH_Write_EIO instead.

When a metadata write fails in the Linux buffer cache, completion handlers have long cleared the BH_Uptodate flag. That tells the rest of the kernel the in-memory buffer is wrong. In reality the buffer still holds exactly what the filesystem asked to write; the disk is stale. Chao Shi's 21-patch series stops that practice and moves every consumer onto BH_Write_EIO, the flag that already means the last write failed.

The old behaviour caused real damage. Retrying a failed write by dirtying the buffer again trips a WARN_ON in mark_buffer_dirty(). Worse, a buffer marked not up to date can be re-read from disk, silently replacing the data the filesystem was trying to persist with the stale on-disk copy. Between completion and the clear there is also a window of inconsistent state visible under the folio lock.

The series first makes BH_Write_EIO safe to leave set across free and invalidate paths, then converts the core buffer helpers and the filesystems that still sniffed write failure through BH_Uptodate: adfs, ext2, omfs, exfat, fat, ext4, ocfs2, gfs2, and jbd2, including private completion handlers used by the journal and ext4 fast commit. Only after those conversions does write completion leave BH_Uptodate alone in both directions.

A final change moves clearing of BH_Write_EIO from write submission to successful completion, so a concurrent resubmit can no longer hide an error that has not been fixed yet. For gfs2 the switch also starts catching failed log writes that its own completion path already marked but that the old uptodate tests missed, causing a withdraw as intended rather than a silent pass.

The work was found via FuzzNvme. Jan Kara reviewed much of the series and suggested parts of the ocfs2 and jbd2 handling. Until the final behavioural cutover each step is intended to leave the tree working as before, because failed writes previously set BH_Write_EIO and cleared BH_Uptodate together.