Creating and flashing UBI / UBIFS images

Flash super heroEmbedded-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.