Skip to main content

ostd/mm/
tlb.rs

1// SPDX-License-Identifier: MPL-2.0
2//! TLB flush operations.
3use vstd::prelude::*;
4
5use vstd_extra::cast_ptr::Repr;
6use vstd_extra::ownership::*;
7
8use alloc::vec::Vec;
9use core::{
10    ops::Range,
11    sync::atomic::{AtomicBool, Ordering},
12};
13
14use super::{
15    PAGE_SIZE, Vaddr,
16    frame::{Frame, meta::AnyFrameMeta},
17};
18
19use crate::specs::mm::cpu::{AtomicCpuSet, CpuSet, PinCurrentCpu};
20use crate::specs::mm::frame::meta_owners::MetaSlotStorage;
21use crate::specs::mm::page_table::Mapping;
22use crate::specs::mm::tlb::TlbModel;
23
24/*use crate::{
25    arch::irq,
26    cpu::{AtomicCpuSet, CpuSet, PinCurrentCpu},
27    cpu_local,
28    sync::{LocalIrqDisabled, SpinLock},
29};*/
30
31#[verus_verify]
32verus! {
33
34/// A TLB flusher that is aware of which CPUs are needed to be flushed.
35///
36/// The flusher needs to stick to the current CPU.
37pub struct TlbFlusher<'a  /*, G: PinCurrentCpu*/ > {
38    target_cpus: &'a AtomicCpuSet,
39    have_unsynced_flush: CpuSet,
40    ops_stack: OpsStack,
41    //_pin_current: G,
42}
43
44impl<'a  /*, G: PinCurrentCpu*/ > TlbFlusher<'a  /*, G*/ > {
45    /// Creates a new TLB flusher with the specified CPUs to be flushed.
46    ///
47    /// The target CPUs should be a reference to an [`AtomicCpuSet`] that will
48    /// be loaded upon [`Self::dispatch_tlb_flush`].
49    ///
50    /// The flusher needs to stick to the current CPU. So please provide a
51    /// guard that implements [`PinCurrentCpu`].
52    pub fn new(target_cpus: &'a AtomicCpuSet  /*, pin_current_guard: G*/ ) -> Self {
53        Self {
54            target_cpus,
55            have_unsynced_flush: CpuSet::new_empty(),
56            ops_stack: OpsStack::new(),
57            //_pin_current: pin_current_guard,
58        }
59    }
60
61    /// Issues a pending TLB flush request.
62    ///
63    /// This function does not guarantee to flush the TLB entries on either
64    /// this CPU or remote CPUs. The flush requests are only performed when
65    /// [`Self::dispatch_tlb_flush`] is called.
66    #[verus_spec(
67        with Tracked(model): Tracked<&mut TlbModel>
68        ensures
69            *final(model) == old(model).issue_tlb_flush(op),
70            final(model).inv(),
71    )]
72    #[verifier::external_body]
73    pub fn issue_tlb_flush(&mut self, op: TlbFlushOp) {
74        self.ops_stack.push(op, None);
75    }
76
77    /// Issues a TLB flush request that must happen before dropping the page.
78    ///
79    /// If we need to remove a mapped page from the page table, we can only
80    /// recycle the page after all the relevant TLB entries in all CPUs are
81    /// flushed. Otherwise if the page is recycled for other purposes, the user
82    /// space program can still access the page through the TLB entries. This
83    /// method is designed to be used in such cases.
84    #[verus_spec(r =>
85        with Tracked(model): Tracked<&mut TlbModel>
86        ensures
87            *final(model) == old(model).issue_tlb_flush(op),
88            final(model).inv(),
89    )]
90    #[verifier::external_body]
91    pub fn issue_tlb_flush_with(
92        &mut self,
93        op: TlbFlushOp,
94        drop_after_flush: Frame<dyn AnyFrameMeta>,
95    ) {
96        self.ops_stack.push(op, Some(drop_after_flush));
97    }
98
99    /// Dispatches all the pending TLB flush requests.
100    ///
101    /// All previous pending requests issued by [`Self::issue_tlb_flush`] or
102    /// [`Self::issue_tlb_flush_with`] starts to be processed after this
103    /// function. But it may not be synchronous. Upon the return of this
104    /// function, the TLB entries may not be coherent.
105    #[verifier::external_body]
106    #[verus_spec(r =>
107        with Tracked(model): Tracked<&mut TlbModel>
108        requires
109            old(model).inv(),
110        ensures
111            *final(model) == old(model).dispatch_tlb_flush_spec(),
112            final(model).inv(),
113    )]
114    pub fn dispatch_tlb_flush(&mut self) {
115        unimplemented!()/*let irq_guard = crate::trap::irq::disable_local();
116
117        if self.ops_stack.is_empty() {
118            return;
119        }
120
121        // `Release` to make sure our modification on the PT is visible to CPUs
122        // that are going to activate the PT.
123        let mut target_cpus = self.target_cpus.load(Ordering::Release);
124
125        let cur_cpu = irq_guard.current_cpu();
126        let mut need_flush_on_self = false;
127
128        if target_cpus.contains(cur_cpu) {
129            target_cpus.remove(cur_cpu);
130            need_flush_on_self = true;
131        }
132
133        for cpu in target_cpus.iter() {
134            {
135                let mut flush_ops = FLUSH_OPS.get_on_cpu(cpu).lock();
136                flush_ops.push_from(&self.ops_stack);
137
138                // Clear ACK before dropping the lock to avoid false ACKs.
139                ACK_REMOTE_FLUSH
140                    .get_on_cpu(cpu)
141                    .store(false, Ordering::Relaxed);
142            }
143            self.have_unsynced_flush.add(cpu);
144        }
145
146        crate::smp::inter_processor_call(&target_cpus, do_remote_flush);
147
148        // Flush ourselves after sending all IPIs to save some time.
149        if need_flush_on_self {
150            self.ops_stack.flush_all();
151        } else {
152            self.ops_stack.clear_without_flush();
153        }*/
154
155    }
156
157    /// Waits for all the previous TLB flush requests to be completed.
158    ///
159    /// After this function, all TLB entries corresponding to previous
160    /// dispatched TLB flush requests are guaranteed to be coherent.
161    ///
162    /// The TLB flush requests are issued with [`Self::issue_tlb_flush`] and
163    /// dispatched with [`Self::dispatch_tlb_flush`]. This method will not
164    /// dispatch any issued requests so it will not guarantee TLB coherence
165    /// of requests that are not dispatched.
166    ///
167    /// # Panics
168    ///
169    /// This method panics if the IRQs are disabled. Since the remote flush are
170    /// processed in IRQs, two CPUs may deadlock if they are waiting for each
171    /// other's TLB coherence.
172    #[verifier::external_body]
173    pub fn sync_tlb_flush(&mut self) {
174        unimplemented!()/*
175        assert!(
176            irq::is_local_enabled(),
177            "Waiting for remote flush with IRQs disabled"
178        );
179
180        for cpu in self.have_unsynced_flush.iter() {
181            while !ACK_REMOTE_FLUSH.get_on_cpu(cpu).load(Ordering::Relaxed) {
182                core::hint::spin_loop();
183            }
184        }
185
186        self.have_unsynced_flush = CpuSet::new_empty();
187        */
188
189    }
190}
191
192/// The operation to flush TLB entries.
193#[derive(Debug, Clone, PartialEq, Eq)]
194pub enum TlbFlushOp {
195    /// Flush all TLB entries except for the global entries.
196    All,
197    /// Flush the TLB entry for the specified virtual address.
198    Address(Vaddr),
199    /// Flush the TLB entries for the specified virtual address range.
200    Range(Range<Vaddr>),
201}
202
203impl TlbFlushOp {
204    /// Performs the TLB flush operation on the current CPU.
205    #[verifier::external_body]
206    pub fn perform_on_current(&self) {
207        unimplemented!()/*use crate::arch::mm::{
208            tlb_flush_addr, tlb_flush_addr_range, tlb_flush_all_excluding_global,
209        };
210        match self {
211            TlbFlushOp::All => tlb_flush_all_excluding_global(),
212            TlbFlushOp::Address(addr) => tlb_flush_addr(*addr),
213            TlbFlushOp::Range(range) => tlb_flush_addr_range(range),
214        }*/
215
216    }
217
218    #[verifier::external_body]
219    fn optimize_for_large_range(self) -> Self {
220        match self {
221            TlbFlushOp::Range(range) => {
222                if range.len() > FLUSH_ALL_RANGE_THRESHOLD {
223                    TlbFlushOp::All
224                } else {
225                    TlbFlushOp::Range(range)
226                }
227            },
228            _ => self,
229        }
230    }
231}
232
233// The queues of pending requests on each CPU.
234/*cpu_local! {
235    static FLUSH_OPS: SpinLock<OpsStack, LocalIrqDisabled> = SpinLock::new(OpsStack::new());
236    /// Whether this CPU finishes the last remote flush request.
237    static ACK_REMOTE_FLUSH: AtomicBool = AtomicBool::new(true);
238}*/
239
240#[verifier::external_body]
241fn do_remote_flush() {
242    unimplemented!()/*
243    // No races because we are in IRQs or have disabled preemption.
244    let current_cpu = crate::cpu::CpuId::current_racy();
245
246    let mut new_op_queue = OpsStack::new();
247    {
248        let mut op_queue = FLUSH_OPS.get_on_cpu(current_cpu).lock();
249
250        core::mem::swap(&mut *op_queue, &mut new_op_queue);
251
252        // ACK before dropping the lock so that we won't miss flush requests.
253        ACK_REMOTE_FLUSH
254            .get_on_cpu(current_cpu)
255            .store(true, Ordering::Relaxed);
256    }
257    // Unlock the locks quickly to avoid contention. ACK before flushing is
258    // fine since we cannot switch back to userspace now.
259    new_op_queue.flush_all();
260    */
261
262}
263
264/// If a TLB flushing request exceeds this threshold, we flush all.
265const FLUSH_ALL_RANGE_THRESHOLD: usize = 32 * PAGE_SIZE;
266
267/// If the number of pending requests exceeds this threshold, we flush all the
268/// TLB entries instead of flushing them one by one.
269const FLUSH_ALL_OPS_THRESHOLD: usize = 32;
270
271struct OpsStack {
272    ops: [Option<TlbFlushOp>; FLUSH_ALL_OPS_THRESHOLD],
273    need_flush_all: bool,
274    size: usize,
275    //    page_keeper: Vec<Frame<dyn AnyFrameMeta>>,
276}
277
278impl OpsStack {
279    #[verifier::external_body]
280    const fn new() -> Self {
281        Self {
282            ops: [const { None };FLUSH_ALL_OPS_THRESHOLD],
283            need_flush_all: false,
284            size: 0,
285            //            page_keeper: Vec::new(),
286        }
287    }
288
289    #[verifier::external_body]
290    fn is_empty(&self) -> bool {
291        !self.need_flush_all && self.size == 0
292    }
293
294    #[verifier::external_body]
295    fn push(&mut self, op: TlbFlushOp, drop_after_flush: Option<Frame<dyn AnyFrameMeta>>) {
296        if let Some(frame) = drop_after_flush {
297            //            self.page_keeper.push(frame);
298        }
299        if self.need_flush_all {
300            return;
301        }
302        let op = op.optimize_for_large_range();
303        if op == TlbFlushOp::All || self.size >= FLUSH_ALL_OPS_THRESHOLD {
304            self.need_flush_all = true;
305            self.size = 0;
306            return;
307        }
308        self.ops[self.size] = Some(op);
309        self.size += 1;
310    }
311
312    #[verifier::external_body]
313    fn push_from(&mut self, other: &OpsStack) {
314        //        self.page_keeper.extend(other.page_keeper.iter().cloned());
315        if self.need_flush_all {
316            return;
317        }
318        if other.need_flush_all || self.size + other.size >= FLUSH_ALL_OPS_THRESHOLD {
319            self.need_flush_all = true;
320            self.size = 0;
321            return;
322        }
323        for i in 0..other.size {
324            self.ops[self.size] = other.ops[i].clone();
325            self.size += 1;
326        }
327    }
328
329    #[verifier::external_body]
330    fn flush_all(&mut self) {
331        if self.need_flush_all {
332            unimplemented!()  /*crate::arch::mm::tlb_flush_all_excluding_global();*/
333
334        } else {
335            for i in 0..self.size {
336                if let Some(op) = &self.ops[i] {
337                    op.perform_on_current();
338                }
339            }
340        }
341
342        self.clear_without_flush();
343    }
344
345    #[verifier::external_body]
346    fn clear_without_flush(&mut self) {
347        self.need_flush_all = false;
348        self.size = 0;
349        //        self.page_keeper.clear();
350    }
351}
352
353} // verus!