0.3.0: converge kernel onto upstream master

- Rebase all Red Bear kernel changes onto upstream master (4d5d36d4).
- Update version to 0.5.12+rb0.3.0 and add Red Bear author attribution.
- Switch redox_syscall direct dependency to local fork path (../syscall).
- Bump rust-toolchain.toml to nightly-2026-05-24.
- Regenerate Cargo.lock for +rb0.3.0 suffixes and path deps.
This commit is contained in:
2026-07-06 18:43:52 +03:00
parent 4d5d36d44e
commit ca67b1da37
66 changed files with 2935 additions and 2225 deletions
+59 -143
View File
@@ -1,19 +1,14 @@
use alloc::{
collections::{BTreeMap, BTreeSet},
sync::Arc,
vec::Vec,
};
use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
use arrayvec::ArrayVec;
use core::{
cmp,
fmt::Debug,
mem::ManuallyDrop,
num::NonZeroUsize,
ops::{Bound, Deref},
ops::Bound,
sync::atomic::{AtomicU32, Ordering},
};
use rmm::{Arch as _, PageFlush};
use smallvec::SmallVec;
use syscall::{error::*, flag::MapFlags, GrantFlags, MunmapFlags};
use crate::{
@@ -59,8 +54,6 @@ pub struct UnmapResult {
pub size: usize,
pub flags: MunmapFlags,
}
pub type UnmapVec = SmallVec<[UnmapResult; 16]>;
impl UnmapResult {
pub fn unmap(mut self, token: &mut CleanLockToken) -> Result<()> {
let Some(GrantFileRef {
@@ -71,9 +64,6 @@ impl UnmapResult {
return Ok(());
};
// TODO: This is not ideal, the lock must be held until try_close(), however that would break borrowing rules.
// Proper unmap operation would be a recursive operation, since closing a file can trigger another unmap().
// We should refactor Result of munmap() to handle unmap and closing files recursively.
let (scheme_id, number) = {
let desc = description.write(token.token());
(desc.scheme, desc.number)
@@ -322,7 +312,8 @@ impl AddrSpaceWrapper {
requested_span: PageSpan,
unpin: bool,
token: &mut CleanLockToken,
) -> Result<UnmapVec> {
) -> Result<Vec<UnmapResult>> {
let mut token = token.token();
let mut guard = self.acquire_write(token.downgrade());
let guard = &mut *guard;
@@ -342,7 +333,7 @@ impl AddrSpaceWrapper {
requested_dst_base: Option<Page>,
new_page_count: usize,
new_flags: MapFlags,
mut notify_files_out: Option<&mut UnmapVec>,
mut notify_files_out: Option<&mut Vec<UnmapResult>>,
token: LockToken<L5>,
) -> Result<Page> {
let dst_lock = self;
@@ -419,7 +410,7 @@ impl AddrSpaceWrapper {
if new_page_count < src_span.count {
let unpin = false;
let notify_files = AddrSpace::munmap_inner(
let notify_files: Vec<UnmapResult> = AddrSpace::munmap_inner(
src_grants,
src_mapper,
src_flusher,
@@ -599,8 +590,8 @@ impl AddrSpace {
this_flusher: &mut Flusher,
mut requested_span: PageSpan,
unpin: bool,
) -> Result<UnmapVec> {
let mut notify_files = UnmapVec::new();
) -> Result<Vec<UnmapResult>> {
let mut notify_files = Vec::new();
let next = |grants: &mut UserGrants, span: PageSpan| {
grants
@@ -671,9 +662,7 @@ impl AddrSpace {
}
// Remove irrelevant region
// TODO: Lock ordering violation
let mut token = unsafe { CleanLockToken::new() };
let unmap_result = grant.unmap(this_mapper, this_flusher, &mut token);
let unmap_result = grant.unmap(this_mapper, this_flusher);
// Notify scheme that holds grant
if unmap_result.file_desc.is_some() {
@@ -698,7 +687,7 @@ impl AddrSpace {
requested_base_opt: Option<Page>,
page_count: NonZeroUsize,
flags: MapFlags,
notify_files_out: Option<&mut UnmapVec>,
notify_files_out: Option<&mut Vec<UnmapResult>>,
map: impl FnOnce(Page, PageFlags<RmmA>, &mut PageMapper, &mut Flusher) -> Result<Grant>,
) -> Result<Page> {
assert_eq!(dst_lock.inner.as_mut_ptr(), self as *mut Self);
@@ -772,40 +761,21 @@ impl AddrSpace {
// longer arc-rwlock wrapped, it cannot be referenced `External`ly by borrowing grants,
// so it should suffice to iterate over PageInfos and decrement and maybe deallocate
// the underlying pages (and send some funmaps).
let res = { grant.unmap(&mut self.table.utable, &mut NopFlusher, token) };
let res = { grant.unmap(&mut self.table.utable, &mut NopFlusher) };
let _ = res.unmap(token);
}
}
}
pub struct AddrSpaceSwitchReadGuard {
pub lock: RwLockReadGuard<'static, L5, AddrSpace>,
}
impl AddrSpaceSwitchReadGuard {
pub fn new(guard: RwLockReadGuard<'_, L5, AddrSpace>) -> Self {
Self {
lock: unsafe { core::mem::transmute(guard) },
}
}
}
impl Deref for AddrSpaceSwitchReadGuard {
type Target = RwLockReadGuard<'static, L5, AddrSpace>;
fn deref(&self) -> &Self::Target {
&self.lock
}
}
#[derive(Debug)]
pub struct UserGrants {
// Using a BTreeMap for its range method.
inner: BTreeMap<Page, GrantInfo>,
// Holes ordered by memory address for merging adjacent holes
holes_by_addr: BTreeMap<VirtualAddress, usize>,
// Holes ordered by size then start address for fast allocations
holes_by_size: BTreeSet<(usize, VirtualAddress)>,
// Using a BTreeMap for its range method.
holes: BTreeMap<VirtualAddress, usize>,
// TODO: Would an additional map ordered by (size,start) to allow for O(log n) allocations be
// beneficial?
}
#[derive(Clone, Copy)]
@@ -907,41 +877,10 @@ impl Debug for PageSpan {
impl UserGrants {
pub fn new() -> Self {
let mut holes_by_addr = BTreeMap::new();
let mut holes_by_size = BTreeSet::new();
let initial_offset = VirtualAddress::new(0);
let initial_size = crate::USER_END_OFFSET;
holes_by_addr.insert(initial_offset, initial_size);
holes_by_size.insert((initial_size, initial_offset));
Self {
inner: BTreeMap::new(),
holes_by_addr,
holes_by_size,
}
}
/// Internal helper to keep the two hole maps in sync
fn insert_hole(&mut self, offset: VirtualAddress, size: usize) {
self.holes_by_addr.insert(offset, size);
self.holes_by_size.insert((size, offset));
}
/// Internal helper to keep the two hole maps in sync
fn remove_hole(&mut self, offset: &VirtualAddress) -> Option<usize> {
if let Some(size) = self.holes_by_addr.remove(offset) {
self.holes_by_size.remove(&(size, *offset));
Some(size)
} else {
None
}
}
/// Internal helper to keep the two hole maps in sync
fn resize_hole(&mut self, offset: &VirtualAddress, new_size: usize) {
if let Some(size) = self.holes_by_addr.get_mut(offset) {
self.holes_by_size.remove(&(*size, *offset));
*size = new_size;
self.holes_by_size.insert((new_size, *offset));
holes: core::iter::once((VirtualAddress::new(0), crate::USER_END_OFFSET))
.collect::<BTreeMap<_, _>>(),
}
}
@@ -1004,16 +943,18 @@ impl UserGrants {
// TODO: Allow explicitly allocating guard pages? Perhaps using mprotect or mmap with
// PROT_NONE?
let req_size = page_count * PAGE_SIZE;
let (_, hole_start) = self
.holes_by_size
.range((req_size, VirtualAddress::new(0))..)
.find(|&&(hole_size, hole_offset)| {
// A hole might be large enough, but the usable
// portion above `min` address might be too small.
let usable_start = cmp::max(hole_offset.data(), min);
let hole_end = hole_offset.data() + hole_size;
usable_start + req_size <= hole_end
let (hole_start, _hole_size) = self
.holes
.iter()
.skip_while(|(hole_offset, hole_size)| hole_offset.data() + **hole_size <= min)
.find(|(hole_offset, hole_size)| {
let avail_size =
if hole_offset.data() <= min && min <= hole_offset.data() + **hole_size {
**hole_size - (min - hole_offset.data())
} else {
**hole_size
};
page_count * PAGE_SIZE <= avail_size
})?;
// Create new region
Some(PageSpan::new(
@@ -1029,14 +970,10 @@ impl UserGrants {
let size = page_count * PAGE_SIZE;
let end_address = base.start_address().add(size);
let previous_hole = self
.holes_by_addr
.range(..start_address)
.next_back()
.map(|(&k, &v)| (k, v));
let previous_hole = self.holes.range_mut(..start_address).next_back();
if let Some((hole_offset, hole_size)) = previous_hole {
let prev_hole_end = hole_offset.data() + hole_size;
let prev_hole_end = hole_offset.data() + *hole_size;
// Note that prev_hole_end cannot exactly equal start_address, since that would imply
// there is another grant at that position already, as it would otherwise have been
@@ -1046,49 +983,46 @@ impl UserGrants {
// hole_offset must be below (but never equal to) the start address due to the
// `..start_address()` limit; hence, all we have to do is to shrink the
// previous offset.
self.resize_hole(&hole_offset, start_address.data() - hole_offset.data());
*hole_size = start_address.data() - hole_offset.data();
}
if prev_hole_end > end_address.data() {
// The grant is splitting this hole in two, so insert the new one at the end.
self.insert_hole(end_address, prev_hole_end - end_address.data());
self.holes
.insert(end_address, prev_hole_end - end_address.data());
}
}
// Next hole
if let Some(hole_size) = self.remove_hole(&start_address) {
if let Some(hole_size) = self.holes.remove(&start_address) {
let remainder = hole_size - size;
if remainder > 0 {
self.insert_hole(end_address, remainder);
self.holes.insert(end_address, remainder);
}
}
}
fn unreserve(&mut self, base: Page, page_count: usize) {
fn unreserve(holes: &mut BTreeMap<VirtualAddress, usize>, base: Page, page_count: usize) {
// TODO
let start_address = base.start_address();
let size = page_count * PAGE_SIZE;
let end_address = base.start_address().add(size);
// The size of any possible hole directly after the to-be-freed region.
let exactly_after_size = self.remove_hole(&end_address);
let exactly_after_size = holes.remove(&end_address);
// There was a range that began exactly prior to the to-be-freed region, so simply
// increment the size such that it occupies the grant too. If in addition there was a grant
// directly after the grant, include it too in the size.
if let Some((hole_offset, _hole_size)) = self
.holes_by_addr
.range(..start_address)
if let Some((hole_offset, hole_size)) = holes
.range_mut(..start_address)
.next_back()
.filter(|(offset, size)| offset.data() + **size == start_address.data())
.map(|(&offset, &size)| (offset, size))
{
self.resize_hole(
&hole_offset,
end_address.data() - hole_offset.data() + exactly_after_size.unwrap_or(0),
);
*hole_size = end_address.data() - hole_offset.data() + exactly_after_size.unwrap_or(0);
} else {
// There was no free region directly before the to-be-freed region, however will
// now unconditionally insert a new free region where the grant was, and add that extra
// size if there was something after it.
self.insert_hole(start_address, size + exactly_after_size.unwrap_or(0));
holes.insert(start_address, size + exactly_after_size.unwrap_or(0));
}
}
pub fn insert(&mut self, mut grant: Grant) {
@@ -1140,7 +1074,7 @@ impl UserGrants {
if (base..base.next_by(info.page_count())).contains(&page) {
let (base, info) = cursor.remove_prev().unwrap();
self.unreserve(base, info.page_count());
Self::unreserve(&mut self.holes, base, info.page_count());
Some(Grant { base, info })
} else {
None
@@ -1469,15 +1403,15 @@ impl Grant {
for dst_page in span.pages() {
let src_page = src.src_base.next_by(dst_page.offset_from(span.base));
let (frame, page_flags, is_cow) = match src.mode {
let (frame, is_cow) = match src.mode {
MmapMode::Shared => {
// TODO: Error code for "scheme responded with unmapped page"?
let (frame, page_flags) = match src_addrspace
let frame = match src_addrspace
.table
.utable
.translate(src_page.start_address())
{
Some((phys, page_flags)) => (Frame::containing(phys), page_flags),
Some((phys, _)) => Frame::containing(phys),
// TODO: ensure the correct context is hardblocked, if necessary
None => {
let (frame, _, new_guard) = correct_inner(
@@ -1488,26 +1422,20 @@ impl Grant {
0,
)
.map_err(|_| Error::new(EIO))?;
let page_flags = new_guard
.table
.utable
.translate(src_page.start_address())
.unwrap()
.1;
guard = new_guard;
(frame, page_flags)
frame
}
};
(frame, page_flags, false)
(frame, false)
}
MmapMode::Cow => unsafe {
let (frame, page_flags) = match guard
let frame = match guard
.table
.utable
.remap_with(src_page.start_address(), |flags| flags.write(false))
{
Some((page_flags, phys, _)) => (Frame::containing(phys), page_flags),
Some((_, phys, _)) => Frame::containing(phys),
// TODO: ensure the correct context is hardblocked, if necessary
None => {
let (frame, _, new_guard) = correct_inner(
@@ -1518,19 +1446,12 @@ impl Grant {
0,
)
.map_err(|_| Error::new(EIO))?;
// FIXME correct_inner should read the page flags instead
let page_flags = new_guard
.table
.utable
.translate(src_page.start_address())
.unwrap()
.1;
guard = new_guard;
(frame, page_flags)
frame
}
};
(frame, page_flags, true)
(frame, true)
},
};
src_addrspace = &mut *guard;
@@ -1590,14 +1511,7 @@ impl Grant {
.map_phys(
dst_page.start_address(),
frame.base(),
new_flags
.write(new_flags.has_write() && !is_cow)
// FIXME make sure this stays in sync with the MemoryType flags
.uncacheable(page_flags.has_flag(RmmA::ENTRY_FLAG_UNCACHEABLE))
.device_memory(page_flags.has_flag(RmmA::ENTRY_FLAG_DEVICE_MEMORY))
.write_combining(
page_flags.has_flag(RmmA::ENTRY_FLAG_WRITE_COMBINING),
),
new_flags.write(new_flags.has_write() && !is_cow),
)
.unwrap();
flush.ignore();
@@ -1991,7 +1905,6 @@ impl Grant {
mut self,
mapper: &mut PageMapper,
flusher: &mut impl GenericFlusher,
token: &mut CleanLockToken,
) -> UnmapResult {
assert!(self.info.mapped);
assert!(!self.info.is_pinned());
@@ -2002,6 +1915,9 @@ impl Grant {
..
} = self.info.provider
{
// TODO: Lock ordering violation
let mut token = unsafe { CleanLockToken::new() };
let mut token = token.token();
let mut guard = address_space.acquire_write(token.downgrade());
for (_, grant) in guard
@@ -2843,7 +2759,7 @@ pub struct BorrowedFmapSource<'a> {
pub addr_space_guard: RwLockWriteGuard<'a, L5, AddrSpace>,
}
pub fn handle_notify_files(notify_files: UnmapVec, token: &mut CleanLockToken) {
pub fn handle_notify_files(notify_files: Vec<UnmapResult>, token: &mut CleanLockToken) {
for file in notify_files {
let _ = file.unmap(token);
}