UBI Volume Manager Explained-Best Embedded Linux Training Online

PREV_LEC | NEXT_LEC

UBI Volume Manager Explained

A free Linux kernel development course lecture on the UBI flash volume manager, wear leveling, and bad block handling

15 min read
Reading time
Beginner-Intermediate
Level
Chapter 7
Storage Strategy

If you have ever wondered how a Linux device with raw NAND flash manages to survive years of writes without a handful of worn-out blocks bricking the whole system, the answer is almost always the UBI flash volume manager. UBI sits between the MTD layer and the filesystem, quietly handling wear leveling and bad block management so that higher layers never have to think about the physical quirks of flash memory. This lecture is part of our free embedded Linux course and walks through UBI from first principles to a hands-on volume creation exercise, building directly on the JFFS2 and YAFFS2 material from earlier lectures in this free linux kernel development course.

Keywords covered in this lecture

UBI UBIFS PEB / LEB mapping wear leveling MTD ubiattach ubiformat ubimkvol UBI fastmap free linux device drivers course

What You Will Learn

Why raw NAND flash needs a volume manager Physical vs logical erase blocks (PEB/LEB) How UBI performs wear leveling Preparing an MTD partition with ubiformat Attaching a UBI device with ubiattach Creating UBI volumes with ubimkvol Reading device state with ubinfo UBI fastmap and attach-time optimization Programmatic volume management via ioctl

Prerequisites

Before this lecture, you should be comfortable with:

The MTD subsystem (mtd character/block devices) Basic NAND flash concepts: pages, blocks, OOB area Why raw flash filesystems like JFFS2/YAFFS2 exist Comfort with the Linux shell and root privileges on a target board

If any of these feel shaky, revisit the earlier lectures in this chapter before continuing — this material assumes that foundation.

Why Raw Flash Needs a Volume Manager

Every earlier lecture in this free embedded systems course chapter dealt with a filesystem — JFFS2 or YAFFS2 — that talked directly to an MTD device and handled wear leveling and bad-block avoidance internally. That design works, but it has a serious limitation: each filesystem has to reinvent the same flash-management logic, and every filesystem instance manages its own private slice of flash in isolation. If you split a chip into two MTD partitions — say, a rarely-written root filesystem and a frequently-written data partition — only the second partition wears out, while spare endurance on the first partition sits unused.

The UBI flash volume manager solves this by pulling wear leveling and bad-block handling out of the filesystem entirely and turning it into a shared service. UBI presents one or more logical volumes on top of a single MTD partition, and any filesystem — most commonly UBIFS — can sit on top of a UBI volume without needing to know anything about physical flash geometry. Splitting the flash translation layer this way makes the whole stack more modular, and it is the approach almost every modern embedded Linux product uses instead of raw JFFS2/YAFFS2.

Physical and Logical Erase Blocks

UBI’s core abstraction is the mapping between a physical erase block (PEB) — an actual erase block on the flash chip — and a logical erase block (LEB) — the erase block number that upper layers, such as UBIFS, actually address. This indirection is what makes wear leveling possible: UBI is free to move the data behind a given LEB number to a different PEB at any time, without the filesystem noticing, simply by updating the mapping table.

PEB to LEB Mapping Inside a UBI Device

+————————————————————-+ | UBI VOLUMES (logical) | | | | vol_1 (rootfs) vol_2 (data, frequently written) | | LEB 0 LEB 1 LEB 2 LEB 0 LEB 1 LEB 2 LEB 3 | | | | | | | | | | +—–|——|——|———-|——|——|——|———-+ | | | | | | | v v v v v v v +————————————————————-+ | MTD PARTITION (physical PEBs) | | | | [PEB0][PEB1][PEB2][PEB3][PEB4][BAD ][PEB6][PEB7][PEB8] | | | | Erase counts: 12 9 11 10 8 — 9 13 | +————————————————————-+ * A bad PEB is dropped from the mapping and never reused * UBI reassigns which PEB backs a given LEB to spread erase counts evenly across BOTH volumes sharing this partition

Because both volumes in the diagram share the same pool of physical erase blocks, wear leveling happens across the whole partition rather than being confined to whichever volume happens to be written most. UBI keeps an erase counter in the header of every PEB and periodically moves data from heavily-erased blocks to lightly-erased ones, and it also tracks bad blocks so they are permanently excluded from the LEB mapping the moment they are detected.

One detail that trips up newcomers: an LEB is always slightly smaller than a PEB, because UBI reserves the first page of every PEB for its own header (the erase counter and volume identifier information). For a chip with 128 KB physical erase blocks and 2 KB pages, each LEB usable by a filesystem is 126 KB, not 128 KB. Forgetting this one-page overhead is a common source of “off-by-a-page” sizing mistakes when preparing filesystem images later in this lecture.

Preparing a Partition: ubiformat

Unlike JFFS2, which can be written to a freshly flash_erase’d MTD partition, UBI requires its own preparation step because it needs somewhere to store the per-PEB erase counters before it can start tracking wear. That preparation tool is ubiformat. It needs to know the minimum I/O unit of the flash chip — for most NAND parts this is the page size, though some chips support sub-page writes. When in doubt, consult the flash datasheet and use the full page size.

# Prepare an MTD partition for UBI using a 2048-byte page size
ubiformat /dev/mtd6 -s 2048

Running this against a partition that already has UBI data on it (rather than blank flash) also preserves the existing erase counters, which is important — throwing away erase-count history would defeat the whole purpose of wear leveling by making UBI think every block is equally fresh.

Attaching the UBI Driver: ubiattach

Once a partition has been formatted, the UBI driver has to be told to load it. This step is called “attaching,” and it is where the kernel scans the partition, rebuilds its PEB-to-LEB mapping in RAM, and exposes a UBI device node.

# Attach mtd6 with a 2048-byte VID header offset
ubiattach -p /dev/mtd6 -O 2048

A successful attach creates /dev/ubi0, the control node for that UBI device. If you attach a second MTD partition, it becomes /dev/ubi1, and so on — a single Linux system can host several independent UBI devices simultaneously, each backed by its own MTD partition.

The attach step reads and validates every PEB header, so its cost scales with the number of physical erase blocks on the partition — on a large NAND chip this can take several seconds, which matters on products with tight boot-time budgets. Linux 3.7 introduced UBI fastmap (kernel config option CONFIG_MTD_UBI_FASTMAP) specifically to address this: fastmap periodically checkpoints the mapping table to flash itself, so on the next attach the kernel can read that checkpoint directly instead of scanning every PEB from scratch, dramatically cutting attach time on large devices.

Creating and Inspecting Volumes

A freshly-formatted and attached UBI device has zero volumes. You create them with ubimkvol, specifying a name and a size. For example, splitting a 128 MB partition into a 32 MB volume and a 96 MB volume:

# Create two UBI volumes on ubi0
ubimkvol /dev/ubi0 -N vol_1 -s 32MiB
ubimkvol /dev/ubi0 -N vol_2 -s 96MiB

This produces device nodes /dev/ubi0_0 and /dev/ubi0_1. To confirm what actually got created, ubinfo gives a full report of the device and every volume on it:

# Inspect the UBI device and its volumes
ubinfo -a /dev/ubi0
ubi0
Volumes count:                        2
Logical eraseblock size:              126976 bytes, 124.0 KiB
Total amount of logical eraseblocks:  1024 (130023424 bytes, 124.0 MiB)
Amount of available logical eraseblocks: 0 (0 bytes)
Maximum count of volumes:             128
Count of bad physical eraseblocks:    0
Count of reserved physical eraseblocks: 20
Current maximum erase counter value:  3
Minimum input/output unit size:       2048 bytes
Character device major/minor:         250:0
Present volumes:                      0, 1

Volume ID:   0 (on ubi0)
Type:        dynamic
Size:        256 LEBs (32505856 bytes, 31.0 MiB)
State:       OK
Name:        vol_1

Volume ID:   1 (on ubi0)
Type:        dynamic
Size:        768 LEBs (97517568 bytes, 93.0 MiB)
State:       OK
Name:        vol_2

Notice that the LEB size shown (124.0 KiB) is smaller than the physical erase block, exactly as explained earlier — this is the one-page header overhead in action.

Volume Types: Dynamic vs Static

TypeBehaviourTypical use case
DynamicFree-form read/write, no built-in integrity checkData partitions, general-purpose UBIFS storage
StaticWritten once, size fixed to actual data, CRC-checked on every readRead-only images such as a kernel or a squashed rootfs

Most UBIFS-backed volumes are dynamic, since UBIFS itself already provides its own on-disk integrity structures. Static volumes are more commonly used for raw binary blobs that need tamper/corruption detection without a full filesystem on top.

Talking to UBI Programmatically

Everything shown above can also be driven from C using the UBI ioctl interface exposed on /dev/ubi_ctrl and the per-device control nodes, which is useful when volume management needs to happen from an application rather than a shell script — for example, a factory provisioning tool. Here is a minimal original example, ep_ubi_mkvol.c, that creates a dynamic UBI volume programmatically:

/* ep_ubi_mkvol.c - minimal example: create a UBI volume via ioctl */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <mtd/ubi-user.h>

int main(int argc, char *argv[])
{
    if (argc != 4) {
        fprintf(stderr, "usage: %s /dev/ubi0  \n", argv[0]);
        return 1;
    }

    const char *ubi_dev  = argv[1];
    const char *vol_name = argv[2];
    long long   vol_size = atoll(argv[3]);

    int fd = open(ubi_dev, O_RDONLY);
    if (fd < 0) {
        perror("open ubi device");
        return 1;
    }

    struct ubi_mkvol_req req;
    memset(&req, 0, sizeof(req));
    req.vol_id   = UBI_VOL_NUM_AUTO;
    req.alignment = 1;
    req.bytes    = vol_size;
    req.vol_type = UBI_DYNAMIC_VOLUME;
    strncpy(req.name, vol_name, UBI_MAX_VOLUME_NAME);
    req.name_len = strlen(vol_name);

    if (ioctl(fd, UBI_IOCMKVOL, &req) < 0) {
        perror("UBI_IOCMKVOL");
        close(fd);
        return 1;
    }

    printf("ep_ubi_mkvol: created volume '%s' (%lld bytes) as vol_id %d\n",
           vol_name, vol_size, req.vol_id);

    close(fd);
    return 0;
}

Build and run it on a target with a UBI device already attached:

$ gcc -o ep_ubi_mkvol ep_ubi_mkvol.c
$ ./ep_ubi_mkvol /dev/ubi0 vol_data 33554432
ep_ubi_mkvol: created volume 'vol_data' (33554432 bytes) as vol_id 2

Behind the scenes this issues the exact same UBI_IOCMKVOL ioctl that the ubimkvol command-line tool uses, which is a good example of how the userspace UBI tools are themselves thin wrappers over a well-defined kernel ioctl ABI — understanding that ABI is what lets you build custom provisioning and diagnostic tools for your own product.

Real-World Use Cases

Splitting a NAND chip into rootfs + application-data volumes with shared wear leveling Providing the volume layer that UBIFS mounts on top of Static volumes for tamper-evident kernel/bootloader storage A/B firmware update schemes using two dynamic volumes

Common Mistakes and Troubleshooting

SymptomLikely CauseFix
ubiattach fails with “bad VID header offset”-O value doesn’t match the page size used at ubiformat timeRe-check the chip’s page size and keep -s / -O consistent
Volume creation fails with “not enough space”Forgetting the one-page LEB overhead when sizing volumesSize volumes against usable LEB capacity, not raw PEB size
Long boot times on large NANDFastmap not enabled, full PEB scan on every attachEnable CONFIG_MTD_UBI_FASTMAP in the kernel config
Erase counters appear reset after reformatRan ubiformat without -y confirmation on a live deviceNever re-run ubiformat on a partition with data you want wear history preserved for

Best Practices

Group volumes with different write frequencies on the same UBI device to share wear leveling Enable UBI fastmap on products where boot time matters Use static volumes for anything you need CRC-verified on every read Always leave a small number of spare PEBs unallocated for future bad-block replacement

Performance consideration: attach time is proportional to partition size unless fastmap is enabled — plan for this explicitly on products with strict boot-time requirements. Security consideration: UBI itself does not encrypt or authenticate data; if you need confidentiality or integrity against a malicious actor with physical access, that has to be layered on top (for example, via dm-crypt or a filesystem with built-in authentication), not assumed from UBI’s CRC/erase-count bookkeeping.

Summary and Key Takeaways

UBI decouples wear leveling and bad-block handling from any single filesystem PEB-to-LEB mapping is the core abstraction that makes block movement transparent ubiformat prepares a partition, ubiattach loads it, ubimkvol creates volumes Fastmap trades a small amount of flash space for much faster attach times

Conclusion

UBI is the piece of infrastructure that made modern raw-NAND embedded Linux storage practical: instead of every filesystem reinventing wear leveling, UBI provides it once, as a shared, well-tested layer that anything — most notably UBIFS, covered in the next lecture of this free linux device drivers course — can build on. Once you’re comfortable creating and inspecting UBI volumes with ubiformat, ubiattach, ubimkvol, and ubinfo, you have everything you need to move on to building an actual filesystem image on top of them.

Frequently Asked Questions

What is the difference between MTD and UBI?

MTD is the low-level interface to raw flash chips (reading, writing, erasing individual blocks). UBI sits on top of MTD and adds wear leveling, bad-block handling, and the ability to host multiple logical volumes on one physical partition — MTD alone provides none of that.

Why is an LEB smaller than a PEB?

UBI reserves the first page of every physical erase block for its own header, which stores the erase counter and volume identifier information. That reserved page is subtracted from the usable size, so the logical erase block is always one page smaller than the physical one.

Do I need to run ubiformat every time I attach a UBI partition?

No. ubiformat is a one-time preparation step. Once a partition holds valid UBI data, you only need ubiattach to load it — running ubiformat again would erase the existing volumes and their wear-leveling history.

What is UBI fastmap and when should I enable it?

Fastmap is an optional mechanism, available from Linux 3.7 onward, that checkpoints the PEB-to-LEB mapping to flash so future attaches don’t need to scan every physical erase block. Enable it via CONFIG_MTD_UBI_FASTMAP on any product where NAND size or boot-time budget makes full-scan attach too slow.

Can one MTD partition host more than one UBI volume?

Yes — that is the whole point of UBI. A single MTD partition, once attached as a UBI device, can host many logical volumes, and wear leveling is shared evenly across all of them rather than being confined per-partition.

What is the difference between a dynamic and a static UBI volume?

A dynamic volume behaves like ordinary read/write storage with no built-in integrity checking. A static volume is written once, has its size fixed to the actual data, and is CRC-checked on every read — making it suited to read-only images like kernels where corruption detection matters.

Does UBI provide encryption or security guarantees?

No. UBI’s erase counters and CRC checks protect against wear and accidental corruption, not against a malicious actor. Confidentiality and tamper-resistance need to be added by a separate layer, such as full-disk encryption or an authenticated filesystem.

Is UBI specific to NAND flash?

UBI is designed primarily around raw NAND characteristics (erase-before-write, bad blocks), but it works with any MTD device that exposes those semantics, including some NOR configurations, provided the underlying MTD driver supports the required operations.

Continue this free Linux kernel development course

Next up: building and mounting a real UBIFS filesystem image on top of the volumes you just created.

Next Lecture: UBIFS Back to Course Index

PREV_LEC | NEXT_LEC

2 Comments

Leave a Reply

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