Merge remote-tracking branch 'upstream/master' into kernel-0.3.0
# Conflicts: # src/acpi/mod.rs # src/acpi/rsdt.rs # src/acpi/rxsdt.rs # src/acpi/srat/aarch64.rs # src/acpi/xsdt.rs # src/arch/x86_shared/start.rs # src/context/mod.rs # src/context/switch.rs # src/main.rs # src/memory/mod.rs # src/numa.rs # src/scheme/sys/mod.rs # src/startup/mod.rs
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![allow(clippy::new_without_default)]
|
#![allow(clippy::new_without_default)]
|
||||||
|
|
||||||
|
use core::ops::Add;
|
||||||
|
|
||||||
pub use crate::{allocator::*, arch::*, page::*};
|
pub use crate::{allocator::*, arch::*, page::*};
|
||||||
|
|
||||||
mod allocator;
|
mod allocator;
|
||||||
|
|||||||
+32
-53
@@ -1,29 +1,34 @@
|
|||||||
//! # ACPI
|
//! # ACPI
|
||||||
//! Code to parse the ACPI tables
|
//! Code to parse the ACPI tables
|
||||||
|
|
||||||
use alloc::{boxed::Box, string::String, vec::Vec};
|
|
||||||
|
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
|
|
||||||
|
use alloc::{boxed::Box, string::String, vec::Vec};
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use spin::{Once, RwLock};
|
use spin::{Once, RwLock};
|
||||||
|
|
||||||
use crate::memory::{KernelMapper, PageFlags, PhysicalAddress, RmmA, RmmArch};
|
use crate::{
|
||||||
|
acpi::rxsdt::RxsdtIter,
|
||||||
|
memory::{KernelMapper, PageFlags, PhysicalAddress, RmmA, RmmArch},
|
||||||
|
};
|
||||||
|
|
||||||
use self::{hpet::Hpet, madt::Madt, rsdp::Rsdp, rsdt::Rsdt, rxsdt::Rxsdt, sdt::Sdt, xsdt::Xsdt};
|
use self::{hpet::Hpet, madt::Madt, rsdp::Rsdp, rsdt::Rsdt, rxsdt::Rxsdt, sdt::Sdt, xsdt::Xsdt};
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
mod gtdt;
|
mod gtdt;
|
||||||
pub mod fadt;
|
|
||||||
pub mod facs;
|
|
||||||
pub mod hpet;
|
pub mod hpet;
|
||||||
pub mod madt;
|
pub mod madt;
|
||||||
mod rsdp;
|
mod rsdp;
|
||||||
mod rsdt;
|
mod rsdt;
|
||||||
mod rxsdt;
|
mod rxsdt;
|
||||||
pub mod sdt;
|
pub mod sdt;
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
|
||||||
|
pub mod slit;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
mod spcr;
|
mod spcr;
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
|
||||||
|
pub mod srat;
|
||||||
mod xsdt;
|
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 crate::memory::PageMapper) {
|
||||||
@@ -76,24 +81,20 @@ pub enum RxsdtEnum {
|
|||||||
Xsdt(Xsdt),
|
Xsdt(Xsdt),
|
||||||
}
|
}
|
||||||
impl Rxsdt for RxsdtEnum {
|
impl Rxsdt for RxsdtEnum {
|
||||||
fn iter(&self) -> Box<dyn Iterator<Item = PhysicalAddress>> {
|
fn iter(&self) -> RxsdtIter {
|
||||||
match self {
|
match self {
|
||||||
Self::Rsdt(rsdt) => <Rsdt as Rxsdt>::iter(rsdt),
|
Self::Rsdt(rsdt) => rsdt.iter(),
|
||||||
Self::Xsdt(xsdt) => <Xsdt as Rxsdt>::iter(xsdt),
|
Self::Xsdt(xsdt) => xsdt.iter(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static RXSDT_ENUM: Once<RxsdtEnum> = Once::new();
|
pub static RXSDT_ENUM: Once<RxsdtEnum> = Once::new();
|
||||||
|
|
||||||
/// Parse the ACPI tables to gather CPU, interrupt, and timer information
|
/// Initialses the global `RXSDT_ENUM` if RSDT or XSDT was found and maps the SDT pages.
|
||||||
pub unsafe fn init(already_supplied_rsdp: Option<NonNull<u8>>) {
|
/// It does not perform any allocations
|
||||||
|
pub unsafe fn init_before_mem(already_supplied_rsdp: Option<NonNull<u8>>) {
|
||||||
unsafe {
|
unsafe {
|
||||||
{
|
|
||||||
let mut sdt_ptrs = SDT_POINTERS.write();
|
|
||||||
*sdt_ptrs = Some(HashMap::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for RSDP
|
// Search for RSDP
|
||||||
let rsdp_opt = Rsdp::get_rsdp(already_supplied_rsdp);
|
let rsdp_opt = Rsdp::get_rsdp(already_supplied_rsdp);
|
||||||
|
|
||||||
@@ -138,6 +139,22 @@ pub unsafe fn init(already_supplied_rsdp: Option<NonNull<u8>>) {
|
|||||||
for sdt in rxsdt.iter() {
|
for sdt in rxsdt.iter() {
|
||||||
get_sdt(sdt, &mut KernelMapper::lock_rw());
|
get_sdt(sdt, &mut KernelMapper::lock_rw());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
error!("NO RSDP FOUND");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse the ACPI tables to gather CPU, interrupt, and timer information. The code performs allocations, so
|
||||||
|
/// it must be called only after the allocator is set up.
|
||||||
|
pub unsafe fn init_after_mem(already_supplied_rsdp: Option<NonNull<u8>>) {
|
||||||
|
if let Some(rxsdt) = RXSDT_ENUM.get() {
|
||||||
|
unsafe {
|
||||||
|
{
|
||||||
|
let mut sdt_ptrs = SDT_POINTERS.write();
|
||||||
|
*sdt_ptrs = Some(HashMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
for sdt_address in rxsdt.iter() {
|
for sdt_address in rxsdt.iter() {
|
||||||
let sdt = &*(RmmA::phys_to_virt(sdt_address).data() as *const Sdt);
|
let sdt = &*(RmmA::phys_to_virt(sdt_address).data() as *const Sdt);
|
||||||
@@ -160,44 +177,6 @@ pub unsafe fn init(already_supplied_rsdp: Option<NonNull<u8>>) {
|
|||||||
Hpet::init();
|
Hpet::init();
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
gtdt::Gtdt::init();
|
gtdt::Gtdt::init();
|
||||||
// Phase II: parse the FADT to extract the PM1a_CNT
|
|
||||||
// and PM1a_STS port addresses used by the S3 entry
|
|
||||||
// path. Hardware-agnostic — works on any platform
|
|
||||||
// with a working FADT.
|
|
||||||
if let Some(fadt_sdts) = find_sdt("FACP").first() {
|
|
||||||
fadt::init(fadt_sdts);
|
|
||||||
} else {
|
|
||||||
warn!("ACPI: no FADT (FACP) found, S3 entry path disabled");
|
|
||||||
}
|
|
||||||
// Phase II.X.W: parse the FACS to extract the
|
|
||||||
// xfirmware_waking_vector. This is the address the
|
|
||||||
// platform firmware jumps to on S3 wake. The kernel's
|
|
||||||
// S3 resume trampoline in arch/x86_shared/s3_resume.rs
|
|
||||||
// is written to this address by acpid via the
|
|
||||||
// SetS3WakingVector AcPiVerb.
|
|
||||||
//
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
};
|
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-23
@@ -2,6 +2,8 @@ use alloc::boxed::Box;
|
|||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use rmm::PhysicalAddress;
|
use rmm::PhysicalAddress;
|
||||||
|
|
||||||
|
use crate::acpi::{rxsdt::RxsdtIter, RxsdtEnum};
|
||||||
|
|
||||||
use super::{rxsdt::Rxsdt, sdt::Sdt};
|
use super::{rxsdt::Rxsdt, sdt::Sdt};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -24,29 +26,11 @@ impl Rsdt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Rxsdt for Rsdt {
|
impl Rxsdt for Rsdt {
|
||||||
fn iter(&self) -> Box<dyn Iterator<Item = PhysicalAddress>> {
|
fn iter(&self) -> RxsdtIter {
|
||||||
Box::new(RsdtIter { sdt: self.0, i: 0 })
|
RxsdtIter {
|
||||||
}
|
sdt: self.0,
|
||||||
}
|
i: 0,
|
||||||
|
rxsdt_enum: RxsdtEnum::Rsdt(Rsdt(self.0)),
|
||||||
pub struct RsdtIter {
|
|
||||||
sdt: &'static Sdt,
|
|
||||||
i: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for RsdtIter {
|
|
||||||
type Item = PhysicalAddress;
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.i < self.sdt.data_len() / size_of::<u32>() {
|
|
||||||
let item = unsafe {
|
|
||||||
(self.sdt.data_address() as *const u32)
|
|
||||||
.add(self.i)
|
|
||||||
.read_unaligned()
|
|
||||||
};
|
|
||||||
self.i += 1;
|
|
||||||
Some(PhysicalAddress::new(item as usize))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-1
@@ -1,6 +1,36 @@
|
|||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use rmm::PhysicalAddress;
|
use rmm::PhysicalAddress;
|
||||||
|
|
||||||
|
use crate::acpi::{sdt::Sdt, RxsdtEnum};
|
||||||
|
|
||||||
pub trait Rxsdt {
|
pub trait Rxsdt {
|
||||||
fn iter(&self) -> Box<dyn Iterator<Item = PhysicalAddress>>;
|
fn iter(&self) -> RxsdtIter;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RxsdtIter {
|
||||||
|
pub sdt: &'static Sdt,
|
||||||
|
pub i: usize,
|
||||||
|
pub rxsdt_enum: RxsdtEnum,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for RxsdtIter {
|
||||||
|
type Item = PhysicalAddress;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.i < self.sdt.data_len() / size_of::<u64>() {
|
||||||
|
let item = unsafe {
|
||||||
|
match self.rxsdt_enum {
|
||||||
|
RxsdtEnum::Rsdt(_) => PhysicalAddress::new(core::ptr::read_unaligned(
|
||||||
|
(self.sdt.data_address() as *const u32).add(self.i),
|
||||||
|
) as usize),
|
||||||
|
RxsdtEnum::Xsdt(_) => PhysicalAddress::new(core::ptr::read_unaligned(
|
||||||
|
(self.sdt.data_address() as *const u64).add(self.i),
|
||||||
|
) as usize),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.i += 1;
|
||||||
|
Some(item)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
use core::{ops::Add, slice, u32};
|
||||||
|
|
||||||
|
use rmm::{Arch, BumpAllocator, FrameAllocator, FrameCount, PhysicalAddress};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
acpi::srat::{to_usize, Srat, SratEntry},
|
||||||
|
cpu_set::MAX_CPU_COUNT,
|
||||||
|
memory::{round_up_pages, PAGE_SIZE},
|
||||||
|
numa::{self, assign_memory_id, assign_node_id, NumaMemory},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init_srat(dom_node_map: &mut [u32], cpus: &mut [u32], mem: &mut [NumaMemory], srat: &Srat) {
|
||||||
|
let mut cpu_count = 0;
|
||||||
|
let mut memory_count = 0;
|
||||||
|
|
||||||
|
srat.into_iter().for_each(|e| match e {
|
||||||
|
SratEntry::GiccAffinity(gicc_affinity) => {
|
||||||
|
if gicc_affinity.flags & 1 != 0 {
|
||||||
|
cpu_count += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SratEntry::MemoryAffinity(memory_affinity) => {
|
||||||
|
if memory_affinity.flags & 1 != 0 && memory_affinity.flags & (1 << 1) == 0 {
|
||||||
|
memory_count += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
memory_count <= numa::MAX_DOMAINS,
|
||||||
|
"Found {} memory blocks while only a maximum of {} are supported",
|
||||||
|
memory_count,
|
||||||
|
numa::MAX_DOMAINS
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
cpu_count <= MAX_CPU_COUNT,
|
||||||
|
"Found more number of CPUs than supported"
|
||||||
|
);
|
||||||
|
|
||||||
|
for affinity in srat {
|
||||||
|
match affinity {
|
||||||
|
SratEntry::MemoryAffinity(memory_affinity) => {
|
||||||
|
let start = to_usize(
|
||||||
|
memory_affinity.base_address_low,
|
||||||
|
memory_affinity.base_address_high,
|
||||||
|
);
|
||||||
|
let length = to_usize(memory_affinity.length_low, memory_affinity.length_high);
|
||||||
|
if length == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if memory_affinity.flags & 1 == 0 {
|
||||||
|
// memory disabled
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if memory_affinity.flags & (1 << 1) != 0 {
|
||||||
|
// memory hot-pluggable
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if dom_node_map[memory_affinity.proximity_domain as usize] == u32::MAX {
|
||||||
|
let node = assign_node_id(true);
|
||||||
|
dom_node_map[memory_affinity.proximity_domain as usize] = node as u32;
|
||||||
|
}
|
||||||
|
let mem_id = assign_memory_id() as u32;
|
||||||
|
mem[mem_id as usize] = NumaMemory {
|
||||||
|
start,
|
||||||
|
length,
|
||||||
|
node_id: dom_node_map[memory_affinity.proximity_domain as usize],
|
||||||
|
_pad: [0u8; 4],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
SratEntry::GiccAffinity(gicc_affinity) => {
|
||||||
|
if gicc_affinity.flags & 1 == 0 {
|
||||||
|
// disabled
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let id = gicc_affinity.processor_uid;
|
||||||
|
let dom = gicc_affinity.proximity_domain;
|
||||||
|
if dom_node_map[dom as usize] == u32::MAX {
|
||||||
|
let node = assign_node_id(true);
|
||||||
|
dom_node_map[dom as usize] = node as u32;
|
||||||
|
}
|
||||||
|
cpus[id as usize] = dom_node_map[dom as usize];
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-21
@@ -2,6 +2,8 @@ use alloc::boxed::Box;
|
|||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use rmm::PhysicalAddress;
|
use rmm::PhysicalAddress;
|
||||||
|
|
||||||
|
use crate::acpi::{rxsdt::RxsdtIter, RxsdtEnum};
|
||||||
|
|
||||||
use super::{rxsdt::Rxsdt, sdt::Sdt};
|
use super::{rxsdt::Rxsdt, sdt::Sdt};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -24,27 +26,11 @@ impl Xsdt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Rxsdt for Xsdt {
|
impl Rxsdt for Xsdt {
|
||||||
fn iter(&self) -> Box<dyn Iterator<Item = PhysicalAddress>> {
|
fn iter(&self) -> RxsdtIter {
|
||||||
Box::new(XsdtIter { sdt: self.0, i: 0 })
|
RxsdtIter {
|
||||||
}
|
sdt: self.0,
|
||||||
}
|
i: 0,
|
||||||
|
rxsdt_enum: RxsdtEnum::Xsdt(Xsdt(self.0)),
|
||||||
pub struct XsdtIter {
|
|
||||||
sdt: &'static Sdt,
|
|
||||||
i: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for XsdtIter {
|
|
||||||
type Item = PhysicalAddress;
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.i < self.sdt.data_len() / size_of::<u64>() {
|
|
||||||
let item = unsafe {
|
|
||||||
core::ptr::read_unaligned((self.sdt.data_address() as *const u64).add(self.i))
|
|
||||||
};
|
|
||||||
self.i += 1;
|
|
||||||
Some(PhysicalAddress::new(item as usize))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ use crate::{
|
|||||||
startup::KernelArgs,
|
startup::KernelArgs,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::numa;
|
||||||
|
|
||||||
/// Test of zero values in BSS.
|
/// Test of zero values in BSS.
|
||||||
static BSS_TEST_ZERO: SyncUnsafeCell<usize> = SyncUnsafeCell::new(0);
|
static BSS_TEST_ZERO: SyncUnsafeCell<usize> = SyncUnsafeCell::new(0);
|
||||||
/// Test of non-zero values in data.
|
/// Test of non-zero values in data.
|
||||||
@@ -102,13 +104,22 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
|
|||||||
|
|
||||||
// Initialize RMM
|
// Initialize RMM
|
||||||
#[cfg(target_arch = "x86")]
|
#[cfg(target_arch = "x86")]
|
||||||
crate::startup::memory::init(&args, Some(0x100000), Some(0x40000000));
|
let mut bump_allocator =
|
||||||
|
crate::startup::memory::init(&args, Some(0x100000), Some(0x40000000));
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
crate::startup::memory::init(&args, Some(0x100000), None);
|
let mut bump_allocator = crate::startup::memory::init(&args, Some(0x100000), None);
|
||||||
|
|
||||||
// Initialize paging
|
// Initialize paging
|
||||||
paging::init();
|
paging::init();
|
||||||
|
|
||||||
|
if cfg!(feature = "acpi") {
|
||||||
|
crate::acpi::init_before_mem(args.acpi_rsdp());
|
||||||
|
}
|
||||||
|
|
||||||
|
numa::init(&mut bump_allocator);
|
||||||
|
|
||||||
|
crate::memory::init_mm(bump_allocator);
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
crate::arch::alternative::early_init(true);
|
crate::arch::alternative::early_init(true);
|
||||||
|
|
||||||
@@ -130,7 +141,7 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
|
|||||||
|
|
||||||
// Read ACPI tables, starts APs
|
// Read ACPI tables, starts APs
|
||||||
if cfg!(feature = "acpi") {
|
if cfg!(feature = "acpi") {
|
||||||
crate::acpi::init(args.acpi_rsdp());
|
crate::acpi::init_after_mem(args.acpi_rsdp());
|
||||||
device::init_after_acpi();
|
device::init_after_acpi();
|
||||||
}
|
}
|
||||||
crate::profiling::init();
|
crate::profiling::init();
|
||||||
|
|||||||
@@ -314,6 +314,7 @@ impl Context {
|
|||||||
pub fn unblock_no_ipi(&mut self) -> bool {
|
pub fn unblock_no_ipi(&mut self) -> bool {
|
||||||
if self.status.is_soft_blocked() {
|
if self.status.is_soft_blocked() {
|
||||||
self.status = Status::Runnable;
|
self.status = Status::Runnable;
|
||||||
|
self.wake = None;
|
||||||
self.status_reason = "";
|
self.status_reason = "";
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|||||||
+39
-9
@@ -3,10 +3,10 @@
|
|||||||
//! For resources on contexts, please consult [wikipedia](https://en.wikipedia.org/wiki/Context_switch) and [osdev](https://wiki.osdev.org/Context_Switching)
|
//! For resources on contexts, please consult [wikipedia](https://en.wikipedia.org/wiki/Context_switch) and [osdev](https://wiki.osdev.org/Context_Switching)
|
||||||
|
|
||||||
use alloc::{
|
use alloc::{
|
||||||
collections::{BTreeSet, VecDeque},
|
collections::{BTreeMap, BTreeSet, VecDeque},
|
||||||
sync::{Arc, Weak},
|
sync::{Arc, Weak},
|
||||||
};
|
};
|
||||||
use core::{num::NonZeroUsize, ops::Deref};
|
use core::{cmp::Reverse, num::NonZeroUsize, ops::Deref};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::memory::AddrSpaceWrapper,
|
context::memory::AddrSpaceWrapper,
|
||||||
@@ -81,16 +81,30 @@ static RUN_CONTEXTS: Mutex<L1, RunContextData> = Mutex::new(RunContextData::new(
|
|||||||
static IDLE_CONTEXTS: Mutex<L2, VecDeque<WeakContextRef>> = Mutex::new(VecDeque::new());
|
static IDLE_CONTEXTS: Mutex<L2, VecDeque<WeakContextRef>> = Mutex::new(VecDeque::new());
|
||||||
|
|
||||||
pub struct RunContextData {
|
pub struct RunContextData {
|
||||||
set: [VecDeque<WeakContextRef>; 40],
|
// 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)
|
||||||
|
count: usize,
|
||||||
|
v: u64,
|
||||||
|
total_weight: u64,
|
||||||
|
min_vtime: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunContextData {
|
impl RunContextData {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
const EMPTY_VEC: VecDeque<WeakContextRef> = VecDeque::new();
|
|
||||||
Self {
|
Self {
|
||||||
set: [EMPTY_VEC; 40],
|
queue: BTreeMap::new(),
|
||||||
|
timers: BTreeSet::new(),
|
||||||
|
count: 0,
|
||||||
|
v: 0,
|
||||||
|
total_weight: 0,
|
||||||
|
min_vtime: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn update_count(&mut self) -> usize {
|
||||||
|
self.count = self.queue.len();
|
||||||
|
self.count
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the global schemes list, const
|
/// Get the global schemes list, const
|
||||||
@@ -117,6 +131,10 @@ pub fn run_contexts(token: LockToken<'_, L0>) -> MutexGuard<'_, L1, RunContextDa
|
|||||||
RUN_CONTEXTS.lock(token)
|
RUN_CONTEXTS.lock(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn run_contexts_try(token: LockToken<'_, L0>) -> Option<MutexGuard<'_, L1, RunContextData>> {
|
||||||
|
RUN_CONTEXTS.try_lock(token)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init(token: &mut CleanLockToken) {
|
pub fn init(token: &mut CleanLockToken) {
|
||||||
let owner = None; // kmain not owned by any fd
|
let owner = None; // kmain not owned by any fd
|
||||||
let mut context = Context::new(owner).expect("failed to create kmain context");
|
let mut context = Context::new(owner).expect("failed to create kmain context");
|
||||||
@@ -126,12 +144,18 @@ pub fn init(token: &mut CleanLockToken) {
|
|||||||
context.name.clear();
|
context.name.clear();
|
||||||
context.name.push_str("[kmain]");
|
context.name.push_str("[kmain]");
|
||||||
|
|
||||||
|
#[cfg(feature = "profiling")]
|
||||||
|
{
|
||||||
|
crate::profiling::DBG_ID_MAP
|
||||||
|
.write(token.token())
|
||||||
|
.insert(context.debug_id, context.name);
|
||||||
|
}
|
||||||
|
|
||||||
self::arch::EMPTY_CR3.call_once(|| RmmA::table(TableKind::User));
|
self::arch::EMPTY_CR3.call_once(|| RmmA::table(TableKind::User));
|
||||||
|
|
||||||
context.status = Status::Runnable;
|
context.status = Status::Runnable;
|
||||||
context.running = true;
|
context.running = true;
|
||||||
context.cpu_id = Some(crate::cpu_id());
|
context.cpu_id = Some(crate::cpu_id());
|
||||||
context.start_time = crate::time::monotonic(token);
|
|
||||||
|
|
||||||
let context_lock = Arc::new(ContextLock::new(context));
|
let context_lock = Arc::new(ContextLock::new(context));
|
||||||
|
|
||||||
@@ -224,9 +248,6 @@ pub fn spawn(
|
|||||||
let stack = Kstack::new()?;
|
let stack = Kstack::new()?;
|
||||||
|
|
||||||
let mut context = Context::new(owner_proc_id)?;
|
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());
|
let _ = context.set_addr_space(Some(AddrSpaceWrapper::new()?), token.downgrade());
|
||||||
context
|
context
|
||||||
@@ -235,6 +256,7 @@ pub fn spawn(
|
|||||||
|
|
||||||
context.kstack = Some(stack);
|
context.kstack = Some(stack);
|
||||||
context.userspace = userspace_allowed;
|
context.userspace = userspace_allowed;
|
||||||
|
context.queue_key = Some((context.vd, Reverse(context.rem_slice), context.debug_id));
|
||||||
|
|
||||||
let context_lock = Arc::new(ContextLock::new(context));
|
let context_lock = Arc::new(ContextLock::new(context));
|
||||||
let context_ref = ContextRef(Arc::clone(&context_lock));
|
let context_ref = ContextRef(Arc::clone(&context_lock));
|
||||||
@@ -326,3 +348,11 @@ impl Drop for PreemptGuardL2<'_> {
|
|||||||
self.context.write(self.token.token()).preempt_locks -= 1;
|
self.context.write(self.token.token()).preempt_locks -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_contexts_stats(token: &mut CleanLockToken) -> (usize, usize, usize) {
|
||||||
|
let alive = contexts(token.downgrade()).len();
|
||||||
|
let running = run_contexts(token.token()).count;
|
||||||
|
let blocked = idle_contexts(token.downgrade()).len();
|
||||||
|
|
||||||
|
(alive, running, blocked)
|
||||||
|
}
|
||||||
|
|||||||
+323
-127
@@ -4,20 +4,25 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{
|
context::{
|
||||||
self, arch, idle_contexts, idle_contexts_try, run_contexts, ArcContextLockWriteGuard,
|
self, arch, idle_contexts, idle_contexts_try, memory::AddrSpaceSwitchReadGuard,
|
||||||
Context, ContextLock, WeakContextRef,
|
run_contexts, run_contexts_try, ArcContextLockWriteGuard, Context, ContextLock,
|
||||||
|
WeakContextRef,
|
||||||
},
|
},
|
||||||
cpu_set::LogicalCpuId,
|
cpu_set::LogicalCpuId,
|
||||||
cpu_stats::{self, CpuState},
|
cpu_stats::{self, CpuState},
|
||||||
percpu::PercpuBlock,
|
percpu::PercpuBlock,
|
||||||
sync::{ArcRwLockWriteGuard, CleanLockToken, L4},
|
sync::{ArcRwLockWriteGuard, CleanLockToken, L4},
|
||||||
};
|
};
|
||||||
use alloc::{sync::Arc, vec::Vec};
|
use alloc::sync::Arc;
|
||||||
use core::{
|
use core::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
hint, mem,
|
cmp::Reverse,
|
||||||
|
hint, matches, mem,
|
||||||
|
option::Option::{None, Some},
|
||||||
sync::atomic::Ordering,
|
sync::atomic::Ordering,
|
||||||
|
u64,
|
||||||
};
|
};
|
||||||
|
use smallvec::SmallVec;
|
||||||
use syscall::PtraceFlags;
|
use syscall::PtraceFlags;
|
||||||
|
|
||||||
enum UpdateResult {
|
enum UpdateResult {
|
||||||
@@ -26,13 +31,18 @@ enum UpdateResult {
|
|||||||
Blocked,
|
Blocked,
|
||||||
}
|
}
|
||||||
|
|
||||||
// A simple geometric series where value[i] ~= value[i - 1] * 1.25
|
// A simple geometric series where value[i] ~= value[i + 1] * 1.25
|
||||||
const SCHED_PRIO_TO_WEIGHT: [usize; 40] = [
|
const SCHED_PRIO_TO_WEIGHT: [usize; 40] = [
|
||||||
88761, 71755, 56483, 46273, 36291, 29154, 23254, 18705, 14949, 11916, 9548, 7620, 6100, 4904,
|
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,
|
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,
|
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
|
||||||
|
|
||||||
/// Determines if a given context is eligible to be scheduled on a given CPU (in
|
/// Determines if a given context is eligible to be scheduled on a given CPU (in
|
||||||
/// principle, the current CPU).
|
/// principle, the current CPU).
|
||||||
///
|
///
|
||||||
@@ -96,7 +106,9 @@ pub fn tick(token: &mut CleanLockToken) {
|
|||||||
ticks_cell.set(new_ticks);
|
ticks_cell.set(new_ticks);
|
||||||
|
|
||||||
// Trigger a context switch after every 3 ticks (approx. 6.75 ms).
|
// Trigger a context switch after every 3 ticks (approx. 6.75 ms).
|
||||||
if new_ticks >= 3 {
|
if new_ticks >= TICK_INTERVAL as usize
|
||||||
|
&& arch::CONTEXT_SWITCH_LOCK.load(Ordering::Relaxed) == false
|
||||||
|
{
|
||||||
switch(token);
|
switch(token);
|
||||||
crate::context::signal::signal_handler(token);
|
crate::context::signal::signal_handler(token);
|
||||||
}
|
}
|
||||||
@@ -175,15 +187,80 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Alarm (previously in update_runnable)
|
// Alarm (previously in update_runnable)
|
||||||
let wakeups = wakeup_contexts(token, switch_time);
|
let mut wakeups = wakeup_contexts(token);
|
||||||
|
let mut push_idle: SmallVec<[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() {
|
||||||
|
if *wake > switch_time {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((_, context_ref)) = run_contexts.timers.pop_first() {
|
||||||
|
wakeups.push(context_ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if wakeups.len() > 0 {
|
if wakeups.len() > 0 {
|
||||||
let mut run_contexts = run_contexts(token.token());
|
let mut run_contexts = run_contexts(token.token());
|
||||||
for (prio, context_lock) in wakeups {
|
for context_ref in wakeups {
|
||||||
run_contexts.set[prio].push_back(context_lock);
|
let Some(context_lock) = context_ref.upgrade() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(mut guard) = (unsafe { context_lock.try_write_arc() }) else {
|
||||||
|
push_idle.push(context_ref);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_vtime = guard.vtime.max(run_contexts.v);
|
||||||
|
guard.vtime = new_vtime;
|
||||||
|
|
||||||
|
let weight = SCHED_PRIO_TO_WEIGHT[guard.prio] as u64;
|
||||||
|
let scaled_slice = (BASE_SLICE_TICKS as u128 * SCALE) / weight as u128;
|
||||||
|
|
||||||
|
if !guard.is_active {
|
||||||
|
guard.is_active = true;
|
||||||
|
run_contexts.total_weight += weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
guard.vd = new_vtime + scaled_slice as u64;
|
||||||
|
guard.rem_slice = BASE_SLICE_TICKS * SCALE as u64;
|
||||||
|
let key = (guard.vd, Reverse(guard.rem_slice), guard.debug_id);
|
||||||
|
guard.queue_key = Some(key);
|
||||||
|
drop(guard);
|
||||||
|
|
||||||
|
run_contexts
|
||||||
|
.queue
|
||||||
|
.insert(key, (new_vtime, weight, context_ref));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut idle_list = idle_contexts(token.downgrade());
|
||||||
|
for context_ref in push_idle {
|
||||||
|
idle_list.push_back(context_ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* // uncomment to debug contexts count
|
||||||
|
let cpu_count = crate::cpu_count() as usize;
|
||||||
|
let len_idle = idle_contexts(token.downgrade()).len();
|
||||||
|
let all_contexts = context::contexts(token.downgrade())
|
||||||
|
.len()
|
||||||
|
.saturating_sub(cpu_count); // ignore kmain
|
||||||
|
print!(
|
||||||
|
"\r TIME {}.{} IDLE {} WAKEUPS {} ALL {} ",
|
||||||
|
switch_time / 1000_000_000,
|
||||||
|
(switch_time / 100_000_000) % 10,
|
||||||
|
len_idle,
|
||||||
|
wakeups_len,
|
||||||
|
all_contexts
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
let cpu_id = crate::cpu_id();
|
let cpu_id = crate::cpu_id();
|
||||||
|
|
||||||
// Update per-cpu times
|
// Update per-cpu times
|
||||||
@@ -192,21 +269,19 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
|
|||||||
let was_idle = percpu.stats.add_time(percpu_ms) == CpuState::Idle as u8;
|
let was_idle = percpu.stats.add_time(percpu_ms) == CpuState::Idle as u8;
|
||||||
percpu.switch_internals.switch_time.set(switch_time);
|
percpu.switch_internals.switch_time.set(switch_time);
|
||||||
|
|
||||||
let switch_context_opt = match select_next_context(
|
let switch_context_opt = select_next_context(
|
||||||
token,
|
token,
|
||||||
percpu,
|
percpu,
|
||||||
cpu_id,
|
cpu_id,
|
||||||
switch_time,
|
switch_time,
|
||||||
|
percpu_nanos,
|
||||||
was_idle,
|
was_idle,
|
||||||
&mut prev_context_guard,
|
&mut prev_context_guard,
|
||||||
) {
|
);
|
||||||
Ok(opt) => opt,
|
|
||||||
Err(early_ret) => return early_ret,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Switch process states, TSS stack pointer, and store new context ID
|
// Switch process states, TSS stack pointer, and store new context ID
|
||||||
match switch_context_opt {
|
match switch_context_opt {
|
||||||
Some(mut next_context_guard) => {
|
Some((mut next_context_guard, addr_space_guard)) => {
|
||||||
// Update context states and prepare for the switch.
|
// Update context states and prepare for the switch.
|
||||||
let prev_context = &mut *prev_context_guard;
|
let prev_context = &mut *prev_context_guard;
|
||||||
let next_context = &mut *next_context_guard;
|
let next_context = &mut *next_context_guard;
|
||||||
@@ -221,13 +296,7 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
|
|||||||
|
|
||||||
// Update times
|
// Update times
|
||||||
if !was_idle {
|
if !was_idle {
|
||||||
let delta = switch_time.saturating_sub(prev_context.switch_time);
|
prev_context.cpu_time += switch_time.saturating_sub(prev_context.switch_time);
|
||||||
prev_context.cpu_time += delta;
|
|
||||||
if prev_context.userspace {
|
|
||||||
prev_context.utime += delta;
|
|
||||||
} else {
|
|
||||||
prev_context.stime += delta;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
next_context.switch_time = switch_time;
|
next_context.switch_time = switch_time;
|
||||||
if next_context.userspace {
|
if next_context.userspace {
|
||||||
@@ -272,6 +341,14 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
|
|||||||
prev_context.inside_syscall =
|
prev_context.inside_syscall =
|
||||||
percpu.inside_syscall.replace(next_context.inside_syscall);
|
percpu.inside_syscall.replace(next_context.inside_syscall);
|
||||||
|
|
||||||
|
#[cfg(feature = "profiling")]
|
||||||
|
{
|
||||||
|
percpu
|
||||||
|
.switch_internals
|
||||||
|
.current_dbg_id
|
||||||
|
.store(next_context.debug_id, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "syscall_debug")]
|
#[cfg(feature = "syscall_debug")]
|
||||||
{
|
{
|
||||||
prev_context.syscall_debug_info = percpu
|
prev_context.syscall_debug_info = percpu
|
||||||
@@ -286,7 +363,11 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
|
|||||||
.being_sigkilled
|
.being_sigkilled
|
||||||
.set(next_context.being_sigkilled);
|
.set(next_context.being_sigkilled);
|
||||||
|
|
||||||
|
// Anything implement Drop must be manually dropped now
|
||||||
|
drop(prev_context_lock);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
percpu.new_addrsp_guard.set(addr_space_guard);
|
||||||
arch::switch_to(prev_context, next_context);
|
arch::switch_to(prev_context, next_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,9 +389,11 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wakeup_contexts(token: &mut CleanLockToken, switch_time: u128) -> Vec<(usize, WeakContextRef)> {
|
fn wakeup_contexts(
|
||||||
// TODO: Optimise this somehow. Perhaps using a separate timer queue?
|
token: &mut CleanLockToken,
|
||||||
let mut wakeups = Vec::new();
|
) -> SmallVec<[WeakContextRef; 16]> {
|
||||||
|
// TODO: Optimise this somehow
|
||||||
|
let mut wakeups = SmallVec::new();
|
||||||
let current_context = context::current();
|
let current_context = context::current();
|
||||||
let Some(idle_contexts) = idle_contexts_try(token.downgrade()) else {
|
let Some(idle_contexts) = idle_contexts_try(token.downgrade()) else {
|
||||||
// other cpus may spawning or killing contexts so let's skip wakeups to avoid contention
|
// other cpus may spawning or killing contexts so let's skip wakeups to avoid contention
|
||||||
@@ -333,21 +416,14 @@ fn wakeup_contexts(token: &mut CleanLockToken, switch_time: u128) -> Vec<(usize,
|
|||||||
idle_contexts.push_back(context_ref);
|
idle_contexts.push_back(context_ref);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if guard.status.is_soft_blocked() {
|
if guard.status.is_dead() {
|
||||||
if let Some(wake) = guard.wake {
|
// TODO: who hold this dead context?
|
||||||
if switch_time >= wake {
|
continue;
|
||||||
let prio = guard.prio;
|
|
||||||
drop(guard);
|
|
||||||
wakeups.push((prio, context_ref));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if guard.status.is_runnable() && !guard.running {
|
if guard.status.is_runnable() && !guard.running {
|
||||||
let prio = guard.prio;
|
|
||||||
drop(guard);
|
drop(guard);
|
||||||
wakeups.push((prio, context_ref));
|
wakeups.push(context_ref);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,137 +433,250 @@ fn wakeup_contexts(token: &mut CleanLockToken, switch_time: u128) -> Vec<(usize,
|
|||||||
wakeups
|
wakeups
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is the scheduler function which currently utilises Deficit Weighted Round Robin Scheduler
|
/// This is the scheduler function which currently utilises EEVDF Scheduler
|
||||||
fn select_next_context(
|
fn select_next_context(
|
||||||
token: &mut CleanLockToken,
|
token: &mut CleanLockToken,
|
||||||
percpu: &PercpuBlock,
|
percpu: &PercpuBlock,
|
||||||
cpu_id: LogicalCpuId,
|
cpu_id: LogicalCpuId,
|
||||||
switch_time: u128,
|
switch_time: u128,
|
||||||
|
elapsed_time: u64,
|
||||||
was_idle: bool,
|
was_idle: bool,
|
||||||
prev_context_guard: &mut ArcRwLockWriteGuard<L4, Context>,
|
prev_context_guard: &mut ArcRwLockWriteGuard<L4, Context>,
|
||||||
) -> Result<Option<ArcContextLockWriteGuard>, SwitchResult> {
|
) -> Option<(ArcContextLockWriteGuard, Option<AddrSpaceSwitchReadGuard>)> {
|
||||||
let contexts_data = run_contexts(token.token());
|
let contexts_data = run_contexts(token.token());
|
||||||
let (mut contexts_data, mut token) = contexts_data.into_split();
|
let (mut contexts_data, mut token) = contexts_data.into_split();
|
||||||
let contexts_list = &mut contexts_data.set;
|
|
||||||
let idle_context = percpu.switch_internals.idle_context();
|
let idle_context = percpu.switch_internals.idle_context();
|
||||||
let mut balance = percpu.balance.get();
|
|
||||||
let mut i = percpu.last_queue.get() % 40;
|
|
||||||
|
|
||||||
// Lock the previous context.
|
// Lock the previous context.
|
||||||
let prev_context_lock = crate::context::current();
|
let prev_context_lock = crate::context::current();
|
||||||
|
let is_idle = Arc::ptr_eq(&prev_context_lock, &idle_context);
|
||||||
|
let prev_runnable = !is_idle && prev_context_guard.status.is_runnable();
|
||||||
|
let is_timer = prev_context_guard.wake.is_some();
|
||||||
|
|
||||||
let mut empty_queues = 0;
|
let elapsed_ticks = elapsed_time as u128 * SCALE / NANOS_PER_TICK;
|
||||||
let mut total_iters = 0;
|
|
||||||
let mut next_context_guard_opt = None;
|
|
||||||
|
|
||||||
let total_contexts: usize = contexts_list.iter().map(|q| q.len()).sum();
|
if prev_runnable {
|
||||||
let mut skipped_contexts = 0;
|
let weight = SCHED_PRIO_TO_WEIGHT[prev_context_guard.prio] as u64;
|
||||||
|
prev_context_guard.rem_slice = prev_context_guard
|
||||||
|
.rem_slice
|
||||||
|
.saturating_sub((elapsed_ticks) as u64);
|
||||||
|
let scaled_task = elapsed_ticks / weight as u128;
|
||||||
|
prev_context_guard.vtime += scaled_task as u64;
|
||||||
|
|
||||||
'priority: loop {
|
if prev_context_guard.vtime < contexts_data.v {
|
||||||
i = (i + 1) % 40;
|
prev_context_guard.vtime = contexts_data.v;
|
||||||
total_iters += 1;
|
|
||||||
|
|
||||||
// The least prioritised queue takes <5000 iters to build up
|
|
||||||
// balance = sched_prio_to_weight[20], if we have already spent
|
|
||||||
// that many iters and not found any context, it is better to just
|
|
||||||
// skip for now
|
|
||||||
if total_iters >= 5000 {
|
|
||||||
break 'priority;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if skipped_contexts > total_contexts && total_contexts > 0 {
|
let is_yield = (elapsed_time as u128) < (TICK_INTERVAL as u128 * NANOS_PER_TICK) / 2;
|
||||||
break 'priority;
|
|
||||||
|
if is_yield {
|
||||||
|
let unconsumed = prev_context_guard.rem_slice as u128;
|
||||||
|
let penalty = unconsumed / weight as u128;
|
||||||
|
prev_context_guard.vtime += penalty as u64;
|
||||||
|
prev_context_guard.rem_slice = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let contexts = contexts_list
|
if prev_context_guard.rem_slice == 0 {
|
||||||
.get_mut(i)
|
prev_context_guard.rem_slice = BASE_SLICE_TICKS * SCALE as u64;
|
||||||
.expect("i should be between [0, 39]!");
|
let scaled_slice = (BASE_SLICE_TICKS as u128 * SCALE) / weight as u128;
|
||||||
|
prev_context_guard.vd = prev_context_guard.vtime + scaled_slice as u64;
|
||||||
|
}
|
||||||
|
} else if !is_idle {
|
||||||
|
if prev_context_guard.is_active {
|
||||||
|
prev_context_guard.is_active = false;
|
||||||
|
let weight = SCHED_PRIO_TO_WEIGHT[prev_context_guard.prio] as u64;
|
||||||
|
contexts_data.total_weight = contexts_data.total_weight.saturating_sub(weight);
|
||||||
|
}
|
||||||
|
prev_context_guard.rem_slice = 0;
|
||||||
|
|
||||||
if contexts.is_empty() {
|
if let Some(wake) = prev_context_guard.wake {
|
||||||
empty_queues += 1;
|
contexts_data
|
||||||
if empty_queues >= 40 {
|
.timers
|
||||||
// If all queues are empty, just break out
|
.insert((wake, WeakContextRef(Arc::downgrade(&prev_context_lock))));
|
||||||
break 'priority;
|
}
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
|
let mut eligible_best = None;
|
||||||
|
let mut prev_is_eligible = false;
|
||||||
|
|
||||||
|
let mut ineligible_best = None;
|
||||||
|
let mut ineligible_min_vtime = u64::MAX;
|
||||||
|
let mut ineligible_vd = u64::MAX;
|
||||||
|
|
||||||
|
if prev_runnable {
|
||||||
|
if prev_context_guard.vtime <= contexts_data.v {
|
||||||
|
prev_is_eligible = true;
|
||||||
} else {
|
} else {
|
||||||
empty_queues = 0;
|
ineligible_min_vtime = prev_context_guard.vtime;
|
||||||
|
ineligible_vd = prev_context_guard.vd;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if balance[i] < SCHED_PRIO_TO_WEIGHT[20] {
|
// New BTreeMap based walk
|
||||||
// This queue does not have enough balance to run,
|
let mut weight_change: u64 = 0;
|
||||||
// increment the balance!
|
let mut contexts_to_remove: SmallVec<[(u64, Reverse<u64>, u32); 16]> = SmallVec::new();
|
||||||
balance[i] += SCHED_PRIO_TO_WEIGHT[i];
|
for ((vd, rem_slice, ctxt_id), (vtime, context_weight, context_ref)) in
|
||||||
|
contexts_data.queue.iter()
|
||||||
|
{
|
||||||
|
if *vtime > ineligible_min_vtime && *vtime > contexts_data.v {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let len = contexts.len();
|
let Some(context_lock) = context_ref.upgrade() else {
|
||||||
for _ in 0..len {
|
weight_change += *context_weight as u64;
|
||||||
let (next_context_ref, next_context_lock) = match contexts.pop_front() {
|
contexts_to_remove.push((*vd, *rem_slice, *ctxt_id));
|
||||||
Some(lock) => match lock.upgrade() {
|
continue;
|
||||||
Some(new_lock) => (lock, new_lock),
|
};
|
||||||
None => {
|
|
||||||
skipped_contexts += 1;
|
|
||||||
continue; // Ghost Process, just continue
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => break, // Empty Queue
|
|
||||||
};
|
|
||||||
|
|
||||||
if Arc::ptr_eq(&next_context_lock, &prev_context_lock) {
|
if Arc::ptr_eq(&context_lock, &idle_context)
|
||||||
contexts.push_back(next_context_ref);
|
|| Arc::ptr_eq(&context_lock, &prev_context_lock)
|
||||||
continue;
|
{
|
||||||
}
|
//weight_change += *context_weight as u64;
|
||||||
if Arc::ptr_eq(&next_context_lock, &idle_context) {
|
//contexts_to_remove.push((*vd, *rem_slice, *ctxt_id));
|
||||||
contexts.push_back(next_context_ref);
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
let mut next_context_guard = unsafe { next_context_lock.write_arc() };
|
|
||||||
|
|
||||||
// Is this context runnable on this CPU?
|
let Some(mut guard) = (unsafe { context_lock.try_write_arc() }) else {
|
||||||
let sw = unsafe { update_runnable(&mut next_context_guard, cpu_id, switch_time) };
|
continue;
|
||||||
if let UpdateResult::CanSwitch = sw {
|
};
|
||||||
next_context_guard_opt = Some(next_context_guard);
|
|
||||||
balance[i] -= SCHED_PRIO_TO_WEIGHT[20];
|
let sw = unsafe { update_runnable(&mut guard, cpu_id, switch_time) };
|
||||||
break 'priority;
|
|
||||||
|
if matches!(sw, UpdateResult::Blocked) {
|
||||||
|
if guard.is_active {
|
||||||
|
guard.is_active = false;
|
||||||
|
weight_change += context_weight;
|
||||||
|
}
|
||||||
|
guard.rem_slice = 0;
|
||||||
|
guard.queue_key = None;
|
||||||
|
|
||||||
|
contexts_to_remove.push((*vd, *rem_slice, *ctxt_id));
|
||||||
|
drop(guard);
|
||||||
|
// Reenqueue should be handled by unblock
|
||||||
|
idle_contexts(token.token()).push_back(context_ref.clone());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matches!(sw, UpdateResult::CanSwitch) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut best_addr_space = None;
|
||||||
|
if let Some(addr_space) = &guard.addr_space {
|
||||||
|
let mut t = unsafe { CleanLockToken::new() };
|
||||||
|
if let Some(addr) = addr_space.inner.try_read(t.token()) {
|
||||||
|
best_addr_space = Some(AddrSpaceSwitchReadGuard::new(addr));
|
||||||
} else {
|
} else {
|
||||||
if matches!(sw, UpdateResult::Blocked) {
|
continue;
|
||||||
idle_contexts(token.token()).push_back(next_context_ref);
|
}
|
||||||
} else {
|
}
|
||||||
contexts.push_back(next_context_ref);
|
|
||||||
};
|
|
||||||
skipped_contexts += 1;
|
|
||||||
|
|
||||||
if skipped_contexts >= total_contexts {
|
if *vtime <= contexts_data.v {
|
||||||
break 'priority;
|
// Eligible
|
||||||
|
eligible_best = Some((guard, best_addr_space));
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// Ineligible
|
||||||
|
if *vtime < ineligible_min_vtime {
|
||||||
|
ineligible_min_vtime = *vtime;
|
||||||
|
ineligible_vd = *vd;
|
||||||
|
if let Some((old_guard, old_addr_space)) = ineligible_best {
|
||||||
|
drop(old_guard);
|
||||||
|
drop(old_addr_space);
|
||||||
}
|
}
|
||||||
|
ineligible_best = Some((guard, best_addr_space));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
percpu.balance.set(balance);
|
|
||||||
percpu.last_queue.set(i);
|
|
||||||
|
|
||||||
if !Arc::ptr_eq(&prev_context_lock, &idle_context) {
|
contexts_data.total_weight = contexts_data.total_weight.saturating_sub(weight_change);
|
||||||
// Send the old process to the back of the line (if it is still runnable)
|
|
||||||
let prev_ctx = WeakContextRef(Arc::downgrade(&prev_context_lock));
|
for old_key in contexts_to_remove {
|
||||||
if prev_context_guard.status.is_runnable() {
|
contexts_data.queue.remove(&old_key);
|
||||||
let prio = prev_context_guard.prio;
|
}
|
||||||
contexts_list[prio].push_back(prev_ctx);
|
|
||||||
} else {
|
// No eligible context was found
|
||||||
idle_contexts(token.token()).push_back(prev_ctx);
|
if !(prev_is_eligible || eligible_best.is_some()) && ineligible_min_vtime != u64::MAX {
|
||||||
|
contexts_data.v = ineligible_min_vtime; // Advance V
|
||||||
|
|
||||||
|
let prev_is_earliest = prev_runnable && prev_context_guard.vtime <= ineligible_min_vtime;
|
||||||
|
|
||||||
|
if prev_is_earliest {
|
||||||
|
eligible_best = None;
|
||||||
|
} else if ineligible_best.is_some() {
|
||||||
|
let prev_has_slice = prev_runnable && prev_context_guard.rem_slice > 0;
|
||||||
|
|
||||||
|
if prev_has_slice && prev_context_guard.vd <= ineligible_vd {
|
||||||
|
eligible_best = None;
|
||||||
|
} else {
|
||||||
|
eligible_best = ineligible_best.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if prev_is_eligible && eligible_best.is_some() {
|
||||||
|
if let Some((ref guard, _)) = eligible_best {
|
||||||
|
if prev_context_guard.vd < guard.vd
|
||||||
|
|| (prev_context_guard.vd == guard.vd
|
||||||
|
&& prev_context_guard.rem_slice > guard.rem_slice)
|
||||||
|
{
|
||||||
|
eligible_best = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(next_context_guard) = next_context_guard_opt {
|
let mut final_winner = None;
|
||||||
// We found a new process!
|
|
||||||
return Ok(Some(next_context_guard));
|
if let Some((mut chosen_guard, addr_space)) = eligible_best {
|
||||||
} else {
|
if let Some(key) = chosen_guard.queue_key.take() {
|
||||||
if !was_idle && !Arc::ptr_eq(&prev_context_lock, &idle_context) {
|
contexts_data.queue.remove(&key);
|
||||||
// We switch into the idle context
|
}
|
||||||
Ok(Some(unsafe { idle_context.write_arc() }))
|
final_winner = Some((chosen_guard, addr_space));
|
||||||
|
}
|
||||||
|
|
||||||
|
if final_winner.is_some() || prev_runnable {
|
||||||
|
if contexts_data.total_weight > 0 {
|
||||||
|
let v_advance = elapsed_ticks as u128 / contexts_data.total_weight as u128;
|
||||||
|
contexts_data.v += v_advance as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((chosen_guard, addr_space)) = final_winner {
|
||||||
|
if prev_runnable {
|
||||||
|
let (vd, rem_slice, ctxt_id, vtime) = (
|
||||||
|
prev_context_guard.vd,
|
||||||
|
prev_context_guard.rem_slice,
|
||||||
|
prev_context_guard.debug_id,
|
||||||
|
prev_context_guard.vtime,
|
||||||
|
);
|
||||||
|
prev_context_guard.queue_key = Some((vd, Reverse(rem_slice), ctxt_id));
|
||||||
|
|
||||||
|
let weight = SCHED_PRIO_TO_WEIGHT[prev_context_guard.prio] as u64;
|
||||||
|
contexts_data.queue.insert(
|
||||||
|
(vd, Reverse(rem_slice), ctxt_id),
|
||||||
|
(
|
||||||
|
vtime,
|
||||||
|
weight,
|
||||||
|
WeakContextRef(Arc::downgrade(&prev_context_lock)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if !is_idle && !is_timer {
|
||||||
|
idle_contexts(token.token())
|
||||||
|
.push_back(WeakContextRef(Arc::downgrade(&prev_context_lock)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some((chosen_guard, addr_space));
|
||||||
} else {
|
} else {
|
||||||
// We found no other process to run.
|
return None;
|
||||||
Ok(None)
|
}
|
||||||
|
} else {
|
||||||
|
if !is_idle && !is_timer {
|
||||||
|
idle_contexts(token.token())
|
||||||
|
.push_back(WeakContextRef(Arc::downgrade(&prev_context_lock)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let prev_is_dead = !is_idle && !prev_context_guard.status.is_runnable();
|
||||||
|
if (!was_idle || prev_is_dead) && !is_idle {
|
||||||
|
return Some(unsafe { (idle_context.write_arc(), None) });
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -503,6 +692,10 @@ pub struct ContextSwitchPercpu {
|
|||||||
|
|
||||||
current_ctxt: RefCell<Option<Arc<ContextLock>>>,
|
current_ctxt: RefCell<Option<Arc<ContextLock>>>,
|
||||||
|
|
||||||
|
// TODO: just access current_ctxt directly?
|
||||||
|
#[cfg(feature = "profiling")]
|
||||||
|
pub(crate) current_dbg_id: core::sync::atomic::AtomicU32,
|
||||||
|
|
||||||
/// The idle process.
|
/// The idle process.
|
||||||
idle_ctxt: RefCell<Option<Arc<ContextLock>>>,
|
idle_ctxt: RefCell<Option<Arc<ContextLock>>>,
|
||||||
pub(crate) being_sigkilled: Cell<bool>,
|
pub(crate) being_sigkilled: Cell<bool>,
|
||||||
@@ -517,6 +710,9 @@ impl ContextSwitchPercpu {
|
|||||||
current_ctxt: RefCell::new(None),
|
current_ctxt: RefCell::new(None),
|
||||||
idle_ctxt: RefCell::new(None),
|
idle_ctxt: RefCell::new(None),
|
||||||
being_sigkilled: Cell::new(false),
|
being_sigkilled: Cell::new(false),
|
||||||
|
|
||||||
|
#[cfg(feature = "profiling")]
|
||||||
|
current_dbg_id: core::sync::atomic::AtomicU32::new(!0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -3,10 +3,8 @@
|
|||||||
//! The Redox OS Kernel is a microkernel that supports `x86_64` systems and
|
//! The Redox OS Kernel is a microkernel that supports `x86_64` systems and
|
||||||
//! provides Unix-like syscalls for primarily Rust applications
|
//! provides Unix-like syscalls for primarily Rust applications
|
||||||
|
|
||||||
#![feature(asm_cfg)] // Stabilized in 1.93
|
|
||||||
#![feature(if_let_guard)]
|
|
||||||
#![feature(int_roundings)]
|
#![feature(int_roundings)]
|
||||||
#![feature(iter_next_chunk)]
|
#![cfg_attr(dtb, feature(iter_next_chunk))]
|
||||||
#![feature(sync_unsafe_cell)]
|
#![feature(sync_unsafe_cell)]
|
||||||
#![feature(btree_cursors)]
|
#![feature(btree_cursors)]
|
||||||
#![cfg_attr(not(test), no_std)]
|
#![cfg_attr(not(test), no_std)]
|
||||||
@@ -70,6 +68,9 @@ mod log;
|
|||||||
/// Memory management
|
/// Memory management
|
||||||
mod memory;
|
mod memory;
|
||||||
|
|
||||||
|
/// NUMA support
|
||||||
|
mod numa;
|
||||||
|
|
||||||
/// Panic
|
/// Panic
|
||||||
mod panic;
|
mod panic;
|
||||||
|
|
||||||
|
|||||||
+343
-142
@@ -4,11 +4,13 @@
|
|||||||
use core::{
|
use core::{
|
||||||
cell::SyncUnsafeCell,
|
cell::SyncUnsafeCell,
|
||||||
num::NonZeroUsize,
|
num::NonZeroUsize,
|
||||||
sync::atomic::{AtomicUsize, Ordering},
|
ops::AddAssign,
|
||||||
|
slice,
|
||||||
|
sync::atomic::{AtomicU8, AtomicUsize, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use kernel_mapper::KernelMapper;
|
pub use kernel_mapper::KernelMapper;
|
||||||
use spin::Mutex;
|
use spin::{once::Once, Mutex};
|
||||||
|
|
||||||
pub use crate::arch::CurrentRmmArch as RmmA;
|
pub use crate::arch::CurrentRmmArch as RmmA;
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -17,11 +19,12 @@ use crate::{
|
|||||||
memory::{AccessMode, PfError},
|
memory::{AccessMode, PfError},
|
||||||
},
|
},
|
||||||
kernel_executable_offsets::{__usercopy_end, __usercopy_start},
|
kernel_executable_offsets::{__usercopy_end, __usercopy_start},
|
||||||
|
numa,
|
||||||
sync::CleanLockToken,
|
sync::CleanLockToken,
|
||||||
syscall::error::{Error, ENOMEM},
|
syscall::error::{Error, ENOMEM},
|
||||||
};
|
};
|
||||||
pub use rmm::{Arch as RmmArch, PageFlags, PhysicalAddress, TableKind, VirtualAddress};
|
pub use rmm::{Arch as RmmArch, PageFlags, PhysicalAddress, TableKind, VirtualAddress};
|
||||||
use rmm::{BumpAllocator, FrameAllocator, FrameCount, FrameUsage};
|
use rmm::{BumpAllocator, FrameAllocator, FrameCount, FrameUsage, MemoryArea};
|
||||||
|
|
||||||
mod kernel_mapper;
|
mod kernel_mapper;
|
||||||
pub mod page;
|
pub mod page;
|
||||||
@@ -54,7 +57,15 @@ pub fn free_frames() -> usize {
|
|||||||
/// Get the number of frames used
|
/// Get the number of frames used
|
||||||
pub fn used_frames() -> usize {
|
pub fn used_frames() -> usize {
|
||||||
// TODO: Include bump allocator static pages?
|
// TODO: Include bump allocator static pages?
|
||||||
FREELIST.lock().used_frames
|
FREE_LISTS
|
||||||
|
.get()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|e| {
|
||||||
|
let l = e.lock();
|
||||||
|
l.used_frames
|
||||||
|
})
|
||||||
|
.sum()
|
||||||
}
|
}
|
||||||
pub fn total_frames() -> usize {
|
pub fn total_frames() -> usize {
|
||||||
// TODO: Include bump allocator static pages?
|
// TODO: Include bump allocator static pages?
|
||||||
@@ -63,19 +74,39 @@ pub fn total_frames() -> usize {
|
|||||||
|
|
||||||
/// Allocate a range of frames
|
/// Allocate a range of frames
|
||||||
pub fn allocate_p2frame(order: u32) -> Option<Frame> {
|
pub fn allocate_p2frame(order: u32) -> Option<Frame> {
|
||||||
allocate_p2frame_complex(order, (), None, order).map(|(f, _)| f)
|
let initial_index = get_round_robin_index();
|
||||||
|
let mut index = initial_index;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn allocate_frame() -> Option<Frame> {
|
pub fn allocate_frame() -> Option<Frame> {
|
||||||
allocate_p2frame(0)
|
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
|
// TODO: Flags, strategy
|
||||||
pub fn allocate_p2frame_complex(
|
pub fn allocate_p2frame_complex(
|
||||||
_req_order: u32,
|
_req_order: u32,
|
||||||
_flags: (),
|
_flags: (),
|
||||||
_strategy: Option<()>,
|
_strategy: Option<()>,
|
||||||
min_order: u32,
|
min_order: u32,
|
||||||
|
index: usize,
|
||||||
) -> Option<(Frame, usize)> {
|
) -> Option<(Frame, usize)> {
|
||||||
let mut freelist = FREELIST.lock();
|
let mut freelist = FREE_LISTS.get().unwrap()[index].lock();
|
||||||
|
|
||||||
let (frame_order, frame) = freelist
|
let (frame_order, frame) = freelist
|
||||||
.for_orders
|
.for_orders
|
||||||
@@ -112,7 +143,9 @@ pub fn allocate_p2frame_complex(
|
|||||||
freelist.for_orders[frame_order as usize] = next_free.frame();
|
freelist.for_orders[frame_order as usize] = next_free.frame();
|
||||||
|
|
||||||
// TODO: Is this LIFO cache optimal?
|
// TODO: Is this LIFO cache optimal?
|
||||||
//info!("MIN{min_order}FRAMEORD{frame_order}");
|
// if min_order > 0 {
|
||||||
|
// info!("MIN {min_order} FRAMEORD {frame_order}");
|
||||||
|
// }
|
||||||
for order in (min_order..frame_order).rev() {
|
for order in (min_order..frame_order).rev() {
|
||||||
//info!("SPLIT ORDER {order}");
|
//info!("SPLIT ORDER {order}");
|
||||||
let order_page_count = 1 << order;
|
let order_page_count = 1 << order;
|
||||||
@@ -146,8 +179,19 @@ pub fn allocate_p2frame_complex(
|
|||||||
Some((frame, PAGE_SIZE << min_order))
|
Some((frame, PAGE_SIZE << min_order))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_index_for_deallocation(addr: usize) -> Option<usize> {
|
||||||
|
for (i, list) in FREE_LISTS.get().unwrap().iter().enumerate() {
|
||||||
|
let l = list.lock();
|
||||||
|
if addr >= l.lower_limit && addr < l.upper_limit {
|
||||||
|
return Some(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub unsafe fn deallocate_p2frame(orig_frame: Frame, order: u32) {
|
pub unsafe fn deallocate_p2frame(orig_frame: Frame, order: u32) {
|
||||||
let mut freelist = FREELIST.lock();
|
let index = get_index_for_deallocation(orig_frame.physaddr.get()).expect("Expected an index");
|
||||||
|
let mut freelist = FREE_LISTS.get().unwrap()[index].lock();
|
||||||
|
|
||||||
let initial_info = get_page_info(orig_frame)
|
let initial_info = get_page_info(orig_frame)
|
||||||
.unwrap_or_else(|| panic!("missing PageInfo for {orig_frame:?} being freed"));
|
.unwrap_or_else(|| panic!("missing PageInfo for {orig_frame:?} being freed"));
|
||||||
@@ -231,7 +275,9 @@ pub unsafe fn deallocate_p2frame(orig_frame: Frame, order: u32) {
|
|||||||
old_head_info.set_prev(P2Frame::new(Some(new_head), largest_order));
|
old_head_info.set_prev(P2Frame::new(Some(new_head), largest_order));
|
||||||
}
|
}
|
||||||
|
|
||||||
//info!("FREED {frame:?}+2^{order}");
|
// if order > 0 {
|
||||||
|
// info!("FREED {current:?}+2^{order}");
|
||||||
|
// }
|
||||||
freelist.used_frames -= 1 << order;
|
freelist.used_frames -= 1 << order;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,13 +522,12 @@ struct AllocatorData {
|
|||||||
}
|
}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct FreeList {
|
struct FreeList {
|
||||||
|
upper_limit: usize,
|
||||||
|
lower_limit: usize,
|
||||||
for_orders: [Option<Frame>; ORDER_COUNT as usize],
|
for_orders: [Option<Frame>; ORDER_COUNT as usize],
|
||||||
used_frames: usize,
|
used_frames: usize,
|
||||||
}
|
}
|
||||||
static FREELIST: Mutex<FreeList> = Mutex::new(FreeList {
|
static FREE_LISTS: Once<&'static [Mutex<FreeList>]> = Once::new();
|
||||||
for_orders: [None; ORDER_COUNT as usize],
|
|
||||||
used_frames: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
pub struct Section {
|
pub struct Section {
|
||||||
base: Frame,
|
base: Frame,
|
||||||
@@ -499,6 +544,8 @@ const _: () = {
|
|||||||
|
|
||||||
#[cold]
|
#[cold]
|
||||||
fn init_sections(mut allocator: BumpAllocator<RmmA>) {
|
fn init_sections(mut allocator: BumpAllocator<RmmA>) {
|
||||||
|
let number_of_memory_regions = numa::number_of_memory_regions();
|
||||||
|
|
||||||
let (free_areas, offset_into_first_free_area) = allocator.free_areas();
|
let (free_areas, offset_into_first_free_area) = allocator.free_areas();
|
||||||
|
|
||||||
let free_areas_iter = || {
|
let free_areas_iter = || {
|
||||||
@@ -524,6 +571,18 @@ fn init_sections(mut allocator: BumpAllocator<RmmA>) {
|
|||||||
.next_multiple_of(MAX_SECTION_SIZE);
|
.next_multiple_of(MAX_SECTION_SIZE);
|
||||||
let aligned_start = area.base.data() / MAX_SECTION_SIZE * MAX_SECTION_SIZE;
|
let aligned_start = area.base.data() / MAX_SECTION_SIZE * MAX_SECTION_SIZE;
|
||||||
|
|
||||||
|
if number_of_memory_regions > 0 {
|
||||||
|
if let Some(next_memory_region) =
|
||||||
|
numa::nearest_next_memory_region(area.base.data(), false)
|
||||||
|
&& next_memory_region.start < area.base.add(area.size).data()
|
||||||
|
&& next_memory_region.start + next_memory_region.length
|
||||||
|
> area.base.add(area.size).data()
|
||||||
|
&& !next_memory_region.start.is_multiple_of(MAX_SECTION_SIZE)
|
||||||
|
{
|
||||||
|
return (aligned_end - aligned_start) / MAX_SECTION_SIZE + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(aligned_end - aligned_start) / MAX_SECTION_SIZE
|
(aligned_end - aligned_start) / MAX_SECTION_SIZE
|
||||||
})
|
})
|
||||||
.sum();
|
.sum();
|
||||||
@@ -543,76 +602,141 @@ fn init_sections(mut allocator: BumpAllocator<RmmA>) {
|
|||||||
|
|
||||||
let mut iter = free_areas_iter().peekable();
|
let mut iter = free_areas_iter().peekable();
|
||||||
|
|
||||||
let mut i = 0;
|
let mut sections_fill = |region: Option<MemoryArea>,
|
||||||
|
i: &mut usize, // out parameter
|
||||||
|
force: bool|
|
||||||
|
-> Option<MemoryArea> {
|
||||||
|
let mut iter = free_areas_iter().peekable();
|
||||||
|
|
||||||
while let Some(mut memory_map_area) = iter.next() {
|
while let Some(mut memory_map_area) = iter.next() {
|
||||||
// TODO: NonZeroUsize
|
if !{
|
||||||
|
if let Some(region) = region
|
||||||
// TODO: x86_32 fails without this check
|
&& !force
|
||||||
if memory_map_area.size == 0 {
|
{
|
||||||
continue;
|
memory_map_area.base >= region.base
|
||||||
}
|
&& memory_map_area.base.data() < region.base.data() + region.size
|
||||||
|
} else {
|
||||||
assert_ne!(
|
true
|
||||||
memory_map_area.size, 0,
|
}
|
||||||
"RMM should enforce areas are not zeroed"
|
} {
|
||||||
);
|
continue;
|
||||||
|
|
||||||
// TODO: Should RMM do this?
|
|
||||||
|
|
||||||
while let Some(next_area) = iter.peek()
|
|
||||||
&& next_area.base == memory_map_area.base.add(memory_map_area.size)
|
|
||||||
{
|
|
||||||
memory_map_area.size += next_area.size;
|
|
||||||
let _ = iter.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
memory_map_area.base.data() % PAGE_SIZE,
|
|
||||||
0,
|
|
||||||
"RMM should enforce area alignment"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
memory_map_area.size % PAGE_SIZE,
|
|
||||||
0,
|
|
||||||
"RMM should enforce area length alignment"
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut pages_left = memory_map_area.size.div_floor(PAGE_SIZE);
|
|
||||||
let mut base = Frame::containing(memory_map_area.base);
|
|
||||||
|
|
||||||
while pages_left > 0 {
|
|
||||||
let page_info_max_count = core::cmp::min(pages_left, MAX_SECTION_PAGE_COUNT);
|
|
||||||
let pages_to_next_section =
|
|
||||||
(MAX_SECTION_SIZE - (base.base().data() % MAX_SECTION_SIZE)) / PAGE_SIZE;
|
|
||||||
let page_info_count = core::cmp::min(page_info_max_count, pages_to_next_section);
|
|
||||||
|
|
||||||
let page_info_array_size_pages =
|
|
||||||
(page_info_count * size_of::<PageInfo>()).div_ceil(PAGE_SIZE);
|
|
||||||
let page_info_array = unsafe {
|
|
||||||
let base = allocator
|
|
||||||
.allocate(FrameCount::new(page_info_array_size_pages))
|
|
||||||
.expect("failed to allocate page info array");
|
|
||||||
core::slice::from_raw_parts_mut(
|
|
||||||
RmmA::phys_to_virt(base).data() as *mut PageInfo,
|
|
||||||
page_info_count,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
for p in &*page_info_array {
|
|
||||||
assert_eq!(p.next.load(Ordering::Relaxed), 0);
|
|
||||||
assert_eq!(p.refcount.load(Ordering::Relaxed), 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sections[i] = Section {
|
// TODO: NonZeroUsize
|
||||||
base,
|
|
||||||
frames: page_info_array,
|
|
||||||
};
|
|
||||||
i += 1;
|
|
||||||
|
|
||||||
pages_left -= page_info_count;
|
// TODO: x86_32 fails without this check
|
||||||
base = base.next_by(page_info_count);
|
if memory_map_area.size == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_ne!(
|
||||||
|
memory_map_area.size, 0,
|
||||||
|
"RMM should enforce areas are not of length 0"
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: Should RMM do this?
|
||||||
|
|
||||||
|
while let Some(next_area) = iter.peek()
|
||||||
|
&& next_area.base == memory_map_area.base.add(memory_map_area.size)
|
||||||
|
&& if let Some(region) = region {
|
||||||
|
next_area.base >= region.base
|
||||||
|
&& next_area.base.data() < region.base.data() + region.size
|
||||||
|
&& !force
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
{
|
||||||
|
memory_map_area.size += next_area.size;
|
||||||
|
let _ = iter.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
memory_map_area.base.data() % PAGE_SIZE,
|
||||||
|
0,
|
||||||
|
"RMM should enforce area alignment"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
memory_map_area.size % PAGE_SIZE,
|
||||||
|
0,
|
||||||
|
"RMM should enforce area length alignment"
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut pages_left = memory_map_area.size.div_floor(PAGE_SIZE);
|
||||||
|
let mut base = Frame::containing(memory_map_area.base);
|
||||||
|
let mut return_value = None;
|
||||||
|
|
||||||
|
while pages_left > 0 {
|
||||||
|
let page_info_max_count = core::cmp::min(pages_left, MAX_SECTION_PAGE_COUNT);
|
||||||
|
let pages_to_next_section =
|
||||||
|
(MAX_SECTION_SIZE - (base.base().data() % MAX_SECTION_SIZE)) / PAGE_SIZE;
|
||||||
|
let mut page_info_count =
|
||||||
|
core::cmp::min(page_info_max_count, pages_to_next_section);
|
||||||
|
|
||||||
|
if !force
|
||||||
|
&& let Some(region) = region
|
||||||
|
&& base.base() >= region.base
|
||||||
|
&& base.base().data() < region.base.data() + region.size
|
||||||
|
&& (page_info_count * PAGE_SIZE
|
||||||
|
> (region.base.data() + region.size - base.base().data()))
|
||||||
|
{
|
||||||
|
page_info_count =
|
||||||
|
(region.base.data() + region.size - base.base().data()) / PAGE_SIZE;
|
||||||
|
return_value = Some(MemoryArea {
|
||||||
|
base: PhysicalAddress::new(region.base.data() + region.size),
|
||||||
|
size: (base.physaddr.get() + pages_left * PAGE_SIZE)
|
||||||
|
- (region.base.data() + region.size),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let page_info_array_size_pages =
|
||||||
|
(page_info_count * size_of::<PageInfo>()).div_ceil(PAGE_SIZE);
|
||||||
|
let page_info_array = unsafe {
|
||||||
|
let base = allocator
|
||||||
|
.allocate(FrameCount::new(page_info_array_size_pages))
|
||||||
|
.expect("failed to allocate page info array");
|
||||||
|
core::slice::from_raw_parts_mut(
|
||||||
|
RmmA::phys_to_virt(base).data() as *mut PageInfo,
|
||||||
|
page_info_count,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
for p in &*page_info_array {
|
||||||
|
assert_eq!(p.next.load(Ordering::Relaxed), 0);
|
||||||
|
assert_eq!(p.refcount.load(Ordering::Relaxed), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sections[*i] = Section {
|
||||||
|
base,
|
||||||
|
frames: page_info_array,
|
||||||
|
};
|
||||||
|
i.add_assign(1);
|
||||||
|
|
||||||
|
pages_left -= page_info_count;
|
||||||
|
base = base.next_by(page_info_count);
|
||||||
|
if let Some(return_value) = return_value {
|
||||||
|
return Some(return_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
if number_of_memory_regions > 0
|
||||||
|
&& let Some(regions) = numa::memory_regions()
|
||||||
|
{
|
||||||
|
for region in regions {
|
||||||
|
let mut sections_fill_result = Some(region);
|
||||||
|
let mut force = false;
|
||||||
|
|
||||||
|
while let Some(mut region) = sections_fill_result {
|
||||||
|
sections_fill_result = sections_fill(Some(region), &mut i, force);
|
||||||
|
force = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sections_fill(None, &mut i, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let sections = &mut sections[..i];
|
let sections = &mut sections[..i];
|
||||||
|
|
||||||
sections.sort_unstable_by_key(|s| s.base);
|
sections.sort_unstable_by_key(|s| s.base);
|
||||||
@@ -634,11 +758,12 @@ fn init_sections(mut allocator: BumpAllocator<RmmA>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut first_pages: [Option<(Frame, &'static PageInfo)>; ORDER_COUNT as usize] =
|
let mut append_page = |page: Frame,
|
||||||
[None; ORDER_COUNT as usize];
|
info: &'static PageInfo,
|
||||||
let mut last_pages = first_pages;
|
order,
|
||||||
|
first_pages: &mut [Option<(Frame, &'static PageInfo)>; 11],
|
||||||
let mut append_page = |page: Frame, info: &'static PageInfo, order| {
|
last_pages: &mut [Option<(Frame, &PageInfo)>; 11],
|
||||||
|
allocator: &mut BumpAllocator<RmmA>| {
|
||||||
let this_page = (page, info);
|
let this_page = (page, info);
|
||||||
|
|
||||||
if page.base() < allocator.abs_offset() {
|
if page.base() < allocator.abs_offset() {
|
||||||
@@ -674,76 +799,153 @@ fn init_sections(mut allocator: BumpAllocator<RmmA>) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
for section in &*sections {
|
let mut free_list_fill = |region: Option<MemoryArea>,
|
||||||
let mut base = section.base;
|
allocator: &mut BumpAllocator<RmmA>|
|
||||||
let mut frames = section.frames;
|
-> [Option<(Frame, &'static PageInfo)>; ORDER_COUNT as usize] {
|
||||||
|
let mut first_pages: [Option<(Frame, &'static PageInfo)>; ORDER_COUNT as usize] =
|
||||||
|
[None; ORDER_COUNT as usize];
|
||||||
|
let mut last_pages = first_pages;
|
||||||
|
|
||||||
for order in 0..=MAX_ORDER {
|
for section in &*sections {
|
||||||
let pages_for_current_order = 1 << order;
|
if let Some(region) = region
|
||||||
|
&& (section.base.physaddr.get() < region.base.data()
|
||||||
debug_assert_eq!(frames.len() % pages_for_current_order, 0);
|
|| section.base.physaddr.get() >= region.base.data() + region.size)
|
||||||
debug_assert!(base.is_aligned_to_order(order));
|
|
||||||
|
|
||||||
if !frames.is_empty() && order != MAX_ORDER && !base.is_aligned_to_order(order + 1) {
|
|
||||||
frames[0].next.store(order as usize, Ordering::Relaxed);
|
|
||||||
// The first section page is not aligned to the next order size.
|
|
||||||
|
|
||||||
//info!("ORDER {order}: FIRST {base:?}");
|
|
||||||
append_page(base, &frames[0], order);
|
|
||||||
|
|
||||||
base = base.next_by(pages_for_current_order);
|
|
||||||
frames = &frames[pages_for_current_order..];
|
|
||||||
} else {
|
|
||||||
//info!("ORDER {order}: FIRST SKIP");
|
|
||||||
}
|
|
||||||
|
|
||||||
if !frames.is_empty()
|
|
||||||
&& order != MAX_ORDER
|
|
||||||
&& !base.next_by(frames.len()).is_aligned_to_order(order + 1)
|
|
||||||
{
|
{
|
||||||
// The last section page is not aligned to the next order size.
|
continue;
|
||||||
|
|
||||||
let off = frames.len() - pages_for_current_order;
|
|
||||||
let final_page = base.next_by(off);
|
|
||||||
|
|
||||||
frames[off].next.store(order as usize, Ordering::Relaxed);
|
|
||||||
|
|
||||||
//info!("ORDER {order}: LAST {final_page:?}");
|
|
||||||
append_page(final_page, &frames[off], order);
|
|
||||||
|
|
||||||
frames = &frames[..off];
|
|
||||||
} else {
|
|
||||||
//info!("ORDER {order}: LAST SKIP");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if frames.is_empty() {
|
let mut base = section.base;
|
||||||
break;
|
let mut frames = section.frames;
|
||||||
}
|
|
||||||
|
for order in 0..=MAX_ORDER {
|
||||||
|
let pages_for_current_order = 1 << order;
|
||||||
|
|
||||||
if order == MAX_ORDER {
|
|
||||||
debug_assert_eq!(frames.len() % pages_for_current_order, 0);
|
debug_assert_eq!(frames.len() % pages_for_current_order, 0);
|
||||||
debug_assert!(base.is_aligned_to_order(MAX_ORDER));
|
debug_assert!(base.is_aligned_to_order(order));
|
||||||
|
|
||||||
for (off, info) in frames.iter().enumerate().step_by(pages_for_current_order) {
|
if !frames.is_empty() && order != MAX_ORDER && !base.is_aligned_to_order(order + 1)
|
||||||
info.next.store(MAX_ORDER as usize, Ordering::Relaxed);
|
{
|
||||||
append_page(base.next_by(off), info, MAX_ORDER);
|
frames[0].next.store(order as usize, Ordering::Relaxed);
|
||||||
|
// The first section page is not aligned to the next order size.
|
||||||
|
|
||||||
|
//info!("ORDER {order}: FIRST {base:?}");
|
||||||
|
append_page(
|
||||||
|
base,
|
||||||
|
&frames[0],
|
||||||
|
order,
|
||||||
|
&mut first_pages,
|
||||||
|
&mut last_pages,
|
||||||
|
allocator,
|
||||||
|
);
|
||||||
|
|
||||||
|
base = base.next_by(pages_for_current_order);
|
||||||
|
frames = &frames[pages_for_current_order..];
|
||||||
|
} else {
|
||||||
|
//info!("ORDER {order}: FIRST SKIP");
|
||||||
|
}
|
||||||
|
|
||||||
|
if !frames.is_empty()
|
||||||
|
&& order != MAX_ORDER
|
||||||
|
&& !base.next_by(frames.len()).is_aligned_to_order(order + 1)
|
||||||
|
{
|
||||||
|
// The last section page is not aligned to the next order size.
|
||||||
|
|
||||||
|
let off = frames.len() - pages_for_current_order;
|
||||||
|
let final_page = base.next_by(off);
|
||||||
|
|
||||||
|
frames[off].next.store(order as usize, Ordering::Relaxed);
|
||||||
|
|
||||||
|
//info!("ORDER {order}: LAST {final_page:?}");
|
||||||
|
append_page(
|
||||||
|
final_page,
|
||||||
|
&frames[off],
|
||||||
|
order,
|
||||||
|
&mut first_pages,
|
||||||
|
&mut last_pages,
|
||||||
|
allocator,
|
||||||
|
);
|
||||||
|
|
||||||
|
frames = &frames[..off];
|
||||||
|
} else {
|
||||||
|
//info!("ORDER {order}: LAST SKIP");
|
||||||
|
}
|
||||||
|
|
||||||
|
if frames.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if order == MAX_ORDER {
|
||||||
|
debug_assert_eq!(frames.len() % pages_for_current_order, 0);
|
||||||
|
debug_assert!(base.is_aligned_to_order(MAX_ORDER));
|
||||||
|
|
||||||
|
for (off, info) in frames.iter().enumerate().step_by(pages_for_current_order) {
|
||||||
|
info.next.store(MAX_ORDER as usize, Ordering::Relaxed);
|
||||||
|
append_page(
|
||||||
|
base.next_by(off),
|
||||||
|
info,
|
||||||
|
MAX_ORDER,
|
||||||
|
&mut first_pages,
|
||||||
|
&mut last_pages,
|
||||||
|
allocator,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//info!("SECTION from {:?}, {} pages, array at {:p}", section.base, section.frames.len(), section.frames);
|
||||||
|
}
|
||||||
|
for (order, tuple_opt) in last_pages.iter().enumerate() {
|
||||||
|
let Some((frame, info)) = tuple_opt else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
debug_assert!(frame.is_aligned_to_order(order as u32));
|
||||||
|
let free = info.as_free().unwrap();
|
||||||
|
debug_assert_eq!(free.prev().order(), order as u32);
|
||||||
|
free.set_next(P2Frame::new(None, order as u32));
|
||||||
}
|
}
|
||||||
|
|
||||||
//info!("SECTION from {:?}, {} pages, array at {:p}", section.base, section.frames.len(), section.frames);
|
first_pages
|
||||||
}
|
};
|
||||||
for (order, tuple_opt) in last_pages.iter().enumerate() {
|
|
||||||
let Some((frame, info)) = tuple_opt else {
|
let free_lists;
|
||||||
continue;
|
if let Some(regions) = numa::memory_regions() {
|
||||||
|
let free_list_page = allocator
|
||||||
|
.allocate(FrameCount::new(
|
||||||
|
(numa::number_of_memory_regions() * size_of::<Mutex<FreeList>>())
|
||||||
|
.div_ceil(PAGE_SIZE),
|
||||||
|
))
|
||||||
|
.expect("Failed to allocate free list page");
|
||||||
|
let va = unsafe { RmmA::phys_to_virt(free_list_page).data() as *mut Mutex<FreeList> };
|
||||||
|
free_lists = unsafe { slice::from_raw_parts_mut(va, numa::number_of_memory_regions()) };
|
||||||
|
for (i, region) in regions.enumerate() {
|
||||||
|
let free_list = FreeList {
|
||||||
|
for_orders: free_list_fill(Some(region), &mut allocator)
|
||||||
|
.map(|pair| pair.map(|(frame, _)| frame)),
|
||||||
|
used_frames: 0,
|
||||||
|
upper_limit: region.base.add(region.size).data(),
|
||||||
|
lower_limit: region.base.data(),
|
||||||
|
};
|
||||||
|
free_lists[i] = Mutex::new(free_list);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let free_list_page = allocator
|
||||||
|
.allocate(FrameCount::new(
|
||||||
|
size_of::<Mutex<FreeList>>().div_ceil(PAGE_SIZE),
|
||||||
|
))
|
||||||
|
.expect("Failed to allocate free list page");
|
||||||
|
let va = unsafe { RmmA::phys_to_virt(free_list_page).data() as *mut Mutex<FreeList> };
|
||||||
|
free_lists = unsafe { slice::from_raw_parts_mut(va, 1) };
|
||||||
|
let free_list = FreeList {
|
||||||
|
for_orders: free_list_fill(None, &mut allocator)
|
||||||
|
.map(|pair| pair.map(|(frame, _)| frame)),
|
||||||
|
used_frames: 0,
|
||||||
|
upper_limit: usize::MAX,
|
||||||
|
lower_limit: 0,
|
||||||
};
|
};
|
||||||
debug_assert!(frame.is_aligned_to_order(order as u32));
|
free_lists[0] = Mutex::new(free_list);
|
||||||
let free = info.as_free().unwrap();
|
|
||||||
debug_assert_eq!(free.prev().order(), order as u32);
|
|
||||||
free.set_next(P2Frame::new(None, order as u32));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FREELIST.lock().for_orders = first_pages.map(|pair| pair.map(|(frame, _)| frame));
|
FREE_LISTS.call_once(|| free_lists);
|
||||||
|
|
||||||
//debug_freelist();
|
//debug_freelist();
|
||||||
debug!("Initial freelist consistent");
|
debug!("Initial freelist consistent");
|
||||||
@@ -1027,10 +1229,9 @@ pub fn page_fault_handler(
|
|||||||
let mut token = unsafe { CleanLockToken::new() };
|
let mut token = unsafe { CleanLockToken::new() };
|
||||||
match context::memory::try_correcting_page_tables(faulting_page, mode, &mut token) {
|
match context::memory::try_correcting_page_tables(faulting_page, mode, &mut token) {
|
||||||
Ok(()) => return Ok(()),
|
Ok(()) => return Ok(()),
|
||||||
Err(PfError::Oom) => {
|
Err(PfError::Oom) => todo!("oom"),
|
||||||
return Err(Segv);
|
Err(PfError::Segv | PfError::RecursionLimitExceeded) => (),
|
||||||
}
|
Err(PfError::NonfatalInternalError) => todo!(),
|
||||||
Err(PfError::Segv | PfError::RecursionLimitExceeded | PfError::NonfatalInternalError) => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+224
@@ -0,0 +1,224 @@
|
|||||||
|
use core::ops::Add;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
acpi,
|
||||||
|
cpu_set::LogicalCpuId,
|
||||||
|
sync::{CleanLockToken, Mutex, L0},
|
||||||
|
};
|
||||||
|
use alloc::{sync::Arc, vec::Vec};
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use rmm::{Arch, BumpAllocator, MemoryArea, PhysicalAddress};
|
||||||
|
use spin::once::Once;
|
||||||
|
use syscall::{Error, Result, ENODATA, EOPNOTSUPP};
|
||||||
|
|
||||||
|
pub const MAX_DOMAINS: usize = 128;
|
||||||
|
|
||||||
|
static DOMAIN_NODE_MAP: Once<&'static [u32]> = Once::new();
|
||||||
|
static NUMA_CPUS: Once<&'static [u32]> = Once::new();
|
||||||
|
static NUMA_MEMORY: Once<&'static [NumaMemory]> = Once::new();
|
||||||
|
static DISTANCES: Once<&'static [u8]> = Once::new();
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NumaMemory {
|
||||||
|
pub start: usize,
|
||||||
|
pub length: usize,
|
||||||
|
pub node_id: u32,
|
||||||
|
pub _pad: [u8; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NumaCpu {
|
||||||
|
pub id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init<A: Arch>(allocator: &mut BumpAllocator<A>) {
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
|
||||||
|
{
|
||||||
|
acpi::srat::init(allocator, &DOMAIN_NODE_MAP, &NUMA_CPUS, &NUMA_MEMORY);
|
||||||
|
acpi::slit::init(allocator, &DISTANCES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assign_node_id(modify: bool) -> u8 {
|
||||||
|
static mut NODE_ID: u8 = 0;
|
||||||
|
if unsafe { NODE_ID } >= 128 {
|
||||||
|
panic!("Maximum number of domains supported is 128");
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
NODE_ID += 1;
|
||||||
|
let return_value = NODE_ID - 1;
|
||||||
|
if !modify {
|
||||||
|
NODE_ID -= 1;
|
||||||
|
}
|
||||||
|
return_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assign_memory_id() -> u8 {
|
||||||
|
static mut MEMORY_ID: u8 = 0;
|
||||||
|
if unsafe { MEMORY_ID } >= 128 {
|
||||||
|
panic!("Maximum number of memory regions supported is 128");
|
||||||
|
}
|
||||||
|
let old = unsafe { MEMORY_ID };
|
||||||
|
unsafe { MEMORY_ID = MEMORY_ID.add(1) };
|
||||||
|
old
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn domain_to_node_id(domain_id: u32) -> Option<u32> {
|
||||||
|
Some(*DOMAIN_NODE_MAP.get()?.get(domain_id as usize)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cpu_belongs_to_which_node(cpu_id: usize) -> Option<u32> {
|
||||||
|
Some(*NUMA_CPUS.get()?.get(cpu_id)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A helper function that prints information about NUMA - available nodes, cpus and memory blocks in them
|
||||||
|
/// their starts and lengths
|
||||||
|
pub fn dump_info() {
|
||||||
|
if let Some(map) = DOMAIN_NODE_MAP.get()
|
||||||
|
&& let Some(cpus) = NUMA_CPUS.get()
|
||||||
|
&& let Some(memories) = NUMA_MEMORY.get()
|
||||||
|
{
|
||||||
|
println!("Number of NUMA nodes: {}", assign_node_id(false));
|
||||||
|
for i in 0..cpus.len() {
|
||||||
|
if cpus[i] == u32::MAX {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
println!("CPU {} : Node {}", i, cpus[i])
|
||||||
|
}
|
||||||
|
for i in 0..memories.len() {
|
||||||
|
if memories[i].length == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
"Memory Block starting at address {:#x} of size {:#x} bytes : Node {}",
|
||||||
|
memories[i].start, memories[i].length, memories[i].node_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"The system has either no support for NUMA or there was an error during initialisation"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NumaMemoryIter {
|
||||||
|
i: usize,
|
||||||
|
mem: &'static [NumaMemory],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for NumaMemoryIter {
|
||||||
|
type Item = MemoryArea;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let mem = self.mem.get(self.i)?;
|
||||||
|
if mem.length == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
self.i += 1;
|
||||||
|
Some(MemoryArea {
|
||||||
|
base: PhysicalAddress::new(mem.start),
|
||||||
|
size: mem.length,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NumaMemoryIter {
|
||||||
|
/// Skips an arbitrarily chosen `i`th element. Unlike `skip`, which skips the first `n` elements,
|
||||||
|
/// `iskip` can ignore non-consecutive elements
|
||||||
|
pub fn iskip(&self, addr: usize) -> Option<usize> {
|
||||||
|
let i = self.mem.binary_search_by_key(&addr, |e| e.start).ok()?;
|
||||||
|
Some(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn number_of_memory_regions() -> usize {
|
||||||
|
if let Some(mem) = NUMA_MEMORY.get() {
|
||||||
|
mem.iter()
|
||||||
|
.map(|e| if e.length != 0 { 1 } else { 0 })
|
||||||
|
.sum::<usize>()
|
||||||
|
} else {
|
||||||
|
0 // TODO: or should 1 be returned?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn memory_regions() -> Option<NumaMemoryIter> {
|
||||||
|
if let Some(mem) = NUMA_MEMORY.get() {
|
||||||
|
Some(NumaMemoryIter { i: 0, mem })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nearest_next_memory_region(addr: usize, overlap: bool) -> Option<&'static NumaMemory> {
|
||||||
|
NUMA_MEMORY
|
||||||
|
.get()?
|
||||||
|
.iter()
|
||||||
|
.filter_map(|e| {
|
||||||
|
if if overlap {
|
||||||
|
e.start >= addr
|
||||||
|
} else {
|
||||||
|
e.start > addr
|
||||||
|
} {
|
||||||
|
Some(e)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.min_by_key(|e| e.start)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nearest_preceding_memory_region(addr: usize, overlap: bool) -> Option<&'static NumaMemory> {
|
||||||
|
NUMA_MEMORY
|
||||||
|
.get()?
|
||||||
|
.iter()
|
||||||
|
.filter_map(|e| {
|
||||||
|
if if overlap {
|
||||||
|
e.start <= addr
|
||||||
|
} else {
|
||||||
|
e.start < addr
|
||||||
|
} {
|
||||||
|
Some(e)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.max_by_key(|e| e.start)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_numa_info(token: &mut CleanLockToken) -> Result<Vec<u8>> {
|
||||||
|
let cpu_info = NUMA_CPUS
|
||||||
|
.get()
|
||||||
|
.ok_or(Error::new(EOPNOTSUPP))?
|
||||||
|
.iter()
|
||||||
|
.map(|e| e.to_ne_bytes())
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<u8>>();
|
||||||
|
let mem_info = NUMA_MEMORY
|
||||||
|
.get()
|
||||||
|
.ok_or(Error::new(EOPNOTSUPP))?
|
||||||
|
.iter()
|
||||||
|
.map(|e| {
|
||||||
|
[
|
||||||
|
e.start.to_ne_bytes(),
|
||||||
|
e.length.to_ne_bytes(),
|
||||||
|
(e.node_id as usize).to_ne_bytes(),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<u8>>();
|
||||||
|
let mut numa_info = Vec::new();
|
||||||
|
numa_info.extend(cpu_info);
|
||||||
|
numa_info.extend(mem_info);
|
||||||
|
Ok(numa_info)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_numa_distance_info(token: &mut CleanLockToken) -> Result<Vec<u8>> {
|
||||||
|
Ok(DISTANCES
|
||||||
|
.get()
|
||||||
|
.ok_or(Error::new(ENODATA))?
|
||||||
|
.iter()
|
||||||
|
.map(|e| *e)
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
@@ -6,8 +6,6 @@
|
|||||||
//! The kernel validates paths and file descriptors before they are passed to schemes,
|
//! The kernel validates paths and file descriptors before they are passed to schemes,
|
||||||
//! also stripping the scheme identifier of paths if necessary.
|
//! also stripping the scheme identifier of paths if necessary.
|
||||||
|
|
||||||
// TODO: Move handling of the global namespace to userspace.
|
|
||||||
|
|
||||||
use alloc::{
|
use alloc::{
|
||||||
sync::{Arc, Weak},
|
sync::{Arc, Weak},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ const FILES: &[(&str, Kind)] = &[
|
|||||||
("irq", Rd(irq::resource)),
|
("irq", Rd(irq::resource)),
|
||||||
("log", Rd(log::resource)),
|
("log", Rd(log::resource)),
|
||||||
("mem", Rd(mem::resource)),
|
("mem", Rd(mem::resource)),
|
||||||
|
("numa", Rd(crate::numa::get_numa_info)),
|
||||||
|
("numa_dist", Rd(crate::numa::get_numa_distance_info)),
|
||||||
("syscall", Rd(syscall::resource)),
|
("syscall", Rd(syscall::resource)),
|
||||||
("uname", Rd(uname::resource)),
|
("uname", Rd(uname::resource)),
|
||||||
("env", Rd(|_| Ok(Vec::from(crate::startup::init_env())))),
|
("env", Rd(|_| Ok(Vec::from(crate::startup::init_env())))),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
arch::CurrentRmmArch,
|
arch::CurrentRmmArch,
|
||||||
memory::PAGE_SIZE,
|
memory::PAGE_SIZE,
|
||||||
|
numa,
|
||||||
startup::{memory::BootloaderMemoryKind::Null, KernelArgs},
|
startup::{memory::BootloaderMemoryKind::Null, KernelArgs},
|
||||||
};
|
};
|
||||||
use core::{
|
use core::{
|
||||||
|
|||||||
+10
-14
@@ -1,18 +1,20 @@
|
|||||||
use core::{
|
use core::{
|
||||||
hint, slice,
|
hint,
|
||||||
|
ptr::NonNull,
|
||||||
|
slice,
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
};
|
};
|
||||||
use core::ptr::NonNull;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::interrupt,
|
arch::interrupt,
|
||||||
context,
|
context::{self, switch::SwitchResult},
|
||||||
context::switch::SwitchResult,
|
|
||||||
memory::{PhysicalAddress, RmmA, RmmArch},
|
memory::{PhysicalAddress, RmmA, RmmArch},
|
||||||
profiling, scheme,
|
profiling, scheme,
|
||||||
sync::CleanLockToken,
|
sync::CleanLockToken,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::numa;
|
||||||
|
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
|
|
||||||
#[repr(C, packed(8))]
|
#[repr(C, packed(8))]
|
||||||
@@ -105,7 +107,7 @@ impl KernelArgs {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
if data.starts_with(b"RSD PTR ") {
|
if data.starts_with(b"RSD PTR ") {
|
||||||
Some(NonNull::new(data.as_ptr() as *mut u8).unwrap())
|
Some(NonNull::from_ref(data).cast())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -186,6 +188,7 @@ pub(crate) fn kmain(bootstrap: Bootstrap) -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
numa::dump_info();
|
||||||
run_userspace(&mut token)
|
run_userspace(&mut token)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,15 +233,8 @@ fn run_userspace(token: &mut CleanLockToken) -> ! {
|
|||||||
interrupt::enable_and_nop();
|
interrupt::enable_and_nop();
|
||||||
}
|
}
|
||||||
SwitchResult::AllContextsIdle => {
|
SwitchResult::AllContextsIdle => {
|
||||||
// Enable interrupts, then enter the deepest MWAIT
|
// Enable interrupts, then halt CPU (to save power) until the next interrupt is actually fired.
|
||||||
// C-state (C6/C7/C8/C9/C10/S0iX). On CPUs without
|
interrupt::enable_and_halt();
|
||||||
// MWAIT (pre-Nehalem), `idle_loop` falls back to
|
|
||||||
// `enable_and_halt` (lands in C1). The MWAIT path
|
|
||||||
// enables Arrow Lake-H to actually reach S0i2/S0i3
|
|
||||||
// substates and dramatically reduce idle power on the
|
|
||||||
// LG Gram 2025; without it the kernel only lands in
|
|
||||||
// C1 and the CPU stays relatively warm.
|
|
||||||
interrupt::idle_loop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user