Skip to main content

strat9_kernel/memory/
cow.rs

1//! Copy-on-Write (COW) support for fork()
2//!
3//! This module provides the core functionality for copy-on-write memory management,
4//! allowing efficient fork() implementation by sharing pages between parent and child
5//! until one of them writes to a shared page.
6//!
7//! # Design
8//!
9//! - Pages are marked as COW by clearing the WRITABLE bit and setting a software COW flag
10//! - The COW flag is stored in bit 9 of the PTE (available for software use on x86_64)
11//! - When a write fault occurs on a COW page:
12//!   1. If refcount > 1: allocate new frame, copy content, update PTE
13//!   2. If refcount == 1: just mark page as writable (no copy needed)
14//!
15//! # SMP Safety
16//!
17//! - Atomic refcount per frame (lock-free inc/dec)
18//! - COW_LOCK protects flag changes and frame free
19//! - TLB shootdown is performed after modifying page table flags
20
21use crate::{
22    memory::{
23        frame::{frame_flags, get_meta, PhysFrame},
24        ownership_table, release_owned_block, resolve_handle, BlockHandle, OwnerError,
25        RemoveRefResult,
26    },
27    sync::SpinLock,
28};
29
30static COW_LOCK: SpinLock<()> = SpinLock::new(());
31
32/// Performs the frame to pfn operation.
33#[inline]
34fn frame_meta(frame: PhysFrame) -> &'static crate::memory::frame::FrameMeta {
35    get_meta(frame.start_address)
36}
37
38/// Returns the metadata slot backing a physical block handle.
39#[inline]
40fn handle_meta(handle: BlockHandle) -> &'static crate::memory::frame::FrameMeta {
41    get_meta(handle.base)
42}
43
44/// Increments the shared reference count of a physical block.
45pub fn handle_inc_ref(handle: BlockHandle) -> Result<u32, OwnerError> {
46    ownership_table().pin(handle)
47}
48
49/// Decrements the shared reference count of a physical block.
50pub fn handle_dec_ref(handle: BlockHandle) {
51    match ownership_table().unpin(handle) {
52        Ok(RemoveRefResult::Freed(block)) => release_owned_block(block),
53        Ok(RemoveRefResult::NowExclusive { .. })
54        | Ok(RemoveRefResult::StillPinned { .. })
55        | Ok(RemoveRefResult::StillShared { .. }) => {}
56        Err(error) => {
57            log::warn!(
58                "memory: failed to unpin shared handle {:#x}/{}: {:?}",
59                handle.base.as_u64(),
60                handle.order,
61                error
62            );
63        }
64    }
65}
66
67/// Returns the current shared reference count of a physical block.
68pub fn handle_get_refcount(handle: BlockHandle) -> u32 {
69    ownership_table()
70        .refcount(handle)
71        .unwrap_or_else(|| handle_meta(handle).get_refcount())
72}
73
74/// Marks a freshly allocated physical block as exclusively owned.
75pub fn handle_init_ref(handle: BlockHandle) {
76    let meta = handle_meta(handle);
77    meta.set_order(handle.order);
78    meta.set_refcount(1);
79}
80
81/// Performs the frame inc ref operation.
82pub fn frame_inc_ref(frame: PhysFrame) -> Result<u32, OwnerError> {
83    handle_inc_ref(resolve_handle(frame.start_address))
84}
85
86/// Performs the frame dec ref operation.
87pub fn frame_dec_ref(frame: PhysFrame) {
88    handle_dec_ref(resolve_handle(frame.start_address));
89}
90
91/// Performs the frame get refcount operation.
92pub fn frame_get_refcount(frame: PhysFrame) -> u32 {
93    handle_get_refcount(resolve_handle(frame.start_address))
94}
95
96/// Set COW flag on a frame (SMP-safe).
97pub fn frame_set_cow(frame: PhysFrame) {
98    let _lock = COW_LOCK.lock();
99    let meta = frame_meta(frame);
100    let flags = meta.get_flags() | frame_flags::COW;
101    meta.set_flags(flags);
102}
103
104/// Clear COW flag on a frame (SMP-safe).
105pub fn frame_clear_cow(frame: PhysFrame) {
106    let _lock = COW_LOCK.lock();
107    let meta = frame_meta(frame);
108    let flags = meta.get_flags() & !frame_flags::COW;
109    meta.set_flags(flags);
110}
111
112/// Check if a frame is marked as COW (lock-free read).
113pub fn frame_is_cow(frame: PhysFrame) -> bool {
114    frame_meta(frame).is_cow()
115}
116
117/// Mark a frame as DLL (shared, never COW) (SMP-safe).
118pub fn frame_set_dll(frame: PhysFrame) {
119    let _lock = COW_LOCK.lock();
120    let meta = frame_meta(frame);
121    let flags = meta.get_flags() | frame_flags::DLL;
122    meta.set_flags(flags);
123}
124
125/// Check if a frame is a DLL frame (lock-free read).
126pub fn frame_is_dll(frame: PhysFrame) -> bool {
127    frame_meta(frame).is_dll()
128}