process: make the unused clone_for_fork sound #154

Open
nonos-sync wants to merge 5 commits from nonos-sync/gh-615 into main AGit
Member

@eKisNonos, fixes #607 (made sound, not deleted).

Correction to the issue: the old teardown never freed leaf frames, so it leaked rather than double-freeing.

4 commits:

  1. Frame references: clone_entries takes a page_info reference for every shared leaf (4K/2M/1G) before copying it. An untracked frame gets add_page (ref 1), then increment_ref_count (ref 2).
  2. Teardown: free_user_page_tables releases leaves through frame_ref::release_frame.
  3. COW on both sides: mark_cow_pages now runs on the parent too, and vma.cow is set on both. Then flush_tlb_everywhere (local flush_tlb_pcid + flush_tlb_all_smp). clone_for_fork now takes &mut self.
  4. What gets freed: only a tracked frame whose count reaches 0. Untracked leaves are never freed (ownership unknown). Device leaves (PCD, PWT, or PAT: bit 7 at 4K, bit 12 on huge pages) are never shared or freed.

Failure safety: a failed fork leaves the parent unchanged. The child's tables are linked before they are filled, a leaf is copied only after its reference succeeds, and the parent is downgraded only after the clone fully succeeds.

Still missing for a real fork (not built here):

  • A write-fault path for this AddressSpace type. memory/paging/manager/faults/cow.rs serves the paging manager's own type.
  • PCID-aware remote flush. PCID is never enabled today.
  • page_info records live in a BTreeMap, so running out of heap panics.

clone_for_fork still has no callers, so nothing here runs on today's system.

Verification: kernel cargo check (x86_64-nonos.json, microkernel-core and microkernel-core,nonos-smp) passes with only the 2 existing quirk_bits.rs warnings. Not yet boot-tested; a combined make dev-image / dev-boot of all seven fix branches is still to run.


Opened on GitHub by senseix21 as pull request 615. Review and merge happen there while this repository is kept in step from GitHub; this copy follows it, and is marked merged or closed when it is.

@eKisNonos, fixes [#607](https://github.com/NON-OS/nonos-micro-kernel/issues/607) (made sound, not deleted). Correction to the issue: the old teardown never freed leaf frames, so it leaked rather than double-freeing. **4 commits:** 1. **Frame references:** `clone_entries` takes a `page_info` reference for every shared leaf (4K/2M/1G) before copying it. An untracked frame gets `add_page` (ref 1), then `increment_ref_count` (ref 2). 2. **Teardown:** `free_user_page_tables` releases leaves through `frame_ref::release_frame`. 3. **COW on both sides:** `mark_cow_pages` now runs on the parent too, and `vma.cow` is set on both. Then `flush_tlb_everywhere` (local `flush_tlb_pcid` + `flush_tlb_all_smp`). `clone_for_fork` now takes `&mut self`. 4. **What gets freed:** only a tracked frame whose count reaches 0. Untracked leaves are never freed (ownership unknown). Device leaves (PCD, PWT, or PAT: bit 7 at 4K, bit 12 on huge pages) are never shared or freed. **Failure safety:** a failed fork leaves the parent unchanged. The child's tables are linked before they are filled, a leaf is copied only after its reference succeeds, and the parent is downgraded only after the clone fully succeeds. **Still missing for a real fork (not built here):** - A write-fault path for this `AddressSpace` type. `memory/paging/manager/faults/cow.rs` serves the paging manager's own type. - PCID-aware remote flush. PCID is never enabled today. - `page_info` records live in a `BTreeMap`, so running out of heap panics. `clone_for_fork` still has no callers, so nothing here runs on today's system. **Verification:** kernel `cargo check` (`x86_64-nonos.json`, `microkernel-core` and `microkernel-core,nonos-smp`) passes with only the 2 existing `quirk_bits.rs` warnings. Not yet boot-tested; a combined `make dev-image` / `dev-boot` of all seven fix branches is still to run. --- Opened on GitHub by senseix21 as [pull request 615](https://github.com/NON-OS/microkernel/pull/615). Review and merge happen there while this repository is kept in step from GitHub; this copy follows it, and is marked merged or closed when it is.
clone_for_fork copied leaf and huge entries into the child verbatim, so
a frame mapped by both spaces had no record that two owners held it and
teardown could never tell a shared frame from an owned one.

Each shared leaf now takes a page_info reference before it is copied:
an untracked frame is added with the parent's single reference, then
incremented for the child. The walk links every new child table before
filling it, so when allocation or tracking fails part way, the child's
teardown reaches exactly the tables and references it was given and
the parent's tables are untouched.

Refs #607
free_user_page_tables freed the table pages and left every leaf frame
allocated, so each torn-down space leaked its memory, and with forks
now sharing frames a plain free would release memory the other space
still maps.

Teardown now walks down to the leaves and drops one reference per
4KB, 2MB and 1GB mapping. A tracked frame is freed and untracked when
its last reference goes; an untracked frame has a single owner and is
freed directly. A huge frame is returned as the whole contiguous run
it maps.

Refs #607
clone_for_fork cleared WRITABLE only in the child's tables. The parent
kept writable mappings of the frames the child now shares, so its
writes landed in the child's memory, and the PTE change was never
flushed from any TLB.

Both sides are now downgraded and both sides' VMAs are marked cow,
which is how this address space type records copy-on-write. The
parent's tables are touched only after every frame reference has been
taken, so a failed fork leaves the parent as it was. The parent's TLB
is then flushed on this cpu and shot down on every other, since it may
be running elsewhere.

Refs #607
Teardown freed every untracked leaf as if the space owned it. These
tables can also map device memory granted to a driver and buffers
shared with other spaces; the frame allocator only rejects frames
outside its range or already free, so freeing such a frame would hand
an MMIO hole out as RAM.

An untracked leaf now has unknown ownership and is left alone, as
before this series. Only a tracked frame whose last reference drops is
freed. A leaf mapped uncached, write-through or with its PAT bit set is
device memory: the fork copies its mapping without taking a reference,
and teardown never frees it.

Refs #607
nonos-sync changed title from WIP: process: make the unused clone_for_fork sound to process: make the unused clone_for_fork sound 2026-10-10 05:09:39 -04:00
This pull request can be merged automatically.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin +refs/pull/154/head:nonos-sync/gh-615
git switch nonos-sync/gh-615
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
NON-OS/nonos-micro-kernel!154
No description provided.