From: Matthew Wilcox (Oracle) Date: Thu, 28 May 2026 17:31:17 +0000 (+0100) Subject: buffer: Add bh_end_read(), bh_end_write() and bh_end_async_write() X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=86ecf5704ed3c731284b9c60e46dcc5d115ebe7c;p=thirdparty%2Fkernel%2Flinux.git buffer: Add bh_end_read(), bh_end_write() and bh_end_async_write() These are the bio_end_io_t versions of end_buffer_read_sync(), end_buffer_write_sync() and end_buffer_async_write(). They do not contain a put_bh() call as it is no longer necessary. Also add the helper function bio_endio_bh(). Signed-off-by: Matthew Wilcox (Oracle) Link: https://patch.msgid.link/20260528173150.1093780-5-willy@infradead.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- diff --git a/fs/buffer.c b/fs/buffer.c index 4dcce64ef006..e22a94afa385 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -129,6 +129,34 @@ static void buffer_io_error(struct buffer_head *bh, char *msg) bh->b_bdev, (unsigned long long)bh->b_blocknr, msg); } +/** + * bio_endio_bh - Discard the bio used to submit a buffer. + * @bio: The bio. + * @bhp: Where to return the buffer_head. + * + * Call this in your bio_end_io handler to retrieve the buffer_head + * submitted in bh_submit(). If you did not call bh_submit(), do not + * call this function; it will return garbage. + * + * This function consumes the bio refcount which will probably free the + * bio. + * + * Return: True if the I/O succeeded. + */ +bool bio_endio_bh(struct bio *bio, struct buffer_head **bhp) +{ + bool success = bio->bi_status == BLK_STS_OK; + struct buffer_head *bh = bio->bi_private; + + if (unlikely(bio_flagged(bio, BIO_QUIET))) + set_bit(BH_Quiet, &bh->b_state); + bio_put(bio); + + *bhp = bh; + return success; +} +EXPORT_SYMBOL(bio_endio_bh); + /* * End-of-IO handler helper function which does not touch the bh after * unlocking it. @@ -159,7 +187,22 @@ void end_buffer_read_sync(struct buffer_head *bh, int uptodate) } EXPORT_SYMBOL(end_buffer_read_sync); -void end_buffer_write_sync(struct buffer_head *bh, int uptodate) +/** + * bh_end_read - I/O end handler for reads + * @bio: The bio being completed. + * + * Pass this function to bh_submit() if you're reading into the buffer, + * unless you need your own special I/O end handler. + */ +void bh_end_read(struct bio *bio) +{ + struct buffer_head *bh; + bool uptodate = bio_endio_bh(bio, &bh); + __end_buffer_read_notouch(bh, uptodate); +} +EXPORT_SYMBOL(bh_end_read); + +static void __end_buffer_write_sync(struct buffer_head *bh, int uptodate) { if (uptodate) { set_buffer_uptodate(bh); @@ -169,10 +212,30 @@ void end_buffer_write_sync(struct buffer_head *bh, int uptodate) clear_buffer_uptodate(bh); } unlock_buffer(bh); +} + +void end_buffer_write_sync(struct buffer_head *bh, int uptodate) +{ + __end_buffer_write_sync(bh, uptodate); put_bh(bh); } EXPORT_SYMBOL(end_buffer_write_sync); +/** + * bh_end_write - I/O end handler for writes + * @bio: The bio being completed. + * + * Pass this function to bh_submit() if you're writing from the buffer, + * unless you need your own special I/O end handler. + */ +void bh_end_write(struct bio *bio) +{ + struct buffer_head *bh; + bool success = bio_endio_bh(bio, &bh); + __end_buffer_write_sync(bh, success); +} +EXPORT_SYMBOL(bh_end_write); + static struct buffer_head * __find_get_block_slow(struct block_device *bdev, sector_t block, bool atomic) { @@ -416,6 +479,21 @@ still_busy: spin_unlock_irqrestore(&first->b_uptodate_lock, flags); } +/** + * bh_end_async_write - I/O end handler for async folio writes + * @bio: The bio being completed. + * + * Pass this function to bh_submit() if you're doing the equivalent of + * block_write_full_folio(). + */ +void bh_end_async_write(struct bio *bio) +{ + struct buffer_head *bh; + bool success = bio_endio_bh(bio, &bh); + end_buffer_async_write(bh, success); +} +EXPORT_SYMBOL(bh_end_async_write); + /* * If a page's buffers are under async readin (end_buffer_async_read * completion) then there is a possibility that another thread of @@ -1150,13 +1228,10 @@ EXPORT_SYMBOL(__bforget); static void end_bio_bh_io_sync(struct bio *bio) { - struct buffer_head *bh = bio->bi_private; - - if (unlikely(bio_flagged(bio, BIO_QUIET))) - set_bit(BH_Quiet, &bh->b_state); + struct buffer_head *bh; + bool uptodate = bio_endio_bh(bio, &bh); - bh->b_end_io(bh, !bio->bi_status); - bio_put(bio); + bh->b_end_io(bh, uptodate); } static void buffer_set_crypto_ctx(struct bio *bio, const struct buffer_head *bh, diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index d59980e4adda..b0a31a90fa79 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -204,6 +204,12 @@ struct buffer_head *create_empty_buffers(struct folio *folio, unsigned long blocksize, unsigned long b_state); void end_buffer_read_sync(struct buffer_head *bh, int uptodate); void end_buffer_write_sync(struct buffer_head *bh, int uptodate); +bool bio_endio_bh(struct bio *bio, struct buffer_head **bhp); + +/* Completion routines suitable for passing to bh_submit() */ +void bh_end_read(struct bio *bio); +void bh_end_write(struct bio *bio); +void bh_end_async_write(struct bio *bio); /* Things to do with metadata buffers list */ void mmb_mark_buffer_dirty(struct buffer_head *bh, struct mapping_metadata_bhs *mmb);