summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2026-08-31 10:19:24 +0200
committerChristian Brauner <brauner@kernel.org>2026-09-10 09:27:27 +0200
commit00db7c70bf7c6a79a548f484c76af60563a7736e (patch)
tree6668f2f26794f2daa6d5a7bd9b11e88ef68d36e5 /include/linux
parentcee9395acd8043be0644b25c34bfa86623f2b935 (diff)
parent336e794c59173407d22cfc1f254c517c264ef9ed (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.h36
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 */