Skip to content

feat(ext4): implement recovery-safe delayed allocation - #2160

Open
fslongjin wants to merge 6 commits into
DragonOS-Community:masterfrom
fslongjin:codex/ext4-delalloc-recovery
Open

feat(ext4): implement recovery-safe delayed allocation#2160
fslongjin wants to merge 6 commits into
DragonOS-Community:masterfrom
fslongjin:codex/ext4-delalloc-recovery

Conversation

@fslongjin

@fslongjin fslongjin commented Jul 28, 2026

Copy link
Copy Markdown
Member

Summary

  • implement recovery-safe ext4 delayed allocation with mount-scoped linear reservations for data blocks, extent metadata, journal credits, and inode publication
  • connect ordered PageCache token writeback to per-inode FIFO claims, stable dirty-incarnation certificates, durable EOF/timestamp publication, lifecycle draining, and errseq reporting
  • batch contiguous delayed-allocation writeback so one bounded batch shares data flush and journal commit costs while retaining one reservation and ownership proof per page
  • make owner-free delayed-allocation admission waits interruptible so Ctrl+C does not leave APT helpers blocked behind writeback progress
  • harden mmap, truncate, reclaim, eviction, fsync, sync-file-range, cancellation, sparse append, partial EOF, and failure recovery interactions
  • split the oversized PageCache implementation into focused mapping, read-DMA, runtime self-test, and writeback modules without changing its public facade
  • add host fault-injection and DragonOS dunitest coverage for allocation, queue ordering, inode identity, synchronization, MM interactions, batching, and recovery

Root cause

The original eager ext4 path had no ownership protocol spanning foreground space admission, PageCache dirty generations, filesystem allocation, journal publication, and inode lifecycle transitions. Delayed allocation added without such a protocol could expose late capacity failures, stale completion, out-of-order EOF publication, and ambiguous retry ownership around journal commit.

The initial recovery-safe implementation correctly established per-page reservations and publication ownership, but it also forced each 4 KiB page through its own data flush and full journal transaction. That reduced an 8 MiB dd conv=fsync workload to about 147 kB/s and a 31.9 MB cold apt update to 8m22s. A foreground writer waiting behind a Claimed queue head also reused an uninterruptible writeback wait before it owned a reservation, so SIGINT could leave APT store and http helpers alive for tens of seconds.

The PageCache implementation had additionally grown beyond 11,000 lines while accumulating independent mapping, DMA, runtime self-test, and writeback state machines, making lock and lifetime boundaries difficult to audit.

Implementation

Reservation-backed delayed allocation

  • reserve exact data capacity and bounded extent-metadata capacity before publishing a new dirty lifetime
  • represent capacity as mount-scoped linear leases with explicit transfer, rollback, finalization, and fail-stop invariants
  • bind PageCache dirty certificates and writeback generations to FIFO per-inode delayed-allocation entries
  • initialize data before atomically publishing extents, inode size, timestamps, and journal state
  • retain retry ownership only before a certain commit boundary; poison mutation after uncertain publication instead of guessing
  • preserve eager allocation for unsupported shapes and no-journal filesystems

Batched writeback

  • let a Token descriptor own an ordered certificate for every selected page while Legacy descriptors keep an empty certificate vector
  • use a two-phase PageCache selector: identify the prospective head, query the backend outside the PageCache lock, then revalidate identity, state, tag, and contiguous range before publication
  • atomically claim a contiguous Ready prefix under the ext4 inode queue lock and carry every per-page reservation through one submission object
  • allocate each lease independently so fragmented physical space remains valid, while grouping adjacent physical blocks for data I/O
  • stage multiple extent updates against the transaction-private view and commit the complete batch with one data flush and one journal transaction
  • compute a conservative lower-layer credit cap before PageCache claim; never shorten an already frozen batch or retry by repeatedly reducing its size
  • query and clear a mapped partial-EOF tail before staging an extent-root promotion, avoiding mixed durable and transaction-private tree views
  • keep the batch bounded to 64 pages and to the lower layer's proven journal/resource limit

Progress, signals, and lifecycle

  • make Claimed admission use a passive interruptible progress wait because the caller has not yet created a reservation owner
  • keep published writeback, fsync, and journal waits non-cancellable
  • prevent a passive waiter from arming a second producer or publishing synthetic progress
  • publish exactly one real progress transition for completion, retry restore, terminal failure, cancellation, and drain endpoints
  • close admission and drain delayed work across truncate, eager fallback, inode teardown, unmount, reclaim, and eviction
  • preserve PageCache errseq reporting for batch failures

PageCache module organization

  • keep PageCacheManager as the existing Weak<PageCache> facade used by MM, VFS, ext4, tmpfs, FUSE, readahead, and reclaim
  • move VMA invalidation, truncate, and mkclean coordination to page_cache/mapping.rs
  • move direct-read DMA reservation lifecycle handling to page_cache/read_dma.rs
  • move debugfs runtime white-box validation to page_cache/selftest.rs
  • move backend contracts, claim/submit/wait/completion, tagged retry, and reclaimer coordination to page_cache/writeback.rs
  • preserve existing public paths, locks, atomic orderings, wait predicates, error paths, and drop semantics

The parent page_cache.rs is reduced from approximately 11,543 lines to approximately 3,272 lines. Mechanical equivalence checks found the same 372 functions and 73 type declarations before and after the split, with unchanged unsafe, cfg(test), and dead-code allowance counts.

Recovery and regression coverage

The host recovery suite now exercises the production batch API rather than a parallel test-only planner, including:

  • journal and no-journal persistence
  • writeback and write-through devices
  • multi-entry batches and fragmented allocation
  • sparse-forward append
  • inline-root growth and full external-leaf split/merge
  • mapped partial EOF plus sparse append plus root promotion
  • linked-tail unlink/rename and reclaim
  • retryable pre-commit failures, fail-stop boundaries, and I/O-error matrices
  • replay/retry and e2fsck validation at every enumerated persistence point

The combined partial-EOF/root-promotion test verifies the old prefix, zeroed tail and logical hole, new payload, metadata-node allocation, and final filesystem consistency.

Performance and user impact

Measured in a 2-vCPU QEMU guest using a newly generated ext4 rootfs and USTC APT sources:

Workload Before batching Current
8 MiB, 4 KiB dd conv=fsync 56.927s, 147 kB/s 1.12-1.49s, 5.6-7.5 MB/s
Cold apt update, 31.9 MB 8m22s, 63.5 kB/s 31s, 1036 kB/s
Independent cold APT run same regression baseline 36s, 883 kB/s
SIGINT during APT store helpers survived up to about 51s prompt returned immediately; no apt, http, store, or gpgv process remained

The optimization does not remove required flushes, disable journaling, enlarge dirty limits, modify TTY/PGID signal delivery, require a physically contiguous allocation, or fall back to eager allocation to hide failures.

Validation

Static and host validation:

  • make fmt — passed, including kernel all-feature Clippy with no new warning
  • make kernel — passed
  • cargo test --manifest-path kernel/crates/another_ext4/Cargo.toml --features test-api — 153 passed
  • full release recovery_fault_injection matrix — passed
  • git diff --check — passed

Targeted 2-vCPU QEMU validation:

  • normal/page_cache_accounting — 1/1 passed
  • normal/mmap_truncate_cow — 2/2 passed
  • normal/sync_file_range — 4/4 passed
  • normal/errseq_writeback_reporting — 6/6 passed
  • fuse/fuse_extended — 70/70 passed
  • normal/ext4_inode_identity — all 30 production I/O, mmap, truncate, FIFO, fsync-frontier, and remount cases passed
  • latest kernel boot, forced-power-loss recovery boot, and clean shutdown — passed
  • rootfs e2fsck -fn after clean shutdown — passed all five phases with no error
  • cold APT performance and store-stage SIGINT/helper cleanup — passed

Two independent adversarial reviews covered architecture, responsibility boundaries, lock ordering, redundancy, performance, recovery, exactly-once ownership, signal handling, security, workaround risk, and overdesign. Their actionable findings—the partial-EOF transaction-view ordering issue and unnecessary Legacy certificate allocation—were fixed and re-reviewed with no remaining blocker.

Current status

  • ready for review
  • three signed commits: recovery-safe delayed allocation, PageCache responsibility split, and batched writeback/performance/exit fixes
  • local static, recovery, QEMU, performance, interrupt, and filesystem-consistency validation completed
  • GitHub Actions is rerunning against the latest pushed commit

Introduce a reservation-backed delayed-allocation pipeline that keeps foreground admission, PageCache ownership, ext4 mapping, journal publication, and durable EOF updates in one explicit protocol.

- reserve data and extent metadata capacity before publishing dirty pages, with mount-scoped linear leases and fail-stop accounting invariants
- serialize per-inode append writeback through opaque capabilities, FIFO claims, stable dirty certificates, and bounded journal credit/extent-node pools
- publish initialized data, extents, inode size, and timestamps with crash-safe journal ordering and orphan recovery coverage
- coordinate truncate, fsync, mmap, reclaim, eviction, and unmount with admission closure, lifecycle ownership, queue draining, and errseq reporting
- strengthen PageCache writeback generations, cancellation, deferred retry, MM fault handoff, and post-commit population semantics
- add host power-loss fault injection plus dunitest coverage for queue ordering, inode identity, writeback accounting, sync ranges, and memory locking

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

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1862b51ed9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread kernel/src/filesystem/page_cache.rs Outdated
Comment thread kernel/src/filesystem/page_cache.rs Outdated
Separate the oversized page cache implementation into focused mapping, read-DMA, runtime self-test, and writeback modules while retaining the existing parent facade and public paths.

- keep PageCacheManager's Weak<PageCache> ownership model and external API unchanged
- isolate VMA invalidation and truncate coordination from cache membership operations
- colocate DMA reservation and writeback state machines with their lifecycle helpers
- constrain cross-module protocol access to the minimum page-cache-local visibility
- preserve all locks, atomic orderings, wait predicates, error paths, and drop semantics

Validated with make fmt, make kernel, symbol/declaration equivalence checks, and targeted 2-vCPU QEMU dunitest coverage for page-cache accounting, mmap truncate, sync_file_range, errseq reporting, ext4 I/O, and FUSE.
@github-actions github-actions Bot added the test Unitest/User space test label Jul 28, 2026
@fslongjin

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 10206eb00b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread kernel/src/filesystem/ext4/inode.rs
Comment thread kernel/src/filesystem/page_cache/writeback.rs
Comment thread kernel/crates/another_ext4/src/ext4/low_level.rs
Comment thread kernel/src/filesystem/ext4/inode.rs Outdated
Aggregate contiguous per-inode delayed-allocation entries at the writeback boundary so a bounded batch shares data flush and journal commit costs without weakening foreground space guarantees.

- carry one dirty-incarnation certificate and reservation per page through PageCache selection, ext4 FIFO claim, rollback, completion, and terminal failure paths
- let the lower mapper consume fragmented physical allocations in one transaction with transaction-aware extent staging and conservative pre-claim credit bounds
- query and clear a partial durable EOF tail before staging a promoted extent root, preserving the single successful data flush
- make owner-free Claimed admission waits passive and interruptible while keeping published writeback and journal ownership non-cancellable
- extend recovery fault injection for real batch submission, sparse forwarding, extent growth and split/merge, partial EOF root promotion, and I/O failure boundaries

Signed-off-by: longjin <longjin@dragonos.org>
@github-actions github-actions Bot added the Bug fix A bug is fixed in this pull request label Jul 29, 2026
@fslongjin

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a2415cc5fc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread kernel/src/filesystem/ext4/filesystem.rs
Preserve frozen range ownership across ordinary reclaim and writeback races by excluding tagged pages, serializing Legacy claims with invalidation, and assigning every Writeback transition a unique incarnation.

Replace yield-based tagged retry loops with exact incarnation completion continuations. Dispatch retries from success, failure, detach, and deferred completion paths without blocking shared workers or allocating in infallible completion paths.

Propagate mapping errseq failures through O_SYNC and O_DSYNC writes, restore delayed-allocation admission after a failed sibling mount, and make FIFO claim admission constant-time.

Add PageCache incarnation/invalidation regression coverage and synchronous-write errseq dunitests.

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

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dc459b997d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread kernel/src/filesystem/ext4/inode.rs
@fslongjin

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dc459b997d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread kernel/src/filesystem/ext4/filesystem.rs Outdated
Comment thread kernel/src/filesystem/ext4/filesystem.rs Outdated
Comment thread kernel/src/filesystem/ext4/inode.rs Outdated
Replace the global mount registry critical section with a short lookup of a per-device mount domain. Keep journal recovery and delayed-allocation draining serialized only against mounts of the same block device.

Restore delayed-allocation admission through an RAII rollback owner on every failed sibling-mount path, including failures while draining existing owners. Preserve fail-stop fencing while allowing healthy mounts with pending queues to resume.

Make O_DIRECT reads persist only the FIFO prefix needed to reach delayed pages overlapping the requested range, then write back eager dirty pages in that range. This matches Linux range-based direct-read coherence without draining an unrelated append tail.

Validated with make fmt, make kernel, and the 31-case ext4_inode_identity guest suite.

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

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0b42dcfee6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread kernel/src/filesystem/page_cache/writeback.rs Outdated
Release the mapping invalidation writer between bounded dirty-tag scan chunks so large sync ranges do not stall file faults and other mapping operations for the complete scan.

Serialize only competing freeze scanners across chunk boundaries to preserve epoch ownership. Resample the writeback incarnation frontier under the final exclusion window so ordinary claims which run between chunks remain covered by the frozen operation.

Add a deterministic runtime self-test which queues an invalidation reader at the initial freeze boundary and verifies that it acquires before the unscanned tail is tagged.

Validated with make fmt, make kernel, page_cache_accounting, sync_file_range, ext4_inode_identity, and repeated proc_self_exec_cmdline guest tests.

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

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 64d9e3dd8d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug fix A bug is fixed in this pull request test Unitest/User space test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant