From: Simon Glass <sjg@chromium.org>
isofs_read() streams whole files through isofs_bread(), which goes
through sb_bread() and therefore populates the shared buffer-head cache.
For a multi-megabyte kernel or initrd this exhausts the U-Boot malloc
heap after a few megabytes (each cached block costs roughly
sizeof(buffer_head) + sizeof(folio) + blocksize) and the read fails
partway through with -EIO before the file is fully read.
Introduce isofs_bread_uncached() alongside isofs_bread() and use it for
file content reads in the interface layer. Directory traversal still
uses the cached path because repeated metadata lookups benefit from it;
only the streaming file read changes.
Signed-off-by: Simon Glass <sjg@chromium.org>
---
fs/isofs/inode.c | 8 ++++++++
fs/isofs/interface.c | 9 +++++++--
fs/isofs/isofs.h | 16 ++++++++++++++++
3 files changed, 31 insertions(+), 2 deletions(-)
@@ -811,6 +811,14 @@ struct buffer_head *isofs_bread(struct inode *inode, sector_t block)
return sb_bread(inode->i_sb, blknr);
}
+struct buffer_head *isofs_bread_uncached(struct inode *inode, sector_t block)
+{
+ sector_t blknr = isofs_bmap(inode, block);
+ if (!blknr)
+ return NULL;
+ return sb_bread_uncached(inode->i_sb, blknr);
+}
+
static int isofs_read_folio(struct file *file, struct folio *folio)
{
return mpage_read_folio(folio, isofs_get_block);
@@ -478,8 +478,13 @@ int isofs_read(const char *filename, void *buf, loff_t offset, loff_t len,
block = offset / blksize;
blk_off = offset % blksize;
- /* Read the block using isofs_bread (handles block mapping) */
- bh = isofs_bread(inode, block);
+ /*
+ * Use the uncached read path: the buffer cache is shared
+ * across the whole VFS layer, and populating it from a
+ * streaming file read of an arbitrarily large file would
+ * exhaust the malloc heap before the read completes.
+ */
+ bh = isofs_bread_uncached(inode, block);
if (!bh)
return -EIO;
@@ -126,6 +126,22 @@ int get_acorn_filename(struct iso_directory_record *, char *, struct inode *);
extern struct dentry *isofs_lookup(struct inode *, struct dentry *, unsigned int flags);
extern struct buffer_head *isofs_bread(struct inode *, sector_t);
+
+/**
+ * isofs_bread_uncached() - Read a file block without populating the cache
+ *
+ * Mirrors isofs_bread() but uses sb_bread_uncached() so the block is freed
+ * the moment the caller drops its reference. Useful for streaming large
+ * files (e.g. the casper kernel/initrd), where caching every block would
+ * exhaust the U-Boot malloc heap long before the read completes.
+ *
+ * @inode: Inode the block belongs to
+ * @block: Logical block number within the inode
+ * Return: buffer_head with the block contents, or NULL if the logical
+ * block does not map to a disk block
+ */
+extern struct buffer_head *isofs_bread_uncached(struct inode *inode,
+ sector_t block);
extern int isofs_get_blocks(struct inode *, sector_t, struct buffer_head **, unsigned long);
struct inode *__isofs_iget(struct super_block *sb,