[Concept,03/16] fs: Add sb_bread_uncached() for streaming reads

Message ID 20260421183511.2044469-4-sjg@u-boot.org
State New
Headers
Series efi-x86: Boot Ubuntu live ISOs via U-Boot + BLS, end to end |

Commit Message

Simon Glass April 21, 2026, 6:34 p.m. UTC
  From: Simon Glass <sjg@chromium.org>

The existing sb_bread() path populates a global buffer-head cache in
sb_getblk() and brelse() does not evict cached entries. That is the
right trade-off for ext4l, where journal and metadata blocks are re-read
from a small working set, but it is a disaster for filesystems that
stream large files: every block read is retained, each allocation
carries a buffer_head, a folio and a data buffer, and the malloc heap
fills up long before the read completes.

Add sb_bread_uncached() which allocates a fresh buffer_head, reads the
block, and returns it without touching the cache. Callers release the
buffer with brelse() exactly as before; since buffer_cached() is not
set, brelse() now frees the buffer immediately.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 fs/linux_fs.c               | 26 ++++++++++++++++++++++++++
 include/linux/buffer_head.h | 14 ++++++++++++++
 2 files changed, 40 insertions(+)
  

Patch

diff --git a/fs/linux_fs.c b/fs/linux_fs.c
index db2b3b3942a..8f36b96a74e 100644
--- a/fs/linux_fs.c
+++ b/fs/linux_fs.c
@@ -472,6 +472,32 @@  struct buffer_head *sb_bread(struct super_block *sb, sector_t block)
 	return bh;
 }
 
+struct buffer_head *sb_bread_uncached(struct super_block *sb, sector_t block)
+{
+	struct buffer_head *bh;
+	int ret;
+
+	if (!sb)
+		return NULL;
+
+	bh = alloc_buffer_head_with_data(sb->s_blocksize);
+	if (!bh)
+		return NULL;
+
+	bh->b_blocknr = block;
+	bh->b_bdev = sb->s_bdev;
+
+	ret = linux_fs_read_block(sb, block, sb->s_blocksize, bh->b_data);
+	if (ret) {
+		free_buffer_head(bh);
+		return NULL;
+	}
+
+	set_buffer_uptodate(bh);
+
+	return bh;
+}
+
 /**
  * bdev_getblk() - Get buffer via block_device
  * @bdev: Block device
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index e844402500d..aad6dec3542 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -178,6 +178,20 @@  static inline void put_bh(struct buffer_head *bh)
 void brelse(struct buffer_head *bh);
 void __brelse(struct buffer_head *bh);
 
+/**
+ * sb_bread_uncached() - Read a block without inserting it in the buffer cache
+ * @sb: Super block
+ * @block: Block number
+ *
+ * Same as sb_bread() but skips the buffer-head cache. Useful for streaming
+ * reads of large file contents where caching every block would exhaust the
+ * malloc heap before the read completes. Caller must release with brelse(),
+ * which frees the buffer immediately since it is not cached.
+ *
+ * Return: Buffer head with valid data, or NULL on error
+ */
+struct buffer_head *sb_bread_uncached(struct super_block *sb, sector_t block);
+
 /*
  * Buffer operation stubs - U-Boot is single-threaded
  */