world leader in high performance signal processing
Trace: » jffs

Journalling Flash File System (JFFS)

Since flash devices have non-standard requirements when it comes to reading/writing of data (such as the need to erase things, and in chunks, and only for a limited number of times), the Journalling Flash File System (JFFS) was designed to provide efficient persistent storage under Linux. The current version is JFFS2 (which supersedes JFFS1).

When using JFFS2 on large (128MB or larger) flash devices - the developer must be aware of the downsides. Since there is no indexing information stored on the flash media, each JFFS2 node contains full information about itself, but there is no central index. The index is the crucial part of any file system as it is used to quickly locate any piece of information (i.e., find all the files kept in a directory, find the physical flash address where the files data is stored, etc.). In JFFS2, the index is maintained in RAM and takes significant amount of it - a 128MB NAND would use about 4MB RAM in node tables. Roughly speaking, there is a in-RAM data structure for each on-flash node.

The in RAM index must be built on each mount. For this reason, JFFS2 must scan the flash media. And it is logically then the more data you have on flash, the longer is JFFS2 mount time and memory consumption. Namely, the above 2 characteristics linearly depend on the flash size (O(N), N - flash size). This can mean scan time at boot time gets longer every day of usage. Eg. when flash is empty it takes ~10 s to mount 20Mb of flash, later when there are some files present it takes > 30s and increasing. This is normal for JFFS2. The more data is in the flash, the longer is JFFS2 being mounted. This issue is being resolved in the development of JFFS3, and is available today by using the YAFFS filesystem.

There are a few steps before you can start playing with a JFFS2 image:

  1. setting the source to describe the JFFS2 image location to the kernel
  2. create a kernel which will understand and mount a jffs2 image
  3. create the jffs2 file system (this is done automatically during a kernel make and is found in: ./uclinux-dist/images/rootfs.jffs2)
  4. write the jffs2 file system (rootfs.jffs2) into flash with U-Boot
  5. tell the kernel where the JFFS2 file system is stored
  6. boot the kernel built in step 1

JFFS2 can be enabled in the uClinux kernel and U-Boot. Enabling JFFS2 in U-Boot allows U-Boot to access kernel images stored in the JFFS2 image.

The JFFS homepage can be found at http://sources.redhat.com/jffs2/.

Partitioning

The method for managing MTD partition tables is documented in the Partitioning document. Here we will focus on just using the default partition scheme which has a partition setup specifically for a root file system

Kernel Settings

Please consult the MTD document for relevant kernel settings for the higher MTD layers. If you plan on using JFFS2 as your root file system, these options will have to be built into your kernel and not as modules. The method for loading modules before mounting the root file system is not documented here and is left as an exercise for the user.

You will also need to enable support for the JFFS2 filesystem itself.x

File systems   --->
  Miscellaneous filesystems  --->
    <*> Journalling Flash File System v2 (JFFS2) support
           (sub-options are up to you)

uClinux-dist Settings

If you wish to program/create JFFS2 images on the board (rather than just loading them via U-Boot), you will need to enable the mtd-utils package. Specifically, you will need the flash_eraseall and mkfs.jffs2 programs.

After this is complete, you can do a make, and this will build the image files in the uClinux-dist directory images.

Kernel Detected Flash Information

When the kernel boots up, you should see information about the relevant flash being detected and MTD partitions being created. If you do not see any such messages, you need to go back and review your kernel settings to make sure it matches your board.

For example, on the BF548-EZKIT, the parallel flash looks like:

physmap platform flash device: 02000000 at 20000000
cfi: mfr=0x89, id=0x891f
physmap-flash.0: Found 1 x16 devices at 0x0 in 16-bit bank
 Intel/Sharp Extended Query Table at 0x010A
 Intel/Sharp Extended Query Table at 0x010A
 Intel/Sharp Extended Query Table at 0x010A
 Intel/Sharp Extended Query Table at 0x010A
 Intel/Sharp Extended Query Table at 0x010A
Using buffer write method
Using auto-unlock on power-up/resume
cfi_cmdset_0001: Erase suspend on write enabled
3 cmdlinepart partitions found on MTD device physmap-flash.0
Creating 3 MTD partitions on "physmap-flash.0":
0x00000000-0x00040000 : "uboot"
0x00040000-0x00200000 : "kernel"
0x00200000-0x02000000 : "rootfs"

The SPI flash on the BF548-EZKIT looks like:

m25p80 spi0.1: m25p16 (2048 Kbytes)
Creating 2 MTD partitions on "m25p80":
0x00000000-0x00040000 : "bootloader"
0x00040000-0x00100000 : "linux kernel"
bfin-spi bfin-spi.0: Blackfin BF5xx on-chip SPI Contoller Driver, Version 1.0, regs_base @ 0xffc00500

And the NAND flash looks like:

BF5xx on-chip NAND FLash Controller Driver, Version 1.2 (c) 2007 Analog Devices, Inc.
bf5xx-nand bf5xx-nand.0: page_size=256, data_width=8, wr_dly=3, rd_dly=3
NAND device: Manufacturer ID: 0x20, Chip ID: 0xda (ST Micro NAND 256MiB 3,3V 8-bit)
Creating 2 MTD partitions on "NAND 256MiB 3,3V 8-bit":
0x00000000-0x00400000 : "Linux Kernel"
0x00400000-0x10000000 : "File System"

Using U-Boot to write the JFFS2 and Kernel image

Assuming that you have followed the instructions above, and want to write the JFFS2 and kernel images to flash the following would be done:

It is important that the offset in flash of the file system matches the partition map you gave the kernel. The offset and size value used bellow are just examples. Please check your kernel code for actual values.

On BF537 STAMP, the last block of NOR flash (0x203F0000 - 0x203FFFFF) is used to store MAC address.

  1. Check on host to ensure rootfs.jffs2 is less than 3M, and will fit into mtd flash partition:
    rgetz@test:~/uClinux-dist>  ls -lh ./images/rootfs.jffs2
    -rw-r--r--  1 rgetz users 961K 2005-08-09 17:12 ./images/rootfs.jffs2

    This images is 961k, so it meets our requirement.

  2. Copy the images to the tftp directory. On linux, this is usually /tftpboot while for coLinux it is /svr/tftp:
    rgetz@test:~/uClinux-dist>  cp ./images/rootfs.jffs2 /tftpboot/rootfs.jffs2
    rgetz@test:~/uClinux-dist>  cp ./images/vmlinux /tftpboot/vmlinux
    rgetz@test:~/uClinux-dist>  cp ./images/vmImage /tftpboot/vmImage
  3. on the target, you must download the Images, and try them out; Depending on your Flash Type:
    • For Parallel NOR Flash, :
      stamp> tftpboot 0x1000000 rootfs.jffs2
      stamp> protect off 0x20100000 0x203FFFFF
      stamp> erase 0x20100000 0x203FFFFF
      stamp> cp.b 0x1000000 0x20100000 $(filesize)
    • For Serial NOR Flash, :
      stamp> tftpboot 0x1000000 rootfs.jffs2
      stamp> eeprom write 0x1000000 0x100000 $(filesize)
  4. Check the magic number - it should be 1985h, Depending on your Flash Type:
    • For Parallel Flash:
      stamp> md.b 0x20100000 0x02
      20100000: 85 19
    • For Serial Flash:
      stamp> eeprom read 0x2000000 0x100000 0x2
      stamp> md.b 0x2000000 0x02
      0000000: 85 19
  5. On the target, save the kernel image, depending on the flash type. Also make sure the kernel image size is smaller than the partition size (in this example 0xC0000 bytes).
    • For Parallel Flash:
      stamp> erase 0x20040000 0x200FFFFF
      stamp> tftp 0x1000000 vmImage
      stamp> cp.b 0x1000000 0x20040000 $(filesize)
    • For Serial Flash, just like parallel flash, it is important to erase the entire device, before putting in a new file system image. This is done by setting the the SDRAM to 0, and copying that to the flash:
      stamp> mw.b 0x1000 0x0 $(SizeOfFlash)
      stamp> tftp 0x1000 vmImage
      stamp> eeprom write 0x1000 $(offset) $(SizeOfFlash)

      If your flash is larger than your SDRAM, do something like:

      stamp> mw.b 0x1000 0x0 $(half_SizeOfFlash)
      stamp> eeprom write 0x1000 $(kernel_start) $(half_SizeOfFlash)
      stamp> eeprom write 0x1000 $(half_SizeOfFlash) $(half_SizeOfFlash)
  6. On the target, set the necessary environmental variables, Depending on the Flash you have:
    • For Parallel Flash:
      stamp> setenv bootargs root=/dev/mtdblock2 rw rootfstype=jffs2
      stamp> setenv flashboot bootm 0x20040000
      stamp> setenv bootcmd run flashboot
      stamp> save
      stamp> run bootcmd
    • For Serial Flash:
      stamp> setenv bootargs root=/dev/mtdblock2 rw rootfstype=jffs2
      stamp> setenv flashboot 'eeprom read 0x1000000 0x40000 0x180000; bootm 0x1000000'
      stamp> setenv bootcmd run flashboot
      stamp> save
      stamp> run bootcmd

      After a reboot, U-boot will automatically boot this new kernel (unless the countdown is aborted with a key press).

The variables used in the example above: $(half_SizeOfFlash), $(kernel_start), $(offset), $(SizeOfFlash) are not U-Boot variables, and are placeholders for the actual size of flash (in hex), kernel start address, offset of where to store the file system or kernel in the Flash. You need to fill these numbers in by hand.

Remarks

  • For Parallel Flash, once the JFFS2 image is in flash the ls and fsload command can be used. This is not available for serial flash.
    • ls
      stamp> ls
       drwxr-xr-x        0 Mon Aug 01 18:43:01 2005 bin
       drwxr-xr-x        0 Mon Aug 01 17:41:33 2005 dev
       drwxr-xr-x        0 Mon Aug 01 18:43:01 2005 etc
       drwxr-xr-x        0 Mon Aug 01 17:41:33 2005 home
       drwxr-xr-x        0 Mon Aug 01 17:41:33 2005 lib
       drwxr-xr-x        0 Mon Aug 01 17:41:33 2005 mnt
       drwxr-xr-x        0 Mon Aug 01 17:41:33 2005 proc
       drwxr-xr-x        0 Mon Aug 01 17:41:33 2005 root
       lrwxrwxrwx        3 Mon Aug 01 18:42:59 2005 sbin -> bin
       drwxr-xr-x        0 Mon Aug 01 17:41:33 2005 tmp
       drwxr-xr-x        0 Mon Aug 01 17:41:34 2005 usr
       drwxr-xr-x        0 Mon Aug 01 17:41:33 2005 var
    • fsload
      stamp> fsload kernel
      stamp> bootm

You can only use the fsload command, if a kernel image is inside the JFFS2 file system. This is not the case with vmImage! In case of abnormal power failure, it had better to put the kernel image into a separate MTD partition with read only option.

  • If you want to boot a standard ramfs ext2 kernel/filesystem image, just reboot and interrupt the countdown of U-boot. Because such a kernel uses mtdblock0 instead of mtdblock2, the bootargs environment setting has to change. So in short:
    stamp> tftp 0x1000000 uImage
    stamp> setenv bootargs root=/dev/mtdblock0 rw
    stamp> bootm

Using JFFS2 Flash partitions in the kernel

The JFFS2 filesystem can be accessed in two ways in the uClinux kernel:

  1. Boot system with EXT2 filesystem in RAM:
    • Access JFFS2 flash partition as storage medium (mounted to e.g. /mnt)
    • Kernel loaded by u-boot contains file system needed by the system (kernel/filesystem image).
  2. Boot system with JFFS2 filesystem in FLASH:
    • All changes to file system are saved into flash
    • Kernel in flash does NOT contain any file system (kernel-only image)
    • The file systems is in flash, as a JFFS2 parition

Let's look at these two cases in more detail:

Booting Kernel/filesystem image and mounting a JFFS2 partition

If the Generic uClinux RAM / ROM FS Support was enabled, the uImage kernel image still contains support for a ram-based filesystem. The ramfs partition is one higher than your highest flash partition. Because mtdblock0 contains the boot loader, mtdblock1 the kernel and mtdblock2 the JFFS2 filesystem, the ramfs is found on mtdblock3. So, to boot using the ram filesystem:

  1. Set the U-Boot bootargs environment variable to your ramfs. In U-Boot:
    stamp> setenv bootargs root=/dev/mtdblock3 rw
    stamp> save

    (save makes the change 'permanent' and is optional).

  2. Upload and boot the kernel:
    stamp> tftp 0x1000000 uImage
    stamp> bootm 0x1000000
  3. After booting, you can mount the JFFS2 partition using:
    mount -t jffs2 /dev/mtdblock2 /mnt
  4. Access partion at /mnt:
    root:~> ls /mnt/
    bin   dev   etc   home  lib   mnt   proc  root  sbin  sys   tmp   usr   var 
  5. Check to see mounted partitions
    cat /proc/mtd 

You also need to disable the init ramdisk support from kernel, for buildroot, run “make linux-meuconfig”, disable following item and make again.

    General setup   --->
     [ ] Initial RAM filesystem and RAM disk (initramfs/initrd) support

Booting Kernel-only image with JFFS2 root file system

This largely corresponds to booting the vmImage kernel which has already been flashed on the Blackfin, just now we upload a new one to ram.

  1. Booting the vmImage kernel (does not contain file system):
    • Upload kernel to the Blackfin:
      stamp> tftp 0x1000000 vmImage
    • Set the u-boot bootargs environment variable to boot from jffs2 partition:
      stamp> setenv bootargs root=/dev/mtdblock2 rw rootfstype=jffs2 
    • Boot the uploaded kernel:
      stamp> bootm 0x1000000
  2. See the available partitions:
    root:~> cat /proc/mtd
    dev:    size   erasesize  name
    mtd0: 00040000 00010000 "Bootloader"
    mtd1: 000c0000 00010000 "Kernel"
    mtd2: 00300000 00010000 "JFFS2"
    mtd3: 00000000 00001000 "EXT2fs" 
  3. Check to see mounted partitions:
    root:~> mount
    /dev/mtdblock2 on / type jffs2 (rw,noatime)
    none on /proc type proc (rw,nodiratime)
    /dev/ram0 on /var type ramfs (rw) 

    The line containing mtd3: only exists if the Generic uClinux RAM / ROM FS Support was enabled and the vmImage kernel created manually using the mkimage tool. However, because this is a kernel-only image, you cannot mount and use it. If you boot uImage instead of vmImage, with the same bootargs, the same kernel is loaded into memory - but now including a ram-based filesystem, which you can mount using e.g.:

    root:~> mount -t ext2 /dev/mtdblock3 /mnt
    root:~> ls /mnt/
    bin         home        mnt         sbin        usr
    dev         lib         proc        sys         var
    etc         lost+found  root        tmp

Mounting JFFS2 via Loop

Sometimes it may be useful to mount the jffs2 image via loop on your development system. Here are the minimum configure options needed to do this:

Device Drivers  --->
  Memory Technology Devices (MTD)  --->
    <*> Memory Technology Device (MTD) support
    [*]   MTD partitioning support
    <*>   Direct char device access to MTD devices
    <*>   Caching block device access to MTD devices
      Self-contained MTD device drivers  --->
        <*> Test driver using RAM

File systems  --->
  Miscellaneous filesystems  --->
    <*> Journalling Flash File System v2 (JFFS2) support

If you build these as modules, make sure you load everything:

# modprobe mtdchar
# modprobe mtdblock
# modprobe mtdram
# modprobe jffs2

Since you cannot mount the image directly, you need to load it into the mtd ram test driver:

# dd if=jffs2.img of=/dev/mtd0
# mount -t jffs2 /dev/mtdblock0 /mnt/jffs2

Like magic, you should now be able to browse/modify/whatever the filesystem!

Troubleshooting

  • If you see an error during kernel boot that looks like:
ftl_cs: FTL header not found.

This can be resolved by ensuring that “FTL (Flash Translation Layer) support” in the kernel config is not enabled.

  • During file system mount you encounter following messages like below:

Empty flash at 0x0001fffc ends at 0x00020000
CLEANMARKER node found at 0x00020000, not first node in block (0x00000000)

JFFS2 warning: (165) jffs2_sum_scan_sumnode: Summary node crc error, skipping summary information.
CLEANMARKER node found at 0x00010000, not first node in block (0x00000000)
CLEANMARKER node found at 0x00020000, not first node in block (0x00000000)

These situations can occur if you create a JFFS2 image with mkfs.jffs2 using the wrong value for the erase block size (the -e option). The default is 64KiB, because that's the smallest erase block size you're likely to encounter often, and creating an image with smaller eraseblock size than the actual hardware is harmless -- it just gives annoying messages, and delays your mount process. If you see the messages mentioned above, check the erase block size of your device (look in /proc/mtd if you don't know), and create your JFFS2 image for it with the correct -e option. [Partially copied form http://www.linux-mtd.infradead.org/doc/jffs2.html]

Example:

root:/> cat /proc/mtd
dev:    size   erasesize  name
mtd0: 00040000 00040000 "bootloader(spi)"
mtd1: 00100000 00040000 "linux kernel(spi)"
mtd2: 00ec0000 00040000 "file system(spi)"

JFFS2 mount process speedup

JFFS2 file system mounts can take up to several seconds, especially on large file systems with many files. There is one technique called Erase Block Summary (EBS) to significantly speed up the mount process.

Erase Block Summary (EBS)

The goal of EBS is to speed up the mount process. It stores summary information at the end of every erase block. At mount time it is no longer necessary to scan all nodes individually (and read all pages of the erase blocks), enough to read this “small” summary.

This summary information is stored in a JFFS2_FEATURE_RWCOMPAT_DELETE node. During mount process if there is no summary node at the end of an erase block, the original scan process will be executed.

This node is generated automatically if EBS enabled for written data, but you should also use the user space tool called sumtool to insert summary information after you created a JFFS2 image with mkfs.jffs2. [From http://www.linux-mtd.infradead.org/doc/jffs2.html]

Example

JFFS2 image with Summary dedicated for an M25P128 Serial SPI flash

uclinux-dist-trunk/user/mtd-utils/mkfs.jffs2 -l -e 0x40000 -p -d uclinux-dist-trunk/romfs -D device_table-min.txt -o uclinux-dist-trunk/images/rootfs.jffs2
uclinux-dist-trunk/user/mtd-utils/sumtool -l -e 0x40000 -p -i uclinux-dist-trunk/images/rootfs.jffs2 -o uclinux-dist-trunk/images/rootfs-summary.jffs2

These steps are typically done by the Blackfin uClinux build system. You therefore only need to initiate make with following options, in case your target device requires a special erase block size.

make  MTD_SUMTOOL_FLAGS=’-l -p -e 0x40000’ MKFS_JFFS2_FLAGS=’-l –p -e 0x40000’

Defaults are:

MTD_SUMTOOL_FLAGS = -l -p
MKFS_JFFS2_FLAGS = -l -p

Kernel Configuration

The Summary feature needs to be enabled during kernel configuration

File systems --->
Miscellaneous filesystems  --->
 <*> Journalling Flash File System v2 (JFFS2) support
 (0)   JFFS2 debugging verbosity (0 = quiet, 2 = noisy)
 [*]   JFFS2 write-buffering support
 [ ]     Verify JFFS2 write-buffer reads
 [*]   JFFS2 summary support (EXPERIMENTAL)
 [ ]   JFFS2 XATTR support (EXPERIMENTAL)
 [ ]   Advanced compression options for JFFS2

Benchmark

  • JFFS2 version 2.2 on M25P128 SPI Serial Flash
  • 334 files total 6.2 Mbyte (uncompressed)
  • JFFS2 rootfs size ~ 3.7 Mbyte
JFFS2 JFFS2 (SUMMARY)
1.5 sec. 0.24 sec.

In this particular case mounting the JFFS2 file system with Summary support is 6x faster than without

Without Summary

JFFS2 version 2.2. (NAND) (c) 2001-2006 Red Hat, Inc.
m25p80 spi0.1: m25p128 (16384 Kbytes)
Creating 3 MTD partitions on "m25p80":
0x00000000-0x00040000 : "bootloader(spi)"
0x00040000-0x00140000 : "linux kernel(spi)"
0x00140000-0x01000000 : "file system(spi)"

root:/> cat /proc/mtd
dev:    size   erasesize  name
mtd0: 00040000 00040000 "bootloader(spi)"
mtd1: 00100000 00040000 "linux kernel(spi)"
mtd2: 00ec0000 00040000 "file system(spi)"
root:/> flash_eraseall -j /dev/mtd2
Erasing 256 Kibyte @ e80000 -- 98 % complete. Cleanmarker written at e80000.
root:/> cp /var/rootfs.jffs2 /dev/mtd2
root:/> time mount -t jffs2 /dev/mtdblock2 /mnt
real    0m 1.50s
user    0m 0.00s
sys     0m 0.05s
root:/> umount /mnt
root:/> time mount -t jffs2 /dev/mtdblock2 /mnt
real    0m 1.50s
user    0m 0.00s
sys     0m 0.06s
root:/>

With Summary

JFFS2 version 2.2. (NAND) (SUMMARY)  (c) 2001-2006 Red Hat, Inc.
m25p80 spi0.1: m25p128 (16384 Kbytes)
Creating 3 MTD partitions on "m25p80":
0x00000000-0x00040000 : "bootloader(spi)"
0x00040000-0x00140000 : "linux kernel(spi)"
0x00140000-0x01000000 : "file system(spi)"

root:/> flash_eraseall -j /dev/mtd2
Erasing 256 Kibyte @ e80000 -- 98 % complete. Cleanmarker written at e80000.
root:/> cp /var/rootfs-summary.jffs2 /dev/mtd2
root:/> time mount -t jffs2 /dev/mtdblock2 /mnt
real    0m 0.24s
user    0m 0.00s
sys     0m 0.02s
root:/> time umount /mnt
real    0m 0.02s
user    0m 0.00s
sys     0m 0.02s
root:/> time mount -t jffs2 /dev/mtdblock2 /mnt
real    0m 0.24s
user    0m 0.00s
sys     0m 0.03s
root:/> cd mnt
root:/mnt> ls
bin   dev   etc   home  lib   mnt   proc  root  sbin  sys   tmp   usr   var
root:/mnt>