[Concept,03/16] fs: Add sb_bread_uncached() for streaming reads
Commit Message
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(+)
@@ -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
@@ -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
*/