buffer: allow a buffer_head to point at memory outside the page cache

jbd2 builds a temporary buffer_head to write out the frozen copy of a
metadata block, and that copy lives in slab memory.  Today jbd2 points the
temporary buffer at the slab folio backing it.  A slab folio's ->mapping is
not an address_space, so anything that follows bh->b_folio->mapping there
gets garbage rather than NULL; mark_buffer_write_io_error() does exactly
that, and we are about to start calling it on this buffer.

Rather than teach every such helper about slab folios, allow bh->b_folio to
be NULL and let b_data point straight at the memory.  Code that needs the
folio has to check.  There are two places in this file:

 - __bh_submit() adds the data by virtual address using
   bio_add_virt_nofail(), and skips the cgroup accounting: a buffer that is
   not in the page cache has no owning folio to attribute writeback to.

 - buffer_set_crypto_ctx() returns early.  fscrypt has no interest in a
   buffer that is not part of a file mapping, which is why it already
   returns when the folio has no mapping.

Nothing sets b_folio to NULL yet, so this patch is a no-op on its own.

A buffer_head without a folio is a narrow thing, not a new general
capability.  Most of the buffer_head API assumes a folio and will fault or
corrupt state without one - touch_buffer(), bh_offset(), the async read
completion path, and plenty more - so it is up to whoever builds such a
buffer to keep it away from all of that.  What NULL buys us is that getting
it wrong fails loudly instead of quietly following a slab folio's
overloaded ->mapping.  It is also only valid over memory that is always
mapped: buffers over highmem have no permanent kernel virtual address,
which is why folio_set_bh() records a folio and an offset instead.

Suggested-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Acked-by: Weidong Zhu <weizhu@fiu.edu>
Signed-off-by: Chao Shi <coshi036@gmail.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Link: https://patch.msgid.link/bb6fab2111a48d7ba61887fc1372362576fce6e6.1785951556.git.coshi036@gmail.com
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
Chao Shi 2026-08-06 12:58:25 -04:00 committed by Christian Brauner
parent 1aeacc2cbf
commit 781bf9c7e3
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2

View File

@ -1070,12 +1070,16 @@ EXPORT_SYMBOL(__bforget);
static void buffer_set_crypto_ctx(struct bio *bio, const struct buffer_head *bh,
gfp_t gfp_mask)
{
const struct address_space *mapping = folio_mapping(bh->b_folio);
const struct address_space *mapping;
/*
* The ext4 journal (jbd2) can submit a buffer_head it directly created
* for a non-pagecache page. fscrypt doesn't care about these.
* for memory that is not in the page cache at all. fscrypt doesn't
* care about these.
*/
if (!bh->b_folio)
return;
mapping = folio_mapping(bh->b_folio);
if (!mapping)
return;
fscrypt_set_bio_crypt_ctx(bio, mapping->host,
@ -1107,7 +1111,8 @@ static void __bh_submit(struct buffer_head *bh, blk_opf_t opf,
bio = bio_alloc(bh->b_bdev, 1, opf, GFP_NOIO);
if (folio_test_dropbehind(bh->b_folio) && op_is_write(opf))
if (bh->b_folio && folio_test_dropbehind(bh->b_folio) &&
op_is_write(opf))
bio_set_flag(bio, BIO_COMPLETE_IN_TASK);
if (IS_ENABLED(CONFIG_FS_ENCRYPTION))
@ -1116,7 +1121,11 @@ static void __bh_submit(struct buffer_head *bh, blk_opf_t opf,
bio->bi_iter.bi_sector = bh->b_blocknr * (bh->b_size >> 9);
bio->bi_write_hint = write_hint;
bio_add_folio_nofail(bio, bh->b_folio, bh->b_size, bh_offset(bh));
if (bh->b_folio)
bio_add_folio_nofail(bio, bh->b_folio, bh->b_size,
bh_offset(bh));
else
bio_add_virt_nofail(bio, bh->b_data, bh->b_size);
bio->bi_end_io = end_bio;
bio->bi_private = bh;
@ -1126,7 +1135,8 @@ static void __bh_submit(struct buffer_head *bh, blk_opf_t opf,
if (wbc) {
wbc_init_bio(wbc, bio);
wbc_account_cgroup_owner(wbc, bh->b_folio, bh->b_size);
if (bh->b_folio)
wbc_account_cgroup_owner(wbc, bh->b_folio, bh->b_size);
}
blk_crypto_submit_bio(bio);