use alloc::vec::Vec;
use core::ops::Range;
use super::{page::DynPage, Vaddr, PAGE_SIZE};
use crate::{
cpu::{CpuSet, PinCurrentCpu},
cpu_local,
sync::SpinLock,
task::disable_preempt,
};
pub struct TlbFlusher<G: PinCurrentCpu> {
target_cpus: CpuSet,
need_remote_flush: bool,
need_self_flush: bool,
_pin_current: G,
}
impl<G: PinCurrentCpu> TlbFlusher<G> {
pub fn new(target_cpus: CpuSet, pin_current_guard: G) -> Self {
let current_cpu = pin_current_guard.current_cpu();
let mut need_self_flush = false;
let mut need_remote_flush = false;
for cpu in target_cpus.iter() {
if cpu == current_cpu {
need_self_flush = true;
} else {
need_remote_flush = true;
}
}
Self {
target_cpus,
need_remote_flush,
need_self_flush,
_pin_current: pin_current_guard,
}
}
pub fn issue_tlb_flush(&self, op: TlbFlushOp) {
self.issue_tlb_flush_(op, None);
}
pub fn dispatch_tlb_flush(&self) {
if !self.need_remote_flush {
return;
}
crate::smp::inter_processor_call(&self.target_cpus, do_remote_flush);
}
pub fn issue_tlb_flush_with(&self, op: TlbFlushOp, drop_after_flush: DynPage) {
self.issue_tlb_flush_(op, Some(drop_after_flush));
}
pub fn need_remote_flush(&self) -> bool {
self.need_remote_flush
}
pub fn need_self_flush(&self) -> bool {
self.need_self_flush
}
fn issue_tlb_flush_(&self, op: TlbFlushOp, drop_after_flush: Option<DynPage>) {
let op = op.optimize_for_large_range();
if !self.need_remote_flush {
if self.need_self_flush {
op.perform_on_current();
}
return;
}
for cpu in self.target_cpus.iter() {
let mut op_queue = FLUSH_OPS.get_on_cpu(cpu).lock();
if let Some(drop_after_flush) = drop_after_flush.clone() {
PAGE_KEEPER.get_on_cpu(cpu).lock().push(drop_after_flush);
}
op_queue.push(op.clone());
}
}
}
#[derive(Debug, Clone)]
pub enum TlbFlushOp {
All,
Address(Vaddr),
Range(Range<Vaddr>),
}
impl TlbFlushOp {
pub fn perform_on_current(&self) {
use crate::arch::mm::{
tlb_flush_addr, tlb_flush_addr_range, tlb_flush_all_excluding_global,
};
match self {
TlbFlushOp::All => tlb_flush_all_excluding_global(),
TlbFlushOp::Address(addr) => tlb_flush_addr(*addr),
TlbFlushOp::Range(range) => tlb_flush_addr_range(range),
}
}
fn optimize_for_large_range(self) -> Self {
match self {
TlbFlushOp::Range(range) => {
if range.len() > FLUSH_ALL_RANGE_THRESHOLD {
TlbFlushOp::All
} else {
TlbFlushOp::Range(range)
}
}
_ => self,
}
}
}
cpu_local! {
static FLUSH_OPS: SpinLock<OpsStack> = SpinLock::new(OpsStack::new());
static PAGE_KEEPER: SpinLock<Vec<DynPage>> = SpinLock::new(Vec::new());
}
fn do_remote_flush() {
let preempt_guard = disable_preempt();
let current_cpu = preempt_guard.current_cpu();
let mut op_queue = FLUSH_OPS.get_on_cpu(current_cpu).lock();
op_queue.flush_all();
PAGE_KEEPER.get_on_cpu(current_cpu).lock().clear();
}
pub(crate) const FLUSH_ALL_RANGE_THRESHOLD: usize = 32 * PAGE_SIZE;
const FLUSH_ALL_OPS_THRESHOLD: usize = 32;
struct OpsStack {
ops: [Option<TlbFlushOp>; FLUSH_ALL_OPS_THRESHOLD],
need_flush_all: bool,
size: usize,
}
impl OpsStack {
const fn new() -> Self {
const ARRAY_REPEAT_VALUE: Option<TlbFlushOp> = None;
Self {
ops: [ARRAY_REPEAT_VALUE; FLUSH_ALL_OPS_THRESHOLD],
need_flush_all: false,
size: 0,
}
}
fn push(&mut self, op: TlbFlushOp) {
if self.need_flush_all {
return;
}
if self.size < FLUSH_ALL_OPS_THRESHOLD {
self.ops[self.size] = Some(op);
self.size += 1;
} else {
self.need_flush_all = true;
self.size = 0;
}
}
fn flush_all(&mut self) {
if self.need_flush_all {
crate::arch::mm::tlb_flush_all_excluding_global();
self.need_flush_all = false;
} else {
for i in 0..self.size {
if let Some(op) = &self.ops[i] {
op.perform_on_current();
}
}
}
self.size = 0;
}
}