狗拿耗子
YAFFS2
———狗拿耗子第三篇 1、YAFFS2 的背景 YAFFS2 是 Charles Manning 开发的 NAND Flash 文件系统。可以从 www.yaffs 获得详细 的描述和最新的版本。关于 Nand Flash 与 Nor Flash 的不同之处,网上有很多文章叙述,这 里不再罗嗦。反正是在嵌入式系统中,一般是 Nor Flash 存放在 Boot 程序,Nand Flash 作为 正式的存储设备使用。 YAFFS (Yet Another Flash File System) was designed and written by Charles Manning, of Whitecliffs, New Zealand, for the company Aleph One. YAFFS is the first file system that was designed specifically for NAND flash. Yaffs1 is the first version of this file system and works on NAND chips that have 512 byte pages + 16 byte spare (OOB;Out-Of-Band) areas. These older chips also generally allow 2 or 3 write cycles per page, which YAFFS takes advantage of - i.e. dirty pages are marked by writing to a specific spare area byte. Newer NAND flash chips have larger pages, 2048 bytes + 64 bytes spare areas, and stricter write requirements. Each page within a block must be written to in sequential order, and each page must be written only once. YAFFS2 was designed to accommodate these newer chips. YAFFS2 is based on the YAFFS1 source code, with the major difference being that internal structures are not fixed to assume 512 byte sizing, and a block sequence number is placed on each written page. In this way older pages can be logically overwritten without violating the "write once" r
ule. YAFFS is a robust log-structured file system that holds data integrity as a high priority. A secondary YAFFS goal is high performance. YAFFS will typically outperform most alternatives. It is also designed to be portable and has been used on Linux, WinCE, pSOS, eCOS, ThreadX and various special-purpose OSes. A variant 'YAFFS/Direct' is used in situations where there is no OS, embedded OSes and bootloaders: it has the same core filesystem but simpler interfacing to the OS and NAND flash hardware. The filesystem is licensed both under the GPL and under per-product licences available from Aleph One. YAFFS1 YAFFS has no inherent formatting, an erased flash chip is formatted. It follows the smart media scheme of marking the 5th byte of the spare area for bad blocks, and ignores any blocks where the spare area byte 5 is not 0xFF. To write file data, YAFFS initially writes a whole page (chunk in YAFFS terminology) that describes the file metadata, such as timestamps, name, path, etc. The new file is assigned a unique object ID number; every data chunk within the file will contain this unique object ID within the spare area. YAFFS maintains a tree structure in RAM memory of the physical location of these
1
狗拿耗子
chunks. When a chunk is no longer valid (the file is deleted, or parts of the file are overwritten), YAFFS marks a particular byte in the spare area of the chunk as ‘dirty’. When an entire block (32 pages) is marked as dirty, YAFFS can erase the block and reclaim the space. If free space on the device is low, YAFFS may need to choose a block that has some number of dirty pages and some number of good pages, move the good pages to a new block, mark the old pages as dirty and erase the block. The process of moving good pages & erasing blocks is called Garbage Collection. When a YAFFS system mounts a NAND flash device, it must scan the spare areas of every block to check for valid data, whereby it can then reconstitute the tree data structures. YAFFS2 YAFFS2 is similar in concept to YAFFS1, and shares much the same code; and the YAFFS2 code base supports YAFFS1 data formats through backward compatibility. The main difference is that YAFFS2 needs to jump through significant hoops to meet the "write once" requirement of modern NAND flash. YAFFS2 marks every newly written block with a sequence number that is monotonically increasing. The sequence of the chunks can be inferred from the block sequence number and the chunk offset within the block. Thereby when YAFFS2 scans the flash and detects multiple chunks that have identical ObjectIDs and ChunkNumbers, it can choose which to use by taking the greatest sequence number. For efficiency reasons YAFFS2 also introduces the concept of shrink headers. For example when a file is resized to a smaller size, YAFFS1 will mark all of the affected chunks as dirty - YAFFS2 cannot do this due to the
"write once" rule. YAFFS2 instead writes a "shrink header", which indicates that a certain number of pages before that point are invalid. This lets YAFFS2 reconstruct the final state of the filesystem when the system reboots. YAFFS2 uses a more abstract definition of the NAND flash allowing it to be used with a wider variety of flash parts with different geometries, bad block handling rules etc. YAFFS2 now supports "checkpointing" which bypasses normal mount scanning, allowing very fast mount times. Mileage will vary, but mount times of c. 3 seconds for 2 GB have been reported.
2
狗拿耗子
2、chunk 与 block K9F1208 是 64M 的 Nand Flash, 一个 chunk 包含 512byte 的 data area, 16byte 的 spare area, 与 32 个 chunk 构成了一个 block。 2.1 spare area 与 tag 在 YAFFS2 中 data area 用来存放数据, spare area 用于存放 chunk 的归属等信息。 而 YAFFS2 给出了一个数据结构 struct yaffs_ExtendedTags 用来存放上述信息。 显然在 16byte 的 spare area 中无法存放 struct yaffs_ExtendedTags,所以必须针对 K9F1208 这种 Flash 来适配 tag。
typedef struct { unsigned validMarker0; unsigned chunkUsed; /* unsigned objectId; unsigned chunkId; unsigned byteCount; Status of the chunk: used or unused */
/* If 0 then this is not part of an object (unused) */ /* If 0 then this is a header, else a data chunk */ /* Only valid for data chunks */
/* The following stuff only has meaning when we read */ yaffs_ECCResult eccResult; unsigned blockBad; /* YAFFS 1 stuff */ unsigned chunkDeleted; unsigned serialNumber; /* YAFFS2 stuff */ unsigned sequenceNumber; /* The sequence number of this block */ /* Extra info if this is an object header (YAFFS2 only) */ unsigned extraHeaderInfoAvailable; unsigned extraParentObjectId; unsigned extraIsShrinkHeader; unsigned extraShadows; /* There is extra info available if this is not zero */ /* The chunk is marked deleted */ /* Yaffs1 2-bit serial number */
/* The parent object */ /* Is it a shrink header? */ /* Does this shadow another object? */ /* What object type? */
yaffs_ObjectType extraObjectType; unsigned extraFileLength;
/* Length if it is a file */ /* Equivalent object Id if it is a hard link */
unsigned extraEquivalentObjectId; unsigned validMarker1; } yaffs_ExtendedTags; 2.1.1 经过适配后的 tag 属性名称 objectId chunkId extraIsShrinkHeader extraShadows extraObjectType serialNumber firstChunkValid byteCount 长度(bit) 16 16 1 1 4 2 8 11
开始于(bit) 0 16 32 33 34 38 40 48
结束于(bit) 15 31 32 33 37 39 47 58
3
狗拿耗子
sequenceNumber extraHeaderInfoAvailable extraParentObjectId extraFileLength reserved extraEquivalentObjectId eccForTag reserved secondChunkValid eccFor1stHalfPage eccFor2stHalfPage reserved
29 1 16 21 2 16 16 8 8 24 24 32
59 88 89 105 126 0 16 32 40 48 72 96
87 88 104 125 127 15 31 39 47 71 95 127
resizedtypedef struct { unsigned char data[32]; } YAFFS2_K9F1208_Tag;
在我的适配中不妨将连续的两个 chunk 叫做 d_chunk。d_chunk 包含 1024 byte 数据,32 byte spare a
rea,此时一个 block 包含 16 个 d_chunk。 K9F1208 的 手 册 没 有 提 到 flash 在 erase 后 , spare area 的 值 是 多 少 。 不 过 在 Yaffs_tagscompat.c 的函数 yaffs_TagsCompatabilityQueryNANDBlock()中可以知道, 判断 block 是否 erase 过的依据是该 block 上的 chunk 的 16 Byte 的 spare area 是否全是 0xff。 所 以 可 以 根 据 struct YAFFS2_K9F1208_Tag 的 值 是 否 全 为 0xff , 来 确 定 yaffs_ExtendedTags.chunkUsed。 K9F1208 的手册说, 可能在出厂的时候, flash 上就存在坏的 block, 而鉴别这些坏 block 的方法是读取 block 前两个 chunk 的 spare area,如果它们的第五个字节均为 0xff,则该 block 可用,否则该 block 是坏的。 struct YAFFS2_K9F1208_Tag 提供了 24 bits 用于 512 字节 chunk 的 ecc 校验, 则两个 512 字节的 chunk 占用 48bits, bits 用于 32 字节 tag 自身的校验。 16 ECC 的校验算法将在下 面给出,这里不再解释。 yaffs_ExtendedTags.chunkDeleted 始终为 false,因为与 YAFFS2 不同的是,YAFFS2 不 会写 spare area,用于表示该 chunk 被删除。所以 struct YAFFS2_K9F1208_Tag 不再包含 该值, YAFFS2_K9F1208_Tag 转换为 yaffs_ExtendedTags 时, 从 chunkDeleted 等于 false。 chunkID 用 16 bits 表示,可以索引范围为 0 到 0xffff,即可索引 0x10000 个 d_chunk, 而每个 d_chunk 大小为 1k Bytes,所以可以覆盖 64M Bytes,恰好为 K9F1208 可以存储 的数据大小。 byteCount 用 11 bits 表示,可以表示 0 到 2047,大于 d_chunk 的大小。 objectId 用 16 个 bits 表示,在 YAFFS2 中 objectId 用一个 key 等于 256 的 hash table 产 生,所以 16 个 bits 足已。 sequenceNumber 用 29 bits 表示,在 YAFFS2 的说明中有:
/* Sequence numbers are used in YAFFS2 to determine block allocation order. * The range is limited slightly to help distinguish bad numbers from good. * This also allows us to perhaps in the future use special numbers for 4
狗拿耗子
* special purposes. * EFFFFF00 allows the allocation of 8 blocks per second (~1Mbytes) for 15 years, * and is a larger number than the lifetime of a 2GB device. */
那么按照上述的分配速度,29 bits 的 sequence 可以用于约两年的分配。 2.2 驱动函数的适配 如下可知 YAFFS2 共需要 6 个驱动函数(用蓝标注出来的函数) 。
static int yaffs_CheckDevFunctions(const yaffs_Device * dev) { /* Common functions, gotta have */ if (!dev->eraseBlockInNAND || !dev->initialiseNAND) return 0; #ifdef CONFIG_YAFFS_YAFFS2 /* Can use the "with tags" style interface for yaffs1 or yaffs2 */ if (dev->writeChunkWithTagsToNAND && dev->readChunkWithTagsFromNAND && !dev->writeChunkToNAND && !dev->readChunkFromNAND && dev->markNANDBlockBad && dev->queryNANDBlock) return 1; #endif /* Can use the "spare" style interface for yaffs1 */ if (!dev->isYaffs2 && !dev->writeChunkWithTagsToNAND && !dev->readChunkWithTagsFromNAND && dev->writeChunkToNAND && dev->readChunkFromNAND && !d
ev->markNANDBlockBad && !dev->queryNANDBlock) return 1; return 0; } /* bad */
static const INT16U D_CHUNK_NUM_IN_BLOCK = 16; static const INT16U K9F1208_CHUNK_SIZE = 512; static const INT16U K9F1208_TAG_SIZE = 16;
static INT8U GetBitFrom16Bytes(const INT8U *data, INT8U pos) { INT8U byte_pos = pos / 8; INT8U bit_pos = pos % 8; INT8U mask = 1 << bit_pos;
return ((data[byte_pos] & mask) > 0 ? 1 : 0); }
5
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论