Selecting a file system

When building an embedded system, one of the important decisions which has to be made is which file system to use. Linux provides a number of file systems, some of which are more suitable to embedded products than others.

There are several questions which will influence the decision. For example:

  • Must the file system be writable?

  • Must the file system be writable in place?

  • Is performance paramount, or can lower performance be accepted in return for for smaller code side or better media utilization?

The types of file system which can be considered for ST systems are disk based, memory based and Flash based (as well as network based for development), and combinations of these.

Disk oriented file systems

Although only a minority of embedded systems use a rotating disk for storage, these are the normal file systems with which people will be familiar from desktop systems, and so can provide a useful point of comparison.

  • ext2 is the traditional file system on desktop and server Linux systems. It is fast, full featured, and makes good use of disk space, but has a fairly large code base. It does have one major failing for an embedded system: if power is removed before the file system is unmounted it can result in disk corruption. Even if there is no data loss, a time-consuming disk check must be performed on the next system start.

  • Journalling file systems are increasingly used on Linux systems. These have a number of advantages, in particular they should provide higher throughput and avoid the need for a long consistency check when the system boots. The two most popular Journalling file systems for Linux are ext3 and Reiser FS. These systems do however add significantly more code to the kernel image.

  • The minix file system, although uncommon, has a number of advantages. It makes good use of disk space, at the expense of not providing some (seldom used) features, and has a reasonably small code footprint. However it does require a file system consistency check on each reboot.

Memory oriented file system

In embedded systems, it is common to present a file system interface to an area of memory. Linux provides several ways to do this. The most common is to treat an area of memory as a block device, and mount a normal file system (for example ext2) on top of this device. This is done by enabling RAM disk support (CONFIG_BLK_DEV_RAM), and giving the number (CONFIG_BLK_DEV_RAM_COUNT) and size (CONFIG_BLK_DEV_RAM_SIZE) of the disks. In the Linux Kernel Configuration:

Device Drivers ---> Block devices ---> RAM disk support
Device Drivers ---> Block devices ---> Default number of RAM disks
Device Drivers ---> Block devices ---> Default RAM disk size (kbytes)

A feature of this is that the RAM disk contents can be initialized from a compressed file system image when the system boots. To do this, enable initial read support (CONFIG_BLK_DEV_INITRD) and pass additional parameters to the kernel to tell it where the image is in memory (CONFIG_INITRAMFS_SOURCE). In the menu:

Device Drivers ---> Block devices ---> Initial RAM disk (initrd) support
Device Drivers ---> Block devices ---> Initramfs source file(s)

Although it is normal to use ext2 or minix file systems on a RAM disk, if it is not necessary to write to the file system it can be more efficient to use the Compressed ROM file system (CONFIG_CRAMFS):

File Systems ---> Miscellaneous fileystems --->
                  Compressed ROM file system support (cramfs)

As it is read-only, and applies some restrictions to the file size and file system size, CramFs uses very little extra memory for file meta data. It can also compress the data effectively, saving even more space.

Another way to hold a file system completely in memory is to use TmpFs by setting CONFIG_TMPFS:

File Systems ---> Pseudo fileystems --->
                  Virtual memory file system support (former shm fs)

This has the advantage that the file system shrinks and expands to contain the contents, whereas a RAM disk size must be decided when the disk is created, and the full memory contents are allocated at that point.

Flash file systems

Flash memory is common in embedded systems to allow field upgrades. It can be used to store a file system, but with some restrictions. Flash devices are characterized by being fairly fast to write to, but data cannot be overwritten unless it is first erased, and most devices only allow erasing of large blocks, which can be time-consuming.

The easiest way to use Flash as a file system is to use it to store a file system image, which can be accessed directly (using the MTD pseudo block device), or copied to a RAM disk. The disadvantage of this is that the file system is either read-only (in the first case) or read/write but volatile (in the RAM disk case). In many applications this is adequate, and has the advantage that if the file system is corrupted for any reason a system reset restores it to its previous state). However, if the file system needs to be updated in place, then other techniques must be used.

The most common way to put a writable file system into Flash in a modern Linux system is to use the Journalling Flash File System JFFS2. This uses Journalling techniques similar to those used by high performance file systems such as ext3, but for very different reasons. As Journalling appends all changes to a file system log, it is not necessary to update data in place, which makes it ideal for Flash. JFFS2 can also compress the data, making very effective use of available space. For more details on JFFS2 see the project's home page.

Rather than being built on top of the standard block device interface, JFFS2 uses the MTD Flash interface. This gives it visibility of the Flash characteristics, allowing more intelligent support of Flash devices, for example by being able to write at a finer granularity than the full block, and by performing `wear leveling', this improves performance when writing to the Flash and extends its life.

The main disadvantages of using JFFS2 are timing related. At boot time it has to scan the Flash, building up tables of its state, which can take some time. Periodically it needs to erase Flash sectors, which can take several seconds, during which time file system access is denied. However this can usually be overcome by using a buffer process or thread which is decoupled from the real-time parts of the system.

Another system is the Flash Translation Layer (FTL). This is a technique for mapping a normal block oriented file system onto Flash devices. It has the disadvantage that it does not provide wear leveling, and as it only presents the normal block device interface it performs more erase cycles trying to emulate the behavior of a magnetic disk. FTL may be subject to patents in some countries. A brief description of how to use it is given in Using the Flash Translation Layer (FTL), but in practice there is little to recommend FTL over JFFS2.

Mix and match

As described above, there are a number of options for file systems, all of which have advantages and disadvantages. However, since it is possible to mount multiple file systems anywhere in the file hierarchy, it is possible to construct a custom file system to meet any requirement.

A typical example would be a system with no need for a writable file system, except for some configuration files which need to be updated and non-volatile, and some temporary data which can be regenerated each time the system boots. In this case, the root file system could be a CramFs file system (compressed and read-only), with a small JFFS2 file system mounted on /etc/config for the configuration files, and a TmpFs file system mounted on /tmp for the temporary data files.

Some problems may be seen when booting. These can be overcome using alternative mechanisms. For example, a kernel module may be needed to access the main file system (which has to be mounted on /), but this module has to be loaded from the file system. A utility called pivot_root can solve this problem: the root file system is initially a small CramFs file system on an initialized RAM disk, but after the start up scripts have finished loading the kernel module pivot_root is invoked to dynamically mount the main file system as the new root file system, and still give access to the small CramFs file system if required.