F2FS Flash-Friendly Filesystem
F2FS (the Flash-Friendly File System) is a log-structured filesystem written by Samsung for Linux, merged in the 3.8 merge window (late 2012) and shipped in the Linux 3.8 release on 18 February 2013 as “a new experimental file system, contributed by Samsung, optimized for flash memory storage devices” (per kernelnewbies on 3.8). It is designed from the ground up for NAND flash with a Flash Translation Layer (FTL) — SSDs, eMMC, and UFS chips — rather than for spinning disks. The central idea is that flash hates random in-place overwrites (it must erase a whole block before rewriting), so F2FS turns all writes into large sequential appends to a “log,” which the device’s own FTL can absorb cheaply, while engineering away the two classic weaknesses of log-structured filesystems: the wandering tree (cascading metadata updates) and cleaning cost (garbage collection). F2FS is the de-facto filesystem of the Android world, backing the
userdatapartition on a large fraction of shipping phones. As of kernel 6.12 LTS (2024-11-17) and 6.18 LTS (2025-11-30), F2FS is infs/f2fs/,S: Maintainedin MAINTAINERS by Jaegeuk Kim and Chao Yu, and unambiguously a normal, supported in-tree filesystem (verified: all ofsuper.c,segment.c,node.c,gc.c,checkpoint.c,f2fs.hreturn HTTP 200 at the v6.18 tag).
Why Flash Wants a Log
To understand F2FS you have to understand what a NAND flash chip cannot do. A flash chip is organized into erase blocks (often megabytes) made of pages (a few KB). You can write a page only if it is erased, and you can erase only a whole block at a time, and each block tolerates a finite number of erase cycles before it wears out. There is no “overwrite this 4 KB in place” primitive — overwriting means erase-then-write of the entire surrounding block. To hide this, every consumer flash device ships a Flash Translation Layer (FTL): firmware that presents a normal block-device interface (read/write 512-byte logical sectors) while internally remapping logical sectors to physical pages, running its own garbage collection, and doing wear leveling. The FTL is, in effect, a tiny log-structured filesystem hidden inside the drive (Lee et al., F2FS: A New File System for Flash Storage, USENIX FAST ‘15).
The performance consequence is stark: a workload of small random in-place writes forces the FTL into constant internal garbage collection, multiplying the physical writes per logical write (write amplification) and burning erase cycles. The same workload as large sequential appends is nearly free for the FTL — it can fill physical blocks in order and erase them whole. F2FS’s design thesis is therefore: make the filesystem write the way the FTL wants to be written to. Instead of fighting the FTL with its own block allocator, F2FS appends everything sequentially so its write pattern matches the FTL’s preferred pattern, collapsing the two layers of garbage collection into something far cheaper. The FAST ‘15 paper reports the payoff: F2FS outperforms ext4 by up to 3.1× (iozone) and 2× (SQLite) on a mobile target, and up to 2.5× on SATA SSD and 1.8× on PCIe SSD on a server (per the paper and its USENIX abstract).
flowchart TB APP["Application write()"] --> PC["Page cache (dirty pages)"] PC --> F2["F2FS: append to one of 6 active logs<br/>(hot/warm/cold x node/data)"] F2 --> SEQ["Large sequential writes<br/>(segment-aligned)"] SEQ --> FTL["SSD/eMMC FTL<br/>(remap + internal GC + wear leveling)"] FTL --> NAND["NAND erase blocks / pages"] F2 -.->|"reclaim free segments"| GC["F2FS garbage collection<br/>(victim selection + valid-block migration)"]
How F2FS aligns with flash. What it shows: an application write becomes a dirty page, which F2FS appends sequentially to one of six logs, producing large segment-aligned sequential writes that the device FTL absorbs cheaply; F2FS runs its own garbage collection to reclaim free segments. The insight: F2FS deliberately mirrors the FTL’s append-only, garbage-collected structure so the two layers cooperate instead of fighting, which is the entire reason it beats a journaling filesystem like ext4 on flash.
On-Disk Layout
F2FS divides the volume into two halves: a metadata region at the front (whose location is fixed and which is updated in place) and the main area at the back (which is written log-structured, append-only). Per the kernel F2FS documentation, the regions in order are:
- Superblock (SB) — at the very start of the partition. F2FS keeps two copies for crash safety. Holds the basic geometry (segment/section/zone sizes, region offsets, total block count) and default parameters. The superblock itself is never written log-structured; it is a fixed anchor.
- Checkpoint (CP) — the consistent on-disk snapshot of filesystem state: which NAT and SIT blocks are valid (via bitmaps), the list of orphan inodes, and the summaries of the currently active segments. The checkpoint is the recovery point (see Checkpointing below).
- Segment Information Table (SIT) — one entry per segment in the main area, recording the valid-block count and a per-block validity bitmap. This is what garbage collection consults to find segments worth cleaning.
- Node Address Table (NAT) — the table that maps every node ID to the physical block address where that node currently lives. This level of indirection is the key trick that kills the wandering-tree problem (explained below).
- Segment Summary Area (SSA) — for every block in the main area, the SSA records who owns it — the parent node and offset that points at this block. During garbage collection, when F2FS finds a valid block it must move, the SSA tells it which pointer to update.
- Main Area — the actual file and directory data plus their index (node) blocks, all written append-only into segments.
The geometry is hierarchical and fixed at format time. The atomic unit is the segment, defined as 2 MB = 512 blocks of 4 KB (kernel docs). One or more consecutive segments form a section (default: one segment); one or more consecutive sections form a zone (default: one section). The hierarchy exists to tune cleaning and to align with the device’s erase geometry: garbage collection works at section granularity, and the FAST ‘15 paper notes the zone level “reduce[s] the cleaning cost” by grouping sections. You set the section and zone sizes with mkfs.f2fs -s <segments-per-section> and -z <sections-per-zone>. F2FS aligns the checkpoint’s start to a segment boundary and the main area’s start to a zone boundary, so its logical structure lines up with the device’s physical erase units.
Multi-Head Logging: Six Active Logs
A naive log-structured filesystem writes everything to a single log, which is disastrous for cleaning: short-lived data (a directory entry rewritten constantly) ends up interleaved on disk with long-lived data (an old media file), so every segment is a mix of garbage and live data and the cleaner can never find a cheaply reclaimable segment. F2FS solves this with multi-head logging: it keeps six segments open for writing simultaneously and routes each block to the log whose data has a similar expected lifetime (“temperature”). Per the kernel docs, the six logs are:
Three node logs (index/metadata blocks):
- Hot node — direct node blocks belonging to directories (constantly churned as files are created/deleted).
- Warm node — direct node blocks of regular files.
- Cold node — indirect and double-indirect node blocks (rarely change once a file’s size is stable).
Three data logs (file/directory contents):
- Hot data — directory entry (dentry) blocks (rewritten on every create/rename/delete).
- Warm data — ordinary file data not classified hot or cold.
- Cold data — multimedia files (recognized by extension) and data migrated by garbage collection (already proven cold by surviving a cleaning pass).
The payoff is that each segment tends to contain blocks of one temperature, so a hot segment becomes entirely garbage quickly (cheap to reclaim, almost no live data to migrate) while a cold segment stays nearly all-live (rarely needs cleaning at all). As the FAST ‘15 paper puts it, “by grouping data with similar lifetimes into the same segment, the cleaning cost is significantly reduced.”
The Node Index and the NAT — Killing the Wandering Tree
In a classic log-structured filesystem the index is a tree of pointers, and because nothing is overwritten in place, updating a single data block forces a rewrite of its parent pointer block (it now lives at a new address), which forces a rewrite of its parent, all the way up to the inode and the checkpoint. This cascade is the wandering tree problem — one small data write “wanders” up the whole tree, generating disproportionate metadata writes.
F2FS breaks the cascade with the Node Address Table (NAT). Every index object — inodes, direct nodes, indirect nodes — is a node identified by a stable node ID, and pointers between nodes are stored as node IDs, not physical block addresses. The NAT is the single table that translates a node ID to its current physical block address. So when a data block is rewritten to a new location, F2FS updates the direct node that points at it — but the direct node’s parent still references it by its unchanged node ID, so nothing above the direct node has to be rewritten. Only the small NAT entry for that node changes. As the kernel docs state, this “is able to cut off the propagation of node updates caused by leaf data writes,” solving the wandering tree “entirely.”
The on-disk index structure is concrete and worth knowing (kernel docs): the inode block is 4 KB and holds 923 direct data-block indices, plus 2 single-indirect (direct-node) pointers, 2 double-indirect (indirect-node) pointers, and 1 triple-indirect (double-indirect-node) pointer. Each direct node holds 1018 data-block indices; each indirect node holds 1018 node pointers. One inode can therefore address roughly 3.94 TB of file data. Small files benefit from inline_data, which stores a file’s contents (under ~3.4 KB) directly inside the spare space of its inode block, avoiding any data-block or node allocation at all; similarly inline_xattr keeps extended attributes inside the inode.
Garbage Collection (Cleaning)
Because the main area is written append-only, overwriting a file leaves the old blocks stranded as garbage scattered through previously-written segments. Garbage collection (GC), called “cleaning” in log-structured terminology, reclaims these by selecting a victim section, migrating its still-valid blocks to the current write head, and freeing the section (kernel docs; FAST ‘15). It runs in two modes:
- Background GC — a kernel thread (
f2fs_gc) that wakes during idle periods and cleans opportunistically, controlled by thebackground_gcmount option (onis the default;offdisables it;syncmakes it synchronous). The kernel docs emphasize “garbage collection is on by default.” - Foreground (on-demand) GC — triggered synchronously when free segments run short and a write needs space now. This is the latency-spiky path F2FS tries hard to avoid.
The two modes use different victim-selection policies:
- Greedy — pick the section with the fewest valid blocks (least data to migrate, fastest to reclaim). Used for foreground GC, where the goal is to free space as cheaply as possible right now.
- Cost-benefit — weigh a section’s valid-block count against its age (how long since it was last modified). An old section that is mostly valid is a good victim because its data has proven cold and is unlikely to be rewritten soon, so migrating it pays off long-term; this policy resists “log thrashing” where you keep cleaning hot segments that immediately fill with garbage again. Used for background GC. A later refinement,
atgc(age-threshold GC), sharpens background efficiency by using block age as an explicit threshold.
The GC walk itself reads the SIT to find a victim, reads the victim’s SSA to learn which node owns each valid block, cross-checks validity, then relocates the survivors (which, being already-survived data, go to the cold data log).
When the volume gets very full, pure append-only logging (LFS mode) starves — there are no free segments to append into. The FAST ‘15 paper describes adaptive logging: under high utilization F2FS switches to threaded logging (also called slack-space recycling), writing into the holes of partially-valid existing segments instead of demanding a wholly free one. This trades some sequentiality for the ability to keep making progress without first running an expensive cleaning pass — a graceful-degradation valve rather than the normal path.
Uncertain
Verify: the exact
atgcsemantics and the precise threshold/heuristics of cost-benefit victim selection as implemented in 6.12/6.18fs/f2fs/gc.c. Reason: the kernel docs describe the policy but not the current code’s exact constants, and the FAST ‘15 paper predates a decade of GC refinement. To resolve: readfs/f2fs/gc.cat the v6.12/v6.18 tags. uncertain
Checkpointing and Recovery
F2FS achieves crash consistency through checkpoints plus roll-forward recovery, both leaning on a shadow-copy discipline (kernel docs). A checkpoint is a consistent on-disk snapshot: it records valid NAT/SIT bitmaps, the orphan-inode list, and the active-segment summaries. F2FS keeps two checkpoint copies, and “one of them always indicates the last valid data” — this is the shadow copy mechanism. NAT and SIT use the same two-copy shadow scheme. The checkpoint explicitly names which NAT and SIT copy is current, so after a crash there is never any ambiguity about which metadata to trust; recovery simply reads back the last good checkpoint and the filesystem is consistent as of that point. This is fundamentally different from the journaling approach used by ext4 and XFS (see The jbd2 Journaling Layer) — F2FS does not maintain a redo log of every transaction; it periodically writes a whole consistent picture and atomically flips to it.
The gap between checkpoints is covered by roll-forward recovery, enabled by default. Data written after the last checkpoint but before a crash is reconstructed by replaying the data-block “summary” footers left in the recently written segments, so a synced file does not lose its just-written data even though no full checkpoint captured it. Roll-forward can be turned off with disable_roll_forward, or recovery skipped entirely by mounting read-only with norecovery (useful for forensics or for mounting a possibly-damaged volume without modifying it). The broader picture of how filesystems stay consistent across crashes — journaling vs copy-on-write vs checkpoint+roll-forward, and what fsck.f2fs does when even the checkpoint is damaged — is the subject of Crash Consistency and fsck.
Mount Options and Configuration
# Format a flash device with 2 segments per section and 2 sections per zone,
# aligning F2FS's cleaning unit to a larger physical erase region.
mkfs.f2fs -s 2 -z 2 /dev/sdb1
# Typical mount: background GC on (default), small files inlined, xattrs inlined.
mount -t f2fs -o background_gc=on,inline_data,inline_xattr /dev/sdb1 /mnt/flash
# Database/VM-style workload that cannot tolerate background GC jitter:
mount -t f2fs -o background_gc=off /dev/sdb1 /mnt/db
# Forensic / read-only mount of a possibly damaged volume — skip recovery entirely:
mount -t f2fs -o ro,norecovery /dev/sdb1 /mnt/inspectLine by line: mkfs.f2fs -s 2 -z 2 sets the section to two 2 MB segments (4 MB) and the zone to two sections (8 MB), so GC reclaims 4 MB at a time and writes align to 8 MB. background_gc=on keeps the idle-time cleaner running (the default; off is for latency-sensitive workloads that prefer to manage free space themselves, sync forces synchronous cleaning). inline_data stores sub-~3.4 KB file contents inside the inode, eliminating a separate data block for tiny files — a large win on directory trees full of small config files. inline_xattr does the same for extended attributes. disable_roll_forward (not shown) drops the post-checkpoint replay to gain a little write performance at the cost of losing un-checkpointed writes on a crash. norecovery plus ro mounts without touching the disk at all. The gc_merge option lets the background GC thread service foreground GC requests, smoothing the transition between the two. The full sysfs surface lives under /sys/fs/f2fs/<device>/ and is documented in Documentation/ABI/testing/sysfs-fs-f2fs.
Failure Modes and Misunderstandings
“F2FS doesn’t need TRIM / wear leveling because the FTL does it.” Half right. The FTL still does the physical wear leveling, but F2FS issues discards and structures writes so the FTL’s job is easy; running F2FS without discard support on a cheap eMMC chip can leave the FTL guessing about which blocks are free, raising write amplification. F2FS supports both real-time discard and batched discard.
“Cold means slow.” No — “cold” in F2FS is a lifetime classification (rarely rewritten), not a performance tier. Multimedia files are tagged cold because they are written once and seldom changed, so isolating them keeps the hot logs clean. This is the opposite of a storage tier.
Full-disk cliff. As utilization approaches 100%, F2FS falls back to threaded logging (adaptive logging) and foreground GC kicks in more often; both erode the sequential-write advantage. The practical symptom is a sharp throughput drop and latency spikes on a nearly-full F2FS volume — the documented reason to keep some free space (and the GC reserve) available.
Checkpoint corruption. If both checkpoint copies are damaged, the shadow scheme cannot recover and fsck.f2fs (or the in-kernel repair) must reconstruct from the SIT/SSA/NAT — covered in Crash Consistency and fsck. The checkpoint=disable mode (added in the 4.x series) lets F2FS run without writing checkpoints temporarily, used during Android OTA updates to avoid checkpoint overhead, but a crash in that window loses everything since the last checkpoint.
Alternatives and When to Choose F2FS
Against the journaling workhorses ext4 and XFS: those overwrite in place and journal their metadata; on a fast SSD with a good FTL the gap narrows, but on cheap mobile flash (eMMC/UFS) F2FS’s sequential-write discipline measurably wins, which is exactly why Android adopted it. Against the copy-on-write filesystems Btrfs and bcachefs: those also never overwrite in place and so are flash-friendly in that sense, but they carry far more machinery (subvolumes, snapshots, multi-device, checksums) and metadata overhead; F2FS is lean and purpose-built for a single flash device with no snapshot ambitions. Against putting a plain mount discard ext4 on an enterprise NVMe SSD with a sophisticated FTL: the FTL is already doing log-structuring internally, so F2FS’s benefit shrinks — F2FS shines most where the device’s own FTL is weak (mobile flash), which is precisely its home turf.
Production Notes — Android and Beyond
F2FS’s killer deployment is Android, where it backs the userdata partition on a large share of shipping devices. Per the F2FS Wikipedia entry and Arch Wiki: Motorola used it in Moto G/E/X and Droid phones from early on; Google first shipped it on the Nexus 9 (2014) and returned to it on the Pixel 3 once F2FS gained inline-crypto hardware support; Huawei has used it since the P9 (2016); OnePlus on the OnePlus 3T; Samsung first on the Galaxy Note 10 (2019); ZTE since the Axon 10 Pro (2019). The Android angle also drove much of F2FS’s feature evolution: transparent encryption integrates with fscrypt (per-directory keys, added around kernel 4.2), fs-verity integrity protection (fs-verity Integrity Protection, ~5.4) authenticates read-only APK content, and transparent compression (LZO/LZ4 in ~5.6, zstd in ~5.7) saves space on data partitions. F2FS also gained zoned block device support (~4.8), letting its bucket-of-segments model map onto host-managed zoned drives and ZNS SSDs, where the device exposes its erase zones directly and F2FS’s append-only segments line up with them naturally.
Uncertain
Verify: the specific kernel versions for F2FS feature milestones (inline_data 3.14, fscrypt 4.2, fs-verity 5.4, LZO/LZ4 compression 5.6, zstd 5.7, zoned support 4.8, checkpoint=disable). Reason: these dates come from the F2FS Wikipedia page, a secondary source, not from per-version changelogs. The current presence of these features in 6.12/6.18 is not in doubt (they are all in
fs/f2fs/at those tags); only the introduction versions are secondary. To resolve: cross-check each against the kernel git history /git logfor the relevantfs/f2fs/commit. uncertain
See Also
- Crash Consistency and fsck — checkpoint + roll-forward vs journaling vs CoW; what
fsck.f2fsdoes when the checkpoint is gone - The jbd2 Journaling Layer — the journaling alternative used by ext4/XFS
- ext4 Filesystem Internals, XFS Filesystem Internals — the in-place journaling filesystems F2FS competes with on flash
- Btrfs Filesystem Internals, bcachefs Filesystem Internals — copy-on-write filesystems that are also flash-friendly but far heavier
- fscrypt Filesystem Encryption, fs-verity Integrity Protection — integrity/encryption features F2FS integrates for Android
- The Page Cache and address_space — where F2FS’s dirty pages live before they are appended to a log
- Up: Linux Filesystems and VFS MOC