[Concept,04/16] fs: isofs: Read file content via the uncached path

Message ID 20260421183511.2044469-5-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>

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(-)
  

Patch

diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 3d9e9301a9f..7639471edf2 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -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);
diff --git a/fs/isofs/interface.c b/fs/isofs/interface.c
index 6f07f9951f1..5f8cc749e08 100644
--- a/fs/isofs/interface.c
+++ b/fs/isofs/interface.c
@@ -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;
 
diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h
index 95d85f58733..7382bcc62b5 100644
--- a/fs/isofs/isofs.h
+++ b/fs/isofs/isofs.h
@@ -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,