SmartFS stands for Sector Mapped Allocation for Really Tiny (SMART) flash. It is a filesystem that has been designed to work primary with small, serial NOR type flash parts that are 1M byte to 16M byte in size (though this is not a limitation). The filesystem operates by segmenting the flash (or flash partition) into "logical sectors" of equal size and then managing them (allocating, mapping, chaining, releasing, etc.) to build files and directories.
The system consists of two layers built on top of a standard NuttX MTD driver layer (with it's associated hardware abstraction layer).
The code directly above the NuttX MTD driver is the SMART MTD layer. This interfaces with the MTD (flash driver) layer and handles low-level media operations such as logical sector allocation, freeing and management, erase block management, low-level formatting, wear leveling, etc.
On top of the SMART MTD layer is the Smart Filesystem code. The SmartFS code uses the logical sector services of the SMART MTD layer to provide file and directory level management, such as creating new files, chaining logical sectors together to create files, creating directories and file / directory search and management routines.
| Smart FS | fs / smartfs / * | |||
|---|---|---|---|---|
| SMART MTD | drivers / mtd / smart.c | |||
| MTD Driver | m25px | sst25 | filmtd | etc. |
| HW Driver | spi dev | spi dev | VFS | … |
Setting up a device for use with SmartFS is typically done in the config specific source initialization files and would look something like:
int board_app_initialize(uintptr_t arg)
{
FAR struct spi_dev_s *spi;
FAR struct mtd_dev_s *mtd;
int minor = 0;
/* Initialize the SPI bus #3 with an M25P FLASH driver */
spi = stm32_spibus_initialize(3);
mtd = m25p_initialize(spi);
/* Initialize SMART MTD to work with M25P FLASH device */
smart_initialize(minor, mtd, NULL);
}
|
Upon successful initialization of the code above, the NuttX Virtual File System (VFS) will contain a new entry called /dev/smart0 to represent the SMART MTD device. Note that this is not a filesystem, but rather a raw block device (which may or may not already be formatted for use with SmartFS). To use the /dev/smart0 as a filesystem, it must be initialized and mounted to the VFS as follows:
nsh> mksmartfs /dev/smart0 nsh> mount -t smartfs /dev/smart0 /mnt |
There are a number of companies that manufacture FLASH parts, and typically they use varying terminology in their data sheets when referring to device geometry. All devices generally have different region sizes for programming, erasing and reading and usually use terms like page, erase block, sector and/or sub-sector. To avoid confusion, NuttX uses the following terminology:
Given that each manufacturer and each part has varying geometry sizes, SmartFS uses a Logical Sector whose size is determined when the device is formatted (defaulting to CONFIG_MTD_SMART_SECTOR_SIZE). The SmartFS code then performs all operations using logical sectors and maps physical accesses to the device based on it's reported geometry. This allows filesystem performance tuning (total sectors, minimum allocation size, overhead waste, etc.) independently from the device's erase block size, etc. Each logical sector contains a 10-byte header (5 for MTD layer, 5 for FS layer) for format management.
An example 128K Byte Flash with 32K and 4K Erase Block sizes and 256 byte page read/write sizes:
| Bulk Erase | Entire Device | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Sector Erase | 32K | 32K | 32K | 32K | ||||||||||||||
| Sector Erase | 4K | … | 4K | 4K | … | 4K | 4K | … | 4K | 4K | … | 4K | ||||||
| Page read/write | 256 | 256 | … | 256 | 256 | … | 256 | … | 256 | |||||||||
| Logical Sector (FS) | 512 | … | 512 | … | ||||||||||||||
In the example above, a SMART MTD logical sector size of 512 bytes was chosen. On the 128K Byte FLASH represented in the table above, this means there would be:
128K / 512 = 256 Logical Sectors Total4K / 512 = 8 Logical Sectors per Erase Block512 / 256 = 2 MTD Read/Write Blocks per SMART Logical Sector10 / 512 = 1.95% Overhead for logical sector headersA maximum of about 250 files / directories supported (considering format overhead)Allocations to files in 512 byte chunks (a zero-length file consumes 512 bytes)Choosing a SMART Logical sector size of 256 bytes instead would give the following results:
128K / 256 = 512 Logical Sectors Total4K / 256 = 16 Logical Sectors per Erase Block256 / 256 = 1 MTD Read/Write Block per SMART Logical Sector10 / 256 = 3.9% Overhead for logical sector headersA maximum of about 506 files / directories supported (considering format overhead)Allocations to files in 256 byte chunks (a zero-length file consumes 256 bytes)Most of the NuttX MTD drivers (in the drivers/mtd directory) have been tested to work with SmartFS / SMART MTD layer and to report block / erase block sizes correctly. However there may be one or more drivers that have not been validated. When configuring SmartFS for the first time using a new / unknown MTD driver, validate the reported geometries are sane based on the description above.
Additionally, care must be taken when selecting the SMART MTD logical sector size. The selected size is represented using 3 bits in the logical sector status byte and stored on each sector. This means valid values for the logical sector are:
As shown in the table / example above, the logical sector size selection controls the maximum number of files and minimum file size on the volume. There are some limitations to the value selected imposed by the SMART MTD code. The logical sector size must be selected such that:
Total MTD Erase Blocks / 2 < (Logical Sector Size - 36) * 3Selecting a logical sector size that creates 256 logical sectors per erase block will create some wasted space on the device. The SMART MTD layer uses a 1-byte variable for the "free sector count" and the "released sector count". This means it can only track up to 255 logical sectors per erase block. When the sectPerEraseBlk == 256, the last logical sector in each erase block will never be used, thus causing wasted space on the device.
Efficient management of the filesystem requires building and maintaining RAM resident status information of the SMART MTD logical sector structure on the physical device. During the 'smart_initialize' function, the code performs a device scan ('smart_scan' routine) to perform this action. Tasks performed by the smart_scan are:
If CONFIG_MTD_SMART_MINIMIZE_RAM is not set:
If CONFIG_MTD_SMART_WEAR_LEVEL is set:
The amount of RAM consumed is dependent on the config settings (MINIMIZE_RAM, WEAR_LEVEL, etc.). Rough calculation details are presented for both settings of CONFIG_MTD_SMART_MINIMIZE_RAM:
CONFIG_MTD_SMART_MINIMIZE_RAM is not set
Item | RAM Requirement | 1MB / 256 Logical | 8MB / 1024 Logical |
|---|---|---|---|
dev struct | 368-380 (approx) | 376 | 376 |
Logical sector map | total_sectors * 2 | 8192 | 16384 |
Erase block free count | total erase blocks | 16 | 128 |
Erase block release count | total erase blocks | 16 | 128 |
Wear status | total erase blocks / 2 | 8 | 64 |
MTD sector R/W buffer | logical sector size | 256 | 1024 |
FS sector R/W buffer | logical sector size | 256 | 1024 |
Total | 9,176 | 19,128 |
On larger volumes, the RAM requirement increases significantly because of the logical sector to physical sector mapping table. To help keep RAM requirement under control, setting the CONFIG_MTD_SMART_MINIMIZE_RAM option eliminates this sector-to-sector map and replaces it with a sector-to-sector cache. The cache size is user defined via the CONFIG_MTD_SMART_SECTOR_CACHE_SIZE option. Using this RAM reduction mode trades off RAM usage for performance.
The cache always contains the format and root-directory logical to physical mapping entries, and then stores additional mappings of recently used logical sectors. When a logical sector is requested that is not contained in the cache, then the MTD device is scanned front-to-back until it's physical location on the device is located. Additionally, if the number of logical sectors per erase block is 16 or less, then the "free count" and "release count" can be packed into a single byte per erase block.
CONFIG_MTD_SMART_MINIMIZE_RAM=y, CONFIG_MTD_SMART_SECTOR_CACHE_SIZE=64
Item | RAM Requirement | 1MB / 256 Logical | 8MB / 1024 Logical |
|---|---|---|---|
dev struct | 368-400 (approx) | 392 | 392 |
Logical sector cache | cache entries * 6 | 384 | 384 |
Free sector bitmap | total sectors / 8 | 512 | 1024 |
Erase block free count | total erase blocks | 16 | 128 |
Erase block release count | total erase blocks | 16 | 128 |
Wear status | total erase blocks / 2 | 8 | 64 |
MTD sector R/W buffer | logical sector size | 256 | 1024 |
FS sector R/W buffer | logical sector size | 256 | 1024 |
Total | 1,832 | 4,168 |
Creating partitions on the MTD flash / media provides the benefits of physically isolating one filesystem from another, as well as a mechanism for creating multiple mount points within the VFS. There are some pros and cons to consider when deciding to use partitions with SmartFS:
Partitions - PROS
Partitions - CONS
When using partitions with SmartFS, a partition name should be specified during invocation of the 'smart_initialize' routine. Though not a rule, a suggested standard is to provide partition names like 'p1', 'p2', etc. Then the SMART MTD device entries in the /dev directory will take the form:
/dev/smart0p1 /dev/smart0p2 etc. |
Prior to the addition of partitions in NuttX, the SmartFS implementation had already implemented a feature which allows multiple VFS mount-points to a single SMART MTD device. This gives the appearance of multiple SmartFS filesystems, though in reality it simply is a single filesystem with multiple logical root-directories. Each of the root-directories on the filesystem will be logically isolated from the others, and after the 'mksmartfs' each will have it's own /dev/smart* entry.
Multi-Root Directory PROS
Multi-Root Directory CONS
This feature must be enabled specifically using the CONFIG_SMARTFS_MULTI_ROOT_DIRS=y option. Setting this option will cause the SMART MTD /dev entries to be appended with a directory number, such as 'd1', 'd2', etc. Prior to creating a SmartFS filesystem on the raw device, a single entry will be identified, such as:
/dev/smart0d1 /dev/smart1p1d1 (device with partitions and multi-root directories) |
Then after executing the 'mksmartfs' command, additional entries will appear (each of which can be mounted to a different VFS location):
nsh> ls /dev ... /dev/ram0 /dev/smart0d1 /dev/zero nsh> mksmartfs /dev/smart0d1 3 nsh> ls /dev ... /dev/ram0 /dev/smart0d1 /dev/smart0d2 /dev/smart0d3 /dev/zero nsh> mount -t smartfs /dev/smart0d1 /data nsh> mount -t smartfs /dev/smart0d2 /apps nsh> mount -t smartfs /dev/smart0d3 /recover |
When the PROCFS interface is enabled, each mounted SmartFS device will appear under:
/proc/fs/smartfs/smart# |
The pseudo files reported for each entry will depend on the configured options. Entries that can currently appear are:
Entry | CONFIG_MTD_SMART_* | Meaning |
|---|---|---|
status | Report volume status including geometry | |
debuglevel | Write ASCII '0' - '2' to set debug print level | |
erasemap | WEAR_LEVEL=y | Report map (A-N) of erase block erasures |
mem | ALLOC_DEBUG=y | Print report of all SMART MTD memory allocs |
Example procfs usage:
nsh> mount -t smartfs /dev/smart0 /mnt nsh> mount -t procfs /proc nsh> cat /proc/fs/smartfs/smart0/status Format version: 1 Name Len: 16 Total Sectors: 4096 Sector Size: 256 Format Sector: 0 Dir Sector: 256 Free Sectors: 4078 Released Sectors: 0 Unused Sectors: 0 Block Erases: 0 Sectors Per Block: 256 Sector Utilization:100% Uneven Wear Count: 0 nsh> cat /proc/fs/smartfs/smart0/erasemap BBACAACA AABAAAAA nsh> |
The SmartFS code and MTD layer have been pretty well tested and used in production products. If things aren't working for you, there are a couple of places to start debugging first.
Ensure the geometry of the FLASH MTD driver is following the NuttX standard for block / erase block geometry sizes. Most of them do, but via simple inspection of the code (as of version 7.16, July 12, 1016), the drivers that are likely to have improper Geometry reporting (and thus incompatible with SmartFS) are:
When configuring to use SmartFS, check the reported geometry of the MTD driver you are using. If the geo.blocksize is reported to be the same as the geo.sectorsize, then there is likely an issue with the MTD driver implementation. But it is likely to be a reporting problem of the geo.blocksize that is incorrect AND possibly the starting address calculations in the _bwrite / _bread routines may be incorrect. These are BLOCK read / write operations and calculations need to be performed using the block size (typically 256), not the sector size (4K, 32K, etc.).
Double check all of the CONFIG options for both the MTD driver and the SMART MTD layer:
Ensure the FLASH can be written successfully. If the WP pin is pull active (i.e. the part is write protected), then the SMART MTD layer will not be able to write any data. Additionally validate there are no individual protected sectors on the device.