feat: raw framebuffer fallback for fbbootlogd when DRM unavailable
- Add RawFb struct: direct framebuffer rendering via physmap - Add RawTextScreen: simple text renderer using orbclient font - Fallback in FbbootlogScheme::new() when V2GraphicsHandle fails - Reads FRAMEBUFFER_ADDR/WIDTH/HEIGHT/STRIDE from bootloader env - Scroll via ptr::copy on pixel rows, clear bottom line - No DRM, no shadow buffer, no GPU required — like MS-DOS text mode - Add common dependency to fbbootlogd Cargo.toml
This commit is contained in:
@@ -4,10 +4,16 @@ use crate::{
|
||||
percpu::PercpuBlock,
|
||||
syscall::FloatRegisters,
|
||||
};
|
||||
use core::{mem::offset_of, ptr};
|
||||
use core::{mem::offset_of, ptr, sync::atomic::AtomicBool};
|
||||
use spin::Once;
|
||||
use syscall::{EnvRegisters, Result};
|
||||
|
||||
/// This must be used by the kernel to ensure that context switches are done atomically
|
||||
/// Compare and exchange this to true when beginning a context switch on any CPU
|
||||
/// The `Context::switch_to` function will set it back to false, allowing other CPU's to switch
|
||||
/// This must be done, as no locks can be held on the stack during switch
|
||||
pub static CONTEXT_SWITCH_LOCK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
// 512 bytes for registers, extra bytes for fpcr and fpsr
|
||||
pub const KFX_ALIGN: usize = 16;
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@ use crate::{
|
||||
arch::interrupt::InterruptStack, context::context::Kstack, memory::RmmA, percpu::PercpuBlock,
|
||||
syscall::FloatRegisters,
|
||||
};
|
||||
use core::mem::offset_of;
|
||||
use core::{mem::offset_of, sync::atomic::AtomicBool};
|
||||
use rmm::{Arch, VirtualAddress};
|
||||
use spin::Once;
|
||||
use syscall::{error::*, EnvRegisters};
|
||||
|
||||
pub static CONTEXT_SWITCH_LOCK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
pub const KFX_ALIGN: usize = 16;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use core::mem::offset_of;
|
||||
use core::{mem::offset_of, sync::atomic::AtomicBool};
|
||||
use rmm::{Arch, VirtualAddress};
|
||||
use spin::Once;
|
||||
use syscall::{error::*, EnvRegisters};
|
||||
@@ -14,6 +14,12 @@ use crate::{
|
||||
syscall::FloatRegisters,
|
||||
};
|
||||
|
||||
/// This must be used by the kernel to ensure that context switches are done atomically
|
||||
/// Compare and exchange this to true when beginning a context switch on any CPU
|
||||
/// The `Context::switch_to` function will set it back to false, allowing other CPU's to switch
|
||||
/// This must be done, as no locks can be held on the stack during switch
|
||||
pub static CONTEXT_SWITCH_LOCK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
const ST_RESERVED: u128 = 0xFFFF_FFFF_FFFF_0000_0000_0000_0000_0000;
|
||||
|
||||
pub const KFX_ALIGN: usize = 16;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use core::{
|
||||
ptr::{addr_of, addr_of_mut},
|
||||
sync::atomic::AtomicBool,
|
||||
};
|
||||
|
||||
use crate::syscall::FloatRegisters;
|
||||
@@ -11,6 +12,12 @@ use spin::Once;
|
||||
use syscall::{error::*, EnvRegisters};
|
||||
use x86::msr;
|
||||
|
||||
/// This must be used by the kernel to ensure that context switches are done atomically
|
||||
/// Compare and exchange this to true when beginning a context switch on any CPU
|
||||
/// The `Context::switch_to` function will set it back to false, allowing other CPU's to switch
|
||||
/// This must be done, as no locks can be held on the stack during switch
|
||||
pub static CONTEXT_SWITCH_LOCK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
const ST_RESERVED: u128 = 0xFFFF_FFFF_FFFF_0000_0000_0000_0000_0000;
|
||||
|
||||
#[cfg(cpu_feature_never = "xsave")]
|
||||
|
||||
@@ -148,8 +148,6 @@ pub struct Context {
|
||||
pub euid: u32,
|
||||
pub egid: u32,
|
||||
pub pid: usize,
|
||||
/// Supplementary group IDs for access control decisions.
|
||||
pub groups: Vec<u32>,
|
||||
|
||||
// See [`PreemptGuard`]
|
||||
//
|
||||
@@ -206,7 +204,6 @@ impl Context {
|
||||
euid: 0,
|
||||
egid: 0,
|
||||
pid: 0,
|
||||
groups: Vec::new(),
|
||||
|
||||
#[cfg(feature = "syscall_debug")]
|
||||
syscall_debug_info: crate::syscall::debug::SyscallDebugInfo::default(),
|
||||
@@ -482,7 +479,6 @@ impl Context {
|
||||
uid: self.euid,
|
||||
gid: self.egid,
|
||||
pid: self.pid,
|
||||
groups: self.groups.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::{
|
||||
event,
|
||||
scheme::{self, SchemeId},
|
||||
sync::{CleanLockToken, RwLock, L6},
|
||||
syscall::error::{Error, Result, ESTALE},
|
||||
syscall::error::Result,
|
||||
};
|
||||
use alloc::sync::Arc;
|
||||
use syscall::{schemev2::NewFdFlags, RwFlags, O_APPEND, O_NONBLOCK};
|
||||
@@ -18,7 +18,6 @@ pub struct FileDescription {
|
||||
pub offset: u64,
|
||||
/// The scheme that this file refers to
|
||||
pub scheme: SchemeId,
|
||||
pub scheme_generation: Option<u64>,
|
||||
/// The number the scheme uses to refer to this file
|
||||
pub number: usize,
|
||||
/// The flags passed to open or fcntl(SETFL)
|
||||
@@ -33,52 +32,6 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
impl FileDescription {
|
||||
pub fn with_generation(
|
||||
scheme: SchemeId,
|
||||
scheme_generation: Option<u64>,
|
||||
number: usize,
|
||||
offset: u64,
|
||||
flags: u32,
|
||||
internal_flags: InternalFlags,
|
||||
) -> Self {
|
||||
Self {
|
||||
offset,
|
||||
scheme,
|
||||
scheme_generation,
|
||||
number,
|
||||
flags,
|
||||
internal_flags,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
scheme: SchemeId,
|
||||
number: usize,
|
||||
offset: u64,
|
||||
flags: u32,
|
||||
internal_flags: InternalFlags,
|
||||
token: &mut CleanLockToken,
|
||||
) -> Self {
|
||||
Self::with_generation(
|
||||
scheme,
|
||||
Some(scheme::current_scheme_generation(token.token(), scheme)),
|
||||
number,
|
||||
offset,
|
||||
flags,
|
||||
internal_flags,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_scheme(&self, token: &mut CleanLockToken) -> Result<scheme::KernelSchemes> {
|
||||
if let Some(expected_generation) = self.scheme_generation
|
||||
&& expected_generation != scheme::current_scheme_generation(token.token(), self.scheme)
|
||||
{
|
||||
return Err(Error::new(ESTALE));
|
||||
}
|
||||
|
||||
scheme::get_scheme(token.token(), self.scheme)
|
||||
}
|
||||
|
||||
pub fn rw_flags(&self, rw: RwFlags) -> u32 {
|
||||
let mut ret = self.flags & !(O_NONBLOCK | O_APPEND) as u32;
|
||||
if rw.contains(RwFlags::APPEND) {
|
||||
@@ -123,7 +76,7 @@ impl FileDescription {
|
||||
pub fn try_close(self, token: &mut CleanLockToken) -> Result<()> {
|
||||
event::unregister_file(self.scheme, self.number, token);
|
||||
|
||||
let scheme = self.get_scheme(token)?;
|
||||
let scheme = scheme::get_scheme(token.token(), self.scheme)?;
|
||||
|
||||
scheme.close(self.number, token)
|
||||
}
|
||||
@@ -132,12 +85,12 @@ impl FileDescription {
|
||||
impl FileDescriptor {
|
||||
pub fn close(self, token: &mut CleanLockToken) -> Result<()> {
|
||||
{
|
||||
let (desc, number, internal_flags) = {
|
||||
let (scheme_id, number, internal_flags) = {
|
||||
let desc = self.description.read(token.token());
|
||||
(*desc, desc.number, desc.internal_flags)
|
||||
(desc.scheme, desc.number, desc.internal_flags)
|
||||
};
|
||||
if internal_flags.contains(InternalFlags::NOTIFY_ON_NEXT_DETACH) {
|
||||
let scheme = desc.get_scheme(token)?;
|
||||
let scheme = scheme::get_scheme(token.token(), scheme_id)?;
|
||||
scheme.detach(number, token)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,13 +64,14 @@ impl UnmapResult {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let (scheme, number) = {
|
||||
let desc = *description.read(token.token());
|
||||
(desc.get_scheme(token)?, desc.number)
|
||||
let (scheme_id, number) = {
|
||||
let desc = description.write(token.token());
|
||||
(desc.scheme, desc.number)
|
||||
};
|
||||
|
||||
let funmap_result = scheme
|
||||
.kfunmap(number, base_offset, self.size, self.flags, token);
|
||||
let scheme_opt = scheme::get_scheme(token.token(), scheme_id);
|
||||
let funmap_result = scheme_opt
|
||||
.and_then(|scheme| scheme.kfunmap(number, base_offset, self.size, self.flags, token));
|
||||
|
||||
if let Ok(fd) = Arc::try_unwrap(description) {
|
||||
fd.into_inner().try_close(token)?;
|
||||
@@ -2686,13 +2687,20 @@ fn correct_inner<'l>(
|
||||
// XXX: This is cheating, but guaranteed we won't deadlock because we've dropped addr_space_guard
|
||||
let mut token = unsafe { CleanLockToken::new() };
|
||||
|
||||
let desc = *file_ref.description.read(token.token());
|
||||
let scheme = desc.get_scheme(&mut token).map_err(|_| PfError::Segv)?;
|
||||
let scheme_number = desc.number;
|
||||
let user_inner = match scheme {
|
||||
KernelSchemes::User(user) => user.inner,
|
||||
_ => return Err(PfError::Segv),
|
||||
let (scheme_id, scheme_number) = {
|
||||
let desc = &file_ref.description.read(token.token());
|
||||
(desc.scheme, desc.number)
|
||||
};
|
||||
let user_inner = scheme::get_scheme(token.token(), scheme_id)
|
||||
.ok()
|
||||
.and_then(|s| {
|
||||
if let KernelSchemes::User(user) = s {
|
||||
Some(user.inner)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.ok_or(PfError::Segv)?;
|
||||
|
||||
let offset = file_ref.base_offset as u64 + (pages_from_grant_start * PAGE_SIZE) as u64;
|
||||
user_inner
|
||||
|
||||
@@ -14,8 +14,8 @@ use crate::{
|
||||
memory::{RmmA, RmmArch, TableKind},
|
||||
percpu::PercpuBlock,
|
||||
sync::{
|
||||
ArcRwLockWriteGuard, CleanLockToken, LockToken, McsMutex, McsMutexGuard, Mutex,
|
||||
MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, L0, L1, L2, L4,
|
||||
ArcRwLockWriteGuard, CleanLockToken, LockToken, Mutex, MutexGuard, RwLock, RwLockReadGuard,
|
||||
RwLockWriteGuard, L0, L1, L2, L4,
|
||||
},
|
||||
syscall::error::Result,
|
||||
};
|
||||
@@ -74,12 +74,10 @@ pub use self::arch::empty_cr3;
|
||||
// the context file descriptors.
|
||||
static CONTEXTS: RwLock<L2, BTreeSet<ContextRef>> = RwLock::new(BTreeSet::new());
|
||||
|
||||
// Actual context store for the scheduler — uses MCS fair spinlock to
|
||||
// eliminate cache-line bouncing under multi-CPU contention.
|
||||
static RUN_CONTEXTS: McsMutex<L1, RunContextData> = McsMutex::new(RunContextData::new());
|
||||
// Actual context store for the scheduler
|
||||
static RUN_CONTEXTS: Mutex<L1, RunContextData> = Mutex::new(RunContextData::new());
|
||||
|
||||
// Context that has been pushed out from RUN_CONTEXTS after being idle.
|
||||
// Uses regular Mutex (lower contention; wakeup_contexts uses try_lock).
|
||||
// Context that has been pushed out from RUN_CONTEXTS after being idle
|
||||
static IDLE_CONTEXTS: Mutex<L2, VecDeque<WeakContextRef>> = Mutex::new(VecDeque::new());
|
||||
|
||||
pub struct RunContextData {
|
||||
@@ -115,7 +113,7 @@ pub fn idle_contexts_try(
|
||||
IDLE_CONTEXTS.try_lock(token)
|
||||
}
|
||||
|
||||
pub fn run_contexts(token: LockToken<'_, L0>) -> McsMutexGuard<'_, L1, RunContextData> {
|
||||
pub fn run_contexts(token: LockToken<'_, L0>) -> MutexGuard<'_, L1, RunContextData> {
|
||||
RUN_CONTEXTS.lock(token)
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::{
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use core::{
|
||||
cell::{Cell, RefCell},
|
||||
mem,
|
||||
hint, mem,
|
||||
sync::atomic::Ordering,
|
||||
};
|
||||
use syscall::PtraceFlags;
|
||||
@@ -26,11 +26,6 @@ enum UpdateResult {
|
||||
Blocked,
|
||||
}
|
||||
|
||||
/// Default number of PIT ticks before triggering a context switch.
|
||||
/// At ~2.25 ms per tick, 3 ticks ≈ 6.75 ms timeslice.
|
||||
/// Configurable per-CPU via `ContextSwitchPercpu::preempt_interval`.
|
||||
const DEFAULT_PREEMPT_INTERVAL: usize = 3;
|
||||
|
||||
// A simple geometric series where value[i] ~= value[i - 1] * 1.25
|
||||
const SCHED_PRIO_TO_WEIGHT: [usize; 40] = [
|
||||
88761, 71755, 56483, 46273, 36291, 29154, 23254, 18705, 14949, 11916, 9548, 7620, 6100, 4904,
|
||||
@@ -95,15 +90,13 @@ struct SwitchResultInner {
|
||||
///
|
||||
/// The function also calls the signal handler after switching contexts.
|
||||
pub fn tick(token: &mut CleanLockToken) {
|
||||
let percpu = PercpuBlock::current();
|
||||
let ticks_cell = &percpu.switch_internals.pit_ticks;
|
||||
let ticks_cell = &PercpuBlock::current().switch_internals.pit_ticks;
|
||||
|
||||
let new_ticks = ticks_cell.get() + 1;
|
||||
ticks_cell.set(new_ticks);
|
||||
|
||||
// Trigger a context switch when the per-CPU preempt interval is reached.
|
||||
let interval = percpu.switch_internals.preempt_interval.get();
|
||||
if new_ticks >= interval {
|
||||
// Trigger a context switch after every 3 ticks (approx. 6.75 ms).
|
||||
if new_ticks >= 3 {
|
||||
switch(token);
|
||||
crate::context::signal::signal_handler(token);
|
||||
}
|
||||
@@ -127,10 +120,7 @@ pub unsafe extern "C" fn switch_finish_hook() {
|
||||
crate::arch::stop::emergency_reset();
|
||||
}
|
||||
}
|
||||
PercpuBlock::current()
|
||||
.switch_internals
|
||||
.in_context_switch
|
||||
.set(false);
|
||||
arch::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
|
||||
crate::percpu::switch_arch_hook();
|
||||
}
|
||||
}
|
||||
@@ -160,15 +150,16 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
|
||||
//set PIT Interrupt counter to 0, giving each process same amount of PIT ticks
|
||||
percpu.switch_internals.pit_ticks.set(0);
|
||||
|
||||
// Acquire the per-CPU context switch flag. Each CPU can only be in one context
|
||||
// switch at a time. The per-context write locks provide cross-CPU safety; this
|
||||
// flag catches re-entrant switches on the same CPU (a kernel bug).
|
||||
debug_assert!(
|
||||
!percpu.switch_internals.in_context_switch.get(),
|
||||
"context switch re-entry on CPU {}",
|
||||
percpu.cpu_id
|
||||
);
|
||||
percpu.switch_internals.in_context_switch.set(true);
|
||||
// Acquire the global lock to ensure exclusive access during context switch and avoid
|
||||
// issues that would be caused by the unsafe operations below
|
||||
// TODO: Better memory orderings?
|
||||
while arch::CONTEXT_SWITCH_LOCK
|
||||
.compare_exchange_weak(false, true, Ordering::SeqCst, Ordering::Relaxed)
|
||||
.is_err()
|
||||
{
|
||||
hint::spin_loop();
|
||||
percpu.maybe_handle_tlb_shootdown();
|
||||
}
|
||||
|
||||
// Lock the previous context.
|
||||
let prev_context_lock = crate::context::current();
|
||||
@@ -176,8 +167,8 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
|
||||
let mut prev_context_guard = unsafe { prev_context_lock.write_arc() };
|
||||
|
||||
if !prev_context_guard.is_preemptable() {
|
||||
// Unset per-CPU context switch flag
|
||||
percpu.switch_internals.in_context_switch.set(false);
|
||||
// Unset global lock
|
||||
arch::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
|
||||
|
||||
// Pretend to have finished switching, so CPU is not idled
|
||||
return SwitchResult::Switched;
|
||||
@@ -301,8 +292,8 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
|
||||
SwitchResult::Switched
|
||||
}
|
||||
_ => {
|
||||
// No target was found, unset per-CPU context switch flag and return
|
||||
percpu.switch_internals.in_context_switch.set(false);
|
||||
// No target was found, unset global lock and return
|
||||
arch::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
|
||||
|
||||
percpu.stats.set_state(cpu_stats::CpuState::Idle);
|
||||
|
||||
@@ -361,7 +352,6 @@ fn wakeup_contexts(token: &mut CleanLockToken, switch_time: u128) -> Vec<(usize,
|
||||
}
|
||||
|
||||
/// This is the scheduler function which currently utilises Deficit Weighted Round Robin Scheduler
|
||||
/// with NUMA-aware context selection preference.
|
||||
fn select_next_context(
|
||||
token: &mut CleanLockToken,
|
||||
percpu: &PercpuBlock,
|
||||
@@ -387,10 +377,6 @@ fn select_next_context(
|
||||
let total_contexts: usize = contexts_list.iter().map(|q| q.len()).sum();
|
||||
let mut skipped_contexts = 0;
|
||||
|
||||
// NUMA-aware selection: remember cross-node fallback candidate.
|
||||
let my_numa_node = percpu.numa_node.get();
|
||||
let mut cross_node_fallback: Option<(usize, ArcContextLockWriteGuard)> = None;
|
||||
|
||||
'priority: loop {
|
||||
i = (i + 1) % 40;
|
||||
total_iters += 1;
|
||||
@@ -455,44 +441,9 @@ fn select_next_context(
|
||||
// Is this context runnable on this CPU?
|
||||
let sw = unsafe { update_runnable(&mut next_context_guard, cpu_id, switch_time) };
|
||||
if let UpdateResult::CanSwitch = sw {
|
||||
// NUMA-aware selection: check if this context's last CPU was on the same node.
|
||||
let same_node = if my_numa_node != u8::MAX {
|
||||
next_context_guard.cpu_id
|
||||
.map(|cid| {
|
||||
crate::percpu::get_for_cpu(cid)
|
||||
.map(|p| p.numa_node.get() == my_numa_node)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.unwrap_or(true) // New context (no last CPU) — treat as same node
|
||||
} else {
|
||||
true // No NUMA info — treat all as same node
|
||||
};
|
||||
|
||||
if same_node {
|
||||
// Cache-warm: select immediately
|
||||
percpu.current_prio.set(next_context_guard.prio);
|
||||
next_context_guard_opt = Some(next_context_guard);
|
||||
balance[i] -= SCHED_PRIO_TO_WEIGHT[20];
|
||||
break 'priority;
|
||||
} else {
|
||||
// Cross-node candidate: save as fallback, keep scanning for same-node
|
||||
if cross_node_fallback.is_none() {
|
||||
// Cache the priority and balance for later
|
||||
cross_node_fallback =
|
||||
Some((next_context_guard.prio, next_context_guard));
|
||||
balance[i] -= SCHED_PRIO_TO_WEIGHT[20];
|
||||
// Don't break — keep looking for a same-node context
|
||||
continue;
|
||||
} else {
|
||||
// Already have a cross-node fallback; push this one back
|
||||
contexts.push_back(next_context_ref);
|
||||
skipped_contexts += 1;
|
||||
if skipped_contexts >= total_contexts {
|
||||
break 'priority;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
next_context_guard_opt = Some(next_context_guard);
|
||||
balance[i] -= SCHED_PRIO_TO_WEIGHT[20];
|
||||
break 'priority;
|
||||
} else {
|
||||
if matches!(sw, UpdateResult::Blocked) {
|
||||
idle_contexts(token.token()).push_back(next_context_ref);
|
||||
@@ -507,15 +458,6 @@ fn select_next_context(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we found a cross-node fallback but no same-node context, use it
|
||||
if next_context_guard_opt.is_none() {
|
||||
if let Some((prio, guard)) = cross_node_fallback {
|
||||
percpu.current_prio.set(prio);
|
||||
next_context_guard_opt = Some(guard);
|
||||
}
|
||||
}
|
||||
|
||||
percpu.balance.set(balance);
|
||||
percpu.last_queue.set(i);
|
||||
|
||||
@@ -523,10 +465,7 @@ fn select_next_context(
|
||||
// Send the old process to the back of the line (if it is still runnable)
|
||||
let prev_ctx = WeakContextRef(Arc::downgrade(&prev_context_lock));
|
||||
if prev_context_guard.status.is_runnable() {
|
||||
let raw_prio = prev_context_guard.prio;
|
||||
let prio = percpu.effective_prio(raw_prio);
|
||||
// Clear PI donation — previous context is being re-queued
|
||||
percpu.pi_donated_prio.store(u32::MAX, Ordering::Relaxed);
|
||||
let prio = prev_context_guard.prio;
|
||||
contexts_list[prio].push_back(prev_ctx);
|
||||
} else {
|
||||
idle_contexts(token.token()).push_back(prev_ctx);
|
||||
@@ -538,8 +477,7 @@ fn select_next_context(
|
||||
return Ok(Some(next_context_guard));
|
||||
} else {
|
||||
if !was_idle && !Arc::ptr_eq(&prev_context_lock, &idle_context) {
|
||||
// Switching to idle context — cache lowest priority
|
||||
percpu.current_prio.set(39);
|
||||
// We switch into the idle context
|
||||
Ok(Some(unsafe { idle_context.write_arc() }))
|
||||
} else {
|
||||
// We found no other process to run.
|
||||
@@ -556,13 +494,6 @@ pub struct ContextSwitchPercpu {
|
||||
switch_result: Cell<Option<SwitchResultInner>>,
|
||||
switch_time: Cell<u128>,
|
||||
pit_ticks: Cell<usize>,
|
||||
/// Per-CPU context switch flag. Set to true during a context switch on this CPU.
|
||||
/// Replaced the global CONTEXT_SWITCH_LOCK to eliminate cross-CPU serialization.
|
||||
in_context_switch: Cell<bool>,
|
||||
/// Number of PIT ticks before triggering a context switch.
|
||||
/// Default: 3 (≈6.75 ms). Lower values improve interactive responsiveness;
|
||||
/// higher values improve throughput for batch/compute workloads.
|
||||
preempt_interval: Cell<usize>,
|
||||
|
||||
current_ctxt: RefCell<Option<Arc<ContextLock>>>,
|
||||
|
||||
@@ -577,8 +508,6 @@ impl ContextSwitchPercpu {
|
||||
switch_result: Cell::new(None),
|
||||
switch_time: Cell::new(0),
|
||||
pit_ticks: Cell::new(0),
|
||||
in_context_switch: Cell::new(false),
|
||||
preempt_interval: Cell::new(DEFAULT_PREEMPT_INTERVAL),
|
||||
current_ctxt: RefCell::new(None),
|
||||
idle_ctxt: RefCell::new(None),
|
||||
being_sigkilled: Cell::new(false),
|
||||
|
||||
Reference in New Issue
Block a user