Reducing JFFS2 mount time

When it comes to choosing a file system for flash memory JFFS2 is top of the list for most projects. It is robust and works with most flash chips using NOR or NAND technologies. The problem is that JFFS2 partitions can be slow to mount and as flash memory sizes increase, so does the issue of the tine to mount.

There is a solution: "erase block summary" or EBS. It has been in Linux kernels since 2.6.15.

How it works

JFFS2 is a log-based file system, meaning that each change to a file is written to the flash memory as a node. There is no directory in the normal sense, but by reading all the nodes in sequence you can deduce one. Doing things this way makes the file system robust - either the last node was written or it wasn't, but in both cases the data seen by the user is consistent. But, it also makes mounting slow because of the time taken to read the nodes and construct a directory in memory.

EBS helps by writing a "summary" node at the end of each complete flash block so that at mount time you only have to read that node and not the entire block. If the summary node is missing, maybe because of a system power-down before it could be written to flash, nothing bad happens - JFFS2 just falls back to scanning the whole block as it would have done before.

The result is a reduction in mount time by a factor of 4 or 5. There are a few downsides as well, but in most cases they are acceptable

  • More RAM used to hold the summary information for the current erase block
  • Less storage space in the flash because the summary node takes some room

Seeing it work in practice

First you need a kernel with CONFIG_JFFS2_SUMMARY enabled. It is one of the options in the JFFS2 configuration in Filesystems->Miscellaneous filesystems. With that turned on, all filled ease blocks in your jffs2 file system will have a summary node.

Seeing is believing, so I wrote a program called nand_check to scan a flash partition and display a character for each erase block:

  B        Bad block
  .        Empty
  -        Partially filled
  =        Full, no summary node
  S        Full, summary node

Here is an example of freshly erased partition, with, as it happens, one bad block:

# /nand_check /dev/mtd4
Flash type is 4
Block size 131072, page size 2048, OOB size 64
16777216 bytes, 128 blocks
...........................B=...........
........................................
........................................
........

Now, copy some files and scan again

# mount -t jffs2 /dev/mtdblock4 /mnt
# cp -a /usr/lib /mnt
# /nand_check /dev/mtd4
Flash type is 4
Block size 131072, page size 2048, OOB size 64
16777216 bytes, 128 blocks
...........................B=...........
........................................
..................................-SSSSS
SSSSSSSS

You can see that blocks at the end of the partition have been written and that they all have summary nodes. There is one partially filled block (marked with a '-'), and of course that does not have summary information.

Preparing JFFS2 images: sumtool

It is quite common to create a jffs2 image file to "pre-load" flash with a set of files. The tools that does that, mkfs.jffs2, does not do anything about summary nodes, or to put it another way, by default you will end up with an image that takes a long time to mount. The secret is that you have to use a second tool from mtdutils: sumtool. Here is an example

mkfs.jffs2 -n -e 0x20000 -p 0x20000 -d rootdir -o rootdir.jffs2
sumtool -n -e 0x20000 -p -i rootdir.jffs2 -o rootdir-sum.jffs2

In both cases, I use '-n' to suppress cleanmarker nodes since this is for NAND flash and the cleanmarkers were created in the OOB area by flash_eraseall -j. The eraseblock size for this flash chip is 128 KB, so I add -e 0x20000. The -p option means pad the last block to the eraseblock size, -d is the directory to use for input files, -o is the output file.

Now lets write the first one to flash and take a look:

# flash_eraseall -j /dev/mtd4
# nandwrite /dev/mtd4 rootdir.jffs2
# /nand_check /dev/mtd4
Flash type is 4
Block size 131072, page size 2048, OOB size 64
16777216 bytes, 128 blocks
==================.........B=...........
........................................
........................................
........

Same thing using the second image

# flash_eraseall -j /dev/mtd4
# nandwrite /dev/mtd4 rootdir-sum.jffs2
# /nand_check /dev/mtd4
Flash type is 4
Block size 131072, page size 2048, OOB size 64
16777216 bytes, 128 blocks
SSSSSSSSSSSSSSSSSS=........B=...........
........................................
........................................
........

Pretty much as you would expect: the first has no summary nodes but the second does.

Mount times

These measurements were made using a Digi development board, with an ARM926EJ-S core running at 155 MHz, and a single 1 GiBit (128 MiByte) ST NAND flash chip with 2KB pages and 128KB erase blocks. I used three file systems

Size (MiB) No. files No. dirs Image size(MiB) Image size with summary (MiB) Overhead
Empty 0 0 0 N/A N/A N/A
Small 4.4 444 40 2.250 2.375 5.5%
Large 56 556 2515 22.125 23.125 4.5%

Here, Size is the size of the files and directories as reported by du -sh. The Image Size is that produced by mkffs.jffs2 using the default zlib compressor. The Overhead column shows the additional percentage of storage needed for the summary nodes.

Time to mount

No summary Summary Improvement
Empty 2.83 N/A N/A
Small 4.55 3.03 150%
Large 19.5 3.6 540%

It seems that there is a constant overhead of almost 3s just to do the mount. With the small file system this dominates so the mount time with EBS is only one and a half times faster. With the large file system the time spent scanning the blocks dominates and so the improvement is more than five fold.

References

http://www.linux-mtd.infradead.org/doc/jffs2.html has a brief description of JFFS2 summary nodes.
http://www.inf.u-szeged.hu/jffs2/mount.php also discusses EBS.