From: Chao Shi <coshi036@gmail.com>
To: Jan Kara <jack@suse.cz>, Christian Brauner <brauner@kernel.org>,
Alexander Viro <viro@zeniv.linux.org.uk>,
Matthew Wilcox <willy@infradead.org>,
linux-fsdevel@vger.kernel.org
Cc: Theodore Ts'o <tytso@mit.edu>,
Andreas Dilger <adilger.kernel@dilger.ca>,
Baokun Li <libaokun@linux.alibaba.com>,
Ojaswin Mujoo <ojaswin@linux.ibm.com>,
Ritesh Harjani <ritesh.list@gmail.com>,
Zhang Yi <yi.zhang@huawei.com>,
Zhang Yi <yi.zhang@huaweicloud.com>,
Bob Copeland <me@bobcopeland.com>,
Namjae Jeon <linkinjeon@kernel.org>,
Sungjong Seo <sj1557.seo@samsung.com>,
Yuezhang Mo <yuezhang.mo@sony.com>,
OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>,
Mark Fasheh <mark@fasheh.com>, Joel Becker <jlbec@evilplan.org>,
Joseph Qi <joseph.qi@linux.alibaba.com>,
Andreas Gruenbacher <agruenba@redhat.com>,
linux-ext4@vger.kernel.org, ocfs2-devel@lists.linux.dev,
gfs2@lists.linux.dev, linux-kernel@vger.kernel.org,
Chao Shi <coshi036@gmail.com>, Weidong Zhu <weizhu@fiu.edu>
Subject: [PATCH v2 03/21] jbd2: point the shadow buffer at the frozen data directly
Date: Thu, 6 Aug 2026 12:58:26 -0400 [thread overview]
Message-ID: <6140cd23beb88e99f40eaeff4044a16213f6caab.1785951556.git.coshi036@gmail.com> (raw)
In-Reply-To: <cover.1785951556.git.coshi036@gmail.com>
When a metadata buffer has to be copied out before it can be journalled,
jbd2_journal_write_metadata_buffer() writes jh->b_frozen_data rather than
the page cache copy. b_frozen_data is kmalloc()ed, so folio_set_bh() makes
the shadow buffer point at a slab folio.
That is not something the buffer_head layer can reason about. A slab folio
overloads ->mapping, so a shadow buffer looks like it belongs to an
address_space when it does not. buffer_set_crypto_ctx() already has to
work around this, and it is the reason mark_buffer_write_io_error() cannot
be called on a shadow buffer today.
Point the shadow buffer at the frozen data itself instead: leave b_folio
NULL, which it already is out of alloc_buffer_head(), and set b_data. The
previous patch taught fs/buffer.c to submit such a buffer. folio_set_bh()
is now needed on only one path - the one that journals the page cache copy
directly - so it moves there, and new_folio, new_offset and the flag that
used to pick between them all go away.
The two commit-path checksum helpers reach the shadow buffer's contents
through a new kmap_local_bh()/kunmap_local_bh() pair, which handle a buffer
with or without a folio. Memory outside the page cache is always mapped,
so for those there is nothing to map or unmap. Mapping it anyway would be
worse than pointless: with CONFIG_DEBUG_KMAP_LOCAL_FORCE_MAP,
kmap_local_page() hands back a one page mapping even for such memory, which
is not enough for a buffer bigger than a page.
Tested with ext4 mounted data=journal,journal_checksum on a metadata_csum
filesystem, writing files whose every block begins with the JBD2 magic so
that escaping forces the copy-out, then crashing with sysrq-b without
unmounting and replaying the journal on the next mount. Recovery
completed, the file contents matched, e2fsck -fn was clean, and an
instrumented build confirmed the b_folio == NULL path was taken.
Suggested-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Acked-by: Weidong Zhu <weizhu@fiu.edu>
Signed-off-by: Chao Shi <coshi036@gmail.com>
---
fs/jbd2/commit.c | 8 ++++----
fs/jbd2/journal.c | 29 +++++++++++++++++------------
include/linux/buffer_head.h | 29 +++++++++++++++++++++++++++++
3 files changed, 50 insertions(+), 16 deletions(-)
diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c
index 3029cb6f6d64..0c85af91f9b2 100644
--- a/fs/jbd2/commit.c
+++ b/fs/jbd2/commit.c
@@ -330,9 +330,9 @@ static __u32 jbd2_checksum_data(__u32 crc32_sum, struct buffer_head *bh)
char *addr;
__u32 checksum;
- addr = kmap_local_folio(bh->b_folio, bh_offset(bh));
+ addr = kmap_local_bh(bh);
checksum = crc32_be(crc32_sum, addr, bh->b_size);
- kunmap_local(addr);
+ kunmap_local_bh(bh, addr);
return checksum;
}
@@ -357,10 +357,10 @@ static void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag,
return;
seq = cpu_to_be32(sequence);
- addr = kmap_local_folio(bh->b_folio, bh_offset(bh));
+ addr = kmap_local_bh(bh);
csum32 = jbd2_chksum(j->j_csum_seed, (__u8 *)&seq, sizeof(seq));
csum32 = jbd2_chksum(csum32, addr, bh->b_size);
- kunmap_local(addr);
+ kunmap_local_bh(bh, addr);
if (jbd2_has_feature_csum3(j))
tag3->t_checksum = cpu_to_be32(csum32);
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index 09efa337649e..6e05dc47e20a 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -327,8 +327,6 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction,
{
int do_escape = 0;
struct buffer_head *new_bh;
- struct folio *new_folio;
- unsigned int new_offset;
struct buffer_head *bh_in = jh2bh(jh_in);
journal_t *journal = transaction->t_journal;
@@ -348,24 +346,31 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction,
/* keep subsequent assertions sane */
atomic_set(&new_bh->b_count, 1);
+ /*
+ * b_frozen_data is slab memory, not page cache, so when we use it the
+ * shadow buffer gets no folio at all: b_folio stays NULL from the
+ * allocation and b_data points straight at the copy. Pointing it at
+ * the slab folio instead would hand its overloaded ->mapping to
+ * anything that goes looking for an address_space.
+ */
+
spin_lock(&jh_in->b_state_lock);
/*
* If a new transaction has already done a buffer copy-out, then
* we use that version of the data for the commit.
*/
if (jh_in->b_frozen_data) {
- new_folio = virt_to_folio(jh_in->b_frozen_data);
- new_offset = offset_in_folio(new_folio, jh_in->b_frozen_data);
do_escape = jbd2_data_needs_escaping(jh_in->b_frozen_data);
if (do_escape)
jbd2_data_do_escape(jh_in->b_frozen_data);
+ new_bh->b_data = jh_in->b_frozen_data;
} else {
+ struct folio *folio = bh_in->b_folio;
+ unsigned int offset = offset_in_folio(folio, bh_in->b_data);
char *tmp;
char *mapped_data;
- new_folio = bh_in->b_folio;
- new_offset = offset_in_folio(new_folio, bh_in->b_data);
- mapped_data = kmap_local_folio(new_folio, new_offset);
+ mapped_data = kmap_local_folio(folio, offset);
/*
* Fire data frozen trigger if data already wasn't frozen. Do
* this before checking for escaping, as the trigger may modify
@@ -379,8 +384,10 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction,
/*
* Do we need to do a data copy?
*/
- if (!do_escape)
+ if (!do_escape) {
+ folio_set_bh(new_bh, folio, offset);
goto escape_done;
+ }
spin_unlock(&jh_in->b_state_lock);
tmp = kmalloc(bh_in->b_size, GFP_NOFS | __GFP_NOFAIL);
@@ -391,7 +398,7 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction,
}
jh_in->b_frozen_data = tmp;
- memcpy_from_folio(tmp, new_folio, new_offset, bh_in->b_size);
+ memcpy_from_folio(tmp, folio, offset, bh_in->b_size);
/*
* This isn't strictly necessary, as we're using frozen
* data for the escaping, but it keeps consistency with
@@ -400,13 +407,11 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction,
jh_in->b_frozen_triggers = jh_in->b_triggers;
copy_done:
- new_folio = virt_to_folio(jh_in->b_frozen_data);
- new_offset = offset_in_folio(new_folio, jh_in->b_frozen_data);
jbd2_data_do_escape(jh_in->b_frozen_data);
+ new_bh->b_data = jh_in->b_frozen_data;
}
escape_done:
- folio_set_bh(new_bh, new_folio, new_offset);
new_bh->b_size = bh_in->b_size;
new_bh->b_bdev = journal->j_dev;
new_bh->b_blocknr = blocknr;
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 699970b4bbf2..20b8fca1abfa 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -172,6 +172,35 @@ static inline unsigned long bh_offset(const struct buffer_head *bh)
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 */
#define page_buffers(page) \
({ \
--
2.43.0
next prev parent reply other threads:[~2026-08-06 16:59 UTC|newest]
Thread overview: 40+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-06 16:58 [PATCH v2 00/21] buffer: stop clearing BH_Uptodate when a write fails Chao Shi
2026-08-06 16:58 ` [PATCH v2 01/21] buffer_head: Remove b_page Chao Shi
2026-08-06 16:58 ` [PATCH v2 02/21] buffer: allow a buffer_head to point at memory outside the page cache Chao Shi
2026-08-06 16:58 ` Chao Shi [this message]
2026-08-25 16:09 ` [PATCH v2 03/21] jbd2: point the shadow buffer at the frozen data directly Jan Kara
2026-09-01 3:37 ` Joseph Qi
2026-09-01 4:19 ` Aithal, Srikanth
2026-09-01 9:40 ` Luca Weiss
2026-09-01 13:21 ` Jan Kara
2026-08-06 16:58 ` [PATCH v2 04/21] buffer: read the folio's mapping directly in buffer_set_crypto_ctx() Chao Shi
2026-08-25 16:10 ` Jan Kara
2026-08-06 16:58 ` [PATCH v2 05/21] buffer: clear BH_Write_EIO when a buffer is forgotten Chao Shi
2026-08-06 16:58 ` [PATCH v2 06/21] buffer: discard BH_Write_EIO along with the rest of the buffer state Chao Shi
2026-08-06 16:58 ` [PATCH v2 07/21] buffer: detect metadata write errors with buffer_write_io_error() Chao Shi
2026-08-06 16:58 ` [PATCH v2 08/21] adfs: check for a directory write error " Chao Shi
2026-08-25 16:12 ` Jan Kara
2026-08-06 16:58 ` [PATCH v2 09/21] ext2: check for an xattr block " Chao Shi
2026-08-06 16:58 ` [PATCH v2 10/21] omfs: check for an inode " Chao Shi
2026-08-25 16:12 ` Jan Kara
2026-08-06 16:58 ` [PATCH v2 11/21] exfat: check for a directory " Chao Shi
2026-08-25 16:17 ` Jan Kara
2026-08-06 16:58 ` [PATCH v2 12/21] fat: check for a metadata " Chao Shi
2026-08-25 16:19 ` Jan Kara
2026-08-06 16:58 ` [PATCH v2 13/21] ext4: " Chao Shi
2026-08-06 16:58 ` [PATCH v2 14/21] ocfs2: " Chao Shi
2026-08-06 16:58 ` [PATCH v2 15/21] ocfs2: check for a stale write error before reusing a metadata buffer Chao Shi
2026-08-06 16:58 ` [PATCH v2 16/21] gfs2: check for a metadata write error with buffer_write_io_error() Chao Shi
2026-08-25 17:06 ` Jan Kara
2026-08-06 16:58 ` [PATCH v2 17/21] jbd2: report journal write errors with BH_Write_EIO Chao Shi
2026-08-25 17:19 ` Jan Kara
2026-08-06 16:58 ` [PATCH v2 18/21] jbd2: say what jbd2_freeze_jh_data()'s assertion is actually checking Chao Shi
2026-08-25 17:18 ` Jan Kara
2026-08-06 16:58 ` [PATCH v2 19/21] ext4, jbd2: report fast commit write errors with BH_Write_EIO Chao Shi
2026-08-25 17:16 ` Jan Kara
2026-08-06 16:58 ` [PATCH v2 20/21] buffer: stop touching BH_Uptodate on write completion Chao Shi
2026-08-25 17:21 ` Jan Kara
2026-08-06 16:58 ` [PATCH v2 21/21] buffer: clear BH_Write_EIO when a write succeeds, not when one starts Chao Shi
2026-08-25 17:24 ` Jan Kara
2026-08-14 15:35 ` [PATCH v2 00/21] buffer: stop clearing BH_Uptodate when a write fails Chris S
2026-08-31 8:20 ` Christian Brauner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=6140cd23beb88e99f40eaeff4044a16213f6caab.1785951556.git.coshi036@gmail.com \
--to=coshi036@gmail.com \
--cc=adilger.kernel@dilger.ca \
--cc=agruenba@redhat.com \
--cc=brauner@kernel.org \
--cc=gfs2@lists.linux.dev \
--cc=hirofumi@mail.parknet.co.jp \
--cc=jack@suse.cz \
--cc=jlbec@evilplan.org \
--cc=joseph.qi@linux.alibaba.com \
--cc=libaokun@linux.alibaba.com \
--cc=linkinjeon@kernel.org \
--cc=linux-ext4@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mark@fasheh.com \
--cc=me@bobcopeland.com \
--cc=ocfs2-devel@lists.linux.dev \
--cc=ojaswin@linux.ibm.com \
--cc=ritesh.list@gmail.com \
--cc=sj1557.seo@samsung.com \
--cc=tytso@mit.edu \
--cc=viro@zeniv.linux.org.uk \
--cc=weizhu@fiu.edu \
--cc=willy@infradead.org \
--cc=yi.zhang@huawei.com \
--cc=yi.zhang@huaweicloud.com \
--cc=yuezhang.mo@sony.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.