Skip to main content

ostd/mm/
tlb.rs

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