Moe qlc - #203
Open
Woong-DoubleK wants to merge 9 commits into
Open
Conversation
The QLC read latencies were extrapolated from TLC (Micron FMS'19) as fixed multipliers, giving 59.33 / 85.25 / 127.20 / 169.60 us. Replace them with measured values at 16 KB per page: 47.9 / 76.2 / 134.6 / 228.1 us. The extrapolation is both faster in the mean (110.34 vs 121.70 us) and narrower in spread (1 : 1.44 : 2.14 : 2.86 vs 1 : 1.59 : 2.81 : 4.76), so it understates what page placement is worth - a bit-plane layout worth 1.346x under the measured vector is worth only 1.221x under the extrapolated one. Write latencies are left as the TLC extrapolation; they were not measured, and the workload this is for writes once and then only reads. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two things found while getting FEMU to run and measure on a shared host.
**Unpinned backing store.** init_dram_backend mlock()s the whole device and
abort()s if that fails. RLIMIT_MEMLOCK is commonly 64 MB with the hard limit
equal to the soft one, so an unprivileged user cannot raise it and cannot
emulate a device larger than 64 MB at all. FEMU_ALLOW_UNPINNED=1 downgrades the
failure to a warning; the default is unchanged, and the error message now names
the limit that would have to be raised. Pinning exists so a page fault cannot
land inside an emulated NAND access, so the variable is only sound on a host
that is not swapping - the comment says to check vmstat si/so, and notes that
swap merely occupied by stale pages is fine while active swap is not.
**The channel stage is dead code, and enabling it is not free.**
bbssd/ftl-media.c copies pg_xfer_lat into cfg.timing.page_xfer_ns and then sets
channel_mode = NAND_CH_OFF unconditionally; nand-media.c reads page_xfer_ns only
under NAND_CH_STAGED. Both call sites (bbssd, zns) select OFF, so NAND_CH_STAGED
has no users and passing pg_xfer_lat on the command line changes nothing today.
hw/femu/nand/test/test_channel.c characterises what turning it on would do. It
builds and runs without QEMU, a guest, or KVM, because nand_media_op() is pure
timing arithmetic over a caller-supplied timeline. It establishes:
- STAGED differs from OFF even with every bus phase at zero, because the
channel timeline is advanced to each op's data-out and the next command is
clamped to it. So a channel_model option has to default to off, and "set the
transfer to zero to reproduce the old numbers" does not work.
- The staged model serialises the channel across LUNs. Reservations are taken
in submission order, so an op's command phase waits for the previous op's
data-out even on a different LUN. Two LUNs on one channel, both reads issued
at t=0, slowest page: 228.1 us and 456.2 us - exactly 2x - and still 2x with
the bus transfer set to zero, which is the clearest statement of the problem.
Adding LUNs to a channel buys nothing; 800 reads take 139.31 ms at 1, 2, 4 and
8 LUNs per channel, unchanged to the nanosecond. Channels do scale.
- Consequently the page-mapping gain it reports is (mean+xfer)/(aware+xfer) flat
at every LUN count, 1.219x for our traffic mix, rather than falling toward 1.0
as a channel saturates. That number is a property of the model, not of a
device.
The header comment in nand-media.c states this reproduces bbssd's
ssd_advance_status faithfully, so this is upstream behaviour rather than a defect
introduced here: FEMU models a controller that does not pipeline within a channel.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The cycle starts at page 8, so it needs rows - 1 iterations to cover the block; rows - 3 stopped at index 495 and left pages 496..511 holding their zero-initialised value, which reads as QLC_LOWER_PAGE. At 256 pages per block nothing reached those entries, so the bug was invisible in every run taken so far and only appeared once the geometry grew to 512. A host-side test extracts init_qlc_page_pairing from this file at build time and asserts the class histogram, so the fix cannot silently regress. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The placement work needs to know which of the four QLC page classes a read landed on, and how many pages of each class the workload actually touched. Counting at the NAND boundary rather than at the host queue includes mapping-table and GC reads, which is the right boundary for NAND-core energy: the array does that work whether or not the host asked for it. active_ns records the raw array latency chosen for the class and excludes queueing on purpose. Queueing belongs to the controller and would make the per-class figure depend on the queue depth rather than on the medium. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A per-class read coefficient alone cannot say where the energy goes. The array term scales with the number of sensing steps a class needs (1, 2, 4, 8) while the peripheral term scales with the time the page is held open, so the two move differently as the placement changes and reporting only their sum hides the mechanism the placement is acting on. The array coefficient is clamped to the read total, so a mis-set pair can only make the peripheral remainder zero, never negative. Coefficients are device properties with the measured defaults rather than constants, and the stats file records the pair it used so a CSV can be read years later without the binary that wrote it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The table is what every physical-layout prediction is checked against, and a mismatch is silent: the mapper still emits a plan, the device still serves the reads, and only the latency is wrong. Reading it back out of a boot log costs a boot; this costs a compile. init_qlc_page_pairing() is static and its translation unit pulls in QEMU, so the function text is extracted from nand.c on every build rather than copied into the test, where the two could drift apart while still both passing. Checked both ways: the current source gives 132/128/126/126 and passes, and restoring the rows - 3 bound makes it fail at page 496. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both tests build outside QEMU, which is the point of them, but the recipes are not obvious: test_channel needs a stub osdep.h because nand-media.c includes one it will not get here, and test_pairing needs the pairing function re-extracted from nand.c on every build so a stale include cannot quietly test nothing. Neither is discoverable from the sources alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
176 upstream commits against this fork's 7. Eight conflicts across six files,
all of them both sides adding different things to the same place, so both sides
are kept except where the two disagree about behaviour.
nvme.h our exit-notifier and energy-coefficient fields alongside
upstream's csd_ctrl_state and pe_cycles_rated
femu.c our process-exit/stats-timer callbacks and e_read/e_array
properties alongside upstream's femu_realize_undo and
pe_cycles_rated
ftl.h our qlc_read_* counters alongside upstream's wear totals
bb.c our bb_flush_stats ahead of upstream's bb_exit comments
ftl-media.c upstream's per-block read_cnt and its new
bb_decode_loc(ssd, ppa, stime) signature, then our QLC
counters, which need the decoded loc and so follow it
backend/dram.c is the one place the two disagree. Upstream now warns and
carries on when mlock fails; this fork aborts unless FEMU_ALLOW_UNPINNED is
set, and keeps doing so: the emulated latency is the measurement here, and a
page fault landing inside one is indistinguishable from the NAND time it is
supposed to be reporting. Upstream also rewrote the comment above it to
describe the warn-and-continue policy, so the comment is restated to match the
code it sits on rather than contradict it.
Verified rather than assumed, because a placement experiment fails silently:
test_pairing passes, 132 128 126 126 -- the rows-1 QLC pairing fix survived
the merge. Against rows-3 it reports FAIL pg 496.
OFF-mode timing is unchanged. Upstream reworked the channel/bus model, but
that work is inside NAND_CH_STAGED, and bbssd selects the mode from the
channel knobs, all of which default to 0 and none of which the run
environment sets. Replaying 4,096 reads over this geometry (2 ch x 4 LUN)
completes at 498483200 ns on both sides of the merge, so the 24-run grid
measured before this merge still stands.
The merged tree builds; femu-qlc:merged carries it.
test_channel now reports six failures and they are left alone. Cases [2]-[4]
exercise NAND_CH_STAGED, whose old expectations encoded a defect -- the LUNs on
a channel used to serialise completely, which case [3] asserted while calling
it unlike real NAND. Upstream fixed that. Rebuilding those expectations is a
decision about what the staged model ought to do, and nothing here uses it yet;
the test README records why they fail so the next reader does not mistake a
known deferral for a regression.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
compose.yaml, the Dockerfile and its entrypoint have driven every measurement on this fork but were never tracked, so the geometry, the QLC stats path and the energy coefficients a run was given lived only in an untracked file. A result is only reproducible if the configuration that produced it is in the history beside the code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Brief description of changes made.
Type of Change
Testing
FEMU Modes Tested
Platform Testing
Checklist
Related Issues
Fixes #(issue number)
Additional Notes
Any additional information, configuration changes, or notes for reviewers.