Embedded-oriented filesystems are a scattered world. Flash-optimized filesystems are less so. JFFS2 has been widely used but has several performance issues (mount time, especially, though CONFIG_SUMMARY
and sumtool
fixes that since 2.6.15). LogFS doesn’t seem to be actively maintained. The most active and promising flash filesystem is UBIFS. It runs on top of UBI (“Unsorted Block Images”), an abstraction layer for MTD devices.
Why flash-oriented filesystems ?
MTDs (Memory Technology Devices) are very different from block devices: instead of a sequence of writable sectors, they contain an array of writable pages, organized in so-called “erase blocks”.
To write on a page that already has data on it, you first have to erase this data. However, it is only possible to erase whole eraseblocks. Only then, you can write your new data (including what you didn’t change). Erasing causes the memory cells to wear out. At some point, they won’t be usable anymore and have to be skipped.
Because it is memory-based, random access is theoretically as fast as sequential access. So, you don’t need to keep the fragments of your files together. It makes it possible to do wear-leveling and thus, “increase” the lifetime of the chip.
A simple way of doing wear-leveling is to keep track of the number of times a block has been erased and use the block that has been the least erased when updating data.
All these constraints make it hard to write a flash filesystem.
UBI intends to deal with all MTD-specific operations while still presenting random-access volumes to the the upper-layer. The first – and as for now, only – implementation using UBI is UBIFS. UBI is a “volume manager” and maps physical erase blocks (PEB) to logical erase blocks (LEB). The LEBs are smaller than the PEBs because of meta-data and headers.
How to use UBI on my board ?
There are mainly 2 ways to do that:
- On a booted Linux system, approximately the same way you would create a partition on your desktop’s hard drive ;
- From the bootloader, by flashing a previously prepared UBI image ;
Whatever solution you choose, you need to know the sizes of:
- the eraseblocks (PEB) ;
- the pages (or “minimum input/output size”) ;
- the subpages (it may be the same as the min i/o size) ;
From these details, you can deduce another one: the size of logical erase blocks. It is the size of the PEB minus a data offset which is:
(int((Subpage_size + Page_size) / Page_size)) * Page_size
(subpage+page truncated to page size). This formula makes some assumption but should be correct if the subpage size is more than 8B and the page size more than 64B (see the source for more information). The best way to be sure of this size is to use mtdinfo
on linux on the board. mtdinfo
is part of the ubi-utils (part of mtd-utils). It’s probably available in your build system.
In both cases, you will also need a UBIFS image. In the way of JFFS2, mkfs.ubifs
comes in mtd-utils (thus, you also need them on your desktop. Warning: mtd-utils in Ubuntu 10.10 are reported to be buggy ; if you use this distribution, recompile them from their git tree). Here is an example of how you can invoke it:
# mkfs.ubifs -r </path/to/your/rootfs/tree> -m <min io size> -e <LEB size> -c <Eraseblocks count> -o </path/to/output/ubifs.img>
Solution A – On a booted Linux system
I think it’s the best method to understand how UBI is structured.
You first need to enable UBI and UBIFS in the kernel and install the mtd-utils
package (for Debian and Ubuntu) on your box. You may also compile mtd-utils
from its sources.
Once you have your UBIFS image at hand, let’s sing the UBI song:
# ubiformat /dev/mtdX # ubiattach -p /dev/mtdX # ubimkvol /dev/ubi0 -N volume_name -s 64MiB # ubiupdatevol /dev/ubi0_0 /path/to/ubifs.img # mount -t ubifs ubi0:volume_name /mount/point
Let’s examine each command. ubiformat
erases an MTD partition but keeps its erase counters ((‘X’ is the number of the partition you want to use). ubiattach
creates a UBI device from the MTD partition. This UBI device is then referred to by UBI as ubi0
(if it is the first device). ubimkvol
creates a volume on a UBI device ; this volume is referred to as ubi0_0
(if it is the first volume on the device). ubiupdatevol
puts an image on an empty volume. (use ubiupdatevol -t /dev/ubi0_0
to empty a volume). At last, the well-known mount
can be invoked using <device>:<volume>
Solution B – Prepare a UBI image ready to be flashed
It is more common to directly flash filesystem images directly from the bootloader. It is made possible by ubinize
to prepare a UBI device image containing one or more volumes.
ubinize
reads a configuration file (in the very simple INI format) describing the volumes and their configuration. Here is an example of a device with two volumes ; one, named rootfs
is read-only (static
), the other one, data
is read-write (dynamic
) ; the autoresize
flag makes UBI resize to volume to use the whole unused space at initialization. The name of the sections is totally arbitrary.
[rootfs_volume] mode=ubi image=rootfs.ubifs vol_id=1 vol_type=static vol_name=rootfs vol_alignment=1 [rwdata_volume] mode=ubi image=data.ubifs vol_id=2 vol_type=dynamic vol_name=data vol_alignment=1 vol_flags=autoresize
Next is the generation of the UBI image. The ubinize
utility will need the Physical Erase Block size (PEB) (option -p
) and the minimum I/O size (-m
):
# ubinize -vv -o <output image> -m <min io size> -p <PEB size>KiB <configuration file>
Your image is ready. You may now want to boot on the rootfs UBIFS partition. Keep on reading, then.
Use a UBIFS partition as root partition
Some options need to be passed to the kernel to boot on a ubi volume and on a UBIFS partition:
ubi.mtd=<mtd partition number> root=<ubi device>:<volume> rootfstype=ubifs
For instance, with the previous examples and assuming the UBI device has been created/flashed on /dev/mtd1
:
ubi.mtd=1 root=ubi0:rootfs rootfstype=ubifs
Conclusion
Creating and using a UBIFS filesystem is not as easy as with JFFS2 but UBI/UBIFS is designed to be more robust and UBI will ease the development of new filesystems. The authors of UBI have pointed some memory usage scalability problems but if a second version of UBI were written, filesystems on top of it would not need to be modified.
Troubleshooting
In case your system is missing the /dev/ubi_ctrl
, /dev/ubi0
or /dev/ubi0_X
device files, we advise you to recompile your kernel with DEVTMPFS
and DEVTMPFS_MOUNT
. This way, all the devices existing on your system will appear in /dev
.
If you get write errors (code -74 or -5, probably), check that CONFIG_MTD_NAND_VERIFY_WRITE
(respectively, ONENAND
) is disabled : verifying subpages writes isn’t supported yet.
Sources
The primary place for information about MTD support in Linux is infradead.org. There also is a mailing list which you can also subscribe to.
The kernel sources under drivers/mtd
and fs/ubisfs
are also very helpful.