The ext4l driver – U-Boot’s port of the Linux kernel ext4 implementation – has until now been limited to a single mounted filesystem at a time. This is a consequence of the legacy filesystem interface, where each operation (load, ls, size) re-probes the filesystem from scratch, and global variables track the current mount state.
With the VFS layer landing in U-Boot, persistent mounts become possible. You can mount a filesystem at /mnt and it stays mounted across operations, just like on an Operating System such as Linux. But to mount two ext4 partitions simultaneously – say a boot partition at /boot and a root filesystem at /rootfs – the driver needs to manage independent state for each mount.
The problem
The ext4l driver stores its mount state in a collection of global variables: the block device pointer, partition info, superblock, open directory count, and mounted flag. Every function, from ext4l_read() to ext4l_mkdir(), accesses these globals directly. Mounting a second filesystem overwrites the first’s state.
The buffer cache has the same issue. When U-Boot reads an ext4 block, it caches the result so subsequent reads of the same block are fast. But the cache is keyed only by block number, not by which filesystem the block belonged to. Two filesystems with data at the same block number return each other’s cached blocks!
The approach
Rather than rewriting the driver from scratch, the series takes an incremental approach across eight patches:
- Convert to struct udevice for I/O – Replace the legacy
blk_dread()/blk_dwrite()calls withblk_read()/blk_write(), which take astruct udeviceinstead ofstruct blk_desc. This is the direction U-Boot’s block layer is heading. - Collect globals into a struct – Gather the scattered global variables into a single
struct ext4l_state, making the mount state explicit and self-contained. - Thread state through the API – Add
struct ext4l_state *as the first parameter toext4l_mount()and all public functions. The state-taking version becomes the primary API; legacy wrappers with a_legacysuffix pass the global state for backward compatibility with thefs_legacyinterface. - Per-device state in the VFS driver – Each
UCLASS_FSdevice stores its ownext4l_stateinpriv_auto. The VFS mount/unmount functions callext4l_mount()/ext4l_umount()directly with the device’s state. - Route I/O through the superblock – Add
bd_blkandbd_part_startfields tostruct block_deviceso the buffer cache and block I/O functions work through the superblock rather than global variables. Include the block device pointer in cache lookups so buffers from different mounts do not collide.
The result
Two ext4 partitions can now be mounted simultaneously through the VFS layer, using the ext4l driver (see post):
=> mount host 0:1 /boot
=> mount host 0:2 /rootfs
=> cat /boot/uImage.cfg
=> cat /rootfs/etc/hostname
Each mount has independent state: its own superblock, its own buffer cache entries, its own block device. Unmounting one does not affect the other.
The legacy single-mount interface (load, ext4ls, etc.) continues to work unchanged through the _legacy wrappers.
Testing
A new dual-mount test creates two ext4 images with different content, mounts both via VFS, reads a file from each, and verifies they return independent data. This caught a real bug during development: the buffer cache was freeing all cached buffers on unmount rather than just those belonging to the filesystem being unmounted, causing a use-after-free crash when the second filesystem tried to commit its superblock.
What’s next
The same per-mount state pattern can be applied to other Linux-ported filesystem drivers. A new ISO 9660 driver (isofs) is coming and the groundwork laid here – bd_blk / bd_part_start on block_device, per-device buffer cache lookups – will be shared infrastructure.


