image_pdfimage_print

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:

  1. Convert to struct udevice for I/O – Replace the legacy blk_dread() / blk_dwrite() calls with blk_read() / blk_write(), which take a struct udevice instead of struct blk_desc. This is the direction U-Boot’s block layer is heading.
  2. Collect globals into a struct – Gather the scattered global variables into a single struct ext4l_state, making the mount state explicit and self-contained.
  3. Thread state through the API – Add struct ext4l_state * as the first parameter to ext4l_mount() and all public functions. The state-taking version becomes the primary API; legacy wrappers with a _legacy suffix pass the global state for backward compatibility with the fs_legacy interface.
  4. Per-device state in the VFS driver – Each UCLASS_FS device stores its own ext4l_state in priv_auto. The VFS mount/unmount functions call ext4l_mount() / ext4l_umount() directly with the device’s state.
  5. Route I/O through the superblock – Add bd_blk and bd_part_start fields to struct block_device so 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 (loadext4ls, 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.

Author

  • Simon Glass is a primary author of U-Boot, with around 10K commits. He is maintainer of driver model and various other subsystems in U-Boot.

Leave a Reply

Your email address will not be published. Required fields are marked *