Skip to content

perf(virtiofs): batch non-DAX writeback#2122

Merged
fslongjin merged 2 commits into
DragonOS-Community:masterfrom
fslongjin:codex/optimize-virtiofs-p3
Jul 15, 2026
Merged

perf(virtiofs): batch non-DAX writeback#2122
fslongjin merged 2 commits into
DragonOS-Community:masterfrom
fslongjin:codex/optimize-virtiofs-p3

Conversation

@fslongjin

@fslongjin fslongjin commented Jul 15, 2026

Copy link
Copy Markdown
Member

Summary

  • batch contiguous non-DAX FUSE writes through the page cache when FUSE_WRITEBACK_CACHE is negotiated
  • bound each writeback batch by the negotiated max_write, max_pages, and transport scatter-gather limits
  • preserve Linux-compatible fsync, close, truncate, mmap, invalidation, stable EOF, redirty, short-write, and errseq behavior
  • isolate terminal writeback completion from generic page-cache workers to prevent invalidation/completion deadlocks
  • harden kernel-thread creation and wakeup ordering used by the new worker pools
  • extend FUSE/page-cache statistics, benchmark phase reporting, dunitest coverage, and root-only debug selftests

Root cause

The non-DAX write path submitted small synchronous FUSE writes instead of retaining dirty pages and flushing bounded contiguous batches. This amplified request count and daemon round trips, especially for workloads issuing 4 KiB writes followed by fsync.

Writeback also needed explicit ordering and lifetime rules around mmap dirties, truncate and invalidation barriers, close/flush error reporting, and worker startup. In particular, published Writeback pages could not safely share a FIFO completion domain with invalidation workers that may wait for those pages.

Behavior

The buffered path is enabled only after the daemon negotiates FUSE_WRITEBACK_CACHE; the existing non-writeback path remains available when the capability is absent. Dirty pages are tagged and claimed in bounded scans, submitted in contiguous batches, and completed with generation-aware redirty and error handling.

Close and flush behavior follows Linux 6.6 semantics, including FOPEN_NOFLUSH, NO_OPEN with fh=0, write-before-flush ordering, and writeback error sequencing. Direct I/O, truncate, mmap faults, and host invalidation synchronize through explicit admission and invalidation barriers.

Performance

The frozen A1/B/A2 campaign used 9 fresh mounts per point and covered 16/64 MiB files with 4 KiB/128 KiB/1 MiB syscall sizes. All 162 samples completed without short I/O and with correct host hashes. A1 and A2 are the unmodified origin/master baseline; B is this writeback implementation.

Raw 64 MiB/4 KiB medians:

Layer data loop fsync data + fsync end to end data-loop throughput
baseline A1 13.569206 s 8.168021 s 21.719323 s 21.721853 s 4.717 MiB/s
candidate B 0.479139 s 0.275695 s 0.745776 s 0.751208 s 133.573 MiB/s
baseline A2 13.916102 s 8.592727 s 22.527501 s 22.528964 s 4.599 MiB/s

The corresponding data + fsync p90 values were 22.940337 s for A1, 0.908964 s for B, and 22.937194 s for A2. Using the faster baseline period conservatively, the candidate improves the median by 29.123x and p90 by 25.234x. The maximum A1/A2 median drift across the full matrix was 6.429%, below the frozen 10% rejection gate.

Raw aggregate CPU measurements over 2160 MiB per layer:

Layer CPU time CPU seconds/MiB
baseline A1 859.48 s 0.397907
candidate B 126.41 s 0.058523
baseline A2 903.22 s 0.418157

Compared with the faster baseline CPU rate, the candidate reduces CPU seconds per MiB by 85.292%.

The accepted WRITE diagnostic conservatively included the 64 MiB data file plus a 107-byte manifest. It observed 265 WRITE requests carrying 67,108,971 bytes, for an average payload of 253,241 bytes against the 258,048-byte effective limit (98.137%). Relative to 16,384 4 KiB data writes, the request count decreased by 98.383%.

The latest post-fix 64 MiB/4 KiB guest smoke measured:

Phase Latest value
data loop 0.591492 s (108.201 MiB/s)
fsync 0.274880 s
close 0.004728 s
end to end 0.871445 s (73.441 MiB/s)

The latest smoke completed with correct contents, short_io=0, and a successful result. The follow-up lint fix only groups the same negotiated statistics fields into a value object and does not alter runtime I/O behavior.

CubeSandbox deployment was also checked. Its current shared filesystem is read-only and has writeback disabled, so it does not exercise this P3 writeback path and no CubeSandbox P3 performance claim is made.

Validation

  • make fmt
  • make kernel -j2
  • git diff --check
  • FuseExtended: 69/69 passed in a DragonOS guest
  • writeback-cache, non-writeback mmap-close, and NO_OPEN close/flush regressions passed
  • multibatch host invalidation regression passed
  • page-cache completion-domain selftest passed with all generic workers occupied
  • kthread create/stop/run and 512 quick-exit selftests passed
  • 256 MiB guest stress completed ten 64 MiB write-and-fsync rounds without panic, OOM, deadlock, or pending requests

Related to #2019.

Buffer writes in the generic page cache only after FUSE_WRITEBACK_CACHE is negotiated, then claim and submit bounded contiguous batches according to the negotiated write and page limits.

Preserve Linux-compatible fsync, close, truncate, mmap, invalidation, stable EOF, redirty, short-write, and errseq behavior. Separate terminal writeback completion from generic page-cache workers so host invalidation cannot strand published Writeback pages behind its own waiters.

Harden kernel-thread creation and wakeup ordering required by the new worker pools. Add exact FUSE/page-cache counters, benchmark phase reporting, focused FUSE regressions, and root-only kthread and completion-domain selftests.

Validated with make kernel -j2, kernel formatting and diff checks, the completion-domain selftest, targeted close/flush coverage, and FuseExtended 69/69 in a DragonOS guest.

Signed-off-by: longjin <longjin@dragonos.org>
@fslongjin

Copy link
Copy Markdown
Member Author

@codex review

Pass negotiated read, write, page, capability, and effective payload limits through a dedicated stats value object.

This keeps the INIT statistics update cohesive and satisfies the project-wide Clippy argument-count lint enforced by make fmt without changing the negotiated values or publication ordering.

Signed-off-by: longjin <longjin@dragonos.org>
@fslongjin
fslongjin merged commit 72ebe13 into DragonOS-Community:master Jul 15, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant