Hardware Engineering · Storage Hardware

Power-Loss Protection: What a Controller Must Persist, and When

The in-flight state a flash controller holds in volatile memory, the energy budget that keeps it alive long enough to flush, and how recovery reconstructs a mapping table from torn pages.

A solid-state drive acknowledges a write when the data is in the controller’s DRAM, not when it is in the array. The array program takes hundreds of microseconds; the acknowledgement takes a few. The gap between them is the write cache, and it is the reason a drive can lose acknowledged data when power disappears — unless the controller has both the energy and the bookkeeping to finish what it promised.

What is in flight at the moment of failure

Three kinds of state exist only in volatile memory. The first is host data: sectors that have been acknowledged but not yet programmed. The second is mapping entries: the table that says which physical page holds each logical address, updated when a page is programmed and held in DRAM because rewriting it to flash on every update would be prohibitive — the table whose size and update policy are described in flash translation layers. The third is the controller’s own working state: the current open block, the write pointer, the list of invalid pages, the garbage-collection cursor.

Losing the first kind is a data-loss event visible to the host. Losing the second is worse in a subtle way: the data may be physically present in the array but unreachable, because nothing records where it went. Losing the third is recoverable if the array can be scanned, but the scan costs time proportional to capacity and the result may be ambiguous.

The controller’s obligation is therefore not “never lose power” but “make the state after a power cut equal to the state after some prefix of the acknowledged writes”. That is a crash-consistency property, and it is enforced by ordering rather than by atomicity, because flash offers no atomic multi-page write.

The hold-up capacitor

Enterprise drives carry energy storage on the board — a bank of capacitors sized to keep the controller and the DRAM alive long enough to flush the cache and complete the in-flight programs. The budget is the sum of the DRAM contents divided by the array’s program bandwidth, plus the time to write the mapping journal, plus margin.

Sizing the bank is an exercise in worst-case derating. Capacitance falls with age and with temperature, so the bank must hold enough energy at end of life and at the coldest rated temperature, not at the datasheet’s nominal value. The capacitors must also be charged before the drive is usable, which imposes an inrush limit and a ready delay at power-on. Some designs monitor the bank’s health and report a degraded state before it fails entirely; a drive whose hold-up has decayed still works, but its guarantee does not.

Client drives often omit the bank and rely on the host’s filesystem to issue flushes at the right times. The distinction is not a quality difference but a contract difference: a client drive that advertises a volatile write cache is telling the host that the host must flush, and a host that does not flush has accepted the risk.

The hold-up covers the controller, but it does not make a program atomic. A program interrupted partway through leaves a page whose cells are partially programmed: some bitlines reached their target thresholds and some did not. The page’s error-correcting code will not decode, and the page’s contents are a mixture of old and new data — a failure the code was designed to detect, as the error-correction path explains. That is a torn page, and no amount of energy storage prevents it — the program takes long enough that the failure can land anywhere inside it.

Ordering: which record goes first

The rule that makes torn pages survivable is that a mapping entry must never point at a page whose program did not complete. Two orderings satisfy it, and they cost differently.

Data first: program the page, verify it, then write the mapping entry that references it. If power fails after the program but before the entry, the page is orphaned (valid data with no reference), and recovery can reclaim it as free space. If power fails during the entry write, the previous mapping still points at the old page, and the new data is lost but the old data is intact. The cost is that every write now involves two dependent operations, and the mapping entry must be durable before the write is acknowledged, which is why the mapping journal must be on flash and not only in DRAM.

Metadata first: write the mapping entry, then the data. This is cheaper in the common case but requires that a reader validate the entry against the page’s own metadata before trusting it, because a crash between the two leaves a pointer to a page that holds stale or garbage content.

Either scheme needs a way to distinguish a committed record from a torn one. The usual mechanism is a per-page metadata field containing the logical address, a monotonically increasing sequence number, and a checksum over both. Recovery scans the block, discards any page whose metadata fails its checksum, and for each logical address keeps the record with the highest sequence number. Sequence numbers must be durable across the wrap of the counter, so they are wide, typically 32 or 64 bits, and the counter is checkpointed.

/* Per-page metadata as written by the controller, used to rebuild the map. */
struct page_meta {
    uint64_t lpn;        /* logical page number this page holds */
    uint64_t seq;        /* monotonic commit sequence */
    uint32_t crc;        /* CRC over lpn and seq, not the payload */
} __attribute__((packed));

/* Recovery: keep the newest valid record per logical page. */
int recover(struct page_meta *m, const void *payload, size_t n,
            struct map_entry *map, size_t map_len)
{
    for (size_t i = 0; i < n; i++) {
        if (crc32(&m[i], sizeof m[i] - sizeof m[i].crc) != m[i].crc)
            continue;                       /* torn or corrupt: ignore */
        if (m[i].lpn >= map_len)
            continue;
        if (map[m[i].lpn].seq < m[i].seq) { /* newer version wins */
            map[m[i].lpn].seq = m[i].seq;
            map[m[i].lpn].phys = page_address(payload, i);
        }
    }
    return 0;
}

The scan is linear in the number of pages, and on a large drive that is hundreds of millions of pages. That is why the mapping table is also checkpointed to flash periodically: recovery loads the last checkpoint and then scans only the blocks written since, which bounds the recovery time by the checkpoint interval rather than by capacity.

Garbage collection multiplies the exposure

A collection cycle copies live pages from a victim block to a new block, updates the mapping for each, and then erases the victim. A power failure at any point in that sequence leaves the drive with two partially valid blocks and a mapping that may reference either.

The recovery rule is the same as for host writes, highest sequence number winning, but the collector must be careful about the order in which it invalidates the source pages. If it erases the victim before the new copies are durable, the data is gone. If it marks the source invalid before the copies are committed, recovery may find neither. The safe order is: write the copies, commit their mapping entries, then erase the victim. The cost is that the drive temporarily holds both copies, which is why the free-space budget must include room for a collection cycle in progress.

The 2013 study of drives under power fault found that a number of the drives tested did not preserve this ordering correctly, losing data or returning corrupt data after a power cut during internal operations. The failure was not in the capacitor but in the firmware’s assumption that a program either completes or leaves the old data intact — an assumption the hardware does not guarantee.

When the protection quietly stops working

Three failure modes recur.

The first is degraded hold-up. Capacitors age; a drive that passed its power-fail test when new may not pass it after years at elevated temperature. A controller that does not monitor the bank cannot warn anyone, and the drive’s guarantee silently weakens.

The second is a cache that is not actually volatile-safe. A drive that reports a volatile write cache and honours flush is correct; one that reports no cache but buffers writes anyway is lying, and the host has no way to detect it except by testing. The interface defines the flush command and the force-unit-access flag precisely so that a host can demand durability, and a drive that ignores either is out of contract.

The third is the interaction between the host’s ordering and the drive’s. A host that issues a flush and then writes more data assumes the flush covered everything before it. If the drive reorders across the flush — because its internal queues are deeper than the flush barrier — the host’s assumption is violated. The flush semantics require the drive to complete all prior writes before acknowledging, and controllers implement it by draining the queues to the array, which is why a flush after a burst of random writes costs the array’s full program latency for each queued page.

Recovery time is the fourth cost, and it is the one users notice. A drive that has to scan a large region after an unclean shutdown takes seconds to become ready, and a host that boots from it waits. Checkpoint frequency trades recovery time against the write amplification of writing the table more often, and the trade is the same shape as every other in the controller: a small amount of extra writing now buys a bounded amount of work later.

References

  1. Understanding the Robustness of SSDs under Power Fault — M. Zheng, J. Tucek, F. Qin, M. Lillibridge, B. W. Zhao, E. S. Yang, USENIX FAST 2013
  2. NVM Express Base Specification — NVM Express
  3. Information technology — ATA/ATAPI Command Set (ATA8-ACS) — INCITS T13
  4. Inside Solid State Drives (SSDs) — R. Micheloni, A. Marelli, K. Eshghi, Springer