Skip to main content

Geometry and capacity

The black-box array's raw capacity is the product of seven geometry values. The guest-visible capacity also depends on the GC reserve and over-provisioning. This calculator generates a page-mapped black-box recipe with 10% explicit over-provisioning relative to exposed capacity. See configuration recipes for other policies.

Black-box geometry with page mapping, default GC thresholds, and op_pcent=10. Raw NAND and exposed capacity are different.

Raw NAND capacity16 GiB
Exposed
14.55 GiB
Page
4 KiB
Block
1 MiB
LUNs
64

Payload backing allocation: 16 GiB for this recipe, plus metadata for 4,194,304 raw pages, guest RAM, and QEMU overhead. This is an allocation size, not measured resident memory. Check the host memory requirements before launching.

Device arguments for this recipe:

-device femu,femu_mode=1,op_pcent=10,\
  secsz=512,secs_per_pg=8,pgs_per_blk=256,blks_per_pl=256,pls_per_lun=1,luns_per_ch=8,nchs=8

Nearest whole-number geometry: blks_per_pl=64. Apply it above to check the FTL and reserve limits.

This recipe exposes raw bytes divided by 1.10, rounded to 512-byte sectors. The calculator checks geometry and default GC reserve; device realization remains the final check. ZNS uses different geometry rules.

Why it is a product​

The black-box model derives raw capacity from its configured array dimensions:

capacity = secsz x secs_per_pg x pgs_per_blk x blks_per_pl
x pls_per_lun x luns_per_ch x nchs

Each factor also means something for performance:

  • nchs and luns_per_ch determine the number of modeled LUN timelines. Requests to different LUNs can overlap; requests to the same LUN contend. The workload, placement, and enabled channel timing determine whether this parallelism is used. Channel count alone does not predict throughput.
  • pls_per_lun adds planes and capacity, and lets line GC batch erases. The ordinary black-box read/program timing still gates on the LUN.
  • pgs_per_blk increases the number of pages in each block and GC line. blks_per_pl increases the number of lines, not the size of each line. The valid pages remaining in the chosen victim determine relocation work.
  • secsz x secs_per_pg is the page, the unit the flash actually reads and writes.

For a hardware comparison, calibrate placement, concurrency, and timing alongside capacity.

Equal capacity, different collection units​

A black-box line groups one block index across all channels, LUNs, and planes:

pages_per_line = nchs × luns_per_ch × pls_per_lun × pgs_per_blk
line_count = blks_per_pl
raw_bytes = pages_per_line × line_count × bytes_per_page

Keep 512-byte sectors, eight sectors per page, 256 pages per block, and one plane per LUN. These two configurations both provide 16 GiB of raw capacity:

QuantityConfiguration AConfiguration B
Channels (nchs)84
LUNs per channel (luns_per_ch)88
Blocks per plane (blks_per_pl)256512
Modeled LUNs6432
Pages per GC line16,3848,192
Bytes per GC line64 MiB32 MiB
Number of lines256512

B trades half the LUN timelines for twice as many lines. Its smaller collection unit does not guarantee lower write amplification: placement, overwrite locality, occupancy, and victim selection determine how many live pages move. Compare GC counters under the same workload and report both geometry tuples. Equal raw capacity is insufficient to isolate a GC policy when geometry also changes.

Over-provisioning​

op_pcent=P exposes raw_bytes * 100 / (100 + P), sector-aligned. With op_pcent=10, that is about 90.91% of raw bytes. With zero explicit over-provisioning, devsz_mb chooses exposed MiB independently of raw geometry, but the FTL still requires sufficient free lines. See capacity and reserve.

Final partial-page boundary​

The calculator requires every exposed byte to fit after the GC reserve, including a partially exposed final page. FEMU's bb_check_capacity() does the same: it rounds the namespace up to whole pages before comparing it with the pages left after the reserve.

For example, use 512-byte sectors, eight sectors per page, one page per block, nine blocks per plane, and one plane, LUN, and channel. Raw capacity is 36,864 bytes. With op_pcent=10, the namespace exposes 33,280 bytes after 512-byte alignment, which is eight pages and 512 bytes. The default reserve leaves eight pages, or 32,768 bytes. Both the calculator and device realization reject this capacity because the partial ninth page does not fit. Increase blocks per plane before using the generated recipe.

What the reserve check covers​

The calculator models the generated recipe: page mapping, one namespace, no hot/cold separation, no Streams, and the default 95% forced-GC watermark. It reserves the forced-GC lines plus one data write-pointer line. Device realization reserves more lines for other configurations: one for hot_cold_sep, one for a hybrid or fast mapping, and streams.max + 1 with Streams. With any of these, forced GC also keeps at least one line free where the watermark would round to zero, which happens below 20 lines. The calculator does not model these cases; see the reserve in the manual.

Zoned devices​

ZNS derives pages per block and zone size using its zns_* geometry and namespace capacity, rather than the black-box product above. See ZNS geometry.

Implementation sources​

The comparison follows FEMU revision 39a55eeb6 and is arithmetic derived from source, not a guest benchmark: