Skip to content

Conversation

mingzi47
Copy link

@mingzi47 mingzi47 commented May 29, 2025

Description

The COW mechanism for fork

Implementation Details

目前存在两种方案,当前 pr 使用的是方案1

方案 1

将在 Backend 中管理当前 vma 的物理页帧的引用计数,使用 Arc 完成计数。

这种方案使用了类似 Arc<Mutex<Vec<Mutex<GlobalPage>>> 的结构来管理分配的页。

目前的实现存在一些问题(边界问题)

优点:

  • 使用 RAII 的方式管理引用计数

  • 查找页面时,没有全局的锁

缺点:

  • 需要额外为 Backend 实现 clone, 为了内部可变性,对 Vec 使用 Arc<Mutex> 进行了包裹,因此默认的 clone 并不能使 GlobalPage 的引用计数增加。

  • Backend::Alloc 中增加了复杂的结构。

方案 2

使用全局的页管理器来管理物理页帧的引用计数,使用原子变量计数。

https://github.com/mingzi47/arceos/tree/cow-global

优点:

  • 相对方案 2,实现更简单

  • 全部页放在一块,没有其他地方引用,内存占用更小

缺点:

  • 全局大锁

  • 手动进行引用计数

How to Test

Tips: Please provide the test results of running testcases for starry-next on the commit corresponding to your PR. You can paste it here in the form of a screenshot, or provide a CI link to a branch or fork of starry-next for us to review.

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

#define N 100

void forktest(void) {
  int n, pid;

  printf("fork test\n");

  for (n = 0; n < N; n++) {
    pid = fork();
    if (pid < 0)
      break;
    if (pid == 0)
      exit(0);
  }

  if (n == N) {
    printf("fork claimed to work %d times!\n", n);
    exit(1);
  }

  for (; n > 0; n--) {
    if (wait(0) < 0) {
      printf("wait stopped early\n");
      exit(1);
    }
  }

  if (wait(0) != -1) {
    printf("wait got too many\n");
    exit(1);
  }

  printf("fork test OK\n");
}

int main(void) {
  forktest();
  exit(0);
}

Here is a log of copying Addrspace when fork:

Only physical pages are alloced for the root and intermediate page tables

[  1.517418 0:4 axmm::aspace:452] used_pages = 18151
[  1.517469 0:4 axmm::aspace:467] area start: VA:0x1000, end: VA:0x2000, flags : READ | USER
[  1.517690 0:4 axmm::aspace:467] area start: VA:0x2000, end: VA:0x3000, flags : READ | EXECUTE | USER
[  1.517859 0:4 axmm::aspace:467] area start: VA:0x3000, end: VA:0x4000, flags : READ | USER
[  1.518063 0:4 axmm::aspace:467] area start: VA:0x4000, end: VA:0x5000, flags : READ | USER
[  1.518242 0:4 axmm::aspace:467] area start: VA:0x5000, end: VA:0x6000, flags : READ | WRITE | USER
[  1.518489 0:4 axmm::aspace:467] area start: VA:0x4001000, end: VA:0x4098000, flags : READ | EXECUTE | USER
[  1.518894 0:4 axmm::aspace:467] area start: VA:0x4098000, end: VA:0x4099000, flags : READ | USER
[  1.519088 0:4 axmm::aspace:467] area start: VA:0x4099000, end: VA:0x409d000, flags : READ | WRITE | USER
[  1.520444 0:4 axmm::aspace:467] area start: VA:0x40000000, end: VA:0x40001000, flags : USER
[  1.520579 0:4 axmm::aspace:467] area start: VA:0x40001000, end: VA:0x40010000, flags : READ | WRITE | USER
[  1.520756 0:4 axmm::aspace:467] area start: VA:0x40010000, end: VA:0x40011000, flags : READ | EXECUTE | USER
[  1.520931 0:4 axmm::aspace:467] area start: VA:0x7ffeffff0000, end: VA:0x7fff00000000, flags : READ | WRITE | USER
[  1.521138 0:4 axmm::aspace:494] used_pages = 18161

- support x86_64

refactor: improve page fault handling and page management

- Fix whitespace in PageFaultErrorCode flags

- Simplify page fault COW logic

- Rename page ref count methods for clarity

- Move page copy logic to Page struct

- Add better documentation for page management

refactor: improve copy_with_cow

    - use `insert_area`

    - add `MemorySet` patch

fmt: fmt

chore: update test workflow dependencies

- Add memory_set crate dependency

- Add memory_addr crate dependency

refactor: copy_with_cow and add support alloc contiguous pages

- Remove myself git dependencies for memory_set and memory_addr

- Update Cargo.toml and Cargo.lock

- Adjust page allocation to support contiguous pages

- Improve COW handling with page size parameter
@mingzi47 mingzi47 force-pushed the cow branch 2 times, most recently from e35db1c to 4468909 Compare June 1, 2025 14:41
mingzi47 and others added 6 commits June 7, 2025 16:14
- Remove Arc and FrameTracker in favor of atomic ref counting

- Add frameinfo module for physical frame management

- Simplify COW fault handling with direct frame operations

- Update backend allocator to use new frame tracking system

- only support with x86
- Remove redundant backend matching in fork

- Reorganize COW fault handling comments

- Clean up page table update flow
- Replace ok() with expect() for protect/map operations

- Simplify page copy with copy_nonoverlapping

- Add physical address validation in phys_to_pfn
@mingzi47
Copy link
Author

mingzi47 commented Jun 9, 2025

Update

vec is used to make a global large table and record the information of the physical frame. Random access through the pfn.

For huge pages, the start address should be used to access the large table

Problem

Problems were found in both loongarch64 and aarch64.

loongarch64:

  • Page modify exception

aarch64:

  • Tlb flush
  • Kernel access user mem

LoongArch

In page_table_multiarch, the D flag is added to set_flags by default, so page_fault cannot be triggered

LoongArch Doc

Page modify exception: This exception is triggered when the virtual address of the store operation finds a match in the TLB with V=1 and privilege level is legal and D=0.

I made some changes:

mingzi47/page_table_multiarch@cc5796c

AArch64

TLB Flush

This code is taken from the arm documentation

STR  X1, [X5]        // Write to translation table entry
DSB  ISH             // Barrier instructions - not covered in this guide
TLBI VAAE1IS  , X0   // Invalidate VA specified by X0, in EL0/1
                     // virtual address space for all ASIDs
DSB  ISH             // Barrier instructions - not covered in this guide
ISB                  // Synchronize context on this processor

The description of the X0 register D8.17.5.1.

For TLB maintenance instructions that take a VA, an ASID, or both as an argument,
and that do not apply to a range of addresses, the register specified by the Xt
argument has the following format:

  • Register bits[63:48] are one of the following:
    • If the instruction requires an ASID argument, the ASID.
    • If the instruction does not require an ASID argument, RES0.
  • If FEAT_TTL is not implemented, then register bits[47:44] are RES0.
  • If FEAT_TTL is implemented, then register bits[47:44] are one of the following:
    • If the instruction requires a VA argument, the translation table level hint, TTL.
    • If the instruction does not require a VA argument, RES0.
  • Register bits[43:0] are one of the following:
    • If the instruction requires a VA argument, VA[55:12].
    • If the instruction does not require a VA argument, RES0.

Code in the current page_table_multiarch crate

#[inline]
fn flush_tlb(vaddr: Option<memory_addr::VirtAddr>) {
    unsafe {
        if let Some(vaddr) = vaddr {
            // TLB Invalidate by VA, All ASID, EL1, Inner Shareable
            asm!("tlbi vaae1is, {}; dsb sy; isb", in(reg) vaddr.as_usize())
        } else {
            // TLB Invalidate by VMID, All at stage 1, EL1
            asm!("tlbi vmalle1; dsb sy; isb")
        }
    }
}

Code in the polyhal crate

#[inline]
pub fn flush_vaddr(vaddr: VirtAddr) {
    unsafe {
        core::arch::asm!(
            "
                tlbi vaale1is, {}
                dsb sy
                isb
            ",
            in(reg) ((vaddr.raw() >> 12) & 0xFFFF_FFFF_FFFF)
        )
    }
}

For testing purposes, I made a few changes.

mingzi47/page_table_multiarch@61b110f

The kernel accesses user memory

This happens in signal, modify the SignalFrame from the user stack.

Accessing a stack frame without the W flag triggers page_fault because the SignalFrame size exceeds 4k

https://github.com/Starry-OS/axsignal/blob/main/src/api/thread.rs#L70-L90

let sp = if stack.disabled() || !action.flags.contains(SignalActionFlags::ONSTACK) {
    tf.sp()
} else {
    stack.sp
};
drop(stack);

// TODO: check if stack is large enough
let aligned_sp = (sp - layout.size()) & !(layout.align() - 1);

let frame_ptr = aligned_sp as *mut SignalFrame;
// SAFETY: pointer is valid
let frame = unsafe { &mut *frame_ptr };

*frame = SignalFrame {
    ucontext: UContext::new(tf, restore_blocked),
    siginfo: sig.clone(),
    tf: *tf,
};

mingzi47 added 2 commits June 11, 2025 20:02
…upport

- Renamed `populate_area` to `ensure_region_mapped`
- Refactored `handle_cow_fault` to take a mutable page table reference
@Azure-stars
Copy link
Collaborator

Port the changes of huge page support(See #49)

mingzi47 added 2 commits June 12, 2025 16:23
- Use align from backend for page iteration

- Remove TODO and FIXME comments

- Simplify page iteration logic
mingzi47 added 3 commits June 13, 2025 23:09
- Replace direct frame info access with FrameRefTable API

- Remove redundant align parameter from protect()

- Rename ensure_region_mapped to populate_area

- Simplify COW handling logic

- Consolidate frame info operations into FrameRefTable struct
- Add conditional compilation for COW feature

- Refactor clone_or_err into try_clone with COW support

- Update frame allocation/deallocation logic

- Move frameinfo module behind feature flag

- Add new cow feature to Cargo.toml
@mingzi47
Copy link
Author

update

Add support for the COW feature in axmm.

  • Merge clone_or_err and copy_with_cow into a single function, renamed to try_clone.

  • Modify the code according to the comments.

test

fn populate_area(mut start: usize, size: usize, areas: Vec<(usize, usize)>) {
    let end = start + size;
    for area in areas.iter() {
        if start >= area.1 {
            continue;
        }

        if start < area.0 {
            panic!("area is not fully mapped")
        }

        start = area.1;
        if start >= end {
            return;
        }
    }

    panic!("area is not fully mapped");
}

#[test]
fn test_populate_area_success() {
    // 连续覆盖 [1000, 1300)
    let areas = vec![(1000, 1100), (1100, 1200), (1200, 1300)];
    populate_area(1000, 300, areas);
}

#[test]
#[should_panic(expected = "area is not fully mapped")]
fn test_populate_area_with_hole() {
    // 缺失 [1100, 1200)
    let areas = vec![(1000, 1100), (1200, 1300)];
    populate_area(1000, 300, areas);
}

#[test]
#[should_panic(expected = "area is not fully mapped")]
fn test_populate_area_not_starting_correctly() {
    // 开始位置是 1000,但第一个区域从 1100 开始
    let areas = vec![(1100, 1300)];
    populate_area(1000, 300, areas);
}

#[test]
#[should_panic(expected = "area is not fully mapped")]
fn test_populate_area_partial_coverage() {
    // 覆盖只到 1250,目标是 1300
    let areas = vec![(1000, 1100), (1100, 1250)];
    populate_area(1000, 300, areas);
}

- Consolidate COW fault handling logic
- Replace LazyInit with lazy_static for frame table
- Remove redundant frame table initialization
- Clean up memory area copying logic
@Azure-stars
Copy link
Collaborator

@Azure-stars
Copy link
Collaborator

- Move PageIterWrapper to root module

- Clean up unused imports

- Simplify cow feature logic

- Fix docstring alignment

- Remove redundant page size handling
@Azure-stars Azure-stars merged commit b55bc93 into oscomp:main Jun 15, 2025
24 checks passed
Mivik pushed a commit to Mivik/arceos that referenced this pull request Jun 21, 2025
* Add basc COW

- support x86_64

refactor: improve page fault handling and page management

- Fix whitespace in PageFaultErrorCode flags

- Simplify page fault COW logic

- Rename page ref count methods for clarity

- Move page copy logic to Page struct

- Add better documentation for page management

refactor: improve copy_with_cow

    - use `insert_area`

    - add `MemorySet` patch

fmt: fmt

chore: update test workflow dependencies

- Add memory_set crate dependency

- Add memory_addr crate dependency

refactor: copy_with_cow and add support alloc contiguous pages

- Remove myself git dependencies for memory_set and memory_addr

- Update Cargo.toml and Cargo.lock

- Adjust page allocation to support contiguous pages

- Improve COW handling with page size parameter

* Squash merge cow-backend into cow

* refactor: replace Arc-based frame tracking with frame info table

- Remove Arc and FrameTracker in favor of atomic ref counting

- Add frameinfo module for physical frame management

- Simplify COW fault handling with direct frame operations

- Update backend allocator to use new frame tracking system

- only support with x86

* refactor: simplify address space handling logic

- Remove redundant backend matching in fork

- Reorganize COW fault handling comments

- Clean up page table update flow

* refactor: improve memory management safety

- Replace ok() with expect() for protect/map operations

- Simplify page copy with copy_nonoverlapping

- Add physical address validation in phys_to_pfn

* chore: clean up

* refactor: Rename `populate_area` to `ensure_region_mapped` with COW support

- Renamed `populate_area` to `ensure_region_mapped`
- Refactored `handle_cow_fault` to take a mutable page table reference

* chore: Modify according to comment

* refactor: replace PageIter4K with PageIterWrapper in AddrSpace

- Use align from backend for page iteration

- Remove TODO and FIXME comments

- Simplify page iteration logic

* refactor: simplify frame reference counting

- Replace direct frame info access with FrameRefTable API

- Remove redundant align parameter from protect()

- Rename ensure_region_mapped to populate_area

- Simplify COW handling logic

- Consolidate frame info operations into FrameRefTable struct

* refactor: (populate_area) use a iterator to walk through the area covered by the specified access interval

* feat: add COW feature for memory management

- Add conditional compilation for COW feature

- Refactor clone_or_err into try_clone with COW support

- Update frame allocation/deallocation logic

- Move frameinfo module behind feature flag

- Add new cow feature to Cargo.toml

* refactor: simplify COW handling and frame table initialization

- Consolidate COW fault handling logic
- Replace LazyInit with lazy_static for frame table
- Remove redundant frame table initialization
- Clean up memory area copying logic

* refactor: reorganize memory management modules

- Move PageIterWrapper to root module

- Clean up unused imports

- Simplify cow feature logic

- Fix docstring alignment

- Remove redundant page size handling

* doc: fix doc link
AsakuraMizu pushed a commit that referenced this pull request Jul 7, 2025
* Add basc COW

- support x86_64

refactor: improve page fault handling and page management

- Fix whitespace in PageFaultErrorCode flags

- Simplify page fault COW logic

- Rename page ref count methods for clarity

- Move page copy logic to Page struct

- Add better documentation for page management

refactor: improve copy_with_cow

    - use `insert_area`

    - add `MemorySet` patch

fmt: fmt

chore: update test workflow dependencies

- Add memory_set crate dependency

- Add memory_addr crate dependency

refactor: copy_with_cow and add support alloc contiguous pages

- Remove myself git dependencies for memory_set and memory_addr

- Update Cargo.toml and Cargo.lock

- Adjust page allocation to support contiguous pages

- Improve COW handling with page size parameter

* Squash merge cow-backend into cow

* refactor: replace Arc-based frame tracking with frame info table

- Remove Arc and FrameTracker in favor of atomic ref counting

- Add frameinfo module for physical frame management

- Simplify COW fault handling with direct frame operations

- Update backend allocator to use new frame tracking system

- only support with x86

* refactor: simplify address space handling logic

- Remove redundant backend matching in fork

- Reorganize COW fault handling comments

- Clean up page table update flow

* refactor: improve memory management safety

- Replace ok() with expect() for protect/map operations

- Simplify page copy with copy_nonoverlapping

- Add physical address validation in phys_to_pfn

* chore: clean up

* refactor: Rename `populate_area` to `ensure_region_mapped` with COW support

- Renamed `populate_area` to `ensure_region_mapped`
- Refactored `handle_cow_fault` to take a mutable page table reference

* chore: Modify according to comment

* refactor: replace PageIter4K with PageIterWrapper in AddrSpace

- Use align from backend for page iteration

- Remove TODO and FIXME comments

- Simplify page iteration logic

* refactor: simplify frame reference counting

- Replace direct frame info access with FrameRefTable API

- Remove redundant align parameter from protect()

- Rename ensure_region_mapped to populate_area

- Simplify COW handling logic

- Consolidate frame info operations into FrameRefTable struct

* refactor: (populate_area) use a iterator to walk through the area covered by the specified access interval

* feat: add COW feature for memory management

- Add conditional compilation for COW feature

- Refactor clone_or_err into try_clone with COW support

- Update frame allocation/deallocation logic

- Move frameinfo module behind feature flag

- Add new cow feature to Cargo.toml

* refactor: simplify COW handling and frame table initialization

- Consolidate COW fault handling logic
- Replace LazyInit with lazy_static for frame table
- Remove redundant frame table initialization
- Clean up memory area copying logic

* refactor: reorganize memory management modules

- Move PageIterWrapper to root module

- Clean up unused imports

- Simplify cow feature logic

- Fix docstring alignment

- Remove redundant page size handling

* doc: fix doc link
AsakuraMizu pushed a commit that referenced this pull request Jul 7, 2025
* Add basc COW

- support x86_64

refactor: improve page fault handling and page management

- Fix whitespace in PageFaultErrorCode flags

- Simplify page fault COW logic

- Rename page ref count methods for clarity

- Move page copy logic to Page struct

- Add better documentation for page management

refactor: improve copy_with_cow

    - use `insert_area`

    - add `MemorySet` patch

fmt: fmt

chore: update test workflow dependencies

- Add memory_set crate dependency

- Add memory_addr crate dependency

refactor: copy_with_cow and add support alloc contiguous pages

- Remove myself git dependencies for memory_set and memory_addr

- Update Cargo.toml and Cargo.lock

- Adjust page allocation to support contiguous pages

- Improve COW handling with page size parameter

* Squash merge cow-backend into cow

* refactor: replace Arc-based frame tracking with frame info table

- Remove Arc and FrameTracker in favor of atomic ref counting

- Add frameinfo module for physical frame management

- Simplify COW fault handling with direct frame operations

- Update backend allocator to use new frame tracking system

- only support with x86

* refactor: simplify address space handling logic

- Remove redundant backend matching in fork

- Reorganize COW fault handling comments

- Clean up page table update flow

* refactor: improve memory management safety

- Replace ok() with expect() for protect/map operations

- Simplify page copy with copy_nonoverlapping

- Add physical address validation in phys_to_pfn

* chore: clean up

* refactor: Rename `populate_area` to `ensure_region_mapped` with COW support

- Renamed `populate_area` to `ensure_region_mapped`
- Refactored `handle_cow_fault` to take a mutable page table reference

* chore: Modify according to comment

* refactor: replace PageIter4K with PageIterWrapper in AddrSpace

- Use align from backend for page iteration

- Remove TODO and FIXME comments

- Simplify page iteration logic

* refactor: simplify frame reference counting

- Replace direct frame info access with FrameRefTable API

- Remove redundant align parameter from protect()

- Rename ensure_region_mapped to populate_area

- Simplify COW handling logic

- Consolidate frame info operations into FrameRefTable struct

* refactor: (populate_area) use a iterator to walk through the area covered by the specified access interval

* feat: add COW feature for memory management

- Add conditional compilation for COW feature

- Refactor clone_or_err into try_clone with COW support

- Update frame allocation/deallocation logic

- Move frameinfo module behind feature flag

- Add new cow feature to Cargo.toml

* refactor: simplify COW handling and frame table initialization

- Consolidate COW fault handling logic
- Replace LazyInit with lazy_static for frame table
- Remove redundant frame table initialization
- Clean up memory area copying logic

* refactor: reorganize memory management modules

- Move PageIterWrapper to root module

- Clean up unused imports

- Simplify cow feature logic

- Fix docstring alignment

- Remove redundant page size handling

* doc: fix doc link
fn err_code_to_flags(err_code: u64) -> Result<MappingFlags, u64> {
let code = PageFaultErrorCode::from_bits_truncate(err_code);
let reserved_bits = (PageFaultErrorCode::CAUSED_BY_WRITE
| PageFaultErrorCode::PROTECTION_VIOLATION
Copy link
Collaborator

Choose a reason for hiding this comment

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

@mingzi47 Sorry to bother you, I was wondering if this change is necessary? Because I'm porting these to axcpu.

lurenjia1213 pushed a commit to lurenjia1213/arceos that referenced this pull request Jul 19, 2025
* Add basc COW

- support x86_64

refactor: improve page fault handling and page management

- Fix whitespace in PageFaultErrorCode flags

- Simplify page fault COW logic

- Rename page ref count methods for clarity

- Move page copy logic to Page struct

- Add better documentation for page management

refactor: improve copy_with_cow

    - use `insert_area`

    - add `MemorySet` patch

fmt: fmt

chore: update test workflow dependencies

- Add memory_set crate dependency

- Add memory_addr crate dependency

refactor: copy_with_cow and add support alloc contiguous pages

- Remove myself git dependencies for memory_set and memory_addr

- Update Cargo.toml and Cargo.lock

- Adjust page allocation to support contiguous pages

- Improve COW handling with page size parameter

* Squash merge cow-backend into cow

* refactor: replace Arc-based frame tracking with frame info table

- Remove Arc and FrameTracker in favor of atomic ref counting

- Add frameinfo module for physical frame management

- Simplify COW fault handling with direct frame operations

- Update backend allocator to use new frame tracking system

- only support with x86

* refactor: simplify address space handling logic

- Remove redundant backend matching in fork

- Reorganize COW fault handling comments

- Clean up page table update flow

* refactor: improve memory management safety

- Replace ok() with expect() for protect/map operations

- Simplify page copy with copy_nonoverlapping

- Add physical address validation in phys_to_pfn

* chore: clean up

* refactor: Rename `populate_area` to `ensure_region_mapped` with COW support

- Renamed `populate_area` to `ensure_region_mapped`
- Refactored `handle_cow_fault` to take a mutable page table reference

* chore: Modify according to comment

* refactor: replace PageIter4K with PageIterWrapper in AddrSpace

- Use align from backend for page iteration

- Remove TODO and FIXME comments

- Simplify page iteration logic

* refactor: simplify frame reference counting

- Replace direct frame info access with FrameRefTable API

- Remove redundant align parameter from protect()

- Rename ensure_region_mapped to populate_area

- Simplify COW handling logic

- Consolidate frame info operations into FrameRefTable struct

* refactor: (populate_area) use a iterator to walk through the area covered by the specified access interval

* feat: add COW feature for memory management

- Add conditional compilation for COW feature

- Refactor clone_or_err into try_clone with COW support

- Update frame allocation/deallocation logic

- Move frameinfo module behind feature flag

- Add new cow feature to Cargo.toml

* refactor: simplify COW handling and frame table initialization

- Consolidate COW fault handling logic
- Replace LazyInit with lazy_static for frame table
- Remove redundant frame table initialization
- Clean up memory area copying logic

* refactor: reorganize memory management modules

- Move PageIterWrapper to root module

- Clean up unused imports

- Simplify cow feature logic

- Fix docstring alignment

- Remove redundant page size handling

* doc: fix doc link
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.

3 participants