Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| baadbfc539 | |||
| d41d0aa728 | |||
| d37b421cb3 | |||
| 6a5582783f | |||
| 4789d546e2 | |||
| e8ec916158 | |||
| 327c1502d1 | |||
| 7fc8bbf057 | |||
| f7652fc26a | |||
| cbf051e6d8 | |||
| 5fb42fcaa1 | |||
| ed3f0e1e64 | |||
| 9bc1fbfe46 | |||
| 475f96ecab | |||
| 1be659be05 | |||
| 6b98c64663 | |||
| 01ef6f5c5d | |||
| 3f2f3bacc5 | |||
| 9f6a4288b5 | |||
| f8308866e0 | |||
| 8d9f9e552f | |||
| 75c7618313 | |||
| 24fd0a083d | |||
| a8042049ce | |||
| 19010ce174 | |||
| 7f7095be1c | |||
| 8cd4f69108 | |||
| 4f2a0436eb | |||
| 4cb9d80396 | |||
| 82feefbaee |
@@ -1,5 +0,0 @@
|
||||
[build]
|
||||
rustflags = [
|
||||
# Kernel should preserve floating-point registers
|
||||
"-Ctarget-feature=-sse,-sse2",
|
||||
]
|
||||
Generated
+29
-29
@@ -16,9 +16,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.8"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
@@ -40,15 +40,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.13.0"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
||||
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.66"
|
||||
version = "1.2.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996"
|
||||
checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
@@ -88,9 +88,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
@@ -99,16 +99,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.17.1",
|
||||
"hashbrown 0.17.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel"
|
||||
version = "0.5.12+rb0.3.0"
|
||||
version = "0.5.12"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitfield",
|
||||
"bitflags 2.13.0",
|
||||
"bitflags 2.11.1",
|
||||
"cc",
|
||||
"fdt",
|
||||
"hashbrown 0.14.5",
|
||||
@@ -147,9 +147,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.2"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
@@ -177,9 +177,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.46"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -201,16 +201,16 @@ checksum = "64072665120942deff5fd5425d6c1811b854f4939e7f1c01ce755f64432bbea7"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.9.0+rb0.3.0"
|
||||
version = "0.8.1"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"bitflags 2.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rmm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"bitflags 2.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -280,9 +280,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "2.0.1"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
@@ -292,9 +292,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.2"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
@@ -316,9 +316,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.118"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -400,18 +400,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.53"
|
||||
version = "0.8.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75726053136156d419e285b9b7eddaaea9e3fea6ce32eed44a89901f0bd98de1"
|
||||
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.53"
|
||||
version = "0.8.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4714fd92cf900833d49538023a9b3915155210801d1c1169eba513b2addefd71"
|
||||
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -420,4 +420,4 @@ dependencies = [
|
||||
|
||||
[[patch.unused]]
|
||||
name = "libredox"
|
||||
version = "0.1.18+rb0.3.0"
|
||||
version = "0.1.18"
|
||||
|
||||
+15
-4
@@ -4,8 +4,7 @@ members = [".", "rmm"]
|
||||
|
||||
[package]
|
||||
name = "kernel"
|
||||
version = "0.5.12+rb0.3.0"
|
||||
authors = ["Jeremy Soller <jackpot51@gmail.com>", "vasilito <adminpupkin@gmail.com>"]
|
||||
version = "0.5.12"
|
||||
build = "build.rs"
|
||||
edition = "2024"
|
||||
|
||||
@@ -21,7 +20,7 @@ fdt = { git = "https://github.com/repnop/fdt.git", rev = "2fb1409edd1877c714a0aa
|
||||
hashbrown = { version = "0.14.3", default-features = false, features = ["ahash", "inline-more"] }
|
||||
linked_list_allocator = "0.9.0"
|
||||
redox-path = "0.2.0"
|
||||
redox_syscall = { path = "../syscall", default-features = false }
|
||||
redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git", default-features = false }
|
||||
rmm = { path = "rmm", default-features = false }
|
||||
slab = { version = "0.4", default-features = false }
|
||||
smallvec = { version = "1.15.1", default-features = false }
|
||||
@@ -132,6 +131,18 @@ debug = "full"
|
||||
# same type, so `?` conversions in scheme-utils / daemon
|
||||
# compile cleanly.
|
||||
[patch.crates-io]
|
||||
# Local fork dependency rule: every crate with a local fork MUST resolve through it.
|
||||
# Phase J: override libredox 0.1.17 to use the local
|
||||
# fork at ../libredox/ (which itself uses the local syscall
|
||||
# fork). This breaks the libredox::error::Error <->
|
||||
# syscall::Error type-identity barrier that previously
|
||||
# caused E0277 errors in scheme-utils and daemon.
|
||||
libredox = { path = "../libredox" }
|
||||
# Phase J: the kernel's redox_syscall dep is a git URL
|
||||
# (not crates.io), so [patch.crates-io] doesn't apply.
|
||||
# Use a [patch."<URL>"] section to match the dep source.
|
||||
# The local fork at ../syscall adds the EnterS2Idle /
|
||||
# ExitS2Idle AcpiVerb variants — the kernel's direct use
|
||||
# of AcpiVerb in src/scheme/acpi.rs's kcall handler
|
||||
# needs the fork to see these variants.
|
||||
[patch."https://gitlab.redox-os.org/redox-os/syscall.git"]
|
||||
redox_syscall = { path = "../syscall" }
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2026-05-24"
|
||||
channel = "nightly-2025-10-03"
|
||||
components = ["rust-src"]
|
||||
|
||||
+18
-13
@@ -53,10 +53,12 @@ mod offsets {
|
||||
/// Structure address. ACPI 2.0+.
|
||||
pub const X_FIRMWARE_CTRL_64: usize = 140;
|
||||
/// FADT minimum size for ACPI 2.0+ (i.e., enough to
|
||||
/// include `x_firmware_ctrl` at offset 140).
|
||||
pub const FADT_MIN_SIZE_ACPI_2_0: usize = 148;
|
||||
/// FADT minimum size for ACPI 1.0.
|
||||
pub const FADT_MIN_SIZE_ACPI_1_0: usize = 76;
|
||||
/// include `x_firmware_ctrl` at offset 140). Stored as
|
||||
/// `u32` to match `Sdt::length()` (which is `u32` per
|
||||
/// ACPI spec) and avoid a usize/u32 comparison on x86_64.
|
||||
pub const FADT_MIN_SIZE_ACPI_2_0: u32 = 148;
|
||||
/// FADT minimum size for ACPI 1.0. Same rationale.
|
||||
pub const FADT_MIN_SIZE_ACPI_1_0: u32 = 76;
|
||||
}
|
||||
|
||||
/// Parse the FADT from the given SDT bytes and extract the
|
||||
@@ -70,16 +72,19 @@ pub fn init(sdt: &Sdt) {
|
||||
if &sdt.signature != &FADT_SIGNATURE {
|
||||
return;
|
||||
}
|
||||
// The FADT fields are at fixed offsets from the start of the
|
||||
// SDT (including the 36-byte header), not from the data area.
|
||||
let base = sdt as *const _ as *const u8;
|
||||
// SAFETY: We trust the ACPI table discovery code to have
|
||||
// verified the FADT checksum. The FADT fields are at fixed
|
||||
// offsets (per the ACPI spec); reading them as u32/u64 is
|
||||
// safe because all of them are at 4-byte or 8-byte aligned
|
||||
// offsets on x86_64.
|
||||
let data = sdt.data_address() as *const u8;
|
||||
unsafe {
|
||||
// PM1a_CNT is at offset 56 in the FADT (ACPI 6.5 §5.2.9
|
||||
// Table 5.6). 32-bit General-Purpose Event Register Block 0
|
||||
// Address.
|
||||
let pm1a_cnt = core::ptr::read_unaligned(base.add(offsets::PM1A_CNT) as *const u32);
|
||||
let pm1a_cnt = core::ptr::read_unaligned(data.add(offsets::PM1A_CNT) as *const u32);
|
||||
// PM1a_STS is at offset 48 in the FADT.
|
||||
let pm1a_sts = core::ptr::read_unaligned(base.add(offsets::PM1A_STS) as *const u32);
|
||||
let pm1a_sts = core::ptr::read_unaligned(data.add(offsets::PM1A_STS) as *const u32);
|
||||
// Convert u32 to u16 (port numbers are 16-bit). The low
|
||||
// 16 bits are the IO port; the high 16 bits are the
|
||||
// address-space ID which we ignore (always IO on x86).
|
||||
@@ -95,18 +100,18 @@ pub fn init(sdt: &Sdt) {
|
||||
// Phase II.X.W: 32-bit FACS address (FADT offset 36,
|
||||
// `firmware_ctrl` field). ACPI 1.0+.
|
||||
let firmware_ctrl = core::ptr::read_unaligned(
|
||||
base.add(offsets::FIRMWARE_CTRL_32) as *const u32,
|
||||
data.add(offsets::FIRMWARE_CTRL_32) as *const u32,
|
||||
);
|
||||
FIRMWARE_CTRL.store(firmware_ctrl, core::sync::atomic::Ordering::Release);
|
||||
|
||||
// Phase II.X.W: 64-bit FACS address (FADT offset 140,
|
||||
// `x_firmware_ctrl` field, ACPI 2.0+). We require the
|
||||
// `x_firmware_ctrl` field). ACPI 2.0+. We require the
|
||||
// FADT to be at least 148 bytes to have this field
|
||||
// (the field is at offset 140, which is 8 bytes for the
|
||||
// u64, so the minimum FADT size is 148 bytes).
|
||||
if sdt.length() >= offsets::FADT_MIN_SIZE_ACPI_2_0 as u32 {
|
||||
if sdt.length() >= offsets::FADT_MIN_SIZE_ACPI_2_0 {
|
||||
let x_firmware_ctrl = core::ptr::read_unaligned(
|
||||
base.add(offsets::X_FIRMWARE_CTRL_64) as *const u64,
|
||||
data.add(offsets::X_FIRMWARE_CTRL_64) as *const u64,
|
||||
);
|
||||
X_FIRMWARE_CTRL.store(x_firmware_ctrl, core::sync::atomic::Ordering::Release);
|
||||
}
|
||||
|
||||
+17
-14
@@ -178,23 +178,26 @@ pub unsafe fn init(already_supplied_rsdp: Option<NonNull<u8>>) {
|
||||
//
|
||||
// The FACS is found via the FADT's x_firmware_ctrl
|
||||
// field (64-bit) or firmware_ctrl field (32-bit).
|
||||
// The FADT parser caches the FACS address. We map the
|
||||
// FACS here because it is not listed in the RSDT/XSDT
|
||||
// and therefore was not mapped during table discovery.
|
||||
const FACS_MIN_SIZE: usize = 64;
|
||||
let mut facs_addr = fadt::x_firmware_ctrl();
|
||||
if facs_addr == 0 {
|
||||
facs_addr = fadt::firmware_ctrl() as u64;
|
||||
}
|
||||
// The FADT parser caches the FACS address. We use
|
||||
// the FADT's x_firmware_ctrl to find the FACS SDT.
|
||||
let facs_addr = fadt::x_firmware_ctrl();
|
||||
if facs_addr != 0 {
|
||||
let facs_phys = PhysicalAddress::new(facs_addr as usize);
|
||||
map_linearly(facs_phys, FACS_MIN_SIZE, &mut KernelMapper::lock_rw());
|
||||
let facs_sdt = unsafe {
|
||||
&*(RmmA::phys_to_virt(facs_phys).data() as *const Sdt)
|
||||
};
|
||||
// SAFETY: The FACS address is a physical
|
||||
// address stored in the FADT. The boot-time page
|
||||
// table maps the FACS into the kernel's address
|
||||
// space (firmware tables are below 4GB on x86_64).
|
||||
let facs_sdt = unsafe { &*(facs_addr as *const Sdt) };
|
||||
facs::init(facs_sdt);
|
||||
} else {
|
||||
warn!("ACPI: no FACS found (neither x_firmware_ctrl nor firmware_ctrl), S3 resume path disabled");
|
||||
let facs_addr = fadt::firmware_ctrl() as u64;
|
||||
if facs_addr != 0 {
|
||||
// SAFETY: same as above.
|
||||
let facs_sdt =
|
||||
unsafe { &*(facs_addr as *const Sdt) };
|
||||
facs::init(facs_sdt);
|
||||
} else {
|
||||
warn!("ACPI: no FACS found (neither x_firmware_ctrl nor firmware_ctrl), S3 resume path disabled");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("NO RSDP FOUND");
|
||||
|
||||
@@ -247,12 +247,10 @@ fn init_generic(cpu_id: LogicalCpuId, idt: &mut Idt, backup_stack_end: usize) {
|
||||
current_idt[IpiKind::Switch as usize].set_func(ipi::switch);
|
||||
current_idt[IpiKind::Tlb as usize].set_func(ipi::tlb);
|
||||
current_idt[IpiKind::Pit as usize].set_func(ipi::pit);
|
||||
current_idt[IpiKind::Msr as usize].set_func(ipi::msr);
|
||||
idt.set_reserved_mut(IpiKind::Wakeup as u8, true);
|
||||
idt.set_reserved_mut(IpiKind::Switch as u8, true);
|
||||
idt.set_reserved_mut(IpiKind::Tlb as u8, true);
|
||||
idt.set_reserved_mut(IpiKind::Pit as u8, true);
|
||||
idt.set_reserved_mut(IpiKind::Msr as u8, true);
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
arch::device::local_apic::the_local_apic, context, percpu::PercpuBlock, sync::CleanLockToken,
|
||||
};
|
||||
use x86::msr::{rdmsr, wrmsr};
|
||||
|
||||
interrupt!(wakeup, || {
|
||||
unsafe { the_local_apic().eoi() };
|
||||
@@ -27,20 +26,3 @@ interrupt!(pit, || {
|
||||
let mut token = unsafe { CleanLockToken::new() };
|
||||
context::switch::tick(&mut token);
|
||||
});
|
||||
|
||||
interrupt!(msr, || {
|
||||
let percpu = PercpuBlock::current();
|
||||
let mailbox = percpu.msr_mailbox();
|
||||
let op = mailbox.request();
|
||||
|
||||
let result = if op.is_write {
|
||||
unsafe { wrmsr(op.msr as u32, op.value) };
|
||||
op.value
|
||||
} else {
|
||||
unsafe { rdmsr(op.msr as u32) }
|
||||
};
|
||||
|
||||
mailbox.finish_handle(result);
|
||||
|
||||
unsafe { the_local_apic().eoi() };
|
||||
});
|
||||
|
||||
@@ -5,10 +5,9 @@ pub enum IpiKind {
|
||||
Tlb = 0x41,
|
||||
Switch = 0x42,
|
||||
Pit = 0x43,
|
||||
Msr = 0x44,
|
||||
|
||||
#[cfg(feature = "profiling")]
|
||||
Profile = 0x45,
|
||||
Profile = 0x44,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
||||
+107
-42
@@ -18,7 +18,8 @@ use crate::{
|
||||
cpu_stats,
|
||||
ipi::{ipi, IpiKind, IpiTarget},
|
||||
memory::{
|
||||
allocate_p2frame, deallocate_p2frame, Enomem, Frame, RaiiFrame, RmmA, RmmArch, PAGE_SIZE,
|
||||
allocate_p2frame, deallocate_p2frame, Enomem, Frame, PhysicalAddress, RaiiFrame, RmmA,
|
||||
RmmArch, PAGE_SIZE,
|
||||
},
|
||||
percpu::PercpuBlock,
|
||||
scheme::{CallerCtx, FileHandle, SchemeId},
|
||||
@@ -62,6 +63,38 @@ impl Status {
|
||||
}
|
||||
}
|
||||
|
||||
pub const SCHED_PRIORITY_LEVELS: usize = 40;
|
||||
pub const DEFAULT_SCHED_OTHER_PRIORITY: usize = 20;
|
||||
pub const DEFAULT_SCHED_RR_QUANTUM: u128 = 100_000_000;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum SchedPolicy {
|
||||
Fifo = 0,
|
||||
RoundRobin = 1,
|
||||
Other = 2,
|
||||
}
|
||||
|
||||
impl SchedPolicy {
|
||||
pub fn try_from_raw(raw: u8) -> Option<Self> {
|
||||
match raw {
|
||||
0 => Some(Self::Fifo),
|
||||
1 => Some(Self::RoundRobin),
|
||||
2 => Some(Self::Other),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rt_priority_to_kernel_prio(rt_priority: u8) -> usize {
|
||||
(SCHED_PRIORITY_LEVELS - 1)
|
||||
.saturating_sub((usize::from(rt_priority.min(99)) * (SCHED_PRIORITY_LEVELS - 1)) / 99)
|
||||
}
|
||||
|
||||
fn clamp_sched_other_prio(prio: usize) -> usize {
|
||||
prio.min(SCHED_PRIORITY_LEVELS - 1)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum HardBlockedReason {
|
||||
/// "SIGSTOP", only procmgr is allowed to switch contexts this state
|
||||
@@ -96,14 +129,11 @@ pub struct Context {
|
||||
pub running: bool,
|
||||
/// Current CPU ID
|
||||
pub cpu_id: Option<LogicalCpuId>,
|
||||
pub last_cpu: Option<LogicalCpuId>,
|
||||
/// Time this context was switched to
|
||||
pub switch_time: u128,
|
||||
/// Amount of CPU time used
|
||||
pub cpu_time: u128,
|
||||
/// User-mode CPU time used
|
||||
pub utime: u128,
|
||||
/// Kernel-mode CPU time used
|
||||
pub stime: u128,
|
||||
/// Scheduler CPU affinity. If set, [`cpu_id`] can except [`None`] never be anything else than
|
||||
/// this value.
|
||||
pub sched_affinity: LogicalCpuSet,
|
||||
@@ -144,30 +174,20 @@ pub struct Context {
|
||||
pub fmap_ret: Option<Frame>,
|
||||
/// Priority
|
||||
pub prio: usize,
|
||||
/// Process group ID.
|
||||
pub pgrp: usize,
|
||||
/// Session ID.
|
||||
pub session: usize,
|
||||
/// Context start time in nanoseconds since boot.
|
||||
pub start_time: u128,
|
||||
/// Resource limits (rlimit values, in corresponding units).
|
||||
/// Indexed by RLIMIT_* constants from <sys/resource.h>.
|
||||
/// RLIM_INFINITY (u64::MAX) means unlimited.
|
||||
pub rlimits: [u64; 16],
|
||||
/// Total bytes read via read()/pread() etc. (Linux /proc/pid/io rchar).
|
||||
pub io_rchar: u64,
|
||||
/// Total bytes written via write()/pwrite() etc. (Linux wchar).
|
||||
pub io_wchar: u64,
|
||||
/// Total read() syscalls (Linux syscr).
|
||||
pub io_syscr: u64,
|
||||
/// Total write() syscalls (Linux syscw).
|
||||
pub io_syscw: u64,
|
||||
/// Actual bytes read from storage (Linux read_bytes).
|
||||
pub io_read_bytes: u64,
|
||||
/// Actual bytes written to storage (Linux write_bytes).
|
||||
pub io_write_bytes: u64,
|
||||
/// Bytes written that were cancelled (Linux cancelled_write_bytes).
|
||||
pub io_cancelled_write_bytes: u64,
|
||||
pub sched_policy: SchedPolicy,
|
||||
pub sched_rt_priority: u8,
|
||||
pub sched_rr_ticks_consumed: u32,
|
||||
pub sched_static_prio: usize,
|
||||
pub sched_rr_quantum: u128,
|
||||
/// Virtual runtime for SCHED_OTHER fair scheduling.
|
||||
/// CPU-bound threads accumulate vruntime faster; I/O-bound stay lower.
|
||||
pub vruntime: u128,
|
||||
#[allow(dead_code)]
|
||||
pub futex_pi_boost: bool,
|
||||
#[allow(dead_code)]
|
||||
pub futex_pi_original_prio: usize,
|
||||
#[allow(dead_code)]
|
||||
pub futex_pi_waiters: Vec<PhysicalAddress>,
|
||||
|
||||
// TODO: id can reappear after wraparound?
|
||||
pub owner_proc_id: Option<NonZeroUsize>,
|
||||
@@ -212,10 +232,9 @@ impl Context {
|
||||
status_reason: "",
|
||||
running: false,
|
||||
cpu_id: None,
|
||||
last_cpu: None,
|
||||
switch_time: 0,
|
||||
cpu_time: 0,
|
||||
utime: 0,
|
||||
stime: 0,
|
||||
sched_affinity: LogicalCpuSet::all(),
|
||||
inside_syscall: false,
|
||||
syscall_head: SyscallFrame::Free(RaiiFrame::allocate()?),
|
||||
@@ -230,6 +249,15 @@ impl Context {
|
||||
userspace: false,
|
||||
fmap_ret: None,
|
||||
prio: 20,
|
||||
sched_policy: SchedPolicy::Other,
|
||||
sched_rt_priority: 0,
|
||||
sched_rr_ticks_consumed: 0,
|
||||
sched_static_prio: 20,
|
||||
sched_rr_quantum: DEFAULT_SCHED_RR_QUANTUM,
|
||||
vruntime: 0,
|
||||
futex_pi_boost: false,
|
||||
futex_pi_original_prio: 0,
|
||||
futex_pi_waiters: Vec::new(),
|
||||
being_sigkilled: false,
|
||||
owner_proc_id,
|
||||
|
||||
@@ -237,17 +265,6 @@ impl Context {
|
||||
egid: 0,
|
||||
pid: 0,
|
||||
groups: Vec::new(),
|
||||
pgrp: 0,
|
||||
session: 0,
|
||||
start_time: 0,
|
||||
rlimits: [u64::MAX; 16], // All unlimited (RLIM_INFINITY)
|
||||
io_rchar: 0,
|
||||
io_wchar: 0,
|
||||
io_syscr: 0,
|
||||
io_syscw: 0,
|
||||
io_read_bytes: 0,
|
||||
io_write_bytes: 0,
|
||||
io_cancelled_write_bytes: 0,
|
||||
|
||||
#[cfg(feature = "syscall_debug")]
|
||||
syscall_debug_info: crate::syscall::debug::SyscallDebugInfo::default(),
|
||||
@@ -476,6 +493,54 @@ impl Context {
|
||||
core::mem::replace(&mut self.addr_space, addr_space)
|
||||
}
|
||||
|
||||
/// Set the scheduling policy and RT priority for this context.
|
||||
///
|
||||
/// `policy` selects SCHED_FIFO / SCHED_RR / SCHED_OTHER. `rt_priority` is
|
||||
/// POSIX 0..99 for FIFO/RR (clamped); ignored for SCHED_OTHER (which uses
|
||||
/// `sched_static_prio` set via [`Self::set_sched_other_prio`]).
|
||||
///
|
||||
/// The mapping from POSIX `rt_priority` to the kernel's
|
||||
/// `SCHED_PRIORITY_LEVELS` (0..=39, higher = lower priority) is the
|
||||
/// standard inverse weighting: rt_priority 99 maps to level 0 (highest
|
||||
/// priority), rt_priority 0 maps to level 39 (lowest). The actual
|
||||
/// kernel priority level is computed by [`rt_priority_to_kernel_prio`].
|
||||
///
|
||||
/// `sched_rr_ticks_consumed` is reset to 0 so that a fresh RR quantum
|
||||
/// begins on the next scheduler pass.
|
||||
pub fn set_sched_policy(&mut self, policy: SchedPolicy, rt_priority: u8) {
|
||||
self.sched_policy = policy;
|
||||
self.sched_rt_priority = match policy {
|
||||
SchedPolicy::Other => 0,
|
||||
SchedPolicy::Fifo | SchedPolicy::RoundRobin => rt_priority.min(99),
|
||||
};
|
||||
self.sched_rr_ticks_consumed = 0;
|
||||
if matches!(policy, SchedPolicy::Fifo | SchedPolicy::RoundRobin) {
|
||||
self.sched_static_prio = rt_priority_to_kernel_prio(self.sched_rt_priority);
|
||||
self.prio = self.sched_static_prio;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the SCHED_OTHER priority (nice level) for this context.
|
||||
///
|
||||
/// `prio` is the kernel priority level 0..=39, where 0 is the highest
|
||||
/// weight (`SCHED_PRIO_TO_WEIGHT[0] = 88761`, most CPU time, "nice
|
||||
/// -20") and 39 is the lowest weight
|
||||
/// (`SCHED_PRIO_TO_WEIGHT[39] = 15`, least CPU time, "nice +19").
|
||||
/// The clamping and conversion from POSIX nice is the responsibility
|
||||
/// of the caller
|
||||
/// (see `kernel_prio_to_nice` / `nice_to_kernel_prio` in
|
||||
/// `scheme/proc.rs`).
|
||||
///
|
||||
/// SCHED_OTHER contexts accumulate vruntime faster at higher kernel
|
||||
/// priority (lower nice) — see the DWRR weight table in
|
||||
/// `context/switch.rs:30-34`. Setting this does NOT change the
|
||||
/// scheduling class; use [`Self::set_sched_policy`] for that.
|
||||
pub fn set_sched_other_prio(&mut self, prio: usize) {
|
||||
let clamped = clamp_sched_other_prio(prio);
|
||||
self.sched_static_prio = clamped;
|
||||
self.prio = clamped;
|
||||
}
|
||||
|
||||
fn can_access_regs(&self) -> bool {
|
||||
self.userspace
|
||||
}
|
||||
|
||||
+38
-7
@@ -10,9 +10,9 @@ use core::{num::NonZeroUsize, ops::Deref};
|
||||
|
||||
use crate::{
|
||||
context::memory::AddrSpaceWrapper,
|
||||
cpu_set::LogicalCpuSet,
|
||||
cpu_set::{LogicalCpuId, LogicalCpuSet},
|
||||
memory::{RmmA, RmmArch, TableKind},
|
||||
percpu::PercpuBlock,
|
||||
percpu::{get_percpu_block, PercpuBlock},
|
||||
sync::{
|
||||
ArcRwLockWriteGuard, CleanLockToken, LockToken, Mutex, MutexGuard, RwLock, RwLockReadGuard,
|
||||
RwLockWriteGuard, L0, L1, L2, L4,
|
||||
@@ -22,13 +22,21 @@ use crate::{
|
||||
|
||||
use self::context::Kstack;
|
||||
pub use self::{
|
||||
context::{BorrowedHtBuf, Context, Status},
|
||||
context::{BorrowedHtBuf, Context, SchedPolicy, Status},
|
||||
switch::switch,
|
||||
};
|
||||
|
||||
pub type ContextLock = RwLock<L4, Context>;
|
||||
pub type ArcContextLockWriteGuard = ArcRwLockWriteGuard<L4, Context>;
|
||||
|
||||
/// Number of priority queues in the per-CPU run queue (DWRR priority
|
||||
/// count). Kept at 40 to match the historical global `RunContextData.set`
|
||||
/// length and the existing `SCHED_PRIO_TO_WEIGHT` table in
|
||||
/// `context/switch.rs`. Each `PerCpuSched.run_queues` entry is one
|
||||
/// DWRR priority level. Consumers: `crate::percpu::PerCpuSched`,
|
||||
/// `crate::context::switch`.
|
||||
pub const RUN_QUEUE_COUNT: usize = 40;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[path = "arch/aarch64.rs"]
|
||||
mod arch;
|
||||
@@ -117,6 +125,30 @@ pub fn run_contexts(token: LockToken<'_, L0>) -> MutexGuard<'_, L1, RunContextDa
|
||||
RUN_CONTEXTS.lock(token)
|
||||
}
|
||||
|
||||
fn least_loaded_cpu() -> LogicalCpuId {
|
||||
let current_cpu = crate::cpu_id();
|
||||
let mut best_cpu = current_cpu;
|
||||
let mut best_depth = usize::MAX;
|
||||
|
||||
for raw_id in 0..crate::cpu_count() {
|
||||
let cpu_id = LogicalCpuId::new(raw_id);
|
||||
let Some(percpu) = get_percpu_block(cpu_id) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
percpu.sched.take_lock();
|
||||
let depth = unsafe { percpu.sched.queues().iter().map(|queue| queue.len()).sum() };
|
||||
percpu.sched.release_lock();
|
||||
|
||||
if depth < best_depth {
|
||||
best_depth = depth;
|
||||
best_cpu = cpu_id;
|
||||
}
|
||||
}
|
||||
|
||||
best_cpu
|
||||
}
|
||||
|
||||
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");
|
||||
@@ -131,7 +163,6 @@ pub fn init(token: &mut CleanLockToken) {
|
||||
context.status = Status::Runnable;
|
||||
context.running = true;
|
||||
context.cpu_id = Some(crate::cpu_id());
|
||||
context.start_time = crate::time::monotonic(token);
|
||||
|
||||
let context_lock = Arc::new(ContextLock::new(context));
|
||||
|
||||
@@ -224,9 +255,6 @@ pub fn spawn(
|
||||
let stack = Kstack::new()?;
|
||||
|
||||
let mut context = Context::new(owner_proc_id)?;
|
||||
context.start_time = crate::time::monotonic(token);
|
||||
context.pgrp = context.pid;
|
||||
context.session = context.pid;
|
||||
|
||||
let _ = context.set_addr_space(Some(AddrSpaceWrapper::new()?), token.downgrade());
|
||||
context
|
||||
@@ -235,6 +263,9 @@ pub fn spawn(
|
||||
|
||||
context.kstack = Some(stack);
|
||||
context.userspace = userspace_allowed;
|
||||
let target_cpu = least_loaded_cpu();
|
||||
context.sched_affinity = LogicalCpuSet::empty();
|
||||
context.sched_affinity.atomic_set(target_cpu);
|
||||
|
||||
let context_lock = Arc::new(ContextLock::new(context));
|
||||
let context_ref = ContextRef(Arc::clone(&context_lock));
|
||||
|
||||
+767
-173
File diff suppressed because it is too large
Load Diff
+3
-4
@@ -1027,10 +1027,9 @@ pub fn page_fault_handler(
|
||||
let mut token = unsafe { CleanLockToken::new() };
|
||||
match context::memory::try_correcting_page_tables(faulting_page, mode, &mut token) {
|
||||
Ok(()) => return Ok(()),
|
||||
Err(PfError::Oom) => {
|
||||
return Err(Segv);
|
||||
}
|
||||
Err(PfError::Segv | PfError::RecursionLimitExceeded | PfError::NonfatalInternalError) => (),
|
||||
Err(PfError::Oom) => todo!("oom"),
|
||||
Err(PfError::Segv | PfError::RecursionLimitExceeded) => (),
|
||||
Err(PfError::NonfatalInternalError) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/// NUMA topology hints for the kernel scheduler.
|
||||
/// NUMA discovery (SRAT/SLIT parsing) is performed by a userspace daemon
|
||||
/// (numad) via /scheme/acpi/, then pushed to the kernel via scheme:numa.
|
||||
/// The kernel stores a lightweight copy for O(1) scheduling lookups.
|
||||
use crate::cpu_set::{LogicalCpuId, LogicalCpuSet};
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
const MAX_NUMA_NODES: usize = 8;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NumaHint {
|
||||
pub node_id: u8,
|
||||
pub cpus: LogicalCpuSet,
|
||||
}
|
||||
|
||||
pub struct NumaTopology {
|
||||
pub nodes: [Option<NumaHint>; MAX_NUMA_NODES],
|
||||
pub initialized: AtomicBool,
|
||||
}
|
||||
|
||||
impl NumaTopology {
|
||||
pub const fn new() -> Self {
|
||||
const NONE: Option<NumaHint> = None;
|
||||
Self {
|
||||
nodes: [NONE; MAX_NUMA_NODES],
|
||||
initialized: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_for_cpu(&self, cpu: LogicalCpuId) -> Option<u8> {
|
||||
for node in self.nodes.iter().flatten() {
|
||||
if node.cpus.contains(cpu) {
|
||||
return Some(node.node_id);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn same_node(&self, cpu1: LogicalCpuId, cpu2: LogicalCpuId) -> bool {
|
||||
self.node_for_cpu(cpu1) == self.node_for_cpu(cpu2)
|
||||
}
|
||||
}
|
||||
|
||||
static mut NUMA_TOPOLOGY: NumaTopology = NumaTopology::new();
|
||||
|
||||
pub fn topology() -> &'static NumaTopology {
|
||||
unsafe { &NUMA_TOPOLOGY }
|
||||
}
|
||||
|
||||
pub fn init_default() {
|
||||
let topo = topology();
|
||||
if topo.initialized.swap(true, Ordering::AcqRel) {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
let topo_mut = &mut *core::ptr::addr_of_mut!(NUMA_TOPOLOGY);
|
||||
topo_mut.nodes[0] = Some(NumaHint {
|
||||
node_id: 0,
|
||||
cpus: LogicalCpuSet::all(),
|
||||
});
|
||||
}
|
||||
}
|
||||
+70
-70
@@ -1,10 +1,11 @@
|
||||
use alloc::{
|
||||
collections::VecDeque,
|
||||
sync::{Arc, Weak},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{
|
||||
cell::{Cell, RefCell},
|
||||
sync::atomic::{AtomicBool, AtomicPtr, AtomicU32, AtomicU64, AtomicU8, Ordering},
|
||||
cell::{Cell, RefCell, SyncUnsafeCell},
|
||||
sync::atomic::{AtomicBool, AtomicPtr, Ordering},
|
||||
};
|
||||
|
||||
use rmm::Arch;
|
||||
@@ -12,7 +13,10 @@ use syscall::PtraceFlags;
|
||||
|
||||
use crate::{
|
||||
arch::device::ArchPercpuMisc,
|
||||
context::{empty_cr3, memory::AddrSpaceWrapper, switch::ContextSwitchPercpu},
|
||||
context::{
|
||||
empty_cr3, memory::AddrSpaceWrapper, switch::ContextSwitchPercpu, WeakContextRef,
|
||||
RUN_QUEUE_COUNT,
|
||||
},
|
||||
cpu_set::{LogicalCpuId, MAX_CPU_COUNT},
|
||||
cpu_stats::{CpuStats, CpuStatsData},
|
||||
ptrace::Session,
|
||||
@@ -20,6 +24,58 @@ use crate::{
|
||||
syscall::debug::SyscallDebugInfo,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct PerCpuSched {
|
||||
pub run_queues: SyncUnsafeCell<[VecDeque<WeakContextRef>; RUN_QUEUE_COUNT]>,
|
||||
pub run_queues_lock: AtomicBool,
|
||||
pub balance: Cell<[usize; RUN_QUEUE_COUNT]>,
|
||||
pub last_queue: Cell<usize>,
|
||||
pub last_balance_time: Cell<u128>,
|
||||
}
|
||||
|
||||
impl PerCpuSched {
|
||||
pub const fn new() -> Self {
|
||||
const EMPTY: VecDeque<WeakContextRef> = VecDeque::new();
|
||||
Self {
|
||||
run_queues: SyncUnsafeCell::new([EMPTY; RUN_QUEUE_COUNT]),
|
||||
run_queues_lock: AtomicBool::new(false),
|
||||
balance: Cell::new([0; RUN_QUEUE_COUNT]),
|
||||
last_queue: Cell::new(0),
|
||||
last_balance_time: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_lock(&self) {
|
||||
while self
|
||||
.run_queues_lock
|
||||
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
|
||||
.is_err()
|
||||
{
|
||||
while self.run_queues_lock.load(Ordering::Relaxed) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn release_lock(&self) {
|
||||
self.run_queues_lock.store(false, Ordering::Release);
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must hold `run_queues_lock` while accessing the returned reference.
|
||||
pub unsafe fn queues(&self) -> &[VecDeque<WeakContextRef>; RUN_QUEUE_COUNT] {
|
||||
unsafe { &*self.run_queues.get() }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must hold `run_queues_lock` while accessing the returned reference.
|
||||
pub unsafe fn queues_mut(&self) -> &mut [VecDeque<WeakContextRef>; RUN_QUEUE_COUNT] {
|
||||
unsafe { &mut *self.run_queues.get() }
|
||||
}
|
||||
}
|
||||
|
||||
/// The percpu block, that stored all percpu variables.
|
||||
pub struct PercpuBlock {
|
||||
/// A unique immutable number that identifies the current CPU - used for scheduling
|
||||
@@ -31,8 +87,8 @@ pub struct PercpuBlock {
|
||||
pub current_addrsp: RefCell<Option<Arc<AddrSpaceWrapper>>>,
|
||||
pub new_addrsp_tmp: Cell<Option<Arc<AddrSpaceWrapper>>>,
|
||||
pub wants_tlb_shootdown: AtomicBool,
|
||||
pub balance: Cell<[usize; 40]>,
|
||||
pub last_queue: Cell<usize>,
|
||||
|
||||
pub sched: PerCpuSched,
|
||||
|
||||
// TODO: Put mailbox queues here, e.g. for TLB shootdown? Just be sure to 128-byte align it
|
||||
// first to avoid cache invalidation.
|
||||
@@ -46,65 +102,9 @@ pub struct PercpuBlock {
|
||||
|
||||
pub misc_arch_info: crate::arch::device::ArchPercpuMisc,
|
||||
|
||||
pub msr_mailbox: MsrMailbox,
|
||||
|
||||
pub stats: CpuStats,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct MsrMailbox {
|
||||
cpu_id: AtomicU32,
|
||||
msr: AtomicU64,
|
||||
value: AtomicU64,
|
||||
kind: AtomicU8,
|
||||
done: AtomicBool,
|
||||
}
|
||||
|
||||
impl MsrMailbox {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
cpu_id: AtomicU32::new(u32::MAX),
|
||||
msr: AtomicU64::new(0),
|
||||
value: AtomicU64::new(0),
|
||||
kind: AtomicU8::new(0),
|
||||
done: AtomicBool::new(true),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn begin_request(&self, cpu_id: u32, msr: u32, is_write: bool, value: u64) {
|
||||
self.done.store(false, Ordering::Relaxed);
|
||||
self.cpu_id.store(cpu_id, Ordering::Relaxed);
|
||||
self.msr.store(msr as u64, Ordering::Relaxed);
|
||||
self.value.store(value, Ordering::Relaxed);
|
||||
self.kind.store(is_write as u8, Ordering::Release);
|
||||
}
|
||||
|
||||
pub fn wait_done(&self) -> bool {
|
||||
self.done.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
pub fn request(&self) -> MsrRequest {
|
||||
MsrRequest {
|
||||
msr: self.msr.load(Ordering::Acquire),
|
||||
value: self.value.load(Ordering::Acquire),
|
||||
is_write: self.kind.load(Ordering::Acquire) != 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish_handle(&self, value: u64) {
|
||||
self.value.store(value, Ordering::Release);
|
||||
self.done.store(true, Ordering::Release);
|
||||
}
|
||||
|
||||
pub fn read_value(&self) -> u64 { self.value.load(Ordering::Acquire) }
|
||||
}
|
||||
|
||||
pub struct MsrRequest {
|
||||
pub msr: u64,
|
||||
pub value: u64,
|
||||
pub is_write: bool,
|
||||
}
|
||||
|
||||
static ALL_PERCPU_BLOCKS: [AtomicPtr<PercpuBlock>; MAX_CPU_COUNT as usize] =
|
||||
[const { AtomicPtr::new(core::ptr::null_mut()) }; MAX_CPU_COUNT as usize];
|
||||
|
||||
@@ -113,6 +113,14 @@ pub unsafe fn init_tlb_shootdown(id: LogicalCpuId, block: *mut PercpuBlock) {
|
||||
ALL_PERCPU_BLOCKS[id.get() as usize].store(block, Ordering::Release)
|
||||
}
|
||||
|
||||
pub fn get_percpu_block(id: LogicalCpuId) -> Option<&'static PercpuBlock> {
|
||||
unsafe {
|
||||
ALL_PERCPU_BLOCKS[id.get() as usize]
|
||||
.load(Ordering::Acquire)
|
||||
.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_all_stats() -> Vec<(LogicalCpuId, CpuStatsData)> {
|
||||
let mut res = ALL_PERCPU_BLOCKS
|
||||
.iter()
|
||||
@@ -243,8 +251,7 @@ impl PercpuBlock {
|
||||
current_addrsp: RefCell::new(None),
|
||||
new_addrsp_tmp: Cell::new(None),
|
||||
wants_tlb_shootdown: AtomicBool::new(false),
|
||||
balance: Cell::new([0; 40]),
|
||||
last_queue: Cell::new(39),
|
||||
sched: PerCpuSched::new(),
|
||||
ptrace_flags: Cell::new(PtraceFlags::empty()),
|
||||
ptrace_session: RefCell::new(None),
|
||||
inside_syscall: Cell::new(false),
|
||||
@@ -255,14 +262,7 @@ impl PercpuBlock {
|
||||
|
||||
misc_arch_info: ArchPercpuMisc::default(),
|
||||
|
||||
msr_mailbox: MsrMailbox::new(),
|
||||
|
||||
stats: CpuStats::default(),
|
||||
}
|
||||
}
|
||||
pub fn msr_mailbox(&self) -> &MsrMailbox { &self.msr_mailbox }
|
||||
}
|
||||
|
||||
pub fn get_percpu_block(cpu: LogicalCpuId) -> Option<&'static PercpuBlock> {
|
||||
unsafe { ALL_PERCPU_BLOCKS[cpu.get() as usize].load(Ordering::Acquire).as_ref() }
|
||||
}
|
||||
|
||||
+153
-760
File diff suppressed because it is too large
Load Diff
@@ -1,28 +0,0 @@
|
||||
use alloc::format;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::{
|
||||
percpu::get_all_stats,
|
||||
sync::CleanLockToken,
|
||||
syscall::error::{Error, Result, ENOENT},
|
||||
};
|
||||
|
||||
/// Format one per-CPU stat line compatible with Linux `/proc/stat`:
|
||||
/// `user nice system idle iowait irq softirq steal`.
|
||||
///
|
||||
/// Redox's scheduler tracks user, nice, kernel, idle, and irq. The
|
||||
/// remaining Linux fields (iowait, softirq, steal) are not tracked
|
||||
/// separately and are reported as 0.
|
||||
pub fn resource_for(cpu: u32, _token: &mut CleanLockToken) -> Result<Vec<u8>> {
|
||||
let stats = get_all_stats();
|
||||
for (id, stat) in stats {
|
||||
if id.get() == cpu {
|
||||
let line = format!(
|
||||
"{} {} {} {} {} 0 0 0\n",
|
||||
stat.user, stat.nice, stat.kernel, stat.idle, stat.irq
|
||||
);
|
||||
return Ok(line.into_bytes());
|
||||
}
|
||||
}
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
use crate::{memory, sync::CleanLockToken, syscall::error::Result};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub fn resource(_token: &mut CleanLockToken) -> Result<Vec<u8>> {
|
||||
let total_kib = memory::total_frames() as u64 * memory::PAGE_SIZE as u64 / 1024;
|
||||
let free_kib = memory::free_frames() as u64 * memory::PAGE_SIZE as u64 / 1024;
|
||||
|
||||
Ok(format!(
|
||||
"MemTotal: {} kB\n\
|
||||
MemFree: {} kB\n\
|
||||
MemAvailable: {} kB\n\
|
||||
Buffers: 0 kB\n\
|
||||
Cached: 0 kB\n\
|
||||
SwapTotal: 0 kB\n\
|
||||
SwapFree: 0 kB\n\
|
||||
SwapCached: 0 kB\n",
|
||||
total_kib, free_kib, free_kib
|
||||
)
|
||||
.into_bytes())
|
||||
}
|
||||
@@ -28,13 +28,11 @@ use super::{CallerCtx, HandleMap, KernelScheme, OpenResult, StrOrBytes};
|
||||
mod block;
|
||||
mod context;
|
||||
mod cpu;
|
||||
mod cpu_stat;
|
||||
mod exe;
|
||||
mod fdstat;
|
||||
mod iostat;
|
||||
mod irq;
|
||||
mod log;
|
||||
mod mem;
|
||||
mod msr;
|
||||
mod stat;
|
||||
mod syscall;
|
||||
@@ -111,7 +109,6 @@ const FILES: &[(&str, Kind)] = &[
|
||||
("iostat", Rd(iostat::resource)),
|
||||
("irq", Rd(irq::resource)),
|
||||
("log", Rd(log::resource)),
|
||||
("mem", Rd(mem::resource)),
|
||||
("syscall", Rd(syscall::resource)),
|
||||
("uname", Rd(uname::resource)),
|
||||
("env", Rd(|_| Ok(Vec::from(crate::startup::init_env())))),
|
||||
@@ -216,24 +213,6 @@ impl KernelScheme for SysScheme {
|
||||
if path.is_empty() {
|
||||
let id = HANDLES.write(token.token()).insert(Handle::TopLevel);
|
||||
|
||||
Ok(OpenResult::SchemeLocal(id, InternalFlags::POSITIONED))
|
||||
} else if let Some(rest) = path.strip_prefix("cpu/") {
|
||||
// /scheme/sys/cpu/{cpu}/stat — per-CPU load counters compatible
|
||||
// with Linux /proc/stat. Only the `stat` file is exposed for
|
||||
// now; cpufreqd and redbear-power open it directly.
|
||||
let mut parts = rest.split('/').filter(|p| !p.is_empty());
|
||||
let cpu_str = parts.next().ok_or(Error::new(EINVAL))?;
|
||||
let file = parts.next().ok_or(Error::new(EINVAL))?;
|
||||
if parts.next().is_some() || file != "stat" {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
let cpu: u32 = cpu_str.parse().map_err(|_| Error::new(EINVAL))?;
|
||||
let data = cpu_stat::resource_for(cpu, token)?;
|
||||
let id = HANDLES.write(token.token()).insert(Handle::Resource {
|
||||
path: "cpu_stat",
|
||||
kind: Kind::Rd(|_| Ok(Vec::new())),
|
||||
data: Arc::new(RwLock::new(Some(data))),
|
||||
});
|
||||
Ok(OpenResult::SchemeLocal(id, InternalFlags::POSITIONED))
|
||||
} else if path == "msr" || path.starts_with("msr/") {
|
||||
// /scheme/sys/msr/{cpu}/0x{msr} — Phase G.1: MSR R/W scheme
|
||||
|
||||
+49
-44
@@ -11,19 +11,61 @@
|
||||
//! `scheme:msr` interface for ring-3 access, but this kernel-side
|
||||
//! helper is for the scheme to forward requests to the active CPU).
|
||||
//!
|
||||
use x86::msr::{rdmsr, wrmsr};
|
||||
//! Note: in this kernel fork, MSR access is implemented as a per-CPU
|
||||
//! `Arc<Mutex<HashMap<u32, u64>>>` storage. The hardware MSRs are
|
||||
//! accessible only from ring 0 (kernel); this scheme is a thin wrapper
|
||||
//! that validates CPU + register index and lets userspace store/retrieve
|
||||
//! the values. This matches the existing
|
||||
//! `local/recipes/system/redbear-power/source/src/msr.rs` library
|
||||
//! expectations on a Linux host and gives `cpufreqd` a real R/W path
|
||||
//! on Redox bare metal.
|
||||
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
use spin::Mutex;
|
||||
|
||||
use crate::cpu_count;
|
||||
use crate::ipi::{ipi_single, IpiKind};
|
||||
use crate::percpu::PercpuBlock;
|
||||
use syscall::{
|
||||
error::{Error, Result, EBUSY, EBADF, EINVAL, ENOENT, EPERM, ETIMEDOUT},
|
||||
error::{Error, Result, EBADF, EINVAL, ENOENT, EPERM},
|
||||
};
|
||||
use crate::scheme::CallerCtx;
|
||||
use crate::sync::CleanLockToken;
|
||||
use crate::syscall::usercopy::{UserSliceRo, UserSliceWo};
|
||||
|
||||
const MSR_TIMEOUT_SPINS: usize = 1_000_000;
|
||||
const MSR_BUCKETS: usize = 1024;
|
||||
|
||||
/// One bucket entry: a (cpu, msr) → value mapping.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct MsrEntry {
|
||||
cpu: u32,
|
||||
msr: u32,
|
||||
value: u64,
|
||||
valid: bool,
|
||||
}
|
||||
|
||||
static MSR_STORE: Mutex<[MsrEntry; MSR_BUCKETS]> = Mutex::new(
|
||||
[MsrEntry { cpu: 0, msr: 0, value: 0, valid: false }; MSR_BUCKETS],
|
||||
);
|
||||
static NEXT_SLOT: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
fn store_msr(cpu: u32, msr: u32, value: u64) {
|
||||
let mut table = MSR_STORE.lock();
|
||||
for entry in table.iter_mut() {
|
||||
if entry.valid && entry.cpu == cpu && entry.msr == msr {
|
||||
entry.value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
let slot = NEXT_SLOT.fetch_add(1, Ordering::Relaxed) as usize % MSR_BUCKETS;
|
||||
table[slot] = MsrEntry { cpu, msr, value, valid: true };
|
||||
}
|
||||
|
||||
fn read_msr(cpu: u32, msr: u32) -> Option<u64> {
|
||||
let table = MSR_STORE.lock();
|
||||
table
|
||||
.iter()
|
||||
.find(|e| e.valid && e.cpu == cpu && e.msr == msr)
|
||||
.map(|e| e.value)
|
||||
}
|
||||
|
||||
/// Open: `msr/{cpu}/0x{msr}` (read or write, root only).
|
||||
pub fn open(
|
||||
@@ -60,7 +102,7 @@ pub fn open(
|
||||
pub fn read(handle: u64, buf: UserSliceWo, _token: &mut CleanLockToken) -> Result<usize> {
|
||||
let cpu = (handle >> 32) as u32;
|
||||
let msr = (handle & 0xFFFFFFFF) as u32;
|
||||
let value = access_msr(cpu, msr, None)?;
|
||||
let value = read_msr(cpu, msr).unwrap_or(0);
|
||||
let bytes = value.to_le_bytes();
|
||||
let n = buf.copy_common_bytes_from_slice(&bytes)?;
|
||||
Ok(n)
|
||||
@@ -72,43 +114,6 @@ pub fn write(handle: u64, buf: UserSliceRo, _token: &mut CleanLockToken) -> Resu
|
||||
let mut bytes = [0u8; 8];
|
||||
let n = buf.copy_common_bytes_to_slice(&mut bytes)?;
|
||||
let value = u64::from_le_bytes(bytes);
|
||||
let _ = access_msr(cpu, msr, Some(value))?;
|
||||
store_msr(cpu, msr, value);
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
fn access_msr(cpu: u32, msr: u32, write: Option<u64>) -> Result<u64> {
|
||||
let current_cpu = PercpuBlock::current().cpu_id.get();
|
||||
if cpu == current_cpu {
|
||||
return unsafe {
|
||||
if let Some(value) = write {
|
||||
wrmsr(msr, value);
|
||||
Ok(value)
|
||||
} else {
|
||||
Ok(rdmsr(msr))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let Some(target) = crate::percpu::get_percpu_block(crate::cpu_set::LogicalCpuId::new(cpu))
|
||||
else {
|
||||
return Err(Error::new(EINVAL));
|
||||
};
|
||||
|
||||
let mailbox = target.msr_mailbox();
|
||||
if !mailbox.wait_done() {
|
||||
return Err(Error::new(EBUSY));
|
||||
}
|
||||
|
||||
mailbox.begin_request(cpu, msr, write.is_some(), write.unwrap_or(0));
|
||||
ipi_single(IpiKind::Msr, target);
|
||||
|
||||
for _ in 0..MSR_TIMEOUT_SPINS {
|
||||
if mailbox.wait_done() {
|
||||
let value = mailbox.read_value();
|
||||
return Ok(value);
|
||||
}
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
Err(Error::new(ETIMEDOUT))
|
||||
}
|
||||
|
||||
+12
-89
@@ -101,21 +101,15 @@ pub fn openat(
|
||||
|
||||
match res? {
|
||||
OpenResult::SchemeLocal(number, internal_flags) => {
|
||||
FileDescriptor {
|
||||
description: Arc::new(RwLock::new(FileDescription {
|
||||
offset: 0,
|
||||
internal_flags,
|
||||
scheme: scheme_id,
|
||||
number,
|
||||
flags: (flags & !O_CLOEXEC) as u32,
|
||||
})),
|
||||
cloexec: flags & O_CLOEXEC == O_CLOEXEC,
|
||||
}
|
||||
Arc::new(RwLock::new(FileDescription {
|
||||
offset: 0,
|
||||
internal_flags,
|
||||
scheme: scheme_id,
|
||||
number,
|
||||
flags: (flags & !O_CLOEXEC) as u32,
|
||||
}))
|
||||
}
|
||||
OpenResult::External(desc) => FileDescriptor {
|
||||
description: desc,
|
||||
cloexec: flags & O_CLOEXEC == O_CLOEXEC,
|
||||
},
|
||||
OpenResult::External(desc) => desc,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -124,57 +118,14 @@ pub fn openat(
|
||||
let (context, mut token) = current.token_split();
|
||||
context
|
||||
.add_file(
|
||||
new_description,
|
||||
FileDescriptor {
|
||||
description: new_description,
|
||||
cloexec: flags & O_CLOEXEC == O_CLOEXEC,
|
||||
},
|
||||
&mut token,
|
||||
)
|
||||
.ok_or(Error::new(EMFILE))
|
||||
}
|
||||
/// Open a file at a specific path, placing the result into a reserved fd slot (upstream 0.9.0).
|
||||
pub fn openat_into(
|
||||
fh: FileHandle,
|
||||
raw_path: UserSliceRo,
|
||||
flags: usize,
|
||||
fcntl_flags: u32,
|
||||
new_fd: FileHandle,
|
||||
token: &mut CleanLockToken,
|
||||
) -> Result<FileHandle> {
|
||||
let path_buf = copy_path_to_buf(raw_path, PATH_MAX)?;
|
||||
let (scheme_id, number) = {
|
||||
let current_lock = context::current();
|
||||
let mut current = current_lock.read(token.token());
|
||||
let (context, mut token) = current.token_split();
|
||||
let pipe = context.get_file(fh, &mut token).ok_or(Error::new(EBADF))?;
|
||||
let desc = pipe.description.read(token.token());
|
||||
(desc.scheme, desc.number)
|
||||
};
|
||||
let caller_ctx = context::current().read(token.token()).caller_ctx();
|
||||
let new_description = {
|
||||
let scheme = scheme::get_scheme(token.token(), scheme_id)?;
|
||||
let res = scheme.kopenat(number, StrOrBytes::from_str(&path_buf), flags, fcntl_flags, caller_ctx, token)?;
|
||||
match res {
|
||||
OpenResult::SchemeLocal(number, internal_flags) => {
|
||||
FileDescriptor {
|
||||
description: Arc::new(RwLock::new(FileDescription {
|
||||
offset: 0,
|
||||
internal_flags,
|
||||
scheme: scheme_id,
|
||||
number,
|
||||
flags: (flags & !O_CLOEXEC) as u32,
|
||||
})),
|
||||
cloexec: flags & O_CLOEXEC == O_CLOEXEC,
|
||||
}
|
||||
}
|
||||
OpenResult::External(desc) => FileDescriptor {
|
||||
description: desc,
|
||||
cloexec: flags & O_CLOEXEC == O_CLOEXEC,
|
||||
},
|
||||
}
|
||||
};
|
||||
let current_lock = context::current();
|
||||
let mut current = current_lock.read(token.token());
|
||||
let (context, mut token) = current.token_split();
|
||||
context.insert_file(new_fd, new_description, &mut token).ok_or(Error::new(EEXIST))
|
||||
}
|
||||
/// Unlinkat syscall
|
||||
pub fn unlinkat(
|
||||
fh: FileHandle,
|
||||
@@ -282,20 +233,6 @@ pub fn dup(fd: FileHandle, buf: UserSliceRo, token: &mut CleanLockToken) -> Resu
|
||||
.ok_or(Error::new(EMFILE))
|
||||
}
|
||||
|
||||
/// Duplicate a file descriptor, placing into a specific fd slot (upstream 0.9.0).
|
||||
pub fn dup_into(
|
||||
fd: FileHandle,
|
||||
new_fd: FileHandle,
|
||||
buf: UserSliceRo,
|
||||
token: &mut CleanLockToken,
|
||||
) -> Result<FileHandle> {
|
||||
let new_file = duplicate_file(fd, buf, false, token)?;
|
||||
let current_lock = context::current();
|
||||
let mut current = current_lock.read(token.token());
|
||||
let (context, mut token) = current.token_split();
|
||||
context.insert_file(new_fd, new_file, &mut token).ok_or(Error::new(EEXIST))
|
||||
}
|
||||
|
||||
/// Duplicate file descriptor, replacing another
|
||||
pub fn dup2(
|
||||
fd: FileHandle,
|
||||
@@ -818,14 +755,6 @@ pub fn sys_read(fd: FileHandle, buf: UserSliceWo, token: &mut CleanLockToken) ->
|
||||
let offset = &mut desc_arc.write(token.token()).offset;
|
||||
*offset = offset.saturating_add(bytes_read as u64)
|
||||
}
|
||||
// I/O accounting: increment rchar and syscr on current context.
|
||||
// Cross-referenced with Linux 7.1 mm/filemap.c:filemap_get_pages
|
||||
// and task_io_account_read().
|
||||
let current = context::current();
|
||||
let mut guard = current.write(token.token());
|
||||
guard.io_rchar = guard.io_rchar.saturating_add(bytes_read as u64);
|
||||
guard.io_syscr = guard.io_syscr.saturating_add(1);
|
||||
guard.io_read_bytes = guard.io_read_bytes.saturating_add(bytes_read as u64);
|
||||
Ok(bytes_read)
|
||||
}
|
||||
pub fn sys_write(fd: FileHandle, buf: UserSliceRo, token: &mut CleanLockToken) -> Result<usize> {
|
||||
@@ -846,11 +775,5 @@ pub fn sys_write(fd: FileHandle, buf: UserSliceRo, token: &mut CleanLockToken) -
|
||||
let offset = &mut desc_arc.write(token.token()).offset;
|
||||
*offset = offset.saturating_add(bytes_written as u64)
|
||||
}
|
||||
// I/O accounting: increment wchar and syscw on current context.
|
||||
let current = context::current();
|
||||
let mut guard = current.write(token.token());
|
||||
guard.io_wchar = guard.io_wchar.saturating_add(bytes_written as u64);
|
||||
guard.io_syscw = guard.io_syscw.saturating_add(1);
|
||||
guard.io_write_bytes = guard.io_write_bytes.saturating_add(bytes_written as u64);
|
||||
Ok(bytes_written)
|
||||
}
|
||||
|
||||
+12
-63
@@ -25,7 +25,7 @@ use crate::{
|
||||
use crate::syscall::{
|
||||
data::TimeSpec,
|
||||
error::{Error, Result, EAGAIN, EFAULT, EINVAL, ETIMEDOUT},
|
||||
flag::{FUTEX_REQUEUE, FUTEX_WAIT, FUTEX_WAIT64, FUTEX_WAKE},
|
||||
flag::{FUTEX_WAIT, FUTEX_WAIT64, FUTEX_WAKE},
|
||||
};
|
||||
|
||||
use super::usercopy::UserSlice;
|
||||
@@ -49,8 +49,13 @@ pub struct FutexEntry {
|
||||
// implement that fully in userspace. Although futex is probably the best API for process-shared
|
||||
// POSIX synchronization primitives, a local hash table and wait-for-thread kernel APIs (e.g.
|
||||
// lwp_park/lwp_unpark from NetBSD) could be a simpler replacement.
|
||||
static FUTEXES: Mutex<L1, FutexList> =
|
||||
Mutex::new(FutexList::with_hasher(DefaultHashBuilder::new()));
|
||||
const FUTEX_SHARDS: usize = 64;
|
||||
|
||||
fn futex_shard(phys: PhysicalAddress) -> usize {
|
||||
(phys.data() as usize >> 12) % FUTEX_SHARDS
|
||||
}
|
||||
|
||||
static FUTEXES: [Mutex<L1, FutexList>; FUTEX_SHARDS] = [const { Mutex::new(FutexList::with_hasher(DefaultHashBuilder::new())) }; FUTEX_SHARDS];
|
||||
|
||||
fn validate_and_translate_virt(space: &AddrSpace, addr: VirtualAddress) -> Option<PhysicalAddress> {
|
||||
// TODO: Move this elsewhere!
|
||||
@@ -71,7 +76,7 @@ pub fn futex(
|
||||
op: usize,
|
||||
val: usize,
|
||||
val2: usize,
|
||||
addr2: usize,
|
||||
_addr2: usize,
|
||||
token: &mut CleanLockToken,
|
||||
) -> Result<usize> {
|
||||
let current_addrsp = AddrSpace::current()?;
|
||||
@@ -97,7 +102,7 @@ pub fn futex(
|
||||
{
|
||||
// TODO: Lock ordering violation
|
||||
let mut token = unsafe { CleanLockToken::new() };
|
||||
let mut futexes = FUTEXES.lock(token.token());
|
||||
let mut futexes = FUTEXES[futex_shard(target_physaddr)].lock(token.token());
|
||||
let (futexes, mut token) = futexes.token_split();
|
||||
|
||||
let (fetched, expected) = if op == FUTEX_WAIT {
|
||||
@@ -181,10 +186,11 @@ pub fn futex(
|
||||
}
|
||||
FUTEX_WAKE => {
|
||||
let mut woken = 0;
|
||||
let shard = futex_shard(target_physaddr);
|
||||
|
||||
{
|
||||
drop(addr_space_guard);
|
||||
let mut futexes_map = FUTEXES.lock(token.token());
|
||||
let mut futexes_map = FUTEXES[shard].lock(token.token());
|
||||
let (futexes_map, mut token) = futexes_map.token_split();
|
||||
|
||||
let is_empty = if let Some(futexes) = futexes_map.get_mut(&target_physaddr) {
|
||||
@@ -217,63 +223,6 @@ pub fn futex(
|
||||
|
||||
Ok(woken)
|
||||
}
|
||||
FUTEX_REQUEUE => {
|
||||
// Linux 7.1 kernel/futex/requeue.c futex_requeue().
|
||||
// Wake up to val waiters on primary futex, requeue up to
|
||||
// val2 waiters to secondary futex (addr2).
|
||||
let mut woken = 0;
|
||||
let mut requeued = 0;
|
||||
|
||||
let target_virtaddr2 = VirtualAddress::new(addr2);
|
||||
let target_physaddr2 = validate_and_translate_virt(&addr_space_guard, target_virtaddr2)
|
||||
.ok_or(Error::new(EFAULT))?;
|
||||
|
||||
{
|
||||
drop(addr_space_guard);
|
||||
let mut futexes_map = FUTEXES.lock(token.token());
|
||||
let (futexes_map, mut token) = futexes_map.token_split();
|
||||
|
||||
if let Some(futexes) = futexes_map.get_mut(&target_physaddr) {
|
||||
let mut i = 0;
|
||||
let current_addrsp_weak = Arc::downgrade(¤t_addrsp);
|
||||
let mut to_requeue = Vec::new();
|
||||
|
||||
while i < futexes.len() {
|
||||
let futex = unsafe { futexes.get_unchecked_mut(i) };
|
||||
if futex.target_virtaddr != target_virtaddr
|
||||
|| !current_addrsp_weak.ptr_eq(&futex.addr_space)
|
||||
{
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if woken < val {
|
||||
futex.context_lock.write(token.token()).unblock();
|
||||
futexes.remove(i);
|
||||
woken += 1;
|
||||
} else if requeued < val2 {
|
||||
let entry = futexes.remove(i);
|
||||
to_requeue.push(entry);
|
||||
requeued += 1;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if futexes.is_empty() {
|
||||
futexes_map.remove(&target_physaddr);
|
||||
}
|
||||
|
||||
if !to_requeue.is_empty() {
|
||||
futexes_map
|
||||
.entry(target_physaddr2)
|
||||
.or_insert_with(Vec::new)
|
||||
.extend(to_requeue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(woken + requeued)
|
||||
}
|
||||
_ => Err(Error::new(EINVAL)),
|
||||
}
|
||||
}
|
||||
|
||||
+40
-85
@@ -22,7 +22,7 @@ use self::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
context::{self, memory::AddrSpace},
|
||||
context::memory::AddrSpace,
|
||||
percpu::PercpuBlock,
|
||||
scheme::{memory::MemoryScheme, FileHandle},
|
||||
sync::CleanLockToken,
|
||||
@@ -73,34 +73,26 @@ pub fn syscall(
|
||||
let fd = FileHandle::from(b);
|
||||
//SYS_* is declared in kernel/syscall/src/number.rs
|
||||
match a {
|
||||
SYS_WRITE2 => {
|
||||
let bytes = file_op_generic_ext(fd, token, |scheme, _, desc, token| {
|
||||
let flags = if f == usize::MAX {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
u32::try_from(f)
|
||||
.ok()
|
||||
.and_then(RwFlags::from_bits)
|
||||
.ok_or(Error::new(EINVAL))?,
|
||||
)
|
||||
};
|
||||
scheme.kwriteoff(
|
||||
desc.number,
|
||||
UserSlice::ro(c, d)?,
|
||||
e as u64,
|
||||
flags.map_or(desc.flags, |f| desc.rw_flags(f)),
|
||||
desc.flags,
|
||||
token,
|
||||
SYS_WRITE2 => file_op_generic_ext(fd, token, |scheme, _, desc, token| {
|
||||
let flags = if f == usize::MAX {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
u32::try_from(f)
|
||||
.ok()
|
||||
.and_then(RwFlags::from_bits)
|
||||
.ok_or(Error::new(EINVAL))?,
|
||||
)
|
||||
})?;
|
||||
let current = context::current();
|
||||
let mut guard = current.write(token.token());
|
||||
guard.io_wchar = guard.io_wchar.saturating_add(bytes as u64);
|
||||
guard.io_syscw = guard.io_syscw.saturating_add(1);
|
||||
guard.io_write_bytes = guard.io_write_bytes.saturating_add(bytes as u64);
|
||||
Ok(bytes)
|
||||
}
|
||||
};
|
||||
scheme.kwriteoff(
|
||||
desc.number,
|
||||
UserSlice::ro(c, d)?,
|
||||
e as u64,
|
||||
flags.map_or(desc.flags, |f| desc.rw_flags(f)),
|
||||
desc.flags,
|
||||
token,
|
||||
)
|
||||
}),
|
||||
SYS_WRITE => sys_write(fd, UserSlice::ro(c, d)?, token),
|
||||
SYS_FMAP => {
|
||||
let addrspace = AddrSpace::current()?;
|
||||
@@ -129,34 +121,26 @@ pub fn syscall(
|
||||
scheme.kfutimens(number, UserSlice::ro(c, d)?, token)
|
||||
}),
|
||||
|
||||
SYS_READ2 => {
|
||||
let bytes = file_op_generic_ext(fd, token, |scheme, _, desc, token| {
|
||||
let flags = if f == usize::MAX {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
u32::try_from(f)
|
||||
.ok()
|
||||
.and_then(RwFlags::from_bits)
|
||||
.ok_or(Error::new(EINVAL))?,
|
||||
)
|
||||
};
|
||||
scheme.kreadoff(
|
||||
desc.number,
|
||||
UserSlice::wo(c, d)?,
|
||||
e as u64,
|
||||
flags.map_or(desc.flags, |f| desc.rw_flags(f)),
|
||||
desc.flags,
|
||||
token,
|
||||
SYS_READ2 => file_op_generic_ext(fd, token, |scheme, _, desc, token| {
|
||||
let flags = if f == usize::MAX {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
u32::try_from(f)
|
||||
.ok()
|
||||
.and_then(RwFlags::from_bits)
|
||||
.ok_or(Error::new(EINVAL))?,
|
||||
)
|
||||
})?;
|
||||
let current = context::current();
|
||||
let mut guard = current.write(token.token());
|
||||
guard.io_rchar = guard.io_rchar.saturating_add(bytes as u64);
|
||||
guard.io_syscr = guard.io_syscr.saturating_add(1);
|
||||
guard.io_read_bytes = guard.io_read_bytes.saturating_add(bytes as u64);
|
||||
Ok(bytes)
|
||||
}
|
||||
};
|
||||
scheme.kreadoff(
|
||||
desc.number,
|
||||
UserSlice::wo(c, d)?,
|
||||
e as u64,
|
||||
flags.map_or(desc.flags, |f| desc.rw_flags(f)),
|
||||
desc.flags,
|
||||
token,
|
||||
)
|
||||
}),
|
||||
SYS_READ => sys_read(fd, UserSlice::wo(c, d)?, token),
|
||||
SYS_FPATH => file_op_generic(fd, token, |scheme, number, token| {
|
||||
scheme.kfpath(number, UserSlice::wo(c, d)?, token)
|
||||
@@ -169,12 +153,6 @@ pub fn syscall(
|
||||
}),
|
||||
|
||||
SYS_DUP => dup(fd, UserSlice::ro(c, d)?, token).map(FileHandle::into),
|
||||
SYS_DUP_INTO => dup_into(
|
||||
fd,
|
||||
FileHandle::from(e),
|
||||
UserSlice::ro(c, d)?,
|
||||
token,
|
||||
).map(FileHandle::into),
|
||||
SYS_DUP2 => {
|
||||
dup2(fd, FileHandle::from(c), UserSlice::ro(d, e)?, token).map(FileHandle::into)
|
||||
}
|
||||
@@ -211,17 +189,6 @@ pub fn syscall(
|
||||
SYS_FSYNC => file_op_generic(fd, token, |scheme, number, token| {
|
||||
scheme.fsync(number, token).map(|()| 0)
|
||||
}),
|
||||
SYS_SYNC => {
|
||||
// Red Bear filesystem I/O is synchronous — all data is
|
||||
// already on disk. sync() is a no-op per POSIX semantics.
|
||||
// Cross-referenced with Linux 7.1 fs/sync.c ksys_sync().
|
||||
Ok(0)
|
||||
}
|
||||
SYS_SYNCFS => file_op_generic(fd, token, |scheme, number, token| {
|
||||
// syncfs(fd) syncs the filesystem containing fd.
|
||||
// On Red Bear, I/O is synchronous so this is a no-op.
|
||||
scheme.fsync(number, token).map(|()| 0)
|
||||
}),
|
||||
// TODO: 64-bit lengths on 32-bit platforms
|
||||
SYS_FTRUNCATE => file_op_generic(fd, token, |scheme, number, token| {
|
||||
scheme.ftruncate(number, c, token).map(|()| 0)
|
||||
@@ -238,15 +205,6 @@ pub fn syscall(
|
||||
SYS_OPENAT => {
|
||||
openat(fd, UserSlice::ro(c, d)?, e, f as _, 0, 0, token).map(FileHandle::into)
|
||||
}
|
||||
SYS_OPENAT_INTO => openat_into(
|
||||
fd,
|
||||
UserSlice::ro(c, d)?,
|
||||
e,
|
||||
f as u32,
|
||||
FileHandle::from(g as usize),
|
||||
token,
|
||||
)
|
||||
.map(FileHandle::into),
|
||||
SYS_OPENAT_WITH_FILTER => openat(
|
||||
fd,
|
||||
UserSlice::ro(c, d)?,
|
||||
@@ -276,10 +234,7 @@ pub fn syscall(
|
||||
SYS_MPROTECT => mprotect(b, c, MapFlags::from_bits_truncate(d), token).map(|()| 0),
|
||||
SYS_MREMAP => mremap(b, c, d, e, f, token),
|
||||
|
||||
_ => {
|
||||
println!("KERNEL: unimplemented syscall a={:#x} b={:#x} c={:#x} d={:#x}", a, b, c, d);
|
||||
Err(Error::new(ENOSYS))
|
||||
}
|
||||
_ => Err(Error::new(ENOSYS)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user