Merge upstream/master into submodule/kernel

Sync the kernel fork with 10 upstream commits (context timer-separation
with unblock_context/wakeup_context, dtb MMIO/bus-address translation
+ tests, event read_with_timeout support, proc wakeup paths, dup2
rewrite with rollback, futex Weak context_lock + unblock_context,
memory/init ordering, and more). Satisfies the verify-fork-functions
gate (11 previously-missing upstream functions now present).

Conflicts resolved (5 files; 9 auto-merged):

- src/syscall/futex.rs: took upstream's version wholesale (Weak
  context_lock, upgrade+unblock_context, TimeSpec::to_nanos,
  get_futex_stat, addr_space strong_count cleanup); re-applied the RB
  FUTEX_WAIT_MULTIPLE/FUTEX_REQUEUE implementations adapted to the
  Weak API, with wake-index bookkeeping preserved
  (Context::futex_wake_index survives in context.rs).

- src/event.rs: merged EventQueue with both RB's fd-refcount refs and
  upstream's timeout_opt (init in new()); kept RB's EventCounter/
  eventfd block; added upstream's read_with_timeout +
  EVENT_TIMEOUT_ID write handling; deduplicated EAGAIN/EINTR imports.

- src/scheme/proc.rs: upstream imports minus UnmapVec (RB deleted that
  machinery from context/memory.rs — import-only dependency);
  ContextHandle::Start keeps RB's require_zero_offset + upstream's
  wakeup_context; FORCEKILL region takes upstream's
  is_dead()/being_sigkilled form.

- src/syscall/fs.rs: took upstream's dup2 structure (natural
  self-dup-with-buf handling — preserves the relibc dup2(ft,ft,
  "refresh") semantic — plus rollback on insert failure), adapted to
  the fork's 4-arg duplicate_file and read/token_split conventions.

- src/arch/aarch64/start.rs: kept upstream's new PageMapper/
  acpi::init_before_mem/memory::init_mm init block.

Verified: repo cook kernel --force-rebuild successful;
verify-fork-functions.sh --no-fetch kernel passes.
This commit is contained in:
2026-07-19 07:02:19 +09:00
23 changed files with 574 additions and 209 deletions
+2
View File
@@ -1,3 +1,5 @@
target
/build
/config.toml
/sysroot
.gitlab-ci-local/
+13 -34
View File
@@ -1,8 +1,3 @@
image: "redoxos/redoxer:latest"
variables:
GIT_SUBMODULE_STRATEGY: recursive
workflow:
rules:
- if: '$CI_PROJECT_NAMESPACE == "redox-os"'
@@ -12,14 +7,12 @@ stages:
- build
- cross-build
- test
- other-features
# TODO: benchmarks and profiling (maybe manually enabled for relevant MRs)?
x86_64:
stage: build
script:
- mkdir -p target/${ARCH}
- redoxer env make BUILD=target/${ARCH}
- redoxer env make
variables:
ARCH: "x86_64"
@@ -27,64 +20,50 @@ aarch64:
stage: cross-build
image: "redoxos/redoxer:aarch64"
script:
- mkdir -p target/${ARCH}
- redoxer env make BUILD=target/${ARCH}
- redoxer env make
variables:
ARCH: "aarch64"
i586:
stage: cross-build
script:
- mkdir -p target/${ARCH}
- TARGET=${ARCH}-unknown-redox redoxer env make BUILD=target/${ARCH}
- TARGET=${ARCH}-unknown-redox redoxer env make
variables:
ARCH: "i586"
riscv64gc:
stage: cross-build
script:
- mkdir -p target/${ARCH}
- TARGET=${ARCH}-unknown-redox redoxer env make BUILD=target/${ARCH}
- TARGET=${ARCH}-unknown-redox redoxer env make
variables:
ARCH: "riscv64gc"
fmt:
stage: build
script:
- rustup component add rustfmt
- rustfmt --check
- redoxer env cargo fmt --check
x86_64:boot:
stage: test
needs: [x86_64]
script:
- mkdir -p target/${ARCH}
- export COOKBOOK_SOURCE_IDENT=$CI_COMMIT_SHA
- redoxer env make BUILD=target/${ARCH}
- timeout -s KILL 9m redoxer exec --folder target/${ARCH}/:/usr/lib/boot uname -a
variables:
ARCH: "x86_64"
- redoxer env make test
x86_64:relibc:
stage: test
needs: [x86_64]
# TODO: remove
allow_failure: true
script:
- redoxer pkg relibc-tests-bins
- export COOKBOOK_SOURCE_IDENT=$CI_COMMIT_SHA
- mkdir -p target/${TARGET}/sysroot/{usr/lib/boot,root} target/${TARGET}/root
- redoxer env make BUILD=target/${TARGET}/sysroot/usr/lib/boot
- (cd target/${TARGET}/sysroot && mv home/user/relibc-tests/* root/)
- timeout -s KILL 9m redoxer exec --folder target/${TARGET}/sysroot/:/ make run
# It is fine if failing sometimes
allow_failure: true
variables:
TARGET: "x86_64-unknown-redox"
- timeout -s KILL 9m redoxer env make test-relibc
profiling-compile:
stage: other-features
allow_failure: true
x86_64:profiling:
stage: build
needs: [x86_64]
script:
make check
- make check
variables:
ARCH: "x86_64"
KERNEL_CHECK_FEATURES: profiling
+36 -4
View File
@@ -2,7 +2,6 @@
.PHONY: all check
SOURCE:=$(dir $(realpath $(lastword $(MAKEFILE_LIST))))
BUILD?=$(CURDIR)
export RUST_TARGET_PATH=$(SOURCE)/targets
ifeq ($(TARGET),)
@@ -11,6 +10,9 @@ else
ARCH?=$(shell echo "$(TARGET)" | cut -d - -f1)
endif
BUILD?=$(CURDIR)/build/$(ARCH)
DESTDIR?=./sysroot
ifeq ($(ARCH),riscv64gc)
override ARCH:=riscv64
GNU_TARGET=riscv64-unknown-redox
@@ -21,6 +23,7 @@ else
GNU_TARGET=$(ARCH)-unknown-redox
endif
OBJCOPY?=$(GNU_TARGET)-objcopy
all: $(BUILD)/kernel $(BUILD)/kernel.sym
@@ -31,7 +34,10 @@ TARGET_SPEC=$(RUST_TARGET_PATH)/$(ARCH)-unknown-kernel.json
KERNEL_CARGO_FEATURES?=
$(BUILD)/kernel.all: $(LD_SCRIPT) $(LOCKFILE) $(MANIFEST) $(TARGET_SPEC) $(shell find $(SOURCE) -name "*.rs" -type f)
$(BUILD):
mkdir -p "$@"
$(BUILD)/kernel.all: $(LD_SCRIPT) $(LOCKFILE) $(MANIFEST) $(TARGET_SPEC) $(shell find $(SOURCE) -name "*.rs" -type f) | $(BUILD)
cargo rustc \
--bin kernel \
--manifest-path "$(MANIFEST)" \
@@ -45,13 +51,13 @@ $(BUILD)/kernel.all: $(LD_SCRIPT) $(LOCKFILE) $(MANIFEST) $(TARGET_SPEC) $(shell
--emit link="$(BUILD)/kernel.all"
$(BUILD)/kernel.sym: $(BUILD)/kernel.all
$(GNU_TARGET)-objcopy \
$(OBJCOPY) \
--only-keep-debug \
"$(BUILD)/kernel.all" \
"$(BUILD)/kernel.sym"
$(BUILD)/kernel: $(BUILD)/kernel.all
$(GNU_TARGET)-objcopy \
$(OBJCOPY) \
--strip-debug \
"$(BUILD)/kernel.all" \
"$(BUILD)/kernel"
@@ -65,3 +71,29 @@ check:
--target "$(TARGET_SPEC)" \
-Z build-std=core,alloc -Zbuild-std-features=compiler-builtins-mem -Z json-target-spec \
--features=$(KERNEL_CHECK_FEATURES)
clean:
rm -rf build sysroot target config.toml
install: all
@mkdir -pv "$(DESTDIR)/usr/lib/boot/"
cp -v $(BUILD)/kernel.all "$(DESTDIR)/usr/lib/boot/"
cp -v $(BUILD)/kernel.sym "$(DESTDIR)/usr/lib/boot/"
cp -v $(BUILD)/kernel "$(DESTDIR)/usr/lib/boot/"
# test if booting
# to ensure it's using this kernel, set COOKBOOK_SOURCE_IDENT env before build
test: all
$(MAKE) install
REDOXER_SYSROOT=$(DESTDIR) redoxer exec uname -a
# test with interactive gui
test-gui: all
$(MAKE) install
REDOXER_SYSROOT=$(DESTDIR) redoxer exec --gui ion
# test with relibc tests
test-relibc: all
$(MAKE) install
REDOXER_SYSROOT=$(DESTDIR) redoxer pkg relibc-tests-bins
REDOXER_SYSROOT=$(DESTDIR) redoxer exec relibc-tests-runner
+19 -7
View File
@@ -6,6 +6,7 @@ use core::ptr::NonNull;
use alloc::{boxed::Box, string::String, vec::Vec};
use hashbrown::HashMap;
use rmm::{BumpAllocator, FrameAllocator, PageMapper};
use spin::{Once, RwLock};
use crate::{
@@ -33,7 +34,11 @@ mod spcr;
pub mod srat;
mod xsdt;
unsafe fn map_linearly(addr: PhysicalAddress, len: usize, mapper: &mut crate::memory::PageMapper) {
unsafe fn map_linearly(
addr: PhysicalAddress,
len: usize,
mapper: &mut PageMapper<RmmA, impl FrameAllocator>,
) {
unsafe {
let base = PhysicalAddress::new(crate::memory::round_down_pages(addr.data()));
let aligned_len = crate::memory::round_up_pages(len + (addr.data() - base.data()));
@@ -50,7 +55,10 @@ unsafe fn map_linearly(addr: PhysicalAddress, len: usize, mapper: &mut crate::me
}
}
pub fn get_sdt(sdt_address: PhysicalAddress, mapper: &mut KernelMapper<true>) -> &'static Sdt {
pub fn get_sdt(
sdt_address: PhysicalAddress,
mapper: &mut PageMapper<RmmA, impl FrameAllocator>,
) -> &'static Sdt {
let sdt;
unsafe {
@@ -93,16 +101,20 @@ impl Rxsdt for RxsdtEnum {
pub static RXSDT_ENUM: Once<RxsdtEnum> = Once::new();
/// Initialses the global `RXSDT_ENUM` if RSDT or XSDT was found and maps the SDT pages.
/// It does not perform any allocations
pub unsafe fn init_before_mem(already_supplied_rsdp: Option<NonNull<u8>>) {
/// Initialises the global `RXSDT_ENUM` if RSDT or XSDT was found and maps the SDT pages.
///
/// It does not use `TheFrameAllocator` nor does it heap-allocate.
pub unsafe fn init_before_mem(
already_supplied_rsdp: Option<NonNull<u8>>,
mapper: &mut PageMapper<RmmA, BumpAllocator<RmmA>>,
) {
unsafe {
// Search for RSDP
let rsdp_opt = Rsdp::get_rsdp(already_supplied_rsdp);
if let Some(rsdp) = rsdp_opt {
debug!("SDT address: {:#x}", rsdp.sdt_address().data());
let rxsdt = get_sdt(rsdp.sdt_address(), &mut KernelMapper::lock_rw());
let rxsdt = get_sdt(rsdp.sdt_address(), mapper);
let rxsdt = if let Some(rsdt) = Rsdt::new(rxsdt) {
let mut initialized = false;
@@ -139,7 +151,7 @@ pub unsafe fn init_before_mem(already_supplied_rsdp: Option<NonNull<u8>>) {
// TODO: Don't touch ACPI tables in kernel?
for sdt in rxsdt.iter() {
get_sdt(sdt, &mut KernelMapper::lock_rw());
get_sdt(sdt, mapper);
}
} else {
error!("NO RSDP FOUND");
+2 -2
View File
@@ -1,8 +1,8 @@
use super::InterruptController;
use crate::{
dtb::{
get_mmio_address,
irqchip::{InterruptHandler, IrqCell, IrqDesc},
translate_mmio_address,
},
sync::CleanLockToken,
};
@@ -60,7 +60,7 @@ impl GenericInterruptController {
if chunk.size.is_none() {
break;
}
let addr = get_mmio_address(fdt, node, &chunk).unwrap();
let addr = translate_mmio_address(fdt, node, &chunk).unwrap();
match idx {
0 => (regs.0, regs.1) = (addr, chunk.size.unwrap()),
2 => (regs.2, regs.3) = (addr, chunk.size.unwrap()),
+3 -3
View File
@@ -5,8 +5,8 @@ use fdt::{node::NodeProperty, Fdt};
use super::{gic::GicDistIf, InterruptController};
use crate::{
dtb::{
get_mmio_address,
irqchip::{InterruptHandler, IrqCell, IrqDesc},
translate_mmio_address,
},
sync::CleanLockToken,
};
@@ -53,7 +53,7 @@ impl GicV3 {
// Read registers
let mut chunks = node.reg().unwrap();
if let Some(gicd) = chunks.next()
&& let Some(addr) = get_mmio_address(fdt, &node, &gicd)
&& let Some(addr) = translate_mmio_address(fdt, &node, &gicd)
{
unsafe {
self.gic_dist_if.init(crate::PHYS_OFFSET + addr);
@@ -62,7 +62,7 @@ impl GicV3 {
for _ in 0..gicrs {
if let Some(gicr) = chunks.next() {
self.gicrs.push((
get_mmio_address(fdt, &node, &gicr).unwrap(),
translate_mmio_address(fdt, &node, &gicr).unwrap(),
gicr.size.unwrap(),
));
}
+9
View File
@@ -100,6 +100,15 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! {
// Initialize paging
paging::init();
let mut mapper = rmm::PageMapper::current(rmm::TableKind::Kernel, bump_allocator);
#[cfg(feature = "acpi")]
{
use crate::acpi;
acpi::init_before_mem(args.acpi_rsdp(), &mut mapper);
}
crate::memory::init_mm(mapper.allocator_mut());
crate::arch::misc::init(crate::cpu_set::LogicalCpuId::new(0));
// Setup kernel heap
+3 -1
View File
@@ -105,7 +105,7 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! {
}
// Initialize RMM
crate::startup::memory::init(&args, None, None);
let mut bump_allocator = crate::startup::memory::init(&args, None, None);
let boot_hart_id =
get_boot_hart_id(args.env()).expect("Didn't get boot HART id from bootloader");
@@ -114,6 +114,8 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! {
paging::init();
crate::memory::init_mm(&mut bump_allocator);
crate::arch::misc::init(crate::cpu_set::LogicalCpuId::new(0));
// Setup kernel heap
+6 -3
View File
@@ -4,6 +4,8 @@
//! defined in other files inside of the `arch` module
use core::{arch::naked_asm, cell::SyncUnsafeCell, mem::offset_of};
use rmm::PageMapper;
use crate::{
allocator,
arch::{device, gdt, idt, interrupt, paging},
@@ -112,13 +114,14 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
// Initialize paging
paging::init();
let mut mapper = PageMapper::current(rmm::TableKind::Kernel, bump_allocator);
if cfg!(feature = "acpi") {
crate::acpi::init_before_mem(args.acpi_rsdp());
crate::acpi::init_before_mem(args.acpi_rsdp(), &mut mapper);
}
numa::init(&mut bump_allocator);
numa::init(mapper.allocator_mut());
crate::memory::init_mm(bump_allocator);
crate::memory::init_mm(mapper.allocator_mut());
#[cfg(target_arch = "x86_64")]
crate::arch::alternative::early_init(true);
-1
View File
@@ -333,7 +333,6 @@ impl Context {
pub fn unblock_no_ipi(&mut self) -> bool {
if self.status.is_soft_blocked() {
self.status = Status::Runnable;
self.wake = None;
self.status_reason = "";
true
+37 -4
View File
@@ -9,13 +9,17 @@ use alloc::{
use core::{cmp::Reverse, num::NonZeroUsize, ops::Deref};
use crate::{
context::memory::AddrSpaceWrapper,
context::{
memory::AddrSpaceWrapper,
switch::{BASE_SLICE_TICKS, NANOS_PER_TICK, SCALE, SCHED_PRIO_TO_WEIGHT, TICK_INTERVAL},
},
cpu_set::LogicalCpuSet,
ipi::{ipi, IpiKind, IpiTarget},
memory::{RmmA, RmmArch, TableKind},
percpu::PercpuBlock,
sync::{
ArcRwLockWriteGuard, CleanLockToken, LockToken, Mutex, MutexGuard, RwLock, RwLockReadGuard,
RwLockWriteGuard, L0, L1, L2, L4,
RwLockWriteGuard, L0, L1, L2, L3, L4,
},
syscall::error::Result,
};
@@ -83,7 +87,7 @@ static IDLE_CONTEXTS: Mutex<L2, VecDeque<WeakContextRef>> = Mutex::new(VecDeque:
pub struct RunContextData {
// queue: VecDeque<WeakContextRef>,
queue: BTreeMap<(u64, Reverse<u64>, u32), (u64, u64, WeakContextRef)>, // ((vd, rem_slice, ctxt_id), (vtime, weight, context))
timers: BTreeSet<(u128, WeakContextRef)>, // (wake, context)
timers: BTreeSet<(u128, WeakContextRef)>, // (wake, context)
count: usize,
v: u64,
total_weight: u64,
@@ -135,6 +139,36 @@ pub fn run_contexts_try(token: LockToken<'_, L0>) -> Option<MutexGuard<'_, L1, R
RUN_CONTEXTS.try_lock(token)
}
pub fn unblock_context(context_lock: &Arc<ContextLock>, token: &mut LockToken<'_, L3>) -> bool {
let cpu_id = {
let mut guard = context_lock.write(token.token());
if !guard.unblock_no_ipi() {
if guard.status.is_runnable() {
// already set to runnable externally
wakeup_context(context_lock);
}
return false;
}
guard.cpu_id
};
wakeup_context(context_lock);
if let Some(cpu_id) = cpu_id
&& cpu_id != crate::cpu_id()
{
ipi(IpiKind::Wakeup, IpiTarget::Other);
}
true
}
pub fn wakeup_context(context_lock: &Arc<ContextLock>) {
let percpu: &PercpuBlock = PercpuBlock::current();
let weak = WeakContextRef(Arc::downgrade(context_lock));
percpu.switch_internals.wakeup_list.borrow_mut().push(weak);
}
pub fn init(token: &mut CleanLockToken) {
let owner = None; // kmain not owned by any fd
let mut context = Context::new(owner).expect("failed to create kmain context");
@@ -261,7 +295,6 @@ pub fn spawn(
let context_lock = Arc::new(ContextLock::new(context));
let context_ref = ContextRef(Arc::clone(&context_lock));
let run_ref = WeakContextRef(Arc::downgrade(&context_ref.0));
idle_contexts(token.downgrade()).push_back(run_ref);
contexts_mut(token.downgrade()).insert(context_ref);
Ok(context_lock)
+34 -11
View File
@@ -13,7 +13,7 @@ use crate::{
percpu::PercpuBlock,
sync::{ArcRwLockWriteGuard, CleanLockToken, L4},
};
use alloc::sync::Arc;
use alloc::{sync::Arc, vec::Vec};
use core::{
cell::{Cell, RefCell},
cmp::Reverse,
@@ -32,16 +32,16 @@ enum UpdateResult {
}
// A simple geometric series where value[i] ~= value[i + 1] * 1.25
const SCHED_PRIO_TO_WEIGHT: [usize; 40] = [
pub const SCHED_PRIO_TO_WEIGHT: [usize; 40] = [
88761, 71755, 56483, 46273, 36291, 29154, 23254, 18705, 14949, 11916, 9548, 7620, 6100, 4904,
3906, 3121, 2501, 1991, 1586, 1277, 1024, 820, 655, 526, 423, 335, 272, 215, 172, 137, 110, 87,
70, 56, 45, 36, 29, 23, 18, 15,
];
const SCALE: u128 = 1 << 40;
const TICK_INTERVAL: u64 = 3; // Approx 6.75 ms
const BASE_SLICE_TICKS: u64 = TICK_INTERVAL * 3; // Approx 20.25 ms
const NANOS_PER_TICK: u128 = 2_250_000; // 2.25 ms
pub const SCALE: u128 = 1 << 40;
pub const TICK_INTERVAL: u64 = 3; // Approx 6.75 ms
pub const BASE_SLICE_TICKS: u64 = TICK_INTERVAL * 3; // Approx 20.25 ms
pub const NANOS_PER_TICK: u128 = 2_250_000; // 2.25 ms
/// Determines if a given context is eligible to be scheduled on a given CPU (in
/// principle, the current CPU).
@@ -190,6 +190,8 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
let mut wakeups = wakeup_contexts(token);
let mut push_idle: SmallVec<[WeakContextRef; 16]> = SmallVec::new();
// These timers coukd have expired
let mut timers: SmallVec<[(u128, WeakContextRef); 16]> = SmallVec::new();
if let Some(mut run_contexts) = run_contexts_try(token.token()) {
// Pop Timers
while let Some((wake, _)) = run_contexts.timers.first() {
@@ -197,12 +199,31 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
break;
}
if let Some((_, context_ref)) = run_contexts.timers.pop_first() {
wakeups.push(context_ref);
if let Some(entry) = run_contexts.timers.pop_first() {
timers.push(entry);
}
}
}
for (wake, context_ref) in timers {
let Some(context_lock) = context_ref.upgrade() else {
continue;
};
let guard = context_lock.read(token.token());
if guard.status.is_soft_blocked() && guard.wake == Some(wake) {
wakeups.push(context_ref);
}
}
// Drain from percpu
{
let mut percpu_wake = percpu.switch_internals.wakeup_list.borrow_mut();
for context_ref in percpu_wake.drain(..) {
wakeups.push(context_ref.clone());
}
}
if wakeups.len() > 0 {
let mut run_contexts = run_contexts(token.token());
for context_ref in wakeups {
@@ -388,9 +409,7 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
}
}
fn wakeup_contexts(
token: &mut CleanLockToken,
) -> SmallVec<[WeakContextRef; 16]> {
fn wakeup_contexts(token: &mut CleanLockToken) -> SmallVec<[WeakContextRef; 16]> {
// TODO: Optimise this somehow
let mut wakeups = SmallVec::new();
let current_context = context::current();
@@ -687,6 +706,9 @@ pub struct ContextSwitchPercpu {
/// The idle process.
idle_ctxt: RefCell<Option<Arc<ContextLock>>>,
pub(crate) being_sigkilled: Cell<bool>,
// wakeups
pub(crate) wakeup_list: RefCell<Vec<WeakContextRef>>,
}
impl ContextSwitchPercpu {
@@ -698,6 +720,7 @@ impl ContextSwitchPercpu {
current_ctxt: RefCell::new(None),
idle_ctxt: RefCell::new(None),
being_sigkilled: Cell::new(false),
wakeup_list: RefCell::new(Vec::new()),
#[cfg(feature = "profiling")]
current_dbg_id: core::sync::atomic::AtomicU32::new(!0),
+187 -43
View File
@@ -102,53 +102,145 @@ pub fn register_dev_memory_ranges(dt: &Fdt) {
}
}
let Some(soc_node) = dt.find_node("/soc") else {
warn!("failed to find /soc in devicetree");
return;
};
let Some(reg) = soc_node.ranges() else {
warn!("devicetree /soc has no ranges");
return;
};
for chunk in reg {
debug!(
"dev mem 0x{:08x} 0x{:08x} 0x{:08x} 0x{:08x}",
chunk.child_bus_address_hi,
chunk.child_bus_address,
chunk.parent_bus_address,
chunk.size
);
if let Some(soc_node) = dt.find_node("/soc") {
if let Some(reg) = soc_node.ranges() {
for chunk in reg {
debug!(
"dev mem 0x{:08x} 0x{:08x} 0x{:08x} 0x{:08x}",
chunk.child_bus_address_hi,
chunk.child_bus_address,
chunk.parent_bus_address,
chunk.size
);
/*TODO: soc memory may contain all free memory!
register_memory_region(
chunk.parent_bus_address,
chunk.size,
BootloaderMemoryKind::Device,
);*/
}
/*TODO: soc memory may contain all free memory!
register_memory_region(
chunk.parent_bus_address,
chunk.size,
BootloaderMemoryKind::Device,
);*/
}
} else {
warn!("devicetree /soc has no ranges");
}
// also add all soc-internal devices because they might not be shown in ranges
// (identity-mapped soc bus may have empty ranges)
for device in soc_node.children() {
if let Some(reg) = device.reg() {
for entry in reg {
if let Some(size) = entry.size {
let addr = entry.starting_address as usize;
if let Some(mapped_addr) = get_mmio_address(dt, &device, &entry) {
debug!(
"soc device {} 0x{:08x} -> 0x{:08x} size 0x{:08x}",
device.name, addr, mapped_addr, size
);
register_memory_region(mapped_addr, size, BootloaderMemoryKind::Device);
// also add direct /soc children because they might not be shown in
// ranges (an identity-mapped bus may have empty ranges)
for device in soc_node.children() {
if let Some(reg) = device.reg() {
for entry in reg {
if let Some(size) = entry.size {
let addr = entry.starting_address as usize;
if let Some(mapped_addr) = get_mmio_address(dt, &device, &entry) {
debug!(
"soc device {} 0x{:08x} -> 0x{:08x} size 0x{:08x}",
device.name, addr, mapped_addr, size
);
register_memory_region(mapped_addr, size, BootloaderMemoryKind::Device);
}
}
}
}
}
} else {
warn!("failed to find /soc in devicetree");
}
// The selected console may be below a nested bus whose own `reg` range
// does not cover all children. Register the exact translated range so it
// remains mapped after the boot-time identity map is replaced.
if let Some((address, size, _, _, _)) = diag_uart_range(dt) {
debug!(
"diagnostic UART 0x{:08x} size 0x{:08x}",
address.data(),
size
);
register_memory_region(address.data(), size, BootloaderMemoryKind::Device);
}
// Interrupt controllers are not required to live below /soc. Some
// devicetrees place the primary GIC directly below the root node, so its
// register ranges would otherwise be absent from the kernel physmap.
if let Some(root) = dt.find_node("/") {
for controller in root
.children()
.filter(|node| node.property("interrupt-controller").is_some())
{
let Some(regions) = controller.reg() else {
continue;
};
for region in regions {
let Some(size) = region.size else {
continue;
};
let Some(address) = translate_mmio_address(dt, &controller, &region) else {
continue;
};
debug!(
"root interrupt controller {} 0x{:08x} size 0x{:08x}",
controller.name, address, size
);
register_memory_region(address, size, BootloaderMemoryKind::Device);
}
}
}
}
// FIXME return PhysicalAddress
pub fn get_mmio_address(fdt: &Fdt, _device: &FdtNode, region: &MemoryRegion) -> Option<usize> {
fn same_node(left: FdtNode<'_, '_>, right: FdtNode<'_, '_>) -> bool {
if left.name != right.name {
return false;
}
// FdtNode is reconstructed while walking the tree and has no identity
// operation. A property's value is a slice into the original DTB, so equal
// `reg` slice pointers identify the same node occurrence without relying
// on names that may repeat below different buses.
match (left.property("reg"), right.property("reg")) {
(Some(left_reg), Some(right_reg)) => core::ptr::eq(left_reg.value, right_reg.value),
_ => false,
}
}
fn translate_bus_address(bus: FdtNode<'_, '_>, address: usize, size: usize) -> Option<usize> {
let ranges_property = bus.property("ranges")?;
if ranges_property.value.is_empty() {
return Some(address);
}
let last_offset = size.saturating_sub(1);
let ranges = bus.ranges()?;
for range in ranges {
let Some(offset) = address.checked_sub(range.child_bus_address) else {
continue;
};
if offset < range.size && last_offset < range.size - offset {
return range.parent_bus_address.checked_add(offset);
}
}
None
}
fn translate_from_subtree(
bus_or_device: FdtNode<'_, '_>,
target: FdtNode<'_, '_>,
address: usize,
size: usize,
) -> Option<usize> {
if same_node(bus_or_device, target) {
return Some(address);
}
for child in bus_or_device.children() {
if let Some(child_address) = translate_from_subtree(child, target, address, size) {
// `bus_or_device` is the bus parent of the subtree that matched.
return translate_bus_address(bus_or_device, child_address, size);
}
}
None
}
/// Translate a device's bus-relative `reg` address through every ancestor's
/// `ranges` property until it reaches the CPU physical address space.
pub fn translate_mmio_address(fdt: &Fdt, device: &FdtNode, region: &MemoryRegion) -> Option<usize> {
/* DT spec 2.3.8 "ranges":
* The ranges property provides a means of defining a mapping or translation between
* the address space of the bus (the child address space) and the address space of the bus
@@ -159,15 +251,32 @@ pub fn get_mmio_address(fdt: &Fdt, _device: &FdtNode, region: &MemoryRegion) ->
* children of the node and the parent address space.
*/
// FIXME assumes all the devices are connected to CPUs via the /soc bus
let address = region.starting_address as usize;
let size = region.size.unwrap_or(1);
let root = fdt.find_node("/")?;
// The root is already the CPU address space, so only its children perform
// translations. This also supports devices that are not under `/soc`.
for child in root.children() {
if let Some(translated) = translate_from_subtree(child, *device, address, size) {
translated.checked_add(size.saturating_sub(1))?;
return Some(translated);
}
}
None
}
// FIXME return PhysicalAddress
pub fn get_mmio_address(fdt: &Fdt, _device: &FdtNode, region: &MemoryRegion) -> Option<usize> {
let mut mapped_addr = region.starting_address as usize;
let size = region.size.unwrap_or(0).saturating_sub(1);
let last_address = mapped_addr.saturating_add(size);
if let Some(parent) = fdt.find_node("/soc") {
let mut ranges = parent.ranges().map(|f| f.peekable())?;
let mut ranges = parent.ranges().map(|ranges| ranges.peekable())?;
if ranges.peek().is_some() {
let parent_range = ranges.find(|x| {
x.child_bus_address <= mapped_addr && last_address - x.child_bus_address <= x.size
let parent_range = ranges.find(|range| {
range.child_bus_address <= mapped_addr
&& last_address - range.child_bus_address <= range.size
})?;
mapped_addr = parent_range
.parent_bus_address
@@ -219,7 +328,7 @@ pub fn diag_uart_range<'a>(dtb: &'a Fdt) -> Option<(PhysicalAddress, usize, bool
let mut reg = uart_node.reg()?;
let memory = reg.next()?;
let address = get_mmio_address(dtb, &uart_node, &memory)?;
let address = translate_mmio_address(dtb, &uart_node, &memory)?;
Some((
PhysicalAddress::new(address),
@@ -244,3 +353,38 @@ pub fn fill_env_data(dt: &Fdt, env_base: usize) -> usize {
0
}
}
#[cfg(test)]
mod tests {
use super::translate_mmio_address;
use fdt::Fdt;
static MMIO_DTB: &[u8] = include_bytes!("testdata/mmio.dtb");
fn translated(path: &str) -> Option<usize> {
let fdt = Fdt::new(MMIO_DTB).unwrap();
let node = fdt.find_node(path).unwrap();
let region = node.reg().unwrap().next().unwrap();
translate_mmio_address(&fdt, &node, &region)
}
#[test]
fn translates_uart_through_nested_and_empty_ranges() {
assert_eq!(translated("/soc/bus@1000/serial@200"), Some(0x1200));
}
#[test]
fn accepts_region_ending_exactly_at_range_boundary() {
assert_eq!(translated("/soc/bus@1000/device@ff0"), Some(0x1ff0));
}
#[test]
fn rejects_region_crossing_range_boundary() {
assert_eq!(translated("/soc/bus@1000/device@ff1"), None);
}
#[test]
fn empty_ranges_is_identity_mapping() {
assert_eq!(translated("/identity-bus/device@3000"), Some(0x3000));
}
}
BIN
View File
Binary file not shown.
+48
View File
@@ -0,0 +1,48 @@
/dts-v1/;
/ {
#address-cells = <2>;
#size-cells = <2>;
chosen {
stdout-path = "/soc/bus@1000/serial@200:115200n8";
};
soc {
compatible = "simple-bus";
#address-cells = <2>;
#size-cells = <2>;
ranges;
bus@1000 {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x0 0x1000 0x0 0x1000>;
ranges = <0x0 0x0 0x1000 0x1000>;
serial@200 {
reg = <0x200 0x18>;
};
device@ff0 {
reg = <0xff0 0x10>;
};
device@ff1 {
reg = <0xff1 0x10>;
};
};
};
identity-bus {
compatible = "simple-bus";
#address-cells = <2>;
#size-cells = <2>;
ranges;
device@3000 {
reg = <0x0 0x3000 0x0 0x20>;
};
};
};
+58 -1
View File
@@ -2,7 +2,7 @@ use alloc::sync::Arc;
use core::sync::atomic::{AtomicUsize, Ordering};
use hashbrown::{hash_map::DefaultHashBuilder, HashMap};
use smallvec::SmallVec;
use syscall::data::GlobalSchemes;
use syscall::{data::GlobalSchemes, EAGAIN, EINTR};
use crate::{
context,
@@ -29,6 +29,9 @@ pub struct EventQueue {
/// handle to the SAME queue; the queue must survive until the last fd
/// closes. Starts at 1 for the fd created by `open`.
refs: AtomicUsize,
/// Pending EVENT_TIMEOUT_ID timeout recorded by `write()` and consumed
/// by `read_with_timeout()` (upstream event-timeout support).
timeout_opt: Mutex<L1, Option<usize>>,
}
const EVENTFD_COUNTER_MAX: u64 = u64::MAX - 1;
@@ -48,6 +51,7 @@ impl EventQueue {
id,
queue: WaitQueue::new(),
refs: AtomicUsize::new(1),
timeout_opt: Mutex::new(None),
}
}
@@ -71,8 +75,61 @@ impl EventQueue {
.receive_into_user(buf, block, "EventQueue::read", token)
}
pub fn read_with_timeout(
&self,
buf: UserSliceWo,
timeout: usize,
token: &mut CleanLockToken,
) -> Result<usize> {
// if zero, instant timeout
let block = timeout > 0;
if block {
let mut time = crate::time::monotonic(token);
time += timeout as u128 * 1_000_000;
context::current().write(token.token()).wake = Some(time);
}
let r = self
.queue
.receive_into_user(buf, block, "EventQueue::read_with_timeout", token);
match (r, block) {
(Ok(r), _) => return Ok(r),
(err @ Err(Error { errno: EINTR }), true) => {
let old_wake = context::current().write(token.token()).wake.take();
// The scheduler clears `wake` on timeout
if !old_wake.is_none() {
return err;
}
// proceed writing timeout
}
(Err(Error { errno: EAGAIN }), false) => {
// proceed writing timeout
}
(err, _) => return err,
};
// TODO: let the scheduler write this for us?
let event = Event {
id: syscall::EVENT_TIMEOUT_ID,
// TODO: it's undefined when flags is written to EVENT_TIMEOUT_ID
flags: EventFlags::EVENT_READ,
data: timeout,
};
let bytes_copied = buf.copy_common_bytes_from_slice(&event)?;
return Ok(bytes_copied);
}
pub fn write(&self, events: &[Event], token: &mut CleanLockToken) -> Result<usize> {
for event in events {
if event.id == syscall::EVENT_TIMEOUT_ID {
if event.flags.is_empty() {
self.timeout_opt.lock(token.token()).take();
} else {
*self.timeout_opt.lock(token.token()) = Some(event.data);
}
continue;
}
let file = {
let context_ref = context::current();
let mut context = context_ref.read(token.token());
+24 -18
View File
@@ -74,30 +74,36 @@ pub fn total_frames() -> usize {
/// Allocate a range of frames
pub fn allocate_p2frame(order: u32) -> Option<Frame> {
let initial_index = get_round_robin_index();
let mut index = initial_index;
static RR_INDEX: Mutex<u8> = Mutex::new(0);
let len = FREE_LISTS.get().unwrap().len();
loop {
if let Some(frame) = allocate_p2frame_complex(order, (), None, order, index).map(|(f, _)| f)
{
return Some(frame);
}
index = get_round_robin_index();
if index == initial_index {
return None;
if len == 1 {
return allocate_p2frame_complex(order, (), None, order, 0).map(|e| e.0);
}
let index = {
let mut lock = RR_INDEX.lock();
let index =
usize::try_from(*lock).expect("Maximum number of memory regions supported is 128");
*lock = u8::try_from((index + 1) % len).expect("Maximum number of memory regions is 128");
index
};
for i in index..len {
if let Some(frame) = allocate_p2frame_complex(order, (), None, order, i) {
return Some(frame.0);
}
}
for i in 0..index {
if let Some(frame) = allocate_p2frame_complex(order, (), None, order, i) {
return Some(frame.0);
}
}
None
}
pub fn allocate_frame() -> Option<Frame> {
allocate_p2frame(0)
}
fn get_round_robin_index() -> usize {
static CURRENT_INDEX: AtomicUsize = AtomicUsize::new(0);
let len = FREE_LISTS.get().unwrap().len();
CURRENT_INDEX.fetch_add(1, Ordering::Relaxed) % len
}
// TODO: Flags, strategy
pub fn allocate_p2frame_complex(
_req_order: u32,
@@ -543,7 +549,7 @@ const _: () = {
};
#[cold]
fn init_sections(mut allocator: BumpAllocator<RmmA>) {
fn init_sections(mut allocator: &mut BumpAllocator<RmmA>) {
let number_of_memory_regions = numa::number_of_memory_regions();
let (free_areas, offset_into_first_free_area) = allocator.free_areas();
@@ -952,7 +958,7 @@ fn init_sections(mut allocator: BumpAllocator<RmmA>) {
}
#[cold]
pub fn init_mm(allocator: BumpAllocator<RmmA>) {
pub fn init_mm(allocator: &mut BumpAllocator<RmmA>) {
init_sections(allocator);
unsafe {
+14 -10
View File
@@ -3,8 +3,8 @@ use crate::{
self,
context::{HardBlockedReason, LockedFdTbl, SignalState},
file::InternalFlags,
memory::{handle_notify_files, AddrSpace, AddrSpaceWrapper, Grant, PageSpan},
Context, ContextLock, Status,
memory::{handle_notify_files, AddrSpace, AddrSpaceWrapper, Grant, PageSpan, UnmapVec},
unblock_context, wakeup_context, Context, ContextLock, Status,
},
memory::{Page, VirtualAddress, PAGE_SIZE},
ptrace,
@@ -1580,6 +1580,7 @@ impl ContextHandle {
reason: HardBlockedReason::NotYetStarted,
} => {
*status = Status::Runnable;
wakeup_context(&context);
Ok(buf.len())
}
_ => Err(Error::new(EINVAL)),
@@ -1747,12 +1748,12 @@ impl ContextHandle {
} = guard.status
{
guard.status = Status::Runnable;
wakeup_context(&context);
}
Ok(size_of::<usize>())
}
ContextVerb::Interrupt => {
let mut guard = context.write(token.token());
guard.unblock();
unblock_context(&context, &mut token.token().downgrade());
Ok(size_of::<usize>())
}
ContextVerb::ForceKill => {
@@ -1783,13 +1784,16 @@ impl ContextHandle {
}
crate::syscall::exit_this_context(None, token);
} else {
let mut ctxt = context.write(token.token());
//trace!("FORCEKILL NONSELF={} {}, SELF={}", ctxt.debug_id, ctxt.pid, context::current().read().debug_id);
if let context::Status::Dead { .. } = ctxt.status {
return Ok(size_of::<usize>());
{
let mut ctxt = context.write(token.token());
//trace!("FORCEKILL NONSELF={} {}, SELF={}", ctxt.debug_id, ctxt.pid, context::current().read().debug_id);
if ctxt.status.is_dead() {
return Ok(size_of::<usize>());
}
ctxt.status = context::Status::Runnable;
ctxt.being_sigkilled = true;
}
ctxt.status = context::Status::Runnable;
ctxt.being_sigkilled = true;
wakeup_context(&context);
Ok(size_of::<usize>())
}
}
+8 -5
View File
@@ -22,10 +22,12 @@ use crate::{
AddrSpace, AddrSpaceWrapper, BorrowedFmapSource, Grant, GrantFileRef, MmapMode,
PageSpan, DANGLING,
},
BorrowedHtBuf, ContextLock, PreemptGuard, PreemptGuardL1, Status,
unblock_context, wakeup_context, BorrowedHtBuf, ContextLock, PreemptGuard, PreemptGuardL1,
Status, WeakContextRef,
},
event,
memory::{Frame, Page, VirtualAddress, PAGE_SIZE},
percpu::PercpuBlock,
scheme::SchemeId,
sync::{CleanLockToken, LockToken, Mutex, RwLock, WaitQueue, L1},
syscall::{
@@ -894,7 +896,7 @@ impl UserInner {
}
};
let context = context.upgrade().ok_or(Error::new(ESRCH))?;
let context_lock = context.upgrade().ok_or(Error::new(ESRCH))?;
let mut lock_token = token.token();
let (frame, _) = AddrSpace::current()?
@@ -905,12 +907,13 @@ impl UserInner {
.ok_or(Error::new(EFAULT))?;
{
let mut context = context.write(token.token());
let mut context = context_lock.write(token.token());
if let Status::HardBlocked {
reason: HardBlockedReason::AwaitingMmap { .. },
} = context.status
{
context.status = Status::Runnable
context.status = Status::Runnable;
wakeup_context(&context_lock);
}
context.fmap_ret = Some(Frame::containing(frame));
}
@@ -982,7 +985,7 @@ impl UserInner {
match context.upgrade() {
Some(context) => {
*o = State::Responded(response);
context.write(lock_token.token()).unblock();
unblock_context(&context, &mut lock_token.token().downgrade());
}
_ => {
states.remove(tag as usize);
+4 -2
View File
@@ -7,7 +7,7 @@ use core::{
use crate::{
arch::interrupt,
context::{self, switch::SwitchResult},
context::{self, switch::SwitchResult, wakeup_context, Status},
memory::{PhysicalAddress, RmmA, RmmArch},
profiling, scheme,
sync::CleanLockToken,
@@ -175,13 +175,15 @@ pub(crate) fn kmain(bootstrap: Bootstrap) -> ! {
match context::spawn(true, owner, userspace_init, &mut token) {
Ok(context_lock) => {
let mut context = context_lock.write(token.token());
context.status = context::Status::Runnable;
context.status = Status::Runnable;
context.name.clear();
context.name.push_str("[bootstrap]");
// TODO: Remove these from kernel
context.euid = 0;
context.egid = 0;
wakeup_context(&context_lock);
}
Err(err) => {
panic!("failed to spawn userspace_init: {:?}", err);
+3 -3
View File
@@ -6,7 +6,7 @@ use alloc::{
};
use crate::{
context::{self, ContextLock, PreemptGuardL2},
context::{self, unblock_context, ContextLock, PreemptGuardL2},
sync::{CleanLockToken, LockToken, Mutex, L1, L2, L3},
};
@@ -33,7 +33,7 @@ impl WaitCondition {
let len = contexts.len();
while let Some(context_weak) = contexts.pop() {
if let Some(context_ref) = context_weak.upgrade() {
context_ref.write(token.token()).unblock();
unblock_context(&context_ref, &mut token.token());
}
}
len
@@ -46,7 +46,7 @@ impl WaitCondition {
let len = contexts.len();
for context_weak in contexts.iter() {
if let Some(context_ref) = context_weak.upgrade() {
context_ref.write(token.token()).unblock();
unblock_context(&context_ref, &mut token.token());
}
}
len
+24 -31
View File
@@ -311,43 +311,36 @@ pub fn dup2(
buf: UserSliceRo,
token: &mut CleanLockToken,
) -> Result<FileHandle> {
if fd == new_fd {
// Self-dup: a no-op if no buffer is supplied. But a self-dup2 WITH a
// buffer is a meaningful in-place operation — e.g. relibc issues
// dup2(ft, ft, "refresh") to re-materialize the inherited filetable, so
// it must NOT short-circuit. Duplicate first (fd is still valid), then
// replace the descriptor in place. Only this self-dup path is new; the
// normal path below is unchanged so ordinary dup2 (process-spawn stdio)
// keeps its exact original behavior.
if buf.is_empty() {
return Ok(new_fd);
}
let new_file = duplicate_file(fd, buf, false, token)?;
let old_file = {
let current_lock = context::current();
let mut current = current_lock.read(token.token());
let (context, mut token) = current.token_split();
let old_file = context.remove_file(new_fd, &mut token);
context
.insert_file(new_fd, new_file, &mut token)
.ok_or(Error::new(EMFILE))?;
old_file
};
if let Some(old) = old_file {
let _ = old.close(token);
}
Ok(new_fd)
} else {
let _ = close(new_fd, token);
let new_file = duplicate_file(fd, buf, false, token)?;
if fd == new_fd && buf.is_empty() {
return Ok(new_fd);
}
let new_file = duplicate_file(fd, buf, false, token)?;
let old_file = {
let current_lock = context::current();
let mut current = current_lock.read(token.token());
let (context, mut token) = current.token_split();
context
let old_file = context.remove_file(new_fd, &mut token);
if context
.insert_file(new_fd, new_file, &mut token)
.ok_or(Error::new(EMFILE))
.is_none()
{
if let Some(old) = old_file {
context.insert_file(new_fd, old, &mut token);
}
return Err(Error::new(EMFILE));
}
old_file
};
if let Some(old) = old_file {
let _ = old.close(token);
}
Ok(new_fd)
}
pub fn call(
fd: FileHandle,
+40 -26
View File
@@ -15,11 +15,10 @@ use crate::{
context::{
self,
memory::{AddrSpace, AddrSpaceWrapper},
ContextLock,
unblock_context, ContextLock,
},
memory::{Page, PhysicalAddress, VirtualAddress},
sync::{CleanLockToken, Mutex, L1},
time,
};
use crate::syscall::{
@@ -39,9 +38,11 @@ pub struct FutexEntry {
// CoW.
target_virtaddr: VirtualAddress,
// Context to wake up, and compare address spaces.
context_lock: Arc<ContextLock>,
context_lock: Weak<ContextLock>,
// address space to check against if virt matches but not phys
addr_space: Weak<AddrSpaceWrapper>,
// FUTEX_WAIT_MULTIPLE: which array index this waiter corresponds to,
// reported back through Context::futex_wake_index on wake.
waitv_index: Option<usize>,
}
@@ -52,6 +53,17 @@ pub struct FutexEntry {
static FUTEXES: Mutex<L1, FutexList> =
Mutex::new(FutexList::with_hasher(DefaultHashBuilder::new()));
pub fn get_futex_stat(token: &mut CleanLockToken) -> (usize, usize) {
let mut regc = 0;
let mut regl = 0;
let registry = FUTEXES.lock(token.token());
for (_, v) in registry.iter() {
regl += v.len();
regc += 1;
}
(regc, regl)
}
fn validate_and_translate_virt(space: &AddrSpace, addr: VirtualAddress) -> Option<PhysicalAddress> {
// TODO: Move this elsewhere!
if addr.data().saturating_add(size_of::<usize>()) >= crate::USER_END_OFFSET {
@@ -142,9 +154,7 @@ pub fn futex(
{
let mut context = context_lock.write(token.token());
context.wake = timeout_opt.map(|TimeSpec { tv_sec, tv_nsec }| {
tv_sec as u128 * time::NANOS_PER_SEC + tv_nsec as u128
});
context.wake = timeout_opt.map(|time| time.to_nanos());
if let Some((tctl, pctl, _)) = context.sigcontrol()
&& tctl.currently_pending_unblocked(pctl) != 0
{
@@ -159,7 +169,7 @@ pub fn futex(
.or_insert_with(Vec::new)
.push(FutexEntry {
target_virtaddr,
context_lock: context_lock.clone(),
context_lock: Arc::downgrade(&context_lock),
addr_space: Arc::downgrade(&current_addrsp),
waitv_index: None,
});
@@ -198,25 +208,28 @@ pub fn futex(
if futex.target_virtaddr != target_virtaddr
|| !current_addrsp_weak.ptr_eq(&futex.addr_space)
{
i += 1;
if futex.addr_space.strong_count() == 0 {
futexes.swap_remove(i);
} else {
i += 1;
}
continue;
}
let should_wake = {
let mut context = futex.context_lock.write(token.token());
if let Some(ctx) = futex.context_lock.upgrade() {
// FUTEX_WAIT_MULTIPLE wake-index bookkeeping:
// record which array index woke the waiter (the
// first wake wins) before unblocking.
if let Some(index) = futex.waitv_index {
let mut context = ctx.write(token.token());
if context.futex_wake_index.is_none() {
context.futex_wake_index = Some(index);
}
drop(context);
}
context.unblock();
true
};
if should_wake {
futexes.swap_remove(i);
woken += 1;
} else {
i += 1;
unblock_context(&ctx, &mut token.token().downgrade());
}
futexes.swap_remove(i);
woken += 1;
}
futexes.is_empty()
@@ -260,7 +273,9 @@ pub fn futex(
continue;
}
if woken < val {
futex.context_lock.write(token.token()).unblock();
if let Some(ctx) = futex.context_lock.upgrade() {
unblock_context(&ctx, &mut token.token().downgrade());
}
futexes.remove(i);
woken += 1;
} else if requeued < val2 {
@@ -288,9 +303,10 @@ pub fn futex(
Ok(woken + requeued)
}
FUTEX_WAIT_MULTIPLE => {
// Wait on multiple futexes atomically; return the index of the first
// one that is woken. Semantics: val = number of FutexWaitv entries,
// addr2 = pointer to the array, val2 = optional TimeSpec timeout.
// Wait on multiple futexes atomically; return the index of the
// first one that is woken. Semantics: val = number of FutexWaitv
// entries, addr2 = pointer to the array, val2 = optional TimeSpec
// timeout. Linux 7.1 kernel/futex/waitv.c futex_waitv().
let count = val;
if count == 0 || count > 128 {
return Err(Error::new(EINVAL));
@@ -344,9 +360,7 @@ pub fn futex(
{
let mut context = context_lock.write(token.token());
context.wake = timeout_opt.map(|TimeSpec { tv_sec, tv_nsec }| {
tv_sec as u128 * time::NANOS_PER_SEC + tv_nsec as u128
});
context.wake = timeout_opt.map(|ts| ts.to_nanos());
context.futex_wake_index = None;
if let Some((tctl, pctl, _)) = context.sigcontrol()
&& tctl.currently_pending_unblocked(pctl) != 0
@@ -362,7 +376,7 @@ pub fn futex(
.or_insert_with(Vec::new)
.push(FutexEntry {
target_virtaddr: virt,
context_lock: context_lock.clone(),
context_lock: Arc::downgrade(&context_lock),
addr_space: Arc::downgrade(&current_addrsp),
waitv_index: Some(i),
});