diff options
| author | Christian Brauner <brauner@kernel.org> | 2026-08-31 10:19:24 +0200 |
|---|---|---|
| committer | Christian Brauner <brauner@kernel.org> | 2026-09-10 09:27:27 +0200 |
| commit | 00db7c70bf7c6a79a548f484c76af60563a7736e (patch) | |
| tree | 6668f2f26794f2daa6d5a7bd9b11e88ef68d36e5 /include/linux | |
| parent | cee9395acd8043be0644b25c34bfa86623f2b935 (diff) | |
| parent | 336e794c59173407d22cfc1f254c517c264ef9ed (diff) | |
Merge patch series "buffer: stop clearing BH_Uptodate when a write fails"
Chao Shi <coshi036@gmail.com> says:
When a metadata write fails, the buffer_head completion handlers clear
BH_Uptodate. The buffer still holds exactly the data the filesystem asked
to be written - it is the disk that is stale, not the buffer - and saying
otherwise has consequences:
- mark_buffer_dirty() has a WARN_ON_ONCE(!buffer_uptodate(bh)), so a
filesystem that dirties the buffer again to retry the write trips it.
That is the warning that started this.
- a buffer that is not up to date gets re-read from disk, which silently
replaces the data the filesystem was trying to write with the stale
on-disk copy.
- the state is not self consistent while it lasts: the window between the
write completing and BH_Uptodate being cleared is visible to anyone
holding the folio lock.
BH_Write_EIO already records that the last write failed. This series moves
every consumer over to it, then stops write completion touching BH_Uptodate
at all.
Patches 1-4 are groundwork. Matthew's patch 1 removes b_page. Patches 2
and 3 let a buffer_head point at memory outside the page cache and use that
for jbd2's shadow buffers, which today sit on a slab folio - a slab folio
overloads ->mapping, so mark_buffer_write_io_error() cannot be called on
them at all. That is what blocked the jbd2 conversion. Patch 4 then drops
the folio_mapping() call that was only there to cope with those slab
folios.
Patches 5-6 make BH_Write_EIO safe to leave set: clear it in bforget() and
discard it on invalidate, so a freed or reused block does not inherit
somebody else's write error.
Patches 7-19 convert the consumers, one filesystem at a time: the two core
helpers in fs/buffer.c, then adfs, ext2, omfs, exfat, fat, ext4, ocfs2,
gfs2 and jbd2. Patch 20 stops write completion touching BH_Uptodate.
Patch 21 moves the point at which BH_Write_EIO is cleared from submission
to successful completion.
Every patch builds on its own and the tree behaves identically at each step
until patch 20, because a failed write currently sets BH_Write_EIO and
clears BH_Uptodate together.
* patches from https://patch.msgid.link/cover.1785951556.git.coshi036@gmail.com: (21 commits)
buffer: clear BH_Write_EIO when a write succeeds, not when one starts
buffer: stop touching BH_Uptodate on write completion
jbd2: check for a fast commit write error with buffer_write_io_error()
jbd2: say what jbd2_freeze_jh_data()'s assertion is actually checking
jbd2: report journal write errors with BH_Write_EIO
gfs2: check for a metadata write error with buffer_write_io_error()
ocfs2: check for a stale write error before reusing a metadata buffer
ocfs2: check for a metadata write error with buffer_write_io_error()
ext4: check for a metadata write error with buffer_write_io_error()
fat: check for a metadata write error with buffer_write_io_error()
exfat: check for a directory write error with buffer_write_io_error()
omfs: check for an inode write error with buffer_write_io_error()
ext2: check for an xattr block write error with buffer_write_io_error()
adfs: check for a directory write error with buffer_write_io_error()
buffer: detect metadata write errors with buffer_write_io_error()
buffer: discard BH_Write_EIO along with the rest of the buffer state
buffer: clear BH_Write_EIO when a buffer is forgotten
buffer: read the folio's mapping directly in buffer_set_crypto_ctx()
jbd2: point the shadow buffer at the frozen data directly
buffer: allow a buffer_head to point at memory outside the page cache
...
Link: https://patch.msgid.link/cover.1785951556.git.coshi036@gmail.com
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Diffstat (limited to 'include/linux')
| -rw-r--r-- | include/linux/buffer_head.h | 36 |
1 files changed, 31 insertions, 5 deletions
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index fd2c7115c054..20b8fca1abfa 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -59,10 +59,7 @@ struct address_space; struct buffer_head { unsigned long b_state; /* buffer state bitmap (see above) */ struct buffer_head *b_this_page;/* circular list of page's buffers */ - union { - struct page *b_page; /* the page this bh is mapped to */ - struct folio *b_folio; /* the folio this bh is mapped to */ - }; + struct folio *b_folio; /* the folio this bh is mapped to */ sector_t b_blocknr; /* start block number */ size_t b_size; /* size of mapping */ @@ -172,7 +169,36 @@ static __always_inline int buffer_uptodate(const struct buffer_head *bh) static inline unsigned long bh_offset(const struct buffer_head *bh) { - return (unsigned long)(bh)->b_data & (page_size(bh->b_page) - 1); + return (unsigned long)(bh)->b_data & (folio_size(bh->b_folio) - 1); +} + +/** + * kmap_local_bh - Map the data of a buffer. + * @bh: The buffer. + * + * Buffers usually live in the page cache, but a few are built over memory + * which is not. Those carry no folio and b_data is already a kernel address + * which is always mapped, so there is nothing to do for them. Pair with + * kunmap_local_bh(). + * + * Return: A pointer to the buffer's data. + */ +static inline void *kmap_local_bh(const struct buffer_head *bh) +{ + if (!bh->b_folio) + return bh->b_data; + return kmap_local_folio(bh->b_folio, bh_offset(bh)); +} + +/** + * kunmap_local_bh - Unmap the data of a buffer. + * @bh: The buffer. + * @addr: The address returned by kmap_local_bh(). + */ +static inline void kunmap_local_bh(const struct buffer_head *bh, void *addr) +{ + if (bh->b_folio) + kunmap_local(addr); } /* If we *know* page->private refers to buffer_heads */ |
