]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
buffer: Add bh_end_read(), bh_end_write() and bh_end_async_write()
authorMatthew Wilcox (Oracle) <willy@infradead.org>
Thu, 28 May 2026 17:31:17 +0000 (18:31 +0100)
committerChristian Brauner <brauner@kernel.org>
Thu, 4 Jun 2026 08:28:06 +0000 (10:28 +0200)
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) <willy@infradead.org>
Link: https://patch.msgid.link/20260528173150.1093780-5-willy@infradead.org
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
fs/buffer.c
include/linux/buffer_head.h

index 4dcce64ef0069e4fb87cc86999a4fd3a35d9f71f..e22a94afa38562153a17dc701c5f010f29f7bec1 100644 (file)
@@ -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,
index d59980e4adda2152bd5a64ab1c01656d25e0eac0..b0a31a90fa7963be3271180eca3418c3c6870c90 100644 (file)
@@ -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);