# SSD Internals: Why Your Disk Is a Small Computer That Lies

> Flash can't overwrite in place, erases in giant blocks, and wears out per cell — so every SSD runs a translation layer that remaps, garbage-collects, and levels wear behind your back. Write amplification, TRIM, and the cliff at 90% full, explained.

URL: https://harshith.in/blog/ssd-internals/
Author: Harshith Sunku (Staff Engineer) — https://harshith.in/about/
Tags: systems, hardware, performance, filesystems

An SSD accepts 4 KB writes to any LBA, forever, like a very fast disk.
The physics underneath permits none of that: NAND flash **cannot
overwrite** a written page — it must be erased first — and erasure only
happens in **blocks** of hundreds of pages, each surviving a finite
number of erase cycles. Between the interface and the physics sits a
full embedded computer (multi-core controller, DRAM, firmware) running
the **flash translation layer**, and every strange SSD behavior — the
performance cliff when full, the need for TRIM, drives dying by
warranty math — is FTL bookkeeping showing through.

## The Constraint Set

```text
  read:   per page (~4-16 KB)      fast (~50-100 µs)
  write:  per page, blank only     slower (~200+ µs... and see below)
  erase:  per BLOCK (~256+ pages)  slow (~ms), and each block endures
                                   only ~1-3k cycles (TLC/QLC era)
```

Overwrite-in-place under these rules would mean: read the whole block,
erase it (milliseconds), write it all back — and burn one of your few
thousand cycles per 4 KB update. No.

## The FTL's Answer: Never Overwrite, Always Remap

The FTL keeps a mapping table (LBA → physical page, gigabytes of DRAM
on big drives) and turns every write into an append: incoming data goes
to a fresh page in a currently-open block, the map is updated, and the
old page is merely *marked stale*. Sound familiar? **Your SSD is an
LSM-ish log-structured store**, and like every such design it owes a
garbage-collection debt: eventually the drive must reclaim stale pages
by picking victim blocks, copying their still-live pages elsewhere, and
erasing. Those internal copies are writes you didn't issue —
**write amplification** (WA): host writes 1 GB, NAND absorbs 2–4 GB,
endurance and performance both divide by that factor.

WA is workload-shaped, and the shape rules are worth memorizing:
sequential writes fill blocks that die together (WA→1); small random
writes across a full drive scatter live/stale pages evenly so every
victim block is half-alive (WA→high); and **free space is the GC's
working room** — which is the real explanation of the famous cliff.
A drive at 50% full finds nearly-dead victim blocks easily; at 95%,
every collection copies mostly-live data to reclaim slivers. Steady-
state random-write throughput can drop 3–10× between an empty and a
full drive — enterprise drives ship 10–28% hidden **over-provisioning**
precisely to floor this, and you can add more by simply leaving a
partition short.

This is also why **TRIM** matters: without it, the FTL doesn't know
deleted-file pages are dead (the filesystem just edited its own
metadata) and dutifully GC-copies garbage. `fstrim.timer` weekly is
the Linux default for a reason. And wear leveling is the FTL's third
job: rotating erase load across blocks (including moving *cold* data
off healthy blocks so they can take heat), tracked publicly in SMART —
`smartctl -a` shows percentage-used/wear-level counters, and endurance
is sold as TBW/DWPD numbers that are just (cycles × capacity ÷ WA)
arithmetic.

## The Performance Personality

Two more internals explain the benchmarks that confuse people.
**SLC caching**: TLC/QLC cells are slow to program, so consumer drives
write incoming data in fast 1-bit-per-cell mode into a dynamic cache
region, folding it to dense storage later — hence the pattern where a
drive writes at 5 GB/s for 30 seconds and then drops to 500 MB/s
(cache exhausted, now folding while ingesting). Sustained-write
workloads should be benchmarked past the cache. **Parallelism is the
whole speed story**: a big SSD is 8–16 channels × multiple dies, and
peak IOPS exist only at high queue depth (QD1 4K random read latency
~60–80 µs is physics; the million-IOPS spec sheet number is QD32+
across many jobs — `fio` with `iodepth=1` vs `32` shows both truths).
Meanwhile NVMe removed the software bottleneck (64k queues, MSI-X per
queue, no SATA AHCI single-queue chokepoint), which is why the
protocol switch mattered as much as the flash.

The through-line for systems people: databases and filesystems built
their own log-structured layers on top (WAL, LSM trees, F2FS), so a
modern write path is often *logs stacked on logs*, each with its own
GC — and the tuning literature (align writes, batch small writes,
leave headroom, TRIM) is the same advice at every layer because it's
the same design underneath.

## Takeaways

- Flash forbids overwrite and erases by block; the FTL converts your
  writes into an internal append-log with mapping + GC + wear leveling.
- Write amplification is the tax: sequential ≈1, small-random-when-full
  worst; endurance and steady-state speed both divide by it.
- Free space is GC headroom — the full-drive cliff is real; over-
  provision (or under-partition) and run fstrim.
- SLC caches make burst ≠ sustained; parallelism makes QD1 ≠ spec-sheet
  IOPS — benchmark the case you actually run.
- Your storage stack is logs-on-logs; the same batching/headroom advice
  applies at every layer because it's the same architecture.
