Repository navigation
fix/futex: forget to .await in futex_exit - #63
Closed
greatbridf wants to merge 153 commits into
Closed
greatbridf wants to merge 153 commits into
greatbridf wants to merge 153 commits into
Conversation
Inode and superblock rework: Remove old Inode trait as it used to undertake too much responsibility. The new method use three new traits: InodeOps is used to acquire generic inode attributes. InodeFileOps and InodeDirOps handle file and directory requests respectively. All the three have async fn trait methods and don't need to be ?Sized. Then, we implement Inode, InodeFile and InodeDir for the implementors of the three "Ops" traits, erasing their actual type and provide generic dyn interface by wrapping the futures in boxes. We should provide an io worker? or some io context with an allocator for futures to reduce the overhead of io requests, or come up with some better ideas? For inode usage, we introduce InodeRef and InodeUse. InodeRef is a simple wrapper for Weak<impl Inode> and InodeUse for Arc<impl Inode>. This helps us use them better as we can't define impls for Arc<dyn Inode>'s as they are foreign types. We also provide some more helper methods for them. After the change, we don't impose ANY structural restriction except for the spinlock wrapped InodeInfo. The InodeInfo struct design might need rethinking but the current implementation seems to be fine aside from unnecessary locks when we try to access some of its fields but this shouldn't be a VERY big problem and very urgent... Similar changes are also made to superblock traits and types. But for the superblock objects, we use a SuperBlockComplex struct to store common fields such as whether the superblock is read only, their device id and so on. Also the structs have a superblock rwsem inside. But we haven't decided how to use that (such as whether we should acquire the lock and pass it to the inode methods) and even whether they should exist and be there. This will need further thinking so we put this off for now... Filesystem rework: Rework tmpfs, fatfs and procfs with the new technology mentioned above, leaving the old ext4 unchanged. The current implementation of ext4 uses some "random" library from the "camp". Its code hasn't been fully reviewed for time reasons but seems to be rather "problematic"... We might rewrite the whole module later and the page cache subsystem requires fully reworking as well. So we put this work off as well. Block device and other parts rework: Wraps PageCacheBackend, MountCreator and BlockRequestQueue with async_trait to provide dyn compatible async functions. Dentry walking functions are also put to the heap since they are recursive functions... This has similar problems to the inode traits, ugly solution. Further optimization is required. Signed-off-by: greatbridf <greatbridf@icloud.com>
The old path walking algorithm requires recursion, which is not supported in async rust. So we boxed them all as a temporary solution in previous commits. This would introduce mass overhead even for fast path walks just because we might sleep in `readlink()` and `lookup()` calls. The new proposed method is to break the walk into several phases similar to that in Linux: RCU walk and REF walk. The RCU walk will hold the RCU lock and never blocks so the function itself can be non-async. If we hit non-present dentries, we will fallback to REF walk. In REF walks, we clone the Arcs and consult to the VFS layer for an accurate answer. Note that in both the two methods mentioned above, symlinks are not handled and will be returned directly with all path components left untouched. We have a dedicated async function to follow the symlinks by recursive calling the walk function. This can be slow and won't be called frequently. So we wrapped the function with `Box::pin()` to break the recursion chain. After the symlink resolution is done, we return to the original position and continue the walk. We found that the association of an inode to a dentry is one way. So the `data` RCUPointer field is actually unnecessary and we can use the atomic dentry type to sync readers with the writer. This way we can eliminate `DentryData` allocations and improve performance. We also introduced a new RCU read lock syntax. In the RCU walk mentioned above, we need to store dentry references protected by some RCU read lock. With the old syntax, we can't express the lifetime associtated by the common RCU read lock. The new syntax provides a `rcu_read_lock()` method to acquire the RCU read lock. The lock returned has a associated lifetime so we can use it in the RCU session. Signed-off-by: greatbridf <greatbridf@icloud.com>
Signed-off-by: greatbridf <greatbridf@icloud.com>
If we have invalid format, we should print a None instead of panicking. Signed-off-by: greatbridf <greatbridf@icloud.com>
Introduce the new page locking mechanism to ensure exclusiveness of pages when we access them. The underlying locks are not implemented yet for now because we will change the paging structs in the following few patches. Introduce a new `PageExcl` struct representing a page that conforms with Rust's ownership rules. The page owned exclusively can be accessed without taking page locks. Remove the `MemoryBlock` structs as they are not easy to use and represent barely no semantic meanings. Signed-off-by: greatbridf <greatbridf@icloud.com>
Separate old UserTLS into UserTLS and UserTLSDescriptor. UserTLS is for threads to hold infomation about its storage. Descriptors are used in clone syscalls. Signed-off-by: greatbridf <greatbridf@icloud.com>
Signed-off-by: greatbridf <greatbridf@icloud.com>
Introduce `Zone`s: a Zone is a region of physical memory that all share the same NUMA node. The Zone will hold all RawPage structs. Buddy allocator will now store a reference to the zone that hold all its pages. Thus, we make the buddy allocator independent of underlying physical page frame management framework. Remove unnecessary page flags and structs. Signed-off-by: greatbridf <greatbridf@icloud.com>
- Bump rust compiler version to nightly-2026-01-09. - Inode rework: add a generic Inode struct. - Add a macro to help function tweaks. - PageCache rework: reduce complexity and try to decouple. - Adapt fat32, tmpfs to the new page cache system. - Change the way we process mapped pages and load ELF executables. - Refine handling flags in `MMArea::handle_mmap`. Signed-off-by: greatbridf <greatbridf@icloud.com>
- Remove struct `Page` and add `Folio`s to represent adjacent pages. - Introduce `Zone`s similar to that in Linux. Looking forward to removing all occurrence of `ArchPhysAccess` and so on. - Adapt existing code to new `Folio` interface in a dirty and rough way. Signed-off-by: greatbridf <greatbridf@icloud.com>
- Add script/backtrace to translate backtraces. - Add a cut sign in the kernel panic routine to indicate the start of stack backtrace. Signed-off-by: greatbridf <greatbridf@icloud.com>
No functional changes
- Add `{extern_,}symbol_addr` macro to retrieve symbol address.
- Remove manual impl Send and Sync for RawPage
- Make elided '_ lifetimes in return types explicit
- Suppress unused warnings by allowing them
- Remove really unused functions
- Refactor `println_trace` macro to suppress unused variable warnings
Signed-off-by: greatbridf <greatbridf@icloud.com>
Make print message prettier. Also panic when we have an error. Signed-off-by: greatbridf <greatbridf@icloud.com>
Signed-off-by: greatbridf <greatbridf@icloud.com>
- thd.exit() will set thd.dead and send it to the reaper - delay the release of process mm until we reap it - extract futex logic out of exit and exec routine Signed-off-by: greatbridf <greatbridf@icloud.com>
- Use intrusive lists to store and organize the process hierarchy. - Remove `FileArray::open_console()`. Do it in the init script instead. - Fix open logic: acquire controlling terminals only if O_NOCTTY is not set. Put this into TerminalFile::open(). - Send SIGHUP and then SIGCONT to foreground pgroup procs when the controlling terminal is dropped. - Set the controlling terminal of sessions in Terminal. - Limit max line width to 80. Format some codes. Signed-off-by: greatbridf <greatbridf@icloud.com>
Reformat the files with new the format style to make the real changes clearer. Signed-off-by: greatbridf <greatbridf@icloud.com>
We already have `FDT.present_ram().free_ram()`. Remove the impl in `ArchMemory` to avoid confusion. Signed-off-by: greatbridf <greatbridf@icloud.com>
- Add `extern_symbol_value` to retrieve far relative symbol values. - Get `BSS_LENGTH` and `__kernel_end` using `extern_symbol_addr`. - Get `_ap_start` using `extern_symbol_value`. Signed-off-by: greatbridf <greatbridf@icloud.com>
Signed-off-by: greatbridf <greatbridf@icloud.com>
With current ldscript, linkers will put vdso data after `__kernel_end`, which is buggy since we use the symbol to indicate the end of our kernel image and newly allocated pages may overwrite those positions. Change by place the vdso inside REGION_DATA. Remove old VDSO memory region. Align .data section end to page size border. Add a helper macro to retrieve .vdso section symbol addresses. Signed-off-by: greatbridf <greatbridf@icloud.com>
Using *const (), no bytes are written to the position, which might result in uninitialized memory access. Fixes: ebd3d12 ("change(x86): optimize bootstrap code, remove kinit.cpp") Fixes: 191877a ("feat(hal): impl basic single hart bootstrap for riscv64") Signed-off-by: greatbridf <greatbridf@icloud.com>
Signed-off-by: greatbridf <greatbridf@icloud.com>
This can shorten qemu memory map. Signed-off-by: greatbridf <greatbridf@icloud.com>
Rearrange code to pass args instead of by envs, no functionality changes. Signed-off-by: greatbridf <greatbridf@icloud.com>
Signed-off-by: greatbridf <greatbridf@icloud.com>
- printf -v is non-standard, use var=$(printf) instead. - expect is not installed in the runner. install it manually Signed-off-by: greatbridf <greatbridf@icloud.com>
Add PLATFORM in Makefile to indicate the actual system setup. Put build image script of each platforms to dedicated folders. This makes creating test setups easier. Signed-off-by: greatbridf <greatbridf@icloud.com>
No functional change. Signed-off-by: greatbridf <greatbridf@icloud.com>
There used to be two `insert()` impls due to historical reasons that we need to operate on raw `MemArea`s. We hope to remove raw MemArea handles, so do the cleanups. Signed-off-by: greatbridf <greatbridf@icloud.com>
Add a helper to create `AnonMapping` struct. Signed-off-by: greatbridf <greatbridf@icloud.com>
I notice that when we isolate the areas for protecting or unmapping, we forgot to update the underlying mappings' offsets. This may cause wrong offset passed to read() in fault handlers, despite we haven't observed the problem yet. Fix this by refactoring the isolate() logic: we recreate every area within the isolate range, call newly defined split functions and handle the offset correctly. Fixes: 9b18fc8 ("WIP: mem: remove unused MemArea member functions") Fixes: f37b288 ("WIP: mem: reimpl `MMList::protect` using `isolate()`") Signed-off-by: greatbridf <greatbridf@icloud.com>
Replace all MemArea construction with `Self::new()` calls so we can modify it more easily. Signed-off-by: greatbridf <greatbridf@icloud.com>
Signed-off-by: greatbridf <greatbridf@icloud.com>
Signed-off-by: greatbridf <greatbridf@icloud.com>
I extract the part cloning the area list and processing cloned areas ( particularly creating new PTEs in the new pgtable and modifying the old ones in the old table) into a separate function where shared areas and private areas are handled differently. Then, creation of new MMLists can be put off till the area list and pgtable are ready. `MemArea::deep_clone()` exists only for creating cloned MMLists, thus can be removed. Signed-off-by: greatbridf <greatbridf@icloud.com>
For repr(transparent) types that need to impl Deref trait and returns &self.0, we introduce a macro called TransparentDeref to derive Deref for them and use the macro. Signed-off-by: greatbridf <greatbridf@icloud.com>
Signed-off-by: greatbridf <greatbridf@icloud.com>
Signed-off-by: greatbridf <greatbridf@icloud.com>
Signed-off-by: greatbridf <greatbridf@icloud.com>
Add a helper function to ensure alignment and create PageOffset. Then use PageOffset and the helper internally while creating file mappings. Signed-off-by: greatbridf <greatbridf@icloud.com>
Signed-off-by: greatbridf <greatbridf@icloud.com>
There are in fact 3 types of mapping: - File private: fault in from underlying files and modifications are not carried back to the file. After faulting in, the folios are associated to some anonymous mapping and CoW on forks. - File shared: fault in just as the previous one but all folios from the same mapping are shared from the page cache. - Anonymous: Always private. Shared anonymous mappings should use tmpfs + shared file mappings. Now add a variant to the enum Mapping, and unify the fault handling logic related to the mapping. Introduce new helpers to create the mappings. Signed-off-by: greatbridf <greatbridf@icloud.com>
The PTE is read to get the pfn and attr. We can reuse it if the PTE is not present and we need to copy the PTE as is. Signed-off-by: greatbridf <greatbridf@icloud.com>
When handling missing anon or writings to private file PTEs, we need to alloc a new anon folio and add a mapping. Pass the anon mapping to the function and alloc the anon folio with the mapping. It's worth noticing that handling CoW PTEs also needs this theoretically. This should be done in next few patches. Signed-off-by: greatbridf <greatbridf@icloud.com>
Signed-off-by: greatbridf <greatbridf@icloud.com>
This function was used by some fs to allocate blank pages. Remove this since it's no longer used. Signed-off-by: greatbridf <greatbridf@icloud.com>
For anonymous pages mapped to user space, introduce a new wrapper around the struct Folio named AnonFolio, which is also a mappable folio type similar to folios for some PageCache. Before mapping a page to some area, associate the folio with the AnonMapping using `add_folio()`. Signed-off-by: greatbridf <greatbridf@icloud.com>
No functional change. Signed-off-by: greatbridf <greatbridf@icloud.com>
The two functions are unused for long so remove them. If they are needed later, add them back manually then. Signed-off-by: greatbridf <greatbridf@icloud.com>
It doesn't make sense for read_page() to accept a &mut CachePage. All it needs to do is to read and copy data from the underlying filesystem to the given folio. So change that to FolioOwned, and allocate and pass a FolioOwned to the function. After the change, we don't need to create CachePages outsides the page cache module. Change CachePage::new() to be private. Signed-off-by: greatbridf <greatbridf@icloud.com>
The standard says that PA_U bit should be cleared and reserved for future use, so we used to ignore the USER flag for table attributes. This results in memory leaks of page table folios as they're never released (we only release page table folios that is both present and for userspace usage, but USER bit is ignored when set). The workaround is to reuse the PA_COW bit to indicate the entry is for userspace usage. An entry can be CoW only if it's a leaf entry. So both PA_COW and PA_MMAP can be reused. We'll take the CoW bit as it has a smaller number... Signed-off-by: greatbridf <greatbridf@icloud.com>
Signed-off-by: greatbridf <greatbridf@icloud.com>
Introduce MapFolio, a wrapper around Folio for folios whose contents can be mapped into page tables, along with the per-folio data backing it. That data, currently a mapcount counting how many page-table entries reference the folio, is stored inline in RawPage's PageData union and is guarded by the new MAPPED page flag. A folio is turned mappable by FolioOwned::into_mappable(), which initializes the data and publishes the flag with release semantics; Folio::drop() tears the data down again on the last reference. AnonFolio and CachePage now wrap MapFolio instead of a bare Folio, so anonymous and file-cache folios become mappable at creation time. The free add_mapping()/duplicate_mapping()/remove_mapping() helpers of mm_list::mapping are folded into MapFolio methods that bump or drop the mapcount while handing a refcounted PFN to (or retrieving one from) the page tables. The CoW path in MemArea::handle_cow() now decides whether the faulted page must be copied by checking whether its mapcount is one (MapFolio::is_exclusive()) instead of reading the shared refcount after removing the mapping. The refcount conflated page-table mappings with the folio's other owners, and the old check had to remove the mapping, inspect the refcount, and re-add it; mapcount tracks mappings directly, so an exclusive page can simply be made writable in place without refcount churn. Checking is not yet race-free against concurrent faults on the same page -- locking the folio is left as an XXX comment. Signed-off-by: greatbridf <greatbridf@icloud.com>
Signed-off-by: greatbridf <greatbridf@icloud.com>
Testcase `pthread_test` freezes when waiting for clear tid futex wakeups. It turns out to be that we forget to call .await in `futex_exit()` so `do_futex_exit()` is never called. Fix by appending the missing `.await`. Fixes: b6d54d6 ("mm, proc: add an exited thread reaper") Signed-off-by: greatbridf <greatbridf@icloud.com>
greatbridf
force-pushed
the
refactor/mm/pte-map
branch
from
October 27, 2026 14:44
59041aa to
f10719d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Testcase
/mnt/pthread_testhangs after the threads are joined (issue #61): the process reaches the end of the test but never exits, freezing forever in the clear-tid futex wait.Root cause:
futex_exit()insrc/kernel/task/futex.rscalleddo_futex_exit(thread)without awaiting the returned future:Because
let _ = <future>merely drops the future,do_futex_exit()was never executed at all. As a result the thread was never detached from its futex state and the clear-tid futex wakeups were never performed, so the joiner kept waiting forever.Fix
One-line fix: actually await the future so
do_futex_exit()runs.pub async fn futex_exit(thread: &Thread) { // We don't care about any error happened inside. // If they've set up a wrong pointer, good luck to them... - let _ = do_futex_exit(thread); + let _ = do_futex_exit(thread).await; }Fixes a regression from b6d54d6 ("mm, proc: add an exited thread reaper").
Verification
Reproduced and verified with
make run MODE=release, running/mnt/pthread_testin the guest:pthread_join.Final counter value: 20000and the process exits normally.Fixes #61